Как узнать, можно ли удалить указанный объект? - PullRequest
10 голосов
/ 19 февраля 2010

У меня есть объект под названием «Клиент», который будет использоваться в других таблицах в качестве внешних ключей.

Проблема в том, что я хочу знать, можно ли удалить «Клиента» (т. Е. На него нет ссылок в других таблицах).

Возможно ли это с Nhibernate?

Ответы [ 7 ]

5 голосов
/ 25 февраля 2010

То, что вы спрашиваете, - это найти наличие значения Customer PK в столбце FK ссылочных таблиц.Есть много способов сделать это:

  1. , как отметил kgiannakakis, попробуйте выполнить удаление и, если исключение вызвано, откатом.Эффективно, но безобразно и не полезно.Это также требует, чтобы вы установили CASCADE = "RESTRICT" в вашей базе данных.Недостатком этого решения является то, что вы должны попытаться удалить объект, чтобы выяснить, что вы не можете

  2. отобразить объекты, которые ссылаются на Customer, как коллекции, а затем для каждой коллекции, еслиих Count > 0 тогда не разрешают удалять.Это хорошо, потому что это безопасно от изменений схемы, пока сопоставление завершено.Это также плохое решение, потому что нужно будет сделать дополнительный выбор.

  3. Есть метод, который выполняет запрос, подобный bool IsReferenced(Customer cust).Хорошо, потому что у вас может быть один запрос, который вы будете использовать, когда захотите.Не очень хорошо, потому что он может быть подвержен ошибкам из-за изменений схемы и / или домена (в зависимости от типа запроса, который вы будете выполнять: sql / hql / критерии).

  4. A вычисленосвойство класса само с элементом сопоставления типа <property name="IsReferenced" type="long" formula="sql-query that sums the Customer id usage in the referenced tables" />.Хорошо, потому что это быстрое решение (по крайней мере, так же быстро, как ваша БД), без дополнительных запросов.Не очень хорошо, потому что он подвержен изменениям схемы, поэтому, когда вы изменяете свою БД, вы не должны забывать обновить этот запрос.

  5. сумасшедшее решение: создайте представление с привязкой к схеме, которое делает расчет,Сделайте запрос на него, когда захотите.Хорошо, потому что он привязан к схеме и менее подвержен изменениям схемы, хорошо, потому что запрос быстрый, но не очень хороший, потому что вам все еще нужно выполнить дополнительный запрос (или вы отображаете результат этого представления в решении 4).

2,3,4 также хороши тем, что вы также можете проецировать это поведение на свой интерфейс (не разрешать удаление)

Лично я бы пошел на 4,3,5 с этим предпочтением

4 голосов
/ 03 марта 2010

Я хочу знать, можно ли удалить «Клиента» (т. Е. На него нет ссылок ни в каких других таблицах).

В действительности, база данных не несет ответственности за определение возможности удаления Клиента. Это скорее часть вашей бизнес-логики .

Вы просите проверить ссылочную целостность в базе данных.

В мире без ООП все нормально. Но при работе с объектами (как и вы) лучше добавить логику к вашим объектам ( объекты имеют состояние и поведение; БД - только состояние ).

Итак, я бы добавил метод в класс Customer, чтобы определить, можно ли его удалить или нет. Таким образом вы можете правильно (единично) проверить работоспособность .

Например, допустим, у нас есть правило Клиента можно удалить, только если у него нет заказов и он не участвовал в форуме .

Тогда у вас будет объект Customer, подобный этому (простейший возможный случай):

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
        }
    }
}

Это очень чистый и простой дизайн, который прост в использовании, тестировании и не сильно зависит от NHibernate или базовой базы данных.

Вы можете использовать это так:

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

В дополнение к этому вы можете настроить NHibernate для удаления связанных заказов и других ассоциаций, если требуется.


Примечание: конечно, приведенный выше пример является просто простейшим иллюстративным решением. Возможно, вы захотите сделать такое правило частью проверки , которое должно применяться при удалении объекта.

2 голосов
/ 02 марта 2010

Думая в сущностях и отношениях вместо таблиц и внешних ключей, существуют следующие ситуации:

  • Клиент имеет отношение «один ко многим», которое составляет часть клиента, например его телефонные номера. Они также должны быть удалены каскадом.
  • Клиент имеет отношение «один ко многим» или «многие ко многим», которое не является частью клиента, но оно известно / доступно клиенту.
  • Некоторые другие лица имеют отношение к Заказчику. Это также может быть любой тип (который не является внешним ключом в базе данных). Например заказы заказчика. Заказы не известны клиенту. Это самый сложный случай.

Насколько я знаю, прямого решения от NHibernate нет. Существует API-интерфейс метаданных, который позволяет исследовать определения отображений во время выполнения. ИМХО, это неправильный способ сделать это.

По моему мнению, бизнес-логика несет ответственность за проверку, может ли объект быть удален или нет. (Даже если существуют внешние ключи и ограничения, обеспечивающие целостность базы данных, это все равно бизнес-логика).

Мы внедрили сервис, который вызывается перед удалением объекта. Другие части программного обеспечения регистрируются для определенных типов. Они могут наложить вето на удаление (например, выбрасывая исключение).

Например, система заказов регистрирует удаление клиентов. Если клиент должен быть удален, система заказов ищет заказы этого клиента и выдает его, если он нашел.

1 голос
/ 19 февраля 2010

Это невозможно напрямую.Предположительно ваша модель домена включает в себя связанные с Заказчиком объекты, такие как адреса, заказы и т. Д. Для этого следует использовать шаблон спецификации .

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
    }
}

Отредактировано, чтобы добавить:

Возможно, самый простой способ - создать хранимую процедуру, которая выполняет эту проверку, и вызвать ее перед удалением.Вы можете получить доступ к IDbCommand из NHibernate (ISession.Connection.CreateCommand()), так что вызов не зависит от базы данных.

См. Также ответы на этот вопрос .

0 голосов
/ 16 июля 2018

Сопоставьте объекты, которые ссылаются на Клиента, как коллекции. Назовите каждую коллекцию в вашем классе Customer определенным суффиксом. Например, если у вашей сущности Customer есть несколько заказов, назовите коллекцию заказов следующим образом:

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

Теперь, используя Reflection, вы можете получить все свойства Customer во время выполнения и получить те свойства, имена которых оканчиваются определенным вами суффиксом (в данном случае «_NHBSet»). Затем проверьте каждую коллекцию, если они содержат какой-либо элемент, и если да, то избегайте удаление клиента.

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();
      }
   }
}

Преимущество этого подхода заключается в том, что вам не нужно менять свой код позже, если вы добавляете новые коллекции в свой класс клиента. И вам не нужно менять SQL-запрос, как Jaguar предложил . Единственное, о чем вы должны позаботиться, это добавить определенный суффикс в ваши новые добавленные коллекции.

0 голосов
/ 25 февраля 2010

Наивным решением будет использование транзакции. Начните транзакцию и удалите объект. Исключение сообщит вам, что объект не может быть удален. В любом случае сделайте откат.

0 голосов
/ 19 февраля 2010

Возможно, стоит взглянуть на свойство cascade, в частности all-delete-orphan, в ваших файлах hbm.xml, и это может позаботиться об этом за вас.

См. Здесь, 16.3 - Каскадный жизненный цикл

...