博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

[索引页]
[源码下载]


稳扎稳打Silverlight(58) - 4.0通信之WCF RIA Services: 通过 Domain Service, 以 MVVM 模式实现数据的添加、删除、修改和查询



作者:webabcd


介绍
Silverlight 4.0 之 WCF RIA Services:DomainService 和 MVVM


在线DEMO
http://www.cnblogs.com/webabcd/archive/2010/08/09/1795417.html


示例
演示如何通过 Domain Service, 以 MVVM 模式实现数据的添加、删除、修改和查询
1、服务端
MyDomainService.cs

代码
/*
 * 一个 Domain Service 服务
 
*/

namespace Silverlight40.Web.Service
{
    
using System;
    
using System.Collections.Generic;
    
using System.ComponentModel;
    
using System.ComponentModel.DataAnnotations;
    
using System.Data;
    
using System.Linq;
    
using System.ServiceModel.DomainServices.EntityFramework;
    
using System.ServiceModel.DomainServices.Hosting;
    
using System.ServiceModel.DomainServices.Server;
    
using Silverlight40.Web.Model;

    
/*
     * 用 LinqToEntities 实现 Domain Service 则继承 LinqToSqlDomainService<NorthwindEntities>;用 LinqToSql 实现 Domain Service 则继承 LinqToSqlDomainService<NorthwindEntities>
     * Domain Service 内所有对客户端可见的方法都应该是 public 的,Domain Service 内的方法不支持重载
     * 对客户端可见的方法要满足命名约定,或为其指定对应的 Attribute。当前支持 6 种数据操作:Query, Update, Insert, Delete, Invoke, Named Update, 详见文档
     * 
     * [EnableClientAccess()] - 该类对客户端可见
     * [EnableClientAccess(RequiresSecureEndpoint = true)] - 使用 HTTPS 协议
     * [Ignore] - 指定的方法不作为服务而公开
     * [Query(IsComposable=true)] - 支持客户端的 linq 查询,而且此查询会被在服务端执行
     * 
     * 在多个 Domain Services 间共享实体:通过 [ExternalReference], [Association()], Context.AddReference() 实现,详见文档
     
*/

    
// 服务端的类名为:MyDomainService,则其生成的客户端上下文的类名为:MyDomainContext
    [EnableClientAccess()]
    
public class MyDomainService : LinqToEntitiesDomainService<NorthwindEntities>
    {
        [Query(IsDefault 
= true)]
        
public IQueryable<Category> GetCategories()
        {
            
return this.ObjectContext.Categories;
        }

        
public void InsertCategory(Category category)
        {
            
if ((category.EntityState != EntityState.Detached))
            {
                
this.ObjectContext.ObjectStateManager.ChangeObjectState(category, EntityState.Added);
            }
            
else
            {
                
this.ObjectContext.Categories.AddObject(category);
            }
        }

        
public void UpdateCategory(Category currentCategory)
        {
            
this.ObjectContext.Categories.AttachAsModified(currentCategory, this.ChangeSet.GetOriginal(currentCategory));
        }

        
public void DeleteCategory(Category category)
        {
            
if ((category.EntityState == EntityState.Detached))
            {
                
this.ObjectContext.Categories.Attach(category);
            }
            
this.ObjectContext.Categories.DeleteObject(category);
        }



        [Query(IsDefault 
= true)]
        
public IQueryable<Product> GetProducts()
        {
            
return this.ObjectContext.Products;
        }

        
public IQueryable<Product> GetProductsBySort(string sort)
        {
            
return ObjectContext.Products.OrderBy(sort);
        }

        
public void InsertProduct(Product product)
        {
            
if ((product.EntityState != EntityState.Detached))
            {
                
this.ObjectContext.ObjectStateManager.ChangeObjectState(product, EntityState.Added);
            }
            
else
            {
                
this.ObjectContext.Products.AddObject(product);
            }
        }

        
public void UpdateProduct(Product currentProduct)
        {
            
this.ObjectContext.Products.AttachAsModified(currentProduct, this.ChangeSet.GetOriginal(currentProduct));
        }

        
public void DeleteProduct(Product product)
        {
            
if ((product.EntityState == EntityState.Detached))
            {
                
this.ObjectContext.Products.Attach(product);
            }
            
this.ObjectContext.Products.DeleteObject(product);
        }

        
public IQueryable<Product> GetProductsByCategoryId(int categoryId)
        {
            
return this.ObjectContext.Products.Where(p => p.CategoryID == categoryId);
        }
    }
}



MyDomainService.metadata.cs

代码
/*
 * [MetadataTypeAttribute()] - 指定类的元数据对象
 * [Include] - 生成的客户端上下文包含此字段
 * [Exclude] - 生成的客户端上下文不包含此字段
 * [Composition] - 指定字段为关联数据,即父实体增删改查时,此关联数据也会做相应的变化。指定此 Attribute 后,需显式指定其为 [Include]
 * [Editable(true)], [Editable(false)] - 指定字段是否可编辑
 
*/

namespace Silverlight40.Web.Model
{
    
using System;
    
using System.Collections.Generic;
    
using System.ComponentModel;
    
using System.ComponentModel.DataAnnotations;
    
using System.Data.Objects.DataClasses;
    
using System.Linq;
    
using System.ServiceModel.DomainServices.Hosting;
    
using System.ServiceModel.DomainServices.Server;

    [MetadataTypeAttribute(
typeof(Category.CategoryMetadata))]
    
public partial class Category
    {
        
internal sealed class CategoryMetadata
        {
            
private CategoryMetadata()
            {
            }

            [Key()]
            
public int CategoryID { getset; }

            [Display(Name 
= "类别名称")] // 显示用
            public string CategoryName { getset; }

            
public string Description { getset; }

            [Exclude]
            
public byte[] Picture { getset; }

            [Include]
            
// [Composition]
            public EntityCollection<Product> Products { getset; }
        }
    }

    [MetadataTypeAttribute(
typeof(Product.ProductMetadata))]
    
public partial class Product
    {
        
internal sealed class ProductMetadata
        {
            
private ProductMetadata()
            {
            }

            
public Category Category { getset; }

            
public Nullable<int> CategoryID { getset; }

            
public bool Discontinued { getset; }

            
public EntityCollection<Order_Detail> Order_Details { getset; }

            [Display(Order 
= 0, Name = "产品ID")]
            
public int ProductID { getset; }

            [Display(Order 
= 1, Name = "产品名称")]
            
public string ProductName { getset; }

            
public string QuantityPerUnit { getset; }

            
public Nullable<short> ReorderLevel { getset; }

            
public Supplier Supplier { getset; }

            
public Nullable<int> SupplierID { getset; }

            
public Nullable<decimal> UnitPrice { getset; }

            
public Nullable<short> UnitsInStock { getset; }

            
public Nullable<short> UnitsOnOrder { getset; }
        }
    }
}



2、客户端
ProductViewModel.cs

代码
/*
 * 演示:通过 Domain Service, 以 MVVM 模式实现数据的添加、删除、修改和查询
 * 此类为 MVVM 中的 ViewModel
 
*/

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using Silverlight40.Web.Model;
using System.Collections.ObjectModel;
using Silverlight40.Web.Service;
using System.ComponentModel;
using System.ServiceModel.DomainServices.Client;
using System.Collections.Generic;

namespace Silverlight40.WCFRIAServices.DomainServiceAndMVVM
{
    
public class ProductViewModel
    {
        
public ObservableCollection<Product> Data { getset; }
       
        
private MyDomainContext _context;

        
public ProductViewModel()
        {
            Data 
= new ObservableCollection<Product>();

            LoadData();
        }

        
private void LoadData()
        {
            _context 
= new MyDomainContext();

            EntityQuery
<Product> query = _context.GetProductsQuery().OrderByDescending(p => p.ProductID);

            
/*
             * System.ServiceModel.DomainServices.Client.LoadOperation - 用于异步加载
             * System.ServiceModel.DomainServices.Client.InvokeOperation - 用于异步调用
             * System.ServiceModel.DomainServices.Client.SubmitOperation - 用于异步提交
             
*/

            LoadOperation
<Product> lo = _context.Load(query);

            lo.Completed 
+= delegate
            {
                Data.Clear();
                
foreach (Product p in lo.Entities)
                {
                    Data.Add(p);
                }
            };
        }


        
public ICommand FilterCommand
        {
            
get { return new FilterCommand(this); }
        }
        
public ICommand InsertCommand
        {
            
get { return new InsertCommand(this); }
        }
        
public ICommand DeleteCommand
        {
            
get { return new DeleteCommand(this); }
        }
        
public ICommand UpdateCommand
        {
            
get { return new UpdateCommand(this); }
        }


        
// 检索数据
        public void Filter(string productName)
        {
            EntityQuery
<Product> query = _context.GetProductsQuery().Where(p => p.ProductName.Contains(productName));

            LoadOperation
<Product> lo = _context.Load(query);

            lo.Completed 
+= delegate
            {
                Data.Clear();

                
foreach (Product p in lo.Entities)
                {
                    Data.Add(p);
                }
            };
        }

        
        
// 插入数据
        public void Insert(string productName)
        {            
            EntitySet
<Product> products = _context.EntityContainer.GetEntitySet<Product>();
            Product product 
= new Product();
            product.ProductName 
= productName;
            product.CategoryID 
= 1;
            product.SupplierID 
= 1;
            products.Add(product);

            _context.SubmitChanges(OnInsertCompleted, product);
        }
        
private void OnInsertCompleted(SubmitOperation so)    
        {
            
if (!so.HasError)
            {
                Data.Insert(
0, so.UserState as Product);
            }
        }


        
// 删除数据
        public void Delete(List<Product> products)
        {
            EntitySet
<Product> productsOriginal = _context.EntityContainer.GetEntitySet<Product>();
            
foreach (Product product in products)
            {
                productsOriginal.Remove(product);
            }

            _context.SubmitChanges(OnDeleteCompleted, products);
        }
        
private void OnDeleteCompleted(SubmitOperation so)
        {
            
if (!so.HasError)
            {
                List
<Product> products = so.UserState as List<Product>;
                
foreach (Product product in products)
                {
                    Data.Remove(product);
                }
            }
        }


        
// 更新数据
        public void Update()
        {
            _context.SubmitChanges(OnUpdateCompleted, 
null);
        }
        
private void OnUpdateCompleted(SubmitOperation so)
        {
            
if (!so.HasError)
            {
                MessageBox.Show(
"更新成功");
            }
        }
    }
}


InsertCommand.cs

代码
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Silverlight40.WCFRIAServices.DomainServiceAndMVVM
{
    
/// <summary>
    
/// 插入的 Command
    
/// </summary>
    public class InsertCommand : ICommand
    {
        
private ProductViewModel _viewModel;

        
public InsertCommand(ProductViewModel viewModel)
        {
            _viewModel 
= viewModel;
        }

        
public bool CanExecute(object parameter)
        {
            
return true;
        }

        
public event EventHandler CanExecuteChanged;

        
// 参数为:productName
        public void Execute(object parameter)
        {
            var productName 
= (string)parameter;
            _viewModel.Insert(productName);
        }
    }
}


DeleteCommand.cs

代码
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using Silverlight40.Web.Model;
using System.Collections.Generic;

namespace Silverlight40.WCFRIAServices.DomainServiceAndMVVM
{
    
/// <summary>
    
/// 删除的 Command
    
/// </summary>
    public class DeleteCommand  : ICommand
    {
        
private ProductViewModel _viewModel;

        
public DeleteCommand(ProductViewModel viewModel)
        {
            _viewModel 
= viewModel;
        }

        
public bool CanExecute(object parameter)
        {
            
return true;
        }

        
public event EventHandler CanExecuteChanged;

        
// 参数为:DataGrid.SelectedItems [System.Collections.IList 类型]
        public void Execute(object parameter)
        {
            var collection 
= parameter as System.Collections.IList;
            var products 
= new List<Product>();

            
foreach (Product product in collection)
            {
                products.Add(product);
            }

            _viewModel.Delete(products);
        }
    }
}


UpdateCommand.cs

代码
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Silverlight40.WCFRIAServices.DomainServiceAndMVVM
{
    
/// <summary>
    
/// 更新的 Command
    
/// </summary>
    public class UpdateCommand : ICommand
    {
        
private ProductViewModel _viewModel;

        
public UpdateCommand(ProductViewModel viewModel)
        {
            _viewModel 
= viewModel;
        }

        
public bool CanExecute(object parameter)
        {
            
return true;
        }

        
public event EventHandler CanExecuteChanged;

        
public void Execute(object parameter)
        {
            _viewModel.Update();
        }
    }
}


FilterCommand.cs

代码
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Silverlight40.WCFRIAServices.DomainServiceAndMVVM
{
    
/// <summary>
    
/// 查询的 Command
    
/// </summary>
    public class FilterCommand : ICommand
    {
        
private ProductViewModel _viewModel;

        
public FilterCommand(ProductViewModel viewModel)
        {
            _viewModel 
= viewModel;
        }

        
public bool CanExecute(object parameter)
        {
            
return true;
        }

        
public event EventHandler CanExecuteChanged;

        
// 参数为:productName
        public void Execute(object parameter)
        {
            var productName 
= (string)parameter;
            _viewModel.Filter(productName);
        }
    }
}
 


Demo.xaml

代码
<navigation:Page x:Class="Silverlight40.WCFRIAServices.DomainServiceAndMVVM.Demo" 
           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"
           xmlns:navigation
="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
           xmlns:sdk
="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
           xmlns:vm
="clr-namespace:Silverlight40.WCFRIAServices.DomainServiceAndMVVM"
           Title
="Demo Page">
    
<Grid x:Name="LayoutRoot">
        
<StackPanel HorizontalAlignment="Left">

            
<!--
                在 View 中引入 ViewModel
            
-->
            
<StackPanel.DataContext>
                
<vm:ProductViewModel />
            
</StackPanel.DataContext>
            
            
<!--
                用于演示添加记录
            
-->
            
<StackPanel Orientation="Horizontal">
                
<sdk:Label Name="lblProductName4Add" Content="产品名称(添加用):" />
                
<TextBox Name="txtProductName4Add" Width="100" />
                
<Button Name="btnAdd" Content="新增" Command="{Binding InsertCommand}" CommandParameter="{Binding ElementName=txtProductName4Add, Path=Text }" />
            
</StackPanel>

            
<!--
                用于演示查询记录
            
-->
            
<StackPanel Orientation="Horizontal">
                
<sdk:Label Name="lblProductName" Content="产品名称(查询用):" />
                
<TextBox Name="txtProductName" Width="100" />
                
<!--
                    如果 Command 需要传递多个参数,则可以
                        1、将 CommandParameter 绑定到一个容器控件,然后在 Command 中去遍历容器控件内的控件(破坏 ViewModel)
                        2、在 View 中调用 Command 之前构造一个复杂类型,再传递给 ViewModel(破坏 View)
                        3、ViewModel 中设置一个对象,其是 Model 层中的某个类的实例,同时此对象双向绑定到 View 上,这样这个复杂类型就可以通过 ViewModel 来传递
                
-->
                
<Button Name="btnFilter" Content="查询" Command="{Binding FilterCommand}" CommandParameter="{Binding ElementName=txtProductName, Path=Text }" />
            
</StackPanel>

            
<!--
                用于演示显示记录
            
-->
            
<sdk:DataGrid Name="dataGrid" Width="600" Height="300" AutoGenerateColumns="False" ItemsSource="{Binding Data}">
                
<sdk:DataGrid.Columns>
                    
<sdk:DataGridTextColumn Header="供应商ID" Binding="{Binding SupplierID}" IsReadOnly="True" />
                    
<sdk:DataGridTextColumn Header="产品类别ID" Binding="{Binding CategoryID}" IsReadOnly="True" />
                    
<sdk:DataGridTextColumn Header="产品ID" Binding="{Binding ProductID}" IsReadOnly="True" />
                    
<sdk:DataGridTextColumn Header="产品名称" Binding="{Binding ProductName}" />
                
</sdk:DataGrid.Columns>
            
</sdk:DataGrid>

            
<!--
                用于演示删除记录
            
-->
            
<Button Name="btnDelete" Content="删除选中(可多选)" Command="{Binding DeleteCommand}" CommandParameter="{Binding ElementName=dataGrid, Path=SelectedItems }" />

            
<!--
                用于演示更新记录
            
-->
            
<Button Name="btnSave" Content="保存全部" Command="{Binding UpdateCommand}" />
            
        
</StackPanel>
    
</Grid>
</navigation:Page>



OK
[源码下载]