通过WCF服务的独立自跟踪实体的异步延迟加载导航属性?

| 我有一个WCF客户端,它将自我跟踪实体传递给使用MVVM构建的WPF应用程序。应用程序本身具有动态接口。用户可以根据自己所扮演的角色或正在执行的任务来选择希望在其工作区域中可见的对象。 我的自跟踪实体具有很多导航属性,并且不需要很多。由于其中一些对象可能很大,因此我只想应要求加载这些属性。 我的应用程序如下所示:
[WCF] <---> [ClientSide Repository] <---> [ViewModel] <---> [View]
我的模型是自我跟踪实体。客户端存储库在将模型返回到请求它的ViewModel之前,连接了LazyLoad方法(如果需要)。所有WCF服务调用都是异步的,这意味着LazyLoad方法也是异步的。 LazyLoad的实际实现给我带来了一些麻烦。这是我提出的选项。 编辑-我删除了代码示例,以尝试使其更易于阅读和理解。如果要查看问题,请查看先前版本的问题 选项A 从Getter的WCF服务器异步地惰性加载模型的属性 良好:按需加载数据非常简单。 XAML中的绑定将加载数据,因此,如果控件在屏幕上,则数据将异步加载,并在该位置通知UI。如果没有,则没有任何负载。例如,“ 1”将加载数据,但是如果接口的“文档”部分不存在,则不会加载任何数据。 错误:启动此属性之前,无法在任何其他代码中使用此属性,因为它将返回一个空列表。例如,如果尚未加载文档,则以下调用将始终返回false。
public bool HasDocuments 
{ 
    get { return ConsumerDocuments.Count > 0; }
}
选项B 必要时手动调用以加载数据 优点:易于实现-只需添加
LoadConsumerDocumentsSync()
LoadConsumerDocumentsAsync()
方法 错误:必须记住在尝试访问数据之前先加载数据,包括在绑定中使用数据时。这看似简单,但很快就会失控。例如,每个ConsumerDocument都有一个UserCreated和UserLastModified。有一个DataTemplate定义了带有工具提示的UserModel,该ToolTip显示了其他用户数据,例如扩展名,电子邮件,团队,角色等。因此在显示文档的ViewModel中,我必须调用call5ѭ,然后遍历它们并调用call6ѭ和
LoadConsumerCreated
。它可能还会继续下去...在那之后,我不得不分别去
LoadUserGroups
LoadUserSupervisor
。它还可能会产生循环循环,其中诸如
User
具有
Groups[]
属性,而
Group
具有
Users[]
属性。 选项C 到目前为止,我最喜欢的选项...创建两种访问属性的方式。一同步和一异步。绑定将完成到Async属性,并且任何代码都将使用Sync属性。 良好:数据根据需要异步加载-正是我想要的。也没有太多额外的编码,因为我要做的就是修改T4模板以生成这些额外的属性/方法。 坏:拥有两种访问同一数据的方式似乎效率低下且令人困惑。您需要记住何时应使用
Consumer.ConsumerDocumentsAsync
而不是
Consumer.ConsumerDocumentsSync
。 WCF服务调用也有可能多次运行,并且这需要为每个导航属性(例如IsConsumerDocumentsLoaded)额外分配一个IsLoaded属性。 选项D 跳过异步加载,然后将所有内容同步加载到setter中。 好:非常简单,不需要额外的工作 不好:加载数据时会锁定UI。不要这个 选项E 有人告诉我,还有另一种方法可以做到这一点,并指出代码示例:) 其他注意事项 在将对象返回给客户端之前,一些NavigationProperty将被加载到WCF服务器上,但是其他一些这样做太昂贵了。 除了在选项C中手动调用Load事件外,所有这些都可以通过T4模板完成,因此我几乎不需要编写任何代码。我要做的就是在客户端存储库中挂接LazyLoad事件,并将其指向正确的服务调用。     
已邀请:
仔细考虑一下,首先我必须说,您必须为读者提供一个清晰的解决方案,当您绑定到User.Documents属性时,可以异步加载DependecyProperties,但是它的副作用非常严重解。如果我们说View中的这种行为是可以的,那么我们必须非常清楚地了解其余代码的意图,以便我们可以看到我们如何尝试访问数据-通过某种冗长的命名(方法,类名,方法)进行异步或同步其他)。 因此,我认为我们可以使用接近于旧.AsSynchronized()方法的解决方案,创建一个装饰器类,并为每个属性提供私有/受保护的AsyncLoad和SyncLoad方法,并且装饰器类将是每个lazyloadable的Sync或Async版本上课,更合适。 当您使用Sync装饰器装饰您的类时,它也使用Sync装饰器将每个lazyloadable类也包装在内,因此您将能够使用SynchUser(User).Documents。依靠同步类版本而没有问题,因为它会像 SynchUser(user).SyncDocuments(Documents。)。在Documents属性的重载版本中落后于此,并将调用sync getter函数。 由于同步版本和异步版本都将在同一对象上运行,因此,如果要修改任何属性,此方法将不会导致修改某些未引用的其他任何对象。 您的任务听起来像可以通过某种神奇的“美丽而简单”的方式解决,但我认为它不可能,或者说它不会比这简单得多。 如果仍然无法解决问题,那么还是100%确保您需要一种清晰的方法来区分代码,无论使用的是同步版本还是异步版本的类,否则您将很难维护代码库。     
选项A应该是解决方案。 创建一个名为LoadingStatus的属性,指示已加载数据或尚未加载数据。 异步加载数据,并相应地设置LoadingStatus属性。 检查每个属性中的加载状态,如果没有加载数据,则调用函数以加载数据,反之亦然。     
我想到的解决方案是修改自我跟踪​​实体的T4模板,以进行如下所示的更改。省略了实际的实现,以使其更易于阅读,但是属性/方法名称应清楚说明所有功能。 旧的T4生成的导航属性
[DataMember]
public MyClass MyProperty { get; set;}

private MyClass _myProperty;
T4生成的新导航属性
[DataMember]
internal MyClass MyProperty {get; set;}
public MyClass MyPropertySync {get; set;}
public MyClass MyPropertyAsync {get; set;}

private MyClass _myProperty;
private bool _isMyPropertyLoaded;

private async void LoadMyPropertyAsync();
private async Task<MyClass> GetMyPropertyAsync();
private MyClass GetMyPropertySync();
我创建了该属性的三个副本,这些副本指向同一私有属性。内部副本用于EF。我可能会摆脱它,但是它最容易留下来,因为EF期望使用该名称的属性,并且比固定EF以使用新的属性名称更容易保留它。它是内部的,因为我不想在类名称空间之外使用任何东西。 加载该值后,该属性的其他两个副本将以完全相同的方式运行,但是它们以不同的方式加载该属性。 异步版本运行
LoadMyPropertyAsync()
,仅运行
GetMyPropertyAsync()
。为此,我需要两个方法,因为我无法将
async
修饰符放在吸气剂上,并且如果从非异步方法进行调用,则需要返回void。 同步版本运行
GetMyPropertySync()
,而同步运行runs19ѭ 由于这都是T4生成的,因此从WCF服务获取实体时,除了挂接异步惰性负载委托外,我不需要做任何事情。 我的绑定指向该属性的异步版本,其他任何代码均指向该属性的同步版本,并且两者都可以正常工作而无需任何额外的编码。
<ItemsControl ItemsSource=\"{Binding CurrentConsumer.DocumentsAsync}\" />

CurrentConsumer.DocumentsSync.Clear();
    
Binding.IsAsync
图书馆的属性对您有帮助吗? 编辑:扩大一点。 具有延迟加载的同步属性,该属性将在首次使用时调用WCF服务。然后,异步绑定将阻止UI阻塞。     
虽然这个问题是在不久前提出的,但它在async-await关键字列表的顶部附近,我认为在.net 4.5中答案会完全不同。 我相信这将是一些站点上描述的
AsyncLazy<T>
类型的完美用例: http://blogs.msdn.com/b/pfxteam/archive/2011/01/15/10116210.aspx http://blog.stephencleary.com/2012/08/asynchronous-lazy-initialization.html http://blog.stephencleary.com/2013/01/async-oop-3-properties.html     
我脑子里有两个想法。 1)在“ 27”服务上执行“ 26”响应。然后以
IQueryable<>
模式直接进入DB。 2)在客户端存储库中,在
ConsumerDocuments
属性上设置getter以获取数据。
private IEnumerable<ConsumerDocuments> _consumerDocuments;

public IEnumerable<ConsumerDocuments> ConsumerDocuments
{
    get
    {
        return _consumerDocuments ?? (_consumerDocuments = GetConsumerDocuments() );
    }
}
    
以我的看法,ViewModel需要知道是否有可用数据。您可以隐藏或禁用在获取数据时没有数据的无意义的UI元素,然后在数据到达时显示它们。 您检测到需要加载一些数据,因此将UI设置为“等待”模式,启动异步获取,然后在数据进入时将其退出等待模式。可能是通过让ViewModel对其感兴趣的对象订阅\“ LoadCompleted \”事件。 (编辑)可以通过跟踪每个模型对象的状态来避免过多的负载或循环依赖:Unloaded / Loading / Loaded。     
这是为您提供的选项E。 异步加载数据。将最初的获取队列放入后台线程中,以缓慢填充完整的对象。并使任何要求在后台加载数据的方法在加载完成时受阻。 (阻止并让它们通知后台线程它们需要的数据具有高优先级,请下一步处理,以便您可以尽快取消阻止。) 这使您的UI可以在可能的情况下立即做出响应,可以编写代码而无需考虑已加载的内容,并且大多数情况下都可以正常工作。一个陷阱是,有时您会在加载数据时进行阻塞调用,但是希望它不会经常这样做。如果这样做了,那么在最坏的情况下,您会降级为选项C之类的东西,在这种情况下,您既可以阻止数据获取,又可以进行轮询以查看是否存在该数据。但是,大多数情况下,您不必太担心。 免责声明:我个人不使用Windows,而是将大部分时间都花在远离UI的后端上。如果您喜欢这个主意,请随时尝试。但是,除了动态网页中AJAX幕后调用之外,我还没有真正采用这种策略。     

要回复问题请先登录注册