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 ...
随机推荐
- 如何提高你的移动开发中AS3/AIR性能
Shawn Blais 让我了解到如何在移动平台上对AS3代码进行优化.他的博客(http://esdot.ca/site/category/blog)只有十来篇文章,但都是些很意思的信息,尤其适合我 ...
- Git恢复删除的分支
1.使用 git reflog 命令查看显示整个本地仓储的commit,包括所有branch的commit,甚至包括已经撤销的commit. 2.找到我们想要恢复的分支 ,可以看到我们当时commit ...
- Class.forName() 与 ClassLoader.loadClass()的区别
看到一个面试题,说说Class.forName() 与 ClassLoader.loadClass()的区别,特意记录一下,方便后续查阅. 在我们写java代码时,通常用这两种方式来动 ...
- Java集合目录
java集合学习(1):集合框架 java集合学习(2):Map和HashMap Java集合学习(4):HashTable Java集合学习(5):LinkedHashMap Java集合学习(6) ...
- spoon数据转换中文乱码(kettle)
(1) 查看mysql数据库是否为utf8(status) (2) 设置spoon (3) 文本打开spoon.bat,找到set OPT=%OPT% %PENTAHO_DI_JAVA_OPTIONS ...
- 景点API支持查询携程旅游门票景点详情
门票景点详情,景点api支持查询携程旅游门票景点详情. 接口名称:景点api 接口平台:开放api 接口地址:http://api2.juheapi.com/xiecheng/senicspot/ti ...
- TinyXML2 使用
TinyXML2 TinyXML2 TinyXML是一个开源的解析XML的解析库,能够用于C++,能够在Windows或Linux中编译,使用TinyXML进行C++ XML解析,使用简单,容易上手. ...
- Zabbix 添加内存告警
1.在Template OS Linux模板创建item Configuration-->Templates-->Template OS Linux-->items-->cre ...
- Alipay SDK验签PHP低于5.5版本错误
低于PHP5.5版本不支持OPENSSL_ALGO_SHA256函数,要想使用RSA2加密,把OPENSSL_ALGO_SHA256函数替换为:sha256WithRSAEncryption 解密方法 ...
- Linux 如何用命令查看binlog文件的创建时间
目录 背景 分析 方法 注意 背景 MySQL在26日 16:23:49产生了大量的慢查询,在这段时间内,binlog文件刷新的很快(查看慢日志是mysql DML并发比较多),想知道写完一个binl ...