DamianEdwards / RazorSlices

Lightweight Razor-based templates for ASP.NET Core without MVC, Razor Pages, or Blazor.
MIT License
312 stars 13 forks source link

ArgumentNullException throw trying to render slice #45

Closed martincostello closed 3 months ago

martincostello commented 3 months ago

I just saw you'd shipped support for layout and sections (and AoT šŸ˜), so thought I'd try migrating one of my simple Razor Pages sites over to use RazorSlices.

Now I'm sure I've done something stupid (I'm like 15 mins in), but I got the following exception trying to render my first slice at runtime:

ArgumentNullException: Value cannot be null. (Parameter 'member')
System.ArgumentNullException.Throw(string paramName)
System.ArgumentNullException.ThrowIfNull(object argument, string paramName)
System.Linq.Expressions.Expression.MakeMemberAccess(Expression expression, MemberInfo member)
RazorSlices.RazorSliceFactory.GetExpressionsSliceFactory(SliceDefinition sliceDefinition) in RazorSliceFactory.cs
RazorSlices.RazorSliceFactory.GetSliceFactory(SliceDefinition sliceDefinition) in RazorSliceFactory.cs
RazorSlices.SliceDefinition..ctor(Type sliceType) in SliceDefinition.cs
MartinCostello.Website.Slices.Home..cctor() in MartinCostello.Website.RazorSliceProxies.g.cs

The slice, the _ViewImports.cshtml and the code to render it are:

app.MapGet("/home", () => Results.Extensions.RazorSlice<MartinCostello.Website.Slices.Home, int>(1));
@inherits RazorSliceHttpResult<int>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Hello from Razor Slices!</title>
</head>
<body>
</body>
</html>
@inherits RazorSliceHttpResult

@using System.Globalization;
@using Microsoft.AspNetCore.Razor;
@using Microsoft.AspNetCore.Http.HttpResults;
@using Microsoft.Extensions.Options
@using MartinCostello.Website
@using MartinCostello.Website.Extensions
@using MartinCostello.Website.Models
@using MartinCostello.Website.Options
@using MartinCostello.Website.Pages
@using MartinCostello.Website.Slices
@using RazorSlices

@tagHelperPrefix __disable_tagHelpers__:
@removeTagHelper *, Microsoft.AspNetCore.Mvc.Razor

@inject IOptions<SiteOptions> Options

The int is just a placeholder as I try and build things up as I migrate, it wouldn't be what I'd really use.

There's a good chance I've made a mistake somewhere and/or not read the documentation properly, but the exception message is certainly confusing to guide me to whatever that is to fix it.

martincostello commented 3 months ago

It works if I add <PublishAot>true</PublishAot> to my project file, so I guess it's just a bug in the reflection-based code paths.

DamianEdwards commented 3 months ago

looking

DamianEdwards commented 3 months ago

Seems it's something to do with initialization of injected properties (in your case the IOptions<SiteOptions>. Hopefully an easy fix.

martincostello commented 3 months ago

Something else I've literally just run into which is quite the Picard-face-palm moment that's also related to injection.

I've been trying to work out why I couldn't get injection working in a different project ("Can't inject because there's no service provider"), and after debugging through my project into yours I realised via some copy-pasta that I'd forgotten to get my top level slice to use @inherits RazorSliceHttpResult<MyModel> and instead I was doing @inherits RazorSlice<MyModel>, and then that ends up never assigning an HttpContext and then stuff all goes šŸ’„.

This one's clearly my fault, but it did make me wonder, is there something missing for the code path when razorSlice isn't a IRazorSliceHttpResult that should be catering for that?

DamianEdwards commented 3 months ago

@martincostello that should be fixed in new version as it should detect and wrap it now. If not, let me know!

I have a fix for this issue coming now.

martincostello commented 3 months ago

I only forked the repo just now, so I think whatever that fix was didn't work. I'll try and debug it a bit further with my mistake back in and see what I find.

martincostello commented 3 months ago

Haven't gotten to the bottom of exactly what is wrong yet, but it looks like when I use a layout slice, when CopySliceState() runs the source slice hasn't had the HttpContext assigned yet, so it just copies null to null, and then the layout fails whenever I have some code running that needs it.

DamianEdwards commented 3 months ago

Are you using the Results.Extensions.RazorSlice<MySlice> extension method?

martincostello commented 3 months ago
Results.Extensions.RazorSlice<MySlice, MyModel>(model);
DamianEdwards commented 3 months ago

OK I figured it out, fix for that coming too I hope.

DamianEdwards commented 3 months ago

OK pushed. Just waiting for ingestion at nuget.org: https://www.nuget.org/packages/RazorSlices/0.8.1

martincostello commented 3 months ago

Cheers for the fast around! I'll try it out once it pops up.

martincostello commented 3 months ago

Yep - both things I stumbled into fixed in 0.8.1 - šŸ„ƒ