I am using Entity Framework 6.1.3 and have the two models as shown below. However, when I run migration I get the below error:
Unable to determine the principal end of an association between the
types 'Example.Models.GiftVoucher' and
'Example.Models.Payment'. The principal end of this
association must be explicitly configured using either the
relationship fluent API or data annotations.
I have searched for it and found this question. One of the solution was to use the Required attribute.
However, I don't know how I can do the following in Entity Framework:
Rename GiftVoucherId and PaymentId in both models that are as foreign keys to Purchase_Id and Redemption_Idas shown in the image.
Then do something equivalent in Entity Framework like this CONSTRAINT fk-giftvoucher-table FOREIGN KEY (Purchase_Id) REFERENCES PAYMENT (PaymentId).
Payment Model
public class Payment {
public int Id { get; set; }
[Required]
public DateTime DateTime { get; set; }
public double Amount { get; set; }
[Required]
public int GiftVoucherId { get; set; }
public GiftVoucher GiftVoucher { get; set; }
}
Gift Voucher Model
public class GiftVoucher
{
public int Id { get; set; }
public double Amount { get; set; }
[Required]
public int PaymentId { get; set; }
public Payment Payment { get; set; }
}
I don't tend to use one-to-one relationships a lot... Unless i want to Vertically Partition for performance needs or there is an incessant OCD need to separate the concerns.
*Note: One-to-one relationship is technically not possible in SQL Server. It will always be one-to-zero-or-one. EF forms One-to-One relationships on entities not in DB.*
However, documentation is your friend
Configuring a Relationship Where Both Ends Are Required (One-to-One)
In most cases the Entity Framework can infer which type is the
dependent and which is the principal in a relationship. However, when
both ends of the relationship are required or both sides are optional
the Entity Framework cannot identify the dependent and principal. When
both ends of the relationship are required, use WithRequiredPrincipal
or WithRequiredDependent after the HasRequired method.
...
Given the following
public class GiftVoucher
{
// your primary key
public int GiftVoucherId { get; set; }
public virtual Payment Payment { get; set; }
// other properties
public double Amount { get; set; }
}
public class Payment
{
// We need to share the same key
public int GiftVoucherId { get; set; }
public virtual GiftVoucher GiftVoucher { get; set; }
// other properties
public DateTime DateTime { get; set; }
public double Amount { get; set; }
}
We can Fluent API like this
// Configure the primary key for the OfficeAssignment
modelBuilder.Entity<Payment>()
.HasKey(t => t.GiftVoucherId);
// we are essentially making GiftVoucher the principle in the DB
modelBuilder.Entity<GiftVoucher>()
.HasRequired(t => t.Payment)
.WithRequiredPrincipal(t => t.GiftVoucher);
So basically HasRequired is making GiftVoucher required and WithRequiredPrincipal is making Payment required.. In turn, EF will throw if the above is not satisfied
Related
Lets say we have Airport and Runway classes:
have perfectly fine one-to-many relationship between two tables: Play and Player:
public class Airport
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<Runway> Runways { get; set; }
}
public class Runway
{
public int Id { get; set; }
public string Name { get; set; }
//public int AirportId { get; set; } <- I don't need it!!!
}
My goal is to omit navigation property on Runway side (I don't need a link to Airport here).
But when I use dotnet-ef migrations add InitialCreate command - an AirportId field is created in database for Runway table.
How to configure one-to-many relationship with navigation property only on one side (on collection side) with EF Core 5?
There is similar question but for navigation property on a collection side.
I've tried the following with no success (nothing changes):
modelBuilder.Entity<Airport>()
.HasMany<Runway>(a => a.Runways)
.WithOne()
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
A foreign key column is the central concept of having a functional one-to-many relationship. The database needs it to identify which child entity (Runway) belongs to which parent entity (Airport). Therefore, you cannot avoid it.
If you don't want the AirportId and the Airport reference in your Runway class you can safely remove them and use the models as -
public class Airport
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<Runway> Runways { get; set; }
}
public class Runway
{
public int Id { get; set; }
public string Name { get; set; }
}
You can even remove the relation configuration code as well. EF will automatically create a nullable foreign key column AirportId in the Runway table and use it as a Shadow property (which you cannot access) behind the scene to manage the relationship.
I have a model which is used for managing friend relationships. It looks as follows:
public class Relationship
{
[Required]
public User User { get; set; }
[Required]
public User Friend { get; set; }
[Required]
public DateTimeOffset RelationshipInitializationDate { get; set; }
}
Users will have multiple records for their ID and there will be multiple records with the same FriendID so defining either of these as a key is a no-go. I would like the key to be a composite between User and Friend but when I define it like this:
modelBuilder.Entity<Relationship>().HasKey(r => new { r.User, r.Friend });
I get an error that states:
The property 'Relationship.User' is of type 'User' which is not supported by current database provider. Either change the property CLR type or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
How should I go about this to create the primary key that will link with a user and friend object. I didn't have any issues with my other objects having typed properties and I don't have an issue if I add an arbitrary key to the Relationship model. Thanks in advance
The basic idea here is that your adding properties to the model that EF can use to make a relationship. Right you're trying to create a relationship of type User and that is creating an error. To assign a composite key each key needs to be a type compatible with a Key field, not a navigation property. So we add UserId and FriendId of type int, string or GUID etc. and create a relationship off those properties.
public class Relationship
{
public User Friend { get; set; }
public User User { get; set; }
public int FriendId { get; set; }
public int UserId { get; set; }
public DateTimeOffset RelationshipInitializationDate { get; set; }
}
public class User
{
public int UserId { get; set; }
}
You can now define a composite key across UserId and FriendId. Something like this should do:
public class NorthwindContext : DbContext
{
public NorthwindContext(DbContextOptions<NorthwindContext> options):base(options) { }
public NorthwindContext() { }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Relationship>().HasKey(table => new {
table.FriendId, table.UserId
});
}
public DbSet<Relationship> Relationships { get; set; }
public DbSet<User> Users { get; set; }
}
Source: Medium - How To: Entity Framework Core relationships, composite keys, foreign keys, data annotations, Code First and Fluent API
I'm having trouble mapping a one to one relationship in EF 6. My classes are:
public class Membership
{
[Key]
public decimal Id { get; set; }
[ForeignKey("Id")]
public virtual User User { get; set; }
}
public class User
{
[Key]
public decimal Id { get; set; }
[ForeignKey("Id")]
public virtual Membership Membership { get; set; }
}
I am getting the exception:
Unable to determine the principal end of an association between the
types 'User' and 'Membership'. The principal end of this association
must be explicitly configured using either the relationship fluent API
or data annotations.
I think my issue is both tables has the same key value and do not store the key of the other. Thanks.
This will work
public class Membership
{
[Key]
public decimal Id { get; set; }
[ForeignKey("User")]
public decimal UserId {get;set;}
public virtual User User { get; set; }
}
public class User
{
[Key]
public decimal Id { get; set; }
[ForeignKey("Membership ")]
public decimal MemberShipId {get;set;}
public virtual Membership Membership { get; set; }
}
You should refer to this thread per ladislav, I'll put the contents here as well though.
In one-to-one relation one end must be principal and second end must
be dependent. Principal end is the one which will be inserted first
and which can exist without the dependent one. Dependent end is the
one which must be inserted after the principal because it has foreign
key to the principal.
In case of entity framework FK in dependent must also be its PK so in
your case you should use:
public class Boo
{
[Key, ForeignKey("Foo")]
public string BooId{get;set;}
public Foo Foo{get;set;}
}
Or using fluent mapping:
modelBuilder.Entity<Foo>()
.HasOptional(f => f.Boo)
.WithRequired(s => s.Foo);
Full error:
One or more validation errors were detected during model generation:
EFEmployee_Identity_Source: : Multiplicity is not valid in Role 'EFEmployee_Identity_Source' in relationship 'EFEmployee_Identity'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
I am dealing with three types of entities: EFEmployee, EFPerson, and EFOffice. It's kind of weird that I'm getting this error because the code I'm testing only creates an instance of an EFOffice entity. Anyway, here is the EFEmployee entity class:
[Table("employee_entity")]
public class EFEmployee : EFBusinessEntity
{
[ForeignKey("Office")]
public Guid OfficeID { get; set; }
[ForeignKey("Identity")]
public Guid PersonID { get; set; }
[Column("hire_date")]
public DateTime HireDate { get; set; }
[Column("job_title")]
public byte[] JobTitle { get; set; }
[Column("salary")]
public int Salary { get; set; }
[Column("certifications")]
public byte[] Certifications { get; set; }
[Column("vacation_time")]
public int VacationTime { get; set; }
[Column("sick_time")]
public int SickTime { get; set; }
public virtual EFOffice Office { get; set; }
public EFPerson Identity { get; set; }
public virtual EFEmployee ReportingTo { get; set; }
}
And this is my EFPerson entity class:
[Table("person_entity")]
public class EFPerson : EFBusinessEntity
{
[Column("first_name")]
[StringLength(50)]
public string FirstName { get; set; }
[Column("last_name")]
[StringLength(50)]
public string LastName { get; set; }
[Column("phone_num")]
public uint? PhoneNum { get; set; }
[Column("date_of_birth")]
public DateTime DateOfBirth { get; set; }
public EFEmployee Employee { get; set; }
}
You can see that they both inherit from EFBusinessEntity, which is here:
[Table("business_entity")]
public abstract class EFBusinessEntity : IBusinessEntity
{
[Column("tenant_id")]
public Guid TenantId
{
get;
set;
}
[Column("id")]
[Key]
public Guid Id
{
get;
set;
}
}
As you can see, there is a one-to-zero-or-one relationship between EFEmployee and EFPerson, with EFEmployee being the dependent side since there can be a person who is not an employee, but there can't be an employee who is not a person too. Since EFEmployee is the dependent side, I have added a PersonID in EFEmployee with the data annotation (attribute?) above denoting that it's the foreign key to Person:
[ForeignKey("Identity")]
public Guid PersonID { get; set; }
I think I've made it pretty clear for Entity Framework that this is a 1:0..1 relationship. Does anyone know how to solve this error using data annotations (or attributes, whatever those square bracket things above properties are). I can't use fluent API for reasons I'm not getting into.
Generally, with 1:0..1 relationships in Entity Framework, the dependent side needs to use its primary key as the foreign key. Fortunately, for your case, this doesn't seem like it would be a bad idea. You would need to:
Remove the EFEmployee.PersonID property
Add [ForeignKey("Id")] to EFEmployee.Identity
Edit: May not work because key and navigation property are on separate classes. See this.
Having EFEmployee inherit from EFPerson seems like it might be a viable option as well. Inheritance uses TPH by default, but if you want to use TPT (table-per-type), add the [Table] attribute to your type.
I did some more playing around with the models and found out what was wrong. So I kept the foreign key attribute with EFPerson.Identity like jjj suggested:
[ForeignKey("PersonID")]
public virtual EFPerson Identity { get; set; }
Then the other change I had to make was in the EFPerson class. In my EFPerson class I had the navigation property to EFEmployee:
public virtual EFEmployee Employee { get; set; }
However, since this is a 1:0..1 relationship with EFEmployee being the dependent side (i.e. the non-essential side), I removed that navigation property, and when I ran my test it worked.
I'm defining a many-to-many relationship as follows:
modelBuilder.Entity<GameSessionEntry>().
HasMany(c => c.Users).
WithMany(p => p.GameSessionEntries).
Map(
m =>
{
m.MapLeftKey("SessionId");
m.MapRightKey("UserId");
m.ToTable("UserSessions");
});
However, I keep getting:
The Foreign Key on table 'UserSessions' with columns 'UserId' could
not be created because the principal key columns could not be
determined. Use the AddForeignKey fluent API to fully specify the
Foreign Key.
I'm new to database work and the EntityFramework in general - what is it asking me to do?
It's the recurring confusion with left and right, see this explanation by Slauma. So you just have to turn around the key names:
m.MapLeftKey("UserId"); // Property in the HasMany call
m.MapRightKey("SessionId"); // Property in the WithMany call
This is how I usually go about creating a many to many table (note this requires no fluent api configuration)
public class User
{
public int Id { get; set; }
public virtual ICollection<UserSession> UserSessions { get; set; }
}
public class Session
{
public int Id { get; set; }
public virtual ICollection<UserSession> UserSessions { get; set; }
}
public class UserSession
{
[Key]
[Column(Order = 1)]
public int UserId { get; set; }
[Key]
[Column(Order = 2)]
public int SessionId{ get; set; }
public virtual User User { get; set; }
public virtual Session Session { get; set; }
}
Instead of fiddling around with a many-many relationship you should rewrite it to a weak entity set.
If you have for instance this relationship:
You can redesign it to a weak entity set:
By doing this you get rid of the many-many relationship and don't have to store the same data in multiple tables.
For more information: http://fileadmin.cs.lth.se/cs/Education/EDA216/lectures/dbtoh4.pdf
Read the lecture slides about "The Relational Data Model" starting on slide 87/360.