以下代码可以使用
[Route("case-studies/{slug}")]
public async Task<ActionResult> Details(string slug)
{
var item = await Db.Pages.OfType<CaseStudy>()
.WithSlug(slug)
.FirstOrDefaultAsync();
if (item == null)
{
return HttpNotFound();
}
var related = await Db.Pages.OfType<CaseStudy>()
.Where(r => r.Client == item.Client && r.Id != item.Id)
.Where(r => !r.IsArchived)
.Include(r => r.Media)
.Take(3)
.Project()
.To<RelatedPageModel>()
.ToListAsync();
var archived = await Db.Pages.OfType<CaseStudy>()
.Where(r => r.Client == item.Client && r.Id != item.Id)
.Where(r => r.IsArchived)
.Take(3)
.Project()
.To<RelatedPageModel>()
.ToListAsync();
ViewData.Model = new DetailPageModel<CaseStudy>()
{
Item = item,
RelatedItems = related,
ArchivedItems = archived
};
return View();
}
但是,当我尝试重构异步方法时,调用如下
[Route("case-studies/{slug}")]
public async Task<ActionResult> Details(string slug)
{
var item = await Db.Pages.OfType<CaseStudy>()
.WithSlug(slug)
.FirstOrDefaultAsync();
if (item == null)
{
return HttpNotFound();
}
var related = await GetRelatedCaseStudies(item, false);
var archived = await GetRelatedCaseStudies(item, true);
ViewData.Model = new DetailPageModel<CaseStudy>()
{
Item = item,
RelatedItems = related,
ArchivedItems = archived
};
return View();
}
private Task<List<RelatedPageModel>> GetRelatedCaseStudies(CaseStudy casestudy, bool archived)
{
return Db.Pages.OfType<CaseStudy>()
.Where(r => r.Client == casestudy.Client && r.Id != casestudy.Id)
.Where(x => x.IsArchived == archived)
.Include(r => r.Media)
.Take(3)
.Project().To<RelatedPageModel>()
.ToListAsync();
}
它没有给我以下错误
第二个操作在此前一个上下文之前开始 异步操作完成。使用'await'确保任何 在调用另一个方法之前已完成异步操作 在这种背景下。任何实例成员都不能保证是线程 安全
这是为什么?我怎样才能做到这一点?
更新
Db在基本控制器中声明如下
private WebSiteDb db;
protected WebSiteDb Db
{
get
{
LazyInitializer.EnsureInitialized(ref db, () => new WebSiteDb());
return db;
}
}
WebSiteDb扩展DbContext,如下所示
[DbConfigurationType(typeof(DbConfig))]
public class WebSiteDb : DbContext
{
static WebSiteDb() {
Database.SetInitializer<WebSiteDb>(new WebSiteDbInitializer());
}
public IDbSet<Page> Pages { get; set; }
public IDbSet<Media> Media { get; set; }
...some missing sets
public WebSiteDb() : base("MyDatabaseName") { }
}
如果我在方法内部等待,则从方法内部抛出错误
WithSlug()如下
public static IQueryable<T> WithSlug<T>(this IQueryable<T> pages, string slug) where T : Page
{
return pages.Where(p => p.Slug == slug);
}
答案 0 :(得分:2)
使用最新的EF 6.1.0 Beta尝试使用您的代码。目前的EF6 definition of thread safety有点模糊:
线程安全
虽然线程安全会使异步更有用,但它是正交的 特征。目前尚不清楚我们是否可以实现对它的支持 考虑到EF与组成的图形交互,最常见的情况 用户代码维护状态并没有简单的方法来确保 这段代码也是线程安全的。
目前,EF将检测开发人员是否尝试执行 一次两个异步操作并抛出。
您的代码看起来不会同时执行两个以上的异步操作 ,但在ASP.NET中,线程切换可能并且确实在await
个延续之间进行。理论上,EF6仍然支持这种情况。但是,由于ASP.NET中缺少线程关联性而导致消除EF6错误的可能性,您可以尝试the related question中的ThreadWithAffinityContext
,如下所示:
public async Task<ActionResult> Details(string slug)
{
Func<Task<ActionResult>> doAsync = async () =>
{
var item = await Db.Pages.OfType<CaseStudy>()
.WithSlug(slug)
.FirstOrDefaultAsync();
if (item == null)
{
return HttpNotFound();
}
var related = await Db.Pages.OfType<CaseStudy>()
.Where(r => r.Client == item.Client && r.Id != item.Id)
.Where(r => !r.IsArchived)
.Include(r => r.Media)
.Take(3)
.Project()
.To<RelatedPageModel>()
.ToListAsync();
var archived = await Db.Pages.OfType<CaseStudy>()
.Where(r => r.Client == item.Client && r.Id != item.Id)
.Where(r => r.IsArchived)
.Take(3)
.Project()
.To<RelatedPageModel>()
.ToListAsync();
ViewData.Model = new DetailPageModel<CaseStudy>()
{
Item = item,
RelatedItems = related,
ArchivedItems = archived
};
return View();
};
using (var staThread = new Noseratio.ThreadAffinity.ThreadWithAffinityContext(
staThread: false, pumpMessages: false))
{
return await staThread.Run(() => doAsync(), CancellationToken.None);
}
}
请注意,这是不是生产解决方案,但它可能有助于发现EF6中的错误。 如果存在错误,您可以考虑使用另一个帮助程序类ThreadAffinityTaskScheduler
,直到将来的EF版本中修复了该错误。 ThreadAffinityTaskScheduler
运行ThreadWithAffinityContext
个线程池,因此应该比上面的代码更好地扩展。 linked question包含一个使用示例。
答案 1 :(得分:0)
以下测试代码工作得很好。显示.WithSlug( slug )
class Program
{
static void Main( string[] args )
{
using( var db = new TestEntities() )
{
db.EntityBs.Add( new EntityB()
{
EntityBId = 78
} );
db.SaveChanges();
Task.WaitAll( Test( db ) );
}
var input = Console.ReadLine();
}
static Task<List<EntityB>> GetEntityBsAsync( TestEntities db )
{
return db.EntityBs.ToListAsync();
}
static async Task Test( TestEntities db )
{
var a0 = await GetEntityBsAsync( db );
var a1 = await GetEntityBsAsync( db );
}
}
答案 2 :(得分:-2)
你错过了函数中的async await,它应该是:
private async Task<List<RelatedPageModel>> GetRelatedCaseStudies(CaseStudy casestudy, bool archived)
{
return await Db.Pages.OfType<CaseStudy>()
.Where(r => r.Client == casestudy.Client && r.Id != casestudy.Id)
.Where(x => x.IsArchived == archived)
.Include(r => r.Media)
.Take(3)
.Project().To<RelatedPageModel>()
.ToListAsync();
}
如果没有它们,它将同时在单独的线程中运行两个调用,同时导致两次到达上下文。