Autofac, Asp.net e Microsoft.Practices.ServiceLocation
-
21-09-2019 - |
Pergunta
Eu tenho trabalhado através dos detalhes da implementação do COI nos meus aplicativos da Web, mas de uma maneira que aproveite o Microsoft.Practices.ServiceLocation. Estou usando especificamente o Autofac e a integração do ASP.NET, mas queria me deixar aberto para outros contêineres. Ao longo da linha de essa questão, eu estava preocupado com como acessar o contêiner no meu código de aplicativo da web.
Eu tenho uma biblioteca 'núcleo' que define principalmente interfaces a serem resolvidas. Esta biblioteca principal também é usada pelo meu aplicativo da web e outros aplicativos. Muito útil para ter interfaces comuns definidas. Eu pensei que este era um excelente lugar para colocar acesso ao contêiner do COI, e o fiz com uma aula estática. O truque está injetando o contêiner na classe estática.
É complicado em um ambiente da web, pois o contêiner pode ser diferente para cada solicitação, enquanto em um aplicativo que não é da Web, provavelmente será o mesmo o tempo todo. No começo, tentei injetar a direção do contêiner com um método, mas isso rapidamente falhou na próxima solicitação da web! Então eu inventei isso:
public static class IoCContainer
{
public static void SetServiceLocator(Func<IServiceLocator> getLocator)
{
m_GetLocator = getLocator;
}
static private Func<IServiceLocator> m_GetLocator = null;
public static T GetInstance<T>(string typeName)
{
return m_GetLocator().GetInstance<T>(typeName);
}
}
Agora no meu global.asax.cs eu faço isso:
protected void Application_Start(object sender, EventArgs e)
{
var builder = new Autofac.Builder.ContainerBuilder();
... register stuff ...
var container = builder.Build();
_containerProvider = new Autofac.Integration.Web.ContainerProvider(container);
Xyz.Core.IoCContainer.SetServiceLocator(() =>
new AutofacContrib.CommonServiceLocator.AutofacServiceLocator
(_containerProvider.RequestContainer));
}
public IContainerProvider ContainerProvider
{
get { return _containerProvider; }
}
static IContainerProvider _containerProvider;
E as chamadas para resolver as dependências se parecem
var someService = Xyz.Core.GetInstance<ISomeService>();
Então, em vez de passar por um contêiner específico, passe um delegado que sabe como obter um contêiner. Para aplicativos que não são da Web, o delegado provavelmente retornaria apenas o que o construtor.build () serve.
Minha pergunta aos especialistas é: isso faz sentido? Eu tenho uma maneira fácil de chegar a algo que pode resolver dependências sem saber qual é o produto do contêiner ou de onde vem o próprio contêiner. O que você acha?
Solução
Usamos um padrão semelhante principalmente devido ao fato de o COI ter sido introduzido em uma arquitetura não-DI. Assim, a necessidade de poder chamar explicitamente o contêiner para obter serviços, que basicamente é o padrão de fábrica.
O verdadeiro benefício do COI é alcançado quando todas as dependências podem ser injetadas e seu código não tem mais dependência do localizador de serviços. Autofac.integration.web possui manipuladores que executarão injeção em seus objetos de página que tornarão o localizador de serviço estático obsoleto. Na IMO, essa é a maneira preferida, no entanto (como no nosso caso), o localizador de serviço nem sempre pode ser evitado.
Dito isto, como você já isolou seu aplicativo do contêiner usando a classe ioccontainer, não vejo razão para ter a abstração adicional do AutofacServicelocator dentro do ioccontainer. Resumindo a linha de baixo é que o ioccontainer já é o seu localizador de serviço e deve ser "permitido" acesso direto à implementação do contêiner.
Aqui está a minha opinião na aula do seu localizador de serviço:
public static class IoCContainer
{
private static IContext GetContainer()
{
var cpa =
(IContainerProviderAccessor)HttpContext.Current.ApplicationInstance;
return cpa.ContainerProvider.RequestContainer;
}
public static T GetInstance<T>()
{
return GetContainer().Resolve<T>();
}
}