2013-03-21 6 views
13

Ich versuche, Autofac zu verwenden, um Abhängigkeiten in FluentValidation in einer MVC 4-Anwendung zu injizieren. Ich denke, dass ich die Strategie ausgearbeitet habe, aber ich bleibe dabei, meine Frage nach der Anfrage aus einem Singleton zu lösen.Autofac - wie man Func für ISomething von Singleton löst, wo ISomething ist InstancePerHttpRequest

Hier ist das Szenario: Ich habe einen Validator, der von FluentValidation AbstractValidator abgeleitet ist. Ich habe gelesen, dass FluentValidation-Validatoren am besten als Singletons funktionieren, daher erwartet mein Konstruktor einen Func und speichert diese Factory zur späteren Verwendung. Wenn der Validator verwendet wird, sollte er die gespeicherte Factory nach einem IDataStore fragen, die Instanz für diese Anfrage erstellen und sie verwenden. Das ist die Theorie. Ich möchte https://github.com/robdmoore/UnobtrusiveMVCTechniques danken, die mir bei dieser Lösung geholfen haben. Hier ist der Validator ...

public class SiteAdminViewModelValidator : AbstractValidator<SiteAdminViewModel> { 
    private readonly Func<IDataStore> _dbFactory; 

    public SiteAdminViewModelValidator(Func<IDataStore> dbFactory) { 
     _dbFactory = dbFactory; 

     RuleFor(model => model.SiteCode).Length(1, 40).Must(BeSpecial); 
    } 

    public bool BeSpecial(string siteCode) { 
     var db = _dbFactory(); 
     List<Stuff> stuffs = db.All<Stuff>().ToList(); 

     return true; 
    } 
} 

Wenn jemand mich zu einem Arbeitsbeispiel zeigen kann, was ich versuche zu erreichen, das wäre toll, aber ich würde auch die Lösung für dieses besondere Stück wissen, von Autofac Trickiness.

Hier ist meine Validator Registrierung ...

public class FluentValidatorModule : Module { 
    protected override void Load(ContainerBuilder builder) { 
     base.Load(builder); 
     builder.RegisterType<AutofacValidatorFactory>().As<IValidatorFactory>().SingleInstance(); 

    var validators = AssemblyScanner.FindValidatorsInAssembly(System.Reflection.Assembly.GetExecutingAssembly()); 
    validators.ToList().ForEach(v => builder.RegisterType(v.ValidatorType).As(v.InterfaceType).SingleInstance()); 
    } 
} 

Hier ist meine Anmeldung für die Fabrik IDataStore ist ...

builder.RegisterType<SuperDB>().As<IDataStore>().InstancePerHttpRequest(); 
builder.Register<Func<IDataStore>>(c => { 
             var context = c.Resolve<IComponentContext>(); 
             return context.Resolve<IDataStore>; 
            }); 

Hier ist der Fehler, den ich immer bin, wenn mein Validator für eine IDataStore fragt auf die Zeile - var db = _dbFactory();

Kein Rahmen mit einem Tag Matching ‚AutofacWebRequest‘ ist sichtbar von dem Bereich, in dem die Instanz angefordert wurde. Dies zeigt im Allgemeinen , die eine Komponente registriert als pro-HTTP-Anforderung wird durch eine SingleInstance() Komponente (oder ein ähnliches Szenario.) Unter der Web- Integration immer anfordern Abhängigkeiten von der DependencyResolver.Current oder ILifetimeScopeProvider.RequestLifetime, angefordert wird niemals aus dem Container selbst.

... was ich genau dann bekommen habe, als ich es vor dem Schreiben meiner eigenen Werksregistrierung - der Func-Registrierung - ausprobiert habe. Vom Lesen verschiedener Antworten bis zu ähnlichen Fragen sah es so aus, als ob das, was ich oben habe, funktionieren sollte, weil ich dachte, dass ich nun den Func auflösen würde, um den aktuellen Resolver zu bekommen.

Jede Hilfe wird sehr geschätzt.

Antwort

17

Ich stimme zu, dass dies funktionieren sollte - die Func<IDataStore> definiert eine Fabrik, die die Abhängigkeit in jeder Methode wie erforderlich erzeugt.

Die Art, wie ich diese Methode umgehen konnte, ist die Verwendung der DependencyResolver.Current wie die Fehlermeldung suggeriert. Der Hauptgrund ist, dass ich es schon hatte das Autofac.Mvc4 nuget Paket mit einrichten ...

DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 

So tatsächlich Setup die Methode, die ich folgende func

public Func<T> PerHttpSafeResolve<T>() 
{ 
    return() => DependencyResolver.Current.GetService<T>(); 
} 

Und wenn die Konstruktion habe

Behälter
builder.RegisterType<SuperDB>().As<IDataStore>().InstancePerHttpRequest(); 
builder.RegisterInstance(PerHttpSafeResolve<IDataStore>()); 

EDIT: die zweite Zeile, die die Instanz registriert sagt - Wenn Sie ein Func<IDataStore> dann müssen Sie den Wert in die Methode übergeben verwenden. Das Ergebnis der PerHttpSafeResolve<IDataStore> ist nur eine Funktion (Fabrik), so dass es als eine einzelne Instanz leben kann.

+0

Das funktioniert! Vielen Dank. Ich bin neu in Autofac. Kann ich nur eine kurze Erläuterung zur Registrierung des Builders erhalten.RegisterInstance (PerHttpSafeResolve [IDataStore]()); Linie? Das heißt also: "Wann immer jemand nach einem Func [IDataStore] fragt, verwenden Sie diese Methode, um ihnen einen zu geben"? –

+0

Siehe meine Bearbeitung - ich werde versuchen, es zu erklären – Felix

+0

Nizza !! Diese Technik ist erstaunlich. Ich liebe es. So sehr habe ich einen Blogbeitrag geschrieben: http://robdmoore.id.au/blog/2013/03/23/resolving-request-scoped-objects-into-a-singleton-with-autofac/. Wenn Sie selbst einen Blog haben, lassen Sie es mich wissen und ich verlinke ihn anstelle Ihres SO-Benutzers. –

7

Das Problem ist der Umfang der Validatoren. Autofac löst immer SingleInstance Abhängigkeiten von dem Anwendungscontainer auf, was bedeutet, dass die Abhängigkeiten der Validierer ebenfalls aus dem Anwendungscontainer stammen.

Autofac fordert die Func<IDataStore> Instanz von dort, nicht aus dem Anfrage-Container. Wenn Sie IComponentContext auflösen, erhalten Sie den Container, in dem der Validierer aufgelöst wird: den Anwendungscontainer. Da Func<IDataStore> auf eine Anforderung beschränkt ist, gibt es keine Möglichkeit für Autofac, sie auf Anwendungsebene bereitzustellen, daher der Fehler.

Der korrekte Ansatz besteht darin, die Prüfer als InstancePerHttpRequest zu registrieren. Die Lebensdauer einer Komponente wird durch ihre längste Lebensdauer bestimmt; Validatoren funktionieren am besten als Singletons, wenn sie keine Abhängigkeiten haben.In diesem Fall ist ein Validator, der auf IDataStore angewiesen ist, auf die Lebensdauer der IDataStore Instanz beschränkt. (Auf der hellen Seite, können Sie die Func loswerden.)

Denken Sie darüber nach wie zu einer Party gehen: Wenn Sie eine Fahrt mit dem Gastgeber zu fangen, müssen Sie dort bleiben, bis die Party vorbei ist. Sie können nicht mit dem Gastgeber fahren, wenn Sie früh abreisen möchten.

+1

Dies hilft mir zu verstehen, was vor sich geht. Ich denke, das ist der Grund, warum Felix 'Lösung funktioniert und meine nicht. Er benutzt explizit den DependencyResolver.Current, während ich gerade c.Resolve benutzt habe. Ich möchte wirklich vermeiden, dass die Validatoren öfter gebaut werden als unbedingt nötig. Da ich eine Singleton-Lösung zur Arbeit bekommen kann, werde ich diesen Weg gehen. –

+0

@TimHardy: Freut mich zu hören. Vergessen Sie nicht, Antworten zu geben, die geholfen haben :-) –

Verwandte Themen