quinta-feira, 9 de abril de 2009

DataContext - Unico por aplicação, varios...?

        Tenho acompanhado em vários foruns uma dúvida comum, sobre se devemos criar um único DataContext por aplicação ou vários, ou por DAO... Enfim, DataContext é um cara um pouco mal compreendido.

Vamos por partes (como diria o Dexter):

  • DataContext por DAO
          Neste cenário temos toda uma arquitetura definida de modo que as classes de DAO herdam de AbstractDAO que por sua vez implementa IDAO, que define os métodos de CRUD de uma entidade. A idéia é que todo DAO já herde o CRUD de forma automática e fácil. 
Na classe abstrata também é definido como o DataContext é obtido, ou seja temos isolado o acesso ao Linq apenas dentro das classes de DAO. Não vou descrever detalhadamente, ou questionar solução pois não é o foco deste post. 

          O ponto chave em questão é que temos um DataContext por DAO. E neste cenário podemos ter transações que alterem o estado de um DataContext enquanto outro não ficou ciente das alterações. Isto acontecendo na mesma requisição web, trará inconsistencias indesejadas. Isso porque o DataContext implementa o padrão "Identity Map", assim uma cópia dos objetos identificados pela "Key" é guardado em cache, não sendo realizada uma nova consulta Sql enquanto a instância do DataContext estiver viva.  Ou seja, para a maioria das aplicações, não é uma boa solução.

  • DataContext único
         Acho que este cenário é o mais utilizado. Nele temos apenas uma instância do DataContext por aplicação e isso é garantido usando o padrão Singleton. Para um cenário de uma aplicação "DeskTop pura", já resolve tranquilamente.

        Nesta solução temos uma baixa complexidade, e não temos problemas com transações que passem por vários métodos que possuam sua própria instancia de DataContext. Não teremos DataContexts com estados diferentes na mesma época, causando inconsistências. 
       Entretanto, "DC" (intimos já) não são thread safe. E se sua solução tiver possibilidade de uma outra aplicação disputando o mesmo Banco de Dados, aí definitivamente você terá um problema dos bons. Pois o DataContext, como bom DataMapper possui um espelho do seu banco de dados e ficará constantemente desatualizado. Outro ponto complicado é que se você setar as propriedades "ObjectTrackingEnabled" e "DeferredLoadingEnabled" pensando em uma operação específica, ela terá impacto em todas as outras operações.
 

Fazendo um simples teste para entender como o DataContext funciona - DataMapper (Vendo com os próprios olhos):

1.Crie uma simples aplicação (só pra testar) com o singleton do DataContext, como o código  ao lado usando o BD NorthWind e tabela Categorias:


public class DataContextFactory
{
//Instancia única
private static DataContext _dataContext;

//Método que devolve a instância única
public DataContext GetContext()
{
if(_dataContext == null)
_dataContext = new DataContext(Settings.Default.NorthwindConnectionString);

return _dataContext;
}
}




2.Crie uma classe de DAO e implemente um simple GetAll()


public class CategoriaDAO
{
//Retorna todas as categorias
public IList getAll()
{
DataContext DataContext = DataContextFactory.GetContext();
return DataContext.GetTable().ToList();
}
}


3.  Popule a página verifique os resultados



4. Altere um registro no Banco


5. Refresh na página e... nada mudou. Pois é. DataContext desatualizado.

  • DataContext e UnitWork
Uma boa forma de utilizar Linq to Sql é utilizando o DataContext conforme o padrão Unit of Work, de modo que o ciclo de vida do DataContext será apenas enquanto durar a "business transaction", que pode afetar o banco de dados. De fato, até o MSDN indica a utilização de DataContext por "unit of work".

É facil de identificar o quanto este padrão é indicado, pela capacidade de monitoramento  das alterações feitas a qualquer objeto que tenha carregado. O DataContext sabe o que mudou (deletado, inserido, atualizado), nos possibilitando o famoso "submitChanges()".

MSDN
In general, a DataContext instance is designed to last for one "unit of work" however your application defines that term


Segundo Martin Folwler
A Unit of Work keeps track of everything you do during a business transaction that can affect the database. When you're done, it figures out everything that needs to be done to alter the database as a result of your work.


Uma boa implementação desse padrão é colocar o DataContext no contexto da aplicação, ou seja, para aplicações web, terá duração da requisição. 

Espero que tenha ajudado!! No próximo Post irei demonstrar formas de como implementar este padrão para aplicações Web ou  DeskTop.

0 comentários:

Postar um comentário