2 个 MEF 插件使用 Entity Framework 与不同的供应商

2 MEF plugins using Entity Framework with different providers

我有一个 WPF 应用程序,我的用户可以使用 MEF 为其创建自己的插件。每个插件都实现了一个接口,允许主应用程序对某些数据源执行 CRUD 操作,例如一个数据库。

我创建了 2 个插件:

两者都在使用 Entity Framework 来完成他们的工作。这些插件中的每一个都需要有自己的 DbConfiguration class.

实现

现在,问题是 WPF 应用程序加载了这 2 个插件,但未能为每个插件分配自己的 DbConfiguration class 实现,因为似乎每个 AppDomain 只能有一个 DbConfiguration . 所以我总是只有一个插件在工作。

我正在考虑只有一个 DbConfiguration class 的实现,并为每个插件提供一个选项以将其所需的配置添加到其中,但问题是它在 WPF 应用程序和 Entity Framework。我想只在插件中保留 Entity Framework 内容,而不需要修改 WPF 应用程序。它不应该关心插件使用什么来访问它们的数据源。

有没有办法让它以这种方式工作?我能否以某种方式为每个插件创建一个单独的 AppDomain,这样也许每个插件都可以使用自己的 DbConfiguration class?

我找到了一个有点 hacky 的解决方案,但它似乎确实有效,所以我想我会 post 它,在不太可能的情况下,某人会在未来。

经过一些额外的研究,我了解到您可以使用 DbConfiguration.Loaded 事件为 EF 注册一些额外的依赖项解析器。因此,在每个插件的构造函数中,我订阅了事件并添加了一个新的依赖关系解析器:SQLite LocalDatabase 和 MySql RemoteDatabase。我摆脱了每个插件的自定义 DbConfiguration 类。

这看起来很有希望,但实际上出现了一个新问题 - 在某些情况下,LocalDatabase 插件调用了 MySql 解析器,它实际上返回了所请求服务类型的 MySql 实现.显然 LocalDatabase 插件无法使用它,因为它需要 SQLite 实现。还有 vice-versa.

因此,每个解析器实际上都需要检查谁调用了 GetService 方法 - 如果它是自定义解析器所在的同一程序集中的某个方法,它会尝试解析。否则,假设来自不同插件的解析器应该处理该请求,并且 returns null 实际让它这样做。

问题是 GetService 方法不提供有关请求者的任何信息。所以这就是我想出 hacky 解决方案的地方,它使用 StackTrace 检查任何被调用的方法是否属于当前 Resolver 所在的相同 Assembly

public class CustomMySqlDbDependencyResolver : IDbDependencyResolver
{
    private readonly Assembly _executingAssembly = Assembly.GetExecutingAssembly();
    private readonly MySqlDependencyResolver _mySqlResolver = new MySqlDependencyResolver();

    public object GetService(Type type, object key)
    {
        var stackTrace = new StackTrace();
        StackFrame[] stackFrames = stackTrace.GetFrames().Skip(1).ToArray();
        bool shouldResolve = stackFrames.Any(f => f.GetMethod().DeclaringType.Assembly.Equals(_executingAssembly));
        if (!shouldResolve)
        {
            return null;
        }

        var resolvedService = _mySqlResolver.GetService(type, key);
        return resolvedService;
    }

    public IEnumerable<object> GetServices(Type type, object key)
    {
        var service = GetService(type, key);
        if (service != null)
        {
            yield return service;
        }
    }
}