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

时间:2013-04-07 06:06:23

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

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

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 =&gt; (Convert(x.MyEnum)== 0)'不是有效的属性表达式。表达式应表示属性(...)

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

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

现在我得到这个EntityCommandCompilationException:

从行开始映射片段的问题(...)映射条件成员'MyBaseClass.MyEnum',其条件不是'IsNull = False'。删除MyBaseClass.MyEnum上的条件或从映射中删除它。

有关如何解决此问题的任何提示?谢谢!

5 个答案:

答案 0 :(得分:7)

据我所知你不能这样做。使用显式Requires来指定说明者只是为了给它一个name - 不要将它连接到你的财产。

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

但你真的不需要。 'enum'和鉴别符是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>();

答案 1 :(得分:4)

从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* ...

答案 2 :(得分:2)

基于@rsenna的答案,但更新了基于Microsofts 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));
    }
}

答案 3 :(得分:0)

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

我认为每个继承人表结构需要每个类型都必须具有有效的区分符。因此,您有3种类型:

  1. MyBaseClass
  2. DerivedOne
  3. DerivedTwo

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

答案 4 :(得分:0)

从EF Core开始,您可以直接在Fluent API上使用枚举。如果未映射MyBaseClass,则可以删除描述基本标识符的第一行 HasValue 行。

use Doctrine\Common\Inflector\Inflector;

public class Server {

    public function setParameters($params) {
        foreach ($params as $k => $p) {
            if (!is_null($p)) { // here is the if statement
                $key = Inflector::camelize($k);
                if (property_exists($this, $key)) {
                    $this->{'set' . ucfirst($key)}($p);
                }
            }
        }
        return $this;
    }
}