一步步學習EF Core(2.事務與日志)


前言

上節我們留了一個問題,為什么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)

 

好了,就說這么多.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM