Entity Framework DB-First, implement inheritance - c#

I'm trying to implement inheritance using entity framework 6.0 and database first approach. OK, let's say I have a Person and an Organization entity like below:
// a simplified version of organization entity
public class Organization
{
public Guid ID { get; set; }
public string Nickname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string OfficialName { get; set; }
public Guid CEOID { get; set; }
public DateTime? RegisterDate { get; set; }
}
// a simplified version of person entity
public class Person
{
public Guid ID { get; set; }
public string Nickname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public Guid PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public string NationalCode { get; set; }
public DateTime? BirthDate { get; set; }
}
I can create these two tables in database, but I want to use inheritance so the fields which is repeated in both Person and Organization could be in another base class like below:
public class Identity
{
// These fields are the common fields between Person and Organization
public Guid ID { get; set; }
public string Nickname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
}
How can I achieve this in db-first approach?

One possible way is to use one table for each type called TPT (table-per-type), which I prefer to use. To achieve this, you define your tables like the model shown in the following picture:
Note that the relationships between child and base entity are one-to-one on their pk columns, and all common fields are moved to the base table. After creating your tables, right click on the models page in your visual studio, and select Update Model from Database..., and then in the add tab, select these 3 tables to add. At first you should see this model diagram, which needs to be changed a bit:
Do these steps for Person and Organization separately:
Right click on entity and select Properties
In the Base Type property select Identity
Select and then delete the association between this entity and Identity
Select and then Delete the PK (ID column) of this entity (Inherits from base entity)
After these steps save your model. Now your model should look like this:
Now compile your project and enjoy your life!
Additional resources:
Entity Framework Designer TPH Inheritance

Related

EF Core 2.2.6: Unable to map 2 foreign keys to the same table

I am having issues trying to map two fields that are foreign keys into the same table. The use case is for a modifier and creator. My class already has the Ids, and then I wanted to add the full User object as virtual.
I am using a base class so that each of my tables have the same audit fields:
public class Entity
{
public long? ModifiedById { get; set; }
public long CreatedById { get; set; } = 1;
[ForeignKey("CreatedById")]
public virtual User CreatedByUser { get; set; }
[ForeignKey("ModifiedById")]
public virtual User ModifiedByUser { get; set; }
}
The child class is very simple:
public class CircleUserSubscription : Entity
{
[Required]
public long Id { get; set; }
public long SponsorUserId { get; set; }
[ForeignKey("SponsorUserId")]
public virtual User User { get; set; }
public long TestId { get; set; }
[ForeignKey("TestId")]
public virtual User Test { get; set; }
}
This is a standard junction table.
When I try to generate the migration, I am getting errors that I don't understand fully.
Unable to determine the relationship represented by navigation property 'CircleUserSubscription.User' of type 'User'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
I tried what this answer had, but the code is basically the same: https://entityframeworkcore.com/knowledge-base/54418186/ef-core-2-2---two-foreign-keys-to-same-table
An inverse property doesn't make sense since every table will have a reference to the user table.
For reference, here is the User entity:
public class User : Entity
{
public long Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
I am hoping you all can help me out, TIA :)
EDIT: One thing to note, all of this worked fine when the entity class was as follows:
public class Entity
{
public long? ModifiedById { get; set; }
public long CreatedById { get; set; } = 1;
}
It was only after I added the entity that things went awry.

EF Core - Shared Primary Key Associations via Fluent API

I have an 'User'class contains two 'Address' properties reference to 'Address' entity, and there is another class - 'Shipment' also associate with 'Address'.
How i can use fluent api on ef core to build a correct relation between entities.
public class Address
{
public int AddressId { get; set; }
public string Street { get; set; }
...
}
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public virtual Address DefaultAddress { get; set; }
public virtual Address BillingAddress { get; set; }
}
public class Shipment
{
public int ShipmentId { get; set; }
public virtual Address DeliveryAddress { get; set; }
}
There is not need to declare explicit configuration, the EF will do everything without any help.
I prepared a working example with and without fluent configuration, you can check it out here. Just switch between commits to see the difference.
As you can notice, there is no differences in generated migration.

Different behavior across domain objects for same properties in Entity Framework Core 2

I am new to .NET Core and using EF Core 2
My domain objects are all derived from a base class with some audit fields on it that get set on SaveChanges as needed:
(Simplified below)
public abstract class AuditableEntity
{
public DateTime CreatedOn { get; set; }
[ForeignKey("CreatedBy")]
public Guid? CreatedByWebUserId { get; set; }
public WebUser CreatedBy { get; set; }
public DateTime? UpdatedOn { get; set; }
[ForeignKey("UpdatedBy")]
public Guid? UpdatedByWebUserId { get; set; }
public WebUser UpdatedBy { get; set; }
public DateTime? DeletedOn { get; set; }
[ForeignKey("DeletedBy")]
public Guid? DeletedByWebUserId { get; set; }
public WebUser DeletedBy { get; set; }
}
On add-migration, I get the error:
Unable to determine the relationship represented by navigation property
'Address.CreatedBy' of type 'WebUser'. Either manually configure the
relationship, or ignore this property using the '[NotMapped]' attribute or by
using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Address is one of the classes derived from AuditableEntity:
(Simplified below)
public class Address : AuditableEntity
{
public Guid Id { get; set; }
public string Nickname { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string City { get; set; }
public string StateProvince { get; set; }
public string PostalCode { get; set; }
public string CountryCode { get; set; }
public decimal Latitude { get; set; }
public decimal Longitude { get; set; }
}
However, I have several objects that use the same "agent and timestamp" pair pattern similar to the above that work just fine such as:
public DateTime? VerifiedOn { get; set; }
[ForeignKey("VerifiedBy")]
public Guid? VerifiedByWebUserId { get; set; }
public WebUser VerifiedBy { get; set; }
The error always comes from Address, and if I remove the base class from Address everything works fine (meaning, these fields get successfully applied to my 15+ other domain objects).
The issue seemingly stems from WebUser having a reference to Address:
(Simplified below)
public class WebUser : AuditableEntity
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string Phone1 { get; set; }
public string Phone1Type { get; set; }
public string Phone2 { get; set; }
public string Phone2Type { get; set; }
[ForeignKey("AddressId")]
public Address Address { get; set; }
public Guid? AddressId { get; set; }
}
What is the correct way of creating these references prioritizing keeping the FK constraints (over keeping the ability to navigate)?
The problem is unrelated to the usage of a base class (the same will happen if you remove the base class, but copy its properties to Address class), but the multiple cross references between the two classes.
By convention EF Core tries to automatically "pair" navigation properties of the two entities in order to form a single relationship, which succeeds in most of the cases. However in this case the WebUser has Address type navigation property and Address class has WebUser type navigation property (actually 3).
Since all they have associated FK property via ForeignKey data annotation, EF Core should be able to correctly identify them as different one-to-many relationships, but it doesn't. Not only it fails with the exception in question, but also doesn't create FK relationships for the WebUser.
Everything works correctly if the base class contains only 1 WebUser type of navigation property, so I'm assuming thet unfortunately you are hitting some current EF Core bug.
As a workaround until they fixed it, I would suggest explicitly configuring the problematic relationships using fluent API, by overriding the OnModelCreating and adding the following code:
var auditableEntityTypes = modelBuilder.Model.GetEntityTypes().Where(t => t.ClrType.IsSubclassOf(typeof(AuditableEntity)));
var webUserNavigations = new[] { nameof(AuditableEntity.CreatedBy), nameof(AuditableEntity.DeletedBy), nameof(AuditableEntity.UpdatedBy) };
foreach (var entityType in auditableEntityTypes)
{
modelBuilder.Entity(entityType.ClrType, builder =>
{
foreach (var webUserNavigation in webUserNavigations)
builder.HasOne(typeof(WebUser), webUserNavigation).WithMany();
});
}
i.e. for each entity class that derives from AuditableEntity we explicitly configure the 3 WebUser reference navigation properties to be mapped to 3 separate one-to-many relationships with no inverse collection navigation properties. Once we do that, EF Core has no problem to correctly map the WebUser.Address FK association.

Entity Framework: how to solve table in table

I'm testing the following scenario (code below): create a shopping mall, with shops, with cashiers, with cash desks and persons operating it.
So I've created the following classes
Table Mall
Table Shop
Table CashDesk
Table Person
Basic classes with an ID and a name.
Then I need a derived class from Person being PersonCashier or PersonCustomer.
Since everything is related, I need to create intersection tables for the many to many scenarios.
I've created the following intersection tables:
The Mall contains shops: MallShop
The shop contains CashDesks: MallShopCashDesk
And the CashDesk containing cashiers: MallShopCashDeskPersonCashier
This just doesnt feel right. Can anyone help me out on a best practice
public class Mall
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
[Required]
public int NumberOfShopSpaces { get; set; }
}
public class Shop
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
}
public class CashDesk
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
public class Person
{
public int Id { get; set; }
[Required]
public string FullName { get; set; }
[Required]
public string Gender { get; set; }
}
public class PersonCashier : Person
{
[Required]
public int ShopId { get; set; }
public virtual Shop Shop { get; set; }
}
These are the base classes. How should i add a shop to a mall, a cashdesk to a shop, and a cashier to a cashdesk? (i've tried alot but posting the code would make the question look like spaghetti)
If this is code first then EF will make the relationship tables for you. You just need to add the relationships in your classes.
Exactly what they should be is not clear from your description because it depends on what relationships you want; if you want a many-many between PersonCashier and CashDesk it would be like this:
public class CashDesk
{
public List<PersonCashier> Cashiers { get; set; }
}
public class PersonCashier : Person
{
public List<CashDesk> CashDesks { get; set; }
}
(not showing all the fields for brevity)
public class Mall
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
[Required]
public int NumberOfShopSpaces { get; set; }
List<Shop> CurrentShops { get; set; }
}
public class Shop
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
public List<CashDesk> CashDesks { get;set; }
}
I added the list of shops to the Mall class and CashDesks to Shops. This gives you a list of shops in the mall, and a list of cashdesks in each shop and you can follow this method for everything else you need.
If you have a database, you will have a Mall table and a Shop table.
The Shop table can have a Foreign Key to the Mall table, that's how you link them and this will work with the class structure at the top.
Or, you can have another table called MallShops where you have 2 fields, one being the MallID, the other the ShopID. This is called a Link table.
Both approaches will work with the second allowing a more complex structure with lots of Malls linked to Lots of Shops.
I would start with the database structure first, make sure you cover all you need, then you can do the classes etc in a way that makes sense. If you use something like EntityFramework then it will create all the classes for you once your database structure is ready.

EF Code first - Lazy Loading How to set up and access the joining table

public class Product
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public bool IsInStock { get; set; }
public string ImageUrl { get; set; }
public List<ProductOption> ProductOptions { get; set; }
public virtual Category Category { get; set; }
}
public class ProductOption
{
[Key]
public int Id { get; set; }
public string ProductOptionName { get; set; }
public string ProductOptionDescription { get; set; }
}
Now I know when your using Code First EF, so that the tables are created correctly. You need to do something like this.
modelBuilder.Entity<Product>().HasMany(p => p.ProductOptions).WithMany().Map(m =>
{
m.MapLeftKey("ProductId").MapRightKey("ProductOptionId").ToTable("SelectedProductOptionsInOrderedItem");
});
So....
Does this mean that if I do something like Product.ProductOptions I will be able to access all associated productoptions.
Is this the best way to set it up, or is there another way?
To enable lazy load and EF can create derived proxy types for your collection, that property should be declared this way:
public virtual ICollection<ProductOptions> ProductOptions { get; set; }
That should be enought. Other aspect is the mapping approach that you use. You choose fluent api, i prefer mapping by convention, but that is a matter of personal taste anyway.
Ok, Mapping by Conventions:
Is the ability of EF that from the name of entities and their properties along with their types, to map our model with the underlying data without providing any other information.
for example
public class Customer {
public long CustomerID {get; September;}
public string CustomerName {get; September;}
public Employee AssignedTo {get; September;}
}
With the previous model EF will map database with a table named Customer with:
. CustomerID bigint primary key column
. CustomerName nvarchar column
. Customer_EmployeeID foreign key to Employee table, with the datatype Corresponding to EmployeeID in that table.
You can read more Here

Categories