Unspoiled9710
Unspoiled9710

Reputation: 102

Blazor Cannot provide a value for property 'ScopeFactory' on type 'MyComponent' because the property has no setter

I'm writing Blazor WASM app in .NET 6.

The app works as it should in Debug when running from Visual Studio 2022, but when I deploy it as a static site using dotnet publish -c Release --nologo and access the bin/Release/net6.0/publish/wwwroot folder on localhost I get the following error:

Unhandled exception rendering component: Cannot provide a value for property 'ScopeFactory' on type 'MySolution.Pages.MyComponent' because the property has no setter.

My component looks like this:

public class MyComponent : OwningComponentBase
{
    public IOptions Option { get; set; } = Options.Default;

    protected override async Task OnInitializedAsync()
    {
        Options = await ScopedServices.GetRequiredService<Task<IOptions>>();
    }
}

I add the service in Program.cs like this

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<Task<IOptions>>(async op => await Options.Create(op.GetRequiredService<HttpClient>()));

and the Options.Create() looks like this

public static async Task<IGameOptions> Create(HttpClient client)
{
    var json = await client.GetStringAsync("/gameOptions.json");

    var options = JsonConvert.DeserializeObject<Options>(json);

    if (options is null)
    {
        throw new InvalidDataException("Given path contains invalid JSON object.");
    }

    return options;
}

I've already done a lot of testing and searching, but I couln't find anything. I tried to only request the HttpClient service in my component, but even that throws the same error.

I see that it is some problem in DI, but I manage the DI scope using the OwningComponentBase, as it is stated in the ASP.NET Core Blazor dependency injection.

What could cause it?

Thank you in advance

Upvotes: 1

Views: 7589

Answers (3)

user3407039
user3407039

Reputation: 1335

I have attempted both current answers given on this post and wanted to share my findings in case they help someone.

The answer from Jesse Good works to resolve this issue but gave me other issues related to performance. It also doubled the length of time of the build.

The answer from Steffan Ossendorf worked for me as well, and despite requiring slightly more tinkering and an extra file, is definitely worth that extra effort.

If it helps anyone, here is exactly what i did after reading through the article on trimming that Steffan mentioned.

I created a new XML file called "Trimmer.xml" in the route of my Blazor project with the following contents -

<linker>
    <assembly fullname="Microsoft.AspNetCore.Components">
        <type fullname="Microsoft.AspNetCore.Components.OwningComponentBase" preserve="all" />
    </assembly>
</linker>

I then added this into the .csproj file of my Blazor project -

<ItemGroup>
    <TrimmerRootDescriptor Include="Trimmer.xml" />
</ItemGroup>

Upvotes: 1

Stefan Ossendorf
Stefan Ossendorf

Reputation: 801

The marked answer can only be used as long as you don't want to trim your app on publish which is not always desirable. If you want to publish with trimming you have to exclude the type from trimming.

<assembly fullname="Microsoft.AspNetCore.Components">
    <type fullname="Microsoft.AspNetCore.Components.OwningComponentBase" preserve="all" />
</assembly>

Because the whole trimming stuff is a topic of itself I can recommend this page for starting with adjusting the trimming process: .Net Blog - Customizing Trimming The blog post was written for .Net5 but I'm using it for .Net6 as well.

Upvotes: 2

Jesse Good
Jesse Good

Reputation: 52365

Thanks for posting the real code in the comments. After looking at it, this looks to be a result of trimming.

Try adding <PublishTrimmed>false</PublishTrimmed> to your project file and I think it will work.

Basically when you run the command dotnet publish -c Release --nologo, it is optimizing the assemblies for size (trimming). In your case, it appears to be changing the following code in OwningComponentBase from:

[Inject] IServiceScopeFactory ScopeFactory { get; set; }

to:

[Inject] IServiceScopeFactory ScopeFactory { get; }

i.e. its removing the setter during trimming.

I would consider this a bug with the implementation

Upvotes: 4

Related Questions