PatchPrivatePreviewFeedbackGroup icon indicating copy to clipboard operation
PatchPrivatePreviewFeedbackGroup copied to clipboard

.NET Ergonomics feedback / discussion

Open onionhammer opened this issue 3 years ago • 6 comments

Just a bunch of ideas

Concrete List<> arg

PatchItemAsync takes an IReadOnlyList, which has to be created explicitly as some concrete type which results in code that looks like this:

List<PatchOperation> patchOperations = new List<PatchOperation>();
patchOperations.Add(PatchOperation.Add("/nonExistentParent/Child", "bar"));
patchOperations.Add(PatchOperation.Remove("/cost"));
patchOperations.Add(PatchOperation.Increment("/taskNum", 6));
patchOperations.Add(patchOperation.Set("/existingPath/newproperty",value));

container.PatchItemAsync<item>(
                id: 5,
                partitionKey: new PartitionKey(“pkey”),
                patchOperations: patchOperations );

If you used a concrete List<> as the input you could use C#'s target-typed new expression:

                container.PatchItemAsync<item>(
                    id: 5,
                    partitionKey: new PartitionKey(“pkey”),
                    patchOperations: new()
                    {
                        PatchOperation.Add("/nonExistentParent/Child", "bar"),
                        PatchOperation.Remove("/cost"),
                        PatchOperation.Increment("/taskNum", 6),
                        PatchOperation.Set("/existingPath/newproperty", value),
                    });

Expression to path string

Also it would be great if Patch Operations could take C# expressions for the paths to compile

patches.Add(p => p.Cost[0], 5)

to

PatchOperation.Add("/Cost[0]", 5)

Fluency

You could define the operation fluently:

var result = await Container.Patch<MyParentType>()
   .Add(p => p.Cost[0], 5)
   .Increment(p => p.Version, 1)
   .ApplyAsync(requestOptions);

Custom value serialization

I dont currently see a way to get away from the Newtonsoft serializer for the individual patch values, unless I'm missing something; the PatchOperations themselves do accept overloads a stream but combining many streams into a single operation may be less efficient than serializing an entire patch operation chain at once.

onionhammer avatar May 28 '21 14:05 onionhammer

Regarding custom value serialization ask, we did address it recently. If the value you pass for the individual PatchOperation is of type Stream, then we won't serialize it.

You can see example.

anujtoshniwal avatar May 31 '21 11:05 anujtoshniwal

@anujtoshniwal thanks, I'll test that again but I created the issue after I got an exception trying to use a stream.

onionhammer avatar May 31 '21 14:05 onionhammer

@onionhammer which version of SDK are you using? Try with 3.0.19-preview1 (link)

anujtoshniwal avatar May 31 '21 14:05 anujtoshniwal

@anujtoshniwal I will get back to you on that.

However I think it would probably be more efficient to not have to pass 6 streams for 6 patches in 1 payload

onionhammer avatar May 31 '21 18:05 onionhammer

Okay, tested with '3.19.0-preview1' and it does work to send streams, although its not mentioned in the root readme anywhere. Updating the top comment

onionhammer avatar Jun 01 '21 13:06 onionhammer

+1 on the Concrete List<> arg and the Expression to path string and the Fluency.

that would make for MUCH neater code.

            IReadOnlyList<PatchOperation> patchOperations = new[] { PatchOperation.Replace("/ShippedDate", DateTime.UtcNow) };
            using (Stream stream = Program.ToStream<IReadOnlyList<PatchOperation>>(patchOperations))
            {
                using (ResponseMessage responseMessage = await container.PatchItemStreamAsync(
                    id: order.Id,
                    partitionKey: new PartitionKey(order.AccountNumber),
                    patchOperations: patchOperations))
                {

the use here of patchOperations is confusing. you pass patchOperations in to ToStream which then gets passed in to the Stream that you're using and then you have patchOperations again in the PathItemStreamAsync method.

this is confusing, why do i need it twice? Should PatchItemStreamAsync just take a stream of List<PatchOperation>?

ryancrawcour avatar Jun 25 '21 03:06 ryancrawcour