如何使用的IoC容器时,将参数传递到构造函数? [英] How to pass parameters into constructors when using IoC containers?

查看:214
本文介绍了如何使用的IoC容器时,将参数传递到构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Arrrgh!我拉我的头发在这里。我一直在试图使用的IoC容器一点点,一切似乎很好,很正常,直到你击出了一些问题,你觉得这是很基本的,像传递参数到构造函数。

说我有一类的地方用的参考类组合可以由国际奥委会和值类型(或其他类型)来解决,只能在运行时解析:

 公共NFLFeedUnitOfWork(NFLFileType的fileType,对象饲料,IConverterMappings< NFLFileType> nflConverterMappings,IDbContext上下文)
    :基地(的fileType,饲料,nflConverterMappings,背景,ContextType.NFL)
{
    //新NFLContext(connstringname,setAutoDetectChanges)
}
 

在这个特殊的例子我通过枚举(NFLFileType),对象实例,2接口参数,并通过一个额外的硬盘codeD属性为基础构造(ContextType.NFL)

我如何在所有的神的名字可以做到这一点在任何的IoC容器?

的问题实际上是2倍:

1)如何通过在只知道在运行时的对象?例如说调用code看起来像这样的时刻:

 保护覆盖IFeedUnitOfWork GetUnitOfWork(NFLFileType的fileType,对象饲料,字符串的connectionString)
{
    返回新NFLFeedUnitOfWork(的fileType,饲料,新NFLConverterMappings(),新NFLContext(的connectionString));
}
 

我如何转换这种code是使用IoC的? 也许是为了这样的事情?

 保护覆盖IFeedUnitOfWork GetUnitOfWork(NFLFileType的fileType,对象饲料,字符串的connectionString)
{
    返回IFLFeedUnitOfWork(的fileType,饲料);
}
 

其中最后两个参数会自动解决,1日2〜我的供应由我自己?

2。)我​​怎样才能传递枚举,对象,价值类型为构造函数中使用的IoC? (或者从这个特殊的情况下使用它不要?)

总之,任何帮助是极大的AP preciated,特别是在第1点。 我公司采用统一的时刻,但是任何其他IoC容器是罚款为好。

我不希望传递一个IoC容器到code或者,我只是想在一个地方在顶层指定它。

解决方案
  

我有一类的地方与引用类的组合,可以   IOC和值类型(或一些其他类型的),其只能分辨   在运行时解析

这是你走到哪里错了。你不应该混在一起运行时数据编译时间依赖性,当您撰写的组件。你的对象图应该是静态的(和preferably无国籍)和运行时的数据应通过对象图使用完整的对象图被构造后的方法调用传递。这可能会导致您的应用程序的开发巨大的简化,因为它可以让你的对象图是静态验证(使用工具,单元测试或使用纯DI),并将其$有今天你有麻烦p $ pvents。

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

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

哪个液取取决于上下文

您通常会去的情况下,选择一个数据是特定的处理请求,将使用情况,你正在处理的一部分。例如:

公共接口IFeedUnitOfWorkProvider {     IFeedUnitOfWork GetUnitOfWork(NFLFileType的fileType,对象饲料); }

下面的 IFeedUnitOfWorkProvider 包含 GetUnitOfWork 方法,它需要运行参数的输入。实现可能是这样的:

公共类FeedUnitOfWorkProvider:IFeedUnitOfWorkProvider {     私人只读IConverterMappings converterMappings;     私人只读IContext环境;     公共FeedUnitOfWorkProvider(IConverterMappings converterMappings,         IContext上下文){         this.converterMappings = converterMappings;         this.context =背景;     }     公共IFeedUnitOfWork GetUnitOfWork(NFLFileType的fileType,对象供稿){         返回新NFLFeedUnitOfWork(的fileType,饲料,this.converterMappings,             this.context);     } }

几件事情需要注意:

  • 在所有静态已知依赖关系通过构造函数注入,而运行时间值是通过方法调用注入。
  • 的connectionString 的价值是未知的 FeedUnitOfWorkProvider 。这不是一个直接的依赖和提供者不必知道它的存在。
  • 假设的connectionString 终止一个配置值,在运行时不发生变化(与典型地存储在应用程序的配置文件中),它可以被注入到 NFLContext 其他依赖注入的方式相同。注意,一个配置值是从一个运行值不同,因为它不会在应用程序的生命周期内发生变化。

第二个选项是非常有用的,当你在处理上下文信息。这是为实现重要的信息,但不应该被传递为与previous例子。这方面的一个很好的例子是有关代表卫生组织的要求正在运行的用户信息。一个典型的抽象,这将是这样的:

公共接口IUserContext {     字符串CURRENTUSERNAME {获得; } }

这个接口可以被注入到任意消费。使用这种抽象,用户可以在运行时查询用户名。通过与请求数据的剩余部分的用户名传递通常将是笨拙的,因为这将允许调用者更改(或忘了)的用户名,使得code只是更难一起工作,难以测试更多,容易出错。相反,我们可以使用 IUserContext

公共IFeedUnitOfWork GetUnitOfWork(NFLFileType的fileType,对象供稿){     如果(this.userContext.CurrentUserName ==史蒂芬){         返回新AdminUnitOfWork(this.context);     }     返回新NFLFeedUnitOfWork(的fileType,饲料,this.converterMappings,         this.context); }

如何在 IUserContext 的实施应该像将高度依赖于应用程序,你正在构建的类型。对于ASP.NET我想是这样的:

公共类AspNetUserContext:IUserContext {     串CURRENTUSERNAME {         {返回HttpContext.Current.User.Name; }     } }

Arrrgh! I am pulling my hair over here. I have been trying to use IoC containers for a little bit, and all seems fine and dandy until you hit some issue that you think would be very basic, like passing parameters into constructors.

Say I have a class somewhere with a mix of reference classes that can be resolved by IoC and value types (or some other types) that can only be resolved at runtime:

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

In this particular example I pass in Enum (NFLFileType), object instance, 2 interface parameters and pass in one extra hardcoded property into the base constructor (ContextType.NFL)

How in the name of all gods can I do this in any IoC container?

The problem is actually 2-fold:

1.) How to pass in an object that is only known at runtime? Say for example the calling code looks like this at the moment:

protected override IFeedUnitOfWork GetUnitOfWork(NFLFileType fileType, object feed, string connectionString)
{
    return new NFLFeedUnitOfWork(fileType, feed, new NFLConverterMappings(), new NFLContext(connectionString));
}

How can I convert this code to be using IoC? Perhaps to something like this?

protected override IFeedUnitOfWork GetUnitOfWork(NFLFileType fileType, object feed, string connectionString)
{
    return IFLFeedUnitOfWork(fileType, feed);
}

Where the last 2 parameters are automatically resolved, and the 1st 2 I supply by myself?

2.) How can i pass in Enum, object, value types into constructor using IoC? (or maybe refrain from using it in this particular instance?)

Anyway, any help is greatly appreciated, especially on the 1st point. I am using Unity at the moment, but any other IoC container is fine as well.

I don't want to pass in an IoC container into the code either, I only want to specify it in one place at the top level.

解决方案

I have a class somewhere with a mix of reference classes that can be resolved by IoC and value types (or some other types) that can only be resolved at runtime

This is where you go wrong. You should not mix compile time dependencies with runtime data when you compose components. Your object graphs should be static (and preferably stateless) and runtime data should be passed through the object graph using method calls after the complete object graph is constructed. This can cause a tremendous simplification in the development of your application, because it allows your object graphs to be statically verified (using a tool, unit tests or by using Pure DI) and it prevents having the trouble you are having today.

In general, you have two options to solve this:

  1. You pass the data on to lower level components through method calls.
  2. You retrieve the data by calling a method on an injected component.

Which solution to take depends on the context.

You would typically go for option one in case the data is specific to the handled request and would be part of the use case you're handling. For instance:

public interface IFeedUnitOfWorkProvider
{
    IFeedUnitOfWork GetUnitOfWork(NFLFileType fileType, object feed);
}

Here the IFeedUnitOfWorkProvider contains a GetUnitOfWork method that requires the runtime parameters as input. An implementation might look like this:

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);
    }       
}

A few things to note here:

  • All statically known dependencies are injected through the constructor, while runtime values are injected through method calls.
  • The connectionString value is unknown to the FeedUnitOfWorkProvider. It's not a direct dependency and the provider doesn't have to know about its existence.
  • Assuming that the connectionString is a configuration value that does not change at runtime (and is typically stored in the application's configuration file), it can be injected into the NFLContext the same way as other dependencies are injected. Note that a configuration value is different from a runtime value, because it won't change during the lifetime of the application.

The second option is especially useful when you're dealing with contextual information. This is information that is important for an implementation, but should not be passed in as with the previous example. A good example of this is information about the user on whos behalf the request is running. A typical abstraction for this would look like this:

public interface IUserContext {
    string CurrentUserName { get; }
}

This interface can be injected into any consumer. Using this abstraction, the consumer can query the user name at runtime. Passing through the user name with the rest of the request data would typically be awkward, because this would allow the caller to change (or forget) the user name, making the code just harder to work with, harder to test, more error prone. Instead we can use the 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);
}

How a IUserContext implementation should look like will highly depend on the type of application you're building. For ASP.NET I imagine something like this:

public class AspNetUserContext : IUserContext {
    string CurrentUserName {
        get { return HttpContext.Current.User.Name; }
    }
}

这篇关于如何使用的IoC容器时,将参数传递到构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆