Я привык использовать столбцы идентификаторов и генерировать для меня 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.
Я исследую это, потому что вспоминаю чтение в DDD Эванса о том, что некорневые идентификаторы сущностей в совокупности должны быть уникальными только в пределахагрегат.Выше приведен пример этого случая:
ЛИЦА, отличные от корня, имеют локальную идентичность, но эта идентичность должна различаться только внутри AGGREGATE, потому что никакой внешний объект никогда не сможет увидеть ее внеконтекст корневой сущности.(Domain-Driven Design, Evans)
Обновление 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-адреса, такие как / рабочие места / 1, / рабочие места / 2 и т. Д. Поскольку в каждом запросе гарантированно есть пользователь, идентификатор пользователя не обязательно указывать в URL-адресе.Я могу идентифицировать пользователей по Request.AnonymousID
, когда ничего не присутствует в User.Identity.Name
.
Если бы WorkPlaceId был столбцом идентификаторов, мои действия контроллера сначала должны были бы проверить, чтобы убедиться, что пользователь владеет рабочим местом, прежде чем отображать его.В противном случае я мог бы взломать URL, чтобы увидеть все WorkPlace, которые каждый пользователь настроил в системе.Делая WorkPlaceId уникальным только для пользователя, мне не нужно беспокоиться об этом.URL / рабочие места / 1 будет отображать совершенно разные данные для 2 разных пользователей.