简体   繁体   English

我可以利用 List<t> 使用 UpdateRange(IEnumerable) 时的函数 Remove(T) 和 Insert(Int32, T)<t> )?</t></t>

[英]Can I take advantage of List<T> functions Remove(T) and Insert(Int32, T) when using UpdateRange(IEnumerable<T>)?

I am attempting to implement a controller method to reorder image indexes that need to be saved in the database using EF Core.我正在尝试实现 controller 方法来重新排序需要使用 EF Core 保存在数据库中的图像索引。

I have the following controller method:我有以下 controller 方法:

[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.调用UpdateRange()和随后的SaveChangesAsync()l_oItemImages的顺序和数据对我来说似乎是正确的。

I've been looking at this question which mentions not creating new classes and using UpdateRange() .我一直在看这个问题,它提到不创建新类和使用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) ?我有这个问题是因为我使用Remove(l_oItemImage)然后Insert(p_iNewOrderIndex, l_oItemImage)操作列表的对象吗? Or is it because I'm using ToListAsync() to begin with when I grab the item images?还是因为我在抓取项目图像时使用ToListAsync()开始?

EDIT: Tried Update(l_oItemImage) in place of UpdateRange(l_oItemImages) with same results.编辑:尝试用Update(l_oItemImage)代替UpdateRange(l_oItemImages) ,结果相同。 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.添加了显示附加实体的 QuickWatch 图像,两者均正确显示State = Modified以及int Orderbool IsPrimary属性的预期更改值。

DBContext.ChangeTracker.Entries() 的 QuickWatch

EDIT 2: Added image of QuickWatch data with highlighted changed properties on entities.编辑 2:添加了 QuickWatch 数据的图像,并在实体上突出显示了更改的属性。

已修改完整实体的 QuickWatch 数据。

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.是的,您应该能够利用 List 方法,但是我认为UpdateRange对于这个常见任务来说是不必要的,这是一个替代实现。

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:此外,为了简化您的 ItemImage object,我注意到您有一个明显的数据库持久属性“IsPrimary” - 您可能希望将其更改为在实体甚至数据库级别上计算,例如:

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:对于您可以查询的 MSSQL 数据库中的计算列,添加到您的 DbContext OnModelCreating:

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.Entity(typeof(ImageObject)).Property("IsPrimary").HasComputedColumnSql("CASE WHEN [Order] = 0 THEN 1 ELSE 0 END");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM