moyomeh
moyomeh

Reputation: 105

Chaining multiple call of the same method in c#

I have an update method that takes the last active item and clones it with new values and sets the active flag to true. My problem is that the update method is called by multiple web api methods, so the last active item is not always the same. I ended up with multiple active item and data inconsistency. So I'm thinking of chaining all calls to resolve this problem but I don't know where to start.`

[HttpPost]
[Route("Route2")]
[ValidateModel]
public async Task<HttpResponseMessage> Post(string contractReference, [FromBody] Family input)
{
    return await CallPartialUpdate(contractReference, p => p.Family = input);
}


[HttpPost]
[Route("Route3")]
[ValidateModel]
public async Task<HttpResponseMessage> Post(string contractReference, [FromBody] Address input)
{
    return await CallPartialUpdate(contractReference, p => p.Address = input);
}



private async Task<HttpResponseMessage> CallPartialUpdate(string reference, Item itemToUpdate)
{
    try
    {
        var existingItem = _bContext.RetrieveLastActive(reference);

        if (existingItem == null)
            return Request.CreateResponse(HttpStatusCode.NotFound);

        var newRecord = existingItem.Document.Clone();

        newRecord.update(itemToUpdate);
        newRecord.active = true;

        await _bContext.PutDocumentAsync(newRecord, reference);

        return Request.CreateResponse(HttpStatusCode.Created);
    }
    catch (System.Exception exception)
    {
        return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exception.Message);
    }
}`

Based on @Asti answer i create a unit test with an Rx observable but i still have data inconsistency in the last item so how i get this working and how i get the result of the callPartialUpdate() Thank you

    [TestFixture]
public class Concurrency
{

    [Test]
    public async Task Update_Should_Always_Change_Last_Item()
    {
        var observer = Observable.Timer(TimeSpan.FromMilliseconds(1));
        var items = new List<Item>()
        {
            new Item() { FirstName = "AA", LastName = "BB" , IsActive = true },
            new Item() { FirstName = "A", LastName = "A" , IsActive = false },
        };
        await Task.Run(() =>
        {
             Parallel.Invoke(async () => await observer.Select(item => Observable.FromAsync(ct => UpdateItem(items, new Item() { FirstName = "AAA" })))
                             .Concat(),
                             async () => await observer.Select(item => Observable.FromAsync(ct => UpdateItem(items, new Item() { LastName = "BBB" })))
                             .Concat());
        });
        var lastItem = items.Single(w => w.IsActive);
        Assert.AreEqual("AAA", lastItem.FirstName);
        Assert.AreEqual("BBB", lastItem.LastName);
    }


    public async Task<bool> UpdateItem(List<Item> items, Item itemToUpdate)
    {
        return await Task.Run(() => update(items, itemToUpdate));
    }

    private bool update(List<Item> items, Item itemToUpdate)
    {
        var lastItem = items.Single(w => w.IsActive == true);
        lastItem.IsActive = false;
        var newItem = new Item()
        {
            FirstName = string.IsNullOrEmpty(itemToUpdate.FirstName) ? lastItem.FirstName : itemToUpdate.FirstName,
            LastName = string.IsNullOrEmpty(itemToUpdate.LastName) ? lastItem.LastName : itemToUpdate.LastName,
            IsActive = true
        };

        items.Add(newItem);
        return true;
    }
}

Upvotes: 2

Views: 397

Answers (2)

Hogan
Hogan

Reputation: 70528

add

public class Concurrency
{
  private Object thisLock = new Object();

then

  private bool update(List<Item> items, Item itemToUpdate)
  {
    lock (thisLock)
    {
      var lastItem = items.Single(w => w.IsActive == true);
      lastItem.IsActive = false;
      var newItem = new Item()
      {
         FirstName = string.IsNullOrEmpty(itemToUpdate.FirstName) ? lastItem.FirstName : itemToUpdate.FirstName,
        LastName = string.IsNullOrEmpty(itemToUpdate.LastName) ? lastItem.LastName : itemToUpdate.LastName,
        IsActive = true
      };

      items.Add(newItem);
    }
    return true;
  }

The area in the lock will only ever be running by one thread at any given time and will function as expected.

There is no guarantee of order for that you would have to do it a different way -- what way would depend on what rules you want to enforce.

Upvotes: 0

Asti
Asti

Reputation: 12687

For a sequences of updates, either enumerable or observable:

        updates
            .Select(item => Observable.FromAsync(ct => CallPartialUpdate(item)))
            .Concat();

If you want to aim for an Rx based API, then

  • Have the method calls return observables
  • Await observables instead of tasks
  • Observe on a dedicated scheduler to partially order units of work

Upvotes: 1

Related Questions