我正在使用由几个不同的,断开连接的组件组成的应用程序,每个组件最多可依赖于三个不同的数据存储(SQL Server,Doc Storage,BLOB存储)。
SQL Server连接详细信息在设计/部署时始终是已知的,但Doc和BLOB存储(当前在Azure中)的详细信息有时在设计时提供,有时在运行时提供,具体取决于我正在工作的特定组件用。由于使用Azure需要一致的成本,我的要求是构建一个可插拔的数据访问层,如果组织想要离开Azure,那么实现新数据提供者的工作量就会很小。虽然我提出的解决方案符合要求,但是我希望删除一些代码气味,但我不确定如何实现它们(以最干净的方式)。以下是我所拥有的结构的简要说明。
数据映射器
public interface IBaseProvider
{
void Configure(IDictionary<string, object> configValues);
}
public interface ISqlProvider : IBaseProvider
{
///CRUD omitted for clarity
}
public interface IBlobProvider : IBaseProvider
{
///CRUD omitted for clarity
}
public interface IDocProvider : IBaseProvider
{
///CRUD omitted for clarity
}
public class SqlDataProvider : ISqlProvider
{
public void Configure(IDictionary<string, object> configValues)
{
//Do stuff
}
}
public class DocDataProvider : IDocProvider
{
public void Configure(IDictionary<string, object> configValues)
{
//Do stuff
}
}
public class BlobDataProvider : IBlobProvider
{
public void Configure(IDictionary<string, object> configValues)
{
//Do stuff
}
}
这里的气味显然是Configure(IDictionary<string, object> configValues)
,原因是:
为了实际向应用程序提供这些对象的实例,我写了一个服务定位器
服务定位器
public interface IProviderLocator
{
T CreateInstance<T>(IDictionary<string, object> configValues) where T : IBaseProvider;
}
public sealed class ProviderLocator : IProviderLocator
{
protected IDictionary<string, object> configValues;
public T CreateInstance<T>(IDictionary<string, object> configurationValues) where T : IBaseProvider
{
configValues = configurationValues;
return Initialize<T>();
}
private T Initialize<T>() where T : IBaseProvider
{
//reach into the configuration system to get providerType
var provider = (T)Activator.CreateInstance(providerType);
provider.Configure(configValues);
return provider;
}
}
然后获得具体提供者的非DI方式可能类似于
var database = new ProviderLocator().CreateInstance<ISqlProvider>(null);
服务定位器实现了定位器模式和提供者“模式”(有人检查看到Mark Seemann没有中风;]),但尽管Mark对这些模式提出了令人信服的论据here和here我不确定如何摆脱这种实施。
这里的快速回答是可能使用抽象工厂并删除对配置系统的依赖。
抽象工厂
public interface IProviderFactory<T>
{
T CreateInstance<T>(IDictionary<string, object> configValues)
}
public sealed class SqlProviderFactory : IProviderFactory<ISqlProvider>
{
public T CreateInstance<T>(IDictionary<string, object> configurationValues)
{
return new SqlDataProvider(configurationValues);
}
}
我对实施这种模式的两个最大担忧是:
TL; DR /总体问题
我的问题是:是否存在一种模式(或者可以修改上述某种模式),这种模式允许我在寻找能够保持DI友好的同时不那么臭的灵活性?