上節我們留了一個問題,為什么EF Core中,我們加載班級,數據並不會出來
其實答案很簡單,~ 因為在EF Core1.1.2 中我們在EF6.0+中用到的的延遲加載功能並沒有被加入,不過在EF Core 2.0中,這個功能將回歸
而且這個功能是否需要被加入進去,社區也在激烈的討論當中,有興趣的可以去看看:
https://github.com/aspnet/EntityFramework/issues/3797
那么我們該如何加載關聯的班級呢?.
直接通過Linq join當然是可以的. 我們也可以通過貪婪加載來獲取,修改查詢代碼如下:
public IActionResult ListView() { return View(_context.UserTable.Include(a=>a.Class).ToList()); }
效果如下:
下面我們開始今天的內容
關於EF Core的事務,其實與EF 6.x幾乎一樣,代碼如下:
using (var tran = _context.Database.BeginTransaction()) { try { _context.ClassTable.Add(new ClassTable { ClassName = "AAAAA", ClassLevel = 2 }); _context.ClassTable.Add(new ClassTable { ClassName = "BBBBB", ClassLevel = 2 }); _context.SaveChanges(); throw new Exception("模擬異常"); tran.Commit(); } catch (Exception) { tran.Rollback(); // TODO: Handle failure } }
在異常中Rollback即可回滾,我這里的寫法,其實有點無恥.
不過目的是告訴大家,要在Commit之前回滾.
不然會得到一個異常:This SqlTransaction has completed; it is no longer usable.”
下面我們來講一下關於EF Core中的日志
我們知道,在ASP.NET Core中,大量的使用了IOC的手法來注入我們所需要的類.
EF Core其實也一樣,.
首先我們需要創建一個EF日志類,繼承Microsoft.Extensions.Logging.ILogger
如下:
private class EFLogger : ILogger { private readonly string categoryName; public EFLogger(string categoryName) => this.categoryName = categoryName; public bool IsEnabled(LogLevel logLevel) { return true; } public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { Debug.WriteLine($"時間:{DateTime.Now.ToString("o")} 日志級別: {logLevel} {eventId.Id} 產生的類{this.categoryName}"); DbCommandLogData data = state as DbCommandLogData; Debug.WriteLine($"SQL語句:{data.CommandText},\n 執行消耗時間:{data.ElapsedMilliseconds}"); } public IDisposable BeginScope<TState>(TState state) { return null; } }
我這里面的Debug.WriteLine是為了方便調試.
正常情況下當然是寫入日志文件,可以用Log4Net
然后,我們創建一個空的日志類(用來過濾不需要記錄的日志)如下:
private class NullLogger : ILogger { public bool IsEnabled(LogLevel logLevel) { return false; } public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { } public IDisposable BeginScope<TState>(TState state) { return null; } }
然后,我們創建一個日志提供類(注入用,EF Core1.0版本注意注釋),如下:
public class MyFilteredLoggerProvider : ILoggerProvider { public ILogger CreateLogger(string categoryName) { // NOTE: 這里要注意,這是 EF Core 1.1的使用方式,如果你用的 EF Core 1.0, 就需把IRelationalCommandBuilderFactory替換成下面的類 // Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory if (categoryName == typeof(IRelationalCommandBuilderFactory).FullName) { return new EFLogger(categoryName); } return new NullLogger(); } public void Dispose() { } }
然后我們到Startup.cs的Configure()方法中注入我們的日志提供類
代碼如下:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddProvider(new MyFilteredLoggerProvider()); ....省略 }
運行程序,得到如下調試信息:
至此,我們就完成了日志的記錄工作.
那么問題來了,在Asp.NET core中,我們可以這樣注入進行日志記錄.
如果在別的項目(比如控制台)中,怎么辦?
下面就來解決這個問題.
在非Asp.NET core的程序中,我們需要把日志提供器從上下文里注入如下:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); LoggerFactory loggerFactory = new LoggerFactory(); loggerFactory.AddProvider(new MyFilteredLoggerProvider()); //注入 optionsBuilder.UseLoggerFactory(loggerFactory); }
寫在最后,其實在EF Core的路線圖中,我們可以看到,在2.0的版本將會提供一個更簡單的日志記錄方式
這段話是在(Features originally considered but for which we have made no progress and are essentially postponed)之后的:
..上面翻譯過來的大概意思就是:我們原來考慮會加入的功能,但是現在並沒有進展,基本要推遲的特點.(..總結三個字,然並卵)
- Simple Logging API (#1199) - We want a simple way to log the SQL being executed (like
Database.Log
from EF6.x). We also want a simple way to view everything being logged. - 嗯..翻譯過來的意思就是..我們想提供一個更簡單的日志記錄,比如像EF6.x中的
Database.Log 這樣...()
還有一個比較有趣的東西如下:
在High priority features(高度優先的功能)中還有一段話:
- Simple command interception provides an easy way to read/write commands before/after they are sent to the database.
- 簡單的命令攔截,將提供在發送到數據庫之前/之后讀取/寫入命令的簡單方法
我覺得這個有點類似於EF6.x的IDbCommandInterceptor.
感興趣的朋友可以去了解一下,我之前的博文也有介紹:
EntityFramework的多種記錄日志方式,記錄錯誤並分析執行時間過長原因(系列4)
好了,就說這么多.