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

.NET6 ASP.NET删除文件时触发ArgumentNullException问题排查

问题描述

我有一个TryGetByStatementId方法,通过FirstOrDefault方法从数据库中获取对应ID的数据。同时维护着一个存储上传文件信息的数据库,上传文件时,文件会保存到wwwroot目录,文件的ID、名称、创建时间等信息存入数据库。但调用TryGetByStatementId方法执行删除操作时,触发错误:System.ArgumentNullException: "Value cannot be null. Arg_ParamName_Name"

相关代码

文件上传控制器代码

public class ManagementController : Controller
{
    private readonly ApplicationDbContext applicationDb;
    private readonly IWebHostEnvironment _webHostEnvironment;

    public ManagementController(ApplicationDbContext applicationDb, IWebHostEnvironment webHostEnvironment)
    {
        this.applicationDb = applicationDb;
        _webHostEnvironment = webHostEnvironment;
    }

    public IActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> AddFile(IFormFile uploadFile)
    {
        if (uploadFile != null) 
        {
            string path = "/Files/" + uploadFile.FileName;

            using (var fileStream = new FileStream(_webHostEnvironment.WebRootPath + path, FileMode.Create)) 
            {
                await uploadFile.CopyToAsync(fileStream);
            }

            StatementsDb file = new StatementsDb { StatementsName = uploadFile.FileName, CreateDateTime = DateTime.Now, Path = path}; 

            applicationDb.statementsDbs.Add(file);
            applicationDb.SaveChanges();
        }
        
        return RedirectToAction("Index");
    }
}

删除操作控制器代码片段

public IActionResult RemoveFile(int statementId)
{
    statementsDb.Remove(statementId);
    return RedirectToAction("Index");
}

数据操作实体代码片段

public StatementsDb TryGetByStatementId(int id)
{
    return applicationDb.statementsDbs.FirstOrDefault(statement => statement.StatementsId == id);
}

public void Remove(int statement)
{
    var existingStatement = TryGetByStatementId(statement);
    applicationDb.statementsDbs.Remove(existingStatement);
    applicationDb.SaveChanges();
}

更新补充的Program代码

using archivingsystem.db;
using archivingsystem.Helpers.AutoMapping;
using AutoMapper;
using Microsoft.EntityFrameworkCore;

namespace archivingsystem
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            IMapper mapper = MappingConfig.RegisterMaps().CreateMapper();

            // Add services to the container.
            builder.Services.AddControllersWithViews();

            builder.Services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));


            // AutoMapper
            builder.Services.AddSingleton(mapper);
            builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());


            // Services
            builder.Services.AddTransient<IStatementsDbRepository, StatementsDbRepository>();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");

            app.Run();
        }
    }
}

错误原因

  1. 空值未处理:当TryGetByStatementId传入的ID不存在时,FirstOrDefault返回null,直接调用Remove(null)会触发ArgumentNullException。
  2. 参数命名混淆:Remove方法参数名statement实际接收的是ID值,命名歧义容易引发逻辑错误。
  3. 删除前未校验:执行删除操作前,没有检查获取到的existingStatement是否为null,直接执行删除。

修复方案

1. 修改数据操作方法,增加空值校验

public StatementsDb TryGetByStatementId(int id)
{
    return applicationDb.statementsDbs.FirstOrDefault(statement => statement.StatementsId == id);
}

public void Remove(int statementId)
{
    var existingStatement = TryGetByStatementId(statementId);
    // 先校验对象是否存在
    if (existingStatement != null)
    {
        applicationDb.statementsDbs.Remove(existingStatement);
        applicationDb.SaveChanges();
        
        // 同步删除文件系统中的对应文件(可选但建议添加)
        var filePath = Path.Combine(_webHostEnvironment.WebRootPath, existingStatement.Path.TrimStart('/'));
        if (System.IO.File.Exists(filePath))
        {
            System.IO.File.Delete(filePath);
        }
    }
    // 可根据需求添加对象不存在时的处理,比如记录日志或返回提示
}

2. 控制器中正确注入仓储服务

避免直接依赖上下文,改用注册的仓储接口:

public class ManagementController : Controller
{
    private readonly IStatementsDbRepository _statementsRepo;
    private readonly IWebHostEnvironment _webHostEnvironment;

    public ManagementController(IStatementsDbRepository statementsRepo, IWebHostEnvironment webHostEnvironment)
    {
        _statementsRepo = statementsRepo;
        _webHostEnvironment = webHostEnvironment;
    }

    // ... 其他代码

    public IActionResult RemoveFile(int statementId)
    {
        _statementsRepo.Remove(statementId);
        return RedirectToAction("Index");
    }
}

3. 可选:添加前端提示逻辑

在控制器中增加不存在时的提示,提升用户体验:

public IActionResult RemoveFile(int statementId)
{
    var existingStatement = _statementsRepo.TryGetByStatementId(statementId);
    if (existingStatement == null)
    {
        TempData["ErrorMessage"] = "要删除的文件不存在";
        return RedirectToAction("Index");
    }
    _statementsRepo.Remove(statementId);
    TempData["SuccessMessage"] = "文件删除成功";
    return RedirectToAction("Index");
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 05:01:40