Entity Framework Core 生成跟踪列
本文翻译自《Entity Framework Core: Generate tracking columns》,由于水平有限,故无法保证翻译完全正确,欢迎指出错误。谢谢!
注意:我使用的是 Entity Framework Core 2.0 (2.0.0-preview2-final)。正式版发布时,功能可能存在变动。
当您设计数据库时,有时需要添加列以跟踪记录何时更改,以及谁进行了更改。例如,您添加以下列:
- CreatedAt
- CreatedBy
- LastUpdatedAt
- LastUpdatedBy
您可以 轻松 地使用默认值和触发器来处理CreatedAt和LastUpdatedAt列。老实说,创建触发器是件无聊的事情,你也不想自己做。此外,很难设置用户名,因为它是应用程序的信息;实际上,在Web程序的上下文中,它们只是连接到数据库的一个用户,因此,您无法使用数据库中CURRENT_USER函数设置跟踪列的值。
当然,您不想访问这个属性。相反,您希望 Entity Framework 为您自动执行,所以,解决的办法是调用SaveChanges或SaveChangesAsync方法之前自动设置这些属性的值。在模型中有多种方法处理这些属性:您可以在模型中添加读写(R/W)属性;您也可以使用只读(R/O)属性;还有一种方案,您可以使用阴影(Shadow)属性。在最后一种方案中,是将列不映射到模型中的属性,只有 Entity Framework 才知道这些属性。是的,EF Core 非常灵活:)
我们用 Entity Framework 实现这3种方案!
读写属性
在这种情况下,实体将具有读/写属性。这意味着,您可以在代码中更改属性的值。
public interface ITrackable
{
    DateTime CreatedAt { get; set; }
    string CreatedBy { get; set; }
    DateTime LastUpdatedAt { get; set; }
    string LastUpdatedBy { get; set; }
}
public class Post : ITrackable
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public DateTime CreatedAt { get; set; }
    public string CreatedBy { get; set; }
    public DateTime LastUpdatedAt { get; set; }
    public string LastUpdatedBy { get; set; }
}
现在,我们将更改SaveChanges方法的默认行为来设置跟踪属性的值。要小心,有两个方法要重写:SaveChanges和异步方法SaveChangesAsync。在代码中遍历上下文中跟踪的实体,并根据状态设置跟踪属性的值。代码非常简单:
public class BloggingContext : DbContext
{
    public DbSet<Post> Posts { get; set; }
    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    {
        OnBeforeSaving();
        return base.SaveChanges(acceptAllChangesOnSuccess);
    }
    public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
    {
        OnBeforeSaving();
        return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }
    private void OnBeforeSaving()
    {
        var entries = ChangeTracker.Entries();
        foreach (var entry in entries)
        {
            if (entry.Entity is ITrackable trackable)
            {
                var now = DateTime.UtcNow;
                var user = GetCurrentUser();
                switch (entry.State)
                {
                    case EntityState.Modified:
                        trackable.LastUpdatedAt = now;
                        trackable.LastUpdatedBy = user;
                        break;
                    case EntityState.Added:
                        trackable.CreatedAt = now;
                        trackable.CreatedAt = user;
                        trackable.LastUpdatedAt = now;
                        trackable.LastUpdatedBy = user;
                        break;
                }
            }
        }
    }
    private string GetCurrentUser()
    {
        return "UserName"; // TODO implement your own logic
    }
}
这个方案有个不容忽视的问题,属性是可写的,您不希望在代码中设置属性的值。让我们看看如何使用只读属性。
只读属性
Entity Framework Core 允许将列映射到字段。这样,您可以使用只读属性,我们来修改 Post类:
public class Post
{
    private DateTime _createdAt;
    private string _createdBy;
    private DateTime _lastUpdatedAt;
    private string _lastUpdatedBy;
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public DateTime CreatedAt => _createdAt;
    public string CreatedBy => _createdBy;
    public DateTime LastUpdatedAt => _lastUpdatedAt;
    public string LastUpdatedBy => _lastUpdatedBy;
}
上面的代码有点麻烦,但属性现在是只读的。您需要指引 Entity Framework 使用这些字段来设置列的值。
public class BloggingContext : DbContext
{
    public DbSet<Post> Posts { get; set; }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Post>()
            .Property(post => post.CreatedAt)
            .HasField("_createdAt");
        modelBuilder.Entity<Post>()
            .Property(post => post.CreatedBy)
            .HasField("_createdBy");
        modelBuilder.Entity<Post>()
            .Property(post => post.LastUpdatedAt)
            .HasField("_lastUpdatedAt");
        modelBuilder.Entity<Post>()
            .Property(post => post.LastUpdatedBy)
            .HasField("_lastUpdatedBy");
    }
}
您不能像以前那样实现OnBeforeSaving方法,因为属性没有setter。不过,您可以使用 ChangeTracker来指引 Entity Framework 为属性设置值。这种方式无需访问属性或字段。
    private void OnBeforeSaving()
    {
        var entries = ChangeTracker.Entries();
        foreach (var entry in entries)
        {
            if (entry.Entity is Post post)
            {
                var now = DateTime.UtcNow;
                var user = GetCurrentUser();
                switch (entry.State)
                {
                    case EntityState.Modified:
                        entry.CurrentValues["LastUpdatedAt"] = now;
                        entry.CurrentValues["LastUpdatedBy"] = user;
                        break;
                    case EntityState.Added:
                        entry.CurrentValues["CreatedAt"] = now;
                        entry.CurrentValues["CreatedBy"] = user;
                        entry.CurrentValues["LastUpdatedAt"] = now;
                        entry.CurrentValues["LastUpdatedBy"] = user;
                        break;
                }
            }
        }
    }
}
我认为应是使用的方式,因为这种方式很清晰。
不定义属性
在某些情况下,您可能需要设置列的值而不在模型中定义属性。例如,您可能会公开最后更新的日期,但不会显示谁做了更改。Entity Framework Core 可以通过使用阴影(Shadow )属性来处理这个。阴影属性在OnModelCreating方法中声明,但在类中不存在。您可以使用ChangeTracker读取和写入阴影属性的值。
public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
}
public class BloggingContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        // Declare properties
        modelBuilder.Entity<Post>().Property<DateTime>("CreatedAt");
        modelBuilder.Entity<Post>().Property<string>("CreatedBy");
        modelBuilder.Entity<Post>().Property<DateTime>("LastUpdatedAt");
        modelBuilder.Entity<Post>().Property<string>("LastUpdatedBy");
    }
    private void OnBeforeSaving()
    {
        var entries = ChangeTracker.Entries();
        foreach (var entry in entries)
        {
            if (entry.Entity is Post post)
            {
                var now = DateTime.UtcNow;
                var user = GetCurrentUser();
                switch (entry.State)
                {
                    case EntityState.Modified:
                        entry.CurrentValues["LastUpdatedAt"] = now;
                        entry.CurrentValues["LastUpdatedBy"] = user;
                        break;
                    case EntityState.Added:
                        entry.CurrentValues["CreatedAt"] = now;
                        entry.CurrentValues["CreatedBy"] = user;
                        entry.CurrentValues["LastUpdatedAt"] = now;
                        entry.CurrentValues["LastUpdatedBy"] = user;
                        break;
                }
            }
        }
    }
}
总结
使用 Entity Framework Core,您可以根据需求将数据库表映射到对象模型。这允许创建干净的类,不需要公开您不想要的属性。Very nice
Entity Framework Core 生成跟踪列的更多相关文章
- Entity Framework Core 生成跟踪列-阴影属性
		摘自:https://www.cnblogs.com/tdfblog/p/entity-framework-core-generate-tracking-columns.html Ef Core 官方 ... 
- Entity Framework Core生成的存储过程在MySQL中需要进行处理及PMC中的常用命令
		在使用Entity Framework Core生成MySQL数据库脚本,对于生成的存储过程,在执行的过程中出现错误,需要在存储过程前面添加 delimiter // 附:可以使用Visual Stu ... 
- entity framework core  生成 postgresql 数据库实体
		.net core 2.0 使用db first 方式生成 表 和context PM 控制台运行命令出错 Scaffold-DbContext "Host=localhost;Databa ... 
- 被Entity Framework Core的细节改进震撼了一下
		今天用 SQL Server Profiler 查看 Entity Framework Core 生成的 SQL 语句时,突然发现一个细节改进,并且被它震撼了一下: exec sp_executesq ... 
- Entity Framework Core Code First 项目实践
		Entity Framework Core Code First 实践 任何一种技术的出现都是为了解决一系列特定的问题,只有了解了技术所要解决的关键问题,才能理解它的真正用途,之后,才能在实践中用好它 ... 
- Entity Framework Core 使用HiLo生成主键
		#cnblogs_post_body.cnblogs-markdown p img { max-width: 95%; } HiLo是在NHiernate中生成主键的一种方式,不过现在我们可以在Ent ... 
- 对Entity Framework Core的一次误会:实体状态不跟踪
		在 Entity Framework 中,当通过 EF 使用 LINQ 查询获取到一个实体(实际得到的是 EF 动态生成的实体类的代理类的实例)时,这个实体的状态默认是被跟踪的.所以,当你修改实体的某 ... 
- Working with Data »  Getting started with ASP.NET Core and Entity Framework Core using Visual Studio » 读取关系数据
		Reading related data¶ 9 of 9 people found this helpful The Contoso University sample web application ... 
- Entity Framework Core 软删除与查询过滤器
		本文翻译自<Entity Framework Core: Soft Delete using Query Filters>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 注意 ... 
随机推荐
- 小小白的python之路------python基础01
			1. 不说python是啥了,百度一堆.,还是说说我学了啥 我说的是python3.5,其他的自己看着办 这个是下载链接啊,自己玩 https://www.python.org/ 我下载完成,使用py ... 
- nodeJS之TCP模块net
			前面的话 TCP服务在网络应用中十分常见,目前大多数的应用都是基于TCP搭建而成的.net模块提供了一个异步网络包装器,用于TCP网络编程,它包含了创建服务器和客户端的方法.本文将详细介绍nodeJS ... 
- 让Chrome看不了WWDC直播的HLS技术详解
			Requirements: Live streaming uses Apple's HTTP Live Streaming (HLS) technology. HLS requires an iPho ... 
- 【数据库】Mean web开发 02-Windows下Mongodb安装配置及常用客户端管理工具
			简介 Mean是JavaScript的全栈开发框架.更多介绍 用MongoDB实现持久数据的存储是Mean Web全栈开发中的一部分. MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非 ... 
- [python标准库]XML模块
			1.什么是XML XML是可扩展标记语言(Extensible Markup Language)的缩写,其中的 标记(markup)是关键部分.您可以创建内容,然后使用限定标记标记它,从而使每个单词. ... 
- 基于docker的 Hyperledger Fabric 多机环境搭建(上)
			环境:ubuntu 16.04 Docker 17.04.0-ce go 1.7.4 consoul v0.8.0.4 ======================================= ... 
- linux-ubuntu下fastQC的安装
			1.fastqc是在Java环境下运行的:所以在安装fastqc之前,Linux下要有相应的Java运行环境(JRE).且java的版本应该在1.8.0版以上 2.java的安装:下载最新版本的Jav ... 
- javascript基础(幼兔、小兔成兔数量等典型例题)
			一张纸的厚度是0.0001米,将纸对折,对折多少次厚度超过珠峰高度8848米var sum=0; var a=0.0001 for(var i=0;i<100;i++){ a=a*2; sum= ... 
- CLR基础与术语
			CLR(Common Language Runtime):一个可由多种编程语言使用的"运行时". CLR的核心功能(内存管理,程序集加载,安全性,异常处理,线程同步等)可由面向CL ... 
- 电脑上的windows键突然失灵了,肿么办
			windows经常会用到,或许平时感觉不出异常来,偶尔用一次的时候,去发现失灵了,肿么办? 如果只是单纯的弹出开始菜单来,可以按Ctrl+Esc,功能是一样的. 这种情况其实是windows被禁用了, ... 
