I'm trying to make a service to AB-test different views on a page. The service has a method that takes a variable amount of ViewResults and returns one of them after determining which version to serve. The problem is that the model applied to the last ViewResult in the arguments list is applied to all arguments. The below code is a simplified version of my code just to illustrate the setup:
This is the method in the service:
public ViewResult GetTestVariantView(params ViewResult[] views){
...omitted randomization code that returns an index
return views[index] ?? views[0]
}
This is how it's used in the controller:
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));
}
I expect to get for example _PageView.cshtml with modelOne if the service returns index 0 of the params list - but no, I get _PageView.cshtml with modelThree. Somehow just the model is taken from the last entry though, because if the service returns index 1 I get _SomeOtherPageView.cshtml with modelThree.
I can't find any documentation or similar questions asked about how to resolve this or even do it in another way. Is there a more proper way to determine which view to present using an external method? I know I could return an index from the method and have a switch/ifelse statement in the controller but I'd like to avoid that if possible simply because it clutters the controller.
What are my options?
CodePudding user response:
Looking at the source code, the model passed to the View
method is stored in the ViewData
collection, not the returned ViewResult
. If you call View
multiple times with different non-null models, you will overwrite the ViewData.Model
property each time, and only the last value will be used.
As Camilo said, change your code so that you only call View
once. For example:
public (string viewName, object model) GetTestVariantView(
params (string viewName, object model)[] views)
{
...
}
(string viewName, object model) = _testingService.GetTestVariantView(
("~/Views/_PageView.cshtml", modelOne),
("~/Views/_SomeOtherPageView.cshtml", modelTwo ),
("~/Views/_PageView.cshtml", modelThree));
return View(viewName, model);