我正在使用EF 4.0自我跟踪实体,我发现导航对象没有IsLoaded属性,这些属性参与标准EF对象上的多对多关系。因此,如果您查询Person并且不包含地址,那么person.Addresses会显示一个空列表,但是无法判断地址是否已加载或该人员是否没有任何地址。
有没有办法判断自我跟踪实体是否加载了导航属性?
如果没有办法从ObjectContext访问当前的ObjectQuery,以便我可以看到用户尝试扩展哪些属性并创建自定义IsLoaded属性?
答案 0 :(得分:1)
不幸的是,我在HandleObjectMaterialized方法(从ObjectMaterialized事件中调用)中自我跟踪实体上填充IsLoaded属性的原始计划没有按预期工作,因为多个集合仅在事件之后填充(请参阅{ {3}}帖子)。我想迭代它正在跟踪的每个实体的上下文中的关系,测试IsLoaded属性并在我的自我跟踪实体上设置相应的IsLoaded属性。
因此,我为First()和ToList()创建了名为FirstWithLoaded()和ToListWithLoaded()的扩展方法,以便将其用作反射:
public static T FirstOrDefaultWithLoaded<T>(this IQueryable<T> source) where T : new()
{
T result = default(T);
if (source != null)
{
//Call the base FirstOrDefault
result = source.FirstOrDefault();
var querySource = source as ObjectQuery<T>;
if (querySource != null)
{
PopulateIsLoaded(result, querySource.Context);
}
}
return result;
}
private static void PopulateIsLoaded(object inputEntity, ObjectContext dataContext)
{
var entry = dataContext.ObjectStateManager.GetObjectStateEntry(inputEntity);
//var relationShipManagerProperty = entryType.GetProperty("RelationshipManager");//.GetValue(entityType, null);
var relationShipManager = GetPropertyValue(entry, "RelationshipManager");// relationShipManagerProperty.GetValue(entry, null);
if (relationShipManager != null)
{
//get the relationships (this is a sealed property)
var relationships = GetPropertyValue(relationShipManager, "Relationships") as IEnumerable<RelatedEnd>;
if (relationships != null)
{
foreach (RelatedEnd relationship in relationships)
{
//check to see whether the relationship is loaded
var isLoaded = GetRelatedEndPropertyValue(relationship, "IsLoaded");
if (isLoaded != null && (bool)isLoaded)
{
//if the relationship is loaded then set the
//<NavigationPropertyName>IsLoaded on entry to true
var navigationProperty = GetRelatedEndPropertyValue(relationship, "NavigationProperty");
var identity = GetPropertyValue(navigationProperty, "Identity");
//get the IsLoaded property on entry
var isLoadedProperty = entry.Entity.GetType().GetProperty(identity + "IsLoaded");
if (isLoadedProperty != null)
{
isLoadedProperty.SetValue(entry.Entity, true, null);
}
}
}
}
}
}
private static object GetPropertyValue(object inputObject, string propertyName)
{
object result = null;
if (inputObject != null)
{
var property = inputObject.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (property != null)
{
result = property.GetValue(inputObject, null);
}
}
return result;
}
private static object GetRelatedEndPropertyValue(RelatedEnd inputObject, string propertyName)
{
object result = null;
if (inputObject != null)
{
PropertyInfo property = null;
property = inputObject.GetType().GetProperty(propertyName, BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (property != null)
{
result = property.GetValue(inputObject, null);
}
}
return result;
}
这个解决方案略显失望,我必须访问密封属性“NavigationProperty”,然后访问NavigationProperty.Identity才能获得正确的导航(例如Person.Addresses而不是Person.Address)。希望未来能有更优雅的东西出现。
请注意,为了使其正常工作,我更新了我的Types T4模板,为我创建了IsLoaded属性,例如在Person上我为地址创建了AddressesIsLoaded属性:
<#
if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
{
#>
//The IsLoaded property for use on the client side when including collections
[DataMember]
<#=Accessibility.ForReadOnlyProperty(navProperty)#> bool <#=code.Escape(navProperty)#>IsLoaded
{
get; set;
}
<#
}
#>
答案 1 :(得分:0)
EF4中没有IsLoaded。使用隐式延迟加载生成所有默认代码。无需再打电话.Load()。
无论当前的SelfTrackingEntities t4不支持任何类型的延迟加载。如果要访问它们,则必须急于加载导航属性。延迟加载是可能的,您只需手动编辑t4模板。