使用IoC容器时如何将参数传递给构造函数?

时间:2015-07-10 15:02:23

标签: .net ninject unity-container ioc-container

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容器传入代码,我只想在顶层的一个地方指定它。

2 个答案:

答案 0 :(得分:7)

  

我有一个类,可以使用各种引用类   由IoC和值类型(或其他类型)解决,只能是   在运行时解决

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

通常,您有两种方法可以解决此问题:

  1. 通过方法调用将数据传递给较低级别​​的组件。
  2. 通过调用注入组件上的方法来检索数据。
  3. 采取哪种解决方案取决于具体情况。

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

    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。