toddams / RazorLight

Template engine based on Microsoft's Razor parsing engine for .NET Core
Apache License 2.0
1.52k stars 259 forks source link

NullReferenceException when @inject directive is used in built-in FilesystemProject (RazorLightEngineBuilder.UseFilesystemProject) #211

Open reponemec opened 6 years ago

reponemec commented 6 years ago

If I use

void ConfigureRazorLight(IServiceCollection services)
        {
            services.AddSingleton<IMyService, MyService>();

            RazorLightEngine engine = new RazorLightEngineBuilder()
              .UseFilesystemProject(_hostingEnvironment.ContentRootPath)
              .UseMemoryCachingProvider()
              .Build();

            services.AddRazorLight(() => engine);
        }

And if I have a

@inject IMyService _myService

directive in Razor view then _myService is always compiled as null. In case of built-in embeded resources (.UseEmbeddedResourcesProject(typeof(Program))) it works fine.

I don't know whether it is a bug or something is wrong in my code.

TrieBr commented 5 years ago

Did you find a solution to this?

smartlei24 commented 5 years ago

Hi, @reponemec , i got the same issue, are you found a solution about this? Thanks!

reponemec commented 4 years ago

https://www.bountysource.com/issues/69012066-no-effect-with-inject-when-use-filesystem-project

jzabroski commented 4 years ago

@reponemec In looking at this, we only have one test covering @inject - do you think you can create a PR with a failing test for me to look into? I wonder if this is a bug due to "cross-wiring". Are you using Microsoft DI or a custom DI container?

        [Fact]
        public async Task Ensure_Registered_Properties_Are_Injected()
        {
            var collection = new ServiceCollection();
            string expectedValue = "TestValue";
            string templateKey = "key";
            collection.AddSingleton(new TestViewModel() { Title = expectedValue });
            var propertyInjector = new PropertyInjector(collection.BuildServiceProvider());

            var builder = new StringBuilder();
            builder.AppendLine("@model object");
            builder.AppendLine("@inject RazorLight.Tests.Models.TestViewModel test");
            builder.AppendLine("Hello @test");

            var engine = new RazorLightEngineBuilder()
                .UseEmbeddedResourcesProject(typeof(Root))
                .Build();

            engine.Options.DynamicTemplates.Add(templateKey, builder.ToString());
            ITemplatePage templatePage = await engine.CompileTemplateAsync(templateKey);

            //Act
            propertyInjector.Inject(templatePage);

            //Assert
            var prop = templatePage.GetType().GetProperty("test").GetValue(templatePage);

            Assert.NotNull(prop);
            Assert.IsAssignableFrom<TestViewModel>(prop);
            Assert.Equal((prop as TestViewModel).Title, expectedValue);
        }
jzabroski commented 4 years ago

Note to self: This issue, #317, and #239 all are related to people wanting to transitively inject IRazorLightEngine into another class instance with Microsoft DI. The action item is to write a test case that demonstrates this failing.

vd3d commented 4 years ago

Hi,

Is there any update or workaround ?

dotnetshadow commented 3 years ago

Hi there,

I am also experiencing this issue, has there been an update to this? With regards to your test case it's using UseEmbeddedResourcesProject where like others I'm using UseFilesystemProject

Trying @inject AppSettings AppSettings

image

jzabroski commented 3 years ago

Please post your DI config. The screenshot is pretty much useless and nobody can Google search it and benefit.

dotnetshadow commented 3 years ago

Sorry about that, I'm using FluentEmail (https://github.com/lukencode/FluentEmail,) which uses RazorLight underneath. For my DI configuration I'm using

var emailSettings = config.GetSection("EmailSettings").Get<AuthMessageSenderOptions>();

            var smtpClientOptions = new SmtpClientOptions
            {
                UseSsl = emailSettings.UseSsl,
                Password = emailSettings.Password,
                Port = emailSettings.Port,
                Server = emailSettings.Server,
                User = emailSettings.Username,
                UsePickupDirectory = false,
                RequiresAuthentication = !string.IsNullOrEmpty(emailSettings.Username),
                SocketOptions = SecureSocketOptions.StartTlsWhenAvailable
            };

            services.AddFluentEmail(emailSettings.SystemEmailFrom, emailSettings.SystemEmailFromName)
                .AddRazorRenderer()
                .AddMailKitSender(smtpClientOptions);

services..AddTransient<IEmailSender, EmailService>();

Not sure if that helps or not.

I think the issue is related to this ticket: https://github.com/toddams/RazorLight/issues/166 so I think I can work around it Thanks for creating a great library much appreciated

ancailliau commented 1 year ago

For the record, I experienced the same issue. The engine was build as follow (i.e. not with some dependency injection).

var engine = new CustomRazorLightEngineBuilder()
  .UseFileSystemProject(Path.Combine(Directory.GetCurrentDirectory(), "Views"))
    .UseMemoryCachingProvider()
    .Build();

Adding the following two lines below the snippet fixed the issue:

var injector = new PropertyInjector(_serviceProvider);
engine.Options.PreRenderCallbacks.Add(template => injector.Inject(template));