如何将计算列添加到EF4模型中?

时间:2010-09-20 22:47:38

标签: sql-server entity-framework entity-framework-4 calculated-columns

在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 实体框架模型对象以包含返回MAX(LoginDate)的“UserLastLogin”列?

我知道我可以围绕SQL视图创建EF4模型:

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模型以包含计算出的列?

编辑:我正在寻找一种获取用户或列表的方法<用户>包括单个数据库查询中的Max(Util.LastLogin)日期。

3 个答案:

答案 0 :(得分:5)

非常好的问题,是的,在EF4中有一个完美的方法:

自定义属性 是一种向实体提供计算属性的方法。好消息是,自定义属性不一定需要从同一个实体上的其他现有属性计算,通过我们即将看到的代码,它们可以从我们喜欢的任何东西计算出来!

以下是步骤:
首先创建一个分部类并在其上定义一个自定义属性(为简单起见,我假设 User_User 表已映射到User类,而 Util_Login 已映射到Util)

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

所以,正如你在这里看到的,不是在模型中创建一个LastLoginDate属性,而是需要映射回数据存储,我们在partial类中创建了属性,然后我们可以选择填充在 对象实现 按需 期间,如果您不相信每个实体对象都需要提供信息。

在您的情况下,为每个实现的 User 预先计算 LastLoginDate 自定义属性非常有用,因为我认为将为所有(或至少大多数)实体实体访问此值。否则,您应该考虑仅在需要时而不是在对象实现期间计算属性。

为此,我们将利用ObjectContext.ObjectMaterialized Event,这是从查询返回数据时引发的,因为ObjectContext正在从该数据创建实体对象。 ObjectMaterialized事件是实体框架4的事情。 所以我们需要做的就是创建一个事件处理程序并将其订阅到ObjectMaterialized Event。

放置此代码(订阅事件)的最佳位置是 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 :(得分:1)

经过深思熟虑,我最终得到了以下解决方案:

首先,创建一个包含所有用户字段和LastLogin日期字段的视图(来自我的原始帖子)。

将用户(称之为 User_Model )和用户视图(称之为 UserView_Model )添加到我的EF模型后,我创建了一个包装类(称之为 User_Wrapper )围绕User_Model并为LastLogin添加了额外的DateTime属性。

我修改了User_Wrapper类以从UserView_Model获取,然后通过反映User_Model和UserView_Model之间共享的所有属性来填充底层User_Model。最后,我根据获取的User_View设置了User_Wrapper.LastLogin属性。

所有其他功能(创建,更新,删除...)在User_Model上运行。只有Fetch使用UserView_Model。


这一切做了什么?我现在只有一个数据库调用来填充单个User_Wrapper或List< User_Wrapper>。

缺点?我想这是因为我的UserView_Model没有任何关联关系,我将无法使用EF ObjectContext进行任何急切的加载。幸运的是,在我的情况下,我认为这不是一个问题。

有更好的方法吗?

答案 2 :(得分:1)

我只是遇到了两个相关实体需要计数属性而不加载集合的情况。我发现的一件事是你需要在连接字符串中使用MultipleActiveResultSets = True,以避免在查询其他实体集时在ObjectMaterialized事件处理程序上抛出异常。