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

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

问题描述:

鉴于MS SQL 2008中的“用户”表和“登录”表:如何将计算列添加到我的EF4模型?

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)?

我知道我可以创建一个围绕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模式,包括计算columnn的方法吗?

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

非常好的问题,是的,有在EF4完成这个完美的方式:

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

步骤如下:
首先创建一个部分类并在其上定义自定义属性(为了简单起见,我假定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)至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); 
    } 
} 


希望这有助于。

+0

感谢您的详细回复!我看到的一个问题是,它需要一个SQL查询来为每个用户设置LastLoginDate。例如如果我有一个包含50个用户的列表,这意味着1个查询来填充列表和50个额外的查询来填充每个用户的LastLoginDate。或者我误解了ObjectMaterialized事件? – uhleeka 2010-09-21 08:30:18

+1

这是正确的,触发列表中每个用户对象的事件。如果这不合乎要求,那么另一种选择是急切地加载Util导航属性,然后在客户端对其执行Max()。 – 2010-09-21 15:49:31

+0

是的,我看着急切的加载了一点...不会有一个渴望加载的Util返回与用户相关的所有Util行吗?所以基本上,一个客户端的Max()会抛弃除第一行之外的所有内容。 – uhleeka 2010-09-21 18:57:03

想来想去,我结束了以下解决方案:

首先,创建一个包含所有用户的字段加上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或列表<User_Wrapper>。

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

有没有更好的方法?

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