Entity Framework Core Inheritance creating child tables - entity-framework-core

public class Product
{
public string Name { get; set; }
public int Qty { get; set; }
public decimal Price { get; set; }``
}
public class CartItem : Product
{
public int CartItemId { get; set; }
public string CartId { get; set; }
}
public class OrderLine : Product
{
public int OrderLineId { get; set; }
public int OrderId { get; set; }
}
public class Kititem : Product
{
public int KititemId { get; set; }
public int OrderId { get; set; }
}
public class SampleContext : DbContext
{
public DbSet<CartItem> CartItems { get; set; }
public DbSet<OrderLine> OrderLines { get; set; }
public DbSet<Kititem> Kititems { get; set; }
}
As you can see in this I am not including the parent class Product in the DbContext, and when doing the migrations it creates a table for each derived class with all the properties form the parent class, and it does not create the parent class because is not included in the Dbcontext, for me it was what I was exptecting and is working, and I am happy with it so far
Mi question is if that is a bad practice and maybe I am not using ef core Inheritance the way I can take all the advantages ?
I am confuse and want to start a new project and do it the best way, so I don't have to redo it again

What you are using is called "base class" as opposed to "base entity", i.e. class participating in database model inheritance.
You are not forced to use database inheritance at all. Moreover EF Core currently supports only Table per Hierarchy (TPH) strategy, which is not the best if you have many derived entities with many different properties (because all the data is stored in a single table).
In other words, there is nothing wrong to not use database inheritance. The only benefit of database inheritance is if you need polymorphic queries, i.e. queries that return Products of different types. It's possible to do such queries w/o database inheritance using Union / Concat LINQ operator, but they won't be efficient due to current EF Core lack of translating such queries to SQL, so they always use client evaluation.
Of course this will be improved in some future EF Core version (as well as support for other inheritance strategies), so the main question should be - do you need such queries. If you don't, then your approach of not using database inheritance is just fine. If you do, then decide which is more suitable for your needs - manual Concat or a single table with a lot of columns.

Related

Specify a Parent-Child relationship in EF Core without using identity columns

Specify a Parent-Child relationship in EF Core without using identity columns
What's an efficient way within Entity Framework Core 5 (C#) to work with the data in a hierarchial table that is linked via non-identity columns.
Here's my primary class:
public class ServiceProvider
{
[Key]
public int Id { get; set; }
public string ParentSPCode { get; set; }
public string SPCode { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ContactEmail { get; set; }
public string Status { get; set; }
}
The SPCode value is unique, which I enforce via C# code. The ParentSPCode may be null or must match an existing SPCode. Again I enforce this via C# code.
I want this table to hold any number of levels of parent-child (1 or more) records, as defined by ParentSPCode-SPCode pairs.
I can retrieve these records via a complex hierarchy of LINQ "joins", but I am thinking there must be a cleaner way by defining the appropriate EF Core 5 relationship.
If I was in SQL Server, I would do this via a CTE.
I want to be able to bring in the child records in a manner similar to .Include(q => q.ParentSPCode == x.SPCode).

Entity framework one foreign key toward two tables - code first

All,
Is it possible to use the same FK for two tables.
Probably it is not a good practice, but I have a two different classes that can be both booked:
public class Course {
public Course() {
BookingRefs = new HashSet<BookingRef>();
}
public long Id { get; set; }
public string Title { get; set; }
// other props ...
[InverseProperty(nameof(BookingRef.Course))]
public virtual ICollection<BookingRef> BookingRefs { get; set; }
}
public class GiftCard {
public GiftCard() {
BookingRefs = new HashSet<BookingRef>();
}
public long Id { get; set; }
public string Prop1 { get; set; }
public int Prop2 { get; set; }
// other props ...
[InverseProperty(nameof(BookingRef.Course))]
public virtual ICollection<BookingRef> BookingRefs { get; set; }
}
// this is the bookin reference for a Course or an GiftCard
public class BookingRef {
public BookingRef() {
}
public long Id { get; set; }
// other props ...
/// <summary>The item (usually the course but theoretically anything with a long id)</summary>
public long? ItemId { get; set; }
// maybe a generic Object?
[ForeignKey(nameof(ItemId))]
public Object GiftCard { get; set; }
// maybe 2 items possibly null?
[ForeignKey(nameof(ItemId))]
public Course Course { get; set; }
// maybe 2 items possibly null?
[ForeignKey(nameof(ItemId))]
public GiftCard GiftCard { get; set; }
}
Is it possible to use the same FK for two tables
No. The relational model doesn't allow that. You can introduce a superclass of all your bookable things and have a FK to that, but you shouldn't do that just get a single collection rather than multiple.
Think of it from the relational data perspective. How would the database know what table an "Item ID" pointed at? How would it index it?
This would be a case for using a null-able FK to each related table on the booking. These FKs do not need to reside in the entity, just the navigation properties. You can leverage .Map(x => x.MapKey) in EF6 or .HasForeignKey("") in EF Core to leverage a shadow property.
This does not enforce if you want a booking to only be associated to a course or a gift card but not both. That would need to be catered for at the application level, and I would recommend using a scheduled maintenance task to evaluate the data for violations to that rule. (Look for bookings holding both a course ID and a gift card ID for example)
You can alternatively keep the joins "loose" and evaluated by the application based on a discriminator similar to an inheritance model. (ItemId + ItemType) However you have to resolve the relationship load separately in your application based on the ItemType and lose out on any FK, indexing, and data integrity checks in the database. This could be a significant performance & maintenance cost to save adding a couple FKs.

EF 5 code first optional one to one mapping accessing foreign key fields on the model

This question is basically a repeat of this question regarding EF4 CTP but specific to EF 5.
I have a POCOs set up such that
public class ClassPrinciple
{
public int ClassPrincipleID { get; set; }
public virtual ClassDependent ClassDependent{ get; set; }
}
and
public class ClassDependent
{
public int ClassDependentID { get; set; }
public virtual ClassPrinciple ClassPrinciple{ get; set; }
}
in my model builder I create the optional one to one mapping like this
modelBuilder.Entity<ClassPrinciple>().HasOptional(p => p.ClassDependent)
.WithOptionalDependent(s => s.ClassPrinciple);
this creates, on the ClassPrinciples table a column called ClassDependent_ClassDependentID . I would like to be able to reference the data in this column through a property on the ClassPrinciple model but I seem unable to do so. The web page I linked to at the top of this question states:
EF in general only supports exposing FK properties on your entities in
one:many relationships (unless the FK is also the PK). This is
somewhat artificial but a side effect of EF not supporting non-PK
unique constraints. We are working on support for unique constraints
for EF at the moment but it won't be there in our first RTM of Code
First.
Sorry not to have a better answer as there really isn't a workaround
at this stage.
Is this still the case or is there a way to resolve this. I have tried fluent api map to column and data annotations in all sorts of combinations without success.
use this code :
public class ClassPrinciple
{
public int ClassPrincipleID { get; set; }
public int ClassDependentId { get; set; }
[ForeignKey("ClassDependentId")]
[InverseProperty("ClassPrinciple")]
public virtual ClassDependent ClassDependent{ get; set; }
}
public class ClassDependent
{
public int ClassDependentID { get; set; }
[InverseProperty("ClassDependent")]
public virtual ClassPrinciple ClassPrinciple{ get; set; }
}

Entity Framework 5 inheritance generating as TPT instead of TPH?

I'm trying to implement various tables that inherent a groups table. When I generate the database from the model it comes out as type-per-table instead of type-per-inheritance like I would like.
I have:
Group set to abstract
Each group type table is mapped to Group conditionally when type(column) = a different int for each table
Can anyone point me in the right direction for what I need to do to change this to type per inheritance?
EDIT: by request via comment here is my db set for group, and none of the group types of a dbset
public DbSet<Group> Groups { get; set; }
Here are the generated classes:
Group:
public abstract partial class Group
{
public Group()
{
this.GroupHierarchies = new HashSet<GroupHierarchy>();
this.GroupHierarchies1 = new HashSet<GroupHierarchy>();
this.NetworkActions = new HashSet<NetworkAction>();
this.PermissionAssignments = new HashSet<PermissionAssignment>();
this.UserProfiles = new HashSet<UserProfile>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Acronym { get; set; }
public string Description { get; set; }
public Nullable<System.DateTime> CreatedDate { get; set; }
public virtual ICollection<GroupHierarchy> GroupHierarchies { get; set; }
public virtual ICollection<GroupHierarchy> GroupHierarchies1 { get; set; }
public virtual ICollection<NetworkAction> NetworkActions { get; set; }
public virtual ICollection<PermissionAssignment> PermissionAssignments { get; set; }
public virtual ICollection<UserProfile> UserProfiles { get; set; }
}
One of the group types:
public partial class HoaManagementCompany : Group
{
public string Address { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
The other group type, there will be many more in the future but only these two until I get it to work.
public partial class HoaAttorney : Group
{
public string Address { get; set; }
}
When I generate the database from the model...
Are you using Model-First strategy? This unfortunately would make it difficult to get TPH inheritance for your model (which would be easy for Code-First or Database-First strategy).
(Default inheritance mapping for Code-First is TPH, so you should not have your problem with Code-First.)
Out of the box TPH is not available with Model-First. The default inheritance strategy for Model-First is TPT and there is no easy way to switch to TPH in the model designer:
It is possible to map to a TPH inheritance using Model First but you
would have to write your own database generation workflow which is
complex. You would then assign this workflow to the Database
Generation Workflow property in the EF Designer. An easier alternative
is to use Code First.
There is an additional tool from Microsoft - the Entity Designer Database Generation Power Pack - which supports TPH database generation workflow for Model-First. But the problem is that it doesn't look very well maintained (last update from May 2012) and it doesn't support Visual Studio 2012. But if you use VS 2010 you can try it.
You should only use your Groups DBSet for TPH.
Also make sure you aren't adding Table annotations to the poco classes
Try following this blog, it worked for me in the past.
Inheritance with EF Code First: Part 1 – Table per Hierarchy (TPH).
Also talks about Table per Type (TPT) and Table per Concrete class (TPC) Inheritances.

EF Code First Muliple Inheritance is exclusive?

Let's say I have a model where I have the Person entity with general info (Names, Date of Birth, etc.), and two additional entities (Customer, Worker) which inherit from Person. As you see there is the option of having a Customer who CAN ALSO play the role of a Worker in the model. There is a way to design this in EF (I saw something about TPH, TPT and TPC) but I see that there is a use of discriminator which doesn't allow a Person table to include values for Worker and Customer "simultaneously".
I don't know if maybe I'm getting wrong with the general OOP concept of inheritance :S.
Thanks in advance for all your help.
You cant have multiple inheritance in .net, it is not supported (and the same applies to entity framework). You can implement multiple interfaces, but this is a slightly different notion - i.e. 'Worker' could be an interface that is implemented by some objects, such as customer
In entity framework, I believe that the discriminator is only implemented when using Table-per-hierarchy. This is where both child entities are stored in the same table, and the discriminator identifies which is which.
Table-per-type is essentially where the entities (person, customer, worker) are stored in different tables, but are accessible as single entities in your code (i.e. customer with an inheritance from person)
It may be that you need to create an interface (maybe IWorker), and create a class (maybe WorkerCustomer??) that inherits from Customer, and implements IWorker.
EDIT: 15/02/2013 19:00
Ok, so the below might be what you're looking for in terms of representing the data in a single table:
public class MyDbContext : DbContext
{
public MyDbContext() : base("TestDB")
{
}
public DbSet<Person> People { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Worker> Workers { get; set; }
public DbSet<WorkerCustomer> WorkerCustomers { get; set; }
}
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
}
public class Customer : Person
{
public string CustomerNumber { get; set; }
}
public interface IWorker
{
string WorkerNumber { get; set; }
}
public class Worker : Person, IWorker
{
public string WorkerNumber { get; set; }
}
public class WorkerCustomer : Customer
{
public string WorkerNumber { get; set; }
}