Reputation: 105
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
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
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
Upvotes: 1