实体框架多对多问题

时间:2010-07-02 17:55:03

标签: entity-framework many-to-many

请帮助EF n00b设计他的数据库。 我有几家公司生产多种产品,因此公司和产品之间存在多对多的关系。我有一个中间表,Company_Product,与他们有关。

每个公司/产品组合都有一个独特的SKU。例如,Acme小部件有SKU 123,但Omega小部件有SKU 456.我在公司_产品中间表中添加了SKU作为字段。

EF生成了一个公司与Company_Product表之间具有1:*关系的模型,以及产品和Company_Product表之间的1:*关系。我真的想要公司和产品之间的关系。但是,最重要的是,没有办法直接从模型访问SKU。

我是否需要将SKU放在自己的表中并编写连接,还是有更好的方法?

2 个答案:

答案 0 :(得分:33)

我刚刚在一个新的VS2010项目(EFv4)中对此进行了测试以确定,这就是我发现的:

如果中间的关联表(Company_Product)只有2个外键到其他表(CompanyID和ProductID),那么将所有3个表添加到设计器最终会建模多对多关系。它甚至没有为Company_Product表生成一个类。每个公司都有一个产品系列,每个产品都有一个公司系列。

但是,如果你的关联表(Company_Product)有其他字段(例如SKU,它自己的主键,或其他描述性字段,如日期,描述等),那么EF建模者将创建一个单独的类,它确实你已经看到了什么。

让中间阶段与公司和产品之间的1:*关系并不是坏事,您仍然可以通过一些简单的查询获得所需的数据。

// Get all products for Company with ID = 1
var q =
    from compProd in context.Company_Product
    where compProd.CompanyID == 1
    select compProd.Product;

是的,例如,当您已经加载了实体对象时,仅仅导航模型的关系并不容易,但这就是数据层的用途。封装获取所需数据的查询。如果你真的想要摆脱那个中间的Company_Product类,并且在类模型中直接表示多对多,那么你将不得不去除Company_Product表只包含2个外键,并摆脱它SKU。

实际上,我不应该说你必须这样做......你可以在设计师中进行一些编辑,无论如何都要这样设置。我会试一试并报告。

<强>更新

将SKU保留在Company_Product表中(意味着我的EF模型有3个类,而不是2个;它创建了Company_Payload类,其他2个表格为1:*),我试图直接在公司和产品。我遵循的步骤是:

  • 右键点击设计器中的公司类
  • 添加&gt;协会
  • 将左侧的“结束”设为公司(应该已经是)
  • 在产品右侧设置“结束”
  • 将两个多重性更改为“*(多个)”
  • 导航属性应命名为“Products”和“Companies”
  • 点击确定。
  • 右键单击模型中的关联&gt;单击“表映射”
  • 在“添加表格或视图”下,选择“Company_Product”
  • 地图公司 - &gt; ID(左侧)到CompanyID(右侧)
  • 地图产品 - &gt; ID(左侧)到ProductID(右侧)

但是,它不起作用。它给出了这个错误: 错误3025:从第175行开始映射片段时出现问题:必须指定表Company_Product的所有关键属性(Company_Product.SKU)的映射。

因此,特定关联无效,因为它使用Company_Product作为表格,但不会将SKU字段映射到任何内容。

此外,在我研究这个时,我从“Entity Framework 4.0 Recipies”这本书中看到了这个“最佳实践”小故事(请注意,对于具有额外字段的关联表,除了2个FK之外,它们还将额外的字段称为“有效负载”。在您的情况下,SKU是Company_Product中的有效负载。)

  

最佳实践

     

不幸的是,一个项目   从几个开始,   无有效载荷,多对多   人际关系往往最终结束   几个,有效载荷丰富,多对多   关系。重构模型,   特别是在发展的后期   循环,以容纳有效载荷   多对多的关系可以   乏味。不仅是额外的   实体介绍,但查询   和导航模式通过   关系也发生了变化。一些   开发人员争辩说每一个   应该开始多对多的关系   关闭一些有效载荷,通常是   合成键,所以不可避免   增加了更多的有效载荷   对...的影响明显减少   项目

     

所以这是最好的做法。   如果您有无负载,   与你有多对多的关系   认为它有可能存在   随时间变化以包括有效载荷,   从一个额外的标识列开始   链接表。导入时   表格进入你的模型,你会得到   两个一对多关系,其中   表示您编写的代码和模型   你已准备好任何号码   其他有效载荷列的   随着项目的成熟而来。该   额外整数身份的成本   专栏通常是一个相当小的价格   付钱以保持模型更多   柔性的。

(来自第2章。实体数据建模基础知识,2.4。使用有效负载建模多对多关系)

听起来很不错。特别是因为你已经有了一个有效载荷(SKU)。

答案 1 :(得分:2)

我想在Samuel的回答中添加以下内容:

如果要直接从多对多关系(带有效负载)的一方查询到另一方,可以使用以下代码(使用相同的示例):

Company c = context.Companies.First();
IQueryable<Product> products = c.Company_Products.Select(cp => cp.Product);

products变量将是与Product记录关联的所有Company c条记录。如果您想为每个产品添加SKU,您可以使用如下的匿名类:

var productsWithSKU = c.Company_Products.Select(cp => new {
  ProductID = cp.Product.ID,
  Name = cp.Product.Name,
  Price = cp.Product.Price,
  SKU = cp.SKU
});
foreach (var 

为简单起见,您可以将第一个查询封装在只读属性中,如下所示:

public partial class Company
{
  public property IQueryable<Product> Products
  {
    get { return Company_Products.Select(cp => cp.Product); }
  }
}

您不能对包含SKU的查询执行此操作,因为您无法返回匿名类型。您必须有一个明确的类,通常可以通过向Product类添加非映射属性或创建另一个继承Product的类来添加SKU属性来完成。如果你使用的是继承的类,你将无法对它进行更改并让它由EF管理 - 它只对显示有用。

干杯。 :)