Dependency injection for IOptions - asp.net-core

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.

Related

DatabaseContext object is disposed ASP.NET Core

When I try to retrieve data from a table from database using Entity Framework Core in class, I get an exception:
System.ObjectDisposedException: 'Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
My code looks like this:
Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<DatabaseContext>(
options => options.UseSqlServer(Configuration.GetConnectionString("DatabaseContextConection")));
services.AddIdentity<ApplicationUser, IdentityRole>(config => {
config.User.RequireUniqueEmail = true;
config.Password.RequiredLength = 8;
config.Password.RequireNonAlphanumeric = false;
config.Password.RequireUppercase = false;
config.Password.RequireLowercase = false;
// config.COO
}).AddEntityFrameworkStores<DatabaseContext>();
services.AddScoped<IDatabaseChangeNotificationService, SqlDependencyService>();
services.AddSignalR();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,IDatabaseChangeNotificationService notificationService)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name:"",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapHub<ChatHub>("/ChatHub");
});
notificationService.Config();
}
}
DatabaseContext.cs:
public class DatabaseContext: IdentityDbContext<ApplicationUser> ,IApplicationDbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options):base(options)
{
}
public DbSet<Message> Messages { get; set; }
public new async Task<int> SaveChanges()
{
return await base.SaveChangesAsync();
}
}
SqlDependencyService.cs:
public interface IDatabaseChangeNotificationService
{
void Config();
}
public class SqlDependencyService : IDatabaseChangeNotificationService
{
private UserManager<ApplicationUser> _userManager;
public string FullName;
public string UserId;
private readonly IConfiguration configuration;
private readonly IHubContext<ChatHub> chatHub;
private DatabaseContext _DBContext;
public SqlDependencyService(DatabaseContext DbContext, IConfiguration _configuration, IHubContext<ChatHub> _chatHub, UserManager<ApplicationUser> userManager)
{
_DBContext = DbContext;
configuration = _configuration;
chatHub = _chatHub;
_userManager = userManager;
}
public void Config()
{
TableUsersAvailabilitySensor();
}
private void TableUsersAvailabilitySensor()
{
string connectionString = configuration.GetConnectionString("DatabaseContextConection");
using (var conn = new SqlConnection(connectionString))
{
if (conn.State != System.Data.ConnectionState.Open)
{
conn.Open();
}
using (var cmd = new SqlCommand(#"Select IsActive from [dbo].AspNetUsers", conn))
{
cmd.Notification = null;
SqlDependency dependency = new SqlDependency(cmd);
dependency.OnChange += TableUsersChanged;
SqlDependency.Start(connectionString);
cmd.ExecuteReader();
}
}
}
List<string> AvailableUsers = new List<string>();
private void TableUsersChanged(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
string text = checkAvailabilityChanged(e);
if (text == "Updated" || text == "Inserted")
{
var list = _DBContext.Users.Where(a => a.IsActive == true).ToList();
AvailableUsers.AddRange(list.Select(a => a.UserName));
var currentUserId = _userManager.GetUserId(AccountController.currentUser);
var _currentUser = _DBContext.Users.Find(currentUserId);
FullName = _currentUser.FirstName + " " + _currentUser.LastName;
UserId = currentUserId;
chatHub.Clients.Clients(AvailableUsers).SendAsync("AddMeToYourContacts", FullName, UserId);
}
}
TableUsersAvailabilitySensor();
}
private string checkAvailabilityChanged(SqlNotificationEventArgs e)
{
switch (e.Info)
{
case SqlNotificationInfo.Update:
return "Updated";
case SqlNotificationInfo.Delete:
return "Deleted";
case SqlNotificationInfo.Insert:
return "Inserted";
default:
return "Nothing occurred";
}
}
}
The exception is thrown on this line of code:
var list = _DBContext.Users.Where(a => a.IsActive == true).ToList();

asp.net core how to change connection string at run time from entity framework (after login)

I have a .NET Core 3.0 web application. I would like to change the connection string at run time once login is successful.
IMO,you could not change the services.AddDbContext<T> at runtime.A workaround is that you add a DBContextFactory to create new dbcontext object when you login successfully.
Refer to following steps:
1.Create a DBContextFactory.cs
public static class DbContextFactory
{
public static Dictionary<string, string> ConnectionStrings { get; set; }
public static void SetConnectionString(Dictionary<string, string> connStrs)
{
ConnectionStrings = connStrs;
}
public static ApplicationDbContext Create(string connid)
{
if (!string.IsNullOrEmpty(connid))
{
var connStr = ConnectionStrings[connid];
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(connStr);
return new ApplicationDbContext(optionsBuilder.Options);
}
else
{
throw new ArgumentNullException("ConnectionId");
}
}
}
2.Intialize DbContextFactory in startup Configure
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
Dictionary<string, string> connStrs = new Dictionary<string, string>();
connStrs.Add("DB1", "Your connection string 1");
connStrs.Add("DB2", "Your connection string 2");
DbContextFactory.SetConnectionString(connStrs);
//other middlewares
}
3.Usage
if(status)
{
var dbContext = DbContextFactory.Create("DB2");//get the dbcontext with connection string 2
}
i'm korean and Do not speak English well.
login after save session/cookie/DB UserConfig use, then DBContext edit
public class GroupwareContext : DbContext
{
private readonly HttpContext _httpContext;
public GroupwareContext(DbContextOptions<GroupwareContext> options, IHttpContextAccessor httpContextAccessor = null)
: base(options)
{
_httpContext = httpContextAccessor?.HttpContext;
}
public GroupwareContext(DbContextOptions<GroupwareContext> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (_httpContext != null) {
if (_httpContext.Session.GetString("connectionString") != null)
{
var connectionString = (string)_httpContext.Session.GetString("connectionString");
optionsBuilder.UseSqlServer(connectionString);
}
}
}
public DbSet<Account> Account { get; set; }
}

Asp.Net Core 2 Web API 500 internal server error

when I try to access the API via Postman,
Send:
localhost:5050/api/Auth/token
Body:
{ "UserName": "jouverc", "Password": "P#ssw0rd!" }
to this method:
[Produces("application/json")]
[Route("api/Auth")]
public class AuthController : Controller
{
#region constructor injection
private readonly IPasswordHasher<User> _hasher;
private readonly UserManager<User> _userManager;
private readonly IConfigurationRoot _config;
private readonly SignInManager<User> _signInManager;
public AuthController(IPasswordHasher<User> hasher, UserManager<User> userManager, SignInManager<User> signInManager, IConfigurationRoot config)
{
_hasher = hasher;
_userManager = userManager;
_signInManager = signInManager;
_config = config;
}
#endregion
#region createToken
[HttpPost("token")]
public async Task<IActionResult> CreateToken([FromBody] CredentialModel model)
{
try
{
var user = await _userManager.FindByNameAsync(model.UserName);
if (user != null)
{
if (_hasher.VerifyHashedPassword(user, user.PasswordHash, model.Password) == PasswordVerificationResult.Success)
{
return Ok(CreateToken(user));
}
}
}
catch (Exception)
{
//log
}
return null;
}
private async Task<JwtPacket> CreateToken(User user)
{
var userClaims = await _userManager.GetClaimsAsync(user);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString())
}.Union(userClaims);
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(issuer: _config["Tokens:Issuer"],
audience: _config["Tokens:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddDays(2),
signingCredentials: cred
);
return new JwtPacket
{
Token = new JwtSecurityTokenHandler().WriteToken(token),
Expiration = token.ValidTo.ToString(),
UserName = user.UserName
};
}
public class JwtPacket
{
public string Token;
public string UserName;
public string Expiration;
}
#endregion
}
I receive a 500 Internal Server Error:
Unable to resolve service for type 'Microsoft.Extensions.Configuration.IConfigurationRoot' while attempting to activate 'WebAPI.Controllers.AuthController
how should i configurate the 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.
In .net core 2.0
IConfigurationRoot is now just IConfiguration.
Explained in this document: Migrating from 1.x to 2.0.
In 2.0 projects, the boilerplate configuration code inherent to 1.x projects runs behind-the-scenes. For example, environment variables and app settings are loaded at startup. The equivalent Startup.cs code is reduced to IConfiguration initialization with the injected instance:
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
Just change IConfigurationRoot to IConfiguration on the constructor for the controller.
But what might be better is to use the IOpions pattern for injecting settings into your controller.
If you are just reading values from the appsettings.json then use the IConfiguration interface instead.
Here's how to implement it in the Startup class
public Startup(IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddEnvironmentVariables()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(Configuration);
}

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);
}
}

How to access Database from Asp.netcore By using Entityfrmework

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());
}
}