EF Core Code First - one to one conditional relationship - entity-framework

I'm not sure if this relationship has a name, so maybe I'm just missing it. I didn't find anything in the docs that looked like this.
I have a many-to-one relationship, sort of, but only one relationship matters, so it's kind of a one-to-one, but on a condition at the many side.
Equipment has many Contracts, but at most one active Contract.
Contract always has one Equipment, and has a reference to the Equipment. That side works just fine. I have business logic to prevent more than one Contract for an Equipment being active.
public class EquipmentModel{
public int Id {get; set;}
//...other properties
}
public class ContractModel{
public int Id {get;set;}
public EquipmentModel Equipment {get;set;}
public bool Active {get;set;}
//...other properties
}
This means I can do a _contracts.Include(x => x.Equipment) and get the equipment entity for the contract.
But I can't wrap my head around the relationship the other direction. I don't want to just put all the Contacts on the Equipment entity, I don't have a need for them and it seems like overkill.
I suppose I could put the Contract entity on the Equipment model and manually manage the relationship in business logic, but I'm hoping Entity Framework has a better way to designate this.
I could also pull back all contracts and use some business logic to filter down to the active one, but that's also less than ideal.
public IEnumerable<ContractModel> Contracts {get;set;}
Ideally, I'd want to be able to do something like _equipment.Include(x => x.ActiveContract) and have the one Contract with an Active == true automatically. Is this a pattern that exists within EF Core?

The easiest way with that design is to use a Filtered Include,
_equipment.Include(x => x.Contracts.Where(c => c.Active))
You can reuse this expression by creating a method on your DbContext, that returns the base query with the Include, eg
public IQueryable<Equipment> GetEquipment() => this.Set<Equipment>().Include(x => x.Contracts.Where(c => c.Active));
Which can be further filtered by the calling code before running the query, eg
var equips = db.GetEquipment().Where(e => e.Name == equipName).ToList();
If you feel strongly about it you can even omit the DbSet<Equipment> property on the DbContext (registering Equipment as an Entity in OnModelCreating), and replace it with
public IQueryable<Equipment> Equipment => this.Set<Equipment>().Include(x => x.Contracts.Where(c => c.Active));

Related

Can EF Core configure a "real" One-To-One relation where both ends are required?

The EF Core documentation about One-To-One relations says: "When configuring the relationship with the Fluent API, you use the HasOne and WithOne methods." A closer look shows that this configures One-To-ZeroOrOne or ZeroOrOne-To-ZeroOrOne relations depending on whether IsRequired is used or not. Example:
public class ParentEntity
{
public Int64 Id { get; set; }
public ChildEntity Child { get; set; }
}
public class ChildEntity
{
public Int64 Id { get; set; }
public ParentEntity Parent { get; set; }
}
The derived context class contains:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ParentEntity>().HasOne(p => p.Child).WithOne(d => d.Parent)
.HasForeignKey<ChildEntity>("ParentFk").IsRequired();
}
With this configuration, context.SaveChanges fails after context.Add(new ChildEntity()) as expected (with SqlException: Cannot insert the value NULL into column 'ParentFk' ... because of IsRequired) but succeeds after context.Add(new ParentEntity()) and context.Add(new ChildEntity() { Parent = new ParentEntity() }), i.e., the ParentEntity-ChildEntity relation is One-To-ZeroOrOne. In other words: the parent of a child is required, the child of a parent is optional.
Is there a way to configure a "real" One-To-One relation where both ends are required?
Maybe this cannot be enforced within the database. But can it be enforced by EF Core? (BTW: It can be enforced by EF6.)
Is there a way to configure a "real" One-To-One relation where both ends are required?
At the time of writing (EF Core 2.1.2), the answer is (unfortunately) negative.
The Required and Optional Relationships section of the documentation says:
You can use the Fluent API to configure whether the relationship is required or optional. Ultimately this controls whether the foreign key property is required or optional.
There is also a closed issue EF Core 2: One to One Required Not Being Enforced (also Navigation no longer needed?) #9152 asking the same question, and part of the response is:
when a relationship is made "Required" it means that a dependent entity cannot exist without an associated principal entity. This is done my making the FK non-nullable--i.e. the FK value must reference some principal entity.
However, it says nothing about the principal entity existing without the dependent. This is always possible because there isn't really any way to restrict it when working with partially loaded graphs. (This was the same with the old stack, although there were some situations where the state manager would, almost arbitrarily, stop certain things happening.) With stronger semantics applied to aggregates that limit partially loading of graphs it may be possible to enforce such a restriction in the future, but that isn't done yet.

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.

class that represents an item with attributes where attributes are in a collection of attribute objects, using code first migrations

I am trying to do a code first entity framework project that consists of two classes/tables.
One class represents an widget. The second class/table contains attributes of this widget. i.e. right threaded, left threaded, small, large, etc..
Each Widget could have 1 or more attributes and each attribute could belong to many widgets.
I'm drawing a blank on how this should be represented in code first.
class Widget{
public int Id {get;set;}
public string Name {get;set;}
public ICollection<Attribute> Attributes{get; set;}
}
class Attribute{
public int Id{ get; set;}
public string Name{get; set;}
}
The classes above give me two tables and multiple copies of the same attribute in the DB.
Ultimately I am trying to use the Attribute table as an expandable enum of sorts.
Because you don't have a navigation property on the Attribute entity, Entity Framework won't determine by convention that a many-to-many relationship is present. It will create a one-to-many instead. To specify a many-to-many relationship, you need to add mapping code to the context.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Widget>()
.HasMany(w => w.Attributes)
.WithMany();
}
The .Map() code in the example you linked is only necessary if you wish to specify column or table names. If you omit that code, Entity Framework will provide names.
I eventually found a related article that explained it well. I am concerned about the age of the article and whether anything has changed since 2011.
Many to Many article

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;

EF: Declaring Many-to-Many relationship in attributes

Many to many relationships can be declared like this in fluent-API.
modelBuilder.Entity<Course>()
.HasMany(t => t.Instructors)
.WithMany()
I would rather if there were certain attributes that could do the same in my domain model.
Are there such attributes somewhere in the framework, or can I make some of my own that can affect EF behavior when it generates the database?
I've assumed your model, try below and see if it works.
public class Course
{
[InverseProperty("Courses")] //add this attribute
public ICollection<Instructor> Instructors { get; set; }
//other properties..
}
public class Instructor
{
[InverseProperty("Instructors")] //add this attribute
public ICollection<Course> Courses { get; set; }
//other properties..
}
that way you tell entity framework to look for what property to map with in the Course model for Instructor.
Moreover, you don't even need to define that way. but In case you have more than one property of type Course in Instructor or vice-versa, you will need to point out correctly which is mapped to what.
Still, using fluent API is much better and scalable and manageable
You don't need any attributes for it. If you declare ICollection<T> in each entity, EF will create many to many relationships by convention. See here.