稳扎稳打Silverlight(61) - 4.0通信之WCF RIA Services: 自定义服务端排序和分页

[索引页]
[源码下载]


稳扎稳打Silverlight(61) - 4.0通信之WCF RIA Services: 自定义服务端排序和分页



作者:webabcd


介绍
Silverlight 4.0 之 WCF RIA Services:实现自定义的服务端排序和分页


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


示例
演示如何在 WCF RIA Services 框架中实现自定义的服务端排序和分页
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);
        }



        
// 通过验证的并且角色属于“管理员”的用户才有权限调用此方法
        [Invoke]
        [RequiresAuthentication()]
        [RequiresRole(
"管理员")]
        
public string GetAuthenticationStatus()
        {
            
return "看到了这条信息,就证明你有权限";
        }
    }
}



MyDomainService.metadata.cs

代码
/*
 * [MetadataTypeAttribute()] - 指定类的元数据对象
 * [Include] - 生成的客户端上下文包含此字段
 * [Exclude] - 生成的客户端上下文不包含此字段
 * [Composition] - 指定字段为关联数据,即父实体增删改查时,此关联数据也会做相应的变化。指定此 Attribute 后,需显式指定其为 [Include]
 *     注:如果使用 DomainDataSource 则不能将字段设置为 [Composition]
 * [Editable(true)], [Editable(false)] - 指定字段是否可编辑
 * 
 * 支持 Data Annotation 方式的数据验证:DataTypeAttribute(具有很多常用的数据类型的验证), RangeAttribute, RegularExpressionAttribute, RequiredAttribute, StringLengthAttribute 等
 * 注:如果需要将自定义的验证既作用于服务端又作用于客户端,则需要把自定义的验证逻辑代码设置为 shared 模式
 
*/

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;

    
using Silverlight40.Web.Service.Validation;

    [MetadataTypeAttribute(
typeof(Category.CategoryMetadata))]
    [CustomValidation(
typeof(CategoryValidator), "ValidateCategory")] // 演示通过自定义的实体验证器来实现验证
    public partial class Category
    {
        
internal sealed class CategoryMetadata
        {
            
private CategoryMetadata()
            {
            }

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

            [Display(Name 
= "类别名称")] // 显示用
            [Numeric(ErrorMessage = "字段“{0}”必须是数字")] // 演示通过继承 RegularExpressionAttribute 实现字段的自定义验证
            [StartsWith(ErrorMessage = "{0}")] // 演示通过继承 ValidationAttribute 实现字段的自定义验证
            [CustomValidation(typeof(EndsWidthValidator), "ValidateCategoryName")]  // 演示通过自定义的字段验证器来实现验证
            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、客户端
ReloadEventArgs.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 System.ComponentModel;

namespace Silverlight40.WCFRIAServices.CustomSortingPaging
{
    
// 刷新事件的事件参数
    public class ReloadEventArgs : EventArgs
    {
        
public SortDescriptionCollection SortDescriptions { getset; }
    }
}


SortableCollectionView.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 System.Collections.ObjectModel;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;

namespace Silverlight40.WCFRIAServices.CustomSortingPaging
{
    
// 为了实现排序功能,所以需要实现 ICollectionView 接口(仅实现其中的排序功能部分)
    
// 为了集合具有改变通知的功能,所以需要继承自 ObservableCollection<T>
    public class SortableCollectionView<T> : ObservableCollection<T>, ICollectionView
    {
        
public SortableCollectionView()
        {
            _currentItem 
= null;
            _currentPosition 
= -1;
        }

        
protected override void ClearItems()
        {
            
base.ClearItems();

            
// 触发 CurrentChanging 事件
            OnCurrentChanging();
        }



        
public bool CanFilter
        {
            
get { return false; }
        }

        
public bool CanGroup
        {
            
get { return false; }
        }

        
// 允许通过 SortDescriptions 属性进行排序
        public bool CanSort
        {
            
get { return true; }
        }

        
public System.Globalization.CultureInfo Culture { getset; }

        
// 当前项
        private object _currentItem;
        
public object CurrentItem
        {
            
get { return _currentItem; }
        }

        
// 当前项的索引
        private int _currentPosition;
        
public int CurrentPosition
        {
            
get { return _currentPosition; }
        }

        
public Predicate<object> Filter { getset; }

        
public ObservableCollection<GroupDescription> GroupDescriptions
        {
            
get { return null; }
        }

        
public ReadOnlyObservableCollection<object> Groups
        {
            
get { return null; }
        }

        
// 当前项是否超出了集合的末尾
        public bool IsCurrentAfterLast
        {
            
get
            {
                
if (!IsEmpty)
                    
return CurrentPosition >= base.Count;

                
return true;
            }
        }

        
// 当前项是否超出了集合的开头
        public bool IsCurrentBeforeFirst
        {
            
get
            {
                
if (!IsEmpty)
                    
return CurrentPosition < 0;

                
return true;
            }
        }

        
// 集合是否为空
        public bool IsEmpty
        {
            
get { return base.Count == 0; }
        }

        
// System.ComponentModel.SortDescription 类型对象的集合,其用于描述集合中的数据的排序方式
        private SortDescriptionCollection _sortDescriptions;
        
public SortDescriptionCollection SortDescriptions
        {
            
get
            {
                
if (_sortDescriptions == null)
                    _sortDescriptions 
= new SortDescriptionCollection();

                
return _sortDescriptions;
            }
        }

        
public System.Collections.IEnumerable SourceCollection
        {
            
get { return this; }
        }

        
// 当前项更改后所触发的事件
        public event EventHandler CurrentChanged;

        
// 当前项准备更改之前所触发的事件
        public event CurrentChangingEventHandler CurrentChanging;

        
// 指定的项是否属于此集合
        public bool Contains(object item)
        {
            
return Contains((T)item);
        }

        
// 延迟刷新
        public IDisposable DeferRefresh()
        {
            
return new DeferRefreshHelper(() => Refresh());
        }
        
private class DeferRefreshHelper : IDisposable
        {
            
private Action _callback;

            
public DeferRefreshHelper(Action callback)
            {
                _callback 
= callback;
            }

            
public void Dispose()
            {
                _callback();
            }
        }

        
// 将指定项设置为当前项
        public bool MoveCurrentTo(object item)
        {
            
if (object.Equals(CurrentItem, item) && (item != null || IsCurrentInView))
                
return IsCurrentInView;

            
int index = base.IndexOf((T)item);

            
return MoveCurrentToPosition(index);
        }

        
// 将第一项设置为当前项
        public bool MoveCurrentToFirst()
        {
            
return MoveCurrentToPosition(0);
        }

        
// 将最后一项设置为当前项
        public bool MoveCurrentToLast()
        {
            
return MoveCurrentToPosition(base.Count - 1);
        }

        
// 将当前项的下一项设置为当前项
        public bool MoveCurrentToNext()
        {
            
return CurrentPosition < base.Count && MoveCurrentToPosition(CurrentPosition + 1);
        }

        
// 将指定位置的项设置为当前项
        public bool MoveCurrentToPosition(int position)
        {
            
if (position < -1 || position > base.Count)
                
throw new Exception("参数“position”不在范围内");

            
if (position != CurrentPosition)
            {
                
bool isCurrentAfterLast = IsCurrentAfterLast;
                
bool isCurrentBeforeFirst = IsCurrentBeforeFirst;

                OnCurrentChanging();
                ChangeCurrentToPosition(position);
                OnCurrentChanged();

                
if (IsCurrentAfterLast != isCurrentAfterLast)
                    OnPropertyChanged(
"IsCurrentAfterLast");
                
if (IsCurrentBeforeFirst != isCurrentBeforeFirst)
                    OnPropertyChanged(
"IsCurrentBeforeFirst");

                OnPropertyChanged(
"CurrentPosition");
                OnPropertyChanged(
"CurrentItem");
            }

            
return IsCurrentInView;
        }

        
// 将当前项的上一项设置为当前项
        public bool MoveCurrentToPrevious()
        {
            
return CurrentPosition >= 0 && MoveCurrentToPosition(CurrentPosition - 1);
        }

        
public event EventHandler<ReloadEventArgs> Reload;
        
// 刷新集合
        public void Refresh()
        {
            
if (Reload != null)
                Reload(
thisnew ReloadEventArgs() { SortDescriptions = SortDescriptions });
        }

        
protected virtual void OnCurrentChanged()
        {
            
if (CurrentChanged != null)
                CurrentChanged(
this, EventArgs.Empty);
        }

        
protected virtual void OnCurrentChanging(CurrentChangingEventArgs args)
        {
            
if (args == null)
                
throw new Exception("参数“args”不能为 null");

            
if (CurrentChanging != null)
                CurrentChanging(
this, args);
        }

        
protected void OnCurrentChanging()
        {
            _currentPosition 
= -1;

            OnCurrentChanging(
new CurrentChangingEventArgs(false));
        }

        
protected virtual void OnPropertyChanged(string propertyName)
        {
            
base.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }



        
// 获取集合内指定位置的对象
        private object GetItemAt(int index)
        {
            
if (index >= 0 && index < base.Count)
                
return this[index];

            
return null;
        }

        
// 当前项是否在集合的范围之内
        private bool IsCurrentInView
        {
            
get { return CurrentPosition >= 0 && CurrentPosition < base.Count; }
        }

        
// 将指定位置的项设置为当前项
        private void ChangeCurrentToPosition(int position)
        {
            
if (position < 0)
            {
                _currentItem 
= null;
                _currentPosition 
= -1;
            }
            
else if (position >= base.Count)
            {
                _currentItem 
= null;
                _currentPosition 
= base.Count;
            }
            
else
            {
                _currentItem 
= this[position];
                _currentPosition 
= position;
            }
        }
    }
}


PageableSortableCollectionView.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 System.ComponentModel;
using System.Collections.ObjectModel;

namespace Silverlight40.WCFRIAServices.CustomSortingPaging
{
    
// 为了实现分页功能,所以需要实现 IPagedCollectionView 接口
    
// 为了集合具有排序的功能,所以需要继承自 SortableCollectionView<T>
    public class PageableSortableCollectionView<T> : SortableCollectionView<T>, IPagedCollectionView
    {
        
// 是否允许分页
        public bool CanChangePage
        {
            
get { return true; ; }
        }

        
// 页索引是否正在改变
        private bool _isPageChanging;
        
public bool IsPageChanging
        {
            
get { return _isPageChanging; }
            
private set
            {
                
if (_isPageChanging != value)
                {
                    _isPageChanging 
= value;
                    OnPropertyChanged(
"IsPageChanging");
                }
            }
        }

        
// 项总数
        public int ItemCount
        {
            
get { return TotalItemCount; }
            
set { TotalItemCount = value; }
        }

        
// 页索引
        private int _pageIndex;
        
public int PageIndex
        {
            
get { return _pageIndex; }
        }

        
// 页大小
        private int _pageSize = 10;
        
public int PageSize
        {
            
get { return _pageSize; }
            
set
            {
                
if (value != _pageSize && value >= 1)
                {
                    _pageSize 
= value;
                    OnPropertyChanged(
"PageSize");
                }
            }
        }

        
// 项总数。未知的话则返回 -1
        private int _totalItemCount;
        
public int TotalItemCount
        {
            
get { return _totalItemCount; }
            
set
            {
                
if (value != _totalItemCount)
                {
                    _totalItemCount 
= value;
                    OnPropertyChanged(
"TotalItemCount");
                    OnPropertyChanged(
"ItemCount");
                }
            }
        }

        
// PageIndex 改变之后所触发的事件
        public event EventHandler<EventArgs> PageChanged;

        
// PageIndex 改变之前所触发的事件
        public event EventHandler<PageChangingEventArgs> PageChanging;

        
// 将第一页设置为当前页
        public bool MoveToFirstPage()
        {
            
return MoveToPage(0);
        }

        
// 将最后一页设置为当前页
        public bool MoveToLastPage()
        {
            
return TotalItemCount != -1 && PageSize > 0 && MoveToPage(PageCount - 1);
        }

        
// 将当前页的下一页设置为当前页
        public bool MoveToNextPage()
        {
            
return MoveToPage(_pageIndex + 1);
        }

        
// 将指定的页索引设置为当前页
        public bool MoveToPage(int pageIndex)
        {
            
if (pageIndex < -1)
                
return false;
            
if (pageIndex == -1 && PageSize > 0)
                
return false;
            
if (pageIndex >= PageCount || _pageIndex == pageIndex)
                
return false;

            
try
            {
                IsPageChanging 
= true;

                PageChangingEventArgs args 
= new PageChangingEventArgs(pageIndex);
                
// 触发 PageChanging 事件
                OnPageChanging(new PageChangingEventArgs(pageIndex));
                
// 监听者的 PageChanging 事件处理完成后继续往下执行
                if (args.Cancel)
                    
return false;

                _pageIndex 
= pageIndex;
                
                IsPageChanging 
= false;

                OnPropertyChanged(
"PageIndex");
                OnPageChanged(EventArgs.Empty);

                
base.Refresh();

                
return true;
            }
            
finally
            {
                IsPageChanging 
= false;
            }
        }

        
// 将当前页的上一页设置为当前页
        public bool MoveToPreviousPage()
        {
            
return MoveToPage(_pageIndex - 1);
        }

        
protected virtual void OnPageChanging(PageChangingEventArgs args)
        {
            
if (PageChanging != null)
                PageChanging(
this, args);
        }

        
protected virtual void OnPageChanged(EventArgs args)
        {
            
if (PageChanged != null)
                PageChanged(
this, args);
        }



        
// 页总数
        public int PageCount
        {
            
get
            {
                
if (_pageSize <= 0)
                    
return 0;

                
return Math.Max(1, (int)Math.Ceiling((double)ItemCount / (double)_pageSize));
            }
        }
    }
}


ProductViewModel.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 System.ComponentModel;
using Silverlight40.Web.Model;
using Silverlight40.Web.Service;
using System.ServiceModel.DomainServices.Client;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Silverlight40.WCFRIAServices.CustomSortingPaging
{
    
public class ProductViewModel : INotifyPropertyChanged
    {
        
public event PropertyChangedEventHandler PropertyChanged;

        
// 数据
        public PageableSortableCollectionView<Product> Data { getprivate set; }

        
// 页大小
        public int PageSize
        {
            
get { return Data.PageSize; }
            
set
            {
                
if (Data.PageSize == value)
                    
return;

                Data.PageSize 
= value;

                
if (PropertyChanged != null)
                    PropertyChanged(
thisnew PropertyChangedEventArgs("PageSize"));
            }
        }

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

            Data.PageChanging 
+= new EventHandler<PageChangingEventArgs>(Data_PageChanging);
            Data.PageChanged 
+= new EventHandler<EventArgs>(Data_PageChanged);
            Data.Reload 
+= new EventHandler<ReloadEventArgs>(Data_OnReload);

            GetData();
        }

        
void Data_PageChanging(object sender, PageChangingEventArgs e)
        {
            
        }

        
void Data_PageChanged(object sender, EventArgs e)
        {

        }

        
void Data_OnReload(object sender, ReloadEventArgs e)
        {
            
// 排序或页索引有变化则从服务端获取新的数据
            GetData();
        }

        
private void GetData()
        {
            MyDomainContext context 
= new MyDomainContext();

            
string sort = "";

            
// 构造排序表达式
            foreach (SortDescription sortDesc in Data.SortDescriptions)
            {
                
// “it”是 ObjectQuery<T> 的默认名称
                sort += string.Format("it.{0} {1},", sortDesc.PropertyName, sortDesc.Direction == ListSortDirection.Ascending ? "asc" : "desc");
            }
            
if (sort == "")
                sort 
= "it.ProductId asc";
            
else
                sort 
= sort.TrimEnd(',');

            
int take = Data.PageSize;
            
int skip = Data.PageIndex * Data.PageSize;

            
// 为了获取数据记录总数,所以需要在这里写分页逻辑
            EntityQuery<Product> query = context.GetProductsBySortQuery(sort).Skip(skip).Take(take);
            
// 返回数据中是否包含数据记录总数(前提,分页逻辑要写在 EntityQuery<Product> 上)
            query.IncludeTotalCount = true;
            LoadOperation
<Product> lo = context.Load(query);
            lo.Completed 
+= new EventHandler(delegate 
            {
                Data.Clear();

                
// 数据记录总数
                Data.ItemCount = lo.TotalEntityCount;

                
// 当前页的数据集合
                foreach (Product p in lo.Entities)
                {
                    Data.Add(p);
                }
            });
        }
    }
}


Demo.xaml

代码
<navigation:Page x:Class="Silverlight40.WCFRIAServices.CustomSortingPaging.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.CustomSortingPaging"
           Title
="Demo Page">
    
<Grid x:Name="LayoutRoot">
        
<StackPanel>

            
<!--
                在 View 中引入 ViewModel
            
-->
            
<StackPanel.DataContext>
                
<vm:ProductViewModel />
            
</StackPanel.DataContext>

            
<sdk:DataGrid x:Name="dataGrid" AutoGenerateColumns="True" ItemsSource="{Binding Data}" />

            
<sdk:DataPager x:Name="dataPager" Source="{Binding Data}" PageSize="{Binding PageSize}" DisplayMode="FirstLastPreviousNext" IsEnabled="True" IsTotalItemCountFixed="True" />
            
        
</StackPanel>
    
</Grid>
</navigation:Page>



OK
[源码下载]

posted @ 2010-10-27 09:03  webabcd  阅读(7042)  评论(26编辑  收藏  举报