Я привык использовать столбцы идентификаторов и создавать для меня pk. Мне интересно, есть ли способ ограничить столбец идентификаторов или иным образом вычислить его, чтобы он был уникальным во внешнем ключе?
Например, при использовании столбца идентификаторов таблица может выглядеть так:
| UserId | WidgetId | <-- Table Widget, WidgetId is identity column & pk
---------------------
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 4 |
| 2 | 5 |
| 2 | 6 |
Что, если я хочу добиться чего-то более похожего на следующее:
| UserId | WidgetId | <-- Table Widget, both UserId and WidgetId compose the pk
---------------------
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
Я знаю, что EF позволяет использовать значения столбцов, сгенерированные базой данных, такие как идентификатор и вычисляемый. Мой вопрос: как можно получить такой вычисляемый столбец?
Я понимаю, что это можно сделать в приложении, найдя самый высокий WidgetId, который есть у пользователя, и увеличив его на единицу. Но это не похоже на правильный путь.
Я предполагаю, что это также возможно сделать с помощью триггера, но добавление триггера в базу данных из DbContext
приведет к зависимости от СУБД, верно?
Есть ли лучший способ, что-то, что требует меньшей зависимости от того, какой серверный EF использует?
Обновить
Чтобы быть более ясным, в приведенной выше модели намеренно существует идентифицирующая связь между виджетом и пользователем. UserId является первичным ключом таблицы User, и наличие FK для пользователя как части PK виджета означает, что виджеты никогда не передаются пользователям. Когда пользователь удаляется, все виджеты удаляются вместе с ним. Запрашивать виджеты только по WidgetId не имеет смысла; Для запроса конкретного виджета необходимы оба параметра UserId и WidgetId.
Я изучаю это, потому что помню, как читал в Evans DDD, что идентификаторы некорневых объектов в агрегате должны быть уникальными только в пределах агрегата. Выше приведен пример этого случая:
СУЩНОСТИ, отличные от корневой, имеют локальную идентичность, но эта идентичность должна быть различима только внутри АГРЕГАТА, потому что ни один внешний объект никогда не сможет увидеть ее вне контекста корневой СУЩНОСТИ. (Domain-Driven Design, Эванс)
Обновление 2
После ответа Горгана я реализовал в своем классе фабрики виджетов что-то вроде следующего. Является ли это правильным подходом для достижения желаемого результата?
public class WidgetFactory : BaseFactory
{
private object _sync = new object();
internal WidgetFactory(IWriteEntities entityWriter) : base(entityWriter)
{
// my DbContext class implements the IWriteEntities interface
}
public Widget CreateOrUpdate(User user, int? widgetId, string prop1,
bool prop2, string prop3)
{
Widget widget = null;
if (!widgetId.HasValue)
{
// when widgetId is null do a create (construct & hydrate), then
EntityWriter.Insert(widget); // does DbEntityEntry.State = Added
}
else
{
// otherwise query the widget from EntityWriter to update it, then
EntityWriter.Update(widget); // does DbEntityEntry.State = Modified
}
// determine the appropriate WidgetId & save
lock (_sync)
{
widget.WidgetId = widgetId.HasValue ? widget.WidgetId
: EntityWriter.Widgets.Where(w => w.UserId == user.Id)
.Max(w => w.WidgetId) + 1;
EntityWriter.SaveChanges(); // does DbContext.SaveChanges()
}
return widget;
}
}
Виджет
Я полагаю, что не должен был маскировать этот термин. Фактическим объектом является WorkPlace. Я создаю приложение, чтобы отслеживать, где я работаю в течение года, так как я должен каждый год отправлять маршрут вместе с формой возврата муниципального налога. Когда закончу, я планирую опубликовать его в облаке, чтобы другие могли пользоваться им бесплатно. Но, конечно же, я хочу, чтобы их рабочие места были полностью изолированы от моих.
Пользователи автоматически создаются для каждого посетителя с использованием анонимной идентификации / Request.AnonymousID
(позже будет функция регистрации, но нет необходимости опробовать демоверсию). Веб-приложение будет иметь спокойные URL-адреса, такие как /work-places/1, /work-places/2 и т. д. Поскольку каждый запрос гарантированно имеет пользователя, идентификатор пользователя не нужно указывать в URL-адресе. Я могу идентифицировать пользователей из Request.AnonymousID
, когда в User.Identity.Name
ничего нет.
Если бы WorkPlaceId был столбцом идентификации, мои действия контроллера сначала должны были бы проверить, чтобы убедиться, что пользователь владеет рабочим местом, прежде чем отображать его. В противном случае я мог бы взломать URL-адрес, чтобы увидеть каждое рабочее место, которое каждый пользователь настроил в системе. Сделав WorkPlaceId уникальным только для пользователя, мне не нужно об этом беспокоиться. URL-адрес /work-places/1 будет отображать совершенно разные данные для двух разных пользователей.
WorkPlace
. Я обновил свой вопрос. Спасибо за вашу помощь. - person danludwig   schedule 17.03.2012