【问题标题】:Join two related tables into a single entity using Entity Framework使用实体框架将两个相关表连接到一个实体中
【发布时间】:2012-04-15 14:19:27
【问题描述】:

我的问题与 ADO.NET 实体框架中的“表连接”功能有关。 想象一个包含一个表“Product”和另一个表“ProductPrice”的数据库。价格表存储产品所有价格变化的历史记录,包括开始日期和结束日期,其中包含当前价格的行由结束日期列中的 NULL 值指示。该数据库结构可用于统计目的,例如,可以将平均每日销售量映射到产品价格的每次变化。但是,对于在线订购网站,只需要当前价格。

这是两个表的结构:

Product
ProductID (PK, int, NOT NULL, auto increment)
Name (varchar 50, NOT NULL)

ProductPrice
ProductPriceID (PK, int, NOT NULL, auto increment)
ProductID (INT, NOT NULL)
StartDate (DATETIME, NOT NULL)
EndDate (DATETIME)
Price (MONEY, NOT NULL)

以下是检索产品和当前价格的 SQL 语句示例:

SELECT Product.ProductID, Product.Name, ProductPrice.Price AS CurrentPrice
FROM Product
LEFT JOIN ProductPrice
ON Product.ProductID = ProductPrice.ProductID
AND ProductPrice.EndDate IS NULL

我想使用实体框架将实体 Product 和 ProductPrice 连接在一起,这样我就可以直接从 Product 实体访问当前价格,如下例所示:

var product = (from p in context.Product where p.ProductID == 2 select p).FirstOrDefault();
Console.WriteLine(product.Name);
Console.WriteLine(product.CurrentPrice);

很遗憾,我遇到了无法解决的错误。

以下是存储模型中的实体:

<EntityType Name="Product">
  <Key>
    <PropertyRef Name="ProductID" />
  </Key>
  <Property Name="ProductID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
  <Property Name="Name" Type="varchar" Nullable="false" MaxLength="50" />
</EntityType>
<EntityType Name="ProductPrice">
  <Key>
    <PropertyRef Name="ProductPriceID" />
  </Key>
  <Property Name="ProductPriceID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
  <Property Name="ProductID" Type="int" Nullable="false" />
  <Property Name="Price" Type="money" Nullable="false" />
  <Property Name="StartDate" Type="datetime" Nullable="false" />
  <Property Name="EndDate" Type="datetime" />
</EntityType>
<Association Name="FK_ProductPrice_Product">
  <End Role="Product" Type="TestingModel.Store.Product" Multiplicity="1" />
  <End Role="ProductPrice" Type="TestingModel.Store.ProductPrice" Multiplicity="*" />
  <ReferentialConstraint>
    <Principal Role="Product">
      <PropertyRef Name="ProductID" />
    </Principal>
    <Dependent Role="ProductPrice">
      <PropertyRef Name="ProductID" />
    </Dependent>
  </ReferentialConstraint>
</Association>

从概念模型来看:

<EntityType Name="Product">
  <Key>
    <PropertyRef Name="ProductID" />
  </Key>
  <Property Name="ProductID" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
  <Property Name="Name" Type="String" Nullable="false" MaxLength="50" Unicode="false" FixedLength="false" />
  <Property Name="SKU" Type="String" Nullable="false" MaxLength="50" Unicode="false" FixedLength="false" />
  <Property Type="Decimal" Name="CurrentPrice" Nullable="false" Precision="19" Scale="4" />
</EntityType>

最后是两者之间的映射:

<EntitySetMapping Name="Product">
  <EntityTypeMapping TypeName="TestingModel.Product">
    <MappingFragment StoreEntitySet="Product">
      <ScalarProperty Name="ProductID" ColumnName="ProductID" />
      <ScalarProperty Name="Name" ColumnName="Name" />
      <ScalarProperty Name="SKU" ColumnName="SKU" />
    </MappingFragment>
  </EntityTypeMapping>
  <EntityTypeMapping TypeName="IsTypeOf(TestingModel.Product)">
    <MappingFragment StoreEntitySet="ProductPrice">
      <ScalarProperty Name="CurrentPrice" ColumnName="Price" />
      <Condition ColumnName="EndDate" IsNull="true" />
    </MappingFragment>
  </EntityTypeMapping>
</EntitySetMapping>

这是我目前正在努力解决的错误消息:

Error   1   Error 3024: Problem in mapping fragments starting at line 76:Must specify mapping for all key properties (Product.ProductID) of the EntitySet Product.
Error   2   Error 3025: Problem in mapping fragments starting at line 76:Must specify mapping for all key properties (ProductPrice.ProductPriceID) of table ProductPrice.

我不确定实体框架是否可以做到这一点,也许我应该自己在 LINQ 中手动进行连接。

任何建议将不胜感激。

【问题讨论】:

    标签: entity-framework entity-framework-4


    【解决方案1】:

    你没有遇到conditions for entity splitting

    仅当满足以下条件时,才应将实体类型映射到多个表:

    • 您要映射到的表共享一个公共键。

    • 被映射的实体类型在每个基础表中都有条目。也就是说,实体类型表示的是两个表之间一一对应的数据;实体类型表示两个表的内连接。

    你比 EF 聪明。您知道条件EndDate == null 产生一条记录。 EF 不知道这一点,因此永远不知道它可以从两个表中创建一个对象。就异常消息而言:PriceIdEndDate == null 应该以某种方式传递您的ProductPrice 记录的所有关键属性,这显然是不可能的。

    替代方案:

    A.您可以在两者之间创建一对多关联并进行查询:

    products.Where(p => p.ProductId == 2)
        .Select(p => new
          {
            Product = p,
            CurrentPrice = p.ProductPrices.Where(pp => pp.EndDate == null)
                           .FirstOrDefault()
          })
    

    没有你想的那么方便,但包装在存储库中可能还不错。

    B.创建数据库视图并使用单独的路径进行查看和更新​​(这并不罕见)。

    【讨论】:

    • 衷心感谢您的详细回复。这消除了歧义。
    【解决方案2】:

    您需要在两个表之间做一个外键(即在 ProductID 列上)

    然后要检索所有产品及其价格,您需要执行以下操作:

    var x = from p in context.Product where p.ProductID == 2 && p.ProductPrice.EndDate == null select new { p.ProductID, p.Name, p.ProductPrice.Price }.FirstOrDefault();
    

    【讨论】:

    • 这是 LINQ 中的手动连接。我想要实现的是将两个表连接到概念模型中的单个实体中。这将消除在需要当前价格的任何地方重复连接的需要。
    猜你喜欢
    • 2017-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多