微软统一。如何在构造函数中指定某个参数?

发布于 2024-09-30 06:31:33 字数 1040 浏览 10 评论 0原文

我正在使用微软Unity。我有一个接口 ICustomerService 及其实现 CustomerService。我可以使用以下代码将它们注册到 Unity 容器:

container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager());

如果 CustomerService 在其构造函数中具有特定参数(例如 ISomeService1),我使用以下代码(我需要指定 SomeService1):

container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager(), new InjectionConstructor(new SomeService1()));

这里没有问题。

CustomerService 类在其构造函数中具有两个参数(而不是上一示例中的一个参数)(例如 ISomeService1ISomeService2)时,就会出现此问题。当我使用以下代码时它工作正常: container.RegisterType(new TransientLifetimeManager(), new InjectionConstructor(new SomeService1(), new SomeService2()));

问题是我不想指定 SomeService2( ) 作为第二个参数。我只想指定第一个参数 - SomeService1()。但我收到错误,我需要指定一个或两个参数。

如何仅指定构造函数的第一个参数?

I'm using Microsoft Unity. I have an interface ICustomerService and its implementation CustomerService. I can register them for the Unity container using the following code:

container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager());

If CustomerService has a certain parameter in its constructor (e.g. ISomeService1), I use the following code (I need to specify SomeService1):

container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager(), new InjectionConstructor(new SomeService1()));

No problems here.

The problem appears when CustomerService class has two parameters (not one param as in the previous example) in its constructor (e.g. ISomeService1 and ISomeService2). It works fine when I'm using the following code:
container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager(), new InjectionConstructor(new SomeService1(), new SomeService2()));

The problem is that I do not want specify SomeService2() for the second parameter. I want to specify only first parameter - SomeService1(). But I get the error that I need to specify none or both of parameters.

How can I specify only first parameter of constructor?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

夜空下最亮的亮点 2024-10-07 06:31:33

最好的答案是实际使用容器。

您所做的是说“构建此类型时,使用该对象的特定实例”。这没有利用容器为您构建实例的能力。相反,您应该在容器中注册 IService1 和 IService2。然后,告诉容器为您解决这些依赖关系。

它看起来像这样:

container.RegisterType<IService1, SomeService1>();
container.RegisterType<IService2, SomeService2>();

它的作用是告诉容器“每当存在 IService1 类型的依赖项时,新建一个 SomeService1 类型的新对象并将其传递给它”,对于 IService2 也是如此。

接下来,您需要告诉容器如何处理 ICustomerService。一般来说,您会这样做:

container.RegisterType<ICustomerService, CustomerService>(
    // Note, don't need to explicitly say transient, that's the default
    new InjectionConstructor(new ResolvedParameter<IService1>(),
        new ResolvedParameter<IService2>()));

这告诉容器:在解析 ICustomerService 时,使用采用 IService1 和 IService2 的构造函数新建一个 CustomerService 实例。要获取这些参数,请回调容器来解析这些类型。

这有点冗长,而且是常见情况,因此有一些快捷方式。首先,您可以传递一个 Type 对象,而不是执行 new ResolvedParameter,如下所示:

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(typeof(IService1), typeof (IService2)));

作为另一种简写,如果 CustomerService 只有一个构造函数,或者您要调用的构造函数是采用最大参数列表的构造函数,则可以保留InjectionConstructor 完全退出,因为这是容器在没有其他配置的情况下将选择的构造函数。

container.RegisterType<ICustomerService, CustomerService>();

当您希望为构造函数参数传递特定值而不是通过容器解析服务时,通常会使用您正在使用的形式。

回答你原来的问题 - 嗯,你不能完全按照你所说的去做。构造函数参数需要某种类型的值。不过,您可以在其中放置任何您想要的其他内容 - null 通常可以工作。

请注意,您也可以混合使用这两种形式。例如,如果您想要解析 IService1 并为 IService2 参数传递 null,请执行以下操作:

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(typeof(IService1), null));

* 编辑 *

根据下面的注释,您真正想要的是另一个功能 - 名为注册。

基本上,您有 IService1 的两个实现和 IService2 的一个实现。因此,您可以做的就是注册它们两者,然后告诉容器使用哪一个。

首先,要注册第二个实现,您需要给出一个显式名称:

container.RegisterType<IService1, OtherService1Impl>("other");

然后您可以告诉容器解析 IService1 但使用该名称。这是 ResolvedParameter 类型存在的主要原因。由于您只需要 IService2 的默认值,因此可以使用 typeof() 作为简写。您仍然需要为参数指定两种类型,但不需要特定值。如果这有任何意义的话。

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(new ResolvedParameter<IService1>("other"), typeof(IService2));

那应该可以满足您的需要。

Your best answer is to actually use the container.

What you're doing is saying "when building this type, use this specific instance of the object." This doesn't take advantage of the ability of the container to build up instance for you. Instead, you should register IService1 and IService2 in the container. Then, tell the container to resolve those dependencies for you.

It looks something like this:

container.RegisterType<IService1, SomeService1>();
container.RegisterType<IService2, SomeService2>();

What this does is tell the container "whenever there's a dependency of type IService1, new up a new object of type SomeService1 and hand it that" and similarly for IService2.

So next, you need to tell the container what to do about ICustomerService. In most generality, you'd do this:

container.RegisterType<ICustomerService, CustomerService>(
    // Note, don't need to explicitly say transient, that's the default
    new InjectionConstructor(new ResolvedParameter<IService1>(),
        new ResolvedParameter<IService2>()));

This tells the container: when resolving ICustomerService, new up an instance of CustomerService using the constructor that takes IService1 and IService2. To get those parameters, call back into the container to resolve those types.

This is a bit verbose, and a common case, so there are some shortcuts. First off, you can pass a Type object instead of doing new ResolvedParameter, like so:

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(typeof(IService1), typeof (IService2)));

As another shorthand, if CustomerService has only one constructor, or if the one you want called is the one that takes the largest parameter list, you can leave the InjectionConstructor out completely, as that's the constructor that the container will pick in the absence of other configuration.

container.RegisterType<ICustomerService, CustomerService>();

The form you're using is typically used when you want a specific value passed for a constructor parameter rather than resolving the service back through the container.

To answer your original question - well, you can't do exactly what you said. The constructor parameter needs A value of some sort. You could put anything else in there you want, though - null typically works.

Note you can also mix the two forms. For example, if you want to resolve IService1 and pass null for the IService2 parameter, do this:

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(typeof(IService1), null));

* EDIT *

Based on the comment below, what you actually want is another feature - named registrations.

Basically, you have two implementations of IService1 and one of IService2. So, what you can do is register both of them, and then tell the container which one to use.

First off, to register the second implementation, you need to give an explicit name:

container.RegisterType<IService1, OtherService1Impl>("other");

Then you can tell the container to resolve IService1 but use the name. This is the main reason that the ResolvedParameter type exists. Since you just want the default for IService2, you can use typeof() as a shorthand. You still need to specify both types for the parameters, but you don't need a specific value. If that makes any sense.

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(new ResolvedParameter<IService1>("other"), typeof(IService2));

That should do what you need.

变身佩奇 2024-10-07 06:31:33

作为 Chris Tavares 答案的替代方案,您可以让容器仅解析第二个参数:

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(new SomeService1(), new ResolvedParameter<IService2>());

As an alternative of Chris Tavares' answer, you can let the container resolve only the second parameter:

container.RegisterType<ICustomerService, CustomerService>(
    new InjectionConstructor(new SomeService1(), new ResolvedParameter<IService2>());
℡寂寞咖啡 2024-10-07 06:31:33

Chris Tavares 给出了很好的答案,提供了很多信息。

如果您有许多参数要注入,通常这些参数是可以由 Unity 解析的接口或实例(使用 diffnet 技术)。但是,如果您只想提供一个无法自动解析的参数(例如文件名字符串)怎么办?

现在您必须提供所有 typeof(IMyProvider) 和一个字符串或实例。但说实话,仅仅提供类型就可以由 Unity 完成,因为 Unity 已经有一个选择最佳 ctor 的策略。

所以我编写了 InjectionConstructor 的替代代码:

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;
using Microsoft.Practices.Unity.Utility;

namespace Microsoft.Practices.Unity
{
    /// <summary>
    /// A class that holds the collection of information for a constructor, 
    /// so that the container can be configured to call this constructor.
    /// This Class is similar to InjectionConstructor, but you need not provide
    /// all Parameters, just the ones you want to override or which cannot be resolved automatically
    /// The given params are used in given order if type matches
    /// </summary>
    public class InjectionConstructorRelaxed : InjectionMember
    {
        private List<InjectionParameterValue> _parameterValues;

        /// <summary>
        /// Create a new instance of <see cref="InjectionConstructor"/> that looks
        /// for a constructor with the given set of parameters.
        /// </summary>
        /// <param name="parameterValues">The values for the parameters, that will
        /// be converted to <see cref="InjectionParameterValue"/> objects.</param>
        public InjectionConstructorRelaxed(params object[] parameterValues)
        {
            _parameterValues = InjectionParameterValue.ToParameters(parameterValues).ToList();
        }

        /// <summary>
        /// Add policies to the <paramref name="policies"/> to configure the
        /// container to call this constructor with the appropriate parameter values.
        /// </summary>
        /// <param name="serviceType">Interface registered, ignored in this implementation.</param>
        /// <param name="implementationType">Type to register.</param>
        /// <param name="name">Name used to resolve the type object.</param>
        /// <param name="policies">Policy list to add policies to.</param>
        public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
        {
            ConstructorInfo ctor = FindExactMatchingConstructor(implementationType);
            if (ctor == null)
            {
                //if exact matching ctor not found, use the longest one and try to adjust the parameters.
                //use given Params if type matches otherwise use the type to advise Unity to resolve later
                ctor = FindLongestConstructor(implementationType);
                if (ctor != null)
                {
                    //adjust parameters
                    var newParams = new List<InjectionParameterValue>();
                    foreach (var parameter in ctor.GetParameters())
                    {
                        var injectionParameterValue =
                            _parameterValues.FirstOrDefault(value => value.MatchesType(parameter.ParameterType));
                        if (injectionParameterValue != null)
                        {
                            newParams.Add(injectionParameterValue);
                            _parameterValues.Remove(injectionParameterValue);
                        }
                        else
                            newParams.Add(InjectionParameterValue.ToParameter(parameter.ParameterType));
                    }
                    _parameterValues = newParams;
                }
                else
                {
                    throw new InvalidOperationException(
                        string.Format(
                            CultureInfo.CurrentCulture,
                            "No constructor found for type {0}.",
                            implementationType.GetTypeInfo().Name));
                }
            }
            policies.Set<IConstructorSelectorPolicy>(
                new SpecifiedConstructorSelectorPolicy(ctor, _parameterValues.ToArray()),
                new NamedTypeBuildKey(implementationType, name));
        }



        private ConstructorInfo FindExactMatchingConstructor(Type typeToCreate)
        {
            var matcher = new ParameterMatcher(_parameterValues);
            var typeToCreateReflector = new ReflectionHelper(typeToCreate);

            foreach (ConstructorInfo ctor in typeToCreateReflector.InstanceConstructors)
            {
                if (matcher.Matches(ctor.GetParameters()))
                {
                    return ctor;
                }
            }

            return null;
        }

       private static ConstructorInfo FindLongestConstructor(Type typeToConstruct)
        {
            ReflectionHelper typeToConstructReflector = new ReflectionHelper(typeToConstruct);

            ConstructorInfo[] constructors = typeToConstructReflector.InstanceConstructors.ToArray();
            Array.Sort(constructors, new ConstructorLengthComparer());

            switch (constructors.Length)
            {
                case 0:
                    return null;

                case 1:
                    return constructors[0];

                default:
                    int paramLength = constructors[0].GetParameters().Length;
                    if (constructors[1].GetParameters().Length == paramLength)
                    {
                        throw new InvalidOperationException(
                            string.Format(
                                CultureInfo.CurrentCulture,
                                "The type {0} has multiple constructors of length {1}. Unable to disambiguate.",
                                typeToConstruct.GetTypeInfo().Name,
                                paramLength));
                    }
                    return constructors[0];
            }
        }
        private class ConstructorLengthComparer : IComparer<ConstructorInfo>
        {
            /// <summary>
            /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
            /// </summary>
            /// <param name="y">The second object to compare.</param>
            /// <param name="x">The first object to compare.</param>
            /// <returns>
            /// Value Condition Less than zero is less than y. Zero equals y. Greater than zero is greater than y.
            /// </returns>
            [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class")]
            public int Compare(ConstructorInfo x, ConstructorInfo y)
            {
                Guard.ArgumentNotNull(x, "x");
                Guard.ArgumentNotNull(y, "y");

                return y.GetParameters().Length - x.GetParameters().Length;
            }
        }
    }
}

用法:

container.RegisterType(new TransientLifetimeManager(), new InjectionConstructorRelaxed(
    new SomeService1("with special options")
    //, new SomeService2() //not needed, normal unity resolving used
    //equivalent to: , typeof(SomeService2)
    ));

Chris Tavares gave a good answer with a lot of information.

If you have many Parameters to inject usually these are interfaces or instances which can be resolved by Unity (using the differnet techniques). But what if you want to provide only one Parameter which cannot be resolved automatically, e.g. a string for a filename?

Now you have to provide all the typeof(IMyProvider) and one string or instance. But to be honest just providing the types could be done by Unity, because Unity has already a strategy to choose the best ctor.

So I coded a replacement for InjectionConstructor:

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;
using Microsoft.Practices.Unity.Utility;

namespace Microsoft.Practices.Unity
{
    /// <summary>
    /// A class that holds the collection of information for a constructor, 
    /// so that the container can be configured to call this constructor.
    /// This Class is similar to InjectionConstructor, but you need not provide
    /// all Parameters, just the ones you want to override or which cannot be resolved automatically
    /// The given params are used in given order if type matches
    /// </summary>
    public class InjectionConstructorRelaxed : InjectionMember
    {
        private List<InjectionParameterValue> _parameterValues;

        /// <summary>
        /// Create a new instance of <see cref="InjectionConstructor"/> that looks
        /// for a constructor with the given set of parameters.
        /// </summary>
        /// <param name="parameterValues">The values for the parameters, that will
        /// be converted to <see cref="InjectionParameterValue"/> objects.</param>
        public InjectionConstructorRelaxed(params object[] parameterValues)
        {
            _parameterValues = InjectionParameterValue.ToParameters(parameterValues).ToList();
        }

        /// <summary>
        /// Add policies to the <paramref name="policies"/> to configure the
        /// container to call this constructor with the appropriate parameter values.
        /// </summary>
        /// <param name="serviceType">Interface registered, ignored in this implementation.</param>
        /// <param name="implementationType">Type to register.</param>
        /// <param name="name">Name used to resolve the type object.</param>
        /// <param name="policies">Policy list to add policies to.</param>
        public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
        {
            ConstructorInfo ctor = FindExactMatchingConstructor(implementationType);
            if (ctor == null)
            {
                //if exact matching ctor not found, use the longest one and try to adjust the parameters.
                //use given Params if type matches otherwise use the type to advise Unity to resolve later
                ctor = FindLongestConstructor(implementationType);
                if (ctor != null)
                {
                    //adjust parameters
                    var newParams = new List<InjectionParameterValue>();
                    foreach (var parameter in ctor.GetParameters())
                    {
                        var injectionParameterValue =
                            _parameterValues.FirstOrDefault(value => value.MatchesType(parameter.ParameterType));
                        if (injectionParameterValue != null)
                        {
                            newParams.Add(injectionParameterValue);
                            _parameterValues.Remove(injectionParameterValue);
                        }
                        else
                            newParams.Add(InjectionParameterValue.ToParameter(parameter.ParameterType));
                    }
                    _parameterValues = newParams;
                }
                else
                {
                    throw new InvalidOperationException(
                        string.Format(
                            CultureInfo.CurrentCulture,
                            "No constructor found for type {0}.",
                            implementationType.GetTypeInfo().Name));
                }
            }
            policies.Set<IConstructorSelectorPolicy>(
                new SpecifiedConstructorSelectorPolicy(ctor, _parameterValues.ToArray()),
                new NamedTypeBuildKey(implementationType, name));
        }



        private ConstructorInfo FindExactMatchingConstructor(Type typeToCreate)
        {
            var matcher = new ParameterMatcher(_parameterValues);
            var typeToCreateReflector = new ReflectionHelper(typeToCreate);

            foreach (ConstructorInfo ctor in typeToCreateReflector.InstanceConstructors)
            {
                if (matcher.Matches(ctor.GetParameters()))
                {
                    return ctor;
                }
            }

            return null;
        }

       private static ConstructorInfo FindLongestConstructor(Type typeToConstruct)
        {
            ReflectionHelper typeToConstructReflector = new ReflectionHelper(typeToConstruct);

            ConstructorInfo[] constructors = typeToConstructReflector.InstanceConstructors.ToArray();
            Array.Sort(constructors, new ConstructorLengthComparer());

            switch (constructors.Length)
            {
                case 0:
                    return null;

                case 1:
                    return constructors[0];

                default:
                    int paramLength = constructors[0].GetParameters().Length;
                    if (constructors[1].GetParameters().Length == paramLength)
                    {
                        throw new InvalidOperationException(
                            string.Format(
                                CultureInfo.CurrentCulture,
                                "The type {0} has multiple constructors of length {1}. Unable to disambiguate.",
                                typeToConstruct.GetTypeInfo().Name,
                                paramLength));
                    }
                    return constructors[0];
            }
        }
        private class ConstructorLengthComparer : IComparer<ConstructorInfo>
        {
            /// <summary>
            /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
            /// </summary>
            /// <param name="y">The second object to compare.</param>
            /// <param name="x">The first object to compare.</param>
            /// <returns>
            /// Value Condition Less than zero is less than y. Zero equals y. Greater than zero is greater than y.
            /// </returns>
            [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class")]
            public int Compare(ConstructorInfo x, ConstructorInfo y)
            {
                Guard.ArgumentNotNull(x, "x");
                Guard.ArgumentNotNull(y, "y");

                return y.GetParameters().Length - x.GetParameters().Length;
            }
        }
    }
}

Usage:

container.RegisterType(new TransientLifetimeManager(), new InjectionConstructorRelaxed(
    new SomeService1("with special options")
    //, new SomeService2() //not needed, normal unity resolving used
    //equivalent to: , typeof(SomeService2)
    ));
缱倦旧时光 2024-10-07 06:31:33

您可以使用容器层次结构。在父容器中注册公共实现,如果通过主容器解析,则该实例将解析。然后创建子容器,并在子容器中注册替代实现。如果通过子容器解决,则此实现将解决,即子容器中的注册覆盖父容器中的注册。

示例如下:

public interface IService {}

public interface IOtherService {}

// Standard implementation of IService
public class StandardService : IService {}

// Alternative implementaion of IService
public class SpecialService : IService {}

public class OtherService : IOtherService {}

public class Consumer
{
    public Consumer(IService service, IOtherService otherService)
    {}
}

private void Test()
{
    IUnityContainer parent = new UnityContainer()
        .RegisterType<IService, StandardService>()
        .RegisterType<IOtherService, OtherService>();

    // Here standardWay is initialized with StandardService as IService and OtherService as IOtherService
    Consumer standardWay = parent.Resolve<Consumer>();

    // We construct child container and override IService registration
    IUnityContainer child = parent.CreateChildContainer()
        .RegisterType<IService, SpecialService>();

    // And here specialWay is initialized with SpecialService as IService and still OtherService as IOtherService
    Consumer specialWay = child.Resolve<Consumer>();

    // Profit!
}

请注意,使用容器层次结构,您对构造函数中的参数数量一无所知,这很好,因为只要您绑定到参数计数及其类型,您就无法使用 IoC 的全部功能。你知道的越少越好。

You can use containers hierarchy. Register common implementation in parent container, this instance will resolve if resolved through master container. Then create child container, and register alternative implementation in child container. This implementation will resolve If resolved through child container, i.e. registration in child container overrides registration in parent container.

Here's the example:

public interface IService {}

public interface IOtherService {}

// Standard implementation of IService
public class StandardService : IService {}

// Alternative implementaion of IService
public class SpecialService : IService {}

public class OtherService : IOtherService {}

public class Consumer
{
    public Consumer(IService service, IOtherService otherService)
    {}
}

private void Test()
{
    IUnityContainer parent = new UnityContainer()
        .RegisterType<IService, StandardService>()
        .RegisterType<IOtherService, OtherService>();

    // Here standardWay is initialized with StandardService as IService and OtherService as IOtherService
    Consumer standardWay = parent.Resolve<Consumer>();

    // We construct child container and override IService registration
    IUnityContainer child = parent.CreateChildContainer()
        .RegisterType<IService, SpecialService>();

    // And here specialWay is initialized with SpecialService as IService and still OtherService as IOtherService
    Consumer specialWay = child.Resolve<Consumer>();

    // Profit!
}

Please note that using containers hierarchy you can know nothing about number of parameters in constructor, and that's great, because as long as you are bound to parameters count and their types you can't use full power of IoC. The less you know the better.

苏佲洛 2024-10-07 06:31:33

您可以将第二个参数默认为构造函数(例如,=null),或者通过重载除了双参数构造函数之外还提供单参数构造函数。

You can default the second parameter to the constructor (e.g., =null) or offer a single parameter constructor in addition to the two parameter constructor by overloading it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文