So I created the following related Classes and I'm trying to do Code-First approach. I want the Quote class to reference 3 instances of the User class by 3 different navigable property names, but when I do DBInitializer to populate and create the DB, the Quote table has 6 columns instead of the expected 3 columns, of which 3 are always null. The navigable properties point to those 3 null columns, so whenever I point to Quote.Manager or one of the other 3 properties, it returns null instead of the actual manager. How can I fix this?
Quote Class (I left a little off, but you get the point):
using System.Web;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace QuoteManager.Models
{
public class Quote
{
public int QuoteID { get; set; }
public virtual int StateID { get; set; }
public virtual State State { get; set; }
public virtual int CreatorID {开发者_StackOverflow中文版 get; set; }
public virtual User Creator { get; set; }
public virtual int AgentID { get; set; }
public virtual User Agent { get; set; }
public virtual int ManagerID { get; set; }
public virtual User Manager { get; set; }
}
}
User class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace QuoteManager.Models
{
public class User
{
public User()
{
this.Quotes = new HashSet<Quote>();
this.CreatedQuotes = new HashSet<Quote>();
this.ManagedQuotes = new HashSet<Quote>();
}
public int UserID { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
public virtual ICollection<Quote> Quotes { get; set; }
public virtual ICollection<Quote> CreatedQuotes { get; set; }
public virtual ICollection<Quote> ManagedQuotes { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
}
Use the InverseProperty
attribute to specify the other property participating in the relationship
public class Quote
{
public int QuoteID { get; set; }
public virtual int StateID { get; set; }
public virtual State State { get; set; }
public virtual int CreatorID { get; set; }
[InverseProperty("CreatedQuotes")]
public virtual User Creator { get; set; }
public virtual int AgentID { get; set; }
public virtual User Agent { get; set; }
public virtual int ManagerID { get; set; }
[InverseProperty("ManagedQuotes")]
public virtual User Manager { get; set; }
}
public class User
{
public User()
{
this.Quotes = new HashSet<Quote>();
this.CreatedQuotes = new HashSet<Quote>();
this.ManagedQuotes = new HashSet<Quote>();
}
public int UserID { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
public virtual ICollection<Quote> Quotes { get; set; }
[InverseProperty("Creator")]
public virtual ICollection<Quote> CreatedQuotes { get; set; }
[InverseProperty("Manager")]
public virtual ICollection<Quote> ManagedQuotes { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
Similarly map the other relations.
Add the attribute [ForeignKey("Creator")] to the CreatorID and so on for the other 2 property pairs.
FINAL SOLUTION
Thanks to your reference to InverseProperty I found an amazing article that covers exactly what I wanted to accomplish using fluent API. This article was written in January, but I'm pretty sure CTP5 is now officially part of the MVC 3 and EF core.
Associations in EF Code First CTP5
Okay...I'm going to document what I found to work great! I hate it when people leave partial answers, so here we go.
There is a little redundancy here, but it works. My Quote Class looks like this:
[ForeignKey("Creator")]
public virtual int CreatorID { get; set; }
[InverseProperty("CreatedQuotes")]
public virtual User Creator { get; set; }
[ForeignKey("Agent")]
public virtual int AgentID { get; set; }
[InverseProperty("OwnedQuotes")]
public virtual User Agent { get; set; }
[ForeignKey("Manager")]
public virtual int ManagerID { get; set; }
[InverseProperty("ManagedQuotes")]
public virtual User Manager { get; set; }
Then my User class looks like this:
public virtual ICollection<Quote> CreatedQuotes { get; set; }
public virtual ICollection<Quote> OwnedQuotes { get; set; }
public virtual ICollection<Quote> ManagedQuotes { get; set; }
Finally, my DBContext class looks like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Quote>()
.HasRequired(a => a.Manager)
.WithMany()
.HasForeignKey(u => u.ManagerID);
modelBuilder.Entity<Quote>()
.HasRequired(a => a.Agent)
.WithMany()
.HasForeignKey(u => u.AgentID).WillCascadeOnDelete(false);
modelBuilder.Entity<Quote>()
.HasRequired(a => a.Manager)
.WithMany()
.HasForeignKey(u => u.ManagerID).WillCascadeOnDelete(false);
}
You can see the redundancy in the ForeignKey annotation in the Quote class and the Fluent API mapping in the DbContext class, but it's not hurting anything. I could probably do away with the annotations in the Quote class, but the Fluent API is necessary to set the cascading rule to false to prevent foreign key conflicts.
I have been able to navigate both directions with no problems and exactly as expected.
Thanks for all your help!
精彩评论