2009-12-10 2 views
1

У меня есть следующая ситуация:Как реализовать рекурсивное удаление?

Заказчики содержат проекты и проекты, содержащие лицензии. Хорошо из-за архивирования мы ничего не будем удалять, но вместо этого будем использовать IsDeleted. otherweise Я мог бы использовать каскадное удаление.

Owkay Я работаю с репозиторием шаблона, так что я называю

customerRepository.Delete(customer); 

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

Я хотел бы знать, есть ли подходящее решение для этого. Это должно быть выполнено.

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

Я работаю в среде C#, используя SQL Server 2008 как базу данных.

редактировать: Я использую библиотеки Enterprice для подключения к базе данных

ответ

1

Я рекомендую просто написать хранимую процедуру (или группа хранимых процедур), чтобы инкапсулировать эту логику, которая будет выглядеть примерно так:

update Customer set isDeleted = 1 
where CustomerId = @CustomerId 

/* Say the Address table has a foreign key to customer */ 
update Address set isDeleted = 1 
where CustomerId = @CustomerId 

/* 
    To delete related records that also have child data, 
    write and call other procedures to handle the details 
*/ 
exec DeleteProjectByCustomer(@CustomerId) 

/* ... etc ... */ 

Затем называют эту процедуру с customerRepository.Delete в рамках транзакции.

2

Одним из вариантов было бы сделать это в базе данных с помощью триггеров. Я предполагаю, что другой вариант будет использовать обновление Cascade, но это может не соответствовать тому, как работает ваш домен.

Лично я, вероятно, просто укушу пулю и напишу код C#, чтобы установить для меня значение поля типа IsDeleted (если было одно и только одно приложение, обращающееся к БД).

+0

Да, я думаю, триггеры опасны. Вы можете потерять их. – user29964

+0

Да. Люди иногда «забывают», что триггеры были созданы тоже. – RichardOD

+0

Лично мне нравится идея триггера, поскольку она гарантирует, что в любое время, когда запись установлена, будет удалена, произойдут соответствующие другие изменения. Я думаю, что это безответственно сделать с помощью кода приложения. Он рискует иметь проблемы с целостностью данных. Триггер также может быть установлен для восстановления через все дочерние записи, если кто-то удаляется случайно. – HLGEM

0

Это полностью зависит от вашего DAL. Например, сопоставления NHibernate могут быть настроены на каскадное удаление всех этих связанных объектов без дополнительного кода. Я уверен, что EF имеет нечто подобное. Как вы подключаетесь к своей БД?

Если ваши объекты не сохранены, то .NET GC выметит все объекты проекта, если нет ссылки на них. Я исхожу из вашего вопроса, хотя вы говорите об удалении их из базы данных?

0

Если ваши отношения фиксированы (т. Е. Лицензия всегда связана с проектом и проектом с клиентом), вы можете уйти от того, чтобы вообще не каскадировать обновление. Так как вы уже имеет дело с болью мягких удалений в ваших запросах, вы можете также добавить в боли проверки иерархии:

SELECT [...] FROM License l 
JOIN Project p ON l.ProjectID = p.ID 
JOIN Customer c on p.CustomerID = c.ID 
WHERE l.IsDeleted <> 1 AND p.IsDeleted <> 1 AND c.IsDeleted <> 1 

Это добавит нагрузку производительности только в том случае, если у вас есть вопросы на дочерней таблице, которые не присоединяются к таблицам предков.

У этого есть дополнительная заслуга над каскадным подходом: он позволяет вам восстанавливать элементы без автоматического восстановления их детей.Если я удалю одну из лицензий проекта, удалив проект, а затем удалю проект, каскадный подход потеряет тот факт, что я удалил эту первую лицензию. Такой подход не будет.

В объектной модели, вы бы реализовать это следующим образом:

private bool _IsDeleted; 
public bool IsDeleted 
{ 
    get 
    { 
     return _IsDeleted || (Parent == null) ? false : Parent.IsDeleted; 
    } 
    set 
    { 
     _IsDeleted = value; 
    } 
} 

... хотя вы должны быть осторожны, чтобы фактически хранить личное _IsDeleted значения в базе данных, а не значение IsDeleted.

+0

Да, это было сделано для одноуровневой выборки, но если вы опускаетесь на 2-3 уровня, запросы получают намного больше, чтобы загрузить только один элемент – user29964

Смежные вопросы