2

Below I have pasted some code. On the first line of the body I call an awaitable call Task<IEnumerable<SkuGcn>> GetSkuGcnList(); On the last line, in the mapPromise.GetAwaiter() it throws when getting the result with this exception:

An exception of type 'System.IO.FileNotFoundException' occurred in System.Private.CoreLib.ni.dll but was not handled in user code

Additional information: Could not load file or assembly 'System.Runtime.Serialization.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.

I know my service that the GetSkuGcnList() call is calling to, is being called, because I can set a break point and I break at it when the call is made. Then the service seems to return.

This code worked just fine with ASP.NET 5 RC1 but now not so good in ASP.Net Core 1.0 Release.

Any help will be greatly appreciated!

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

My project.json looks like this:

{
  "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"
      ]
    }
  }
}

The implementation of GetSkuGcnList() is this:

    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;
    }
Irv Lennert
  • 555
  • 5
  • 20
  • `mapPromise.GetAwaiter().GetResult()` is very odd. Can you not make `ProductItemsCacheStore` async or make `GetSkuGcnList` synchronous and not return a task? Mixing sync and async always leads to problems – Scott Chamberlain Jul 11 '16 at 19:12
  • I suppose problem is not in code but in project.json configuration. nugets references and runtimes configurations from project.json might be useful to find a root of problem. Can you update post with them? – Chizh Jul 11 '16 at 19:49
  • I'm having a hard time determining if I'm using the right WebApi client assembly but I'm getting no reference errors.... – Irv Lennert Jul 11 '16 at 20:49
  • 1
    Mixing Async (TAP) and non-async especially in web based projects can lead to trouble shooting hell. It is strongly advised to go Async all the way or non at all. I have run into similar Issues and have since (reluctantly) follow @Stephen Cleary recommendations: Asynch All the Way : https://msdn.microsoft.com/en-us/magazine/jj991977.aspx – DaniDev Jul 11 '16 at 20:50
  • But this is just overlapped IO, right? This way I'm able to make my call then go do things here then get my results. Isn't this the way I should do it? – Irv Lennert Jul 11 '16 at 21:11
  • In this particular instance it seems (to me) pretty straight forward. You are relaying on mapPromise, but you are not waiting for it. It probably has not returned. I am guessing you probably got a compiler warning(?) Try making GetSkuGcnList awaitable. I would post an answer but I cant guarantee it will work and also some async Troll will find something negative to say about it. Let me know if you need help making it await able and I'll post the code. – DaniDev Jul 11 '16 at 21:55

2 Answers2

4

I have determined that the assembly that I am calling on is not working correctly for ASP.Net Core release. If you look in the above project.json it is this line:

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

I have changed that line to this line:

"System.Net.Http": "4.1.0" 

This assembly does not expose the same API so I had to change my implementation code to:

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

This solution is working well. It is important to note I was also having the same problem with post and they are a little more complicated so I'll provide another call I make that is a post for anyone interested.

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

Thanks to all that contributed!

Irv Lennert
  • 555
  • 5
  • 20
1

As @DaniDev mentioned in the comments, mixing synchronous and asynchronous code is not recommended. The canonical article on this topic is Stephen Cleary's Don't Block on Async Code.

It's a little tricky to make your code async, because you are calling async operations in the constructor and constructors can't be async.

One way to restructure your code would be to split up the constructor (dependency injection) and the initialization (async calls) code:

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

Your consuming code (wherever you were building a new ProductItemsCacheStore(...) before) now looks like:

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

Of course, this means that your consuming code also has to be async. It's async all the way up.

Community
  • 1
  • 1
Nate Barbettini
  • 51,256
  • 26
  • 134
  • 147
  • I like it but who calls `Initialize()` – Irv Lennert Jul 12 '16 at 14:56
  • @IrvLennert The code that is consuming `ProductItemsCacheStore`. Updated my answer with an additional code snippet. – Nate Barbettini Jul 12 '16 at 15:05
  • Ok, Nate, here's what I think I'm doing. On line 1) I returning a Task> (no blocking) on lines 2) 3) 4) stuff I need to do that I will need the answer from 1) to merge in then, on line 5) I am blocking till I have my answer. If that is not your interpretation please correct me. Thanks... – Irv Lennert Jul 12 '16 at 15:55
  • Actually the point to my code is I know I'm going to be taking time doing steps 2),3) and 4) this way I take less time because I'm able to start my request of the `GetSkuGcnList()` info and do the work I need to do at the same time. If that's not what asnc await are for it's a waste of time.... – Irv Lennert Jul 12 '16 at 16:02
  • 1
    Also I just want to re-iterate, this code worked just fine before I updated to ASP.Net Core 1.0 release. Thanks! – Irv Lennert Jul 12 '16 at 16:29