Better support for POST and PUT requests.
See original GitHub issueShopifySharp runs into an issue that many C# libs find themselves facing: non-nullable types and POST/PUT requests don’t mesh that well. For example, take the following class:
public class MyObject
{
public int Id { get; set; }
public DateTime Created { get; set; }
public double Price { get; set; }
public bool Active { get; set; }
}
Suppose that we need to create a “MyObject” through the Shopify API, but the POST option expects just the Price
property. We create a new “MyObject” like so:
var myObj = new MyObject() { Price = 123.00 };
And we send that object to our pretend API. However, because of the way C# works with non-nullable types, C# will send the following:
{ "id": 0, "created": "0001-01-01T00:00:00", "price": 123.00, "active": false }
That’s not good. The API only wanted the price, but C# sent an empty Id and Created date, and a false Active flag too. This isn’t a huge problem when using POST endpoints because Shopify will typically just ignore the extra stuff it isn’t looking for.
The big problem is working with updates to objects through PUT requests. Suppose you now want to update just the Active property for an object that was already created. You create your object like so:
var myUpdatedObj = new MyObject() { Active = true };
But when you send it, C# serializes it into this:
{ "id": 0, "created": "0001-01-01T00:00:00", "price": 0, "active": true }
Now that’s a big problem. Active got set to true
, but the object’s price was just set to 0 too. You probably didn’t want to do that, but thanks to C#'s non-nullable types it happened anyway without any input from you. The only way to avoid doing this is to pull the object from the API first, then make changes to that object and send the whole object through the update.
That’s far from ideal; now updating an object goes from one request to two, and wastes bandwidth too. For ShopifySharp 3.0, I’m looking into ways to prevent sending unnecessary information through POST and PUT requests. I have two solutions right now, but welcome any suggestions:
- Make all non-string properties nullable. Drawback is that you now have to check if a prop has a value when operating on an object (e.g.
Created.Value.ToShortDateString()
instead ofCreated.ToShortDateString()
). - Create separate classes for creating and updating an object, akin to the way Stripe.net handles create/update requests. Drawback is a lot of duplicated code, and making changes to one object will need to be reflected to its create/update objects too.
Issue Analytics
- State:
- Created 7 years ago
- Comments:13 (7 by maintainers)
Thanks for the suggestion @vinhch. Right now we’re considering doing exactly what you’re suggesting as one of the solutions to this problem.
Regarding the rate limit, I don’t have any plans to add auto-retry to the lib as I believe that’s better handled by the developer for their specific use-case. You should be able to quickly implement an auto-retry yourself right now by wrapping your API call in a try/catch and then catch the exception when the HttpStatusCode is 429 (too many requests):
I’ve got plans to throw a specific rate limit exception in a future release, but for now the above code is what I use in my own apps. I’d also like to somehow expose the current rate limit after every request, but don’t have any ideas as to what that might look like right now.
One idea comes to mind. For each entity type (Customer, Product, etc…), create a corresponding enum annotated with the flags attribute that enumerates all fields of the entity.
For example
Then change each property setter to keep track of what fields were updated (a private property of type CustomerFields would exist on each Customer). Whenever updating the entity, the code would serialize only the fields that were set…
Another idea would be to keep a copy of each loaded entity and then compare it to the entity being posted to find out what changed…
This is similar to how Entity Framework and other ORMs work…