【问题标题】:Entity Framework 6 - ASP.NET Filter - Concurrent AddsEntity Framework 6 - ASP.NET 过滤器 - 并发添加
【发布时间】:2015-03-20 15:20:24
【问题描述】:

我使用实体框架设置了 Web Api 2。

我已经创建了一个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();
        }
    }
}

LogEntry 类 ...

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; }
}

【问题讨论】:

    标签: c# asp.net entity-framework entity-framework-6 asp.net-web-api2


    【解决方案1】:

    ASP.NET 中的过滤器被创建一次并被缓存,这就是为什么您不能通过过滤器的构造函数注入依赖项的原因,因为它们将被解析/注入一次,所以在您的情况下,如果您将上下文声明为私有变量,它将是一个单例对象,这就是为什么你会遇到并发问题,因为你使用相同的上下文和相同的 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);
    

    希望这会有所帮助。

    【讨论】:

    • 任何没有 IOC 的想法?
    • 谢谢,移动 'AppContext db = new AppContext();'在方法内部修复了它。过滤器的缓存是个问题。
    • 那是因为每次执行过滤器都会创建一个新的实例,如果你对接口工作,那么你将需要GetDependencyScope的方式。
    猜你喜欢
    • 1970-01-01
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多