かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

Hello world

前回:http://d.hatena.ne.jp/okazuki/20091014/1255532892

.NET RIA Servicesの環境を前回整えました。
今回は、はじめてということもあるので、Hello worldからはじめてみようと思います。

プロジェクトの作成

.NET RIA ServicesをインストールするとSilverlight Business Applicationというプロジェクトのテンプレートが出来ます。でも、別にこれを選択しなくても、.NET RIA Servicesは使えるので、気にせず普通にSilverlight Applicationを作成します。
プロジェクト名は「RIAHelloWorld」にしました。
ここで要注意!New Silverlight Applicationの画面でEnable .NET RIA Servicesのチェックをつけてください。
後で有効に出来ますが、.NET RIA ServicesのHello worldを作ろうと思っているので、ここでつけない道理はありません。
(図中のマウスカーソルの位置のチェックです)

Domain Serviceの作成

次に、Webアプリケーション側に、DomainServiceを作ります。
よく見るサンプルだとEntity Frameworkを対象にしていますが今回は最初なので、POCOベースでいってみようと思います。

新規作成からDomain Service Classを選択して、HelloWorldDomainServiceという名前で作成します。Add New Domain Service Classというダイアログが出てきますが、何もいじらずにOKを選択します。

HelloWorldDomainServiceという名前のクラスにGetMessageというメソッドを追加します。このメソッドは、Hello worldというメッセージを持つMessageクラスを返すようにします。

ということで、まず、Messageクラスの作成にとりかかります。.NET RIA Servicesでやりとりするためのクラスは、シリアライズ可能で、System.ComponentModel.DataAnnotations.KeyAttribute属性のついたプロパティが必要になります。シリアライズ可能は、デフォルトコンストラクタがあれば.NET 3.5あたり(SP1からかな?)から自動でシリアライズ可能になるらしいので、特に気にしなくても問題ありません。
Key属性をつけることだけ注意して作っていきます。

public class Message
{
    [Key]
    public string Text { get; set; }
}

これしきのコードに、新しいファイル作るのもめんどくさかったので、HelloWorldDomainServiceクラスの定義のあとに↑のコードを追加しました。
そして、HelloWorldDomainServiceクラスに、このMessageクラスを返すメソッドを追加します。

[EnableClientAccess()]
public class HelloWorldDomainService : DomainService
{
    // IQueryable<T>を返してGet〜という名前づけでメソッドを定義する
    public IQueryable<Message> GetMessage()
    {
        return new[] 
        {
            new Message { Text = "Hello world" }
        }.AsQueryable();
            
    }
}

これで、ビルドすると、Silverlightプロジェクトのほうに、以下のようなコードが自動生成されています。
(全てのファイルを表示にして、Generated_Codeフォルダを覗くと、見つかります)
ちょっと長いですけど、これだけ単純なものでも、こんなにコードを生成してくれているということを示すために全部のせます。

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:2.0.50727.3082
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace RIAHelloWorld
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Web.Ria.Data;
    using System.Windows.Ria.Data;
    
    
    /// <summary>
    /// Context for the RIA application.
    /// </summary>
    /// <remarks>
    /// This context extends the base to make application services and types available
    /// for consumption from code and xaml.
    /// </remarks>
    public sealed partial class RiaContext : System.Windows.Ria.RiaContextBase
    {
        
        #region Extensibility Method Definitions

        /// <summary>
        /// This method is invoked from the constructor once initialization is complete and
        /// can be used for further object setup.
        /// </summary>
        partial void OnCreated();

        #endregion
        
        
        /// <summary>
        /// Initializes a new instance of the RiaContext class.
        /// </summary>
        public RiaContext()
        {
            this.OnCreated();
        }
        
        /// <summary>
        /// Gets the context that is registered as a lifetime object with the current application.
        /// </summary>
        /// <exception cref="InvalidOperationException"> is thrown if there is no current application,
        /// no contexts have been added, or more than one context has been added.
        /// </exception>
        /// <seealso cref="Application.ApplicationLifetimeObjects"/>
        public new static RiaContext Current
        {
            get
            {
                return ((RiaContext)(System.Windows.Ria.RiaContextBase.Current));
            }
        }
    }
}
namespace RIAHelloWorld.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;
    
    
    public sealed partial class HelloWorldDomainContext : DomainContext
    {
        
        #region Extensibility Method Definitions

        /// <summary>
        /// This method is invoked from the constructor once initialization is complete and
        /// can be used for further object setup.
        /// </summary>
        partial void OnCreated();

        #endregion
        
        
        /// <summary>
        /// Default constructor.
        /// </summary>
        public HelloWorldDomainContext() : 
                this(new HttpDomainClient(new Uri("DataService.axd/RIAHelloWorld-Web-HelloWorldDomainService/", System.UriKind.Relative)))
        {
        }
        
        /// <summary>
        /// Constructor used to specify a data service URI.
        /// </summary>
        /// <param name="serviceUri">
        /// The HelloWorldDomainService data service URI.
        /// </param>
        public HelloWorldDomainContext(Uri serviceUri) : 
                this(new HttpDomainClient(serviceUri))
        {
        }
        
        /// <summary>
        /// Constructor used to specify a DomainClient instance.
        /// </summary>
        /// <param name="domainClient">
        /// The DomainClient instance the DomainContext should use.
        /// </param>
        public HelloWorldDomainContext(DomainClient domainClient) : 
                base(domainClient)
        {
            this.OnCreated();
        }
        
        public EntityList<Message> Messages
        {
            get
            {
                return base.Entities.GetEntityList<Message>();
            }
        }
        
        /// <summary>
        /// Returns an EntityQuery for query operation 'GetMessage'.
        /// </summary>
        public EntityQuery<Message> GetMessageQuery()
        {
            return base.CreateQuery<Message>("GetMessage", null, false, true);
        }
        
        protected override EntityContainer CreateEntityContainer()
        {
            return new HelloWorldDomainContextEntityContainer();
        }
        
        internal sealed class HelloWorldDomainContextEntityContainer : EntityContainer
        {
            
            public HelloWorldDomainContextEntityContainer()
            {
                this.CreateEntityList<Message>(EntityListOperations.None);
            }
        }
    }
    
    [DataContract(Namespace="http://schemas.datacontract.org/2004/07/RIAHelloWorld.Web")]
    public sealed partial class Message : Entity
    {
        
        private string _text;
        
        #region Extensibility Method Definitions

        /// <summary>
        /// This method is invoked from the constructor once initialization is complete and
        /// can be used for further object setup.
        /// </summary>
        partial void OnCreated();
        partial void OnTextChanging(string value);
        partial void OnTextChanged();

        #endregion
        
        
        /// <summary>
        /// Default constructor.
        /// </summary>
        public Message()
        {
            this.OnCreated();
        }
        
        [DataMember()]
        [Key()]
        public string Text
        {
            get
            {
                return this._text;
            }
            set
            {
                if ((this._text != value))
                {
                    this.ValidateProperty("Text", value);
                    this.OnTextChanging(value);
                    this.RaiseDataMemberChanging("Text");
                    this._text = value;
                    this.RaiseDataMemberChanged("Text");
                    this.OnTextChanged();
                }
            }
        }
        
        public override object GetIdentity()
        {
            return this._text;
        }
    }
}

まず、Messageクラスの定義が一番下にされています。PropertyChangedイベントの実装もされていますし、DataAnnotationを使ったValidationのロジックも組み込まれています。
そして、HelloWorldDomainContextクラスにGetMessageQueryというメソッドが作成されています。これが、サーバーサイドのIQueriable GetMessage()に対応しています。


これを使うコードをSilverlightプロジェクトに書いていきます。
今回はViewModelとか考えずに、WindowsFormのときに近いプログラミングモデルで書いていこうと思います。
まず、画面にTextBlockとButtonをおきます。TextBlockはtextBlockMessageという名前をつけて、Buttonは、GetMessageButton_Clickイベントを登録しておきます。

<UserControl x:Class="RIAHelloWorld.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <StackPanel x:Name="LayoutRoot">
        <TextBlock x:Name="textBlockMessage" />
        <Button Content="GetMessage" Click="GetMessageButton_Click" />
    </StackPanel>
</UserControl>

そして、Clickイベントハンドラに、.NET RIA ServicesからHello worldを取得するコードを書きます。

using System.Linq;
using System.Windows;
using System.Windows.Controls;
using RIAHelloWorld.Web;

namespace RIAHelloWorld
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void GetMessageButton_Click(object sender, RoutedEventArgs e)
        {
            // contextを作って
            var context = new HelloWorldDomainContext();
            // queryを取得して
            var query = context.GetMessageQuery();
            // 読み込み
            context.Load(query, (result) =>
                {
                    // 読み込み結果はコールバックで取得する
                    var message = result.Entities.FirstOrDefault();
                    // 結果が返ってきてたらメッセージを表示する
                    textBlockMessage.Text = message != null ? message.Text : "失敗";
                }, null);
        }
    }
}

このコードからわかるように、サーバーサイドのデータ取得系のメソッドは、〜Queryメソッドを呼び出して、クエリを取得し、Loadメソッドを使ってクエリを読み込みます。

実行してみます!

ボタンを押すと・・・

Hello worldが表示されます。

ということで、Hello worldでした。