【问题标题】:Async GetAwaiter() throwing an odd exception异步 GetAwaiter() 抛出一个奇怪的异常
【发布时间】:2016-07-11 19:07:22
【问题描述】:

下面我粘贴了一些代码。在正文的第一行,我调用了一个等待调用 Task<IEnumerable<SkuGcn>> GetSkuGcnList(); 在最后一行,在mapPromise.GetAwaiter() 中,它在获取结果时抛出此异常:

System.Private.CoreLib.ni.dll 中出现“System.IO.FileNotFoundException”类型的异常,但未在用户代码中处理

附加信息:无法加载文件或程序集“System.Runtime.Serialization.Xml,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a”。系统找不到指定的文件。

我知道GetSkuGcnList() 调用正在调用的服务正在被调用,因为我可以设置一个断点并在调用时中断它。然后服务似乎恢复了。

此代码在 ASP.NET 5 RC1 中运行良好,但现在在 ASP.Net Core 1.0 版本中效果不佳。

任何帮助将不胜感激!

        public ProductItemsCacheStore(IItemsProductCacheRepository itemsRepo, IProductAvailabilityPricing proxyGoliathApi,
        IProductItemListRepo itemsListsRepo, IItemPricesCacheRepo itemPricesRepo)
        {
            var mapPromise = proxyGoliathApi.GetSkuGcnList();
            _items = itemsRepo.GetAllProductOrderListForCache();
            _itemsLists = itemsListsRepo.GetItemListsForCache();
            _priceZones = itemPricesRepo.GetAllPriceZonesAndItemPrices();
            MergeProductsWithGcn(_items, mapPromise.GetAwaiter().GetResult());
        }

我的 project.json 看起来像这样:

{
  "version": "1.0.0-alpha.1",
  "description": "AvailabilityPricingClient Class Library",
  "authors": [ "irving.lennert" ],
  "packOptions": {
    "tags": [ "" ],
    "projectUrl": "",
    "licenseUrl": ""
  },

  "tooling": {
    "defaultNamespace": "AvailabilityPricingClient"
  },

  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.0",
      "type": "platform"
    },
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
    "Microsoft.Extensions.Configuration.Json": "1.0.0",
    "Microsoft.Extensions.Logging": "1.0.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0",
    "Microsoft.Extensions.Logging.Debug": "1.0.0",
    "Microsoft.AspNet.WebApi.Client": "5.2.3"
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dotnet5.6",
        "portable-net45+win8"
      ]
    }
  }
}

GetSkuGcnList()的实现是这样的:

    public async Task<IEnumerable<SkuGcn>> GetSkuGcnList()
    {
        HttpResponseMessage response = _client.GetAsync("/api/skuGcnList").Result;

        if (response.IsSuccessStatusCode)
        {
            var skuGcns = await response.Content.ReadAsAsync<IEnumerable<SkuGcn>>();
            return skuGcns;
        }

        return null;
    }

【问题讨论】:

  • mapPromise.GetAwaiter().GetResult() 很奇怪。你能不让ProductItemsCacheStore异步或让GetSkuGcnList同步而不返回任务吗?混合同步和异步总是会导致问题
  • 我想问题不在于代码,而在于 project.json 配置。来自 project.json 的 nugets 引用和运行时配置可能有助于找到问题的根源。你能和他们一起更新帖子吗?
  • 我很难确定我是否使用了正确的 WebApi 客户端程序集,但我没有收到任何参考错误......
  • 混合使用异步 (TAP) 和非异步,尤其是在基于 Web 的项目中可能会导致故障排除。强烈建议一路异步或完全不异步。我遇到了类似的问题,并且(不情愿地)遵循@Stephen Cleary 的建议:Asynch All the Way :msdn.microsoft.com/en-us/magazine/jj991977.aspx
  • 但这只是重叠的IO,对吧?这样我就可以打电话然后去这里做事然后得到我的结果。这不是我应该做的吗?

标签: c# asp.net asp.net-core .net-core asp.net-core-1.0


【解决方案1】:

我已确定我调用的程序集在 ASP.Net Core 版本中无法正常工作。如果你查看上面的 project.json 就是这一行:

"Microsoft.AspNet.WebApi.Client": "5.2.3"

我已经把那行改成了这一行:

"System.Net.Http": "4.1.0" 

这个程序集没有公开相同的 API,所以我不得不将我的实现代码更改为:

    public async Task<IEnumerable<SkuGcn>> GetSkuGcnList()
    {
        HttpResponseMessage response = _client.GetAsync("/api/skuGcnList").Result;

        if (response.IsSuccessStatusCode)
        {
            var skuGcns = await response.Content.ReadAsStringAsync()
                .ContinueWith<IEnumerable<SkuGcn>>(getTask =>
                {
                    return JsonConvert.DeserializeObject<IEnumerable<SkuGcn>>(getTask.Result);
                });
            return skuGcns;
        }

        return null;
    }

此解决方案运行良好。需要注意的是,我在发布时也遇到了同样的问题,而且它们有点复杂,所以我会提供另一个我拨打的电话,这是给任何感兴趣的人的帖子。

    public async Task<IEnumerable<Availablity>> GetAvailabilityBySkuList(IEnumerable<string> skuList)
    {
        var output = JsonConvert.SerializeObject(skuList);
        HttpContent contentPost = new StringContent(output, System.Text.Encoding.UTF8, "application/json");
        HttpResponseMessage response = _client.PostAsync("/api/availabilityBySkuList", contentPost).Result;

        if (response.IsSuccessStatusCode)
        {
            var avail = await response.Content.ReadAsStringAsync()
                .ContinueWith<IEnumerable<Availablity>>(postTask =>
                {
                    return JsonConvert.DeserializeObject<IEnumerable<Availablity>>(postTask.Result);
                });
            return avail;
        }

        return null;
    }

感谢所有贡献者!

【讨论】:

    【解决方案2】:

    正如@DaniDev 在 cmets 中提到的,不建议混合同步和异步代码。有关该主题的规范文章是 Stephen Cleary 的 Don't Block on Async Code

    让你的代码异步有点棘手,因为你在构造函数和constructors can't be async中调用异步操作。

    重构代码的一种方法是将构造函数(依赖注入)和初始化(异步调用)代码分开:

    public class ProductItemsCacheStore
    {
        private readonly IItemsProductCacheRepository _itemsProductCacheRepo;
        private readonly IProductAvailabilityPricing _productAvailabilityPricing;
        private readonly IProductItemListRepo _productItemListRepo;
        private readonly IItemsPricesCacheRepo _itemsPricesCacheRepo;
    
        private bool _initialized = false;
    
        public ProductItemsCacheStore(
            IItemsProductCacheRepository itemsRepo,
            IProductAvailabilityPricing proxyGoliathApi,
            IProductItemListRepo itemsListsRepo,
            IItemPricesCacheRepo itemPricesRepo)
        {
            _itemsProductCacheRepo = itemsRepo;
            _productAvailabilityPricing = proxyGoliathApi;
            _productItemListRepo = itemsListsRepo;
            _itemsPricesCacheRepo = itemsPricesRepo
        }
    
        public async Task Initialize()
        {
            // Could possibly move these to the constructor as well
            var _items = itemsRepo.GetAllProductOrderListForCache();
            var _itemsLists = itemsListsRepo.GetItemListsForCache();
            var _priceZones = itemPricesRepo.GetAllPriceZonesAndItemPrices();
    
            MergeProductsWithGcn(_items, await _productAvailabilityPricing.GetSkuGcnList());
    
            _initialized = true;
        }
    }
    

    您的消费代码(无论您之前在哪里构建 new ProductItemsCacheStore(...))现在看起来像:

    var productItemsCacheStore = new ProductItemsCacheStore(/* dependencies */);
    await productItemsCacheStore.Initialize();
    

    当然,这意味着您的消费代码也必须是异步的。我是async all the way up

    【讨论】:

    • 我喜欢但谁叫Initialize()
    • @IrvLennert 使用 ProductItemsCacheStore 的代码。用附加代码 sn-p 更新了我的答案。
    • 好的,内特,这就是我想我正在做的事情。在第 1 行)我在第 2 行返回 Task>(无阻塞) 3)4)我需要做的事情我需要 1)的答案然后在第 5 行合并)我我一直在阻止,直到我得到我的答案。如果这不是你的解释,请纠正我。谢谢...
    • 实际上我的代码的重点是我知道我将花时间执行步骤 2)、3) 和 4) 这样我花费的时间更少,因为我能够开始我的请求GetSkuGcnList() 的信息,同时做我需要做的工作。如果这不是 asnc await 的意思,那就是浪费时间......
    • 另外我只是想重新迭代一下,这段代码在我更新到 ASP.Net Core 1.0 版本之前运行良好。谢谢!
    猜你喜欢
    • 2020-02-24
    • 2010-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-09
    • 1970-01-01
    • 2011-04-11
    相关资源
    最近更新 更多