Arrrgh!我把头发拉过来。我一直在尝试使用IoC容器,所有看起来都很好用,直到你遇到一些你认为非常基本的问题,比如将参数传递给构造函数。
假设我的某个类有一个可以由IoC解决的引用类和可以在运行时解析的值类型(或其他类型):
public NFLFeedUnitOfWork(NFLFileType fileType, object feed, IConverterMappings<NFLFileType> nflConverterMappings, IDbContext context)
: base(fileType, feed, nflConverterMappings, context, ContextType.NFL)
{
//new NFLContext(connstringname, setAutoDetectChanges)
}
在这个特定的例子中,我传入Enum(NFLFileType),对象实例,2个接口参数,并将一个额外的硬编码属性传入基础构造函数(ContextType.NFL)
我可以在任何IoC容器中以所有神的名义进行此操作吗?
问题实际上是双重的:
1。)如何传入仅在运行时已知的对象?比如说,目前调用代码看起来像这样:
protected override IFeedUnitOfWork GetUnitOfWork(NFLFileType fileType, object feed, string connectionString)
{
return new NFLFeedUnitOfWork(fileType, feed, new NFLConverterMappings(), new NFLContext(connectionString));
}
如何将此代码转换为使用IoC? 也许是这样的?
protected override IFeedUnitOfWork GetUnitOfWork(NFLFileType fileType, object feed, string connectionString)
{
return IFLFeedUnitOfWork(fileType, feed);
}
最后2个参数是自动解决的,第1个我是自己提供的吗?
2.。)如何使用IoC将枚举,对象,值类型传递给构造函数? (或者在这个特定的例子中可能不使用它?)
无论如何,非常感谢任何帮助,特别是在第一点。 我目前正在使用Unity,但任何其他IoC容器也都可以。
我不想将IoC容器传入代码,我只想在顶层的一个地方指定它。
答案 0 :(得分:7)
我有一个类,可以使用各种引用类 由IoC和值类型(或其他类型)解决,只能是 在运行时解决
这是你出错的地方。在编写组件时,不应将编译时依赖项与运行时数据混合在一起。您的对象图应该是静态的(最好是无状态的),并且在构造完整的对象图之后,应使用方法调用将运行时数据传递到对象图中。这可以极大地简化应用程序的开发,因为它允许对象图形进行静态验证(使用工具,单元测试或使用Pure DI),它可以防止您遇到麻烦。
通常,您有两种方法可以解决此问题:
采取哪种解决方案取决于具体情况。
如果数据特定于已处理的请求,您通常会选择第一个选项,并且该选项将成为您正在处理的用例的一部分。例如:
public interface IFeedUnitOfWorkProvider
{
IFeedUnitOfWork GetUnitOfWork(NFLFileType fileType, object feed);
}
此处IFeedUnitOfWorkProvider
包含一个GetUnitOfWork
方法,该方法需要运行时参数作为输入。实现可能如下所示:
public class FeedUnitOfWorkProvider : IFeedUnitOfWorkProvider
{
private readonly IConverterMappings converterMappings;
private readonly IContext context;
public FeedUnitOfWorkProvider(IConverterMappings converterMappings,
IContext context) {
this.converterMappings = converterMappings;
this.context = context;
}
public IFeedUnitOfWork GetUnitOfWork(NFLFileType fileType, object feed) {
return new NFLFeedUnitOfWork(fileType, feed, this.converterMappings,
this.context);
}
}
这里需要注意的一些事项:
connectionString
未知FeedUnitOfWorkProvider
值。它不是直接依赖,提供者也不必知道它的存在。connectionString
是一个在运行时没有改变的配置值(通常存储在应用程序的配置文件中),它可以注入到NFLContext
中注入其他依赖项的方式。请注意,配置值与运行时值不同,因为它在应用程序的生命周期内不会发生变化。当您处理上下文信息时,第二个选项特别有用。这是对实现很重要的信息,但不应像前面的示例那样传递。一个很好的例子就是代表请求运行的用户信息。对此的典型抽象看起来像这样:
public interface IUserContext {
string CurrentUserName { get; }
}
此界面可以注入任何消费者。使用此抽象,使用者可以在运行时查询用户名。使用其余的请求数据传递用户名通常会很麻烦,因为这会允许调用者更改(或忘记)用户名,使代码更难以使用,更难测试,更容易出错。相反,我们可以使用IUserContext
:
public IFeedUnitOfWork GetUnitOfWork(NFLFileType fileType, object feed) {
if (this.userContext.CurrentUserName == "steven") {
return new AdminUnitOfWork(this.context);
}
return new NFLFeedUnitOfWork(fileType, feed, this.converterMappings,
this.context);
}
IUserContext
实现应如何显示将在很大程度上取决于您正在构建的应用程序类型。对于ASP.NET,我想象这样的事情:
public class AspNetUserContext : IUserContext {
string CurrentUserName {
get { return HttpContext.Current.User.Name; }
}
}
答案 1 :(得分:1)
您有两种不同的选择,具体取决于值的来源。 (我只熟悉Ninject,我认为Unity有类似的功能)
如果数据依赖于其他服务或存储库,则可以将对象绑定到委托,以便在满足请求时解析该对象。例如:
Bind<NFLFileType>().ToMethod( context => context.Kernel.Get<IConfigProvider>().NFLFileType );
Ninject还支持仅在某些情况下使用.When()
语法解析的绑定。例如:
Bind<NFLFileType>().ToConstant( NFLFileType.MyValue ).WhenInjectedInto<NFLFeedUnitOfWork>();
Bind<NFLFileType>().ToConstant( NFLFileType.MyValue ).When(request => request.Target.Type.GetCustomAttributes(typeof(MyValueAttribute)) != null );
如果所有其他方法都失败了,您可以将注入的接口绑定到像Factory模式一样工作的Provider,以生成对象:
Bind<IFeedUnitOfWork>().ToProvider<UnitOfWorkProvider>();
并且提供者知道如何根据上下文解析前两个参数。
HTH。