J'utilise RazorEngine pour l'analyse de modèles dans mon projet MVC 6 comme ceci:
Engine.Razor.RunCompile(File.ReadAllText(fullTemplateFilePath), templateName, null, model);
Cela fonctionne très bien pour la version 6. Cela ne fonctionne pas après la mise à niveau vers la version 7 avec l’erreur suivante:
MissingMethodException: Méthode introuvable: "Annuler Microsoft.AspNet.Razor.CodeGenerators.GeneratedClassContext.set_ResolveUrlMethodName (System.String)" . dans RazorEngine.Compilation.CompilerServiceBase.CreateHost (Type templateType, Type modelType, String className)
C'est global.json:
{
"projects": [ "src", "test" ],
"sdk": {
"version": "1.0.0-beta7",
"runtime": "clr",
"architecture": "x64"
}
}
C'est project.json:
...
"dependencies": {
"EntityFramework.SqlServer": "7.0.0-beta7",
"EntityFramework.Commands": "7.0.0-beta7",
"Microsoft.AspNet.Mvc": "6.0.0-beta7",
"Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-beta7",
"Microsoft.AspNet.Authentication.Cookies": "1.0.0-beta7",
"Microsoft.AspNet.Authentication.Facebook": "1.0.0-beta7",
"Microsoft.AspNet.Authentication.Google": "1.0.0-beta7",
"Microsoft.AspNet.Authentication.MicrosoftAccount": "1.0.0-beta7",
"Microsoft.AspNet.Authentication.Twitter": "1.0.0-beta7",
"Microsoft.AspNet.Diagnostics": "1.0.0-beta7",
"Microsoft.AspNet.Diagnostics.Entity": "7.0.0-beta7",
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-beta7",
"Microsoft.AspNet.Server.IIS": "1.0.0-beta7",
"Microsoft.AspNet.Server.WebListener": "1.0.0-beta7",
"Microsoft.AspNet.StaticFiles": "1.0.0-beta7",
"Microsoft.AspNet.Tooling.Razor": "1.0.0-beta7",
"Microsoft.Framework.Configuration.Abstractions": "1.0.0-beta7",
"Microsoft.Framework.Configuration.Json": "1.0.0-beta7",
"Microsoft.Framework.Configuration.UserSecrets": "1.0.0-beta7",
"Microsoft.Framework.Logging": "1.0.0-beta7",
"Microsoft.Framework.Logging.Console": "1.0.0-beta7",
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-beta7",
"RazorEngine": "4.2.2-beta1"
},
...
"frameworks": {
"dnx451": { }
},
...
Mon modèle est:
@model dynamic
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Registration</title>
</head>
<body>
<p>
Hello, @Model
</p>
</body>
</html>
Quelqu'un a-t-il des problèmes similaires? Il existe un autre moyen d'analyser les modèles dans MVC 6?
Fonctionne bien sur les versions suivantes 1.0.0
, RC2
À qui s'adresse aspnetcore RC2, cet extrait de code pourrait vous aider:
Startup
https://Gist.github.com/ahmad-moussawi/1643d703c11699a6a4046e57247b4d09
// using a Model
string html = view.Render("Emails/Test", new Product("Apple"));
// using a Dictionary<string, object>
var viewData = new Dictionary<string, object>();
viewData["Name"] = "123456";
string html = view.Render("Emails/Test", viewData);
Les liens dans Razor sont affichés sous la forme d'URL relative. Par conséquent, cela ne fonctionnera pas avec les vues externes (telles que les courriels, etc.).
Pour l'instant, je crée le lien sur le contrôleur et le passe à la vue via le ViewModel.
La source est extraite de (Merci à @pholly): https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs )
J'ai trouvé ce fil qui en parle: https://github.com/aspnet/Mvc/issues/3091
Une personne du fil a créé un exemple de service ici: https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs
Après des essais et des erreurs, j'ai été en mesure de réduire le service. Il n'a donc besoin que d'une HttpContext
valide et d'une ViewEngine
et j'ai ajouté une surcharge ne nécessitant pas de modèle. Les vues sont relatives à la racine de votre application (elles ne doivent pas nécessairement résider dans un dossier Views
).
Vous devrez enregistrer le service dans Startup.cs
et également enregistrer HttpContextAccessor
:
//Startup.cs ConfigureServices()
services.AddTransient<ViewRenderService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System;
using System.IO;
namespace LibraryApi.Services
{
public class ViewRenderService
{
IRazorViewEngine _viewEngine;
IHttpContextAccessor _httpContextAccessor;
public ViewRenderService(IRazorViewEngine viewEngine, IHttpContextAccessor httpContextAccessor)
{
_viewEngine = viewEngine;
_httpContextAccessor = httpContextAccessor;
}
public string Render(string viewPath)
{
return Render(viewPath, string.Empty);
}
public string Render<TModel>(string viewPath, TModel model)
{
var viewEngineResult = _viewEngine.GetView("~/", viewPath, false);
if (!viewEngineResult.Success)
{
throw new InvalidOperationException($"Couldn't find view {viewPath}");
}
var view = viewEngineResult.View;
using (var output = new StringWriter())
{
var viewContext = new ViewContext();
viewContext.HttpContext = _httpContextAccessor.HttpContext;
viewContext.ViewData = new ViewDataDictionary<TModel>(new EmptyModelMetadataProvider(), new ModelStateDictionary())
{ Model = model };
viewContext.Writer = output;
view.RenderAsync(viewContext).GetAwaiter().GetResult();
return output.ToString();
}
}
}
}
Exemple d'utilisation:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using LibraryApi.Services;
using System.Dynamic;
namespace LibraryApi.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
ILogger<ValuesController> _logger;
ViewRenderService _viewRender;
public ValuesController(ILogger<ValuesController> logger, ViewRenderService viewRender)
{
_logger = logger;
_viewRender = viewRender;
}
// GET api/values
[HttpGet]
public string Get()
{
//ViewModel is of type dynamic - just for testing
dynamic x = new ExpandoObject();
x.Test = "Yes";
var viewWithViewModel = _viewRender.Render("eNotify/Confirm.cshtml", x);
var viewWithoutViewModel = _viewRender.Render("MyFeature/Test.cshtml");
return viewWithViewModel + viewWithoutViewModel;
}
}
}
Auparavant, j’utilisais la RazorEngine
dans une bibliothèque de classes car mon objectif était de restituer des modèles à partir de cette bibliothèque de classes.
Si je comprends bien, vous semblez être à l’intérieur d’un projet MVC 6.0, pourquoi ne pas utiliser une méthode RenderPartialViewToString()
sans devoir ajouter la dépendance à la RazorEngine
?
Gardez à l'esprit, je demande seulement parce que je suis curieux.
Par exemple, à partir de VS2015, j'ai créé une nouvelle application Web ASP.NET et sélectionné le modèle d'application Web dans les modèles de prévisualisation ASP.NET 5.
Dans le dossier ViewModels
, j'ai créé une PersonViewModel
:
public class PersonViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", this.FirstName, this.LastName);
}
}
}
J'ai ensuite créé une nouvelle BaseController
et ajouté une méthode RenderPartialViewToString()
:
public string RenderPartialViewToString(string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = ActionContext.ActionDescriptor.Name;
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
var engine = Resolver.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
ViewEngineResult viewResult = engine.FindPartialView(ActionContext, viewName);
ViewContext viewContext = new ViewContext(ActionContext, viewResult.View, ViewData, TempData, sw,new HtmlHelperOptions());
var t = viewResult.View.RenderAsync(viewContext);
t.Wait();
return sw.GetStringBuilder().ToString();
}
}
Le mérite revient à @DavidG pour sa méthode
Dans le dossier Views-->Shared
, j’ai créé un nouveau dossier Modèles dans lequel j’ai ajouté une simple vue RegistrationTemplate.cshtml
View fortement typée à ma PersonViewModel
, comme ceci:
@model MyWebProject.ViewModels.PersonViewModel
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Registration</title>
</head>
<body>
<p>
Hello, @Model.FullName
</p>
</body>
</html>
La dernière étape consiste à faire hériter ma Controller
de ma BaseController
public class MyController : BaseController
Et créer quelque chose comme:
public IActionResult Index()
{
var model = new PersonViewModel();
model.FirstName = "Frank";
model.LastName = "Underwood";
var emailbody = base.RenderPartialViewToString("Templates/RegistrationTemplate", model);
return View();
}
Bien sûr, l’exemple ci-dessus est inutile car je ne fais rien avec la variable emailbody
mais l’idée est de montrer comment elle est utilisée.
À ce stade, j'aurais pu (par exemple) invoquer une EmailService
et transmettre la emailbody
:
_emailService.SendEmailAsync("[email protected]", "registration", emailbody);
Je ne suis pas sûr que ce soit une alternative appropriée à votre tâche actuelle.
Aujourd'hui, j'ai fini avec ma bibliothèque qui peut résoudre votre problème. Vous pouvez l'utiliser en dehors d'ASP.NET car il n'y a pas de dépendances
Exemple:
string content = "Hello @Model.Name. Welcome to @Model.Title repository";
var model = new
{
Name = "John Doe",
Title = "RazorLight"
};
var engine = new RazorLightEngine();
string result = engine.ParseString(content, model);
//Output: Hello John Doe, Welcome to RazorLight repository
Pour améliorer la réponse de @vlince (cela ne fonctionnait pas pour moi), voici ce que j'ai fait:
1- Créez un contrôleur de base que votre autre contrôleur héritera
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.Rendering;
using System.IO;
namespace YourNameSpace
{
public class BaseController : Controller
{
protected ICompositeViewEngine viewEngine;
public BaseController(ICompositeViewEngine viewEngine)
{
this.viewEngine = viewEngine;
}
protected string RenderViewAsString(object model, string viewName = null)
{
viewName = viewName ?? ControllerContext.ActionDescriptor.ActionName;
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
IView view = viewEngine.FindView(ControllerContext, viewName, true).View;
ViewContext viewContext = new ViewContext(ControllerContext, view, ViewData, TempData, sw, new HtmlHelperOptions());
view.RenderAsync(viewContext).Wait();
return sw.GetStringBuilder().ToString();
}
}
}
}
2- Hériter du contrôleur de base et appeler la méthode
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewEngines;
namespace YourNameSpace
{
public class YourController : BaseController
{
public YourController(ICompositeViewEngine viewEngine) : base(viewEngine) { }
public string Index(int? id)
{
var model = new MyModel { Name = "My Name" };
return RenderViewAsString(model);
}
}
}
ResolveUrlMethodName
a été supprimé. Par conséquent, dans votre CreateHost
ici vous essayez de définir une propriété qui n'existe pas :).
Nous avons décidé de déplacer le traitement ~/
du noyau Razor vers un TagHelper
implémenté dans l’assemblage Microsoft.AspNet.Mvc.Razor
. Voici le commit aux bits qui ont supprimé la méthode.
Espérons que cela aide.
Méthode d'extension pour convertir des vues partielles en réponse en chaîne.
public static class PartialViewToString
{
public static async Task<string> ToString(this PartialViewResult partialView, ActionContext actionContext)
{
using(var writer = new StringWriter())
{
var services = actionContext.HttpContext.RequestServices;
var executor = services.GetRequiredService<PartialViewResultExecutor>();
var view = executor.FindView(actionContext, partialView).View;
var viewContext = new ViewContext(actionContext, view, partialView.ViewData, partialView.TempData, writer, new HtmlHelperOptions());
await view.RenderAsync(viewContext);
return writer.ToString();
}
}
}
Utilisation dans vos actions de contrôleur.
public async Task<IActionResult> Index()
{
return await PartialView().ToString(ControllerContext)
}
Une solution alternative utilisant uniquement ASP.NET Core, aucune bibliothèque externe et aucune réflexion ne peut être trouvée ici: https://weblogs.asp.net/ricardoperes/getting-html-for-a-viewresult-in-asp- net-core . Il nécessite juste un ViewResult et un HttpContext.