问题:

    在数据库的链接表中除了原有的外键列之外,还有其他的数据列。现在想把链接表及其关联表导入到EDM中,以形成一个多对多关系或2个一对多关系。

解决方案:

    EF不支持带有属性的关联。如果链接表包含有除了外键之外的其他列,EF将产生一个单独的实体类型以表示这个链接表。最终的模型将包含一个链接表实体类型和2个一对多的关联。

    数据库图表如下:

wKioL1ZcFHjB7KkHAABwUcTtsKA486.png   

数据库脚本如下:

use [EF6Recipes]
go

create table Chapter2.Item(
SKU int primary key,
[Description] varchar(50) not null,
Price decimal(18,2) not null);

create table Chapter2.[Order](
OrderId int identity primary key,
OrderDate datetime not null);

create table Chapter2.OrderItem(
OrderId int foreign key references Chapter2.[Order](OrderId),
SKU int foreign key references Chapter2.Item(SKU),
[Count] int not null,
constraint PK_OrderItem primary key (OrderId,SKU));

    一个订单(Order)能有多个项(Item),一个项也能包含在多个订单里。另外,还有一个Count属性关联到每个订单与项的实例。这个Count属性就被称为负载(Payload)。

    操作步骤同EF6 秘籍 2th:实体数据建模基础 (五)从现有数据库生成模型一致,不再重复。

    生成的EDM如下图:

wKioL1ZcF3GCb9EOAAKNEBHcZ0M712.png

原理:

    EF不支持带有其他属性列的关联。它将转换这个链接表为一个包含2个一对多关联的实体。在这种情况下,OrderItem表没有被转换成一个关联,而是转换为一个1对多关联到Order和一个1对多关联到Item的实体类型。

    额外属性列的增加需要关联表的实体提供一个额外的跳板以获取相关的项。

            using (var context = new EF6RecipesContext())
            {
                var oiList = new List<OrderItem>();
                var order1 = new Order { OrderDate = new DateTime(2010, 1, 18) };
                var order2 = new Order { OrderDate = new DateTime(2010, 1, 19) };
                var item1 = new Item { SKU = 1729, Description = "Backpack", Price = 29.97M };
                var item2 = new Item { SKU = 2929, Description = "Water Filter", Price = 13.97M };
                var item3 = new Item { SKU = 1847, Description = "Camp Stove", Price = 43.99M };

                oiList.Add(new OrderItem { Order = order1, Item = item1, Count = 1 });
                oiList.Add(new OrderItem { Order = order1, Item = item2, Count = 3 });
                oiList.Add(new OrderItem { Order = order1, Item = item3, Count = 1 });
                oiList.Add(new OrderItem { Order = order2, Item = item1, Count = 2 });
                oiList.Add(new OrderItem { Order = order2, Item = item2, Count = 2 });
                oiList.Add(new OrderItem { Order = order2, Item = item3, Count = 2 });

                context.OrderItems.AddRange(oiList);
                context.SaveChanges();       
            }

            using (var context = new EF6RecipesContext())
            {
                foreach (var order in context.Orders)
                {
                    Console.WriteLine("Order # {0}, orderd on {1}",
                        order.OrderId,
                        order.OrderDate.ToShortDateString());
                    Console.WriteLine("SKU\tDescription\tQty\tPrice");
                    Console.WriteLine("---\t-----------\t---\t-----");
                    foreach (var oi in order.OrderItems)
                    {
                        Console.WriteLine("{0}\t{1}\t{2}\t{3}",
                            oi.SKU, oi.Item.Description, oi.Count, oi.Item.Price);
                    }
                }                
            }

运行结果如下:

wKioL1ZepQuw7thIAAAfcwqVLJ0752.png


该代码与原文提供的程序不一样,原文提供的代码我执行的时候只插入了Order的数据,附原文代码:

var order = new Order { OrderId = 1,
OrderDate = new DateTime(2010, 1, 18) };
var item = new Item { SKU = 1729, Description = "Backpack",
Price = 29.97M };
var oi = new OrderItem { Order = order, Item = item, Count = 1 };
item = new Item { SKU = 2929, Description = "Water Filter",
Price = 13.97M };
oi = new OrderItem { Order = order, Item = item, Count = 3 };
item = new Item { SKU = 1847, Description = "Camp Stove",
Price = 43.99M };
oi = new OrderItem { Order = order, Item = item, Count = 1 };
context.Orders.Add(order);
context.SaveChanges();

最佳实践:

    由于链接表有负载和没有负载,生成的模型完全不同。这就需要我们在设计之初就需要分析链接表会不会有其他列的可能,如果不能确定,使用一个自增列。这样的话就会生成一个包含2个1对多关系的实体类型。在以后进行其他属性列扩展时,对模型的影响最小。