Pergunta

Tenho um objeto chamado "Cliente" que será utilizado nas demais tabelas como chaves estrangeiras.

O problema é que quero saber se um "Cliente" pode ser excluído (ou seja, não está sendo referenciado em nenhuma outra tabela).

Isso é possível com o Nhibernate?

Foi útil?

Solução

O que você está pedindo é encontrar a existência do Customer Valor da PK na coluna FK Tabelas referenciadas. Existem muitas maneiras de fazer isso:

  1. Como Kgiannakakis observou, tente fazer o Excluir e se uma exceção for lançada. Eficaz, mas feio e não útil. Isso também exige que você tenha definido uma cascata = "restringir" no seu banco de dados. Esta solução tem a desvantagem que você precisa tentar excluir o objeto para descobrir que você não pode

  2. Mapear as entidades que fazem referência Customer como coleções e depois para cada coleção, se o seu Count > 0 Em seguida, não permita a exclusão. Isso é bom porque isso é seguro contra as mudanças de esquema, desde que o mapeamento esteja completo. Também é uma solução ruim, porque as seleções adicionais terão que ser feitas.

  3. Tem um método que executa uma consulta como bool IsReferenced(Customer cust). Bom porque você pode ter uma única consulta que você usará quando quiser. Não é tão bom porque pode ser suscetível a erros devido a alterações de esquema e/ou domínio (dependendo do tipo de consulta que você fará: SQL/HQL/Critérios).

  4. Uma propriedade calculada na classe é eu com um elemento de mapeamento como <property name="IsReferenced" type="long" formula="sql-query that sums the Customer id usage in the referenced tables" />. Bom porque é uma solução rápida (pelo menos tão rápido quanto seu banco de dados), sem perguntas adicionais. Não é tão bom porque é suscetível às mudanças de esquema; portanto, quando você altera seu banco de dados, você não deve esquecer de atualizar esta consulta.

  5. Solução Crazy: Crie uma visão ligada ao esquema que faça o cálculo. Faça a consulta nela quando quiser. Bom porque é ligado ao esquema e é menos suscetível a mudanças de esquema, bom porque a consulta é rápida, não tão boa, porque você ainda precisa fazer uma consulta adicional (ou mapeia o resultado dessa exibição na solução 4.)

2,3,4 também são bons porque você também pode projetar esse comportamento à sua interface do usuário (não permita a exclusão)

Pessoalmente, eu iria para 4,3,5 com essa preferência

Outras dicas

Quero saber se um "cliente" pode ser excluído (ou seja, ele não está sendo referenciado em nenhuma outra tabela).

Não é realmente responsabilidade do banco de dados determinar se o cliente pode ser excluído. É antes parte da sua lógica de negócios.

Você está pedindo para verificar a integridade referencial no banco de dados.

Está tudo bem no mundo não OOP. Mas ao lidar com objetos (como você), é melhor adicionar a lógica aos seus objetos (Objetos têm estado e comportamento; Db - apenas o estado).

Então, eu adicionaria um método à classe do cliente para determinar se pode ser excluído ou não. Deste jeito Você pode testar corretamente (unidade) a funcionalidade.

Por exemplo, digamos que temos uma regra O cliente só pode ser excluído se não tiver ordens e não tiver participado do fórum.

Em seguida, você terá objeto de cliente semelhante a este (caso mais simples possível):

public class Customer
{
    public virtual ISet<Order> Orders { get; protected set; }
    public virtual ISet<ForumPost> ForumPosts { get; protected set; }

    public virtual bool CanBedeleted
    {
        get
        {
            return Orders.Count == 0 && ForumPosts.Count == 0
        }
    }
}

Este é um design muito limpo e simples que é fácil de usar, testar e não depende muito do banco de dados Nibernate ou subjacente.

Você pode usá -lo assim:

if (myCustomer.CanBeDeleted)
    session.Delete(mycustomer)

Além disso, você pode ajustar o Nibernate para excluir ordens relacionadas e outras associações, se necessário.


A nota: é claro que o exemplo acima é apenas uma solução ilustrativa mais simples possível. Você pode querer fazer essa regra parte da validação Isso deve ser aplicado ao excluir o objeto.

Pensando em entidades e relações em vez de tabelas e chaves estrangeiras, existem estas diferentes situações:

  • O cliente tem uma relação um-para-muitos que constrói uma parte do cliente, por exemplo, seus números de telefone.Eles também devem ser excluídos por meio de cascata.
  • O cliente tem uma relação um-para-muitos ou muitos-para-muitos que não faz parte do cliente, mas é conhecida/alcançável pelo cliente.
  • Alguma outra entidade tem uma relação com o Cliente.Também pode ser de qualquer tipo (que não é uma chave estrangeira no banco de dados).Por exemplo, pedidos do cliente.As encomendas não são conhecidas pelo cliente.Este é o caso mais difícil.

Pelo que eu sei, não existe uma solução direta do NHibernate.Existe a API de metadados, que permite explorar as definições de mapeamento em tempo de execução.IMHO, esta é a maneira errada de fazer isso.

Na minha opinião, é o responsabilidade da lógica de negócios para validar se uma entidade pode ser excluída ou não.(Mesmo que existam chaves estrangeiras e restrições que garantam a integridade do banco de dados, ainda é lógica de negócios).

Implementamos um serviço que é chamado antes da exclusão de uma entidade.Outras partes do software são registradas para determinados tipos.Eles podem vetar a exclusão (por exemplo.lançando uma exceção).

Por exemplo, o sistema de pedidos registra a exclusão de clientes.Se um cliente for excluído, o sistema de pedidos procura os pedidos desse cliente e lança se encontrar algum.

Não é possível diretamente. Presumivelmente, seu modelo de domínio inclui objetos relacionados ao cliente, como endereços, pedidos, etc. Você deve usar o padrão de especificação por esta.

public class CustomerCanBeDeleted
{

    public bool IsSatisfiedBy(Customer customer)
    {
        // Check that related objects are null and related collections are empty
        // Plus any business logic that determines if a Customer can be deleted
    }
}

Editado para adicionar:

Talvez o método mais direto seja criar um procedimento armazenado que execute essa verificação e ligue antes de excluir. Você pode acessar um IDBCommand de Nibernate (ISession.Connection.CreateCommand()) para que a chamada seja agnóstica de banco de dados.

Veja também as respostas a essa questão.

Pode valer a pena olhar para a propriedade Cascade, em particular todos os contratados em seus arquivos hbm.xml e isso pode cuidar disso para você.

Veja aqui, 16.3 - Cascata de vida em cascata

Uma solução ingênua será usar uma transação. Inicie uma transação e exclua o objeto. Uma exceção informará que o objeto não pode ser excluído. De qualquer forma, faça um rolo.

Mapeie as entidades que fazem referência ao cliente como coleções. Nomeie cada coleção em sua classe de clientes com um sufixo específico. Por exemplo, se a entidade do cliente tiver alguns pedidos, nomeie a coleção de pedidos como abaixo:

public virtual ISet<Order> Orders_NHBSet { get; set; } // add "_NHBSet" at the end 

Agora, usando a reflexão, você pode obter todas as propriedades do cliente no tempo de execução e obter essas propriedades que seus nomes terminem com o sufixo definido (neste caso "_nhbset"), verifique cada coleção se eles contiverem algum elemento e, se assim for, evite excluir o cliente.

public static void DeleteCustomer(Customer customer)
{
   using (var session = sessions.OpenSession())
   {
       using (var transaction = session.BeginTransaction())
       {

           var listOfProperties =typeof(Customer).GetProperties();
           foreach (var classProperty in listOfProperties )
           {
                if (classProperty.Name.EndsWith("_NHBSet"))
                {
                    PropertyInfo myPropInfo = typeof(Customer).GetProperty(classProperty.Name);
                    dynamic Collection =  myPropInfo.GetValue(customer, null);
                    if (Enumerable.FirstOrDefault(Collection) !=null)// Check if collection contains any element
                    {
                       MessageBox.Show("Customer Cannot be deleted");
                       return;
                    }   
                }  
            }
            session.Delete(customer);
            transaction.Commit();
      }
   }
}

A vantagem dessa abordagem é que você não precisa alterar seu código mais tarde se adicionar novas coleções à sua classe de clientes. E você não precisa alterar sua consulta SQL como Jaguar sugerido. A única coisa com a qual você deve se importar é adicionar o sufixo específico às suas coleções recém -adicionadas.

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