Domanda Layout e sezioni delle stringhe RazorEngine?


Uso il motore del rasoio in questo modo:

public class EmailService : IService
{
    private readonly ITemplateService templateService;

    public EmailService(ITemplateService templateService)
    {
        if (templateService == null)
        {
            throw new ArgumentNullException("templateService");
        }
        this.templateService = templateService;
    }

    public string GetEmailTemplate(string templateName)
    {
        if (templateName == null)
        {
            throw new ArgumentNullException("templateName");
        }
        Assembly assembly = Assembly.GetAssembly(typeof(EmailTemplate));
        Stream stream = assembly.GetManifestResourceStream(typeof(EmailTemplate), "{0}.cshtml".FormatWith(templateName));
        string template = stream.ReadFully();
        return template;
    }

    public string GetEmailBody(string templateName, object model = null)
    {
        if (templateName == null)
        {
            throw new ArgumentNullException("templateName");
        }
        string template = GetEmailTemplate(templateName);
        string emailBody = templateService.Parse(template, model, null, null);
        return emailBody;
    }
}

Il servizio di template che utilizzo viene iniettato sebbene sia solo un'implementazione predefinita:

    internal ITemplateService InstanceDefaultTemplateService()
    {
        ITemplateServiceConfiguration configuration = new TemplateServiceConfiguration();
        ITemplateService service = new TemplateService(configuration);
        return service;
    }

Dato che in questo caso in particolare costruirò email da questi modelli. Voglio essere in grado di usare @sections per l'oggetto dell'e-mail, e diverse sezioni del corpo dell'email, mentre si usa un layout in cui specifichi gli stili che sono comuni a tutta la struttura dell'email (che apparirà come uno dei MailChimpprobabilmente).

La domanda è quindi duplice:

  • Come posso specificare i layout in RazorEngine?
  • Come posso specificare questi layout dalle stringhe (o da uno stream)? poiché, come puoi vedere, utilizzo le risorse incorporate per archiviare i modelli di email per rasoio.

Aggiornare

Forse non ero chiaro, ma mi riferisco al RazorEngine biblioteca.


19
2018-04-29 03:44


origine


risposte:


Si scopre dopo aver scavato che i layout sono supportati, dobbiamo solo dichiararli con _Layout invece di Layout

Per quanto riguarda il problema delle risorse incorporate, ho implementato quanto segue ITemplateResolver

using System;
using System.IO;
using System.Reflection;
using Bruttissimo.Common;
using RazorEngine.Templating;

namespace Website.Extensions.RazorEngine
{
    /// <summary>
    /// Resolves templates embedded as resources in a target assembly.
    /// </summary>
    public class EmbeddedTemplateResolver : ITemplateResolver
    {
        private readonly Assembly assembly;
        private readonly Type type;
        private readonly string templateNamespace;

        /// <summary>
        /// Specify an assembly and the template namespace manually.
        /// </summary>
        /// <param name="assembly">The assembly where the templates are embedded.</param>
        /// <param name="templateNamespace"></param>
        public EmbeddedTemplateResolver(Assembly assembly, string templateNamespace)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException("assembly");
            }
            if (templateNamespace == null)
            {
                throw new ArgumentNullException("templateNamespace");
            }
            this.assembly = assembly;
            this.templateNamespace = templateNamespace;
        }

        /// <summary>
        /// Uses a type reference to resolve the assembly and namespace where the template resources are embedded.
        /// </summary>
        /// <param name="type">The type whose namespace is used to scope the manifest resource name.</param>
        public EmbeddedTemplateResolver(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            this.assembly = Assembly.GetAssembly(type);
            this.type = type;
        }

        public string Resolve(string name)
        {
            if (name == null)
            {
                throw new ArgumentNullException("name");
            }
            Stream stream;
            if (templateNamespace == null)
            {
                stream = assembly.GetManifestResourceStream(type, "{0}.cshtml".FormatWith(name));
            }
            else
            {
                stream = assembly.GetManifestResourceStream("{0}.{1}.cshtml".FormatWith(templateNamespace, name));
            }
            if (stream == null)
            {
                throw new ArgumentException("EmbeddedResourceNotFound");
            }
            string template = stream.ReadFully();
            return template;
        }
    }
}

Quindi lo colleghi semplicemente in questo modo:

    internal static ITemplateService InstanceTemplateService()
    {
        TemplateServiceConfiguration configuration = new TemplateServiceConfiguration
        {
            Resolver = new EmbeddedTemplateResolver(typeof(EmailTemplate))
        };
        ITemplateService service = new TemplateService(configuration);
        return service;
    }

Il tipo che si passa è solo per fare riferimento all'assembly e allo spazio dei nomi in cui sono incorporate le risorse.

namespace Website.Domain.Logic.Email.Template
{
    /// <summary>
    /// The purpose of this class is to expose the namespace of razor engine templates in order to
    /// avoid having to hard-code it when retrieving the templates embedded as resources.
    /// </summary>
    public sealed class EmailTemplate
    {
    }
}

Un'ultima cosa, per avere i template risolti con il nostro risolutore, dobbiamo risolverli in questo modo:

ITemplate template = templateService.Resolve(templateName, model);
string body = template.Run();
return body;

.Run è solo un semplice metodo di estensione dal momento che non riesco a trovare alcun uso per a ViewBag.

public static class ITemplateExtensions
{
    public static string Run(this ITemplate template)
    {
        ExecuteContext context = new ExecuteContext();
        string result = template.Run(context);
        return result;
    }
}

AGGIORNARE

Ecco le estensioni mancanti

    public static string FormatWith(this string text, params object[] args)
    {
        return string.Format(text, args);
    }

    public static string ReadFully(this Stream stream)
    {
        using (StreamReader reader = new StreamReader(stream))
        {
            return reader.ReadToEnd();
        }
    }

16
2018-05-12 18:13



Avevo bisogno di fornire il mio layout come stringa o nome di file. Ecco come ho risolto questo (basato su questo post del blog)

public static class RazorEngineConfigurator
{
    public static void Configure()
    {
        var templateConfig = new TemplateServiceConfiguration
            {
                Resolver = new DelegateTemplateResolver(name =>
                    {
                        //no caching cause RazorEngine handles that itself
                        var emailsTemplatesFolder = HttpContext.Current.Server.MapPath(Properties.Settings.Default.EmailTemplatesLocation);
                        var templatePath = Path.Combine(emailsTemplatesFolder, name);
                        using (var reader = new StreamReader(templatePath)) // let it throw if doesn't exist
                        {
                            return reader.ReadToEnd();
                        }
                    })
            };
        RazorEngine.Razor.SetTemplateService(new TemplateService(templateConfig));
    }
}

Quindi chiamo RazorEngineConfigurator.Configure () in Global.asax.cs ed è pronto.

Il percorso dei miei modelli si trova in Properties.Settings.Default.EmailTemplatesLocation

A mio avviso, ho questo:

@{ Layout = "_layout.html";}

_layout.html è in e-mailTemplatesFolder

È un codice HTML piuttosto standard con una chiamata @RenderBody () nel mezzo.

Per quanto ho capito, RazorEngine utilizza il nome del modello ("_layout.html" in questo caso) come chiave per la sua cache in modo che il delegato nel mio configuratore venga chiamato una sola volta per modello.

Penso che usi quel risolutore per ogni nome di modello che non conosce (ancora).


4
2018-05-07 07:57



Sembra che qualcun altro l'abbia risolto per te.

https://github.com/aqueduct/Appia/blob/master/src/Aqueduct.Appia.Razor/RazorViewEngine.cs

Il codice desiderato è nel secondo metodo ExecuteView. Sebbene stiano creando il proprio motore di visualizzazione, puoi invece creare la tua soluzione personalizzata per i modelli e utilizzare qualcosa di simile. Fondamentalmente stanno cercando la proprietà Layout del Template, e se esiste cerca e sostituisce il contenuto dal layout.

Ecco un link al modello personalizzato di RazorEngine:

http://razorengine.codeplex.com/wikipage?title=Building%20Custom%20Base%20Templates&referringTitle=Documentation

Ecco dove ho trovato la tua soluzione:

Motore .NET Razor - Implementazione di layout


3
2018-05-08 19:46