前回: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
これを使うコードを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でした。