And*_*i M 33 c# asp.net ioc-container unity-container
我正在使用Microsoft Unity.我有一个接口ICustomerService及其实现CustomerService.我可以使用以下代码为Unity容器注册它们:
container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager());
Run Code Online (Sandbox Code Playgroud)
如果CustomerService在其构造函数中有某个参数(例如ISomeService1),我使用以下代码(我需要指定SomeService1):
container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager(), new InjectionConstructor(new SomeService1()));
Run Code Online (Sandbox Code Playgroud)
这里没问题.
当CustomerService类在其构造函数中有两个参数(不是前一个示例中的一个参数)时会出现问题(例如ISomeService1和ISomeService2).当我使用以下代码时它工作正常:
container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager(), new InjectionConstructor(new SomeService1(), new SomeService2()));
问题是我不想指定SomeService2()第二个参数.我只想指定第一个参数 - SomeService1().但我得到的错误是我需要指定一个或两个参数.
如何只指定构造函数的第一个参数?
Chr*_*res 66
你最好的答案是实际使用容器.
你正在做的是说"在构建这种类型时,使用该对象的这个特定实例." 这并没有利用容器为您构建实例的能力.相反,您应该在容器中注册IService1和IService2.然后,告诉容器为您解决这些依赖关系.
它看起来像这样:
container.RegisterType<IService1, SomeService1>();
container.RegisterType<IService2, SomeService2>();
Run Code Online (Sandbox Code Playgroud)
这样做是告诉容器"只要存在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>()));
Run Code Online (Sandbox Code Playgroud)
这告诉容器:在解析ICustomerService时,使用带有IService1和IService2的构造函数新建一个CustomerService实例.要获取这些参数,请回调容器以解析这些类型.
这有点冗长,而且是常见的情况,所以有一些快捷方式.首先,您可以传递一个Type对象,而不是执行新的ResolvedParameter,如下所示:
container.RegisterType<ICustomerService, CustomerService>(
new InjectionConstructor(typeof(IService1), typeof (IService2)));
Run Code Online (Sandbox Code Playgroud)
作为另一种简写,如果CustomerService只有一个构造函数,或者你想要调用的那个是获取最大参数列表的那个,你可以完全保留InjectionConstructor,因为这是容器在没有其他的情况下将选择的构造函数.组态.
container.RegisterType<ICustomerService, CustomerService>();
Run Code Online (Sandbox Code Playgroud)
当您希望为构造函数参数传递特定值而不是通过容器解析服务时,通常会使用您正在使用的表单.
要回答你原来的问题 - 好吧,你不能完全按照你说的做.构造函数参数需要某种值的值.你可以在其中放置任何你想要的东西,但是null通常都有效.
请注意,您也可以混合使用这两种形式.例如,如果要解析IService1并为IService2参数传递null,请执行以下操作:
container.RegisterType<ICustomerService, CustomerService>(
new InjectionConstructor(typeof(IService1), null));
Run Code Online (Sandbox Code Playgroud)
*编辑*
根据下面的评论,你真正想要的是另一个功能 - 命名注册.
基本上,您有两个IService1实现和一个IService2实现.所以,你可以做的就是注册它们,然后告诉容器使用哪一个.
首先,要注册第二个实现,您需要给出一个明确的名称:
container.RegisterType<IService1, OtherService1Impl>("other");
Run Code Online (Sandbox Code Playgroud)
然后,您可以告诉容器解析IService1但使用该名称.这是ResolvedParameter类型存在的主要原因.由于您只需要IService2的默认值,因此可以使用typeof()作为简写.您仍需要为参数指定两种类型,但不需要特定值.如果那有意义的话.
container.RegisterType<ICustomerService, CustomerService>(
new InjectionConstructor(new ResolvedParameter<IService1>("other"), typeof(IService2));
Run Code Online (Sandbox Code Playgroud)
那应该做你需要的.
ono*_*nof 14
作为Chris Tavares的回答的替代方案,您可以让容器仅解析第二个参数:
container.RegisterType<ICustomerService, CustomerService>(
new InjectionConstructor(new SomeService1(), new ResolvedParameter<IService2>());
Run Code Online (Sandbox Code Playgroud)
克里斯塔瓦雷斯给出了很多信息的答案.
如果您要注入许多参数,通常这些是Unity可以解析的接口或实例(使用不同的技术).但是,如果您只想提供一个无法自动解析的参数,例如文件名的字符串,该怎么办?
现在你必须提供所有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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
container.RegisterType(new TransientLifetimeManager(), new InjectionConstructorRelaxed(
new SomeService1("with special options")
//, new SomeService2() //not needed, normal unity resolving used
//equivalent to: , typeof(SomeService2)
));
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
27994 次 |
| 最近记录: |