需求
视图模型设计
从前端发来的请求,服务端需要通过类来封装。所有的请求都涉及到分页、集团相关、项目相关、连接字符串相关,提炼出一个基类。
BaseQuery.cs
public SieveModel PagingModel{get;set;}
public string GroupId{get;set;}
public string ProjectId{get;set;}
public string ConnKey{get;set;}
以上,SieveModel
来自一个第三方的有关分页的库,叫做Sieve
。
考虑到首页和内页不同之处,在基类基础上再设计: GeneralSumQuery.cs
//IRequest<T>来自一个中介者模式的库MediatR
//意思是说,只要给我GeneralSumQuery,我就返回GeneralSumViewModel给外界
//GeneralSumViewModel这个模型是给手机端,手机端拿着这个模型去填充页面
public class GeneralSumQuery : BaseQuery, IRequest<GeneralSumViewModel>
{
//首页的Project和内页的Location都需要用到
//当Level=0,就代表首页Project,当Level=1比如代表楼层,以此类推
public int Level{get;set;}
//这个属性是给Location用的,就是根据不同的LocationId去加载其下的子级Location
//子级Location有一个属性叫做ParentId,这个属性就指向父级的LocationId
//所有,根据父级的主键,也就是这里的LocationId能获得其下的所有子级Location
public string LocationId{get;set;}
}
GeneralSumViewModel
是返回给手机端用的,它代表如下红框中的部分:
对应代码:
public class GeneralSumViewModel
{
//一旦有集合属性,一定要在构造函数内初始化,否则容易报null的错
public GeneralSumViewModel()
{
Items = new List<GeneralSumItemViewModel>();
}
public List<GeneralSumItemViewmodel> Items{get;set;}
}
GeneralSumItemViewmodel
代表这列表项,即如下红框中的部分:
代码如下:
public class GeneralSumItemViewModel : BaseViewModel
{
/// <summary>
/// 名称:项目名称、楼层名称、区域名称、电箱名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 报警颜色:WarningColorEnum
/// </summary>
public short Color { get; set; }
/// <summary>
/// 是否可以进入到下一级
/// </summary>
public bool IsContinue { get; set; }
/// <summary>
/// 下一个位置层级
/// </summary>
public int NextLevel { get; set; }
/// <summary>
/// 本层Location主键,项目的LocationId=-1
/// </summary>
public string LocationId { get; set; }
/// <summary>
/// 报警
/// </summary>
public GeneralWarningItemViewModel WarningItem { get; set; }
/// <summary>
/// 负荷
/// </summary>
public GeneralFuHeItemViewModel FuHeItem { get; set; }
/// <summary>
/// 开关比例
/// </summary>
public GeneralKGBLItemViewModel KGBLItem { get; set; }
视图模型的基类:
public class BaseViewModel
{
public string ProjectId { get; set; }
public string ConnKey { get; set; }
}
有关报警的视图模型:
public class GeneralWarningItemViewModel : BaseViewModel
{
/// <summary>
/// 报警描述
/// </summary>
public string Description { get; set; }
/// <summary>
/// 报警颜色:WarningColorEnum
/// </summary>
public short Color { get; set; }
/// <summary>
/// 报警数
/// </summary>
public string WarningNum { get; set; }
}
有关负荷的视图模型:
public class GeneralFuHeItemViewModel : BaseViewModel
{
/// <summary>
/// 负荷值
/// </summary>
public string FuHeValue { get; set; }
}
有关开关比例的视图模型:
public class GeneralKGBLItemViewModel : BaseViewModel
{
public string OpenNum { get; set; }
public string CloseNum { get; set; }
}
以上,包括接口所接受的模型和接口响应给前端的视图模型。
输入和输出定下来了,现在需要通过中介者模式让处理请求,输出响应。如下:
处理请求和响应,中介者模式
MediatR是一个有关中介者模式的第三方库,它的作者就是大名鼎鼎的AutopMapper的作者,而AutoMapper是用来建立DTO和Entity Framework模型之间映射的第三方库。
HomeController.cs
[Route("api/home")]
public class HomeController : BaseController
{
private readonly IHostingEnvironment _env;
public HomeController(IHostingEnvironment env)
{
_env = env;
}
/// <summary>
/// 获取首页项目列表
/// </summary>
/// <returns></returns>
/// <response code="200">返回首页项目列表</response>
[Authorize]
[Route("projects")]
[HttpPost]
[ProducesResponseType(200)]
public async Task<IActionResult> GetProjects(BaseFlatQuery query)
{
var generalQuery = new GeneralSumQuery
{
Level = 0,
LocationId = string.Empty,
GroupId = User.FindFirst(GlobalSettings.Auth_GroupId).Value,
ConnKey = string.Empty,
ProjectId = string.Empty,
PagingModel = new SieveModel
{
Page=query.Page,
PageSize = query.PageSize
}
};
return Ok(await Mediator.Send(generalQuery));
}
/// <summary>
/// 获取楼层、区域等列表
/// </summary>
/// <returns></returns>
/// <response code="200">返回楼层、区域等列表</response>
[Authorize]
[Route("locations")]
[HttpPost]
[ProducesResponseType(200)]
public async Task<IActionResult> GetLocations(GeneralLocationQuery query)
{
var generalQuery = new GeneralSumQuery
{
Level = query.Level,
LocationId = query.LocationId,
GroupId=string.Empty,
ConnKey=query.ConnKey,
ProjectId = query.ProjectId,
PagingModel = new SieveModel
{
Page = query.Page,
PageSize = query.PageSize
}
};
return Ok(await Mediator.Send(generalQuery));
}
/// <summary>
/// 首页测试数据
/// </summary>
/// <returns></returns>
/// <response code="200">返回首页测试数据</response>
[Authorize]
[HttpPost]
[Route("test")]
[ProducesResponseType(200)]
public IActionResult Test()
{
//return Ok(new { Id = 1, Name="这里是测试数据"});
return Ok(User.FindFirst(GlobalSettings.Auth_GroupId).Value);
}
}
为什么需要BaseFlatQuery
这个类?为什么不能用BaseQuery
这个类?因为BaseQuery
有些属性不是前端输入的,比如GroupId, ConnKey,这些属性是根据用户的相关信息赋值的,前端的请求不需要这个。而且在BaseQuery
中有关分页的属性SieveModel中的Page和PageSize是可空的,在空的情况Sieve这个分页库会区找全局设置中的相关设置,即到appsettings.json中去找。总之,BaseFlatQuery
是用来方便前端输入的,尽量让前端输入最简,并且没有嵌套类。同时,使用BaseFlatQuery
这个类更安全,因为外界无从知晓服务端类的属性结构。
MediatR的工作原理:
public class MyQuery : IRequest<MyViewMedel>
{}
public class MyQueryHandler : IRequestHandler<MyQuery,MyViewModel>
{
public async Task<MyViewMedel> Handler(MyQuery request, CancellationToken cancellationToken)
{
}
}
MediatR的实现细节略去。