【发布时间】:2015-12-30 07:55:34
【问题描述】:
我正在尝试将每个请求记录到我的网站 (Asp.net mvc)。为此,我创建了一个全局过滤器属性,用于从当前请求中收集日志信息。异步执行此操作的主要思想是不要阻止我的主应用程序。并且只是静默记录数据。当我的操作记录器无法记录请求时,当我设置错误记录器以记录错误时,一切都正常。
下面的代码是我的过滤器
public class RequestLoggingAttribute : ActionFilterAttribute
{
private readonly IActionLogService _actionLogService;
public RequestLoggingAttribute(IActionLogService actionLogService)
{
_actionLogService = actionLogService;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Run an async method here to log any data you need.
Task.Run(() => GatherActionLog(filterContext)).ConfigureAwait(false);
base.OnActionExecuting(filterContext);
}
private async Task GatherActionLog(ActionExecutingContext filterContext)
{
try
{
var httpContext = filterContext.RequestContext.HttpContext;
var request = httpContext.Request;
if (!request.Url.AbsoluteUri.Contains("elmah"))
{
var roles = ((ClaimsIdentity) httpContext.User.Identity).Claims
.Where(c => c.Type == ClaimTypes.Role)
.Select(c => c.Value).ToArray();
var parameters = filterContext.ActionParameters.Select(x => new {x.Key, x.Value});
var jsonParameters = Json.Encode(parameters);
var actionLog = new ActionLog()
{
Action = filterContext.ActionDescriptor.ActionName,
Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
DateTimeUTC = DateTime.UtcNow,
IpAddress = request.UserHostAddress,
UserName = httpContext.User.Identity.Name,
Request = request.Url.AbsoluteUri,
ComputerName = request.UserHostName,
UserRole = String.Join(",", roles),
UserAgent = request.UserAgent,
ClientLoggedInUser = request.LogonUserIdentity.Name,
HttpMethod = httpContext.Request.HttpMethod,
Parameters = jsonParameters,
BrowserName = request.Browser.Id,
BrowserVersion = request.Browser.Version
};
await _actionLogService.InsertAsync(actionLog);
}
}
catch (Exception ex)
{
Elmah.ErrorLog.GetDefault(filterContext.HttpContext.ApplicationInstance.Context).Log(new Elmah.Error(ex));
}
}
}
我的 Elmah 的错误列表中有 两个错误。如下:
第一:
在前一个异步操作完成之前,在此上下文上启动了第二个操作。使用 'await' 确保在此上下文上调用另一个方法之前已完成任何异步操作。不保证任何实例成员都是线程安全的。
第二:
保存或接受更改失败,因为多个“Domain.LogEntities.ActionLog”类型的实体具有相同的主键值。确保显式设置的主键值是唯一的。确保在数据库和实体框架模型中正确配置了数据库生成的主键。使用实体设计器进行数据库优先/模型优先配置。使用“HasDatabaseGeneratedOption”流式 API 或“DatabaseGeneratedAttribute”进行 Code First 配置。
ActionLog 的域类如下:
public class ActionLog
{
public Guid ActionLogId { get; set; }
public string UserRole { get; set; }
public string UserName { get; set; }
public string ClientLoggedInUser { get; set; }
public string UserFullName { get; set; }
public DateTime DateTimeUTC { get; set; }
public string Action { get; set; }
public string Controller { get; set; }
public string Request { get; set; }
public string ComputerName { get; set; }
public string IpAddress { get; set; }
public string HttpMethod { get; set; }
public string Parameters { get; set; }
public string UserAgent { get; set; }
public string BrowserName { get; set; }
public string BrowserVersion { get; set; }
}
如果重要的话,我使用了两个数据库,一个用于我的主应用程序,第二个用于日志记录(错误和请求日志记录)。所以我的 DAL 层中有两个实体框架 DbContext。一个 DbContext 用于应用程序,第二个用于记录器。但是我使用了一个接受 DbContext 的 BaseRepository。我对 BaseRepository 的调用如下:
public class GenericRepository<T> : BaseRepository<T, ApplicationDbContext> where T : class
{
public GenericRepository(ApplicationDbContext db)
:base(db)
{
}
}
public class LoggerRepository<T> : BaseRepository<T, LoggerDbContext> where T : class
{
public LoggerRepository(LoggerDbContext db)
:base(db)
{
}
}
1) 有没有更好的方法! (我不想改变我的整个代码,只是适合这种方法的更好的方法)
2) 如何防止出现上述错误?
谢谢
【问题讨论】:
-
您是否在
Task.Run()上缺少await? -
我不想等待这个任务,所以我为任务设置了 ConfigureAwait(false)。我不希望我的请求记录器阻止主应用程序。
-
“使用 'await' 确保所有异步操作都已完成,然后再在此上下文中调用另一个方法” - 只是说'
-
我没有看到您在哪里设置 ActionLogId?他们都可以用默认(Guid)创建吗
-
@Fran,它使用Identity生成,我在fluent Api中设置了这一行:this.Property(p => p.ActionLogId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
标签: c# asp.net-mvc entity-framework asp.net-mvc-5 async-await