2个使用Entity Framework的MEF插件与不同的提供者

时间:2018-03-19 20:47:42

标签: c# entity-framework mef

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

我创建了2个插件:

  • LocalDatabase - 提供来自SQLite数据库的数据
  • RemoteDatabase - 从MySQL数据库提供数据

两者都在使用Entity Framework来完成他们的工作。每个插件都需要有自己的 DbConfiguration 类的实现。

现在,问题是WPF应用程序加载了这两个插件,但未能为每个插件分配他们自己的DbConfiguration类实现,因为看起来每个AppDomain只能有一个DbConfiguration。 所以我总是只有其中一个插件工作。

我正在考虑只有一个DbConfiguration类的实现,并为每个插件提供一个选项来添加其所需的配置,但问题是它在WPF应用程序和实体框架之间创建了一些耦合。我想将实体框架内容仅保留在插件内,而无需修改WPF应用程序。它不应该关心插件用于访问其数据源的内容。

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

1 个答案:

答案 0 :(得分:0)

我找到了一个有点hacky的解决方案,但它似乎确实有效,所以我想我会发布它,在一个不太可能的情况下,某人将来会在某个地方遇到同样的问题。

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

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

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

问题是GetService方法不提供有关请求者的任何信息。这就是我提出hacky解决方案的地方,它使用StackTrace检查任何被调用的方法是否属于当前解析器所在的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;
        }
    }
}