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

本文介绍ADO.NET Data Service的客户端调用特点,包括基于REST架构的WCF+EF+OData服务搭建过程,客户端如何通过DataServiceContext操作数据,以及实现表间联动绑定与更新处理的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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<NORTHWNDEntities> { // 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<T> (封装的是请求的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<T>)并没有数据 // 在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<object>(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

<think>好的,我现在需要解决用户的问题:在基于.NET 4.0WinForm项目中引用.NET 4.7的System.ValueTuple会有哪些隐患。首先,我要理解用户的问题背景。用户可能正在维护一个较旧的.NET 4.0项目,但需要使用到较新版本的ValueTuple类型,可能因为某些库或代码需要元组功能。 首先,我需要确认System.ValueTuple的版本和依赖关系。System.ValueTuple是在.NET Framework 4.7中引入的,但也可以通过NuGet包在较低版本的.NET中使用。用户提到的是直接引用.NET 4.7的System.ValueTuple,而不是通过NuGet安装。这可能带来问题。 接下来,考虑兼容性问题。.NET 4.0项目默认不支持高版本的程序集,直接引用可能会导致运行时错误。需要检查是否可以通过添加绑定重定向来解决问题,但.NET 4.0的app.config配置可能不支持自动绑定到高版本,可能需要手动配置,但不确定是否有效。 然后,部署环境的问题。如果用户的服务器或客户端环境没有安装.NET 4.7,那么即使项目编译成功,运行时也会缺少必要的程序集,导致崩溃。此时,用户可能需要确保所有运行环境都安装了.NET 4.7,但这可能不现实,特别是对于需要兼容旧环境的情况。 另外,考虑使用NuGet包的情况。正确的做法应该是通过NuGet安装System.ValueTuple包,这样即使在.NET 4.0中也能正常工作,因为NuGet包是向下兼容的。而直接引用高版本的系统程序集可能导致不可预见的问题,比如类型加载异常或方法缺失。 还需要考虑序列化和反射的问题。如果项目中涉及到了元组的序列化或反射操作,不同版本的ValueTuple可能导致行为不一致,尤其是在类型名称或结构发生变化时,这可能会引发难以调试的问题。 最后,建议用户使用NuGet来获取正确的System.ValueTuple版本,而不是直接引用高版本的框架程序集。同时,确保项目配置正确,包括绑定重定向和必要的依赖项,以保持兼容性和稳定性。如果必须使用.NET 4.7的功能,考虑升级整个项目到更高版本的.NET框架,以获得更好的支持和兼容性。</think>在.NET Framework 4.0WinForm项目中直接引用.NET 4.7的System.ValueTuple程序集可能带来以下隐患: $$AssemblyLoadException = \text{运行时类型加载失败} \quad (当环境仅安装.NET4.0)$$ **主要风险分析:** 1. **二进制兼容性问题** - 程序集版本冲突:`System.ValueTuple`在.NET4.0中不存在原生实现 - 强签名验证失败:高版本程序集无法被低版本运行时验证 - 类型加载失败概率:$\text{当缺少.NET4.7运行时} \rightarrow 100\%$ 2. **部署环境限制** - 必须要求客户端安装.NET4.7+运行时 - 与Win7兼容性问题:原生支持.NET4.7需要Win7 SP1+(部分企业环境可能仍在使用旧系统) 3. **替代方案对比表** | 方案 | 兼容.NET4.0 | 需要NuGet包 | 部署要求 | |---------------------|-------------|-------------|--------------| | 直接引用4.7程序集 | ❌ | ❌ | 必须安装4.7+ | | 通过NuGet安装 | ✔️ | ✔️ | 仅需4.0 | | 升级项目目标框架 | - | - | 需要4.7+ | **推荐解决方案:** ```xml <!-- 正确做法:通过NuGet安装兼容包 --> <PackageReference Include="System.ValueTuple" Version="4.5.0" /> ``` **技术验证步骤:** 1. 在VS2019中创建.NET4.0测试项目 2. 尝试直接添加`C:\Program Files\...\v4.7\System.ValueTuple.dll` 3. 观察编译结果:$\text{成功} \quad \text{(CLR版本检查在运行时进行)}$ 4. 在仅安装.NET4.0的机器上运行:$\text{触发FileLoadException}$ **最佳实践建议:** - 使用NuGet获取向后兼容的System.ValueTuple包 - 如需使用C#7.0+元组语法,需确保编译器版本≥VS2017 - 项目文件需包含重定向配置: ```xml <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> ``` **结论**:直接引用高版本框架程序集将导致运行时环境强依赖,建议通过NuGet获取向下兼容的官方实现包,可完全兼容.NET4.0环境。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值