How can I implement a 1..n bi-directional relationship in Entity Framework Code First - entity-framework

I am absolutely stumped in trying to figure out how to implement a bi-directional 1..n relationship in Entity Framework using Code First. For example, a team (represented by a Team entity) has a coach and a manager (both represented by a Person entity). So, my Team model could be as follows:
public class Team
{
public Team()
{
Manager = new Person();
Coach = new Person();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TeamID { get; set; }
public string Name { get; set; }
[ForeignKey("Manager")]
public int ManagerID { get; set; }
public virtual Person Manager { get; set; }
[ForeignKey("Coach")]
public int CoachID { get; set; }
public virtual Person Coach { get; set; }
}
I can implement one-way navigation by implementing the Person Entity as follows:
public class Person
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PersonID { get; set; }
public string Name { get; set; }
}
and the fluent API:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Team>()
.HasRequired(t => t.Manager);
modelBuilder.Entity<Team>()
.HasRequired(t => t.Coach);
base.OnModelCreating(modelBuilder);
}
However, while that allows me to navigate from the Team entity to the related Coach and Manager (both instances of Person), it doesn't allow me to directly navigate from the Coach or Manager to the related Team. So, to implement 2-way navigation, I modified the Person entity as follows:
public class Person
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PersonID { get; set; }
public string Name { get; set; }
[ForeignKey("Team")]
public int TeamID { get; set; }
public virtual Team Team { get; set; }
}
While that builds ok, I get the following runtime error when I try to save to the database:
System.Data.Entity.Core.UpdateException
InnerException: Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
So, to specify the ordering between the entities, I tried to modify the fluent API by adding the "WithRequiredPricipal" as follows:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Team>()
.HasRequired(t => t.Manager)
.WithRequiredPrincipal(t => t.Team);
modelBuilder.Entity<Team>()
.HasRequired(t => t.Coach)
.WithRequiredPrincipal(t => t.Team);
base.OnModelCreating(modelBuilder);
}
However, when I attempt to execute "add-migration" in the Package Manager Console, I get the following error:
System.InvalidOperationException: The navigation property 'Team' declared on type 'RelatedEntities.Models.Person' has been configured with conflicting foreign keys.
What I'm attempting to achieve seems like a straightforward requirement but I've done heaps of searching for a solution without yet finding an answer. Am I missing something in the fluent API or the annotations?
(I don't want to implement a workaround, such as implementing separate models for Coach and Manager because a team may have numerous other roles (eg, Assistant Coach, Public Relations Manager, etc). I would want each to simply be an instance of a Person entity.)

You didn't define your foreign key constraints.
.HasRequired(t => t.Manager)
.WithMany()
.HasForeignKey(t => t.ManagerId)
As a side note. If your manager was managing multiple teams the WithMany would be .WithMany(m => m.Teams) and your manager model would need something like this:
private ICollection<Team> _teams
public ICollection<Team> Teams
{
get { return _teams ?? (teams = new List<Team>()); }
protected set { _teams = value; }
}
Sorry for formatting. On my phone. Good luck.

Ok, I can now answer my own question in case anyone else encounters the same problem.
Firstly, as I mention in my comment above, the problem that I described in my question turns out to be a recognized shortcoming in Entity Framework that the EF Triage Team has flagged to be addressed in a future release of EF.
In the meantime, the workaround that has been suggested by a number of contributors in response to questions about how to implement circular dependencies in EF (which is what my example above is trying to do) is to do it in stages as follows:
Create the principal entity
Call SaveChanges() on DbContext
Create the dependent entity and set the foreign key before calling SaveChanges() again (at some subsequent point)
So, using the Team example in my original question above, instead of creating the dependent Manager and Coach entities in the Team constructor, the first change was to make the Team's foreign keys to the Coach and Manager optional (instead of required), so that the Team could be instantiated without the Coach and Manager, and then call SaveChanges():
Team team = Teams.Add(new Team());
SaveChanges();
After that, I then create the dependent Manager and Coach entities and set their foreign keys to the ID of the Team instance:
team.Manager = new Person();
team.Manager.TeamID = team.TeamID;
team.Coach = new Person();
team.Coach.TeamID = team.TeamID;
At any time after that, SaveChanges() can be called without causing a runtime error as previously.
When this issue is addressed in a future release of EF, it should be possible to create the principal and dependent entities without having to call SaveChanges() in between.

Related

"The association has been severed but the relationship is either marked as 'Required' or is implicitly required..."

I am getting the following error when trying to add a migration:
PS C:\Code\morpher.ru\Morpher.Database> dotnet ef migrations add AddQazaqFeatures --startup-project=../Morpher.Database.Design
Build started...
Build succeeded.
System.InvalidOperationException: The association between entity types 'Service' and 'Deployment' has been severed but the relationship is either m
arked as 'Required' or is implicitly required because the foreign key is not nullable. If the dependent/child entity should be deleted when a requi
red relationship is severed, then setup the relationship to use cascade deletes. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLoggin
g' to see the key values.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.HandleConceptualNulls(Boolean sensitiveLoggingEnabled, Boolean forc
e, Boolean isCascadeDelete)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.CascadeDelete(InternalEntityEntry entry, Boolean force, IEnumerable`1 fore
ignKeys)
...
My code:
public class Deployment
{
public int Id { get; set; }
public virtual Service Service { get; set; }
public int ServiceId { get; set; }
public string Host { get; set; }
public short? Port { get; set; }
public string BasePath { get; set; }
}
public class Service
{
public int Id { get; set; }
public string Name { get; set; }
public string UrlSlug { get; set; }
public virtual ICollection<Endpoint> Endpoints { get; set; }
public virtual ICollection<Deployment> Deployments { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Service>().HasData(new Service
{
Name = "Веб-сервис «Морфер»",
UrlSlug = "ws",
Id = 1
});
modelBuilder.Entity<Deployment>().HasData(new Deployment
{
Host = "ws3.morpher.ru",
ServiceId = 1,
Id = 1
});
modelBuilder.Entity<Deployment>(entity =>
{
entity.Property(e => e.Host).IsRequired().HasMaxLength(256);
entity.Property(e => e.BasePath).HasMaxLength(512);
entity.HasOne(deployment => deployment.Service)
.WithMany(service => service.Deployments)
.HasForeignKey(d => d.ServiceId)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK_Deployments_Services");
});
}
There are numerous StackOverflow questions mentioning the same error (1, 2, 3), but they are mostly to do with removing entities while not having a CASCADE delete policy or a nullable foreign key.
In my case, I am trying to add new rows and I don't see why it is considering the relationship 'severed'. Is setting ServiceId = 1 not enough?
I was able to reproduce the issue in latest at this time EF Core 3.1 version (3.1.28) by first removing the model data seeding code (HasData calls), then adding migration for just creating the tables/relationships, then adding the data seeding code and attempting to add new migration.
It does not happen in latest EF Core 6.0, so apparently you are hitting EF Core 3.1 defect/bug which has been fixed somewhere down on the road. So you either need to upgrade to a newer EF Core version (with all associated burdens like retesting everything, breaking changes etc.), or use the workaround below.
The workaround is to replace the DeleteBehavior.Restrict with either ClientNoAction or NoAction. Values of that enum and documentation of what they do is kind of messy, but all these 3 values seem to generate one and the same regular enforced FK constraint (with no cascade) in the database, and differ only by client side behavior, or in other words, what does EF Core change tracker do with related tracked entities when "deleting" a principal entity. And in this particular case, `Restrict" throws exception when there are tracked (loaded) related entity instances, while the other two won't.
I know you think you are just "adding data", but EF Core model data seeding is more than that - it tries to keep that data, so in some circumstances it needs to update or delete previously added data. Which in general works, except when there are bugs in the EF Core codebase, like in this case.

EF "The member with identity X does not exist in the metadata collection" (and NopCommerce)

First of all and as you can see in the title, I'm designing a plugin for nopCommerce. I believe that the problem is not related to it (but with Entity Framework) but I'm not really sure.
My plugin has access to data, and nopCommerce let's me create my own context, creating the required tables and relations.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ParentMap());
modelBuilder.Configurations.Add(new ChildMap());
}
Conceptually, what I would like to have is:
ROOT TABLE ----(1:N)---- CHILDTABLE ----(1:1)---- EXISTING NOPCOMMERCE TABLE
And the tables and columns created in the DB seems correct to me. The mappings are pretty simple:
public ParentMap()
{
ToTable("TableA");
HasKey(m => m.Id);
Property(m => m.SomeProperty);
HasMany(m => m.Child).WithRequired(s => s.Parent).WillCascadeOnDelete();
}
public ChildMap()
{
ToTable("TableB");
HasKey(m => new { m.Id });
HasRequired(m => m.NopCommerceEntity);
HasRequired(m => m.Parent);
}
Till this point, everything seems to work pretty well, things get registered and the application starts. When I try to insert or update anything that has relation with the nopCommerce entity, I get the following exception:
"The member with identity 'Namespace.ChildObject_NopCommerceEntity' does not exist in the metadata collection.\r\nParameter name: identity".
I've been thinking that the problem could be related with the fact that I'm not adding other mappings in my OnModelCreating. I've tried to add all the related mappings there but I get the same exception.
It's a bit complicated. I'd be very greatful for your help & time :)
You are right this issue related to Entity Framework because EF does not support cross-project POCO relations. You have to remove any property that has an entity from NopEntities in your plugin and instead use an Id of that entity as int as below :
public int ProductId { get; set; }
instead of
public virtual Product Product { get; set; }
and in the mapping you should not specify the relationship between your entity and the Nop.
I think you can still use the following relation in your child class
public int ProductId { get; set; }
public virtual Product Product { get; set; }
but when you save or update the child class, instead of setting
child.Product = product
instead set the following
child.ProductId = product.Id
and also i think in your PluginObjectContext plugin class inherits from NopObjectContext.

EF Code first and optional:optional relationship

According to msdn article, the following should create an optional:optional relationship, but instead it creates optional:many relationship. Is the article wrong?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Optional_1>()
.HasKey(o1 => o1.id1);
modelBuilder.Entity<Optional_2>()
.HasKey(o2 => o2.id2);
modelBuilder.Entity<Optional_1>()
.HasOptional(o1 => o1.Dependent)
.WithOptionalPrincipal(o2 => o2.Principal);
}
public class Optional_1
{
public int id1 { get; set; }
public Optional_2 Dependent { get; set; }
}
public class Optional_2
{
public int id2 { get; set; }
public Optional_1 Principal { get; set; }
}
thank you
The table might look like one to many, but Entity Framework will enforce it as optional:optional because of the navigation properties. Since the navigation property is only a single object and not a collection, there is no way to add multiple.
If you look at the generated tables, it creates a nullable foreign key to your principal table (Optional_1). This allows you to create an Optional_2 that is not associated with an Optional_1.
If you were to insert multiple rows into Optional_2 that have the same foreign key to Optional_1 outside of EF, there wouldn't be anything preventing it from going through. If you were to try and load these entities you would get an error. You can't add a unique index to the column because it needs to allow NULL since it is optional.

In Entity Framework, is it possible to auto-map a column to an entity property? ID => [tablename]ID

I want to use .Id in my entity classes for the unique id, but our dba wants [tablename]Id in the database tables. Is there a way that Entity Framework can make this mapping automatically without having to create a new map file for every entity?
As long as I understand you correctly, you have something like:
public class Foo
{
public Int32 ID { get; set; }
// ...
}
public class Bar
{
public Int32 ID { get; set; }
// ...
}
And, without too much effort (or creating multiple entityTypeConfiguration<T> models) you'd like something along the lines of the following outcome:
Current Mapping Desired Mapping
[Foo] [Foo]
ID FooID
... ...
[Bar] [Bar]
ID BarID
... ...
For this, a few methods exist (and depend on which version of EF you're using). With that said, some approachable tactics:
ColumnAttribute
You can visit each entity model and decorate the ID property with the ColumnAttribute. This tells EF that, despite what we named the column, we want something else to be the name within the database. e.g.
public class Foo
{
[Column("FooID")]
public Int32 ID { get; set; }
// ...
}
public class Foo
{
[Column("BarID")]
public Int32 ID { get; set; }
// ...
}
The only problem here is that you're now going to every model and adding the attribute.
OnModelCreating & Fluent Mapping
Another method is to do the mapping but keep it all in one place. The OnModelCreating event is great for this kind of thing.
public class MyDbContext : DbContext
{
public Dbset<Foo> Foos { get; set; }
public DbSet<Bar> Bars { get; set; }
protected override void OnmodelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Foo>()
.Property(x => x.ID).HasColumnName("FooID");
modelBuilder.Entity<Bar>()
.Property(x => x.ID).HasColumnName("BarID");
}
}
Again, the problem here is that you're creating a configuration for each entity.
Custom Conventions
As of EF6, you can use Custom Conventions which make things easier (Including developing your own convention that would make ID=TableNameID). Unfortunately I don't have the time to write an example, but the docs are pretty enlightening.
According to MSDN , both way should work.
Primary key detection is case insensitive. Recognized naming patterns
are, in order of precedence: 'Id' [type name]Id

Entity Framework Code First One-to-One Required-Required Relationship

When using Entity Framework Code First 4.3.1 it is possible to create relationships with a multiplicity of 1-to-1. That is, one entity on each end of the relationship.
It is possible to configure 1-to-1 relationships to be required-required or required-optional ^. However, when I switch between the two I do not see any differences in:
The database schema generated. I am targeting SQL Server 2008.
The runtime behaviour of EF.
As such, I am able to create a RequiredPrincipalAs record without a corresponding RequiredDependentAs record, despite the relationship being configured as required-required. This seems to contradict the documentation for HasRequired(...):
Configures a required relationship from this entity type. Instances of the entity type will not be able to be saved to the database unless this relationship is specified. The foreign key in the database will be non-nullable.
http://msdn.microsoft.com/en-us/library/gg671317
The required-required relationship entities:
public class RequiredPrincipalA
{
public int Id { get; set; }
public virtual RequiredDependentA DependentA { get; set; }
}
public class RequiredDependentA
{
public int Id { get; set; }
public virtual RequiredPrincipalA PrincipalA { get; set; }
}
The required-optional relationship entities:
public class RequiredPrincipalB
{
public int Id { get; set; }
public virtual OptionalDependentB DependentB { get; set; }
}
public class OptionalDependentB
{
public int Id { get; set; }
public virtual RequiredPrincipalB PrincipalB { get; set; }
}
The DbContext and model configuration:
public class AppContext : DbContext
{
public DbSet<RequiredPrincipalA> PrincipalAs { get; set; }
public DbSet<RequiredDependentA> DependentAs { get; set; }
public DbSet<RequiredPrincipalB> PrincipalBs { get; set; }
public DbSet<OptionalDependentB> DependentBs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<RequiredPrincipalA>()
.HasRequired(o => o.DependentA)
.WithRequiredPrincipal(o => o.PrincipalA);
modelBuilder.Entity<RequiredPrincipalB>()
.HasOptional(o => o.DependentB)
.WithRequired(o => o.PrincipalB);
}
}
The test code:
Database.SetInitializer(new DropCreateDatabaseAlways<AppContext>());
using (var ctx = new AppContext())
{
ctx.Database.Initialize(force: false);
ctx.PrincipalAs.Add(new RequiredPrincipalA());
ctx.PrincipalBs.Add(new RequiredPrincipalB());
ctx.SaveChanges();
}
I am aware I could add a [Required] data attribute to the navigation properties of RequiredPrincipalA.DependentA and RequiredDependentA.PrincipalA. This would cause EF validation to prevent the scenario above. However, I do not want to do this because it also validates the navigation property is populated when updating an existing entity. This means the application has to pre-fetch the entity at the other end of the relationship for every update.
Why do I not see any difference in the behaviour of EF just when changing a relationship between required-required and required-optional?
^ Note that optional-optional is also supported but this doesn't form part of my question. There are obvious differences in the generated database schema and runtime behaviour when an optional-optional relationship is configured.
I don't know why required-required is allowed for this case but it cannot exist in the database because relation is build on primary keys. Required-required means that A cannot be inserted if related B doesn't exist and B cannot be inserted if related A doesn't exist => neither A or B can be inserted.
Database relation has always principal and dependent entity - principal can always exist without dependent.
Real required-required in EF can be achieved only when both A and B are mapped to the same table (table splitting) because in such case they are both inserted with single insert command.
Not really an answer but I have more to say than will fit in comments. But you know, I write 900 page books...it's just how I roll. :)
Oddly I would expect the fluent configuration to behave the same way as the data annotation and am confused that it's not doing it. (I've pinged Rowan Miller with a link to this thread to get his feedback.) And the behavior I mean is: validating the constraint during SaveChanges.
On the database side, I'm with Ladislav.In the model, EF defines the 1:1 using the keys of the related entities. But in the database, you can't have FKs in both tables, so only the dependent table in the database will require that constraint that it's PK maps to an existing PK in the principal table.
And finally, I understand your reason for not wanting EF to enforce the relationship if you aren't going to always deal with teh full graph. I think 1:1 relationships are the most confusing of the EF relationship mappings and I always find myself having to go back for reminders of the rules and how things should work.
Old question. But since EF6 is still used and even available for .Net standard and this issue can be a real nuisance, I think it's worth mentioning something I couldn't find in other answers.
It is true that both HasRequired - WithRequiredPrincipal and HasOptional - WithRequired produce the same database schema and the same runtime behavior. That is, with both mappings it's possible to save a principal without a dependent entity and to remove the dependent later. So much for HasRequired.
But there is a way to make EF validate the required relationship when creating the entities, which is by simply adding a [Required] attribute:
public class RequiredPrincipalA
{
public int Id { get; set; }
[Required] // <== here
public virtual RequiredDependentA DependentA { get; set; }
}
public class RequiredDependentA
{
public int Id { get; set; }
public virtual RequiredPrincipalA PrincipalA { get; set; }
}
As said, only when creating the entities. It's still possible to set RequiredPrincipalA.RequiredDependentA = null and save it successfully. But I think that, fortunately, the likelihood of that happening in code is far lower than forgetting to set the required dependent.