简体   繁体   English

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

[英]How do I add a calculated column to my EF4 model?

Given a "User" table and a "Login" table in MS SQL 2008: 给定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,
)

How do I adjust my User_User entity framework model object to include a "UserLastLogin" column that returns a MAX(LoginDate)? 如何调整User_User实体框架模型对象以包含返回MAX(LoginDate)的“UserLastLogin”列?

I know that I can create an EF4 model around a SQL View: 我知道我可以围绕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]

But is there a way that I can just modify the User_User model to include the calculated columnn? 但有没有办法可以修改User_User模型以包含计算的列?

EDIT: I am looking for a way to fetch a User or a List<User> including the Max(Util.LastLogin) date in a single db query. 编辑:我正在寻找一种方法来获取用户或列表<用户>包括单个数据库查询中的最大(Util.LastLogin)日期。

Very good question, and Yes, there is a perfect way to accomplish this in EF4: 非常好的问题,是的,在EF4中有一个完美的方法:

Custom properties are a way to provide computed properties to entities. 自定义属性是一种向实体提供计算属性的方法。 The good news is that Custom properties don't necessarily need to be calculated from other existing properties on the very same entity, by the code we are about to see, they can computed from just about anything we like! 好消息是,自定义属性不一定需要从同一个实体上的其他现有属性计算,通过我们即将看到的代码,它们可以从我们喜欢的任何东西计算出来!

Here are the steps: 以下是步骤:
First create a partial class and define a custom property on it (For simplicity, I assumed User_User table has been mapped to User class and Util_Login to Util) 首先创建一个部分类并在其上定义一个自定义属性(为简单起见,我假设User_User表已映射到User类, Util_Login已映射到Util)

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

So, as you can see here, rather than creating a LastLoginDate property in the model, which would be required to map back to the data store, we have created the property in the partial class and then we have the option to populate it during object materialization or on demand if you don't believe that every entity object will need to provide that information. 所以,正如你在这里看到的,不是在模型中创建LastLoginDate属性,而是需要映射回数据存储,我们在partial类中创建了属性,然后我们可以选择在对象期间填充它如果您不相信每个实体对象都需要提供该信息,则实现按需提供。

In your case precalculating the LastLoginDate custom property for every User being materialized is useful since I think this value will be accessed for all (or at least most) of the entities being materialized. 在您的情况下,预先计算每个正在实现的用户LastLoginDate自定义属性是有用的,因为我认为将为所有(或至少大多数)实体实现此值。 Otherwise, you should consider calculating the property only as needed and not during object materialization. 否则,您应该考虑仅在需要时而不是在对象实现期间计算属性。

For that, we are going to leverage ObjectContext.ObjectMaterialized Event which is raised anytime data is returned from a query since the ObjectContext is creating the entity objects from that data. 为此,我们将利用ObjectContext.ObjectMaterialized Event ,该事件是从查询返回数据时引发的,因为ObjectContext正在从该数据创建实体对象。 ObjectMaterialized event is an Entity Framework 4 thing. ObjectMaterialized事件是实体框架4的事情。 So all we need to do is to create an event handler and subscribe it to the ObjectMaterialized Event. 所以我们需要做的就是创建一个事件处理程序并将其订阅到ObjectMaterialized Event。

The best place to put this code (subscribing to the event) is inside the OnContextCreated Method . 放置此代码(订阅事件)的最佳位置是OnContextCreated方法 This method is called by the context object's constructor and the constructor overloads which is a partial method with no implementation, merely a method signature created by EF code generator. 此方法由上下文对象的构造函数和构造函数重载调用,这是一个没有实现的部分方法,只是由EF代码生成器创建的方法签名。

Ok, now you need to create a partial class for your ObjectContext. 好的,现在您需要为ObjectContext创建一个分部类。 (I assume the name is UsersAndLoginsEntities ) and subscribe the event handler (I named it Context_ObjectMaterialized ) to ObjectMaterialized Event . (我假设名称是UsersAndLoginsEntities )并将事件处理程序(我将其命名为Context_ObjectMaterialized )订阅到ObjectMaterialized Event

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

The last step (the real work) would be to implement this handler to actually populate the Custom Property for us, which in this case is very easy: 最后一步(实际工作)将实现此处理程序以实际填充我们的自定义属性,在这种情况下非常简单:

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


Hope this helps. 希望这可以帮助。

I just had a situation where I needed count properties for two related entities without loading the collections. 我只是有一种情况,我需要计算两个相关实体的属性而不加载集合。 One thing I found out is that you need to have MultipleActiveResultSets=True in the connection string to avoid an exception being thrown on the ObjectMaterialized eventhandler when querying other entitycollections. 我发现的一件事是你需要在连接字符串中使用MultipleActiveResultSets = True,以避免在查询其他实体集时在ObjectMaterialized事件处理程序上抛出异常。

After much deliberation, I ended up with the following solution: 经过深思熟虑,我最终得到了以下解决方案:

First, create a view containing all User fields plus a LastLogin date field (from my original post). 首先,创建一个包含所有用户字段和LastLogin日期字段的视图(来自我的原始帖子)。

After adding the user (call it User_Model ) and the user view (call it UserView_Model ) to my EF model, I created a wrapper class (call it User_Wrapper ) around the User_Model and added an additional DateTime property for LastLogin. 在将用户(称为User_Model )和用户视图(称为UserView_Model )添加到我的EF模型后,我在User_Model周围创建了一个包装类(称为User_Wrapper ),并为LastLogin添加了一个额外的DateTime属性。

I modifed the User_Wrapper class to fetch from the UserView_Model, and then populate the underlying User_Model by reflecting over all the properties shared between the User_Model and UserView_Model. 我修改了User_Wrapper类以从UserView_Model获取,然后通过反映User_Model和UserView_Model之间共享的所有属性来填充底层User_Model。 Finally, I set the User_Wrapper.LastLogin property based on the fetched User_View. 最后,我根据获取的User_View设置User_Wrapper.LastLogin属性。

All other functions (Create,Update,Delete...) operate on the User_Model. 所有其他功能(创建,更新,删除...)在User_Model上运行。 Only the Fetch uses the UserView_Model. 只有Fetch使用UserView_Model。


What did all this do? 这一切做了什么? I now only have one database call to populate a single User_Wrapper or a List<User_Wrapper>. 我现在只有一个数据库调用来填充单个User_Wrapper或List <User_Wrapper>。

The drawbacks? 缺点? I guess that because my UserView_Model does not have any associated relationships, I would not be able to do any eager loading using the EF ObjectContext. 我想这是因为我的UserView_Model没有任何关联关系,我将无法使用EF ObjectContext进行任何急切的加载。 Fortunately, in my situation, I don't find that to be an issue. 幸运的是,在我的情况下,我发现这不是一个问题。

Is there a better way? 有没有更好的办法?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM