asp.net核心DI只注入單例,範圍和瞬態不工作

asp.net-core asp.net-core-mvc c# dependency-injection entity-framework-core

我被卡住了,我似乎無法解決這個問題。我有一個帶接口的簡單類。我正在將EFContext和Logger注入此服務。出於某種原因,無論我如何註冊服務,它總是一個單身人士。我把Guid屬性放在類上,看它是否在每個請求上都有變化,但它保持不變。

這是AccountService類及其接口:

public interface IAccountService 
{
    Account GetAccountByEmailAndPassword(string emailAddress, string password);
}

public class AccountService : IAccountService
{
    private readonly IEFContext _context;
    private readonly ILogger<AccountService> _logger;
    private string _guid;

    public AccountService()
    {
        _context = context;
        _logger = logger;
        _guid = Guid.NewGuid().ToString();
    }

    public Account GetAccountByEmailAndPassword(string emailAddress, string password)
    {
        try
        {
            //get the account
            var account = _context.Account.FirstOrDefault(x => x.EmailAddress == emailAddress);

            //make sure we have an account
            if (account == null)
                return null;

            //generate hash from account
            var accountHash = GeneratePasswordSaltHash(account.Password, account.PasswordSalt);

            //generate hash from credentials passed in
            var passedInHash = GeneratePasswordSaltHash(
                Convert.ToBase64String(HashPassword(password)),
                account.PasswordSalt);

            // TODO: number of failed attempts should lock account etc.
            return accountHash == passedInHash ? account : null;
        } catch (Exception ex)
        {
            _logger.LogError("Exception in AccountService: " + ex.ToString());
            throw;
        }
    }
}

以下是我註冊服務的方式:

public void ConfigureServices(IServiceCollection services)
{
    // App Settings
    services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));

    // Add DBContext
    var connectionString = Configuration["AppSettings:Data:ConnectionString"];
    services.AddDbContext<EFContext>(options => options.UseSqlServer(connectionString));

    // Add framework services.
    services.AddMvc();

    //  Add DI
    services.AddScoped<IEFContext, EFContext>();
    services.AddScoped<IAccountService, AccountService>();
}

這是EFContext類及其接口:

public interface IEFContext
{
    DbSet<Account> Account { get; set; }

    int SaveChanges();
    EntityEntry Update(object entity);
}

public class EFContext : DbContext, IEFContext
{
    public EFContext(DbContextOptions options) : base(options) {}
    public DbSet<Account> Account { get; set; }
}

我可以使用上下文來訪問數據庫和所有內容,但是,一切都是單例。我第一次收到了這個問題的警告,因為如果我進入數據庫並手動更新帳戶上的某些數據,然後在代碼中再次請求帳戶,數據將會過時。我認為這是一個上下文問題,但我認為我正在使用.AddScoped<>正確配置上下文生命週期,但我無法讓它工作。那麼我嘗試將_guid屬性添加到AccountService以確定它是否在每個請求上被新增,並且它似乎不是。我也試過.AddTransient<> 。任何幫助表示讚賞。非常感謝。

編輯這是我的配置方法:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));

        loggerFactory.AddDebug();
        loggerFactory.AddSerilog();

        //Token stuff
        // secretKey contains a secret passphrase only your server knows
        var secretKey = "mysupersecret_secretkey!123";
        var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));

        var tokenValidationParameters = new TokenValidationParameters
        {
            // The signing key must match!
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = signingKey,

            // Validate the JWT Issuer (iss) claim
            ValidateIssuer = true,
            ValidIssuer = "ExampleIssuer",

            // Validate the JWT Audience (aud) claim
            ValidateAudience = true,
            ValidAudience = "ExampleAudience",

            // Validate the token expiry
            ValidateLifetime = true,

            // If you want to allow a certain amount of clock drift, set that here:
            ClockSkew = TimeSpan.Zero
        };

        app.UseJwtBearerAuthentication(new JwtBearerOptions
        {
            AutomaticAuthenticate = true,
            AutomaticChallenge = true,
            TokenValidationParameters = tokenValidationParameters
        });

        // Token generator
        var options = new TokenProviderOptions
        {
            Audience = "ExampleAudience",
            Issuer = "ExampleIssuer",
            SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
        };

        app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));

        app.UseMvc();
    }

在我的令牌中間件中,我確實使用了AccountService,這裡是令牌中間件:

public class TokenProviderMiddleware
{
    private readonly RequestDelegate _next;
    private readonly TokenProviderOptions _options;
    private readonly IAccountService _accountService;

    public TokenProviderMiddleware(RequestDelegate next, IOptions<TokenProviderOptions> options, IAccountService accountService)
    {
        _next = next;
        _options = options.Value;
        _accountService = accountService;
    }

    public Task Invoke(HttpContext context)
    {
        // If the request path doesn't match, skip
        if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))
        {
            return _next(context);
        }

        if (!context.Request.Method.Equals("POST")
           || !context.Request.ContentType.Contains("application/json"))
        {
            context.Response.StatusCode = 400;
            return context.Response.WriteAsync("Bad request.");
        }

        return GenerateToken(context);
    }

    private async Task GenerateToken(HttpContext context)
    {
        var rawAccount = await new StreamReader(context.Request.Body).ReadToEndAsync();
        var authAccount = JsonConvert.DeserializeObject<AuthAccount>(rawAccount);

        var account = _accountService.GetAccountByEmailAndPassword(authAccount.EmailAddress, authAccount.Password);
        if (account == null)
        {
            context.Response.StatusCode = 400;
            await context.Response.WriteAsync("Invalid email address or password.");
            return;
        }

        var now = DateTime.UtcNow;

        // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
        // You can add other claims here, if you want:
        var claims = new Claim[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, account.EmailAddress),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(JwtRegisteredClaimNames.Iat, ((DateTimeOffset)now).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
            new Claim(ClaimTypes.Role, account.RoleId.ToString()),
            new Claim(ClaimTypes.Name, account.EmailAddress)
        };

        // Create the JWT and write it to a string
        var jwt = new JwtSecurityToken(
            issuer: _options.Issuer,
            audience: _options.Audience,
            claims: claims,
            notBefore: now,
            expires: now.Add(_options.Expiration),
            signingCredentials: _options.SigningCredentials);
        var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

        var response = new ApiResponse<AuthAccount>
        {
            StatusCode = (int)HttpStatusCode.OK,
            Message = "Access granted",
            Data = new AuthAccount
            {
                Access_Token = encodedJwt,
                Expires_In = (int)_options.Expiration.TotalSeconds
            }
        };

        // Serialize and return the response
        context.Response.ContentType = "application/json";
        await context.Response.WriteAsync(JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented }));
    }
}

一般承認的答案

中間件只被實例化一次,因此它是一個有效的單例。

因此,您注入到Middlewares構造函數中的所有內容都將從單例容器(您可以通過Configure方法中的app.ApplicationServices訪問的容器)中解析出來。

我看到你的IAccountService被注入中間件,所以這似乎導致了這個問題。您必須在Invoke方法中使用,基於每個上下文解析它

public Task Invoke(HttpContext context, IAccountService accountService)
{
    // If the request path doesn't match, skip
    if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))
    {
        return _next(context);
    }

    if (!context.Request.Method.Equals("POST")
       || !context.Request.ContentType.Contains("application/json"))
    {
        context.Response.StatusCode = 400;
        return context.Response.WriteAsync("Bad request.");
    }

    return GenerateToken(context, accountService);
}

要么

public Task Invoke(HttpContext context)
{
    var accountService = context.RequestServices.GetRequiredService<IAccountService>();

    // If the request path doesn't match, skip
    if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))
    {
        return _next(context);
    }

    if (!context.Request.Method.Equals("POST")
       || !context.Request.ContentType.Contains("application/json"))
    {
        context.Response.StatusCode = 400;
        return context.Response.WriteAsync("Bad request.");
    }

    return GenerateToken(context, accountService);
}


Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因