2011-06-14 76 views
4

實體框架可以輕鬆跟蹤兩個實體之間關係的更改。下面將返回ObjectStateEntry情況下爲所有加入和刪除的關係:實體框架:跟蹤對獨立關聯的更改

ObjectStateManager.GetObjectStateEntries(
       EntityState.Added | EntityState.Deleted) 
       .Where(e => e.IsRelationship); 

對於審計目的,我想檢索每個關聯端的以下信息:

  • 導航屬性的名稱(如果有的話)
  • 多重性

對於添加的關聯ObjectStateEntry實例包含兩個實體涉及的EntityKey對象d的關係:

var key0 = entry.CurrentValues[0] as EntityKey; 
var key1 = entry.CurrentValues[1] as EntityKey; 

還引用了代表當前關係類型:

var relationshipType = objectStateEntry.EntitySet.ElementType; 

我所試圖做的是地圖中的每個實體按鍵(KEY0,KEY1)以適當末端關聯(relationshipType.KeyMembers)。據我所知,做到這一點的唯一方法是查看每個關聯端的引用類型,並將其與實體關鍵字所表示的實體的類型進行匹配。但是,如果關聯的兩端引用相同的實體類型(認爲僱員實體與經理FK引用另一個員工),則這不起作用。

所以這裏是幫助函數,我到目前爲止獲取導航屬性名稱和關係的多樣性結束。有沒有更好的方法來做同樣的事情?

public static string GetNavigationPropertyName(
     this ObjectStateEntry entry, EntityKey key) 
{ 
    var relationshipType = entry.EntitySet.ElementType; 
    var entitySet = key.GetEntitySet(
      entry.ObjectStateManager.MetadataWorkspace); 
    var property = entitySet.ElementType.NavigationProperties.Where(
      p => p.RelationshipType == relationshipType) 
      .SingleOrDefault(); 

    if (property == null) 
    { 
     return null; 
    } 

    return property.Name; 
} 

public static RelationshipMultiplicity GetAssociationEndMultiplicity(
     this ObjectStateEntry entry, EntityKey key) 
{ 
    var relationshipType = entry.EntitySet.ElementType; 

    var entitySet = key.GetEntitySet(
      entry.ObjectStateManager.MetadataWorkspace); 

    foreach (EdmMember member in relationshipType.KeyMembers) 
    { 
     var refType = member.TypeUsage.EdmType as RefType; 
     if (refType.ElementType == entitySet.ElementType) 
     { 
      var relEndMember = member as RelationshipEndMember; 
      return relEndMember.RelationshipMultiplicity; 
     } 
    } 
    throw new InvalidOperationException(
      "couldn't find association end"); 
} 

歡迎任何建議/想法。

感謝, 丹尼爾

回答

3

而不是看着實體按鍵應該提取協會從對象狀態進入結束。我在Daniel Simmons的博客http://blogs.msdn.com/b/dsimmons/archive/2008/01/17/ef-extension-methods-extravaganza-part-ii-relationship-entry-irelatedend.aspx中看到了一條提示。這裏是我想出了一些擴展方法(UsableValues也從丹尼爾的博客):

public static AssociationEndMember[] GetAssociationEnds(
     this ObjectStateEntry entry) 
{ 
    var fieldMetadata = 
      entry.UsableValues().DataRecordInfo.FieldMetadata; 

    return fieldMetadata.Select(
      m => m.FieldType as AssociationEndMember).ToArray(); 
} 

public static AssociationEndMember GetOtherAssociationEnd(
     this ObjectStateEntry entry, AssociationEndMember end) 
{ 
    end.ValidateBelongsTo(entry); 
    AssociationEndMember[] ends = entry.GetAssociationEnds(); 
    if (ends[0] == end) 
    { 
     return ends[1]; 
    } 
    return ends[0]; 
} 

public static EntityKey GetEndEntityKey(
     this ObjectStateEntry entry, AssociationEndMember end) 
{ 
    end.ValidateBelongsTo(entry); 

    AssociationEndMember[] ends = entry.GetAssociationEnds(); 

    if (ends[0] == end) 
    { 
     return entry.UsableValues()[0] as EntityKey; 
    } 

    return entry.UsableValues()[1] as EntityKey; 
} 

public static NavigationProperty GetNavigationProperty(
     this ObjectStateEntry entry, AssociationEndMember end) 
{ 
    end.ValidateBelongsTo(entry); 

    var otherEnd = entry.GetOtherAssociationEnd(end); 
    var relationshipType = entry.EntitySet.ElementType; 
    var key = entry.GetEndEntityKey(end);    
    var entitySet = key.GetEntitySet(
      entry.ObjectStateManager.MetadataWorkspace); 
    var property = entitySet.ElementType.NavigationProperties.Where(
      p => p.RelationshipType == relationshipType && 
        p.FromEndMember == end && p.ToEndMember == otherEnd) 
      .SingleOrDefault(); 
    return property; 
} 

ValidateBelongsTo擴展方法(UsableValue是丹尼爾的博客另一個擴展方法):

static void ValidateBelongsTo(
     this AssociationEndMember end, ObjectStateEntry entry) 
{ 
    if (!entry.IsRelationship) 
    { 
     throw new ArgumentException("is not a relationship entry", "entry"); 
    } 

    var fieldMetadata = 
      entry.UsableValues().DataRecordInfo.FieldMetadata; 
    if (fieldMetadata[0].FieldType as AssociationEndMember != end && 
     fieldMetadata[1].FieldType as AssociationEndMember != end) 
    { 
     throw new InvalidOperationException(string.Format(
       "association end {0} does not participate in the " + 
       "relationship {1}", end, entry)); 
    } 
} 
+0

您好,我可以似乎找不到ValidateBelongsTo方法。能否請你幫忙。 – SolarX 2011-09-20 09:13:29

+1

我用ValidateBelongsTo更新了帖子。它是一個完整的檢查,但沒有做任何工作。 – LeffeBrune 2011-09-20 19:21:50

0

所選答案沒有爲我工作,這可能是因爲我使用繼承。以下修改LeffeBrune的答案實體框架6.1解決了這個問題對我來說:

public static NavigationProperty GetNavigationProperty(this ObjectStateEntry entry, AssociationEndMember end) 
{ 
    end.ValidateBelongsTo(entry); 

    var otherEnd = entry.GetOtherAssociationEnd(end); 
    var relationshipType = entry.EntitySet.ElementType; 
    var entitySet = end.GetEntityType(); 
    var property = entitySet.NavigationProperties 
          .SingleOrDefault(p => p.RelationshipType == relationshipType && 
                p.FromEndMember == end && 
                p.ToEndMember == otherEnd); 
    return property; 
} 

與原來的問題是,從鍵檢索的EntitySet的是基類的EntitySet的,這意味着一套NavigationProperties不包含子類中的任何屬性。

有我需要的以及其他的東西,這樣以來,我花了很長的時間讓這一切工作,我在這裏張貼其餘爲後人:

internal void GetPropertyNames(ObjectContext ctx, ObjectStateEntry entry, 
           out string fromPropName, out string toPropName, 
           out object sourceEntity, out object destEntity) 
{ 
    var valueCollection = entry.UsableValues(); 

    EntityKey entityKeyFrom = (EntityKey) valueCollection[1]; 
    EntityKey entityKeyTo = (EntityKey) valueCollection[0]; 

    ObjectStateEntry sourceEntry = ctx.ObjectStateManager.GetObjectStateEntry(entityKeyFrom); 
    ObjectStateEntry destEntry = ctx.ObjectStateManager.GetObjectStateEntry(entityKeyTo); 
    sourceEntity = sourceEntry.Entity; 
    destEntity = destEntry.Entity; 

    Debug.Assert(sourceEntity != null || destEntity != null, "Wait, what? One of these MUST exist."); 

    AssociationEndMember[] ends = entry.GetAssociationEnds(); 

    Debug.Assert(ends.Length == 2); 

    NavigationProperty sourceProp = entry.GetNavigationProperty(ends[1]); 
    NavigationProperty destProp = entry.GetNavigationProperty(ends[0]); 

    fromPropName = sourceProp.Name; 
    toPropName = destProp.Name; 

    if (fromPropName == null && toPropName == null) 
    { 
     AssociationSet assoc = (AssociationSet)entry.EntitySet; 
     AssociationType assocType = (AssociationType)assoc.ElementType; 

     throw new Exception(String.Format("Couldn't find at least one property for relationship \"{0}\"", assocType.Name)); 
    } 
}