EntityType 'CustomUserLogin' has no key defined. Define the key for this EntityType - entity-framework

I've just started a new project in Web Forms because I thought it'd be quicker than learning MVC, but how wrong was I! I'm struggling with the Identity aspect of my project. I've followed this link here: http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity
And it all compiles ok and works (my ID column is now an integer).
So I've created a new class for a 'Property' context:
using System.Collections.Generic;
using System.Data.Entity;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Web.Security;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace MyApp.Models
{
public class PropertyContext : DbContext
{
public PropertyContext()
: base("DefaultConnection")
{
}
public DbSet<Property> Property { get; set; }
}
public class Property
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), ScaffoldColumn(false)]
public int PropertyID { get; set; }
//[Required, StringLength(128)]
//public string OwnerID { get; set; }
// [ForeignKey("OwnerID")]
//public virtual ApplicationUser ApplicationUser { get; set; }
//[ForeignKey("OwnerID")]
//public int OwnerID { get; set; }
// public virtual ApplicationUser ApplicationUser { get; set; }
public int OwnerID { get; set; }
[ForeignKey("OwnerID")]
public virtual ApplicationUser ID { get; set; }
[Required, StringLength(255), Display(Name = "Address 1")]
public string Address1 { get; set; }
[Required, StringLength(255), Display(Name = "Address 2")]
public string Address2 { get; set; }
[Required, StringLength(255), Display(Name = "Address 3")]
public string Address3 { get; set; }
[Required, StringLength(255), Display(Name = "Town/City")]
public string Settlement { get; set; }
[Required, StringLength(255), Display(Name = "County")]
public string County { get; set; }
[Required, StringLength(255), Display(Name = "Post Code")]
public string PostCode { get; set; }
[Required, StringLength(255), Display(Name = "Country")]
public string Country { get; set; }
}
}
Whereby I'm trying to make a new int column called 'OwnerID' a foreign key into the 'AspNetUser' table's ID column. When i build my project, it builds fine. However when I try to add a migration I get this:
MyApp.Models.CustomUserLogin: : EntityType 'CustomUserLogin' has no key defined. Define the key for this EntityType.
MyApp.Models.CustomUserRole: : EntityType 'CustomUserRole' has no key defined. Define the key for this EntityType.
CustomUserLogins: EntityType: EntitySet 'CustomUserLogins' is based on type 'CustomUserLogin' that has no keys defined.
CustomUserRoles: EntityType: EntitySet 'CustomUserRoles' is based on type 'CustomUserRole' that has no keys defined.
So in my ApplicationDbContext I've added this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>().ToTable("Users").HasKey<int>(u => u.Id);
modelBuilder.Entity<CustomUserRole>().ToTable("UserRoles").HasKey(ur => new { ur.RoleId, ur.UserId });
modelBuilder.Entity<CustomUserLogin>().ToTable("UserLogins").HasKey<int>(ul => ul.UserId);
modelBuilder.Entity<CustomUserClaim>().ToTable("UserClaims").HasKey<int>(uc => uc.Id);
modelBuilder.Entity<CustomRole>().ToTable("Roles").HasKey<int>(r => r.Id);
}
and even this
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<CustomUserLogin>().HasKey(cul => cul.UserId);
modelBuilder.Entity<CustomUserRole>().HasKey(cur => cur.UserId);
But it still doesn't work. And to be quite honest I'm burning up a whole weekend by stabbing in the dark. I can't believe something as simple as creating a foreign key is so difficult in this Entity Framework environment.
Can anybody give me a pointer? Thanks.

I did exactly as you did.
Added Identity to my MVC Web Application
Changed the primary key to be an INT rather a GUID
Added an EmployeeProfile and a CustomerProfile to the ApplicationUser
I did not have to override OnModelCreating as I made my own changes to the database tables to support what I needed.
Added two new properties to the ApplicationUser class in IdentityModels.cs
public virtual CustomerProfile CustomerProfile { get; set; }
public virtual EmployeeProfile EmployeeProfile { get; set; }
I created the CustomerProfile and EmployeeProfile classes. Note the Data Annotations on the Id property of each class. The Id property is the ForeignKey property to guide the virtual User property back to the ApplicationUser.
public class EmployeeProfile
{
[Key, ForeignKey("User")]
public int Id { get; set; }
public DateTime HireDate { get; set; }
public DateTime BirthDate{ get; set; }
public virtual ApplicationUser User { get; set; }
}
public class CustomerProfile
{
[Key, ForeignKey("User")]
public int Id { get; set; }
public CustomerType CustomerType { get; set; }
public string Company { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string WorkPhone { get; set; }
public string CustomerId { get; set; }
public string AccountType { get; set; }
public virtual ApplicationUser User { get; set; }
}
In my ApplicationDbContext class, I added the following lines of code:
public DbSet<CustomerProfile> CustomerProfile { get; set; }
public DbSet<EmployeeProfile> EmployeeProfile { get; set; }
Everything was working as expected. I logged in and saw the Customer and Employee info in my views.
I started seeing the error you received "EntityType 'CustomUserLogin' has no key defined. Define the key for this EntityType" when I added a many to many relation ship in the EmployeeProfile class to a Departments class. In my scenario an Employee can belong to 1 or many departments and a department can have many employees. My database changes were good and even the classes compiled just like you experienced, however once I ran the project, the website would error out.
Backing out my changes resolved the issue. Something in my classes was throwing off the validation of the model, so I'll need to approach the problem differently. So just like in my case, your poco classes may have an issue.
Remove your 'Property' class and references. Make the project work with the base essentials. Once you have it working, add the Property class back in with just the Id property.
Let me know if you still need help.

Place the base.OnModelCreating at the bottom:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<ApplicationUser>().ToTable("Users").HasKey<int>(u => u.Id);
modelBuilder.Entity<CustomUserRole>().ToTable("UserRoles").HasKey(ur => new { ur.RoleId, ur.UserId });
modelBuilder.Entity<CustomUserLogin>().ToTable("UserLogins").HasKey<int>(ul => ul.UserId);
modelBuilder.Entity<CustomUserClaim>().ToTable("UserClaims").HasKey<int>(uc => uc.Id);
modelBuilder.Entity<CustomRole>().ToTable("Roles").HasKey<int>(r => r.Id);
base.OnModelCreating(modelBuilder);
}

Related

Net Core ApplicationUser with foreign key to itself

I'm using Net Core 2.2 and Entity Framework. I have an ApplicationUser class used for multiple user types with foreign keys to each other but entity framework gives an error when adding a migration:
Unable to determine the relationship represented by navigation property 'ApplicationUser.Class' of type 'Class'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
I'm unsure what the proper way to implement this is. Can anyone help me?
My ApplicationUser class:
public class ApplicationUser : IdentityUser
{
public string CustomerId { get; set; }
public string TeacherId { get; set; }
public int? ClassId { get; set; }
public int? SettingsId { get; set; }
public virtual ApplicationUser Customer { get; set; }
public virtual ApplicationUser Teacher { get; set; }
public virtual Class Class { get; set; }
public virtual Settings Settings { get; set; }
public virtual ICollection<Class> Classes { get; set; }
public virtual ICollection<License> Licenses { get; set; }
public virtual ICollection<Exercise> Exercises { get; set; }
public virtual ICollection<GameProgress> GameProgresses { get; set; }
public virtual ICollection<StudentExercise> StudentExercises { get; set; }
}
And because it's in the error, my Class class:
public class Class
{
public int Id { get; set; }
public string TeacherId { get; set; }
public int? SettingsId { get; set; }
public virtual ApplicationUser Teacher { get; set; }
public virtual Settings Setting { get; set; }
public virtual ICollection<Exercise> Exercises { get; set; }
public virtual ICollection<ApplicationUser> Students { get; set; }
}
The problem has nothing to do with self-referential ApplicationUser relationships. The exception specifically tells you this is a problem with Class. The issue is that you've got a reference property to Class on ApplicationUser, but on Class, you've got both a collection of ApplicationUsers (Students) and a separate reference to ApplicationUser (Teacher). Simply, EF doesn't know which this foreign key is actually referring to.
The solution is exactly what the exception tells you: add fluent config to clear up the ambiguity:
public override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ApplicationUser>()
.HasOne(x => x.Class)
.WithMany(x => x.Students);
}

How can I name a navigation property a different name from it's entity name in my EF POCO?

I have a POCO Entity named Employee.
And then I have a second POCO Entity named Case.
I want a navigation property that looks like instead this:
public class Case : BaseEntity
{
public long EmployeeId { get; set; }
public virtual Employee Employee{ get; set; }
like this:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
public virtual Employee InitialContact { get; set; }
I want to name my property InitialContact. Not Employee.
But I get this error when EF tries to create the Database:
Unable to determine the relationship represented by navigation property 'Case.InitialContact' of type 'Employee'. Either manually configure the relationship, or ignore this property from the model.
Update 1:
I got it to work like this:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
[ForeignKey("Id")]
public virtual Employee InitialContact { get; set; }
public DateTime InitalConsultDate { get; set; }
public Guid AppUserId { get; set; }
public virtual AppUser LerSpecialist { get; set; }
}
The primary key is ID in my BaseEntity. Not EmployeeId.
But I have second part to my question.
Here is my Complete Employee POCO:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using Hrsa.Core.Generic.Model.Framework.Concrete;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Hrsa.Core.Generic.Model.Lerd
{
public class Employee : BaseEntity
{
[BindNever]
public string Email { get; set; }
[BindNever]
public long OrganizationId { get; set; }
[BindNever]
public string Supervisor { get; set; }
[BindNever]
public string SupervisorEmail { get; set; }
[BindNever]
public string FirstName { get; set; }
[BindNever]
public string LastName { get; set; }
public string Notes { get; set; }
[BindNever]
public long BargainingUnitId { get; set; }
[BindNever]
public long PayPlanId { get; set; }
[BindNever]
public long GradeRankId { get; set; }
[BindNever]
public long PositionTitleId { get; set; }
[BindNever]
public long SeriesId { get; set; }
public bool IsUnionEmployee { get; set; }
public virtual Organization Organization { get; set; }
public virtual BargainingUnit BargainingUnit { get; set; }
public virtual PayPlan PayPlan { get; set; }
public virtual GradeRank GradeRank { get; set; }
public virtual PositionTitle PositionTitle { get; set; }
public virtual Series Series { get; set; }
public virtual ICollection<UnionHours> UnionHours { get; set; }
public virtual ICollection<Case> Cases { get; set; }
[NotMapped]
public string UnionEmployeeYesNo => (IsUnionEmployee) ? "Yes" : "No";
}
}
I want my Employee to have many Cases:
public virtual ICollection<Case> Cases { get; set; }
Here is my complete Cases POCO:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
[ForeignKey("Id")]
public virtual Employee InitialContact { get; set; }
public DateTime InitalConsultDate { get; set; }
public Guid AppUserId { get; set; }
public virtual AppUser LerSpecialist { get; set; }
}
So now my DB looks like this:
So I have my InitialContactId in Cases ok.
But now I need my Case to have many Employees.
So I add this in to my Case POCO:
public virtual ICollection<Employee> Employees { get; set; }
Now it looks like this:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
[ForeignKey("Id")]
public virtual Employee InitialContact { get; set; }
public DateTime InitalConsultDate { get; set; }
public Guid AppUserId { get; set; }
public virtual AppUser LerSpecialist { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
Now when I run it, I get this error again:
Unable to determine the relationship represented by navigation property 'Case.InitialContact' of type 'Employee'. Either manually configure the relationship, or ignore this property from the model.
Update 2:
I found this article for a Many-Many relationship in .Net Core 1:
http://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration
So now I have a bridge lookup entity:
public class EmployeeCase
{
[ForeignKey("Id")]
public long EmployeeId { get; set; }
public Employee Employee { get; set; }
[ForeignKey("Id")]
public long CaseId { get; set; }
public Case Case { get; set; }
}
Employee POCO:
Changed:
public virtual ICollection<Case> Cases { get; set; }
to:
// Mapping - Collection of Cases
public virtual ICollection<EmployeeCase> EmployeeCases { get; set; }
Case POCO:
Changed:
public virtual ICollection<Employee> Employees { get; set; }
to:
// Mapping - Collection of Employees
public virtual ICollection<EmployeeCase> EmployeeCases { get; set; }
In my AppDbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
#region Many-to-Many Employees Cases
modelBuilder.Entity<EmployeeCase>()
.HasKey(ec => new { ec.EmployeeId, ec.CaseId });
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Employee)
.WithMany(e => e.EmployeeCases)
.HasForeignKey(ec => ec.EmployeeId);
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Case)
.WithMany(c => c.EmployeeCases)
.HasForeignKey(ec => ec.CaseId);
#endregion
}
Now when I run I get this error:
An exception of type 'System.Data.SqlClient.SqlException' occurred in Microsoft.EntityFrameworkCore.Relational.dll but was not handled in user code
Additional information: Introducing FOREIGN KEY constraint 'FK_EmployeeCase_Employees_EmployeeId' on table 'EmployeeCase' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
Update 3:
Finally got my tables the way I want with this piece of code from:
Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why?
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Get rid of Cascading Circular error on ModelBuilding
foreach (var relationShip in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationShip.DeleteBehavior = DeleteBehavior.Restrict;
}
#region Many-to-Many Employees Cases
modelBuilder.Entity<EmployeeCase>()
.HasKey(ec => new { ec.EmployeeId, ec.CaseId });
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Employee)
.WithMany(e => e.EmployeeCases)
.HasForeignKey(ec => ec.EmployeeId);
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Case)
.WithMany(c => c.EmployeeCases)
.HasForeignKey(ec => ec.CaseId);
#endregion
base.OnModelCreating(modelBuilder);
}
Update 4:
This did not work after all.
Remvoving the delete behavior for everything messes up my other relationships and I get errors.
How can I fix this?
This is disgusting.
So wishing I did not go Core.
Entity Framework uses conventions to guess how to map your C# model to database objects.
In your case you violate convention by custom name, so you should explain Entity Framework how to map this stuff.
There are two possible ways: attributes and fluent API. I'd suggest to use the latter one.
See section "Configuring a Foreign Key Name That Does Not Follow the Code First Convention" here: Entity Framework Fluent API - Relationships
I have made it a habit of explicitly defining my relationships as EF does not always get them the way I want. I like to create a Mapping folder that contains my entity maps. The fluent api works great for this and inherits from EntityTypeConfiguration.
Try this.
public class CaseMap : EntityTypeConfiguration<Case>
{
public CaseMap()
{
HasKey(m => m.Id)
HasRequired(m => m.InitialContact)
.WithMany(e => e.Cases)
.HasForeignKey(m => m.InitialContactId);
}
}
Almost forgot. You need to tell your DbContext where to find these mappings. Add this to your DbContexts OnModelCreating method.
modelBuilder.Configurations.AddFromAssembly(typeof(MyContext).Assembly);
This is what worked finally for the Cascading Delete circular references on the many-to-many in EF Core:
// Get rid of Cascading Delete Circular references error.
var type = modelBuilder.Model.GetEntityTypes().Single(t => t.Name == "Hrsa.Core.Generic.Model.Lerd.EmployeeCase");
foreach (var relationship in type.GetForeignKeys())
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
You have to get the Entity representing the many to many lookup only.
And from there restrict the DeleteBehavior.

Entity Framework Code First relation in one table

I've a problem with creating a relation in one table. I have a UserProfile class. An another user can by only created by existing user. So I want to have fields with information about who created user and when the user was created. Maybe the code will say more :)
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Required, MaxLength(30)]
public string UserName { get; set; }
[Column(TypeName = "date")]
[Display(Name = "Data ważności")]
public DateTime ExpirationDate { get; set; }
[DataType(DataType.DateTime)]
[Display(Name = "Data dodania")]
[Column(TypeName = "datetime")]
public DateTime sysRegDate { get; set; }
[ForeignKey("sysRegUser")]
public int? sysRegUserId { get; set; }
[DataType(DataType.DateTime)]
[Column(TypeName = "datetime")]
public DateTime sysModDate { get; set; }
[ForeignKey("sysModUser")]
public int? sysModUserId { get; set; }
public virtual UserProfile sysRegUser { get; set; }
public virtual UserProfile sysModUser { get; set; }
}
But when I'm creating a migration I get error:
System.Data.Entity.ModelConfiguration.ModelValidationException: One or more validation errors were detected during model generation:
\tSystem.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'UserProfile_sysModUser_Source' in relationship 'UserProfile_sysModUser'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
Thanks in advance for answers.
I have used this code to create a parent-child relation, maybe it helps:
public class MyModel
{
public int Id {get;set;}
// ...
public int? ParentId {get;set;}
public virtual MyModel Parent {get;set;}
public virtual IList<MyModel> Children {get;set;}
}
And in the DbContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyModel>().HasOptional(p => p.Parent).WithMany(p => p.Children).HasForeignKey(p => p.ParentId);
modelBuilder.Entity<MyModel>().Property(p => p.ParentId).HasColumnName("Parent");
}

How to map two properties in one code first object to the same parent type

I've been at this for hours and have tried many suggestions I found searching but no luck. I'm using code first EF 5.
The situation is that I have a class Employee. Then I have another class that has two properties on it, both are of type Employee. I want these both to be foreign key constraints but the requirements allow many of the same requests to and from the same users so I can't just use them as keys. I don't really care about Employee having the two collections for navigation but in my working through the problem that seemed a requirement. If it simplifies the problem I can remove those.
I get this message.
System.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'Employee_RequestsForEmployee_Target' in relationship 'Employee_RequestsForEmployee'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
I've tried this using the Fluent API in the OnModelCreation method of the context;
modelBuilder.Entity()
.HasRequired(u => u.ForEmployee)
.WithMany()
.HasForeignKey(u => u.ForEmployeeId);
modelBuilder.Entity<RevenueTransferRequest>()
.HasRequired(u => u.FromEmployee)
.WithMany()
.HasForeignKey(u => u.FromEmployeeId);
The classes in conflict are (I've removed some properties for clarity);
public class Employee : IEmployee
{
[Key]
public string Id { get; set; }
[InverseProperty("ForEmployee")]
public ICollection<RevenueTransferRequest> RequestsForEmployee { get; set; }
[InverseProperty("FromEmployee")]
public ICollection<RevenueTransferRequest> RequestsFromEmployee { get; set; }
}
public class RevenueTransferRequest : IRevenueTransferRequest
{
[Key]
public Guid Id { get; set; }
[Required]
[ForeignKey("ForEmployee")]
public String ForEmployeeId { get; set; }
[InverseProperty("RequestsForEmployee")]
public Employee ForEmployee { get; set; }
[Required]
[ForeignKey("FromEmployee")]
public String FromEmployeeId { get; set; }
[InverseProperty("RequestsFromEmployee")]
public Employee FromEmployee { get; set; }
}
Any help would be much appreciated. Thanks in advance.
I never did figure out how to do it using data annotations but using the Fluent API I was able to do it. What I was missing was that I had to specify in the HasMany() method what the relationship on the other side was which I assumed was understood through the data annotations and conventions.
This is called in the DbContext OnModelCreating override (The WillCascadeOnDelete(false) is related to another issue).
modelBuilder.Entity<RevenueTransferRequest>()
.HasRequired(e => e.FromEmployee)
.WithMany(x=>x.RequestsFromEmployee)
.WillCascadeOnDelete(false);
modelBuilder.Entity<RevenueTransferRequest>()
.HasRequired(e => e.ForEmployee)
.WithMany(x => x.RequestsForEmployee)
.WillCascadeOnDelete(false);
With the classes:
[Key]
public String Id { get; set; }
public String BusinessUnitLeaderId { get; set; }
public Employee BusinessUnitLeader { get; set; }
[Required]
[MaxLength(150)]
public String DisplayName { get; set; }
public ICollection<Project> BusinessUnitLeaderProjects { get; set; }
public ICollection<RevenueTransferRequest> RequestsForEmployee { get; set; }
public ICollection<RevenueTransferRequest> RequestsFromEmployee { get; set; }
public class RevenueTransferRequest
{
[Key]
public Guid Id { get; set; }
[Required]
public String ForEmployeeId { get; set; }
public Employee ForEmployee { get; set; }
[Required]
public String FromEmployeeId { get; set; }
public Employee FromEmployee { get; set; }
[Required]
public String ProjectId { get; set; }
public Project Project { get; set; }
[Required]
public Double? TransferAmount { get; set; }
public int WorkflowState { get; set; }
}

M:M Mapping - EF 4.3 CodeFirst (Existing Database)

I have two tables (Table A, Table B) joined with a join table (TableAB) with 3 payload columns. By Payload I mean columns apart from Id, TableAId, and TableBId.
I can insert into all tables successfully, but I need to insert data into one of the payload columns on Insert. I'm using EF 4.3, Fluent API. Can anyone help? Thanks in advance.
public class Organisation : EntityBase<int>, IAggregateRoot
{
public string Name { get; set; }
public string Url { get; set; }
public int CountryId { get; set; }
public int? OwnershipTypeId { get; set; }
public int OrganisationStatusId { get; set; }
public virtual ICollection<Feature> Features { get; set; }
public virtual ICollection<OrganisationType> OrganisationTypes { get; set; }
public virtual ICollection<PricePlan> PricePlans { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class User: EntityBase<Guid>, IAggregateRoot
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string JobTitle { get; set; }
public int? PhoneCallingCodeId { get; set; }
public int? PhoneAreaCode{ get; set; }
public string PhoneLocal { get; set; }
public int? MobileCallingCodeId { get; set; }
public int? MobileAreaCode { get; set; }
public string MobileLocal { get; set; }
public virtual ICollection<Organisation.Organisation> Organisations { get; set; }
}
public class OrganisationUser : EntityBase<int>, IAggregateRoot
{
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int OrganisationRoleId {get; set;}//Foreign Key - have tried leaving it out, tried it as public virtual Organisation Organisation {get;set;
public bool IsApproved { get; set; }
}
public class SDContext : DbContext
{
public ObjectContext Core
{
get
{
return (this as IObjectContextAdapter).ObjectContext;
}
}
public IDbSet<User> User { get; set; }
public IDbSet<Organisation> Organisation { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Organisation>().HasMany(u => u.Users).WithMany(o => o.Organisations).Map(m =>
{
m.MapLeftKey("OrganisationId");
m.MapRightKey("UserId");
m.ToTable("OrganisationUser");
});
//I have tried specifically defining the foreign key in fluent, but I really need to understand how I can add the payload properties once I access and edit them.
Your mapping is not correct for your purpose. If you want to treat OrganisationUser as an intermediate entity between Organisation and User you must create relationships between Organisation and OrganisationUser and between User and OrganisationUser, not directly between Organisation and User.
Because of the intermediate entity which contains its own scalar properties you cannot create a many-to-many mapping. EF does not support many-to-many relationships with "payload". You need two one-to-many relationships:
public class Organisation : EntityBase<int>, IAggregateRoot
{
// ...
// this replaces the Users collection
public virtual ICollection<OrganisationUser> OrganisationUsers { get; set; }
}
public class User : EntityBase<Guid>, IAggregateRoot
{
// ...
// this replaces the Organisations collection
public virtual ICollection<OrganisationUser> OrganisationUsers { get; set; }
}
public class OrganisationUser : EntityBase<int>, IAggregateRoot
{
public int OrganisationId { get; set; }
public Organisation Organisation { get; set; }
public Guid UserId { get; set; }
public User User { get; set; }
// ... "payload" properties ...
}
In Fluent API you must replace the many-to-many mapping by the following:
modelBuilder.Entity<Organisation>()
.HasMany(o => o.OrganisationUsers)
.WithRequired(ou => ou.Organisation)
.HasForeignKey(ou => ou.OrganisationId);
modelBuilder.Entity<User>()
.HasMany(u => u.OrganisationUsers)
.WithRequired(ou => ou.User)
.HasForeignKey(ou => ou.UserId);
Your derived DbContext may also contain a separate set for the OrganisationUser entity:
public IDbSet<OrganisationUser> OrganisationUsers { get; set; }
It's obvious now how you write something into the intermediate table:
var newOrganisationUser = new OrganisastionUser
{
OrganisationId = 5,
UserId = 8,
SomePayLoadProperty = someValue,
// ...
};
context.OrganisastionUsers.Add(newOrganisastionUser);
context.SaveChanges();
If you want to make sure that each pair of OrganisationId and UserId can only exist once in the link table, it would be better to make a composite primary key of those two columns to ensure uniqueness in the database instead of using a separate Id. In Fluent API it would be:
modelBuilder.Entity<OrganisationUser>()
.HasKey(ou => new { ou.OrganisationId, ou.UserId });
More details about such a type of model and how to work with it is here:
Create code first, many to many, with additional fields in association table