在2.3中完成依赖注入后,这次主要实现栏目的添加功能。按照前面思路栏目有三种类型,常规栏目即可以添加子栏目也可以选择是否添加内容,内容又可以分文章或其他类型,所以还要添加一个模块功能。这次主要实现栏目的添加,附带实现模块列表功能,并将业务逻辑层的功能都实现了异步方法。

先来个完成后的界面吧。

.Net Core MVC 网站开发(Ninesky) 2.4、添加栏目与异步方法插图

一、业务逻辑层异步方法

.net Core中异步方法很简单,只需要Task、async、await三个关键字就行。比如要实现统计记录数异步方法,只要给方法添加关键字async,然后返回Task类型,并在方法中使用await调用异步方法就可以。

public async Task<int>CountAsync(Expression<Func<T,bool>> predicate){return await _dbContext.Set<T>().CountAsync(predicate);}

一般在异步方法中也是调用异步方法,如果调用同步方法,如果直接调用同步方法也没问题,但是编译器会显示警告,看着警告烦可以Task.FromResult,像下面这样子。

publicvirtual async Task<IQueryable<T>>FindListAsync(){IQueryable<T> result = _dbContext.Set<T>();return await Task.FromResult(result);}

基本内容都差不多,下面直接贴代码。

1、异步接口

在Ninesky.InterfaceBase中打开InterfaceBaseService文件。添加接口异步的方法,结果如下:

using Ninesky.Models;

using System;

using System.Linq;

using System.Linq.Expressions;

using System.Threading.Tasks;namespace Ninesky.InterfaceBase

{

///

/// 服务基础接口

///

public interface InterfaceBaseService where T:class

{ ///

/// 添加

///

/// 实体

/// 是否立即保存

/// 添加的记录数[isSave=true时有效]

int Add(T entity, bool isSave = true); ///

/// 添加

///

/// 实体

/// 是否立即保存

/// 添加的记录数[isSave=true时有效]

Task AddAsync(T entity, bool isSave = true); ///

/// 添加[批量]

///

/// 实体

/// 是否立即保存

/// 添加的记录数

int AddRange(T[] entities, bool isSave = true);

///

/// 添加[批量]

///

/// 实体

/// 是否立即保存

/// 添加的记录数[isSave=true时有效]

Task AddRangeAsync(T[] entities, bool isSave = true); ///

/// 查询记录数

///

/// 查询条件

/// 记录数

int Count(Expression

/// 查询记录数

///

/// 查询条件

/// 记录数

Task CountAsync(Expression

/// 查询是否存在

///

/// 查询条件

/// 是否存在

bool Exists(Expression

/// 查询是否存在

///

/// 查询条件

/// 是否存在

Task ExistsAsync(Expression

/// 查找

///

/// 主键

///

T Find(int Id); ///

/// 查找

///

/// 主键

///

Task FindAsync(int Id); ///

/// 查找

///

/// 主键

///

T Find(object[] keyValues); ///

/// 查找

///

/// 主键

///

Task FindAsync(object[] keyValues); ///

/// 查找

///

/// 查询条件

///

T Find(Expression

/// 查找

///

/// 查询条件

///

Task FindAsync(Expression

/// 查询

///

/// 实体列表

IQueryable FindList(); ///

/// 查询

///

/// 实体列表

Task

/// 查询

///

/// 查询条件

/// 实体列表

IQueryable FindList(Expression

/// 查询

///

/// 查询条件

/// 实体列表

Task

/// 查询

///

/// 返回记录数

/// 查询条件

/// 实体列表

IQueryable FindList(int number, Expression

/// 查询

///

/// 返回记录数

/// 查询条件

/// 实体列表

Task

/// 查询

///

/// 显示数量[小于等于0-不启用]

/// 排序字段

/// 查询条件

/// 排序

/// 正序

///

IQueryable FindList(int number, Expression

/// 查询

///

/// 显示数量[小于等于0-不启用]

/// 排序字段

/// 查询条件

/// 排序

/// 正序

///

Task

/// 查询[分页]

///

/// 排序属性

/// 查询条件

/// 排序

/// 是否正序

/// 分页数据

///

Paging FindList(Expression

/// 查询[分页]

///

/// 排序属性

/// 查询条件

/// 排序

/// 是否正序

/// 分页数据

///

Task

/// 查询[分页]

///

/// 排序属性

/// 查询条件

/// 排序

/// 是否正序

/// 当前页

/// 每页记录数

///

Paging FindList(Expression

/// 查询[分页]

///

/// 排序属性

/// 查询条件

/// 排序

/// 是否正序

/// 当前页

/// 每页记录数

///

Task

/// 删除

///

/// 实体

/// 是否立即保存

/// 是否删除成功[isSave=true时有效]

bool Remove(T entity, bool isSave = true); ///

/// 删除

///

/// 实体

/// 是否立即保存

/// 是否删除成功[isSave=true时有效]

Task RemoveAsync(T entity, bool isSave = true); ///

/// 删除[批量]

///

/// 实体数组

/// 是否立即保存

/// 成功删除的记录数[isSave=true时有效]

int RemoveRange(T[] entities, bool isSave = true); ///

/// 删除[批量]

///

/// 实体数组

/// 是否立即保存

/// 成功删除的记录数[isSave=true时有效]

Task RemoveRangeAsync(T[] entities, bool isSave = true); ///

/// 保存数据

///

/// 更改的记录数

int SaveChanges(); ///

/// 保存数据

///

/// 更改的记录数

Task SaveChangesAsync(); ///

/// 更新

///

/// 实体

/// 是否立即保存

/// 是否保存成功[isSave=true时有效]

bool Update(T entity, bool isSave = true); ///

/// 更新

///

/// 实体

/// 是否立即保存

/// 是否保存成功[isSave=true时有效]

Task UpdateAsync(T entity, bool isSave = true); ///

/// 更新[批量]

///

/// 实体数组

/// 是否立即保存

/// 更新成功的记录数[isSave=true时有效]

int UpdateRange(T[] entities, bool isSave = true); ///

/// 更新[批量]

///

/// 实体数组

/// 是否立即保存

/// 更新成功的记录数[isSave=true时有效]

Task UpdateRangeAsync(T[] entities, bool isSave = true); }

}
2、异步接口的实现

在Ninesky.Base中打开BaseService 添加异步接口的实现,代码如下

using Microsoft.EntityFrameworkCore;

using Ninesky.InterfaceBase;

using Ninesky.Models;

using System;

using System.Linq;

using System.Linq.Expressions;

using System.Threading.Tasks;namespace Ninesky.Base

{

///

/// 服务基类

///

public class BaseService:InterfaceBaseService where T:class

{

protected DbContext _dbContext;

public BaseService(DbContext dbContext)

{

_dbContext = dbContext;

} ///

/// 添加

///

/// 实体

/// 是否立即保存

/// 添加的记录数[isSave=true时有效]

public virtual int Add(T entity, bool isSave = true)

{

_dbContext.Set().Add(entity);

if (isSave) return _dbContext.SaveChanges();

else return 0;

} ///

/// 添加

///

/// 实体

/// 是否立即保存

/// 添加的记录数[isSave=true时有效]

public virtual async Task AddAsync(T entity, bool isSave = true)

{

await _dbContext.Set().AddAsync(entity);

if (isSave) return await _dbContext.SaveChangesAsync();

else return 0;

} ///

/// 添加[批量]

///

/// 实体

/// 是否立即保存

/// 添加的记录数[isSave=true时有效]

public virtual int AddRange(T[] entities, bool isSave = true)

{

_dbContext.Set().AddRange(entities);

if (isSave) return _dbContext.SaveChanges();

else return 0;

} ///

/// 添加[批量]

///

/// 实体

/// 是否立即保存

/// 添加的记录数[isSave=true时有效]

public virtual async Task AddRangeAsync(T[] entities, bool isSave = true)

{

await _dbContext.Set().AddRangeAsync(entities);

if (isSave) return await _dbContext.SaveChangesAsync();

else return 0;

} ///

/// 查询记录数

///

/// 查询条件

/// 记录数

public virtual int Count(Expression

{

return _dbContext.Set().Count(predicate);

} ///

/// 查询记录数

///

/// 查询条件

/// 记录数

public virtual async Task CountAsync(Expression

{

return await _dbContext.Set().CountAsync(predicate);

} ///

/// 查询是否存在

///

/// 查询条件

/// 是否存在

public virtual bool Exists(Expression

{

return _dbContext.Set().Any(predicate);

} ///

/// 查询是否存在

///

/// 查询条件

/// 是否存在

public virtual async Task ExistsAsync(Expression

{

return await _dbContext.Set().AnyAsync(predicate);

} ///

/// 查找

///

/// 主键

///

public virtual T Find(int Id)

{

return _dbContext.Set().Find(Id);

} ///

/// 查找

///

/// 主键

///

public virtual async Task FindAsync(int Id)

{

return await _dbContext.Set().FindAsync(Id);

} ///

/// 查找

///

/// 主键

///

public virtual T Find(object[] keyValues)

{

return _dbContext.Set().Find(keyValues);

} ///

/// 查找

///

/// 主键

///

public virtual async Task FindAsync(object[] keyValues)

{

return await _dbContext.Set().FindAsync(keyValues);

} public virtual T Find(Expression

{

return _dbContext.Set().SingleOrDefault(predicate);

} ///

/// 查找

///

/// 查询条件

///

public virtual async Task FindAsync(Expression

{

return await _dbContext.Set().SingleOrDefaultAsync(predicate);

} ///

/// 查询

///

///

public virtual IQueryable FindList()

{

return _dbContext.Set();

} ///

/// 查询

///

/// 实体列表

public virtual async Task

{

IQueryable result = _dbContext.Set();

return await Task.FromResult(result);

} ///

/// 查询

///

/// 查询条件

/// 实体列表

public virtual IQueryable FindList(Expression

{

return _dbContext.Set().Where(predicate);

} ///

/// 查询

///

/// 查询条件

/// 实体列表

public virtual async Task

{

return await Task.FromResult(FindList(predicate));

} ///

/// 查询

///

/// 返回记录数

/// 查询条件

/// 实体列表

public virtual IQueryable FindList(int number, Expression

{

var entityList = _dbContext.Set().Where(predicate);

if (number > 0) return entityList.Take(number);

else return entityList;

} ///

/// 查询

///

/// 返回记录数

/// 查询条件

/// 实体列表

public virtual async Task

{

return await Task.FromResult(FindList(number, predicate));

} ///

/// 查询

///

/// 显示数量[小于等于0-不启用]

/// 排序字段

/// 查询条件

/// 排序

/// 正序

///

public virtual IQueryable FindList(int number, Expression

{

var entityList = _dbContext.Set().Where(predicate);

if (isAsc) entityList = entityList.OrderBy(keySelector);

else entityList.OrderByDescending(keySelector);

if (number > 0) return entityList.Take(number);

else return entityList;

} ///

/// 查询

///

/// 显示数量[小于等于0-不启用]

/// 排序字段

/// 查询条件

/// 排序

/// 正序

///

public virtual async Task

{

var entityList = _dbContext.Set().Where(predicate);

if (isAsc) entityList = entityList.OrderBy(keySelector);

else entityList.OrderByDescending(keySelector);

if (number > 0) entityList = entityList.Take(number);

return await Task.FromResult(entityList);

} ///

/// 查询[分页]

///

/// 排序属性

/// 查询条件

/// 排序

/// 是否正序

/// 分页数据

///

public virtual Paging FindList(Expression

{

var entityList = _dbContext.Set().Where(predicate);

paging.Total = entityList.Count();

if (isAsc) entityList = entityList.OrderBy(keySelector);

else entityList.OrderByDescending(keySelector);

paging.Entities = entityList.Skip((paging.PageIndex – 1) * paging.PageSize).Take(paging.PageSize).ToList();

return paging;

} ///

/// 查询[分页]

///

/// 排序属性

/// 查询条件

/// 排序

/// 是否正序

/// 分页数据

///

public virtual async Task

{

var entityList = _dbContext.Set().Where(predicate);

paging.Total = await entityList.CountAsync();

if (isAsc) entityList = entityList.OrderBy(keySelector);

else entityList.OrderByDescending(keySelector);

paging.Entities = await entityList.Skip((paging.PageIndex – 1) * paging.PageSize).Take(paging.PageSize).ToListAsync();

return paging;

} ///

/// 查询[分页]

///

/// 排序属性

/// 查询条件

/// 排序

/// 是否正序

/// 当前页

/// 每页记录数

///

public virtual Paging FindList(Expression

{

Paging paging = new Paging { PageIndex = pageIndex, PageSize = pageSize };

return FindList(predicate, keySelector, isAsc, paging);

} ///

/// 查询[分页]

///

/// 排序属性

/// 查询条件

/// 排序

/// 是否正序

/// 当前页

/// 每页记录数

///

public virtual async Task

{

Paging paging = new Paging { PageIndex = pageIndex, PageSize = pageSize };

return await FindListAsync(predicate, keySelector, isAsc, paging);

} ///

/// 删除

///

/// 实体

/// 是否立即保存

/// 是否删除成功[isSave=true时有效]

public virtual bool Remove(T entity, bool isSave = true)

{

_dbContext.Set().Remove(entity);

if (isSave) return _dbContext.SaveChanges() > 0;

else return false;

} ///

/// 删除

///

/// 实体

/// 是否立即保存

/// 是否删除成功[isSave=true时有效]

public virtual async Task RemoveAsync(T entity, bool isSave = true)

{

_dbContext.Set().Remove(entity);

if (isSave) return await _dbContext.SaveChangesAsync() > 0;

else return false;

} ///

/// 删除[批量]

///

/// 实体数组

/// 是否立即保存

/// 成功删除的记录数

public virtual int RemoveRange(T[] entities, bool isSave = true)

{

_dbContext.Set().RemoveRange(entities);

if (isSave) return _dbContext.SaveChanges();

else return 0;

} ///

/// 删除[批量]

///

/// 实体数组

/// 是否立即保存

/// 成功删除的记录数[isSave=true时有效]

public virtual async Task RemoveRangeAsync(T[] entities, bool isSave = true)

{

_dbContext.Set().RemoveRange(entities);

if (isSave) return await _dbContext.SaveChangesAsync();

else return 0;

} ///

/// 保存数据

///

/// 更改的记录数

public virtual int SaveChanges()

{

return _dbContext.SaveChanges();

} ///

/// 保存数据

///

/// 更改的记录数

public virtual async Task SaveChangesAsync()

{

return await _dbContext.SaveChangesAsync();

} ///

/// 更新

///

/// 实体

/// 是否立即保存

/// 是否保存成功

public virtual bool Update(T entity, bool isSave = true)

{

_dbContext.Set().Update(entity);

if (isSave) return _dbContext.SaveChanges() > 0;

else return false;

} ///

/// 更新

///

/// 实体

/// 是否立即保存

/// 是否保存成功[isSave=true时有效]

public async Task UpdateAsync(T entity, bool isSave = true)

{

_dbContext.Set().Update(entity);

if (isSave) return await _dbContext.SaveChangesAsync() > 0;

else return false;

} ///

/// 更新[批量]

///

/// 实体数组

/// 是否立即保存

/// 更新成功的记录数

public virtual int UpdateRange(T[] entities, bool isSave = true)

{

_dbContext.Set().UpdateRange(entities);

if (isSave) return _dbContext.SaveChanges();

else return 0;

} ///

/// 更新[批量]

///

/// 实体数组

/// 是否立即保存

/// 更新成功的记录数[isSave=true时有效]

public virtual async Task UpdateRangeAsync(T[] entities, bool isSave = true)

{

_dbContext.Set().UpdateRange(entities);

if (isSave) return await _dbContext.SaveChangesAsync();

else return 0;

}

}

}

二、模块功能

按照设想模块应该可以包含文章模块、咨询模块、产品模块、图片模块等,这里先实现文章模块。由于模块功能是系统预先写好的功能,所以模块不用添加、修改、删除等功能,只需要显示和启用(禁用)就好了。

1、添加模块模型

在Ninesky.Models中添加Module类

using System.Collections.Generic;

using System.ComponentModel.DataAnnotations;namespace Ninesky.Models

{

///

/// 模块模型

///

public class Module

{

[Key]

public int ModuleId { get; set; } ///

/// 模块名称

///

[Required(ErrorMessage = “{0}必填”)]

[StringLength(50)]

[Display(Name = “模块名称”)]

public string Name { get; set; } ///

/// 模块控制器

///

[StringLength(50)]

[Display(Name = “模块控制器”)]

public string Controller { get; set; } ///

/// 模块说明

///

[DataType(DataType.MultilineText)]

[StringLength(1000)]

[Display(Name = “模块说明”)]

public string Description { get; set; } ///

/// 是否启用

///

[Required(ErrorMessage = “{0}必填”)]

[Display(Name = “启用”)]

public bool Enabled { get; set; } ///

/// 排序

///

public virtual List ModuleOrders { get; set; }

}

}

类有个导航属性ModuleOrders类型是ModuleOrder的列表。这个表示模块支持的排序类型,在添加栏目的时候选择排序类型,在前台栏目显示内容列表的时候按功能进行排序。代码如下:

using System.ComponentModel.DataAnnotations;namespace Ninesky.Models

{

///

/// 模块排序类型

///

public class ModuleOrder

{

[Key]

public int ModuleOrderId { get; set; } ///

/// 模块ID

///

[Required]

[Display(Name = “模块ID”)]

public int ModuleId { get; set; } ///

/// 名称

///

[Required]

[StringLength(50)]

[Display(Name = “名称”)]

public string Name { get; set; } ///

/// 值

///

[Required]

[Display(Name = “值”)]

public int Order { get; set; } ///

/// 模块

///

public virtual Module Module { get; set; }

}

}
2、模块接口

在Ninesky.InterfaceBase添加模块的接口Ninesky.InterfaceBase,继承自InterfaceBaseService<Module>,并添加两个新的方法。查找模块列表和查找模块支持的排序列表

using Ninesky.Models;

using System.Linq;

using System.Threading.Tasks;namespace Ninesky.InterfaceBase

{

public interface InterfaceModuleService:InterfaceBaseService

{

///

/// 查找

///

/// 启用

///

Task

///

/// 查找排序列表

///

/// 模块ID

///

Task

}

}
3、在Ninesky.Base中添加类ModuleService来实现模块接口
using Microsoft.EntityFrameworkCore;

using Ninesky.InterfaceBase;

using Ninesky.Models;

using System.Linq;

using System.Threading.Tasks;namespace Ninesky.Base

{

public class ModuleService:BaseService,InterfaceModuleService

{

public ModuleService(DbContext dbContext) : base(dbContext)

{ } public override Module Find(int Id)

{

return _dbContext.Set().Include(m => m.ModuleOrders).SingleOrDefault(m => m.ModuleId == Id);

} ///

/// 查找

///

/// 启用

///

public async Task

{

if (enable == null) return await FindListAsync();

else return await FindListAsync(m => m.Enabled == enable);

}

///

/// 查找排序列表

///

/// 模块ID

///

public async Task

{

return await Task.FromResult(_dbContext.Set().Where(mo => mo.ModuleId == moduleId));

}

}

}
4、模块控制器

在Ninesky.Web.Areas.System.Controllers中添加模块控制器ModuleController,控制器有一个几个功能

Index,首页,返回页面视图,在视图冲通过ajax方式加载模块列表
List,模块列表,返回json类型数据,方便其他视图调用。
Details,详细视图,包含功能信息和排序列表,可以在次视图中启用或禁用模块
Enable,启用或禁用模块,此Action没有视图,通过post提交数据,返回json类型的操作状态数据
OrderList,模块排序列表,返回json类型数据。

Enable方法返回操作结果时,考虑到不光要返回是否操作成功,可能还要返回消息字符串,所以专门写个类型统一返回ajax操作的结果。把类型命名为JsonResponse,放到Ninesky.Web.Models下,代码如下图:

namespace Ninesky.Web.Models

{

///

/// 返回Json数据类型

///

public class JsonResponse

{

///

/// 操作是否成功

///

public bool succeed { get; set; } ///

/// 操作结果详细代码【必要时】

///

public int code { get; set; } ///

/// 操作结果消息

///

public string message { get; set; } ///

/// 操作产生的数据【必要时】

///

public dynamic Data { get; set; } public JsonResponse()

{

succeed = false;

message = “未知错误”;

}

}

}

整个控制器代码如下:

using Microsoft.AspNetCore.Mvc;

using Ninesky.InterfaceBase;

using Ninesky.Web.Models;

using System.Linq;

using System.Threading.Tasks;namespace Ninesky.Web.Areas.System.Controllers

{

[Area(“System”)]

public class ModuleController : Controller

{

private InterfaceModuleService _moduleService; public ModuleController(InterfaceModuleService moduleService)

{

_moduleService = moduleService;

} ///

/// 详细

///

/// 模块ID

///

public async Task Details(int id)

{

return View(await _moduleService.FindAsync(id));

} [HttpPost]

public async Task Enable (int id, bool enabled)

{

JsonResponse jsonResponse = new JsonResponse();

var module = await _moduleService.FindAsync(id);

if(module == null)

{

jsonResponse.succeed = false;

jsonResponse.message = “模块不存在”;

}

else

{

module.Enabled = enabled;

if(await _moduleService.UpdateAsync(module))

{

jsonResponse.succeed = true;

jsonResponse.message = “模块已” + (enabled ? “启用” : “禁用”);

}

else

{

jsonResponse.succeed = false;

jsonResponse.message = “保存数据失败”;

}

}

return Json(jsonResponse);

} public IActionResult Index()

{

return View();

}

///

/// 模块列表

///

///

public async Task List()

{

return Json((await _moduleService.FindListAsync()).ToList());

} ///

/// 排序列表

///

/// 模块Id

///

public async Task OrderList(int id)

{

return Json((await _moduleService.FindOrderListAsync(id)).ToList());

} }

}
5、视图

在控制器代码中可以看出只有两个action返回了视图,一个是Index,另一个是Details。

模块首页视图

在控制器中index没有像视图提供数据,在视图中通过ajax方式加载。在视图中使用bootstrapTable组件来显示视图列表。改组件可以在项目的依赖项->Bower【右键】->管理Bower程序包中搜索bootstrap-Table,并安装。

.Net Core MVC 网站开发(Ninesky) 2.4、添加栏目与异步方法插图1

视图代码:

@{

ViewData[“Title”] = “模块管理”;

}

首页
系统配置
模块管理@section aside{ @Html.Partial(“SystemAside”)

}

@section Scripts {

@{await Html.RenderPartialAsync(“_ValidationScriptsPartial”);}

$(document).ready(function () {

$(#moduletable).bootstrapTable({

url: @Url.Action(“List”),

columns: [{

field: moduleId,

title: 序号

}, {

field: name,

title: 名称,

formatter: function (value, row, index) {

return + value + ;

}

}, {

field: controller,

title: 控制器

}, {

field: description,

title: 说明

}, {

field: enabled,

title: 状态,

formatter:function(value,row,index)

{

return value ? : ;

}

}]

});

});

}

完成的效果图

.Net Core MVC 网站开发(Ninesky) 2.4、添加栏目与异步方法插图2

详细信息视图

Details中使用了tabs,一个标签显示基本信息,另一个标签显示排序列表。基本信息的启用属性使用checkbox,点击可以启用/禁用模块。排序列表继续使用bootstrapTable组件显示列表。

@model Ninesky.Models.Module

@{

ViewData[“Title”] = Model.Name;

}

首页
系统配置
模块管理@Model.Name

@section aside{ @Html.Partial(“SystemAside”)

}

基本信息
排序方式

@Html.DisplayFor(model => model.Name)

@Html.DisplayFor(model => model.Controller)

@Html.DisplayFor(model => model.Description)

@section Scripts {

@{await Html.RenderPartialAsync(“_ValidationScriptsPartial”);}

$(document).ready(function () {

$(#moduleordertable).bootstrapTable({

url: @Url.Action(“OrderList”,new{

id=Model.ModuleId}),

columns: [{

field: moduleOrderId,

title: 序号

}, {

field: name,

title: 名称,

}, {

field: order,

title: 值

}]

});

$(#Enabled).click(function () {

$.post(@Url.Action(“Enable”, “Module”), { id: $(#ModuleId).val(), enabled: $(#Enabled).prop(checked) }, function (response) {

if(response.succeed)

{

BootstrapDialog.alert({

title:消息,

message: response.message,

buttonLabel: 确定

}); }

}, json);

});

});

}

界面显示如下:

.Net Core MVC 网站开发(Ninesky) 2.4、添加栏目与异步方法插图3

三、栏目

1、栏目接口

通常会用树形菜单的形式显示栏目结构,需要添加栏目的树形菜单数据,需要添加一个方法,另外显示子栏目也需要添加一个方法。

在栏目接口中添加方法。

using Ninesky.Models;

using System.Linq;

using System.Threading.Tasks;namespace Ninesky.InterfaceBase

{

///

/// 栏目服务接口

///

public interface InterfaceCategoryService:InterfaceBaseService

{

///

/// 查找树形菜单

///

/// 栏目类型,可以为空

///

Task

/// 查找子栏目

///

/// 栏目ID

///

IQueryable FindChildren(int id); ///

/// 查找子栏目

///

/// 栏目ID

///

Task

}

}
2、接口的实现。

在基类中的方法只返回栏目实体并没有返回外键,所以实现类中需要重写查找栏目的方法将三种栏目类型的导航属性都加载进来。
查找子栏目的方法直接返回结果就行。
查找栏目树稍微麻烦点,在添加栏目的时候需要选择父栏目,父栏目的类型只能是常规栏目,所以方法要有栏目类型参数:在参数为null时返回所有栏目列表;栏目不为空时,先在数据库中查找所有此类型的栏目,然后根据ParentPath来取出所有的父栏目(重复的剔除)。计划在前台使用ztree组件显示树,该组件有个简单数据类型,根据pid自动生成树,这里就不用在组织成树形接口直接返回列表就可以了。

using System;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

using Microsoft.EntityFrameworkCore;

using Ninesky.Models;

using Ninesky.InterfaceBase;namespace Ninesky.Base

{

///

/// 栏目服务类

///

public class CategoryService:BaseService,InterfaceCategoryService

{

public CategoryService(DbContext dbContext):base(dbContext)

{

}

///

/// 查找

///

/// 栏目ID

///

public override Category Find(int Id)

{

return _dbContext.Set().Include(“General”).Include(“Page”).Include(“Link”).SingleOrDefault(c => c.CategoryId == Id);

} ///

/// 查找子栏目

///

/// 栏目ID

///

public IQueryable FindChildren(int id)

{

return FindList(0, c => c.ParentId == id, c => c.Order, true);

} ///

/// 查找子栏目

///

/// 栏目ID

///

public async Task

{

return await FindListAsync(0, c => c.ParentId == id, c => c.Order, true);

} ///

/// 查找树形菜单

///

/// 栏目类型,可以为空

///

public async Task

{

var categories = await FindListAsync();

//根据栏目类型分类处理

switch (categoryType)

{

case null:

break;

case CategoryType.General:

categories = categories.Where(c => c.Type == categoryType);

break;

//默认-Page或Link类型

default:

//Id数组-含本栏目及父栏目

List idArray = new List();

//查找栏目id及父栏目路径

var categoryArray = categories.Where(c => c.Type == categoryType).Select(c => new { CategoryId = c.CategoryId, ParentPath = c.ParentPath });

if(categoryArray != null)

{

//添加栏目ID到

idArray.AddRange(categoryArray.Select(c => c.CategoryId));

foreach (var parentPath in categoryArray.Select(c=>c.ParentPath))

{

var parentIdArray = parentPath.Split(new char[] { , }, StringSplitOptions.RemoveEmptyEntries);

if (parentIdArray != null)

{

int parseId = 0;

foreach(var parentId in parentIdArray)

{

if (int.TryParse(parentId, out parseId)) idArray.Add(parseId);

}

}

}

}

categories = categories.Where(c => idArray.Contains(c.CategoryId));

break;

}

return categories.OrderBy(c => c.ParentPath).ThenBy(C => C.Order);

}

}

}
3、栏目控制器。

栏目控制器有一下几个action

Index,首页,返回页面视图
Add,添加栏目,一个有两个action,一个用于显示,另一个接受post过来的数据进行处理。
Tree,全部栏目数据,返回json类型数据用于显示侧栏显示。
ParentTree,常规栏目数据,返回json类型的常规栏目及其父栏目数据,用于选择父栏目时控件。

在显示树形菜单时使用的ztree组件,是一个功能非常强大的国产组件,需要的朋友可以去看看http://www.treejs.cn/v3/api.php

控制器中只有Add方法麻烦一点,添加时需要对其父栏目和导航属性进行判断和处理。代码如下:

using Microsoft.AspNetCore.Mvc;

using Microsoft.AspNetCore.Mvc.Rendering;

using Ninesky.InterfaceBase;

using Ninesky.Models;

using Ninesky.Web.Models;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;namespace Ninesky.Web.Areas.System.Controllers

{

///

/// 栏目控制器

///

[Area(“System”)]

public class CategoryController : Controller

{

private InterfaceCategoryService _categoryService;

public CategoryController(InterfaceCategoryService categoryService)

{

_categoryService = categoryService;

} public async Task Add([FromServices]InterfaceModuleService moduleService, CategoryType? categoryType)

{ var modules = await moduleService.FindListAsync(true);

var modeleArry = modules.Select(m => new SelectListItem { Text = m.Name, Value = m.ModuleId.ToString() }).ToList();

modeleArry.Insert(0, new SelectListItem() { Text = “无”, Value = “0”, Selected = true });

ViewData[“Modules”] = modeleArry;

return View(new Category() { Type = CategoryType.General, ParentId = 0, View=”Index”, Order = 0, Target = LinkTarget._self, General = new CategoryGeneral() { ContentView = “Index” } });

} [HttpPost]

public async Task Add([FromServices]InterfaceModuleService moduleService,Category category)

{

if(ModelState.IsValid)

{

//检查父栏目

if (category.ParentId > 0)

{

var parentCategory = await _categoryService.FindAsync(category.ParentId);

if (parentCategory == null) ModelState.AddModelError(“ParentId”, “父栏目不存在”);

else if (parentCategory.Type != CategoryType.General) ModelState.AddModelError(“ParentId”, “父栏目不能添加子栏目”);

else category.ParentPath = parentCategory.ParentPath + “,” + parentCategory.CategoryId;

}

else category.ParentPath = “0”;

//检查栏目类型

switch (category.Type)

{

case CategoryType.General:

if (category.General == null) ModelState.AddModelError(“General.Type”, “请填写常规栏目内容”);

else

{

if (category.General.ModuleId > 0)

{

if (string.IsNullOrEmpty(category.General.ContentView)) ModelState.AddModelError(“General.ContentView”, “请填写栏目视图”);

if (category.General.ContentOrder == null) ModelState.AddModelError(“General.ContentOrder”, “请选择内容排序方式”);

}

else

{

if (category.Page != null) category.Page = null;

if (category.Link != null) category.Link = null;

}

}

break;

case CategoryType.Page:

//检查

if (category.Page == null) ModelState.AddModelError(“General.Type”, “请填写单页栏目内容”);

else

{

if (string.IsNullOrEmpty(category.Page.Content)) ModelState.AddModelError(“Page.Content”, “请输入单页栏目内容”);

else

{

if (category.General != null) category.General = null;

if (category.Link != null) category.Link = null;

}

}

break;

case CategoryType.Link:

//检查

if (category.Link == null) ModelState.AddModelError(“General.Type”, “请填写连接栏目内容”);

else

{

if (string.IsNullOrEmpty(category.Link.Url)) ModelState.AddModelError(“Link.Url”, “请选择输入链接地址”);

else

{

if (category.General != null) category.General = null;

if (category.General != null) category.General = null;

}

}

break;

} //保存到数据库

if(ModelState.IsValid)

{

if (await _categoryService.AddAsync(category) > 0) return View(“AddSucceed”, category);

else ModelState.AddModelError(“”, “保存数据失败”);

}

}

var modules = await moduleService.FindListAsync(true);

var modeleArry = modules.Select(m => new SelectListItem { Text = m.Name, Value = m.ModuleId.ToString() }).ToList();

modeleArry.Insert(0, new SelectListItem() { Text = “无”, Value = “0”, Selected = true });

ViewData[“Modules”] = modeleArry;

return View(category);

} ///

/// 栏目首页

///

///

public IActionResult Index()

{

return View(“Index”);

} ///

/// 父栏目树

///

///

public async Task ParentTree()

{

var categories = await _categoryService.FindTreeAsync(CategoryType.General);

return Json(categories.Select(c => new zTreeNode { id = c.CategoryId, name = c.Name, pId = c.ParentId, iconSkin=”fa fa-folder” }));

} ///

/// 栏目树

///

///

public async Task Tree()

{

List nodes;

var categories = await _categoryService.FindTreeAsync(null);

if (categories != null)

{

nodes = new List(categories.Count());

foreach(var category in categories)

{

var node = new zTreeNode() { id = category.CategoryId, pId= category.ParentId, name = category.Name, url = Url.Action(“Details”, “Category”, new { id = category.CategoryId }) };

switch(category.Type)

{

case CategoryType.General:

node.iconSkin = “fa fa-folder”;

node.iconOpen = “fa fa-folder-open”;

node.iconClose = “fa fa-folder”;

break;

case CategoryType.Page:

node.iconSkin = “fa fa-file”;

break;

case CategoryType.Link:

node.iconSkin = “fa fa-link”;

break;

}

nodes.Add(node);

}

}

else nodes = new List();

return Json(nodes);

}

}

}

4、视图

左侧导航栏视图

视图名Aside,视图中采用ztree加载栏目树

栏目列表</div>

添加栏目视图

html、js都混在一起了代码很乱,凑活看吧。

@model Ninesky.Models.Category

@{

ViewData[“Title”] = “添加栏目”;

}

首页
栏目管理
添加常规栏目
基本信息
常规栏目
单页栏目
链接栏目

发表回复

您的电子邮箱地址不会被公开。