Как сделать ASP.NET MVC представление в виде строки?

У нас здесь две проблемы.

  • Сначала мы должны контролировать файл конфигурации, поставляемый с программным обеспечением. Разработчику достаточно легко проверить нежелательное изменение основного файла конфигурации, если они используют один и тот же файл в среде передачи. С другой стороны, если у вас есть отдельный файл конфигурации, который включен установщиком, очень легко забыть добавить к нему новый параметр или позволить комментариям в нем не синхронизироваться с комментариями при передаче Конфигурирование файла.
  • . Тогда у нас есть проблема, что разработчики должны постоянно обновлять копию конфигурационного файла, так как другие разработчики добавляют новые параметры конфигурации. Однако некоторые настройки, такие как строки подключения к базе данных, различны для каждого разработчика.
  • Существует 3-я проблема, которую вопрос / ответы не затрагивают. Как вы сходите на изменения, внесенные клиентом в файл конфигурации при установке новой версии вашего программного обеспечения?

Мне еще нужно см. хорошие решения, которые хорошо работают во всех случаях, однако я видел некоторые частичные решения (которые могут быть объединены в различные комбинации по мере необходимости), что значительно уменьшает проблему.

  • Сначала уменьшите количество элементов конфигурации, которые у вас есть в вашем основном файле конфигурации. Если вам не нужно позволять вашим клиентам изменять ваши сопоставления, используйте Fluent NHibernate (или иначе), чтобы переместить конфигурацию в код. Аналогично для установки установки вставки.
  • Разделите файл конфигурации, когда это возможно, например. используйте отдельный файл для настройки журнала Log4Net.
  • Не повторяйте элементы между множеством конфигурационных файлов, например. если у вас есть 4 веб-приложения, которые все установлены на одном компьютере, у вас есть общий файл конфигурации, на который указывает файл web.config в каждом приложении. (Используйте относительный путь по умолчанию, поэтому редко нужно менять файл web.config)
  • Обработать файл конфигурации разработки, чтобы получить файл конфигурации доставки. Это можно сделать с помощью значений по умолчанию в комментариях Xml, которые затем устанавливаются в файле конфигурации при выполнении сборки. Или разделы, которые были удалены как часть процесса создания установщика.
  • Вместо того, чтобы иметь только одну строку подключения к базе данных, имейте одну для разработчиков. Например, сначала найдите «database_ianr» (где ianr - мое имя пользователя или имя машины) в файле конфигурации во время выполнения, если он не найден, тогда найдите «базу данных». Сделайте 2-й уровень «eg -oracle или -sqlserver» make это быстрее для разработчиков, чтобы добраться до обеих систем баз данных. Это, конечно же, можно сделать и для любого другого значения конфигурации. Затем все значения, которые заканчиваются на «_userName», могут быть вычеркнуты перед отправкой файла конфигурации.

Однако в конце концов вы являетесь «владельцем файла конфигурации», который принимает ответственным за управление конфигурационными файлами (файлами), как указано выше или иным образом.

Вы не можете удалить необходимость в заботливом человеке, если это не проблема.

463
задан 9 revs, 4 users 67% 3 July 2018 в 17:08
поделиться

7 ответов

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

Стиль MVC2 .ascx

protected string RenderViewToString<T>(string viewPath, T model) {
  ViewData.Model = model;
  using (var writer = new StringWriter()) {
    var view = new WebFormView(ControllerContext, viewPath);
    var vdd = new ViewDataDictionary<T>(model);
    var viewCxt = new ViewContext(ControllerContext, view, vdd,
                                new TempDataDictionary(), writer);
    viewCxt.View.Render(viewCxt, writer);
    return writer.ToString();
  }
}

Стиль Razor .cshtml

public string RenderRazorViewToString(string viewName, object model)
{
  ViewData.Model = model;
  using (var sw = new StringWriter())
  {
    var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext,
                                                             viewName);
    var viewContext = new ViewContext(ControllerContext, viewResult.View,
                                 ViewData, TempData, sw);
    viewResult.View.Render(viewContext, sw);
    viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
    return sw.GetStringBuilder().ToString();
  }
}

Редактировать: добавлен код Razor.

563
ответ дан 22 November 2019 в 22:49
поделиться

Это работает на меня:

public virtual string RenderView(ViewContext viewContext)
{
    var response = viewContext.HttpContext.Response;
    response.Flush();
    var oldFilter = response.Filter;
    Stream filter = null;
    try
    {
        filter = new MemoryStream();
        response.Filter = filter;
        viewContext.View.Render(viewContext, viewContext.HttpContext.Response.Output);
        response.Flush();
        filter.Position = 0;
        var reader = new StreamReader(filter, response.ContentEncoding);
        return reader.ReadToEnd();
    }
    finally
    {
        if (filter != null)
        {
            filter.Dispose();
        }
        response.Filter = oldFilter;
    }
}
32
ответ дан Tim Scott 4 July 2018 в 03:08
поделиться

Дополнительная подсказка для ЯДРА СЕТИ ASP:

Интерфейс:

public interface IViewRenderer
{
  Task<string> RenderAsync<TModel>(Controller controller, string name, TModel model);
}

Реализация:

public class ViewRenderer : IViewRenderer
{
  private readonly IRazorViewEngine viewEngine;

  public ViewRenderer(IRazorViewEngine viewEngine) => this.viewEngine = viewEngine;

  public async Task<string> RenderAsync<TModel>(Controller controller, string name, TModel model)
  {
    ViewEngineResult viewEngineResult = this.viewEngine.FindView(controller.ControllerContext, name, false);

    if (!viewEngineResult.Success)
    {
      throw new InvalidOperationException(string.Format("Could not find view: {0}", name));
    }

    IView view = viewEngineResult.View;
    controller.ViewData.Model = model;

    await using var writer = new StringWriter();
    var viewContext = new ViewContext(
       controller.ControllerContext,
       view,
       controller.ViewData,
       controller.TempData,
       writer,
       new HtmlHelperOptions());

       await view.RenderAsync(viewContext);

       return writer.ToString();
  }
}

Регистрация в Startup.cs

...
 services.AddSingleton<IViewRenderer, ViewRenderer>();
...

И использование в контроллере:

public MyController: Controller
{
  private readonly IViewRenderer renderer;
  public MyController(IViewRendere renderer) => this.renderer = renderer;
  public async Task<IActionResult> MyViewTest
  {
    var view = await this.renderer.RenderAsync(this, "MyView", model);
    return new OkObjectResult(view);
  }
}
0
ответ дан Marcin 4 November 2019 в 08:29
поделиться
  • 1
    На самом деле можно преобразовать форму в строку JSON в jQuery с .serialize () функция. Но почему необходимо особенно отправить json, хотя? What' s неправильно только с отправкой данных формы? – Daniel Roseman 30 July 2009 в 18:45

Подсказка

Для строго типизированного Модель просто добавьте его в свойство ViewData.Model перед передачей в RenderViewToString. например,

this.ViewData.Model = new OrderResultEmailViewModel(order);
string myString = RenderViewToString(this.ControllerContext, "~/Views/Order/OrderResultEmail.aspx", "~/Views/Shared/Site.Master", this.ViewData, this.TempData);
1
ответ дан 22 November 2019 в 22:49
поделиться

Я использую MVC 1.0 RTM, и ни одно из вышеперечисленных решений у меня не помогло. Но вот этот:

Public Function RenderView(ByVal viewContext As ViewContext) As String

    Dim html As String = ""

    Dim response As HttpResponse = HttpContext.Current.Response

    Using tempWriter As New System.IO.StringWriter()

        Dim privateMethod As MethodInfo = response.GetType().GetMethod("SwitchWriter", BindingFlags.NonPublic Or BindingFlags.Instance)

        Dim currentWriter As Object = privateMethod.Invoke(response, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.InvokeMethod, Nothing, New Object() {tempWriter}, Nothing)

        Try
            viewContext.View.Render(viewContext, Nothing)
            html = tempWriter.ToString()
        Finally
            privateMethod.Invoke(response, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.InvokeMethod, Nothing, New Object() {currentWriter}, Nothing)
        End Try

    End Using

    Return html

End Function
3
ответ дан 22 November 2019 в 22:49
поделиться

Не в том смысле, который вы описываете, у вас могут быть очень неэффективные регулярные выражения, которые занимают много ресурсов и в конечном итоге убивает механизм регулярных выражений, это не то же самое, что остановка.

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

//...

//Use HtmlHelper to render partial view to fake context
var html = new HtmlHelper(
    new ViewContext(fakeControllerContext, new FakeView(),
        new ViewDataDictionary(), new TempDataDictionary(), memWriter),
    new ViewPage());
html.RenderPartial(viewName, viewData);

//...
29
ответ дан 22 November 2019 в 22:49
поделиться

Чтобы повторить более неизвестный вопрос, взгляните на MvcIntegrationTestFramework .

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

 private static readonly string mvcAppPath = 
     Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory 
     + "\\..\\..\\..\\MyMvcApplication");
 private readonly AppHost appHost = new AppHost(mvcAppPath);

    [Test]
    public void Root_Url_Renders_Index_View()
    {
        appHost.SimulateBrowsingSession(browsingSession => {
            RequestResult result = browsingSession.ProcessRequest("");
            Assert.IsTrue(result.ResponseText.Contains("<!DOCTYPE html"));
        });
}
0
ответ дан 22 November 2019 в 22:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: