Entity Framework Map With Fake Foreign Key Property - c#

My models:
class FooEntity
{
[Key]
int Id { get; set; }
[ForeignKey("Bar"), Column("other_id")]
int OtherId { get; set; } // <-- This should be the FK
virtual BarEntity Bar { get; set; }
}
class BarEntity
{
[Key]
int Id { get; set; }
[Column("other_id")]
int OtherId { get; set; } // <-- This is the other side of the FK
}
Essentially I want to reproduce this SQL:
select *
from foo f
left join bar b on b.other_id = f.other_id -- AND other conditions to "guarantee" uniqueness
But with the model building configuration:
modelBuilder.Entity<FooEntity>()
.HasOptional(f => f.Bar)
.WithRequired()
.Map(m => m.MapKey("other_id"))
.WillCascadeOnDelete(false);
I end up with the error: "Each property name in a type must be unique. Property name 'other_id' is already defined." But when I add:
modelBuilder.Entity<BarEntity>().Ignore(b => b.OtherId);
before the other configuration, I get the error: "the specified type member 'OtherId' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported."
So how can I get this working? Changing the underlying data structure is absolutely not an option.

In EF6 an FK must point back to a PK.
In your RDBMS it could depend on the implementation. Sql Server will allow an FK back to a non PK column as long as there is a unique index constraint. Other RDBMS might or might not allow it.
I recommend you omit the FK relationship and when you need to retrieve both entities manually include the join in your Linq/Lambda statement.

There's no shortcut for what you want to accomplish; you will need to create the query manually, something along the lines of:
dbContext.Foos
.GroupJoin(
dbContext.Bars,
f => f.OtherId,
b => b.OtherId,
( foo, bars ) => new { foo, bars } )

Related

EF Core - how to model relation of Grandparent - Parent - Child on same model

Imagine a model of User that can have Parents and also can have Children.
How would you model such a case in EF Core?
I tried with something like that (pseudo-code)
public class User
{
public ICollection<Relation> Relations {get;set;}
public ICollection<User> Parents => Relation.Where(r => r.Relation == 'Parents')
public ICollection<User> Children => Relation.Where(r => r.Relation == 'Children')
}
public class Relaction
{
public User User1 {get;set;}
public Guid User1Id {get;set;}
public User User2 {get;set;}
public Guid User2Id {get;set;}
public Relation Relation {get;set;} //some enum or sth to indicate relation type
}
But in such modeling, I'm not able to force EF DbContext to fetch into User.Relations data where UserId is in User1Id and in User2Id.
Any idea?
What you are asking for is a classic many-to-many self relationship - (1) user as parent can have many users as children, and (2) user as child can have many users as parents.
Thus it is modelled with one main entity and one join (linking) entity similar to what you have shown. The linking entity does not need special indicator because the two FKs determine the role. i.e. lets change your example with more descriptive names:
public class User
{
public Guid Id { get; set; }
}
public class UserRelation
{
public User Parent { get; set; }
public User Child { get; set; }
public Guid ParentId { get; set; }
public Guid ChildId { get; set; }
}
Now, in pseudo code, given User user, then
user.Parents = db.Users.Where(u => user == u.Child)
user.Children = db.Users.Where(u => user == u.Parent)
EF Core 5.0+ allows you to hide the join entity (it still exists, but is maintained implicitly) and model the relationship with the so called skip navigations, which are the natural OO way of representing such relationship, e.g. the model becomes simply
public class User
{
public Guid Id { get; }
public ICollection<User> Parents { get; set; }
public ICollection<User> Children { get; set; }
}
This is all needed to create such relationship.
However the name of the join table and its columns by convention won't be what normally you would do - in this case, they would be "UserUser" table with "ParentsId" and "ChildrenId" columns.
If you use migrations and don't care about the names, then you are done and can safely skip the rest.
If you do care though, luckily EF Core allows you to change the defaults with fluent configuration (even though in a not so intuitive way):
modelBuilder.Entity<User>()
.HasMany(e => e.Parents)
.WithMany(e => e.Children)
.UsingEntity<Dictionary<string, object>>("UserRelation",
je => je.HasOne<User>().WithMany()
.HasForeignKey("ParentId").IsRequired().OnDelete(DeleteBehavior.Restrict),
je => je.HasOne<User>().WithMany()
.HasForeignKey("ChildId").IsRequired().OnDelete(DeleteBehavior.Restrict),
je => je.ToTable("UserRelations")
.HasKey("ParentId", "ChildId")
);
Here Dictionary<string, object> is the shared type EF Core will use to maintain the join entity in memory (change tracker). And is the most annoying thing in the above configuration since in a future they might change their minds and use different type (there are actually plans to do that in EF Core 6.0), so you'll have to update your mappings. Note that this does not affect the database design, just the memory storage type in EF Core change tracker.
So, because of that and the fact that in some scenarios it is better to work directly with the join entity, you could actually combine both approaches (explicit join entity and skip navigations) and get the best of both worlds.
To do than, you add the explicit entity and (optionally) navigations from/to it. The next is w/o collection navigations from User to UserRelation (with fully defined navigation you would need two ICollection<UserRelation> properties there):
public class User
{
public Guid Id { get; }
public ICollection<User> Parents { get; set; }
public ICollection<User> Children { get; set; }
}
public class UserRelation
{
public User Parent { get; set; }
public User Child { get; set; }
public Guid ParentId { get; set; }
public Guid ChildId { get; set; }
}
and required minimal fluent configuration
modelBuilder.Entity<User>()
.HasMany(e => e.Parents)
.WithMany(e => e.Children)
.UsingEntity<UserRelation>(
je => je.HasOne(e => e.Parent).WithMany(), // <-- here you would specify the corresponding collection nav property when exists
je => je.HasOne(e => e.Child).WithMany(), // <-- here you would specify the corresponding collection nav property when exists
je => je.ToTable("UserRelations")
);
The result is the same database model, but with different in-memory representation of the join entity and ability to query/manipulate it directly. Actually you can do the same with implicit entity, but in type unsafe way using sting names and object values which need to be cast to the appropriate type. This probably will improve in the future if they replace Dictionary<string, object> with some generic type, but for now explicit entity combined with skip navigations looks the best.
You can find (I guess better than mine) explanation of all this in the official EF Core documentation - Many-to-many and the whole Relationships section in general.

Create Foreign Key to a Unique Key in another Entity with EF Core

I have a Program entity like this
public class Program : IEntityBase
{
public int Id { get; set; }
public string ProgramCode { get; set; }
public string Name { get; set; }
public int DegreeTypeID { get; set; }
public DegreeType DegreeType { get; set; }
}
with programCode created as a unique key with this implementation
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Program>().HasAlternateKey(d => d.ProgramCode).HasName("AK_ProgramCode");
}
I have another entity ApplicantProgram with this definition
public class ApplicantProgram : IEntityBase
{
public int Id { get; set; }
public int ApplicantID { get; set; }
public Applicant Applicant { get; set; }
[Required]
public string FirstChoiceID { get; set; }
[Required]
public string SecondChoiceID { get; set; }
[Required]
public string ThirdChoiceID { get; set; }
public string SessionID { get; set; }
}
Which has FirstChoiceID, SecondChoiceID & ThirdChoiceID having ProgramCode in the program table.
Now these are my questions,
How do i get Program.Name property from ApplicantProgram knowing the FirstChoiceID that is to link to Program.ProgramCode?
Is it possible to create a Navigation property to program from ApplicantProgram?
How do i create a foreign key from ApplicantProgram to Program based off the ChoiceIDs that should link to Program.ProgramCode without using Program.Id?
Thank you for pausing to read this.
(1) How do I get Program.Name property from ApplicantProgram knowing the FirstChoiceID that is to link to Program.ProgramCode?
There is nothing specific to EF here, you could use the typical data correlation operator - join. Just because you have 3 related properties, you would need 3 joins as well:
var query =
from applicantProgram in db.ApplicantPrograms
join firstChoice in db.Programs on applicantProgram.FirstChoiceID equals firstChoice.ProgramCode
join secondChoice in db.Programs on applicantProgram.SecondChoiceID equals secondChoice.ProgramCode
join thirdChoice in db.Programs on applicantProgram.ThirdChoiceID equals thirdChoice.ProgramCode
select new
{
ApplicantProgram = applicantProgram,
FirstChoice = firstChoice,
SecondChoice = secondChoice,
ThirdChoice = thirdChoice,
};
Inside the select, you could get the whole related objects as above, or specific properties like firstChoice.Name, secondChoice.Name etc.
But you won't need all that in EF once you define the navigation properties, which leads us to:
(2) Is it possible to create a Navigation property to program from ApplicantProgram?
(3) How do I create a foreign key from ApplicantProgram to Program based off the ChoiceIDs that should link to Program.ProgramCode without using Program.Id?
These two are interrelated. While it's possible to define a FK without navigation property, the navigation property would allow you simple access to related entity properties inside the LINQ queries as well as simple eager loading the related entity as part of the entity which is using it.
Start by adding the 3 navigation properties (one for each FK property) in ApplicantProgram class:
public Program FirstChoice { get; set; }
public Program SecondChoice { get; set; }
public Program ThirdChoice { get; set; }
and the following fluent configuration:
builder.Entity<ApplicantProgram>()
.HasOne(e => e.FirstChoice)
.WithMany()
.HasForeignKey(e => e.FirstChoiceID)
.HasPrincipalKey(e => e.ProgramCode)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<ApplicantProgram>()
.HasOne(e => e.SecondChoice)
.WithMany()
.HasForeignKey(e => e.SecondChoiceID)
.HasPrincipalKey(e => e.ProgramCode)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<ApplicantProgram>()
.HasOne(e => e.ThirdChoice)
.WithMany()
.HasForeignKey(e => e.ThirdChoiceID)
.HasPrincipalKey(e => e.ProgramCode)
.OnDelete(DeleteBehavior.Restrict);
What we have here is the standard many-to-one relationship configuration - with HasOne(...) specifying the reference navigation property, WithMany() specifying no corresponding collection navigation property, HasForeighKey(...) specifying the corresponding FK property, and also the typical for multiple relationships to one and the same table turning off the cascade delete in order to avoid the multiple cascade paths problem.
What is specific thought (and is the EF Core improvement over EF6) is the HasPrincipalKey(...) method which allows you to specify other unique key property instead of the PK (by default) to be used by the FK relationship. Which in the combination with HasAlternateKey(...) on the other end allows to achieve the desired FK relationship setup.
And basically that's all. Now the query from (1) could be simply
var query =
from applicantProgram in db.ApplicantPrograms
select new
{
applicantProgram,
firstChoice = applicantProgram.FirstChoice,
secondChoice = applicantProgram.SecondChoice,
thirdChoice = applicantProgram.ThirdChoice,
};
Similar to (1), you could project the whole related objects or just properties you need.
Or you could get the ApplicantProgram instances with populated related Program properties by adding Include operators to the ApplicantProgram query (the so called eager loading):
var query = db.ApplicantPrograms
.Include(applicantProgram => applicantProgram.FirstChoice)
.Include(applicantProgram => applicantProgram.SecondChoice)
.Include(applicantProgram => applicantProgram.ThirdChoice);

Is it important to define both ends of relationships/FKs with Entity Framework?

Using EntityFramework, I'm curious if there is a performance gain when you specify both ends of a foreign key verses only one. Suppose I have a table that is used by most other tables, e.g. a lookup table. Let's say this table, LookupValue, has 20 incoming foreign keys and another table, User, is on the other end of one of those foreign keys via the column TypeId.
LookupValue
------------------
Id INT <--\
Values NVARCHAR |
|
User | FK_User_NameValue
------------------ |
Id INT |
TypeId INT <--/
Using code-first I define these tables as follows:
modelBuilder
.Entity<LookupValue>()
.ToTable("LookupValue")
.HasKey(o => o.Id);
modelBuilder
.Entity<User>()
.ToTable("User")
.HasKey(o => o.Id)
.HasRequired(o => o.Type)
.WithMany(o => o.UsersOfThisType)
.HasForeignKey(o => o.TypeId);
My questions are...
Is the WithMany...HasForeignKey portion necessary or helpful to EF to allow it to better generate queries between these two tables?
Do navigation properties have any role other than navigation via code?
Or would EF know about this foreign key relationship if I didn't specify the other end of the relationship?
E.g.:
modelBuilder
.Entity<User>()
.ToTable("User")
.HasKey(o => o.Id)
.HasRequired(o => o.Type);
As long as you only need to traverse from User to LookupValue, you can just have the accessor property in User. No need to have an ICollection<User> in the LookupValue object if you don't need to find all users that refer to a LookupValue.
public class LookupValue
{
public int Id { get; set; }
public string Value { get; set; }
}
public class User
{
public int Id { get; set; }
public int TypeId { get; set; }
public LookupValue Type { get; set; }
}
Entity Framework will generate a foreign key from Users to LookupValues via the TypeId attribute in Users. I think that's all you should need for performant traversing from Users to LookupValue.

Entity Framework: Foreign Key in code first

What is wrong in my code that i get below error:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values
Code:
Class Food:
public class Food
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public short Id { get; set; }
//some Property
public string Name { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
Class Person:
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
//some Property
public string FirstName { get; set; }
[ForeignKey("BestFoodId")]
public Food BestFood { get; set; }
public short BestFoodId { get; set; }
public virtual ICollection<Food> FavoriteFoods { get; set; }
}
Seed Method:
protected override void Seed(MyContext context)
{
Food food1 = new Food() { Name = "foo1" };
Food food2 = new Food() { Name = "foo2" };
Food food3 = new Food() { Name = "foo3" };
context.Persons.AddOrUpdate(new Person()
{
FirstName = "Jack",
BestFood = food2,
FavoriteFoods = new List<Food>() { food1, food2, food3 }
});
}
Cause of the error: confused associations
This happens because Entity Framework, by convention, assumes that the inverse property of Person.BestFoodId is Food.Persons. Stated differently: Person.BestFood and Food.Persons are assumed to be the two ends of a one-to-many association, having Person.BestFoodId as foreign key.
You can verify that by adding an [InverseProperty] attribute to BestFood:
public class Person
{
...
[ForeignKey("BestFoodId")]
[InverseProperty("Persons")]
public Food BestFood { get; set; }
...
}
This causes the same error.
This error --no valid ordering-- always indicates a chicken-and-egg problem. In your case, EF tries to insert the foods, which need the generated Id of the inserted person as foreign key, while the inserted person needs the generated Id of the inserted foo2 food.
Solution: explicitly mapped association
In reality, Person and Food have two associations:
1-n: Food can be BestFood of n people.
n-m: n Foods can be the FavoriteFoods of m people.
In your model, BestFood doesn't have an inverse property, which could have been something as ...
public virtual ICollection<Person> BestFoodOf { get; set; }
... but it isn't necessary and because it's missing, it obscures how EF infers the associations.
You can fix this by explicitly mapping the associations, for instance in the OnModelCreating override of your DbContext subclass:
modelBuilder.Entity<Person>()
.HasRequired(p => p.BestFood)
.WithMany() // No inverse property
.HasForeignKey(p => p.BestFoodId)
//.WillCascadeOnDelete(false)
;
modelBuilder.Entity<Person>()
.HasMany(p => p.FavoriteFoods)
.WithMany(f => f.Persons)
.Map(m => m.MapLeftKey("PersonId")
.MapRightKey("FoodId")
.ToTable("PersonFavoriteFood"));
I have commented out WillCascadeOnDelete(false). You either have to add this line, or add ...
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
... to prevent multiple cascaded delete paths (a SQL Server restrictions).
Having this in place, EF knows how to determine a valid ordering for the inserts: it will will first insert the foods, then insert the person (using the generated foo2 Id as foreign key) and then the junction records in the PersonFavoriteFood table.
Looks like you have a circular dependency.
Answers are here:
Unable to determine a valid ordering for dependent operations
Unable to determine a valid ordering for dependent operations?
Entity Framework Code First Circular Dependices
Entity Framework 4: inheritance and Associations
Entity Framework Circular Reference
Code First Circular Reference Foreign Key Configuration
How to configure a self referencing object in entity framework
Optional improvements:
You should declare your navigation property as virtual!
If you are using C# 6.0 or above, change your [ForeignKeyAttribute] Data Annotation definition to [ForeignKey([nameof(BestFoodId))] to avoid errors with hard coded property names. nameof is a really cool compiler feature! :)

Entity Framework 5 - Relationship Multiplicity Constraint on Multiple Foreign Keys to Same Table

I'm working on a project that required an update to Entity Framework 5. This has required some slight changes to the entity and configuration classes (code first) to bring the data layer current. The upgrade is complete, except for one remaining entity. When executing queries for this entity, I'm getting the following error:
System.InvalidOperationException: A relationship multiplicity constraint violation occurred: An EntityReference can have no more than one related object, but the query returned more than one related object. This is a non-recoverable error.
The entity models a table that contains two optional, foreign keys that relate to another table in the database.
Here is a portion of what the tables look like in the database (first, the table in question):
LocationMap Location
----------- --------
Id (PK, not null) Id (PK, not null)
SourceId (FK, null) ...
TargetId (FK, null)
In this model, both LocationMap.SourceId and LocationMap.TargetId refer to Location.Id. Here is the portion of the entity and configuration classes used to represent this relationship in my data layer:
public class LocationMap
{
public int Id { get; set; }
public int SourceId { get; set; }
public int TargetId { get; set; }
...
public virtual Location Source { get; set; }
public virtual Location Target { get; set; }
}
public class Location
{
public int Id { get; set; }
...
public virtual ICollection<LocationMap> TargetMaps { get; set; }
public virtual ICollection<LocationMap> SourceMaps { get; set; }
}
public LocationMapConfiguration()
{
HasKey(x => x.Id);
HasRequired(map => map.Source)
.WithMany(location => location.SourceMaps)
.HasForeignKey(map => map.SourceId)
.WillCascadeOnDelete(false);
HasRequired(map => map.Target)
.WithMany(location => location.TargetMaps)
.HasForeignKey(map => map.TargetId)
.WillCascadeOnDelete(false);
}
public LocationConfiguration()
{
HasKey(x => x.Id);
...
}
When running the following code ...
using (var context = new MyDbContext())
{
var map = context.LocationMaps
.FirstOrDefault();
Logger.Info("Source name: {0}", map.Source.Name);
Logger.Info("Target name: {0}", map.Target.Name);
}
... map.Source.Name works, while map.Target.Name produces the exception above. It does not matter how the two mappings are called - Source always works and Target always throws the exception.
The original Location entity class did not have the ICollection navigational properties defined, and was in fact how I set this up when creating the updated data layer. It was in doing research for the exception that multiple sources (including several here) involved solutions implementing the navigational properties in the fashion displayed in the examples. Thus, I added them, but it has not resolved my issue.
As usual, any help on this would be greatly appreciated.
Thanks!

Categories