Есть ли способ обработать представление MVC (файл aspx) из не веб-приложения? - PullRequest
8 голосов
/ 13 сентября 2010

У меня работает фоновая служба, которая рассылает электронные письма пользователям моего сайта.Я хотел бы написать шаблоны электронной почты в виде представлений MVC, чтобы все было согласованно (чтобы можно было использовать одну и ту же модель для отправки электронной почты и отображения веб-страницы).

К сожалению, когда я пытаюсьсделать LoadControl (который просто подключается к BuildManager.CreateInstanceFromVirtualPath), я получаю следующее:

System.NullReferenceException at
  System.Web.dll!System.Web.VirtualPath.GetCacheKey() + 0x26 bytes  
  System.Web.dll!System.Web.Compilation.BuildManager.GetCacheKeyFromVirtualPath + 0x2a bytes
  System.Web.dll!System.Web.Compilation.BuildManager.GetVPathBuildResultFromCacheInternal  + 0x30 bytes

Кажется, что если бы я установил MvcBuildViews в true, то должен быть какой-то простой способ использоватьСкомпилированные представления для создания шаблона электронной почты, но я не могу понять, как.

Я нашел следующий блог от Рика Строла, который может помочь: http://www.west -wind.com / presentation/aspnetruntime/aspnetruntime.asp

Однако кажется, что для обработки запросов запускается целый сервер ASP.NET.

Есть ли простой способ загрузить представление MVC и выполнить рендерингЭто?Или это единственный способ загрузить среду выполнения ASP.NET, как предложил Рик Страл?

Ответы [ 5 ]

11 голосов
/ 13 сентября 2010

Механизм представления asp.net по умолчанию связан с механизмом asp.net.Это связано с контекстом, я думаю, что вы можете обойти это, но это определенно не просто .

Проблема заключается в комбинации движка представления по умолчанию + движка asp.net, другие движки представления не должнынет этой проблемы.По крайней мере, движок Spark View не делает этого.


Редактировать: Операционная система решена с последними подсказками, но пока моя версия, которая использует действие home index контроллера по умолчаниюasp.net шаблон проекта MVC:

public class MyAppHost : MarshalByRefObject
{
    public string RenderHomeIndexAction()
    {
        var controller = new HomeController();
        using (var writer = new StringWriter())
        {
            var httpContext = new HttpContext(new HttpRequest("", "http://example.com", ""), new HttpResponse(writer));
            if (HttpContext.Current != null) throw new NotSupportedException("httpcontext was already set");
            HttpContext.Current = httpContext;
            var controllerName = controller.GetType().Name;
            var routeData = new RouteData();
            routeData.Values.Add("controller", controllerName.Remove(controllerName.LastIndexOf("Controller")));
            routeData.Values.Add("action", "index");
            var controllerContext = new ControllerContext(new HttpContextWrapper(httpContext), routeData, controller);
            var res = controller.Index();
            res.ExecuteResult(controllerContext);
            HttpContext.Current = null;
            return writer.ToString();
        }
    }
}

... из отдельного проекта:

    [TestMethod]
    public void TestIndexAction()
    {
        var myAppHost = (MyAppHost)ApplicationHost.CreateApplicationHost(
            typeof(MyAppHost), "/", @"c:\full\physical\path\to\the\mvc\project");
        var view = myAppHost.RenderHomeIndexAction();
        Assert.IsTrue(view.Contains("learn more about"));

    }

Некоторые дополнительные примечания:

  • url в новом HttpRequestне имеет значения, но должен быть действительным URL-адресом
  • , он не предназначен для использования из приложения asp.net, в котором уже есть контекст, в котором говорится, что я не уверен, чтофактически порождает новый AppDomain и работает
  • Конструктор типа контроллера и конкретный экземпляр явно в коде, может быть заменен чем-то, что должно быть передано в параметрах, но необходимо иметь дело с ограничениями MarshalByRef / наихудшего случая, некоторыедля этого можно использовать простое отражение
7 голосов
/ 22 сентября 2010

Закончилось ответом на мой собственный вопрос:)

public class AspHost : MarshalByRefObject
{
    public string _VirtualDir;
    public string _PhysicalDir;

    public string ViewToString<T>(string aspx, Dictionary<string, object> viewData, T model)
    {
        StringBuilder sb = new StringBuilder();
        using (StringWriter sw = new StringWriter(sb))
        {
            using (HtmlTextWriter tw = new HtmlTextWriter(sw))
            {
                var workerRequest = new SimpleWorkerRequest(aspx, "", tw);
                HttpContext.Current = new HttpContext(workerRequest);

                ViewDataDictionary<T> viewDataDictionary = new ViewDataDictionary<T>(model);
                foreach (KeyValuePair<string, object> pair in viewData)
                {
                    viewDataDictionary.Add(pair.Key, pair.Value);
                }

                object view = BuildManager.CreateInstanceFromVirtualPath(aspx, typeof(object));

                ViewPage viewPage = view as ViewPage;
                if (viewPage != null)
                {
                    viewPage.ViewData = viewDataDictionary;
                }
                else
                {
                    ViewUserControl viewUserControl = view as ViewUserControl;
                    if (viewUserControl != null)
                    {
                        viewPage = new ViewPage();
                        viewPage.Controls.Add(viewUserControl);
                    }
                }

                if (viewPage != null)
                {
                    HttpContext.Current.Server.Execute(viewPage, tw, true);

                    return sb.ToString();
                }

                throw new InvalidOperationException();
            }
        }
    }

    public static AspHost SetupFakeHttpContext(string physicalDir, string virtualDir)
    {
        return (AspHost)ApplicationHost.CreateApplicationHost(
            typeof(AspHost), virtualDir, physicalDir);
    }
}

Затем, чтобы отобразить файл:

var host = AspHost.SetupFakeHttpContext("Path/To/Your/MvcApplication", "/");
var viewData = new ViewDataDictionary<SomeModelType>(){ Model = myModel };
String rendered = host.ViewToString("~/Views/MyView.aspx", new Dictionary<string, object>(viewData), viewData.Model);
0 голосов
/ 17 сентября 2010

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

Это настолько близко, насколько я смог, но все равно не сработало. Теперь он жалуется на то, что get_Server вызывает исключение NullreferenceException.

Просто подумал, что я опубликую здесь, что я сделал и как далеко я продвинулся, если кто-то захочет продолжить исследование.

Я изменил свой файл csproj, чтобы сгенерировать сборку с предварительно скомпилированными файлами ASPX:

<PropertyGroup>
...
    <MvcBuildViews>true</MvcBuildViews>
    <AspNetMergePath>C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools\aspnet_merge.exe</AspNetMergePath>
...
</PropertyGroup>
<Target Name="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
    <AspNetCompiler PhysicalPath="$(ProjectDir)" TargetPath="$(ProjectDir)..\$(ProjectName)_CompiledAspx" Updateable="false" VirtualPath="$(ProjectName)" Force="true" />
    <Exec Command="%22$(AspNetMergePath)%22 %22$(ProjectDir)..\$(ProjectName)_CompiledAspx%22 -o %22$(ProjectName)_views%22" />
    <Copy SourceFiles="$(ProjectDir)..\$(ProjectName)_CompiledAspx\bin\$(ProjectName)_views.dll" DestinationFolder="$(TargetDir)CompiledAspx\" />
</Target>

Это создало «MyProject_CompiledAspx.dll», на которое я затем ссылался из своего приложения. Это, однако, вызвало новое исключение NullReferenceException.

Жаль, что файлы ASPX, будучи такими же мощными, настолько тесно интегрированы с сервером ASP.NET.

0 голосов
/ 13 сентября 2010

Одним словом, нет - рендеринг представления ASP.NET связан с циклом веб-ответа.Вероятно, было совершенно необходимо получить разумную производительность в прежние времена.

Теперь существуют некоторые другие варианты, в том числе новый бритвенный движок от Microsoft или открытый исходный код Spark ViewДвигатель .

0 голосов
/ 13 сентября 2010

Мы использовали веб-сервер Cassini для нашего веб-приложения, когда оно находилось в автономном режиме.Может быть, этот подход будет работать для вас тоже?Взгляните сюда Кассини

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...