首页 > 编程语言 >篇(17)-Asp.Net Core入门实战-文章管理之文章类别管理(Linq子查询)

篇(17)-Asp.Net Core入门实战-文章管理之文章类别管理(Linq子查询)

时间:2022-11-21 13:33:17浏览次数:46  
标签:Core Asp return int articleCategory id 文章 Id public

篇(17)-Asp.Net Core入门实战-文章管理之文章类别的管理

如果要做一个CMS系统,那么文章管理算是入门,文章管理附带一个类别管理,用来对文章进行类别区分。所以,本章简单讲一些类别管理,这也是一个数据操作。

(1).文章类别Sql表的建立

CREATE TABLE [dbo].[ArticleCategory](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Title] [varchar](128) NOT NULL,
[ParentId] [int] NOT NULL,
[ClassList] [varchar](128) NULL,
[ClassLayer] [int] NULL,
[Sort] [int] NOT NULL,
[ImageUrl] [varchar](128) NULL,
[SeoTitle] [varchar](128) NULL,
[SeoKeywords] [varchar](256) NULL,
[SeoDescription] [varchar](512) NULL,
[IsDeleted] [bit] NOT NULL,
CONSTRAINT [PK_ARTICLECATEGORY] 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]
GO

 

(2).文章类别的视图

(2.1)Create视图

@{ ViewData["Title"] = "新建文章分类"; }
@model ArticleCategory
<form action="/ArticleCategory/Create" method="post">
@Html.AntiForgeryToken()
<div>
<label asp-for="ParentId">父类别</label>
<div>
<label name="Parent_DisplayName" id="Parent_DisplayName">@ViewData["DisplayName"]</label>
<input type="hidden" asp-for="ParentId" id="ParentId" name="ParentId" value="@ViewData["ParentId"]" />
</div>
</div>
<div>
<label asp-for="Title">类别名称</label>
<div>
<input type="text" asp-for="Title" name="Title" placeholder="请输入类别名">
</div>
</div>
<div>
<label asp-for="Sort">排序</label>
<div>
<input type="text" asp-for="Sort" name="Sort" placeholder="排序">
</div>
</div>
<div>
<div>
<button type="submit">确定</button>
<button type="reset">重置</button>
</div>
</div>
</form>

 

(2.2)Edit视图

@{ ViewData["Title"] = "编辑菜单"; }
@model ArticleCategory
<form action="/ArticleCategory/Edit" method="post">
@Html.AntiForgeryToken()
<div>
<label asp-for="ParentId">父菜单</label>
<div>
<input type="text" asp-for="ParentId" name="ParentId" />
<input type="hidden" asp-for="Id" />
</div>
</div>
<div>
<label asp-for="Title">类别名成</label>
<div>
<input type="text" asp-for="Title" name="Title" placeholder="类别名成">
</div>
</div>
<div>
<label asp-for="Sort">排序</label>
<div>
<input type="text" asp-for="Sort" name="Sort" placeholder="排序">
</div>
</div>
<div>
<div>
<button type="submit">确定</button>
<button type="reset">重置</button>
</div>
</div>
</form>

 

(2.3)Index列表视图

单纯的列表在现实父类名称时,显示的还是Id值,看第一步的表结构可以发现,这个字段是ParentId,我们得显示成名称才行。

@using Humanizer;
@using RjWebCms.Db;
@using RjWebCms.Models.Articles;
@using RjWebCms.Common;
@model PaginatedList<ArticleCategoryView>
@{
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: "/ArticleCategory/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="ArticleCategory" 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>
</tr>
@foreach (var item in Model)
{
if (item.ParentId == 0)
{
<tr>
<td><input type="checkbox" class="done-checkbox" name="chk_ids" value="@item.Id"></td>
<td>@item.Title</td>
<td>\</td>
<td>@item.Sort</td>
<td>
<a asp-action="Create" asp-route-id="@item.Id">Add</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>
}
else
{
<tr>
<td><input type="checkbox" class="done-checkbox" name="chk_ids" value="@item.Id"></td>
<td>@item.Title</td>
@*<td>@Html.Action("GetParentName",new { id=item.ParentId})</td>*@
<td>@item.ParentName</td>
<td>@item.Sort</td>
<td>
<a asp-action="Create" asp-route-id="@item.Id">Add</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>

 

所以,需要编写ViewModel是实现,这在前面章节中详细讲过。

(3).Model对象的编码实现。

(3.1)表的直接映射对象ArticleCategory.cs

/// <summary>

/// 文章类别

/// </summary>

public class ArticleCategory

{

/// <summary>

/// 主键

/// </summary>

[Key]

public Int32 Id { get; set; }

/// <summary>

/// 分类标题

/// </summary>

[Required]

public String Title { get; set; }

/// <summary>

/// 父分类ID

/// </summary>

[Required]

public Int32 ParentId { get; set; }

/// <summary>

/// 类别ID列表(逗号分隔开)

/// </summary>

public String ClassList { get; set; }

/// <summary>

/// 类别深度

/// </summary>

public Int32? ClassLayer { get; set; }

/// <summary>

/// 排序

/// </summary>

[Required]

public Int32 Sort { get; set; }

/// <summary>

/// 分类图标

/// </summary>

public String ImageUrl { get; set; }

/// <summary>

/// 分类SEO标题

/// </summary>

public String SeoTitle { get; set; }

/// <summary>

/// 分类SEO关键字

/// </summary>

public String SeoKeywords { get; set; }

/// <summary>

/// 分类SEO描述

/// </summary>

public String SeoDescription { get; set; }

/// <summary>

/// 是否删除

/// </summary>

[Required]

public Boolean IsDeleted { get; set; }

}

(3.2). ArticleCategoryView.cs 也就是文章类别列表页的ViewModel代码
public class ArticleCategoryView
{
public int Id { get; set; }
public string Title { get; set; }
public int ParentId { get; set; }
public string ParentName { get; set; }
public int Sort { get; set; }
}

(4).Controller的编码实现
public class ArticleCategoryController : Controller
{
private readonly IArticleCategoryService _articleCategoryService;
private readonly AppDbContext _appDbContext;
public ArticleCategoryController(IArticleCategoryService articleCategoryService, AppDbContext appDbContext)
{
_appDbContext = appDbContext;
_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 == "sort" ? "sort_desc" : "sort";
if (searchString != null)
{
pageNumber = 1;
}
else
{
searchString = currentFilter;
}

ViewData["CurrentFilter"] = searchString;
var articleCategories = from s in _appDbContext.ArticleCategory.OrderBy(s=>s.Id)
join p in _appDbContext.ArticleCategory on s.ParentId equals p.Id into T1
from m in T1.DefaultIfEmpty()
select new ArticleCategoryView
{
Id = s.Id,
Title = s.Title,
ParentId = s.ParentId,
ParentName = m.Title,
Sort = s.Sort
};
if (!string.IsNullOrEmpty(searchString))
{
articleCategories = articleCategories.Where(s => s.Title.Contains(searchString));
}
switch (sortOrder)
{
case "name_desc":
articleCategories = articleCategories.OrderByDescending(s => s.Title);
break;
case "sort":
articleCategories = articleCategories.OrderBy(s => s.Sort);
break;
case "sort_desc":
articleCategories = articleCategories.OrderByDescending(s => s.Sort);
break;
default:
articleCategories = articleCategories.OrderBy(s => s.Title);
break;
}

int pageSize = 4;
return View(await PaginatedList<ArticleCategoryView>.CreateAsync(articleCategories.AsNoTracking(), pageNumber ?? 1, pageSize));
}

[HttpGet]
public async Task<IActionResult> Create(int id)
{
if (id != 0)
{
var articleCategory = await _articleCategoryService.FindArticleCategoryAsync(id);
if (articleCategory != null)
{
ViewData["DisplayName"] = articleCategory.Title;
ViewData["ParentId"] = articleCategory.Id; //增加子目录时,以id作为子目录的parentid
}
}
else
{
ViewData["DisplayName"] = "顶级根目录";
ViewData["ParentId"] = 0;
}
return View();
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(int id, ArticleCategory articleCategory)
{
//去掉对字段IsSystem的验证,IsSystem在数据库是bool类型,而前端是0和1,ModelState的验证总是报false,所以去掉对其验证
//ModelState.Remove("IsSystem");//在View端已经解决了了bool类型,那么此行代码可以不用
if (id != 0)
articleCategory.ParentId = id;
if (ModelState.IsValid)
{
var successful = await _articleCategoryService.AddArticleCategoryAysnc(articleCategory);
if (successful)
return RedirectToAction("Index");
else
return BadRequest("失败");
}
return View(articleCategory);
}
[HttpGet] public async Task<IActionResult> Edit(int id) { if (string.IsNullOrEmpty(id.ToString())) return NotFound(); var articleCategory = await _articleCategoryService.FindArticleCategoryAsync(id); if (articleCategory == null) return NotFound(); return View(articleCategory); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Edit(int id, ArticleCategory articleCategory) { if (id != articleCategory.Id) { return NotFound(); } if (ModelState.IsValid) { try { var result = await _articleCategoryService.UpdateArticleCategoryAsync(id, articleCategory); } catch (Exception ex) { return BadRequest("编辑失败"); } return RedirectToAction("Index"); } return View(articleCategory); } [HttpGet] public async Task<IActionResult> Delete(int id) { var result = await _articleCategoryService.DeleteArticleCategoryAsync(id); if (result) return RedirectToAction("Index"); else return BadRequest("删除失败"); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> DeleteAll(string[] ids) { int countSuccessDel = 0;//记录删除成功的数据条数 int countFailedDel = 0;//记录删除成功的数据条数 foreach (string id in ids) { if (await _articleCategoryService.DeleteArticleCategoryAsync(int.Parse(id))) countSuccessDel++; else countFailedDel++; } if (countSuccessDel > 0) return RedirectToAction("Index"); else return BadRequest("删除失败"); } public async Task<IActionResult> Details(int id) { if (string.IsNullOrEmpty(id.ToString())) return NotFound(); var articleCategory = await _articleCategoryService.FindArticleCategoryAsync(id); if (articleCategory == null) return NotFound(); return View(articleCategory); } [HttpGet] public async Task<IActionResult> GetCategory() { var items = await _articleCategoryService.GetArticleCategory(); return View(items); } public IActionResult GetParentName(int id) { var category = _appDbContext.ArticleCategory.Where(x => x.Id == id).FirstOrDefault(); if (category != null) return Content(category.Title); else return Content(""); } }

 

注意,在Index的Action中的我用了个简单的Linq关联查询

因为,父Id与Id都是存在一张表中,关联子查询在使用时要用到:from m in T1.DefaultIfEmpty() 这一句,可以将空也计算在内,否则,会排除掉最前面2行(下图),我的本意sql语句是:

Select a.Id,a.Title,a.parentId,b.Title as ParentName,a.sort from ArticleCategory a

Left join ArticleCategory b on a.ParentId = b.Id ,这样可以将父Id的名称作为一个新列放在查询表中。

要注意在关键地方的改变,其他内容与将菜单功能时类似。

(5).服务层代码实现

(5.1)接口代码

public interface IArticleCategoryService
{
Task<ArticleCategory[]> GetArticleCategory();
Task<ArticleCategory[]> GetArticleCategoryByParentId(int pId);
Task<ArticleCategory> FindArticleCategoryAsync(int Id);
Task<bool> AddArticleCategoryAysnc(ArticleCategory menu);
Task<bool> UpdateArticleCategoryAsync(int id, ArticleCategory menu);
Task<bool> DeleteArticleCategoryAsync(int Id);
}

 

(5.2)实现代码

public class ArticleCategoryService : IArticleCategoryService
{
private readonly AppDbContext _appDbContext;
public ArticleCategoryService(AppDbContext appDbContext)
{
_appDbContext = appDbContext;
}
public async Task<bool> AddArticleCategoryAysnc(ArticleCategory articleCategory)
{
articleCategory.IsDeleted = false;
if (articleCategory.ParentId == 0)
articleCategory.ClassLayer = 1;
else
articleCategory.ClassLayer = 2;
await _appDbContext.ArticleCategory.AddAsync(articleCategory);
var result = await _appDbContext.SaveChangesAsync();
return result == 1;
}

public async Task<bool> DeleteArticleCategoryAsync(int Id)
{
var articleCategory = await _appDbContext.ArticleCategory.FirstOrDefaultAsync(x => x.Id == Id);
if (articleCategory != null)
{
_appDbContext.ArticleCategory.Remove(articleCategory);
}
var result = await _appDbContext.SaveChangesAsync();
return result == 1; //注意(result==1 如果等式成立,则返回true,说明删除成功)
}

public async Task<ArticleCategory> FindArticleCategoryAsync(int Id)
{
var item = await _appDbContext.ArticleCategory.Where(x => x.Id == Id).FirstOrDefaultAsync();
return item;
}

public async Task<ArticleCategory[]> GetArticleCategory()
{
var items = await _appDbContext.ArticleCategory.Where(x => x.IsDeleted==false).OrderByDescending(x => x.Sort).ToArrayAsync();
return items;
}

public async Task<ArticleCategory[]> GetArticleCategoryByParentId(int pId)
{
var items = await _appDbContext.ArticleCategory.Where(x => x.ParentId == pId).OrderByDescending(x => x.Sort).ToArrayAsync();
return items;
}

public async Task<bool> UpdateArticleCategoryAsync(int id, ArticleCategory articleCategory)
{
var oldArticleCategory = await FindArticleCategoryAsync(id); //找出旧对象
//将新值赋到旧对象上
oldArticleCategory.Title = articleCategory.Title;
oldArticleCategory.ParentId = articleCategory.ParentId;
oldArticleCategory.Sort = articleCategory.Sort;
//对旧对象执行更新
_appDbContext.Entry(oldArticleCategory).State = EntityState.Modified;
var result = await _appDbContext.SaveChangesAsync();
return result == 1;
}
}

 

至此,文章管理功能编写完成。通篇只有2个主要知识点:linq子查询和ViewModel显示

标签:Core,Asp,return,int,articleCategory,id,文章,Id,public
From: https://www.cnblogs.com/mushaobai/p/16911138.html

相关文章

  • ABP.Core+EF 左连接查询
    EF的Join查询,一般默认是内连接,但是有些情况会需要左连接,则需要如下方式://左连接varquery=fromrinthis.Repository.GetAll()        ......
  • ASP.NET Core教程-Routing(路由)
    更新记录转载请注明出处:2022年11月21日发布。2022年11月20日从笔记迁移到博客。RouteBasic(路由基础)路由说明请求URL映射到控制器方法的过程,这个映射过程由路由......
  • 常用 ASP.NET Core 十二个例子
    原文地址:http://piotrgankiewicz.com/2017/04/17/asp-net-core-12-samples/作者:PiotrGankiewicz翻译:杨晓东(Savorboard)前言在今天的博客中,我将介绍十几个可以在A......
  • 如何一秒钟从头构建一个 ASP.NET Core 中间件
    前言其实地上本没有路,走的人多了,也便成了路。--鲁迅就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件......
  • ASP.NET Core 中间件之压缩、缓存
    前言今天给大家介绍一下在ASP.NETCore日常开发中用的比较多的两个中间件,它们都是出自于微软的ASP.NET团队,他们分别是Microsoft.AspNetCore.ResponseCompression和......
  • ASP.NET Core 之 Identity 入门(二)
    前言在上篇文章中讲了关于Identity需要了解的单词以及相对应的几个知识点,并且知道了Identity处在整个登入流程中的位置,本篇主要是在.NET整个认证系统中比较重要的......
  • ASP.NET Core 之 Identity 入门(三)
    前言在上一篇文章中,我们学习了CookieAuthentication中间件,本篇的话主要看一下Identity本身。最早2005年ASP.NET2.0的时候开始,Web应用程序在处理身份验证和授权......
  • 我眼中的ASP.NET Core之微服务(一)
    前言前几天在博客园看到有园友在分享关于微软的一个微服务架构的示例程序,想必大家都已经知道了,那就是eShopOnContainers。我们先不看项目的后缀名称OnXXX,因为除了OnC......
  • 我眼中的ASP.NET Core之微服务(二)
    前言接上一篇。上一篇未完待续的原因是当时刚好是6-30号晚上马上12点了还没写完,然后我想赶在7月1号之前发出去,所以当时就发了。然后在发的时候出了一点问题,结果发出去的......
  • .NET Core工作流
    命令#.NETCorev2#Build,test,package,orpublishadotnetapplication,orrunacustomdotnetcommand.-task:DotNetCoreCLI@2inputs:command:'bu......