0

Ich möchte Registrierungen in einem Unity-Container konfigurieren, der von ASP.NET Web API 2 basierend auf Eigenschaften einer HTTP-Anfrage verwendet wird. Eine Anforderung an /api/database1/values sollte beispielsweise zu einer Unity-Containerkonfiguration mit einer für Datenbank1 konfigurierten IDbContext führen, während eine Anforderung an /api/database4/values eine für Datenbank4 konfigurierte IDbContext erhält.Unity-Container pro Anfrage in OWIN-Middleware konfigurieren

Ich habe so weit mit der Verwendung UnityHierarchicalDependencyResolver als Abhängigkeits-Resolver, so registriert Typen mit HierarchicalLifetimeManager letzten nur für die Lebensdauer der Anfrage. Dies funktioniert gut für die Typen gelöst pro Anfrage. Aber wie man sie registriert pro Anfrage mit OWIN Middleware ist über mich hinaus.

In meiner Middleware, wird ein Aufruf an System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnityContainer)) eine Instanz von IUnityContainer, aber es ist die gleichen Container für alle Anfragen aus früheren Anfragen aller Registrierungen inklusive.

Durch die Kapselung UnityHierarchicalDependencyResolver mit meiner eigenen Implementierung von IDependencyResolver kann ich sehen, dass IDependencyResolver.BeginScope nicht viel später im Prozess aufgerufen wird. Daher scheint das Problem so zu sein, dass der untergeordnete Container nicht erstellt wird, bis die Web-API aufwacht, lange nachdem meine Middleware Next(..) aufgerufen hat.

Gibt es eine Möglichkeit, den Umfang meines Dependency Resolvers früher zu starten? Gibt es eine andere Strategie, die ich vermisse? Für den Fall, dass es einen Unterschied macht, hosste ich in IIS, bevorzuge aber den OWIN-Middleware-Ansatz.

aktualisiert

Dies ist keine Antwort, und es ist zu groß für einen Kommentar, aber nach kämpfen, diese zu lösen mit Unity entschied ich mich zu Autofac wechseln und es fiel einfach an seinen Platz.

Die Autofac OWIN Pakete (Autofac.Mvc5.Owin, Autofac.Owin, Autofac.WebApi2.Owin) machen es einfach tot Autofac innerhalb der OWIN Pipeline zu verwenden und entsprechende Lebensdauerverwaltung in ASP.NET MVC und Web API zu gewährleisten. Dies war das fehlende Glied.

Ich konnte keine Möglichkeit finden, den Container pro Anfrage neu zu konfigurieren, aber es war zumindest möglich, eine Fabrik pro Anfrage zu konfigurieren (also ja, @Haukinger und @alltej, du warst richtig, da reinzukommen) ., dass Richtung

So melde ich eine Fabrik wie:

builder.RegisterType<DataDependencyFactory>().InstancePerRequest(); 

Und die Methode der das Werk schaffen registrieren wie:

builder 
    .Register(c => c.Resolve<DataDependencyFactory>().CreateDataDependency()) 
    .As<IDataDependency>() 
    .InstancePerRequest(); 

die Fabrik Registrierung dieses Weg ist besonders nützlich, weil stromabwärts Abhängige nicht auf die Fabrik achten müssen. Ich mag das, weil meine Angehörigen keine Fabrik brauchen, sie brauchen eine Instanz. Der Container biegt sich auf die Bedürfnisse meiner Angehörigen, nicht andersherum :)

Dann, in einem Stück OWIN Middleware, ich die Fabrik auflösen, und legen Sie eine Eigenschaft auf sie nach den Eigenschaften der Anfrage. Bei einer nachfolgenden Auflösung von IDataDependency in einem MVC- oder Web-API-Controller oder einem beliebigen späteren Element in der OWIN-Pipeline wird eine Instanz entsprechend der Eigenschaft in der Factory konfiguriert.

+0

FWIW, ich fühle mich bestätigt, dass ich nicht versuche, etwas wirklich lächerliches zu tun, da die Autofac-Dokumentation das Konzept validiert. http://docs.autofac.org/en/latest/faq/per-request-scope.html#implementing-custom-per-request-semantics _ ** Sie können einen benutzerdefinierten Mechanismus implementieren, der die Möglichkeit zur Registrierung und Lösung bietet Abhängigkeiten auf Anfragebasis ** _. Obwohl ich es vorziehen würde, bei Unity zu bleiben, würde ich, wenn das ein harter Kampf ist, einen alternativen IoC-Container wie Autofac in Erwägung ziehen. – Snixtor

+0

Betrachten Sie eine Factory für Ihren 'IDBContext', die den Kontext erstellt und die relevanten Anforderungseigenschaften als Parameter erhält? Auf diese Weise benötigen Sie nur eine Registrierung für die Fabrik. – Haukinger

+0

Eine Fabrik benötigt immer noch eine Konfiguration pro Anfrage. Das bedeutet, dass die Verbindung direkt (nicht ideal) erkannt werden muss oder dass eine Art Verbindungskontext über den IoC-Container eingegeben werden muss, was mich nur auf den ersten Platz bringt. Oder gibt es etwas, das ich vermisse? – Snixtor

Antwort

1

Basierend auf Ihrer API-URL ("/ api/database4/values") empfehle ich, dass Sie ein Filterattribut erstellen (zB DbIdFilter), damit Sie das Filterattribut für andere Controllermethoden verwenden können, die einem ähnlichen URL-Pfad folgen. Segment wie diese unter:

[HttpGet] 
[DbIdFilter] 
[Route("{databaseId}/values")] 
public IHttpActionResult GetValues() 
{ 
    return Ok(); 
} 


[HttpGet] 
[DbIdFilter] 
[Route("{databaseId}/products")] 
public IHttpActionResult GetProducts() 
{ 
    return Ok(); 
} 

zuerst das Filterattribut erstellen:

public class DbIdFilterAttribute : ActionFilterAttribute 
{ 
    private readonly string _routeDataId; 
    private const string defaultRouteName = "databaseId"; 
    public DbIdFilterAttribute():this(defaultRouteName) 
    {} 

    public DbIdFilterAttribute(string routeDataId) 
    { 
     _routeDataId = routeDataId; 
    } 

    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 

     var routeData = actionContext.Request.GetRouteData(); 

     var dbId = routeData.Values[_routeDataId] as string; 

     //here we create the db instance at the filter level. 

     DbInstanceFactory.RegisterDbInstance(dbId); 


    } 

} 

nächste eine Instanz Fabrik erstellen, die die DB-Instanz zur Laufzeit werden registrieren/beheben:

public class DbInstanceFactory : IDbInstanceFactory 
{ 
    public static IDbInstance RegisterDbInstance(string databaseId) 
    { 
     var factory = UnityConfig.GetConfiguredContainer().Resolve<IDbInstanceFactory>(); 
     return factory.CreateInstance(databaseId); 
    } 

    public IDbInstance CreateInstance(string databaseId) 
    { 
     var container = UnityConfig.GetConfiguredContainer(); 
     //container.RegisterType<IDbInstance, DbInstance>(); 

     container.RegisterType<IDbInstance, DbInstance>(new InjectionConstructor(databaseId)); 
     var dbInstance = container.Resolve<IDbInstance>(); 
     return dbInstance; 
    } 

    public IDbInstance GetInstance() 
    { 
     var container = UnityConfig.GetConfiguredContainer(); 
     var dbInstance = container.Resolve<IDbInstance>(); 
     return dbInstance; 
    } 
} 

public interface IDbInstanceFactory 
{ 
    IDbInstance CreateInstance(string databaseId); 
    IDbInstance GetInstance(); 
} 

Register diese Factory-Klasse in UnityConfig.cs (oder wo auch immer Sie sich gerade die Typen registrieren):

container.RegisterType<IDbInstanceFactory, DbInstanceFactory> 
    (new ContainerControlledLifetimeManager()); 

Es ContainerControlledLifetimeManager registriert ist, da diese Fabrik kein pro Anfrage sein muss.

Also nur eine grundlegende DBINSTANCE Klasse unten (für Klarheit), die einen Parameter im Konstruktor (dieser Parameter kann die Verbindungszeichenfolge oder eine benannte Verbindung sein):

public class DbInstance : IDbInstance 
{ 
    public string DbId { get; } 

    public DbInstance(string databaseId) 
    { 
     DbId = databaseId; 
    } 
} 

public interface IDbInstance 
{ 
    string DbId { get; } 
} 

In Controller-Klasse, können Sie es gefällt mir:

+0

Es ist in der Theorie gut, hat aber Probleme. ** 1. ** "ActionFilterAttribute.OnActionExecuting" wird * ausgeführt, nachdem * Controller erstellt wurden, sodass das Registrieren von Abhängigkeiten nicht funktioniert. ** 2. ** Der Status in "ActionFilterAttribute" ist nicht Thread-sicher. http://StackOverflow.com/a/8937793/855363, https://msdn.microsoft.com/en-us/library/system.web.http.filters.actionfilterattribute(v=vs.118).aspx "Any Instanzmitglieder sind nicht garantiert threadsicher. " ** 3. ** Es ist ein Aktionsfilter, keine OWIN Middleware, was meine Frage ist. – Snixtor

+0

Der obige Code wurde getestet und konnte die Abhängigkeit registrieren und auflösen. – alltej

+0

Genauer gesagt können Sie Controller-Konstruktor-Abhängigkeiten nicht im Aktionsfilter registrieren. Es ist kein Show-Stopper, aber es ist nicht ideal. Die anderen Probleme, die ich angesprochen habe, sind Show-Stopper. – Snixtor