Using Hangfire and SendGrid


#1

I’m currently using SendGrid but now need to use Hangfire for scheduling.
This is what I’ve done
BackgroundJob.Enqueue(() => client.SendEmailAsync(msg, new CancellationToken()));

But I get this exception…
System.InvalidOperationException
Unable to resolve service for type ‘System.Net.IWebProxy’ while attempting to activate ‘SendGrid.SendGridClient’.

System.InvalidOperationException: Unable to resolve service for type ‘System.Net.IWebProxy’ while attempting to activate ‘SendGrid.SendGridClient’.
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
at Hangfire.Server.CoreBackgroundJobPerformer.Perform(PerformContext context)
at Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass9_0.b__0()
at Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter filter, PerformingContext preContext, Func1 continuation) at Hangfire.Server.BackgroundJobPerformer.PerformJobWithFilters(PerformContext context, IEnumerable1 filters)
at Hangfire.Server.BackgroundJobPerformer.Perform(PerformContext context)
at Hangfire.Server.Worker.PerformJob(BackgroundProcessContext context, IStorageConnection connection, String jobId)

Am I doing something wrong?


#2

From what i can see in the stacktrace, it seems that you’re trying to inject SendGridClient but IWebProxy is not registered in your dependency injection.


#3

@TimK Were you able to find a solution to this problem?


#4

Hi, No not yet, still struggling with it :frowning:


#5

Can you provide a sample of your startup class (without connection string and such)


#6
namespace xxx

{
using System.Net;
using Application.Dapper;
using Application.Infrastructure;
using Microsoft.AspNetCore;
using Persistence.Repository;

public class Startup
{
    private readonly IHostingEnvironment _env;
    private readonly IConfiguration _config;
    private readonly ILoggerFactory _loggerFactory;

    public Startup(IConfiguration configuration, IHostingEnvironment env, IConfiguration config, ILoggerFactory loggerFactory)
    {
        Configuration = configuration;
        _env = env;
        _config = config;
        _loggerFactory = loggerFactory;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        var logger = _loggerFactory.CreateLogger<Startup>();

        if (_env.IsDevelopment())
        {
            // Development service configuration

            logger.LogInformation("Development environment");
        }
        else
        {
            // Non-development service configuration

            logger.LogInformation($"Environment: {_env.EnvironmentName}");
        }

        services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
        services.Configure<ConnectionSettings>(Configuration.GetSection("ConnectionStrings"));
        services.Configure<RedisConfiguration>(Configuration.GetSection("redis"));

        var environment = services.BuildServiceProvider().GetRequiredService<IHostingEnvironment>();

        var settings = Configuration.GetSection(nameof(AppSettings)).Get<AppSettings>();
        var cookieExpiryPeriod = Convert.ToInt32(settings.CookieExpiryPeriod);
        var securityLockoutPeriod = Convert.ToInt32(settings.SecurityLockoutPeriod);

        services.AddDbContext<ApplicationDbContext>(options => options
            .UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
            .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
            .EnableSensitiveDataLogging(true)
        );

        // NEW: Add DbContext using SQL Server Provider
        services.AddDbContext<IApplicationDbContext, ApplicationDbContext>(options => options
            .UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
            .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
        );

        // Add application services.
        services.Configure<EmailSenderOptions>(Configuration.GetSection("EmailSender"));

        // Email
        services.AddTransient<IEmailSender, EmailSender>();

        // Security messaging
        services.Configure<AuthMessageSenderOptions>(Configuration);

        // AutoMapper
        services.AddAutoMapper(new Assembly[] { typeof(AutoMapperProfile).GetTypeInfo().Assembly });

        // MediatR
        services.AddMediatR(typeof(Startup));

        // Add framework services.
        services.AddTransient<INotificationService, NotificationService>();
        services.AddTransient<IDateTime, MachineDateTime>();

        services.AddHtmlTags(new TagConventions());

        services.AddScoped(_ =>
            new DapperContext(Configuration["ConnectionStrings:DefaultConnection"]));

        services.AddScoped<IDapperContext, DapperContext>(_ =>
            new DapperContext(Configuration.GetConnectionString("DefaultConnection")));

        services.AddScoped<IDatabase, Database>();
        services.AddScoped<Application.Interfaces.ISession, Session>();
        services.AddScoped<IViewRenderService, ViewRenderService>();
        services.AddScoped<ValidateReCaptchaAttribute>();

        services.AddHangfire(x => x.UseSqlServerStorage(Configuration["ConnectionStrings:DefaultConnection"]));

        services.AddMvc(o =>
            {
                // only allow authenticated users
                var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
                o.Filters.Add(new AuthorizeFilter(policy));
                o.Filters.Add(typeof(ValidatorActionFilter));
            })
            .AddFeatureFolders()
            .AddAreaFeatureFolders()
            // DO NOT REMOVE THIS LINE - This stops camel casing the results
            .AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver())
            .AddFluentValidation(cfg => { cfg.RegisterValidatorsFromAssemblyContaining<Startup>(); });

        // "Features" is the default feature folder root. To override, pass along 
        // a new FeatureFolderOptions object with a different FeatureFolderName

        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddScoped<IFileRepository, FileRepository>();
        services.AddScoped<ValidateMimeMultipartContentFilter>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, AutoMapper.IConfigurationProvider autoMapper)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();
        app.UseSession();
        app.UseAuthentication();
        app.UseDeveloperExceptionPage();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "areaRoute",
                template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });

        app.UseHangfireServer();
        app.UseHangfireDashboard("/hangfire", new DashboardOptions
        {
            Authorization = new[] { new HangfireAuthorization() }
        });
    }


}

}


#7

Where are you adding Sendgrid into your injection?

Are you properly injecting all dependencies of the SendGridClient?


#8

Could you possibly provide an example of properly injecting all dependencies of the SendGridClient?


#9

I never used it, so you’re probably better than me on that domain.

Maybe something like this would work inside your startup : services.AddTransient<ISendGridClient>(isp => new SendGridClient(apiKey));

I noticed that in the example that i saw on the internet, they never use injection, so if my example before doesn’t work, you probably could remove it from your constructor parameter and try to use it inside of your method

kind of link this