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 ...
随机推荐
- i春秋——“百度杯”CTF比赛 十月场——GetFlag(md5碰撞、文件包含、网站绝对路径)
需要提交的captcha满足等式,肯定就是MD5碰撞了 附上脚本 import hashlib def func(md5_val): for x in range(1,100000000): md5_ ...
- windows下搭建vue+webpack的开发环境
1. 安装git其右键git bash here定位比cmd的命令行要准确,接下来的命令都是利用git bash here.2. 安装node.js一般利用vue创建项目是要搭配webpack项目构建 ...
- android studio学习----添加项目依赖包总结
Gradle Library Projects Gradle 项目可以依赖于其它组件.这些组件可以是外部二进制包,或者是其它的 Gradle 项目. 在本例中, app/build.gradle 中有 ...
- QT POST/GET HTTP操作
工程文件 Qt += network 举例 Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui-> ...
- mysql--日志文件
1 选择常规查询日志和慢查询日志输出目标 1.1 log_output查看.定义 所谓的输出目标就是日志写入到哪里,mysql中用系统变量 log_output来指定输出目标,log_output的 ...
- (原)netbeans中使用libtorch
转载请注明处处: https://www.cnblogs.com/darkknightzh/p/11479330.html 说明:第一种方式在netbeans中无法debug代码,设置了断点也不会在断 ...
- win10锁屏壁纸文件夹位置
Win10默认系统下载的壁纸怎么下载?在哪里找出来呢?首先我们要把系统的锁屏壁纸要设置为Windows聚焦才会自动从微软的服务器上去下载壁纸.这些都是随机下载的.每个人的都Win10 都有可能不一样. ...
- 对象存储服务 OSS(Object Storage Service),知识点(待补充上仓库代码)
资料 网址 官方文档 https://help.aliyun.com/product/31815.html?spm=a2c4g.11186623.3.1.3e1459669xRokl OSS Brow ...
- Python进阶-I 初识函数(function)
函数 在java中叫方法. 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print() ...
- mybatis学习4
QL99中的连接查询 连接查询 交叉连接 select * from A,B; 内连接 隐式内连接 select * from A,B where 条件 显示内连接 select * fr ...