I’m trying to use Entity Framework code first to try to map a simple lookup table to a heirarchy of types and want to use the primary key of the table as the discriminator column for a “table per heirarchy” entity. More acturately, I’m trying to make this work against an existing database.
Here is a contrived sample app I put together trying to figure out if I can make it work or not:
using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
namespace EfTest
{
public abstract class Base
{
public int LookupId { get; set; }
public String Name { get; set; }
public abstract String GetTest();
}
public class Derived1 : Base
{
public override string GetTest() { return Name + "1"; }
}
public class Derived2 : Base
{
public override string GetTest() { return Name + "2"; }
}
public class Derived3 : Base
{
public override string GetTest() { return Name + "3"; }
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer<MyContext>(new DropCreateDatabaseIfModelChanges<MyContext>());
using(var context = new MyContext())
{
foreach (var item in context.Lookups)
{
Console.WriteLine("{0} {1} {2}", item.LookupId, item.Name, item.GetType().FullName);
}
}
Console.ReadKey();
}
}
public class MyContext : DbContext
{
public DbSet<Base> Lookups { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var config = modelBuilder.Entity<Base>();
config.HasKey(e => e.LookupId).ToTable("dbo.Lookup");
config.Property(e => e.LookupId)
.HasColumnName("LookupId")
.IsRequired()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
config.Property(e => e.Name)
.IsRequired()
.HasColumnName("Name")
.HasMaxLength(32)
.HasColumnType("varchar");
config.Map<Derived1>(e => e.Requires("LookupId").HasValue(1).IsRequired());
config.Map<Derived2>(e => e.Requires("LookupId").HasValue(2).IsRequired());
config.Map<Derived3>(e => e.Requires("LookupId").HasValue(3).IsRequired());
//config.Map<Derived1>(e => e.Requires("Name").HasValue("Item1").IsRequired());
//config.Map<Derived2>(e => e.Requires("Name").HasValue("Item2").IsRequired());
//config.Map<Derived3>(e => e.Requires("Name").HasValue("Item3").IsRequired());
}
}
}
However, this raises an exception stating:
Problem in mapping fragments starting at line 24:Condition member
‘Base.LookupId’ with a condition other than ‘IsNull=False’ is mapped.
Either remove the condition on Base.LookupId or remove it from the
mapping.
I’ve also tried discrimiating with the “Name” column with similar results.
The errors look like it complaining that I’m trying to map to nullable column in the database, however, the table that actually gets created has both columns marked not null, as I would expect.
Is what I’m trying to do simply not supported by EF?
This is not possible. Each column can have only single special purpose in the mapping. Having a column as a key and discriminator are two special purposes. Discriminator column has special meaning of selecting the correct type to be materialized and because if that it must not be present in mapped entity (you cannot set it from your application). Also having discriminator on column which must be unique is incorrect.