1

Я привык к использованию столбцов идентификации и создания pk для меня. Мне интересно, есть ли способ охватить столбец идентификатора или иначе вычислить его как уникальный в пределах внешнего ключа?Как выполнить вычисляемые значения столбцов в SQL Server с Entity Framework

Например, используя столбец идентификации таблицы может выглядеть следующим образом:

| 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 позволяет database- генерируемые значения столбцов, такие как идентификация и вычисление. Мой вопрос в том, как можно достичь вычисленного столбца, подобного этому?

Я понимаю, что это можно сделать в приложении, найдя самый высокий WidgetId, который пользователь имеет и увеличивает на единицу. Но это не похоже, что это правильный путь.

Я предполагаю, что это также возможно сделать с помощью триггера, но добавление триггера в db из DbContext будет устанавливать зависимость от РСУБД, не так ли?

Есть ли лучший способ, который требует меньше зависимости от того, какой бэкэнд использует EF?

Обновление

Для того, чтобы быть более ясным, в приведенной выше модели, существует определения отношения между Widget пользователя и по назначению. UserId является первичным ключом таблицы User и, если FK принадлежит пользователю как часть PK Widget, это означает, что Widgets никогда не разделяются между пользователями. Когда пользователь удаляется, все Виджеты удаляются вместе с ним. Запрос Widget от WidgetId не имеет смысла; Для запроса конкретного виджета необходимы параметры UserId и WidgetId.

Я изучаю это, потому что я помню, как читал в Evans DDD, что идентификатор объекта без полномочий root в агрегате должен быть уникальным в совокупности. Выше приведен пример этого случая:

кроме корня ENTITIES имеет локальную идентичность, но идентичность должна быть различимы только в агрегированном, потому что вне объекта никогда не сможет увидеть его из контекста от корня ENTITY. (Domain-Driven Design, Evans)

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

Widget

Я предполагаю, что я не должен был замаскирован этот термин. Фактическим объектом является WorkPlace. Я создаю приложение, чтобы отслеживать, где я работаю в течение года, так как я должен каждый год представлять маршрут с моей формой возврата муниципального налога. Когда закончите, я планирую опубликовать его в облаке, чтобы другие могли использовать его бесплатно. Но, конечно, я хочу, чтобы их WorkPlaces были полностью изолированы от моих.

Пользователи создаются автоматически для каждого посетителя, используя анонимную идентификацию/Request.AnonymousID (позже будет функция регистрации, но нет необходимости пробовать демонстрацию). Веб-приложение будет иметь похожие URL-адреса/рабочие места/1,/work-places/2 и т. Д. Поскольку каждый запрос гарантированно имеет пользователя, идентификатор пользователя не должен быть идентифицирован в URL-адресе. Я могу идентифицировать пользователей от Request.AnonymousID, когда ничего не найдено в User.Identity.Name.

Если WorkPlaceId был столбцом идентификации, сначала мне необходимо проверить действия контроллера, чтобы убедиться, что пользователь владеет рабочим местом перед его отображением. В противном случае я мог бы взломать URL-адрес, чтобы увидеть каждый WorkPlace, который каждый пользователь установил в системе. Сделав WorkPlaceId уникальным только для пользователя, мне не нужно об этом беспокоиться. URL/work-places/1 будет отображать совершенно разные данные для двух разных пользователей.

+0

Если вы используете EF 5, вы должны пометить его этим. – Yuck

+0

Удивительный, последний раз, когда я проверил, он еще не был создан. Спасибо за головы. – danludwig

+0

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

ответ

1

Назначение столбца идентификации - иметь определенный идентификатор в таблице, если вам нужен столбец, который будет уникальным независимо от того, какие значения имеют другие значения столбца, а сервер sql поддерживает автоматическое генерирование только для одного столбца идентификации в таблице (без множественного или сложного первичные автоматически сгенерированные ключи).

Если ваш виджет отличается (даже если тот же самый вид виджетов используется большим числом пользователей), то этот первичный ключ имеет смысл, но он не может быть вычислен на сервере sql (кроме случаев, когда вы используете хранимую процедуру или другую программируемость db для вставки). И вы можете читать только автогенерированный столбец с помощью ef (см. How should I access a computed column in Entity Framework Code First?).

Если, однако больше виджетов типов (в данном примере типы виджетов 1, 2 и 3 EDIT:WidgetId может быть FK для таблицы WidgetType где автогенерируемые Id, так что вы бы PK состоит из 2 автоматически сгенерированный ID), вы можете создать WidgetType (или WidgetInstance, WidgetSomething, что-либо, сущность, которая описывает часть виджета, не затронутую пользовательской) таблицей, и иметь в ней автогенерированный идентификатор и использовать ее в этой таблице как FK и часть первичного ключ. Это позволит вам автоматически генерировать и запускать UserId и WidgetId в момент вставки в эту таблицу.

+0

Я думаю, что это может быть ответ, но вы вроде как потерял меня в третьем абзаце. Я уточнил вопрос, чтобы уточнить. Если такое значение «WidgetId» должно быть вычислено, я слышал, что вы говорите, что это должно произойти на уровне EF или выше, если я не хочу создать sproc/trigger (я этого не делаю). Итак, что такое хороший подход? Я могу запросить идентификатор max widget для пользователя, увеличить его на единицу, установить свойство в сущности и SaveChanges ... но даже если две строки смежны в исходном коде, нет ли шанса конфликта параллелизма, если 2 потоки каждый пытается вставить виджет для одного и того же пользователя одновременно? – danludwig

+0

Да, вам нужно иметь синхронизированный раздел между потоками, который будет выполнять сохранение. Я думаю, что 3-й абзац - лучшее решение, но если один пользователь может иметь 5 экземпляров «одинакового» виджета, тогда это неприменимо. У вас есть что-то общее между некоторыми виджетами (например, имя «Часы» или «Погода»), которые не меняются пользователями? Если нет, и ваша единственная проблема заключается в том, чтобы убедиться, что виджет не существует без пользователя, вы можете сделать только UserId FK NOT NULL, а когда пользователь будет удален, виджет должен быть удален. –

+0

Основная причина, по которой я выбрал «Идентификация отношений», связан с тем, как ведет себя EF, когда вы пытаетесь удалить сущность из коллекции (то есть «User.Widgets.Remove (User.Widgets.First())». По умолчанию , он пытается установить значение FK равным нулю.Если FK не имеет значения NULL, то EF генерирует исключение. Однако если FK является частью PK, тогда EF удаляет сущность вместо этого. – danludwig

0

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

Виджеты и пользователи считаются основными данными. Таблица виджета должна содержать только атрибуты виджета, а таблица пользователя должна содержать только атрибуты пользователя. Если виджет связан с пользователем, вероятно, было бы хорошо включить идентификатор пользователя в таблицу виджета, но виджет-идентификатор должен быть идентификатором, а идентификатор пользователя будет FK.

Связывание виджета и пользователя должно выполняться вне этих основных таблиц. Если они связаны с счетом/счетом-фактурой, обычно используется комбинация таблицы заголовков счетов (invoiceid, userid, datetime, общая стоимость, общая цена ...) и таблица строк счета (invoicelineid, invoiceid, widgetid, unit стоимость, цена единицы, количество ...)

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

+0

Опять же, я думаю, может быть, я должен был быть более ясным в своем вопросе. Для этих двух сущностей нет большого масштаба, это простое личное приложение, над которым я работаю, чтобы ознакомиться с VS11, EF5, MVC4 и т. Д. Для этих двух сущностей существует определенная «1 пользовательская .. виджеты» отношения. Я думаю, вы предлагаете таблицу «gerund» для отображения «m Users .. n Widgets». В моей модели виджет не может существовать без пользователя ... UserId является частью его личности. Я считаю, что я нормализовал его правильно, и добавление отдельной таблицы для сопоставления отношений звучит как осложнение, а не упрощение. – danludwig