Identity framework composite key - entity-framework

I'm working with Entity Framework and identity framework (IdentityUser, IdentityRole). I have a table with a composite key (table Country) that points to the Users table.
Unfortunately, EF can only build a relation when all keys are the same, otherwise you'll get this:
The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
So, how can I handle this? Right now I have tried to add this composite key to the ApplicationUser:IdentityUser too, but in this way I have to add the composite key to all entities of the identity framework (that means user, role, claim, login, ...).
Here are my model classes:
class Country
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
public int ID { get; set; }
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 2)]
public int Version { get; set; }
[Required]
[ForeignKey(nameof(Chief))]
[Column(Order = 1)]
public string Chief_Id { get; set; }
[Required]
[ForeignKey(nameof(Chief)), Column(Order = 2)]
public int Chief_Version { get; set; }
public virtual ApplicationUser Chief{ get; set; }
}
class ApplicationUser : IdentityUser
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
public override string Id
{
get { return base.Id; }
set { base.Id = value; }
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 2)]
public int Version { get; set; }
}
Nico

Look into your foreign key nameof(Chief), you might not have the full relationship set up.
There is another question that might help here.

Related

Code first mapping in entity framework

I have created 3 tables using Code First Approach. I get the following Model Validation Exception when i execute a Find on student table.
Student_courses_Target_Student_courses_Source: : The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
public class University
{
[Key]
public string Uni_ID { get; set; }
public virtual List<Course> Courses { get; set; }
}
public class Course
{
[Key]
[Column(Order = 1)]
public string Course_ID { get; set; }
[Key,ForeignKey("uni")]
[Column(Order = 2)]
public string Uni_ID { get; set; }
public virtual ICollection<Student> Students { get; set; }
public virtual University uni { get; set; }
}
public class Student
{
[Key,ForeignKey("course"), Column(Order = 1)]
public string Course_ID { get; set; }
[ForeignKey("course"),Column(Order = 2)]
public string Uni_ID { get; set; }
[Key]
[Column(Order = 3)]
public string Student_ID { get; set; }
public virtual Course course { get; set; }
}
By my understanding , the exception means that i have not mapped my foreign keys in student table to the primary keys in course table. But i have done it . Is there an issue as to how the 'Uni_ID' occurs as Primary key in both University and Course Tables and perhaps i have gone wrong in referencing it as foreign key in the Student table ?

Mapping composite foreign key to composite primary key where the foreign key is also a primary key

I want to make VM_hostname,datetime and name properties as a composite Key for Disk class . At the same time VM_hostname and datetime of Disk class should refer to VM_hostname and datetime of VirtualMachine class (ie Foreign keys) .
I did this but it gives me this exception :
The ForeignKeyAttribute on property 'datetime' on type 'WebJob1.Historical.Disk' is not valid. The navigation property 'Datetime' was not found on the dependent type 'WebJob1.Historical.Disk'. The Name value should be a valid navigation property name
Anyone have a clue ? Also, please note that im using Data Annotation.
public class VirtualMachine
{
[Key]
[Column(Order = 0)]
public string VM_Hostname { get; set; }
[Key]
[Column(Order = 1)]
public DateTime Datetime;
public virtual List<Disk> disks { get; set; }
}
public class Disk
{
[Key,ForeignKey("VirtualMachine"),Column(Order = 0)]
public string VM_hostname { get; set; }
[Key,ForeignKey("Datetime"), Column(Order = 1)]
public DateTime datetime { get; set; }
[Key, Column(Order = 2)]
public string name { get; set; }
public virtual VirtualMachine VirtualMachine{ get; set; }
}
The main difference between your question and the one I suggested as duplicate is that your ForeignKey attributes don't refer -
from a primitive property to a navigation property
from a navigation property to a primitive property
In your case, the reference is from a primitive property to another primitive property, in another type. Also, little detail, VirtualMachine.Datetime should be a property, not a member. But I have to admit that the "duplicate" didn't cover your case.
So let's try to make this into a comprehensive answer how to handle this situation in Entity Framework 6. I'll use an abstracted model to explain the various options:
public class Parent
{
public int Id1 { get; set; } // Key
public int Id2 { get; set; } // Key
public string Name { get; set; }
public virtual List<Child> Children { get; set; }
}
public class Child
{
public int Id1 { get; set; } // Key
public int Id2 { get; set; } // Key
public int Id3 { get; set; } // Key
public string Name { get; set; }
public virtual Parent Parent { get; set; }
}
There are three options to setup the mappings.
Option 1
Data annotations, ForeignKey attribute:
public class Parent
{
[Key]
[Column(Order = 1)]
public int Id1 { get; set; }
[Key]
[Column(Order = 2)]
public int Id2 { get; set; }
public string Name { get; set; }
public virtual List<Child> Children { get; set; }
}
public class Child
{
[Key]
[Column(Order = 0)]
public int Id1 { get; set; }
[Key]
[Column(Order = 1)]
public int Id2 { get; set; }
[Key]
[Column(Order = 2)]
public int Id3 { get; set; }
public string Name { get; set; }
[ForeignKey("Id1,Id2")]
public virtual Parent Parent { get; set; }
}
As you see, here the ForeignKey attribute refers from a navigation property to primitive properties. Also, the absolute numbers in the column order don't matter, only their sequence.
Option 2
Data annotations, InverseProperty attribute:
public class Parent
{
[Key]
[Column(Order = 1)]
public int Id1 { get; set; }
[Key]
[Column(Order = 2)]
public int Id2 { get; set; }
public string Name { get; set; }
public virtual List<Child> Children { get; set; }
}
public class Child
{
[Key]
[Column(Order = 0)]
[InverseProperty("Children")]
public int Id1 { get; set; }
[Key]
[Column(Order = 1)]
[InverseProperty("Children")]
public int Id2 { get; set; }
[Key]
[Column(Order = 2)]
public int Id3 { get; set; }
public string Name { get; set; }
public virtual Parent Parent { get; set; }
}
InverseProperty points from one or more properties in a type at one end of a relationship to a navigation property in the type on the other end of the relationship. Another way to achieve the same mapping is to apply [InverseProperty("Parent")] on both key properties of Parent.
Option 3
Fluent mapping:
modelBuilder.Entity<Parent>().HasKey(p => new { p.Id1, p.Id2 });
modelBuilder.Entity<Child>().HasKey(p => new { p.Id1, p.Id2, p.Id3 });
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithRequired(c => c.Parent)
.HasForeignKey(c => new { c.Id1, c.Id2 });
As said in the comments, fluent mapping is less error-prone than data annotations. Data annotations offer too many options to configure mappings and it's not always easy to see which parts are connected. That's why fluent mapping is my favorite.
Entity Framework Core
In EF-core (current version 3.1.6) composite primary keys can't be modeled by data annotations. It throws a run-time exception:
Entity type 'Parent' has composite primary key defined with data annotations. To set composite primary key, use fluent API.
So for EF-core only option 3 is feasible. The mapping is almost identical:
modelBuilder.Entity<Parent>().HasKey(p => new { p.Id1, p.Id2 });
modelBuilder.Entity<Child>().HasKey(p => new { p.Id1, p.Id2, p.Id3 });
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithOne(c => c.Parent) // Different here
.HasForeignKey(c => new { c.Id1, c.Id2 });

Entity Framework Add-Migration cannot detect changes that I made on foreign key

I am using Entity Framework Code First approach. I was able to use Add-Migration to automatically detect when I added new table into my context. But, when I change the foreign key on one of the models, Add-Migration unable to detect the changes and generate the codes to update the foreign key.
Here is my Model
public class PhoneInfo
{
[Key]
[Column(Order = 2)]
public String ID { get; set; }
[Required]
[Key]
[ForeignKey("Contact"), Column(Order = 1)]
public String UserID { get; set; }
[Required]
[Key]
[ForeignKey("Contact"), Column(Order = 0)]
public String ContactID { get; set; }
public String Value { get; set; }
public DateTime LastChanged { get; set; }
public String Type { get; set; }
public int Order { get; set; }
public bool IsDeleted { get; set; }
public DateTime DeletedDate { get; set; }
public virtual Contact Contact { get; set; }
}
I am updating the Column(Order = 1) below
[ForeignKey("Contact"), Column(Order = 1)]
Is there any ways to make Add-Migration automatically generate codes that I needed to update the database?
for migrations you need to execute two commands in package manager console:
1. Add-Migration AddOrder1ContactFK
2. Update-Database
Set AutomaticMigrationsEnabled property to true which is defined in the constructor of the Configuration.cs. Its default value is false when you first enable the migrations. Then rebuilt your solution and try update-database once more.

Referencing a "partition table" foreign key in Multi-Tenant Code-First Entity Framework

I am creating a multi-tenant application that needs to be built in a single database. To partition the tables, we have a Tenant entity whose primary key will be referenced as part of the keys of other tables that need partitioning. The Tenant entity looks like this:
public class Tenant
{
[Key]
public string TenantId { get; set; }
public string TenantName { get; set; }
}
An example of where this partitioning is used is in a Store-Item scenario, where a tenant can have multiple stores as well as multiple items. Stores can have multiple items, and their relationships are maintained in a StoreItem entity. Our current implementation looks like this:
Store Entity
public class Store
{
[Key, Column(Order = 1)]
public string TenantId { get; set; }
[Key, Column(Order = 2)]
public string StoreId { get; set; }
[ForeignKey("TenantId")]
public virtual Tenant Tenant { get; set; }
}
Item Entity
public class Item
{
[Key, Column(Order = 1)]
public string TenantId { get; set; }
[Key, Column(Order = 2)]
public string ItemId { get; set; }
[ForeignKey("TenantId")]
public virtual Tenant Tenant { get; set; }
}
StoreItem Entity
public class StoreItem
{
[Key, Column(Order = 1)]
public string TenantId { get; set; }
[Key, Column(Order = 2)]
public string StoreId { get; set; }
[Key, Column(Order = 3)]
public string ItemId { get; set; }
[ForeignKey("TenantId")]
public virtual Tenant Tenant { get; set; }
[ForeignKey("StoreId")]
public virtual Store Store { get; set; }
[ForeignKey("ItemId")]
public virtual Item Item { get; set; }
}
When we try to build the database, I encounter the ff. error:
StoreItem_Item_Target_StoreItem_Item_Source: : The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
StoreItem_Store_Target_StoreItem_Store_Source: : The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
What is wrong with the way I structured my keys? Should TenantId not be referenced as part of the Key for other entities?
The problem is that you're using [ForeignKey("ItemId")] to describe part of the relationship between StoreItem and Item but Item has multiple key columns. If you need the TenantId to be part of the key -- which you shouldn't if ItemId is enough to uniquely identify an Item record -- then I think you'd need to use the fluent API to define the relationship.
On the other hand, I'm guessing you don't need TenantId as a key column for either Item or Store, which would simplify things greatly.

EntityFramework - Composite Key Table with FK to another Composite Key Table

I've two tables both with composite primary keys. Both have in common one of the primary key's with a foreign key to another table.
The problem is that when i create the migrations, it messes up the foreign keys.
I must use data annotations.
Example:
public class City
{
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string CityCode { get; set; }
[Key, Column(Order = 2)]
public string CompanyCode { get; set; }
public string Description { get; set; }
[ForeignKey("CompanyCode")]
public virtual Company Company { get; set; }
}
public class PostCode
{
[Key, Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Code { get; set; }
[Key, Column(Order = 1)]
public string CompanyCode { get; set; }
public string Description { get; set; }
public string CityCode { get; set; }
[ForeignKey("CompanyCode")]
public virtual Company Company { get; set; }
[ForeignKey("CityCode, CompanyCode")]
public virtual City City { get; set; }
}
PostCode and City have a composite primary key (Code, CompanyCode).
PostCode has a foreign key to the table City (CityCode, CompanyCode).
The problem is that the CompanyCode is part of the primary key and at the same time is part of the composite foreign key to City.
When i say that it messes up the foreign keys i mean the following:
CONSTRAINT [FK_dbo.PostCodes_dbo.Companies_CompanyCode] FOREIGN KEY ([CompanyCode]) REFERENCES [dbo].[Companies] ([CompanyCode]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.PostCodes_dbo.Cities_CompanyCode_CityCode] FOREIGN KEY ([CompanyCode], [CityCode]) REFERENCES [dbo].[Cities] ([CityCode], [CompanyCode])
In the second constraint, it references CompanyCode with CityCode and CityCode with CompanyCode.
I can't find any example in the internet with any scenario like this.
Where am i wrong?
Thanks in advance.
Edit 1
Between City and Company there is a simples primar key CompanyCode.
The same for PostCodes and Company.
If between City and Company you want to create a one-to-one relationship I'm afraid that is not possible following your model. When you are configuring a one-to-one relationship, Entity Framework requires that the primary key of the dependent end also be the foreign key, otherwise EF doesn't see it as one-to-one relationship. The dependend end in your case es City, but you have a problem, you want to add another PK, that is CityCode, that breaks what it means a one to one relationship because, for example, the below records could happen:
Company City
Id CityCode CompanyId
1 ee33a 1
2 aa23b 1
That's way, if you want to achieve your escenario, I guess that you have to create a one-to-many relationship between Company and City. Using Data Annotations could be this way:
public class City
{
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string CityCode { get; set; }
[Key, ForeignKey("Company"),Column(Order = 2)]
public string CompanyCode { get; set; }
public string Description { get; set; }
public virtual Company Company { get; set; }
}
public class Company
{
public string Id { get; set; }
public virtual ICollection<City> Cities { get; set; }
}
You can omit the Cities navigation property in Company if you don't want have reference to the cities related to a Company.
The same applies to the PostCode Entity.
Update:
To achieve what you want in the PostCode entity, you have to map the FKs this way:
public class PostCode
{
[Key, Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Code { get; set; }
[Key,ForeignKey("City"), Column(Order = 2)]
public string CityCompanyCode { get; set; }
public string Description { get; set; }
[ForeignKey("City"), Column(Order = 1)]
public string CityCode { get; set; }
public virtual City City { get; set; }
[ForeignKey("Company")]
public string CompanyCode { get; set; }
public virtual Company Company { get; set; }
}
Here is a good example of how you should treat the composite FKs
I always add a primary key to my tables, por example: CityId int, PostCode int. with this i resolve relations.
public class PostCode
{
[Key, Column(Order = 0)]
public string PostCodeId { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Code { get; set; }
public string CompanyCode { get; set; }
public string Description { get; set; }
public string CityCode { get; set; }
[ForeignKey("CompanyCode")]
public virtual Company Company { get; set; }
[ForeignKey("CityCode, CompanyCode")]
public virtual City City { get; set; }
}
thanks