实体框架可以轻松跟踪两个实体之间关系的变化。以下内容将返回所有已添加和已删除关系的ObjectStateEntry实例:
ObjectStateManager.GetObjectStateEntries(
EntityState.Added | EntityState.Deleted)
.Where(e => e.IsRelationship);
出于审计目的,我想检索有关每个关联结束的以下信息:
对于添加的关联,ObjectStateEntry实例包含关系中涉及的两个实体的EntityKey对象:
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");
}
欢迎任何建议/想法。
谢谢, 丹尼尔
答案 0 :(得分:3)
不应查看实体键,而应从对象状态条目中提取关联结束。我在丹尼尔西蒙斯的博客http://blogs.msdn.com/b/dsimmons/archive/2008/01/17/ef-extension-methods-extravaganza-part-ii-relationship-entry-irelatedend.aspx中看到了一个暗示。以下是我提出的一些扩展方法(UsableValues也来自Daniel的博客):
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是Daniel博客的另一种扩展方法):
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));
}
}
答案 1 :(得分:0)
选择的答案对我不起作用,这可能是因为我正在使用继承。以下对LeffeBrune的回答的修改在Entity Framework 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));
}
}