Azure function v3 config file with DI getting nulls - dependency-injection

We have an Azure function v3 and we need to inject a service that requires an IConfiguration so our code is the following:
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton<IService>((s) =>
{
var configuration = s.GetService<IConfiguration>();
return new Service(configuration);
});
}
}
The local.settings.json:
{
"IsEncrypted": false,
"Values": {
"myConfig1": "xx"
}
}
The service is attempting to get the values but gets nulls:
public Service(IConfiguration configuration)
{
string myConfig1 = configuration["myConfig1"];

I solved it with this:
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton<IService>((s) =>
{
IConfiguration configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("local.settings.json", true, true)
.Build();
return new Service(configuration);
});
}
}

Related

Background Thread that uses ApplicaitonDBContext

I am trying to wire up a background thread that will update the database once an hour from Active Directory. I am not sure how to pass the current
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Connection")));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddSessionStateTempDataProvider();
services.AddSession();
services.AddHttpContextAccessor();
services.AddSingleton(Configuration);
services.AddScoped<IAppDbRepository, AppDbRepository>();
services.AddScoped<IActiveDirectoryUtility, ActiveDirectoryUtility>();
services.AddScoped<IActiveDirectoryManager, ActiveDirectoryManager>();
services.AddHostedService<LdapManager>();
services.AddScoped<ILdapManager, LdapManager>();
}
In the LdapManager class I would like to call the UpdateUsers method every hour:
public class LdapManager : ILdapManager, IHostedService
{
private IConfiguration _configuration = null;
private Logging _logger;
private List<string> ldapConnectorForDirectoryEntries = new List<string>();
public LdapManager(IConfiguration configuration)
{
_configuration = configuration;
UpdateUsers();
SyncActiveDirectoryUsers();
}
public void SyncActiveDirectoryUsers()
{
try
{
using (var waitHandle = new AutoResetEvent(false))
{
ThreadPool.RegisterWaitForSingleObject(waitHandle, (state, timeout) => { UpdateUsers(); }, null, TimeSpan.FromHours(1), false);
}
}
catch
{
throw;
}
}
}
The UpdateUsers() method should be able to call the applicationDBContext.SaveChanges() method.
How can I ensure that the LDAP manger class can use the Application DB context?
You probably want class LdapManager : BackgroundService, ILdapManager
BackgroundService is .NET Core 2.1, there is a code sample available for core 2.0
Inject IServiceScopeFactory and override Task ExecuteAsync( ), run a while loop there.
while(!stoppingToken.IsCancellationRequested)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
...; // do your stuff
}
await Task.Delay(myConfig.BackgroundDelay, stoppingToken);
}
And here is a good read about this on MSDN, including the code sample for 2.0
For accessing ApplicationDbContext from HostedService.
DbHostedService
public class DbHostedService : IHostedService
{
private readonly ILogger _logger;
public DbHostedService(IServiceProvider services,
ILogger<DbHostedService> logger)
{
Services = services;
_logger = logger;
}
public IServiceProvider Services { get; }
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Consume Scoped Service Hosted Service is starting.");
DoWork();
return Task.CompletedTask;
}
private void DoWork()
{
_logger.LogInformation("Consume Scoped Service Hosted Service is working.");
using (var scope = Services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
var user = context.Users.LastOrDefault();
_logger.LogInformation(user?.UserName);
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Consume Scoped Service Hosted Service is stopping.");
return Task.CompletedTask;
}
}
Register DbHostedService
services.AddHostedService<DbHostedService>();

How to inject dependency in Entity Framework based controller

I need to read the appsetings.json from inside an auto generate controller based on entity framework and I'm trying to inject the IConfiguration interface on it but I cannot find out how.
In the Startup.cs file, I just have this:
services.AddDbContext<controle_eventosContext>(options => options.UseNpgsql(connection));
and in the controller this:
public ContatosController(controle_eventosContext context)
{
_context = context;
}
Where should I inject the IConfiguration dependency?
As Dhreeraj comment, you can inject in controller constructor, for example:
In your Startup.cs:
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(Configuration);
}
In your Controller
public class ContactController : Controller
{
private readonly IConfiguration _configuration;
public AccountController(IConfiguration configuration)
{
_configuration = configuration;
}
public IActionResult Index()
{
var data = Configuration["version"].ToString();
data += Configuration["Settings:Name"].ToString();
return Content(data);
}
}
Your appsettings.json:
{
"Version": "1.0.0",
"Settings": {
"Name": "App"
}
}
More info:
https://mva.microsoft.com/en-US/training-courses/aspnet-core-advanced-18155
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.1&tabs=basicconfiguration

Asp core, Object reference not set to an instance of an object in Repository pattern

I am working on an asp core 1.1 and i want to create a select repository pattern in my project.
my code in repository class :
public class AuthorReposetory : IDisposable
{
private static ApplicationDbContext _context;
public AuthorReposetory(ApplicationDbContext context)
{
_context = context;
}
public static List<Author> GetAuthorList()
{
List<Author> model = new List<Author>();
model = _context.authors.Select(a => new Author
{
AuthorId = a.AuthorId,
AuthorName = a.AuthorName,
AuthorDescription = a.AuthorDescription
}).ToList();
return model;
}
public void Dispose()
{
throw new NotImplementedException();
}
~AuthorReposetory()
{
Dispose();
}
}
and in controller
[HttpGet]
public IActionResult Index()
{
var q = AuthorReposetory.GetAuthorList();
return View(q);
}
Update
Here is my StartUp Class
public class Startup
{
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)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//define Connection string
services.AddDbContext<ApplicationDbContext>(option => option.UseSqlServer(Configuration.GetConnectionString("DefualtConnection")));
//For Create and use identity in database
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Add framework services.
services.AddMvc();
services.AddAutoMapper();
services.AddPaging();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentity();
app.UseMvc(routes =>
{
//New Area For Admin
routes.MapRoute(
name: "Admin",
template: "{area:exists}/{controller=Admin}/{action=Index}/{id?}");
//New Area For User
routes.MapRoute(
name: "UserProfile",
template: "{area:exists}/{controller=UserProfile}/{action=Index}/{id?}");
//tranditional Routing
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
My Connection string in appsettings.json
{
"ConnectionStrings": {
"DefualtConnection" : "Data Source=.;Initial Catalog=DataBaseName;User ID = sa; Password = 123"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
}
The problem is Object reference not set to an instance of an object when run model = _context.authors.Select(a => new Author in repository.
In the above i show controller code, Repository class code and start up code to figure out.
Where can the problem be?
Note : everything is good in controller. just problem is in repository class.
You are using static methods and the constructor is never called as you never create the object. You would have to change your constructor to:
public static AuthorReposetory()
{
_context = new ApplicationDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefualtConnection")));
}
but that's quite bad practice since you have to setup the connection in the constructor and this also created a hard dependency on your context class.
A better solution would be to create a non static class
public class AuthorRepository : IAuthorRepository
{
private ApplicationDbContext _context;
public AuthorRepository(ApplicationDbContext context)
{
_context = context;
}
// ...
}
public interface IAuthorRepository
{
// ...
}
Reverse the dependency with injection:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//define Connection string and setup dependency injection
services.AddDbContext<ApplicationDbContext>(option => option.UseSqlServer(Configuration.GetConnectionString("DefualtConnection")));
services.AddScoped<IAuthorRepository, AuthorRepository>();
// ...
}
Controller:
public HomeController
{
private IAuthorRepository _authorRepository;
public HomeController(IAuthorRepository authorRepository)
{
_authorRepository = authorRepository;
}
[HttpGet]
public IActionResult Index()
{
var q = _autorRepository.GetAuthorList();
return View(q);
}
}
Probably your _context object is null. How is your DI/IoC configured? I would investigate in that way.
Your db context should be added like this :
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext(options => options.UseSqlite("Data Source=blog.db"));
}
Here is the documentation on how to configure your db context: https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext
You have not registered your repository class in the services so it is not getting resolved via the container. In your ConfigureServices method try this:
services.AddScoped<AuthorReposetory>();

Resolve dependency based on configuration

In my windows service application I need to resolve components using configuration when service is starting. I use Castle Windsor as my IoC container.
Application looks like:
public class RootComponent : IRootComponent {
public RootComponent (IDataProvider1 provider1, IDataProvider2 provider2)
{
this.provider1 = provider1;
this.provider2 = provider2;
}
...
}
public class DataProvider1 : IDataProvider1
{
...
public DataProvider1 (IDbHelper dbHelper)
{
this.dbHelper = dbHelper;
}
...
}
public class DataProvider2 : IDataProvider2
{
...
public DataProvider1 (IDbHelper dbHelper)
{
this.dbHelper = dbHelper;
}
...
}
public interface IDbHelper
{
IDbConnection GetNewConnection();
DbParameter CreateDbParameter(string paramName, object paramValue);
string GetCommandString(string commandName);
}
public class MsSqlDbHelper : IDbHelper
{
...
public MsSqlDbHelper(string connectionString)
{
this.connectionString = connectionString;
}
...
}
public class PostgreDbHelper : IDbHelper
{
...
public PostgreDbHelper(string connectionString)
{
this.connectionString = connectionString;
}
...
}
I would like to register dependencies in service constructor and in OnStart method read the configuration and resolve correct IDbHelper based on it:
public partial class MyWindowsService : ServiceBase
{
public MyWindowsService()
{
InitializeComponent();
this.container = new WindsorContainer();
RegisterDependencies();
}
private void RegisterDependencies()
{
container.Register(
Classes.FromAssemblyInThisApplication().BasedOn<IRootComponent>().WithServiceFromInterface(),
Classes.FromAssemblyInThisApplication().BasedOn<IDataProvider1>().WithServiceFromInterface(),
Classes.FromAssemblyInThisApplication().BasedOn<IDataProvider2>().WithServiceFromInterface(),
Classes.FromAssemblyInThisApplication().BasedOn<IDbHelper>().WithServiceFromInterface()
.ConfigureFor<MsSqlDbHelper>(
registration => {
registration.DependsOn(Dependency.OnValue<string>(ConnectStringProvider.GetConnectionString("connectString1")));
registration.Named("msSql");
})
.ConfigureFor<PostgreDbHelper>(
registration => {
registration.DependsOn(Dependency.OnValue<string>(ConnectStringProvider.GetConnectionString("connectString2")));
registration.Named("postgreSql");
}));
}
protected override void OnStart(string[] args)
{
ResolveDependencies();
}
private void ResolveDependencies()
{
// Helper properties contain "msSql" or "postgreSql" value
root = container.Resolve<IRootComponent>(Config.Provider1Helper, Config.Provider2Helper);
}
...
}
I see three options how to resolve configured IDbHepler:
Typed factory facility
Child containers
Implement IHandlerSelector
What is the best way and why?
Solution is simpler than I expected. DynamicParameters is what I neeed:
private void RegisterDependencies()
{
container.Register(
Classes.FromAssemblyInThisApplication().BasedOn<IRootComponent>().WithServiceFromInterface(),
Classes.FromAssemblyInThisApplication().BasedOn<IDataProvider1>().WithServiceFromInterface()
.Configure(registration => registration.DynamicParameters((kernel, parameters) =>
{
IDbHelper dbHelper = kernel.Resolve<IDbHelper>(Config.Provider1Helper);
parameters.Add("dbHelper", dbHelper);
return k => k.ReleaseComponent(dbHelper);
})),
Classes.FromAssemblyInThisApplication().BasedOn<IDataProvider2>().WithServiceFromInterface(),
.Configure(registration => registration.DynamicParameters((kernel, parameters) =>
{
IDbHelper dbHelper = kernel.Resolve<IDbHelper>(Config.Provider2Helper);
parameters.Add("dbHelper", dbHelper);
return k => k.ReleaseComponent(dbHelper);
})),
Classes.FromAssemblyInThisApplication().BasedOn<IDbHelper>().WithServiceFromInterface()
.ConfigureFor<MsSqlDbHelper>(
registration => {
registration.DependsOn(Dependency.OnValue<string>(ConnectStringProvider.GetConnectionString("connectString1")));
registration.Named("msSql");
})
.ConfigureFor<PostgreDbHelper>(
registration => {
registration.DependsOn(Dependency.OnValue<string>(ConnectStringProvider.GetConnectionString("connectString2")));
registration.Named("postgreSql");
}));
}
protected override void OnStart(string[] args)
{
// Helper properties should contain "msSql" or "postgreSql" value
// take configuration from arguments
Config.Provider1Helper = UseMsSql(args, 0) ? "msSql" : "postgreSql";
Config.Provider2Helper = UseMsSql(args, 1) ? "msSql" : "postgreSql";
ResolveDependencies();
}
private void ResolveDependencies()
{
root = container.Resolve<IRootComponent>();
}
private bool UseMsSql(string[] args, int argNumber)
{
...
}

Orchard how to get module settings in Controller

I got 2 kind of setting in my module.
Global settings (for the whole module)
Local settings (different for every widget)
Now I have a controller in wich I want both settings.
In controller I can get global settings like:
var globalSettings = Services.WorkContext.CurrentSite.As<Interspire_MailingSettingsPart>();
But how can I get local settings?
Both handlers:
Global
public class MailingSettingsHandler: ContentHandler
{
public MailingSettingsHandler(IRepository repository)
{
T = NullLocalizer.Instance;
Filters.Add(new ActivatingFilter("Site"));
Filters.Add(new TemplateFilterForPart("Nastavitve", "Parts/MailingSettings", "Interspire"));
Filters.Add(StorageFilter.For(repository));
}
public Localizer T { get; set; }
protected override void GetItemMetadata(GetContentItemMetadataContext context) {
if (context.ContentItem.ContentType != "Site")
return;
base.GetItemMetadata(context);
context.Metadata.EditorGroupInfo.Add(new GroupInfo(T("Interspire")));
}
}
Local
public class MailingHandler : ContentHandler
{
public MailingHandler(IRepository repository)
{
Filters.Add(StorageFilter.For(repository));
}
}
EDIT:
Adding driver if it could help things:
public class MailingDriver : ContentPartDriver<Interspire_MailingPart>
{
private readonly IOrchardServices _services;
public MailingDriver(INotifier notifier, IOrchardServices services)
{
_services = services;
}
protected override DriverResult Display(Interspire_MailingPart part, string displayType, dynamic shapeHelper)
{
var mailingSettings = _services.WorkContext.CurrentSite.As<Interspire_MailingSettingsPart>();
return ContentShape("Parts_Mailing",
() => shapeHelper.Parts_Mailing(
Url: mailingSettings.Url,
SubscribeFormID: part.SubscribeFormID
));
}
//GET
protected override DriverResult Editor(Interspire_MailingPart part, dynamic shapeHelper)
{
return ContentShape("Parts_Mailing_Edit",
() => shapeHelper.EditorTemplate(
TemplateName: "Parts/Mailing",
Model: part,
Prefix: Prefix));
_services.WorkContext.CurrentSite.As<Interspire_MailingPart>() = part;
}
//POST
protected override DriverResult Editor(Interspire_MailingPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}

Resources