Entity Framework com grandes sistemas - como dividir modelos?

50

Estou trabalhando com um banco de dados do SQL Server com mais de 1000 tabelas, outras centenas de visualizações e vários milhares de procedimentos armazenados. Esperamos começar a usar o Entity Framework para nossos projetos mais recentes e estamos trabalhando em nossa estratégia para fazê-lo. O que mais me preocupa é a melhor forma de dividir as tabelas em diferentes modelos (EDMX ou DbContext, se for o código primeiro). Eu posso pensar em algumas estratégias logo de cara:

  • Dividir por esquema
    Temos nossas tabelas divididas em provavelmente uma dúzia de esquemas. Poderíamos fazer um modelo por esquema. Isso não é perfeito, no entanto, porque o dbo ainda acaba sendo muito grande, com mais de 500 tabelas / visualizações. Outro problema é que certas unidades de trabalho acabam tendo que fazer transações que abrangem vários modelos, o que aumenta a complexidade, embora eu assuma que a EF torne isso bastante simples.
  • Dividir por intenção
    Em vez de se preocupar com esquemas, divida os modelos por intenção. Portanto, teremos modelos diferentes para cada aplicativo, projeto, módulo ou tela, dependendo da granularidade que queremos obter. O problema que vejo com isso é que existem certas tabelas que inevitavelmente precisam ser usadas em todos os casos, como User ou AuditHistory. Nós os adicionamos a todos os modelos (viola o DRY, eu acho) ou estão em um modelo separado que é usado por todos os projetos?
  • Não divida nada - um modelo gigante
    Isso é obviamente simples do ponto de vista do desenvolvimento, mas da minha pesquisa e da minha intuição parece que ele pode ter um desempenho terrível, tanto em tempo de design, compilação e possivelmente em tempo de execução.

Qual é a melhor prática para usar o EF em um banco de dados tão grande? Especificamente, quais estratégias as pessoas usam no design de modelos para esse volume de objetos de banco de dados? Existem opções que eu não estou pensando que funcionam melhor do que as que tenho acima?

Além disso, isso é um problema em outras ORMs como o NHibernate? Em caso afirmativo, eles apresentaram soluções melhores que a EF?

RationalGeek
fonte
"ter que fazer transações que abranjam vários modelos, o que aumenta a complexidade" Apenas uma observação aqui de que você precisará ativar o Coordenador de Transações Distribuídas da Microsoft. Depois de ter instalado e funcionando, deve ser simples realizar o que você fala.
Tjaart 10/09/12
@Tjaart thanks. Eu usei o MS DTC antes e, embora seja bastante simples, ele adiciona complexidade além de um simples banco de dados txn, portanto, desejo evitá-lo sempre que possível.
RationalGeek
2
4 anos depois, o que você decidiu e o que recomendaria agora?
Rory #

Respostas:

31

Pessoalmente, tentei criar um esquema enorme para todas as minhas entidades em um projeto relativamente complexo, porém pequeno (~ 300 tabelas). Tínhamos um banco de dados extremamente normalizado (normalização da 5ª forma (digo isso vagamente)) com muitos relacionamentos "muitos para muitos" e extrema imposição de integridade referencial.

Também usamos uma estratégia de "instância única por solicitação", que também não estou convencida de que tenha ajudado.

Ao fazer listagens "definidas explicitamente" simples e razoavelmente simples, pesquisas e salvamentos do desempenho geralmente eram aceitáveis. Mas quando começamos a aprofundar relacionamentos profundos, o desempenho parecia ter caído drasticamente. Comparado a um processo armazenado nesse caso, não houve comparação (é claro). Tenho certeza de que poderíamos ajustar a base de código aqui e ali para melhorar o desempenho, no entanto, neste caso, apenas precisávamos de um aumento de desempenho sem análise devido a restrições de tempo e voltamos ao processo armazenado (ainda o mapeou através da EF, porque a EF forneceu resultados fortemente tipados), precisávamos apenas disso em algumas áreas. Quando tivemos que percorrer todo o banco de dados para criar uma coleção (usando .include () sem par), o desempenho foi notavelmente degradante, mas talvez estivéssemos pedindo demais ..

Portanto, com base na minha experiência, eu recomendaria a criação de um .edmx separado por intenção. Gere apenas o que você usará com base no escopo dessa necessidade. Você pode ter alguns arquivos .edmx com escopo menor para tarefas planejadas e outros grandes, nos quais é necessário atravessar relacionamentos complexos para criar objetos. Eu não tenho certeza de onde está o ponto mágico, mas tenho certeza de que há um ... lol ...

Honestamente, apesar de algumas armadilhas que nós meio que vimos chegando (travessias complexas), o enorme .edmx funcionou bem da perspectiva de "trabalho". Mas você terá que observar a mágica "consertada" que o contexto faz nos bastidores, se você não a desabilitar explicitamente. Além de manter o .edmx sincronizado quando são feitas alterações no banco de dados .. às vezes era mais fácil limpar toda a superfície e recriar as entidades, o que levava 3 minutos, para que não fosse um grande problema.

Isso foi tudo com o EntityFramework 4.1. Eu também estaria realmente interessado em saber sobre sua escolha e experiência final.

E em relação à sua pergunta no nHibernate, na minha opinião, essa é uma questão de lata de minhocas, você latirá nos dois lados da cerca ... Eu ouço muitas pessoas criticando a EF por causa da expulsão sem trabalhar com o desafia e compreende as nuances exclusivas da própria EF. e, embora eu nunca tenha usado o nHibernate na produção, geralmente, se você precisar criar manualmente e explicitamente coisas como mapeamentos, terá um controle mais finito. pode arrastar e soltar, gerar e iniciar CRUD e consulta usando LINQ, eu poderia dar uma porcaria sobre granularidade.

Eu espero que isso ajude.

hanzolo
fonte
11
FYI - Existe um NHibernate Mapping Utility que torna esses mapeamentos MUITO fáceis e automatizados.
Ganders
@ ganders - Possui uma interface do usuário e como está a integração do IDE? Suponho que você aponte para uma fonte de dados e respeite a integridade referencial e a travessia de objetos e crie os objetos de mapeamento?
hanzolo
11
Sim, faz (GUI). Eu tive zero problemas com isso até agora. Utilizou-o em 4 ou 5 projetos / sites diferentes. Nota: Eu o uso com o Fluent NHibernate, que faz o mapeamento no código c #, não nos arquivos config / xml. Aqui está um link: nmg.codeplex.com
ganders
13

Deixe-me começar por um esclarecimento simples: não tenho experiência com um banco de dados tão grande, então o restante da minha resposta não se baseia no exemplo do mundo real.

Então você tem um banco de dados GRANDE e deseja usá-lo com ORM / EF. Eu iria com a segunda escolha. Aqui está minha explicação simples por que:

  • O mapeamento adiciona complexidade. Não há necessidade de adicionar complexidade às entidades que seu aplicativo / projeto / módulo atual nunca precisa, mas não torna a granularidade muito baixa. Ter um conjunto de mapeamento separado por tela também não ajudará.
  • Você deseja alcançar a unidade de trabalho. Você deve poder especificar o que o módulo de tabelas precisa na maioria dos casos (não é necessário em todos os casos). Se você colocar essas tabelas em um conjunto de mapeamento único, poderá lidar com a leitura e a modificação de dados por instância de contexto único - esse é o seu objetivo final.
  • Não sei o que exatamente você quer dizer com modelo, mas mesmo com conjuntos de mapeamento diferentes, você pode compartilhar classes entre conjuntos de mapeamentos usando os mesmos tipos de entidade. Portanto, se você usar a tabela Usuário em dois módulos, não precisará de duas classes de Usuário para representar a mesma. Você ainda pode usar tabela única e, no caso de mapeamento de código (também conhecido como code-first), pode até definir o mapeamento uma vez e carregá-lo em vários conjuntos de mapeamentos, para que o princípio DRY não seja violado, mas a abordagem do code-first tem mais limitações quando se trata para visualizações e procedimentos armazenados. O EDMX torna isso mais difícil. Você ainda pode reutilizar classes, mas reutilizar o mapeamento impossível.
  • E as consultas entre módulos? Essas consultas podem ocorrer, mas para ser honesto, nem tudo deve ser tratado pela EF. Você pode tirar proveito do EF em casos comuns para simplificar o acesso regular a dados, mas se precisar de algum lugar para uma consulta especial que une tabelas pertencentes a 5 módulos diferentes, você pode simplesmente executá-lo diretamente ou envolvê-lo no procedimento armazenado. A substituição 100% do acesso a dados nativos pode ser difícil, complexa e contraproducente.
  • O último ponto é simplesmente prático: não acredito que as ferramentas do VS estejam prontas para trabalhar com um conjunto tão grande de objetos - nem no designer nem na ferramenta de importação. Eu costumava trabalhar em um banco de dados muito grande com projeto tradicional de acesso a dados e banco de dados SQL no VS2008 - a experiência do usuário com um projeto complexo era muito ruim. Você deve manter o número de tabelas usadas baixo - o limite para o designer deve estar entre 100-200, mas até 100 tabelas tratadas por contexto único (conjunto de mapeamento) parecem muita responsabilidade para uma classe (suponha que você tenha 100 propriedades de conjunto exposto no contexto - não parece um bom design).
Ladislav Mrnka
fonte
4

Eu diria que você não pode decidir esse tipo de pergunta de uma perspectiva técnica. Eu recomendaria que você construa sua arquitetura com base em seus casos de uso (histórias de usuário etc.). Primeiro encontre seus objetos de negócios. Um objeto de entidade não é por padrão um objeto de negócios. Típico, você terá um objeto de negócios na frente dos objetos da entidade. Em seguida, você pode decidir incrementalmente o que realmente precisa, com base nos requisitos do usuário.

"Um bom arquiteto maximiza o número de decisões não tomadas." Robert C. Martin

http://cleancoder.posterous.com/architecture-deference

ollins
fonte
3

Eu uso uma abordagem híbrida - o material OLTP é tratado pela EF, enquanto operações pesadas, como inserções em lote, atualizações em massa, consultas de relatório etc. são tratadas por Procs armazenados. Isso também facilita o caminho da migração se você não estiver reescrevendo toda a sua camada de dados de uma só vez.

Nik
fonte
Parece uma boa estratégia, mas realmente não aborda a questão de como dividir entidades entre diferentes modelos de EF. Você tem todas as entidades em um modelo ou divide e conquista de alguma forma?
RationalGeek
11
Se o desempenho do OLTP for suficiente com a abordagem de modelo completo, faça isso. Você sempre pode acabar com isso mais tarde, se for necessário, mas a maneira mais rápida e ágil é carregar tudo. Você pode nunca precisar dos ganhos de desempenho obtidos ao quebrá-lo; portanto, desperdiçando tempo e tornando seu sistema mais complicado sem motivo. Depois, há a questão de qual modelo você colocaria uma nova tabela / entidade quando decidir expandir. E o que acontece quando você precisa executar uma atualização em vários modelos. Salve-se a dor de cabeça, a menos que você realmente não tenha uma alternativa.
1112 Nik Nik
Esqueceu de mencionar que você sempre pode ajustar seu desempenho ao acessar seus dados. Veja as opções de carregamento preguiçoso / ansioso e quais entidades filho você está trazendo. Não vejo razão para que um modelo completo se comporte pior do que um menor se você não estiver carregando árvores de objetos maciças.
1112 Nik Nik
i diria árvores objeto massivo e uma estrutura de dados andam lado normalizada quando se lida com grandes do esquema
hanzolo
Você controla quão pouco ou quanto deseja saturar o gráfico do objeto.
Nik