Entity Framework data modelling best practices - c#

I have Entity structure like below
public abstract class Entity
{
public int Id { get; set; }
}
public class User : Entity
{
public ICollection<Product> Products { get; set; }
}
public class Warehouse : Entity
{
public ICollection<Product> Products { get; set; }
}
public class Product : Entity
{
public Warehouse Warehouse { get; set; }
public User User { get; set; }
}
As you can see User can has products and Warehouse also can have products. So Entity framework put 2 foreign keys over Product table that can be nullable.
We could also achieve similiar structure by bit of different entity modelling like below
public class User : Entity
{
public ICollection<UserProduct> Products { get; set; }
}
public class Warehouse : Entity
{
public ICollection<WarehouseProduct> Products { get; set; }
}
public class Product : Entity
{
}
public class WarehouseProduct : Entity
{
public Product Product { get; set; }
public Warehouse Warehouse { get; set; }
}
public class UserProduct : Entity
{
public Product Product { get; set; }
public User user { get; set; }
}
First Design look simpler without introduce new entitties but not sure that it is better or not.
I am trying to find which is best or which circumtances makes one of it better than other.

Inheritance would also be possible (EF/CodeFirst):
public abstract class Entity
{
public int Id { get; set; }
}
public class Product : Entity
{
}
public class Warehouse : Product
{
/* all product fields are available */
}
public class User : Product
{
/* all product fields are available */
}
this is more DRY in my point of view => "CodeFirst view".
good post about Inheritance: http://goo.gl/1igQ3

Related

Entity Framework Inheritance relational tables

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.

Problems with Data modeling and / or Querying a TPT model in Entity Framework

I have an abstract object which have two list of abstract objects in it. The Model is being created and the Database looks fine, but I am unable to make the queries I would expect.
The Data model looks something like this
public abstract class Vehicle
{
protected Vehicle()
{
this.CrashIncidents = new List<Incident>();
this.SpeedingIncidents = new List<Incident>();
}
[Key]
public int Id { get; set; }
public virtual ICollection<Incident> CrashIncidents { get; set; }
public virtual ICollection<Incident> SpeedingIncidents { get; set; }
}
public class Car : Vehicle
{
public string Color { get; set; }
}
public class Lorry : Vehicle
{
public int MaxCarryWeight { get; set; }
}
public abstract class Incident
{
[Key]
public int Id { get; set; }
public virtual ICollection<Incident> VehicleCrashIncidents { get; set; }
public virtual ICollection<Incident> VehicleSpeedingIncidents { get; set; }
}
public class CrashIncident : Incident
{
public string Severity { get; set; }
}
public class SpeedingIncident : Incident
{
public string MPHRegistered { get; set; }
}
Any my OnModelCreating in the Context class looks something like this
modelBuilder.Entity<Vehicle>().HasMany<Incident>(o => o.CrashIncident).WithMany(a => a.VehicleCrashIncidents).Map(m => m.MapLeftKey("Id").MapRightKey("VehicleCrashIncidentId").ToTable("VehicleCrashIncident"));
modelBuilder.Entity<Vehicle>().HasMany<Incident>(o => o.SpeedingIncident).WithMany(a => a.VehicleSpeedingIncidents).Map(m => m.MapLeftKey("Id").MapRightKey("VehicleSpeedingIncidentId").ToTable("VehicleSpeedingIncident"));
modelBuilder.Entity<CrashIncident>().ToTable("CrashIncident");
modelBuilder.Entity<SpeedingIncident>().ToTable("SpeedingIncident");
However I am unable to query things like: Get all Vehicles (or concrete classes) with an Incident of Severity of X i.e. something like this:
var problems = context.Vehicle.Where(x => x.CrashIncidents.Any(y => y.Severity == "High");
The problem is the last part of the query (in the y-part) where I am not able to select Severity, only the Properties of the Abstract class is visible. I am unable to determine (and thus Google) if the problem lies with my Data model or with my Query.
Inspired by this:
Issue with many-to-many relationship + TPH inhertitance in Entity Framework 6
I got this working. I removed the specific virtual parts from the Abstract part of my model like this:
public abstract class Vehicle
{
protected Vehicle()
{
this.Incidents= new List<Incident>();
}
[Key]
public int Id { get; set; }
public virtual ICollection<Incident> Incidents{ get; set; }
}
And changed the navigation properties to
public abstract class Incident
{
[Key]
public int Id { get; set; }
public virtual ICollection<Incident> VehicleIncidents { get; set; }
}
In the OnModelCreating I could remove the two "modelBuilder.Entity().HasMany" lines. In the end I could execute this query:
var problems = context.Vehicle.Where(x => x.Incidents.OfType<CrashIncidents>.Any(y => y.Severity == "High");
I must admit I am unsure of whether I tried that specific query before, so I am not sure if it is the changes in my Data model that allowed me to make that query or it was possible all along and I just didn't know it.

Entity Framework: Model for Categories/Products relation

I have following model of data for Entity Framework.
I have abstract Product. Every Product relates with one Category of products. For example:
public abstract class Product
{
public int Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
And there are concrete products:
public class ConcreteProduct1 : Product
{
// some specific member
}
public class ConcreteProduct2 : Product
{
// some specific member
}
//etc.
I have hierarchical Categories, for example:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public Category Parent { get; set; }
public ICollection<Category> Children { get; set; }
public ICollection<Product> Products { get; set; }
}
Every Category has ICollection<Product> Products.
Problem: Category should be related with only products some concrete product type. I.e. I need be able get Concrete Products into Category, for example:
public Category<ConcreteProduct1> GetCategory<ConcreteProduct1> ()
{
// should return Category that contain ICollection<ConcreteProduct1>
}
How I can describe this restriction in my Entity Framework model? Or may be there are some best practice for building these relations?
It is hard to answer because it depends too much on the requisites of your project.
There are three different approaches to representing an inheritance hierarchy:
Table per Hierarchy (TPH): Enable polymorphism by denormalizing the SQL schema, and utilize a type discriminator column that holds type information.
Table per Type (TPT): Represent "is a" (inheritance) relationships as "has a" (foreign key) relationships.
Table per Concrete class (TPC): Discard polymorphism and inheritance relationships completely from the SQL schema.
You should check the links and find what is the best suitable model for what you need.
Use lists and a categorymanager:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public Category Parent { get; set; }
public ICollection<Category> Children { get; set; }
public List<Product> Products = new List<Product>();
}
public static class CategoryManager
{
public List<Category> Categories = new List<Category>();
}
public Product test = new Product
{
Id = 1
};
public Category add = new Category
{
Id = 1
};
public void Init()
{
add.Products.Add(test);
CategoryManager.Categories.Add(add);
}
public Product GetByID(Category cat, string val)
{
return cat.Where(x => x.Id == val).ToArray()[0];
}
public Category GetCat(Product pro)
{
foreach (var cat in CategoryManager.Categories)
{
if (cat == pro) return cat;
}
return null;
}

Entity Framework: Table that holds common data for different multiply entities

I have got a lot of tables in my DB and one that needs to hold common data for all of them. How can I map these tables using EntityFramework CodeFirst so I could Lazy or Eager load such data using normal queries?
Here is what I mean:
Entities:
public class Table1
{
[Key]
public int Id { get; set; }
...etc data
}
public class Table2
{
[Key]
public Guid Id { get; set; }
...etc data
}
public class Table3
{
[Key]
public string Id { get; set; }
...etc data
}
And a table with common data for all of them:
public class CommonDataTable
{
[Key]
public Guid Id { get; set; }
}
Normally I would map this by creating a table like:
public class CommonDataMappingTable
{
[Key]
public Guid Id { get; set; }
public string Mask { get; set; } // like: typeof(Table3).Name
public string MappedID { get; set; } // like: Table3.Id.ToString()
public virtual CommonDataTable MappedData { get; set; }
}
But as soon as I am new to Entity Framework I just do not know how to map such relation in EF CodeFirst model.
Thank you for your time and sorry for my bad English.

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