2014-12-11 11 views
6

L'autore fornisce un example di come utilizzare MediatR in un'applicazione console utilizzando Autofac:Come utilizzare MediatR con Autofac in ASP MVC 5?

var builder = new ContainerBuilder(); 
builder.RegisterSource(new ContravariantRegistrationSource()); 
builder.RegisterAssemblyTypes(typeof (IMediator).Assembly).AsImplementedInterfaces(); 
builder.RegisterAssemblyTypes(typeof (Ping).Assembly).AsImplementedInterfaces(); 
builder.RegisterInstance(Console.Out).As<TextWriter>(); 

var lazy = new Lazy<IServiceLocator>(() => new AutofacServiceLocator(builder.Build())); 
var serviceLocatorProvider = new ServiceLocatorProvider(() => lazy.Value); 
builder.RegisterInstance(serviceLocatorProvider); 

Ho preso questo esempio e tentato per farlo funzionare con ASP MVC 5 e il pacchetto Autofac.Mvc5:

var builder = new ContainerBuilder(); 
builder.RegisterSource(new ContravariantRegistrationSource()); 
builder.RegisterAssemblyTypes(typeof(IMediator).Assembly).AsImplementedInterfaces(); 
builder.RegisterAssemblyTypes(typeof(AddPostCommand).Assembly).AsImplementedInterfaces(); 
builder.RegisterControllers(typeof(HomeController).Assembly); 
var container = builder.Build(); 
var lazy = new Lazy<IServiceLocator>(() => new AutofacServiceLocator(container)); 
var serviceLocatorProvider = new ServiceLocatorProvider(() => lazy.Value); 
builder.RegisterInstance(serviceLocatorProvider); 
DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 

Quando si esegue l'applicazione Web, viene visualizzata una pagina di errore che indica che la dipendenza ServiceLocationProvider non è stata registrata. Che cosa sto facendo di sbagliato?

ho sospetto che il problema è dovuto al fatto che sto registrando il ServiceLocatorProvider esempio dopo chiamando Build - nell'esempio dell'autore, il metodo Build viene richiamato in seguito grazie alla Lazy<>. Non so come ovviare a questo, però.

risposta

0

Non è possibile chiamare Builder.Build prima di aver completato la registrazione dei tipi.

nell'esempio si chiama Builder.Build prima di chiamare builder.RegisterInstance che spiega perché non può inferire il tipo in fase di esecuzione.

sono arrivato fino a questo dopo aver colpito lo stesso problema di oggi, ma è un lavoro in corso, come si pretende molto a risolvere i miei implementazioni ancora ...

builder.RegisterSource(new ContravariantRegistrationSource()); 
     builder.RegisterAssemblyTypes(typeof(IMediator).Assembly).AsImplementedInterfaces(); 
     builder.RegisterAssemblyTypes(typeof(HomePageThumbnail).Assembly).AsImplementedInterfaces(); 

     var lifetimeScope = new Lazy<ILifetimeScope>(() => builder.Build()); 
     var lazy = new Lazy<IServiceLocator>(() => new AutofacServiceLocator(lifetimeScope.Value)); 

     var serviceLocatorProvider = new ServiceLocatorProvider(() => lazy.Value); 
     builder.RegisterInstance(serviceLocatorProvider); 


     DependencyResolver.SetResolver(new AutofacDependencyResolver(lifetimeScope.Value)); 

     app.UseAutofacMiddleware(lifetimeScope.Value); 
     app.UseAutofacMvc(); 

sto creando il contenitore in un separato Lazy<T> e di passaggio quello intorno.

Mentre questo costruisce ed i carichi del sito, non si può dedurre quale gestore da utilizzare per la richiesta della mia azione di controllo ...

var response = _mediator.Send(new RecentActivityThumbnailsQuery()); 

che solleva l'eccezione ...

An exception of type 'Microsoft.Practices.ServiceLocation.ActivationException' occurred in Microsoft.Practices.ServiceLocation.dll but was not handled in user code 

Additional information: Activation error occurred while trying to get instance of type IRequestHandler`2, key "" 

Si sta utilizzando la stessa registrazione basata sulla convenzione fornita nel progetto di esempi Autofac incluso in MediatR, e ho anche provato a registrarlo esplicitamente ....

builder.RegisterType<RecentActivityThumbnailsHandler>().As<IRequestHandler<RecentActivityThumbnailsQuery, RecentActivityThumbnailsResults>>(); 

Aggiornerò quando funzionerò. Per favore, fai lo stesso se lo capisci prima di me.

3

Ho riscontrato problemi per registrare correttamente entrambe le classi Mediator e ServiceLocatorProvider.
Mi sono tirato i capelli per un po ', ma alla fine sono riuscito a girarlo intorno.

Il mio errore è stato quello di registrare ServiceLocatorProvider contro il contenitore principale Autofac, in questo modo:

var lazyContainer = new Lazy<IContainer>(() => builder.Build()); 
builder.Register(x => new ServiceLocatorProvider(() => new AutofacServiceLoator(lazyContainer.Value))); 

DependencyResolver.SetCurrent(new AutofacDependencyResolver(lazyContainer.Value); 

In fase di esecuzione, Autofac stava gettando un'eccezione perché uno dei miei Request aveva una dipendenza sul mio EF DbContext che ho configurato per essere ambito dalla richiesta HTTP.

Il trucco è registrare ServiceLocatorProvider rispetto alla richiesta HTTP corrente ILifetimeScope.
Fortunatamente, il messaggio di errore da Autofac è autoesplicativo:

No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance 
was requested. This generally indicates that a component registered as per-HTTP request is being 
requested by a SingleInstance() component (or a similar scenario.) Under the web integration 
always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, 
never from the container itself. 

Questo accade perché la AutofacServiceLocator stati alimentati con il contenitore principale.
Il contenitore, quando si richiede di risolvere il numero DbContext, non è a conoscenza di un interno - associato alla richiesta HTTP corrente - ambito.

Utilizzando JustDecompile, ho SAX che l'unica classe che implementa l'interfaccia ILifetimeScopeProvider era AutofacDependencyResolver, e si può accedere all'istanza corrente con la proprietà statica AutofacDependencyResolver.Current.

È possibile accedere alla corrente ILifetimeScope utilizzando AutofacDependencyResolver.Current.RequestLifetimeScope come spiegato nel messaggio di errore, quindi alla fine della registrazione si presenta come:

builder 
    .Register(x => new ServiceLocatorProvider(() => new AutofacServiceLocator(AutofacDependencyResolver.Current.RequestLifetimeScope))) 
    .InstancePerHttpRequest(); 

La parte InstancePerHttpRequest() è facoltativo, ma dal momento che la ILifetimeScope sarà lo stesso durante l'intera richiesta HTTP, ciò impedisce ad Autofac di creare n istanze di AutofacServiceLocator.

Spero che sia stato chiaro, non esitate a fare qualche modifica se ritenete che sia necessario, perché ho difficoltà a spiegarlo chiaramente.

+0

bella, mickaeld. Mi è stato chiaro, quindi grazie. –

2

Sto usando Webapi 2 + Autofac + OWIN e riesco a farlo funzionare. Ecco il mio codice:

Qui è la mia autofac Constructor

 //Constructor 
     var builder = new ContainerBuilder(); 

     builder.RegisterSource(new ContravariantRegistrationSource()); 
     builder.RegisterAssemblyTypes(typeof(IMediator).Assembly).AsImplementedInterfaces(); 
     builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces(); 

     // Register Web API controller in executing assembly. 
     builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest(); 


     var lazyContainer = new Lazy<IContainer>(() => builder.Build()); 
     var serviceLocatorProvider = new ServiceLocatorProvider(() => new AutofacServiceLocator(lazyContainer.Value)); 
     builder.RegisterInstance(serviceLocatorProvider); 
     config.DependencyResolver = new AutofacWebApiDependencyResolver(lazyContainer.Value); 


     // This should be the first middleware added to the IAppBuilder. 
     app.UseAutofacMiddleware(lazyContainer.Value); 

     // Make sure the Autofac lifetime scope is passed to Web API. 
     app.UseAutofacWebApi(config); 

qui sono i miei spazi dei nomi:

using System; 
using System.Reflection; 
using System.Web.Http; 
using Autofac; 
using CommonServiceLocator.AutofacAdapter.Unofficial; 
using Autofac.Features.Variance; 
using Autofac.Integration.WebApi; 
using MediatR; 
using Microsoft.Practices.ServiceLocation; 
using Owin; 

tutto ha funzionato bene e non ha dovuto explict registrare ogni requestHandler o CommandHandler. Dal momento che ho perso anche MOLTO tempo per metterlo in sintonia, spero che aiuti gli altri a fare lo stesso problema. Le risposte precedenti sono state utili per arrivare a questo.

UPDATE:

Beh, ho appena refactoring de codice per rimuovere tutti i pigri legame che lo rende molto più semplice. Bellow sono i cambiamenti:

Invece di:

 var lazyContainer = new Lazy<IContainer>(() => builder.Build()); 
     var serviceLocatorProvider = new ServiceLocatorProvider(() => new AutofacServiceLocator(lazyContainer.Value)); 
     builder.RegisterInstance(serviceLocatorProvider); 

Basta usare:

 builder.RegisterType<AutofacServiceLocator>().AsImplementedInterfaces(); 
     var container = builder.Build(); 

     //ServiceLocator.SetLocatorProvider(serviceLocatorProvider);    
     config.DependencyResolver = new AutofacWebApiDependencyResolver(container);