如何从可插拔DAL中去除异味

时间:2016-10-27 15:17:49

标签: c# design-patterns

我正在使用由几个不同的,断开连接的组件组成的应用程序,每个组件最多可依赖于三个不同的数据存储(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对这些模式提出了令人信服的论据herehere我不确定如何摆脱这种实施。

这里的快速回答是可能使用抽象工厂并删除对配置系统的依赖。

抽象工厂

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);
    }
}

我对实施这种模式的两个最大担忧是:

  • 我的类现在将拥有3个工厂依赖项(每个数据提供程序一个);这不是一个大问题,因为我的DI容器将构建我的对象图,但它确实给类增加了一定的混乱。
  • 如果我必须更改具体提供程序(例如,SqlDataProvider成为AzureDataProvider),抽象工厂会违反SOLID

TL; DR /总体问题

我的问题是:是否存在一种模式(或者可以修改上述某种模式),这种模式允许我在寻找能够保持DI友好的同时不那么臭的灵活性?

0 个答案:

没有答案