我正在创建一个中间件来存储所有传入的 API 请求和响应正文以及一些其他数据,我使用实体框架作为我的 ORM。
它运行良好,但在某些请求和响应中,响应正文没有被存储。
当我记录错误时,它显示:
对象名称:'TransactionDBContext'.第 248 行
无法访问已处置的对象。导致此错误的一个常见原因是处置从依赖项注入解析的上下文,然后尝试在应用程序的其他位置使用相同的上下文实例。如果您在上下文上调用 Dispose() 或将上下文包装在 using 语句中,则可能会发生这种情况。如果您使用依赖注入,则应该让依赖注入容器负责处理上下文实例。
我只是使用异步返回任务和等待关键字的每个方法正确使用
Middleware.cs
:
public class TransactionLoggingMiddleWare
{
private readonly RequestDelegate _next;
private TransactionLogService _transactionLogService;
public TransactionLoggingMiddleWare(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, TransactionLogService transactionLogService)
{
_transactionLogService = transactionLogService;
Guid GUID = Guid.NewGuid();
try
{
var request = context.Request;
if (request.Path.ToString().Contains("Remittance/Post"))
{
var requestTime = DateTime.Now;
var requestBodyContent = await ReadRequestBody(request);
int bcode = requestBodyContent.IndexOf("content-disposition:");
if (bcode > 0)
{
string jung1 = requestBodyContent.Substring(0, bcode - 1);
requestBodyContent = requestBodyContent.Replace(jung1, "");
}
context.Items["Guid"] = GUID;
requestBodyContent = requestBodyContent.Replace("content-disposition: form-data; name=", "");
var form = await context.Request.ReadFormAsync();
var transactionData = new RemittanceRQ();
var properties = typeof(RemittanceRQ).GetProperties();
int remIdValue = 0;
long TxnRefNo = 0;
string productcode = null;
foreach (var property in properties)
{
// Get the property name
var propertyName = property.Name;
// Check if the form data contains the key corresponding to the property name
if (form.ContainsKey(propertyName))
{
// Get the value from the form data
var value = form[propertyName];
// Convert and assign the value to the property
if (value != String.Empty)
{
// Handle different property types accordingly
if (property.PropertyType == typeof(int))
{
property.SetValue(transactionData, Convert.ToInt32(value));
}
else if (property.PropertyType == typeof(Int16))
{
property.SetValue(transactionData , Convert.ToInt16(value));
}
else if (property.PropertyType == typeof(long))
{
property.SetValue(transactionData, long.Parse(value));
}
else if (property.PropertyType == typeof(decimal))
{
property.SetValue(transactionData, decimal.Parse(value));
}
else if (property.PropertyType == typeof(short))
{
property.SetValue(transactionData, Convert.ToInt16(value));
}
else if (property.PropertyType == typeof(String))
{
property.SetValue(transactionData, value.ToString());
}
if (propertyName == "RemID")
{
remIdValue = Convert.ToInt32(value);
}
if (propertyName == "TxnRefNumber")
{
TxnRefNo = long.Parse(value);
}
}
}
}
// to store only request body
await _transactionLogService.AddTransaction(new TransactionLogItem
{
TransactionGuid = GUID.ToString(),
CreatedDate = requestTime,
APIRequest = JsonConvert.SerializeObject(transactionData),
RemId = remIdValue,
TxnNumber = TxnRefNo,
});
// reading the response
var orginalBodyStream = context.Response.Body;
var originalBodyCode = context.Response.StatusCode;
try
{
using (var responseBody = new MemoryStream())
{
//context.Response.Body = responseBody;
var response = context.Response;
response.Body = responseBody;
await _next(context);
responseBody.Seek(0, SeekOrigin.Begin);
var responseBodyContent = await new StreamReader(responseBody).ReadToEndAsync();
var responseData = JsonConvert.DeserializeObject<ResponseData>(responseBodyContent);
if (responseData != null)
{
var txnReferenceNo = responseData.Data?.TxnReferenceNo;
}
await _transactionLogService.UpdateTransactionLog(new TransactionLogItem
{
TransactionGuid = GUID.ToString(),
APIResponse = responseBodyContent,
Status_Code = response.StatusCode,
TxnNumber = responseData.Data?.TxnReferenceNo,
TransactionStatus = responseData.IsSuccess,
XPIN = responseData.Data?.TTNUM,
});
responseBody.Seek(0, SeekOrigin.Begin);
await responseBody.CopyToAsync(orginalBodyStream);
}
// end of reading response body
//await _next(context);
}
catch (Exception ex)
{
await _transactionLogService.UpdateTransactionLog(new TransactionLogItem
{
TransactionGuid = GUID.ToString(),
ExceptionMsg = ex.Message
});
File.AppendAllText("loggerMiddlewareException.txt",ex.Message.ToString()+"line no 231"+$"{GUID}"+"\n");
}
}
else
{
await _next(context);
}
}
catch (Exception ex)
{
bool translogGuid = context.Items.TryGetValue("Guid", out var requestIdObj);
var request = context.Request;
var requestTime = DateTime.Now;
File.AppendAllText("loggerMiddlewareException.txt", ex.Message.ToString() + "line no 248" + "\n");
var requestBodyContent = await ReadRequestBody(request);
await _transactionLogService.UpdateTransactionLog(new TransactionLogItem
{
TransactionGuid = GUID.ToString(),
ExceptionMsg = ex.Message,
});
await _next(context);
}
}
private async Task<string> ReadRequestBody(HttpRequest request)
{
request.EnableBuffering();
var buffer = new byte[Convert.ToInt32(request.ContentLength??0)];
await request.Body.ReadAsync(buffer, 0, buffer.Length);
var bodyAsText = Encoding.UTF8.GetString(buffer);
request.Body.Seek(0, SeekOrigin.Begin);
return bodyAsText;
}
}
服务寄存器:
services.AddDbContext<TransactionDBContext>(options => options.UseSqlServer(connectionString));
DbContext
public class TransactionDBContext:DbContext{
public TransactionDBContext(DbContextOptions<TransactionDBContext> options):base(options)
{
}
public DbSet<TransactionLogItem> tblt_TransactionLogs { get; set; }}
服务.cs:
public class TransactionLogService{
private readonly TransactionDBContext _dbContext;
public int TransactionId { get; set; }
public TransactionLogService(TransactionDBContext transactionDBContext)
{
_dbContext = transactionDBContext;
}
public async Task AddTransaction(TransactionLogItem transactionLogItem)
{
_dbContext.tblt_TransactionLogs.Add(transactionLogItem);
await _dbContext.SaveChangesAsync();
//TransactionId = transactionLogItem.TransactionLogID;
}
public async Task UpdateTransactionLog(TransactionLogItem LogItem)
{
var existingLog = await _dbContext.tblt_TransactionLogs.FirstOrDefaultAsync(t => t.TransactionGuid == LogItem.TransactionGuid);
if (existingLog != null)
{
existingLog.EngineCode = LogItem.EngineCode = string.IsNullOrEmpty(LogItem.EngineCode) ? existingLog.EngineCode : LogItem.EngineCode; ;
existingLog.APIResponse = LogItem.APIResponse=string.IsNullOrEmpty(LogItem.APIResponse)? existingLog.APIResponse: LogItem.APIResponse;
existingLog.Status_Code = LogItem.Status_Code==0?existingLog.Status_Code:LogItem.Status_Code;
existingLog.TxnNumber= LogItem.TxnNumber == null || LogItem.TxnNumber == 0 ? existingLog.TxnNumber:LogItem.TxnNumber;
existingLog.XPIN = LogItem.XPIN=string.IsNullOrEmpty(LogItem.XPIN)?existingLog.XPIN:LogItem.XPIN;
existingLog.MTORequest=LogItem.MTORequest=string.IsNullOrEmpty(LogItem.MTORequest)?existingLog.MTORequest:LogItem.MTORequest;
existingLog.MTOResponse=LogItem.MTOResponse=string.IsNullOrEmpty(LogItem.MTOResponse)?existingLog.MTOResponse:LogItem.MTOResponse;
existingLog.ExceptionMsg = LogItem.ExceptionMsg = string.IsNullOrEmpty(LogItem.ExceptionMsg) ? existingLog.ExceptionMsg : LogItem.ExceptionMsg;
existingLog.TransactionStatus = LogItem.TransactionStatus;
}
await _dbContext.SaveChangesAsync();
}
}
它在几乎 80% 的情况下工作正常,但在某些情况下它无法仅存储响应。
如何解决?生产中的问题
您描述的问题似乎与服务(可能是 TransactionLogService)的生命周期有关。 请注意,DbContext 始终具有作用域(为每个请求创建一个新作用域),因此如果您有依赖它的单例服务(具有更长的生命周期并且在请求之间保持不变)。在某些情况下,可能会发生这样的情况:当 dbcontext 已被访问时,它会尝试访问它。 已经处置了。 一些可能的解决方案:
检查如何将其添加到 Program.cs 中的依赖注入容器中,并研究考虑到 dbcontext 将是范围的服务的适当生命周期。
确保它不会发生的更机械/确定的方法是,不直接注入 DbContext 类,而是注入 IServiceScopeFactory ,并在使用上下文的每个方法中,使用工厂创建一个新作用域并获取一个新副本从中获取 DbContext。这使您可以完全控制 dbcontext 实例的生命周期,并且不会发生这些错误(唯一的缺点是 dbcontext 数据可能在 SaveChanges 调用之间不一致)。
我希望您觉得这有帮助。
让我知道您是否需要 2 解决方案的示例代码,我稍后会尝试添加它们。