Каков наиболее элегантный способ реализации бизнес-правила, касающегося дочерней коллекции в LINQ? - PullRequest
0 голосов
/ 08 августа 2009

В моей базе данных две таблицы:

Wiki
    WikiId
    ...

WikiUser
    WikiUserId (PK)
    WikiId
    UserId
    IsOwner
    ...

Эти таблицы имеют отношение один (Wiki) к многим (WikiUser).

Как мне реализовать следующее бизнес-правило в моих классах сущностей LINQ:

"У Вики должен быть ровно один владелец?"

Я попытался обновить таблицы следующим образом:

Wiki
    WikiId (PK)
    OwnerId (FK to WikiUser)
    ...

WikiUser
    WikiUserId (PK)
    WikiId
    UserId
    ...

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

Есть ли способ выполнить эту проверку перед генерацией SqlException? Лучший способ структурировать мою базу данных? Способ поймать и перевести SqlException во что-то более полезное?

Редактировать: Я бы предпочел сохранить правила валидации в классах сущностей LINQ, если это возможно.

Редактировать 2: Еще несколько подробностей о моей конкретной ситуации.

В моем приложении пользователь должен иметь возможность удалять пользователей из вики. Они должны иметь возможность удалить любого пользователя, кроме пользователя, который в настоящее время помечен как «владелец» вики (у вики всегда должен быть ровно один владелец).

В моей логике управления я хотел бы использовать что-то вроде этого:

wiki.WikiUsers.Remove(wikiUser);
mRepository.Save();

И перенести все нарушенные правила на уровень пользовательского интерфейса.

То, что я не хочу делать, это:

if(wikiUser.WikiUserId != wiki.OwnerId) {
    wiki.WikiUsers.Remove(wikiUser);
    mRepository.Save();
}
else {
    //Handle errors.
}

Я также не особо хочу перемещать код в свой репозиторий (потому что нет ничего, что указывало бы на то, что он не использовал встроенные функции Remove), поэтому я также НЕ хочу, чтобы код был таким:

mRepository.RemoveWikiUser(wiki, wikiUser)
mRepository.Save();

Это было бы приемлемо:

try {
    wiki.WikiUsers.Remove(wikiUser);
    mRepository.Save();
}
catch(ValidationException ve) {
    //Display ve.Message
}

Но это отлавливает слишком много ошибок:

try {
    wiki.WikiUsers.Remove(wikiUser);
    mRepository.Save();
}
catch(SqlException se) {
    //Display se.Message
}

Я бы также НЕ ПРЕДПОЧЛЕНО, чтобы явно вызывать проверку бизнес-правил (хотя это может стать необходимым):

wiki.WIkiUsers.Remove(wikiUser);
if(wiki.CheckRules()) {
    mRepository.Save();
}
else {
   //Display broken rules
}

1 Ответ

0 голосов
/ 08 августа 2009

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

Для чего-то вроде вики, вам, вероятно, не нужен чрезвычайно сложный механизм бизнес-правил просто потому, что ваша проблемная область уже довольно хорошо ограничена. Кроме того, это звучит как правило, которое почти никогда не меняется, поэтому его изоляция в слое формальных правил немного излишня. Таким образом, вы, вероятно, лучше всего поместите такого рода ограничения в слой данных или поставщика данных.

Если вы используете, например, Linq to Sql, вы можете отразить то, что вы сделали в SQL Server, сделав свойство OwnerId в вашей модели непустимым свойством, связанным с помощью таблицы WikiUser. Это обеспечило бы заполнение свойства OwnerId (потому что оно не может иметь значение NULL) и предоставило бы вам уникальное ограничение, которое вы ищете.

...