在ASP.NET Core 10 Web API项目中遇到连接字符串为空的错误

后端开发 2026-07-09

我在.NET 10上运行,所有东西都已更新到最新版。我在使用EF Core和 ASP.NET Core。

这是我的 program.cs 文件;获取连接字符串时,返回的正是我预期的结果。当我进入 LoginController 时,我能看到传入的 IConfiuguration 中的设置。然而,当我在 FSMUserStore 构造函数中设置断点时,查看传入的上下文,发现它的连接字符串根本没有被正确设置。

我已经用Visual Studio调试器调试了无数次,尝试了无数方法,现在真的有点迷茫。有人有什么建议吗?

using ElmahCore.Mvc;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Portal.Libraries;
using PortalDataModels.Models;
using System.Text;
using Twilio.TwiML.Voice;

var builder = WebApplication.CreateBuilder(args);
IConfiguration configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .AddEnvironmentVariables()
    .Build();

var connString = configuration.GetConnectionString("DefaultConnection");

// Add services to the container.

// For Entity Framework
builder.Services.AddDbContext<PortalDataModels.Models.POA_CSMContext>(options => options.UseSqlServer(configuration.GetConnectionString(connString)));

builder.Services.AddScoped<IUserStore<FSMUser>, FSMUserStore>();

builder.Services.AddIdentity<FSMUser, IdentityRole>()
    .AddEntityFrameworkStores<POA_CSMContext>()
    .AddUserManager<UserManager<FSMUser>>()
    .AddSignInManager<SignInManager<FSMUser>>()
    .AddDefaultTokenProviders();

builder.Services.AddElmah<ElmahCore.Sql.SqlErrorLog>(options => {
    options.ConnectionString = connString;
    options.SqlServerDatabaseTableName = "Elmah_Error"; //Defaults to ELMAH_Error if not set
    options.OnPermissionCheck = context => false;
});

// Adding Authentication
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
// Adding Jwt Bearer
.AddJwtBearer(options =>
{
    options.SaveToken = true;
    options.RequireHttpsMetadata = false;
    options.MapInboundClaims = false;
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidAudience = configuration["Jwt:ValidAudience"],
        ValidIssuer = configuration["Jwt:ValidIssuer"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:Key"]))
    };
});

builder.Services.AddScoped<UserManager<FSMUser>>();

builder.Services.AddElmah<ElmahCore.Sql.SqlErrorLog>(options =>
{
    options.ConnectionString = connString;
    options.SqlServerDatabaseTableName = "Elmah_Error"; //Defaults to ELMAH_Error if not set
    options.OnPermissionCheck = context => false;
});

builder.Services.AddControllers();
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

LoginController.cs 文件:

[Produces("application/json")]
[Route("api/[controller]")]
[ApiController]
public class LoginController : ControllerBase
{
        private readonly SignInManager<FSMUser> _signInManager;
        private readonly UserManager<FSMUser> _userManager;
        private readonly RoleManager<IdentityRole> _roleManager;
        private readonly IConfiguration _configuration;

        public LoginController(
            SignInManager<FSMUser> signInManager, 
            UserManager<FSMUser> userManager,
            RoleManager<IdentityRole> roleManager,
            IConfiguration configuration)
        {
            _signInManager = signInManager;
            _userManager = userManager;
            _roleManager = roleManager;
            _configuration = configuration;
        }

        [HttpPost]
        public async Task<IActionResult> Login([FromBody] Login model)
        {
            var user = await _userManager.FindByNameAsync(model.Username);

            if (user != null && await _userManager.CheckPasswordAsync(user, model.Password))
            {
                // var userRoles = await _userManager.GetRolesAsync(user);
                var authClaims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, user.UserName.Trim()),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                };

                // authClaims.Add(new Claim("UserId", user.Id));
                var token = GetToken(authClaims);
                return Ok(new
                {
                    token = new JwtSecurityTokenHandler().WriteToken(token),
                    expiration = token.ValidTo,
                    Id = user.Id.Trim(),
                });
            }

            return Unauthorized();
        }

        private JwtSecurityToken GetToken(List<Claim> authClaims)
        {
            var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(s: _configuration["Jwt:Key"]));

            var token = new JwtSecurityToken(
                issuer: _configuration["Jwt:ValidIssuer"],
                audience: _configuration["Jwt:ValidAudience"],
                expires: DateTime.Now.AddYears(30),
                claims: authClaims,
                signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
                );

            return token;
        }
}

FSMUserStore.cs 文件:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using PortalDataModels.Models;
using System.Data.Common;
using System.Data.SqlClient;

namespace Portal.Libraries
{
    public class FSMUserStore : IUserStore<FSMUser>, IUserPasswordStore<FSMUser>
    {
        private bool disposedValue;
        private POA_CSMContext _ctx;

        public FSMUserStore(POA_CSMContext ctx)
        {
            _ctx = ctx;  // <!-- no database connection string.
        }

        public async Task<IdentityResult> CreateAsync(FSMUser user, CancellationToken cancellationToken)
        {
            var fsmu = new Arcustmr();
            fsmu.Email = user?.UserName;
            fsmu.Userid = user?.UserName;
            fsmu.Code = user?.PasswordHash;
            _ctx.Arcustmrs.Add(fsmu);
            await _ctx.SaveChangesAsync();

            return IdentityResult.Success;
        }

        public async Task<IdentityResult> DeleteAsync(FSMUser user, CancellationToken cancellationToken)
        {
            var users = await (from u in _ctx.Arcustmrs where u.Email == user.UserName select u).ToListAsync();
            _ctx.Arcustmrs.RemoveRange(users);
            await _ctx.SaveChangesAsync();

            return IdentityResult.Success;
        }

        public async Task<FSMUser?> FindByIdAsync(string userId, CancellationToken cancellationToken)
        {
            var user = await (from c in _ctx.Arcustmrs where c.Custno == userId select new FSMUser() { Id = c.Custno, UserName = c.Email, NormalizedUserName = c.Email, PasswordHash = c.Code }).FirstOrDefaultAsync();
            return user;
        }

        // An error occurs in the call here stating that the 
        // ConnectionString property has not been set.
        public async Task<FSMUser?> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
        {
            var user = await (from c in _ctx.Arcustmrs where c.Email == normalizedUserName select new FSMUser() { Id = c.Custno, NormalizedUserName = c.Email, UserName = c.Email, PasswordHash = c.Code }).FirstOrDefaultAsync();
            return user;
        }

        public Task<string?> GetNormalizedUserNameAsync(FSMUser user, CancellationToken cancellationToken)
        {
            return Task.FromResult(user.NormalizedUserName);
        }

        public Task<string> GetUserIdAsync(FSMUser user, CancellationToken cancellationToken)
        {
            return Task.FromResult(user.Id);
        }

        public Task<string?> GetUserNameAsync(FSMUser user, CancellationToken cancellationToken)
        {
            return Task.FromResult(user.UserName);
        }

        public Task SetNormalizedUserNameAsync(FSMUser user, string? normalizedName, CancellationToken cancellationToken)
        {
            user.NormalizedUserName = normalizedName;
            return Task.CompletedTask;
        }

        public Task SetUserNameAsync(FSMUser user, string? userName, CancellationToken cancellationToken)
        {
            user.UserName = userName;
            return Task.CompletedTask;
        }

        public Task<IdentityResult> UpdateAsync(FSMUser user, CancellationToken cancellationToken)
        {
            return Task.FromResult(IdentityResult.Success);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: dispose managed state (managed objects)
                }

                // TODO: free unmanaged resources (unmanaged objects) and override finalizer
                // TODO: set large fields to null
                disposedValue = true;
            }
        }

        // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
        // ~FSMUserStore()
        // {
        //     // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
        //     Dispose(disposing: false);
        // }

        public void Dispose()
        {
            // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }

        public Task SetPasswordHashAsync(FSMUser user, string? passwordHash, CancellationToken cancellationToken)
        {
            var us = (from u in _ctx.Arcustmrs where u.Email == user.UserName select u).SingleOrDefault();
            if (us != null)
            {
                us.Code = passwordHash;
                _ctx.SaveChanges();
            }

            return Task.CompletedTask;
        }

        public Task<string?> GetPasswordHashAsync(FSMUser user, CancellationToken cancellationToken)
        {
            return Task.FromResult(user.PasswordHash);
        }

        public async Task<bool> HasPasswordAsync(FSMUser user, CancellationToken cancellationToken)
        {
            var result = false;
            var email = user.UserName;

            var use = await (from u in _ctx.Arcustmrs where u.Email.Contains(email) select u).SingleOrDefaultAsync(cancellationToken: cancellationToken);

            if (use != null)
            {
                if((use.Code == null) || 
                    (String.IsNullOrEmpty(use.Code)))
                {
                    result = false;
                }
                else
                {
                    result = true;
                }
            }
            else
            {
                result = false;
            }
            return result;
        }

        public async Task<List<string>> GetRolesAsync(FSMUser user)
        {
            var rl = new List<string>();
            rl.Add("User");
            return rl;
        }
    }
}

解决方案

问题在于你把第一次检索到的 "DefaultConnection" 连接字符串的 获取到的值,作为同一连接字符串配置段中第二次查找的 来使用。

var connString = configuration.GetConnectionString("DefaultConnection");

builder.Services.AddDbContext<POA_CSMContext>(options => options.UseSqlServer(
   configuration.GetConnectionString(connString))); // <-- second lookup with 
                                                    // the value of the DefaultConnection as the key
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章