Как добавить вычисляемый столбец в мою модель EF4? - PullRequest
7 голосов
/ 21 сентября 2010

С учетом таблицы «Пользователь» и таблицы «Вход в систему» ​​в MS SQL 2008:

CREATE TABLE [dbo].[User_User](
    [UserID] [int] IDENTITY(1000,1) NOT NULL,
    [UserName] [varchar](63) NOT NULL,
    [UserPassword] [varchar](63) NOT NULL
)
CREATE TABLE [dbo].[Util_Login](
    [LoginID] [int] IDENTITY(1000,1) NOT NULL,
    [User_UserID] [int] NOT NULL, -- FK REFERENCES [dbo].[User_User] ([UserID])
    [LoginDate] [datetime] NOT NULL,
)

Как настроить объект модели структуры сущности User_User для включения «UserLastLogin»столбец, который возвращает MAX (LoginDate)?

Я знаю, что могу создать модель EF4 на основе представления SQL:

CREATE VIEW [v_User_User]
AS
SELECT 
        [User_User].*, 
        (
                SELECT MAX(LoginDate) 
                FROM [Util_Login] 
                WHERE User_UserID = UserID
        ) AS UserLastLogin
FROM [User_User]

Но есть ли способ, которым я могу просто изменить модель User_User для включения вычисляемого столбца n?

РЕДАКТИРОВАТЬ: Я ищу способ получить пользователя или список , включая дату Max (Util.LastLogin) в одном запросе БД.

Ответы [ 3 ]

5 голосов
/ 21 сентября 2010

Очень хороший вопрос, и да, в EF4 есть идеальный способ сделать это:

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

Вот шаги:
Сначала создайте частичный класс и определите для него настраиваемое свойство (Для простоты я предположил, что User_User таблица была сопоставлена ​​с классом User и Util_Login to Util)

public partial class User {
    public DateTime LastLoginDate { get; set; }
}

Итак, как вы можете видеть здесь, вместо создания свойства LastLoginDate в модели, которое потребуется для сопоставления с хранилищем данных, мысоздали свойство в частичном классе, и затем мы можем заполнить его во время материализации объекта или по требованию , если вы неНе верьте, что каждый объект сущности должен будет предоставить эту информацию.

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

Для этого мы собираемся использовать Событие ObjectContext.ObjectMaterialized , которое вызывается каждый раз, когда данные возвращаются из запроса, поскольку ObjectContextсоздание объектов объектов из этих данных.Событие ObjectMaterialized - это вещь Entity Framework 4.Поэтому все, что нам нужно сделать, это создать обработчик события и подписать его на событие ObjectMaterialized.

Лучшее место для размещения этого кода (подписка на событие) - метод OnContextCreated .Этот метод вызывается конструктором объекта контекста и перегружает конструктор, который является частичным методом без реализации, это просто сигнатура метода, созданная генератором кода EF.

Хорошо, теперь вам нужно создать частичный класс для вашего ObjectContext.(Я предполагаю, что имя UsersAndLoginsEntities ) и подписываю обработчик событий (я назвал его Context_ObjectMaterialized ) на ObjectMaterialized Event .

public partial class UsersAndLoginsEntities {
    partial void OnContextCreated() {
        this.ObjectMaterialized += Context_ObjectMaterialized;
    }
}

Последним шагом (реальной работой) будет реализация этого обработчика для фактического заполнения пользовательского свойства для нас, что в этом случае очень просто:

void Context_ObjectMaterialized(object sender, ObjectMaterializedEventArgs args) 
{
    if (args.Entity is User) {        
        User user = (User)args.Entity;
        user.LastLoginDate = this.Utils
                .Where(u => u.UserID == user.UserID)
                .Max(u => u.LoginDate);
    }
}


Надеюсь, это поможет.

1 голос
/ 23 мая 2012

У меня просто была ситуация, когда мне нужно было считать свойства для двух связанных сущностей без загрузки коллекций.Одна вещь, которую я обнаружил, заключается в том, что вам нужно иметь MultipleActiveResultSets = True в строке подключения, чтобы избежать возникновения исключения в обработчике событий ObjectMaterialized при запросе других коллекций сущностей.

1 голос
/ 24 сентября 2010

После долгих размышлений я получил следующее решение:

Сначала создайте представление, содержащее все поля пользователя и поле даты LastLogin (из моего исходного сообщения).

После добавления пользователя (назовите его User_Model ) и представления пользователя (назовите его UserView_Model ) к моей модели EF, я создал класс-оболочку (назовите его User_Wrapper) ) вокруг User_Model и добавлено дополнительное свойство DateTime для LastLogin.

Я изменил класс User_Wrapper для выборки из UserView_Model, а затем заполнил базовую User_Model, отразив все свойства, общие для User_Model и UserView_Model. Наконец, я установил свойство User_Wrapper.LastLogin на основе извлеченного User_View.

Все остальные функции (Создать, Обновить, Удалить ...) работают с User_Model. Только Fetch использует UserView_Model.


Что все это делало? Теперь у меня есть только один вызов базы данных для заполнения одного User_Wrapper или List .

Недостатки? Я предполагаю, что, поскольку мой UserView_Model не имеет каких-либо связанных отношений, я не смог бы выполнить какую-либо активную загрузку, используя EF ObjectContext. К счастью, в моей ситуации я не считаю это проблемой.

Есть ли лучший способ?

...