Entity Framework 5 - transition-based discriminator for derived classes

I have the following (in short for clarity) - an enumeration, a base class with this enumeration, and two derived classes that set the enumeration 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; } } 

I want Entity Framework 5 to automatically distinguish between DerivedOne and DerivedTwo, with a discriminator based on MyEnum values . I should be able to do this because, by convention, each MyEnum == MyEnum.Value1 represents DerivedOne, and MyEnum == MyEnum.Value2 represents DerivedTwo.

I tried this in my 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 raises the following InvalidOperationException:

The expression 'x => (Convert (x.MyEnum) == 0)' is not a valid property expression. An expression must represent a property (...)

Edit: I believe I used this a bit:

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

Now I get this EntityCommandCompilationException:

Problem with displaying fragments starting from the line (...) The status element "MyBaseClass.MyEnum" with a condition other than "IsNull = False" is displayed. Either remove the condition on MyBaseClass.MyEnum, or remove it from the mapping.

Any hints on how I can solve this? Thanks!

+9
c # entity-framework-5 ef-code-first
source share
4 answers

As far as I know, you cannot do this. Doing an explicit Requires to indicate the dissimulator is to give it a name - not to connect it to your property.

As far as I know, this always leads to the error (later) that you describe. If you want to specify a discriminator, it must be "automatic" (at least I was not able to determine it)

But you really don't need it . The "enumeration" and discriminator is the built into type that you return - based on the values โ€‹โ€‹of the discriminator, EF / CF creates either "Base", either "DerivedOne" or 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) ...

 if (entity is MyBaseClass) // instead of enum 

or Request using ...

 .OfType<MyBaseClass>(); 
+6
source share

As in EF 6.1, I really could use the enumeration as the discriminator column, despite this error:

Additional Information: Values โ€‹โ€‹of type "MyEnum" cannot be used as values โ€‹โ€‹of a discriminator of a type. Supported types include bytes, signed bytes, bool, int16, int32, int64 and string.

All I need to do is 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 used (int)MyEnum.Value* instead of MyEnum.Value* ...

+4
source share

Based on @rsenna answer, but updated with matching based on original Microsofts Fluent Api documentation.

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)); } } 
+2
source share

I wonder if adding a third value to MyEnum will help to represent the base class. And then set MyBaseClass.MyEnum to this specific default enum value.

I think the Table-per-heirarchy structure requires EACH type to have a valid discriminator. So you have 3 types:

  1. MyBaseClass
  2. DerivedOne
  3. DerivedTwo

Even if your application will never use MyBaseClass in its basic form, EF will still need a valid discriminator display.

0
source share

All Articles