我有汽车零件:
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的实例。
答案 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();
}
}
}