.NET 4.0 - Winform Control - DataGridView 数据绑定(ADO.NET Data Service)

ADO.NET Data Service 是一种基于REST架构的WCF + EF + OData(协议)的服务,.NET 客户端可以像引用其他WCF服务一样,添加services reference获得很好的客户端支持。客户端代理主要由 DataServiceContext 和 Entities 组成,因此客户端可以用类似 Linq2Entites 的方法获取 Data Service 的数据,底层框架将 Linq 语句转成 DataServiceQuery 再转成 HttpWebRequest 发起对服务的请求,并对返回的 HttpWebResponse 解析反序列化成实体。对 Data Service Client Library 的用户而言,事情很简单,执行效率也相应提高。但是其他语言的用户可就遭罪了,得自己拼接大量的字符串。其他详细参看:http://www.rainsts.net/article.asp?id=809 (雨痕的blog)。本篇文章,主要针对客户端调用来了解 Data Service 的特点。

 

1. 服务端的创建:
(1) 创建 WCF Application Service 工程
(2) 添加 ADO.NET Entity Data Model
(3) 删除既有的 .svc 文件,添加一个 AppFabric-enabled WCF Data Service

     (该模板可以通过 Extension Manager 在线下载) 
 

(4) 修改刚才添加的 DataService 类为如下:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class NorthwindDataService : DataService
{
    // This method is called only once to initialize service-wide policies.
    public static void InitializeService(DataServiceConfiguration config)
    {            
        // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
        // Examples:
        // config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead);
        // config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All);
        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
        config.SetEntitySetAccessRule("*", EntitySetRights.All);
        RouteTable.Routes.Add(
            new ServiceRoute("NorthwindDataService", new DataServiceHostFactory(), typeof(NorthwindDataService)));

    }
}

2. 客户端添加 Service Reference

3. 要完成的任务:实现下面3个表的连动绑定并实现Update处理。

简单说明:
(1)  客户端每次访问 Data Service 都是调用一次 Http Request,默认使用的是 Atom XML 格式。

       (Execute,LoadProperty, SaveChanges等方法)

(2)  DataServiceContext 会将查询过的数据在客户端缓存,并对缓存的数据进行 State Tracing。

       直到 DataServiceContext 被销毁。可以通过 DataServiceContext.Entities 获得跟踪实体的实例。

(3)  因为在客户端缓存了数据,所以存在普遍的并发风险。另外查询时,会根据 MergeOptions 来更新客户端缓存。

(4)  Navigation Property 在客户端的实体里不支持,关联数据需要手动查询,可以利用(LoadProperty方法)

      提交的时候可以用: SetLink, AddLink, DeleteLink 进行关联。
(5)  将数据提交到服务端需要先对应调用下面的方法:

      添加 -- AddToXXX 或者 AddObject(string entitySetName, object entity);

      更新 -- UpdateObject(object entity);

      删除 -- DeleteObject(object entity);

      最后调用 SaveChanges() 方法

(6)  Linq2DataService 返回的是 DataServiceQuery (封装的是请求的Uri),

      需要调用Execute(同步)或者BeginExecute(异步)才能获取结果。
(7) 每一个实体都对应服务端一个Identity(uri)

 

客户端UI:

根据上面说明的第(5)条,数据提交之前必须调用UpdateObject方法,因此可以利用 BindingSource 的ListChanged 事件即时调用UpdateObject,等"Update"按钮事件处理方法中直接调用 SaveChanges 方法就可以了。 

 

客户端代码:详细看注释

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Services.Client;
using System.Collections.ObjectModel;
using System.Net;

namespace WcfDataServiceClient
{
    public partial class DataBindDemo : Form
    {
        
        public DataBindDemo()
        {
            InitializeComponent();
        }

        private NorthwindDataSvc.NORTHWNDEntities northwind;

        private void Form1_Load(object sender, EventArgs e)
        {
            // 将地址设置到TcpTracer监听的20000端口上,TcpTracer将转发至服务的真实IP:58960
            var svcUri = new Uri("http://localhost:20000/NorthwindDataService.svc");
            northwind = new NorthwindDataSvc.NORTHWNDEntities(svcUri);
            // 绑定数据,最初只有customers
            BindData();

            // 为了避免绑定时触发 SelectedIndexChanged 事件,
            // SelectedIndexChanged 事件的绑定放在Customers数据加载之后
            // 绑定customers的选择变更事件,加载orders
            lstCustomer.SelectedIndexChanged += new EventHandler(lstCustomer_SelectedIndexChanged);
            // 绑定orders的选择变更事件,加载order_details
            dgvOrders.SelectionChanged += new EventHandler(dgvOrders_SelectionChanged);

            // 绑定各BindingSource的ListChanged事件,修改缓存状态
            customerBindingSource.ListChanged += new ListChangedEventHandler(UpdateEntity);

            // 触发一次SelectedIndexChanged, 加载orders和order_detailss
            lstCustomer.SelectedIndex = -1;
            lstCustomer.SelectedIndex = 0;
        }

        private void UpdateEntity(object sender, ListChangedEventArgs e)
        {
            if (e.ListChangedType != ListChangedType.ItemChanged) return;
            // 从BindingSource中获取当前变更的数据
            var data = ((BindingSource)sender).Current;
            // 告知本地缓存该数据发生变更
            northwind.UpdateObject(data);
        }

        private void lstCustomer_SelectedIndexChanged(object sender, EventArgs e)
        {
            var customer = lstCustomer.SelectedItem as NorthwindDataSvc.Customer;
            if (customer == null) return;
            orderBindingSource.ListChanged -= UpdateEntity;
            // 这里DataService里没有Navigation Property,
            // customer.Orders(DataServiceCollection)并没有数据
            // 在SaveChanges之前都使用客户端数据
            northwind.MergeOption = MergeOption.PreserveChanges;
            northwind.LoadProperty(customer, "Orders");
            orderBindingSource.DataSource = customer.Orders;
            dgvOrders.DataSource = orderBindingSource;
            orderBindingSource.ListChanged += UpdateEntity;
        }

        private void dgvOrders_SelectionChanged(object sender, EventArgs e)
        {
            if (dgvOrders.SelectedRows.Count <= 0) return;
            orderDetailBindingSource.ListChanged -= UpdateEntity;
            var order = dgvOrders.BindingContext[dgvOrders.DataSource].Current as NorthwindDataSvc.Order;
            northwind.MergeOption = MergeOption.PreserveChanges;
            northwind.LoadProperty(order, "Order_Details");
            orderDetailBindingSource.DataSource = order.Order_Details.ToList();
            dgvOrderDetails.DataSource = orderDetailBindingSource;
            orderDetailBindingSource.ListChanged += UpdateEntity;
        }

        private void BindData()
        {
            customerBindingSource.DataSource = northwind.Customers.Execute().ToList();
            lstCustomer.DataSource = customerBindingSource;
            lstCustomer.ValueMember = "ContactName";
            lstCustomer.DisplayMember = "ContactName";

            txtName.DataBindings.Add("Text", customerBindingSource, "ContactName");
            txtContactTitle.DataBindings.Add("Text", customerBindingSource, "ContactTitle");
            txtAddress.DataBindings.Add("Text", customerBindingSource, "Address");
            txtCity.DataBindings.Add("Text", customerBindingSource, "City");
            txtRegion.DataBindings.Add("Text", customerBindingSource, "Region");
            txtPostalCode.DataBindings.Add("Text", customerBindingSource, "PostalCode");
            txtPhone.DataBindings.Add("Text", customerBindingSource, "Phone");
            txtFax.DataBindings.Add("Text", customerBindingSource, "Fax");
            txtCountry.DataBindings.Add("Text", customerBindingSource, "Country");
        }

        private void btnUpdate_Click(object sender, EventArgs e)
        {
            try
            {
                // 获得DataServiceContext中跟踪的Entities中的变更过的数据
                var changedEntities = northwind.Entities.
                                      Where(et => et.State != EntityStates.Unchanged).ToList();
                string log = string.Format("Have [{0}] datas been changed.\n", changedEntities.Count);
                foreach (var entity in changedEntities)
                    log += entity.ServerTypeName + ":" + entity.State + "\n";
                MessageBox.Show(log);
                // 如果有变更
                if (changedEntities.Count > 0)
                {
                    // 批量提交数据(即数据在一个Request内提交)
                    var resp = northwind.SaveChanges(SaveChangesOptions.Batch);
                    MessageBox.Show(((HttpStatusCode)resp.BatchStatusCode).ToString());
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);               
            }
            finally
            {
                try
                {
                    // 获得DataServiceContext中跟踪的Entities中的变更过的数据
                    var changedEntities = northwind.Entities.Where(et => et.State != EntityStates.Unchanged);
                    // 指定更新行为:如果本地数据不一致则用服务端数据覆盖
                    northwind.MergeOption = MergeOption.OverwriteChanges;
                    foreach (var entity in changedEntities)
                    {
                        // 无论请求成功还是失败,重新查询把跟踪对象的State改为Unchanged.
                        northwind.Execute(new Uri(entity.Identity)).ToList();
                    }
                }
                catch
                {
                }
            }
        }

        private void btnFirst_Click(object sender, EventArgs e)
        {
            customerBindingSource.MoveFirst();
        }

        private void btnPre_Click(object sender, EventArgs e)
        {
            customerBindingSource.MovePrevious();
        }

        private void btnNext_Click(object sender, EventArgs e)
        {
            customerBindingSource.MoveNext();
        }

        private void btnLast_Click(object sender, EventArgs e)
        {
            customerBindingSource.MoveLast();
        }
    }
}

执行成功的话,将会获得 Accept 的 ResponseStatus

你可能感兴趣的:([03],WinForm,[05],WCF,[03],WinForm,DataGridView)