Base solution for your next web application
Open Closed

WebClientDbContext could not connect to database.';System.NullReferenceException: Object reference not set to an instance of an object. #11414


User avatar
0
henriksorensen created

Sometimes, during healthchecking, we get error about connection to database. It happens 2-3 times in 2500 healthcheck requests. Could you help us to find possible reason of this error?

Log: TASWebclient;2022-10-30 23:34:50,139;ERROR;Microsoft.Extensions.Diagnostics.HealthChecks.DefaultHealthCheckService;[.NET ThreadPool Worker];(null);(null);Health check Database Connection with user check with status Unhealthy completed after 4.4974ms with message 'WebClientDbContext could not connect to database.';System.NullReferenceException: Object reference not set to an instance of an object. at Microsoft.AspNetCore.Http.DefaultHttpRequest.get_Scheme() at Microsoft.AspNetCore.Http.Extensions.UriHelper.GetDisplayUrl(HttpRequest request) at Abp.EntityHistory.EntityHistoryHelper.CreateEntityChangeSet(ICollection1 entityEntries) at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext3.SaveChangesAsync(CancellationToken cancellationToken) at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesAsync() at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.CompleteUowAsync() at Abp.Domain.Uow.UnitOfWorkBase.CompleteAsync() at TAS.WebClient.HealthChecks.WebClientDbContextUsersHealthCheck.CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken) in D:\BuildAgents\DevOpsAgent39_Work\4\s\TASWebclient\aspnet-core\src\TAS.WebClient.Application\HealthChecks\WebClientDbContextUsersHealthCheck.cs:line 58 ;

WebClientDbContextUsersHealthCheck.cs :

using System;
using System.Threading;
using System.Threading.Tasks;
using Abp.Domain.Uow;
using Abp.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using TAS.WebClient.EntityFrameworkCore;

namespace TAS.WebClient.HealthChecks
{
    public class WebClientDbContextUsersHealthCheck : IHealthCheck
    {
        private readonly IDbContextProvider<WebClientDbContext> _dbContextProvider;
        private readonly IUnitOfWorkManager _unitOfWorkManager;

        public WebClientDbContextUsersHealthCheck(
            IDbContextProvider<WebClientDbContext> dbContextProvider,
            IUnitOfWorkManager unitOfWorkManager
            )
        {
            _dbContextProvider = dbContextProvider;
            _unitOfWorkManager = unitOfWorkManager;
        }

        public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
        {
            try
            {
                using (var uow = _unitOfWorkManager.Begin())
                {
                    // Switching to host is necessary for single tenant mode.
                    using (_unitOfWorkManager.Current.SetTenantId(null))
                    {
                        var dbContext = await _dbContextProvider.GetDbContextAsync();
                        if (!await dbContext.Database.CanConnectAsync(cancellationToken))
                        {
                            return HealthCheckResult.Unhealthy(
                                "WebClientDbContext could not connect to database"
                            );
                        }

                        var user = await dbContext.Users.AnyAsync(cancellationToken);
                        await uow.CompleteAsync();

                        if (user)
                        {
                            return HealthCheckResult.Healthy("WebClientDbContext connected to database and checked whether user added");
                        }

                        return HealthCheckResult.Unhealthy("WebClientDbContext connected to database but there is no user.");

                    }
                }
            }
            catch (Exception e)
            {
                return HealthCheckResult.Unhealthy("WebClientDbContext could not connect to database.", e);
            }
        }
    }
}

AbpZeroHealthCheck.cs:

using Microsoft.Extensions.DependencyInjection;
using TAS.WebClient.HealthChecks;

namespace TAS.WebClient.Web.HealthCheck
{
    public static class AbpZeroHealthCheck
    {
        public static IHealthChecksBuilder AddAbpZeroHealthCheck(this IServiceCollection services)
        {
            var builder = services.AddHealthChecks();
            builder.AddCheck<WebClientDbContextHealthCheck>("Database Connection");
            builder.AddCheck<WebClientDbContextUsersHealthCheck>("Database Connection with user check");
            builder.AddCheck<CacheHealthCheck>("Cache");
            builder.AddCheck<ExternalApiHealthCheck>("ExternalApi Connection");
            builder.AddCheck<DocumentApiHealthCheck>("DocumentApi Connection");

            // add your custom health checks here
            // builder.AddCheck<MyCustomHealthCheck>("my health check");

            return builder;
        }
    }
}

Startup.cs:

if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
            {
                services.AddAbpZeroHealthCheck();

                var healthCheckUISection = _appConfiguration.GetSection("HealthChecks")?.GetSection("HealthChecksUI");

                if (bool.Parse(healthCheckUISection["HealthChecksUIEnabled"]))
                {
                    services.Configure<HealthChecksUISettings>(settings =>
                    {
                        healthCheckUISection.Bind(settings, c => c.BindNonPublicProperties = true);
                    });
                    services.AddHealthChecksUI()
                        .AddInMemoryStorage();
                }
            }

...



app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<AbpCommonHub>("/signalr");
                endpoints.MapHub<ChatHub>("/signalr-chat");

                endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
                endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");

                if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
                {
                    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
                    {
                        Predicate = _ => true,
                        ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
                    });
                }

                app.ApplicationServices.GetRequiredService<IAbpAspNetCoreConfiguration>().EndpointConfiguration.ConfigureAllEndpoints(endpoints);
            });

            if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
            {
                if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksUI:HealthChecksUIEnabled"]))
                {
                    app.UseHealthChecksUI();
                }
            }

appsettings.json:

  "HealthChecks": {
    "HealthChecksEnabled": true,
    "HealthChecksUI": {
      "HealthChecksUIEnabled": true,
      "HealthChecks": [
        {
          "Name": "TAS.WebClient.Web.Host",
          "Uri": "https://localhost:44301/health"
        }
      ],
      "EvaluationTimeOnSeconds": 10,
      "MinimumSecondsBetweenFailureNotifications": 60
    }
  },
  

3 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Could you place [DisableAuditing] attribute to CheckHealthAsync method and try again ?

  • User Avatar
    0
    henriksorensen created

    Thanks Asp.Net Zero Support

    We have now tried adding that attribute, see below. No change experienced, still same issue.

        [DisableAuditing]
        public async Task&lt;HealthCheckResult&gt; CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
        {
    

    Any other suggestions or ideas?

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @henriksorensen

    Currently, I don't have any suggestion. Could you create an issue on https://github.com/aspnetzero/aspnet-zero-core/issues ? We will investigate this issue deeper.