zoukankan      html  css  js  c++  java
  • SilverLight商业应用程序开发学习笔记(5)从服务器中获取数据之一

    异步通信

    在Silverlight中所有与服务器间的通信都是异步执行的。因此需要熟悉异步编程。典型的编程模式包括发起一个对服务器的调用,然后等待事件引发通知调用完成。调用在后台线程执行,一旦完成就立即返回,从而避免了UI因该方法的调用而阻塞。一般说来,如果异步调用在完成时引发事件,该事件通常都会在UI线程引发,更新UI线程上的显示结果。但是如果使用异步调用委托(使用AsyncCallback对象作为参数),回调方法会在后台线程执行,这样就无法在UI线程更新了,这种情况下,需要使用Dispatcher对象的BeginInvoke方法,在UI线程上调用代码执行。

    使用RIA服务从服务器中获取数据

    image

    在Generated_Code文件夹下有很多文件,核心类,称之为AdventureWorks.Web.g.cs ,包含了大部分的生成代码,剩余的文件标记为shared,是直接从Web项目中复制过来的,保持了Web项目中相同的文件夹结构。检视生成的代码有助于识别代码生成错误。在调试时,可以打开这些文件并设置断点以协助分析问题。记住不要修改这些文件,重新编译就会覆盖所有修改。想要为生成的代码添加功能,创建单独的文件,使用部分类来扩展。

    AdventureWorks.Web.g.cs (是根据【web项目名称】.g.cs来命名的)包含如下核心类:

    • 域上下文类
    • Model类
    • WebContext类

    域上下文类

    RIA代码生成器为每个域服务生成相应的域上下文类。在Silverlight项目(客户端)所写的代码可以使用域上下文类(代理与桥梁)与服务器上相应的域服务进行通信。遵循默认命名规则,如果域服务命名为XXXService,则域上下文类为XXXContext。(不符合默认规则,需要进行查询)。域上下文类也有相对应于每个查询,调用与自定义操作的方法,可在客户端进行调用。insert/update/delete操作在域上下文没有相应的方法对应,这是因为这些操作不会在客户端显示调用。当在域上下文上调用SubmitChanges方法时,RIA服务会将变更集发送到服务器调用相应的insert/update/delete操作方法。

    Entiy/Model类

    每个由域服务暴露的实体(或Presentation Model类)都有相应的类在此文件中创建。任何在Web项目中应用到的特性标记(通过相应元数据类)也会直接应用于生成的客户端类;

    WebContext类

    WebContext类在Silverlight程序启动时初始化,该实例将在整个程序的执行周期内以“扩展服务”的形式进行保持。在AdventureWorks项目的App.xaml.cs文件中,该类在应用程序启动时实例化,并加入到ApplicationLifetimeObjects集合中。命名静态的Current属性就可以获得对该实例的引用。WebContext类充当应用程序 的上下文,维护当前用户对象,提供对AuthenticationContext类实例的访问,可以使用部分类扩展该类。

    使用DomainDataSource控件

    实例:使用DomainDataSource控件查询数据,显示在DataGrid控件上。

    1)在View文件夹下添加ProductList.xaml文件;

    2)打开数据源窗口(数据--显示数据源)。有关的域上下文类自动显示在数据源窗口里;

    image

    3)从数据源窗口拖动实体(Product)放置在设置界面上,系统自动为DomainDataSource控件进行了配置,并与DataGrid控件进行了绑定:

    <riaControls:DomainDataSource AutoLoad="True"  
                    d:DesignData="{d:DesignInstance my:Product, CreateList=true}"  
                    LoadedData="productDomainDataSource_LoadedData"  
                    Name="productDomainDataSource" QueryName="GetProductsQuery"  
                    Height="0" Width="0"> 
        <riaControls:DomainDataSource.DomainContext> 
            <my:ProductContext /> 
        </riaControls:DomainDataSource.DomainContext> 
    </riaControls:DomainDataSource> 
     
    <sdk:DataGrid AutoGenerateColumns="False" Height="200"  
           HorizontalAlignment="Left"  
           ItemsSource="{Binding ElementName=productDomainDataSource, Path=Data}"  
           Margin="185,53,0,0" Name="productDataGrid" Width="400" 
           RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top"> 
        <sdk:DataGrid.Columns> 
            <sdk:DataGridTextColumn x:Name="classColumn"  
                                    Binding="{Binding Path=Class}"  
                                    Header="Class" Width="SizeToHeader" /> 
    <sdk:DataGridTextColumn x:Name="colorColumn"  
                                    Binding="{Binding Path=Color}"  
                                    Header="Color" Width="SizeToHeader" /> 
            <!-- Additional columns removed for brevity--> 
        </sdk:DataGrid.Columns> 
    </sdk:DataGrid> 

    4)运行程序,所有Product数据都从服务器获取并显示在客户端的DataGrid控件上。

    使用域上下文类以代码方式获取数据

    域服务上的查询/调用/自定义操作方法在域上下文对象中都有相对应的方法以供调用。这些方法通常的命名方法是在域服务的操作方法上附加Query后缀:

    image

    通过代码从服务器中获取数据需要几个步骤。简单地调用GetProductsQuery方法并不会向服务器请求数据,该方法只返回一个EntityQuery对象。

    ProductContext context = new ProductContext();
    EntityQuery<Product> qry = context.GetProductsQuery();

    获得EntityQuery对象以后,可以将其传递给域上下文的Load方法,这时才真正向服务器请求数据:

    LoadOperation<Product> operation = context.Load(qry);

    Load方法返回的是LoadOperation对象,该对象包含了一个实体集属性,是由请求对象的集合构成的;事实上,现在集合还是空的,因为RIA服务框架要求所有对域服务的调用都是异步的,因此Load方法需要等到服务器响应结束以外才能获得数据,有两种方法可以达到这个目的:

    1)通过LoadOperation对象的Completed事件,该事件在数据从服务器获取完毕后发生。该事件的的e参数有一个Entities属性,可以用于获取访问结果;这种方式可以识别在请求过程中是否有错误发生,可以对相应错误进行处理;

    2)通过LoadOpertion对象的Entities属性。该属性初始时为空,但是当从服务器获取到数据以后,该属性自动完成数据充填。这是因为该属性集合实现了INotifyCollectionChanged接口,这个接口有一个CollectionChanged事件,该事件监听是否有项目添加或移出集合。Silverlight控件如ListBox或DataGrid可以直接以该属性集合作为数据源。

    productDataGrid.ItemsSource = operation.Entities;

    注意EntityQuery<T>是对实体集合的LINQ查询的范型类,可以直接在该类型的变量上使用Lambda表达式或Linq查询语法:

    ProductContext context = new ProductContext(); 
    EntityQuery<Product> qry = context.GetProductsQuery(); 
    qry = qry.Where(p => p.SellStartDate <= DateTime.Now); 
    LoadOperation<Product> loadOperation = context.Load(qry); 
    productDataGrid.ItemsSource = loadOperation.Entities; 

    或者

    ProductContext context = new ProductContext(); 
    EntityQuery<Product> qry = from p in context.GetProductsQuery() 
                               where p.SellStartDate <= DateTime.Now 
                               select p; 
    LoadOperation<Product> loadOperation = context.Load(qry); 
    productDataGrid.ItemsSource = loadOperation.Entities; 
     

    处理加载错误

    由于Silverlight应用程序调用方法的异步性,处理加载数据中出现的错误不能使用常规的方式(比如使用try/catch块)。正确的做法是处理服务器通信结束时引发的事件,如果使用DomainDataSource控件,则处理控件的LoadedData事件,如果使用基于代码访问数据的方式,则需要处理Loadperation的Completed事件。两种情况的处理代码如下:

    1)拖放实体到设计界面,在创建DomainDataSource控件的同时,也为该控件生成了LoadedData事件处理程序:

    private void productDomainDataSource_LoadedData(object sender,  
                                                    LoadedDataEventArgs e) 
    { 
        if (e.HasError) 
        { 
            System.Windows.MessageBox.Show(e.Error.ToString(), "Load Error",  
                                           System.Windows.MessageBoxButton.OK); 
            e.MarkErrorAsHandled(); 
        } 
    } 

    2)LoadOperation的Completed事件与DomainDataSource的 LoadedData事件类似,不同之处需要将sender参数强制转换为泛型的LoadOperation对象:

    private void loadOperation_Completed(object sender, EventArgs e) 
    { 
        LoadOperation<Product> op = sender as LoadOperation<Product>; 
     
        if (op.HasError) 
        { 
            System.Windows.MessageBox.Show(op.Error.ToString(), "Load Error", 
                                           System.Windows.MessageBoxButton.OK); 
            op.MarkErrorAsHandled(); 
        } 
    } 

    注意我们在两个事件处理函数里都调用了MarkErrorAsHandled方法。如果不调用这个方法,域上下文会抛出”未处理”异常,该异常可以由App类的Application_UnhandledException事件处理方法进行处理,弹出一个错误窗口以显示错误细节,避免应用程序崩溃。该异常可以强制转换为DomainOperationException类型,可以获取更多信息,比如该类型的Status属性,可以通过OperationErrorStatus枚举来判断异常发生的类型:

    1)ServerError:指在服务器上发生的异常或者应用程序无法连接到服务器

    2)Unauthorized:用户无权执行操作。

    因此错误处理的部分还可以细化为:

    if (e.HasError) 
    { 
        DomainOperationException error = e.Error as DomainOperationException; 
     
        switch (error.Status) 
        { 
            case OperationErrorStatus.ServerError: 
                // Handle server errors 
                break; 
            case OperationErrorStatus.Unauthorized: 
                // Handle unauthorized domain operation access 
                break; 
        } 
    } 

  • 相关阅读:
    (CSDN迁移)js中的判空
    (CSDN迁移) 输入一个链表,从尾到头打印链表每个节点的值
    (CSDN迁移) 替换字符串中的空格
    (CSDN迁移) Java路径获取
    Apache JMeter 做接口并发测试
    用Postman做接口测试
    高并发或高负载下的系统设计
    编译时异常与运行时异常的区别
    使用JUNIT进行单元测试
    hexo 博客如何更换电脑
  • 原文地址:https://www.cnblogs.com/qouoww/p/2490840.html
Copyright © 2011-2022 走看看