2015-07-10 165 views
2

Arrrgh!我正在这里拉我的头发。我一直在尝试使用IoC容器,并且一切看起来都很好,并且很棒,直到遇到一些您认为非常基本的问题,例如将参数传递给构造函数。如何在使用IoC容器时将参数传递给构造函数?

说我有与引用类可以由IOC和值类型(或一些其他类型的)来解决的混合某处的类,它可以只在运行时被解决:

public NFLFeedUnitOfWork(NFLFileType fileType, object feed, IConverterMappings<NFLFileType> nflConverterMappings, IDbContext context) 
    : base(fileType, feed, nflConverterMappings, context, ContextType.NFL) 
{ 
    //new NFLContext(connstringname, setAutoDetectChanges) 
} 

在该特定示例我传入枚举(NFLFileType),对象实例,2个接口参数,并将一个额外的硬编码属性传递给基础构造函数(ContextType.NFL)

如何在任何IoC容器中都可以做到这一点?

这个问题实际上是2倍:

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个参数自动解决,并且自己提供1st 2 I?

2.)我如何传入枚举,对象,值类型到构造函数使用IoC? (或者可能不会在这个特定的情况下使用它?)

无论如何,任何帮助非常感谢,特别是在第一点。 此刻我正在使用Unity,但任何其他IoC容器也不错。

我不想将IoC容器传入代码中,我只想在顶层的一个地方指定它。

回答

1

您有几个不同的选项,具体取决于值的来源。 (我只熟悉Ninject,我认为Unity具有类似的功能)

如果数据依赖于另一个服务或存储库,则可以将该对象绑定到委托,以便在请求满足时解析它。例如:

Bind<NFLFileType>().ToMethod(context => context.Kernel.Get<IConfigProvider>().NFLFileType); 

Ninject还支持绑定,只有在使用​​语法某些情况下解决。例如:

Bind<NFLFileType>().ToConstant(NFLFileType.MyValue).WhenInjectedInto<NFLFeedUnitOfWork>(); 

Bind<NFLFileType>().ToConstant(NFLFileType.MyValue).When(request => request.Target.Type.GetCustomAttributes(typeof(MyValueAttribute)) != null); 

如果一切都失败时,可以将注入的接口,可以像一个工厂模式提供者生成对象:

Bind<IFeedUnitOfWork>().ToProvider<UnitOfWorkProvider>(); 

和提供者知道如何解决前两个基于上下文的参数。

HTH。

5

我有的引用类的混合,可以是 IOC和值类型(或一些其他类型的)解决某处一类只能 解决在运行时

这是你出错了。编写组件时,不应将编译时间依赖性与运行时数据混合在一起。您的对象图应该是静态的(并且最好是无状态的),运行时数据应该在完成对象图构建后使用方法调用通过对象图传递。这可以在应用程序的开发中引起极大的简化,因为它可以静态验证对象图形(使用工具,单元测试或使用Pure DI),并且可以防止您今天遇到麻烦。

一般情况下,你有两个选择来解决这个问题:

  1. 您通过方法调用传递到较低级别的组件中的数据。
  2. 您可以通过调用注入组件上的方法来检索数据。

要采取的解决方案取决于上下文。

如果数据是特定于处理的请求并且是您正在处理的用例的一部分,您通常会选择一个选项。例如:

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; } 
    } 
} 
+2

+1。这显示了OP的痛点并提供了很好的建议。 Unity还可以提供ResolverOverrides,以便在解析时覆盖现有注册(如[使用覆盖解析对象](https://msdn.microsoft.com/en-us/library/ff660920(v = pandp 0.20)的.aspx))。我毫不犹豫地提到这一点,因为我认为上述建议应该是第一种方法。 –

+0

这样一个很好的答案!非常感谢Steven,这绝对回答了我的大部分问题,并指出了我的正确方向。但是你能澄清一下吗?在案例1提供的例子中,我们基本上为调用'GetUnitOfWork'方法的类创建了一个Provider。然而,在'GetUnitOfWork'本身的实现中,我们仍然将仅在运行时已知的2个值传递给'new NFLFeedUnitOfWork(...'这是否意味着我还需要为'NFLFileType fileType'和'object创建另外2个提供者(这似乎有点太多,1个运行时值的提供者) – Tanuki

+0

@Tanuki:我无法回答这个问题,我不知道那些vakues是什么,以及它们是如何确定的。与这方面的信息,我会尝试看看。 – Steven