2015-02-09 6 views
5

Ich habe eine Schnittstelle, Implementierung und Ziel bekommt:Wie man eine optionale Abhängigkeit in AutoFac macht?

public interface IPerson { public string Name { get; } } 
public class Person: IPerson { public string Name { get { return "John"; } } } 
public class Target { public Target(IPerson person) {} } 

Ich Autofac mit Sachen zusammen zu binden:

builder.RegisterType<Person>().As<IPerson>().SingleInstance(); 

Das Problem ist, dass IPerson Leben in einer gemeinsamen Versammlung, Person lebt in einem Plugin (das kann oder darf nicht dort sein), und lebt in der Hauptanwendung, die Plugins lädt. Wenn keine Plugins geladen sind, die IPerson implementieren, geht Autofac ballistisch davon aus, keine Target Abhängigkeiten auflösen zu können. Und ich kann es nicht wirklich dafür verantwortlich machen.

Allerdings weiß ich, dass Target in der Lage ist, den Mangel an IPerson zu behandeln und würde mehr als glücklich sein, stattdessen eine null zu bekommen. In der Tat bin ich ziemlich sicher, dass alle Komponenten, die auf eine IPerson angewiesen sind, bereit sind, eine null es stattdessen zu nehmen. Also, wie kann ich Autofac sagen - "Es ist OK Sweety, mach dir keine Sorgen, gib mir einfach eine null, in Ordnung?"

Ein Weg, ich fand ist ein Standard-Parameter zu Target hinzuzufügen:

public class Target { public Target(IPerson person = null) {} } 

Das funktioniert, aber dann muss ich diese Komponenten für alle tun, die eine IPerson erfordern. Kann ich das auch anders herum machen? Irgendwie sagen Sie Autofac "Wenn alles andere für die Auflösung IPerson fehlschlägt, geben Sie null zurück"?

+0

Siehe auch 'ResolveOptional()'. – abatishchev

+0

Wenn 'Person' im Plugin lebt, wie können Sie es registrieren? Wohin geht dieser Anruf? 'builder.RegisterType () .As () .SingleInstance();' –

+1

@SriramSakthivel - jedes Plugin hat eine 'Initialize (ContainerBuilder)' Methode, die von der Hauptanwendung aufgerufen wird. Plugins registrieren dort ihre Komponenten. –

Antwort

3

Sie diese Syntax verwenden:

builder.RegisterType<Target>().WithParameter(TypedParameter.From<IPerson>(null)); 

Leider

builder.Register(c => (IPerson)null).As<IPerson>(); 
    // will throw : Autofac.Core.DependencyResolutionException: A delegate registered to create instances of 'ConsoleApplication17.Program+IPerson' returned null. 

und

builder.RegisterInstance<IPerson>(null).As<IPerson>(); 
    // will throw : Unhandled Exception: System.ArgumentNullException: Value cannot be null. 

Wenn Sie nicht wollen eine WithParameter für jede Registrierung hinzufügen möchten, können Sie hinzufügen ein Modul, das es für Sie tun wird

public class OptionalAutowiringModule : Autofac.Module 
{ 
    public OptionalAutowiringModule(IEnumerable<Type> optionalTypes) 
    { 
     this._optionalTypes = optionalTypes; 
    } 
    public OptionalAutowiringModule(params Type[] optionalTypes) 
    { 
     this._optionalTypes = optionalTypes; 
    } 


    private readonly IEnumerable<Type> _optionalTypes; 


    protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) 
    { 
     base.AttachToComponentRegistration(componentRegistry, registration); 

     registration.Preparing += (sender, e) => 
     { 
      e.Parameters = e.Parameters.Concat(new Parameter[] { new OptionalAutowiringParameter(this._optionalTypes) }); 
     }; 
    } 
} 
public class OptionalAutowiringParameter : Parameter 
{ 
    public OptionalAutowiringParameter(IEnumerable<Type> optionalTypes) 
    { 
     this._optionalTypes = optionalTypes.ToList(); 
    } 


    private readonly List<Type> _optionalTypes; 


    public override Boolean CanSupplyValue(ParameterInfo pi, IComponentContext context, out Func<Object> valueProvider) 
    { 
     if (this._optionalTypes.Contains(pi.ParameterType) && !context.IsRegistered(pi.ParameterType)) 
     { 
      valueProvider =() => null; 
      return true; 
     } 
     else 
     { 
      valueProvider = null; 
      return false; 
     } 
    } 
} 

Dann alles, was Sie tun müssen, ist das Modul mit der optionalen Abhängigkeiten

builder.RegisterModule(new OptionalAutowiringModule(typeof(IPerson))); 

Aber statt Injektion eines Nullverweis zu registrieren, die eine Nullreferenceexception verursachen kann. Eine andere Lösung wäre es, eine Implementierung zu erstellen.

builder.RegisterType<NullPerson>().As<IPerson>(); 
    builder.RegisterType<Target>(); 

Wenn Sie Ihre reale Implementierung haben es erst wieder registriert, wird es überschreibt die ursprüngliche Implementierung.

+0

Nun, die erste Option ist nicht viel besser als nur '= null' in der Parameterdeklaration angeben. In der Tat ist es noch länger. Eine "NullPerson" ist eine Art Lösung, aber ziemlich peinlich. –

+0

Ich habe mein Beispiel aktualisiert, um die Registrierung eines Parameters für jede Registrierung mit einem Modul zu vermeiden. –

+0

OK, also eine benutzerdefinierte Registrierungsquelle. Nun, ich denke, das ist der bestmögliche Weg. Vielen Dank! :) –

0

Als Workaround können Sie eine Fassade ähnlich wie Lazy<IPerson> injizieren, die versucht, sie beim Aufruf aus dem Container aufzulösen.

0

Der normale Mechanismus zum Deklarieren optionaler Abhängigkeiten besteht darin, sie in Eigenschaften anstelle von Konstruktorargumenten einzufügen.

3

Nur optionale Parameter verwenden, finden Sie im folgenden Beispiel:

public class SomeClass 
{ 
    public SomeClass(ISomeDependency someDependency = null) 
    { 
      // someDependency will be null in case you've not registered that before, and will be filled whenever you register that. 
    } 
} 
1

Sie konnte Ihre Abhängigkeit von IPerson person = null nehmen Sie nur in Ihrem Konstruktor, der eine implizite Deklaration eines optionalen IPerson Abhängigkeit ist (vgl @YaserMoradi) . Aber das bringt Sie in der Lage, diese zu verfestigen ist, jetzt und für immer nach:

“... Ich bin mir ziemlich sicher, dass alle Komponenten, die auf einer IPerson angewiesen sind bereit, ein null es zu nehmen seine Stelle. "

Besser, dass dies überhaupt keine Frage sein muss.

Die „best practice“ Muster (die @CyrilDurand als Suffix auf seiner Antwort gibt) dafür sind ein default implementation zu verwenden (Link Fazit: Autofac wird die letzte registrierte Komponente als Standardanbieter dieses Dienstes verwenden). Wenn Sie keine andere Implementierung von Ihrem Plugin haben (registriert nach dem Standard), wird diese Standardeinstellung verwendet.

In Ihrem Fall sollte die Standardkomponente eine Art von No-Op- oder Basisimplementierung des IPerson-Diensts sein, wobei jede aufgerufene Methode das Standardverhalten für Ihre Anwendung aufweist. Dies führt auch zu einer besseren Wiederverwendungsgeschichte, da Sie das Standardverhalten ein für alle Mal definieren können.

Verwandte Themen