Criando um aplicativo de banco de dados com OOP

5

Freqüentemente desenvolvo aplicações de banco de dados SQL usando Linq, e minha metodologia é construir classes de modelo para representar cada tabela, e cada tabela que precisa inserir ou atualizar obtém um método Save () que faz um InsertOnSubmit () ou SubmitChanges ( ), dependendo do estado do objeto). Muitas vezes, quando preciso representar uma coleção de registros, criarei uma classe que herda de um objeto semelhante a uma lista da classe atômica.

ex.

public class CustomerCollection : CoreCollection<Customer>
{

}

Recentemente, eu estava trabalhando em um aplicativo em que os usuários finais vivenciavam lentidão, em que cada um dos objetos precisava ser salvo no banco de dados se atendesse a um determinado critério. Meu método Save () era lento, presumivelmente porque eu estava fazendo todos os tipos de visitas de ida e volta ao servidor e chamando DataContext.SubmitChanges () após cada salvamento atômico.

Então, o código pode ter sido parecido com isso

foreach(Customer c in customerCollection)
{
   if(c.ShouldSave())
   {
       c.Save();
   }
}

Trabalhei com várias estratégias para otimizar, mas finalmente resolvi passar uma grande cadeia de dados para um procedimento armazenado SQL, onde a cadeia de caracteres possui todos os dados que representam os registros com os quais eu estava trabalhando - pode ser algo assim:

CustomerID:34567;CurrentAddress:23 3rd St;CustomerID:23456;CurrentAddress:123 4th St

Assim, o SQL Server analisa a sequência, executa a lógica para determinar a adequação do salvamento e insere, atualiza ou ignora.

Com C # / Linq fazendo este trabalho, ele salvou 5-10 registros / s. Quando o SQL faz isso, recebo > 100 registros / s, portanto, não há como negar que o Stored Proc é mais eficiente; no entanto, odeio a solução porque não parece tão limpa ou segura.

A minha verdadeira preocupação é que não tenho nenhuma solução melhor que apareça no desempenho da solução de proc armazenados. Estou fazendo algo obviamente errado em como estou pensando em projetar aplicativos de banco de dados? Existem maneiras melhores de projetar aplicativos de banco de dados?

    
por Tim C 17.10.2012 / 18:03
fonte

4 respostas

2

Nas versões modernas do SQL Server (> 2008 IIRC), você pode usar os tipos definidos pelo usuário. Então, o seu sproc pode realmente ter uma variedade de tipos de usuários:

link

Em nosso aplicativo, usamos o padrão de repositório, mas usamos um pouco para resolver problemas de lentidão, como você descreve.

Então, no seu exemplo, eu faria algo assim:

using (CustomerBulkContext ctx = customerCollection.GetSaveContext())
{

foreach(Customer c in customerCollection)
{
   if(c.ShouldSave())
   {
       c.Save(ctx);
   }
}
ctx.Save();
}

A ideia é que todos os clientes sejam adicionados a uma lista na memória dentro do contexto e, em seguida, a confirmação real para o banco de dados é manipulada por meio do método .Save () do contexto de salvamento. Esse método cria uma lista de objetos SQL e os entrega a um sproc que faz a atualização / inserção / exclusão em massa.

    
por 17.10.2012 / 20:35
fonte
2

Uma "regra geral" para obter uma velocidade incrível em um banco de dados é usar comandos baseados em conjuntos em vez de comandos de estilo procedural separados. Não são apenas as viagens de ida e volta da rede que afetam o desempenho. Mesmo se você tiver o loop embutido no sql, você encontrará ordens de magnitude mais lentas que um comando baseado em conjunto.

--SLOW way, procedural
loop i=0 to 999999
    update foobar set status='test' where id=i;
end loop

vs

--FAST way, set-based
update foobar set status='test' where id between 0 and 999999

Em qualquer lugar que você veja os dados processados em um loop, você pode substituí-los por comandos baseados em configurações. Se houver mudanças nas condições verificadas no meio do loop, você poderá ser forçado a usar o estilo procedural. Mas isso é muito raro.

Identifique apenas os gargalos da garrafa. Você pode manter o restante da lógica controlada com seus objetos. Se você operar somente em um único registro, o estilo procedural é tão rápido quanto.

NOTA: internamente dentro do RDBMS, os comandos "baseados em conjuntos" são executados através de um loop procedural. Não há nada errado com loops por digamos, é apenas a interface fornecida pela maioria dos RDBMSs que são otimizados para serem usados com comandos baseados em conjuntos.

    
por 17.10.2012 / 21:17
fonte
1

Eu consideraria usar uma das soluções ORM de grande nome como o Entity Framework ou o NHibernate. A vantagem de fazer isso é que eles realizam todos os tipos de truques para otimizar os cálculos para o banco de dados. Por exemplo:

  1. Em cache, você só precisa ler os valores do banco de dados uma vez.
  2. Acompanhamento de alterações, para atualizar somente os campos que realmente foram alterados.
  3. Carregamento preguiçoso, para que eles apenas leiam os registros em uma coleção quando você realmente os usa.

Além disso, você não estará preso a uma única tabela por classe (o chamado padrão de "registro ativo").

Os pontos negativos da minha sugestão são que a sua base de código existente precisará de algumas modificações (talvez consideráveis), e você terá uma curva de aprendizado íngreme à sua frente.

No entanto, tendo feito uma jornada similar, estou convencido de que esta é uma direção de viagem que vale a pena.

    
por 17.10.2012 / 21:22
fonte
0

Quando você está processando uma lista de registros, simplesmente chamando c.Save() você deve passá-los para algum tipo de agregador que possa analisar os registros e escrever as instruções SQL apropriadas (ou suas classes podem simplesmente suportar um emitDML() método que retorna o SQL para executar). Então, quando terminar a lista, basta dizer ao agregador para persistir as alterações e executar o SQL em uma única transação. Se você está lidando com centenas de registros, pode fazer sentido serializá-los para o disco e invocar o programa de cópia em massa para inseri-los, mas isso é uma boa quantia de trabalho.

    
por 17.10.2012 / 23:21
fonte