简体   繁体   English

我可以在ASP.NET Core启动期间访问数据库吗?

[英]Can I access a database during startup in ASP.NET Core?

I have recently been working on a .NET Core web API. 我最近一直在研究.NET Core Web API。 I have just attempted authentication using JWT, by following the guide on https://stormpath.com/blog/token-authentication-asp-net-core . 我刚刚通过https://stormpath.com/blog/token-authentication-asp-net-core上的指南尝试使用JWT进行身份验证。

All was going well until I had to replace the hard-coded username and passwords in the GetIdentity method with a DB query and realized I do not know how to access the DB from within this file! 一切顺利,直到我不得不用数据库查询GetIdentity方法中的硬编码用户名和密码,并意识到我不知道如何从这个文件中访问数据库!

The method I am referring to is shown in the link below on line 70. https://github.com/nbarbettini/SimpleTokenProvider/blob/master/test/SimpleTokenProvider.Test/Startup.Auth.cs 我所指的方法显示在第70行的链接中.https://github.com/nbarbettini/SimpleTokenProvider/blob/master/test/SimpleTokenProvider.Test/Startup.Auth.cs

My questions are as follows. 我的问题如下。

  1. Can I access the database here? 我可以在这里访问数据库吗? If so how? 如果是这样的话?
  2. Should this be where the GetIdentity method is, or is there a better way? 这应该是GetIdentity方法的位置,还是有更好的方法?

Yes, you can access the database! 是的,您可以访问数据库! Code that runs in the Configure method can access any services that are added in the ConfigureServices method, including things like database contexts. ,在运行的代码Configure方法都不能访问在添加任何服务ConfigureServices方法,包括像数据库环境。

For example, if you have a simple Entity Framework context: 例如,如果您有一个简单的Entity Framework上下文:

using Microsoft.EntityFrameworkCore;
using SimpleTokenProvider.Test.Models;

namespace SimpleTokenProvider.Test
{
    public class SimpleContext : DbContext
    {
        public SimpleContext(DbContextOptions<SimpleContext> options)
            : base(options)
        {
        }

        public DbSet<User> Users { get; set; }
    }
}

And you add it in ConfigureServices : 然后在ConfigureServices添加它:

services.AddDbContext<SimpleContext>(opt => opt.UseInMemoryDatabase());

Then, you can access it when you are setting up the middleware: 然后,您可以在设置中间件时访问它:

var context = app.ApplicationServices.GetService<SimpleContext>();

app.UseSimpleTokenProvider(new TokenProviderOptions
{
    Path = "/api/token",
    Audience = "ExampleAudience",
    Issuer = "ExampleIssuer",
    SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
    IdentityResolver = (username, password) => GetIdentity(context, username, password)
});

And rewrite the GetIdentity method a little: 并稍微重写GetIdentity方法:

private Task<ClaimsIdentity> GetIdentity(SimpleContext context, string username, string password)
{
    // Access the database using the context
    // Here you'd need to do things like hash the password
    // and do a lookup to see if the user + password hash exists
}

I'm the author of the original sample. 我是原始样本的作者。 Sorry it wasn't clear initially! 对不起,最初还不清楚! I tried to write the IdentityResolver delegate in a way that makes it easy to provide your own functionality -- like integrating with your own database (as above), or hooking it up to ASP.NET Core Identity. 我试图以一种易于提供自己的功能的方式编写IdentityResolver委托 - 比如与您自己的数据库集成(如上所述),或者将其挂钩到ASP.NET Core Identity。 Of course, you're free to throw away my code and do something better, too. 当然,您可以随意丢弃我的代码并做更好的事情。 :) :)

On .NET CORE 2.1, just pass the context as an argument to the Configure method: 在.NET CORE 2.1上,只需将上下文作为参数传递给Configure方法:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, YourDbContext context)
{
        //do whatever you want with the context here...
}

The accepted answer does not work for scoped services ( scoped services are created per request, if you are using Entity Framework and adding the context with AddDbContext then this is the case ). 接受的答案不适用于作用域服务(每个请求都会创建作用域服务,如果您使用的是Entity Framework并使用AddDbContext添加上下文,则情况就是这样 )。

You can use scoped services in startup as follows ( source ): 您可以在启动时使用作用域服务,如下所示( ):

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    using (var serviceScope = app.ApplicationServices.CreateScope())
    {
        var services = serviceScope.ServiceProvider;
        var myDbContext = services.GetService<MyDbContext>();
    }
}

or pass it in the argument of the Configure method as shown in the answer of juanora 或者在Configure方法的参数中传递它,如juanora的答案所示

I might be wrong on some other level but the solution I found is to create a scope. 我可能在其他一些层面上错了,但我找到的解决方案是创建一个范围。

I passed the app instead of the ctx in GetIdentity, then in GetIdentity using a scope: 我在GetIdentity中传递了app而不是ctx,然后在使用范围的GetIdentity中传递:

using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope()) {
    if (serviceScope.ServiceProvider.GetService<YourAppDbContext>() != null) 
    {
      var ctx = serviceScope.ServiceProvider.GetService<YourAppDbContext>();

      if (AnAuthenticateMethodHereMaybe(ctx, username, password)) {
        return Task.FromResult(new ClaimsIdentity(new 
GenericIdentity(username, "Token"), new Claim[] { }));
      }
    }
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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