使用LINQ和ADO.NET创建Silverlight程序

使用LINQ和ADO.NET创建Silverlight程序,第1张

概述使用LINQ和ADO.NET创建Silverlight程序   注:本文转自 51cto.com     在Silverlight中可以创建行业和其它以数据为中心的应用系统,但在Silverlight中处理数据不是一件容易的事情,由于Silverlight包括许多处理数据和支持Web Service及XML的工具,但这些工具仅代表跨过防火墙进行数据访问的最基础的部分。 常见的数据访问策略是使用We

使用liNQ和ADO.NET创建Silverlight程序@H_419_9@

 @H_419_9@

注:本文转自 51cto.com@H_419_9@

 @H_419_9@

 @H_419_9@

Silverlight中可以创建行业和其它以数据为中心的应用系统,但在Silverlight中处理数据不是一件容易的事情,由于Silverlight包括许多处理数据和支持Web ServiceXML的工具,但这些工具仅代表跨过防火墙进行数据访问的最基础的部分。 @H_419_9@

常见的数据访问策略是使用Web Service和客户端liNQ共同实现的,如果你正在修改现有Web Service端点强化你的Silverlight应用程序,那么我推荐你使用这个方法。但如果你在使用Silverlight创建一个新的Web Service,就没有必要这么做了。@H_419_9@

对一个典型的Web Service层而言,你在服务器上实现一个传统的数据访问策略(自定义业务对象、liNQ to sql、实体框架、Nhibernate等)通过Web Service暴露数据对象,Web Service仅仅是数据访问策略下面的网关。@H_419_9@

但为了开启完整的数据连通性,你必须要映射四个数据 *** 作(创建、读取、更新和删除)到Web Service方法,下面是一个简单的支持Product类的service contract,注意我在本文中使用的都是C#@H_419_9@

1 Product Web ServiceService Contract@H_419_9@

[ServiceContract]@H_419_9@

public interface ICustomerService@H_419_9@

{@H_419_9@

  [OperationContract]@H_419_9@

  List GetAllProducts();@H_419_9@

  [OperationContract]@H_419_9@

  Product GetProduct(int productID);@H_419_9@

  [OperationContract]@H_419_9@

  List GetAllProductsWithCategorIEs();@H_419_9@

  [OperationContract]@H_419_9@

  Product SaveProduct(Product productToSave);@H_419_9@

  [OperationContract]@H_419_9@

  voID DeleteProduct(Product productToDelete);@H_419_9@

}@H_419_9@

创建一套服务来处理应用程序完整的数据模型可能是相当费时的,正如这个例子中显示的,特殊特性的 *** 作可能导致Web Service非常臃肿,换句话说,Web Service将有新的要求和 *** 作要增加,甚至包括不属于核心业务域的 *** 作。@H_419_9@

在例1中你看到GetAllProductsWithCategorIEs *** 作默认用于检索Product和分类。即使添加排序、过滤和分页机制到这个简单的例子你也不要感到惊讶,如果有一个简单的方法支持数据 *** 作(如查询、排序、过滤等)不用每次都手动构建这些机制那将是非常吸引人的,ADO.NET Data Service就正是为此而生的。@H_419_9@

ADO.NET Data Service@H_419_9@

ADO.NET Data Service的目标是为数据模型提供Web访问端点,这些端点提供了数据排序、过滤、调整和分页功能,因此开发人员就不需要在自己去编写这部分代码了,实际上,每个端点都是liNQ查询的起点,就从这个端点上你就可以查询你想要查找的数据。@H_419_9@

但不要认为ADO.NET Data Service是另一个数据访问策略,实际上,ADO.NET Data Service不执行任何直接的数据访问 *** 作,它位于数据访问的上层,图1显示了ADO.NET Data Service和它在一个应用程序架构中的位置。@H_419_9@

 @H_419_9@

1 ADO.NET Data Service@H_419_9@

由于ADO.NET Data Service依赖于数据访问程序完成真实的数据访问工作,你必须指定这个方法该如何做,在ADO.NET Data Service中,每个服务(Service)必须回到开启liNQ的提供程序的后面,实际上,每个端点就是一个Iqueryable端点,因此ADO.NET Data Service支持任何支持Iqueryable的对象。@H_419_9@

创建服务(Service@H_419_9@

当你将ADO.NET Data Service添加到你的项目中时,会创建一个新的.svc文件,代表一个服务的类,和Web Service不同,你不需要自己亲自实现服务的 *** 作,但要允许DataService类处理这些工作,为了运行这些服务,有两个小任务必须执行。首先,DataService类需要一个类型参数叫做上下文对象,它是将数据作为服务暴露的类,当你的服务从关系数据库暴露数据时,这个类是从实体框架(EntityFramework)的ObjectContextliNQ to sqlDataContext衍生而来的。@H_419_9@

//使用我的northwindEntitIEs上下文对象(context object)作为服务(Service)的数据源@H_419_9@

public class Products : DataService@H_419_9@

上下文对象没有基数类要求,实际上,你可以创建你自己的上下文对象,只要它的属性实现了Iqueryable接口,ADO.NET Data Service将会以端点形式暴露这些属性:@H_419_9@

public class StateContext@H_419_9@

{@H_419_9@

  StateList _states = new StateList();@H_419_9@

  public Iqueryable States @H_419_9@

  {@H_419_9@

    get { return _states.Asqueryable(); } @H_419_9@

  }@H_419_9@

}@H_419_9@

InitializeService调用中,你可以使用IDataServiceConfiguration对象指定什么类型的许可允许进入服务,ADO.NET Data Service使用名词和动词具体指定许可,如例2所示:@H_419_9@

2 设置访问规则@H_419_9@

//这个方法只被调用一次初始化服务端策略@H_419_9@

public static voID InitializeService(IDataServiceConfiguration config)@H_419_9@

{@H_419_9@

  //只允许我们读取和更新Products实体,不允许删除和创建@H_419_9@

  config.SetEntitySetAccessRule("Products",@H_419_9@

                                EntitySetRights.AllRead | @H_419_9@

                                EntitySetRights.WriteUpdate);@H_419_9@

  //只允许读取categorysuppliers实体@H_419_9@

  config.SetEntitySetAccessRule("CategorIEs",EntitySetRights.AllRead);@H_419_9@

  config.SetEntitySetAccessRule("suppliers",EntitySetRights.AllRead);@H_419_9@

}@H_419_9@

完成这个之后,你可以直接浏览服务了,它将会显示每个端点的原子反馈信息,为了调试ADO.NET Data Service,我建议你禁用Internet ExplorerRSS 反馈视图,或使用另一个浏览器查看服务的XML格式。 @H_419_9@

查询和更新数据 @H_419_9@

ADO.NET Data Service将服务作为具有代表性的状态转换器(Representational State Transfer (REST))暴露,它是一个基础服务,不是基于SOAP的服务,这意味着要替换掉SOAP封包,服务响应的有效负载只包括数据,不包括原数据(Metadata),所有请求都使用http动词(GETPUTPOST等)和请求URI描述,假定你有一个如图2所示的模型描述ProductsCategorIEssuppliersADO.NET Data Service服务将会产生三个端点,每个实体集一个,URI为了确定一个模型中的实体集,只需要使用服务的地址和端点的名字就可以了:http://localhost/{服务名}/{端点名}http://localhost/Product.svc/Products@H_419_9@

 @H_419_9@

2 数据模型示例@H_419_9@

URI语法支持许多不同的特性,包括检索特殊的实体,对结果进行排序、过滤、分页和调整。@H_419_9@

ADO.NET Data Service使用这些URI风格的查询将数据返回给服务的用户,目前支持两个序列化格式(将来的版本很可能会进行扩展):JavaScript对象标记(JavaScript Object NotationJsON)和基于原子的XMLAtom-based XML)。JsON对于客户端Web代码非常有吸引力,而Atom是基于XML的格式,因此需要借助XML解析器。@H_419_9@

ADO.NET Data Service在查询中使用标准的http访问头来确定向客户端返回什么格式,如果你从客户端(如一个浏览器)发出一个请求可以破坏XML,如果你不通过Accept头指定一个优先选用的格式,默认将使用Atom作为返回的格式。@H_419_9@

查询数据只是解决方案的一部分,我们的最终目标是同时支持查询和更新,为了支持这些要求,ADO.NET Data Service映射了四个最基本的数据访问 *** 作到基本的http动词(如表1所示):@H_419_9@

数据访问动词@H_419_9@

http动词@H_419_9@

Create@H_419_9@

POST@H_419_9@

Read@H_419_9@

GET@H_419_9@

Update@H_419_9@

PUT@H_419_9@

Delete@H_419_9@

DELETE@H_419_9@

1 数据访问动词 vs http动词@H_419_9@

通过使用这些动词,ADO.NET Data Service让服务的用户可以利用所有的数据 *** 作类型,而不用为不同类型创建专门的端点。使用ADO.NET Data Service更新数据的唯一要求是在数据访问技术下支持Iupdatable接口,这个接口定义了如何从ADO.NET Data Service更新和传播到数据源。@H_419_9@

Silverlight 2.0客户端库@H_419_9@

如果你使用ADO.NET Data Service通过URI语法和 *** 作XML来进行查询和更新数据,你可能会得到你想要的许多功能,但你仍然要自行构建一些管道,ADO.NET Data Service客户端库的引入就是要解决这个问题,这个库允许你直接在Silverlight程序中进行liNQ查询,由客户端库将liNQ查询翻译成http查询或更新请求。@H_419_9@

首先,你需要生成一些代码,这些代码读取ADO.NET Data Service服务的元数据,并为服务的实体生成数据类。@H_419_9@

为了生成这些代码,你需要在你的项目(Project)中添加Service Reference,你可以在项目资源管理器(Project Explorer)中Silverlight项目上点击右键,然后选择添加服务引用(即Add Service Reference,在d出的对话框中点击查找(discover按钮,显示你项目中的服务(包括ADO.NET Data Service),选择ADO.NET Data Service端点,点击确定按钮。这样会创建一个新的文件,包含了每个端点对应的data contract类和一个DataServiceContext衍生类,DataServiceContext类用作服务接入点(暴露可查询的服务端点),这样会在你的Silverlight项目中包含这些类,并在System.Data.Services.ClIEnt.dllSilverlight 2 SDK的一部分)中添加一个引用。Silverlight客户端代码和其它使用.NET的代码基于liNQ的查询非常相似,下面是示例代码:@H_419_9@

// 创建服务类指定ADO.NET Data Service的位置@H_419_9@

 northwindEntitIEs ctx = @H_419_9@

  new northwindEntitIEs(new Uri("Products.svc",UriKind.relative));@H_419_9@

//创建liNQ查询@H_419_9@

var qry = from p in ctx.Products@H_419_9@

               orderby p.Productname@H_419_9@

                select p;@H_419_9@

当你执行这个查询时,它会直接向目标数据发送一个Web请求,但这里的Silverlight代码和标准的liNQ查询不同,在Silverlight中不允许同步Web请求,因此,如果要执行异步,你首先需要将查询转换成DataServicequery对象,然后再调用BeginExecute启动异步执行:@H_419_9@

// 创建一个DataServicequery,因为查询返回的是Products@H_419_9@

DataServicequery productquery =@H_419_9@

  (DataServicequery)qry;@H_419_9@

//指定一个callback函数执行异步查询@H_419_9@

  productquery.BeginExecute(new @H_419_9@

    AsyncCallback(OnLoadComplete),@H_419_9@

    productquery);@H_419_9@

当这些查询执行完后,无论 *** 作是否成功,在AsyncCallback中指定的方法都会执行,通常你会在AsyncCallback中包含原始查询,因此可以在callback方法中检索它,你也可以将其保存为类的一部分,正如你在例3中看到的:@H_419_9@

3 将结果添加到集合中@H_419_9@

voID OnLoadComplete(IAsyncResult result)@H_419_9@

{@H_419_9@

  //为查询获取一个引用@H_419_9@

  DataServicequery productquery =@H_419_9@

    (DataServicequery)result.AsyncState;@H_419_9@

 @H_419_9@

  try@H_419_9@

  {@H_419_9@

    //获得结果并将其添加到集合中@H_419_9@

    List products = productquery.EndExecute(result).ToList();@H_419_9@

 @H_419_9@

  }@H_419_9@

  catch (Exception ex)@H_419_9@

  {@H_419_9@

    if (HTMLPage.IsEnabled)@H_419_9@

    {@H_419_9@

      HTMLPage.Window.Alert("Failed to retrIEve data: " + ex.ToString());@H_419_9@

    }@H_419_9@

  }@H_419_9@

 @H_419_9@

}@H_419_9@

如果你以前还没有处理过liNQ,理解这些模型可能就非常困难,在写本文的时候,除了在异步包中执行liNQ(如ThreadPoolBackgrounDWorker)外,还没有关于异步liNQ很好的模型,Silverlight需要所有的请求都是异步的,因此在使用ADO.NET Data Service客户端库时需要使用这个模型。@H_419_9@

载入相关实体@H_419_9@

ADO.NET Data Service也允许你选择如何载入相关的实体,在前面的例子中,我是从服务器中载入Products(产品)的,每个产品与供应商都有一个关系。使用前面的liNQ查询,我们只检索了产品,如果我还想显示供应商和分类信息,我们可以按需载入相关信息,也可以在原始查询中明确地从服务器去检索,这两种技术各有各的优势,但如果你清楚地知道需要显示哪些信息,明确地载入可能更有效,如果你只想为一些实体载入数据,使用按需检索可能会更好。@H_419_9@

默认情况下,如果你没有明确地载入属性,关系属性(如产品供应商)就是空的,为便于按需载入,DataServiceContext类有一个BeginLoadProperty方法(遵循相同的异步模式)可以指定源实体,属性名和callback@H_419_9@

public voID LoadsupplierAsync(Product theProduct)
{
TheContext.BeginLoadProperty(theProduct,
"supplier",
new AsyncCallback(supplierLoadComplete),
null);
}

public voID supplierLoadComplete(IAsyncResult result)
{
TheContext.EndLoadProperty(result);
}
@H_419_9@

调用EndLoadProperty后,属性和相关的实体就被正确地载入,在许多情况下,你可能想在原始查询中明确地载入它们,因为如此,liNQ提供者支持Expand扩展方法,这个方法允许你指定属性的名称路径便于查询执行时载入它们,Expand方法在liNQ查询的from子句中使用,它告诉提供者视图载入这些相关实体,例如,如果你使用Expand方法改变了category supplier原始查询,在原始查询执行期间,我们的对象将会载入这些相关实体:@H_419_9@

var qry =
from p in TheContext.Products.Expand("supplier").Expand("category")
orderby p.Productname
select p;
@H_419_9@

如果你使用ADO.NET Data Service读取数据,知道如何创建一个查询,运行它,载入你想要的相关实体。如果你需要真实地修改数据,只需要将你的新数据绑定到你的Silverlight控制器即可。 @H_419_9@

变化管理 @H_419_9@

ADO.NET Data Service客户端库不支持对象的自动变更监视,这意味着当对象,集合和关系发生变化时,需要开发人员告诉DataServiceContext这些变化,通知DataServiceContext对象的API相当简单,如例4所示:@H_419_9@

4 DataServiceContext变更API@H_419_9@

方法@H_419_9@

描述@H_419_9@

Addobject@H_419_9@

添加一个新创建的对象@H_419_9@

UpdateObject@H_419_9@

标记一个已经变化的对象@H_419_9@

DeleteObject@H_419_9@

标记一个删除的对象@H_419_9@

Addlink@H_419_9@

在两个对象之间添加一个链接@H_419_9@

Updatelink@H_419_9@

更新两个对象之间的链接@H_419_9@

Deletelink@H_419_9@

删除两个对象之间的链接@H_419_9@

这意味着你要监视对象的变化,并在你自己的代码中通知DataServiceContext对象,表面上看起来这样让人很失望,因为没有实现自动化的变化管理,但这样可以让库变得更有效也更mini@H_419_9@

你可能会对如何监视对象的变化感到奇怪,答案就是生成的代码中,在每个生成的data contract类中,当类中的数据变化时partial方法被调用,如果这些方法从来没有使用过,它们本身不会造成任何资源消耗,你可以在任何支持变化通知的data contracts上使用partial方法机制,只需要在partial方法中调用DataServiceContract即可,不用连接DataServiceContract整个类。@H_419_9@

幸运的是,Silverlight已经有一个接口支持变化通知了(INotifyPropertyChange),通过这个接口可以在你的实现中将任何变化通知给感兴趣的人,例如你可以在你的data contract类(在我们的例子中是Product类)中调用InotifyPropertyChange定义一个事件,当数据发生变化时可以激活它,下面就是具体的示例:@H_419_9@

public partial class Product : INotifyPropertyChanged@H_419_9@

{@H_419_9@

public event PropertyChangedEventHandler PropertyChanged;@H_419_9@

}@H_419_9@

这样当任何属性发生变化时都可以触发一个事件,你可以通过partial方法决定什么时候触发这个事件,例如,当Productname发生变化时要通知预定人,只需要调用OnProductnameChanged方法,然后触发PropertyChanged事件,传递Productname通知变化的属性给事件预定人,下面是代码:@H_419_9@

partial voID OnProductnameChanged()
{
if (PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs("Productname"));

}
@H_419_9@

通过在这些可写的属性上调用这些partial方法,监视你对象的变化就很简单了,当对象发生变化时,你可以注册PropertyChanged事件然后通知DataServiceContext对象:@H_419_9@

//OnLoadComplete方法中,获取结果然后将它们添加到集合中
List

products = productquery.EndExecute(result).ToList();
foreach (Product product in products)
{
//
触发变化通知
product.PropertyChanged +=
new PropertyChangedEventHandler(product_PropertyChanged);
} >
@H_419_9@

 @H_419_9@

最后你可以调用product_PropertyChanged方法通知DataServiceContext对象:@H_419_9@

 @H_419_9@

voID product_PropertyChanged(object sender,PropertyChangedEventArgs e)@H_419_9@

{@H_419_9@

Product product = (Product)sender;@H_419_9@

TheContext.UpdateObject(product);@H_419_9@

}@H_419_9@

同样,在创建对象或删除对象时也需要通知DataServiceContext,如:@H_419_9@

voID addNewbutton_Click(object sender,RoutedEventArgs e)@H_419_9@

{@H_419_9@

  Product theProduct = new Product();@H_419_9@

  // ...@H_419_9@

  TheContext.Addobject(theProduct);@H_419_9@

}@H_419_9@

 @H_419_9@

voID deletebutton_Click(object sender,RoutedEventArgs e)@H_419_9@

{@H_419_9@

  Product theProduct = (Product)theList.SelectItem;@H_419_9@

  TheContext.DeleteObject(theProduct);@H_419_9@

  theCollection.Remove(theProduct);@H_419_9@

}@H_419_9@

在这些代码中,你可以在你的Silverlight UI中修改这些对象,让数据绑定和变化通知代码确保让DataServiceContext知道所有变化都会引发什么后果,但你如何对这些服务执行真实的更新呢?@H_419_9@

通过服务更新@H_419_9@

现在你的DataServiceContext对象已经知道数据的变化,但还需要一个方法通知给服务器,为了解决这个问题,DataServiceContext类提供了一个BeginSaveChanges方法,它和本文前面描述的查询都使用了相同的异步方法,BeginSaveChanges方法将所有变化都吸收进DataServiceContext,并将它们发送给服务器:@H_419_9@

TheContext.BeginSaveChanges(SaveChangesOptions.None,@H_419_9@

new AsyncCallback(OnSaveAllComplete),@H_419_9@

null);@H_419_9@

调用BeginSaveChanges时,有一个标志枚举调用SaveChangesOptions,这个枚举允许你指定两个选项:是否使用批处理,是否继续,即使某些变化保存失败。通常,我建议使用批处理,实际上,在某些父/子关系类型上批处理是必须的,因为父子之间可能使用了引用完整性约束,这样更新才能保证父子之间的一致性。@H_419_9@

保存完毕时,将会执行callback,有两个机制可以传播错误消息给你,首先,如果在执行保存时出现了异常,当你在调用EndSaveChanges时,会抛出异常,因为如此,你可能想要使用try/catch来捕获灾难性的错误;另外,EndSaveChanges返回的类型是一个DataServiceResponse对象,DataServiceResponse有一个HasErrors属性,但在Silverlight 2 Beta 2版本库中它还不够安全:@H_419_9@

voID OnSaveAllComplete(IAsyncResult result)@H_419_9@

{@H_419_9@

  bool succeeded = true;@H_419_9@

  try@H_419_9@

  {@H_419_9@

    DataServiceResponse response = @H_419_9@

      (DataServiceResponse)TheContext.EndSaveChanges(result);@H_419_9@

 @H_419_9@

    foreach (OperationResponse opResponse in response)@H_419_9@

    {@H_419_9@

      if (opResponse.HasErrors)@H_419_9@

      {@H_419_9@

        succeeded = false;@H_419_9@

      }@H_419_9@

    }@H_419_9@

 @H_419_9@

  }@H_419_9@

  catch (Exception ex)@H_419_9@

  {@H_419_9@

    succeeded = false;@H_419_9@

  }@H_419_9@

 @H_419_9@

  // Alert the User@H_419_9@

}@H_419_9@

你可以重复使用OperationResponse对象来查看是否出现了错误,DataServiceResponseOperationResponse对象的一个集合,在以后的版本中,你应该可以依赖于DataServiceResponse类自身的HasErrors属性了。@H_419_9@

服务调式@H_419_9@

在调试服务时,你要执行三个重要的任务:查看DataServiceContext对象中数据的状态,查看ADO.NET Data Services产生的请求,以及捕获服务器错误。@H_419_9@

首先我们处理DataServiceContext对象中的实体状态,DataServiceContext类暴露了两个有用的集合:EntitIEslinks,这些集合是只读的,由DataServiceContext进行跟踪,在调式时,不管你是将对象标记为已变化还是未变化,在调试器中查看这些集合是非常有用的,可以帮助你确定跟踪思路是不是正确的。@H_419_9@

注意对你而言,查看你的Silverlight 2程序对服务器的真实请求也是很重要的,最好的方法是使用网络代理,我个人使用的是fiddler2,如果你对fiddler2不熟悉,也可以使用Web traffic之类的工具来捕获数据包,查看真正发生了什么。@H_419_9@

对于ADO.NET Data Service而言,你可能想查看你在线上传来传去的都是什么,即Silverlight程序发出的数据和接收到的数据,可以去我的博客(http://wildermuth.com/2008/06/07/DeBUGging_ADO_NET_Data_Services_with_fiddler)转转。@H_419_9@

最后,.NET Framework 3.5 SP1不会将服务端错误传递给客户端了,实际上,服务器上的大部分错误都是服务器吞下去的,调试服务端错误的最好办法是在调试菜单(DeBUG->Exceptions…)中使用Exception选项,配置调试器停止一切.NET异常,如果你选择了这个选项,你可以通过服务看到抛出的异常。@H_419_9@

我在本文的目标是展示ADO.NET Data Service是如何在Silverlight 2和基于服务的模块之间建立起连接的,现在你应该已经知道如何使用ADO.NET Data Service从服务器读取数据和往服务器写数据了,再也不用自己动手设计Web Service了,正如你所看到的,SilverlightADO.NET Data ServiceliNQ三者的组合让你可以创建强大的基于数据驱动的Web应用程序,具有Web 2.0技术的所有有点。@H_419_9@ 总结

以上是内存溢出为你收集整理的使用LINQ和ADO.NET创建Silverlight程序全部内容,希望文章能够帮你解决使用LINQ和ADO.NET创建Silverlight程序所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1047464.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存