我正在开发一个从服务器查看数据的Silverlight 4应用程序。我对RIA服务相对缺乏经验,因此一直在完成将我需要的数据提供给客户端的任务,但是我添加到拼图中的每一件新作品似乎都越来越成问题。我觉得我在这里缺少一些基本的概念,而且看起来我只是在以一种耗时的方式“破解”碎片,每一个都在我试图添加它们时打破以前的碎片。我很想获得有关RIA服务经验的开发人员的反馈,以找出我想要做的事情的预期方式。让我列出我正在做的事情:
首先是数据。此数据的来源是各种来源,主要由共享库创建,该库从我们的数据库中读取数据,并将其公开为POCO(普通旧CLR对象)。我正在创建自己的POCO来表示我需要在服务器和客户端之间传递的不同类型的数据。
DataA - 此应用程序用于查看特定类型的数据,我们可以近乎实时地调用DataA。自上次请求数据以来,客户端应每隔3分钟从服务器中提取所有新DataA中的数据。
DataB - 用户可以在应用中查看DataA对象,并可以从列表中选择其中一个,这将显示有关该DataA的其他详细信息。我将这些额外的细节作为DataB从服务器中删除。
DataC - DataB包含的内容之一是一段时间内几个重要值的历史记录。我将此历史记录的每个数据点称为DataC对象,每个DataB对象包含许多DataC。
数据模型 - 在服务器端,我有一个DomainService:
[EnableClientAccess]
public class MyDomainService : DomainService
{
public IEnumerable<DataA> GetDataA(DateTime? startDate)
{
/*Pieces together the DataAs that have been created
since startDate, and returns them*/
}
public DataB GetDataB(int dataAID)
{
/*Looks up the extended info for that dataAID,
constructs a new DataB with that DataA's data,
plus the extended info (with multiple DataCs in a
List<DataC> property on the DataB), and returns it*/
}
//Not exactly sure why these are here, but I think it
//wouldn't compile without them for some reason? The data
//is entirely read-only, so I don't need to update.
public void UpdateDataA(DataA dataA)
{
throw new NotSupportedException();
}
public void UpdateDataB(DataB dataB)
{
throw new NotSupportedException();
}
}
DataA / B / C的类看起来像这样:
[KnownType(typeof(DataB))]
public partial class DataA
{
[Key]
[DataMember]
public int DataAID { get; set; }
[DataMember]
public decimal MyDecimalA { get; set; }
[DataMember]
public string MyStringA { get; set; }
[DataMember]
public DataTime MyDateTimeA { get; set; }
}
public partial class DataB : DataA
{
[Key]
[DataMember]
public int DataAID { get; set; }
[DataMember]
public decimal MyDecimalB { get; set; }
[DataMember]
public string MyStringB { get; set; }
[Include] //I don't know which of these, if any, I need?
[Composition]
[Association("DataAToC","DataAID","DataAID")]
public List<DataC> DataCs { get; set; }
}
public partial class DataC
{
[Key]
[DataMember]
public int DataAID { get; set; }
[Key]
[DataMember]
public DateTime Timestamp { get; set; }
[DataMember]
public decimal MyHistoricDecimal { get; set; }
}
我想我在这里有一个很大的问题是......我应该使用实体代替POCO吗?我的类是否正确构建以便能够正确传递数据?我应该在DomainService上使用Invoke方法而不是Query(Get)方法吗?
在客户端,我遇到了很多问题。令人惊讶的是,我最大的一个是线程。我没想到MyDomainContext会出现这么多线程问题。我学到的是你似乎只能在UI线程上创建MyDomainContextObjects,你可以做的所有查询都是异步完成的,如果你试图通过阻塞调用线程来同步伪造它,直到LoadOperation完成后,你必须在后台线程上完成,因为它使用UI线程来进行查询。所以这就是我到目前为止所做的。
应用程序应该显示一个DataA对象流,在接下来的3分钟内传播它们每3分钟(因此它们最终会在发生后3分钟显示,看起来像一个连续的流,但只需要在3分钟的时间内下载)。为此,主窗体初始化,创建一个私有的MyDomainContext,并启动一个后台工作程序,它会连续循环一段时间(true)。在每个循环中,它检查是否有剩余的DataAs显示。如果是这样,它将显示Data和Thread.Sleep(),直到计划显示下一个DataA。如果它没有数据,则使用以下方法查询更多:
public DataA[] GetDataAs(DateTime? startDate)
{
_loadOperationGetDataACompletion = new AutoResetEvent(false);
LoadOperation<DataA> loadOperationGetDataA = null;
loadOperationGetDataA =
_context.Load(_context.GetDataAQuery(startDate),
System.ServiceModel.DomainServices.Client.LoadBehavior.RefreshCurrent, false);
loadOperationGetDataA.Completed += new
EventHandler(loadOperationGetDataA_Completed);
_loadOperationGetDataACompletion.WaitOne();
List<DataA> dataAs = new List<DataA>();
foreach (var dataA in loadOperationGetDataA.Entities)
dataAs.Add(dataA);
return dataAs.ToArray();
}
private static AutoResetEvent _loadOperationGetDataACompletion;
private static void loadOperationGetDataA_Completed(object sender, EventArgs e)
{
_loadOperationGetDataACompletion.Set();
}
似乎有点笨拙试图强制它同步,但由于这已经在后台线程,我认为这没关系?到目前为止,一切都确实有效,就像它看起来可能是一样。重要的是要注意,如果我尝试在UI线程上运行该代码,它会锁定,因为它会永久等待WaitOne(),锁定线程,因此它无法向服务器发出Load请求。
因此,一旦显示数据,用户可以点击其中的一个,以填充详细信息窗格,其中包含有关该对象的完整DataB数据。为此,我有详细信息窗格用户控件订阅我已设置的选择事件,当选择更改时(在UI线程上)将触发该事件。我在那里使用类似的技术来获取DataB对象:
void SelectionService_SelectedDataAChanged(object sender, EventArgs e)
{
DataA dataA = /*Get the selected DataA*/;
MyDomainContext context = new MyDomainContext();
var loadOperationGetDataB =
context.Load(context.GetDataBQuery(dataA.DataAID),
System.ServiceModel.DomainServices.Client.LoadBehavior.RefreshCurrent, false);
loadOperationGetDataB.Completed += new
EventHandler(loadOperationGetDataB_Completed);
}
private void loadOperationGetDataB_Completed(object sender, EventArgs e)
{
this.DataContext =
((LoadOperation<DataB>)sender).Entities.SingleOrDefault();
}
同样,它似乎有点hacky,但它的工作原理......除了它加载的DataB之外,DataCs列表是空的。我在那里尝试了各种各样的东西,但是我没有看到我做错了什么让DataCs能够使用DataB。我准备好为DataCs做第三次查询了,但这对我来说更加尖叫。
这真的感觉我在这里与谷物作斗争,就像我是以一种完全无意的方式做到这一点。如果有人能提供任何帮助,并指出我在这里做错了什么,我非常感谢!
谢谢!
答案 0 :(得分:0)
我不得不说这看起来有点过于复杂。
如果你使用实体框架(版本4可以根据需要生成POCO)和Ria / LINQ你可以使用延迟加载隐式地完成所有这些操作,'expand'语句和每个类型继承的表。