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

Blazor服务中DbContext注入空引用异常问题求助

Blazor服务中EF Core数据库操作空引用问题排查与解决

问题描述

尝试在Blazor服务(非组件)中执行数据库插入操作,无论使用DbContext还是DbContextFactory,均出现DbContext空对象引用错误,怀疑问题出在ConsoleWebhookReceiver服务的依赖注入环节。已尝试配置DbContext、DbContextFactory,调整多种启动配置,有无仓储模式都试过。作为Blazor新手,不确定是否仅能在服务器端进行Entity Framework依赖注入,是否需要编写API控制器处理该操作,但认为Webhook接收器中做数据库插入应是简单操作,应该遗漏了某个细节。

相关代码

Program.cs

using BlazorApp.Services;
using BotTrading.Controllers;
using BotTrading.Data;
using BotTrading.Services;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();

builder.Services.AddScoped<IReceiveWebhook, ConsoleWebhookReceiver>();
builder.Services.AddScoped<IWebhookService, WebhookService>();
builder.Services.AddScoped<WebhookService>(); // 重复注册,可移除
builder.Services.AddControllers();

builder.Services.AddDbContextFactory<AppDBContext>(options => 
    options
        .EnableSensitiveDataLogging()
        .UseSqlite(builder.Configuration.GetConnectionString("Database")));

var app = builder.Build();

// 这里直接获取服务可能引发问题,建议移除,或通过作用域获取
// var webhookService = app.Services.GetRequiredService<WebhookService>();
// var cryptoService = app.Services.GetRequiredService<CryptoService>();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.MapControllers();

app.UseRouting();

app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

//Listen for POST webhooks
app.MapPost("/webhooks", async (HttpContext context, IReceiveWebhook receiveWebook) =>
{
    using StreamReader stream = new StreamReader(context.Request.Body);
    return await receiveWebook.ProcessRequest(await stream.ReadToEndAsync());
});

app.Run();

ConsoleWebhookReceiver.cs

public class ConsoleWebhookReceiver : IReceiveWebhook
{
    private readonly IWebhookService _webhookService;

    // 通过构造函数注入依赖,避免手动实例化服务
    public ConsoleWebhookReceiver(IWebhookService webhookService)
    {
        _webhookService = webhookService;
    }

    /// <summary>
    /// 写入POST请求体到控制台,返回JSON并插入数据库
    /// </summary>
    public async Task<string> ProcessRequest(string requestBody)
    {
        Console.WriteLine($"Request Body: {requestBody}");
        Webhook item = JsonConvert.DeserializeObject<Webhook>(requestBody);

        // 使用注入的服务执行数据库操作,用await避免死锁
        await _webhookService.CreateWebhook(item);
        Webhook webhook = await _webhookService.GetWebhookById(1);

        return "{\"message\" : \"Thanks! We got your webhook\"}";
    }
}

IReceiveWebhook.cs

namespace BlazorApp.Services
{
    public interface IReceiveWebhook
    {
        Task<string> ProcessRequest(string requestBody);
    }
}

WebhookService.cs

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using BotTrading.Data;
using BotTrading.Services;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;

namespace BlazorApp.Services
{
    public class WebhookService : IWebhookService
    {
        private readonly IDbContextFactory<AppDBContext> _contextFactory;

        // 统一使用DbContextFactory构造函数
        public WebhookService(IDbContextFactory<AppDBContext> contextFactory)
        {
            _contextFactory = contextFactory;
        }

        public async Task<List<Webhook>> GetWebhookList()
        {
            using (var context = await _contextFactory.CreateDbContextAsync())
            {
                return await context.Webhooks.ToListAsync();
            }
        }

        public async Task<Webhook> GetWebhookById(int id)
        {
            using (var context = await _contextFactory.CreateDbContextAsync())
            {
                return await context.Webhooks.FirstOrDefaultAsync(x => x.Id == id);
            }
        }
  
        public async Task<Webhook> CreateWebhook(Webhook webhook)
        {
            using (var context = await _contextFactory.CreateDbContextAsync())
            {
                Console.WriteLine("webhook: " + webhook.Base);
                await context.Webhooks.AddAsync(webhook);
                await context.SaveChangesAsync();
                return webhook;
            }
        }
        
        public async Task UpdateWebhook(Webhook webhook)
        {
            using (var context = _contextFactory.CreateDbContext())
            {
                context.Webhooks.Update(webhook);
                await context.SaveChangesAsync();
            }
        }
  
        public async Task DeleteWebhook(Webhook webhook)
        {
            using (var context = _contextFactory.CreateDbContext())
            {
                context.Webhooks.Remove(webhook);
                await context.SaveChangesAsync();
            }
        }
    }
}

IWebhookService.cs

using BotTrading.Data;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BotTrading.Services;

public interface IWebhookService
{
    Task<List<Webhook>> GetWebhookList();
    Task<Webhook> GetWebhookById(int id);
    Task<Webhook> CreateWebhook(Webhook webhook);
    Task UpdateWebhook(Webhook webhook);
    Task DeleteWebhook(Webhook webhook);
}

AppDBContext.cs

using Microsoft.EntityFrameworkCore;

namespace BotTrading.Data;

public class AppDBContext : DbContext
{
    public AppDBContext(DbContextOptions<AppDBContext> options)
        : base(options)
    {
    }

    // 移除冗余的OnConfiguring配置,依赖Program.cs中传入的DbContextOptions
    // protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    // {
    //     optionsBuilder
    //         .EnableSensitiveDataLogging()
    //         .EnableDetailedErrors()
    //         .LogTo(Console.WriteLine, new[] { DbLoggerCategory.Infrastructure.Name })
    //         .UseSqlite(Configuration.GetConnectionString("Database"));
    // }

    public DbSet<Crypto> Cryptos { get; set; }
    public DbSet<Trade> Trades { get; set; }
    public DbSet<Webhook> Webhooks { get; set; }
}

异常信息

fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.NullReferenceException: Object reference not set to an instance of an object.
at BotTrading.Controllers.ConsoleWebhookReceiver.ProcessRequest(String requestBody)
at Program.<>c.<<$>b__0_2>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Http.RequestDelegateFactory.g__ExecuteAwaited|134_0(Task`1 task, HttpContext httpContext)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

问题根源与解决步骤

核心问题

  1. ConsoleWebhookReceiver未注入依赖:类中声明的依赖字段未通过构造函数接收DI容器实例,导致调用时出现空引用。
  2. WebhookService依赖混乱:注释了IDbContextFactory构造函数,改用AppDBContext构造,但方法内仍使用未初始化的_contextFactory字段。
  3. 手动实例化服务:直接new WebhookService绕过DI容器,且使用.Result会引发死锁风险。
  4. AppDBContext配置错误:OnConfiguring中使用未注入的Configuration字段,同时重复配置数据库连接。

解决步骤

  1. 修复ConsoleWebhookReceiver的依赖注入:添加构造函数注入IWebhookService,移除手动实例化代码,用await替代.Result。
  2. 统一WebhookService依赖:启用IDbContextFactory构造函数,确保所有方法使用工厂创建的DbContext。
  3. 清理AppDBContext:移除未注入的Configuration字段和冗余的OnConfiguring方法,依赖DbContextOptions传入的配置。
  4. 优化服务注册:移除重复的WebhookService注册,避免启动时直接获取服务。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 02:24:50