免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
ASP.NET Core ActionFilter引發(fā)的一個(gè)EF異常

最近在使用ASP.NET Core的時(shí)候出現(xiàn)了一個(gè)奇怪的問題。在一個(gè)Controller上使用了一個(gè)ActionFilter之后經(jīng)常出現(xiàn)EF報(bào)錯(cuò)。

InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()

這個(gè)異常說Context在完成前一個(gè)操作的時(shí)候第二個(gè)操作依據(jù)開始。這個(gè)錯(cuò)誤還不是每次都會(huì)出現(xiàn),只有在并發(fā)強(qiáng)的時(shí)候出現(xiàn),基本可以判斷跟多線程有關(guān)系??匆幌麓a:

   public static class ServiceCollectionExt    {        public static void AddAgileConfigDb(this IServiceCollection sc)        {            sc.AddScoped<ISqlContext, AgileConfigDbContext>();        }    }
  [TypeFilter(typeof(BasicAuthenticationAttribute))]    [Route("api/[controller]")]    public class ConfigController : Controller    {        private readonly IConfigService _configService;        private readonly ILogger _logger;        public ConfigController(IConfigService configService, ILoggerFactory loggerFactory)        {            _configService = configService;            _logger = loggerFactory.CreateLogger<ConfigController>();        }        // GET: api/<controller>        [HttpGet("app/{appId}")]        public async Task<List<ConfigVM>> Get(string appId)        {            var configs = await _configService.GetByAppId(appId);            var vms = configs.Select(c => {                return new ConfigVM() {                    Id = c.Id,                    AppId = c.AppId,                    Group = c.Group,                    Key = c.Key,                    Value = c.Value,                    Status = c.Status                };            });            _logger.LogTrace($"get app {appId} configs .");            return vms.ToList();        }           }

代碼非常簡(jiǎn)單,DbContext使用Scope生命周期;Controller里只有一個(gè)Action,里面只有一個(gè)訪問數(shù)據(jù)庫的地方。怎么會(huì)造成多線程訪問Context的錯(cuò)誤的呢?于是把目光移到BasicAuthenticationAttribute這個(gè)Attribute。

 public class BasicAuthenticationAttribute : ActionFilterAttribute    {        private readonly IAppService _appService;        public BasicAuthenticationAttribute(IAppService appService)        {            _appService = appService;        }        public async override void OnActionExecuting(ActionExecutingContext context)        {            if (!await Valid(context.HttpContext.Request))            {                context.HttpContext.Response.StatusCode = 403;                context.Result = new ContentResult();            }        }        public async Task<bool> Valid(HttpRequest httpRequest)        {            var appid = httpRequest.Headers["appid"];            if (string.IsNullOrEmpty(appid))            {                return false;            }            var app = await _appService.GetAsync(appid);            if (app == null)            {                return false;            }            if (string.IsNullOrEmpty(app.Secret))            {                //如果沒有設(shè)置secret則直接通過                return true;            }            var authorization = httpRequest.Headers["Authorization"];            if (string.IsNullOrEmpty(authorization))            {                return false;            }            if (!app.Enabled)            {                return false;            }            var sec = app.Secret;            var txt = $"{appid}:{sec}";            var data = Encoding.UTF8.GetBytes(txt);            var auth = "Basic " + Convert.ToBase64String(data);            return auth == authorization;        }    }

BasicAuthenticationAttribute的代碼也很簡(jiǎn)單,Attribute注入了一個(gè)Service并且重寫了OnActionExecuting方法,在方法里對(duì)Http請(qǐng)求進(jìn)行Basic認(rèn)證。這里也出現(xiàn)了一次數(shù)據(jù)查詢,但是已經(jīng)都加上了await。咋一看好像沒什么問題,一個(gè)Http請(qǐng)求進(jìn)來的時(shí)候,首先會(huì)進(jìn)入這個(gè)Filter對(duì)其進(jìn)行Basic認(rèn)證,如果失敗返回403碼,如果成功則進(jìn)入真正的Action方法繼續(xù)執(zhí)行。如果是這樣的邏輯,不可能出現(xiàn)兩次EF的操作同時(shí)執(zhí)行。繼續(xù)查找問題,點(diǎn)開ActionFilterAttribute的元數(shù)據(jù):

    public abstract class ActionFilterAttribute : Attribute, IActionFilter, IFilterMetadata, IAsyncActionFilter, IAsyncResultFilter, IOrderedFilter, IResultFilter    {        protected ActionFilterAttribute();        //        public int Order { get; set; }        //        public virtual void OnActionExecuted(ActionExecutedContext context);        //        public virtual void OnActionExecuting(ActionExecutingContext context);        //        [DebuggerStepThrough]        public virtual Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next);        //        public virtual void OnResultExecuted(ResultExecutedContext context);        //        public virtual void OnResultExecuting(ResultExecutingContext context);        //        [DebuggerStepThrough]        public virtual Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next);    }

這玩意這么看著跟以前有點(diǎn)不一樣啊,除了原來的4個(gè)方法,多了2個(gè)Async結(jié)尾的方法。到了這里其實(shí)心里已經(jīng)有數(shù)了。這里應(yīng)該重寫OnResultExecutionAsync,因?yàn)槲覀兊腁ction方法是個(gè)異步方法。改一下BasicAuthenticationAttribute,重寫OnResultExecutionAsync方法:

public class BasicAuthenticationAttribute : ActionFilterAttribute    {        private readonly IAppService _appService;        public BasicAuthenticationAttribute(IAppService appService)        {            _appService = appService;        }        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)        {            if (!await Valid(context.HttpContext.Request))            {                context.HttpContext.Response.StatusCode = 403;                context.Result = new ContentResult();            }            await base.OnActionExecutionAsync(context, next);        }        public async Task<bool> Valid(HttpRequest httpRequest)        {            var appid = httpRequest.Headers["appid"];            if (string.IsNullOrEmpty(appid))            {                return false;            }            var app = await _appService.GetAsync(appid);            if (app == null)            {                return false;            }            if (string.IsNullOrEmpty(app.Secret))            {                //如果沒有設(shè)置secret則直接通過                return true;            }            var authorization = httpRequest.Headers["Authorization"];            if (string.IsNullOrEmpty(authorization))            {                return false;            }            if (!app.Enabled)            {                return false;            }            var sec = app.Secret;            var txt = $"{appid}:{sec}";            var data = Encoding.UTF8.GetBytes(txt);            var auth = "Basic " + Convert.ToBase64String(data);            return auth == authorization;        }    }

修改完后經(jīng)過并發(fā)測(cè)試,EF報(bào)錯(cuò)的問題得到了解決。
再來解釋下這個(gè)問題是如何造成的:一開始BasicAuthenticationAttribute是framework版本的ASP.NET MVC遷移過來的,按照慣例重寫了OnActionExecuting。其中注入的service里面的方法是異步的,盡管標(biāo)記了await,但是這并沒有什么卵用,因?yàn)榭蚣茉谡{(diào)用OnActionExecuting的時(shí)候并不會(huì)在前面加上await來等待這個(gè)方法。于是一個(gè)重寫了OnActionExecuting的Filter配合一個(gè)異步的Action執(zhí)行的時(shí)候并不會(huì)如預(yù)設(shè)的一樣先等待OnActionExecuting執(zhí)行完之后再執(zhí)行action。如果OnActionExecuting里出現(xiàn)異步方法,那這個(gè)異步方法很可能跟Action里的異步方法同時(shí)執(zhí)行,這樣在高并發(fā)的時(shí)候就出現(xiàn)EF的Context被多線程操作的異常問題。這里其實(shí)還是一個(gè)老生常談的問題,就是盡量不要在同步方法內(nèi)調(diào)用異步方法,這樣很容易出現(xiàn)多線程的問題,甚至出現(xiàn)死鎖。
ASP.NET Core已經(jīng)全面擁抱異步,與framework版本有了很大的差異還是需要多多注意??磥磉@個(gè)Core版本的ActionFilter還得仔細(xì)研究研究,于是上微軟官網(wǎng)查了查有這么一段:

Implement either the synchronous or the async version of a filter interface, not both. The runtime checks first to see if the filter implements the async interface, and if so, it calls that. If not, it calls the synchronous interface's method(s). If both asynchronous and synchronous interfaces are implemented in one class, only the async method is called. When using abstract classes like ActionFilterAttribute, override only the synchronous methods or the asynchronous method for each filter type.

就是說對(duì)于filter interface要么實(shí)現(xiàn)同步版本的方法,要么實(shí)現(xiàn)異步版本的方法,不要同時(shí)實(shí)現(xiàn)。運(yùn)行時(shí)會(huì)首先看異步版本的方法有沒有實(shí)現(xiàn),如果實(shí)現(xiàn)則調(diào)用。如果沒有則調(diào)用同步版本。如果同步版本跟異步版本的方法都同時(shí)實(shí)現(xiàn)了,則只會(huì)調(diào)用異步版本的方法。當(dāng)使用抽象類,比如ActionFilterAttribute,只需重寫同步方法或者異步方法其中一個(gè)。

參考:filters in asp.net core

關(guān)注我的公眾號(hào)一起玩轉(zhuǎn)技術(shù)

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
異步編程最佳實(shí)踐
Python Playwright API使用實(shí)例詳解
.Net6 WebApi基本權(quán)限控制
Nodejs的單線程與異步的初步理解 | Freewind.me
Ajax async(同步/異步)
delphi 播放聲音 采用 異步方式,比較流暢
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服