Pergunta

Estou tentando fazer com que a unidade gerencie a criação de meus objetos e quero ter alguns parâmetros de inicialização que não são conhecidos até o tempo de execução:

No momento, a única maneira de pensar na maneira de fazer isso é ter um método init na interface.

interface IMyIntf {
  void Initialize(string runTimeParam);
  string RunTimeParam { get; }
}

Então, para usá -lo (em unidade), eu faria isso:

var IMyIntf = unityContainer.Resolve<IMyIntf>();
IMyIntf.Initialize("somevalue");

Neste cenário runTimeParam O param é determinado no tempo de execução com base na entrada do usuário. O caso trivial aqui simplesmente retorna o valor de runTimeParam Mas, na realidade, o parâmetro será algo como o nome do arquivo e o método Initialize fará algo com o arquivo.

Isso cria uma série de questões, a saber, que o Initialize O método está disponível na interface e pode ser chamado várias vezes. Definindo uma bandeira na implementação e a exceção de arremesso em chamadas repetidas para Initialize Parece muito desajeitado.

No ponto em que resolvo minha interface, não quero saber nada sobre a implementação de IMyIntf. O que eu quero, porém, é o conhecimento de que essa interface precisa de certos parâmetros de inicialização única. Existe uma maneira de anotar de alguma forma (atributos?) A interface com essas informações e passar a estrutura quando o objeto é criado?

Editar: descrevi a interface um pouco mais.

Foi útil?

Solução

Qualquer lugar onde você precise de um valor de tempo de execução para construir uma dependência específica, Fábrica abstrata é a solução.

Ter métodos inicializados nas interfaces cheiros de um Abstração com vazamento.

No seu caso, eu diria que você deveria modelar o IMyIntf interface em Como você precisa usá -lo - Não como você pretende criar implementações. Esse é um detalhe de implementação.

Assim, a interface deve ser simplesmente:

public interface IMyIntf
{
    string RunTimeParam { get; }
}

Agora defina a fábrica abstrata:

public interface IMyIntfFactory
{
    IMyIntf Create(string runTimeParam);
}

Agora você pode criar uma implementação concreta de IMyIntfFactory que cria instâncias concretas de IMyIntf como este:

public class MyIntf : IMyIntf
{
    private readonly string runTimeParam;

    public MyIntf(string runTimeParam)
    {
        if(runTimeParam == null)
        {
            throw new ArgumentNullException("runTimeParam");
        }

        this.runTimeParam = runTimeParam;
    }

    public string RunTimeParam
    {
        get { return this.runTimeParam; }
    }
}

Observe como isso nos permite proteger os invariantes da classe pelo uso do readonly palavra -chave. Não são necessários métodos de inicialização fedorenta.

Um IMyIntfFactory A implementação pode ser tão simples assim:

public class MyIntfFactory : IMyIntfFactory
{
    public IMyIntf Create(string runTimeParam)
    {
        return new MyIntf(runTimeParam);
    }
}

Em todos os seus consumidores onde você precisa de um IMyIntf Por exemplo, você simplesmente depende IMyIntfFactory solicitando Injeção de construtor.

Qualquer contêiner DI que vale o seu sal será capaz de conectar-se automaticamente e IMyIntfFactory instância para você se você o registrar corretamente.

Outras dicas

Geralmente, quando você encontra essa situação, precisa revisitar seu design e determinar se está misturando seus objetos de dados/dados com seus serviços puros. Na maioria dos casos (não todos), você deseja manter esses dois tipos de objetos separados.

Se você precisar de um parâmetro específico do contexto passado no construtor, uma opção é criar uma fábrica que resolve suas dependências de serviço através do construtor e assume seu parâmetro de tempo de execução como um parâmetro do método create () (ou gerar ( ), Build () ou o que você nomeia seus métodos de fábrica).

Ter setters ou um método inicialize () são geralmente Pensado para ser um design ruim, pois você precisa "lembrar" de chamá-los e garantir que eles não abrem muito do estado da sua implementação (ou seja, o que é impedir que alguém recorrente da inicialização ou do setter?).

Eu também encontrei essa situação algumas vezes em ambientes em que estou criando dinamicamente objetos ViewModel com base em objetos de modelo (descritos muito bem por este outro Postagem de Stackoverflow).

Eu gostei de como o Extensão do Ninject que permite criar fábricas dinamicamente com base em interfaces:

Bind<IMyFactory>().ToFactory();

Não consegui encontrar nenhuma funcionalidade semelhante diretamente em Unidade; Então eu escrevi minha própria extensão para o IunityContainer que permite registrar fábricas que criarão novos objetos com base em dados de objetos existentes, mapeando essencialmente de uma hierarquia de tipos para uma hierarquia de tipos diferentes: UnityMappingFactory@github

Com um objetivo de simplicidade e legibilidade, acabei com uma extensão que permite especificar diretamente os mapeamentos sem declarar classes ou interfaces individuais de fábrica (um economizador em tempo real). Você apenas adiciona os mapeamentos exatamente onde registra as aulas durante o processo normal de bootstrapping ...

//make sure to register the output...
container.RegisterType<IImageWidgetViewModel, ImageWidgetViewModel>();
container.RegisterType<ITextWidgetViewModel, TextWidgetViewModel>();

//define the mapping between different class hierarchies...
container.RegisterFactory<IWidget, IWidgetViewModel>()
.AddMap<IImageWidget, IImageWidgetViewModel>()
.AddMap<ITextWidget, ITextWidgetViewModel>();

Então você acabou de declarar a interface de fábrica de mapeamento no construtor para CI e usa seu Crio() método...

public ImageWidgetViewModel(IImageWidget widget, IAnotherDependency d) { }

public TextWidgetViewModel(ITextWidget widget) { }

public ContainerViewModel(object data, IFactory<IWidget, IWidgetViewModel> factory)
{
    IList<IWidgetViewModel> children = new List<IWidgetViewModel>();
    foreach (IWidget w in data.Widgets)
        children.Add(factory.Create(w));
}

Como um bônus adicional, quaisquer dependências adicionais no construtor das classes mapeadas também serão resolvidas durante a criação de objetos.

Obviamente, isso não resolverá todos os problemas, mas me serviu muito bem até agora, então pensei em compartilhá -lo. Há mais documentação no site do projeto no Github.

Não posso responder com terminologia específica da unidade, mas parece que você está apenas aprendendo sobre injeção de dependência. Nesse caso, peço que você leia as informações breves, claras e embaladas Guia do usuário para o Ninject.

Isso o levará a várias opções que você tem ao usar DI e como explicar os problemas específicos que você enfrentará ao longo do caminho. No seu caso, você provavelmente desejará usar o contêiner DI para instanciar seus objetos e fazer com que esse objeto obtenha uma referência a cada uma de suas dependências através do construtor.

O passo a passo também detalha como anotar métodos, propriedades e até parâmetros usando atributos para distingui -los em tempo de execução.

Mesmo se você não usar o Ninject, o passo a passo fornecerá os conceitos e a terminologia da funcionalidade que se adapta ao seu propósito, e você poderá mapear esse conhecimento para a unidade ou outras estruturas de DI (ou convencer você a experimentar o Ninject) .

Eu acho que resolvi e parece bastante saudável, então deve estar meio certo :))

Eu me divido IMyIntf em uma interfaces "getter" e um "setter". Então:

interface IMyIntf {
  string RunTimeParam { get; }
}


interface IMyIntfSetter {
  void Initialize(string runTimeParam);
  IMyIntf MyIntf {get; }
}

Então a implementação:

class MyIntfImpl : IMyIntf, IMyIntfSetter {
  string _runTimeParam;

  void Initialize(string runTimeParam) {
    _runTimeParam = runTimeParam;
  }

  string RunTimeParam { get; }

  IMyIntf MyIntf {get {return this;} }
}

//Unity configuration:
//Only the setter is mapped to the implementation.
container.RegisterType<IMyIntfSetter, MyIntfImpl>();
//To retrieve an instance of IMyIntf:
//1. create the setter
IMyIntfSetter setter = container.Resolve<IMyIntfSetter>();
//2. Init it
setter.Initialize("someparam");
//3. Use the IMyIntf accessor
IMyIntf intf = setter.MyIntf;

IMyIntfSetter.Initialize() ainda pode ser chamado várias vezes, mas usando bits de Paradigma do localizador de serviços Podemos embrulhá -lo muito bem para que IMyIntfSetter é quase uma interface interna que é distinta de IMyIntf.

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