How to access Database from Asp.netcore By using Entityfrmework - asp.net-core

Here i setup some Db connection from Anguar2 to database but when i try to hit database I'm Getting error as Additional information: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the
This is my database connection
{
"connectionStrings": {
"DefaultConnection": "Server=MD;Database=Md;userid=sa;password=123;Trusted_Connection=True;MultipleActiveResultSets=true;"
}
startUp.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<StudentContext>(option => option.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
}
DbContext.cs
public class StudentContext:DbContext
{
public StudentContext(DbContextOptions<StudentContext> options) : base(options) { }
public StudentContext() { }
public DbSet<StudentMaster> StudentMaster { get; set; }
}

Reference the correct variable in the appsettings.json (or whatever the name of your file)
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
then make sure you get the intended variable (connectionStrings:DefaultConnection for your case)
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<StudentContext>(option => option.UseSqlServer(Configuration.GetConnectionString("connectionStrings:DefaultConnection")));
services.AddMvc();
}
you can also set the connection string on the data context directly by overriding the OnConfiguring like this
public class StudentContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer("_connectionString_", _options => _options.EnableRetryOnFailure());
}
}

Related

Dependency injection for IOptions

Not sure what I am doing wrong here. IOptions<ConnectionStrings> is not updating with new values.
appSettings.json
"ConnectionStrings": {
"Database": "UserID={0};Password={1};Host=xyz.com;Port=5432;Database=xyz;Pooling=true;SSL Mode=Require;Trust Server Certificate=true"
}
Program.cs
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.SetSecretsAsEnvironmentVariables() //where I am setting environment variable from secret.json
.UseStartup<Startup>()
.ConfigureAppConfiguration((hostContext, config) =>
{
var env = hostContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
config.AddEnvironmentVariables();
if (hostContext.HostingEnvironment.IsDevelopment())
{
config.AddUserSecrets<Program>();
}
})
.Build();
host.Run();
}
StartUp:
public IConfiguration Configuration { get; private set; }
public IHostingEnvironment HostingEnvironment { get; private set; }
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
this.HostingEnvironment = env;
this.Configuration = configuration;
}
private void ConfigureAppSettings(IServiceCollection services)
{
services.Configure<ConnectionStrings>(Configuration.GetSection("ConnectionStrings"));
Configuration.GetSection("ConnectionStrings").Bind(Service.Configuration.Configuration.ConnectionStrings);
Service.Configuration.Configuration.ConnectionStrings.Database =
string.Format(Service.Configuration.Configuration.ConnectionStrings.Database, "abc",
"xyz"); //here I am setting up new value for UserID and Password.
}
MyConnectionFactory.cs
public class ConnectionFactory : IConnectionFactory
{
private readonly ConnectionStrings _connectionStrings;
public ConnectionFactory(IOptions<ConnectionStrings> connectionStrings)
{
_connectionStrings = connectionStrings.Value;// Here connection string values are always withn{0},{1}.
}
public NpgsqlConnection CreateOpenConnection()
{
var conn = new NpgsqlConnection(_connectionStrings.QuoteDatabase);
conn.Open();
return conn;
}
In my ServiceRegistry.cs
ForSingletonOf<IConnectionFactory>().Use<MyConnectionFactory>();
ForSingletonOf<ConnectionStrings>().Use(new ConnectionStrings() { Database = Configuration.Configuration.ConnectionStrings.Database });
Don't know What I am doing wrong here. My Connection string values are not updating with new values. If I am changing in MyConnectionFactory.cs from IOptions<ConnectionStrings> to ConnectionStrings the values are updating with new one.
Please anyone me help me out here.

.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

App Settings .Net Core

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.

IConfiguration object is null.

I'm trying to read connection strings from appsettings.json and I'm using:
services.AddSingleton(Configuration);
This line from startup throws null. I'm pretty new to core2.0. Can someone tell what I'm missing?
My startup:
public class Startup
{
public static string ConnectionString { get; private set; }
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton(Configuration);
services.AddSingleton<IConfiguration>(Configuration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
My controller:
public class CreateController : Controller
{
public IConfiguration _ConnectionString;
public CreateController(IConfiguration configuration)
{
_ConnectionString = configuration;
}
public IEnumerable<string> Get()
{
Markets();
}
public string Markets()
{
using(SqlConnection con = new SqlConnection(_ConnectionString.GetSection("Data").GetSection("ConnectionString").Value))
{
return con.Database;
}
}
}
I've noticed your Startup is missing a constructor. In ASP.NET Core 2, when it calls startup (based on a typical WebHost.CreateDefaultBuilder(args).UseStartup<Startup>() inside a vanilla Program.BuildWebHost) will automatically pass the configuration into the Startup constructor:
public class Startup
{
public IConfiguration Configuration { get; }
// This configuration is automatic, if WebHost.CreateDefaultBuilder(args) is used in Program.cs
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
Just adding that will solve your IConfiguration is null issue.
Now, you should not have to add that Configuration into DI because with the defaults it should already be in there and you can add it as-is to your controller constructor. However, there's no harm in doing so.
Lastly, to join the chorus, using IConfiguration directly in you controllers is not a good idea. Rather look into strongly typed configuration settings. There are tutorials out there that can help - here's the first link I found - but the gist is your controller will end up looking sort of like this:
public class CreateController : Controller
{
public ConnectionStrings _ConnectionStrings;
public CreateController(IOptions<ConnectionStrings> connectionStrings)
{
_ConnectionStrings = connectionStrings.Value;
...
You shouldn't be calling services.AddSingleton(Configuration) in ConfigureServices. It is already in the DI container by default.
You simply need to reference it within your Controler:
public class CreateController : Controller
{
public IConfiguration _configuration;
public CreateController(IConfiguration configuration)
{
_configuration = configuration;
}
public IEnumerable<string> Get()
{
Markets();
}
public string Markets()
{
var connectionString = _configuration.GetConnectionString("ConnectionStringName");
using( SqlConnection con = new SqlConnection(connectionString) )
{
return con.Database;
}
}
}
It is null because it hasn't been set. You need to build your configuration first which is best done in the constructor. As others have pointed out it is not recommended to do this.
Example
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();
if (env.IsDevelopment())
{
builder.AddUserSecrets<Startup>();
}
Configuration = builder.Build();
}
private IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Config);
}
}

.Net Core passing connection string to DBContext class

Just started using .Net Core and facing passing connection string information to Context console project.
I have 4 projects, created using .Net Core.
MVC
Service Layer
Domain Layer
Data Layer
In MVC project, I have Startup.cs file where i am reading appsettings.json file
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
// Add appsettings
services.Configure<AppSettingsConfig>(Configuration.GetSection("AppSettings"));
}
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
if (env.IsDevelopment())
{
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
builder.AddApplicationInsightsSettings(developerMode: true);
}
Configuration = builder.Build();
}
In my 4th project (Data Layer), which Console Project and having following DBContext class. This project doesn't have Startup.cs as i MVC project having. Not created by default by VS 2015.
public class MyDWContext : DbContext
{
public MyDWContext() : base ()
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Data Source=localhost;Initial Catalog=MyDW; Persist Security Info = False; User ID = TempUser; Password = Temp123");
}
public DbSet<User> Users { get; set; }
public DbSet<Class> Classs { get; set; }
}
I have been to other post as well but i believe its created using older version or RC version. So some time i cannot find correct object or .Net classes.
As i have connection string is in MVC project, how can i use connection string during my MVC call to Data layer.
I have Web.API (Core) project as well and that having own connection string (different user configuration in connection string which having only read access). How can i use Web2.API connection string when i am making call from Web2.API project.
Instead of passing connection string to DbContext, configuring DbContext in Startup.cs(if possible) is better way. See official docs to understand how to configure DbContext and use it via Dependency Injection.
EDIT : Below code is not good way
However, if you want to pass connection string to DbContext you can use options pattern.
Here is an example how to pass connection string with options pattern:
First you need an options class which accessible from Data Layer and MVC layer
public class ConnectionStringOption
{
public string ConStr { get ; set; }
}
Then set option value
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<ConnectionStringOption>(options=>
{
// set connection string from configuration
options.ConStr = Configuration.GetConnectionString("Default");
});
}
appsetting.json
{
"ConnectionStrings": {
"Default": "<your connection string>"
}
}
Finally DbContext
private readonly IOptions<ConnectionStringOption> _conStrOptions;
protected YourDbContext()
{
}
public YourDbContext(IOptions<ConnectionStringOption> conStrOptions, DbContextOptions options)
: base(options)
{
_conStrOptions= conStrOptions;
}
Edit for another way
Using Static Service Locator may be a solution:
Create a DependencyResolver in Data Layer
public static class DependencyResolver
{
private static IServiceProvider _provider;
public static IServiceProvider ServiceProvider
{
get
{
return _provider;
}
set
{
if(_provider == null)
{
_provider = value;
}
}
}
}
In ConfigureServices method
public void ConfigureServices(IServiceCollection services)
{
// other stuff
services.AddOptions();
services.Configure<ConnectionStringOption>(options=>
{
// set connection string from configuration
options.ConStr = Configuration.GetConnectionString("Default");
});
DependencyResolver.ServiceProvider = services.BuildServiceProvider();
}
And finally get option:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var conStr = DependencyResolver.ServiceLocator.GetService<IOptions<ConnectionStringOption>>().Value.ConStr;
optionsBuilder.UseSqlServer();
}
Final Edit for previous stupid way
public static class ConnectionStringGetter
{
public static string ConStr{get;set;}
}
public Startup(IHostingEnvironment env)
{
//...
Configuration = builder.Build();
ConnectionStringGetter.ConStr = Configuration.GetConnectionString("Default");
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(ConnectionStringGetter.ConStr);
}