I have created an HTTP trigger-based .Net 6 Azure FunctionApp and trying to configure the database connection strings, other key values, and dependency injections for my service classes but, I don't know how to call my configure method of Startup.cs file from Program.cs main function. I am new to FunctionApp-based hosting.
I have tried with IHostBuilder like the following in the Program.cs file, but it says: "does not contain a definition for ConfigureWebHostDefaults" even used the namespace => using Microsoft.AspNetCore.Hosting;
public static void Main(string[] args)
{
var host = new HostBuilder().ConfigureFunctionsWorkerDefaults()
.Build();
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
My Startup.cs file,
[assembly: FunctionsStartup(typeof(Startup))]
namespace kpi
{
public class Startup : FunctionsStartup
{
private static IConfiguration _configuration = null;
public override void Configure(IFunctionsHostBuilder builder)
{
var serviceProvider = builder.Services.BuildServiceProvider();
_configuration = serviceProvider.GetRequiredService<IConfiguration>();
var appSettingsSection = _configuration.GetSection("AppSetting");
builder.Services.Configure<AppSetting>(appSettingsSection);
var appSettings = appSettingsSection.Get<AppSetting>();
RuntimeConfig.appsettings = appSettings;
var ConnectionString = RuntimeConfig.appsettings.AppDBConnection;
builder.Services.AddDbContext<ShardingDbContext>(options =>
options.UseSqlServer(ConnectionString), ServiceLifetime.Transient);
}
}
}
I have used the FunctionStartup assembly, I don't know where I did go wrong, Can anyone help me to configure my connection strings from Startup.cs file in .Net6 Function App project?
You can refer below code to fix your issue. For more details, please read official doc.
Guide for running C# Azure Functions in an isolated process
1. Startup.cs file
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(triggerFunc.Startup))]
namespace triggerFunc
{
public class Startup : FunctionsStartup
{
private static IConfiguration _configuration = null;
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
var context = builder.GetContext();
// optional: customize your configuration sources
// here, we add appsettings.json files
// Note that these files are not automatically copied on build or publish.
//builder.ConfigurationBuilder
// .AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: true, reloadOnChange: false)
// .AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{context.EnvironmentName}.json"), optional: true, reloadOnChange: false);
}
public override void Configure(IFunctionsHostBuilder builder)
{
// get the configuration from the builder
//var configuration = builder.GetContext().Configuration;
var serviceProvider = builder.Services.BuildServiceProvider();
_configuration = serviceProvider.GetRequiredService<IConfiguration>();
var appSettingsSection = _configuration.GetSection("AppSetting");
builder.Services.Configure<AppSetting>(appSettingsSection);
var appSettings = appSettingsSection.Get<AppSetting>();
RuntimeConfig.appsettings = appSettings;
var ConnectionString = RuntimeConfig.appsettings.AppDBConnection;
builder.Services.AddDbContext<ShardingDbContext>(options =>
options.UseSqlServer(ConnectionString), ServiceLifetime.Transient);
}
}
}
2. Program.cs file
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace triggerFunc
{
public class Program
{
public static async Task Main(string[] args)
{
var builder = Host
.CreateDefaultBuilder(args)
.ConfigureFunctionsWorkerDefaults()
.ConfigureAppConfiguration((hostingContext, configBuilder) =>
{
var env = hostingContext.HostingEnvironment;
;
})
.ConfigureServices((appBuilder, services) =>
{
var configuration = appBuilder.Configuration;
});
await builder.Build().RunAsync();
}
}
}
I just did override the function ConfigureAppConfiguration as below
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
FunctionsHostBuilderContext context = builder.GetContext();
builder.ConfigurationBuilder
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "local.settings.json"), optional: true, reloadOnChange: false)
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"local.settings.{context.EnvironmentName}.json"), optional: true, reloadOnChange: false)
.AddEnvironmentVariables();
}
It's working fine in my local without an isolated way or Program.cs file and I don't know whether this same code will work on a server or not.
My local.setings.json
{
"IsEncrypted": false,
"AppSettings": {
"AppDBConnection": "xyz....."
}
My Startup.cs file below and I can access the AppSettings section from my json file
public class Startup : FunctionsStartup
{
private static IConfiguration _configuration = null;
public override void Configure(IFunctionsHostBuilder builder)
{
//var connectionString = Environment.GetEnvironmentVariable("ConnectionStrings:DBConnection");
var serviceProvider = builder.Services.BuildServiceProvider();
_configuration = serviceProvider.GetRequiredService<IConfiguration>();
var appSettingsSection = _configuration.GetSection("AppSettings");
builder.Services.Configure<AppSettings>(appSettingsSection);
var appSettings = appSettingsSection.Get<AppSettings>();
RuntimeConfig.appsettings = appSettings;
var ConnectionString = RuntimeConfig.appsettings.AppDBConnection;
builder.Services.AddDbContext<ShardingDbContext>(options => options.UseSqlServer(ConnectionString), ServiceLifetime.Transient);
builder.Services.AddScoped<ITestService, TestService>();
}
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
FunctionsHostBuilderContext context = builder.GetContext();
builder.ConfigurationBuilder
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "local.settings.json"), optional: true, reloadOnChange: false)
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"local.settings.{context.EnvironmentName}.json"), optional: true, reloadOnChange: false)
.AddEnvironmentVariables();
}
}
Related
I'm using .NET Core 2.2 and SignalR Core and I need to inject IHubContext<MyClass> via Simple Injector in my Webjob.
It works perfectly in my web app but when I'm trying to reach my service via web job, it's complaining about lacking injection of IHubContext<IHubContext<BroadcastHub>>
I need a way to register it via Simple Injector
This is my Configuration in Program.cs file in my Webjob
using AutoMapper;
using Gateway.BLL.BaseClasses;
using Gateway.BLL.Config;
using Gateway.BLL.Services;
using Gateway.BLL.Services.Interfaces;
using Gateway.BLL.SignalR;
using Gateway.Model.MappingProfiles;
using Gateway.Repository;
using Gateway.Repository.Interfaces;
using Gateway.Repository.Repositories;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using SimpleInjector;
using System;
using System.IO;
using System.Net.Http;
namespace Gateway.WebJob
{
class Program
{
private static void Main()
{
var container = new Container();
DbContextOptionsBuilder ob = new DbContextOptionsBuilder();
var config = new MapperConfiguration
(cfg =>
{
cfg.AddProfile(new ModelMappingProfile());
}
);
var mapper = config.CreateMapper();
var loggerFactory = new LoggerFactory();
ServiceCollection sr = new ServiceCollection();
sr.AddSignalR();
var serviceProvider = sr.AddHttpClient().BuildServiceProvider();
var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
// Duplicate here any configuration sources you use.
configurationBuilder.AddJsonFile("appsettings.json");
IConfiguration configuration = configurationBuilder.Build();
var medchartApiConfiguration = new MedchartApiConfiguration();
configuration.Bind("MedchartApiConfiguration", medchartApiConfiguration);
var serviceBusConfiguration = new ServiceBusConfiguration();
configuration.Bind("ServiceBusConfiguration", serviceBusConfiguration);
ob = ob.UseSqlServer(configuration["ConnectionString:GatewayDB"]);
IMemoryCache memoryCache = new MemoryCache(new MemoryCacheOptions());
ConfigureServices(sr);
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddServiceBus(o =>
{
o.MessageHandlerOptions.AutoComplete = true;
o.MessageHandlerOptions.MaxConcurrentCalls = 10;
o.ConnectionString = "Endpoint=sb://gatewayqueue.servicebus.windows.net/;SharedAccessKeyName=admin;SharedAccessKey=Wd2YwCEJT2g3q4ykvdOIU2251YD5FizCn5aCuumzdz4=";
}).AddSignalR();
});
builder.ConfigureLogging((context, b) =>
{
b.AddConsole();
string instrumentationKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
if (!string.IsNullOrEmpty(instrumentationKey))
{
b.AddApplicationInsightsWebJobs(o => o.InstrumentationKey = instrumentationKey);
}
});
builder.ConfigureServices((hostContext, services) => {
//services.AddHttpClient();
//hostContext.Configuration.Bind("MedchartApiConfiguration", medchartApiConfiguration);
//services.AddSingleton(medchartApiConfiguration);
services.AddSingleton(container);
services.AddScoped<JobActivator.ScopeDisposable>();
services.AddScoped<IJobActivator, JobActivator>();
});
container.Register<IPatientService, PatientService>();
container.Register<IPatientRepository, PatientRepository>();
container.Register<IProviderService, ProviderService>();
container.Register<IPatientGroupProviderRepository, PatientGroupProviderRepository>();
container.Register<IPatientGroupRepository, PatientGroupRepository>();
container.Register<IConsentRepository, ConsentRepository>();
container.Register<IHttpClientWrapper, HttpClientWrapper>();
container.Register<IMedchartService, MedchartService>();
container.Register<IGroupRepository, GroupRepository>();
container.Register<IReportRepository, ReportRepository>();
container.Register<IProviderRepository, ProviderRepository>();
container.RegisterSingleton(httpClientFactory);
container.RegisterSingleton(memoryCache);
container.RegisterSingleton(medchartApiConfiguration);
container.RegisterSingleton(serviceBusConfiguration);
container.Register<ILoggerFactory>(() => loggerFactory, Lifestyle.Singleton);
container.RegisterSingleton(configuration);
container.RegisterSingleton(typeof(ILogger<PatientRepository>), typeof(Logger<PatientRepository>));
container.RegisterSingleton(typeof(ILogger<PatientService>), typeof(Logger<PatientService>));
container.RegisterSingleton(typeof(ILogger<HttpClientWrapper>), typeof(Logger<HttpClientWrapper>));
container.RegisterSingleton(typeof(ILogger<MedchartService>), typeof(Logger<MedchartService>));
container.RegisterSingleton(typeof(ILogger<ProviderService>), typeof(Logger<ProviderService>));
container.RegisterSingleton(typeof(ILogger<ProviderRepository>), typeof(Logger<ProviderRepository>));
container.RegisterSingleton(typeof(ILogger<ReportRepository>), typeof(Logger<ReportRepository>));
container.RegisterSingleton(mapper);
container.Register<GatewayDBContext>(() => {
var options = ob.Options;
return new GatewayDBContext(options);
});
var host = builder.Build();
using (host)
{
host.Run();
}
}
private static IConfiguration Configuration { get; set; }
private static void ConfigureServices(IServiceCollection services)
{
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
Configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
services.AddSingleton(Configuration);
services.AddTransient<Functions, Functions>();
services.AddLogging(builder => builder.AddConsole());
}
}
}
This is my simplified Service that is using SignalR: (this is another project that webjob will use it
using Microsoft.AspNetCore.SignalR;
using Gateway.BLL.SignalR;
// namespace Gateway.BLL.Services
public class PatientService : HttpClientWrapper, IPatientService
{
private readonly IHubContext<BroadcastHub> _hubContext;
public PatientService(IHubContext<BroadcastHub> hubContext)
: base(logger,httpClientFactory,medchartConfig)
{
_hubContext = hubContext;
}
public async Task<OutputHandler<IEnumerable<PatientEnrollmentParams>>>
CreatePatientAsync(List<PatientEnrollmentParams> patients,
CancellationToken ct)
{
var result = new OutputHandler<IEnumerable<PatientEnrollmentParams>>();
await _hubContext.Clients.All.SendAsync("BroadcastMessage");
return result;
}
}
this is my webjob that will call PatientService in another project
using Gateway.BLL.Config;
using Gateway.BLL.Processors;
using Gateway.BLL.Queues;
using Gateway.BLL.Services;
using Gateway.Model.Queues;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using SimpleInjector;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Gateway.WebJob
{
public class Functions
{
private Container _container;
public Functions(Container container)
{
_container = container;
}
public async Task ProcessEnrollmentStatus([ServiceBusTrigger("%ServiceBusConfiguration:EnrollmentMessage:QueueName%")]string message, ILogger log)
{
var _patientService = _container.GetInstance<IPatientService>();
GetEnrollmentStatusTaskProcessor processor = new GetEnrollmentStatusTaskProcessor(_patientService);
EnrollmentStatusTask data = JsonConvert.DeserializeObject<EnrollmentStatusTask>(message);
await processor.Process(data);
}
public async Task ProcessConsentRequestStatus([ServiceBusTrigger("%ServiceBusConfiguration:ConsentRequestMessage:QueueName%")]string message, ILogger log)
{
var _patientService = _container.GetInstance<IPatientService>();
GetConsentRequestTaskProcessor processor = new GetConsentRequestTaskProcessor(_patientService);
ConsentRequestTask data = JsonConvert.DeserializeObject<ConsentRequestTask>(message);
await processor.Process(data);
}
}
}
and this is the process method that will call patientService:
using Gateway.BLL.Services;
using Gateway.Model.Queues;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Gateway.BLL.Processors
{
public class GetEnrollmentStatusTaskProcessor : IProcessor<EnrollmentStatusTask>
{
private IPatientService _patientService;
public GetEnrollmentStatusTaskProcessor(IPatientService patientService)
{
_patientService = patientService;
}
public async Task<bool> Process(EnrollmentStatusTask data)
{
bool updated = await _patientService.UpdatePatientEnrollmentStatus(data.PatientId, data.PatientMedchartId.ToString(), data.GroupId);
return updated;
}
}
}
I need to register IHubContext<MyClass> in my webjob in program.cs but I'm not able to register it via the following ways:
hubContext = serviceProvider.GetService<IHubContext<BroadcastHub>>();
container.RegisterSingleton(hubContext);
or this way
container.Register<IHubContext<BroadcastHub>>(Lifestyle.Singleton);
Update 2019-12-02:
I was able to resolve IHubContext but now i'm receiving e new issue. this is my function class:
using Gateway.BLL.Config;
using Gateway.BLL.Processors;
using Gateway.BLL.Queues;
using Gateway.BLL.Services;
using Gateway.Model.Queues;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using SimpleInjector;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Gateway.WebJob
{
public class Functions
{
private Container _container;
public Functions(Container container)
{
_container = container;
}
public async Task ProcessEnrollmentStatus([ServiceBusTrigger("%ServiceBusConfiguration:EnrollmentMessage:QueueName%")]string message, ILogger log)
{
var _patientService = _container.GetInstance<IPatientService>();
GetEnrollmentStatusTaskProcessor processor = new GetEnrollmentStatusTaskProcessor(_patientService);
EnrollmentStatusTask data = JsonConvert.DeserializeObject<EnrollmentStatusTask>(message);
await processor.Process(data);
}
public async Task ProcessConsentRequestStatus([ServiceBusTrigger("%ServiceBusConfiguration:ConsentRequestMessage:QueueName%")]string message, ILogger log)
{
var _patientService = _container.GetInstance<IPatientService>();
GetConsentRequestTaskProcessor processor = new GetConsentRequestTaskProcessor(_patientService);
ConsentRequestTask data = JsonConvert.DeserializeObject<ConsentRequestTask>(message);
await processor.Process(data);
}
}
}
and this is my progrm.cs class after all updates:
using AutoMapper;
using Gateway.BLL.BaseClasses;
using Gateway.BLL.Config;
using Gateway.BLL.Services;
using Gateway.BLL.Services.Interfaces;
using Gateway.BLL.SignalR;
using Gateway.Model.MappingProfiles;
using Gateway.Repository;
using Gateway.Repository.Interfaces;
using Gateway.Repository.Repositories;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using SimpleInjector;
using System;
using System.IO;
using System.Net.Http;
namespace Gateway.WebJob
{
class Program
{
private static void Main()
{
var container = new Container();
DbContextOptionsBuilder ob = new DbContextOptionsBuilder();
var config = new MapperConfiguration
(cfg =>
{
cfg.AddProfile(new ModelMappingProfile());
}
);
var mapper = config.CreateMapper();
var loggerFactory = new LoggerFactory();
ServiceCollection sr = new ServiceCollection();
sr.AddLogging();
sr.AddSignalR();
sr.AddDbContextPool<GatewayDBContext>(options => { /*options */ });
sr.AddSimpleInjector(container, options =>
{
options.AddLogging();
//options.CrossWire<ILoggerFactory>();
});
sr.BuildServiceProvider(validateScopes: true).UseSimpleInjector(container);
var serviceProvider = sr.AddHttpClient().BuildServiceProvider();
var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
// Duplicate here any configuration sources you use.
configurationBuilder.AddJsonFile("appsettings.json");
IConfiguration configuration = configurationBuilder.Build();
var medchartApiConfiguration = new MedchartApiConfiguration();
configuration.Bind("MedchartApiConfiguration", medchartApiConfiguration);
var serviceBusConfiguration = new ServiceBusConfiguration();
configuration.Bind("ServiceBusConfiguration", serviceBusConfiguration);
ob = ob.UseSqlServer(configuration["ConnectionString:GatewayDB"]);
IMemoryCache memoryCache = new MemoryCache(new MemoryCacheOptions());
ConfigureServices(sr);
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddServiceBus(o =>
{
o.MessageHandlerOptions.AutoComplete = true;
o.MessageHandlerOptions.MaxConcurrentCalls = 10;
o.ConnectionString = "Endpoint=sb://gatewayqueue.servicebus.windows.net/;SharedAccessKeyName=admin;SharedAccessKey=Wd2YwCEJT2g3q4ykvdOIU2251YD5FizCn5aCuumzdz4=";
});
});
builder.ConfigureLogging((context, b) =>
{
b.AddConsole();
b.Services.AddLogging();
string instrumentationKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
if (!string.IsNullOrEmpty(instrumentationKey))
{
b.AddApplicationInsightsWebJobs(o => o.InstrumentationKey = instrumentationKey);
}
});
builder.ConfigureServices((hostContext, services) =>
{
//services.AddHttpClient();
//hostContext.Configuration.Bind("MedchartApiConfiguration", medchartApiConfiguration);
//services.AddSingleton(medchartApiConfiguration);
//services.AddSingleton(container);
//services.AddScoped<JobActivator.ScopeDisposable>();
//services.AddScoped<IJobActivator, JobActivator>();
});
container.Register<IPatientService, PatientService>();
container.Register<IPatientRepository, PatientRepository>();
container.Register<IProviderService, ProviderService>();
container.Register<IPatientGroupProviderRepository, PatientGroupProviderRepository>();
container.Register<IPatientGroupRepository, PatientGroupRepository>();
container.Register<IConsentRepository, ConsentRepository>();
container.Register<IHttpClientWrapper, HttpClientWrapper>();
container.Register<IMedchartService, MedchartService>();
container.Register<IGroupRepository, GroupRepository>();
container.Register<IReportRepository, ReportRepository>();
container.Register<IProviderRepository, ProviderRepository>();
container.RegisterSingleton(httpClientFactory);
container.RegisterSingleton(memoryCache);
container.RegisterSingleton(medchartApiConfiguration);
container.RegisterSingleton(serviceBusConfiguration);
//container.Register<ILoggerFactory>(() => loggerFactory, Lifestyle.Singleton);
container.RegisterSingleton(configuration);
container.RegisterSingleton(typeof(ILogger<PatientRepository>), typeof(Logger<PatientRepository>));
container.RegisterSingleton(typeof(ILogger<PatientService>), typeof(Logger<PatientService>));
container.RegisterSingleton(typeof(ILogger<HttpClientWrapper>), typeof(Logger<HttpClientWrapper>));
container.RegisterSingleton(typeof(ILogger<MedchartService>), typeof(Logger<MedchartService>));
container.RegisterSingleton(typeof(ILogger<ProviderService>), typeof(Logger<ProviderService>));
container.RegisterSingleton(typeof(ILogger<ProviderRepository>), typeof(Logger<ProviderRepository>));
container.RegisterSingleton(typeof(ILogger<ReportRepository>), typeof(Logger<ReportRepository>));
container.RegisterSingleton(mapper);
//container.Register<GatewayDBContext>(() =>
//{
// var options = ob.Options;
// return new GatewayDBContext(options);
//});
container.Verify();
var host = builder.Build();
using (host)
{
host.Run();
}
}
private static IConfiguration Configuration { get; set; }
private static void ConfigureServices(IServiceCollection services)
{
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
Configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
services.AddSingleton(Configuration);
services.AddTransient<Functions, Functions>();
services.AddLogging(builder => builder.AddConsole());
}
}
}
Now I'm getting this error that it means the container inside the function class is not resolved:
fail: Host.Results[0]
System.InvalidOperationException: Unable to resolve service for type 'SimpleInjector.Container' while attempting to activate 'Gateway.WebJob.Functions'.
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method(Closure , IServiceProvider , Object[] )
at Microsoft.Azure.WebJobs.Host.Executors.DefaultJobActivator.CreateInstance[T](IServiceProvider serviceProvider) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\DefaultJobActivator.cs:line 37
at Microsoft.Azure.WebJobs.Host.Executors.FunctionInvoker`2.CreateInstance(IFunctionInstanceEx functionInstance) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionInvoker.cs:line 44
at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ParameterHelper.Initialize() in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs:line 846
at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.TryExecuteAsyncCore(IFunctionInstanceEx functionInstance, CancellationToken cancellationToken) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs:line 117
I just tried this out myself, but as long as "auto cross wiring" is enabled in the Simple Injector integration (which is the default), you should be able to get IHubContext<T> implementations injected without having to do anything.
Here's an example startup class:
public class Startup
{
private readonly Container container = new Container();
public Startup(IConfiguration configuration) => Configuration = configuration;
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// default asp stuff here
// add signalr
services.AddSignalR();
// add simple injector (enables auto cross wiring)
services.AddSimpleInjector(this.container, options =>
{
options.AddAspNetCore().AddControllerActivation();
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSimpleInjector(container);
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
And here's a hub context injected into a controller that is created by Simple Injector:
public class HomeController : Controller
{
private readonly IHubContext<MyHub> context;
public HomeController(IHubContext<MyHub> context, Container container)
{
this.context = context;
}
public IActionResult Index()
{
return View();
}
}
We override SaveChangesAsync() to update automatically for DateCreated, CreatedBy, LastDateModified and LastModifiedBy. With CreatedBy and LastModifiedBt, we need to the User Id of Identity.
In our constructor for ApplicationDbContext, we've added something like this:
_userName = httpContextAccessor.HttpContext.User.Identity.Name;
//_userID = userManager.GetUserId(httpContext.HttpContext.User);
.. and always get the null in this httpContextAccessor.HttpContext. Any ideas? We included the source below.
Environment:
.NET Core 2.1
SQL Server
ApplicationDBContext.cs:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using AthlosifyWebArchery.Models;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
namespace AthlosifyWebArchery.Data
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
private readonly string _userID;
private readonly string _userName;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options,
IHttpContextAccessor httpContextAccessor
)
: base(options)
{
_userName = httpContextAccessor.HttpContext.User.Identity.Name;
//_userID = userManager.GetUserId(httpContext.HttpContext.User);
}
public DbSet<AthlosifyWebArchery.Models.TournamentBatchItem> TournamentBatchItem { get; set; }
public DbSet<AthlosifyWebArchery.Models.TournamentBatch> TournamentBatch { get; set; }
public virtual DbSet<AthlosifyWebArchery.Models.Host> Host { get; set; }
public DbSet<AthlosifyWebArchery.Models.HostApplicationUser> HostApplicationUser { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
foreach (var entityType in builder.Model.GetEntityTypes())
{
// 1. Add the IsDeleted property
entityType.GetOrAddProperty("IsDeleted", typeof(bool));
// 2. Create the query filter
var parameter = Expression.Parameter(entityType.ClrType);
// EF.Property<bool>(post, "IsDeleted")
var propertyMethodInfo = typeof(EF).GetMethod("Property").MakeGenericMethod(typeof(bool));
var isDeletedProperty = Expression.Call(propertyMethodInfo, parameter, Expression.Constant("IsDeleted"));
// EF.Property<bool>(post, "IsDeleted") == false
BinaryExpression compareExpression = Expression.MakeBinary(ExpressionType.Equal, isDeletedProperty, Expression.Constant(false));
// post => EF.Property<bool>(post, "IsDeleted") == false
var lambda = Expression.Lambda(compareExpression, parameter);
builder.Entity(entityType.ClrType).HasQueryFilter(lambda);
}
// Many to Many relationship
builder.Entity<HostApplicationUser>()
.HasKey(bc => new { bc.HostID, bc.Id });
builder.Entity<HostApplicationUser>()
.HasOne(bc => bc.Host)
.WithMany(b => b.HostApplicationUsers)
.HasForeignKey(bc => bc.HostID);
builder.Entity<HostApplicationUser>()
.HasOne(bc => bc.ApplicationUser)
.WithMany(c => c.HostApplicationUsers)
.HasForeignKey(bc => bc.Id);
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
OnBeforeSaving();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
{
OnBeforeSaving();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
private void OnBeforeSaving()
{
// Added
var added = ChangeTracker.Entries().Where(v => v.State == EntityState.Added && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
added.ForEach(entry =>
{
((IBaseEntity)entry.Entity).DateCreated = DateTime.UtcNow;
((IBaseEntity)entry.Entity).CreatedBy = _userID;
((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow;
((IBaseEntity)entry.Entity).LastModifiedBy = _userID;
});
// Modified
var modified = ChangeTracker.Entries().Where(v => v.State == EntityState.Modified &&
typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
modified.ForEach(entry =>
{
((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow;
((IBaseEntity)entry.Entity).LastModifiedBy = _userID;
});
// Deleted
//var deleted = ChangeTracker.Entries().Where(v => v.State == EntityState.Deleted &&
//typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList();
var deleted = ChangeTracker.Entries().Where(v => v.State == EntityState.Deleted).ToList();
deleted.ForEach(entry =>
{
((IBaseEntity)entry.Entity).DateDeleted = DateTime.UtcNow;
((IBaseEntity)entry.Entity).DeletedBy = _userID;
});
foreach (var entry in ChangeTracker.Entries()
.Where(e => e.State == EntityState.Deleted &&
e.Metadata.GetProperties().Any(x => x.Name == "IsDeleted")))
{
switch (entry.State)
{
case EntityState.Added:
entry.CurrentValues["IsDeleted"] = false;
break;
case EntityState.Deleted:
entry.State = EntityState.Modified;
entry.CurrentValues["IsDeleted"] = true;
break;
}
}
}
}
}
Startup.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using AthlosifyWebArchery.Data;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using AthlosifyWebArchery.Models;
using DinkToPdf.Contracts;
using DinkToPdf;
namespace AthlosifyWebArchery
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
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)
{
//services.AddHttpContextAccessor();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools()));
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
// Extended Application User from IdentityUser
// and ApplicationRole from IdentityRole
services.AddIdentity<ApplicationUser, ApplicationRole>(
options => options.Stores.MaxLengthForKeys = 128)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders();
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.Conventions.AuthorizeFolder("/Tournaments");
options.Conventions.AuthorizeFolder("/TournamentAtheletes");
options.Conventions.AuthorizeFolder("/TournamentBatches");
options.Conventions.AuthorizeFolder("/TournamentContingents");
options.Conventions.AuthorizeFolder("/Admin");
//options.Conventions.AuthorizeFolder("/Private");
//options.Conventions.AllowAnonymousToPage("/Private/PublicPage");
//options.Conventions.AllowAnonymousToFolder("/Private/PublicPages");
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ApplicationDbContext context,
RoleManager<ApplicationRole> roleManager,
UserManager<ApplicationUser> userManager)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc();
//UserManagerInitialData.Initialize(context, userManager, roleManager).Wait();
}
}
}
HttpContext is only valid during a request. When .NET Core creates an ApplicationDbContext class for the call to Configure there is no valid context.
You need to store a reference to the IHttpContextAccessor in your DbContext constructor and then you can use that variable to access the HttpContext property in your OnBeforeSaving() method.
For example:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options,
IHttpContextAccessor httpContextAccessor
)
: base(options)
{
_httpContextAccessor = httpContextAccessor;
}
....
}
Then, in your OnBeforeSaving() method:
private void OnBeforeSaving()
{
var userName = _httpContextAccessor.HttpContext.User.Identity.Name;
...
}
Think of HttpContext as a telephone call. If you pick the phone up when no-one has called then there is no context i.e. it is null. When someone does call then you have a valid context. This is the same principal for a web call. The Configure method in Startup is not a web call and, as such, does not have a HttpContext.
From another site:
HttpContext object will hold information about the current http
request. In detail, HttpContext object will be constructed newly for
every request given to an ASP.Net application and this object will
hold current request specific informations like Request, Response,
Server, Session, Cache, User and etc. For every request, a new
HttpContext object will be created which the ASP.Net runtime will use
during the request processing. A new HttpContext object will be
created at the beginning of a request and destroyed when the request
is completed.
Above answer explain it well but i would like to highlight another scenario where it could be null as well. For ex:
public class SomeClass
{
SomeClass(IHttpContextAccessor accessor) {}
IActionResult SomeMethod()
{
_ = Task.Run(() =>
{
// use accessorHere
}
return Ok();
}
}
There is a chance that Api call is returned before Thread can access the IHttpContextAccessor and there is a chance that IHttpConextAccessor.HttpContext could be null.
So it is better if we can fetch the required values from the HttpContext for ex: userclaims and pass them as seperate object to the required function.
I am trying to add an appsettings.json and followed a lot of tutorials and still can not do it.
I create appsettings.json
{
"option1": "value1_from_json",
"ConnectionStrings": {
"DefaultConnection": "Server=,\\SQL2016DEV;Database=DBName;Trusted_Connection=True"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
}
Add my class:
public class MyOptions
{
public string Option1 { get; set; }
}
public class ConnectionStringSettings
{
public string DefaultConnection { get; set; }
}
then on my Startup.cs
public IConfiguration Configuration { get; set; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
{
builder.AddUserSecrets<Startup>();
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
and :
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddScoped<IDataService<Sale>, DataService<Sale>>();
// add My services
// Register the IConfiguration instance which MyOptions binds against.
services.AddOptions();
// Load the data from the 'root' of the json file
services.Configure<MyOptions>(Configuration);
// load the data from the 'ConnectionStrings' section of the json file
var connStringSettings = Configuration.GetSection("ConnectionStrings");
services.Configure<ConnectionStringSettings>(connStringSettings);
}
and also injected the Dependency into the controller constructor.
public class ForecastApiController : Controller
{
private IDataService<Sale> _SaleDataService;
private readonly MyOptions _myOptions;
public ForecastApiController(IDataService<Sale> service, IOptions<MyOptions> optionsAccessor)
{
_SaleDataService = service;
_myOptions = optionsAccessor.Value;
var valueOfOpt1 = _myOptions.Option1;
}
}
EDITED:
The problem is that I get Configuration underlined in red
services.Configure<MyOptions>(Configuration);
Error CS1503
Argument 2: cannot convert from 'Microsoft.Extensions.Configuration.IConfiguration' to 'System.Action Exercise.Models.MyOptions
I know there are similar questions explaining how to:
ASP.NET Core MVC App Settings
but it doesn't work for me
Cheers
Did you include the correct namespace?
using Microsoft.Extensions.DependencyInjection;
Also did you have a reference to?:
Microsoft.Extensions.Options.ConfigurationExtensions
In above Assembly we have:
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration config) where TOptions : class;
Most probably you are using the extension method from Microsoft.Extensions.Options assembly (which is wrong)
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, Action<TOptions> configureOptions) where TOptions : class;
Make sure that you imported everything that is necessary and have the required packages installed. Then you can do the following
services.Configure<MyOptions>(options => Configuration.GetSection("options1").Bind(options));
this will cause the options to be updated at runtime whenever you change the appssettings programatically.
In my Program.cs Main method, I would like to read user secrets, configure a logger and then build the WebHost.
public static Main(string[] args)
{
string instrumentationKey = null; // read from UserSecrets
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.ApplicationInsightsEvents(instrumentationKey)
.CreateLogger();
BuildWebHost(args).Run();
}
I can get to the configuration by building my own, but I quickly end up in a mess trying to access hosting environment properties:
public static Main(string[] args)
{
var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
var configBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{envName}.json", optional: true, reloadOnChange: true);
// Add user secrets if env.IsDevelopment().
// Normally this convenience IsDevelopment method would be available
// from HostingEnvironmentExtensions I'm using a private method here.
if (IsDevelopment(envName))
{
string assemblyName = "<I would like the hosting environment here too>"; // e.g env.ApplicationName
var appAssembly = Assembly.Load(new AssemblyName(assemblyName));
if (appAssembly != null)
{
configBuilder.AddUserSecrets(appAssembly, optional: true); // finally, user secrets \o/
}
}
var config = configBuilder.Build();
string instrumentationKey = config["MySecretFromUserSecretsIfDevEnv"];
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.ApplicationInsightsEvents(instrumentationKey) // that.. escallated quickly
.CreateLogger();
// webHostBuilder.UseConfiguration(config) later on..
BuildWebHost(args, config).Run();
}
Is there an easier way to access IHostingEnvironment before building the WebHost?
In the main method, you cannot get the IHostingEnvironment instance before building the WebHost, as hosting is not yet created. And you cannot create a new valid instance properly, as it must be initialized using WebHostOptions`.
For application name you may use Assembly.GetEntryAssembly()?.GetName().Name
For environment name use what you currently do (I assume something like this is used in your IsDevelopment() method):
var environmentName = System.Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
bool isDevelopment = string.Equals(
"Development",
environmentName,
StringComparison.OrdinalIgnoreCase);
See, methods like IHostingEnvironment.IsDevelopment() are extension methods that simply do string comparison internally:
public static bool IsDevelopment(this IHostingEnvironment hostingEnvironment)
{
if (hostingEnvironment == null)
{
throw new ArgumentNullException(nameof(hostingEnvironment));
}
return hostingEnvironment.IsEnvironment(EnvironmentName.Development);
}
public static bool IsEnvironment(
this IHostingEnvironment hostingEnvironment,
string environmentName)
{
if (hostingEnvironment == null)
{
throw new ArgumentNullException(nameof(hostingEnvironment));
}
return string.Equals(
hostingEnvironment.EnvironmentName,
environmentName,
StringComparison.OrdinalIgnoreCase);
}
Note regarding AddJsonFile: as file name is sensitive in Unix OS, sometimes it's better to use $"appsettings.{envName.ToLower()}.json" instead of.
I wanted to do something similar. I wanted to set Kestrel listen options based on the environment. I just save the IHostingEnvironment to a local variable while I am configuring the apps configuration. Then later on I use that variable to make a decision based on the environment. You should be able to follow this pattern to accomplish your goal.
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
IHostingEnvironment env = null;
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration((hostingContext, config) =>
{
env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
if (env.IsDevelopment())
{
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null)
{
config.AddUserSecrets(appAssembly, optional: true);
}
}
config.AddEnvironmentVariables();
if (args != null)
{
config.AddCommandLine(args);
}
})
.UseKestrel(options =>
{
if (env.IsDevelopment())
{
options.Listen(IPAddress.Loopback, 44321, listenOptions =>
{
listenOptions.UseHttps("testcert.pfx", "ordinary");
});
}
else
{
options.Listen(IPAddress.Loopback, 5000);
}
})
.Build();
}
}
In .Net Core 2.0 you can do like this
var webHostBuilder = WebHost.CreateDefaultBuilder(args);
var environment = webHostBuilder.GetSetting("environment");
I am building an ASP.NET Core version 1.1 application that I want Kestrel to run over HTTPS/SSL. Here is the Program.cs bootstrap code...
public class Program
{
public static void Main(string[] args)
{
var contentRoot = Directory.GetCurrentDirectory();
var certFilePath = Path.Combine(contentRoot, #"Certificates\Kestrel.pfx");
// TODO Store password in Secrets
var certificate = new X509Certificate2(certFilePath, "kr0GEE6lJ5Ok");
var host = new WebHostBuilder()
.UseKestrel(cfg => cfg.UseHttps(certificate))
.UseContentRoot(contentRoot)
.UseSetting("detailedErrors", "true")
.UseIISIntegration()
.UseStartup<Startup>()
.UseUrls("https://localhost:5001/")
.CaptureStartupErrors(true)
.Build();
host.Run();
}
}
This works as you might expect, but I would like to remove the certificate's password string from the code.
I have used the new (to me anyway) Secrets Manager Tool in the rest of the application, but I cannot find a way to reference this at this stage in the application pipeline.
Is this possible? If not, what other options might I look at?
Thanks.
I am not sure whether you can use the Secrets API. But you can read the password either from Environment variables or appsettings.json file. Here is the sample code. I am using .NET Core 2.0 code, which is similar to .NET Core 1.1.
public class Program
{
public static void Main(string[] args)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddEnvironmentVariables()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
BuildWebHost(args).Run();
}
public static IConfigurationRoot Configuration { get; set; }
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args).UseKestrel(options =>
{
var password = Configuration["certPassword"];
options.Listen(System.Net.IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps("testCert.pfx", password);
listenOptions.UseConnectionLogging();
});
})
.UseStartup<Startup>()
.Build();
}
Hope it helps.
This is a quick solution based on the following article who explain how to add the user secret in console application.
// secrets.json (you can access to this file in Visual Studio via right click on your project in Solution Explorer => Manage user secrets)
{
"SecretSection": {
"Secret1": "Value1",
"Secret2": "Value2"
}
}
// YourSecretSettings.cs
public class YourSecretSettings
{
public string Secret1 { get; set; }
public string Secret2 { get; set; }
}
// Program.cs
public class Program
{
public static void Main(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddEnvironmentVariables()
.AddJsonFile("appsettings.json", optional: false)
.AddUserSecrets<YourSecretSettings>()
.Build();
var secretSettings = config.GetSection("SecretSection").Get<YourSecretSettings>();
// Do Something with your secret settings ...
CreateWebHostBuilder(args)
.Build()
.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
Et VoilĂ :)