dependency-injectionblazorblazor-server-sideclass-libraryrazor-class-library

Move component's Dependency Injected methods to separate CS library in Blazor Server-Side


Would like to make reusable functions/methods in separate .cs files and/or in a separate library. Of course i know how to do this, my problem is i don't know how to do this in the case where i need to use dependency injected elements. For example, here is a very easy function about getting a user's property:

[Inject]
public UserManager<IdentityUser> UserManager { get; set; }

public async Task<string> GetUserId(string emailName)
{
    var user = await userManager.FindByNameAsync(emailName);
    if (user == null)
        return null;
    return user.Id;
}

This is working in every razor file/component, if (!) the component initialized. If not, the injected services also not initialized and get a null-exception error.

I don't want to rewrite/copy this code-snippet into every component where i would like to use, so i would like to create a class or library for it. What should be the right way to do it? The best thing would be if i can move these kind of functions into a separate Class Library or Razor Class Library.

UPDATE:

@Nkosi provide the prefect solution, but i would like to think forward a little bit. That previous code sample was really small, so what's about if i have 2-3-4 or more DI needed for my custom method? For example (in the Razor component):

[Inject]
public UserManager<IdentityUser> UserManager { get; set; }
[Inject]
public SignInManager<IdentityUser> SignInManager { get; init; }
[Inject]
public IJSRuntime jsRuntime { get; init; }
[Inject]
public CookieAuthenticationOptions cookieAuthenticationOptions { get; set; }
[Inject]
public IOptionsMonitor<CookieAuthenticationOptions> c_options { get; set; }


public async Task<string> GetUserWithOtherStuff(string email, string psw)
{
    cookieAuthenticationOptions = c_options.Get("schema");
    var user = await UserManager.FindByNameAsync(email);
    var valid = await SignInManager.UserManager.CheckPasswordAsync(user, psw);
    // etc..

    return something;
}

Solution

  • Move it to separate class / library

    public interface IUserService {
        Task<string> GetUserId(string emailName);
    }
    
    public class UserService : IUserService {
        private readonly UserManager<IdentityUser> userManager;
        
        public UserService(UserManager<IdentityUser> userManager) {
            this.userManager = userManager;
        }
    
        public async Task<string> GetUserId(string emailName) {
            var user = await userManager.FindByNameAsync(emailName);
            if (user == null)
                return null;
            return user.Id;
        }
    }    
    

    and inject the encapsulated service where needed

    [Inject]
    public IUserService  UserService { get; set; }
    

    Make sure that all the necessary dependencies are registered with the service collection.

    an extension method can be made to group the needed dependency registration

    public static IServiceCollection AddUserServices(this IServiceCollection services) {
        services
            .AddScoped<IUserService, UserService>()
            .AddIdentity<.....>()
            //... add what is needed for this library library to function
    
        return services;
    }
    

    and invoked/reused where needed

    //...
    
    services.AddUserServices();
    
    //...