nozzlegear / ShopifySharp

ShopifySharp is a .NET library that helps developers easily authenticate with and manage Shopify stores.
https://nozzlegear.com/shopify-development-handbook
MIT License
756 stars 313 forks source link

Api call, never returns #816

Open mikesmithfreels opened 1 year ago

mikesmithfreels commented 1 year ago

Hello Team, I am trying to call api but response never came back to function.

Here is my code, Can you please help me on this? Here our function in sync/non async.

I checked my shop url is correct

var service = new ProductService(myShopUrl, "****"); var product = service.ListAsync().Result;

or

var product = service.ListAsync().GetAwaiter().GetResult();

Environment:

SDK : Microsoft Visual Studio Community 2019 Version 16.10.3

Framework : 4.7.1/4.7.2

tdanti commented 1 year ago

Same problem. I have a Shopify CUSTOM APP and use the ADMIN TOKEN.

My code:

ProductService ps = new ProductService("MYSHOP.myshopify.com", "shpat_0000000MYADMINTOKEN0000000000000"); int p = ps.CountAsync().Result;

Debug: In method ExecuteRequestAsync the value of baseRequestMessage is {Method: GET, RequestUri: 'https://MYSHOP.myshopify.com/admin/api/2022-04/products/count.json', Version: 1.1, Content: , Headers:{ X-Shopify-Access-Token: shpat_0000000MYADMINTOKEN0000000000000 Accept: application/json}} The "using (var baseRequestMessage" NEVER ENDS.

I checked these parameters by setting them to Postman like this: GET https://MYSHOP.myshopify.com/admin/api/2022-04/products/count.json Authorizathion > Apikey "X-Shopify-Access-Token" "shpat_0000000MYADMINTOKEN0000000000000" On Postman it works.

Help Please!

nozzlegear commented 1 year ago

Hey! I think the issue with both of your code examples here is that you're forcing asynchronous code into a synchronous context by using .Result, which can cause deadlocks and would lead to the code never returning like you're experiencing. If it's possible, you want to wrap your calls in async methods and use await on all async ShopifySharp methods:

async Task MyMethodAsync()
{
    var service = new ProductService(myShopUrl, "****");
    var products = await service.ListAsync();
}

Basically, you never want to use .Result or .GetAwaiter().GetResult() if you can avoid it. It's very dangerous and leads to issues like the ones you're finding now. Here's a good article explaining why it causes deadlocks.

I know that saying "just use async" isn't as easy as it sounds if you've got a legacy project that doesn't support it, but unfortunately all of ShopifySharp's methods are asynchronous due to the HttpClient it uses under the hood.

mikesmithfreels commented 1 year ago

Hi @nozzlegear , I totally agree with you but what you said is that we have our legacy app. So in any way we have to use Result or GetAwaiter and GetResult combination.

The strange thing is actually one of my collogue Chris, created new console app that has 4.7.2 framework and it worked totally fine with sync method. Our legacy app has version of 4.7.1 and we tried to migrate that to 4.7.2 but with both responses are same from ShopifySharp, or I would say no response.

So @tdanti in our case we migrated 4.7.1 to 4.7.2. But our unit test console app for testing has 4.7.2 and it worked fine. So I believe in our existing code some nuget/dll conflicting or blocking call.

@nozzlegear actually we are not able to get event error from that call, so we can not even debug that what's happening over there, is there any way to check logs/errors?

tdanti commented 1 year ago

Hey! I think the issue with both of your code examples here is that you're forcing asynchronous code into a synchronous context by using .Result, which can cause deadlocks and would lead to the code never returning like you're experiencing. If it's possible, you want to wrap your calls in async methods and use await on all async ShopifySharp methods:

This works. Thank you.

EDIT: A code example of usage in a WindowsForm application. This works.

` public partial class Form1 : Form { string url = "https://MYSHOP.myshopify.com/"; string accesstoken = "MYADMINTOKEN";

    List<Product> products = null;

    public Form1()
    {
        InitializeComponent();
        GetProductsAsync();
    }

    async Task GetProductsAsync()
    {
        //new "products"
        products = new List<Product>();
        //
        //get async data as ListResult
        var service = new ProductService(url, accesstoken);
        ListResult<Product> ps = await service.ListAsync();
        //
        if (ps != null)
        {
            foreach(Product p in ps.Items)
            {
                products.Add(p);
            }
        }
        //call end to do something
        OnEndGetProductsAsync();
    }

    public void OnEndGetProductsAsync()
    {
        //do something with "products"
        if (products != null)
        {
            foreach (Product p in products)
            {
                //example: write products list in textbox
                TBox_Log.Text += p.Id + " " + p.Title + Environment.NewLine;
            }
        }
    }
}

`