在上一篇中,咱们提到了如何建立一个UnitOfWork并经过ActionFilter设置启用。这一篇咱们将简单介绍一下ActionFilter以及如何利用ActionFilter,顺便补齐一下上一篇的工具类。app
ActionFilter全称是ActionFilterAttribute,咱们根据微软的命名规范能够看出这是一个特性类,看一下它的声明:框架
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public abstract class ActionFilterAttribute : Attribute, IActionFilter, IFilterMetadata, IAsyncActionFilter, IAsyncResultFilter, IOrderedFilter, IResultFilter
这是一个容许标注在类和方法上的特性类,容许多个标记,标注以后子类会继承父类的特性。而后,这个类是一个抽象类,因此咱们能够经过继承ActionFilterAttribute来编写本身的ActionFilter。asp.net
对于一个ActionFilter而言,最重要的是它的四个方法:ide
public virtual void OnActionExecuted(ActionExecutedContext context);
public virtual void OnActionExecuting(ActionExecutingContext context);
public virtual void OnResultExecuted(ResultExecutedContext context);
public virtual void OnResultExecuting(ResultExecutingContext context);
上图是这四个方法在一次请求中执行的顺序。在一次请求真正执行以前,想要拦截这个请求,应该使用OnActionExecuting
。工具
为何单独说这个呢?由于这个方法的出镜率很高,大多数时候都会使用这个方法进行请求过滤。优化
咱们来简单介绍一下,四个方法中的四种上下文类型,看一看里面有哪些咱们能够利用的方法:ui
这是一个Action执行前的上下文,表示Action并未开始执行,可是已经获取到了控制器实例:spa
public class ActionExecutingContext : FilterContext
{
public virtual IDictionary<string, object> ActionArguments { get; }
public virtual object Controller { get; }
public virtual IActionResult Result { get; set; }
}
ActionExecutingContext继承自FilterContext,咱们暂且不关注它的父类,只看一下它本身的属性。.net
ActionArguments 表示Action的参数列表,这里面放着各类从用户接到请求参数以及其余中间处理程序添加的参数code
Controller 表示执行该请求的控制器,在以前咱们提过,asp.net core 对于控制器的限制很小,因此控制器什么类型均可能,故而这里使用object做为控制器类型
Result 执行结果,正常状况下,在此处获取这个属性的值没有意义。可是咱们能够经过修改这个属性的值,来让咱们拦截请求
ActionExecutedContext 表示Action执行完成后的上下文,这时候Action已经执行完成,咱们能够经过这个获取Action执行结果:
public class ActionExecutedContext : FilterContext
{
public virtual bool Canceled { get; set; }
public virtual object Controller { get; }
public virtual Exception Exception { get; set; }
public virtual ExceptionDispatchInfo ExceptionDispatchInfo { get; set; }
public virtual bool ExceptionHandled { get; set; }
public virtual IActionResult Result { get; set; }
}
一样,继承自FilterContext,暂且忽略。
Canceled 表示是否被设置短路
Controller 处理请求的控制器
Exception 执行过程当中是否发生异常,若是有异常则 有值,不然为Null
ExceptionHandled 异常是否被处理
Result 此处对Result进行修改不会屏蔽执行的ActionResult,可是能够向用户隐藏对应的实现
这是在Result渲染以前执行的上下文,这时候Action已经执行完毕,正准备渲染Result:
public class ResultExecutingContext : FilterContext
{
public virtual bool Cancel { get; set; }
public virtual object Controller { get; }
public virtual IActionResult Result { get; set; }
}
Cancel 取消当前结果执行以及后续筛选器的执行
Controller 控制器
Result 处理结果
Result已经执行完成了,获取执行结果上下文:
public class ResultExecutedContext : FilterContext
{
public virtual bool Canceled { get; set; }
public virtual object Controller { get; }
public virtual Exception Exception { get; set; }
public virtual ExceptionDispatchInfo ExceptionDispatchInfo { get; set; }
public virtual bool ExceptionHandled { get; set; }
public virtual IActionResult Result { get; }
}
这个类与 ActionExecutedContext相似,就不作介绍了。
在上面的四个上下文都继承自 FilterContext,那么咱们来看一下FilterContext中有哪些属性或者方法:
public abstract class FilterContext : ActionContext
{
public virtual IList<IFilterMetadata> Filters { get; }
public TMetadata FindEffectivePolicy<TMetadata>() where TMetadata : IFilterMetadata;
}
能够看到FilterContext继承了另外一个ActionContext的类。小伙伴们应该对这个类要有必定的概念,这个类是Action的上下文类。它完整存在于一个Action的生命周期,因此有时候能够经过ActionContext进行Action级的数据传递(不推荐)。
那么,继续让咱们回过头来看看ActionContext里有什么:
public class ActionContext
{
public ActionDescriptor ActionDescriptor { get; set; }
public HttpContext HttpContext { get; set; }
public ModelStateDictionary ModelState { get; }
public RouteData RouteData { get; set; }
}
ActionDescriptor 执行的Action描述信息,包括Action的显示名称、一些参数等,具体用到的时候,再为大伙详细说
HttpContext 能够经过这个属性获取这次请求的Request和Response对象
ModelState 模型校验信息, 这部分在后续再为小伙伴们细说
RouteData 路由信息,asp.net core 在处理请求时解析出来的路由信息,包括在程序中修改的路由信息
在《【asp.net core 系列】9 实战之 UnitOfWork以及自定义代码生成》也就是上一篇中,介绍到了ActionFilter与普通特性类一致,能够经过标注控制器而后启用该ActionFilter。
由于大多数状况下,一个ActionFilter并不会仅仅局限于一个控制器,而是应用于多个控制器。因此这时候,咱们一般会设置一个基础控制器,在这个控制器上进行标注,而后让子类继承这个控制器。经过这种方式来实现一次声明屡次使用。
固然,在asp.net core 中添加了另外的一种使用ActionFilter的方式,Setup.cs中
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
}
默认是这样的,咱们能够经过设置参数来添加一个全局应用的Filter,例如说咱们上一篇中建立的 UnitOfWorkFilterAttribute:
services.AddControllersWithViews(options=>
{
options.Filters.Add<UnitOfWorkFilterAttribute>();
});
经过这种方式能够启用一个全局ActionFilter。若是须要使用asp.net core的默认依赖注入可使用 AddService进行配置。(依赖注入的内容在后续会讲解)。
继续上一篇遗留的内容:
public static void CreateEntityTypeConfig(Type type)
{
var targetNamespace = type.Namespace.Replace("Data.Models", "");
if (targetNamespace.StartsWith("."))
{
targetNamespace = targetNamespace.Remove(0);
}
var targetDir = Path.Combine(new[] { CurrentDirect, "Domain.Implements", "EntityConfigures" }.Concat(
targetNamespace.Split('.')).ToArray());
if (!Directory.Exists(targetDir))
{
Directory.CreateDirectory(targetDir);
}
var baseName = type.Name.Replace("Entity", "");
if (!string.IsNullOrEmpty(targetNamespace))
{
targetNamespace = $".{targetNamespace}";
}
var file = $"using {type.Namespace};" +
$"\r\nusing Microsoft.EntityFrameworkCore;" +
$"\r\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;" +
$"\r\nnamespace Domain.Implements.EntityConfigures{targetNamespace}" +
"\r\n{" +
$"\r\n\tpublic class {baseName}Config : IEntityTypeConfiguration<{type.Name}>" +
"\r\n\t{" +
"\r\n\t\tpublic void Configure(EntityTypeBuilder<SysUser> builder)" +
"\r\n\t\t{" +
$"\r\n\t\t\tbuilder.ToTable(\"{baseName}\");" +
$"\r\n\t\t\tbuilder.HasKey(p => p.Id);" +
"\r\n\t\t}\r\n\t}\r\n}";
File.WriteAllText(Path.Combine(targetDir, $"{baseName}Config.cs"), file);
}
工具类其实本质上就是一次文件写入的方法,自己没什么难度。
不过,这里还有有个小问题,每次调用都会覆盖原有的文件,还有就是这里面有不少能够优化的地方,小伙伴们能够本身试试去优化一下,让代码更好看一点。
到目前为止,实战系列也有了几篇,不少小伙伴问我能提供一下源码吗?固然,能呀。不过不是如今,容我留个谜底。当主要框架功能完成以后,我就会给小伙伴们发代码的。
其实也是由于如今还没个完整的,开放给小伙伴们也没啥意义。固然了,跟着一块敲,也是能实现的哈。关键地方的代码都有。