Padrão de design para registro de alterações em objetos pai / filho salvos no banco de dados

5

Eu tenho duas tabelas de banco de dados no relacionamento pai / filho como um-muitos.

Eu tenho três turmas representando os dados nessas duas tabelas:

Parent Class
{
    Public int ID {get; set;}
    .. other properties
}

Child Class
{
    Public int ID {get;set;}
    Public int ParentID {get; set;}
    .. other properties
}

TogetherClass
{
    Public Parent Parent;
    Public List<Child> ChildList;
}

Por fim, tenho um aplicativo de cliente e servidor - estou no controle de ambas as extremidades para poder fazer alterações em ambos os programas, conforme necessário.

O cliente faz uma solicitação de ParentID e recebe uma Together Class para o pai correspondente e todos os registros filhos.

O aplicativo cliente pode fazer alterações nos filhos - adicione novos filhos, remova ou modifique os existentes. O aplicativo cliente envia a Together Class de volta ao aplicativo do servidor.

O aplicativo do servidor precisa atualizar os registros pai e filho no banco de dados.

Além disso, gostaria de poder registrar as alterações. Estou fazendo isso com duas tabelas separadas, uma para pai e outra para filho; cada um contendo as mesmas colunas que o original, mais a data e hora modificadas, por quem e uma lista das alterações.

Não tenho certeza sobre a melhor abordagem para detectar as alterações nos registros - novos registros, registros a serem excluídos, registros sem campos alterados, registros com alguns campos alterados.

Eu acho que preciso ler o pai & registros de crianças e compare-os com os da Classe Juntos.

Estratégia A:

Se o registro filho da turma Juntos tiver uma ID de 0, isso indica um novo registro; inserir. Todos os registros filhos excluídos não estão mais na classe Juntos; veja se algum dos registros filho de comparação não foi encontrado na classe Together e exclua se não encontrado (Compare usando ID).

Verifique se há alterações no registro de cada filho e se o log foi alterado.

Estratégia B: Faça um novo TogetherClass Atualizado

UpdatedClass
{
    Public Parent Parent {get; set}
    Public List<Child> ListNewChild {get;set;}
    Public List<Child> DeletedChild {get;set;}
    Public List<Child> ExistingChild {get;set;} // used for no changes and modified rows
}

E, em seguida, processe conforme a lista.

A razão pela qual estou pedindo ideias é que ambas as soluções não parecem ideais para mim e suspeito que esse problema já tenha sido resolvido - algum tipo de padrão de design?

Estou ciente de um problema em potencial nessa abordagem geral - onde o Aplicativo do Cliente A solicita um registro; App B solicita o mesmo registro; A então salva as alterações; B salva as alterações que podem sobrescrever as alterações feitas. Este é um problema de bloqueio separado, que levantarei uma questão diferente se eu tiver problemas para implementar.

A implementação real é c #, SQL Server e WCF entre cliente e servidor - compartilhando uma biblioteca contendo as implementações de classe.

Desculpas se este for um post duplicado - tentei pesquisar vários termos sem encontrar uma correspondência.

Atualize com base nos comentários do svick e do TimG:

Para acompanhar a mudança, a classe base seria algo como:

public class ChangedRowBase
    {
        public enum StatusState
        {
        New, Unchanged, Updated, Deleted
    };

    protected StatusState _Status;

    public StatusState Status
    {
        get
        {
            return _Status;
        }
    }

    public void SetUnchanged()
    {
        _Status = StatusState.Unchanged;
    }

    public void SetDeleted()
    {
        _Status = StatusState.Deleted;
    }

}

Então minha turma infantil teria algo como:

    public int SomeDumbProperty
    {
        get;
        set
        {
            base.Status = StatusState.Updated;
            // Missing line here to set the property itself
        }
    }

    private int _SomeIntelligentProperty;

    public int SomeIntelligentProperty
    {
        get { return _SomeIntelligentProperty; }

        set
        {
            if (value != _SomeIntelligentProperty)
            {
                _SomeIntelligentProperty = value;
                base.Status = StatusState.Updated;
            }
        }
    }

A idéia é que o inteligente detecta se realmente houve uma mudança; caso contrário, usar o mudo que o aplicativo cliente precisaria para detectar a alteração e atualizar somente se houve uma alteração, ou usar o setter de qualquer maneira sinalizando uma alteração que realmente não ocorreu.

Eu também sei que a minha burrice não funciona (ou compila) agora. Se eu quiser código no getter ou setter, isso significa que eu preciso definir uma variável privada separada eu mesmo? Eu conheço o ponteiro curto {get; set;} faz isso nos bastidores.

    
por andrew 25.06.2013 / 15:53
fonte

2 respostas

1

Aqui estão padrões de design comuns para lidar com esses cenários.

Configure uma classe base. Filho e pai herdarão da base. A classe base deve fazer o seguinte:

  1. Ter uma propriedade .Status (somente leitura) que rastreia se um objeto é {inalterado, novo, atualizado, excluído} O status é definido durante o método {constructor, .delete (), o conjunto de cada Propriedade}. Você precisará de um método de fábrica para gerar uma instância de objeto com um status "inalterado" ou um método para se marcar como "inalterado". btw. Essa abordagem torna um objeto mais "atômico" porque é autocontido e controla suas próprias mudanças. Este também é um exemplo de por que as pessoas usam Properties (getters / setters) em vez de public vars.

  2. Um mecanismo de pseudo-travamento comumente usado é usar uma combinação de colunas .LastModifiedDateTime (somente leitura) e .LastModifier (somente leitura) em cada tabela DB (não tabela de códigos / tabela estática). Estes também são definidos no método construtor / fábrica. Em Atualizar / Salvar, compare as últimas LastModifiedDateTime & LastModifier para o banco de dados. Se eles não corresponderem, rejeite essa alteração.

# 1 é semelhante ao modo como muitos ORMs mantêm o estado (manipular alterações). # 2 é frequentemente referido como um "mecanismo de bloqueio suave".

    
por 25.06.2013 / 20:25
fonte
0

Acho que uma boa solução aqui seria para o modelo do cliente detectar alterações em si mesmo e enviar ao servidor apenas as alterações.

Isso significa que os setters de suas propriedades terão que, pelo menos, registrar que algo mudou. E as coleções não podem ser simples List<T> s, mas algo que pode ter reação personalizada às alterações. (Você pode usar Collection<T> com métodos substituídos como InsertItem() para isso.)

    
por 25.06.2013 / 16:42
fonte