在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
通过本篇,你能了解到 Asp.net MVC 模型绑定处理过程,一种解决并发颗粒度到一条数据的方法.
* 如何解决互联网中某条数据的并发问题 通过Ticks构造的DateTime对象
*如何在Asp.net MVC中使用DateTime对象作为验证数据是否已过期依据 Asp.net MVC 的模型肩负着承载数据的责任.同时又很容易配置,使用.下面这张图介绍了Asp.net MVC如何处理实体对象模型的(截自Pro Asp.net MVC 2 Framework.
同样的,作为客户端,以单纯的字符串是无法直接在.net中使用的,又需要进行模型绑定.才能转换为.net对象,继而使用他.
知道了整个模型的整个流程后,我们有了几个对策,思考再三决定传递DateTime对象的Ticks作为依据,因为直接输出DateTime最多也就精确到秒,不能和数据库中的原始数据DateTime匹配.所以获得其Ticks,作为字符串传输,然后返回的时候再解析.有了解决思路后就好做了. 这是我们的模型,注意其中的HashTime可空属性. 注意他标上了一个UIHint Attribute,能够绑定指定的用户控件,继而单独的渲染该属性.
1 public partial class ModelSimpleChangeUser : IHashTime 2 { 3 [HiddenInput] 4 [DisplayName("用户编号")] 5 [Required] 6 public int UserId { get; set; } 7 8 [Required(ErrorMessage="名称必须不为空")] 9 [DisplayName("用户名")] 10 [DataType(System.ComponentModel.DataAnnotations.DataType.Text)] 11 public string UserName { get; set; } 12 [Required(ErrorMessage="Email必须存在")] 13 [DisplayName("电子邮件")] 14 [DataType(System.ComponentModel.DataAnnotations.DataType.EmailAddress)] 15 public string Email { get; set; } 16 17 [HiddenInput(DisplayValue = false)] 18 [UIHint("HiddenDateTime")] 19 [Required(ErrorMessage="HashTime必须存在")] 20 public DateTime? HashTime { get; set; } 21 22 23 24 public ModelSimpleChangeUser() { } 25 public ModelSimpleChangeUser(ES.DAL.User u) 26 { 27 this.UserId = u.UserId; 28 this.Email = u.Email; 29 this.HashTime = u.HashTime; 30 this.UserName = u.UserName; 31 } 32 33 public ES.DAL.User ChangeTo(ES.DAL.User user) 34 { 35 if(UserId == user.UserId && user.HashTime.HasValue) 36 { 37 user.UserName = UserName; 38 user.Email = Email; 39 } 40 return user; 41 } 42 } 整个编辑视图也就一行起作用的代码.之所以能够这样是因为Asp.net MVC 2强大的模型特性标记功能,他能够在模型中声明如何显示,这就使得视图和模型完全分离,在视图里我都没有内联ViewPage的泛型类.使得页面可以编写完全独立的效果,而不必纠缠于和模型属性配对的问题中.
<%= Html.EditorForModel() %>
在HiddenDateTime控件实现了一个简单的从当前实体模型对象读取Ticks的功能,由于是可空DateTime,所以处理了为null情况(标记字段在测试的时候有些是Null,所以这里给处理了一下,如果在真实的环境中这是不存在的,因为在建立和修改时都已经自动赋值了)
1 <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DateTime?>" %> 2 3 <% 4 Dictionary<string, object> others = new Dictionary<string, object>(); 5 //others.Add("Oth", "ccc"); 6 %> 7 <%= Html.Hidden("", Model.HasValue ? Model.Value.Ticks : -1, others)%> 效果如下:
然后是绑定从客户端发来的数据问题.在这个点上费了很久,后来还是在书上找到了解决办法.
1. Request.Form 2. RouteData.Values 3. Request.QueryString 4. Request.Files 5. null 前台输出的是DateTime的Ticks而不是默认的DateTime输出,所以还需要自己解析下.这里有一段ValueProviderFactroy的代码是截自书中的,在Global.asax的Appcation_Start事件注册后,就能截获所有参数的传递了.
1 protected void Application_Start() 2 { 3 AreaRegistration.RegisterAllAreas(); 4 5 RegisterRoutes(RouteTable.Routes); 6 7 ValueProviderFactories.Factories.Insert(0, new ES.WEB.Models.HiddentTimeValueProviderFactory()); 8 } 下面是那个解析工厂
public class HiddentTimeValueProviderFactory : ValueProviderFactory { public override IValueProvider GetValueProvider(ControllerContext ctx) { return new HiddentTimeValueProvider(ctx.HttpContext.Request); } private class HiddentTimeValueProvider : IValueProvider { private HttpRequestBase request; public HiddentTimeValueProvider(HttpRequestBase Request) { request = Request; } public bool ContainsPrefix(string prefix) { return "HashTime".Equals(prefix, StringComparison.OrdinalIgnoreCase); } public ValueProviderResult GetValue(string key) { var result = ContainsPrefix(key) ? new ValueProviderResult(new DateTime(long.Parse(request["HashTime"])), null, CultureInfo.CurrentCulture) : null; return result; } } } 自此我们的代码已经完全可以跑起来了,在控制器里对传过来的数据直接分析,看Ticks是否相等就能判断有并发情况了.当然在返回错误的视图中我们也做了一些改善用户体验的处理. 下面是我们的控制器:
1 public ActionResult Edit(Models.ModelSimpleChangeUser users) 2 { 3 return DoSafe(() => 4 { 5 var u = UserManager.FindById(users.UserId); 6 if (u.HashTime != null) 7 if(users.HashTime != null && 8 users.HashTime.Value.Ticks == u.HashTime.Value.Ticks) 9 { 10 u.UserName = users.UserName; 11 u.Email = users.Email; 12 UserManager.Update(u); 13 return RedirectToAction("Index") as ActionResult; 14 } 15 ViewData["lastAction"] = "Edit"; 16 ViewData["lastController"] = "User"; 17 return View(RetiredPage, users); 18 }); 19 } 注意15,16行,他将当前处理的Controller和Action的名称都保存了起来,以备在视图中使用,同样将用户输入的数据返回了出去.
下面是视图页面:
1 <asp:Content ID="RetiredMain" ContentPlaceHolderID="MainContent" runat="server"> 2 3 <h2>对不起,您访问的页面已过期!</h2> 4 <p> 5 您所操作的原始数据可能被更改,请您返回获取最新数据. 6 </p> 7 <%= Html.ActionLink("返回", 8 ViewData[BaseController.LastAction].ToString(), 9 ViewData[BaseController.LastController].ToString(), 10 Model, new { id = "PostBackLink"}) %> 11 <script type="text/javascript"> 12 setTimeout(function () { 13 var g = document.getElementById("PostBackLink"); 14 try { g.click(); } catch (ex) { } 15 }, 2000); 16 </script> 17 </asp:Content> 用HtmlHelper生成了一个超链接,使用了我们之前保存的控制器数据,从而得知是在哪里引发了错误,并且还保存了用户输入的数据模型,最后给这个超链接赋值了一个id属性,供我们在客户端脚本上使用. 在客户端脚本里我们触发了一个延迟脚本,两秒后触发返回超链接的click事件,由于在FireFox下没有该对象,所以只是简单的容错了一下,还有更多FireFox下的超链接的脚本触发请移步JavaScript模拟用户单击事件. 整个过程还是相当简单的,不过条条大路通罗马,不一定都要坐技术含量高的飞机嘛.
1 private void setHashTime(object o) 2 { 3 (o as IHashTime).HashTime = DateTime.Now; 4 }
|
请发表评论