gpt4 book ai didi

c# - asp.net core DI 只注入(inject)单例,作用域和 transient 不工作

转载 作者:太空狗 更新时间:2023-10-30 00:30:06 24 4
gpt4 key购买 nike

我被卡住了,我似乎无法弄清楚这一点。我有一个带有接口(interface)的简单类。我正在将 EFContext 和 Logger 注入(inject)到该服务中。出于某种原因,无论我如何注册服务,它始终是单例。我将 Guid 属性放在类上以查看它是否根据每个请求更改,但它保持不变。

这是 AccountService 类及其接口(interface):

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 类及其接口(interface):

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属性(property)给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();
}

在我的 token 中间件中,我确实使用了 AccountService,这是 token 中间件:

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 }));
}
}

最佳答案

中间件只被实例化一次,所以它实际上是一个单例。

因此,您注入(inject)中间件构造函数的所有内容都从单例容器(您可以通过 Configure 方法中的 app.ApplicationServices 访问的容器)解析。

我看到您的 IAccountService 被注入(inject)到中间件中,所以这似乎是导致问题的原因。您必须在 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);
}

关于c# - asp.net core DI 只注入(inject)单例,作用域和 transient 不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41856278/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com