Injeção de dependência com a solução Entity Framework de n camadas


12

Atualmente, estou projetando uma solução de n camadas que usa o Entity Framework 5 (.net 4) como estratégia de acesso a dados, mas estou preocupada em como incorporar a injeção de dependência para torná-lo testável / flexível.

Meu layout de solução atual é o seguinte (minha solução é chamada Alcatraz):

Alcatraz.WebUI : um projeto de formulário da web asp.net, a interface do usuário front-end, faz referência aos projetos Alcatraz.Business e Alcatraz.Data.Models .

Alcatraz.Business : Um projeto de biblioteca de classes, contém a lógica de negócios, faz referência a projetos Alcatraz.Data.Access , Alcatraz.Data.Models

Alcatraz.Data.Access : um projeto de biblioteca de classes, abriga AlcatrazModel.edmx e AlcatrazEntitiesDbContext, faz referência a projetos Alcatraz.Data.Models .

Alcatraz.Data.Models : um projeto de biblioteca de classes, contém POCOs para o modelo Alcatraz, sem referências.

Minha visão de como essa solução funcionaria é que a Web-UI instanciaria um repositório dentro da biblioteca de negócios, esse repositório teria uma dependência (através do construtor) de uma cadeia de conexão (não uma AlcatrazEntitiesinstância). O web-ui conheceria as cadeias de conexão do banco de dados, mas não que fosse uma cadeia de conexão da estrutura da entidade.

No projeto de negócios:

public class InmateRepository : IInmateRepository
{
    private string _connectionString;

    public InmateRepository(string connectionString)
    {
        if (connectionString == null)
        {
            throw new ArgumentNullException("connectionString");
        }

        EntityConnectionStringBuilder connectionBuilder = new EntityConnectionStringBuilder();

        connectionBuilder.Metadata = "res://*/AlcatrazModel.csdl|res://*/AlcatrazModel.ssdl|res://*/AlcatrazModel.msl";
        connectionBuilder.Provider = "System.Data.SqlClient";
        connectionBuilder.ProviderConnectionString = connectionString;

        _connectionString = connectionBuilder.ToString();
    }

    public IQueryable<Inmate> GetAllInmates()
    {
        AlcatrazEntities ents = new AlcatrazEntities(_connectionString);

        return ents.Inmates;
    }
}

Na interface da Web:

IInmateRepository inmateRepo = new InmateRepository(@"data source=MATTHEW-PC\SQLEXPRESS;initial catalog=Alcatraz;integrated security=True;");

List<Inmate> deathRowInmates = inmateRepo.GetAllInmates().Where(i => i.OnDeathRow).ToList();

Tenho algumas perguntas relacionadas a este design.

  1. Esse design ainda faz sentido em termos de recursos do Entity Frameworks? Ouvi dizer que a estrutura do Entity já usa o padrão Unidade de trabalho, estou adicionando outra camada de resumo desnecessariamente?

  2. Não quero que meu web-ui se comunique diretamente com o Entity Framework (nem faça referência a ele), quero que todo acesso ao banco de dados passe pela camada de negócios, pois no futuro terei vários projetos usando a mesma camada de negócios (serviço web, aplicativo windows, etc.) e desejo facilitar a manutenção / atualização, tendo a lógica de negócios em uma área central. Essa é uma maneira apropriada de conseguir isso?

  3. A camada de negócios deve conter repositórios ou deve estar contida na camada de acesso? Se está tudo bem, a passagem de uma cadeia de conexão é uma boa dependência a ser assumida?

Obrigado por reservar um tempo para ler!

Respostas:


11

O jeito que você está fazendo DI está errado.

Primeiro, a cadeia de conexão pertence à camada de dados. Ou no arquivo web.config.

A próxima abstração com a qual você estará lidando é o DbContext, não uma cadeia de conexão. Seus repositórios não devem saber sobre cadeias de conexão. Sua lógica de negócios não terá conhecimento sobre o DbContext etc.

Sua interface do usuário não tem idéia e não instancia nada relacionado ao EF.

Respostas concretas aos seus pontos:

  1. Não adicione abstrações até que você esteja familiarizado com o EF. Ele já adiciona boas abstrações como UoW, consultas, usando POCOs etc.

  2. Para o DI funcionar, você tem uma Raiz de Composição que faz referência a todos os componentes necessários. Isso pode ou não estar no projeto WebUI. Caso contrário, você deve esperar que ele não faça referência à EF ou a qualquer outra tecnologia relacionada a dados.

  3. Pare aqui. Pare de adicionar abstrações sobre abstrações. Comece com uma arquitetura direta e 'ingênua' e desenvolva-a com o tempo.

As abstrações são uma ferramenta para lidar com a complexidade. Ausência de complexidade significa que não são necessárias abstrações (ainda).


Para ficar claro que eu entendo o que você está dizendo: O repositório (que a interface existe nos negócios e concreto existe no Alcatraz.Data.Access?) Aceita a DbContextcomo sua dependência. As classes de negócios têm repositórios como uma dependência. Para a injeção de dependência, estou fazendo isso manualmente (para entender o que está acontecendo). O motivo pelo qual desejo poder definir a cadeia de conexão DbContexté usar o sharding do banco de dados; em certos casos, preciso ter uma estrutura de entidade para conectar-se a diferentes bancos de dados (da mesma estrutura). Eu entendo você corretamente?
Matthew

Pelo código que você forneceu, parece que você não está fazendo o DI. O principal objetivo do DI é liberar você e seu código do gerenciamento das dependências. Não consigo imaginar você fazendo isso efetivamente manualmente sem um contêiner de DI.
Boris Yankov

também mantenha sua mente aberta com DI. Eu já, por diversão, fiz exatamente a mesma pergunta aqui e depois em outro fórum, para obter respostas opostas. DI é um padrão, não uma arquitetura. Dependendo do seu objetivo, você pode optar por usá-lo ou não. Eu o uso, mas não pelos motivos que a maioria das pessoas me diz para usá-lo.
Bastien Vandamme

4

Alguns comentários rápidos. Eu, pessoalmente, provavelmente não passaria uma string de conexão. Se alguma coisa eu tentaria criar interfaces talvez para os repositórios e apenas passar as interfaces? Faça com que os repositórios implementem ou exponham uma interface IOW.

Dessa forma, o evento não precisa ser um banco de dados que implemente seus repositórios. eles podem ser um cache de memória ou qualquer coisa. Talvez você possa usar algum tipo de estrutura de injeção de dependência para instanciar isso mesmo?

Então, em resposta a algumas de suas perguntas:

  1. Sim, acho que está bem
  2. Eu ainda teria a interface do usuário fazendo referência ao projeto EF e às interfaces de referência da camada de negócios que a camada de repositório EF implementa. Dessa forma, outros projetos ainda podem usar os mesmos conjuntos, mas eles têm a flexibilidade de trocar, se desejado.
  3. hmmm, provavelmente os repositórios na camada de acesso, mas implementando uma definição de interface exposta na camada de negócios?

Estes são apenas alguns pensamentos para refletir.


Sobre o ponto 2, um objetivo que eu estava tentando estabelecer é não ter CRUD diretamente na camada da interface do usuário. O que quero dizer é que quero garantir que apenas o CRUD possa acontecer, percorrendo a camada de negócios, dessa forma, é gerenciada.
Matthew Matthew
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.