You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core中关联表AssetsMixedUseTypes更新失效问题求助

问题描述

我有Asset表和MixedUseType表,通过关联表AssetsMixedUseTypes建立多对多关系。目前该关联表支持INSERT和DELETE操作,但UPDATE操作无法正常执行,找不到配置错误的位置。


相关代码文件

Asset.cs

public partial class Asset
{
    public int AssetId { get; set; }

    public string AssetName { get; set; }

    public ICollection<AssetsMixedUseType>? AssetsMixedUseTypes { get; set; } = new List<AssetsMixedUseType>();
}

MixedUseType.cs

public partial class MixedUseType
{
    public int MixedUseTypeId { get; set; }

    public string MixedUseTypeName { get; set; }

    public ICollection<AssetsMixedUseType>? AssetsMixedUseTypes { get; set; } = new List<AssetsMixedUseType>();
}

AssetsMixedUseType.cs

public partial class AssetsMixedUseType
{
    public int AssetId { get; set; }

    public int MixedUseTypeId { get; set; }
    
    public virtual Asset? Asset { get; set; } = null!;

    public virtual MixedUseType? MixedUseType { get; set; } = null!;
}

DatabaseContext.cs

modelBuilder.Entity<AssetsMixedUseType>(entity =>
{
    entity.HasKey(amut => new { amut.AssetId, amut.MixedUseTypeId }).HasName("PK_Mixed_Use_Types");

    entity.ToTable("Assets_Mixed_Use_Types");

    entity.Property(e => e.AssetId).HasColumnName("AssetID");
    entity.Property(e => e.MixedUseTypeId).HasColumnName("MixedUseTypeID");

    entity.HasOne(d => d.Asset).WithMany(a => a.AssetsMixedUseTypes)
        .HasForeignKey(d => d.AssetId)
        .OnDelete(DeleteBehavior.ClientSetNull)
        .HasConstraintName("FK_Assets_Mixed_Use_Types_Assets");

    entity.HasOne(d => d.MixedUseType).WithMany(a => a.AssetsMixedUseTypes)
        .HasForeignKey(d => d.MixedUseTypeId)
        .OnDelete(DeleteBehavior.ClientSetNull)
        .HasConstraintName("FK_Assets_Mixed_Use_Types_Mixed_Use_Types");
});

Edit.cshtml.cs

[BindProperty]
public AssetsMixedUseType AssetsMixedUseType { get; set; } = default!;

public async Task<IActionResult> OnGetAsync(int? id, int? assetId)
{
    if (id == null || assetId == null || _context.AssetsMixedUseTypes == null)
    {
        return NotFound();
    }

    var assetsmixedusetype =  await _context.AssetsMixedUseTypes.Where(m => m.MixedUseTypeId == id).Where(m => m.AssetId == assetId).FirstOrDefaultAsync();
    if (assetsmixedusetype == null)
    {
        return NotFound();
    }
    AssetsMixedUseType = assetsmixedusetype;
    ViewData["AssetId"] = AssetsMixedUseType.AssetId;
    ViewData["MixedUseTypes"] = new SelectList(_context.MixedUseTypes, nameof(MixedUseType.MixedUseTypeId), nameof(MixedUseType.MixedUseTypeName));
    return Page();
}

public async Task<IActionResult> OnPostAssetMixedUseTypeEditAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Attach(AssetsMixedUseType).State = EntityState.Modified;


    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!AssetsMixedUseTypeExists(AssetsMixedUseType.AssetId))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return Page();
}

Edit.cshtml

<form method="post" asp-page-handler="AssetMixedUseTypeEdit" data-ajax="true" data-ajax-method="post" data-ajax-success="refreshPartial('GetAssetMixedUseTypes', 'MixedUseTypes-container')" data-ajax-failure="ajaxValidationHandler">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <input type="hidden" asp-for="AssetsMixedUseType.AssetId" value="@ViewBag.AssetId" />
    <div class="form-group">
        <label asp-for="AssetsMixedUseType.MixedUseTypeId" class="control-label"></label>
        <select asp-for="AssetsMixedUseType.MixedUseTypeId" class="form-control" asp-items="ViewBag.MixedUseTypes"><option disabled selected>- Select Mixed Use Type -</option></select>
    </div>
    <div class="form-group">
        <input type="submit" value="Save" class="btn btn-primary" />
        <a class="btn btn-secondary" href="javascript:parent.actionPaneClose();">Cancel</a>
    </div>
</form>

问题原因与解决方案

核心问题

关联表AssetsMixedUseType的主键是复合主键(AssetId + MixedUseTypeId),EF Core不允许直接修改实体的主键值。你当前试图修改MixedUseTypeId,这属于修改主键的一部分,EF Core会判定为无效操作,不会生成UPDATE语句。同时,直接标记实体为EntityState.Modified也无法解决这个问题,因为主键变更本身不被支持。

正确处理方式

多对多关联表的“更新”本质上是删除原关联记录,再插入新的关联记录,具体步骤如下:

1. 修改后端处理逻辑

更新OnPostAssetMixedUseTypeEditAsync方法:

public async Task<IActionResult> OnPostAssetMixedUseTypeEditAsync(int originalMixedUseTypeId)
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    // 获取原始关联记录
    var originalRecord = await _context.AssetsMixedUseTypes
        .FirstOrDefaultAsync(m => m.AssetId == AssetsMixedUseType.AssetId && m.MixedUseTypeId == originalMixedUseTypeId);

    if (originalRecord == null)
    {
        return NotFound();
    }

    // 删除原关联,插入新关联
    _context.AssetsMixedUseTypes.Remove(originalRecord);
    _context.AssetsMixedUseTypes.Add(new AssetsMixedUseType
    {
        AssetId = AssetsMixedUseType.AssetId,
        MixedUseTypeId = AssetsMixedUseType.MixedUseTypeId
    });

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!_context.Assets.Any(a => a.AssetId == AssetsMixedUseType.AssetId))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return Page();
}

2. 页面传递原始主键值

在Edit.cshtml中添加隐藏字段,传递原始的MixedUseTypeId:

<form method="post" asp-page-handler="AssetMixedUseTypeEdit" data-ajax="true" data-ajax-method="post" data-ajax-success="refreshPartial('GetAssetMixedUseTypes', 'MixedUseTypes-container')" data-ajax-failure="ajaxValidationHandler">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <input type="hidden" asp-for="AssetsMixedUseType.AssetId" value="@ViewBag.AssetId" />
    <!-- 添加原始MixedUseTypeId的隐藏字段 -->
    <input type="hidden" name="originalMixedUseTypeId" value="@AssetsMixedUseType.MixedUseTypeId" />
    <div class="form-group">
        <label asp-for="AssetsMixedUseType.MixedUseTypeId" class="control-label"></label>
        <select asp-for="AssetsMixedUseType.MixedUseTypeId" class="form-control" asp-items="ViewBag.MixedUseTypes"><option disabled selected>- 选择混合用途类型 -</option></select>
    </div>
    <div class="form-group">
        <input type="submit" value="保存" class="btn btn-primary" />
        <a class="btn btn-secondary" href="javascript:parent.actionPaneClose();">取消</a>
    </div>
</form>

3. 修复辅助方法逻辑

原AssetsMixedUseTypeExists方法逻辑错误,应判断关联记录是否存在,而非Asset是否存在:

private bool AssetsMixedUseTypeExists(int assetId, int mixedUseTypeId)
{
    return _context.AssetsMixedUseTypes?.Any(e => e.AssetId == assetId && e.MixedUseTypeId == mixedUseTypeId) ?? false;
}

4. 可选:调整删除行为配置

关联表的OnDelete(DeleteBehavior.ClientSetNull)可能不符合业务逻辑,多对多关联通常使用DeleteBehavior.Cascade(删除主表时自动删除关联)或DeleteBehavior.Restrict(禁止删除关联的主表),可根据实际业务调整。


内容的提问来源于stack exchange,提问作者s15199d

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 13:17:33