7

鑑於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)日期。

回答

5

非常好的問題,是的,有在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

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或列表<User_Wrapper>。

缺點?我想這是因爲我的UserView_Model沒有任何關聯關係,所以我無法使用EF ObjectContext進行任何急切的加載。幸運的是,在我的情況下,我不認爲這是一個問題。

有沒有更好的方法?

1

我剛剛有一種情況,我需要兩個相關實體的計數屬性而不加載集合。我發現的一件事是,您需要在連接字符串中使用MultipleActiveResultSets = True,以避免查詢其他實體集合時在ObjectMaterialized事件處理程序上拋出異常。