【发布时间】: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