简体   繁体   English

实体框架 5 - 派生类的基于枚举的鉴别器

[英]Entity Framework 5 - Enum based Discriminator for derived classes

I have the following (abbreviated for clarity) - an enum, a base class with that enum, and two derived classes that set the enum to a specific value.我有以下内容(为清楚起见而缩写) - 一个枚举,一个带有该枚举的基类,以及两个将枚举设置为特定值的派生类。

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; }
}

What I want to do, is have Entity Framework 5 automatically distinguish between DerivedOne and DerivedTwo, with a MyEnum value based discriminator .我想要做的是让实体框架 5 自动区分 DerivedOne 和 DerivedTwo,并使用基于 MyEnum 值的 discriminator I should be able to do this as, by convention, every MyEnum == MyEnum.Value1 represents DerivedOne, and MyEnum == MyEnum.Value2 represents DerivedTwo.我应该能够这样做,因为按照惯例,每个 MyEnum == MyEnum.Value1 代表 DerivedOne,而 MyEnum == MyEnum.Value2 代表 DerivedTwo。

I tried this in my DbContext:我在我的 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);
    }
}

However, this throws the following InvalidOperationException:但是,这会引发以下 InvalidOperationException:

The expression 'x => (Convert(x.MyEnum) == 0)' is not a valid property expression.表达式 'x => (Convert(x.MyEnum) == 0)' 不是有效的属性表达式。 The expression should represent a property (...)表达式应该代表一个属性 (...)

Edit: I believe I got a little farther using this:编辑:我相信我使用这个可以走得更远:

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

Now I get this EntityCommandCompilationException:现在我得到这个 EntityCommandCompilationException:

Problem in mapping fragments starting at line (...) Condition member 'MyBaseClass.MyEnum' with a condition other than 'IsNull=False' is mapped.映射从第 (...) 行开始的片段问题 映射条件成员 'MyBaseClass.MyEnum' 的条件不是 'IsNull=False'。 Either remove the condition on MyBaseClass.MyEnum or remove it from the mapping.删除 MyBaseClass.MyEnum 上的条件或将其从映射中删除。

Any hints on how I can solve this?关于我如何解决这个问题的任何提示? Thanks!谢谢!

As far as I know you cannot do that.据我所知,你不能那样做。 Doing the explicit Requires to specify the disciminator is only to give it a name - not to connect it to your property.做显式Requires指定鉴别器只是给它一个name - 而不是将它连接到您的财产。

As far as I know that always results in that error (later) that you're describing.据我所知,这总是会导致您所描述的错误(稍后)。 If you want to specify discriminator it has to be 'automatic'one (at least I never managed to define it that way)如果你想指定鉴别器,它必须是“自动”的(至少我从来没有设法这样定义它)

But you don't need that really .但你真的不需要那个 The 'enum' and discriminator is built into the type you get back - based on the discriminator values, EF/CF is constructing either 'Base` or 'DerivedOne' or DerivedTwo. “枚举”和鉴别器built into您返回的类型中——基于鉴别器值,EF/CF 正在构建“Base”或“DerivedOne”或“DerivedTwo”。

So to implement what you want you can do the following...因此,要实现您想要的功能,您可以执行以下操作...

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; } }
}

Or just use is instead (if it works for you)...或者只是使用is代替(如果它适合你)......

if (entity is MyBaseClass) // instead of enum  

or Query by...或查询...

.OfType<MyBaseClass>();

As of EF 6.1, I was in fact able to use an enum as a discriminator column, in spite of this error:从 EF 6.1 开始,我实际上能够使用枚举作为鉴别器列,尽管有这个错误:

Additional information: Values of type 'MyEnum' cannot be used as type discriminator values.附加信息:“MyEnum”类型的值不能用作类型鉴别器值。 Supported types include byte, signed byte, bool, int16, int32, int64, and string.支持的类型包括字节、有符号字节、bool、int16、int32、int64 和 string。

All I have to do was something like this:我所要做的就是这样:

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());
    }
}

So the secret was using (int)MyEnum.Value* instead of MyEnum.Value* ...所以秘密是使用(int)MyEnum.Value*而不是MyEnum.Value* ...

As of EF Core you can use Enums directly on Fluent API.从 EF Core 开始,您可以直接在 Fluent API 上使用枚举。 If your MyBaseClass is not mapped, you can remove the first HasValue line describing the base discriminator.如果您的 MyBaseClass 未映射,您可以删除描述基本鉴别器的第一行HasValue

modelBuilder.Entity<MyBaseClass>()
    .HasDiscriminator<MyEnum>("MyEnum")
    .HasValue<MyBaseClass>(MyEnum.Value0)
    .HasValue<DerivedOne>(MyEnum.Value1)
    .HasValue<DerivedTwo>(MyEnum.Value2);

Based on the answer of @rsenna but updated with mapping based on Microsofts Fluent Api original documentation.基于@rsenna 的回答,但更新了基于 Microsofts Fluent Api 原始文档的映射。

https://msdn.microsoft.com/en-us/library/jj591617%28v=vs.113%29.aspx?f=255&MSPPError=-2147217396 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));
    }
}

I wonder if adding a third value to MyEnum to represent the base class will help.我想知道向MyEnum添加第三个值来表示基类是否会有所帮助。 And then set MyBaseClass.MyEnum to that particular 'default' enum value in the constructor.然后在构造函数中将MyBaseClass.MyEnum设置MyBaseClass.MyEnum特定的“默认” enum值。

I think the Table-per-heirarchy structure needs that EVERY type must have a valid discriminator.我认为 Table-per-heirarchy 结构需要每个类型都必须有一个有效的鉴别器。 So, you have 3 types:因此,您有 3 种类型:

  1. MyBaseClass
  2. DerivedOne
  3. DerivedTwo

Even if your application won't use MyBaseClass in its base form ever, EF still needs a valid discriminator mapping for it.即使您的应用程序永远不会以其基本形式使用 MyBaseClass,EF 仍然需要一个有效的鉴别器映射。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM