【问题标题】:Controlling column mapping with a custom convention in Entity Framework 6在 Entity Framework 6 中使用自定义约定控制列映射
【发布时间】:2017-09-16 00:23:48
【问题描述】:

我有一个类TypeEntity,它将作为几十个实体的基类。我使用的是TPC,所以需要将基类上的所有属性映射到具体类名的表中,并将Key字段设置为数据库生成。

目前我正在为每个看起来像这样的实体类型使用EntityTypeConfiguration

class LineItemType : EntityTypeConfiguration<Models.LineItemType>
{
    public LineItemType()
    {
        this.Property(e => e.Key)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        this.Map(e => e.MapInheritedProperties()
                       .ToTable(nameof(LineItemType)));
    }
}

这很好用,但非常重复。我必须记住为从TypeEntity 继承的每种类型创建一个配置类,设置键,并映射继承的属性。这似乎是自定义 Convention 的理想案例。


我创建了一个TypeEntityTpcConvention Convention 如下:

class TypeEntityTpcConvention : Convention
{
    public TypeEntityTpcConvention()
    {
        this.Types<TypeEntity>()
            .Configure(e => e.Property(p => p.Key)
                             .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity));
    }
}

这可以将Key 设置为数据库生成,但我找不到任何方法可以从约定内部访问属性的表映射。


理想情况下,我希望是这样的:

this.Types<TypeEntity>()
    .Configure(e => e.MapInheritedProperties()
    .ToTable(e.ClrType.Name));

甚至对需要映射的每个属性都进行这样的调用:

this.Types<TypeEntity>()
    .Configure(e => e.Property(p=>p.Key)
                     .ToTable(e.ClrType.Name));

似乎都不存在。有什么方法可以让我从Convention 内部控制属性的映射?


经过一些额外的研究,似乎有更高级的约定选项可用,如 IStoreModelConventionIConceptualModelConvention,但是关于如何使用这些的有用文档严重缺乏。在用断点和监视窗口浏览了几个小时后,我还没有弄清楚如何使用这些接口控制列映射。


我目前的解决方案是使用反射在OnModelCreating 中查找从TypeEntity 继承的所有类型,并将属性映射到正确的表。这行得通,但如果可能的话,我更喜欢使用约定,因为这看起来确实像是约定的类型。我觉得我错过了一些明显的东西。

【问题讨论】:

  • 问题不在于获取类的名称,而是.ToTable(或其他允许我将属性重定向到不同表的东西)似乎在上下文中不存在Convention.
  • 啊……我误会了。我会删除评论。
  • 我有一个解决方案。这不完全是一个约定,但它确实自动适用于您指定的所有类型(即您不必为每个实体添加新的配置行)。如果你有兴趣,我可以发帖。
  • 您真的需要 TPC 而不仅仅是基类层次结构吗?因为 TPC 的唯一好处是自动多态(联合)查询。
  • @IvanStoev 我相信是的。 TPH 会将所有内容存储在一个表中。我希望来自TypeEntity 的所有内容都在自己的、完全独立的、单独的表中。子类实际上彼此无关。我使用基类只是因为每个都有完全相同的架构。

标签: c# entity-framework entity-framework-6


【解决方案1】:

据我所知,您可以在DbContextOnModelCreating 方法中编写类型配置,它与您在问题中已经提到的代码非常相似。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Types<TypeEntity>().Configure(c =>
    {
        c.HasKey(p => p.Key);
        // probably not needed, but won't do any harm
        c.Property(p => p.Key).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        c.ToTable(c.ClrType.Name);
    });
}

如果这种方法有任何问题,请告诉我,我会重新讨论该主题。

编辑:

完全相同的原则也适用于约定:

class TypeEntityConvention : Convention
{
    public TypeEntityConvention()
    {
        this.Types<TypeEntity>().Configure(c =>
        {
            c.HasKey(p => p.Key);
            c.Property(p => p.Key).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            c.ToTable(c.ClrType.Name);
        });
    }
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Conventions.Add<TypeEntityConvention>();
}

【讨论】:

  • 这也是我的解决方案。不是惯例,但它是通用的,没有反射。
  • 我更喜欢你的解决方案,而不是我目前使用的基于反射的解决方案,但这真的感觉就像 Convention 应该能够处理的事情。在这一点上,我有一个可行的解决方案,我只是想学习如何有效地使用约定,这让这个问题暂时保持开放。
  • @grek40 不会c.ToTable(c.ClrType.Name); 将整个类映射到该表,而不仅仅是基类字段?我无权访问我的项目进行测试。
  • 您提到您使用“TPC”,因此如果您的 DbSet 定义针对实际类型,那么一切都应该没问题 - 就像“将整个类映射到该表”中一样
  • @IvanStoev 你是对的......由于尝试了DatabaseGeneratedOption.Identity,我有点假设不是真正的 TPC 方法,这与单个 DbSet 多表方法相矛盾。也许我们可以查看问题作者 DbContext 以获取更多信息。
猜你喜欢
  • 2017-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-23
  • 2013-07-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多