Parallel request handling in .NET Framework v.4.7.2 and .NET 8 Issue ?
Following is the controller method for uploading product images. We are using Dropzone jquery for uploading products
from client side. It sends 2 parallel requests to server at once if uploaded multiple images simultaneously.
In .NET Framework
public async Task<ActionResult> UploadProductImage(string productId)
{
try
{
Product product = await _productService.FindById(productId, SelectedStoreView);
if (product == null)
{
return Failure($"Product with Id {productId} does not exist");
}
Debug.WriteLine($"PRODUCT ETAG WHILE READING AT {DateTime.Now:O} : {product.ETag}");
int totalUploaded = 0;
foreach (string fileFromRequest in Request.Files)
{
HttpPostedFileBase file = Request.Files[fileFromRequest];
if (file == null || file.ContentLength <= 0) continue;
byte[] fileContent;
using (var reader = new BinaryReader(file.InputStream))
{
fileContent = reader.ReadBytes(file.ContentLength);
}
//Uploading product to blob storage
var image = await _productService.UploadProductImage(product, ApplicationSettings, file.FileName, Request.ContentType, fileContent, null, SelectedStoreViewCode);
if (product.Images.Exists(e => e.Url == image.Url)) continue;
//adding uploaded image to product object with url
product.Images.Add(image);
if (product.Images.Count > 0)
{
await ProcessImageAttributeValues(product);
}
//saving product to cosmos db
var updatedProduct = await _productService.UpsertAsync(product);
Debug.WriteLine($"PRODUCT ETAG AFTER UPDATE AT {DateTime.Now:O} : {updatedProduct.ETag}");
totalUploaded += 1;
}
return Success($"{totalUploaded}");
}
catch (Exception ex)
{
return Exception(ex);
}
}
In .NET 8
public async Task<IActionResult> UploadProductImage(string productId)
{
try
{
Product product = await _productService.FindById(productId, SelectedStoreView);
Debug.WriteLine($"PRODUCT ETAG WHILE READING AT {DateTime.Now:O} : {product.ETag}");
if (product == null)
{
return Failure($"Product with Id {productId} does not exist");
}
int totalUploaded = 0;
foreach (string fileFromRequest in Request.Form.Files.Select(x => x.Name))
{
var file = Request.Form.Files[fileFromRequest];
if (file == null || file.Length <= 0) continue;
byte[] fileContent;
using (var mstream = new MemoryStream())
{
file.OpenReadStream().CopyTo(mstream);
fileContent = mstream.ToArray();
}
var image = await _productService.UploadProductImage(product, ApplicationSettings, file.FileName, Request.ContentType, fileContent, null, SelectedStoreViewCode);
if (product.Images.Exists(e => e.Url == image.Url)) continue;
image.Position = product.Images.Count + 1;
product.Images.Add(image);
var updatedProduct = await _productService.UpsertAsync(product);
Debug.WriteLine($"PRODUCT ETAG AFTER UPDATE AT {DateTime.Now:O} : {updatedProduct.ETag}");
totalUploaded += 1;
}
return Success($"{totalUploaded}");
}
catch (Exception ex)
{
return Exception(ex);
}
}
Following are the logs from the .NET Framework application when dropped two images simultaneously.
PRODUCT ETAG WHILE READING AT 2024-09-12T15:07:56.5250628+05:30 : "00000000-0000-0000-0377-85cf51a901db"
PRODUCT ETAG AFTER UPDATE AT 2024-09-12T15:07:58.7006044+05:30 : "00000000-0000-0000-04f7-73faf3de01db"
PRODUCT ETAG WHILE READING AT 2024-09-12T15:07:59.0344485+05:30 : "00000000-0000-0000-04f7-73faf3de01db"
PRODUCT ETAG AFTER UPDATE AT 2024-09-12T15:08:00.7209253+05:30 : "00000000-0000-0000-04f7-752eee6a01db"
Following are the logs from the .NET Framework application when dropped two images simultaneously.
PRODUCT ETAG WHILE READING AT 2024-09-12T15:11:48.9364483+05:30 : "00000000-0000-0000-0371-aaa244bf01db"
PRODUCT ETAG WHILE READING AT 2024-09-12T15:11:48.9364480+05:30 : "00000000-0000-0000-0371-aaa244bf01db"
PRODUCT ETAG AFTER UPDATE AT 2024-09-12T15:12:00.8437338+05:30 : "00000000-0000-0000-04f8-043f320d01db"
PRODUCT ETAG AFTER UPDATE AT 2024-09-12T15:12:00.9038454+05:30 : "00000000-0000-0000-04f8-044da3eb01db"
Both the application are hosted on azure app service.
Here the functionality is working properly in .NET Framework, we can see the logs and ETags, though the request to server are concurrently but executes in a proper manner so there is no issue of overwriting data. But in .NET 8, we can see the logs here both the request reads the same objects and later one request overwrites the data updated by the another request. I can't find why the behavior has been changes in .NET 8 from .NET Framework.
Code is same in both but what causes this issue I can't understand ? Why the behavior in .NET Framework is sequential for simultaneous requests ?