技术开发 频道

Microsoft .NET RIA Services快速上手

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

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

  在去年这个时候,Silverlight 2Beta刚发布,有个朋友问我能不能使用Silverlight直接操作数据库。当时的答案当然是:很遗憾,不行。我们不得不使用大量的Web Services或者WCF来提供对数据库操作的每一个环节,Silverlight只能与数据层“间接接触”。
 
 
 
  上图表明了整个过程。这样的数据操作虽然已经被大家习惯,但它是不合理的。就像是在实现“三通”以前,咱们去台湾只能先去香港转机。

  这是因为,这样的“间接接触”,不仅不直观,还浪费了开发者大量的经理去考虑一些不该考虑的问题。开发者需要在客户端、Web Service端,BLL端各写一个不同版本的数据操作代码,并且还要考虑他们之间交互的安全性、网络情况等等,简直就是一个浪费大量ATP只产生微量GDP的过程。
 
  合理的数据操作应该怎样的
 
 
 
  上图展示了微软在RIA与数据库交互上的宏伟构想:无论是Silverlight,WPF,Javascript,还是ASP.NET,WCF,它们都应该使用无差别的数据逻辑,能够直接访问到数据层面,而不需要通过一层类似“代理”的数据服务。
 
  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 silerlight_sdk.exe ,Silverlight Tools 3.0 silverlight3_tools.exe , Microsoft .NET RIA Services March '09 Preview RiServices.msi , SQL Server 2005

  2.在VS2008中新建Silverlight项目

 

  3.将Silverlight连接到ASP.NET Server project上



  完成该步骤后的Solution Explorer如下图所示

 

  在Web项目上单击右键,新建ADO.NET Entity Data Mode

 

  选择SQL Server2005里的数据库和表。VS会帮我们生成一个ADO.NET的实体(Entity)。

未命名5 

未命名6 

  生成的文件后缀名为.edmx,如本例中的RiaServiceModel.edmx

  1.编译整个Solution。

  2.再次在Web项目上右击,新增本文的主角——Domain Service Class Domain Service Class 。"Domain Service Class”这名字挺熟的吧?嗯,上文介绍过了。

未命名7 

  根据提示勾选需要的部分。在本例中,我们选择了Messages表作为实体,并选择”Enable editing”,这样在生成的类中会初始包括Get,Insert,Update,Delete 4个基本的实体操作方法

未命名8

  完成上面的操作后,会在Web项目下生成RdChat_DomainService.cs类。

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);
        }
    }
}

  再次编译整个Solution。

  点击Show All Files,你会发现系统已经为你生成了客户端的类,放在了Generated_Code目录下。我们将它include进来。

无标题10 无标题11

  这个RiaServices_1.Web.g.cs,是服务端RdChat_DomainService.cs的对应客户端版本。它包含了所有服务端版本类中定义的方法、实体等,而可在客户端直接调用。它打代码如下:

//------------------------------------------------------------------------------
//
//     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);
            }
        }
    }
}

  打开Silverlight项目的 MainPage.xaml ,我们放入一些控件,做简单的界面。

  包括一个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>

  在MainPage.xaml后台对应的cs文件 MainPage.xaml.cs 中,写入初始方法和处理按钮点击的方法。

  初始时,将所有已有的数据绑定到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();
        }
    }
}

  F5  运行之!

未命名12

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