記錄和攔截數據庫命令
這一節介紹EF6怎么記錄和攔截發送給數據庫的查詢和操作命令。
1.記錄EF發送給數據庫命令(DbContext.Database.Log)
以前給了查看EF發送給數據庫的命令我們需要借助數據庫的追蹤工具或者第三方追蹤工具,現在EF6中提供了DbContext.Database.Log屬性(Action<string>類型),使用這個屬性我們可以很方便地記錄EF發送給數據庫的命令。
下邊是一個栗子:
static void Main(string[] args) { using (EFDbContext context=new EFDbContext()) { context.Database.Log = Console.WriteLine; var std1 = context.Students.Find(1); std1.Name = "newName"; context.SaveChanges(); Console.ReadKey(); } }
輸出如下:
在上邊的栗子中,Console.Write()方法屬於Action<string>類型,所以可以賦值給Log屬性。可以看到EF打開和關閉數據庫,執行查詢,和使用事務進行CUD都會被記錄下來。
我們也可以自定義一個Action<string>委托的實例賦值給Log屬性:
public class Logger { public static void Log(string message) { Console.WriteLine("EF Message: {0} ", message); } } class EF6Demo { public static void DBCommandLogging() { using (var context = new SchoolDBEntities()) { context.Database.Log = Logger.Log; var std1 = context.Students.Find(1); std1.Name = "newName"; context.SaveChanges(); Console.ReadKey(); } } }
2.攔截EF生成的數據庫命令(IDbCommandIntercepter)
EF6提供了攔截數據庫的接口IDbCommandIntercepter,這個接口提供了攔截EF發送給數據庫的命令的方法,我們也可以使用這個接口實現在context的操作執行前或執行后去做一些自定義的操作(類似mvc/api中的filter)。因為DbContext執行操作的底層實現是利用ADO.NET進行ExecuteNonQuery
, ExecuteScalar
, 和ExecuteReader,所以我們可以通過如
NonQueryExecuted、
NonQueryExecuting等方法進行攔截。
為了實現SQL命令攔截我們首先要定義一個實現IDbCommandIntercepter接口的類:
class EFCommandInterceptor : IDbCommandInterceptor { public void NonQueryExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext) { LogInfo("NonQueryExecuted", String.Format(" IsAsync: {0}, Command Text: {1}", interceptionContext.IsAsync, command.CommandText)); } public void NonQueryExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext) { LogInfo("NonQueryExecuting", String.Format(" IsAsync: {0}, Command Text: {1}", interceptionContext.IsAsync, command.CommandText)); } public void ReaderExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext) { LogInfo("ReaderExecuted", String.Format(" IsAsync: {0}, Command Text: {1}", interceptionContext.IsAsync, command.CommandText)); } public void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext) { LogInfo("ReaderExecuting", String.Format(" IsAsync: {0}, Command Text: {1}", interceptionContext.IsAsync, command.CommandText)); } public void ScalarExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { LogInfo("ScalarExecuted", String.Format(" IsAsync: {0}, Command Text: {1}", interceptionContext.IsAsync, command.CommandText)); } public void ScalarExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { LogInfo("ScalarExecuting", String.Format(" IsAsync: {0}, Command Text: {1}", interceptionContext.IsAsync, command.CommandText)); } private void LogInfo(string command, string commandText) { Console.WriteLine("Intercepted on: {0} :- {1} ", command, commandText); } }
可以看出,IDbCommandInterceptor
接口提供了6個方法,分別用於在ADO.NET執行ExecuteNonQuery(),ExcuteReader(),ExcuteScalar()方法的執行前/后攔截命令。這個栗子目的是:記錄context的操作是否是異步和發送到數據庫的命令。我們也可以使用這些方法來實現自定義邏輯(和filter簡直一模一樣有木有)。
接下來把攔截器添加到配置中去,兩種實現方式:
① 通過配置文件,在app.config或web.config中添加如下節點
<entityFramework> <interceptors> <interceptor type="EF6DBFirstTutorials.EFCommandInterceptor, EF6DBFirstTutorials"> </interceptor> </interceptors> </entityFramework>
②代碼配置
public class FE6CodeConfig : DbConfiguration { public FE6CodeConfig() { this.AddInterceptor(new EFCommandInterceptor()); } }
配置完成我們就可以記錄EF發送給數據庫的命令了,一個栗子:
var newStudent = new Student() { FirstName = "Bill" }; using (var context = new SchoolDBEntities()) { context.Students.Add(newStudent); context.SaveChanges(); }
栗子輸出為:
Intercepted on: ReaderExecuting :- IsAsync: False, Command Text: INSERT [dbo].[Student]([FirstName], [StandardId], [LastName]) VALUES (@0, NULL, NULL) SELECT [StudentID], [RowVersion] FROM [dbo].[Student] WHERE @@ROWCOUNT > 0 AND [StudentID] = scope_identity() Intercepted on: ReaderExecuted :- IsAsync: False, Command Text: INSERT [dbo].[Student]([FirstName], [StandardId], [LastName]) VALUES (@0, NULL, NULL) SELECT [StudentID], [RowVersion] FROM [dbo].[Student] WHERE @@ROWCOUNT > 0 AND [StudentID] = scope_identity()
EF系列目錄鏈接:Entity Franmework系列教程匯總