ASP.NET MVC AB测试服务多ViewResult均被应用最后一个Model如何解决
问题根因
ASP.NET MVC/.NET Core MVC中Controller.View()方法返回的ViewResult并不会独立存储传入的Model,而是引用当前控制器实例的ViewData对象。你连续调用三次View()时,每次调用都会覆盖ViewData.Model属性的值,所有返回的ViewResult都指向同一个ViewData实例,所以最终所有ViewResult取到的Model都是最后一次赋值的modelThree。
解决方案
方案1:传入视图构造委托,延迟创建ViewResult
不要提前创建所有ViewResult,而是把每个视图的构造逻辑封装成委托传给测试服务,服务选中对应索引后再执行委托生成对应的ViewResult,从根源上避免ViewData被覆盖。
服务方法修改:
public ViewResult GetTestVariantView(params Func<ViewResult>[] viewFactories) { // 省略原有随机返回索引的逻辑 return viewFactories[index]?.Invoke() ?? viewFactories[0].Invoke(); }
控制器调用修改:
public ActionResult Index() { var modelOne = new PageViewModel(letter: "A"); var modelTwo = new PageViewModel(letter: "B"); var modelThree = new PageViewModel(letter: "C"); return _testingService.GetTestVariantView( () => View("~/Views/_PageView.cshtml", modelOne), () => View("~/Views/_SomeOtherPageView.cshtml", modelTwo ), () => View("~/Views/_PageView.cshtml", modelThree)); }
该方案改动最小,完全不需要修改原有随机选版本的逻辑,控制器代码也没有冗余。
方案2:提前深拷贝每个ViewResult的独立数据
如果你一定要提前创建所有ViewResult,就在生成每个ViewResult后手动复制一份独立的ViewData,避免引用同一个对象:
// 可以封装成控制器的扩展方法 private ViewResult CreateIndependentView(string viewPath, object model) { var viewResult = View(viewPath, model); // 新建独立的ViewData实例,避免共享控制器的全局ViewData viewResult.ViewData = new ViewDataDictionary(ViewData) { Model = model }; return viewResult; }
调用时用这个扩展方法代替原生View()方法即可:
return _testingService.GetTestVariantView( CreateIndependentView("~/Views/_PageView.cshtml", modelOne), CreateIndependentView("~/Views/_SomeOtherPageView.cshtml", modelTwo ), CreateIndependentView("~/Views/_PageView.cshtml", modelThree));
方案3:调整服务返回值为视图元组,控制器统一构造ViewResult
让测试服务返回选中的视图路径和Model,控制器只需要调用一次View()即可,也不会有冗余代码:
服务方法修改:
public (string ViewPath, object Model) GetTestVariant(params (string ViewPath, object Model)[] variants) { // 省略原有随机返回索引的逻辑 return variants[index]; }
控制器调用修改:
public ActionResult Index() { var modelOne = new PageViewModel(letter: "A"); var modelTwo = new PageViewModel(letter: "B"); var modelThree = new PageViewModel(letter: "C"); var selected = _testingService.GetTestVariant( ("~/Views/_PageView.cshtml", modelOne), ("~/Views/_SomeOtherPageView.cshtml", modelTwo ), ("~/Views/_PageView.cshtml", modelThree)); return View(selected.ViewPath, selected.Model); }
该方案性能最好,不需要创建多余的ViewResult对象,也没有引用共享问题。
内容的提问来源于stack exchange,提问作者purple_cwsh
相关产品推荐
相关产品推荐

