艾伟:Microsoft .NET RIA Services快速上手

简介: 在MIX 09上,Nikhil Kothari发布了微软的一神作——Microsoft .NET RIA Services。虽然目前的版本仅仅是可怜的"March '09 Preview”,但它已经足够让人兴奋不已。

在MIX 09上,Nikhil Kothari发布了微软的一神作——Microsoft .NET RIA Services。虽然目前的版本仅仅是可怜的"March '09 Preview”,但它已经足够让人兴奋不已。简单地说,在这之前,如果你用到了现在的RIA技术比如Silverlight,你只能选择写大量的服务或者WCF来实现数据的操作功能;而有了.NET RIA Services,你在RIA项目上操作数据,就像ASP.NET那样方便!

Nikhil Kothari在MIX09上介绍.NET RIA Services的视频:

http://www.nikhilk.net/RIA-Services-MIX09.aspx

 

Microsoft .NET RIA Services March '09 Preview及文档下载地址:

http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=76bb3a07-3846-4564-b0c3-27972bcaabce

 

MSDN Code Gallery中的.NET RIA Services Samples

http://code.msdn.microsoft.com/RiaServices

好了,以上是概要,下面让我们说得更详细些。

 

传统的RIA是怎样操作数据的

在去年这个时候,Silverlight 2Beta刚发布,有个朋友问我能不能使用Silverlight直接操作数据库。当时的答案当然是:很遗憾,不行。我们不得不使用大量的Web Services或者WCF来提供对数据库操作的每一个环节,Silverlight只能与数据层“间接接触”。

image

上图表明了整个过程。这样的数据操作虽然已经被大家习惯,但它是不合理的。就像是在实现“三通”以前,咱们去台湾只能先去香港转机。

博客园的大牛Shareach前几天写了一个Silverlight的聊天程序,数据操作使用的是WCF Duplex Service实现双向通讯,非常牛,大家可以去看看。(围观连接:http://www.cnblogs.com/yinpengxiang/archive/2009/03/23/slChat.html)这是Silverlight操作数据层的一个成功案例,但也会让人觉得悲哀:这样一个表面上很简单的聊天程序,为什么有了WCF的参与就变得很复杂?

这是因为,这样的“间接接触”,不仅不直观,还浪费了开发者大量的经理去考虑一些不该考虑的问题。开发者需要在客户端、Web Service端,BLL端各写一个不同版本的数据操作代码,并且还要考虑他们之间交互的安全性、网络情况等等,简直就是一个浪费大量ATP只产生微量GDP的过程。

 

合理的数据操作应该怎样的

image

上图展示了微软在RIA与数据库交互上的宏伟构想:无论是Silverlight,WPF,Javascript,还是ASP.NET,WCF,它们都应该使用无差别的数据逻辑,能够直接访问到数据层面,而不需要通过一层类似“代理”的数据服务。

 

Microsoft .NET RIA Services将如何实现“合理”

image

以上就是.NET RIA Services的实现原理。开发者在ASP.NET端的数据处理类(本图中是HRService)继承自一个叫做DomainService的类,在里面实现一些数据操作。.NET RIA Services就会自动生成相应的客户端类(本图中是HRContext)。而在我们开发客户端的时候,我们就可以直接调用.NET RIA Services生成的那个类,直接操作数据层面。

 

入门实例:
在了解.NET RIA Services想要完成的任务及其具体实现方法后,我们可以开始通过实例的方式来体验一下了。

  1. 开发环境:Visual Studio 2008 SP1 ,Silverlight 3 Beta SDK image ,Silverlight Tools 3.0image , Microsoft .NET RIA Services March '09 Previewimage , SQL Server 2005
  2. 在VS2008中新建Silverlight项目
    无标题
  3. 将Silverlight连接到ASP.NET Server project上
    无标题1
    完成该步骤后的Solution Explorer如下图所示
    无标题3
  4. 在Web项目上单击右键,新建image
    未命名4
  5. 选择SQL Server2005里的数据库和表。VS会帮我们生成一个ADO.NET的实体(Entity)。
    未命名5 未命名6
    生成的文件后缀名为.edmx,如本例中的image
  6. 编译整个Solution。
  7. 再次在Web项目上右击,新增本文的主角——Domain Service Classimage 。"Domain Service Class”这名字挺熟的吧?嗯,上文介绍过了。
    未命名7 
    根据提示勾选需要的部分。在本例中,我们选择了Messages表作为实体,并选择”Enable editing”,这样在生成的类中会初始包括Get,Insert,Update,Delete 4个基本的实体操作方法
    未命名8
  8. 完成上面的操作后,会在Web项目下生成RdChat_DomainService.cs类。
    Code

    namespace RiaServices_1.Web
    {
        
    using System;
        
    using System.Collections.Generic;
        
    using System.ComponentModel;
        
    using System.ComponentModel.DataAnnotations;
        
    using System.Linq;
        
    using System.Web.Ria;
        
    using System.Web.Ria.Data;
        
    using System.Web.DomainServices;
        
    using System.Data;
        
    using System.Web.DomainServices.LinqToEntities;


        
    // Implements application logic using the RdChatEntities context.
        
    // TODO: Add your application logic to these methods or in additional methods.
        [EnableClientAccess()]
        
    public class RdChat_DomainService : LinqToEntitiesDomainService<RdChatEntities>
        
    {

            
    // TODO: Consider
            
    // 1. Adding parameters to this method and constraining returned results, and/or
            
    // 2. Adding query methods taking different parameters.
            public IQueryable<Messages> GetMessages()
            
    {
                
    return this.Context.Messages;
            }


            
    public void InsertMessages(Messages messages)
            
    {
                
    this.Context.AddToMessages(messages);
            }


            
    public void UpdateMessages(Messages currentMessages, Messages originalMessages)
            
    {
                
    this.Context.AttachAsModified(currentMessages, originalMessages);
            }


            
    public void DeleteMessages(Messages messages)
            
    {
                
    if ((messages.EntityState == EntityState.Detached))
                
    {
                    
    this.Context.Attach(messages);
                }

                
    this.Context.DeleteObject(messages);
            }

        }

    }



  9. 再次编译整个Solution。
  10. 点击Show All Files,你会发现系统已经为你生成了客户端的类,放在了Generated_Code目录下。我们将它include进来。
    无标题10 无标题11
    这个RiaServices_1.Web.g.cs,是服务端RdChat_DomainService.cs的对应客户端版本。它包含了所有服务端版本类中定义的方法、实体等,而可在客户端直接调用。它打代码如下:
    Code
    //------------------------------------------------------------------------------
    // 
    //     This code was generated by a tool.
    //     Runtime Version:2.0.50727.3053
    //
    //     Changes to this file may cause incorrect behavior and will be lost if
    //     the code is regenerated.
    // 
    //------------------------------------------------------------------------------

    namespace RiaServices_1.Web
    {
        
    using System;
        
    using System.Collections.Generic;
        
    using System.ComponentModel;
        
    using System.ComponentModel.DataAnnotations;
        
    using System.Linq;
        
    using System.Runtime.Serialization;
        
    using System.Web.Ria.Data;
        
    using System.Windows.Ria.Data;
        
        
        [DataContract(Namespace
    ="http://schemas.datacontract.org/2004/07/RiaServices_1.Web")]
        
    public sealed partial class Messages : Entity
        {
            
            
    private string _body;
            
            
    private string _name;
            
            
    private string _partitionKey;
            
            
    private string _rowKey;
            
            
    private Nullable<DateTime> _timestamp;
            
            [DataMember()]
            
    public string Body
            {
                
    get
                {
                    
    return this._body;
                }
                
    set
                {
                    
    if ((this._body != value))
                    {
                        
    this.ValidateProperty("Body", value);
                        
    this.RaiseDataMemberChanging("Body");
                        
    this._body = value;
                        
    this.RaiseDataMemberChanged("Body");
                    }
                }
            }
            
            [DataMember()]
            
    public string Name
            {
                
    get
                {
                    
    return this._name;
                }
                
    set
                {
                    
    if ((this._name != value))
                    {
                        
    this.ValidateProperty("Name", value);
                        
    this.RaiseDataMemberChanging("Name");
                        
    this._name = value;
                        
    this.RaiseDataMemberChanged("Name");
                    }
                }
            }
            
            [DataMember()]
            [Key()]
            
    public string PartitionKey
            {
                
    get
                {
                    
    return this._partitionKey;
                }
                
    set
                {
                    
    if ((this._partitionKey != value))
                    {
                        
    this.ValidateProperty("PartitionKey", value);
                        
    this.RaiseDataMemberChanging("PartitionKey");
                        
    this._partitionKey = value;
                        
    this.RaiseDataMemberChanged("PartitionKey");
                    }
                }
            }
            
            [DataMember()]
            [Key()]
            
    public string RowKey
            {
                
    get
                {
                    
    return this._rowKey;
                }
                
    set
                {
                    
    if ((this._rowKey != value))
                    {
                        
    this.ValidateProperty("RowKey", value);
                        
    this.RaiseDataMemberChanging("RowKey");
                        
    this._rowKey = value;
                        
    this.RaiseDataMemberChanged("RowKey");
                    }
                }
            }
            
            [DataMember()]
            
    public Nullable<DateTime> Timestamp
            {
                
    get
                {
                    
    return this._timestamp;
                }
                
    set
                {
                    
    if ((this._timestamp != value))
                    {
                        
    this.ValidateProperty("Timestamp", value);
                        
    this.RaiseDataMemberChanging("Timestamp");
                        
    this._timestamp = value;
                        
    this.RaiseDataMemberChanged("Timestamp");
                    }
                }
            }
            
            
    public override object GetIdentity()
            {
                
    return EntityKey.Create(this._partitionKey, this._rowKey);
            }
        }
        
        
    public sealed partial class RdChat_DomainContext : DomainContext
        {
            
            
    #region Query root fields
            
    private static IQueryable<Messages> _MessagesQuery = new Messages[0].AsQueryable();
            
    #endregion
            
            
    /// 
            
    /// Default constructor.
            
    /// 
            public RdChat_DomainContext() : 
                    
    base(new HttpDomainClient(new Uri("DataService.axd/RiaServices_1-Web-RdChat_DomainService/", System.UriKind.Relative)))
            {
            }
            
            
    /// 
            
    /// Constructor used to specify a data service URI.
            
    /// 
            
    /// 
            
    /// The RdChat_DomainService data service URI.
            
    /// 
            public RdChat_DomainContext(Uri serviceUri) : 
                    
    base(new HttpDomainClient(serviceUri))
            {
            }
            
            
    /// 
            
    /// Constructor used to specify a DomainClient instance.
            
    /// 
            
    /// 
            
    /// The DomainClient instance the DomainContext should use.
            
    /// 
            public RdChat_DomainContext(DomainClient domainClient) : 
                    
    base(domainClient)
            {
            }
            
            
    public EntityList<Messages> Messages
            {
                
    get
                {
                    
    return this.Entities.GetEntityList<Messages>();
                }
            }
            
            
    #region Query root properties
            
    public static IQueryable<Messages> MessagesQuery
            {
                
    get
                {
                    
    return _MessagesQuery;
                }
            }
            
    #endregion
            
            
    #region LoadMessages method overloads
            
    /// 
            
    /// Invokes the server-side method 'GetMessages' and loads the result into .
            
    /// 
            [LoadMethod(typeof(Messages))]
            
    public void LoadMessages(IQueryable<Messages> query, MergeOption mergeOption, object userState)
            {
                
    base.Load("GetMessages"null, query, mergeOption, userState);
            }
            
            
    /// 
            
    /// Invokes the server-side method 'GetMessages' and loads the result into .
            
    /// 
            [LoadMethod(typeof(Messages))]
            
    public void LoadMessages()
            {
                
    this.LoadMessages(null, MergeOption.KeepCurrentValues, null);
            }
            
            
    /// 
            
    /// Invokes the server-side method 'GetMessages' and loads the result into .
            
    /// 
            [LoadMethod(typeof(Messages))]
            
    public void LoadMessages(IQueryable<Messages> query, object userState)
            {
                
    this.LoadMessages(query, MergeOption.KeepCurrentValues, userState);
            }
            
    #endregion
            
            
    protected override EntityContainer CreateEntityContainer()
            {
                
    return new RdChat_DomainContextEntityContainer();
            }
            
            
    internal sealed class RdChat_DomainContextEntityContainer : EntityContainer
            {
                
                
    public RdChat_DomainContextEntityContainer()
                {
                    
    this.CreateEntityList<Messages>(EntityListOperations.All);
                }
            }
        }
    }
  11. 打开Silverlight项目的image ,我们放入一些控件,做简单的界面。
    包括一个DataGrid ,TextBox和Button。在按钮中添加Click方法。
    < UserControl  xmlns:data ="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"   x:Class ="RiaServices_1.MainPage"
        xmlns
    ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x
    ="http://schemas.microsoft.com/winfx/2006/xaml"  
        Width
    ="640"  Height ="450" >
        
    < Grid  x:Name ="LayoutRoot"  Background ="White" >
            
    < StackPanel  Orientation ="Vertical"   Height ="420" >
                
    < data:DataGrid  x:Name ="dataGrid"   > data:DataGrid>
                
    <StackPanel Orientation="Horizontal" Height="25">
                    
    <TextBox x:Name="txtMsg" Text=" " Width="580"  > TextBox>
                    
    <Button x:Name="btnSend" Content="发送消息"  Click="btnSend_Click"  > Button>
                
    StackPanel>
            
    StackPanel>
        
    Grid>
    UserControl>
  12. 在MainPage.xaml后台对应的cs文件image 中,写入初始方法和处理按钮点击的方法。
    初始时,将所有已有的数据绑定到DataGrid中。
    点击Click方法后,将添加一条新数据到数据库,并刷新外观使新数据显示出来。
    在这个过程中,我们直接使用.NET RIA Services为你生成的客户端版本数据处理类了!很爽吧?
    using  System;
    using  System.Collections.Generic;
    using  System.Linq;
    using  System.Net;
    using  System.Windows;
    using  System.Windows.Controls;
    using  System.Windows.Documents;
    using  System.Windows.Input;
    using  System.Windows.Media;
    using  System.Windows.Media.Animation;
    using  System.Windows.Shapes;
    using  RiaServices_1.Web;

    namespace  RiaServices_1
    {
        
    public partial class MainPage : UserControl
        
    {
            
    public MainPage()
            
    {
                InitializeComponent();
                RdChat_DomainContext context 
    = new RdChat_DomainContext();
                dataGrid.ItemsSource 
    = context.Messages;
                context.LoadMessages();

            }


            
    private void btnSend_Click(object sender, RoutedEventArgs e)
            
    {
                Messages msg
    =new Messages();
                msg.Body
    =txtMsg.Text;
                msg.Name
    ="匿名用户";
                msg.PartitionKey 
    = Guid.NewGuid().ToString();
                msg.RowKey 
    = "slChat";
                msg.Timestamp 
    = DateTime.Now;
                RdChat_DomainContext context 
    = new RdChat_DomainContext();
                context.Messages.Add(msg);
                context.SubmitChanges();
                dataGrid.ItemsSource 
    = context.Messages;
                context.LoadMessages();
            }

        }

    }

  13. F5  运行之!
    未命名12

 

怎样?非常方便吧?其实这只是一个非常简易的实例而已, .NET RIA Services还包括了许许多多强大的功能,包括metadata,shared code等等等等,请感兴趣的读者自行阅读SDK吧,那是一件非常享受的事情。

现在让我们再次回过头来看Shareach前几天写的那个Silverlight+WCF聊天程序,如果他用上.NET RIA Services的话,是不是会容易很多呢?

 

___________肌肉萎缩的分割线_________

本例源代码:

RiaServices_1.rar

目录
相关文章
|
5月前
|
存储 对象存储 Python
`openpyxl`是一个用于读写Excel 2010 xlsx/xlsm/xltx/xltm文件的Python库。它不需要Microsoft Excel,也不需要.NET或COM组件。
`openpyxl`是一个用于读写Excel 2010 xlsx/xlsm/xltx/xltm文件的Python库。它不需要Microsoft Excel,也不需要.NET或COM组件。
|
7月前
|
安全 C# 开发者
.NET开源的一键自动化下载、安装、激活Microsoft Office利器
.NET开源的一键自动化下载、安装、激活Microsoft Office利器
170 5
|
7月前
|
SQL 数据库连接 数据库
VB.NET 中使用SqlConnection类连接到Microsoft SQL Server数据库的详细步骤
VB.NET 中使用SqlConnection类连接到Microsoft SQL Server数据库的详细步骤
337 0
|
开发框架 .NET 数据库
asp.net企业费用报销管理信息系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio
asp.net 企业费用报销管理信息系统是一套完善的web设计管理系统,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为vs2010,数据库为sqlserver2008,使 用c#语言开发 应用技术:asp.net c#+sqlserver 开发工具:vs2010 +sqlserver
108 0
|
XML 开发工具 数据格式
Microsoft .NET:Microsoft .NET之.net4.5.1简介、安装、使用方法之详细攻略
Microsoft .NET:Microsoft .NET之.net4.5.1简介、安装、使用方法之详细攻略
|
物联网
Microsoft .NET Gadgeteer 简介及其它
Microsoft .NET Gadgeteer 为开发小型电子模块或嵌入式设备的用户,提供一个快速构建原型机的平台。它结合了面向对象编程的优点,提供一系列电子模块,可以快速地用这些模块进行计算机辅助设计。
760 0
|
Web App开发 Windows
Microsoft .NET Framework引起的浏览器登录窗口空白
Microsoft .NET Framework引起的浏览器登录窗口空白 问题截图 1、在浏览器窗口点击左下角-黄色感叹号“完成”(如问题截图)-会有窗口弹出提示“Sys'未定义和'Silverlight'未定义”(如图1-1)。
953 0
|
数据安全/隐私保护
重装Microsoft .NET Framework 4.0
重装Microsoft .NET Framework 4.0 1、开始-输入CMD-回车(如图1-1)。 图 1-1 2、在弹出的黑屏窗口输入control回车(如图1-2)。
1517 0
|
C# Windows
怎样使用.NET RIA Services 创建 Silverlight Business Application(一)
HRApp 项目是.NET RIA Services MSDN  上的案例,一个使用Silverlight 3.0 + .Net RIA Service 构建的完整商业应用程序, 学习英文版的开发手册总是比较费力的,所以顺带着简略翻译一下,也有部分自己心得体会,不足之处还望大家多多交流。
1069 0
|
3月前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
51 7