下面是一些使用Azure Application Insights进行分析的图片。它来自于执行那个简单的请求:实体框架6 - 阻止等待连接的时间
public HttpResponseMessage Login(LoginRequest loginRequest)
{
var t = new DependencyTracker();
using (var context = t.TrackDependency(() => new SocialDbContext(), "NewContext"))
{
var user = t.TrackDependency(() => context.Users.AsNoTracking().SingleOrDefault(x => x.Email == loginRequest.Id || x.Name == loginRequest.Id), "GetUser");
if (user == null) return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.WrongCredentials), "WrongCredentialsUser");
var sid = t.TrackDependency(() => context.IdMappers.AsNoTracking().SingleOrDefault(x => x.Provider == "custom" && x.UserId == user.Id)?.Sid, "GettingSid");
var account = t.TrackDependency(() => context.Accounts.AsNoTracking().SingleOrDefault(a => a.Id == sid), "GettingAccount");
if (account == null) return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.WrongCredentials), "WrongCredentialsAccount");
var incoming = t.TrackDependency(() => Hash(loginRequest.Password, account.Salt), "Hash");
var isLoginOk = t.TrackDependency(() => SlowEquals(incoming, account.SaltedAndHashedPassword), "SlowEquals");
if (isLoginOk && account.TempPasswordExpiry != null)
{
if (DateTimeOffset.Now.CompareTo(account.TempPasswordExpiry.Value) > 0)
return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.WrongCredentials), "WrongCredentialsPassword");
return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.TemporaryPassword), "WrongCredentialsExpiredTempPassword");
}
return isLoginOk
? t.TrackDependency(() => SendToken(user.Id, sid), "SendToken")
: t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.Unauthorized, ApiError.WrongCredentials), "WrongCredentialsPassword");
}
}
有3 SingleOrDefault
调用。在分析器结果中,我看到了这3个调用。
- 第一个就是,3325ms(3017ms等待连接)
- 第二个需要2356ms(2349ms等待连接)
- 然后最后一个需要0.28ms
连接的这些令人难以置信的漫长等待时间是什么?
是什么引起了他们,以及如何获得它?
UPDATE
我可能是错的,但我想我知道什么是延迟加载的混合物发生,从that line在this documentation的解释上Metdata锁定。
帐户将需要sid,但它锁定了元数据。所以sid等待。然后sid需要用户,但它锁定了元数据。所以用户等待。然后用户得到执行然后sid然后帐户。
虽然我会认为user == null
和.Sid
会强制执行。
有没有办法让懒加载测试我在说什么?
更新2
我只是this.Configuration.LazyLoadingEnabled = false;
试图在我的上下文构造函数,但问题仍然存在:-(
请注意,我只能重现该问题,每5分钟左右。在这之间似乎有一些缓存,因为查询立即执行。
UPDATE 3
我取代了代码使用一个查询,而不是三个:
public HttpResponseMessage Login(LoginRequest loginRequest)
{
var t = new DependencyTracker();
using (var context = t.TrackDependency(() => new SocialDbContext(), "NewContext"))
{
var query = from a in context.Users
join b in context.IdMappers on a.Id equals b.UserId
join c in context.Accounts on b.Sid equals c.Id
where b.Provider == "custom" && (a.Email == loginRequest.Id || a.Name == loginRequest.Id)
select new { Sid = c.Id, c.Salt, c.SaltedAndHashedPassword, c.TempPasswordExpiry, UserId = a.Id };
var account = t.TrackDependency(() => query.SingleOrDefault(), "GettingAccount");
if (account == null) return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.WrongCredentials), "WrongCredentialsAccount");
var incoming = t.TrackDependency(() => Hash(loginRequest.Password, account.Salt), "Hash");
var isLoginOk = t.TrackDependency(() => SlowEquals(incoming, account.SaltedAndHashedPassword), "SlowEquals");
if (isLoginOk && account.TempPasswordExpiry != null)
{
if (DateTimeOffset.Now.CompareTo(account.TempPasswordExpiry.Value) > 0)
return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.WrongCredentials), "WrongCredentialsPassword");
return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.TemporaryPassword), "WrongCredentialsExpiredTempPassword");
}
return isLoginOk
? t.TrackDependency(() => SendToken(account.UserId, account.Sid), "SendToken")
: t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.Unauthorized, ApiError.WrongCredentials), "WrongCredentialsPassword");
}
}
但是,即使现在只有一个等待的问题仍然存在。
但是,它只是每5分钟发生一次。
注意:我无法在本地重现它,它只发生在Azure上。
UPDATE 4
问题通过上下文调用存储过程时仍然连。所以我想这与查询计划无关。
我不明白的是,即使我调用存储过程与SqlCommand
问题仍未以及因此实体内部消除框架...
UPDATE 5
这是不是实体框架问题。问题在于等待sql连接。无论使用EF还是使用SqlCommand
,每5分钟都会发生相同的缓慢等待时间。我会解决这个问题,并用适当的问题开一个新问题。