使用实体框架将两个相关表联接到单个实体中



我的问题与 ADO.NET 实体框架中的"表连接"函数有关。假设一个数据库包含一个表"产品"和另一个表"产品价格"。 价格表存储产品所有价格更改的历史记录,包括开始日期和结束日期,其中包含当前价格的行由结束日期列中的 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 中手动执行联接。

任何建议将不胜感激。

您不满足实体拆分的条件:

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

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

  • 正在映射的实体类型在每个基础表中都有条目。换句话说,实体类型表示两个表之间具有一对一对应关系的数据;实体类型表示两个表的内部联接。

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

选择:

一个。您可以在两者和查询之间创建一对多关联:

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

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

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

您需要

在两个表之间执行外键(即在 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();

最新更新