我目前正尝试使用依赖注入,到目前为止,我喜欢。但是,有一件事我无法理解我现在的解决方案似乎错了。
我正在使用WPF,MVVM,我注入的许多类需要一个项目配置类的实例,在用户在应用程序中创建或打开一个新项目之前,该实例未初始化。
所以我目前的解决方案是使用带有加载/保存方法的“ConfigurationHandler”和一个在加载后保存配置类实例的属性。我将ConfigurationHandler注入其他类,然后他们可以在加载后访问配置。但是让那些永远不应该保存/加载配置的类处理整个“ConfigurationHandler”并且100%他们只是用它来访问配置实例似乎很奇怪:
var configuration = configurationHandler.Configuration;
另一个问题是,如果他们在加载之前尝试访问配置,他们将获得异常(不应该真正发生,因为在创建/加载项目之前你不能做任何事情,但仍然如此)。
但我能想到的唯一其他解决方案是在创建/打开项目后使用“初始化”方法,但这看起来同样糟糕。
那么你通常如何处理这样的案件?
编辑:应该添加此配置类处理项目路径,项目名称等信息,这与依赖注入本身无关。
答案 0 :(得分:0)
考虑使用Ambient Context
方法,而不是构造函数注入。
我们将讨论的最后一种DI是使依赖性可用 通过静态访问器。它也被称为注射 环境背景。在实施跨领域问题时使用它。
如果需要访问您的配置的类在不同的层或库中具有不同的类型,这是一个很好的选择 - 这是一个真正的跨领域问题。
abstract class CustomConfiguration
{
//current dependency stored in static field
private static CustomConfiguration current;
//static property which gives access to dependency
public static CustomConfiguration Current
{
get
{
if (current == null)
{
//Ambient Context can't return null, so we assign a Local Default
current = new DefaultCustomConfiguration();
}
return current;
}
set
{
//allows to set different implementation of abstraction than Local Default
current = (value == null) ? new DefaultCustomConfiguration() : value;
}
}
//service which should be override by subclass
public virtual string SomeSetting { get; }
}
//Local Default
class DefaultCustomConfiguration : CustomConfiguration
{
public override string SomeSetting
{
get { return "setting"; }
}
}
用法
CustomConfiguration.Current.SomeSetting;
可以使用其他DI Patterns,但需要更改需要它的类。如果配置是一个跨领域问题,环境上下文可能是最合适的。
构造函数注入示例
public SomeClass(IConfiguration config)
{
}
物业注入
public SomeClass()
{
IConfiguration configuration { get; set; }
}
方法注入
public SomeClass()
{
public void DoSomethingNeedingConfiguation(IConfiguration config)
{
}
}
还有服务定位器,但服务定位器是(IMO)anti-pattern。
答案 1 :(得分:0)
如果您的配置是静态的(读取:它仅在启动应用程序期间读取,例如从project.json
或Web.Config
),您也可以在应用启动期间/组合根目录中进行设置。
新的ASP.NET 5大量使用它并且它运行良好。基本上,您将拥有一个IConfiguration<T>
接口和一个POCO类,您可以在应用启动期间设置该类,并将其解析/注入您的服务。
public interface IConfiguration<T> where T : class
{
T Configuration { get; }
}
这是默认实现
public interface DefaultConfiguration<T> where T : class
{
private readonly T configuration;
public T Configuration {
return configuration;
}
public DefaultConfiguration<T>(T config)
{
this.configuration = this.configuration;
}
}
和你的POCO课程
public class AppConfiguration
{
public string OneOption { get; set; }
public string OtherOption { get; set; }
}
在你的作品根目录中,你会注册它,比如
// read Web.Config
Configuration rootWebConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(null);
container.AddSingleton<IConfiguration<AppConfiguration>>(new DefaultConfiguration<AppConfiguration>(
new AppConfiguration
{
OneOption = rootWebConfig.AppSettings.Settings["oneSetting"],
OtherOption = rootWebConfig.AppSettings.Settings["otherSetting"],
})
);
最后,您必须在服务中声明的是
public class MyService : IMyService
{
public MyService(IUserRepository, IConfiguration<AppConfiguration> appConfig)
{
...
if(appConfig.OneOption=="someValue") {
// do something
};
}
}
最后,如果编写像
这样的扩展方法,可以使配置更容易一些public static class MyContainerExtension
{
public static void Configure<T>(this IMyContainer container, Action<T> config) where T : class, new()
{
var t = new T();
config(t);
container.AddSingelton<IConfiguration<T>>(t);
}
}
然后您需要做的就是
container.Configure<AppConfiguration>(
config =>
{
config.OneOption = rootWebConfig.AppSettings.Settings["oneSetting"],
config.OtherOption = rootWebConfig.AppSettings.Settings["otherSetting"],
})
);
进行设置