FluentEmail icon indicating copy to clipboard operation
FluentEmail copied to clipboard

using @inject in templates?

Open dotnetshadow opened this issue 4 years ago • 3 comments

Hi there,

I can't seem to get @inject working in my templates, it always gives a null warning

Using UsingTemplateFromFile()

Trying @inject AppSettings AppSettings image

I think the issue could be related to this: https://github.com/toddams/RazorLight/issues/211

dotnetshadow avatar Jan 29 '21 04:01 dotnetshadow

I've just had the same problem.

The following extension looked like it would help: https://www.gitmemory.com/issue/toddams/RazorLight/166/500361356 This solution came so close and might work for you, but for me it still didn't work as I was trying to load embedded resource templates from a sub-project (i.e. a class library) and it couldn't find them using that approach.

In the end I couldn't get FluentEmail to obey the Razor project setting when using the above extension. So, my workaround was to instantiate RazorLight myself, render the template directly and then pass the generated output to Fluent to send.

In startup:

        var engine = new RazorLightEngineBuilder()
                    .UseEmbeddedResourcesProject(AnyClassInSubProject.GetType().Assembly, "SubProjectName")          // Look for templates here
                    .UseMemoryCachingProvider()             // Think this is required to support @inject in Razor templates
                    .Build();

Adding RazorLight normally then generated an AggregateException error upon startup: https://github.com/toddams/RazorLight/issues/317#issuecomment-733693312 Adding the engine.Handler as a singleton first took care of that:

            services.AddSingleton(engine.Handler);
            services.AddRazorLight(() => engine);

Next I initialise FluentEmail - but without adding Razor to it. i.e.

            services.AddFluentEmail(emailConfig.DefaultSenderEmail, emailConfig.DefaultSenderName)
                 /*       .AddSendGridSender(emailConfig.SendGridApiSecret, emailConfig.SandboxMode)*/;

Finally I'm ready to send a mail. I inject IFluentEmail and IRazorLightEngine and pass the rendered content to Fluent:

            var email = _fluentEmail.To(recipientEmail)
                                                    .Subject(subject);
            string htmlRendered = await _razorLightEngine.CompileRenderAsync<T>(templateName, model);
            email.Body(htmlRendered, true); 
            var result = email.SendAsync();

Remember to set the embedded .cshtml file to be an embedded resource. Hope it helps.

Many thanks @lukencode and contributors for such an incredibly well written library!

TheObliterator avatar Jan 29 '21 23:01 TheObliterator

@TheObliterator Thank you so much for pointing me to a workaround, I will definitely try this and report back. Appreciate the thorough steps you have given makes it easy to understand.

For completeness, the github link to the solution you proposed is here: https://github.com/toddams/RazorLight/issues/166#issuecomment-500361356

I agree thanks @lukencode for a great library.

UPDATE I managed to get it working with the extension.

I was checking out the source code and noticed how the razorerenderer is currently implemented https://github.com/lukencode/FluentEmail/commit/c4edbfa0f11b80daf2b5b31a1d90f903a5050b3c#diff-72653a1101d601113d5f3f8e9947a374334a7b5d9c39ea538b12b503b5d0c345

Seems the difference between the default razor renderer and the custom extension one, is that the engine is being injected instead of being created. Perhaps it could be worth having this as part of the library?

dotnetshadow avatar Jan 31 '21 06:01 dotnetshadow

Because I could not get this working with minimal code, and didn't to go and change a lot of code, I opted for injecting the service into a property of the model that gets passed into the mail/razor template. Don't know why I wasted an hour trying to fix the injection in the view.

DavidDeSloovere avatar Mar 03 '21 12:03 DavidDeSloovere