Установка внешних ключей в Linq для SQL - PullRequest
9 голосов
/ 26 января 2009

Хорошо известно, что вы не можете установить идентификаторы внешнего ключа непосредственно в Linq для SQL, если объекты уже были загружены. Однако вы можете искать сущность по ее внешнему ключу, а затем установить сущность как внешнюю сущность, используя отношение сущности. (Я вынул перечисление здесь и использовал целочисленные значения для простоты). Т.е. если у меня есть загруженный объект Appointment и связанный объект AppoinmentStatus, я не могу сделать это: -

ExistingAppointment.AppointmentStatusID = 7

Но я могу сделать это: -

ExistingAppointment.AppointmentStatus = (From appstat In db.AppointmentStatus _
                                        Where appstat.StatusID = 7 _
                                        Select appstat).Single

У меня такая штуковина засоряет мой код, и я бы хотел провести рефакторинг. Итак ...

Я, очевидно, мог бы использовать вспомогательный метод в модуле, подобном этому: -


Module Helper
    Public Shared Function GetAppointmentStatus(ByVal AppStatusID As Integer) As AppointmentStatus
        GetAppointmentStatus = (From appstat In db.AppointmentStatus _
                                       Where appstat.AppointmentStatusID = AppStatus _
                                       Select appstat).Single
    End Function
End Module

Я мог бы даже превратить это в метод расширения, как этот.


Imports System.Runtime.CompilerServices
Module Helper
Extension()> _
    Public Shared Function GetAppointmentStatus(ByVal db as DataClassesDataContext, ByVal AppStatusID As Integer) As AppointmentStatus
        GetAppointmentStatus = (From appstat In db.AppointmentStatus _
                                       Where appstat.AppointmentStatusID = AppStatusID _
                                       Select appstat).Single
    End Function
End Module

Я мог бы также поместить это в частичный класс Linq to SQL, как это.


Partial Public Class DataClassesDataContext    
    Public Function GetAppointmentStatus(ByVal AppStatusID As Integer) As AppointmentStatus
        GetAppointmentStatus = (From appstat In Me.AppointmentStatus _
                                       Where appstat.AppointmentStatusID = AppStatusID _
                                       Select appstat).Single
    End Function
End Class

Далее я мог бы поместить код в частичный класс сущности Linq to SQL Appointment Entity, например: -


Partial Public Class Appointment    
    Public Function GetAppointmentStatus(ByVal db as DataClassesDataContext, ByVal AppStatusID As Integer) As AppointmentStatus
            GetAppointmentStatus = (From appstat In db.AppointmentStatus _
                                       Where appstat.AppointmentStatusID = AppStatusID _
                                       Select appstat).Single
    End Function
End Class

Что мне делать и почему, или есть лучшая альтернатива?

Ответы [ 3 ]

4 голосов
/ 31 января 2010

Существует две основные мысли об этом:

  1. Поместите логику в DataContext (частичный класс или фактический класс, если вы кодируете свой DataContext вручную). Это объясняется тем, что ваш DataContext уже знает обо всех ваших различных сущностях, так что это не создает никакой дополнительной связи и не приводит к раздутию классов.

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

  2. Поместите логику в класс репозитория, т.е. AppointmentRepository. Два преимущества этого подхода: (а) возможность использовать внедрение зависимостей в хранилище и инфраструктуру IoC, если вы решите изменить свою модель данных, и (б) тот факт, что вы придерживаетесь Single Принцип ответственности - на самом деле имеет смысл смысл , чтобы метод находился там, где он есть.

    Основными недостатками их размещения в репозиториях являются: (а) они могут дублировать очень похожую логику, которая уже есть в ваших DataContext в качестве хранимых процедур; (б) у них есть способ создать головную боль, когда дело доходит до управления транзакциями (если вы также используете их для сохранения); и (c) когда вы начинаете иметь множество пользовательских запросов, которые возвращают DTO со специально подобранными для определенных операций или отчетами, у вас остается два непристойных варианта: создать один репозиторий для каждого DTO или создать один мастер. «служебный» репозиторий для всех DTO или какой-то их слабо связанной группы. Оба заканчивают тем, что были довольно плохим дизайном.

Это компромиссы; только вы можете решить, что лучше для ваших собственных целей.

I будет определенно рекомендовать против подхода с расширением, так как методы расширения трудно обнаружить (вы не можете просто набрать метод и заставить Intellisense подобрать соответствующую ссылку) ), и они также просто не нужны, если у вас есть возможность напрямую изменять или расширять (через частичные) исходный класс.

Я бы также советовал не расширять класс Appointment; одна из причин, по которой мы используем такие инструменты, как Linq To SQL, заключается в том, что мы можем иметь дело с POCO-сущностями, которым не нужно знать что-либо о том, откуда они пришли. По этой причине я лично очень против связи классов сущностей с их DataContext - зависимость должна быть только односторонней.

1 голос
/ 31 января 2010

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

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

0 голосов
/ 10 января 2010

Обычно установщик AppointmentStatusId выглядит следующим образом: (код на C #, но его должно быть легко получить)

[Column(Storage="_AppointmentStatusId", DbType="Int")]
public System.Nullable<int> AppointmentStatusId
{
    get
    {
        return this._AppointmentStatusId;
    }
    set
    {
        if ((this._AppointmentStatusId != value))
        {
            if (this._AppointmentStatus.HasLoadedOrAssignedValue)
            {
                throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
            }
            this.OnCapabilityIdChanging(value);
            this.SendPropertyChanging();
            this._AppointmentStatusId = value;
            this.SendPropertyChanged("AppointmentStatusId");
            this.OnCapabilityIdChanged();
        }
    }
}

Итак, на вашем месте я бы добавил метод SetAppointmentStatusId (int statusId) к частичному файлу, который поставляется вместе с созданным дизайнером, и добавлял больше при необходимости для других свойств. Вы даже можете сделать установщик свойства частным (в конструкторе выберите свойство и нажмите F4, чтобы в окне свойств VS изменить доступность).
В этом методе я бы написал код, идентичный установщику, за исключением того, что он НЕ включает эту часть:

            if (this._AppointmentStatus.HasLoadedOrAssignedValue)
            {
                throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
            }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...