2010-11-12 19 views
0

我在倉庫的通用方法,更新常用的屬性在我的EDMX模型中的所有對象:如何在泛型方法中導航子實體框架對象?

private void SetUpdateParams(TEntity entity) 
    { 
     PropertyInfo prop = typeof(TEntity).GetProperty("CommonProperty"); 

     prop.SetValue(entity, "Some Value", null); 
    } 

此屬性是由添加,更新打來電話,刪除方法。例如:

public void Delete(TEntity entity) 
    { 
     SetUpdateParams(entity); 
     _objectSet.DeleteObject(entity); 
     txDB.SaveChanges(); 
    } 

這一切都工作得很好,直到我試圖包括兒童級聯刪除方案。由於我必須使用的sprocs需要設置此特定屬性,因此我現在必須通過關係遞歸併在ObjectSet中的任何加載的子項上設置此屬性。問題是我似乎無法找到任何方式來做到這一點。有沒有人做過這樣的事情?

回答

1

這不是最簡單的解決方案,可能有點乏味,但我通過跨越對象圖,更新屬性,並在我發現它時跟蹤我所執行的項目,已經訪問過。它給了本科計算機科學課程的重大回憶。 =)

本質上,拿你的對象,得到它的屬性,並將它們推入堆棧。對於堆棧中的每個屬性,測試它是否是您要查找的屬性。如果匹配則處理,如果簡單數據類型忽略,則複雜對象添加到堆棧。

一對夫婦的事情,幫助我在我的實現:

  • 創建一個包含你正在尋找物業的接口,並具有屬性的所有類實現它。它使用反射審查你的課程變得更容易。
  • 儘量使用堆棧中要遍歷的複雜屬性的類型。我使用了一個接口來指定這個複雜類型可能有一個需要更新的屬性。
  • 使用一些東西(我使用了一個HashSet)來標記你見過的對象,並幫助防止循環引用遍歷。
  • 我將此功能作爲BaseContext(繼承EF的ObjectContext)自定義Save()方法的一部分。我打電話給這個fixup,然後打電話給base.SaveChanges()

就像我說的,不是一個簡單,直接的問題,但我的代碼處理深層對象圖並更新多個屬性實例。如果有興趣,我可以跟進一些僞代碼。


EDIT/UPDATE

作爲代碼所示,我感興趣的任何數量的可能出現在圖形對象的更新當前用戶名和日期時間值。

注:

  • 接口IInsertedInfo包含我們需要更新的屬性。所以如果一個對象實現IInsertedInfo,我們知道要更新它的屬性。
  • 實現IRequiresCurrentUserDateTime的對象是在圖的某處具有實現IInsertedInfo的對象的對象。
  • 這是在EF保存的情況下完成的。我正在詢問需要保存/更新的對象ObjectStateManager

我不認爲這是最簡潔的解決方案,但它對我來說工作正常。此外,對於IRequiresCurrentUserDateTime和其他過濾器(有些省略清晰的緣故)檢查僅用於保留搜索空間管理,避免跨越.NET對象,非類類型等

private void HandleInsertedUserNames(string userName) 
{ 
    // grab any entity that requires a current user value 
    var requiresUser = this.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified) 
               .Where(ose => ose.Entity is IRequiresCurrentUserDateTime 
                  || ose.Entity is IInsertedInfo) 
               .Select(ose => ose.Entity); 

    var now = DateTime.Now; 
    object current; 
    var seen = new HashSet<object>(); 
    var stack = new Stack<object>(); 
    // for each entity requiring a current user value... 
    foreach (var obj in requiresUser) 
    { 
     // traverse its object graph and update any objects that implement IRequiresCurrentUserDateTime 
     stack.Push(obj); 
     while (stack.Count > 0) 
     { 
      current = stack.Pop(); 
      if (current != null && !seen.Contains(current)) 
      { 
       // mark object as seen 
       seen.Add(current); 
       // if object implements IInsertedInfo, then set its property 
       if (current is IInsertedInfo) 
       { 
        (current as IInsertedInfo).UserName = userName; 
        (current as IInsertedInfo).DateTime = now; 
        // we can continue on to the next object in the stack if we've hit an IInsertedInfo 
        continue; 
       } 

    // REMOVED FILTERING TESTS I USED TO REDUCE SEARCH SPACE (e.g.: is modified?) 

       if (current is IRequiresCurrentUserDateTime) 
       { 
        // push any instance, public properties and push them to the stack 
        current.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public) 
    // HERE YOU CAN FILTER THE PROPERTY COLLECTION VIA WHERE CLAUSES (e.g.: only certain namespace, type, etc) 
          // select the actual value of the property 
          .Select(type => type.GetValue(current, null)) 
          // further filter -- only values NOT already in the requiresUser 
          // list and those that implement IRequiresCurrentUserDateTime or IInsertedInfo 
          .Where(value => !requiresUser.Contains(value) 
              && (value is IRequiresCurrentUserDateTime 
               || value is IInsertedInfo)) 
          .ToList().ForEach(stack.push); 
       } 
      } 
     } 
    } 

} 
+0

HRM - 這是更比我想象的要複雜。想知道成本與價值比率是否太高......但是如果您不介意,我希望看到一些僞代碼。非常感謝這個方向......我永遠不會想到使用界面來完成這個工作! – morganpdx 2010-11-17 18:47:22

+0

用一些代碼更新了我的答案。看一看! – kdawg 2010-11-17 19:14:59

+0

不錯!非常感謝 :) – morganpdx 2010-11-18 20:34:02