Blazor DbContext in a service

67 Views Asked by At

I'm trying to do a database insert from within a service as opposed to from a component and no matter what I do it is giving me a null object error for my DbContext. I have also tried this with DbContextFactory and have had the same problem. I am assuming it is something to do with the dependency injection in the ConsoleWebhookReceiver Service.

Tried DbContext and DbContextFactory for my service, and also different startup configs and with and without repositories. I'm new to Blazor and I don't quite understand everything yet. I am wondering if you are only able to do Entity Framework dependency injection on the server side and if I am supposed to make an API controller to handle this but it seems like such a simple thing to do a database insert in this webhook receiver and I must just be missing something simple.

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.UseMvc();

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 AppDBContext _appDbContext;
    private readonly IDbContextFactory<AppDBContext> _contextFactory;

    /// <summary>
    /// Writes the POST request body to the console and returns JSON and then inserts into database
    /// </summary>
    public async Task<string> ProcessRequest(string requestBody)
    {
        var context = await _contextFactory.CreateDbContextAsync();
        WebhookService webhookService = new WebhookService(context);
        
        // This is where you would put your actual business logic for receiving webhooks
        Console.WriteLine($"Request Body: {requestBody}");
        Webhook item = JsonConvert.DeserializeObject<Webhook>(requestBody);

        // These two calls to WebhookReceiver is where I am getting 
        // the null object reference. The item is passed into my 
        // webhookService, the item is not null, but in the service 
        // there is no context
        webhookService.CreateWebhook(item);
        Webhook webhook = webhookService.GetWebhookById(1).Result;

        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.Threading.Tasks;
using BotTrading.Data;
using BotTrading.Services;
using Flurl.Util;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using NgrokApi;

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

        // public WebhookService(IDbContextFactory<AppDBContext> contextFactory)
        // {
        //     _contextFactory = contextFactory;
        // }
        public WebhookService(AppDBContext context)
        {
            _context = context;
        }

        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);
                context.Webhooks.AddAsync(webhook);
                await context.SaveChangesAsync();
                return webhook;
            }
        }
        
        public async Task UpdateWebhook(Webhook player)
        {
            using (var context = _contextFactory.CreateDbContext())
            {
                context.Webhooks.Update(player);
                await context.SaveChangesAsync();
            }
        }
  
        public async Task DeleteWebhook(Webhook player)
        {
            using (var context = _contextFactory.CreateDbContext())
            {
                context.Webhooks.Remove(player);
                await context.SaveChangesAsync();
            }
        }
    }
}

IWebhookService.cs:

using BotTrading.Data;

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
{
    protected readonly IConfiguration Configuration;

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

    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; }
}

Here is the exception

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)

0

There are 0 best solutions below