6

Der UserService Konstruktor hat zwei Parameter, ein IUnitOfWork und ein IUserRepository:Einheit: Implizite ResolvedParameter für unbenannte Registrierungen

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

ich named registrations verwende zwischen mehreren Instanzen von IUnitOfWork zu unterscheiden, so dass, wenn die UserService mit der Unity Registrierung Behälter, ich brauche die Parameter explizit angeben, eine InjectionConstructor mit:

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

Kann new ResolvedParameter<IUserRepository>() weggelassen werden? Ich möchte, dass Unity diesen Parameter implizit herleitet, da eine benannte Registrierung nicht erforderlich ist. Der Code würde wie folgt aussehen:

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

Nachdem dies geschehen würde, ist jedenfalls, wenn ich brauche nicht die InjectionConstructor zu verwenden.

Antwort

7

Basierend auf InjectionConstructor, kam ich mit diesem RequiredInjectionConstructor. Sie können einen beliebigen Satz von Argumenten angeben und versuchen, einen Konstruktor zu finden, der (mindestens) den übergebenen Satz von Injektionsparametern haben muss. Wenn mehrere Konstruktoren diese Kriterien erfüllen, wird der Konstruktor mit der geringsten Anzahl an Parametern ausgewählt. Die verbleibenden Konstruktorparameter werden als unbenannte aufgelöste Parameter angenommen.

Ich habe noch nicht eine ganze Reihe von Komponententests durchgeführt, also lassen Sie mich wissen, wenn Sie Probleme haben.

/// <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

Das funktioniert super. Könnte es irgendwelche Nachteile geben, wenn man eine solche Reflexion (z. B. Leistung) verwendet? Vielen dank für Deine Hilfe. – davenewza

+1

Dies verwendet Reflexion in der gleichen Weise wie der Standard InjectionConstructor, so sollte es etwa die gleiche Leistung haben. Froh, dass ich helfen konnte! =) – TylerOhlsen

2

Möchten Sie Ihren Konstruktor mit dem DependencyAttribute von Unity dekorieren? Diese Lösung ist unkompliziert, integriert und ermöglicht die Auswahl und Auswahl der benannten Abhängigkeit. Aber es macht deinen Konstruktor mit Unity-Goo "schmutzig".

Eine andere Lösung wäre, eine benutzerdefinierte BuilderStrategy und UnityContainerExtension zu schreiben. Dies könnte mit etwas mehr Arbeit erledigt werden.

+1

Ich würde nicht bevorzugen, da diese Dienste Teil unserer Domain-Schicht sind. Ich möchte nicht, dass sie von der Einheit (oder irgendeinem Infrastrukturproblem) abhängig sind. Ich bin überrascht, dass das nicht möglich ist. Ich glaube, dass es mit Ninject möglich ist, von dem, was ich gelesen und verstanden habe. – davenewza