EF 6.1.3 Multiplicity conflicts - entity-framework

I have been getting error when I try to run add-migration script.
The error is:
Domain.DataAccessLayer.AllRoutines_Product: : Multiplicity conflicts with the referential constraint in Role 'AllRoutines_Product_Target' in relationship 'AllRoutines_Product'. Because all of the properties in the Dependent Role are non-nullable, multiplicity of the Principal Role must be '1'.
And I cannot figure what I am doing wrong. I have AllRoutines and Product entities. AllRoutines can have 0 or 1 Products. Here is my AllRoutines class (some code has been omitted for clarity):
public class AllRoutines
{
[Key]
public long Id { get; set; }
public string Name { get; set; }
public string Tags { get; set; }
public int? RoutineLevelId { get; set; }
public RoutineLevel RoutineLevel { get; set; }
public Guid? ProductId { get; set; }
public virtual Product Product { get; set; }
}
Here is FluetnApi mapping (again some code is omitted):
public void MapRoutine(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<RoutineLevel>().HasKey(r => r.RoutineLevelId);
modelBuilder.Entity<AllRoutines>()
.HasKey(r => r.Id)
.Property(r => r.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<AllRoutines>()
.HasOptional(r => r.RoutineLevel)
.WithMany()
.HasForeignKey(r => r.RoutineLevelId);
modelBuilder.Entity<AllRoutines>().HasOptional(c => c.Product)
.WithMany()
.HasForeignKey(c => c.ProductId)
.WillCascadeOnDelete(false);
}
Also I am not sure if this is important or not, but there is also class CustomRoutine which inherits AllRoutines and looks like this:
public class CustomRoutine : AllRoutines
{
public DateTime DateCreated { get; set; }
public string UserId { get; set; }
public User UserWhoCreatedRoutine { get; set; }
}
The inheritance approach was Table per Hierarchy.
I've tried to add to mapping configuration this:
modelBuilder.Entity<CustomRoutine>().HasOptional(c => c.Product)
.WithMany()
.HasForeignKey(c => c.ProductId)
.WillCascadeOnDelete(false);
But the error was same. I am not sure why this is happening, because, as you can see in the code same mapping was already done (without any problems) for RoutineLevel, also I have same mapping for Product and the other class, again with no problems.
EDIT
Here is also Product class:
public class Product
{
public Guid Id { get; protected set; }
public string Code { get; set; }
public string Name { get; set; }
public bool IsFree { get; set; }
public ICollection<SubscriptionProduct> SubscriptionProducts { get; set; }
}
And FluentAPI mapping:
modelBuilder.Entity<Product>()
.HasKey(p => p.Id);
modelBuilder.Entity<Product>()
.Property(p => p.Code)
.IsRequired()
.HasMaxLength(10);
modelBuilder.Entity<Product>()
.Property(p => p.Name)
.IsRequired()
.HasMaxLength(100);
modelBuilder.Entity<Product>()
.HasMany(p => p.SubscriptionProducts)
.WithRequired()
.HasForeignKey(p => p.ProductId)
.WillCascadeOnDelete(true);

Related

Correct way to use Many2Many in EF Core6?

I am quite new to EF Core 6.0. We currently have a projet to upgrade, we cannot change the actual tables (use by another program) so we use Database fisrt approch.
So I need to add some Permission on user (the database are in french) We curently have an UsagerEW table (user table) and we add an Permission Table and an joint table PermissionUsagerEW for the Many2Many. After doing Scaffold-dbContect here is the result:
UsagerEW (primary key is Code_Int)
public partial class UsagerEW
{
public UsagerEW()
{
PermissionUsagerEW = new HashSet<PermissionUsagerEW>();
RefreshToken = new HashSet<RefreshToken>();
}
public string Code { get; set; }
public string Nom { get; set; }
public string Email { get; set; }
public string ModeLogin { get; set; }
public string PasswordTemp { get; set; }
public DateTime? PasswordTempExp { get; set; }
public int code_int { get; set; }
public virtual ICollection<PermissionUsagerEW> PermissionUsagerEW { get; set; }
}
Pemrssion and PermissionUsagerEW
public partial class Permission
{
public Permission()
{
PermissionUsagerEW = new HashSet<PermissionUsagerEW>();
}
public int id { get; set; }
public string code { get; set; }
public string description { get; set; }
public int? moduleId { get; set; }
public virtual Module module { get; set; }
public virtual ICollection<PermissionUsagerEW> PermissionUsagerEW { get; set; }
}
public partial class PermissionUsagerEW
{
public int id { get; set; }
public int permissionId { get; set; }
public int usagerCodeInt { get; set; }
public virtual Permission permission { get; set; }
public virtual UsagerEW usagerCodeIntNavigation { get; set; }
}
That compile and I can "navigate with include" from UsagerEW and get an list of PermissionUsagerEW for a specific UsagerEW.
Now like I am in EF COre 6.0 that supposed to support Many2Many
I add this nav propertie in the Permnission class
public virtual ICollection<UsagerEW> UsagerEW { get; set; }
and this in the UsagerEW class:
public virtual ICollection<Permission> Permission { get; set; }
But I got execution error either I just try to load some user wintout any include:
UsagerEW user = _EWContext.UsagerEW.Where(u=>u.Code == usagerId).SingleOrDefault();
System.InvalidOperationException: 'Cannot use table
'PermissionUsagerEW' for entity type 'PermissionUsagerEW
(Dictionary<string, object>)' since it is being used for entity type
'PermissionUsagerEW' and potentially other entity types, but there is
no linking relationship. Add a foreign key to 'PermissionUsagerEW
(Dictionary<string, object>)' on the primary key properties and
pointing to the primary key on another entity type mapped to
'PermissionUsagerEW'.'
The FK are detect by the scaffold:
modelBuilder.Entity<PermissionUsagerEW>(entity =>
{
entity.HasOne(d => d.permission)
.WithMany(p => p.PermissionUsagerEW)
.HasForeignKey(d => d.permissionId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_PermissionUsager_Permission");
entity.HasOne(d => d.usagerCodeIntNavigation)
.WithMany(p => p.PermissionUsagerEW)
.HasForeignKey(d => d.usagerCodeInt)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_PermissionUsager_Usager");
});
Any idea?
---EDIT 1
I change your code to reflect the scaffolded PermissionUsagerEW table:
//--UsagewrEW
modelBuilder.Entity<UsagerEW>()
.HasKey(u => u.code_int);
modelBuilder.Entity<UsagerEW>()
.HasMany(u => u.Permissions)
.WithMany(p => p.Users)
.UsingEntity<PermissionUsagerEW>(
p => p.HasOne(e => e.permission)
.WithMany()
.HasForeignKey(e => e.permissionId),
p => p.HasOne(p => p.usagerCodeIntNavigation)
.WithMany()
.HasForeignKey(e => e.usagerCodeInt)
);
modelBuilder.Entity<PermissionUsagerEW>()
.HasOne(p => p.usagerCodeIntNavigation)
.WithMany()
.HasForeignKey(p => p.usagerCodeInt);
When testing with
UsagerEW user = _EWContext.UsagerEW.Where(u=>u.Code == usagerId).Include(u => u.Permissions).SingleOrDefault();
Now I got this error:
Microsoft.Data.SqlClient.SqlException: 'Invalid column name
'UsagerEWcode_int'.'
I think EF tries to link something automatically. I do not have any UsagerEWcode_int in my solution.
EDIT2:
There is the SQL generated. Wierd column name and some repetition...
SELECT [u].[code_int], [u].[Administrateur], [u].[Code], [u].[Email], [u].[EmpContact], [u].[Inactif], [u].[KelvinConfig], [u].[LectureSeule], [u].[ModeLogin], [u].[Nom], [u].[ParamRole], [u].[Password], [u].[PasswordTemp], [u].[PasswordTempExp], [u].[RestreintCommContrat], [u].[RestreintProjet], [u].[Role], [u].[UsagerAD], [u].[doitChangerPW], [u].[estSuperviseur], [u].[idSuperviseur], [u].[infoSession], [u].[paramRole2], [u].[permsGrps], [t].[id], [t].[Permissionid], [t].[UsagerEWcode_int], [t].[permissionId0], [t].[usagerCodeInt], [t].[id0], [t].[code], [t].[description], [t].[moduleId]
FROM [UsagerEW] AS [u]
LEFT JOIN (
SELECT [p].[id], [p].[Permissionid], [p].[UsagerEWcode_int], [p].[permissionId] AS [permissionId0], [p].[usagerCodeInt], [p0].[id] AS [id0], [p0].[code], [p0].[description], [p0].[moduleId]
FROM [PermissionUsagerEW] AS [p]
INNER JOIN [Permission] AS [p0] ON [p].[permissionId] = [p0].[id]
) AS [t] ON [u].[code_int] = [t].[usagerCodeInt]
WHERE [u].[Code] = #__usagerId_0
ORDER BY [u].[code_int], [t].[id]
You can configure direct Many-to-Many relationships with an existing database, and you can have the linking entity in the model or exclude it. There are several examples in the docs. And you can leave the foreign key properties in the model, or you can replace them with shadow properties. But the Scaffolding code doesn't do any of this for you. It creates the simplest correct model for the database schema.
Also you usually should rename the entities and properties to align with .NET coding conventions.
Anyway something like this should work:
public partial class UsagerEW
{
public string Code { get; set; }
public string Nom { get; set; }
public string Email { get; set; }
public string ModeLogin { get; set; }
public string PasswordTemp { get; set; }
public DateTime? PasswordTempExp { get; set; }
public int code_int { get; set; }
public virtual ICollection<Permission> Permissions { get; } = new HashSet<Permission>();
}
public partial class Permission
{
public int Id { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public int? ModuleId { get; set; }
//public virtual Module module { get; set; }
public virtual ICollection<UsagerEW> Users { get; } = new HashSet<UsagerEW>();
}
public partial class PermissionUsagerEW
{
public int Id { get; set; }
public int PermissionId { get; set; }
public int UsagerCodeInt { get; set; }
public virtual Permission Permission { get; set; }
public virtual UsagerEW User { get; set; }
}
public class Db : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<UsagerEW>()
.HasKey(u => u.code_int);
builder.Entity<UsagerEW>()
.HasMany(u => u.Permissions)
.WithMany(p => p.Users)
.UsingEntity<PermissionUsagerEW>(
p => p.HasOne(e => e.Permission)
.WithMany()
.HasForeignKey(e => e.PermissionId),
p => p.HasOne(p => p.User)
.WithMany()
.HasForeignKey( e => e.UsagerCodeInt)
);
builder.Entity<PermissionUsagerEW>()
.HasOne(p => p.User)
.WithMany()
.HasForeignKey(p => p.UsagerCodeInt);
foreach (var prop in builder.Model.GetEntityTypes().SelectMany(e => e.GetProperties()))
{
prop.SetColumnName(char.ToLower(prop.Name[0]) + prop.Name.Substring(1));
}
base.OnModelCreating(builder);
}
But when you're working in a database-first workflow, there's a downside to deeply customizing the EF model: you loose the ability to regenerate the EF model from the database.
So you can use a "nice" customized EF model, or a "plain" scaffolded model. If you customize the model, you can no longer regenerate it, and need to alter it to match future database changes by hand.
You can apply some customizations, though, like the convention-based property-to-column and entity-to-table mappings in the example. But changing the generated "indirect many-to-many" to "direct many-to-many" will prevent you from regenerating the EF model through scaffolding.

Change many-to-one relationship into one-to-one EF

I am building a platform using .NET CORE 2.1, and it is about questions and answers. Before I started I scaffolded my existing DB into the .net project so I got the model up and running.
Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=MYDATABASE;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
But it generated QUESTION and QUESTION_DETAIL with a many-to-one relation ship whereas QUESTION should only have 1 QUESTION DETAIl. But when I am trying to change it with the fluent API in my context it gives an error:
Cannot Convert lambda expression to type 'Type' because it is not a delegated type
Here are my 2 model classes
public partial class Question
{
public Question()
{
AnswerClientBot = new HashSet<AnswerClientBot>();
InverseOriginalQuestion = new HashSet<Question>();
QuestionFollowUpFollowUpQuestion = new HashSet<QuestionFollowUp>();
QuestionFollowUpOriginalQuestion = new HashSet<QuestionFollowUp>();
}
public int Id { get; set; }
public int BotInstanceId { get; set; }
public string LanguageCode { get; set; }
public string Question1 { get; set; }
public int? OriginalQuestionId { get; set; }
[ForeignKey("QuestionDetail")]
public int QuestionDetailId { get; set; }
public Instance BotInstance { get; set; }
public Question OriginalQuestion { get; set; }
public QuestionDetail QuestionDetail { get; set; }
public ICollection<AnswerClientBot> AnswerClientBot { get; set; }
public ICollection<Question> InverseOriginalQuestion { get; set; }
public ICollection<QuestionFollowUp> QuestionFollowUpFollowUpQuestion { get; set; }
public ICollection<QuestionFollowUp> QuestionFollowUpOriginalQuestion { get; set; }
}
public partial class QuestionDetail
{
public int Id { get; set; }
public int OriginalQuestionId { get; set; }
public string Topic { get; set; }
public string Intent { get; set; }
public string CustDetail01 { get; set; }
public string CustDetail02 { get; set; }
public string CustDetail03 { get; set; }
public string CustDetail04 { get; set; }
public string CustDetail05 { get; set; }
public string Keywords { get; set; }
public Question OriginalQuestion { get; set; }
}
And this is the context I am trying to change, the error occurs on
HasForeignKey(d => d.OriginalQuestionId)
modelBuilder.Entity<QuestionDetail>(entity =>
{
entity.ToTable("QuestionDetail", "Bot");
entity.Property(e => e.CustDetail01)
.HasColumnName("CUST_Detail01")
.HasMaxLength(250)
.IsUnicode(false);
entity.Property(e => e.CustDetail02)
.HasColumnName("CUST_Detail02")
.HasMaxLength(250)
.IsUnicode(false);
entity.Property(e => e.CustDetail03)
.HasColumnName("CUST_Detail03")
.HasMaxLength(250)
.IsUnicode(false);
entity.Property(e => e.CustDetail04)
.HasColumnName("CUST_Detail04")
.HasMaxLength(250)
.IsUnicode(false);
entity.Property(e => e.CustDetail05)
.HasColumnName("CUST_Detail05")
.HasMaxLength(250)
.IsUnicode(false);
entity.Property(e => e.Intent)
.HasMaxLength(250)
.IsUnicode(false);
entity.Property(e => e.Keywords)
.HasMaxLength(250)
.IsUnicode(false);
entity.Property(e => e.Topic)
.HasMaxLength(250)
.IsUnicode(false);
entity.HasOne(d => d.OriginalQuestion)
.WithOne(p => p.QuestionDetail)
.HasForeignKey(d => d.OriginalQuestionId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Bot_QuestionDetail_OriginalQuestionId");
});
Does anyone have any clue how I can fix this? Thank you!
Declare both classes with navigation properties to each other. Mark one of the tables (the dependent table) with the ForeignKey attribute on its Primary Key. EF infers 1-to-1 from this:
public class Question
{
...
// [ForeignKey("QuestionDetail")]
// public int QuestionDetailId { get; set; }
public QuestionDetail QuestionDetail { get; set; }
...
}
public partial class QuestionDetail
{
//public int Id { get; set; }
[ForeignKey("Question")]
public int QuestionId { get; set; }
...
public Question Question { get; set; }
}
So I found this Cannot convert lambda expression to type 'object' because it is not a delegate type stackoverflow post, and at the start I tried to strong type it but didn't know really how to but eventually I figured it out. So I changed it to HasForeignKey<Question>(d => d.OriginalQuestionId) which worked for me.

Entity Framework many to many fluent API

I'm working with an existing database whose schema is fixed.
There are 3 many-to-many relations, Contact-Group, Contact-Department and Contact-Team.
There is a common table, ContactRelation that acts as the middle table for all 3 relations.
public class Contact
{
[Column("CtcID")]
public int Id { get; set; }
[Column("CtcFirstName")]
public string FirstName { get; set; }
[Column("CtcFamilyName")]
public string LastName { get; set; }
public virtual ICollection<ContactRelation> GroupRelations { get; set; }
public virtual ICollection<ContactRelation> DepartmentRelations { get; set; }
public virtual ICollection<ContactRelation> TeamRelations { get; set; }
}
public class Group
{
[Key, Column("GRID")]
public int Id { get; set; }
[Column("GRName")]
public string Name { get; set; }
public virtual ICollection<ContactRelation> ContactRelations { get; set; }
}
public class Department
{
[Key, Column("DEPID")]
public int Id { get; set; }
[Column("DEPName")]
public string Name { get; set; }
public virtual ICollection<ContactRelation> ContactRelations { get; set; }
}
public class Team
{
[Key, Column("TMID")]
public int Id { get; set; }
[Column("TransCode")]
public string TransCode { get; set; }
public virtual ICollection<ContactRelation> ContactRelations { get; set; }
}
public class ContactRelation
{
[Key]
[Column("CtcRelnID")]
public int Id { get; set; }
[Column("GRID")]
public int GroupId { get; set; }
[Column("DEPID")]
public int DepartmentId { get; set; }
[Column("TMID")]
public int TeamId { get; set; }
[Column("CUCtcID")]
public int ContactId { get; set; }
[Column("RCode")]
public string RoleCode { get; set; }
}
In my mapping, I have the following code:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Group>()
.HasMany(g => g.ContactRelations)
.WithRequired()
.HasForeignKey(r => r.GroupId);
modelBuilder.Entity<Department>()
.HasMany(c => c.ContactRelations)
.WithRequired()
.HasForeignKey(r => r.DepartmentId);
modelBuilder.Entity<Team>()
.HasMany(s => s.ContactRelations)
.WithRequired()
.HasForeignKey(r => r.TeamId);
modelBuilder.Entity<Contact>()
.HasMany(c => c.GroupRelations)
.WithRequired()
.HasForeignKey(r => r.ContactId);
modelBuilder.Entity<Contact>()
.HasMany(c => c.DepartmentRelations)
.WithRequired()
.HasForeignKey(r => r.ContactId);
modelBuilder.Entity<Contact>()
.HasMany(c => c.TeamRelations)
.WithRequired()
.HasForeignKey(r => r.ContactId);
}
I then try to execute the following query:
var ctc = repo.Contacts
.Include("GroupRelations")
.Include("DepartmentRelations")
.FirstOrDefault(c => c.FirstName.ToLower() == "jason");
and I keep getting error:
System.Data.SqlClient.SqlException:
Invalid column name 'Contact_Id1'.
Invalid column name 'Contact_Id'.
Invalid column name 'Contact_Id1'.
Invalid column name 'Contact_Id2'.
I read somewhere that a table cannot participate in more than one many-to-many relations. Is that true? Is it because the ContactRelation table is used more than once that I'm getting this error?
If so, what's the correct way to map these relations, without modifying the database schema.?
PS: I'm working with EF6.1
Thanks for your help.
Remove this mapping
modelBuilder.Entity<Contact>()
.HasMany(c => c.TeamRelations)
.WithRequired()
.HasForeignKey(r => r.ContactId);
and then try executing your query.

Code First - Two foreign keys as primary keys, unable to add migration

My User table:
public class User
{
[Key]
public int UserId { get; set; }
public virtual ICollection<PollVote> PollVotes { get; set; }
}
My Poll table:
public class Poll
{
[Key]
public int PollId { get; set; }
public virtual ICollection<PollVote> PollVotes { get; set; }
}
My PollVote table
public class PollVote
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int VoteId { get; set; }
[Key]
public int PollId { get; set; }
[Key]
public int UserId { get; set; }
public DateTime TimeVoted { get; set; }
public int Answer { get; set; }
public virtual Poll Poll { get; set; }
public virtual User User { get; set; }
}
My Configurations:
//User config:
this.HasMany(x => x.PollVotes)
.WithRequired()
.HasForeignKey(x => x.UserId)
.WillCascadeOnDelete(false);
//Poll Config
this.HasMany(x => x.PollVotes)
.WithRequired()
.HasForeignKey(x => x.PollId)
.WillCascadeOnDelete(false);
//PollVote Config
this.HasKey(x => x.UserId)
.HasRequired(x => x.User)
.WithMany()
.HasForeignKey(x => x.UserId);
this.HasKey(x => x.PollId)
.HasRequired(x => x.Poll)
.WithMany()
.HasForeignKey(x => x.PollId);
The relation is: One Poll can have many votes, but a User can only give one vote to every poll.
I get this error when i try to Add-Migration in PM-Console
\tSystem.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'PollVote_Poll_Source' in relationship 'PollVote_Poll'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
\tSystem.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'Poll_PollVotes_Target' in relationship 'Poll_PollVotes'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
Any suggestions?
You specify a composite key by either adding the [Column] attribute to the data annotations...
[Key, Column(Order = 1)]
public int PollId { get; set; }
[Key, Column(Order = 2)]
public int UserId { get; set; }
...or by using an anonymous object with Fluent API:
this.HasKey(x => new { x.UserId, x.PollId });
this.HasRequired(x => x.User)
.WithMany(u => u.PollVotes)
.HasForeignKey(x => x.UserId);
this.HasRequired(x => x.Poll)
.WithMany(p => p.PollVotes)
.HasForeignKey(x => x.PollId);
Don't forget the lambda expressions for the inverse navigation properties in WithMany, as shown above, and remove the redundant configurations in UserConfig and PollConfig.

One to Many with two Properties from the same class

I have the following classes that I would really like to map correctly in EF:
internal class Wallet : EntityFrameworkEntity
{
public Wallet()
{
this.Requests = new List<FinancialRequest>();
}
public string Name { get; set; }
public string Description { get; set; }
public decimal CurrentBalance { get; set; }
public decimal BlockedBalance { get; set; }
public virtual ICollection<Paper> Papers { get; set; }
public virtual ICollection<FinancialRequest> Requests { get; set; }
public virtual User Manager { get; set; }
}
internal class Request : EntityFrameworkEntity
{
public Int64 UserId { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public RequestStatus Status { get; set; }
public virtual User User { get; set; }
}
internal class FinancialRequest : Request
{
public DateTime ValidUntil { get; set; }
public FinancialRequestType RequestType { get; set; }
public int Quantity { get; set; }
public bool UseMarketValue { get; set; }
public decimal? Value { get; set; }
public virtual Wallet Source { get; set; }
public virtual Wallet Destination { get; set; }
public virtual Team Team { get; set; }
}
I'm using Code First, so this is my method that maps those classes:
modelBuilder.Entity<Wallet>()
.HasMany(x => x.Requests)
.WithOptional();
modelBuilder.Entity<Wallet>()
.HasMany(x => x.Papers)
.WithOptional(x => x.Owner)
.Map(configuration => configuration.MapKey("OwnerId"));
modelBuilder.Entity<Wallet>()
.HasMany(x => x.Requests)
.WithOptional();
modelBuilder.Entity<Request>().ToTable("Requests");
modelBuilder.Entity<FinancialRequest>().ToTable("FinancialRequests");
modelBuilder.Entity<FinancialRequest>()
.HasRequired(x => x.Team)
.WithOptional()
.Map(configuration => configuration.MapKey("TeamId"));
modelBuilder.Entity<FinancialRequest>()
.HasOptional(x => x.Destination)
.WithOptionalDependent()
.Map(configuration => configuration.MapKey("DestinationWalletId"));
modelBuilder.Entity<FinancialRequest>()
.HasRequired(x => x.Source)
.WithRequiredDependent()
.Map(configuration => configuration.MapKey("SourceWalletId"));
If I leave this mapping the way it's now, my database schema looks like this:
If you look carefully, you'll see that there's a column called "Wallet_Id" that it's not suposed to be there. This column only exists because the Wallet class has the "Requests" collection.
If I remove the collection from the the columns goes away, but I need this collection! It representes a importante relation between the classes. What I don't need is the 3rd column in the database wrongly generated.
Does anybody knows how can I avoid this? What am I doing wrong here?
The problem that causes the redundant foreign key column Wallet_Id is that EF doesn't know if the Wallet.Requests collection is the inverse navigation property of FinancialRequest.Source or FinancialRequest.Destination. Because it cannot decide between the two EF assumes that Wallet.Requests doesn't have an inverse navigation property at all. The result is a third redundant one-to-many relationship with the third FK.
Basically you have three options:
Remove the Wallet.Requests collection and the third relationship will disappear (as you already have noticed). But you don't want that.
Tell EF explicitly if Wallet.Requests has Source or Destination as inverse navigation property:
// Remove the modelBuilder.Entity<Wallet>().HasMany(x => x.Requests) mapping
modelBuilder.Entity<FinancialRequest>()
.HasOptional(x => x.Destination)
.WithMany(x => x.Requests)
.Map(config => config.MapKey("DestinationWalletId"));
modelBuilder.Entity<FinancialRequest>()
.HasRequired(x => x.Source)
.WithMany()
.Map(config => config.MapKey("SourceWalletId"));
Use WithMany(x => x.Requests) in one of the two (Destination in the example, it could also be Source), but not in both.
Introduce a second collection in Wallet and map the two collections to Source and Destination respectively:
internal class Wallet : EntityFrameworkEntity
{
public Wallet()
{
this.SourceRequests = new List<FinancialRequest>();
this.DestinationRequests = new List<FinancialRequest>();
}
// ...
public virtual ICollection<FinancialRequest> SourceRequests { get; set; }
public virtual ICollection<FinancialRequest> DestinationRequests { get; set; }
}
Mapping:
// Remove the modelBuilder.Entity<Wallet>().HasMany(x => x.Requests) mapping
modelBuilder.Entity<FinancialRequest>()
.HasOptional(x => x.Destination)
.WithMany(x => x.DestinationRequests)
.Map(config => config.MapKey("DestinationWalletId"));
modelBuilder.Entity<FinancialRequest>()
.HasRequired(x => x.Source)
.WithMany(x => x.SourceRequests)
.Map(config => config.MapKey("SourceWalletId"));
BTW: Shouldn't both Source and Destination be required? If yes, you can replace the HasOptional by HasRequired but you must append WillCascadeOnDelete(false) to at least one of the two mappings to avoid a multiple cascading delete path exception.