在我的解决方案中,我有 4 个项目。
目前(可能会发生变化,具体取决于答案)我在“域”层中创建了上下文创建/定义和 EF 迁移。
App.Web(以及 App.API)需要使用“通用”DbContext(它们都引用“域”至极引用“存储库”)。
我在 program.cs(复制在下面)中创建/配置 DI,但我的问题是我无法从存储库访问 UsersDbContext,因为它在“域”中。
一个解决方案可能是创建另一个名为 App.Data 的单独项目,其中仅定义上下文,“Web”、“Api”和“Repository”将引用该项目以访问上下文,但我不确定这是最好的解决方案。
这是我的程序.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
#region swagger
//builder.Services.AddSwaggerGen();
builder.Services.AddSwaggerGen(option =>
{
option.SwaggerDoc("v1", new OpenApiInfo { Title = "Demo API", Version = "v1" });
option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please enter a valid token",
Name = "Authorization",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT",
Scheme = "Bearer"
});
option.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type=ReferenceType.SecurityScheme,
Id="Bearer"
}
},
new string[]{}
}
});
});
#endregion
#region jwt data
var validIssuer = builder.Configuration["JWT:ValidIssuer"];
var validAudience = builder.Configuration["JWT:ValidAudience"];
var issuerSigningKey = builder.Configuration["JWT:IssuerSigningKey"];
#endregion
if (validIssuer != null && validAudience != null && issuerSigningKey != null)
{
#region jwt
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
//.AddAuthentication(options =>
//{
// options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
// options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
// options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
//})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ClockSkew = TimeSpan.FromDays(1),
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = validIssuer,
ValidAudience = validAudience,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(issuerSigningKey)
),
};
});
#endregion
#region DbContext
//builder.Services.AddDbContext<UsersDbContext>(options =>
//{
// options.UseSqlServer(builder.Configuration.GetConnectionString("SurveyConnection"));
//});
//builder.Services.AddDbContext<UsersDbContext>();
var connectionString = Util.getAppSettingsValue("ConnectionStrings:SurveyConnection");
if (connectionString == null)
{
throw new Exception("SQL connection string is not set in appsettings.json!");
}
builder.Services.AddSingleton(new UsersDbContext(connectionString));
#endregion
#region neo4j data
var neo4jServer = builder.Configuration["Neo4J:server"];
var neo4jUser = builder.Configuration["Neo4J:user"];
var neo4jPwd = builder.Configuration["Neo4J:pwd"];
#endregion
if (neo4jServer != null && neo4jUser != null && neo4jPwd != null)
{
#region neo4j
var client = new BoltGraphClient(new Uri(neo4jServer), neo4jUser, neo4jPwd);
client.ConnectAsync();
#endregion
#region services
#region repository
builder.Services.AddSingleton<ISurveysRepository, SurveysRepository>();
builder.Services.AddSingleton<IBlocksRepository, BlocksRepository>();
builder.Services.AddSingleton<IQuestionsRepository, QuestionsRepository>();
builder.Services.AddSingleton<IOptionsRepository, OptionsRepository>();
builder.Services.AddSingleton<IAnswersRepository, AnswersRepository>();
#endregion
#region domain
builder.Services.AddSingleton<ISurveysDomain, SurveysDomain>();
builder.Services.AddSingleton<IBlocksDomain, BlocksDomain>();
builder.Services.AddSingleton<IQuestionsDomain, QuestionsDomain>();
builder.Services.AddSingleton<IOptionsDomain, OptionsDomain>();
builder.Services.AddSingleton<IAnswersDomain, AnswersDomain>();
#endregion
builder.Services.AddSingleton<IGraphClient>(client);
//builder.Services.AddSingleton<UsersDbContext>();
builder.Services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
builder.Services.AddScoped<TokenService, TokenService>();
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
#endregion
#region users
builder.Services
.AddIdentityCore<IdentityUser>(options =>
{
options.SignIn.RequireConfirmedAccount = false;
options.User.RequireUniqueEmail = true;
options.Password.RequireDigit = false;
options.Password.RequiredLength = 6;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
}).AddRoles<IdentityRole>().AddEntityFrameworkStores<UsersDbContext>();
#endregion
var app = builder.Build();
// Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())
//{
app.UseSwagger();
app.UseSwaggerUI();
//}
#region swagger default page in release mode
if (!app.Environment.IsDevelopment())
{
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Surveys Web API");
options.RoutePrefix = string.Empty;
});
app.UseHsts();
}
#endregion
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseAuthentication();
app.MapControllers();
app.Run();
}
else
{
throw new Exception("Neo4J is not configured!");
}
}
else
{
throw new Exception("JWT is not configured!");
}
问题.cs
public class QuestionsRepository : IQuestionsRepository
{
private readonly UsersDbContext _context;
public QuestionsRepository(UsersDbContext context) //=> cannot access UsersDbContext
{
_context = context;
}
...
}
关于如何处理这个问题的任何帮助?
您可以将 DbContext 及其关联的实体放在 App.Data 项目中。
数据库实体是,并且应该与您的域实体不同。领域模型应该只包含他们需要在其中工作的数据。
例如,如果您在数据库中有一个具有以下属性的用户实体:
public class User{
public Guid ID {get; set;}
public string Username {get; set;}
public string Password {get; set;}
public string EmailAddress {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
}
您的域逻辑将分为两部分,例如为了显示目的需要名字和姓氏,但在这种情况下不需要密码或 ID,或者只需要用户名和密码即可登录。它不会始终传递完整的数据库实体是有意义的。