一步步學習EF Core

SQL 技術 科技優家 2017-05-22

前言

上節我們留了一個問題,為什麼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 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 (likeDatabase.Logfrom 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.

感興趣的朋友可以去了解一下,我之前的博文也有介紹:

好了,就說這麼多.

相關推薦

推薦中...