简体   繁体   中英

Entity Framework Multiple Collections of Same Type

I am trying to create a class with two collections of the same type. However when I read one of the collections, I get data from both. Which means, there must be something wrong with my Fluent API configuration.

In LINQPad, I can see that the collections "Alternatives" and "Answers" has been combined into a single "PageElements"-collection.

Sample Code

class Program
{
    static void Main(string[] args)
    {
        var alternatives = new List<PageElement>
            {
                new PageElement {Data = "Alternative 1"},
                new PageElement {Data = "Alternative 2"},
                new PageElement {Data = "Alternative 3"},
                new PageElement {Data = "Alternative 4"},
            };

        var answers = new List<PageElement>
            {
                new PageElement { Data = "Answer 1" },
                new PageElement { Data = "Answer 2" },
            };

        var page = new Page
            {
                Alternatives = alternatives,
                Answers = answers
            };


        using (var context = new MyContext())
        {
            context.Pages.Add(page);
            context.SaveChanges();

            var loadedPage = context.Pages.First();

            foreach (var answer in loadedPage.Answers)
            {
                Console.WriteLine("Answer: " + answer.Data);
            }

        }


        Console.ReadLine();
    }
}



public class MyContext : DbContext
{
    public DbSet<Page> Pages { get; set; }

    public MyContext()
    {

    }

    protected override void OnModelCreating(DbModelBuilder mb)
    {

        mb.Entity<PageElement>()
          .HasRequired(p => p.Page)
          .WithMany(p => p.Alternatives)
          .HasForeignKey(p => p.PageId);

        mb.Entity<PageElement>()
          .HasRequired(p => p.Page)
          .WithMany(p => p.Answers)
          .HasForeignKey(p => p.PageId);

    }

}

public class Page
{
    public int Id { get; set; }
    public virtual ICollection<PageElement> Alternatives { get; set; }
    public virtual ICollection<PageElement> Answers { get; set; }
}

public class PageElement
{
    public int Id { get; set; }
    public int PageId { get; set; }
    public virtual Page Page { get; set; }

    public string Data { get; set; }
}

Output

Answer: Alternative 1
Answer: Alternative 2
Answer: Alternative 3
Answer: Alternative 4
Answer: Answer 1
Answer: Answer 2

Desired Output

Answer: Answer 1
Answer: Answer 2

This is because both your alternatives and answers are stored in a single table PageElements - even though you construct them seperately, once they hit the database they are saved together. Because of this, when reloaded, EF has no way to split them back into two groups.

A simple solution would be to split them into two types, eg AlternativeElement and AnswerElement which would therefor create two tables, and thus avoid the problem.

EDIT

To answer your follow on question: You could follow a pattern of derived classes, even if the subclasses don't add new fields. Then use the Table per Hierarchy pattern, so that you just have a single database table, which has a column to indicate which subclass each row belongs to., So:

public class PageElement
{
    public int Id { get; set; }
    public int PageId { get; set; }
    public virtual Page Page { get; set; }

    public string Data { get; set; }
}

public class PageElementAnswer : PageElement 
{
}

public class PageElementAlternative : PageElement 
{
}

public class Page
{
    public int Id { get; set; }
    public virtual ICollection<PageElementAlternative> Alternatives { get; set; }
    public virtual ICollection<PageElementAnswer> Answers { get; set; }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM