Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/304.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有构造函数参数的通用可访问类/对象_C#_Unit Testing_Design Patterns_Architecture_Dependency Injection - Fatal编程技术网

C# 具有构造函数参数的通用可访问类/对象

C# 具有构造函数参数的通用可访问类/对象,c#,unit-testing,design-patterns,architecture,dependency-injection,C#,Unit Testing,Design Patterns,Architecture,Dependency Injection,我正在创建一个平台/基础组件(一组.NET程序集),它将负责访问(读取/写入)配置值。此组件将由将在平台上创建的其他组件使用 我希望这个组件易于用户使用,但也具有很高的可测试性和可维护性。在我当前的设计中,我有一个静态类(ConfigurationManager),它包含一些方法(例如GetKeyValueSetting)。此ConfigurationManager类的特殊之处在于,它既可以从本地.NET.config文件中获取配置值,也可以从SharePoint列表(某些组件托管在共享Shar

我正在创建一个平台/基础组件(一组.NET程序集),它将负责访问(读取/写入)配置值。此组件将由将在平台上创建的其他组件使用

我希望这个组件易于用户使用,但也具有很高的可测试性和可维护性。在我当前的设计中,我有一个静态类(ConfigurationManager),它包含一些方法(例如GetKeyValueSetting)。此ConfigurationManager类的特殊之处在于,它既可以从本地.NET.config文件中获取配置值,也可以从SharePoint列表(某些组件托管在共享SharePoint环境中)甚至从另一个共享配置值存储中获取配置值。经理应能够以优先方式从多个位置读取:

  • 如果在SharePoint上运行:1。SharePointSettingsProvider,2。共享存储设置提供程序
  • 如果未在SharePoint上运行:1。配置文件设置提供程序2。共享存储设置提供程序
我知道静态类会在可测试性、可扩展性等方面产生很多问题,所以我不想使用它。我的下一个选择是单例,但对于上述功能,这不是一个更好的解决方案。有没有更好的解决方案

我目前的设计如下:

public static class ConfigurationManager
{
    // internal for testability
    internal IEnumerable<ISettingsProvider> SettingsProviders {get;set;}

    // internal for testability
    internal ISettingsProviderFactory ProvidersFactory {get;set;}

    public static string GetKeyValueSetting(string key)
    {        
    }
}

public interface ISettingsProvider
{
    string GetKeyValueSetting(string key);
}

public class ConfigFileSettingsProvider : ISettingsProvider
{
}

public class SharePointSettingsProvider : ISettingsProvider
{
}

public class SharedStoreSettingsProvider : ISettingsProvider
{
}

public interface ISettingsProviderFactory
{
    IEnumerable<ISettingsProvider> GetProviders();
}
公共静态类配置管理器
{
//内部可测试性
内部IEnumerable设置提供程序{get;set;}
//内部可测试性
内部ISettingsProviderFactory提供程序工厂{get;set;}
公共静态字符串GetKeyValueSetting(字符串键)
{        
}
}
公共接口设置提供程序
{
字符串GetKeyValueSetting(字符串键);
}
公共类ConfigFileSettingsProvider:ISettingsProvider
{
}
公共类SharePointSettingsProvider:ISettingsProvider
{
}
公共类SharedStoreSettingsProvider:ISettingsProvider
{
}
公共接口正在设置ProviderFactory
{
IEnumerable GetProviders();
}
为什么不这样做:

public abstract class ConfigurationManager
{
    public abstract string GetKeyValueSetting(string key);

    public static ConfigurationManager GetInstance()
    {
         return GetInstance(GetDefaultSettingProvider(), GetDefaultProviderFactory());
    }

    public static ConfigurationManager GetInstance(ISettingsProvider provider, IProviderFactory factory)
    {
         return new InternallyVisibleConfigurationManagerImplementation(provider, factory);
    }
}
这有很多好处:

  • ConfigurationManager是抽象的,因此调用代码可以很容易地模拟它
  • 很容易获得默认实现
  • 调用程序集可以轻松地使用不同的提供程序和工厂对其进行配置
  • 调用程序集不能耦合到具体类型
  • 您可以轻松更改特定的实现类型(例如,您可能需要一个缓存代理,这样您就不需要经常对sharepoint进行昂贵的调用)
  • 您保留了所有简单的可测试性
  • 您可以重构为单例或生命周期管理的其他变体,而无需更改客户端代码
如果调用者真的不喜欢这种方式,并且不介意他们的单元测试有时调用sharepoint站点,那么您可以添加一种方便的方法,如下所示:

public static string GetKeyValueSetting(string key)
{
    return GetInstance().GetKeyValueSetting(key);
}
internal class OrderedCompositeSettingProvider : ISettingProvider
{
    private readonly ISettingProvider[] _providers;

    private OrderedCompositeSettingProvider(ISettingProvider[] providers)
    {
        _providers = providers;
    }

    internal static ISettingProvider GetInstance(params ISettingProvider[] providers)
    {
         return new OrderedCompositeSettingProvider(providers)
    }

    public string GetKeyValueSetting(string key)
    {
        foreach(var provider in _providers)
        {
             var setting = provider.GetKeyValueSetting(key);
             if(!string.IsNullOrEmpty(setting)) return setting;
        }
        return string.empty;
    }

}
编辑

要使用多个存储,请创建如下类:

public static string GetKeyValueSetting(string key)
{
    return GetInstance().GetKeyValueSetting(key);
}
internal class OrderedCompositeSettingProvider : ISettingProvider
{
    private readonly ISettingProvider[] _providers;

    private OrderedCompositeSettingProvider(ISettingProvider[] providers)
    {
        _providers = providers;
    }

    internal static ISettingProvider GetInstance(params ISettingProvider[] providers)
    {
         return new OrderedCompositeSettingProvider(providers)
    }

    public string GetKeyValueSetting(string key)
    {
        foreach(var provider in _providers)
        {
             var setting = provider.GetKeyValueSetting(key);
             if(!string.IsNullOrEmpty(setting)) return setting;
        }
        return string.empty;
    }

}
然后在ConfigurationManager工厂方法中:

public static ConfigurationManager GetInstance()
{
     return GetInstance(GetAppropriateProvider(), GetDefaultProviderFactory());
}

private static ISettingsProvider GetAppropriateProvider()
{
     if(ShouldUseSharepoint())
          return OrderedCompositeSettingProvider.GetInstance(new SharepointProvider(), new StoredSettingsProvider());

     return OrderedCompositeSettingProvider.GetInstance(new ConfigFileProvider(), new StoredSettingsProvider());
}

出于某种原因,您想要/需要全局状态吗?它会以多线程方式访问吗?也许我应该更新我的问题。我不需要全球国家。我只希望我的消费者能够使用我的ConfigurationManager类,而无需为他们要检索的每个键值提供ISettingsProvider实例。Thx获取答案Tallseth。我更新了问题,并添加了一些信息,说明经理需要为特定环境决定以何种顺序使用哪种设置Provider。您能详细说明一下您将如何实施该要求吗?更新。根据上下文的更多细节,您可能希望将GetAppPriateProvider中的逻辑封装到ProviderFactory中。我之所以没有这样做,是因为我不完全理解您希望如何使用该类,也因为它让我有了更多的自由。