ASp.net Core EF ActionFilterAttribute AOP
在项目中经常遇到一些数据的修改,很多时候业务方需要一个修改日志记录,这里我们计划用mssql数据库来存放日志记录,用EF来操作,记录日志可以用mvc的ActionFilterAttribute 来完成也可以用AOP来完成。以前在asp.net的AOP用的是IMessageSink这里我们计划用Castle.DynamicProxy来完成。
准备工作:
创建数据库表:
CREATE TABLE [dbo].[logs](
[Id] [int] IDENTITY(,) NOT NULL,
[Title] [nvarchar]() NULL,
[Content] [nvarchar](max) NULL,
[CreateTime] [datetime] NULL,
CONSTRAINT [PK_logs] PRIMARY KEY CLUSTERED
(
[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]
这里的Title是根据业务划分的,Content是修改后的内容,实际生产应该还要加上修改人。这里都简化了(个人并不推荐用EF来迁移数据)
创建 Asp.netCore项目
这里我们以asp.netcore2.2创建一个WebAppLog视图模型程序

在appsettings.json添加数据库连接串:
 "ConnectionStrings": {
    "SqlServerConnection": "Server=192.168.100.5;initial catalog=test;UID=sa;PWD=xxxx"
  }
在Models文件夹下新建Log.cs
namespace WebAppLog.Models
{
public class Log
{
public int Id { set; get; }
public string Title { set; get; }
public string Content { set; get; }
public DateTime CreateTime { set; get; }
}
}
创建LogContext.cs文件:
namespace WebAppLog
{
using Microsoft.EntityFrameworkCore;
using WebAppLog.Models; public class LogContext : DbContext
{
public LogContext(DbContextOptions<LogContext> options) : base(options)
{
}
public virtual DbSet<Log> Log { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Log>().ToTable("logs");
}
}
}
修改HomeController.cs文件:
namespace WebAppLog.Controllers
{
using Microsoft.AspNetCore.Mvc;
using System.Linq;
public class HomeController : Controller
{
private LogContext context; public HomeController(LogContext context)
{
this.context = context; }
public IActionResult Index()
{
var data = context.Log.ToList();
return View(data);
} }
}
修改Home的Index.cshtml视图:
@{
    var list = Model as List<Log>;
}
<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <table border="">
        @foreach (var item in list)
        {
            <tr>
                <td>Title</td>
                <td>@item.Title</td>
            </tr>
            <tr>
                <td>Content</td>
                <td class="htmlcontent">@item.Content</td>
            </tr>
            <tr>
                <td>CreateTime</td>
                <td>@item.CreateTime.ToString("yyyy-MM-dd HH:mm:ss")</td>
            </tr>
        }
    </table>
</div>
在Startup.cs的ConfigureServices方法最后添加如下:
string connection = Configuration["ConnectionStrings:SqlServerConnection"];
services.AddDbContext<LogContext>(options => options.UseSqlServer(connection));
这时候我们的程序就可以运行了。
ActionFilterAttribute
这里我们首先用ActionFilterAttribute来实现日志记录,在ActionFilterAttribute里面需要用到LogContext,我这里用 filterContext.HttpContext.RequestServices.GetService(typeof(LogContext))来获取的。
新建LogAttribute.cs文件:在OnActionExecuting方法我们获取参数,在OnResultExecuted获取返回值并记录到数据库
namespace WebAppLog
{
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using System;
using WebAppLog.Models;
public class LogAttribute : ActionFilterAttribute
{
public string Title { get; set; } public LogAttribute(string title)
{
Title = title;
}
private string _arguments = null; public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_arguments = JsonConvert.SerializeObject(filterContext.ActionArguments);
base.OnActionExecuting(filterContext);
} public override void OnResultExecuted(ResultExecutedContext filterContext)
{
var context = filterContext.HttpContext.RequestServices.GetService(typeof(LogContext)) as LogContext;
string result = JsonConvert.SerializeObject(filterContext.Result);
var log = new Log
{
Title = Title,
Content = $"{{\"arguments\":{_arguments},\"result\":{result}}}",
CreateTime = DateTime.Now
};
context.Log.Add(log);
context.SaveChanges(); base.OnResultExecuted(filterContext);
}
}
}
在HomeController.cs中增加一个Action
 [Log("test")]
  public ActionResult Update(int id, string content)
   {
      return Ok();
   }
运行程序用postman发送一个请求:

由于我们的日志是json格式,所以需要修改home的Index.cshtml让他以表格来显示
在table结束标签后追加一下js代码(目的就是让Content更加好看)
<script type="text/javascript" src="~/js/jquery.min.js"></script>
<script type="text/javascript">
function GetHtml(txt) {
try {
var obj = $.parseJSON(txt);
var html = "<table border='1'>"
for (var i in obj) {
var temp = '';
var obj2 = obj[i];
if (typeof (obj2) == "object" && Object.prototype.toString.call(obj2).toLowerCase() == "[object object]" && !obj2.length) {
temp = GetHtml(JSON.stringify(obj2));
}
else {
temp = obj2;
} html += "<tr><td>" + i + "</td><td>" + temp + "</td></tr>";
}
html += "</table>";
return html;
} catch (e) {
return txt;
}
}
$(".htmlcontent").each(function () {
var text = $(this).text();
console.log(text);
text = GetHtml(text);
$(this)[].innerHTML = text;
});
</script>
运行程序:

AOP
首先我们需要安装相应的nuget包
Autofac.Extensions.DependencyInjection
Autofac.Extras.DynamicProxy
首先我们创建一个LogInterceptor.cs文件来实现AOP,但是不是所有的方法都要记录日志,所以我们创建了一个UsageAttribute来标记是否记录日志:
namespace WebAppLog
{
using Castle.DynamicProxy;
using Microsoft.EntityFrameworkCore;
using System;
using System.Reflection;
using WebAppLog.Models;
public class LogInterceptor : IInterceptor
{
LogContext context;
public LogInterceptor(string connstr)
{
var optionsBuilder = new DbContextOptionsBuilder<LogContext>();
optionsBuilder.UseSqlServer(connstr);
context = new LogContext(optionsBuilder.Options);
}
public void Intercept(IInvocation invocation)
{
//真正调用方法
invocation.Proceed();
var methodAttribute = (UsageAttribute)invocation.Method.GetCustomAttribute(typeof(UsageAttribute));
if (methodAttribute != null)
{
var args = invocation.Arguments;
var pars = invocation.Method.GetParameters();
string json = "";
for (int i = ; i < args.Length; i++)
{
string tmp = $"\"{pars[i].Name}\":\"{args[i].ToString()}\"";
json += tmp + ",";
}
string argument = "{" + json.TrimEnd(',') + "}";
string result = invocation.ReturnValue.ToString();
string title = methodAttribute.Description;
var log = new Log
{
Title = title,
Content = $"{{\"arguments\":{argument},\"result\":\"{result}\"}}",
CreateTime = DateTime.Now
};
context.Log.Add(log);
context.SaveChanges();
}
}
} [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class UsageAttribute : Attribute
{
public string Description { set; get; } public UsageAttribute(string description)
{
Description = description;
}
}
}
要实现AOP 我们需要创建一个LogService.cs(还有对应的接口,这里必须要有接口不然aop搞不定)
namespace WebAppLog
{
using Autofac.Extras.DynamicProxy;
public interface ILogService
{
[Usage("update")]
bool Update(int id, string content);
} [Intercept(typeof(LogInterceptor))]
public class LogService : ILogService
{
public bool Update(int id, string content)
{
return true;
}
}
}
修改HomeController.cs并增加相应的Action
private LogContext context;
public ILogService LogService { get; set; } public HomeController(LogContext context, ILogService logService)
{
this.context = context;
LogService = logService;
}
public ActionResult Modify()
{
LogService.Update(, "test");
return Ok();
}
现在修改Startup.cs文件,用Autofac的DI替换asp.netCore 默认的DI。把原先默认的ConfigureServices放注释,新增ConfigureServices方法如下:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
string connection = Configuration["ConnectionStrings:SqlServerConnection"];
services.AddDbContext<LogContext>(options => options.UseSqlServer(connection));
///上面的是原先ConfigureServices的类容,下面是增加的代码
var containerBuilder = new ContainerBuilder();
containerBuilder.Register(c => new LogInterceptor(connection));
containerBuilder.RegisterType<LogService>().As<ILogService>().PropertiesAutowired().EnableInterfaceInterceptors();
containerBuilder.Populate(services); var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
然后运行程序,访问http://localhost:5000/home/modify
最后回到主页如下:

参考:
Aspect Oriented Programming (AOP) in .NET Core and C# using AutoFac and DynamicProxy
ASP.NET Core 使用 AutoFac 注入 DbContext
ASp.net Core EF ActionFilterAttribute AOP的更多相关文章
- C#无限极分类树-创建-排序-读取 用Asp.Net Core+EF实现之方法二:加入缓存机制
		
在上一篇文章中我用递归方法实现了管理菜单,在上一节我也提到要考虑用缓存,也算是学习一下.Net Core的缓存机制. 关于.Net Core的缓存,官方有三种实现: 1.In Memory Cachi ...
 - asp.net core+ef core
		
asp.net core+ef core 官方的文档https://docs.asp.net/en/latest/tutorials/first-mvc-app/start-mvc.html 先来看一 ...
 - Asp.net Core + EF Core + Bootstrap搭建的MVC后台通用管理系统模板(跨平台版本)
		
Asp.net Core + EF Core + Bootstrap搭建的MVC后台通用管理系统模板(跨平台版本) 原创 2016年07月22日 10:33:51 23125 6月随着.NET COR ...
 - C#无限极分类树-创建-排序-读取 用Asp.Net Core+EF实现
		
今天做一个管理后台菜单,想着要用无限极分类,记得园子里还是什么地方见过这种写法,可今天找了半天也没找到,没办法静下心来自己写了: 首先创建节点类(我给它取名:AdminUserTree): /// & ...
 - 在ASP.NET Core中使用AOP来简化缓存操作
		
前言 关于缓存的使用,相信大家都是熟悉的不能再熟悉了,简单来说就是下面一句话. 优先从缓存中取数据,缓存中取不到再去数据库中取,取到了在扔进缓存中去. 然后我们就会看到项目中有类似这样的代码了. pu ...
 - ASP.NET Core&EF 笔记
		
首先创建Asp.net Core项目,然后通过 NuGet 安装 EntityFrameworkCore: Microsoft.EntityFrameworkCore.SqlServer Micros ...
 - (17)ASP.NET Core EF基于数据模型创建数据库
		
1.简介 使用Entity Framework Core构建执行基本数据访问的ASP.NET Core MVC应用程序.使用迁移(Migrations)基于数据模型创建数据库,你可以在Windows上 ...
 - 基于Asp.net core + EF + Sqlite 5分钟快速上手一个小项目
		
虽然该方法不会用在实际开发中,但该过程对于初学者还是非常友好的,真应了麻雀虽小,五脏俱全这句话了.好了不多废话了,直接开始!! 1.建立一个名为test的Asp.net core web应用程序 这一 ...
 - asp.net Core  EF core ( Entity Framework 7 ) 数据库更新维护
		
CreateDatabaseIfNotExists等之前的API已经废弃,现在采用的是微软封装好,简化.高效的API,migrations 因为,旧API,要付出高昂的代价,以及局限性 打开VS20 ...
 
随机推荐
- oracle高级部分
			
回顾 多表关联查询的方式 内连接 根据AB表关联的条件进行过滤查询,只保留满足条件数据 Select * from a,b where a.xxx=b.xxx; Select * from a inn ...
 - iOS7的十个更“佳”:简洁直观更受青睐
			
转自:http://www.25pp.com/news/news_27792.html iOS7自发布以来一直是褒贬不一,虽然苹果还只是发布了第二个测试版,但普通用户早已经在纠结到底该不该升级iOS7 ...
 - php+Apache2+Nginx+Mysql
			
Nginx 1.安装Nginx sudo apt-get clean sudo apt-get update sudo apt-get install ...
 - 项目中 直接跳后台 不用输入/newadmin
 - Kubernetes概念之mater、node
			
很久没写博客了,终于把重心找回来了,不过没有以前有斗志.有理想.有目标了.慢慢来.你若问我我最近几年的规划是什么,还真不知道.突然发现摧毁一个人真的很简单.k8s也是一遍一遍的从入门到放弃,还是要好好 ...
 - Streaming Systems笔记
			
一直心心念的<Streaming Systems>终于有了影印版本,京东110块钱果断买了,很惊喜还是彩印版本. 挖个坑,书看完后写一篇关于流式处理总结的笔记,大体翻看了一遍,总体来说流式 ...
 - 批量部署SSH基于key的验证脚本
			
工作中,使用ansible等自动化运维工具实现服务器批量自动化运维管理,需要先解决管理端和被管理端的免密码登录,可以脚本实现ssh基于key的验证,代码如下: #!/bin/bash PASS=123 ...
 - 编写体面的UI测试
			
--01-- PageObject简介 PageObject是编写UI测试时的一种模式.简而言之,你可以将所有知道页面细节的部分放入到这个对象上,对于编写测试的人来说,一个PageObject代表 ...
 - 201871010102-《面向对象程序设计(java)》第6-7周学习总结
			
博文正文开头:(2分) 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/ ...
 - Docker Hello-World镜像运行测试
			
一.命令:docker run hello-world 命令解释:以docker客户端命令的方式运行hello-world镜像 命令运行结果: hadoop@Docker:/opt/docker$ d ...