Pergunta

Eu vi o padrão de unidade de trabalho implementado com algo como um código seguinte:

    private HashSet<object> _newEntities = new HashSet<object>();
    private HashSet<object> _updatedEntities = new HashSet<object>();
    private HashSet<object> _deletedEntities = new HashSet<object>();

E há métodos para adicionar entidades a cada um desses hashsets.

On Commit UnitOfwork cria alguma instância do mapeador para cada entidade e chama inserção, atualizar e excluir métodos de algum mapeador imaginário.

O problema para mim com essa abordagem é: os nomes de inserção, atualização, métodos de exclusão são codificados, por isso parece que essa unidade de trabalho é capaz apenas de fazer operações simples de CRUD. Mas e se eu precisar do seguinte uso:

UnitOfWork ouw = new UnitOfWork();
uow.Start();

ARepository arep = new ARepository();
BRepository brep = new BRepository(); 

arep.DoSomeNonSimpleUpdateHere();
brep.DoSomeNonSimpleDeleteHere();

uow.Commit();

Agora, a abordagem de três hashset falha, porque eu poderia registrar entidades A e B apenas para inserção, atualização e exclusão de operações, mas preciso dessas operações personalizadas agora.

Então parece que eu não posso sempre empilhar as operações do repositório e depois executá -las com todas com UnitOfWork.Commit();

Como resolver este problema? A primeira idéia é - eu poderia armazenar endereços de métodos

arep.DoSomeNonSimpleUpdateHere();
brep.DoSomeNonSimpleDeleteHere(); 

na instância de Uow e executá -los em uow.Commit() Mas então eu também tenho que armazenar todos os parâmetros do método. Isso parece complicado.

A outra idéia é tornar os repositórios completamente conscientes: em DoSomeNonSimpleUpdateHere Eu posso detectar que há um uow em execução e, portanto, eu não realizo DoSomeNonSimpleUpdateHere Mas salve os parâmetros de operação e o status 'pendente' em alguma pilha da instância do repositório (obviamente não posso salvar tudo no Uow porque o Uow não deve depender de implementações de repositório concreto). E então eu registro o repositório envolvido na instância do UOW. Quando o Uow liga Commit, Ele abre uma transação e chama algo como Flush () para cada repositório pendente. Agora, todo método de repositório precisa de algumas coisas para detecção e operação de Uow adiar para mais tarde Commit().

Portanto, a curta pergunta é - qual é a maneira mais fácil de registrar todas as mudanças pendentes em vários repositórios em Uow e depois Commit() Todos eles em uma única transação?

Foi útil?

Solução

Parece que mesmo atualizações complicadas podem ser divididas em uma série de modificações em um ou mais DomainObjects. Chamar o DossomenOnsimpleUpdatehere () pode modificar vários DomainObjects diferentes, o que desencadearia chamadas correspondentes para unidadefwork.ReGisterDirty (DomainObject) para cada objeto. No código de amostra abaixo, substituí a chamada para dosomenonsimpleUpdatehere pelo código que remove usuários inativos do sistema.

UnitOfWork uow = GetSession().GetUnitOfWork();
uow.Start();

UserRepository repository = new UserRespository();
UserList users = repository.GetAllUsers();

foreach (User user in users)
{
  if (!user.IsActive())
    users.Remove( user );
}

uow.Commit();

Se você está preocupado em ter que iterar sobre todos os usuários, aqui está uma abordagem alternativa que usa um objeto de critérios para limitar o número de usuários retirados do banco de dados.

UnitOfWork uow = GetSession().GetUnitOfWork();
uow.Start();

Repository repository = new UserRespository();
Criteria inactiveUsersCriteria = new Criteria();
inactiveUsersCriteria.equal( User.ACTIVATED, 0 );
UserList inactiveUsers = repository.GetMatching( inactiveUsersCriteria );
inactiveUsers.RemoveAll();

uow.Commit();

Os métodos UserList.Remove e UserList.Removeall notificarão a unidade de trabalho de cada usuário removido. Quando o UnitOfwork.Commit () for chamado, ele excluirá cada usuário encontrado em suas _deletentities. Essa abordagem permite criar um código arbitrariamente complexo sem precisar escrever consultas SQL para cada caso especial. O uso de atualizações em lote será útil aqui, pois o UnitOfwork terá que executar várias instruções de exclusão em vez de apenas uma instrução para todos os usuários inativos.

Outras dicas

O fato de você ter esse problema sugere que você não está usando o padrão de repositório como tal, mas algo mais como vários gateways de dados da tabela. Geralmente, um repositório é para carregar e salvar uma raiz agregada. Como tal, quando você salva uma entidade, sua camada de persistência salva todas as alterações nesse gráfico de objeto da instância da entidade raiz agregada.

Se, no seu código, você tiver aproximadamente um "repositório" por uma tabela (ou entidade), você provavelmente está realmente usando um gateway de dados da tabela ou um objeto de transferência de dados. Nesse caso, você provavelmente precisa ter um meio de passar em uma referência à transação ativa (ou à unidade de trabalho) em cada método save ().

No livro de Evans DDD, ele recomenda deixar o controle de transações para o cliente de um repositório, e eu concordaria que não é uma boa prática, embora possa ser mais difícil evitar se você estiver realmente usando um padrão de gateway de dados da tabela.

Eu finalmente encontrei este:

http://www.goeleven.com/blog/82

O autor resolve o problema usando três listas para atualização/inserção/exclusão, mas ele não armazena entidades lá. Em vez disso, os delegados do repositório e seus parâmetros são armazenados. Então, ao comprometer, o autor chama cada delegado registrado. Com essa abordagem, eu poderia registrar até alguns métodos complexos de repositório e, portanto, evite usar um tobledatagateway separado.

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