ネタ元→
WCF から得られた Entities に対して LINQ は発行できるでしょうか?
そういえば、Silverlight + WCF RIA Services + ADO.NET Entity Framework を組み合わせたサンプルってあんまり見ないですね。ネタ元のスレッドでも要望されているので、ちょっとこしらえてみました。WCF RIA Services ちゃんと触ったの今回が初めてなんで、Silverlight からサービスを呼び出す部分はあんまり自信ない。
紙面の都合で Visual Studio が生成してくれる Entity Data Model のコードは省略。自分が書いた部分だけを掲載します。作成したプロジェクトは、後で github に公開する予定です。記事の最後に、作成したプロジェクトへのリンクを貼っています。
CustomerDomainService
取得用メソッドはウィザードが生成してくれたので、保存と削除を追加。クライアントから呼び出せるように、Invoke 属性をつけています。
namespace RiaSample.Web { using System; using System.Data; using System.Linq; using System.ServiceModel.DomainServices.EntityFramework; using System.ServiceModel.DomainServices.Hosting; using System.ServiceModel.DomainServices.Server; /// <summary> /// SQL Server の操作に ADO.NET Entity Framework を使う DomainService。 /// </summary> [EnableClientAccess()] public class CustomerDomainService : LinqToEntitiesDomainService<TestEntities> { /// <summary> /// 得意先を全件取得します。 /// </summary> /// <returns>全ての得意先データ。</returns> public IQueryable<Customer> GetCustomer() { return this.ObjectContext.Customer; } // ここから下は追加分。 // クライアントから呼び出せるように Invoke 属性をつけておく。 /// <summary> /// 得意先を追加または更新します。 /// </summary> /// <param name="customer">追加または更新する得意先。</param> [Invoke] public void InsertOrUpdate(Customer customer) { var target = (from c in ObjectContext.Customer where c.Id == customer.Id select c).FirstOrDefault(); if (null != target) { // 更新 target.Name = customer.Name; target.Updated = DateTime.Now; ObjectContext.SaveChanges(); } else { // 追加 customer.Updated = DateTime.Now; ObjectContext.Customer.AddObject(customer); ObjectContext.SaveChanges(); } } /// <summary> /// 得意先を削除します。 /// </summary> /// <param name="customer">削除する得意先。</param> [Invoke] public void DeleteCustomer(Customer customer) { if (customer.EntityState == EntityState.Detached) { ObjectContext.Customer.Attach(customer); } ObjectContext.Customer.DeleteObject(customer); ObjectContext.SaveChanges(); } } }
MainViewModel
サービス呼び出しを Window にべた書きしたくなかったので、MVVM パターンを適用。データの保持と操作、サービスの呼び出しを、ViewModel に隠ぺいしています。
using System; using System.ComponentModel; using System.Linq; using System.ServiceModel.DomainServices.Client; using System.Windows; using RiaSample.Web; namespace RiaSample { public class MainViewModel : INotifyPropertyChanged { /// <summary> /// 表示中のデータ。 /// </summary> public Customer _currentCustomer = new Customer(); /// <summary> /// 表示中のデータを取得または設定します。 /// </summary> public Customer CurrentCustomer { get { return _currentCustomer; } set { if (_currentCustomer!=value) { _currentCustomer = value; OnPropertyChanged("CurrentCustomer"); } } } /// <summary> /// 検索対象データのID /// </summary> private int _findId = 0; /// <summary> /// 検索対象データのIDを取得または設定します。 /// </summary> public int FindId { get { return _findId; } set { if (_findId != value) { _findId = value; OnPropertyChanged("FindId"); } } } /// <summary> /// サービスからデータを取得します。 /// </summary> public void Load() { CustomerDomainContext context = new CustomerDomainContext(); LoadOperation<Customer> operation = context.Load(context.GetCustomerQuery()); operation.Completed += (sender, e) => { LoadOperation<Customer> op = (LoadOperation<Customer>)sender; CurrentCustomer = (from customer in op.Entities where customer.Id == FindId select customer).FirstOrDefault(); }; } /// <summary> /// メッセージを表示。 /// ※単体テストができるように、差し替え可能にしておく。 /// </summary> internal Action<string> ShowMessageBox = message => { MessageBox.Show(message); }; /// <summary> /// 表示を新しいデータに差し替えます。 /// </summary> public void New() { CurrentCustomer = new Customer(); } /// <summary> /// 編集された内容をサービスに保存します。 /// </summary> public void Save() { if (null == CurrentCustomer) { return; } CustomerDomainContext context = new CustomerDomainContext(); InvokeOperation operation = context.InsertOrUpdate(CurrentCustomer); operation.Completed += (sender, e) => { InvokeOperation op = (InvokeOperation)sender; if (op.HasError) { ShowMessageBox(op.Error.Message); } else { ShowMessageBox("保存に成功しました。"); } }; } /// <summary> /// 表示中のデータを削除します。 /// </summary> public void Delete() { if (null == CurrentCustomer) { return; } CustomerDomainContext context = new CustomerDomainContext(); InvokeOperation operation = context.DeleteCustomer(CurrentCustomer); operation.Completed += (sender, e) => { InvokeOperation op = (InvokeOperation)sender; if (op.HasError) { ShowMessageBox(op.Error.Message); } else { ShowMessageBox("削除に成功しました。"); } New(); }; } /// <summary> /// プロパティが変更されたときに発生します。 /// </summary> public event PropertyChangedEventHandler PropertyChanged; /// <summary> /// PropertyChanged イベントを発生させます。 /// </summary> /// <param name="propertyName">プロパティ名。</param> protected void OnPropertyChanged(string propertyName) { var h = PropertyChanged; if (null != h) { h(this, new PropertyChangedEventArgs(propertyName)); } } } }
MainWindow
MainViewModel が持つデータの表示と、MainViewModel のメソッドを呼び出すだけの、簡単なお仕事。
<UserControl x:Class="RiaSample.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:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0"> <TextBlock Text="SearchId"/> <TextBox Text="{Binding Path=FindId, Mode=TwoWay}"/> <TextBlock Text="Id"/> <TextBox Text="{Binding Path=CurrentCustomer.Id}" IsReadOnly="True"/> <TextBlock Text="Name"/> <TextBox Text="{Binding Path=CurrentCustomer.Name, Mode=TwoWay}"/> </StackPanel> <StackPanel Grid.Row="1" Orientation="Horizontal"> <Button Content="New" Click="NewButtonClick"/> <Button Content="Load" Click="LoadButtonClick"/> <Button Content="Save" Click="SaveButtonClick"/> <Button Content="Delete" Click="DeleteButtonClick"/> </StackPanel> </Grid> </UserControl>
using System.Windows; using System.Windows.Controls; namespace RiaSample { public partial class MainPage : UserControl { /// <summary> /// ビューモデル。サービス呼び出しやデータの保持を隠ぺい。 /// </summary> private readonly MainViewModel _viewModel; public MainPage() { InitializeComponent(); _viewModel = new MainViewModel(); DataContext = _viewModel; } // ビューモデルのメソッドを呼び出すだけ。 private void NewButtonClick(object sender, RoutedEventArgs e) { _viewModel.New(); } private void LoadButtonClick(object sender, RoutedEventArgs e) { _viewModel.Load(); } private void SaveButtonClick(object sender, RoutedEventArgs e) { _viewModel.Save(); } private void DeleteButtonClick(object sender, RoutedEventArgs e) { _viewModel.Delete(); } } }
作成したプロジェクト
下のリンクからダウンロードできます。