2010-10-30 8 views
28

Ich verwende Microsoft Unity. Ich habe eine Schnittstelle ICustomerService und ihre Implementierung CustomerService. Ich kann sie für die Einheit Container registrieren Sie den folgenden Code:Microsoft Unity. Wie spezifiziert man einen bestimmten Parameter im Konstruktor?

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

Wenn CustomerService einen bestimmten Parameter in seinem Konstruktor hat (zB ISomeService1), habe ich den folgenden Code verwenden (Ich brauche SomeService1 angeben):

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

Keine Probleme hier.

Das Problem erscheint, wenn CustomerService Klasse zwei Parameter hat (nicht eine param wie im vorherigen Beispiel) in dem Konstruktor (z.B. ISomeService1 und ISomeService2). Es funktioniert gut, wenn ich den folgenden Code verwende: container.RegisterType<ICustomerService, CustomerService>(new TransientLifetimeManager(), new InjectionConstructor(new SomeService1(), new SomeService2()));

Das Problem ist, dass ich nicht SomeService2() für den zweiten Parameter angeben möchte. Ich möchte nur den ersten Parameter angeben - SomeService1(). Aber ich bekomme den Fehler, dass ich keinen oder beide Parameter angeben muss.

Wie kann ich nur den ersten Parameter des Konstruktors angeben?

+0

Hallo and.maz, Sie eine Lösung erhalten haben, wo Sie nicht brauchen, wie auch die anderen Parameter zu schaffen geben. etwas wie Schlüsselwert, wo wir den Namen des Konstruktors und den Wert angeben können – rdhaundiyal

Antwort

0

Sie können den zweiten Parameter dem Konstruktor als Standard zuweisen (z. B. =null) oder einen einzelnen Parameterkonstruktor zusätzlich zu dem Konstruktor mit zwei Parametern anbieten, indem Sie ihn überladen.

57

Ihre beste Antwort ist, den Container tatsächlich zu verwenden.

Was Sie tun, ist "Wenn Sie diesen Typ erstellen, verwenden Sie diese spezifische Instanz des Objekts." Dies nutzt nicht die Fähigkeit des Containers, eine Instanz für Sie aufzubauen. Stattdessen sollten Sie IService1 und IService2 im Container registrieren. Sagen Sie dem Container dann, dass diese Abhängigkeiten für Sie aufgelöst werden sollen.

Es sieht ungefähr so ​​aus:

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

Was dies bedeutet ist, den Behälter sagen „wenn es eine Abhängigkeit vom Typ IService1, neu auf ein neues Objekt vom Typ SomeService1 und gibt ihm, dass“ und in ähnlicher Weise für IService2 .

Als nächstes müssen Sie dem Container mitteilen, was mit ICustomerService zu tun ist. In den meisten Allgemeinheit, würden Sie dies tun:

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

Dies teilt den Behälter steht, wenn ICustomerService Lösung, neu eine Instanz von Customer mit dem Konstruktor auf, die IService1 und IService2 nimmt. Um diese Parameter abzurufen, rufen Sie den Container erneut auf, um diese Typen aufzulösen.

Dies ist ein bisschen ausführlich und ein häufiger Fall, also gibt es einige Abkürzungen. Zunächst einmal können Sie ein Type-Objekt zu tun, anstatt neue ResolvedParameter, wie so passieren:

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

Als weitere Stenografie, wenn Customer nur einen Konstruktor hat, oder wenn das, die Sie genannt wollen, ist derjenige, der die größte nimmt In der Parameterliste können Sie den InjectionConstructor vollständig auslassen, da dies der Konstruktor ist, den der Container in Abwesenheit einer anderen Konfiguration auswählt.den Dienst als Lösung zurück durch den Behälter

container.RegisterType<ICustomerService, CustomerService>(); 

Das Formular Sie verwenden normalerweise verwendet wird, wenn Sie einen bestimmten Wert eher für einen Konstruktor Parameter übergeben werden soll.

Um Ihre ursprüngliche Frage zu beantworten - nun, Sie können nicht genau das tun, was Sie gesagt haben. Der Konstruktorparameter benötigt einen Wert irgendeiner Art. Du könntest alles andere hineinbringen, was du willst - null funktioniert normalerweise.

Hinweis Sie können auch die beiden Formen mischen. Zum Beispiel, wenn Sie IService1 lösen wollen und für die IService2 Parameter null übergeben, dies tun:

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

* EDIT *

Basierend auf dem Kommentar unten, was Sie wirklich wollen, ist ein weiteres Merkmal - benannte Registrierungen.

Im Grunde haben Sie zwei Implementierungen von IService1 und IService2. Also, was Sie tun können, registrieren Sie beide, und dann sagen Sie dem Container, welchen zu verwenden.

Zunächst einmal, die zweite Implementierung zu registrieren, benötigen Sie einen eindeutigen Namen geben:

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

Dann können Sie den Behälter sagen IService1 zu lösen, aber den Namen verwenden. Dies ist der Hauptgrund dafür, dass der ResolvedParameter-Typ existiert. Da Sie nur den Standardwert für IService2 möchten, können Sie typeof() als Kurzschrift verwenden. Sie müssen weiterhin beide Typen für die Parameter angeben, benötigen jedoch keinen bestimmten Wert. Wenn das irgendeinen Sinn ergibt.

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

Das sollte tun, was Sie brauchen.

+1

Chris, danke. Aber das Problem ist, dass ich zwei Implementierungen von IService1 habe. Nennen wir sie Service1Impl1 und Service1Impl2. Beide werden in der Lösung verwendet. CustomerManager sollte Service1Impl1 verwenden und OrderManager sollte Service1Impl2 verwenden. Aus diesem Grund muss ich Service1Impl1 als ersten Parameter von CustomerService angeben. Aber ich möchte den zweiten Parameter nicht angeben, da es nur eine verwendete Implementierung von IService2 gibt, die bereits mit dem folgenden Quellcode registriert wurde: container.RegisterType (); –

+0

Ich bearbeite meine Antwort, es erfordert ein wenig mehr Details, als ich in einen Kommentar einfügen kann. –

+1

Große Antwort Chris. –

11

Als Alternative der Antwort Chris Tavares, können Sie die Behälter nur den zweiten Parameter lösen lassen:

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

Sie Container-Hierarchie verwenden können. Registrieren Sie die allgemeine Implementierung im übergeordneten Container. Diese Instanz wird aufgelöst, wenn sie über den Master-Container aufgelöst wird. Erstellen Sie anschließend einen untergeordneten Container, und registrieren Sie die alternative Implementierung im untergeordneten Container. Diese Implementierung wird aufgelöst, wenn sie durch den untergeordneten Container aufgelöst wird, d. H. Die Registrierung im untergeordneten Container überschreibt die Registrierung im übergeordneten Container.

Hier ist das Beispiel:

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! 
} 

Bitte beachten Sie, dass Sie nichts über die Anzahl der Parameter in Konstruktor kennen mit Containern Hierarchie kann, und das ist großartig, denn solange Sie Parameter gebunden sind, zählen und Sie ihre Typen kann nicht die volle Leistung von IoC nutzen. Je weniger du weißt, desto besser.

6

Chris Tavares gab eine gute Antwort mit vielen Informationen.

Wenn Sie viele zu injizierende Parameter haben, handelt es sich normalerweise um Schnittstellen oder Instanzen, die von Unity aufgelöst werden können (unter Verwendung der verschiedenen Techniken). Aber was, wenn Sie nur einen Parameter bereitstellen möchten, der nicht automatisch aufgelöst werden kann, z. eine Zeichenfolge für einen Dateinamen?

Jetzt müssen Sie alle typeof(IMyProvider) und eine Zeichenfolge oder Instanz bereitstellen.Aber um ehrlich zu sein, kann nur die Bereitstellung der Typen von Unity durchgeführt werden, da Unity bereits eine Strategie hat, den besten Ctor zu wählen.

So codiert ich einen Ersatz für 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; 
      } 
     } 
    } 
} 

Verbrauch:

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