开发者

Use MVC 3 and Entity Framework, how do I map a different instance of the same class to 3 different collection properties?

开发者 https://www.devze.com 2023-04-11 00:06 出处:网络
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 whe

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!

0

精彩评论

暂无评论...
验证码 换一张
取 消