I have a 2 classes:
public class Item
{
public int Id { get; set; }
public string ItemName { get; set; }
}
public class ItemStats //inhenrit from Item
{
public int Id { get; set; }
public int MaxEnhanceLevel { get; set; }
public Item Item { get; set; }
}
This is a TPT but since it's not supported out of the box I can't use inheritance. I know how to achieve this using data annotation
[ForeignKey(nameof(Item))]
public int Id { get; set; }
But how can I do this via FluentAPI? I don't want data annotation in my Entitie Classes.
What you have is a One-to-one relationship with single navigation property, principal entity Item and dependent entity ItemStats, using the so called shared primary key association, where the dependent entity PK is also a FK to the principal entity.
Fluent API for one-to-one relationships are HasOne, WithOne, HasForeignKey and HasPrincipalKey. Please note that the generic type arguments to HasForeignKey and HasPrincipalKey (which normally are omitted for one-to-many relationship) here are important because they indentify which entity is principal and which - dependent.
With that being said, the fluent configuration for your model is:
modelBuilder.Entity<ItemStats>()
.HasOne(e => e.Item)
.WithOne()
.HasForeignKey<ItemStats>(e => e.Id);
Related
I'm following a code-first approach with EF Core and have the following entities defined.
Role:
// This table will contain a list of all possible roles within the app
public class Role
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
Permission:
// This table will contain a list of all possible permissions within the app
public class Permission
{
[Key]
public int Id { get; set; }
[Required]
public string Name{ get; set; }
}
RolePermission:
// Links roles to permissions
public class RolePermission
{
[Key]
public int Id { get; set; }
public Role Role { get; set; }
public Permission Permission { get; set; }
}
As you can see, the idea is to have a one-to-many relationship between Role and Permission through a RolePermission table, giving the users of my application a way to configure what roles should have what permissions attached to them during runtime.
They will be able to create new roles as they wish, but the set of permissions available to them will not be configurable.
So how do I define this relationship and the foreign keys correctly? Do I use the FluentAPI, DataAnnotations, or both? How do I do it?
From Microsoft docs.
"Relationships that are discovered by convention will always target the primary key of the principal entity. To target an alternate key, additional configuration must be performed using the Fluent API."
In your scenario you have a M:N relationship that tells that the correct path is Fluent API.
You must create a joining entity class for a joining table. The joining entity for the above Role and Permission entities should include a foreign key property and a reference navigation property for each entity.
The steps for configuring many-to-many relationships would the following:
Define a new joining entity class which includes the foreign key
property and the reference navigation property for each entity.
Define a one-to-many relationship between other two entities and the
joining entity, by including a collection navigation property in
entities at both sides (Role and Permission, in this case).
Configure both the foreign keys in the joining entity as a composite
key using Fluent API.
So, first of all, define the joining entity RolePermission, as shown below.
RolePermission.cs
public class RolePermission
{
public int RoleId { get; set; }
public Role Role { get; set; }
public int PermissionId { get; set; }
public Permission Permission { get; set; }
}
The above joining entity RolePermission includes reference navigation properties Role and Permission and their foreign key properties RoleId and PermissionId respectively (foreign key properties follow the convention).
Now, we also need to configure two separate one-to-many relationships between Role-> RolePermission and Permission -> RolePermission entities. We can do it by just following the convention for one-to-many relationships, as shown below.
Role.cs
public class Role
{
public int RoleId { get; set; }
public string Name { get; set; }
public IList<RolePermission> RolePermissions { get; set; }
}
Permission.cs
public class Permission
{
public int PermissionId { get; set; }
public string Name { get; set; }
public IList<RolePermission> RolePermissions { get; set; }
}
Now, the foreign keys must be the composite primary key in the joining table. This can only be configured using Fluent API, as below.
public class MyContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("CONNECTIONSTRING");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<RolePermission>().HasKey(rp => new { rp.RoleId, rp.PermissionId });
}
public DbSet<Role> Roles { get; set; }
public DbSet<Permission> Permissions { get; set; }
public DbSet<RolePermission> RolePermissions { get; set; }
}
In the above code, modelBuilder.Entity().HasKey(rp => new { rp.RoleId, rp.PermissionId }) configures RoleId and PermissionId as the composite key.
This is how you can configure many-to-many relationships if entities follow the conventions for one-to-many relationships with the joining entity. Suppose that the foreign key property names do not follow the convention (e.g. RId instead of RoleId and PId instead of PermissionId), then you can configure it using Fluent API, as shown below.
modelBuilder.Entity<RolePermission>().HasKey(rp => new { rp.RId, rp.PId });
modelBuilder.Entity<RolePermission>()
.HasOne<Role>(rp => rp.Role)
.WithMany(r => r.RolePermissions)
.HasForeignKey(rp => rp.RId);
modelBuilder.Entity<RolePermission>()
.HasOne<Permission>(rp => rp.Permission)
.WithMany(p => p.RolePermissions)
.HasForeignKey(rp => rp.PId);
IMPORTANT: You can configure the relationships in the UsingEntity arguments. For instance,constraints,cascades, etc.
How I get an Entity with it's navigation property in Entity Framework Core? I have seen in docs.microsoft that I have to use .Include() and it's working in a project but this time it's not working.
Model.cs
public class UniversityModel
{
public int ID { get; set; }
public string Name { get; set; }
public string LongDescription { get; set; }
public string Address { get; set; }
public List<UniSession> Sessions { get; set; }
public DateTime Date { get; set; }
public List<UniEmail> Emails { get; set; }
public List<UniPhone> Phones { get; set; }
}
And I'm accessing the UniversityModel with it's navigation property like.
UniversityModel university = await _context.Universities
.Include(u => u.Phones)
.Include(u => u.Emails)
.Include(u => u.Sessions)
.SingleOrDefaultAsync(u => u.ID == id);
It's getting the university correctly but Navigation properties are not including.
For make it clear look at the Model below all the navigation property models are same with foreign key of university.
public class UniEmail
{
public int ID { get; set; }
public int UniversityId { get; set; }
[StringLength(50)]
public string Email { get; set; }
}
What is the wrong how correctly I can Include all the navigation properties and If my Including code is wrong then how it's worked in another project?
The problem is that your entity model does not follow EF Core conventions and you don't use fluent configuration. Although EF discovers the relationship through collection navigation property, since there is no inverse reference navigation property and UniversityId field is not identified as FK, EF maps FK to a shadow property called UniversityModelId.
EF Core FK naming conventions are explained in the Fully Defined Relationships section of the documentation:
If the dependent entity contains a property named <primary key property name>, <navigation property name><primary key property name>, or <principal entity name><primary key property name> then it will be configured as the foreign key.
In other words, UniversityId will be considered conventionally as FK if:
(1) UniversityModel class is called University
(2) ID property of UniversityModel class is called UniversityID (and tagged with [Key] attribute because it would not match the PK convention)
(3) You add inverse navigation property called University:
public UniversityModel University { get; set; }
public int UniversityId { get; set; }
And of course it can be mapped explicitly with fluent API:
modelBuilder.Entity<UniversityModel>()
.HasMany(e => e.Emails) // with collection navigation property
.WithOne() // and no reference navigation property
.HasForeignKey(e => e.UniversityId); // and foreign key
"The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations."
I am getting this error in Entity Framework 4.4 when updating/migrating the database, but I am not trying to specify a 1:1 relationship. I want something like this:
public class EntityA
{
public int ID { get; set; }
public int EntityBID { get; set; }
[ForeignKey("EntityBID")]
public virtual EntityB EntityB { get; set; }
}
public class EntityB
{
public int ID { get; set; }
public Nullable<int> PreferredEntityAID { get; set; }
[ForeignKey("PreferredEntityAID")]
public virtual EntityA PreferredEntityA { get; set; }
}
where EntityA must have an EntityB parent, whereas EntityB can have a preferred EntityA child, but doesn't have to. The preferred child should be one of the children associated with the parent, but I don't know how to enforce this in the database. I'm planning on enforcing it programmatically.
How do I get around this error or what is a better way of accomplishing these relationships?
Entity Framework Code-First conventions are assuming that EntityA.EntityB and EntityB.PreferredEntityA belong to the same relationship and are the inverse navigation properties of each other. Because both navigation properties are references (not collections) EF infers a one-to-one relationship.
Since you actually want two one-to-many relationships you must override the conventions. With your model it's only possible with Fluent API:
modelBuilder.Entity<EntityA>()
.HasRequired(a => a.EntityB)
.WithMany()
.HasForeignKey(a => a.EntityBID);
modelBuilder.Entity<EntityB>()
.HasOptional(b => b.PreferredEntityA)
.WithMany()
.HasForeignKey(b => b.PreferredEntityAID);
(If you use this you can remove the [ForeignKey] attributes.)
You cannot specify a mapping that would ensure that the preferred child is always one of the associated childs.
If you don't want to use Fluent API but only data annotations you can add a collection property in EntityB and relate it to EntityA.EntityB using the [InverseProperty] attribute:
public class EntityB
{
public int ID { get; set; }
public Nullable<int> PreferredEntityAID { get; set; }
[ForeignKey("PreferredEntityAID")]
public virtual EntityA PreferredEntityA { get; set; }
[InverseProperty("EntityB")] // <- Navigation property name in EntityA
public virtual ICollection<EntityA> EntityAs { get; set; }
}
Using code-first Entity Framework and .NET 4, I'm trying to create a one-to-many relationship between parents to children:
public class Parent
{
[Key]
public int ParentId { get; set; }
[Required]
public string ParentName { get; set; }
public IEnumerable<Child> Children { get; set; }
}
public class Child
{
[Key]
public int ChildId { get; set; }
[ForeignKey]
public int ParentId { get; set; }
[Required]
public string ChildName { get; set; }
}
As pointed out here, in order for foreign key relationship to carry into the database, the actual objects must be linked, not just their IDs. The normal way to do this if for a child to contain a reference to its parent (example).
But how do I enforce foreign keys in my implementation, which is the other way around (parent referencing children)?
First of all: You cannot use IEnumerable<T> for a collection navigation property. EF will just ignore this property. Use ICollection<T> instead.
When you have changed this, in your particular example you don't need to do anything because the foreign key property name follows the convention (name of primary key ParentId in principal entity Parent) so that EF will detect a required one-to-many relationship between Parent and Child automatically.
If you had another "unconventional" FK property name you still could define such a mapping with Fluent API, for example:
public class Child
{
[Key]
public int ChildId { get; set; }
public int SomeOtherId { get; set; }
[Required]
public string ChildName { get; set; }
}
Mapping:
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithRequired()
.HasForeignKey(c => c.SomeOtherId);
As far as I can tell it is not possible to define this relationship with data annotations. Usage of the [ForeignKey] attribute requires a navigation property in the dependent entity where the foreign key property is in.
I'm just a beginner in EF code first model. Given two POCO classes mapped to current legacy MS SQL database. They are associated with a composite foreign key setting up one to many relation. Since it's actually one-to-one relation I'd like to have corresponding navigation properties in my POCO objects and do mapping in fluent API. Here is my example:
public partial class Answer
{
//primary key
public int id { get; set; }
//foreign keys
public int question { get; set; }
public int assignedForm { get; set; }
//simple fields
public short state { get; set; }
public int author { get; set; }
//navigation property
public virtual AssignedQuestion AssignedQuestion { get; set; }
}
public partial class AssignedQuestion
{
// primary keys
public int id { get; set; }
public int assignedForm { get; set; }
//simple field
public string content { get; set; }
//navigation property
//public virtual ICollection<Answer> Answers { get; set; }
public virtual Answer Answer { get; set; }
}
If I wanted to do one-to-many relation I would simply uncomment "Answers" collection and have Fluent API mapping:
modelBuilder.Entity<AssignedQuestion>()
.HasKey(q => new { q.id, q.assignedForm });
modelBuilder.Entity<Answer>()
.HasRequired(a => a.AssignedQuestion)
.WithMany(aq=>aq.Answers)
.HasForeignKey(a => new { a.question,a.assignedForm});
My goal is to go with one-to-one relation and use "Answer" property in AssignedQuestion with such Fluent API as:
modelBuilder.Entity<AssignedQuestion>()
.HasKey(q => new { q.id, q.assignedForm });
modelBuilder.Entity<Answer>()
.HasRequired(a => a.AssignedQuestion)
.WithOptional(aq => aq.Answer);
//.HasForeignKey(a => new { a.question, a.assignedForm });
The problem is I can't specify exactly foreign key fields (as in previous example) and uncomment HasForeignKey call. In this case EF tries to join tables using conventional field names "AssignedQuestion_ID" and "AssignedQuestion_AssignedForm" instead of "question" and "assignedForm" in Answer table. Is there a walkaround in Fluent API other than changing field names?
It is not one-to-one relationship so your first mapping is correct. The reason why it is one-to-many is that EF understands one-to-one only when build on PKs on both sides. If AssignedQuestion has PK id and assignedForm your Answer will need to have FK and PK on its id and assignedForm otherwise EF doesn't see it as one-to-one relation. Even if you mark your question and assignedForm with unique constaint in database (to make it one-to-one in the database) EF will still not be able to handle it as one-to-one because it doesn't support unique constraints yet (except PK).