1

I have an application with an Emails project, which exposes a class to compile an email template using IRazorViewEngine, and send it.

However, this is used in several different projects, and by default RazorViewEngine seems to only look in the Views folder for the Web project in question. This means I have to duplicate the same views across projects.

I've looked at extending the RazorViewEngine to modify where it looks for views, but every example changes which folder in the project it looks in, not which project, and from a quick look at the RazorViewEngine source it explicitly expects application-local paths (i.e. starting with "~" or "/"), thwarting my naive attempt to use an absolute path or one beginning with "../" .

Is there a way to make sure that RazorViewEngine (or a subclass) checks a specific (non-dynamic) project, as opposed to the Web Project calling it?

The application is using .Net Core 1.1, which RazorEngine doesn't seem to support

CoreyGrant
  • 432
  • 1
  • 4
  • 16

2 Answers2

2

I had a little trouble getting RazorLight to work, so I figured I best post my solution here. I embedded the .cshtml files in the assembly with the following .csproj change

<ItemGroup>
    <EmbeddedResource Include="Emails/*.cshtml" />
    <Content Include="Emails/*.cshtml">
      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    </Content>
</ItemGroup>

Then, I included an empty class (FakeClass) in the same folder as the templates, and used it like so with RazorLight EngineFactory

var engine = EngineFactory.CreateEmbedded(typeof(FakeClass));
return engine.Parse(name, model);

For reasons I don't understand without looking at the source CreateEmbedded requires a class in the same location as the views or it will error when looking for the views.

Name is simply the name of the template without extension, and model is your strongly typed model.

This approach has the advantage that there is no need to mess with path strings, and it keeps everything nicely contained in the one project.

CoreyGrant
  • 432
  • 1
  • 4
  • 16
1

I read the Views as files in controller and I will use that string for razor compile.

Controller:

     _emailBuilder = new RazorHtmlBuilder();
     var tempatePath = 
     HttpContext.Current.Server.MapPath("~/Views/Mail/HelloFriend.cshtml");
     string template = File.ReadAllText(tempatePath);
     var body = _emailBuilder.BuildEmail("someUniqueId", template , new Friend() {Name: "John"});

Razor Email Builder outside of context (different project) using razor engine

    using System;
    using RazorEngine;
    using RazorEngine.Templating;
    namespace Project.ServiceLayer.Helpers.EmailBuilder
    {
        public class RazorHtmlBuilder
        {
            public string BuildEmail<T>(string templateName, string template, T model)
            {
                try
                {
                    return Engine.Razor.RunCompile(template, templateName, typeof (T), model);
                }
                catch (Exception ex)
                {
                    //log error
                }
            }
    }
SilentTremor
  • 4,747
  • 2
  • 21
  • 34