一步步学习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())
_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();