【问题标题】:AspNetCore Middleware UserManager Dependency InjectionAspNetCore 中间件 UserManager 依赖注入
【发布时间】:2017-08-02 05:27:12
【问题描述】:

我有一个多层应用程序,我开始使用 ASP.NET Core 1.1 编写它,我仍在不断学习。我已经像以前在 Web API 中完成的应用程序一样组织它,我有主机服务(网络核心应用程序)、业务层和数据库之上的数据层。业务和数据层是 net core 标准库,但是当我想添加实体框架时,我必须修改数据层以使其看起来像 net core 应用程序,所以现在我有 Startup.cs 和那里的配置。这使我能够配置实体框架服务并在数据层中创建迁移。但是现在我有一个问题,因为我想添加 asp.net 身份。网上的每个教程都是关于在一个项目中包含所有内容的 SPA。 我已将身份添加到 Startup.cs 并且数据库生成良好

public void ConfigureServices(IServiceCollection services)
{
    var connectionString = Configuration.GetConnectionString("DefaultConnection");
    services.AddEntityFramework(connectionString);
    services.AddMyIdentity();
    services.Configure<IdentityOptions>(options =>
    {
        // Password settings
        options.Password.RequireDigit = true;
        options.Password.RequiredLength = 8;
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireUppercase = true;
        options.Password.RequireLowercase = false;

        // Lockout settings
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
        options.Lockout.MaxFailedAccessAttempts = 10;


        // User settings
        options.User.RequireUniqueEmail = true;
    });
}
public void Configure(IApplicationBuilder app)
{
    app.UseIdentity();
}

但现在我需要使用非控制器类中的 UserManager,而且我不知道如何处理依赖注入。 为了更好地解释,我的主机服务中有一个帐户控制器

[HttpPost]
[Route("Register")]
public async Task<IActionResult> Register([FromBody]RegisterUserDto dto)
{
    var result = await Business.Commands.Accounts.Register(dto);
    return Ok(result);
}

业务层只调用数据层

public async static Task<ResponseStatusDto> Register(RegisterUserDto dto)
{
    // some code here        
    var identityLogon = await Data.Commands.ApplicationUsers.Register(dto);
    // some code here as well

    return new ResponseStatusDto();
}

现在的问题是,如何在 Data Register 方法中获取 UserManager?这是一个简单的类,它不是从控制器继承的,依赖注入不适用于构造函数,就像在此处找到的示例中一样 Core Identity

public class AccountController : Controller
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly IEmailSender _emailSender;
    private readonly ISmsSender _smsSender;
    private static bool _databaseChecked;
    private readonly ILogger _logger;

    public AccountController(
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager,
        IEmailSender emailSender,
        ISmsSender smsSender,
        ILoggerFactory loggerFactory)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _emailSender = emailSender;
        _smsSender = smsSender;
        _logger = loggerFactory.CreateLogger<AccountController>();
    }

    //
    // GET: /Account/Login

那么,如何将 Startup 中配置的 UserManager 传递给中间件某处的某个随机类?我已经看到了这个question,但是只将空值传递给 UseManager 构造函数的答案是行不通的,我认为这很好。

//根据 Set 的回答编辑

我已经删除了所有静态引用,但我仍然不完全在那里。我遵循了this 依赖注入说明,但我不确定如何实例化和调用 Add 方法。

我已经创建了一个界面

public interface IIdentityTransaction
{
    Task<IdentityResult> Add(ApplicationUser appUser, string password);
}

并实施

public class IdentityTransaction : IIdentityTransaction
{
    private readonly ApplicationDbContext _dbContext;

    private readonly UserManager<ApplicationUser> _userManager;
    private readonly RoleManager<IdentityRole> _roleManager;


    public IdentityTransaction(ApplicationDbContext context, UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
    {
        _roleManager = roleManager;
        _userManager = userManager;
        _dbContext = context;
    }

    public async Task<IdentityResult> Add(ApplicationUser applicationUser, string password)
    {
        return await _userManager.CreateAsync(applicationUser, password);
    }

}

然后我将它注入到 Startup.cs 中的服务集合中

services.AddScoped<IIdentityTransaction, IdentityTransaction>();

但是如何从 IdentityTransaction 服务调用 Add 方法?

我不能实例化它,也不能在构造函数上使用依赖注入,因为它只是循环我的问题。 @Set 提到

或将 UserManager userManager 作为参数传递给方法 从哪里传过来?

我想我已经很接近了,但我错过了一些东西。 我尝试过使用

    IIdentityTransaction it = services.GetRequiredService<IIdentityTransaction>();

但是 IServiceProvider 的服务是空的,我也不知道从哪里得到它。

【问题讨论】:

    标签: asp.net asp.net-core asp.net-identity-3


    【解决方案1】:

    是的,你非常接近。

    第一件事,要么从 IdentityTransaction 构造函数中删除上下文参数,就像在你的代码中一样,它似乎没用。或者如果您打算以后使用它,请在 DI 容器中声明它:

    services.AddScoped<ApplicationDbContext, ApplicationDbContext>();
    

    第二件事,您只需在控制器的构造函数中添加 IIdentityTransaction 作为依赖项,并从其依赖项中删除 SignInManager 和 UserManager,因为最终您不会直接在控制器中使用它们:

    public class AccountController : Controller
    {
        private readonly IEmailSender _emailSender;
        private readonly ISmsSender _smsSender;
        private static bool _databaseChecked;
        private readonly ILogger _logger;
        IIdentityTransaction _identityTransaction;
    
        public AccountController(
            IEmailSender emailSender,
            ISmsSender smsSender,
            ILoggerFactory loggerFactory,
            IIdentityTransaction identityTransaction)
        {
            _emailSender = emailSender;
            _smsSender = smsSender;
            _logger = loggerFactory.CreateLogger<AccountController>();
            _identityTransaction = identityTransaction;
        }
    

    如果控制器之间需要额外的业务层(IBusinessLayer),同一个流程,启动时在DI容器中声明类,在业务类构造函数中添加IIdentityTransaction作为依赖,将控制器的依赖从IIdentityTransaction更新为IBusinessLayer .

    更多的精度。

    services.AddScoped<IIdentityTransaction, IdentityTransaction>();
    

    这段代码不会注入实例或依赖项。它在 DI 容器中声明了一个接口及其关联的实现,因此可以在以后需要时注入。当实际创建需要它们的对象时注入实际实例。 IE。控制器在实例化时注入其依赖项。

     IIdentityTransaction it = services.GetRequiredService<IIdentityTransaction>();
    

    您在此处尝试执行的操作称为依赖定位器模式,通常被视为反模式。你应该坚持通过构造函数进行依赖注入,它更干净。

    关键是在启动时声明 DI 容器中的所有内容,甚至是您的自定义业务/数据层类,再也不要自己实例化它们,并在任何需要它们的类的构造函数中将它们声明为必需的依赖项。

    【讨论】:

    • 让我们看看我是否理解正确。我有 3 层,Host 是一个真正的 NetCore 应用程序,我在调试时启动它。业务层是网络标准库,数据层是库,但现在是 Startup.cs 的“伪核心应用程序”,因为我遵循了这个michael-whelan.net/ef-core-101-migrations-in-separate-assembly我的 app.UseIdentity 在数据层启动中。主机没有对数据层的引用,只有业务。如果我理解正确,您建议我开始从 Host 中的 Account Controller 传递我的引用?
    • 我建议每一层都在其构造函数中声明它所需的依赖关系:Controller --> Business、Business --> DataLayer。并且接口/实现在宿主应用的 startup.cs 中的 DI 容器中声明。
    • 您应该在主机的 startup.cs 中设置所有内容。
    • ApplicationDbContext “必须”在数据层中声明,因为从不同程序集引用数据模型存在问题。至少在我遵循上述教程之前,我遇到了问题。我认为我不应该有从 Host 到 Data 的引用(我在 Web API 中构建类似项目时没有),但如果这是我现阶段需要实现的解决方法,那就没关系了。跨度>
    • 如果你说的是程序集引用,有一个间接的,因为引用图是Host --> Business --> Data,而数据层DLL最终会在host的bin中,所以它可以在运行时加载。以前的行为是相同的,即使使用 Web API。
    【解决方案2】:

    ASP.NET Core 中的 DI 对使用“构造函数注入”方法的控制器和非控制器类的工作方式相同。

    您遇到的问题是Register 方法是static,因此无法访问实例变量/属性。你需要

    • 使Register方法非静态
    • 或将UserManager&lt;ApplicationUser&gt; userManager 作为参数传递给方法

    一般来说,您应该避免将静态类用于业务逻辑,因为它们无助于正确测试您的代码并产生代码耦合。通过 Internet/SO 搜索,您会发现很多为什么静态不好的主题。

    使用 DI 在您的控制器中获取 Data.Commands.ApplicationUsers 类的实例。如果您的应用程序只需要一个此类的实例 - 使用单例生命周期。


    更新。同样,使用构造函数注入:修改您的“数据层”类,以便它可以获取IIdentityTransaction 的实例作为构造函数参数:

    public class YourDataLayerClass : IYourDataLayerClass
    {
        private IIdentityTransaction _identityTransaction;
        public YourDataLayerClass(IIdentityTransaction identityTransaction)
        {
           _identityTransaction = identityTransaction;
        }
    
        public void MethodWhereYouNeedToCallAdd()
        {
            _identityTransaction.Add(...);
        }
    }
    

    IYourDataLayerClass 实例的想法相同:注册依赖

    services.AddScoped<IYourDataLayerClass, YourDataLayerClass>();
    

    然后依赖于它的类(在您的情况下为中间件,如果我理解正确的话)应该通过构造函数接收该实例:

    public class YourMiddleware
    {
        private IYourDataLayerClass _yourDataLayerClass;
        public YourMiddleware(IYourDataLayerClass yourDataLayerClass)
        {
           _yourDataLayerClass = yourDataLayerClass;
        }
        ...
    }
    

    【讨论】:

    • @Pierre Murasso 给出了类似的答案,但我不确定我是否完全理解它。如果我遵循您的建议,那么尝试实例化 YourMiddleware 类的业务层将要求我为构造函数提供 IYourDataLayerClass。而且我在业务层没有它。我在Business中将其添加为构造函数参数,那么Host Account Controller需要提供吗?然后呢?我在 Host 中添加对 Data Layer 的引用,在 Host Startup 中配置它并从那里推送?
    猜你喜欢
    • 2020-09-14
    • 1970-01-01
    • 2016-09-01
    • 1970-01-01
    • 2023-01-18
    • 2016-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多