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

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;

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

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.

Want Entity Framework 6.1 eager loading to load only first level

I am not sure I am approaching wrong way or it is a default behaviour but it is not working the way I am expecting ...
Here are two sample classes ...
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Department Department { get; set; }
}
Second one is Department
public class Department
{
public string Name { get; set; }
public List<Person> People { get; set; }
}
Context Configuration
public MyDbContext() : base("DefaultConnection")
{
this.Configuration.ProxyCreationEnabled = false;
this.Configuration.LazyLoadingEnabled = false;
}
public DbSet<Person> People { get; set; }
public DbSet<Department> Departments { get; set; }
I am try to load people where last name is from 'Smith'
var foundPeople
= context
.people
.Where(p => p.LastName == "Smith");
Above query load foundPeople with just FirstName and LastName no Department object. It is a correct behaviour as my LazyLoading is off. And that was expected as well.
Now in another query with Eager loading Department,
var foundPeople
= context
.people
.Where(p => p.LastName == "Smith")
.Include(p => p.Department);
Above query loads foundPeople with FirstName, LastName, Department with Department->Name as well as Deparment->People (all people in that department, which I dont want, I just want to load first level of the Included property.
I dont know is this an intended behaviour or I have made some mistake.
Is there any way to just load first level of Included property rather then complete graph or all levels of included property.
Using Include() to achieve eager loading only works if lazy loading is enabled on your objects--that is, your navigation properties must be declared as virtual, so that the EF proxies can override them with the lazy-loading behavior. Otherwise, they will eagerly load automatically and the Include() will have no effect.
Once you declare Person.Department and Department.People as virtual properties, your code should work as expected.
Very sorry, my original answer was wholly incorrect in the main. I didn't read your question closely enough and was incorrect in fact on the eager behavior. Not sure what I was thinking (or who upvoted?). Real answer below the fold:
Using the example model you posted (with necessary modifications: keys for the entities and removed "this" from context constructor) I was unable to exactly reproduce your issue. But I don't think it's doing what you think it's doing.
When you eagerly load the Department (or explicitly load, using context.Entry(...).Reference(...).Load()) inspect your results more closely: there are elements in the Department.People collections, but not all the Persons, only the Persons that were loaded in the query itself. I think you'll find, on your last snippet, that !foundPeople.SelectMany(p => p.Department.People).Any(p => p.LastName != "Smith") == true. That is, none of them are not "Smith".
I don't think there's any way around this. Entity Framework isn't explicitly or eagerly loading People collections (you could Include(p => p.Department.People) for that). It's just linking the ones that were loaded to their related object, because of the circular relationship in the model. Further, if there are multiple queries on the same context that load other Persons, they will also be linked into the object graph.
(An aside: in this simplified case, the proxy-creation and lazy-loading configurations are superfluous--neither are enabled on the entities by virtue of the fact that neither have lazy or proxy-able (virtual) properties--the one thing I did get right the first time around.)
By desing, DbContext does what it's called "relationship fix-up". As your model has information on which are the relations between your entities, whenever an entity is attached, or modified, in the context, EF will try to "fix-up" the relations between entities.
For example, if you load in the context an entity with a FK that indicates that it's a children of another entity already attached to the context, it will be added to the children collection of the existing entity. If you make any chages (change FK, delete entity, etc.) the relationships will be automatically fixed up. That's what the other answer explains: even if you load the related entities separatedly, with a different query, they'll be attached to the children collection they belong to.
This functionality cannot be disabled. See other questions related to this:
AsNoTracking and Relationship Fix-Up
Is it possible to enable relationship fixup when change tracking is disabled but proxies are generated
How to get rid of the related entities
I don't know what you need to do, but with the current version of EF you have to detach the entity from the context and manually remove the related entities.
Another option is to map using AutoMapper or ValueInjecter, to get rid of the relationship fix-up.
You could try using a LINQ query so you can select only the fields that you need. I hope that helps.

EF Code First One-To-One with Join Table

I am trying to configure my model to an existing database, and am running into a problem. The previous developer modeled a one-to-one relationship using a join table. If I have the following classes and database structure below, how can I map this using code first?
public class Title {
public Property Property { get; set; }
}
public class Property {
public Title TitleInsurance { get; set; }
}
tbTitle
-TitleID = PK
tbPropertyToTitle
-TitleID - FK to tbTitle.TitleID
-PropertID - FK to tbProperty.PropertyID
tbProperty
-PropertyID = PK
Code in VB.Net here, but should be easy to translate. Mark primary keys with the Key data attribute. Entity Framework will automatically look for properties named Class + ID, i.e. tbTitleID to assign as primary keys, but since that isn't applicable here, we need the Key attribute.
Overridable properties denote Navigation Properties. In C#, this should be equivalent to Virtual properties. When this navigation property is accessed, Entity Framework will automatically look for valid foreign key relations, and populate the appropriate data.
For a one-to-one relationship, Entity Framework expects that your two tables share the same primary key, as shown by TitleID here.
Public Class tbTitle
<Key()>
Public Property TitleID As Integer
...
Public Overridable Property Property As tbProperty
End Class
Public Class tbProperty
<Key()>
Public Property TitleID As Integer
...
Public Overridable Property Title As tbTitle
End Class
Looking through the fluent API, I don't see any way to map one to one relations through a join table. You might be able to fake it by setting it up as a many to many but then you would need a bit of extra code to ensure that your relation collections only ever have one item in them.

What is the meaning of the "Pluralize or singularize generated object names" setting?

When setting up a new Entity data Model, there is an option to
[x] Pluralize or singularize generated object names
I have noticed this is an option in LINQ as well. Also, now that I am studying the ADO.NET entity framework, I noticed it also has 'DEFAULT' to 'pluralize or singularize generated object names'
What is the result of not checking/allowing this option when setting up the 'Entity Data Model'.
What Advantages/Disadvantages/issues will I face by making a selection one way or the other?
If you check Pluralize or singularize generated object names, the set in the class context.cs genrated by EF will be named in the format:
public virtual DbSet<SomeTableName> SomeTableNames { get; set; }
if not check, it'll be named:
public virtual DbSet<SomeTableName> SomeTableName { get; set; }
Advantages/Disadvantages IMHO:
I would like to see collection set be named ending with 's', such as dbset colleciton of Employee class of Employee Table named Employees, so I'll check the option. But I guess maybe someone would like to treat the dbset as a table, so he/she would like to name it same as table name Employee.
No problem at all, except that you'll probably want to do it manually. Usually, you want entity names singular and entity set names plural.