当前位置: 首页 > 知识库问答 >
问题:

为什么我不能将异步查询移动到方法中?

陆建木
2023-03-14

以下代码有效

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

共有3个答案

堵德曜
2023-03-14

如果函数中缺少async Wait,则应为:

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

如果没有这些,它将在不同的线程中同时运行两个调用,导致同时对上下文进行两次点击。

齐夕
2023-03-14

下面的测试代码运行良好。显示的实现。用弹头(弹头)

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 );
    }
}
松秦斩
2023-03-14

用最新的EF 6.1.0测试版试试你的代码。当前EF6对线程安全的定义有点模糊:

线程安全性

虽然线程安全会使异步更有用,但它是一种正交特性。考虑到EF与由用户代码组成的图进行交互以维护状态,并且没有简单的方法来确保该代码也是线程安全的,因此在最常见的情况下,我们是否能够实现对它的支持尚不清楚。

目前,EF将检测开发人员是否一次尝试执行两个异步操作并抛出。

看起来您的代码不会同时执行两个以上的异步操作,但是在ASP. NET中,线程切换可能并且确实发生在wait延续之间。理论上,EF6仍然应该支持这种情况。但是,要消除由于ASP. NET中缺乏线程亲和力而导致的EF6错误的可能性,您可以从相关问题中尝试我的ThreadWitAffinityContext,如下所示:

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中的错误。如果有bug,您可以考虑使用另一个助手类ThreadAffinityTaskScheduler,直到该bug在未来的EF版本中修复为止ThreadAffinityTaskScheduler运行一个由ThreadWithAffinityContext线程组成的池,因此应比上述代码具有更好的扩展性。链接的问题包含一个使用示例。

 类似资料:
  • 问题内容: 经过一整天的研究和尝试,我终于放弃了 纯AJAX的 上传文件(ps:本文如何与JQuery异步上传文件?埋了我的最后希望) 我的问题可能没什么意义,但是我仍然想知道为什么ajax(或XMLHttpRequest)不能处理这个问题?为什么文件不能像真正的httprequest那样传输? 问题答案: 出于安全原因,JavaScript无法读取本地文件,因此我们无法使用AJAX发送数据。 但

  • 我正在运行这样一个简单的SQL连接和select,但在运行查询时,我遇到了非常奇怪的错误。 警告:mysqli_fetch_array()希望参数1是mysqli_结果,布尔值在第23行的C:\xampp\htdocs\Webmaster\run\forum_mc.php中给出 警告:mysqli_query():第24行()上C:\xampp\htdocs\Webmaster\run\forum

  • 问题内容: 为什么将AJAX称为异步?它如何完成与服务器的异步通信? 问题答案: 它是异步的,因为它不会锁定浏览器。如果您触发Ajax请求,则用户在等待响应时仍可以工作。当服务器返回响应时,将运行一个回调来处理它。 您可以根据需要进行同步,如果需要,可以在请求未完成时将浏览器锁定(因此,大多数情况下这是不合适的)

  • 所以我理解为什么从异步返回空洞通常没有意义,但我遇到了一种我认为完全有效的情况。请考虑以下人为的示例: 我意识到这是一个不寻常的例子,但我试图使其简单化和更普遍化。有人能向我解释为什么这是可怕的代码,以及我如何修改它以正确遵循约定吗? 谢谢你的任何帮助。

  • 问题内容: 我对git和詹金斯都很陌生。 我将密钥添加到bitbucket和本地计算机中时: 我可以克隆。 但是,当我将相同的url()添加到Jenkins存储库url时,出现以下错误: 问题答案: 您还需要为Jenkins用户设置ssh密钥。 通常的想法是,您登录到Jenkins框,并成为“ jenkins”用户。您可以为您的Jenkins用户打电话,所以请确保使用正确的名称。一旦成为Jenki

  • 问题内容: 以下语句: 因错误而失败: 为什么在这里需要演员表? 我发现了几篇文章,解释了为什么您不能进行反向操作(将T分配给a),但这是显而易见的(可以理解)。 注意:我在Eclipse Luna下对此进行编码,所以我不知道这是Luna Quirk还是泛型中确实不了解的东西。 问题答案: 协方差vs协方差vs不变性 是 不变的 。 结果是, 是 不是一个亚型 的 在Java中,变量可以保存 相同