かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

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でした。