Entity Framework dynamic fields - entity-framework

I am designing a data entry application using EntityFramework (Code First) to collect customers detail.
The data structure required is simple.
Customer entity has some flat and one-to-many details (eg. Name, Telephone Numbers, etc.) and then a large number of many-to-many properties which always follow the same pattern which allow for multiple choice from a list (in the UI, this would be shown as a checkbox list), which the user can also add items to. For each of these multiple choice properties, there is also one Notes property that allow the user to explain why these details where connected to the customer (in other words, this is just a string within the Customer entity).
Because of the similarity of these properties and the relative simplicity of the data, I started looking to model using inheritance however I am now thinking that perhaps there are better ways to achieve this, especially because there would be a major benefit if the system allowed an admin user to add a new property of this type dynamically.
I am looking for any suggestions to achieve this without having to define and connect all the entities manually or at least to minimize the amount of code required to do so.

SQL does not know the concept of inheritance. However there are several strategies to let entity framework accept your inherited classes. Which strategy you ought to use depends on the type of queries you will ask most.
Suppose you have two classes Student and Teacher. Both classes have a lot of properties (and possibly methods) in common. You'd like to put them in a base class: Person. You don't want to be able to create a Person object, so your Person class will be abstract.
In C#:
abstract class Person
{
... // common person properties
}
class Teacher : Person
{
public int Id {get; set;} // Primary Key
... // Teacher properties
}
class Student : Person
{
public int Id {get; set;} // Primary Key
... // Student properties
}
You are not planning to create Person objects, only Teachers and Students. Therefore you could create a Teachers table and a Students table. The Teachers table contains all Teacher properties plus all Person properties. Similarly a Student table contains the Student properties and all Person properties. For every concrete (= non-abstract) class you create a table.
This strategy is called Table-Per-Concrete-Class (TPC). It is very similar to a composition: a Teacher 'has' Person properties, instead of inherits Person properties. It follows the old adagium "Favour composition over inheritance"
You inform entity framework that you want this strategy in your DbContext
class MyDbContext : DbContext
{
public DbSet<Student> Students {get; set;}
public DbSet<Teacher> Teachers {get; set;}
// we don't want a Person table, so no DbSet<Person>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Tell entity framework that the Teacher Table will include Person properties
modelBuilder.Entity<Teacher>().Map(m =>
{
m.MapInheritedProperties();
});
// Tell entity framework that the Student table will include Person properties
modelBuilder.Entity<Student>().Map(m =>
{
m.MapInheritedProperties();
});
}
}
A query for "Give me all Teachers who ..." or "Give me the Students that ..." will involve one table. However, if you ask: "Give me all Persons that ..." will require a concat of two tables.
var result = myDbContext.Teachers.Cast<Person>()
.Concat(myDbContext.Students.Cast<Person>())
.Where(person => ... // something with Person properties)
.Select(person => ... // something with Person properties);
Whenever I need to model inheritence, I use this TPC strategy most of the time.
If you think that you'll be querying quite often for Persons that ... instead of Teachers who ..., consider using Table Per Type (TPT)
In TPT you'll have three tables: a Person table, containing all the Person properties, a Teacher table with the Teacher properties and a foreign key to the Person properties of this Teacher. Similarly you'll have a Student table with a foreign key to the Person properties it inherits.
Asking for "all Persons that ..." will only involve one table, whether the Person is a Student or a Teacher. Because you ask for Persons, you don't want any Student properties.
Asking for "all Teachers that ..." will always involve two tables, namely the Teacher table to get the Teacher properties and the Person table to access the Person properties.
So if you ask more often for "Persons who ..." that for "Teachers who ...", consider using TPT.

Related

C# EF Core Models independent FK

I have a table for a spot. This spot can have different categories. For example: restaurant, hotel, park.
Each individual category has additional and different details.
My questions now:
Is it possible to create a column (FK) in the spot table that is independent of the category?
I mean when I create a new spot with the category Resteraunt and the next one is with the category park.
If I now select a spot that I get the correct data from the respective spot category
And if possible, how can I write that in C# EF Core Models?
Or is this not possible and i have to take another way?
Thanks in advance
Database Design for Example:
No, you can't have an FK column that is independent of the table it's referencing (the manifestations of your category). What you're looking for is inheritance. EF Core 5 currently supports two types of inheritance - Table Per Hierarcy (TPH) and Table Per Type. Either can work for your purposes.
First, you implement your entity Classe in an inheritance hierarchy to be mapped to the database with the derived classes representing the "category" of Spot:
public abstract class Spot
{
public Guid Id { get; set; }
// common spot properties here
// (i.e. props shared by different types of spots like address)
}
public class Restaurant : Spot
{
// restaurant specific properties here
}
public class Park : Spot
{
// park specific properties here
}
Then you map the entities in one of two ways - either to a single table (TPH) which will use a discriminator column to type each record in the DB (would be considered your category) and each property for the derived types would also be included but only populated for the specific type of record (i.e. Park properties will be null when the record in the DB represents a Restaurant and vice versa). Faster query performance with this method but all type-specific columns must be nullable and this implementation violates 3NF.
TPH is the default inheritance implementation but you can configure how it handles the discriminator like any other property (name, datatype) and specify the values to use for each derived type:
modelBuilder.Entity<Spot>()
.HasDescriminator("Category") // name it what you want
.HasValue<Restaurant>("R") // value for restaurants
.HasValue<Park>("P") // value for parks
;
In TPT each type in the inheritance hierarchy is mapped to its own table which contains their specific properties. The tables for the derived types use shared primary keys to reference their corresponding Spot record. Query performance can be slower and while it doesn't violate 3NF, its possible for manual data manipulation operations to mess things up (e.g. a Park and a Restaurant can reference the same Spot record).
For this configuration, merely map each type in the entity hierarchy to its own table:
modelBulider.Entity<Restaurant>().ToTable("Restaurant");
modelBuilder.Entity<Park>().ToTable("Park");
For both implementations, you can implement the DbSet properties normally:
public DbSet<Spot> Spots { get; set; }
public DbSet<Restaurant> Restaurants { get; set; }
public DbSet<Park> Parks { get; set; }
You can get specific types of Spots from Spots by using .OfType<T>()
var parks = dbContext.Spots.OfType<Park>();
So you do not need the Restaurants or Parks DbSet<T>s if you include the Spots DbSet<T>. Alternatively, Spots is optional if you include DbSet<T>s for the derived types.
I encourage you to model your entities both ways to see how EF models the DB and choose which you prefer.
https://learn.microsoft.com/en-us/ef/core/modeling/inheritance

Why collections of owned types generate an Id and how can I avoid that?

As Microsoft says here :
Owned entities are essentially a part of the owner and cannot exist without it, they are conceptually similar to aggregates
It means in DDD architecture (Domain Driven Design) we can use owned types (or collection of owned types ) as an entity inside an aggregation or as a value object. On the other hand, we know that ValueObject in DDD has no Identity value because of its structure and its Immutability. I want to know if I decide to use the Owned type to implement the value object how can I force it to avoid making Id in the creation table?
For example, as you see in the following picture (that Microsoft mentioned here) when we use a collection of owned types, EF makes an "Id" field in the table that no sense in Address Value Object! How to avoid it? and Is it really a correct option?
That example is from the OwnsMany scenario where it clearly explains that it needs a FK in the table to associate addresses back to their Distributor. How else would an Address record associate back to the Distriburor when loading the entities?
If a Distributor only has 0-1 address then you don't need an OwnerId on Address, the Address' Id column would serve as PK and FK back to the Distributor. EF needs a "Key" on each table to uniquely identify each row. You could possibly avoid an "Id" column by mapping a composite key, essentially:
public class Address
{
[Key, Column(0), ForeignKey("Owner")]
public int OwnerId { get; set; }
[Key, Column(1)]
public string Street { get; set; }
[Key, Column(2)]
public string City { get; set; }
public virtual Distributor Owner { get; set; }
}
A dedicated unique, and DB Generated Id column for the Address table IMO makes more sense than a large, composite key of strings and FK.
Ownership as far as the database is concerned is identical to HasOne / HasMany in the way the schema is laid out & relational rules. What differentiates OwnsMany from HasMany is how EF will allow you to access those owned entities. You cannot have a DbSet<Address>, only access Addresses through it's Distributor. It serves no real purpose except to scratch particular design pattern itches. :)

Entity Framework Core Many-to-Many Implementation

So Many-to-Many did not make it into .NET Core 3.0 release, bummer...
I'm aware of how to implement m:m using a join entity as in this example: https://stackoverflow.com/a/53972658/980917
My question is about the model classes themselves. Consider Student & Class example:
Student - has ICollection<StudentClass>
StudentClass - joining entity
Class - has ICollection<StudentClass>
That is fine for data loading purposes. But in your business logic collection of StudentClasses is not useful as its just a joining entity with 2 IDs. When working with Student or Class you actually want an actual collection of Classes inside a Student and a collection of Students inside a Class. (ie: Student.Classes & Class.Students.
What's the current recommended approach / workaround to retrieve the many-to-many collections (not joining entity)?
Do we have to do a 2nd select based on on the joining entity or is there something more elegant?
A quick example or a link would be great. Thanks.
What's the current recommended approach / workaround to retrieve the many-to-many collections (not joining entity)?
You can do easily with .Include extension method as follows:
Lets say your Student class as follows:
public class Student
{
public int Id {get; set;}
public string StudentName {get; set;}
public ICollection<StudentClass> StudentClasses {get; set;}
}
To retrieve all the students with their associated classes:
var studentsWithClasses = _context.Students.Include(s => s.StudentClasses).ToList();
To retrieve a single student with its classes:
var studentWithClasses = _context.Students.Where(s => s.Id = studentId).Include(s => s.StudentClasses).FirstOrDefault();

Is it possible to map a property based on a criteria in EF Code First?

I have an Employee entity that has multiple Unit entities they are associated too in various ways (e.g. as a Manager, Supervisor, etc...). What their relationships are to a Unit are defined in a UnitRelationships table that includes an IsActive attribute in addition to EmployeeId, UnitId, and RelationshipTypeId attributes.
What I'd like to do is create specific properties on the Employee entity as such:
SupervisedUnits {get;set;}
ManagedUnits{get;set;}
... and configure the mapping so that "SupervisedUnits" returns only the Units where an active (IsActive = true) relationship exists and the RelationshipTypeId='Supervisor'. Same type of thing with ManagedUnits.
So, is there a way to do this in EF Code First so that I can actually use these properties (SupervisedUnits and ManagedUnits) in my LINQ-to-Entities queries?
Thanks
There are probably a couple of ways of doing this.
The "Active" relationship is a one to many between Unit and Employee and the "Inactive" relationship is many-to-many. So set them up as two different relationships and when the relationship is made inactive, move the Supervisor into the InactiveSupervisors. Doing it this way gives you a SupervisedUnits navigation property (as you wanted) and an InActiveSupervisedUnits property
The relationship is many-to-many and has attributes such as IsActive, ActiveFrom and ActiveTo. Now the relationship really needs to be an entity. You could use inheritance on your UnitRelationship entity:
public class SupervisorUnitRelationShip : UnitRelationship{ }
public class ManagerUnitRelationShip : UnitRelationship
{
}
then you can add your navigation properties by type:
public virtual Collection<SupervisorUnitRelationship> SupervisedRelations {get;set;}
public virtual Collection<ManagerUnitRelationship> ManagedRelations {get;set;}
to get to the Units you'll have to go through the UnitRelationship
var SupervisedUnits = SupervisedRelations.Units;

Entity Framework Code First - Table Per Type Inheritance - Insertion Issue?

I'm having an issue inserting an instance of a subclass that inherits from a base class.
Consider the following code snippets from these POCOs:
public abstract class EntityBase<T>
{
private T _id;
[Key]
public T ID
{
// get and set details ommitted.
}
}
public abstract class PersonBase<T> : EntityBase<T>
{
// Details ommited.
}
public class Applicant : PersonBase<int>
{
// Details ommitted for brevity.
}
public class Employee : Applicant {}
Pretty standard inheritance right now. In our system, when an applicant finally becomes an employee, we collect extra data. If not hired, they remain an applicant with a limited set of information.
Now consider the fluent API that sets up the table-per-type inheritance model:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Set up table per type object mapping for the Visitor Hierarchy.
modelBuilder.Entity<Employee>().ToTable("Employees");
}
So far, so good...
If I look at the database this creates, I have a table named Applicants with an Id column of type int, auto-incrementing ID and I have an Employees table with an ID field as the primary key (non auto incrementing).
Basically, the ID field in the Employees table is a foreign key to the Applicants table.
This is what I want. I don't want a record into the Employees table corresponding to the Applicants table until they actually become an Employee.
The problem comes when I try to insert an Employee which comes down to this code:
public void PersistCreationOf(T entity)
{
DataContextFactory.GetDataContext().Set(typeof(T)).Add(entity);
}
The problem: It inserts a brand new applicant and Employee. I hooked it up to the Sql Profiler and looked at both insert queries that come down.
I want to just insert the Employee record with the ID it already has (the foreign key from the Visitors table).
I understand by default it needs to this: Obviously if you create a subclass and insert it, it needs to insert into both tables.
My question is is possible to tell the Framework - the base table already has information - just insert into the child table?
Thanks in advance!
Aside from sending raw SQL commands to insert the Employee minus Applicant properties fragment into the Employees table I believe it's impossible. You can either update or insert an entity. What you want is basically to update the base part of the Employee (or do nothing if nothing changed) and insert the derived part which is not possible.
Imagine what an ORM does: It maps key identities in the database to object identities in memory. Even in memory you couldn't achieve what you want: If you have an object in memory which is a Applicant, it is always an applicant. You cannot magically "upgrade" it to an Employee. You would have to create a new object of type Employee, copy the properties of the Applicant into the base properties of your new Employee and then delete the Applicant. The result is a new object with a new object identity.
I think you have to follow the same procedure in EF. Your Employee will be a new entity with new rows in both Applicant and Employee table and you need to delete the old Applicant. If you have autogenerated keys it will be a new identity with a new ID. (If you hadn't autogenerated keys you could supply the old ID again after deleting the old Applicant, thus "faking" an unchanged identity.) This will of course create big potential trouble if you have references to the old applicant with FK constraints.
Perhaps inheritance is not optimal for this scenario to "upgrade" an applicant into an employee. An optional navigation property (1-to-0...1 relationship) inside of the Applicant which refers to another entity containing the additional properties which make the applicant an employee would solve the problem. This navigation property could be set or not, letting you distinguish between an applicant and applicant which is also an employee. And you would not need to delete and change the ID of the applicant when you make it an employee.
(As said, "I believe". Maybe there is a hidden way, I didn't see.)