6

UserService构造函数有两个参数,一个IUnitOfWorkIUserRepository统一:隐ResolvedParameter未命名注册

public UserService(IUnitOfWork unitofWork, IUserRepository userRepository) 
{ ... } 

我使用named registrations与Unity注册UserServiceIUnitOfWork多个实例之间进行区分,所以容器,我需要明确指定参数使用InjectionConstructor

container.RegisterType<IUserService, UserService>(
    new InjectionConstructor(
     new ResolvedParameter<IUnitOfWork>("someContext"), 
     new ResolvedParameter<IUserRepository>() 
    ) 
); 

是否可以省略new ResolvedParameter<IUserRepository>()?我希望Unity隐含地推导出这个参数,因为不需要命名注册。的代码应该是这样的:

container.RegisterType<IUserService, UserService>(
    new InjectionConstructor(
     new ResolvedParameter<IUnitOfWork>("someContext") 
    ) 
); 

这将做的是,当我不需要使用InjectionConstructor任何情况。

回答

7

基于InjectionConstructor,我想出了这个RequiredInjectionConstructor。它允许你指定任何一组参数,它将试图找到一个构造函数,它需要(至少)传入的注入参数集合。如果有多个符合此条件的构造函数,它将选择参数数目最少的构造函数。其余的构造函数参数假定为未命名解析参数。

我还没有对它进行全套的单元测试,所以让我知道你是否遇到任何问题。

/// <summary> 
/// A class that holds the collection of minimum required 
/// parameters for a constructor, so that the container can 
/// be configured to call this constructor. 
/// </summary> 
public class RequiredInjectionConstructor : InjectionMember 
{ 
    private readonly List<InjectionParameterValue> _requiredParameterValues; 

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

    /// <summary> 
    /// Add policies to the <paramref name="policies"/> to configure the 
    /// container to call this constructor with the required 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 = FindConstructor(implementationType, _requiredParameterValues); 
     IEnumerable<InjectionParameterValue> selectedConstructorParameterValues = GetSelectedConstructorParameterValues(ctor, _requiredParameterValues); 

     policies.Set<IConstructorSelectorPolicy>(
      new SpecifiedConstructorSelectorPolicy(ctor, selectedConstructorParameterValues.ToArray()), 
      new NamedTypeBuildKey(implementationType, name)); 
    } 

    private static ConstructorInfo FindConstructor(Type typeToCreate, IEnumerable<InjectionParameterValue> requiredInjectionParameters) 
    { 
     var typeToCreateReflector = new ReflectionHelper(typeToCreate); 

     var matchedConstructors = typeToCreateReflector.InstanceConstructors. 
      Where(ctor => 
      { 
       var constructorParameterTypes = ctor.GetParameters().Select(info => info.ParameterType); 
       return requiredInjectionParameters.All(required => constructorParameterTypes.Any(required.MatchesType)); 
      }); 

     if (matchedConstructors.Any()) 
     { 
      // Prefer the constructor that has the least number of arguments. 
      // Other preference models could be implemented here. 
      return matchedConstructors.OrderBy(ctor => 
       ctor.GetParameters().Count()). 
       FirstOrDefault(); 
     } 

     string signature = string.Join(", ", requiredInjectionParameters.Select(required => required.ParameterTypeName).ToArray()); 

     throw new InvalidOperationException(
      string.Format("Unable to find a constructor with the minimum required parameters. Type: {0}, RequiredParameters: {1}", 
       typeToCreate.FullName, 
       signature)); 
    } 

    private static IEnumerable<InjectionParameterValue> GetSelectedConstructorParameterValues(ConstructorInfo ctor, IEnumerable<InjectionParameterValue> requiredInjectionParameters) 
    { 
     var injectionParameterValues = new List<InjectionParameterValue>(); 

     foreach (var parameter in ctor.GetParameters()) 
     { 
      var existingInjectionParameter = requiredInjectionParameters.FirstOrDefault(required => required.MatchesType(parameter.ParameterType)); 
      injectionParameterValues.Add(existingInjectionParameter ?? new ResolvedParameter(parameter.ParameterType)); 
     } 

     return injectionParameterValues; 
    } 
} 
+0

这很好。使用这种反射会有什么缺点(例如性能)?非常感谢你的帮助。 – davenewza

+1

这使用与标准InjectionConstructor相同的庄园反射,所以它应该具有相同的性能。很高兴我能帮上忙! =) – TylerOhlsen

2

你愿意用Unity中的DependencyAttribute来装饰你的构造函数吗?这个解决方案是直接的,内置的,并允许您选择和选择命名依赖项。但它会使你的构造函数与Unity goo“变脏”。

public UserService(
    [Dependency("someContext")]IUnitOfWork unitofWork, 
    IUserRepository userRepository) 
{ ... } 

另一个解决方案是编写一个定制的BuilderStrategy和UnityContainerExtension。这可以做更多的工作。

+1

我不希望这些服务构成我们领域层的一部分。我不希望他们对Unity有任何依赖(或任何基础设施问题)。我很惊讶这是不可能的。我相信Ninject可以从我所阅读和理解的内容中获益。 – davenewza