禁用延迟加载和eager-loading实体引用未按预期工作

时间:2011-10-01 00:10:17

标签: sql wcf entity-framework c#-4.0 wcf-ria-services

我一直在使用WCF RIA Services和Silverlight,并且在公开服务中取得了一些成功,该服务提供了从现有SQL Server 2008 Express数据库建模的ADO.NET实体数据模型中获取的数据。数据库定义了表之间的许多关系,我希望能够使用客户端进行数据绑定。

在尝试执行以下服务方法之前,一切都进展顺利:

public IQueryable<Timeline> GetHighlights() {

    var self = from x in Database.Timelines
                where User.Id == x.UserId || User.Id == x.SenderId
                select x;

    var friends = from x in Database.FriendItems
                    where User.Id == x.UserId
                    from y in Database.Timelines
                    where x.FriendId == y.UserId || x.FriendId == y.SenderId
                    select y;

    return self.Concat(friends).OrderByDescending(s => s.Id);
}

注意:'User'是类的内部属性,用于选择当前经过身份验证的用户,Database仅包装ObjectContext属性(为方便起见)。

“时间轴”实体包含2个导航属性“用户”和“发件人”,它们与“Silverfish用户”实体相关联。当我迭代“自我”查询的结果时,我看到前面提到的属性已经被当前用户填充(这是正确的)。但是,当我从'friends'查询迭代结果时,两个属性都为null(在序列化到客户端之前)。

我尝试过设置:

this.ContextOptions.LazyLoadingEnabled = false;
//and
this.ContextOptions.ProxyCreationEnabled = false;

我也尝试使用Include查询方法(启用和禁用延迟加载)来急切加载引用无效。

我成功填写时间轴实体的User和Sender属性的唯一方法是使用以下语句:

friends.ForEach(s => { 
    if (!s.UserReference.IsLoaded) s.UserReference.Load();
    if (!s.SenderReference.IsLoaded) s.SenderReference.Load();
});

根据我的理解,'加载'操作导致在数据库上执行单独的查询。正如您所看到的,当用户拥有许多包含许多时间轴帖子的朋友时,这会带来潜在的低效率。我试图通过禁用延迟加载来避免的确切情况。我想向客户端返回一个完全加载的实体,该实体可以在尽可能少的查询中绑定。

我已经克服了一个问题,即通过在域服务向导生成的元数据属性定义上应用[Include]属性,相对属性未序列化到客户端。这个问题似乎有点复杂,我试过的解决方案已经被其他人广泛宣称,应该在理论上解决我的问题,但事实并非如此。同样,我能够成功填充实体的唯一方法是使用生成的EntityReference&lt;&gt;显式加载引用。为相关属性创建的属性。

非常感谢有关此问题的任何帮助,经验或信息。

[编辑]当我执行这样的查询时,我的一些研究的更新:

    var friends = Database.FriendItems
                .Include("Friend.Timeline")
                .Where(s => User.Id == s.UserId);

并访问导航属性(“friends.First()。Friend.Timeline.First()。User”)该值不为null。只有当我通过添加以下内容选择时间轴到新集合时才会这样:

.SelectMany(s => s.Friend.Timeline);

导航属性不再具有任何值。现在这只是一个猜测,但我只能假设它将属性值投影到一个新的对象实例中,所以它不会重新填充这些属性以避免循环引用?无论如何,这是一个要解决的泡菜。希望那里的人比我更了解这个。

1 个答案:

答案 0 :(得分:0)

我已经设法找到了一些解决方法,虽然不是很优雅。我会将其作为答案发布,以便人们可以查看它,但如果可能的话,我会打开问题并提供更合适的解决方案。

这是我的修复:

public IQueryable<Timeline> GetHighlights() {

    var self = from x in Database.Timelines
               where User.Id == x.UserId || User.Id == x.SenderId
               select x;

    var friends = from x in Database.FriendItems.Include("Friend.Timeline")
                  where (User.Id == x.UserId)
                  select x.Friend.Timeline;

    List<Timeline> highlights = new List<Timeline>();
    highlights.AddRange(self);

    friends.ForEach(x => x.ForEach(y => highlights.Add(y)));
    return highlights.AsQueryable().OrderByDescending(s => s.Id);
}

认为这个工作的原因是手动创建新集合,我阻止将实体投影到新对象中,从而保留加载的关系属性。同样,这只是推测,但假设遵循相当好的模式lol。