ASP.NET CORE: Change connectionString through WEBAPI - asp.net-web-api

I have declared connection string in appsetting.json file. I would like to change the connection string through web api call.
Is there a way to do it?
I am working with ASP.Net Core.
appsettings.json:
"Database": {
"ProviderName": "MySQL",
"ConnectionString": "server=localhost;database=sampledb;uid=user;pwd=user"
},
Startup.cs
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
//Accessing Database section from appsettings.json
services.Configure<dbSettings>(StaticSettings.GetSection("Database"));

Firstly, in development environment the right place to store connection string is in User Secrets. In Visual Studio, Right click on Project > Manage User Secrets, opens secrets.json. User Secrets option works only on your dev. machine.
While if your app is running on Azure, Azure Portal has application settings options to define connection strings for your app.
Coming to your question, you can store multiple connection strings in application settings. For example:
"ConnectionStrings": {
"dev_db": "Server=devSQLServerName;Database=dev;User Id=userName;Password=yourPassword",
"test_db": "Server=testSQLServerName;Database=test;User Id=userName;Password=yourPassword",
"staging_db": "Server=stagingSQLServerName;Database=staging;User Id=userName;Password=yourPassword",
"production_db": "Server=prodSQLServerName;Database=staging;User Id=userName;Password=yourPassword"
}
Now, write a method to get the connection string based on your environment.
public string GetDBConnectionString(string environment)
{
string connectionString = string.Empty;
if (environment == "production")
{
connectionString = Configuration["ConnectionStrings:production_db"];
}
else if (environment == "development")
{
connectionString = Configuration["ConnectionStrings:dev_db"];
}
else if (environment == "test")
{
connectionString = Configuration["ConnectionStrings:test_db"];
}
else if (environment == "stage")
{
connectionString = Configuration["ConnectionStrings:staging_db"];
}
if (connectionString == null)
{
throw new Exception("Could not locate production DB connection string for env: " + environment );
}
else
{
return connectionString;
}
}
Now call this method from your Web API's action method or where ever you want to consume.

Related

How to start GraphQL server when running .net5 integration tests?

I believe I am missing/misunderstanding something fundamental about the way .net5 works. In setting up an integration test environment for my GraphQL API, I am missing the step on how to start the GraphQL server from said test environment.
When I run the main project, the server is started properly and I can navigate to localhost in the browser and successfully execute GraphQL queries/mutations. My goal here is to set up some automated integration tests.
I'm using NUnit as my test runner and am using WebApplicationFactory<Startup> to "start the server" as I understand it.
In my test project, I'm under the impression that WebApplicationFactory<Startup> is supposed to basically use the Startup.cs class from my main project in my test project so that I don't have to duplicate all the settings, configurations, and injected services. Please correct me if that assumption is not correct.
I've pasted the code I think is relevant.
ApiWebApplicationFactory<Startup>
public class ApiWebApplicationFactory<TStartup> : WebApplicationFactory<Startup> where TStartup : class
{
public static IConfigurationRoot Configuration { get; set; }
public ApiWebApplicationFactory()
{
var configBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
Configuration = configBuilder.Build();
}
protected override void ConfigureClient(HttpClient client)
{
base.ConfigureClient(client);
client.BaseAddress = new Uri("https://localhost");
client.Timeout = new TimeSpan(0, 10, 0);
}
// Based on my assumption this class reuses everything in the Startup.cs class
// I don't actually think this is necessary, but thought it was worth trying
// the test with and without this code.
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddType<GraphQLContentItem>()
.AddType<GraphQLFolder>();
});
}
}
OneTimesetUp
[OneTimeSetUp]
public void OneTimeSetUp()
{
_factory = new ApiWebApplicationFactory<Startup>();
_client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
services.AddScoped<ICacheRepository, MockCache>();
});
}).CreateClient();
var connString = ApiWebApplicationFactory<Startup>.Configuration.GetConnectionString("DefaultConnection");
var options = new DbContextOptionsBuilder<CmsContext>()
.UseMySql(connString, ServerVersion.AutoDetect(connString))
.Options;
_dbContext = new CmsContext(options);
_dbContext.Database.EnsureCreated();
}
Test
[Test]
public async Task Test()
{
// If I set a breakpoint here, I can't navigate to the URL like I'm expecting to
var graphQLHttpClient =
new GraphQLHttpClient(
new GraphQLHttpClientOptions { EndPoint = new Uri("https://localhost/graphql") },
new NewtonsoftJsonSerializer(),
_client);
var request = new GraphQLRequest
{
Query = #"
query GetCurrentSession() {
getCurrentSession() {
id
name
}
}",
OperationName = "GetCurrentSession"
};
// Error is thrown here with "Bad Request"
var response = await graphQLHttpClient.SendQueryAsync<Session>(request);
// Further code is omitted
}
Please let me know if you see what I am missing. Thanks in advance~

Override default Identity Server 4 Client

I created a .NET Core 3.0 Angular project with Identity Server. I want to add claims and roles to my app.
My Identity Server is mostly out of the box with some simple route changes:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer(options =>
{
options.UserInteraction.LoginUrl = "/auth/login";
options.UserInteraction.LogoutUrl = "/auth/logout";
})
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
I currently add my a simple policy in startup.cs
services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRole", policy =>
{
policy.RequireRole("Admin");
policy.RequireClaim("CreateUser");
});
});
At the bottom of my Configure() method I call this method to add roles:
private async Task CreateUserRoles(IServiceProvider serviceProvider)
{
var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var UserManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
IdentityResult adminRoleResult;
//Adding Admin Role
var adminRoleCheck = await RoleManager.RoleExistsAsync("Admin");
if (!adminRoleCheck)
{
//create the roles and seed them to the database
adminRoleResult = await RoleManager.CreateAsync(new IdentityRole("Admin"));
await RoleManager.AddClaimAsync(new IdentityRole("Admin"), new Claim("CreateUser", "Create User"));
}
}
In my register.cshtml.cs I've successfully set a role and a claim via:
var roleResult = await _userManager.AddToRoleAsync(user, "Admin");
var claimResult = await _userManager.AddClaimAsync(user, new Claim("CreateUser", "Create User"));
I've confirmed that the new user has that claim and role.
The client userinfo call returns correctly but when I look at the id_token I dont have those claims:
{
"nbf": 1574787988,
"exp": 1574788288,
"iss": "https://localhost:5001",
"aud": "MyAppSpa",
"iat": 1574787988,
"at_hash": "ndzldxAE3EiVzI4PeThNPQ",
"s_hash": "dIqJXx372XhOESn1XYH36A",
"sid": "VQLp--MHdoOoxXiVASWZ0g",
"sub": "4a0450dd-fe4f-4b3d-ac12-3f70876183e1",
"auth_time": 1574787983,
"idp": "local",
"amr": [
"pwd"
]
}
According to oidc-client-js is not getting claims correctly from Identity Server 4, I should just need to include AlwaysIncludeUserClaimsInIdToken = true to the client configuration.
However, the template doesnt have a configuration. Its all under the hood.
Questions:
1) Will adding AlwaysIncludeUserClaimsInIdToken = true to the client fix my issue?
2) How do I add it given my current configuration?
Concern about the size of ID Token , by default the claims won't include in ID token .You can get the claims from userinfo endpoint with ID token(read from user.profile) . That is by design .
The new .net core 3.0 angular authentication template just configures IdentityServer to use template supported configuration , but the configuration is not fully customize compare to Identity server provided configuration ,such as AlwaysIncludeUserClaimsInIdToken . So the workaround is not use ApiAuthorization service, the full power of IdentityServer is still available to customize authentication to suit your needs.

Will publish profile work for setting EnvironmentName if not publishing to IIS?

I use this same method to set the eonvironmentname for a web app that I publish to a folder. However, when publishing a .net core console app that is using IWebhostBuilder, the environmentname isnt seeming to get updated?
I have a .net core console app that I am publishing to a folder location and the console app uses IWebhostBuilder, hence I need to be able to set the environment name prior to publishing. It seems to want to grab the default appsettings, rather than the one for my target environment (Dev). I have a appsettings.Dev.json file
My publish profile looks like this
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Configuration>Dev</Configuration>
<Platform>Any CPU</Platform>
<TargetFramework>netcoreapp2.2</TargetFramework>
<PublishDir>\\dev-server1\d$\Services\BoxService</PublishDir>
<SelfContained>false</SelfContained>
<_IsPortable>true</_IsPortable>
<EnvironmentName>Dev</EnvironmentName>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
</Project>
[ Edit ]
Nothing was working, so I did an experiment because obviously I am missing something or just dont understand. I created a new test console app with nothing in it except one class with a Main method. Added the necessary packages (Extensions.Configuration etc), then proceeded to publish the app to a local folder with the following in my publishprofile.
<EnvironmentName>Local</EnvironmentName>
This is obviously not being used, because when I run it from visual studio, it reports the correct environment has been set, but when run from the published folder, its as if the environment has not been set.
Here is Program.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.EnvironmentVariables;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Cmd1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var environmentName =
Environment.GetEnvironmentVariable("ENVIRONMENT");
// create service collection
var services = new ServiceCollection();
// build config
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false)
.AddJsonFile($"appsettings.{environmentName}.json", true)
.AddEnvironmentVariables()
.Build();
// setup config
services.AddOptions();
services.Configure<AppSettings>(configuration.GetSection("App"));
// create service provider
var serviceProvider = services.BuildServiceProvider();
var appSettings = serviceProvider.GetService<IOptions<AppSettings>>();
string env = Environment.GetEnvironmentVariable("Environment");
Console.WriteLine($" We are looking at {appSettings.Value.TempDirectory} from environment: {env}");
}
}
}
Appsettings.json
{
"App": {
"TempDirectory": "d:\temp"
}
}
Appsettings.Local.json
{
"App": {
"TempDirectory": "c:\\temp\\rss-hero\\tmp\\"
}
}
Setting Environment in VS
Results, from VS
Results when run from published folder
You need to configure IWebhostBuilder like this
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration(builder =>
{
builder.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.Dev.json", optional: true) // add more if you have
.AddEnvironmentVariables();
});
Then depending on the environment it will use appsettings{envrionment}.json. If no other present it will use appsettings.json (default). After publishing you can check the web.config to check the environment name it's using.
EDIT
For console app you do something like this
private static IConfigurationBuilder Configure(IConfigurationBuilder config, string environmentName)
{
return config
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
}
And use like this (.Net core generic host)
Host.CreateDefaultBuilder(args)
.ConfigureHostConfiguration(builder =>
{
Configure(builder, "Dev");
})
.UseConsoleLifetime()
Or if you want to get the environment from System environment variables.
public static IConfiguration CreateConfiguration()
{
var env = new HostingEnvironment
{
EnvironmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"),
ApplicationName = AppDomain.CurrentDomain.FriendlyName,
ContentRootPath = AppDomain.CurrentDomain.BaseDirectory,
ContentRootFileProvider = new PhysicalFileProvider(AppDomain.CurrentDomain.BaseDirectory)
};
var config = new ConfigurationBuilder();
var configured = Configure(config, env);
return configured.Build();
}

Connection not established after database backup

Deployed web application in weblogic 12c server and it has data source configure to make use of connection pool.
Database is Oracle 12,
Usecase: when application is alive up and running, for a min database backup script has been run(stop db, backup, start db), after that when trying to access application(session got established) then got SQL error (connection already closed). What could be an issue?
Temporary solution: after restarting application it was working fine with out any issue. Still wondering how it got worked?
Datasource configuration :
**dataSource {
configClass = GrailsAnnotationConfiguration.class
dialect = "org.hibernate.dialect.Oracle10gDialect"
loggingSql = false
jmxExport = false
}
hibernate {
cache.use_second_level_cache = true
cache.use_query_cache = true
cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
hbm2ddl.auto = null
show_sql = false
// naming_strategy = "org.hibernate.cfg.ImprovedNamingStrategy"
dialect = "org.hibernate.dialect.Oracle10gDialect"
config.location = [
"classpath:hibernate-core.cfg.xml",
"classpath:hibernate-utility.cfg.xml"
]
}
// environment specific settings
environments {
development {
dataSource {
}
}
test {
dataSource {
}
}
production {
dataSource {
}
}
}**

How to establish connection between couchbase server and couchbase sync gateway in Mac OS?

Establishing connection between couchbase server and couchbase sync gateway in Mac OS -
$ ../sync_gateway
==== Couchbase Sync Gateway/1.0.4(34;04138fd) ====
Configured Go to use all 2 CPUs; `setenv GOMAXPROCS` to override this
Opening db /sync_gateway as bucket "sync_gateway", pool "default", server <walrus:>
Opening Walrus database sync_gateway on <walrus:>
Using default sync function `'channel(doc.channels)'` for database "sync_gateway"
Starting profile server on
***Starting admin server on 127.0.0.1:4985
Starting server on :4984 ...***
I created a config.json file and trying to connect it to that sever but its not happening by default its going to 127.0.0.1:4985
Can anyone help me out??
Add the following config value to your config.json file:
"adminInterface":"[YOUR_PREFERRED_IP_FOR_ADMIN_INTERFACE]:4985",
The adminInterface field let's the sync_gateway know on which IP:PORT to run the admin interface on.
Also, you need to tell sync_gateway where to fire the rest APIs for a bucket (todos in the sample below). As shown in the example below you can do that by adding "server": "http://[COUCHBASE_SERVER_IP]:8091", in the config for the database.
So,
You will fire admin rest APIs on [YOUR_PREFERRED_IP_FOR_ADMIN_INTERFACE]:4985.
And, sync_gateway will fire rest apis for CRUD operations for the "todos" bucket on [COUCHBASE_SERVER_IP]:8091.
Here is sample config file:
{
"interface":"192.168.1.117:4984",
"adminInterface":"127.0.0.1:4985",
"log": ["CRUD", "REST+", "Access"],
"facebook": { "register": true },
"databases": {
"todos": {
"server": "http://[COUCHBASE_SERVER_IP]:8091",
"users": {
"GUEST": {"disabled": true}
},
"sync":
`
function(doc, oldDoc) {
// NOTE this function is the same across the iOS, Android, and PhoneGap versions.
if (doc.type == "task") {
if (!doc.list_id) {
throw({forbidden : "Items must have a list_id"})
}
channel("list-"+doc.list_id);
} else if (doc.type == "list") {
channel("list-"+doc._id);
if (!doc.owner) {
throw({forbidden : "List must have an owner"})
}
if (oldDoc) {
var oldOwnerName = oldDoc.owner.substring(oldDoc.owner.indexOf(":")+1);
requireUser(oldOwnerName)
}
var ownerName = doc.owner.substring(doc.owner.indexOf(":")+1);
access(ownerName, "list-"+doc._id);
if (Array.isArray(doc.members)) {
var memberNames = [];
for (var i = doc.members.length - 1; i >= 0; i--) {
memberNames.push(doc.members[i].substring(doc.members[i].indexOf(":")+1))
};
access(memberNames, "list-"+doc._id);
}
} else if (doc.type == "profile") {
channel("profiles");
var user = doc._id.substring(doc._id.indexOf(":")+1);
if (user !== doc.user_id) {
throw({forbidden : "Profile user_id must match docid : " + user + " : " + doc.user_id})
}
requireUser(user);
access(user, "profiles"); // TODO this should use roles
}
}
`
}
}
}

Resources