static types cannot be used as type arguments - asp.net-core

I am porting from traditional .net mvc application to .netcore.
Original application flow:
OSMSelectController -> OSMDB_Loader.cs -> StatusController
where the connectionstring is read and DAL is initialized.
This connectionstring is coming from static class but when I debug the value is null in here:
WPSGlobalSettings.ToolboxConnString
I have a static class for reading connectionstring from appsettings.
WPSGlobalSettings
public static class WPSGlobalSettings
{
public static NpgsqlConnectionStringBuilder ToolboxConnString = build_conn_str(ToolboxDatabaseName);
private static NpgsqlConnectionStringBuilder build_conn_str(string dbname)
{
string dbSetting = ConfigurationHelper.config.GetSection("ConnectionStrings")["DefaultConnection"];
...
}
}
Controller
public class StatusController : Microsoft.AspNetCore.Mvc.ControllerBase
{
protected StatusDAL status_dal = new StatusDAL(WPSGlobalSettings.ToolboxConnString);
}
Here it gives type exception, wpsglobalsettings was not initialized and toolboxconnstring is null.
I have tried adding it as singleton to Startup but then i get
static types cannot be used as type arguments
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(Configuration);
services.AddControllersWithViews();
services.AddSingleton<WPSGlobalSettings>();
ConfigurationHelper.Initialize(Configuration);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
WPSGlobalSettings.Configure(env);
...
}
Edit:
I have removed following from Startup.cs
services.AddSingleton<WPSGlobalSettings>();
Also, introduced DI as follows
protected StatusDAL status_dal;//= new StatusDAL(WPSGlobalSettings.ToolboxConnString);
public StatusController(IConfiguration config)
{
status_dal = new StatusDAL(config.GetConnectionString("toolboxConnectionStrWPS"));
}
Now another problem is older code calls controller constructor from another class as follows:
OSMDB_Loader.cs
public StatusAck LoadOSMSections(OsmLoadRequest request)
{
StatusAck statusack = new StatusController().PostStatusRecord();
}
Therefore I also added simple constructor in StatusController:
public StatusController()
{
}
but now ofcourse status_dal is always null
Something is not quiet right!

I got some help here and here
Also, the fact that static creates problem with multi-threading..from here
Here is my solution:
Original application flow was
OSMSelectController -> OSMDB_Loader.cs -> StatusController
where the connectionstring was read and DAL was initialized.
Now:
I injected IConfiguration in OSMSelectController and passed it to OSMDB_Loader.cs
private IConfiguration _config;
public OsmSelectController(IConfiguration config)
{
_config = config;
}
public IActionResult AddRoadsOsm([FromBody]OsmLoadRequest request)
{
...
StatusAck statusack = new OSM_DBLoader(user_id).LoadOSMSections(request, _config);
}
OSMDB_Loader.cs
public StatusAck LoadOSMSections(OsmLoadRequest request, IConfiguration conf)
{
StatusAck statusack = new StatusController(conf).PostStatusRecord(); // Issue here is that you need to get the uri
...
}
This way StatusController's correct constructor is hit from where I am able to read the configuration value for connection string
StatusController
protected StatusDAL status_dal;
public StatusController()
{
status_dal = new StatusDAL(WPSGlobalSettings.ToolboxConnString);
}
public StatusController(IConfiguration config)
{
status_dal = new StatusDAL(config.GetConnectionString("toolboxConnectionStrWPS"));
}
I would still appreciate if someone can detail how GlobalSettings Static files pose a problem and to cater which we have DependencyInjection and Singleton pattern in .net core.

Related

ASP.NET Core - Create a singleton class to supply connection string across the application

I'm creating a ASP.NET Core Web API using ADO.NET (without Entity Framework). I need a singleton class to supply connection string to all the controllers. I have done the following.
Defined a class DBUtils to have just one public property DBConnectionString.
Try to register the class as a singleton in startup.cs.
Use the class through DI in each controller to access the connection string.
public class DBUtils
{
public string DBConnectionString { get; set; }
public DBUtils(string connectionString)
{
this.DBConnectionString = connectionString;
}
}
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.AddSingleton<DBUtils>();
services.AddControllers();
}
}
public class CommonController : ControllerBase
{
private string conStr;
public CommonController(DBUtils utils)
{
conStr = utils.DBConnectionString;
}
public IActionResult GetData() {
SqlConnection connection = new SqlConnection(conStr);
//Get dat from the database
return null;
}
}
Now the problem is I'm not able to pass the connection string to the DBUtils constructor. I read from other posts that we should not use parameters to Singleton classes. But my class will only have one parameter and it will never change during execution. It gets the connection string from config file.
please help how to I pass connection string to my controllers.
I don't want to use IConfiguration as DI in the controller class directly.
UPDATE: I realised that Singleton is not the approach for my requirement and as #Ceemah Four suggested we should use Options Pattern.
Thanks
This scenario has already been catered for in dotnet core.
You do not need to create the DBUtils class. Neither do you need to set up the Singleton DI etc.
Assuming this is your appsettings.json
"ConnectionStrings": {
"SqlDatabase": "connection string here"
}
There are two potential approaches:
Inject IConfiguration in Controller constructor - you can simply access the connection string value from the injected Configuration.
public class CommonController : ControllerBase
{
private readonly IConfiguration _config;
private string conStr;
public CommonController(IConfiguration config)
{
_config = config;
}
public IActionResult GetData()
{
SqlConnection connection = new SqlConnection(_config.GetConnectionString("SqlDatabase"));
//Get data from the database
return null;
}
}
Create a Settings class, bind the settings class in Startup and inject the Settings class in the controller constructor. This uses the IOPtions pattern * and is a cleaner and recommended approach*
public class ConnectionSettings
{
public string SqlDatabase { get; set; }
}
In your startup.cs:
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.Configure<ConnectionSettings>(Configuration.GetSection("ConnectionStrings"));
services.AddControllers();
}
}
Then in your controller:
public class CommonController : ControllerBase
{
private readonly IOptions<ConnectionSettings> _connectionSettings;
public CommonController(IOptions<ConnectionSettings> connectionSettings)
{
_connectionSettings = connectionSettings;
}
public IActionResult GetData()
{
SqlConnection connection = new SqlConnection(_connectionSettings.Value.SqlDatabase));
//Get data from the database
return null;
}
}

Create database context from cookie and base path in Entity Framework Core

Postgres database has multiple schemes like company1, company2, ... companyN
Browser sends cookie containing scheme name . Data access operations should occur in this scheme. Web application user can select different scheme. In this case different cookie value is set.
Npgsql EF Core Data provider is used.
ASP NET MVC 5 Core application registers factory in StartUp.cs :
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddScoped<IEevaContextFactory, EevaContextFactory>();
....
Home controller tries to use it:
public class HomeController : EevaController
{
public ActionResult Index()
{
var sm = new SchemeManager();
sm.PerformInsert();
....
This throws exception since factory member is null. How to fix this ?
public interface IEevaContextFactory
{
EevaContext Create();
}
public class EevaContextFactory : IEevaContextFactory
{
private IHttpContextAccessor httpContextAccessor;
private IConfiguration configuration;
public EevaContextFactory(IHttpContextAccessor httpContextAccessor, IConfiguration configuration)
{
this.httpContextAccessor = httpContextAccessor;
this.configuration = configuration;
}
public EevaContext Create()
{
var builder = new DbContextOptionsBuilder<EevaContext>();
var pathbase = httpContextAccessor.HttpContext.Request.PathBase.Value;
var scheme = httpContextAccessor.HttpContext.Request.Cookies["Scheme"];
var csb = new NpgsqlConnectionStringBuilder()
{
Host = pathbase,
SearchPath = scheme
};
builder.UseNpgsql(csb.ConnectionString);
return new EevaContext(builder.Options);
}
}
Scheme data acess methods:
public class SchemeManager
{
readonly IEevaContextFactory factory;
public SchemeManager(IEevaContextFactory factory)
{
this.factory = factory;
}
public SchemeManager()
{
}
public void PerformInsert()
{
using (var context = factory.Create())
{
var commandText = "INSERT into maksetin(maksetin) VALUES (CategoryName)";
context.Database.ExecuteSqlRaw(commandText);
}
}
}
var sm = new SchemeManager()
... will call the no-parameter constructor on SchemeManager so the IEevaContextFactory is not injected. You should inject your factory into your controller and pass it into your SchemeManager.
Remove your no-parameter constructor. It's not needed.
public class HomeController : EevaController
{
private IEevaContextFactor eevaFactory;
public HomeController(IEevaContextFactory factory)
{
eevaFactory = factory;
}
public ActionResult Index()
{
var sm = new SchemeManager(eevaFactory);
sm.PerformInsert();
....
}
}
Your other option is to put the SchemeManager in the DI container and then the DI container will auto-resolve IEevaContextFactory on the constructor and then just inject SchemeManager into your controller.
Either way, remove that no-parameter constructor.

How to Pass IConfiguration from API controller to Data Access Layer

I am new to ASP.net core, and trying to convert my existing RESTful WepAPIs to ASP.Net Core API (2.2).
Following is my present application structure:
[WebAPI Methods] calls -> [Business Logic Layer (BLL)] calls -> [Data Access Layer (DAL)].
In order to open DB connection from DAL, I am calling static method of another class library (Infrstructure.Data.dll) and passing connection string name as Enum. This library gets the actual connection string from the Web.Config of the webAPI using the following
string connString = ConfigurationManager.ConnectionStrings[GetDatabaseHint(dbSource)].ConnectionString;
and returns IDbConnection which my DAL uses it to transact with DB.
Now, I am looking for similar options in ASP.net core Web API.
I have so far come up with the following:
In the Startup.cs file of API, added
services.AddSingleton(Configuration);
In the Infrstructure.Data.dll class library, added
private static IConfiguration _config;
public ConnectionFactory(IConfiguration config)
{
_config = config;
}
public static IDbConnection GetConnection(DBSource dbSource)
{
string connString = _config.GetConnectionString(GetDatabaseHint(dbSource));
//... Code to create connection using DB Providers
return conn;
}
IConfiguration is always null here in this Infrstructure.Data.dll.
Please let me know if in case this is an incorrect approach, and what should be the right way of achieving this.
Follow steps below to access IConfiguration in Infrstructure.Data
Define ConnectionFactory
public class ConnectionFactory
{
private static IConfiguration _config;
public ConnectionFactory(IConfiguration config)
{
_config = config;
}
public string GetConnection()
{
return _config.GetConnectionString("any connection string name");
}
//public static IDbConnection GetConnection(DBSource dbSource)
//{
// return new dbc
//}
}
Register ConnectionFactory in Startup.cs
services.AddScoped<ConnectionFactory>();
Useage
public class HomeController : Controller
{
private ApplicationDbContext _context;
private readonly ConnectionFactory connectionFactory;
public HomeController(ApplicationDbContext context
, ConnectionFactory connectionFactory)
{
_context = context;
this.connectionFactory = connectionFactory;
}
public IActionResult Index()
{
string result = connectionFactory.GetConnection();
return View();
}
}
Note IConfiguration is registed default by WebHost.CreateDefaultBuilder(args)
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}

.Net Core How to Access Configuration Anywhere in application

I have read through the documentation on the different ways to setup and access configuration in .Net Core 2.1 and also the options pattern that seems to be recommended (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.1). However, I can't seem to get what I want working:
I have done the following:
AppSettings:
{
"ConnectionStrings": {
"DefaultConnStr": "Server=(localdb)\\MSSQLLocalDB;Database=_CHANGE_ME;Trusted_Connection=True;MultipleActiveResultSets=true;Integrated Security=true",
"AW2012ConnStr": "Server=localhost;Database=AW2012;Trusted_Connection=True;MultipleActiveResultSets=true;Integrated Security=true"
}
}
MyConfig:
public class MyConfig
{
public string AWConnStr { get; }
public string DefaultConnStr { get; }
}
Startup:
public class Startup
{
public IConfiguration _config { get; set; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
_config = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
//add config to services for dependency injection
//services.AddTransient<IMyConfig, MyConfig>();
//services.AddScoped<IMyConfig, MyConfig>();
var section = _config.GetSection("ConnectionStrings");
services.Configure<MyConfig>(section);
}
private static void HandleGetData(IApplicationBuilder app)
{
//DataHelper dataHelper = new DataHelper(_dataHelper);
var _dataHelper = app.ApplicationServices.GetService<DataHelper>();
app.Run(async context =>
{
//await context.Response.WriteAsync("<b>Get Data</b>");
//await context.Response.WriteAsync(dataHelper.GetCompetitions(context.Request.QueryString.ToString()));
await context.Response.WriteAsync(_dataHelper.GetCompetitions(context.Request.QueryString.ToString()));
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Map("/Route1", HandleRoute1);
app.Map("/Route2", HandleRoute2);
app.Map("/GetData", HandleGetData);
app.Run(async (context) =>
{
await context.Response.WriteAsync("Non Mapped Default");
});
}
}
I would like to then access the configuration in any class anywhere in my code. So for example I have the following class where I would like to just read the configuration information:
public interface IDataHelper
{
string GetCompetitions(string val);
}
public class DataHelper : IDataHelper
{
private readonly MyConfig _settings;
public DataHelper(IOptions<MyConfig> options)
{
_settings = options.Value;
}
public string GetCompetitions( string queryStringVals)
{
return _settings.AWConnStr;
}
}
As shown above in my Startup class I then want to access/call something in the HandleGetData function in my startup, so that when I browse to the following route: http://localhost:xxxxx/getdata I get back the response from the Something.GetData function.
Is this correct? The problem I'm having is that when I create an instance of class Something, it is requiring me to pass in the configuration object, but doesn't that defeat the purpose of injecting it. How should I be setting this up to work similar to how DBContext gets the context injected with the configuration options. And what's the difference between services.AddTransient and services.AddScoped? I've seen both as a way to register the service.
I would say that in .Net Core application you shouldn't pass instance of IConfiguration to your controllers or other classes. You should use strongly typed settings injected through IOtions<T> instead. Applying it to your case, modify MyConfig class (also property names should match names in config, so you have to rename either config (DefaultConnection->DefaultConnStr, AW2012ConnStr->AWConnStr or properies vice versa):
public class MyConfig
{
public string AWConnStr { get; set; }
public string DefaultConnStr { get; set; }
}
Register it:
public void ConfigureServices(IServiceCollection services)
{
// in case config properties specified at root level of config file
// services.Configure<MyConfig>(Configuration);
// in case there are in some section (seems to be your case)
var section = Configuration.GetSection("ConnectionStrings");
services.Configure<MyConfig>(section);
}
Inject it to required service:
public class MyService
{
private readonly MyConfig _settings;
public MyService(IOptions<MyConfig> options)
{
_settings = options.Value;
}
}
And what's the difference between services.AddTransient and
services.AddScoped? I've seen both as a way to register the service.
Transient lifetime services are created each time they're requested.
Scoped lifetime services are created once per request.
You have to do the same thing for the Something as you did for MyConfig like:
public interface ISomething
{
string GetSomeData();
}
Then:
public class Something : ISomething
{
public IConfiguration _config { get; set; }
public Something(IConfiguration configuration)
{
_config = configuration;
}
public string GetSomeData()
{
return _config["DefaultConnStr"];
}
}
Then in the ConfigureService method of the Startup class as follows:
services.AddScoped<ISomething,Something>();
Then call the GetSomeData() as follows:
public class CallerClass
{
public ISomething _something { get; set; }
public CallerClass(ISomething something)
{
_something = something;
}
public string CallerMethod()
{
return _something.GetSomeData();
}
}
Then:
And what's the difference between services.AddTransient and services.AddScoped? I've seen both as a way to register the service.
Here is the details about this from microsoft:
Service Lifetime details in ASP.NET Core

Which HttpConfiguration object do I need to access to create a custom HttpParameterBinding?

In this post, Mike Wasson states:
"Besides ParameterBindingAttribute, there is another hook for adding a custom HttpParameterBinding. On the HttpConfiguration object"
But I have three HttpConfiguration objects in my Web API app, namely:
public static void Register(HttpConfiguration config, IWindsorContainer container) <-- in WebApiConfig.cs
private static void MapRoutes(HttpConfiguration config) <-- ""
public static void ConfigureWindsor(HttpConfiguration configuration) <-- in Global.asax.cs
Which of these (config, config, or configuration) should I use (if any)?
UPDATE
I tried this, with a breakpoint on the "if" line:
public static void ConfigureWindsor(HttpConfiguration configuration)
{
_container = new WindsorContainer();
_container.Install(FromAssembly.This());
_container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
var dependencyResolver = new WindsorDependencyResolver(_container);
configuration.DependencyResolver = dependencyResolver;
if (configuration.Properties.Values.Count > 0) // <-- I put a Casey Jones here
{
object o = configuration.Properties.Values.ElementAt(configuration.Properties.Values.Count - 1);
string s = o.ToString();
}
}
...but I only hit that spot once, on the server starting up, but not when the client sent a request to it...there must be some event that gets fired when a server passes a request where the incoming URL can be examined...no?
Usually you do have only one instance of HttpConfiguration which is the one you get from GlobalConfiguration.Configuration.
Said so, that's how I plugged custom binders
In global.asax
var binderMappings = new Dictionary<Type, Type>
{
{typeof(YourModelType), typeof(YourModelTypeBinder)},
//....
};
config.Services.Add(
typeof(ModelBinderProvider),
new WindsorModelBinderProvider(container, binderMappings));
WindsorModelBinderProvider
public class WindsorModelBinderProvider : ModelBinderProvider
{
private readonly IWindsorContainer _container;
private readonly IDictionary<Type, Type> _binderMappings;
public WindsorModelBinderProvider(IWindsorContainer container, IDictionary<Type, Type> binderMappings)
{
_container = container;
_binderMappings = binderMappings;
}
public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType)
{
IModelBinder binder = null;
if (_binderMappings.ContainsKey(modelType))
{
binder = _container.Resolve(_binderMappings[modelType]) as IModelBinder;
if (binder == null)
{
throw new ComponentNotFoundException(modelType);
}
}
return binder;
}
}
YourModelTypeBinder
public class YourModelTypeBinder : IModelBinder
{
public YourModelTypeBinder(IYourServiceToLoadYourModelType service)
{
//...
}
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
bindingContext.Model = YourCustomCodeToLoadYourModelTypeUsingTheConstructorDependecies(actionContext.Request);
return true;
}
private YourModelType YourCustomCodeToLoadYourModelTypeUsingTheConstructorDependecies(HttpRequestMessage requestMessage)
{
...
}
}
YourModelTypeBinder will be resolved by the container(see WindsorModelBinderProvider), so you need to registered it first.
After all that plumbing, your controller may have a parameter, among others, as following
[ModelBinder]YourModelType user