实体框架(Core),对同一个表但不同对象的多个引用

时间:2017-06-26 16:50:45

标签: c# entity-framework

我有汽车零件:

CREATE TABLE dbo.Part (
    Id int IDENTITY(1,1) not null,
    PartNumber nvarchar(48) not null,
    Colour nvarchar(100) null,
    Height int null,
    -- etc
)

汽车零件通常可以互换。您可以使用OEM部件替换汽车/卡车上的1个部件" A"或售后市场部分" B"。

CREATE TABLE dbo.Interchanges (
    Part1 int not null,
    Part2 int not null,
    Notes nvarchar(500) not null,
)

ALTER TABLE dbo.Interchanges WITH CHECK ADD CONSTRAINT fk_Interchanges_Part1
FOREIGN KEY (Part1) REFERENCES dbo.Part (Id)

ALTER TABLE dbo.Interchanges WITH CHECK ADD CONSTRAINT fk_Interchanges_Part2
FOREIGN KEY (Part2) REFERENCES dbo.Part (Id)

注意:1部分可以与许多其他部分互换,在这种情况下,互换是双向的。

现在让我们假设我有两个部分(部分编号A和B),它们彼此互换。

当我实例化A部分时,我希望它有一个交换集合。在这种情况下,集合将具有一个对象。该对象将具有交换注释和部件号为B的部件对象实例。

同样,如果我编辑B部分,它的集合将有一个具有交换注释的对象和一个部件号为A的部件实例。

有一种优雅的方式来模拟这个吗?

我可以使用Google这个模式的名称吗?

使用上面的SQL,当我加载A部分时,它有一个ICollection(使用Entity Framework Power Tools插件创建)。该交换对象具有交换注释,部分B的实例,以及部分A的实例。

1 个答案:

答案 0 :(得分:1)

在关系模型中,没有一流的方法来建模Symmetric Relation,所以你要么必须在应用程序代码中处理它,要么将它存储为两个单独的行(A,B,& #39; A和B是可互换的')和(B,A,' A和B是可互换的')

如果为每个关系的反转添加行,则EF模型很简单,因为Part只需要一个导航属性。

这是我们正在寻找的(我认为)的Code First模型版本:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;

namespace ConsoleApp6
{
    public class Part
    {
        public int Id { get; set; }
        public string PartNumber { get; set; }
        public string Colour { get; set; }
        public int Height { get; set; }

        public virtual ICollection<Interchange> Interchanges { get; } = new HashSet<Interchange>();

    }

    public class Interchange
    {
        [Key]
        [Column(Order =1 )]
        public int FromPartId { get; set; }

        [Key]
        [Column(Order = 2)]
        public int ToPartId { get; set; }

        public virtual Part FromPart { get; set; }
        public virtual Part ToPart { get; set; }
    }



    class Db : DbContext
    {

        public DbSet<Part> Parts { get; set; }
        public DbSet<Interchange> Interchanges { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Part>()
                        .HasMany(p => p.Interchanges)
                        .WithRequired()
                        .HasForeignKey(i => i.FromPartId)
                        .WillCascadeOnDelete(false);

            base.OnModelCreating(modelBuilder);
        }


    }
    class Program
    {
        static void Main(string[] args)
        {
            Database.SetInitializer(new DropCreateDatabaseAlways<Db>());

            using (var db = new Db())
            {
                db.Database.Log = a => Console.WriteLine(a);

                var A = db.Parts.Create();
                A.PartNumber = "A";

                var B = db.Parts.Create();
                B.PartNumber = "B";

                A.Interchanges.Add(new Interchange() {  FromPart = A, ToPart = B });
                B.Interchanges.Add(new Interchange() { FromPart = B, ToPart = A });

                db.Parts.Add(A);
                db.Parts.Add(B);

                db.SaveChanges();
            }

            using (var db = new Db())
            {
                db.Configuration.LazyLoadingEnabled = true;
                db.Configuration.ProxyCreationEnabled = true;

                var A = db.Parts.Where(p => p.PartNumber == "A").Single();

                Console.WriteLine($"Part {A.PartNumber} has {A.Interchanges.Count()} Interchanges");

                Console.WriteLine($"Part {A.PartNumber} is interchangable with {A.Interchanges.First().ToPart.PartNumber}");
            }
                Console.ReadKey();
        }
    }
}