【问题标题】:Entity Framework 5 - Enum based Discriminator for derived classesEntity Framework 5 - 派生类的基于枚举的鉴别器
【发布时间】:2013-03-29 09:00:13
【问题描述】:

我有以下(为清楚起见而缩写) - 一个枚举、一个具有该枚举的基类,以及两个将枚举设置为特定值的派生类。

public enum MyEnum
{ 
    Value1, Value2
}

public class MyBaseClass
{ 
    public MyEnum { get; protected set; }
}

public class DerivedOne: MyBaseClass
{
    public DerivedOne { MyEnum = MyEnum.Value1; } 
}

public class DerivedTwo: MyBaseClass
{
    public DerivedTwo { MyEnum = MyEnum.Value2; }
}

我想做的是让 Entity Framework 5 自动区分 DerivedOne 和 DerivedTwo,使用基于 MyEnum 值的鉴别器。我应该能够这样做,因为按照惯例,每个 MyEnum == MyEnum.Value1 代表 DerivedOne,而 MyEnum == MyEnum.Value2 代表 DerivedTwo。

我在我的 DbContext 中尝试过这个:

public class MyDbContext : DbContext
{
    DbSet<MyBaseClass> MyBaseClass { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MyBaseClass>()
                    .Map<DerivedOne>(m => m.Requires(x => x.MyEnum == MyEnum.Value1));

        base.OnModelCreating(modelBuilder);
    }
}

但是,这会引发以下 InvalidOperationException:

表达式 'x => (Convert(x.MyEnum) == 0)' 不是有效的属性表达式。表达式应该代表一个属性 (...)

编辑:我相信我使用这个更进一步:

modelBuilder.Entity<MyBaseClass>().Map<DerivedOne>(m => m.Requires("MyEnum")
                                  .HasValue((Int32)MyEnum.Value1));

现在我得到了这个 EntityCommandCompilationException:

从第 (...) 行开始映射片段时出现问题 映射条件成员“MyBaseClass.MyEnum”且条件不是“IsNull=False”。删除 MyBaseClass.MyEnum 上的条件或将其从映射中删除。

关于如何解决这个问题的任何提示?谢谢!

【问题讨论】:

    标签: c# .net-4.0 ef-code-first entity-framework-5


    【解决方案1】:

    据我所知,你不能这样做。执行明确的Requires 来指定鉴别器只是给它一个name - 而不是将它连接到您的属性。

    据我所知,这总是会导致您所描述的错误(稍后)。如果你想指定鉴别器,它必须是“自动”的(至少我从来没有设法这样定义它)

    但你真的不需要那个。 “枚举”和鉴别器是 built into 您返回的类型 - 基于鉴别器值,EF/CF 正在构造“Base”或“DerivedOne”或 DerivedTwo。

    所以要实现您想要的,您可以执行以下操作...

    public class MyBaseClass
    {
        [NotMapped()]
        public virtual MyEnum MyEnum { get { return MyEnum.Base; } }
    }
    
    public class DerivedOne: MyBaseClass
    {
        public string OneProp { get; set; }
        public override MyEnum MyEnum { get { return MyEnum.One; } }
    }
    
    public class DerivedTwo: MyBaseClass
    {
        public string TwoProp { get; set; }
        public override MyEnum MyEnum { get { return MyEnum.Two; } }
    }
    

    或者只使用is 代替(如果它适合你)......

    if (entity is MyBaseClass) // instead of enum  
    

    或查询...

    .OfType<MyBaseClass>();
    

    【讨论】:

      【解决方案2】:

      从 EF Core 开始,您可以通过 Fluent API 直接使用 Enums。如果您的 MyBaseClass 没有被映射(是一个抽象类),您可以删除描述基本鉴别器的第一行 HasValue。在您的 ApplicationDbContext 中尝试下一个代码:

      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
          modelBuilder.Entity<MyBaseClass>()
              .HasDiscriminator<MyEnum>("MyEnum")
              .HasValue<MyBaseClass>(MyEnum.Value0)
              .HasValue<DerivedOne>(MyEnum.Value1)
              .HasValue<DerivedTwo>(MyEnum.Value2);
      }
      

      【讨论】:

        【解决方案3】:

        从 EF 6.1 开始,我实际上能够使用枚举作为鉴别器列,尽管出现了这个错误:

        附加信息:“MyEnum”类型的值不能用作类型鉴别器值。支持的类型包括 byte、signed byte、bool、int16、int32、int64 和 string。

        我所要做的就是这样:

        public enum MyEnum
        { 
            Value1, Value2
        }
        
        public class MyBaseClass
        { 
            public MyEnum { get; protected set; }
        }
        
        public class DerivedOne: MyBaseClass
        {
            public DerivedOne()
            {
                MyEnum = MyEnum.Value1;
            } 
        }
        
        public class DerivedTwo: MyBaseClass
        {
            public DerivedTwo()
            {
                MyEnum = MyEnum.Value2;
            }
        }
        
        public class MyDbContext : DbContext
        {
            DbSet<MyBaseClass> MyBaseClass { get; set; }
        
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Configurations
                    .Add(new DerivedOneConfiguration())
                    .Add(new DerivedTwoConfiguration());
            }
        }
        
        public class DerivedOneConfiguration : EntityTypeConfiguration<DerivedOne>
        {
            public DerivedOneConfiguration()
            {
                Map<DerivedOne>(_ => _.Requires("MyEnum").HasValue((int)MyEnum.Value1).IsRequired());
            }
        }
        
        public class DerivedTwoConfiguration : EntityTypeConfiguration<DerivedTwo>
        {
            public DerivedTwoConfiguration()
            {
                Map<DerivedTwo>(_ => _.Requires("MyEnum").HasValue((int)MyEnum.Value2).IsRequired());
            }
        }
        

        所以秘密是使用(int)MyEnum.Value* 而不是MyEnum.Value*...

        【讨论】:

          【解决方案4】:

          基于@rsenna 的回答,但更新了基于 Microsoft 的 Fluent Api 原始文档的映射。

          https://msdn.microsoft.com/en-us/library/jj591617%28v=vs.113%29.aspx?f=255&MSPPError=-2147217396

          public enum MyEnum
          { 
              Value1, Value2
          }
          
          public class MyBaseClass
          { 
              [NotMapped]
              public MyEnum MyEnum { get; protected set; }
          }
          
          public class DerivedOne: MyBaseClass
          {
              public DerivedOne()
              {
                  MyEnum = MyEnum.Value1;
              } 
          }
          
          public class DerivedTwo: MyBaseClass
          {
              public DerivedTwo()
              {
                  MyEnum = MyEnum.Value2;
              }
          }
          
          public class MyDbContext : DbContext
          {
              DbSet<MyBaseClass> MyBaseClass { get; set; }
          
              protected override void OnModelCreating(DbModelBuilder modelBuilder)
              {
                  base.OnModelCreating(modelBuilder);
          
                  modelBuilder.Entity<MyBaseClass>()
                      .Map<DerivedOne>(x => x.Requires("MyEnum").HasValue((int)MyEnum.Value1))
                      .Map<DerivedTwo>(x => x.Requires("MyEnum").HasValue((int)MyEnum.Value2));
              }
          }
          

          【讨论】:

            【解决方案5】:

            我想知道向MyEnum 添加第三个值来表示基类是否会有所帮助。 然后在构造函数中将 MyBaseClass.MyEnum 设置为特定的“默认”enum 值。

            我认为 Table-per-heirarchy 结构需要每个类型都必须有一个有效的鉴别器。所以,你有 3 种类型:

            1. MyBaseClass
            2. DerivedOne
            3. DerivedTwo

            即使您的应用程序永远不会在其基本形式中使用 MyBaseClass,EF 仍然需要一个有效的鉴别器映射。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2023-03-21
              • 1970-01-01
              • 1970-01-01
              • 2011-03-31
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多