Как сделать вычисляемые значения столбцов в SQL Server с Entity Framework - PullRequest
1 голос
/ 15 марта 2012

Я привык использовать столбцы идентификаторов и генерировать для меня 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 разных пользователей.

Ответы [ 2 ]

1 голос
/ 16 марта 2012

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

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

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

0 голосов
/ 16 марта 2012

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

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

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

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

...