篇(18)-Asp.Net Core入門實戰-文章管理之文章內容管理(下拉框二級結構遞迴)

2022-11-21 18:06:09

篇(18)-Asp.Net Core入門實戰-文章管理之文章內容管理(下拉框二級結構遞迴實現)

文章管理是CMS系統的核心表之一,儲存文章內容,特點就是欄位端,屬性多,比如是否標識為熱點、推薦等屬性,是否釋出,類別,SEO關鍵字等。我們本章講解文章內容的增刪改查。

(1).文章Sql表結構設計

CREATE TABLE [dbo].[Article](
[Id] [int] IDENTITY(1,1) NOT NULL,
[CategoryId] [int] NOT NULL,
[Title] [varchar](128) NOT NULL,
[ImageUrl] [varchar](128) NULL,
[Content] [text] NULL,
[ViewCount] [int] NOT NULL,
[Sort] [int] NOT NULL,
[Author] [varchar](64) NULL,
[Source] [varchar](128) NULL,
[SeoTitle] [varchar](128) NULL,
[SeoKeyword] [varchar](256) NULL,
[SeoDescription] [varchar](512) NULL,
[AddManagerId] [int] NOT NULL,
[AddTime] [datetime] NOT NULL,
[ModifyManagerId] [int] NULL,
[ModifyTime] [datetime] NULL,
[IsTop] [bit] NOT NULL,
[IsSlide] [bit] NOT NULL,
[IsRed] [bit] NOT NULL,
[IsPublish] [bit] NOT NULL,
[IsDeleted] [bit] NOT NULL,
CONSTRAINT [PK_ARTICLE] PRIMARY KEY NONCLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[Article] ADD DEFAULT (getdate()) FOR [AddTime]
GO
ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsTop]
GO
ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsSlide]
GO
ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsRed]
GO
ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsPublish]
GO
ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsDeleted]
GO
ALTER TABLE [dbo].[Article] WITH CHECK ADD CONSTRAINT [FK_ARTICLE_RELATIONS_ARTICLEC] FOREIGN KEY([CategoryId])
REFERENCES [dbo].[ArticleCategory] ([Id])
GO
ALTER TABLE [dbo].[Article] CHECK CONSTRAINT [FK_ARTICLE_RELATIONS_ARTICLEC]
GO

 

那麼對應的Article Model程式碼如下:

public class Article
{
/// <summary>
/// 主鍵
/// </summary>
[Key]
public Int32 Id { get; set; }
/// <summary>
/// 分類ID
/// </summary>
[Required]
public Int32 CategoryId { get; set; }
/// <summary>
/// 文章標題
/// </summary>
[Required]
public String Title { get; set; }
/// <summary>
/// 圖片地址
/// </summary>
public String ImageUrl { get; set; }
/// <summary>
/// 文章內容
/// </summary>
public String Content { get; set; }
/// <summary>
/// 瀏覽次數
/// </summary>
[Required]
public Int32 ViewCount { get; set; }
/// <summary>
/// 排序
/// </summary>
[Required]
public Int32 Sort { get; set; }
/// <summary>
/// 作者
/// </summary>
public String Author { get; set; }
/// <summary>
/// 來源
/// </summary>
public String Source { get; set; }
/// <summary>
/// SEO標題
/// </summary>
public String SeoTitle { get; set; }
/// <summary>
/// SEO關鍵字
/// </summary>
public String SeoKeyword { get; set; }
/// <summary>
/// SEO描述
/// </summary>
public String SeoDescription { get; set; }
/// <summary>
/// 新增人ID
/// </summary>
[Required]
public Int32 AddManagerId { get; set; }
/// <summary>
/// 新增時間
/// </summary>
[Required]
public DateTime AddTime { get; set; }
/// <summary>
/// 修改人ID
/// </summary>
public Int32? ModifyManagerId { get; set; }
/// <summary>
/// 修改時間
/// </summary>
public DateTime? ModifyTime { get; set; }
/// <summary>
/// 是否置頂
/// </summary>
public Boolean IsTop { get; set; }
/// <summary>
/// 是否輪播顯示
/// </summary>
public Boolean IsSlide { get; set; }
/// <summary>
/// 是否熱門
/// </summary>
public Boolean IsRed { get; set; }
/// <summary>
/// 是否釋出
/// </summary>
public Boolean IsPublish { get; set; }
/// <summary>
/// 是否刪除
/// </summary>
[Required]
public Boolean IsDeleted { get; set; }
}

 

(2).檢視Create程式碼

(2.1)檢視程式碼

考慮到要同時上傳圖片,注意form表單的額 enctype型別;

@{ ViewData["Title"] = "新建文章"; }
@model Article
<form action="/Article/Create" method="post" enctype="multipart/form-data">
@Html.AntiForgeryToken()
<div>
<label asp-for="Title">標題</label>
<div>
<input type="text" asp-for="Title" name="Title" placeholder="請輸入標題">
</div>
</div>
<div>
<label asp-for="CategoryId">文章型別</label>
<div>
@Html.DropDownList("ddl_CategoryId", ViewBag.database as IEnumerable<SelectListItem>)
</div>
</div>
<div>
<label>設定</label>
<div>
@*@Html.CheckBox("IsTop") 置頂
@Html.CheckBox("IsRed") 熱點
@Html.CheckBox("IsSlide") 幻燈*@
<input type="checkbox" name="IsTop" asp-for="IsTop" />置頂
<input type="checkbox" name="IsRed" asp-for="IsRed"/>熱點
<input type="checkbox" name="IsSlide" asp-for="IsSlide" />幻燈
</div>
</div>
<div>
<label asp-for="ImageUrl">文章首頁圖</label>
<div>
<input type="file" asp-for="ImageUrl" name="ImageUrl"/>
</div>
</div>
<div>
<label asp-for="Content">內容</label>
<div>
<textarea placeholder="內容" asp-for="Content" name="Content" cols="30" rows="10"></textarea>
</div>
</div>
<div>
<label asp-for="Sort">排序</label>
<div>
<input type="text" placeholder="排序" asp-for="Sort" name="Sort" />
</div>
</div>
<div>
<label asp-for="ViewCount">點選量</label>
<div>
<input type="text" placeholder="點選量" asp-for="ViewCount" name="ViewCount" />
</div>
</div>
<div>
<label asp-for="IsPublish">是否釋出</label>
<div>
<select asp-for="IsPublish" name="IsPublish" class="IsPublish">
<option value="False"></option>
<option value="True" selected></option>
</select>
</div>
</div>
<div>
<label asp-for="Author">作者</label>
<div>
<input type="text" asp-for="Author" name="Author" placeholder="作者名">
</div>
</div>
<div>
<label asp-for="Source">來源</label>
<div>
<input type="text" asp-for="Source" name="Source" placeholder="文章來源">
</div>
</div>
<div>
<label asp-for="SeoTitle">SEO標題</label>
<div>
<input type="text" asp-for="SeoTitle" name="SeoTitle" placeholder="SEO標題">
</div>
</div>
<div>
<label asp-for="SeoKeyword">SEO關鍵詞</label>
<div>
<input type="text" asp-for="SeoKeyword" name="SeoKeyword" placeholder="SEO關鍵詞">
</div>
</div>
<div>
<label asp-for="SeoDescription">SEO摘要描述</label>
<div>
<input type="text" asp-for="SeoDescription" name="SeoDescription" placeholder="SEO摘要描述">
</div>
</div>
<div>
<div>
<button type="submit">確定</button>
<button type="reset">重置</button>
</div>
</div>
</form>

 

(2.2)檢視中的下拉框的實現方式(遞迴和迴圈巢狀)

我想在新增文章時,實現一個具有二級層次結構的下拉框,如上圖所示。所以,在對下拉框進行資料繫結時,就要費點功夫,上個章節講文章類別管理時,的表結構就一個,分類都存在一張表中,所以要進行遞迴的獲取子選單或者通過迴圈巢狀來實現。

遞迴的主要核心函數為:

/// <summary>
/// 遞迴函數,實現獲取子選單
/// </summary>
/// <param name="lists">遞迴前的列表</param>
/// <param name="newlists">遞迴後的新列表</param>
/// <param name="parentId">父Id</param>
/// <returns></returns>
public static List<CategorySelectItemListView> GetChildCategory(List<CategorySelectItemListView> lists, List<CategorySelectItemListView> newlists, int parentId)
{
newlists = new List<CategorySelectItemListView>();
List<CategorySelectItemListView> tempList = lists.Where(c => c.ParentId == parentId).ToList();
for (int i = 0; i < tempList.Count; i++)
{
CategorySelectItemListView category = new CategorySelectItemListView();
category.Id = tempList[i].Id;
category.ParentId = tempList[i].ParentId;
category.Title = tempList[i].Title;
category.Children = GetChildCategory(lists, newlists, category.Id);
newlists.Add(category);
}
return newlists;
}


/// <summary>
/// 迴圈巢狀,實現獲取子選單
/// </summary>
/// <param name="lists">迴圈遍歷前的列表</param>
/// <returns></returns>
public static List<CategorySelectItemListView> GetChildCategory(List<CategorySelectItemListView> lists)
{
List<CategorySelectItemListView> categorylist = new List<CategorySelectItemListView>();
for (int i = 0; i < lists.Count; i++)
{
if (0 == lists[i].ParentId)
categorylist.Add(lists[i]);
for (int j = 0; j < lists.Count; j++)
{
if (lists[j].ParentId == lists[i].Id)
lists[i].Children.Add(lists[j]);
}
}
return categorylist;
}

 

然後在Create和Edit的Action中去繫結對應的下拉式選單即可。

注意:List<CategorySelectItemListView> 集合的CategorySelectItemListView,這個是新建的ViewModel物件,用來專門繫結下拉式選單使用,其程式碼如下:

public class CategorySelectItemListView
{
public int Id { get; set; }
public string Title { get; set; }
public int ParentId { get; set; }
public List<CategorySelectItemListView> Children { get; set; }
public CategorySelectItemListView()
{
Children = new List<CategorySelectItemListView>();
}

public CategorySelectItemListView(int id,string title,int parentid)
{
this.Id = id;
this.Title = title;
this.ParentId = parentid;
Children = new List<CategorySelectItemListView>();
}

public CategorySelectItemListView(int id, string title, CategorySelectItemListView parent)
{
this.Id = id;
this.Title = title;
this.ParentId = parent.Id;
Children = new List<CategorySelectItemListView>();
}

 

(3).檢視Edit程式碼,註解部分的程式碼可以參考,我嘗試用過,也可以達到目的,演練程式碼最好是用多種方式實現,檢視其區別,這樣掌握的牢固一些

@{ ViewData["Title"] = "編輯文章"; }
@model Article
@section Scripts{
<script type="text/javascript" src="~/js/jquery-3.6.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
type: "GET",
url: "/ArticleCategory/GetCategory",
data: "{}",
success: function (data) {
console.log(data);
var s = '<option value="0">請選擇</option>';
for (var i = 0; i < data.length; i++) {
s += '<option value="' + data[i].Title + '"+>' + data[i].Id + '</option>';
}
$("#ArticleCategory").html(s);
}
});
});
</script>
}

<form action="/Article/Edit" method="post" enctype="multipart/form-data">
@Html.AntiForgeryToken()
<div>
<label asp-for="Title">標題</label>
<div>
<input type="text" asp-for="Title" name="Title" placeholder="請輸入標題">
<input type="hidden" asp-for="Id" />
</div>
</div>
<div>
<label asp-for="CategoryId">文章型別</label>
<div>
@Html.DropDownList("ddl_CategoryId", ViewBag.database as IEnumerable<SelectListItem>)
</div>
</div>
<div>
<label>設定</label>
<div>
@*@Html.CheckBox("IsTop", Model.IsTop,new { value = Model.IsTop}) 置頂
@Html.CheckBox("IsRed", Model.IsRed, new { value = Model.IsRed }) 熱點
@Html.CheckBox("IsSlide", Model.IsSlide, new { value = Model.IsSlide }) 幻燈*@
<input asp-for="IsTop" />置頂
<input asp-for="IsRed" />熱點
<input asp-for="IsSlide" />幻燈
@*<input type="checkbox" name="IsTop" @(Html.Raw(@Model.IsTop ? "checked=\"checked\"" : "")) asp-for="IsTop" />置頂
<input type="checkbox" name="IsRed" @(Html.Raw(@Model.IsRed ? "checked=\"checked\"" : "")) asp-for="IsRed" />熱點
<input type="checkbox" name="IsSlide" @(Html.Raw(@Model.IsSlide ? "checked=\"checked\"" : "")) asp-for="IsSlide" />幻燈*@
@*<input type="checkbox" name="IsTop" asp-for="IsTop" />置頂
<input type="checkbox" name="IsRed" asp-for="IsRed"/>熱點
<input type="checkbox" name="IsSlide" asp-for="IsSlide"/>幻燈*@
</div>
</div>
<div>
<label asp-for="ImageUrl">文章首頁圖</label>
<div>
<input type="file" asp-for="ImageUrl" name="ImageUrl" />
<label asp-for="ImageUrl">@Model.ImageUrl</label>
</div>
</div>
<div>
<label asp-for="Content">內容</label>
<div>
<textarea placeholder="內容" asp-for="Content" name="Content" cols="30" rows="10"></textarea>
</div>
</div>
<div>
<label asp-for="Sort">排序</label>
<div>
<input type="text" placeholder="排序" asp-for="Sort" name="Sort" />
</div>
</div>
<div>
<label asp-for="ViewCount">點選量</label>
<div>
<input type="text" placeholder="點選量" asp-for="ViewCount" name="ViewCount" />
</div>
</div>
<div>
<label asp-for="IsPublish">是否釋出</label>
<div>
<select asp-for="IsPublish" name="IsPublish" class="IsPublish">
<option value="False"></option>
<option value="True" selected></option>
</select>
</div>
</div>
<div>
<label asp-for="Author">作者</label>
<div>
<input type="text" asp-for="Author" name="Author" placeholder="作者名">
</div>
</div>
<div>
<label asp-for="Source">來源</label>
<div>
<input type="text" asp-for="Source" name="Source" placeholder="文章來源">
</div>
</div>
<div>
<label asp-for="SeoTitle">SEO標題</label>
<div>
<input type="text" asp-for="SeoTitle" name="SeoTitle" placeholder="SEO標題">
</div>
</div>
<div>
<label asp-for="SeoKeyword">SEO關鍵詞</label>
<div>
<input type="text" asp-for="SeoKeyword" name="SeoKeyword" placeholder="SEO關鍵詞">
</div>
</div>
<div>
<label asp-for="SeoDescription">SEO摘要描述</label>
<div>
<input type="text" asp-for="SeoDescription" name="SeoDescription" placeholder="SEO摘要描述">
</div>
</div>
<div>
<div>
<button type="submit">確定</button>
<button type="reset">重置</button>
</div>
</div>
</form>

 

(4).檢視Index列表的程式碼

針對列表的顯示,又專門編寫了ArticeView的這個ViewModel。

public class ArticleView
{
public int Id { get; set; }
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public string Title { get; set; }
public int ViewCount { get; set; }
public int Sort { get; set; }
public string Author { get; set; }
public string Source { get; set; }
public int AddManagerId { get; set; }
public DateTime AddTime { get; set; }
}

 

@using Humanizer;
@using RjWebCms.Db;
@using RjWebCms.Models.Articles;
@model PaginatedList<ArticleView>
@{
ViewData["Title"] = "文章列表";
}
@section Scripts{
<script src="~/js/jquery-2.1.0.min.js"></script>
<script type="text/javascript">
function DelAll() {
var ids = document.getElementsByName("#chk_ids");
var arrIds = "";
var n = 0;
for (var i = 0; i < ids.length; i++)
{
if (ids[i].checked == true) {
arrIds += ids[i].value + ",";
n++;
}
}
if (n == 0) {
alert("請選擇要刪除的資訊");
return;
}
arrIds = arrids.substr(0, arrIds.length - 1);
//
if (confirm("確定要全部刪除選擇資訊嗎")) {
$.ajax({
type: "post",
url: "/Article/DeleteAll",
data: { ids: arrIds },
success: function (data, state) {
alert('刪除成功!');
window.location.href = "";
},

error: function (data, state) {
alert('刪除失敗');
}
});
}
}
</script>
}
<div class="panel panel-default todo-panel">
<div class="panel-heading">@ViewData["Title"]</div>
@Html.AntiForgeryToken()
<form asp-action="Index" method="get">
<table>
<tr><td><a asp-controller="Article" asp-action="Create">新增</a></td></tr>
<tr>
<td>查詢關鍵詞:<input type="text" name="SearchString" value="@ViewData["CurrentFilter"]" /></td>
<td><input type="submit" value="查詢" /></td>
<td><a asp-action="Index">Back</a></td>
<td><a asp-action="DeleteAll">批次刪除</a></td>
</tr>
</table>
</form>
<table class="table table-hover">
<thead>
<tr>
<td>&#x2714;</td>
<td><a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">標題</a></td>
<td>類別</td>
<td><a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">新增時間</a></td>
<td>作者</td>
<td>操作</td>
</tr>
@foreach (var item in Model)
{
<tr>
<td><input type="checkbox" class="done-checkbox" name="chk_ids" value="@item.Id"></td>
<td>@item.Title</td>
<td>@item.CategoryName</td>
<td>@item.AddTime</td>
<td>@item.Author</td>
<td>
<a asp-action="Details" asp-route-id="@item.Id">Details</a>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a>
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</thead>
</table>
@{
var prevDisabled = !Model.HasPreviousPage ? "disabled" : "";
var nextDisabled = !Model.HasNextPage ? "disabled" : ""; ;
}

<a asp-action="Index"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-pageNumber="@(Model.PageIndex - 1)"
asp-route-currentFilter="@ViewData["CurrentFilter"]"
class="btn btn-default @prevDisabled">
上一頁
</a>
<a asp-action="Index"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-pageNumber="@(Model.PageIndex + 1)"
asp-route-currentFilter="@ViewData["CurrentFilter"]"
class="btn btn-default @nextDisabled">
下一頁
</a>
<div class="panel-footer add-item-form">
<!--TODO: Add item form -->
</div>
</div>

 

(5).Controller部分的全部程式碼,注意看程式碼註釋

public class ArticleController : Controller
    {
        private readonly IHostEnvironment _hostEnvironment;
        private readonly IArticleService _articleService;
        private readonly IArticleCategoryService _articleCategoryService;
        private readonly AppDbContext _appDbContext;
        public ArticleController(IArticleService articleService, IArticleCategoryService articleCategoryService,AppDbContext appDbContext,IHostEnvironment hostEnvironment)
        {
            _hostEnvironment = hostEnvironment;
            _appDbContext = appDbContext;
            _articleService = articleService;
            _articleCategoryService = articleCategoryService;
        }

        public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? pageNumber)
        {
            ViewData["CurrentSort"] = sortOrder;
            ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
            ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";

            if (searchString != null)
            {
                pageNumber = 1;
            }
            else
            {
                searchString = currentFilter;
            }
            ViewData["CurrentFilter"] = searchString;
            var article = from s in _appDbContext.Article
                          join p in _appDbContext.ArticleCategory on s.CategoryId equals p.Id
                          select new ArticleView { 
                            Id = s.Id,
                            CategoryId = s.CategoryId,
                            CategoryName = p.Title,
                            Title = s.Title,
                            Sort = s.Sort,
                            AddManagerId = s.AddManagerId,
                            AddTime = s.AddTime,
                            Author = s.Author,
                            Source = s.Source,
                            ViewCount = s.ViewCount,
                          };
            if (!string.IsNullOrEmpty(searchString))
            {
                article = article.Where(s => s.Title.Contains(searchString));
            }
            switch (sortOrder)
            {
                case "name_desc":
                    article = article.OrderByDescending(s => s.Title) ;
                    break;
                case "Date":
                    article = article.OrderBy(s => s.AddTime);
                    break;
                case "date_desc":
                    article = article.OrderByDescending(s => s.AddTime);
                    break;
                default:
                    article = article.OrderBy(s => s.Title);
                    break;
            }
            int pageSize = 4;
            return View(await PaginatedList<ArticleView>.CreateAsync(article.AsNoTracking(), pageNumber ?? 1, pageSize));
        }


        [HttpGet]
        public async Task<IActionResult> CreateAsync()
        {
            #region 繫結類別下拉框
            var categories = await _articleCategoryService.GetArticleCategory();//列出文章類別字典
            var categoryItems = new List<SelectListItem>()
            {
                new SelectListItem(){ Value="0",Text="全部",Selected=true}
            };
            //全部列出並轉成DropDownList物件
            List<CategorySelectItemListView> list = new List<CategorySelectItemListView>();
            foreach (var category in categories)
            {
                list.Add(new CategorySelectItemListView { 
                    Id=category.Id,
                    Title = category.Title,
                    ParentId = category.ParentId
                });
            }

            #region 迴圈巢狀呼叫
            //List<CategorySelectItemListView> list1 = GetChildCategory(list);
            //foreach (var li in list1)
            //{
            //    categoryItems.Add(new SelectListItem { Value = li.Id.ToString(), Text = li.Title });
            //    if (li.Children.Count > 0)
            //    { 
            //        foreach(var t in li.Children)
            //            categoryItems.Add(new SelectListItem { Value = t.Id.ToString(),Text= "|-" + t.Title });
            //    }
            //}
            #endregion

            #region  遞迴呼叫
            List<CategorySelectItemListView> list1 = GetChildCategory(list, new List<CategorySelectItemListView>(), 0);
            foreach (var li in list1)
            {
                categoryItems.Add(new SelectListItem { Value = li.Id.ToString(), Text = li.Title });
                if (li.Children.Count > 0)
                {
                    foreach (var t in li.Children)
                        categoryItems.Add(new SelectListItem { Value = t.Id.ToString(), Text = "  |-" + t.Title });
                }
            }
            #endregion

            ViewBag.database = categoryItems;
            #endregion
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> CreateAsync(Article article,[FromForm]IFormCollection fromData)
        {
            //去掉對欄位IsSystem的驗證,IsSystem在資料庫是bool型別,而前端是0和1,ModelState的驗證總是報false,所以去掉對其驗證
            //ModelState.Remove("IsSystem");//在View端已經解決了了bool型別,那麼此行程式碼可以不用
            #region 下拉式選單
            string strCategoryId = Request.Form["ddl_CategoryId"];
            if (!string.IsNullOrEmpty(strCategoryId))
                article.CategoryId = int.Parse(strCategoryId);
            else
                article.CategoryId = 0;
            #endregion

            #region 核取方塊
            article.IsTop = fromData["IsTop"] != "false";//使用FormCollection時,可以這樣
            article.IsRed = fromData["IsRed"] != "false";
            article.IsSlide = fromData["IsSlide"] != "false";
            //也可以這樣取值,但要注意View內的寫法
            //if (!string.IsNullOrEmpty(fromData["IsTop"]))
            //    article.IsTop = true;
            //else
            //    article.IsTop = false;
            #endregion

            #region 上傳檔案
            IFormFileCollection files = fromData.Files;
           foreach(var formFile in files)
            {
                //var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
                string webContentPath = _hostEnvironment.ContentRootPath;
                var fileExt = formFile.FileName.Substring(formFile.FileName.LastIndexOf('.'));//副檔名
                var fileNew = DateTime.Now.ToString("yyyyMMddHHmmss") + fileExt; //給檔案重新命名
                
                //string upLoadPath = webContentPath + $@"\{fileName}";
                string upLoadPath = webContentPath + $@"\UpFiles";
                var fileUrl = upLoadPath + $@"\{fileNew}";

                if (formFile.Length > 0)
                {
                    using (var stream = new FileStream(fileUrl,FileMode.Create))
                    {
                        await formFile.CopyToAsync(stream);
                    }
                }
                article.ImageUrl = "../UpFiles/" + fileNew;


            }       
            #endregion 

            if (ModelState.IsValid)
            {
                var successful = await _articleService.AddArticleAysnc(article);
                if (successful)
                    return RedirectToAction("Index");
                else
                    return BadRequest("失敗");
            }
            return View(article);
        }


        [HttpGet]
        public async Task<IActionResult> Edit(int id)
        {

            if (string.IsNullOrEmpty(id.ToString()))
                return NotFound();

            var article = await _articleService.FindArticleAsync(id);
            if (article == null)
                return NotFound();

            #region 繫結角色下拉框
            var categories = await _articleCategoryService.GetArticleCategory();//列出文章類別字典
            var categoryItems = new List<SelectListItem>()
            {
                new SelectListItem(){ Value="0",Text="全部",Selected=true}
            };
            //全部列出並轉成DropDownList物件
            List<CategorySelectItemListView> list = new List<CategorySelectItemListView>();
            foreach (var category in categories)
            {
                list.Add(new CategorySelectItemListView
                {
                    Id = category.Id,
                    Title = category.Title,
                    ParentId = category.ParentId
                });
            }
            #region  遞迴呼叫
            List<CategorySelectItemListView> list1 = GetChildCategory(list, new List<CategorySelectItemListView>(), 0);
            foreach (var li in list1)
            {
                categoryItems.Add(new SelectListItem { Value = li.Id.ToString(), Text = li.Title });
                if (li.Children.Count > 0)
                {
                    foreach (var t in li.Children)
                        categoryItems.Add(new SelectListItem { Value = t.Id.ToString(), Text = "  |-" + t.Title });
                }
            }
            #endregion

            #region 遍歷並選中
            foreach (SelectListItem item in categoryItems)
            {
                if (item.Value == article.CategoryId.ToString())
                    item.Selected = true;
            }
            #endregion 
            ViewBag.database = categoryItems;
            #endregion

            return View(article);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [FromForm]Article article)
        {
            if (id != article.Id)
            {
                return NotFound();
            }
            #region 下拉式選單
            string strCategoryId = Request.Form["ddl_CategoryId"];
            if (!string.IsNullOrEmpty(strCategoryId))
                article.CategoryId = int.Parse(strCategoryId);
            else
                article.CategoryId = 0;
            #endregion
            #region 核取方塊
            if (Request.Form["IsTop"].Contains("true"))
                article.IsTop = true;
            else
                article.IsTop = false;
            if (Request.Form["IsRed"].Contains("true"))
                article.IsRed = true;
            else
                article.IsRed = false;
            if (Request.Form["IsSlide"].Contains("true"))
                article.IsSlide = true;
            else
                article.IsSlide = false;
            #endregion 
            //ModelState.Remove("IsTop");
            //ModelState.Remove("IsRed");
            //ModelState.Remove("IsSlide");
            if (ModelState.IsValid)
            {
                try
                {
                    var result = await _articleService.UpdateArticleAsync(id, article);
                    //跳轉
                    if (result)
                        return RedirectToAction("Index");
                    else
                        return BadRequest("編輯失敗");
                }
                catch (Exception ex)
                {
                    return BadRequest("編輯失敗");
                }
            }
            else
            {
                return BadRequest("資料輸入有誤!");
            }
        }

        /// <summary>
        /// 遞迴函數,實現獲取子選單
        /// </summary>
        /// <param name="lists">遞迴前的列表</param>
        /// <param name="newlists">遞迴後的新列表</param>
        /// <param name="parentId">父Id</param>
        /// <returns></returns>
        public static List<CategorySelectItemListView> GetChildCategory(List<CategorySelectItemListView> lists, List<CategorySelectItemListView> newlists, int parentId)
        {
            newlists = new List<CategorySelectItemListView>();
            List<CategorySelectItemListView> tempList = lists.Where(c => c.ParentId == parentId).ToList();
            for (int i = 0; i < tempList.Count; i++)
            {
                CategorySelectItemListView category = new CategorySelectItemListView();
                category.Id = tempList[i].Id;
                category.ParentId = tempList[i].ParentId;
                category.Title = tempList[i].Title;
                category.Children = GetChildCategory(lists, newlists, category.Id);
                newlists.Add(category);
            }
            return newlists;
        }

        /// <summary>
        /// 迴圈巢狀,實現獲取子選單
        /// </summary>
        /// <param name="lists">迴圈遍歷前的列表</param>
        /// <returns></returns>
        public static List<CategorySelectItemListView> GetChildCategory(List<CategorySelectItemListView> lists)
        {
            List<CategorySelectItemListView> categorylist = new List<CategorySelectItemListView>();
            for (int i = 0; i < lists.Count; i++)
            {
                if (0 == lists[i].ParentId)
                    categorylist.Add(lists[i]);

                for (int j = 0; j < lists.Count; j++)
                {
                    if (lists[j].ParentId == lists[i].Id)
                        lists[i].Children.Add(lists[j]);
                }
            }
            return categorylist;
        }


    }

 

(6).Service應用層程式碼

 

    public class ArticleService : IArticleService
    {
        private readonly AppDbContext _appDbContext;
        public ArticleService(AppDbContext appDbContext)
        {
            _appDbContext = appDbContext;
        }
        /// <summary>
        /// 新增文章
        /// </summary>
        /// <param name="article"></param>
        /// <returns></returns>
        public async Task<bool> AddArticleAysnc(Article article)
        {
            article.IsDeleted = false;
            article.AddManagerId = 1;//使用者id
            article.AddTime = DateTime.Now;
            article.IsPublish = true;
            await _appDbContext.Article.AddAsync(article);
            var result = await _appDbContext.SaveChangesAsync();
            return result == 1;
        }

        /// <summary>
        /// 刪除文章
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public async Task<bool> DeleteArticleAsync(int Id)
        {
            var article = await _appDbContext.Article.FirstOrDefaultAsync(x => x.Id == Id);
            if (article != null)
            {
                _appDbContext.Article.Remove(article);
            }
            var result = await _appDbContext.SaveChangesAsync();
            return result == 1; //注意(result==1 如果等式成立,則返回true,說明刪除成功)
        }

        /// <summary>
        /// 按Id查詢文章
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public async Task<Article> FindArticleAsync(int Id)
        {
            var item = await _appDbContext.Article.Where(x => x.Id == Id).FirstOrDefaultAsync();
            return item;
        }

        /// <summary>
        /// 按標題查詢文章
        /// </summary>
        /// <param name="title"></param>
        /// <returns></returns>
        public async Task<Article[]> GetArtcleByTitle(string title)
        {
            var items = await _appDbContext.Article.Where(x => x.Title.Contains(title)).ToArrayAsync();
            return items;
        }

        /// <summary>
        /// 查詢文章
        /// </summary>
        /// <returns></returns>
        public async Task<Article[]> GetArticles()
        {
            var items = await _appDbContext.Article.Where(x => x.IsDeleted==false).ToArrayAsync();
            return items;
        }

        /// <summary>
        /// 更新文章
        /// </summary>
        /// <param name="id"></param>
        /// <param name="article"></param>
        /// <returns></returns>
        public async Task<bool> UpdateArticleAsync(int id, Article article)
        {
            var oldArticle = await  FindArticleAsync(id); //找出舊物件

            //將新值賦到舊物件上
            oldArticle.Title = article.Title;
            oldArticle.CategoryId = article.CategoryId;
            oldArticle.SeoDescription = article.SeoDescription;
            oldArticle.SeoTitle = article.SeoTitle;
            oldArticle.SeoKeyword = article.SeoKeyword;
            oldArticle.Content = article.Content;
            oldArticle.Sort = article.Sort;
            oldArticle.Source = article.Source;
            oldArticle.IsSlide = article.IsSlide;
            oldArticle.IsPublish = article.IsPublish;
            oldArticle.IsRed = article.IsRed;
            oldArticle.IsTop = article.IsTop;
            oldArticle.ViewCount = article.ViewCount;
            oldArticle.Author = article.Author;
            oldArticle.ImageUrl = article.ImageUrl;

            oldArticle.ModifyManagerId = 11;//
            oldArticle.ModifyTime = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));

            //對舊物件執行更新
            _appDbContext.Entry(oldArticle).State = EntityState.Modified;
            var result = await _appDbContext.SaveChangesAsync();
            return result == 1;
        }
    }

 

 

再談CheckBox的使用

1.在View檢視頁增加的程式碼格式如果為:

<input type="checkbox" name="IsTop" asp-for="IsTop" />置頂

或者是這樣:

<input asp-for="IsTop" />置頂

那麼在生成的html程式碼中,都會自動成id,name,type=「checkbox」 value的屬性。

2.在Controller中進行取值時的程式碼為:

if (Request.Form["IsTop"].Contains("true"))

article.IsTop = true;

else

article.IsTop = false;

跟蹤時發現,View中Checkbox選中是,會產生true和false兩個值,如圖跟蹤變數發現:

如此,取值時,就用了Contains功能,因為View中CheckBox沒選中,這隻有一個false值;

3.在View檢視頁增加程式碼的格式如果為:

<input type="checkbox" name="IsTop" @(Html.Raw(@Model.IsTop ? "checked=\"checked\"" : "")) asp-for="IsTop" />置頂

4.在Controller中進行取值時的程式碼為:

if (!string.IsNullOrEmpty(Request.Form["IsTop"]))

article.IsTop = true;

else

article.IsTop = false;

跟蹤時發現,View中的CheckBox選中是,取到的值為「on」,如圖跟蹤發現:

所以,才用了IsNullOrEmpty這個函數,依據判空來確定是否選中。

但是這樣寫有個問題,在ModelState.IsValid()的模型驗證中,一直無法通過,IsTop一直為false,為此,我乾脆就把其去除掉驗證:

ModelState.Remove("IsTop");//去除name=IsTop的checkbox的模型驗證

5.使用Checkbox,還是要看給在資料表中為其定義的欄位型別,Model中的指定型別和驗證屬性,如果你賦予了Value值,那麼就在Controller中取值,Asp.Net Core中Checkbox預設是True和False的值,網上關於@Html.CheckBox()形式也行,你可以嘗試跟蹤變數值來判斷如何處理,其宗旨就是根據具體條件來處理。