3

I would like to determine the view model type from a (strongly typed) ASP.NET MVC 4 View before the view is executed. My Controller logic enables me to determine the view name, and thus load the view programatically, however there doesn't appear to be anything to give a clue about the model type:

var res = ViewEngines.Engines.FindPartialView(this.ControllerContext, viewName);
if (res.View != null)
{
     Type modelType = res.View.GetType(); //returns System.Web.Mvc.RazorView 
     //...so it would be great to be able to do:
     modelType = res.View.GetModelType();//...but this does not exist
}

The reason I want to do this is because I am automatically mapping my domain models to view models - the request contains information from which I can derive the view name, but not the view model type, so I want to derive this from the view in order to do the model mapping.

Will
  • 965
  • 5
  • 19
  • Where do you do that? In that stage the view have not been compiled. It's when you get an object of type `WebViewPage` that you can determine if the view is generic or not – jgauffin May 07 '14 at 07:21
  • You should know the both the object which is going to be bound to the view, and the view which is going to be rendered, in the controller. You can't infer one from the other. If you have to do that, you are probably doing something very wrong. – Bhushan Shah May 07 '14 at 07:42
  • I don't think this is necessarily true if you have separate domain and view models – Will May 07 '14 at 07:50

2 Answers2

4

This will do the trick (I verified this in a controller, but it can run in a filter as well).

** Note this works for the default view engine/default view page and might need tweaking otherwise, it's not hardened by any means, it's just to show the pattern

Type modelType = null;

var view = ViewEngines.Engines.FindView(this.ControllerContext, "Index", string.Empty);

var bmView = (BuildManagerCompiledView)view.View;

// this need caching, no reason to call build manager again and again.
var razorView = BuildManager.GetCompiledType(bmView.ViewPath);

// this doesn't allow for customizing the page type (but not a common scenario)
if (typeof(WebViewPage).IsAssignableFrom(razorView) && razorView.BaseType.IsGenericType)
{
    modelType = razorView.BaseType.GetGenericArguments()[0];
}
Yishai Galatzer
  • 8,791
  • 2
  • 32
  • 41
  • That works well. I don't bother checking if it is a WebViewPage though as I am only interested in getting the generic argument (ie the model type) regardless of the type of the view itself. I also cache this, so it only executes the first time a particular view is requested – Will May 09 '14 at 07:31
  • Good idea to put this in a filter - its a generic process (find the model for a view and map the domain model to this type) - putting it in a filter makes it easier to reuse and turn on/off – Will May 09 '14 at 07:39
  • @Will in your application not checking for the base type is ok, if you never mix aspx pages or have custom base types. If you use custom base types the generic argument might not be the first. But it is such an edge case that I tend to agree with what you are doing. – Yishai Galatzer Apr 29 '15 at 18:04
1

You can do it like this:

//viewName can be "~/Views/Account/Login.cshtml"
var type = BuildManager.GetCompiledType(viewName); 
bool isGeneric = type.BaseType.IsGenericType;
jgauffin
  • 99,844
  • 45
  • 235
  • 372