C# EF Core Models independent FK - entity-framework

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

Related

EF Core deserialization of a database item into various types

I have a range of entities which have a series of common properties being a informationItem and a series of specific entity properties which I intend to store as JSON property Detail in SQL Server; e.g.
public class InformationItem
{
public Guid InformationItemId { get; set; }
public InformationItemType Type { get; set; }
public string Detail { get; set; }
}
InformationItemType is from an enum with a collection of project elements such as Assumptions, benefits, constraints, tasks etc.. There are some 20 object types which specific specific Detail properties.
I don't want to have 20+ entity specific tables in the database but what I would like to do is 'inflate' them into their real concrete classes of Assumption, Benefit, Constraint, Task etc deserializing the JSON Detail into the specific properties of the type when entity framework does its 'magic'.
the main reason I am after this is that each entity will have lost of many to many relationships associated with them such as their relationship to a user etc and I don't wat the user to end up with multiple virtual ICollections of the various types.
Is it possible to store InformationItems with a specific type identifier and Detail JSON information in the database and have EFCore return these as their concrete class types?

Entity Framework dynamic fields

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.

Entity Framework Database First - Map to Generic List

I'm using Entity Framework - Database First in my project. My model has a view with only one VARCHAR column:
CREATE VIEW MyView
AS
SELECT 'Eris' Eris
FROM MyTable
By default, this view gets mapped to its own entity with just one property:
public virtual DbSet<MyView> MyViews { get; set; }
How can I change this so that the view gets mapped to a List of strings instead:
public virtual List<string> Eris { get; set; }
Unfortunately EF does not support mapping collections of value types. If you really want to implement this scenario then you might want to look into other ORM frameworks that have this feature like NHibernate.
If that's not an option and you have to stick to EF then you're forced to create an entity with one property like you mentioned in your question.
The entity model represents one element in the table.
When you retrieve data from the table, you will get a list of entity model objects.

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;

Is there a known issue with Code First default mapping on a table with 'Statuses' suffix

I have the following code in my context, and no explicit table-class mapping, yet my database keeps getting created (by my DropCreateDatabaseIfModelChanges initializer) with an EmployeeStatus table, not EmployeeStatuses. Is there a known issue with this, or am I going insane or what?
public DbSet<Department> Departments { get; set; }
public DbSet<EmployeeStatus> EmployeeStatuses { get; set; }
All my other tables are named exactly after their DbSet names, as I expect.
Entity Framework uses its pluralization service to infer database table names based on
the class names in the model—Destination becomes Destinations, Person becomes
People, etc. By convention, Code First will do its best to pluralize the class name and use the results as the name of the table. However, it might not be the same as your
table naming conventions.
You can use the Table Data Annotation to ensure that Code First maps your class to
the correct table name.