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

在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只能与数据层“间接接触”。

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

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

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

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

 

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

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

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

 

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

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

以上就是.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 艾伟:Microsoft .NET RIA Services快速上手 ,Silverlight Tools 3.0艾伟:Microsoft .NET RIA Services快速上手 , Microsoft .NET RIA Services March '09 Preview艾伟:Microsoft .NET RIA Services快速上手 , SQL Server 2005
  2. 在VS2008中新建Silverlight项目
    艾伟:Microsoft .NET RIA Services快速上手
  3. 将Silverlight连接到ASP.NET Server project上
    艾伟:Microsoft .NET RIA Services快速上手
    完成该步骤后的Solution Explorer如下图所示
    艾伟:Microsoft .NET RIA Services快速上手
  4. 在Web项目上单击右键,新建艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手
  5. 选择SQL Server2005里的数据库和表。VS会帮我们生成一个ADO.NET的实体(Entity)。
    艾伟:Microsoft .NET RIA Services快速上手 艾伟:Microsoft .NET RIA Services快速上手
    生成的文件后缀名为.edmx,如本例中的艾伟:Microsoft .NET RIA Services快速上手
  6. 编译整个Solution。
  7. 再次在Web项目上右击,新增本文的主角——Domain Service Class艾伟:Microsoft .NET RIA Services快速上手 。"Domain Service Class”这名字挺熟的吧?嗯,上文介绍过了。
    艾伟:Microsoft .NET RIA Services快速上手 
    根据提示勾选需要的部分。在本例中,我们选择了Messages表作为实体,并选择”Enable editing”,这样在生成的类中会初始包括Get,Insert,Update,Delete 4个基本的实体操作方法
    艾伟:Microsoft .NET RIA Services快速上手
  8. 完成上面的操作后,会在Web项目下生成RdChat_DomainService.cs类。
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手Code
    艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手
    namespace RiaServices_1.Web
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手{
    艾伟:Microsoft .NET RIA Services快速上手    
    using System;
    艾伟:Microsoft .NET RIA Services快速上手    
    using System.Collections.Generic;
    艾伟:Microsoft .NET RIA Services快速上手    
    using System.ComponentModel;
    艾伟:Microsoft .NET RIA Services快速上手    
    using System.ComponentModel.DataAnnotations;
    艾伟:Microsoft .NET RIA Services快速上手    
    using System.Linq;
    艾伟:Microsoft .NET RIA Services快速上手    
    using System.Web.Ria;
    艾伟:Microsoft .NET RIA Services快速上手    
    using System.Web.Ria.Data;
    艾伟:Microsoft .NET RIA Services快速上手    
    using System.Web.DomainServices;
    艾伟:Microsoft .NET RIA Services快速上手    
    using System.Data;
    艾伟:Microsoft .NET RIA Services快速上手    
    using System.Web.DomainServices.LinqToEntities;
    艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手    
    // Implements application logic using the RdChatEntities context.
    艾伟:Microsoft .NET RIA Services快速上手    
    // TODO: Add your application logic to these methods or in additional methods.
    艾伟:Microsoft .NET RIA Services快速上手
        [EnableClientAccess()]
    艾伟:Microsoft .NET RIA Services快速上手    
    public class RdChat_DomainService : LinqToEntitiesDomainService<RdChatEntities>
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手    
    艾伟:Microsoft .NET RIA Services快速上手{
    艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手        
    // TODO: Consider
    艾伟:Microsoft .NET RIA Services快速上手        
    // 1. Adding parameters to this method and constraining returned results, and/or
    艾伟:Microsoft .NET RIA Services快速上手        
    // 2. Adding query methods taking different parameters.
    艾伟:Microsoft .NET RIA Services快速上手
            public IQueryable<Messages> GetMessages()
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手        
    艾伟:Microsoft .NET RIA Services快速上手{
    艾伟:Microsoft .NET RIA Services快速上手            
    return this.Context.Messages;
    艾伟:Microsoft .NET RIA Services快速上手        }

    艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手        
    public void InsertMessages(Messages messages)
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手        
    艾伟:Microsoft .NET RIA Services快速上手{
    艾伟:Microsoft .NET RIA Services快速上手            
    this.Context.AddToMessages(messages);
    艾伟:Microsoft .NET RIA Services快速上手        }

    艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手        
    public void UpdateMessages(Messages currentMessages, Messages originalMessages)
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手        
    艾伟:Microsoft .NET RIA Services快速上手{
    艾伟:Microsoft .NET RIA Services快速上手            
    this.Context.AttachAsModified(currentMessages, originalMessages);
    艾伟:Microsoft .NET RIA Services快速上手        }

    艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手        
    public void DeleteMessages(Messages messages)
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手        
    艾伟:Microsoft .NET RIA Services快速上手{
    艾伟:Microsoft .NET RIA Services快速上手            
    if ((messages.EntityState == EntityState.Detached))
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手            
    艾伟:Microsoft .NET RIA Services快速上手{
    艾伟:Microsoft .NET RIA Services快速上手                
    this.Context.Attach(messages);
    艾伟:Microsoft .NET RIA Services快速上手            }

    艾伟:Microsoft .NET RIA Services快速上手            
    this.Context.DeleteObject(messages);
    艾伟:Microsoft .NET RIA Services快速上手        }

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

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

    艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手
  9. 再次编译整个Solution。
  10. 点击Show All Files,你会发现系统已经为你生成了客户端的类,放在了Generated_Code目录下。我们将它include进来。
    艾伟:Microsoft .NET RIA Services快速上手 艾伟:Microsoft .NET RIA Services快速上手
    这个RiaServices_1.Web.g.cs,是服务端RdChat_DomainService.cs的对应客户端版本。它包含了所有服务端版本类中定义的方法、实体等,而可在客户端直接调用。它打代码如下:
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手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项目的艾伟:Microsoft .NET RIA Services快速上手 ,我们放入一些控件,做简单的界面。
    包括一个DataGrid ,TextBox和Button。在按钮中添加Click方法。
    艾伟:Microsoft .NET RIA Services快速上手UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"  x:Class="RiaServices_1.MainPage"
    艾伟:Microsoft .NET RIA Services快速上手    xmlns
    ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    艾伟:Microsoft .NET RIA Services快速上手    xmlns:x
    ="http://schemas.microsoft.com/winfx/2006/xaml" 
    艾伟:Microsoft .NET RIA Services快速上手    Width
    ="640" Height="450">
    艾伟:Microsoft .NET RIA Services快速上手    
    Grid x:Name="LayoutRoot" Background="White">
    艾伟:Microsoft .NET RIA Services快速上手        
    StackPanel Orientation="Vertical"  Height="420">
    艾伟:Microsoft .NET RIA Services快速上手            
    data:DataGrid x:Name="dataGrid" >data:DataGrid>
    艾伟:Microsoft .NET RIA Services快速上手            
    StackPanel Orientation="Horizontal" Height="25">
    艾伟:Microsoft .NET RIA Services快速上手                
    TextBox x:Name="txtMsg" Text=" " Width="580"  >TextBox>
    艾伟:Microsoft .NET RIA Services快速上手                
    Button x:Name="btnSend" Content="发送消息"  Click="btnSend_Click"  >Button>
    艾伟:Microsoft .NET RIA Services快速上手            
    StackPanel>
    艾伟:Microsoft .NET RIA Services快速上手        
    StackPanel>
    艾伟:Microsoft .NET RIA Services快速上手    
    Grid>
    艾伟:Microsoft .NET RIA Services快速上手
    UserControl>
    艾伟:Microsoft .NET RIA Services快速上手
  12. 在MainPage.xaml后台对应的cs文件艾伟:Microsoft .NET RIA Services快速上手 中,写入初始方法和处理按钮点击的方法。
    初始时,将所有已有的数据绑定到DataGrid中。
    点击Click方法后,将添加一条新数据到数据库,并刷新外观使新数据显示出来。
    在这个过程中,我们直接使用.NET RIA Services为你生成的客户端版本数据处理类了!很爽吧?
    艾伟:Microsoft .NET RIA Services快速上手using System;
    艾伟:Microsoft .NET RIA Services快速上手
    using System.Collections.Generic;
    艾伟:Microsoft .NET RIA Services快速上手
    using System.Linq;
    艾伟:Microsoft .NET RIA Services快速上手
    using System.Net;
    艾伟:Microsoft .NET RIA Services快速上手
    using System.Windows;
    艾伟:Microsoft .NET RIA Services快速上手
    using System.Windows.Controls;
    艾伟:Microsoft .NET RIA Services快速上手
    using System.Windows.Documents;
    艾伟:Microsoft .NET RIA Services快速上手
    using System.Windows.Input;
    艾伟:Microsoft .NET RIA Services快速上手
    using System.Windows.Media;
    艾伟:Microsoft .NET RIA Services快速上手
    using System.Windows.Media.Animation;
    艾伟:Microsoft .NET RIA Services快速上手
    using System.Windows.Shapes;
    艾伟:Microsoft .NET RIA Services快速上手
    using RiaServices_1.Web;
    艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手
    namespace RiaServices_1
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手{
    艾伟:Microsoft .NET RIA Services快速上手    
    public partial class MainPage : UserControl
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手    
    艾伟:Microsoft .NET RIA Services快速上手{
    艾伟:Microsoft .NET RIA Services快速上手        
    public MainPage()
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手        
    艾伟:Microsoft .NET RIA Services快速上手{
    艾伟:Microsoft .NET RIA Services快速上手            InitializeComponent();
    艾伟:Microsoft .NET RIA Services快速上手            RdChat_DomainContext context 
    = new RdChat_DomainContext();
    艾伟:Microsoft .NET RIA Services快速上手            dataGrid.ItemsSource 
    = context.Messages;
    艾伟:Microsoft .NET RIA Services快速上手            context.LoadMessages();
    艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手        }

    艾伟:Microsoft .NET RIA Services快速上手
    艾伟:Microsoft .NET RIA Services快速上手        
    private void btnSend_Click(object sender, RoutedEventArgs e)
    艾伟:Microsoft .NET RIA Services快速上手艾伟:Microsoft .NET RIA Services快速上手        
    艾伟:Microsoft .NET RIA Services快速上手{
    艾伟:Microsoft .NET RIA Services快速上手            Messages msg
    =new Messages();
    艾伟:Microsoft .NET RIA Services快速上手            msg.Body
    =txtMsg.Text;
    艾伟:Microsoft .NET RIA Services快速上手            msg.Name
    ="匿名用户";
    艾伟:Microsoft .NET RIA Services快速上手            msg.PartitionKey 
    = Guid.NewGuid().ToString();
    艾伟:Microsoft .NET RIA Services快速上手            msg.RowKey 
    = "slChat";
    艾伟:Microsoft .NET RIA Services快速上手            msg.Timestamp 
    = DateTime.Now;
    艾伟:Microsoft .NET RIA Services快速上手            RdChat_DomainContext context 
    = new RdChat_DomainContext();
    艾伟:Microsoft .NET RIA Services快速上手            context.Messages.Add(msg);
    艾伟:Microsoft .NET RIA Services快速上手            context.SubmitChanges();
    艾伟:Microsoft .NET RIA Services快速上手            dataGrid.ItemsSource 
    = context.Messages;
    艾伟:Microsoft .NET RIA Services快速上手            context.LoadMessages();
    艾伟:Microsoft .NET RIA Services快速上手        }

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

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

    艾伟:Microsoft .NET RIA Services快速上手
  13. F5  运行之!
    艾伟:Microsoft .NET RIA Services快速上手

 

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

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

 

___________肌肉萎缩的分割线_________

本例源代码:

RiaServices_1.rar

上一篇:艾伟:编写自文档化的代码


下一篇:一起谈.NET技术,Silverlight 4中把DataGrid数据导出Excel—附源码下载