У нас здесь две проблемы.
Мне еще нужно см. хорошие решения, которые хорошо работают во всех случаях, однако я видел некоторые частичные решения (которые могут быть объединены в различные комбинации по мере необходимости), что значительно уменьшает проблему.
blockquote >
- Сначала уменьшите количество элементов конфигурации, которые у вас есть в вашем основном файле конфигурации. Если вам не нужно позволять вашим клиентам изменять ваши сопоставления, используйте Fluent NHibernate (или иначе), чтобы переместить конфигурацию в код. Аналогично для установки установки вставки.
- Разделите файл конфигурации, когда это возможно, например. используйте отдельный файл для настройки журнала Log4Net.
- Не повторяйте элементы между множеством конфигурационных файлов, например. если у вас есть 4 веб-приложения, которые все установлены на одном компьютере, у вас есть общий файл конфигурации, на который указывает файл web.config в каждом приложении. (Используйте относительный путь по умолчанию, поэтому редко нужно менять файл web.config)
- Обработать файл конфигурации разработки, чтобы получить файл конфигурации доставки. Это можно сделать с помощью значений по умолчанию в комментариях Xml, которые затем устанавливаются в файле конфигурации при выполнении сборки. Или разделы, которые были удалены как часть процесса создания установщика.
- Вместо того, чтобы иметь только одну строку подключения к базе данных, имейте одну для разработчиков. Например, сначала найдите «database_ianr» (где ianr - мое имя пользователя или имя машины) в файле конфигурации во время выполнения, если он не найден, тогда найдите «базу данных». Сделайте 2-й уровень «eg -oracle или -sqlserver» make это быстрее для разработчиков, чтобы добраться до обеих систем баз данных. Это, конечно же, можно сделать и для любого другого значения конфигурации. Затем все значения, которые заканчиваются на «_userName», могут быть вычеркнуты перед отправкой файла конфигурации.
Однако в конце концов вы являетесь «владельцем файла конфигурации», который принимает ответственным за управление конфигурационными файлами (файлами), как указано выше или иным образом.
blockquote>Вы не можете удалить необходимость в заботливом человеке, если это не проблема.
Вот что я придумал, и у меня это работает. Я добавил следующие методы в базовый класс моего контроллера. (Вы всегда можете создать эти статические методы где-нибудь еще, которые принимают контроллер в качестве параметра, я полагаю)
Стиль 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.
Это работает на меня:
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;
}
}
Дополнительная подсказка для ЯДРА СЕТИ 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);
}
}
Подсказка
Для строго типизированного Модель просто добавьте его в свойство 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);
Я использую 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
Не в том смысле, который вы описываете, у вас могут быть очень неэффективные регулярные выражения, которые занимают много ресурсов и в конечном итоге убивает механизм регулярных выражений, это не то же самое, что остановка.
Я не думаю, что остановка действительно применима здесь, как так проницательно отметили другие комментаторы этого сообщения.
//...
//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);
//...
Чтобы повторить более неизвестный вопрос, взгляните на 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"));
});
}