【问题标题】:Compose queries across Entity Data Models跨实体数据模型编写查询
【发布时间】:2011-02-27 21:49:17
【问题描述】:

如果模型访问相同的底层数据库,有没有办法从 2 个不同的实体模型中组合查询。

我的情况是这样的: 我有一个使用 EF 进行数据访问的框架。(EDM 1) 我有一个使用框架服务的客户端应用程序,还使用 ​​EF 进行自己的数据访问。(EDM2)

在某些情况下,我需要编写查询并加入跨越 2 个 EDM 的实体。

有没有办法做到这一点,而无需从第一个 EDM 获取内存中的数据,然后从第二个 EDM 的实体在内存中应用额外的谓词/连接?

我希望我以正确的方式表达这一点

编辑 @Ladislav Mrnka: 第一个 EDM 是可重用框架的数据访问层。 将此 EDM 中的 EF 生成实体与 消费客户的 如果我这样做,它会破坏 API 的可重用性,而且我必须携带额外的膨胀 (客户端的 EF 元数据和数据库表)每次我想重新部署框架时。 这也会使在设计器中管理模型变得笨拙。

我目前正在使用您提到的第 7 项作为解决方案,性能非常糟糕 由于我最终必须返回比使用框架所需的更多数据(即实体) EDM1,然后根据谓词/条件过滤掉不需要的值 来自第二个 EDM 中实体的属性。最终结果是性能大幅下降和 DBA 不满意。

出于这个原因,我最终推动了检索实体所需的逻辑 到一个 SPROC,我可以在其中访问两个 EDM 使用的表并应用所需的谓词 并让整个查询在数据库中运行,而不是将数据放入内存 然后过滤掉不必要的。缺点是我不能使用LINQ

您提到的第 8 项听起来很有趣,但从听起来我怀疑 你在设计时得到强类型,对吗? 你能把你的代码示例上传到某个地方让我试试吗?

【问题讨论】:

  • 您不能只在后端执行此操作,还是 sql 数据库目录不能通过链接服务器或同等服务器相互看到?
  • @Carnotaurus:这就是我目前正在做的事情。两个 EDM 的表都在同一个数据库中,所以我使用 Sproc 来查询框架(EDM1)和客户端(EDM2)的表。缺点是我不能使用 LINQ
  • 我添加了 EF4 标签,因为我的答案中描述的大部分功能仅在 EF4 中有效。
  • 顺便说一句。可重用框架意味着它包装了数据访问,因此您无法在使用框架的应用程序中访问它(它是黑匣子,您根本不知道它的模型)。在这种情况下,最好的方法是第二项。您所描述的更像是包含一些已定义数据访问的项目 - 它看起来仍然是重构数据访问和使用单个 EDMX 的好候选。但这是哲学讨论。我也在我的项目中使用了两个 EDMX,但出于不同的原因。
  • 它确实包装了框架实体的数据访问。问题是发出 LINQ 查询,这些查询基于一些关键字段从客户端实体到框架实体。例如:var query = from r in myclientContext.Request from e in myreusableAPIContext.Entities where r.ID = e.externalEntityID and e.FieldValues.Contains(r.somefieldName)

标签: entity-framework entity-framework-4


【解决方案1】:

重要修改

没有内置支持使用两个ObjectContext 类型实现此目的。您的查询必须始终针对单个 ObjectContext 执行。

可能是最好的方法:这对我来说很有趣,我自己尝试了一下。我从一个非常简单的想法开始。两个 EDMX 文件(与 POCO T4 生成器一起使用),每个文件都包含一个实体。我从第二个连接字符串中获取元数据描述并将其添加到第一个连接字符串中。我直接使用了ObjectContextObjectSet。通过这样做,我能够从单个 ObjectContext 实例查询和修改这两个实体。我还尝试从这两个模型创建查询连接实体并且它有效。 这显然只有在两个 EDMX 都映射到同一个数据库(相同的数据库连接字符串)时才有效。

重要的部分是连接字符串:

<configuration>
  <connectionStrings>
    <add name="TestEntities" connectionString="metadata=res://*/FirstModel.csdl|res://*/FirstModel.ssdl|res://*/FirstModel.msl|res://*/SecondModel.csdl|res://*/SecondModel.ssdl|res://*/SecondModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=.;Initial Catalog=Test;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>
</configuration>

此连接字符串包含来自两个模型的元数据 - FirstModel.edmx 和 SecondModel.edmx。

另一个问题是强制 EF 使用这两个文件的映射。每个 EDMX 文件必须为 SSDL 和 CSDL 定义唯一的容器。 ObjectContext 提供名为 DefaultContainerName 的属性。该属性可以直接设置,也可以通过一些构造函数重载来设置。设置此属性后,您将 ObjectContext 实例绑定到单个 EDMX - 对于这种情况您不得设置此属性。省略 DefaultContainerName 可能会产生一些后果,因为某些功能和声明可能会停止工作(您会遇到运行时错误)。除非您想使用一些高级功能,否则您应该不会遇到 POCO 问题。如果您使用实体对象(重型 EF 实体),您很可能会遇到问题。使用定义为字符串的实体集的所有方法都依赖于容器。因此,我建议仅在必要时使用此类配置 - 用于跨模型查询。

最后一个问题是生成实体和“强类型”派生的ObjectContext。可行的方法是修改 T4 模板,以便一个模板从多个 EDMX 文件中读取数据并为所有这些文件生成上下文结束实体——我已经在我的项目中这样做了,它可以工作。默认 T4 实现不遵循上一段中描述的所需方法。从默认 T4 实现派生的 ObjectContext 依赖于单个 EDMX 和实体容器。


这部分是在上次编辑之前编写的。

我将留下其余信息,只是因为其中一些信息在其他场景中可能有用,包括使用多个数据库。

类似 ORM 的实体框架在对象世界和数据库世界之间的映射之上运行。在 EF 中,对象世界由CSDL 描述,数据库世界被描述为SSDL,它们之间的映射被描述为MSL(都只是具有众所周知的模式的XML)。在设计时,这些描述是存储在 EDMX 文件中的模型的一部分。在编译期间,这些描述是从 EDMX 中提取的,默认情况下作为资源文件包含在已编译的程序集中。

当您创建ObjectContext 的实例时,它会收到包含对 CSDL、SSDL 和 MSL 资源文件的引用的连接字符串。 SSDL 或 MSL 不指定包含元素以添加来自其他文件的信息。 CSDL 提供Using 元素,允许您重用现有映射,但设计器不支持此功能。 ConnectionString 用于初始化EntityConnection 实例,该实例又用于初始化ObjectContext 的MetadataWorkspace(运行时映射信息)。 ObjectContext 也不提供任何将多个上下文嵌套到层次结构中的功能。 连接字符串不能包含对这些文件的多个实例的引用。 编辑:它可以。我刚刚测试了它。请参阅开头的段落。

当您在ObjectContext 的实例上运行 Linq 或 ESQL 查询时,它使用 MSL 将您的实体或 POCO 类(由 CSDL 定义)映射到 DB 查询(由数据库表的 SSDL 描述定义)。如果没有此信息,它将无法工作(如果存储在单独的 EDMX 中,则无法获得该信息)。

那么如何解决这个问题呢?有几种方法:

  1. 始终考虑:将您的映射合并到一个文件中(如果多个文件用于同一个数据库)。这应该是使用 EF 的方式,正如您所提到的,您正在查询同一个数据库,因此不需要两个 EF 模型。
  2. 第二个模型中的重复实体描述。如果您使用 EF4 和 POCO,您可以将来自多个模型的相同描述映射到一个 POCO 类定义中。我不喜欢这个解决方案,但有时它会有所帮助。
  3. 定义包含查询(或查询核心)的数据库视图或存储过程,并将其在一个模型中映射到新实体。
  4. 在一个模型中使用DefiningQuery(如果您使用从数据库更新功能,您可能需要第三个)并将其映射到新实体。 DefiningQuery 是在 SSDL 中定义的自定义 SQL 查询,而不是表或视图描述。
  5. 使用Function 与自定义CommandText 指定数据库查询。它类似于使用 DefiningQuery 并且具有相同的限制。您必须手动(在 EDMX 中)将函数的结果映射到新的复杂类型(与映射到新实体的 DefiningQuery 的另一个区别)。
  6. 为查询结果定义新类型(该类型的属性必须与查询中返回的列同名)并使用 ObjectContext 的ExecuteStoreQuery(仅在 EF4 中)。
  7. 将查询分成两部分,每部分在自己的上下文中单独执行,并使用 linq-to-objects 获取结果。我不喜欢这个解决方案。

  8. 这只是一个高级想法 - 我没有尝试过,我不知道它是否有效。 如上所述,运行时映射取决于 @987654346 的内容从EntityConnection 填充的@ 实例。 EntityConnection 还提供了直接接收MetadataWorkspace 实例的构造函数。因此,一般来说,如果可以从多个 EDMX 填充 MetadataWorkspace,您将不需要多个 ObjectContext 实例,但您的映射仍将分为两个 EDMX。这有望让您在两个映射文件之上编写自定义 Linq 查询)。 编辑:这应该是可能的,因为如果您在连接字符串中定义多个映射,这正是 EF 正在做的事情。

  9. 使用CSDL Using feature 将模型分解为多个重复使用的部分。

【讨论】:

  • 感谢您的详细回复。我有一堆 cmets,因此将我的 cmets 添加到原始问题中作为编辑,因为我添加 cmets 的字符有限...
猜你喜欢
  • 1970-01-01
  • 2014-10-20
  • 1970-01-01
  • 1970-01-01
  • 2013-02-15
  • 1970-01-01
  • 1970-01-01
  • 2021-10-15
  • 1970-01-01
相关资源
最近更新 更多