Dependency Injection on Blazor server App

Viewed 67

I'm trying to inject some services in my Blazor app using a generic way.I've created an interface IService to identify the service i want to inject :

(The Service):

namespace OrbitModule.SpaceRazor.Service
{
    public class SpaceRazorService:IService
    {
        public SpaceRazorService()
        {
            
        }

        public void DoSomething()
        {

        }
    }
}

(Code in Program.cs that inject the service):


IEnumerable<Type> allTypes = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
    .Select(x => Assembly.Load(AssemblyName.GetAssemblyName(x)))
    .Where(assembly => !assembly.FullName.Contains("Microsoft") && !assembly.FullName.Contains("System") && !assembly.FullName.Contains("NetCore"))
    .SelectMany(x => x.GetTypes());

IEnumerable<Type> services = allTypes.Where(x => typeof(IService).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract);

foreach (Type service in services)
{
    builder.Services.AddTransient(service); 
}


var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

To test it, I've added the following:

var test = app.Services.GetService(typeof(SpaceRazorService));

And when i've added a break point, I've seen an instance of the wanted behavior of the service....

However, when i create a page :

@using OrbitModule.SpaceRazor.Service

@page "/SpaceRazor/Page1"
@inject SpaceRazorService spaceRazorService

<h3>List des Spaces Razors</h3>


@code {
    void thing()
    {
        spaceRazorService.DoSomething();
    }
}

The program crashes with the following stack trace :

nvalidOperationException: Cannot provide a value for property 'spaceRazorService' on type 'OrbitModule.SpaceRazor.Page.Page1'. There is no registered service of type 'OrbitModule.SpaceRazor.Service.SpaceRazorService'.

    Microsoft.AspNetCore.Components.ComponentFactory+<>c__DisplayClass7_0.<CreateInitializer>g__Initialize|1(IServiceProvider serviceProvider, IComponent component)
    Microsoft.AspNetCore.Components.ComponentFactory.PerformPropertyInjection(IServiceProvider serviceProvider, IComponent instance)
    Microsoft.AspNetCore.Components.ComponentFactory.InstantiateComponent(IServiceProvider serviceProvider, Type componentType)
    Microsoft.AspNetCore.Components.RenderTree.Renderer.InstantiateChildComponentOnFrame(ref RenderTreeFrame frame, int parentComponentId)
    Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewComponentFrame(ref DiffContext diffContext, int frameIndex)
    Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewSubtree(ref DiffContext diffContext, int frameIndex)
    Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(ref DiffContext diffContext, int newFrameIndex)
    Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(ref DiffContext diffContext, int oldStartIndex, int oldEndIndexExcl, int newStartIndex, int newEndIndexExcl)
    Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, int componentId, ArrayRange<RenderTreeFrame> oldTree, ArrayRange<RenderTreeFrame> newTree)
    Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, out Exception renderFragmentException)
    Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
    Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.HandleException(Exception exception)
    Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
    Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessPendingRender()
    Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(int componentId, RenderFragment renderFragment)
    Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged()
    Microsoft.AspNetCore.Components.ComponentBase.CallOnParametersSetAsync()
    Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
    Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.HandleException(Exception exception)
    Microsoft.AspNetCore.Components.RenderTree.Renderer.HandleExceptionViaErrorBoundary(Exception error, ComponentState errorSourceOrNull)
    Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToPendingTasks(Task task, ComponentState owningComponentState)
    Microsoft.AspNetCore.Components.Rendering.ComponentState.SupplyCombinedParameters(ParameterView directAndCascadingParameters)
    Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters)
    Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderRootComponentAsync(int componentId, ParameterView initialParameters)
    Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.CreateInitialRenderAsync(Type componentType, ParameterView initialParameters)
    Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.RenderComponentAsync(Type componentType, ParameterView initialParameters)
    Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext+<>c__11<TResult>+<<InvokeAsync>b__11_0>d.MoveNext()
    Microsoft.AspNetCore.Mvc.ViewFeatures.StaticComponentRenderer.PrerenderComponentAsync(ParameterView parameters, HttpContext httpContext, Type componentType)
    Microsoft.AspNetCore.Mvc.ViewFeatures.ComponentRenderer.PrerenderedServerComponentAsync(HttpContext context, ServerComponentInvocationSequence invocationId, Type type, ParameterView parametersCollection)
    Microsoft.AspNetCore.Mvc.ViewFeatures.ComponentRenderer.RenderComponentAsync(ViewContext viewContext, Type componentType, RenderMode renderMode, object parameters)
    Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output)
    Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.<RunAsync>g__Awaited|0_0(Task task, TagHelperExecutionContext executionContext, int i, int count)
    BlazOrbit.Pages.Pages__Host.ExecuteAsync() in _Host.cshtml

    <component type="typeof(App)" render-mode="ServerPrerendered" />

Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0<TFilter, TFilterAsync>(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

But when i initiate the service directly in the @code, it works :

@using OrbitModule.SpaceRazor.Service

@page "/SpaceRazor/Page1"

<h3>List des Spaces Razors</h3>


@code {
    SpaceRazorService spaceRazorService = new SpaceRazorService();

    void thing()
    {
        spaceRazorService.DoSomething();
    }
}

I want to know if it is possible to inject services in external blazor's components .....

Details that may be important :

  • the service is in an external project (as you can see in the picture)
  • the interface IService is in an other project too.

Architecture of the project

1 Answers

Try this extension method:

public static class DependencyInjectionExtensions
    {
        public static void InstallIServices(
            this IServiceCollection services)
        {
            var assembly = Assembly.GetExecutingAssembly();

            RegisterImplementationsOfServiceType(
                services, assembly, typeof(IService));
        }

        private static void RegisterImplementationsOfServiceType(
            IServiceCollection services, Assembly assembly, Type type)
        {
            var implementationsType = assembly.GetTypes()
                .Where(x => !x.IsInterface && x.GetInterface(type.Name) != null);

            foreach (var implementationType in implementationsType)
            {
                var servicesType = implementationType.GetInterfaces()
                    .Where(r => !r.Name.Contains(type.Name));

                foreach (var serviceType in servicesType)
                {
                    Console.WriteLine($"{serviceType} , {implementationType}");
                    services.AddTransient(serviceType, implementationType); //Replace with the type of service they need to be: Transient, Singleton, Scoped
                }
            }
        }
    }

Use in Program.cs:

public static async Task Main(string[] args)
{
    var builder = WebAssemblyHostBuilder.CreateDefault(args);
    builder.Services.InstallIServices();
}

Use in Startup.cs:

public void ConfigureServices(IServiceCollection services) {
...
services.InstallIServices()
...
Related