EF is creating 2 foreign keys for navigation property - entity-framework

The following entity configuration it creating a double foreign key for one of the navigation properties:
public User : IdentityUser
{
//public string Id { get; set; } //on base class
public virtual ICollection<Earning> Earnings { get; set; }
}
public class Earning
{
public int EarningId { get; set; }
public virtual User User { get; set; }
public string UserId { get; set; }
public virtual User Sender { get; set; }
public string SenderId { get; set; }
}
public class EarningConfiguraiton : EntityTypeConfiguration<Earning>
{
public EarningConfiguraiton()
{
this.HasRequired(e => e.User)
.WithMany(u => u.Earnings)
.HasForeignKey(e => e.UserId);
this.HasRequired(e => e.Sender)
.WithMany()
.HasForeignKey(e => e.SenderId)
.WillCascadeOnDelete(false);
}
}
However it generates 3 foreign keys for 2 navigation properties. "User_Id" is seemingly redundant. Should I remove , if so how?
CreateTable(
"dbo.Earnings",
c => new
{
EarningId = c.Int(nullable: false, identity: true),
UserId = c.String(maxLength: 128),
SenderId = c.String(maxLength: 128),
User_Id = c.String(maxLength: 128),
})
.PrimaryKey(t => t.EarningId)
.ForeignKey("dbo.AspNetUsers", t => t.SenderId)
.ForeignKey("dbo.AspNetUsers", t => t.UserId)
.ForeignKey("dbo.AspNetUsers", t => t.User_Id)
.Index(t => t.UserId)
.Index(t => t.SenderId)
.Index(t => t.User_Id);
Further more context.Users.Include(u => u.Earnings) only works with User_Id column populated, rather than UserId.
I even get this migration generated on a brand new database

There is actually a long outstanding work item on this issue with the Fluent API not correctly handling inverse navigation properties. It has been considered to be a low priority issue, and was not included in the 6.1 release due to Triage. http://entityframework.codeplex.com/workitem/1135

Ok I'm able to get past this but the bounty is still available for a better way.
Gert's comments in this answer helped me put it together.
Basically since there are 2 User entities on the Earning, EF is not going to assume which of them is bi-directional for the Earnings collection on the User.
Using [InversePropoperty] attribute works but I'm not able to configure this using Fluent Api:
Solution:
public User : IdentityUser
{
[InverseProperty("User")]
public virtual ICollection<Earning> Earnings { get; set; }
}
public class Earning
{
[InverseProperty("Earnings")]
public virtual User User { get; set; }
public virtual User Sender { get; set; }
public string SenderId { get; set; }
}
Now it generates one less foreign key. However I'm unable to configure this using Fluent Api.
public class EarningConfiguraiton : EntityTypeConfiguration<Earning>
{
public EarningConfiguraiton()
{
this.HasRequired(e => e.User)
.WithMany(u => u.EmailEarnings)
.HasForeignKey(e => e.UserId) //doesnt honor this. creates User_Id anyways
.WillCascadeOnDelete(false);
}
}
I don't know if it's a bug but bounty goes to whoever can figure it out. I'm using EF 6.1.1

Related

EF 6 Code First relationships using Fluent API. How to set relation between first and third tables or get grouped collection

I have three related entities, here is a structure and relations declared using Fluent API
An event, participating many persons (actually a group of persons) so there is a possibility to collect them all by GroupID. So how to do this?
public class Event
{
public int EventID { get; set; }
public string DocID { get; set; }
public string GroupID { get; set; }
public virtual Person Person { get; set; }
public virtual Group Group { get; set; }
public virtual ICollection<Person> GroupPerson {get; set}
}
Person entity, here I have all information about person, such as name, surname, birthdate...
public class Person
{
public string PersonID { get; set; }
public string PersonName { get; set; }
public string PersonSurName { get; set; }
public string PersonCode { get; set; }
}
Group entity, here is an information about the group
public class Group
{
public string GroupID { get; set; }
public string GroupName { get; set; }
public virtual ICollection<Event> EventGroup { get; set; }
}
Now I describe relations using Fluent API. Primary keys first of all:
modelBuilder.Entity<Event>().HasKey(e => e.EventID);
modelBuilder.Entity<Person>().HasKey(e => e.PersonID);
modelBuilder.Entity<Group>().HasKey(e => e.GroupID);
Here I will have person related to event
modelBuilder.Entity<Event>()
.HasRequired(s => s.Person)
.WithMany()
.HasForeignKey(fk=> fk.PersonID);
Here I will have PersonGroup
modelBuilder.Entity<Group>()
.HasKey(e => e.GroupID)
.HasMany(e => e.EventGroup)
.WithOptional()
.HasForeignKey(f => f.GroupID);
And my question is how to set a relation to get that list of persons in group?
PersonGroup is an Event type and I need list of persons type: Person => ICollection<Person> GroupPerson in Event class.
Given that your relationships are like this:
One event has (is related to) exactly one group (required)
One group has (is related to) zero to many events
One group has (is related to) zero to many people
One person has (is related to) zero to many groups
That is, your relationship Events-Groups is one-to-many, and your relationship Groups-People is many-to-many (I'm assuming that the same person can be in more than one group). There is no direct relationship between Events and People, but a transitive relationship Event -> Group -> People.
Then it can be modelled like this:
public class Event
{
public int EventID { get; set; }
public string DocID { get; set; }
public string GroupID { get; set; }
public virtual Group Group { get; set; }
public virtual ICollection<Person> People { get { return Group.People; } }
}
public class Person
{
public string PersonID { get; set; }
public string PersonName { get; set; }
public string PersonSurName { get; set; }
public string PersonCode { get; set; }
}
public class Group
{
public string GroupID { get; set; }
public string GroupName { get; set; }
public virtual ICollection<Event> Events { get; set; }
public virtual ICollection<Person> People { get; set; }
}
With these DbSets in the DbContext:
public DbSet<Person> People { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<Event> Events { get; set; }
And this EF configuration:
modelBuilder.Entity<Event>()
.HasKey(e => e.EventID)
.Ignore(e => e.People)
.HasRequired(e => e.Group)
.WithMany(g => g.Events);
modelBuilder.Entity<Group>()
.HasKey(g => g.GroupID)
.HasMany(g => g.People)
.WithMany();
modelBuilder.Entity<Person>()
.HasKey(p => p.PersonID);
Note that there is an explicit Ignore() for Event.People. This is because the relationship between Event and Person is transitive, you don't need extra columns in your database for it. If you don't see why, try commenting out the Ignore() line and regenerating the migration, and see that an extra column for the Event ID is generated in the People table (this column doesn't make much sense).
As a consequence the People property in Events is not populated by EF, you have to do it yourself:
public virtual ICollection<Person> People { get { return Group.People; } }
To add people to an Event you should use the Group navigation property, something like this:
public class Event
{
...
public void AddPerson(Person p)
{
this.Group.People.Add(p);
}
}
With this code the migration is generated like this, with four tables: Events, Groups, People and and extra table PeopleGroups for the many-to-many relationship between Person and Group.
public override void Up()
{
CreateTable(
"dbo.Events",
c => new
{
EventID = c.Int(nullable: false, identity: true),
DocID = c.String(),
GroupID = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.EventID)
.ForeignKey("dbo.Groups", t => t.GroupID, cascadeDelete: true)
.Index(t => t.GroupID);
CreateTable(
"dbo.Groups",
c => new
{
GroupID = c.String(nullable: false, maxLength: 128),
GroupName = c.String(),
})
.PrimaryKey(t => t.GroupID);
CreateTable(
"dbo.People",
c => new
{
PersonID = c.String(nullable: false, maxLength: 128),
PersonName = c.String(),
PersonSurName = c.String(),
PersonCode = c.String(),
})
.PrimaryKey(t => t.PersonID);
CreateTable(
"dbo.GroupPersons",
c => new
{
Group_GroupID = c.String(nullable: false, maxLength: 128),
Person_PersonID = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => new { t.Group_GroupID, t.Person_PersonID })
.ForeignKey("dbo.Groups", t => t.Group_GroupID, cascadeDelete: true)
.ForeignKey("dbo.People", t => t.Person_PersonID, cascadeDelete: true)
.Index(t => t.Group_GroupID)
.Index(t => t.Person_PersonID);
}
If you don't like the names of the columns in the relationship table GroupPersons you can add a .Map() configuration (but you don't really need to do this, as this table isn't directly used, there is no model entity for it, and it doesn't even have a DbSet property in the DbContext).

Entity Framework code-first, Many-to-Many relationship on the same table

I'm building a social network with Friend function.
My idea is that I've already had the default ApplicationUser class, so I create a new table called Friend
public class Friend
{
[Key]
[Column(Order = 1)]
public string SenderId { get; set; }
[Key]
[Column(Order = 2)]
public string ReceiverId { get; set; }
//Status == true : Friend request accepted
//Status == false : Friend request not accepted
public bool Status { get; set; }
}
In the ApplicationUser, I define 2 navigation properties Senders and Receivers (to link to Friend table)
public class ApplicationUser : IdentityUser
{
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
public bool Gender { get; set; }
[StringLength(255)]
public string Address { get; set; }
[StringLength(255)]
public string Job { get; set; }
[StringLength(255)]
public string Image { get; set; }
public DateTime Birthday { get; set; }
public ICollection<ApplicationUser> Senders { get; set; }
public ICollection<ApplicationUser> Receivers { get; set; }
}
And finally in ApplicationDbContext, I declare relationships between 2 tables using Fluent Api
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Friend> Friends { get; set; }
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ApplicationUser>()
.HasMany(a => a.Senders)
.WithMany(a => a.Receivers)
.Map(m =>
{
m.MapLeftKey("ReceiverId");
m.MapRightKey("SenderId");
m.ToTable("Friends");
});
base.OnModelCreating(modelBuilder);
}
}
But when I add-migration, it creates 2 tables like this, and neither of them is what I need (one doesn't have foreign keys, one doesn't have Status properties)
public override void Up()
{
CreateTable(
"dbo.Friends1",
c => new
{
SenderId = c.String(nullable: false, maxLength: 128),
ReceiverId = c.String(nullable: false, maxLength: 128),
Status = c.Boolean(nullable: false),
})
.PrimaryKey(t => new { t.SenderId, t.ReceiverId });
CreateTable(
"dbo.Friends",
c => new
{
SenderId = c.String(nullable: false, maxLength: 128),
ReceiverId = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => new { t.SenderId, t.ReceiverId })
.ForeignKey("dbo.AspNetUsers", t => t.SenderId)
.ForeignKey("dbo.AspNetUsers", t => t.ReceiverId)
.Index(t => t.SenderId)
.Index(t => t.ReceiverId);
}
What am I supposed to do :( I've searched on internet and this seems legit but it doesn't work
But when I add-migration, it creates 2 tables like this, and neither of them is what I need (one doesn't have foreign keys, one doesn't have Status properties)
This is because you mixed the two possible many-to-many associations supported by EF - one using implicit junction table (from the Fluent API configuration) and the other using explicit junction table (Friend entity) and two one-to-many associations.
Since the first approach can be used only if you don't have additional data associated with the association, and you have one (Status property), you need to use the second approach.
In order to do that, change the type of the navigation properties as follows:
public class ApplicationUser : IdentityUser
{
// ...
public ICollection<Friend> Senders { get; set; }
public ICollection<Friend> Receivers { get; set; }
}
and the configuration:
modelBuilder.Entity<ApplicationUser>()
.HasMany(e => e.Senders)
.WithRequired()
.HasForeignKey(e => e.ReceiverId);
modelBuilder.Entity<ApplicationUser>()
.HasMany(e => e.Receivers)
.WithRequired()
.HasForeignKey(e => e.SenderId);

Entity framework 6, same column for many foreign keys

I need help with this issue, because after many hours of investigation I am stuck.
I created a datamodel from an existing old database, using Entity Framework 6 (I am using Code First approach). This database was multi-company oriented, so most of its tables has a column "Company" that its used as a part of almost all primary keys and foreign keys.
The datamodel creation created all the foreign keys using Fluent API. But this don't helps and when I try to select data from any table I received errors "invalid columna name 'TABLE_COLUMN'. Because in this database usually the columns has different name in every table and the Entity framework can't determine the relation, so its required to map the column names.
So, I can solve the issue using DataAnnotations, and I can do, for example:
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[ForeignKey("BLOQHOR"), InverseProperty("CODHOR")]
public int NUMHOR { get; set; }
[Key]
[Column(Order = 2)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[ForeignKey("BLOQHOR"), InverseProperty("DISTAINIC")]
public int DISTAINIC { get; set; }
[Key]
[Column(Order = 3)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[ForeignKey("BLOQHOR"), InverseProperty("COMPANY")]
public int COMPANY{ get; set; }
What happends now?
The table has another foreign key that also needs the column COMPANY. Because data annotations don't allow me to use the column twice, I can't make the table to work.
I repeat, in the data model, it created a fluent api definition for the second foreign key, but it don't works.
modelBuilder.Entity<CABAJUSTES>()
.HasMany(e => e.AJUSBLOQ)
.WithRequired(e => e.CABAJUSTES)
.HasForeignKey(e => new { e.NUMAJUST, e.COMPANY})
The fact its that everytime I try to get data I received errors like "Invalid column name CABAJUSTES_CODAJUSTE" and "Invalid column name CABAJUSTES_COMPANY". And I am unable to map this second foreign key.
What can I do?
Thanks in advance.
Its a bit hard to follow your table structure, so I've tried to set up a comprehensive example using some common entities anyone should be able to follow. Please comment if this does not fully describe your problem.
Note that I've deliberately used pretty shitty foreign keys to make sure the helping automapping in Entity Framework doesn't help me, and to show that this works with any legacy database design you may have.
First the expected structure in the example
One Company holds many Articles and many Invoices.
One Invoice holds many InvoiceRows.
Each InvoiceRow may optionally refer to an Article.
The actual Entities
class Company
{
public int TheCompanyKey { get; set; }
public virtual ICollection<Invoice> Its_Invoices { get; set; }
public virtual ICollection<Article> Its_Articles { get; set; }
}
class Invoice
{
public int Its_CompanyKey { get; set; }
public int TheInvoiceKey { get; set; }
public string InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public virtual Company Its_Company { get; set; }
public virtual ICollection<InvoiceRow> Its_Rows { get; set; }
}
class InvoiceRow
{
public int Rows_Company_Key { get; set; }
public int Its_InvoiceID { get; set; }
public int RowNumber { get; set; }
public int? Its_Articles_ID { get; set; }
public string Text { get; set; }
public double Price { get; set; }
public virtual Invoice Its_Invoice { get; set; }
public virtual Article Its_Article { get; set; }
}
class Article
{
public int TheArticleCompany_Key { get; set; }
public int TheArticleKey { get; set; }
public string ArticleNumber { get; set; }
public double Cost { get; set; }
public double TargetPrice { get; set; }
public virtual Company Its_Company { get; set; }
}
The DbContext with OnModelCreating()
There are multiple ways to generate the required structure, depending on if you think top-down or bottom-up. My take on modelling is to start with the base tables and the describe how children relate to them.
class MyContext : DbContext
{
public MyContext() : base("name=MyContext")
{
}
public virtual IDbSet<Company> Companies { get; set; }
public virtual IDbSet<Invoice> Invoices { get; set; }
public virtual IDbSet<InvoiceRow> InvoiceRows { get; set;}
public virtual IDbSet<Article> Articles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Company>()
.HasKey(e => e.TheCompanyKey);
modelBuilder.Entity<Article>()
.HasKey(e => new { e.TheArticleCompany_Key, e.TheArticleKey })
.HasRequired(e => e.Its_Company).WithMany(e => e.Its_Articles).HasForeignKey(e => e.TheArticleCompany_Key);
modelBuilder.Entity<Invoice>()
.HasKey(e => new { e.Its_CompanyKey, e.TheInvoiceKey })
.HasRequired(e => e.Its_Company).WithMany(e => e.Its_Invoices).HasForeignKey(e => e.Its_CompanyKey);
modelBuilder.Entity<InvoiceRow>()
.HasKey(e => new { e.Rows_Company_Key, e.Its_InvoiceID, e.RowNumber });
modelBuilder.Entity<InvoiceRow>()
.HasRequired(e => e.Its_Invoice).WithMany(e => e.Its_Rows)
.HasForeignKey(e => new { e.Rows_Company_Key, e.Its_InvoiceID }).WillCascadeOnDelete();
modelBuilder.Entity<InvoiceRow>()
.HasOptional(e => e.Its_Article)
.WithMany()
.HasForeignKey(e => new { e.Rows_Company_Key, e.Its_Articles_ID });
}
}
Finally the generated migration
Running add-migration multikeys in the Package Manager Console window results in the following migration:
public partial class multikeys : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Articles",
c => new
{
TheArticleCompany_Key = c.Int(nullable: false),
TheArticleKey = c.Int(nullable: false),
ArticleNumber = c.String(),
Cost = c.Double(nullable: false),
TargetPrice = c.Double(nullable: false),
})
.PrimaryKey(t => new { t.TheArticleCompany_Key, t.TheArticleKey })
.ForeignKey("dbo.Companies", t => t.TheArticleCompany_Key, cascadeDelete: true)
.Index(t => t.TheArticleCompany_Key);
CreateTable(
"dbo.Companies",
c => new
{
TheCompanyKey = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.TheCompanyKey);
CreateTable(
"dbo.Invoices",
c => new
{
Its_CompanyKey = c.Int(nullable: false),
TheInvoiceKey = c.Int(nullable: false),
InvoiceNumber = c.String(),
InvoiceDate = c.DateTime(nullable: false),
})
.PrimaryKey(t => new { t.Its_CompanyKey, t.TheInvoiceKey })
.ForeignKey("dbo.Companies", t => t.Its_CompanyKey, cascadeDelete: true)
.Index(t => t.Its_CompanyKey);
CreateTable(
"dbo.InvoiceRows",
c => new
{
Rows_Company_Key = c.Int(nullable: false),
Its_InvoiceID = c.Int(nullable: false),
RowNumber = c.Int(nullable: false),
Its_Articles_ID = c.Int(),
Text = c.String(),
Price = c.Double(nullable: false),
})
.PrimaryKey(t => new { t.Rows_Company_Key, t.Its_InvoiceID, t.RowNumber })
.ForeignKey("dbo.Articles", t => new { t.Rows_Company_Key, t.Its_Articles_ID })
.ForeignKey("dbo.Invoices", t => new { t.Rows_Company_Key, t.Its_InvoiceID }, cascadeDelete: true)
.Index(t => new { t.Rows_Company_Key, t.Its_Articles_ID })
.Index(t => new { t.Rows_Company_Key, t.Its_InvoiceID });
}
public override void Down()
{
DropForeignKey("dbo.Articles", "TheArticleCompany_Key", "dbo.Companies");
DropForeignKey("dbo.InvoiceRows", new[] { "Rows_Company_Key", "Its_InvoiceID" }, "dbo.Invoices");
DropForeignKey("dbo.InvoiceRows", new[] { "Rows_Company_Key", "Its_Articles_ID" }, "dbo.Articles");
DropForeignKey("dbo.Invoices", "Its_CompanyKey", "dbo.Companies");
DropIndex("dbo.InvoiceRows", new[] { "Rows_Company_Key", "Its_InvoiceID" });
DropIndex("dbo.InvoiceRows", new[] { "Rows_Company_Key", "Its_Articles_ID" });
DropIndex("dbo.Invoices", new[] { "Its_CompanyKey" });
DropIndex("dbo.Articles", new[] { "TheArticleCompany_Key" });
DropTable("dbo.InvoiceRows");
DropTable("dbo.Invoices");
DropTable("dbo.Companies");
DropTable("dbo.Articles");
}
}
Summary
I believe this describes the OP problem and with a little study gives a good understanding of how Fluent can be used to map entities.
Good luck!

Creating a 1:1 relationship using EF CodeFirst

I am battling with EF code first and trying to map a 1:1 relationship with no joy.
Basically a user can have a userdetail.
I am setting primary keys on both tables.On my UserDetail table has field UserId I am trying to use as the FK.
public class User:BaseModel
{
public virtual UserDetail UserDetail { get; set; }
public string UserName { get; set; }
}
public class UserDetail:BaseModel
{
public virtual User User { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public string UserDetailName { get; set; }
}
public class BaseModel{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
}
When I try using the add-migration command I get this error
UserDetail_User_Source: : Multiplicity is not valid in Role 'UserDetail_User_Source' in relationship 'UserDetail_User'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
What do I need to do to get this working? Surely this shouldnt be that difficult?
Update below based on comment from #steve-green
I configured the fluentapi like steve suggested
modelBuilder.Entity<User>()
.HasRequired(t => t.UserDetail)
.WithRequiredPrincipal(t => t.User);
however the generated migration step looks wrong to me
CreateTable(
"dbo.Users",
c => new
{
UserId = c.Int(nullable: false, identity: true),
UserName = c.String(),
})
.PrimaryKey(t => t.UserId);
CreateTable(
"dbo.UserDetails",
c => new
{
UserDetailId = c.Int(nullable: false, identity: true),
UserId = c.Int(nullable: false),
UserDetailName = c.String(),
})
.PrimaryKey(t => t.UserDetailId)
.ForeignKey("dbo.Users", t => t.UserDetailId)
.Index(t => t.UserDetailId);
The FK is configured as UserDetailId shouldnt it be UserId?
Slightly modified model
public class UserDetail
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserDetailId { get; set; }
public virtual User User { get; set; }
public int UserId { get; set; }
public string UserDetailName { get; set; }
}
Get rid of the attribute
[ForeignKey("User")
in UserDetail. Also, i would add
int UserDetailId
to User
That should then work.
If you are going to use a base table, then you can't do it with annotations because (as you mention) the key on the required dependent needs to be a foreign key. You will need fluent api code:
modelBuilder.Entity<User>()
.HasRequired(t => t.UserDetail)
.WithRequiredPrincipal(t => t.User);
https://msdn.microsoft.com/en-us/data/jj591620.aspx#RequiredToRequired

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.