I am attempting to implement a controller method to reorder image indexes that need to be saved in the database using EF Core.
I have the following controller method:
[HttpPost]
public async Task<JsonResult> ReorderImage(int p_iImageID, int p_iNewOrderIndex)
{
if (p_iImageID <= 0)
return Json(new { Status = "Error", Message = $"Unable to retrieve item image with ID of {p_iImageID}" });
ItemImage l_oItemImage = await _Context.ItemImages.FirstOrDefaultAsync(l_oImage => l_oImage.ID == p_iImageID);
if (l_oItemImage.IsNull())
return Json(new { Status = "Error", Message = $"Unable to retrieve item image with ID of {p_iImageID}" });
List<ItemImage> l_oItemImages = await _Context.ItemImages.Where(l_oImage => l_oImage.ItemID == l_oItemImage.ItemID)
.OrderBy(l_oImage => l_oImage.Order)
.ToListAsync();
l_oItemImages.Remove(l_oItemImage);
l_oItemImages.Insert(p_iNewOrderIndex, l_oItemImage);
foreach(ItemImage l_oImage in l_oItemImages)
{
l_oImage.Order = l_oItemImages.IndexOf(l_oImage);
if (l_oItemImages.IndexOf(l_oImage) == 0)
l_oImage.IsPrimary = true;
else
l_oImage.IsPrimary = false;
l_oImage.Uri = _AzureBlobStorage.GetBlobUri(_ItemImageAzureBlobContainerName, l_oImage.GetFileName());
}
_Context.ItemImages.UpdateRange(l_oItemImages);
await _Context.SaveChangesAsync();
return Json(l_oItemImages)
}
The order and data of l_oItemImages
when calling UpdateRange()
and subsequently SaveChangesAsync()
appears correct to me.
I've been looking at this question which mentions not creating new classes and using UpdateRange()
. This seems a bit different but I can see how this might be my issue.
Am I having this issue because I'm manipulating the objects of the list using Remove(l_oItemImage)
and then Insert(p_iNewOrderIndex, l_oItemImage)
? Or is it because I'm using ToListAsync()
to begin with when I grab the item images?
EDIT: Tried Update(l_oItemImage)
in place of UpdateRange(l_oItemImages)
with same results. Added image of QuickWatch showing tacked entities both are correctly showing State = Modified
as well as the expected changed values for int Order
and bool IsPrimary
properties.
EDIT 2: Added image of QuickWatch data with highlighted changed properties on entities.
Yes, you should be able to take advantage of the List methods however I think UpdateRange
is unnecessary for this common task, here is an alternative implementation.
You may want to consider something like the following instead where the Sequence is reassigned for a subset of sequenced entities:
public async Task SetSequenceAsync(int forPageComponentId, int newSequence)
{
var infoAboutItemWereChangingSequenceFor = await context.PageComponents
.Where(x => x.Id == forPageComponentId)
.Select(x => new {
OriginalSequence = x.Sequence, // I need to know it's current sequence.
x.PageId // I need to only adjust sequences for items that have the same PageId, so I need to know what the pageId is for the item we're targeting.
}).FirstOrDefaultAsync();
// Get just the data we want to modify, we're going to include the item we're targeting so this list is inclusive of it.
// Including the item we're changing to make logic below a little mor consise instead of managing the list and the item we're targeting
// seperately.
var allItemsWithSequenceThatWillChange = await context.PageComponents
.Where(x =>
x.PageId == infoAboutItemWereChangingSequenceFor.PageId // Only those items sharing the same page Id.
// Only those items we need to change the sequence for.
&& x.Sequence >= Math.Min(infoAboutItemWereChangingSequenceFor.OriginalSequence, newSequence)
&& x.Sequence <= Math.Max(infoAboutItemWereChangingSequenceFor.OriginalSequence, newSequence)
)
.Select(x =>
new PageComponent() // The type of object EF knows about.
{
// The Primary key, so Entity Framework knows what record to change the sequence on.
Id = x.Id,
// The sequence value we need to change.
Sequence = x.Sequence
}
).ToListAsync();
// Set the sequence of the item we're targeting.
allItemsWithSequenceThatWillChange
.Where(x => x.Id == forPageComponentId)
.First()
.Sequence = newSequence;
// Now update the sequence on the other items (excluding the target item)
foreach (var item in allItemsWithSequenceThatWillChange.Where(x => x.Id != forPageComponentId))
{
// Either increment or decrement the sequence depending on how the original item was moved.
item.Sequence += infoAboutItemWereChangingSequenceFor.OriginalSequence > newSequence ? 1 : -1;
// Add any other property changes here.
}
// Save changes.
await context.SaveChangesAsync();
}
Also, as a matter of simplification on your ItemImage object, I notice you have an apparently DB persisted property "IsPrimary" - you may want to change this to be calculated on the entity and even at the db level instead, eg:
public class ItemImage {
// ... Other Properties ...
public int Order { get; set; }
public bool IsPrimary {
get => Order == 0;
set {}
}
}
For a calculated column in your MSSQL Database you can query against, add to your DbContext OnModelCreating:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity(typeof(ImageObject)).Property("IsPrimary").HasComputedColumnSql("CASE WHEN [Order] = 0 THEN 1 ELSE 0 END");
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.