Как добавить вычисляемый столбец в мою модель EF4?

Учитывая таблицу "Пользователь" и таблицу "Вход" в 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 View:

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, чтобы включить вычисляемый столбец?

EDIT: Я ищу способ получить пользователя или список <User> включая дату Max (Util.LastLogin) в одном запросе db.

Ответ 1

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

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

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

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

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

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

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

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

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

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


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

Ответ 2

После долгих обсуждений я получил следующее решение:

Сначала создайте представление, содержащее все поля пользователя, плюс поле даты 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.

Все остальные функции (Create, Update, Delete...) работают с User_Model. Только Fetch использует UserView_Model.


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

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

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

Ответ 3

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