Entity Framework Inheritance relational tables - c#

I have tried several things before coming here, such as different model approach, annotation, declarations in DbContext, different fluent API usages but I can't seem to see what the issue is.
I have a YogaClass record but when I iterate over the subscriptions from a person, I have a subscription but no YogaClass (NULL) and yes I.Include(Person.Subscriptions) when querying the DB, I'm getting the subs but not the relational YogaClass/WorkShop associated with it.
In short, I have the following classes :
Subscription (base class)
public class Subscribtion
{
[Key]
public int SubscribtionID { get; set; }
public Person Person { get; set; }
public bool IsPayed { get; set; }
}
WorkshopSubscription (inherrits Subscription)
public class WorkshopSubscribtion : Subscribtion
{
[Key]
public int WorkshopSubscribtionID { get; set; }
public Workshop Workshop { get; set; }
}
YogaClassSubscription (inherrits Subscription)
public class YogaClassSubscribtion : Subscribtion
{
[Key]
public int YogaClassSubscribtionID { get; set; }
public YogaClass YogaClass { get; set; }
}
YogaClass (base class)
public class YogaClass
{
[Key]
public int YogaClassID { get; set; }
public List<Subscriptions> Subscriptions { get; set; }
}
WorkShop (base class)
public class WorkShop
{
[Key]
public int WorkShopID { get; set; }
public List<Subscriptions> Subscriptions { get; set; }
}
Now after I insert some records in the Seeder I have the following issue when I look into my DataBase:
Table Subscription : SubscriptionID:1 , WorkShop_WorkShopID:NULL , YogaClass_YogaClassID:NULL . (Why are they both NULL ?)
Table YogaClassSubscription : SubscriptionID:1 , YogaClassID:1
Same for workshop.
I don't get why the FK from Yoga & Workshop Subscription is NULL in Subscription table.
I have a DbSet declared in my Context and in modelBuilder fluent API method I have mapped both YogaClassSubscribtion & WorkShopSubscribtion to their own table.
public DbSet<Subscribtion> Subscribtions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<WorkshopSubscribtion>().ToTable("WorkshopSubscribtions");
modelBuilder.Entity<YogaClassSubscribtion>().ToTable("YogaClassSubscribtions");
}

I think your problem comes from using the base class Subscription as the type of your generic lists Subscriptions. You should use the specific derived classes YogaClassSubscription and WorkShopSubscription:
public class YogaClass
{
[Key]
public int YogaClassID { get; set; }
public virtual List<YogaClassSubscription> Subscriptions { get; set; }
}
public class WorkShop
{
[Key]
public int WorkShopID { get; set; }
public virtual List<WorkShopSubscription> Subscriptions { get; set; }
}
This way EF knows about the relationship between WorkShops and WorkShopSubscriptions, and between YogaClasses and YogaClassSubscriptions.
Another thing that seems wrong: redefining the ID property and the Key annotations in your derived classes. You only need to define the ID in the base class. Remove those properties and their annotations. EF will create a foreign key with a one-to-one relationship between your base class table and the derived classes tables.
public class WorkshopSubscription : Subscription
{
public virtual Workshop Workshop { get; set; }
}
public class YogaClassSubscription : Subscription
{
public virtual YogaClass YogaClass { get; set; }
}
An advice: define your navigation properties as virtual, in order to allow EF to use proxies to track status changes in your entities, and also to allow the use of Lazy Loading.

Related

Entity Framework 6 - Code first - FK is generated in inherited classes but relation is defined in base class

What i want is to have a base class and two separate lists of inherited classes.
This is my model:
public class Instance
{
public int InstanceId { get; set; }
public virtual ICollection<User> Users { get; internal set; }
public virtual ICollection<MasterUser> MasterUsers { get; internal set; }
}
public abstract class CoreUser
{
[Key]
public int UserId { get; set; }
public int InstanceId { get; set; }
public virtual Instance Instance { get; set; }
}
[Table("Users")]
public class User : CoreUser
{
public string UserName { get; set; }
}
[Table("MasterUsers")]
public class MasterUser : CoreUser
{
public string MasterUserName { get; set; }
}
This is my DbContext:
public class MyContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<MasterUser> MasterUsers { get; set; }
public DbSet<Instance> Instances { get; set; }
}
This will create 4 tables with TPT inheritance model which is fine. The problem is Users and MasterUsers table will contain foreign key to Instance (it will be called Instance_InstanceId) which is redundant since this FK is defined in the CoreUser base class. These two FK columns are not even populated, they are always NULL, CoreUsers InstanceId column is populated when you add either User or MasterUser.
If I remove both referenced from Instance class like so:
public class Instance
{
public int InstanceId { get; set; }
}
Problem goes away but that also renders my application unusable.
I can also solve my problem like so:
public class Instance
{
public int InstanceId { get; set; }
public virtual ICollection<CoreUser> Users { get; internal set; }
}
And then iterate trough collection filtering out each type of user but this approach will lazy load all of the users even though I just want to iterate trough MasterUsers only.
One possible solution was to use TPC but in reality, CoreUser class will contain FKs to some other Classes which is not supported in TPC (only top level classes in hierarchy can contain FKs).
Is there any way I can get this to work in EF using two separate lists in Instance class and have them lazy loaded?
EDIT
Actually, the above code would work just fine. It will break if you introduce one more class that references CoreUser for example:
public class UserPolicy
{
public int PolicyId { get; set; }
public virtual CoreUser PolicyUser { get; internal set; }
}
Managed to get around this. Solution that I was able to use is to move relationship between CoreUser and Instance to User and MasterUser like so:
public class Instance
{
public int InstanceId { get; set; }
// Still referencing two lists
public virtual ICollection<User> Users { get; internal set; }
public virtual ICollection<MasterUser> MasterUsers { get; internal set; }
}
public abstract class CoreUser
{
[Key]
public int UserId { get; set; }
// No reference to instance. Works if you don't need it from CoreUser
}
[Table("Users")]
public class User : CoreUser
{
// FK to Instance defined in CoreUser
public int InstanceId { get; set; }
public virtual Instance Instance { get; set; }
public string UserName { get; set; }
}
[Table("MasterUsers")]
public class MasterUser : CoreUser
{
// FK to Instance defined in MasterUser
public int InstanceId { get; set; }
public virtual Instance Instance { get; set; }
public string MasterUserName { get; set; }
}

TPT inheritance with Abstract base doesn't reference children in related table

I'm struggling with TPT inheritance in MVC - EF.
I have an AAnimal abstract class and two classes that inherit it, Zebra and Lion. There is a Cage class which holds an AAnimal.
My problem is that because AAnimal is abstract, the EF cannot create an instance of it when I load all Cages. So what I want is a way to override this behavior and make it understand whether it needs to load a Zebra or a Lion.
Zebra and Lion have a Primary key which is also foreign key to Animal table. This is done by the EF (TPT inheritance model).
public abstract class AAnimal
{
[Key]
public int AnimalId { set; get; }
public string Name { set; get; }
}
public class Lion : AAnimal {}
public class Zebra : AAnimal {}
public class Εmployee
{
[Key]
public int EmployeeId { set; get; }
public string Name { set; get; }
}
public class Cage
{
[Key]
public int CageId { set; get; }
[ForeignKey("CagedAnimal")]
public int CagedAnimalId { set; get; }
public AAnimal CagedAnimal { set; get; }
[ForeignKey("CageEmployee")]
public int CageEmployeeId { set; get; }
public Employee CageEmployee { set; get; }
}
// Model mapping
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AAnimal>().ToTable("Animal");
modelBuilder.Entity<Lion>().ToTable("Lion");
modelBuilder.Entity<Zebra>().ToTable("Zebra");
modelBuilder.Entity<Εmployee>().ToTable("Εmployee");
}
// Load all cages
public ActionResult Index()
{
var allCages = db.Cages.ToList();
}
At this point all cages are loaded, all fields have values except the CagedAnimal which is null. Even the CagedAnimalId has value.
How can I tell the EF to follow the same procedure while saving data, in order to load entities?
Note that this is just an example. Also, TPT inheritance model has been selected over other inheritance models.

EF5 Model-First, DBContext code generation and derived class

I'm creating a EF5 entity model with the designer (VS2012), and used the EF5 DbContext generator as code generation item.
My model contains an entity deriving from another (not abstract).
So let's say the base entity is called BaseEntity, and the derived entity is DerivedEntity.
Now I see in the generated context class, that there is no
Public DbSet<DerivedEntity> DerivedEntities { get; set; }
defined.
Only
Public DbSet<BaseEntity> BaseEntities { get; set; }
is defined.
Is this normal ? And if yes, how do I query the derived entities in linq ?
I'm used to query like this:
using(var ctx = new EntityContainer)
{
var q = from e in ctx.DerivedEntities <-- but this is now not possible since it doesn't exist
select e;
return q.ToList();
}
Thanks for replying.
EDIT:
As requested, generated classes posted:
public partial class Scheduling
{
public int Id { get; set; }
public string Subject { get; set; }
public System.DateTime BeginDate { get; set; }
public System.DateTime EndDate { get; set; }
}
public partial class TeamScheduling : Scheduling
{
public int TeamId { get; set; }
public Nullable<int> AssignmentId { get; set; }
public virtual Team Team { get; set; }
public virtual Assignment Assignment { get; set; }
}
public partial class EntityContainer : DbContext
{
public EntityContainer()
: base("name=EntityContainer")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<Team> Teams { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<Country> Countries { get; set; }
public DbSet<Assignment> Assignments { get; set; }
public DbSet<ProductType> ProductTypes { get; set; }
public DbSet<AssignmentPreference> AssignmentPreferences { get; set; }
public DbSet<Scheduling> Schedulings { get; set; }
}
As you see, the EntityContainer class does not contain
public DbSet<TeamScheduling> TeamSchedulings { get; set; }
This is expected when you use inheritance the way you have. context.Schedulings contains both Scheduling objects and TeamScheduling objects. You can get the TeamScheduling objects only by asking for context.Schedulings.OfType<TeamScheduling>(). Note that you cannot meaningfully use context.Schedulings.OfType<Scheduling>() to get the others: that will also include the TeamScheduling objects.
You could alternatively try context.Set<TeamScheduling>(), but I'm not entirely sure that will work.
If your intention is to have two tables come up, say a parent Scheduling entity as well as a child TeamScheduling entity that has a foreign key back to the Scheduling entity, consider using a Table-per-Type (TPT) mapping as discussed here.
In essence, you should modify your "OnModelCreating" method to have the following code:
modelBuilder.Entity<TeamScheduling>().ToTable("TeamScheduling");
This explicitly tells EF that you want to have the TeamScheduling subclass to be represented as its own table. Querying it via LINQ would be simple as you would be able to do something like the following:
var teamScheds = context.Set<TeamScheduling>().Where(s => s.Id == 1).FirstOrDefault();

Entity Framework Many to many through containing object

I was curious if it is possible to map an intermediate table through a containing object.
public class Subscriber : IEntity
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
private ChannelList _subscribedList { get; set; }
public int NumSubscribedChannels { get { return _subscribedList.Count(); } }
}
public class HelpChannel : IEntity
{
[Key]
public int Id { get; set; }
public string name { get; set; }
public string category { get; set; }
public int group { get; set; }
}
I need to have a subscriber table, channel table and an intermediate table to link a subscriber to his/her channels.
Is it possible to map the list that is within the ChannelList object to the Subscriber Model?
I figured that's probably not possible and that I would need to just have a private List for EF to map. But I wasn't sure if EF will do that for private variables. Will it?
I'm hoping that is does because if it has to be public to maintain the encapsulation.
You can map private properties in EF code-first. Here is a nice description how to do it. In your case it is about the mapping of Subscriber._subscribedList. What you can't do is this (in the context's override of OnModelCreating):
modelBuilder.Entity<Subscriber>().HasMany(x => x._subscribedList);
It won't compile, because _subscribedList is private.
What you can do is create a nested mapping class in Subscriber:
public class Subscriber : IEntity
{
...
private ICollection<HelpChannel> _subscribedList { get; set; } // ICollection!
public class SubscriberMapper : EntityTypeConfiguration<Subscriber>
{
public SubscriberMapper()
{
HasMany(s => s._subscribedList);
}
}
}
and in OnModelCreating:
modelBuilder.Configurations.Add(new Subscriber.SubscriberMapping());
You may want to make _subscribedList protected virtual, to allow lazy loading. But it is even possible to do eager loading with Include:
context.Subscribers.Include("_subscribedList");

Interfaces and multiplicity contraint violation on EF 4.1 Code-first

I'm trying to define a supply chain with Suppliers, Dealers and Retailers. This entities are bound by a Contract class that also defines the ProductLine and the Products they will work with.
For a given ProductLine, there will be a contract between a Supplier (the sole owner of that ProductLine) and a Dealer, and then another contract between this Dealer and a Retailer.
The problem is that there's also a contract between two dealers so I tried creating two interfaces (ISeller and IBuyer). Supplier implements ISeller, Retailer implements IBuyer and Dealer implements both interfaces:
public class Supplier : ISeller
{
public int Id { get; set; }
public virtual ICollection<Contract> Contracts { get; set; }
}
public class Dealer : ISeller, IBuyer
{
public int Id { get; set; }
public virtual ICollection<Contract> Contracts { get; set; }
}
public class Retailer : IBuyer
{
public int Id { get; set; }
public virtual ICollection<Contract> Contracts { get; set; }
}
The Contract then bounds a ISeller to a IBuyer, like this:
public class Contract
{
public int Id { get; set; }
public virtual ISeller Seller { get; set; }
public virtual IBuyer Buyer { get; set; }
}
Creating contracts between Supplier/Dealer or Dealer/Retailer works as intended, but I get a 'Multiplicity constraint violated' when trying to create a Dealer/Dealer contract.
It seems that the problem with this code is the interfaces. As Slauma said in the comments, the interface members of Contract class are not mapped at all since EF does not know, for example, which entities - Supplier, Dealer or both - map to Seller member.
From the other direction we have that each of the supply chain participants have multiple contracts. This results in Supplier_id, Dealer_id, Reseller_id columns in Contracts table. From EF perspective, Supplier and Dealer have nothing in common, neither do Retailer and Dealer.
What you need to do is to have entity inheritance. Dealer can be both seller and buyer though so you cannot have 2 separate classes as C# does not allow multiple inheritance. Define ContractParticipant base entity and have Supplier, Dealer and Retailer inherit from it. Then your data model would look something like:
public abstract class ContractParticipant
{
public int Id { get; set; }
[InverseProperty("Seller")]
public virtual ICollection<Contract> SellerContracts { get; set; }
[InverseProperty("Buyer")]
public virtual ICollection<Contract> BuyerContracts { get; set; }
}
public class Supplier : ContractParticipant
{
<...other properties here...>
}
public class Dealer : ContractParticipant
{
<...other properties here...>
}
public class Retailer : ContractParticipant
{
<...other properties here...>
}
public class Contract
{
public int Id { get; set; }
public virtual ContractParticipant Seller { get; set; }
public virtual ContractParticipant Buyer { get; set; }
}
This model should generate database structure that would support your scenario without any other configuration. However it would also allow contracts between any types of participants but If you try to map multiple inheritance in data model you would end up with something like this - consider if you want to complicate your data model to preserve these constraints.
Trying using this one :
public class Contract
{
public int Id { get; set; }
public virtual Seller Sellers { get; set; }
public virtual Buyer Buyers { get; set; }
}

Categories