Pergunta

Embora esta questão esteja relacionada ao StructureMap, meu em geral questão é:

Ao conectar componentes com um contêiner de COI em código (em vez de configurar via xml) Você geralmente precisa de referências explícitas de projeto/construção para todas as assembléias?

Por que as assembleias separadas?Porque:


"Classes abstratas residentes em uma montagem separada de suas implementações concretas são uma ótima maneira de alcançar essa separação". -Diretrizes de design da estrutura p.91


Exemplo:

Digamos que eu tenho PersonBase.dll e Bob.dll

Prumo herda da classe abstrata PessoaBase.Ambos estão no Pessoa espaço para nome. Mas em diferentes assembléias.

Estou programando para PessoaBase, não Prumo.

De volta ao meu código principal, preciso de uma pessoa.StructureMap pode digitalizar montagens.Ótimo, vou pedir um ao StructureMap!

Agora, no meu código principal, estou me referindo apenas a PessoaBase, não para Prumo.Na verdade, não quero que meu código saiba qualquer coisa sobre Prumo.Nenhuma referência de projeto, nada.Esse é o ponto principal.

Então eu quero dizer:

//Reference: PersonBase.dll (only)
using Person;  
...

//this is as much as we'll ever be specific about Bob:
Scan( x=> { x.Assembly("Bob.dll"); }

//Ok, I should now have something that's a PersonBase (Bob). But no ?
ObjectFactory.GetAllInstances<PersonBase>().Count == 0

Sem sorte.O que funciona é deixar explícito que quero Bob:

//Reference: PersonBase.dll and Bob.dll
using Person; 
...
Scan( x => {x.Assembly("Bob.dll"); }

//If I'm explicit, it works. But Bob's just a PersonBase, what gives?
ObjectFactory.GetAllInstances<Bob>().Count == 1 //there he is!

Mas agora eu tive que fazer referência Bob.dll no meu projeto que é exatamente o que eu não queria.

Posso evitar essa situação usando a configuração Spring + Xml.Mas então volto à configuração do Spring + Xml...!

Estou perdendo alguma coisa com o uso do StructureMap ou, como princípio geral, as configurações (fluentes) do COI precisam explicar referências a todas as assembléias?

Pergunta possivelmente relacionada: StructureMap e varredura de montagens

Foi útil?

Solução

Finalmente resolvi isso.Se parece com isso:

IoC Uml http://img396.imageshack.us/img396/1343/iocuml.jpg

com as assembleias

  • Core.exe
  • PersonBase.dll (referenciado tempo de compilação por Core.exe)
  • Bob.dll (tempo de execução carregado via verificação do StructureMap)
  • Betty.dll (tempo de execução carregado via verificação do StructureMap)

Para obtê-lo com o StructureMap, eu precisava de um "ITypeScanner" personalizado para oferecer suporte à verificação de assemblies:

public class MyScanner : ITypeScanner {
  public void Process(Type type, PluginGraph graph) {

    if(type.BaseType == null) return;

    if(type.BaseType.Equals(typeof(PersonBase))) {
      graph.Configure(x => 
        x.ForRequestedType<PersonBase>()
        .TheDefault.Is.OfConcreteType(type));
    }
  }
} 

Então meu código principal se parece com:

ObjectFactory.Configure(x => x.Scan (
  scan =>
  {
    scan.AssembliesFromPath(Environment.CurrentDirectory 
    /*, filter=>filter.You.Could.Filter.Here*/);

    //scan.WithDefaultConventions(); //doesn't do it

    scan.With<MyScanner>();
  }
));

ObjectFactory.GetAllInstances<PersonBase>()
 .ToList()
  .ForEach(p => 
  { Console.WriteLine(p.FirstName); } );

Outras dicas

Você também pode fazer a configuração xml com o StructureMap.Você pode até misturá-los se quiser.

Existem também atributos do StructureMap que você pode colocar em sua classe Bob para informar ao StructureMap como carregar o assembly.DefaultConstructor é aquele que acabo usando de vez em quando.

A opção de verificação automática só funciona quando você mantém as convenções de nomenclatura, montagem e namespace.Você pode configurar manualmente o mapa estrutural com uma interface fluente.Exemplo:

ObjectFactory.Initialize(initialization => 
   initialization.ForRequestedType<PersonBase>()
    .TheDefault.Is.OfConcreteType<Bob>());

O que fazemos no meu projeto atual (que usa AutoFac, não StructureMap, mas acho que não deveria fazer diferença):

Temos as interfaces que definem os serviços externos que a aplicação utiliza em um assembly principal, digamos App.Core (como seu PersonBase).

Então temos as implementações dessas interfaces em Services.Real (como Bob.dll).

No nosso caso também temos Service.Fake, que são usados ​​para facilitar testes de UI com dependências de outros serviços corporativos e bancos de dados, etc.

O próprio aplicativo "cliente" front-end (em nosso caso, aplicativo ASP.NET MVC) faz referência App.Core.

Quando o aplicativo é iniciado, usamos Assembly.Load para carregar a DLL de implementação de "Serviços" apropriada, com base em uma configuração.

Cada uma dessas DLLs possui uma implementação de IServiceRegistry que retorna uma lista dos serviços que ela implementa:

public enum LifestyleType { Singleton, Transient, PerRequest}

public class ServiceInfo {
    public Type InterfaceType {get;set;}
    public Type ImplementationType {get;set;}
    // this might or might not be useful for your app, 
    // depending on the types of services, etc.
    public LifestyleType Lifestyle {get;set;} 
}

public interface IServiceRegistry {
    IEnumerable<ServiceInfo> GetServices();
}

...o aplicativo encontra esse ServiceRegistry por meio de reflexão e enumera essas instâncias de ServiceInfo e as registra no contêiner.Para nós, esse registro de todos os serviços reside na aplicação Web, mas é possível (e preferível em muitos casos) tê-lo em um assembly separado.

Dessa forma, podemos isolar a lógica do domínio do código de infraestrutura e evitar soluções alternativas do tipo "só desta vez", em que o aplicativo acaba dependendo de uma referência direta ao código de infraestrutura.Também evitamos ter uma referência ao contêiner em cada implementação de Serviços.

Uma coisa realmente importante se você estiver fazendo isso:fazer claro que você tenha testes que verifiquem se é possível criar cada tipo de "nível superior" (em nosso caso, controladores ASP.NET MVC) com cada configuração potencial do contêiner IOC.

Caso contrário, é muito fácil esquecer de implementar uma interface e quebrar grandes seções do seu aplicativo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top