I've a problem mapping foreign keys to the correct table when using abstract classes. Here's my model :
public abstract class Entity
{
public Guid UID { get; set; }
}
public abstract class Product : Entity
{
public DeviceModel Model { get; set; }
public User Operator { get; set; }
}
public abstract class OrderEntry : Entity
{
public Order Order { get; set; }
}
public class Device : Product
{
public String Reference { get; set; }
public String Serial { get; set; }
public String SoftwareVersion { get; set; }
}
public class OrderEntryDevice : OrderEntry
{
public DeviceModel Model { get; set; }
}
And the fluent api configurations (TPT schema) :
public class EntityConfiguration : EntityTypeConfiguration<Entity>
{
public EntityConfiguration()
{
ToTable("Entities");
HasKey(t => t.UID);
}
}
public class ProductConfiguration : EntityTypeConfiguration<Product>
{
public ProductConfiguration()
{
ToTable("Products");
HasOptional(t => t.Operator)
.WithMany()
.Map(t => t.MapKey("FK_Operator"))
.WillCascadeOnDelete(false);
}
}
public class OrderEntryConfiguration : EntityTypeConfiguration<OrderEntry>
{
public OrderEntryConfiguration()
{
ToTable("OrderEntries");
HasRequired(t => t.Order)
.WithMany()
.Map(t => t.MapKey("FK_Order"))
.WillCascadeOnDelete(false);
}
}
public class DeviceConfiguration : EntityTypeConfiguration<Device>
{
public DeviceConfiguration()
{
ToTable("Devices");
Property(t => t.Reference)
.IsRequired();
Property(t => t.Serial)
.IsRequired();
HasRequired(t => t.Model)
.WithMany()
.Map(t => t.MapKey("FK_Model"))
.WillCascadeOnDelete(false);
}
}
public class OrderEntryDeviceConfiguration : EntityTypeConfiguration<OrderEntryDevice>
{
public OrderEntryDeviceConfiguration()
{
ToTable("OrderEntriesDevice");
HasRequired(t => t.Model)
.WithMany()
.Map(t => t.MapKey("FK_Model"))
.WillCascadeOnDelete(false);
}
}
Creation of the database will put the "FK_Operator" foreign key in the "Products" table (exactly where I want it) but the "FK_Order" foreign key is placed in the "Entities" table instead of "OrderEntries" table. If I change the abstract property of class "OrderEntry" to concrete, then everything is OK. Do I have to avoid abstract class int this case ?
I've tried your model and I cannot reproduce the problem. I get the FK_Order
column in the OrderEntries
table and not in Entities
table - as expected.
You can copy the following into Program.cs
of a console app (also add EntityFramework.dll
and System.ComponentModel.DataAnnotations.dll
to references).
I have created three dummy classes for User
, DeviceModel
and Order
to get the code compiled and running. But the other classes are a copy from your question.
The question is: Where is the important difference between the code below and your code which could cause the wrong mapping you have?
using System;
using System.Linq;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
namespace EFAbstractTest
{
public class User
{
[Key]
public Guid UID { get; set; }
public string Name { get; set; }
}
public class DeviceModel
{
[Key]
public Guid UID { get; set; }
public string Name { get; set; }
}
public class Order
{
[Key]
public Guid UID { get; set; }
public string Name { get; set; }
}
public abstract class Entity
{
public Guid UID { get; set; }
}
public abstract class Product : Entity
{
public DeviceModel Model { get; set; }
public User Operator { get; set; }
}
public abstract class OrderEntry : Entity
{
public Order Order { get; set; }
}
public class Device : Product
{
public String Reference { get; set; }
public String Serial { get; set; }
public String SoftwareVersion { get; set; }
}
public class OrderEntryDevice : OrderEntry
{
public DeviceModel Model { get; set; }
}
public class EntityConfiguration : EntityTypeConfiguration<Entity>
{
public EntityConfiguration()
{
ToTable("Entities");
HasKey(t => t.UID);
}
}
public class ProductConfiguration : EntityTypeConfiguration<Product>
{
public ProductConfiguration()
{
ToTable("Products");
HasOptional(t => t.Operator)
.WithMany()
.Map(t => t.MapKey("FK_Operator"))
.WillCascadeOnDelete(false);
}
}
public class OrderEntryConfiguration : EntityTypeConfiguration<OrderEntry>
{
public OrderEntryConfiguration()
{
ToTable("OrderEntries");
HasRequired(t => t.Order)
.WithMany()
.Map(t => t.MapKey("FK_Order"))
.WillCascadeOnDelete(false);
}
}
public class DeviceConfiguration : EntityTypeConfiguration<Device>
{
public DeviceConfiguration()
{
ToTable("Devices");
Property(t => t.Reference)
.IsRequired();
Property(t => t.Serial)
.IsRequired();
HasRequired(t => t.Model)
.WithMany()
.Map(t => t.MapKey("FK_Model"))
.WillCascadeOnDelete(false);
}
}
public class OrderEntryDeviceConfiguration : EntityTypeConfiguration<OrderEntryDevice>
{
public OrderEntryDeviceConfiguration()
{
ToTable("OrderEntriesDevice");
HasRequired(t => t.Model)
.WithMany()
.Map(t => t.MapKey("FK_Model"))
.WillCascadeOnDelete(false);
}
}
public class MyContext : DbContext
{
public DbSet<Entity> Entities { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<DeviceModel> DeviceModels { get; set; }
public DbSet<Order> Orders { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new EntityConfiguration());
modelBuilder.Configurations.Add(new ProductConfiguration());
modelBuilder.Configurations.Add(new OrderEntryConfiguration());
modelBuilder.Configurations.Add(new DeviceConfiguration());
modelBuilder.Configurations.Add(new OrderEntryDeviceConfiguration());
}
}
class Program
{
static void Main(string[] args)
{
using (var ctx = new MyContext())
{
// some query, just to trigger database creation
ctx.Orders.Count();
}
}
}
}