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

Entity Framework Core中SQL Server数据重复插入问题求助

Blazor EF Core 数据重复插入问题解决

问题描述

在Blazor+Entity Framework Core应用中,创建并保存关联了Transaction、TransactionDocument和TransactionItem的新数据时,所有数据会被SQL Server数据库重复插入两次,不符合预期。

相关代码

TransactionsCreate.razor(Razor页面)

@page "/TransactionsCreate"
@using Aneta.Models
@using Aneta.Data

@code {
    [Inject]
    private DsaClusterContext? Context { get; set; }
    [Inject]
    private UnitOfWork? UnitOfWork { get; set; }

    private Transaction _transaction;
    private TransactionDocument _transactionDocument;
    private TransactionItem _transactionItem;

    protected override async Task OnInitializedAsync()
    {
        if (UnitOfWork == null || Context == null)
            return;

        try
        {
            // Create and configure the transaction
            _transaction = new Transaction
            {
                MunicipalityName = "Digos City",
                BarangayName = "Zone 3",
            };

            // Create and configure the document
            _transactionDocument = new TransactionDocument
            {
                PunongBarangayName = "Aidre Cabrera",
                BarangayTreasurerName = "Trisha Talagtag"
            };

            // Create and configure the item
            _transactionItem = new TransactionItem
            {
                Amount = 500,
                Cost = 58,
                Particulars = "Jasmine Rice",
                Price = 58,
                Quantity = 55,
                UnitName = "KG"
            };

            // Establish the relationships
            _transaction.TransactionDocuments.Add(_transactionDocument);
            _transactionDocument.Transaction = _transaction;
            _transactionDocument.TransactionItems.Add(_transactionItem);
            _transactionItem.Document = _transactionDocument;

            // Start a unit of work and add the transaction to the repository
            UnitOfWork = new UnitOfWork(Context);
            UnitOfWork.Transaction.Add(_transaction);

            // Save all changes
            await UnitOfWork.CompleteAsync();
        }
        catch (Exception ex)
        {
            // Handle exceptions as needed
        }
        finally
        {
            if (UnitOfWork != null)
            {
                UnitOfWork.Dispose();
            }
        }
    }
}

UnitOfWork类

using Aneta.Data.Repositories;
using Aneta.Models;

public class UnitOfWork : IUnitOfWork
{
    private readonly DsaClusterContext? _context;

    public UnitOfWork(DsaClusterContext? context)
    {
        _context = context;
        Transaction = new SqlRepository<Transaction>(_context);
        TransactionDocuments = new SqlRepository<TransactionDocument>(_context);
        // Other repository initializations...
    }

    // Repository properties...

    public int Complete()
    {
        return _context!.SaveChanges();
    }
    
    public async Task<int> CompleteAsync()
    {
        return await _context?.SaveChangesAsync()!;
    }

    public void Dispose()
    {
        _context?.Dispose();
    }
}

Transaction模型类

public partial class Transaction
{
    public int TransactionId { get; set; }
    public string? MunicipalityName { get; set; }
    public string? BarangayName { get; set; }
    public virtual ICollection<TransactionDocument> TransactionDocuments { get; set; } = new List<TransactionDocument>();
}

TransactionDocument模型类

public partial class TransactionDocument
{
    public int DocumentId { get; set; }
    public int? TransactionId { get; set; }
    public string? PunongBarangayName { get; set; }
    public string? BarangayTreasurerName { get; set; }
    public virtual ICollection<AbstractQuotation> AbstractQuotations { get; set; } = new List<AbstractQuotation>();
    public virtual ICollection<PurchaseOrder> PurchaseOrders { get; set; } = new List<PurchaseOrder>();
    public virtual ICollection<PurchaseRequest> PurchaseRequests { get; set; } = new List<PurchaseRequest>();
    public virtual Transaction? Transaction { get; set; }
    public virtual ICollection<TransactionItem> TransactionItems { get; set; } = new List<TransactionItem>();
}

问题排查与解决方案

1. 核心原因:组件生命周期方法重复执行

Blazor组件的OnInitializedAsync方法在开发环境下(尤其是服务器端Blazor)可能因预渲染、热重载或组件重新渲染被执行多次,每次执行都会触发一次数据插入,导致重复数据。

2. 具体修复步骤

  • 将插入逻辑移至用户触发事件:不要在OnInitializedAsync中自动执行插入,改为通过按钮点击等用户交互触发,示例:
    <button @onclick="HandleCreateTransaction">创建交易</button>
    
    @code {
        private async Task HandleCreateTransaction()
        {
            // 原OnInitializedAsync中的插入逻辑移至此处
        }
    }
    
  • 避免重复实例化UnitOfWork:已通过依赖注入获取UnitOfWork实例,无需手动new UnitOfWork(Context),直接使用注入实例即可,减少上下文跟踪冲突:
    // 移除该行:UnitOfWork = new UnitOfWork(Context);
    UnitOfWork.Transaction.Add(_transaction);
    await UnitOfWork.CompleteAsync();
    
  • 简化关联配置:EF Core会自动维护导航属性关联,无需同时设置双向关联,保留集合添加操作即可:
    // 保留以下两行,移除另外两行赋值
    _transaction.TransactionDocuments.Add(_transactionDocument);
    _transactionDocument.TransactionItems.Add(_transactionItem);
    
  • 检查DbContext生命周期:确保DsaClusterContext在DI容器中配置为Scoped(Blazor推荐配置),避免单例上下文导致的共享跟踪问题:
    // Program.cs中的配置示例
    builder.Services.AddDbContext<DsaClusterContext>(options => 
        options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")), 
        ServiceLifetime.Scoped);
    

环境信息

  • EF Core版本:7.0.11
  • 数据库提供程序:Microsoft.EntityFrameworkCore.SqlServer
  • 目标框架:.NET 6.0
  • 操作系统:Windows 11
  • IDE:JetBrains Rider

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 23:35:55