在我的项目中,我使用带有EF Core的LazyLoading,我有两个实体:
public class OfferEntity : BaseEntity
{
public string Category { get; set; }
public virtual List<ImagePreviewEntity> ImagePreviews { get; set; }
}
public class ImagePreviewEntity: BaseEntity
{
public string PreviewUrl { get; set; }
}
我可以通过这种方式从创建剃刀页面创建它们(使用js动态添加新行(:
<div class="form-group">
<input asp-for="OfferEntity.Category" class="form-control" />
</div>
<div id="ImagePreviews" class="form-group">
<input id="addImageBtn" type="button" value="Add new">
@for (var i = 0; i < Model.OfferEntity.ImagePreviews.Count; i++)
{
<input asp-for="OfferEntity.ImagePreviews[i].PreviewUrl" class="form-control" />
}
</div>
在standart Create.cshtml.cs中,我只为绑定模型添加了第1个实体创建:OfferEntity = new() { ImagePreviews = new() { new() } };
而且在编辑之前,它运行得非常好。我的编辑剃刀页面与创建页面相同。我的编辑.cshtml.cs:
public async Task<IActionResult> OnGetAsync(string id)
{
OfferEntity = await _context.Offers.Include(x => x.ImagePreviews).FirstOrDefaultAsync(m => m.Id == id);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
return Page();
//await _context.Offers.Include(p => p.ImagePreviews).LoadAsync();
var toRemove = OfferEntity.ImagePreviews
.Where(x => string.IsNullOrEmpty(x.PreviewUrl))
.ToList();
_context.Attach(OfferEntity).State = EntityState.Modified;
_context.ImagePreviews.RemoveRange(toRemove);
await _context.SaveChangesAsync();
return return RedirectToPage("./Index");
}
有两个问题我无法解决:
- ImagePreviews更改不会保存在父实体上
- 删除空的ImagePreviews导致DbUpdateConcurrencyException:数据库操作应影响1行,但实际影响0行
我尝试过的:
- 从scopeFactory注入数据库上下文
- 将已删除的实体标记为状态=已删除
- 使用include(imagePreview(直接从上下文加载数据库集,而不是附加
- 正在从OfferEntity.ImagePreviews集合中删除实体
- 手鼓跳舞
我缺少什么?
在与EF斗争了一天后,找到了一个不优雅但有效的解决方案。其中一些问题是:
- 我的新实体没有ID
- 我在"编辑剃刀"页面中的现有实体没有附加ID
- 我曾多次尝试附加嵌套属性
这是我的OnPostAsync:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
return Page();
var newImages = OfferEntity.ImagePreviews
.Where(x => string.IsNullOrEmpty(x.Id))
.ToList();
foreach (var imagePreviewEntity in newImages)
{
imagePreviewEntity.Id = Guid.NewGuid().ToString();
await _context.ImagePreviews.AddAsync(imagePreviewEntity);
}
_context.Attach(OfferEntity).State = EntityState.Modified;
var relatedPreviewIds = OfferEntity.ImagePreviews.Select(x => x.Id).ToList();
var previews = await _context.ImagePreviews
.Where(x => relatedPreviewIds.Contains(x.Id))
.ToListAsync();
previews.ForEach(entity =>
{
_context.Entry(entity).CurrentValues.SetValues(OfferEntity.ImagePreviews.First(p => p.Id == entity.Id));
if (string.IsNullOrEmpty(entity.PreviewUrl))
{
_context.Attach(entity).State = EntityState.Deleted;
return;
}
if (_context.Entry(entity).State != EntityState.Added)
_context.Attach(entity).State = EntityState.Modified;
});
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
if (!OfferEntityExists(OfferEntity.Id))
return NotFound();
throw;
}
return RedirectToPage("./Index");
}
我觉得一定有更清洁的解决方案。只写那么多代码来从嵌套列表中添加/删除实体是疯狂的。