IsLoaded用于自我跟踪实体

时间:2009-12-21 05:02:59

标签: entity-framework

我正在使用EF 4.0自我跟踪实体,我发现导航对象没有IsLoaded属性,这些属性参与标准EF对象上的多对多关系。因此,如果您查询Person并且不包含地址,那么person.Addresses会显示一个空列表,但是无法判断地址是否已加载或该人员是否没有任何地址。

有没有办法判断自我跟踪实体是否加载了导航属性?

如果没有办法从ObjectContext访问当前的ObjectQuery,以便我可以看到用户尝试扩展哪些属性并创建自定义IsLoaded属性?

2 个答案:

答案 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模板。