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 ...
随机推荐
- Risc-V简要概括
1.Risc-V硬件平台术语 一个RiscV硬件平台可以包含一个或多个RiscV兼容的核心.其它非RiscV兼容的核心.固定功能的加速器.各种物理存储器结构.I/O设备以及允许这些部件相互连通的互联结 ...
- 【学习笔记】PYTHON网络爬虫与信息提取(北理工 嵩天)
学习目的:掌握定向网络数据爬取和网页解析的基本能力the Website is the API- 1 python ide 文本ide:IDLE,Sublime Text集成ide:Pychar ...
- Mysql数据库之备份还原(mysqldump,LVM快照,select备份,xtrabackup)
备份类型: 热备份:读写不受影响 温备份:仅可执行读备份 冷备份:离线备份,读写均不能执行,关机备份 物理备份和逻辑备份 物理备份:复制数据文件,速度快. 逻辑备份:将数据导出之文本文件中,必要时候, ...
- Nginx 负载均衡实例redis
Nginx 负载均衡实例redis 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.
- properties文件属性值@Value注解为 java entity属性赋值
一.使用@Value为 java entity类中的非static属性赋值 举个栗子,一目了然 1.1 properties文件 1.2 servlet.xml 文件增加的配置: 1.2.1 serv ...
- PHP获取当前服务器版本,Ip等详细信息
1. 服务器IP地址 $_SERVER['SERVER_ADDR'] 服务器域名 $_SERVER['SERVER_NAME'] 服务器端口 $_SERVER['SERVER_PORT'] 服务器版本 ...
- 设置linux代理完成apt-get
最近ubuntu的服务器被公司关闭了外网使用权限,但是安装软件又需要连接外网,那么就只能通过代理来连接了. 先按照下面的这篇帖子来设置windows端的代理. https://blog.csdn.ne ...
- 监控进程cpu meminfo
https://github.com/cdrandin/cpsc_351 https://github.com/cdrandin?after=Y3Vyc29yOnYyOpK5MjAxNC0wNy0xM ...
- Codeforces Round #609 (Div. 2) A到C题
签到,乘以两个相邻的合数 #include<bits/stdc++.h> using namespace std; int main(int argc, char const *argv[ ...
- USACO Party Invitations
洛谷 P3068 [USACO13JAN]派对邀请函Party Invitations 洛谷传送门 JDOJ 2343: USACO 2013 Jan Silver 3.Party Invitatio ...