![](/img/trans.png)
[英]How to access Snowflake Database from .Net application using Entity Framework 6
[英]How to access the database in Middleware using Entity Framework 6
我寫了一些中間件來記錄數據庫中的請求路徑和查詢。 我有兩個單獨的模型。 一個用於記錄和一個商業模型。 在嘗試了幾件事之后我想出了這個:
public class LogMiddleware
{
private readonly RequestDelegate _next;
private readonly DbConnectionInfo _dbConnectionInfo;
public LogMiddleware(RequestDelegate next, DbConnectionInfo dbConnectionInfo)
{
_next = next;
_dbConnectionInfo = dbConnectionInfo;
}
public async Task Invoke(HttpContext httpContext)
{
httpContext.Response.OnStarting( async () =>
{
await WriteRequestToLog(httpContext);
});
await _next.Invoke(httpContext);
}
private async Task WriteRequestToLog(HttpContext httpContext)
{
using (var context = new MyLoggingModel(_dbConnectionInfo))
{
context.Log.Add(new Log
{
Path = request.Path,
Query = request.QueryString.Value
});
await context.SaveChangesAsync();
}
}
}
public static class LogExtensions
{
public static IApplicationBuilder UseLog(this IApplicationBuilder builder)
{
return builder.UseMiddleware<LogMiddleware>();
}
}
該模型:
public class MyLoggingModel : DbContext
{
public MyLoggingModel(DbConnectionInfo connection)
: base(connection.ConnectionString)
{
}
public virtual DbSet<Log> Log { get; set; }
}
你可以看到沒什么特別的。 它有效,但不是我希望它的方式。 問題可能在於EF6,而不是線程安全。
我從Startup開始:
public class Startup
{
private IConfigurationRoot _configuration { get; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
_configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<ApplicationSettings>(_configuration.GetSection("ApplicationSettings"));
services.AddSingleton<ApplicationSettings>();
services.AddSingleton(provider => new DbConnectionInfo { ConnectionString = provider.GetRequiredService<ApplicationSettings>().ConnectionString });
services.AddTransient<MyLoggingModel>();
services.AddScoped<MyModel>();
}
public void Configure(IApplicationBuilder app)
{
app.UseLog();
app.UseStaticFiles();
app.UseMvc();
}
}
MyLoggingModel
需要是瞬態的,以便讓它適用於中間件。 但這種方法會立即引發問題:
System.NotSupportedException:在上一個異步操作完成之前,在此上下文中啟動了第二個操作。 使用'await'確保在此上下文上調用另一個方法之前已完成任何異步操作。 任何實例成員都不保證是線程安全的。
我可以向你保證,我確實await
各地增加了await
。 但這並沒有解決這個問題。 如果我刪除異步部分,那么我收到此錯誤:
System.InvalidOperationException:已成功提交對數據庫的更改,但更新對象上下文時發生錯誤。 ObjectContext可能處於不一致狀態。 內部異常消息:保存或接受更改失敗,因為多個“MyLoggingModel.Log”類型的實體具有相同的主鍵值。 確保顯式設置的主鍵值是唯一的。 確保在數據庫和Entity Framework模型中正確配置了數據庫生成的主鍵。 使用實體設計器進行數據庫優先/模型優先配置。 使用'HasDatabaseGeneratedOption'fluent API或DatabaseGeneratedAttribute'進行Code First配置。
這就是我提出上述代碼的原因。 我本來想對模型使用依賴注入。 但我不能讓這個工作。 我也找不到從中間件訪問數據庫的示例。 所以我覺得我可能在錯誤的地方做這件事。
我的問題:有沒有辦法使用依賴注入來完成這項工作,或者我不應該訪問中間件中的數據庫? 我想知道,使用EFCore會有所作為嗎?
- 更新 -
我嘗試將代碼移動到一個單獨的類並注入:
public class RequestLog
{
private readonly MyLoggingModel _context;
public RequestLog(MyLoggingModel context)
{
_context = context;
}
public async Task WriteRequestToLog(HttpContext httpContext)
{
_context.EventRequest.Add(new EventRequest
{
Path = request.Path,
Query = request.QueryString.Value
});
await _context.SaveChangesAsync();
}
}
在初創公司:
services.AddTransient<RequestLog>();
在middelware中:
public LogMiddleware(RequestDelegate next, RequestLog requestLog)
但這與原始方法沒有區別,同樣的錯誤。 唯一可行的(除了非DI解決方案)是:
private async Task WriteRequestToLog(HttpContext httpContext)
{
var context = (MyLoggingModel)httpContext.RequestServices.GetService(typeof(MyLoggingModel));
但我不明白為什么會有所不同。
考慮在服務后面抽象db上下文,或者為db上下文本身創建一個並由中間件使用。
public interface IMyLoggingModel : IDisposable {
DbSet<Log> Log { get; set; }
Task<int> SaveChangesAsync();
//...other needed members.
}
並從抽象派生出實現。
public class MyLoggingModel : DbContext, IMyLoggingModel {
public MyLoggingModel(DbConnectionInfo connection)
: base(connection.ConnectionString) {
}
public virtual DbSet<Log> Log { get; set; }
//...
}
服務配置似乎正確完成。 根據我的上述建議,它需要更新db上下文的注冊方式。
services.AddTransient<IMyLoggingModel, MyLoggingModel>();
中間件可以通過構造函數注入抽象,也可以直接注入Invoke
方法。
public class LogMiddleware {
private readonly RequestDelegate _next;
public LogMiddleware(RequestDelegate next) {
_next = next;
}
public async Task Invoke(HttpContext context, IMyLoggingModel db) {
await WriteRequestToLog(context.Request, db);
await _next.Invoke(context);
}
private async Task WriteRequestToLog(HttpRequest request, IMyLoggingModel db) {
using (db) {
db.Log.Add(new Log {
Path = request.Path,
Query = request.QueryString.Value
});
await db.SaveChangesAsync();
}
}
}
如果所有其他方法都失敗,請考慮從請求的服務中獲取上下文,並將其用作服務定位器。
public class LogMiddleware {
private readonly RequestDelegate _next;
public LogMiddleware(RequestDelegate next) {
_next = next;
}
public async Task Invoke(HttpContext context) {
await WriteRequestToLog(context);
await _next.Invoke(context);
}
private async Task WriteRequestToLog(HttpContext context) {
var request = context.Request;
using (var db = context.RequestServices.GetService<IMyLoggingModel>()) {
db.Log.Add(new Log {
Path = request.Path,
Query = request.QueryString.Value
});
await db.SaveChangesAsync();
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.