简体   繁体   中英

How can I abstract dependent type from base C# base project using microsoft identity framework?

I have an authentication project that I would like to reuse.

I have a AspNetCore Identity project called authentication which is intended to be a reusable authentication project. If you have looked at the AspNetCore Identity implementation you will see a type called UserManager where the application user is the class that you store your implementation of the user in the AspNetUsers database table.

public class ApplicationUser : IdentityUser
{
}

The problem I am having is that there is a controller in this isolated authentication project called AccountController which holds all the logging in/out, registering, and other related account actions. I wish to abstract the application user out of this project's class so I can change it based on the needs of the project for multiple solutions.

The authentication project has a startup class which is initiated as follows from within the project in which it is being used:

authenticationStartUp.ConfigureServices<ApplicationUser, MyDatabase>
      (services, Configuration);

As you can see, the subscribing project adds its own implementation of the ApplicationUser. Next, comes the configuration of the identity which references the TApplicationUser in the services.AddIdentity call.

    public void ConfigureServices<TApplicationUser, TContext>
          (IServiceCollection services, IConfigurationRoot Configuration)
        where TApplicationUser : IdentityUser
        where TContext : DbContext
    {
        services.AddIdentity<TApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<TContext>()
            .AddDefaultTokenProviders();

Unfortunately, this now breaks down in the AccountController which is using the injected service. How, can the TApplicationUser be added to the controller.

public class AccountController : Controller
{
    private readonly UserManager<TApplicationUser> _userManager;

    public AccountController(UserManager<TApplicationUser> userManager) 
    {
        _userManager = userManager;
    }
}

This is obviously broken since there is no TApplicationUser. Furthermore, if I add the TApplicationUser as follows the controller is nolonger resolved and nothing happens.

public class AccountController<TApplicationUser> : Controller 
   where TApplicationUser : IdentityUser
{
    private readonly UserManager<TApplicationUser> _userManager;

    public AccountController(UserManager<TApplicationUser> userManager) 
    {
        _userManager = userManager;
    }
} 

Is there anyway to get application controller to still be resolved with the included type parameters?

And also, I have found another problem in that even if I add the type parameter in the base class how will I be able to add the type parameter in the views that use TApplicationUser. Here is an example

View embedded in authentication project

@* TApplicationUser obviously doesn't resolve *@
@inject SignInManager<TApplicationUser> SignInManager 
 @{
    var loginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList();
    if (loginProviders.Count == 0)
    {
        <div>
            <p>
                There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
                for details on setting up this ASP.NET application to support logging in via external services.
            </p>
        </div>
    }
    else
    {
        <form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
            <div>
                <p>
                    @foreach (var provider in loginProviders)
                    {
                        <button type="submit" class="btn btn-default" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.AuthenticationScheme</button>
                    }
                </p>
            </div>
        </form>
    }
}

Have the generic controller as a base controller that lives in the reusable project

public abstract class AccountControllerBase<TApplicationUser> : Controller 
    where TApplicationUser : IdentityUser {
    protected readonly UserManager<TApplicationUser> _userManager;

    protected AccountController(UserManager<TApplicationUser> userManager) {
        _userManager = userManager;
    }
}

The using class/project would derive from the base controller and define the generic argument

public class AccountController : AccountControllerBase<ApplicationUser> {

    public AccountController(UserManager<ApplicationUser> userManager) 
        : base(userManager) {  }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM