Entity Framework 6 - ASP.NET 过滤器 - 并发添加
Entity Framework 6 - ASP.NET Filter - Concurrent Adds
我有 Web Api 2 设置 Entity Framework。
我做了一个 ActionFilterAttribute
应该记录每次调用到数据库中。
如果一次调用一个,这会很好用。但是,如果同时进行多个调用,则会失败。对不同类型的并发感到困惑,我会针对我的特定场景提出问题。
如何修改我现有的代码,以便可以添加数据库条目而不会发生并发调用冲突?
附带说明:每个日志条目都有一个应该由数据库生成的 ID。
public class AppLogAttribute : ActionFilterAttribute
{
//private AppContext db = new AppContext(); //I can use this or the using statement.
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
var ip = ((System.Web.HttpContextWrapper)actionContext.Request.Properties["MS_HttpContext"]).Request.UserHostAddress;
var userId = Convert.ToInt32(actionContext.RequestContext.Principal.Identity.Name);
var url = actionContext.Request.RequestUri.ToString();
var date = DateTime.Now;
var logEntry = new LogEntry
{
Date = date,
IpAddress = ip,
Url = url,
UserID = userId
};
//Problems here for concurrent calls!
using (var db = new AppContext())
{
db.LogEntries.Add(logEntry);
db.SaveChanges();
}
}
}
日志条目Class ...
public class LogEntry
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long ID { get; set; }
public int UserID { get; set; }
public string IpAddress { get; set; }
public DateTime Date { get; set; }
public string Url { get; set; }
}
ASP.NET 中的过滤器创建一次并缓存,这就是为什么你不能通过过滤器的构造函数注入你的依赖项,因为它们将是 resolved/injected 一次,所以在你的情况下,如果你声明了上下文作为私有变量,它将是一个单例对象,这就是为什么您遇到并发问题的原因,因为您使用相同的上下文同时添加相同的 2 个请求。
解决您的问题的一个方法是通过 actionContext 参数使用 GetDependencyScope 方法,因此您的代码可以是这样的:
public class AppLogAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
var ip = ((System.Web.HttpContextWrapper)actionContext.Request.Properties["MS_HttpContext"]).Request.UserHostAddress;
var userId = Convert.ToInt32(actionContext.RequestContext.Principal.Identity.Name);
var url = actionContext.Request.RequestUri.ToString();
var date = DateTime.Now;
var logEntry = new LogEntry
{
Date = date,
IpAddress = ip,
Url = url,
UserID = userId
};
// get the AppContext from the current scope
var appContext = actionContext.Request.GetDependencyScope().GetService(typeof(AppContext)) as AppContext;
if (appContext != null)
{
appContext.LogEntries.Add(logEntry);
appContext.SaveChanges();
}
}
}
请注意,上述解决方案假定您已经在 Api 配置部分将 Ioc 容器设置为依赖项解析器,因此如果您使用的是 Autofac Ioc,则如下所示:
_container = builder.Build();
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(_container);
希望对您有所帮助。
我有 Web Api 2 设置 Entity Framework。
我做了一个 ActionFilterAttribute
应该记录每次调用到数据库中。
如果一次调用一个,这会很好用。但是,如果同时进行多个调用,则会失败。对不同类型的并发感到困惑,我会针对我的特定场景提出问题。
如何修改我现有的代码,以便可以添加数据库条目而不会发生并发调用冲突?
附带说明:每个日志条目都有一个应该由数据库生成的 ID。
public class AppLogAttribute : ActionFilterAttribute
{
//private AppContext db = new AppContext(); //I can use this or the using statement.
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
var ip = ((System.Web.HttpContextWrapper)actionContext.Request.Properties["MS_HttpContext"]).Request.UserHostAddress;
var userId = Convert.ToInt32(actionContext.RequestContext.Principal.Identity.Name);
var url = actionContext.Request.RequestUri.ToString();
var date = DateTime.Now;
var logEntry = new LogEntry
{
Date = date,
IpAddress = ip,
Url = url,
UserID = userId
};
//Problems here for concurrent calls!
using (var db = new AppContext())
{
db.LogEntries.Add(logEntry);
db.SaveChanges();
}
}
}
日志条目Class ...
public class LogEntry
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long ID { get; set; }
public int UserID { get; set; }
public string IpAddress { get; set; }
public DateTime Date { get; set; }
public string Url { get; set; }
}
ASP.NET 中的过滤器创建一次并缓存,这就是为什么你不能通过过滤器的构造函数注入你的依赖项,因为它们将是 resolved/injected 一次,所以在你的情况下,如果你声明了上下文作为私有变量,它将是一个单例对象,这就是为什么您遇到并发问题的原因,因为您使用相同的上下文同时添加相同的 2 个请求。
解决您的问题的一个方法是通过 actionContext 参数使用 GetDependencyScope 方法,因此您的代码可以是这样的:
public class AppLogAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
var ip = ((System.Web.HttpContextWrapper)actionContext.Request.Properties["MS_HttpContext"]).Request.UserHostAddress;
var userId = Convert.ToInt32(actionContext.RequestContext.Principal.Identity.Name);
var url = actionContext.Request.RequestUri.ToString();
var date = DateTime.Now;
var logEntry = new LogEntry
{
Date = date,
IpAddress = ip,
Url = url,
UserID = userId
};
// get the AppContext from the current scope
var appContext = actionContext.Request.GetDependencyScope().GetService(typeof(AppContext)) as AppContext;
if (appContext != null)
{
appContext.LogEntries.Add(logEntry);
appContext.SaveChanges();
}
}
}
请注意,上述解决方案假定您已经在 Api 配置部分将 Ioc 容器设置为依赖项解析器,因此如果您使用的是 Autofac Ioc,则如下所示:
_container = builder.Build();
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(_container);
希望对您有所帮助。