7

给定 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) 日期。

4

3 回答 3

5

非常好的问题,是的,在 EF4 中有一个完美的方法来实现这一点:

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

以下是步骤:
首先创建一个部分类并在其上定义一个自定义属性(为简单起见,我假设User_User表已映射到 User 类,Util_Login到 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);
    }
}


希望这可以帮助。

于 2010-09-21T02:09:55.487 回答
1

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

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

将用户(称为User_Model)和用户视图(称为UserView_Model)添加到我的 EF 模型后,我在 User_Model 周围创建了一个包装类(称为User_Wrapper)并为 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 进行任何急切加载。幸运的是,在我的情况下,我认为这不是问题。

有没有更好的办法?

于 2010-09-23T20:01:19.210 回答
1

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

于 2012-05-23T08:15:31.003 回答