前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >EntityFramework Core 自动绑定模型映射

EntityFramework Core 自动绑定模型映射

作者头像
潘成涛
发布2018-07-09 16:48:20
1.2K0
发布2018-07-09 16:48:20
举报
文章被收录于专栏:程序员与猫

笔者最近在和同事共同开发项目时,需要从他们提供的包含数据库实体类型的类库中读取实体信息绑定到自己的项目中(但是都在同一个解决方案里),所以很直接的一种方式就是把项目中所有的实体都以 public DbSet<Blog> Blogs { get; set; } 的形式加入到自己的 Context 中,但是这显然十分麻烦,而且如果又新增或减少了实体,每次又得在Context中做修改。

先放上示例的两个实体,假设它们都处于Synyi.EntityDemo这个项目类库中。其实IEntity是一个空接口,起指示作用。

代码语言:javascript
复制
namespace Synyi.EntityDemo
{
    public class Blog : IEntity
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    
        public List<Post> Posts { get; set; }
    }
    
    public class Post : IEntity
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
    
        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
}

所以有没有什么办法可以直接让 EntityFramework Core 来代劳这项工作呢?从这个想法出发,其实我们很自然地就可以想到 Context 中的 OnModelCreating 方法,在传统的 EF 6中,它也是作为实体模型属性映射的方法容器存在。如果大家看过笔者之前的那篇《EntityFramework Core 学习扫盲》,就会知道 Fluent Api 的使用都是在这个方法中的。它的方法签名如下:

代码语言:javascript
复制
protected internal virtual void OnModelCreating(ModelBuilder modelBuilder)
{
}

配置的方法容器找到了,读取实体信息也是水到渠成的一件事,我们可以直接利用对程序集的反射读取所有的内部实体信息。代码如下:

代码语言:javascript
复制
var entityTypes = Assembly.Load(new AssemblyName("存放实体类型的程序集名称")).GetTypes()
    .Where(type => !string.IsNullOrWhiteSpace(type.Namespace))
    .Where(type => type.GetTypeInfo().IsClass)
    .Where(type => type.GetTypeInfo().BaseType != null)
    .Where(type => typeof(IEntity).IsAssignableFrom(type)).ToList();

其中 typeof(IEntity).IsAssignableFrom(type) 只是为了能获取到确定的继承了 IEntity 接口的实体而已。在这一步以后,通过查看 modelBuilder 上的相应方法,我们找到了 FindEntityTypeAddEntityType 方法。所以最后的代码如下:

代码语言:javascript
复制
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(
        "Server=(localdb)\\MSSQLLocalDB;Database=ExampleDb;Trusted_Connection=True;MultipleActiveResultSets=true");
}

protected override void OnModelCreating(ModelBuilder builder)
{
    var entityTypes = Assembly.Load(new AssemblyName("存放实体类型的程序集名称")).GetTypes()
        .Where(type => !string.IsNullOrWhiteSpace(type.Namespace))
        .Where(type => type.GetTypeInfo().IsClass)
        .Where(type => type.GetTypeInfo().BaseType != null)
        .Where(type => typeof(IEntity).IsAssignableFrom(type)).ToList();

    foreach (var entityType in entityTypes)
    {
        //  防止重复附加模型,否则会在生成指令中报错
        if (builder.Model.FindEntityType(entityType) != null)
            continue;

        builder.Model.AddEntityType(entityType);
    }

    base.OnModelCreating(builder);
}

使用 Add-Migration XXUpdate-Database 指令后,我们的 ExampleDb 中就生成了相应的数据库表,一些隐藏的诸如“实体中命名为 Id 或者 ClassName+Id 的属性将自动设置为主键”的规则也会自动生效。假如目标数据库是类似于 PostgreSql 这种,数据库的表名和列名都得定义成小写字母,否则在 sql 时将不得不使用双引定义,十分的麻烦。所幸我们也可以直接在 OnModelCreating 方法中指定这一项规则。在上述方法末尾加上如下代码:

代码语言:javascript
复制
foreach (var entity in builder.Model.GetEntityTypes())
{
    var currentTableName = builder.Entity(entity.Name).Metadata.Relational().TableName;
    builder.Entity(entity.Name).ToTable(currentTableName.ToLower());

    var properties = entity.GetProperties();
    foreach (var property in properties)
        builder.Entity(entity.Name).Property(property.Name).HasColumnName(property.Name.ToLower());
}

至于其他的配置,就要靠大家去挖掘了。

消失的 EntityTypeConfiguration

在传统的 EF 编程中,大家对 EntityTypeConfiguration 应该都十分的熟悉。比如如下的代码:

代码语言:javascript
复制
public class BlogConfiguration : EntityTypeConfiguration<Blog>
{
    public BlogConfiguration()
    {
        ToTable("Blogs");
        HasKey(x => x.Id);
        Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("BlogId");
        Property(x => x.Title).HasMaxLength(175);
        HasRequired(x => x.Url).WithRequiredPrincipal();
    }
}

// 在 OnModelCreating 方法中加入以下代码
modelBuilder.Configurations.Add(new BlogConfiguration());

就是这样一个好用的东西,却没有随着 EF 的迁移而保留下来,在 EF Core 中,我们已经看不到它的身影了,残念ですね。不过这也不是多难解决的事情,Github上已经有人给出了相关的解决方案

做些简单的分析——一句比较完整的Fluent Api 设置方式形如 builder.Entity<Blog>().ToTable("Blogs"); 所以我们只要抓住 builder.Entity<XXX>()的返回类型 EntityTypeBuilder 做文章即可。笔者在下面也给出另一种接口+反射方式的实现(第二个参考链接中的代码并不能直接使用)。

代码语言:javascript
复制
public interface IEntityTypeConfiguration
{
}

public class BlogConfiguration : IEntityTypeConfiguration
{
    public BlogConfiguration(ModelBuilder builder)
    {
        builder.Entity<Blog>().ToTable("Blogs");
    }
}

public static class ModelBuilderExtensions
{
    public static void ExecuteConfigurations(this ModelBuilder modelBuilder,string assemblyName)
    {
        var configurationTypes = Assembly.Load(new AssemblyName(assemblyName)).GetTypes()
            .Where(type => !string.IsNullOrWhiteSpace(type.Namespace))
            .Where(type => type.GetTypeInfo().IsClass)
            .Where(type => type.GetTypeInfo().BaseType != null)
            .Where(type => typeof(IEntityTypeConfiguration).IsAssignableFrom(type))
            .ToList();

        foreach (var type in configurationTypes)
            Activator.CreateInstance(type, modelBuilder);
    }
}

// 在 OnModelCreating 方法中加入以下代码
builder.ExecuteConfigurations("存放实体配置的程序集名称");
base.OnModelCreating(builder);

至此,Entity Framework Core 中的自动绑定实体映射应该就告一段落了,其他的功能也很容易基于上文扩展。如果大家有更好的想法,也可以在评论中留言(这语气听起来就好像自己的文章真的会有很多读者一样)。

参考资料

  1. 《Model configuration: Entity type configuration can be factored into a class》
  2. 《Organizing Fluent Configurations into Separate Classes in EF Core 1.0》
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-07-08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 消失的 EntityTypeConfiguration
  • 参考资料
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档