[WCF REST] 解决资源并发修改的一个有效的手段:条件更新(Conditional Update)
2012-02-14 11:25
274 查看
[code] public class IncomingWebRequestContext
{
//其他成员
public void CheckConditionalUpdate(Guid entityTag);
public void CheckConditionalUpdate(int entityTag);
public void CheckConditionalUpdate(long entityTag);
public void CheckConditionalUpdate(string entityTag);
}
[/code]
[/code]
实现在CheckConditionalUpdate方法中的条件更新检测具有这样的逻辑:对于HTTP方法为PUT的请求,如果If-Match报头值不为“*”,则直接抛出HTTP状态为PreconditionFailed的WebFaultException异常;对于HTTP方法为POST和DELETE的请求来说,如果If-Match报头值为“*”或者包含指定的entityTag则验证通过,否则同样则直接抛出HTTP状态为PreconditionFailed的WebFaultException异常。
表示出栈请求上下文的OutgoingWebRequestContext类型具有如下一个IfMatch属性,客户端可以通过该属性对请求消息的If-Match报头进行设置。
[code] [code] public class OutgoingWebRequestContext
{
//其他成员
public string IfMatch{ get; set;}
}
[/code]
[/code]
三、实例演示:通过条件更新解决对相同资源的并发修改
我们同样通过对EmployeesService进行相应的改造来模拟如何通过添加更新实现对相同资源的并发操作问题,这次我们修改的是用于获取指定ID员工信息的Get操作和用于修改员工信息的Update操作。Get操作在返回与指定员工ID匹配的Employee对象之前我们将该对象的哈希码作为了回复消息的ETag报头(Employee类型重写了GetHashCode方法)。[code] [code] public class EmployeesService : IEmployees
{
//其他成员
public Employee Get(string id)
{
Employee employee = employees.FirstOrDefault(e => e.Id == id);
if (null == employee)
{
throw new WebFaultException(HttpStatusCode.NotFound);
}
WebOperationContext.Current.OutgoingResponse.SetETag(employee.GetHashCode());
return employee;
}
public void Update(Employee employee)
{
var existing = employees.FirstOrDefault(e => e.Id == employee.Id);
if (null == existing)
{
throw new WebFaultException(HttpStatusCode.NotFound);
}
//模拟并发修改
existing.Name += Guid.NewGuid().ToString();
WebOperationContext.Current.IncomingRequest.CheckConditionalUpdate(existing.GetHashCode());
employees.Remove(existing);
employees.Add(employee);
WebOperationContext.Current.OutgoingResponse.SetETag(employee.GetHashCode());
}
}
[/code]
[/code]
Update方法中我们通过手工修改相应员工的Name属性的方式来模拟针对相同员工信息的并发修改。在真正实施修改之前调用当前IncomingWebRequestContext的CheckConditionalUpdate方法进行条件更新检测,而作为参数传入的ETag值为代表目前员工的Employee对象的哈希码。方法的最后我们对回复消息的ETag报头作了更新。
我们通过手工创建HTTP请求的方式对上述的两个服务操作进行调用。如下面的代码片断所示,我们首先通过创建的HttpWebRequest对象调用Get操作获得ID为001的员工信息并将其打印出来。然后创建调用Update操作的HttpWebRequest,并对HTTP方法(POST)和内容类型(application/xml)进行了相应的设置。我们之前针对员工获取请求得到ETag报头和员工数据作为本次请求的If-Match报头和主体。如果调用GetResponse方法抛出WebException异常,并且其回复状态为PreconditionFailed,则表明试图修改的员工信息已被另一个用户修改过了,所以我么打印“服务端数据已发生变化”字样。
[code] [code] Uri address = new Uri("http://127.0.0.1:3721/employees/001");
var request = (HttpWebRequest)HttpWebRequest.Create(address);
request.Method = "GET";
var response = (HttpWebResponse)request.GetResponse();
string employee;
using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
employee = reader.ReadToEnd();
Console.WriteLine("获取员工信息:");
Console.WriteLine(employee + "\n");
}
try
{
address = new Uri("http://127.0.0.1:3721/employees/");
request = (HttpWebRequest)HttpWebRequest.Create(address);
request.Method = "POST";
request.ContentType = "application/xml";
byte[] buffer = Encoding.UTF8.GetBytes(employee);
request.GetRequestStream().Write(Encoding.UTF8.GetBytes(employee), 0, buffer.Length);
request.Headers.Add(HttpRequestHeader.IfMatch, response.Headers[HttpResponseHeader.ETag]);
Console.WriteLine("修改员工信息:");
request.GetResponse();
}
catch (WebException ex)
{
response = ex.Response as HttpWebResponse;
if (null == response)
{
throw;
}
if (response.StatusCode == HttpStatusCode.PreconditionFailed)
{
Console.WriteLine("服务端数据已发生变化");
}
else
{
throw;
}
}
[/code]
[/code]
在服务成功寄宿的情况下调用这段程序会在控制台上输出如下的结果。由于并发错误的发生,员工信息其实并没有被真正修改。
[code] [code] 获取员工信息:
<Employee xmlns="http://www.artech.com/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Department>开发部</Department><Grade>G7</Grade><Id>001</Id><Name>张三</Name></Employee>
修改员工信息:
服务端数据已发生变化
[/code]
[/code]
分类: [01] 技术剖析
绿色通道:好文要顶已关注收藏该文与我联系
相关文章推荐
- [WCF REST] 解决资源并发修改的一个有效的手段:条件更新(Conditional Update)
- [WCF REST] 提高性能的一个有效的手段:条件资源获取(Conditional Retrieval)
- [WCF REST] 提高性能的一个有效的手段:条件资源获取(Conditional Retrieval)
- [WCF REST] 提高性能的一个有效的手段:条件资源获取(Conditional Retrieval)
- “当传递具有已修改行的 DataRow 集合时,更新要求有效的 UpdateCommand”错误解决
- cocos2d-x-3.12 打包android apk时 怎么修改已经配置好的setup.py参数 和解决 XX不是一个有效的 Android 目标平台 错误
- 关于sql server的纪录修改冲突解决和oracle的for update 的并发锁定测试
- MySQL中SELECT+UPDATE处理并发更新问题解决方案分享
- 当传递具有已修改行的 DataRow 集合时,更新要求有效的 UpdateCommand问题解决
- MySQL中SELECT+UPDATE处理并发更新问题解决方案分享
- MySQL中SELECT+UPDATE处理并发更新问题解决方案
- 乐观锁和update内个解决并发,where后面的条件
- [总结]在VS中编辑修改资源视图后保存时提示-cannot change standard mfc resources ,无法保存资源视图的一个解决方法
- MySQL中SELECT+UPDATE处理并发更新问题解决方案分享
- b/s软件中使用Access数据库,只能查询,不能添加、修改、删除,提示操作必须是一个可更新的查询解决。
- MySQL中SELECT+UPDATE处理并发更新问题解决方案分享
- Transact-SQL 示例 - 一个UPDATE实现多个数据列的条件更新
- PostgreSQL忘记输入where条件update更新整张表的解决办法
- 关于sql server的纪录修改冲突解决和oracle的for update 的并发锁定测试
- datagridview当传递具有已修改行的 DataRow 集合时,更新要求有效的 UpdateCommand。