在您在问题中描述的情况下,MEF 不会帮助您解决可扩展性问题,因为它似乎没有任何问题。对于这种特殊情况,MEF 可以带来的是依赖注入。如果您以一种表示层依赖于业务抽象而业务层依赖于数据访问抽象而不是具体实现的方式来开发您的应用程序,您将获得一个高度解耦的应用程序,该应用程序易于测试、维护和更改而不会影响不必要的组件。这是执行此操作的一种方法:
假设您有对员工执行 CRUD 操作的数据访问类:
[Export(typeof(IEmployeeRepository))]
public class EmployeeRepository : IEmployeeRepository
{
//Some logic here
}
如您所见,EmployeeRepository 类实现了IEmployeeRepository 接口,该接口增加了开发解耦应用程序所需的抽象级别。现在假设在业务层中有一个类需要从EmployeeRepository 类中调用一些方法。为了能够调用EmployeeRepository 的方法,您需要它的一个实例。不使用 MEF(或任何其他 IoC 框架,这将是一种方法:
public class EmployeeManager
{
private EmployeeRepository _employeeRepository;
public EmployeeManager
{
_employeeRepository = new EmployeeRepository();
}
}
通过使用上面的代码示例,在EmployeeManager 和EmployeeRepository 类之间创建了硬依赖关系。这种硬依赖在编写单元测试时很难隔离,并导致EmployeeRepository 类的任何更改都会直接影响EmployeeManager 类。通过稍微重构代码示例并将 MEF 放入游戏中,您将得到:
[Export(typeof(IEmployeeManager))]
public class EmployeeManager : IEmployeeManager
{
private IEmployeeRepository _employeeRepository;
[ImportingConstructor]
public EmployeeManager(IEmployeeRepository employeeRepository)
{
_employeeRepository = employeeRepository;
}
}
如您所见,EmployeeManager 类现在不依赖于EmployeeRepository 类,但它依赖于IEmployeeRepository 接口,换句话说,它依赖于抽象。而且它不会自己创建EmployeeRepository 类的实例。该工作留给 MEF。现在应该清楚 export 和 ImportingConstructor 属性是 MEF 的一部分,并被它用于在运行时发现部件和解决依赖关系。使用最后一个代码示例,类被解耦,易于测试和维护,您可以更改EmployeeRepository 类中的内部逻辑,而无需让EmployeeManager 类意识到它。当然,他们之间的合同,IEmployeeRepository 必须保持。
上面说的,也可以用来解耦表现层和业务层。上面也说过,可以通过使用任何其他 IoC 框架来实现,如 Ninject、Autofac、StructureMap 等。这些 IoC 框架和 MEF 之间的区别在于,如果您使用它们,您需要在应用程序启动时配置哪个实例获取遇到某些接口时创建:
//something like this, in the case of Ninject
this.Bind<IEmployeeRepository>().To<EmployeeRepository>();
另一方面,MEF 能够在运行时发现部件。在应用程序启动时,您只需要告知它在哪里寻找部件:目录、程序集、类型等。MEF 的自动连接功能(在运行时发现部件的能力)使其不仅仅是一个常规的 IoC 框架.这种能力使 MEF 非常适合开发可插入和可扩展的应用程序,因为您将能够在应用程序运行时添加插件。 MEF 能够加载它们并让应用程序使用它们。