2012-12-06 14 views
6

Ich arbeite an einem ASP.NET MVC-Projekt, das externe Plugins unterstützt, jetzt bin ich von Unity zu Autofac und ich muss die Lebensdauer Objekte von Autofac umhüllen, so dass die Plugins nicht müssen referenziere es, in der Einheit könnte ich etwas tun.Autofac Lifetime Management

public sealed class UnityScopeFactory : IDependencyScopeFactory 
{ 
    private HttpRequestScope _httpRequest; 

    private SingletonScope _singleton; 

    private TransientScope _transient; 

    public IDependencyScope HttpRequest() 
    { 
     return _httpRequest ?? (_httpRequest = new HttpRequestScope()); 
    } 

    public IDependencyScope Singleton() 
    { 
     return _singleton ?? (_singleton = new SingletonScope()); 
    } 

    public IDependencyScope Transient() 
    { 
     return _transient ?? (_transient = new TransientScope()); 
    } 

    private class HttpRequestScope : IDependencyScope 
    { 
     public object CreateScope() 
     { 
      return new HttpPerRequestLifetimeManager(); 
     } 
    } 

    private class SingletonScope : IDependencyScope 
    { 
     public object CreateScope() 
     { 
      return new ContainerControlledLifetimeManager(); 
     } 
    } 

    private class TransientScope : IDependencyScope 
    { 
     public object CreateScope() 
     { 
      return new TransientLifetimeManager(); 
     } 
    } 
} 

ich ähnliche Sache in Autofac gemacht, aber ich bin mir nicht sicher, ob es der richtige Weg ist zu tun, dass ich in die RegistrationBuilder von Autofac sah, die (leider) interne und ich kam mit dieser.

Auch, nachdem ich dies zur Arbeit gebracht habe, wie kann ich es zum ContainerBuilder übergeben?

In Unity konnte ich so etwas tun.

public sealed class UnityDependencyContainer : IDependencyContainer 
{ 
    private readonly IUnityContainer _container; 

    public UnityDependencyContainer() 
    { 
     _container = new UnityContainer() 
    } 

    public void Register<TContract, TImplementation>(IDependencyScope scope) where TImplementation : TContract 
    { 
     LifetimeManager manager = scope.CreateScope() as LifetimeManager; 

     if (manager != null) 
     { 
      _container.RegisterType<TContract, TImplementation>(manager); 
     } 
    } 
} 

Wie übergebe ich eine Instanz von IComponentLifetime an die Methodenkette? Ist es eine Sackgasse?

public class AutofacContainer : IDependencyContainer 
{ 
    private static readonly ContainerBuilder Builder; 

    static AutofacContainer() 
    { 
     Builder = new ContainerBuilder(); 
    } 

    public void RegisterType<TContract, TImplementation>(IDependencyScope scope) where TImplementation : TContract 
    { 
     IComponentLifetime manager = scope.CreateScope() as IComponentLifetime; 

     if (manager != null) 
     { 
      Builder.RegisterType<TImplementation>().As<TContract>(); 
     } 
    } 
} 

Antwort

9

Autofac nicht trennt Bereiche ganz so, wie Sie es skizziert, so Sie einen quadratischen Pflock in ein rundes Loch passen könnte versuchen.

Autofac-Bereiche sind hierarchischer. Jeder lebenslange Gültigkeitsbereich kann einen transienten Kindbereich generieren. Zum Beispiel könnten Sie sehen ...

  • Container/root Lebensdauer
    • Httprequest Umfang
      • Kleine taskspezifische transiente Umfang

Sie kann einen Bereich "markieren" und Komponenten zu einem sp registrieren ecific named/etikettierter Bereich - so funktioniert der HttpRequest-Bereich. Es wird mit einer speziellen Kennung "markiert".

Wenn Sie Objekte auflösen, wird bestimmt, welcher Lebensdauerbereich es besitzt. Das Auflösen geschieht aus dem am meisten verschachtelten Bereich. In der obigen Hierarchie können Sie Elemente aus dem kleinen aufgabenspezifischen temporären Bereich auflösen, unabhängig davon, ob es sich um Singletons, Anforderungsbereiche oder was auch immer handelt. Wenn der Singleton aufgelöst wird, durchsucht er den Lebenszeit-Scope-Stack und weist dem Root-Lifetime-Bereich automatisch "Ownership" des Objekts zu. Wenn ein Artikel pro Anfrage aufgelöst wird, durchsucht er den Stapel nach dem Gültigkeitszeitraum mit der speziellen Kennung "HTTP-Anfrage" und ordnet dort die Eigentumsrechte zu. Factory-Bereiche werden im aktuellen Gültigkeitszeitraum aufgelöst.

Hinweis: Diese Diskussion ist eine grobe Vereinfachung der Funktionsweise.

Punkt ist, ich sehe einige Dinge in der oben genannten Design, die nicht wirklich "jive" mit der Art und Weise Autofac macht Sachen.

Die DependencyScopeFactory kann keine eigenen transienten oder HttpRequest-Bereiche erstellen. Es gibt bestimmte Komponenten für die Lebensdauerverwaltung, die den HttpRequest-Bereich starten und beenden. Daher müssen Sie diese verwenden. Es gibt keinen "globalen" Übergangsbereich, Sie können also nicht wirklich einen erstellen.

Httprequest Umfang, vorausgesetzt, Sie MVC verwenden, würde mehr aussehen ...

public ILifetimeScope HttpRequestScope 
{ 
    get { return AutofacDependencyResolver.Current.RequestLifetime; } 
} 

Es gibt keine Entsprechung für einen vorübergehenden Rahmen, da Verwendung auf das soll inline sein:

using(var transientScope = parentScope.BeginLifetimeScope()) 
{ 
    // Do stuff and resolve dependencies using the transient scope. 
    // The IDisposable pattern here is important so transient 
    // dependencies will be properly disposed at the end of the scope. 
} 

Wenn Sie Komponenten registrieren, registrieren Sie sie nicht "auf Lebenszeit". Sie registrieren sie tatsächlich in einer Komponentenregistrierung, und ein Teil der Komponentenregistrierung enthält die Eigentümerinformationen über die Lebensdauer der Komponente, sobald diese aufgelöst wurde.

var builder = new ContainerBuilder(); 

// This component is factory-scoped and will be "owned" by whatever 
// lifetime scope resolves it. You can resolve multiple of these 
// in a single scope: 
builder.RegisterType<FirstComponent>().As<ISomeInterface>(); 

// This component is a singleton inside any given lifetime scope, 
// but if you have a hierarchy of scopes, you'll get one in each 
// level of the hierarchy. 
builder.RegisterType<SecondComponent>().InstancePerLifetimeScope(); 

// This component will be a singleton inside a specifically named 
// lifetime scope. If you try to resolve it in a scope without that 
// name, it'll search up the scope stack until it finds the scope 
// with the right name. If no matching scope is found - exception. 
builder.RegisterType<ThirdComponent>().InstancePerMatchingLifetimeScope("scopename"); 

// This is a per-HTTP-request component. It's just like the 
// above InstancePerMatchingLifetimeScope, but it has a special 
// tag that the web integration knows about. 
builder.RegisterType<FourthComponent>().InstancePerHttpRequest(); 

Wenn Sie versuchen, einen Container/Registrierung Agnostiker Schnittstelle zu machen, wäre es nicht eine „Lebensdauer Umfang Manager“ müssen - stattdessen müssten Sie einige Parameter zu übergeben, die vorgesehene Laufzeit Umfang angibt, und führen Sie die entsprechende Registrierungssyntax (oben) basierend auf den eingehenden Parametern durch.

Wieder, I'd recommend you check out that documentation.

Auch wenn Sie Unity verwenden, tut Autofac eine Enterprise Library Configurator Paket haben, die Sie Autofac in einem Unity-Stil konfigurieren können (denn das ist, wie EntLib mag Dinge zu tun). Das könnte etwas zu überprüfen sein.

Wenn Sie die Unity-Syntax überhaupt nicht verwenden müssen ... Ich würde nur empfehlen, die Dinge auf native Autofac-Weise zu erledigen. Der Versuch, einen Container so aussehen und verhalten zu lassen, wie er ist, ist ein ziemlich schmerzhaftes Unterfangen.

Angenommen, Ihre Plugins befinden sich in separaten Assemblys oder was auch immer, können Sie einfach die Vorteile der Assembly-Scanning-Syntax zusammen mit Autofac-Modulen nutzen und Ihre Plugins auf diese Weise verbinden.

+0

Wow! Vielen Dank für die Zeit nehmen, wirklich geschätzt. Es ist wirklich PITA, in diesem Stadium zu ändern, dann war ich so begierig, zu Autofac zu wechseln und es auszuprobieren, aber wenn ich es nicht benutzen kann, weil es anders funktioniert, würde ich einen anderen Behälter benutzen oder bleiben müssen, wie die Kosten sind zu groß, um es zu ändern. –

+0

Schöne Erklärung +1! –