Overriding view location for Razor View Engine

2019-07-14 17:11发布

I prefer to work with one class per controller action in my MVC apps, however I'd to be able to structure my view folder with one folder per "controller".

I use routes like /Admin/Login which maps to a class called AdminLoginController and /Admin/Index which would map to AdminIndexController. However, I'd like to structure my view folder such that I have a single folder called Admin and then 2 files called login.cshtml and index.cshtml.

To do that it looks like I need to override the ViewEngine. I've got it working with the following code:

public class MyViewEngine : RazorViewEngine
{
    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        var controllerTypeName = controllerContext.RouteData.GetRequiredString("controller");
        var controllerParts =_GetControllerNameParts(controllerTypeName);
        var folderName = controllerParts.Take(controllerParts.Length - 1).Aggregate((s1, s2) => s1 + s2);
        viewName = controllerParts[controllerParts.Length - 1];
        var filename = _GetFilename(folderName, viewName, controllerContext);

        return new ViewEngineResult(CreateView(controllerContext, filename, masterName), this);
    }

    private string[] _GetControllerNameParts(string controllerTypeName)
    {
        var r = new Regex(@"
            (?<=[A-Z])(?=[A-Z][a-z]) |
             (?<=[^A-Z])(?=[A-Z]) |
             (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace);
        return r.Split(controllerTypeName);
    }

    private string _GetFilename(string folderName, string viewName, ControllerContext controllerContext)
    {
        var path = string.Format("~/Views/{0}/{1}.cshtml", folderName, viewName);
        var filename = controllerContext.HttpContext.Server.MapPath(path);
        if (File.Exists(filename)) { return path; }
        return null;
    }
}

I'd like to know if there's a better way, in particular, the base view engine does a bunch of caching etc which I'd like to take advantage of.

Has anyone done something similar?

Thanks, Matt

1条回答
倾城 Initia
2楼-- · 2019-07-14 17:44

Have a look at the ViewLocationCache to add some caching.

It's quite simple to use here is a snippet from my FindView:

        string cacheKey = CreateCacheKey(name, areaName, cacheKeyPrefix, specificPath, controllerName, skin, deviceManufacturerName, deviceName);

        if (useCache)
        {
            return ViewLocationCache.GetViewLocation(controllerContext.HttpContext, cacheKey);
        }
查看更多
登录 后发表回答