【问题标题】:Providing an on-the fly custom db provider for EF为 EF 提供动态自定义数据库提供程序
【发布时间】:2011-09-09 15:02:35
【问题描述】:

作为分析工具的一部分,我有一个自定义 ADO.NET 堆栈,它充当标准 ADO.NET 周围的“装饰器”,因此它实际上不做任何 工作 - 它只是传递调用(但带有日志记录等)。除其他外,我从实现IServiceProvider 并提供自定义DbProviderServices 的自定义连接中提供了DbProviderFactory

这适用于大多数工具,包括 LINQ-to-SQL - 但是,实体框架并不满意。

例如 - 假设我有:

MetadataWorkspace workspace = new MetadataWorkspace(
     new string[] { "res://*/" }, 
     new Assembly[] { Assembly.GetExecutingAssembly() });
using(var conn = /* my custom wrapped DbConnection */)
{
    var provider = DbProviderServices
          .GetProviderServices(conn); // returns my custom DbProviderServices
    var factory = DbProviderServices
          .GetProviderFactory(conn); // returns my custom DbProviderFactory
    ...

到目前为止一切顺利——以上两行有效;返回正确的(自定义)提供者信息。

现在我们可以添加一个 EF 模型:

    using (var ec = new EntityConnection(workspace,conn))
    using (var model = new Entities(ec))
    {
        count = model.Users.Count(); // BOOM!
    }

异常失败:

无法将“(我的自定义连接)”类型的对象转换为“System.Data.SqlClient.SqlConnection”类型。

在将连接分配给命令期间;本质上,它默认为SSpace 的sql-server 提供程序,并生成了一个裸SqlCommand。然后它尝试将 conn 分配给生成的命令,但该命令无法正常工作(如果所有装饰器都已就位,则它正常工作,并且改用了已装饰的 DbCommand)。

现在,动态包装它的全部意义在于我真的不想更改 EDMX 来注册一个单独的工厂。我只是想让它知道我的谎言、该死的谎言和装饰师。

以下工作,通过侵入SSpace 的胆量(设置我无权知道或滥用的private readonly 字段):

    StoreItemCollection itemCollection =
        (StoreItemCollection)workspace.GetItemCollection(DataSpace.SSpace);
    itemCollection.GetType().GetField("_providerFactory",
        BindingFlags.NonPublic | BindingFlags.Instance)
        .SetValue(itemCollection, factory);

有了这个,SSpace 就会使用正确的工厂。但是,这显然是令人讨厌的。

那么:我在这里错过了一个技巧吗?如何以不那么激烈的措施拦截 EF 提供者?

【问题讨论】:

  • 您以另一种方式将供应商和工厂连接到工作区,对吧?该代码未显示。
  • @Henk 这是缺少的步骤。据我所见,它是基于阅读所有大量 xml 来实现的,这不适合我的场景。 is 显示了连接它的代码 - 这是可怕的反射步骤。请注意,我提到了 connection 挂钩起作用,因此factory 变量是我定制的。如果您知道如何将工作区与提供者挂钩:去吧!
  • 检查EFProviderWrappers中使用的方法
  • @Ladislav - 是的,如果有任何事情证实了我的恐惧:这需要 either 更改对象上下文类型或更改 SSDL - 几乎是我想要的避免。并且上面的 hack 确实 避免了。通过运行时执行此操作并不容易,这似乎很疯狂。
  • @Ladislav 具有讽刺意味的是,考虑到 EF 优于 LINQ-to-SQL 的关键特性之一是......可扩展性;p

标签: .net entity-framework ado.net provider


【解决方案1】:

应该可以使用EFProviderWrappers 中的方法。我知道你提到你尝试过,但我自己只是成功地完成了这种事情。

在上面的代码示例中,您不能将标准 MetadataWorkspace 传递给 EntityConnection 构造函数,因为 MetadataWorkspace 仍将指向 SSDL 部分中的原始 DbProviderFactory(在您的情况下为 SqlClient)。我知道您不想直接修改 EDMX/SSDL(我也没有),但 EFProviderWrappers 工具包有一些可能对您有用的辅助方法。 EntityConnectionWrapperUtils.cs 类尤其有用。它将获取您的原始 EDMX(它甚至可以从嵌入式资源中提取它)并更新 XML,使其指向您的自定义 DbProviderFactory。它在运行时完成这一切,因此您无需进行任何更改。您需要为您的 DbProviderFactory 提供一个不变的名称并注册它 - 如果您还没有这样做的话。

然后您可以将自定义 MetadataWorkspace 与您的自定义 DbConnection 一起传递给 EntityConnection 构造函数,然后 EF 应该在需要创建 DbCommand 时调用您的工厂。

【讨论】:

  • 如果我能给你加 10 我会的。回答这个问题解决了我一年来无法解决的问题。 EFProviderWrappers 是解决方案:)
猜你喜欢
  • 2022-08-02
  • 1970-01-01
  • 1970-01-01
  • 2020-07-08
  • 2021-03-14
  • 1970-01-01
  • 2020-08-07
  • 2021-02-11
  • 2013-04-17
相关资源
最近更新 更多