Предварительная загрузка представлений ASP.NET MVC на этапе подготовки IIS

Недавно я начал экспериментировать со способностью IIS применять этап подготовки к моему веб-приложению с помощью интерфейса IProcessHostPreloadClient (см. здесь для руководства по настройке ). Это сработало отлично, или, по крайней мере, я так думал, потому что одна из «умных» вещей, которые я сделал, заключалась в том, чтобы попытаться предварительно загрузить свои представления, перебирая мои контроллеры и отображая их.

После нескольких проб и ошибок я заработал, и все было хорошо. То есть, пока я не заметил, что все валидации для моей системы перестали работать, ни клиентская, ни серверная валидация. Я предполагаю, что проверка обычно подключается к представлениям, когда MVC извлекает представление в первый раз, и мне это не удалось. Кто-нибудь знает, как это можно включить в мое решение или, возможно, сделать по-другому?

Код:

public class Warmup : IProcessHostPreloadClient
{
    public void Preload(string[] parameters)
    {
        //Pre-render all views
        AutoPrimeViewCache("QASW.Web.Mvc.Controllers", @"Views\");
        AutoPrimeViewCache("QASW.Web.Mvc.Areas.Api.Controllers", @"Areas\Api\Views\", "Api");
    }

    private void AutoPrimeViewCache(string controllerNamespace, string relativeViewPath, string area = null)
    {
        var controllerTypes = typeof(Warmup).Assembly.GetTypes().Where(t => t.Namespace == controllerNamespace && (t == typeof(Controller) || t.IsSubclassOf(typeof(Controller))));
        var controllers = controllerTypes.Select(t => new { Instance = (Controller)Activator.CreateInstance(t), Name = t.Name.Remove("Controller") });

        foreach (var controller in controllers)
        {
            var viewPath = Path.Combine(HostingEnvironment.ApplicationPhysicalPath, relativeViewPath + controller.Name);
            var viewDir = new DirectoryInfo(viewPath);
            if (viewDir.Exists)
            {
                var viewNames = viewDir.EnumerateFiles("*.cshtml").Select(f => f.Name.Remove(".cshtml")).ToArray();
                PreloadController(controller.Instance, area, viewNames);
            }
        }
    }

    private void PreloadController(Controller controller, string area, params string[] views)
    {
        var viewEngine = new RazorViewEngine();

        var controllerName = controller.GetType().Name.Remove("Controller");
        var http = new HttpContextWrapper(new HttpContext(new HttpRequest(null, "http://a.b.com", null), new HttpResponse(TextWriter.Null)));
        var routeDescription = area == null ? "{controller}/{action}/{id}" : area + "/{controller}/{action}/{id}";
        var route = new RouteCollection().MapRoute(
            "Default", // Route name
            routeDescription, // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );

        var routeData = new RouteData(route, route.RouteHandler);
        routeData.Values.Add("controller", controllerName);
        if (area != null)
        {
            routeData.Values.Add("area", area);
            routeData.DataTokens.Add("area", area);
        }
        routeData.DataTokens.Add("controller", controllerName);
        routeData.Values.Add("id", 1);
        routeData.DataTokens.Add("id", 1);
        var controllerContext = new ControllerContext(http, routeData, controller);
        var vDic = new ViewDataDictionary();
        var vTemp = new TempDataDictionary();

        foreach (var view in views)
        {
            var viewResult = viewEngine.FindView(controllerContext, view, null, false);
            if (viewResult.View == null)
                throw new ArgumentException("View not found: {0} (Controller: {1})".Args(view, controllerName));
            var viewContext = new ViewContext(controllerContext, viewResult.View, vDic, vTemp, TextWriter.Null);
            try { viewResult.View.Render(viewContext, TextWriter.Null); }
            catch { }
        }
    }
}

person Morten Christiansen    schedule 03.10.2011    source источник


Ответы (3)


Не прямой ответ на ваш вопрос, но я думаю, вам следует взглянуть на Предварительная компиляция представлений MVC Razor с использованием RazorGenerator, Дэвид Эббо

Одна из причин сделать это — избежать любого попадания во время выполнения при запуске вашего сайта, поскольку во время выполнения нечего компилировать. Это может быть важно для сайтов с большим количеством просмотров.

person archil    schedule 03.10.2011
comment
Я использую некоторые глобальные вспомогательные методы Razor и не могу заставить генератор Razor работать с ними. В остальном это очень полезный инструмент. - person Morten Christiansen; 09.11.2011
comment
Я не уверен, что предварительная компиляция представлений стоит усилий. Я думаю, что новая инициализация приложения IIS с его поддержкой перезапуска перекрывающихся процессов решает проблему лучше. - person Jacob Hamacher; 03.10.2012

Проблема не в коде в вопросе, а во времени его выполнения. Перемещение кода в действие позволяет мне без проблем выполнить подготовительный этап. В моем случае, я думаю, я просто заставлю процесс установки вызвать действие прогрева после того, как система будет настроена.

person Morten Christiansen    schedule 09.11.2011

Существует новый модуль от Microsoft, который является частью IIS 8.0 и заменяет предыдущий модуль прогрева. Этот модуль инициализации приложений для IIS 7.5 доступен для отдельной загрузки.

Модуль создаст фазу прогрева, где вы можете указать количество запросов, которые должны быть выполнены, прежде чем сервер начнет принимать запросы. Эти запросы будут выполнять и компилировать все ваши представления более надежно, чем то, чего вы пытаетесь достичь.

Я ответил на аналогичный вопрос с более подробной информацией по адресу Как разогреть приложение ASP.NET MVC в IIS 7.5?.

person Jacob Hamacher    schedule 03.10.2012