.NET Core pass configuration section via command line - c#

In appsettingsjson file i have:
"DataSource": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "Root",
"CollectionName": "ApiLog"
},
in Program.cs, i get this data like this
builder.Services.Configure<DatabaseSettings>(
builder.Configuration.GetSection("DataSource"));
where DatabaseSettings class is;
public class DatabaseSettings
{
public string ConnectionString { get; set; } = null!;
public string DatabaseName { get; set; } = null!;
public string CollectionName { get; set; } = null!;
}
Then i can access instance of DatabaseSettings via dependency injection like:
public class LogService
{
private readonly IMongoCollection<Log> _collection;
public LogService(
IOptions<DatabaseSettings> databaseSettings)
{
var mongoClient = new MongoClient(
databaseSettings.Value.ConnectionString);
var mongoDatabase = mongoClient.GetDatabase(
databaseSettings.Value.DatabaseName);
_collection = mongoDatabase.GetCollection<ElekseLog>(
databaseSettings.Value.CollectionName);
}
}
the question is i dont want to store db info in appsettings json file. I want to pass tis info from command line without changing the code. How can I achieve this?

You need to "flatten" the parameters by joining "path elements" with : and pass as key-value pairs. For example:
yourapp "DataSource:ConnectionString"="mongodb://localhost:27017"
Or
yourapp --DataSource:ConnectionString=mongodb://localhost:27017
Some info can be found in the docs - Command-line configuration provider.

Related

Net 6 Multiple Connection Strings

How to setup in Net 6 program.cs a multiple connection strings?
I want to work with Development, Staging, and Production environments, all of them pointing to different database servers.
NET 6. Program.cs:
builder.Services.AddDbContext<MyContext>(options =>
{
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
});
Thanks in advance.
Here's what you could do.
First, create an appsettings.json like this:
appsettings.json
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://*:5000"
}
}
},
"WillAppConfig": {
"ActiveEnvironment": "Development",
"DevDatabase": "server:123.123.123.123, user: will, pass:1234",
"STGDatabase": "server:123.123.123.123, user: will, pass:1234",
"ProdDatabase": "server:123.123.123.123, user: will, pass:1234"
}
}
Then create a class somewhere in your project, that will serve to map the configuration to an object.
WillAppConfigurationMap.cs
public class WillAppConfigurationMap
{
public string ActiveEnvironment { get; set; }
public string DevDatabase { get; set; }
public string STGDatabase { get; set; }
public string ProdDatabase { get; set; }
}
Finally in your Program.cs, you could select the connection string to use depending on the value of ActiveEnvironment.
var builder = WebApplication.CreateBuilder(args);
WillAppConfig = builder.Configuration.GetSection("WillAppConfig").Get<WillAppConfigurationMap>();
var connectionString = "";
if (WillAppConfig.ActiveEnvironment == "Development")
{
connectionString = WillAppConfig.DevDatabase
}
else if (WillAppConfig.ActiveEnvironment == "Staging")
{
connectionString = WillAppConfig.STGDatabase
}
else if (WillAppConfig.ActiveEnvironment == "Production")
{
connectionString = WillAppConfig.ProdDatabase
}
builder.Services.AddDbContext<MyContext>(options =>
{
options.UseSqlServer(connectionString));
});
partial class Program
{
public static WillAppConfigurationMap WillAppConfig { get; private set;}
}
You can remove the "Kestrel" section from the appsettings.json if you don't use it. You can use this approach to map any appsettings.json structure.
Then, you can access your configuration object from ANYWHERE in your app doing Program.WillAppConfig.
Create multiple connection strings with different names in appsetting.json:
builder.Services.AddDbContext<MyContext>(options => {
options.UseSqlServer(builder.Configuration.GetConnectionString(UseNameofConnectionString));
});
Also you can create extension method which gives you required connectionString when you call it.

Can't create database EF Core C#

Im trying to create my database after finnishing up my classes, getting error message
"Unable to create an object of type 'HamsterDbContext'. For the different patterns supported at design time"
public class HamsterDbContext : DbContext
{
public HamsterDbContext(DbContextOptions<HamsterDbContext> options) : base(options) { }
public virtual DbSet<Hamster> Hamsters { get; set; }
public virtual DbSet<Cage> Cages { get; set; }
public virtual DbSet<ExerciseArea> ExerciseArea { get; set; }
}
internal class DatabaseHelper
{
public static HamsterDbContext NewContext()
{
const string host = "(LocalDB)\\MSSQLLocalDB";
const string database = "HamsterDaycare";
var connectionString = $"Data Source={host};Initial Catalog={database};Integrated Security=True;";
var dbContextBuilder = new DbContextOptionsBuilder<HamsterDbContext>();
dbContextBuilder.UseSqlServer(connectionString);
return new HamsterDbContext(dbContextBuilder.Options);
}
}
Looks like this, typing add-migration CreateDatabase, what am i doing wrong here?
Fix your string connection. Instead DataSource, put server. Your ConnectionString must be this way:
{
"ConnectionStrings": {
"DefaultConnection": "server=yourserver;database=yourdatabase;user=youruser;password=yourpassword"},
...
},

Injecting app settings via DI based on generic type in .NET Core 3.1

I'm trying to be cheeky and create a base service for my MongoDB client. Inside my DI container I've injected database connection settings from the application.json (as per Microsoft’s MongoDB tutorial).
What I can't figure out is the best way to access one of these setting properties based on a generic type passed into the constructor for the base service.
For example, the MongoDB collection name for Type Player is named is Players. One idea I had was to do typeof(T) instead of putting the key/value in the settings, but that cements the collection name to the type name.
Here is the database settings object being injected:
public class GameDatabaseSettings : IGameDatabaseSettings
{
public string PlayersCollectionName { get; set; }
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
public string LobbiesCollectionName { get; set; }
}
The application json:
"GameDatabaseSettings": {
"PlayersCollectionName": "Players",
"LobbiesCollectionName": "Lobbies",
"ConnectionString": "mongodb+srv://bugbeeb:*******#csharptest-yzm6y.mongodb.net/test?retryWrites=true&w=majority",
"DatabaseName": "DiceGameDB"
},
And here is how the base service operates:
public interface IGameItem
{
public string Id { get; set; }
}
public class BaseService<T> where T:IGameItem
{
private readonly IMongoCollection<T> _collection;
public BaseService(IGameDatabaseSettings settings)
{
var client = new MongoClient(settings.ConnectionString);
var db = client.GetDatabase(settings.DatabaseName);
_collection = db.GetCollection<T>(settings.CollectionName); //<-- How to make this T specific??
}
public virtual async Task<T> GetAsync(string id)
{
var result = await _collection.FindAsync(item => item.Id == id);
return result.FirstOrDefault();
}
public virtual async Task DeleteAsync(string id)
{
await _collection.FindOneAndDeleteAsync(item => item.Id == id);
}
}
You were on the right track with typeof(T). The only step you were missing is that you can bind a list of key/value pairs in your appSettings.json to a Dictionary<string, string> in your GameDatabaseSettings.
That will let you look up configuration values based on a string name, thus allowing you to configure the mappings between any generic Type and an associated MongoDB collection name via your appSettings.json.
So given the following appSettings.json:
"GameDatabaseSettings": {
"CollectionNames": {
"Players": "Players",
"Lobbies": "Lobbies"
},
"ConnectionString": "mongodb+srv://bugbeeb:*******#csharptest-yzm6y.mongodb.net/test?retryWrites=true&w=majority",
"DatabaseName": "DiceGameDB"
}
}
You can bind to a settings object like:
public class GameDatabaseSettings : IGameDatabaseSettings
{
public Dictionary<string, string> CollectionNames { get; set; }
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
}
And now, within your service, you should be able to do something like:
_collection = db.GetCollection<T>(settings.CollectionNames[typeof(T).Name]);
Of course, you shouldn’t assume that the correct mapping will be configured, so you’ll likely want to write more defensive code, similar to:
_collection = db.GetCollection<T>(
settings.CollectionNames.TryGetValue(
typeof(T).Name,
out var collectionName
)?
collectionName :
throw new ConfigurationError(
$"The ‘{typeof(T).Name}’ mapping is not configured as part of {nameof(GameDatabaseSettings)}."
)
);

MVC 6 Create View issues : There was an error creating the DBContext to get the model

Attempting to create a view for "Create" using the NavBar Model and the NavBarEntity shown below (in MVC6) receives this message...
There was an error running the selected code generator: There was an error creating the DBVContext instance to get the model... Value cannot be null... Parameter Name: connectionString
I picked this mode in View Wizard...
public class NavBarModel
{
public string ID { get; set; }
public List<LinkModel> Links { get; set; }
}
This DBContext class is shown here...
public class NavBarEntity : DbContext
{
public NavBarEntity()
{
ID = Guid.NewGuid().ToString();
}
[Key]
public string ID { get; set; }
public DbSet<List<LinkModel>> Links { get; set; }
}
And the LinkModel shown here..
public class LinkModel
{
public LinkModel()
{
ID = Guid.NewGuid().ToString();
}
[Key]
private string ID { get; set; }
public string HREF { get; set; }
public string Text { get; set; }
}
Configure Services looks like this...
var cfg2 = Configuration["Data Source=MyPC\\SQLEXPRESS;Initial Catalog=Dashboard;Integrated Security=True;Pooling=False"];
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(cfg))
.AddDbContext<NavBarEntity>(options =>
{
options.UseSqlServer(cfg2);
});
Question: What am I doing wrong?
Thanks for the help listed above..
For newbies to MVC6 and EF7, the method named ConfigureServices, must contain a json pointer to the appsetting.json. That method is found in the Startup.cs file.
This is the services configuration to match the code shown above. The string value in the brackets points to the json location...
var cfg2 = Configuration["Data:DashboardContext:ConnectionString"];
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<DashboardContext>(options =>
{
options.UseSqlServer(cfg2);
})
But, you must also put a value into appsettings.json like this:
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-TestWebApplication1-d91c23e4-3565-476d-a7c0-45665bc0c367;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"DashboardContext": {
"ConnectionString": "Data Source= MYPC\\SQLEXPRESS;Initial Catalog=Dashboard;Integrated Security=True;Pooling=False"
}
},
The root cause of the Parameter Name: connectionString being null was that the appsettings.json has to be exactly as shown above. The json parsing routines must be able to locate the string name/value pair... Notice that these configurations fall under the "Data" name that contains other names. in this case "DefaultConnection" was there by default, and I added "DashboardContext" portion.
Also in MVC 6 you must change the connectionString type to IServiceProvider and NOT string as was done before...
public class DashboardContext : DbContext
{
public DashboardContext(IServiceProvider connectionString) : base (connectionString)
{}
public DbSet<NavBarEntity> NavBars { get; set; }
}
Alas: The Views created no problem...Yes!
The way you've tried to combine the DbContext and your entity isn't right. The DbContext should reference any entities you have as DbSets - entities should not inherit from it.
Your DbContext should look similar to this (EF6)
public class MyDbContext : DbContext
{
public MyDbContext(string connectionString)
: base(connectionString)
{ }
public DbSet<NavBarEntity> NavBars { get; set; }
// Other entities
}
The constructor takes the name of the connecting string entry that's defined in your web.config you want to use. There are other ways to do this though - see here
Then create your entities as a simple class (POCO):
public class NavBarEntity
{
public NavBarEntity()
{
ID = Guid.NewGuid().ToString();
}
[Key]
public string ID { get; set; }
// Other properties/columns here
}
EDIT
My original answer was based on EF6 rather than EF7. Here's how I would implement the context in EF7 for completeness:
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{ }
public DbSet<NavBarEntity> NavBars { get; set; }
// Other entities
}

How to handle a hierarchy of properties in ASP.NET 5 AppSettings?

In ASP.NET 4 to organize settings, I am prefixing the setting key with a small word that indicates where this config is used (e.g. key="dms:url", "sms:fromNumber" .... etc).
In ASP.NET 5, the AppSettings configuration is mapped to a strongly typed class.
what is the property that i need to build for "dms:url"? How could map dashes & special chars to a C# property in ASP.NET 5?
You can organize your configuration file within a hierarchy in the config.json
{
"AppSettings": {
"SiteTitle": "PresentationDemo.Web",
"Dms": {
"Url": "http://google.com",
"MaxRetries": "5"
},
"Sms": {
"FromNumber": "5551234567",
"APIKey": "fhjkhededeudoiewueoi"
}
},
"Data": {
"DefaultConnection": {
"ConnectionString": "MyConnectionStringHere. Included to show you can use the same config file to process both strongly typed and directly referenced values"
}
}
}
We defined the AppSettings as a POCO class.
public class AppSettings
{
public AppSettings()
{
Dms = new Dms(); // need to instantiate (Configuration only sets properties not create the object)
Sms = new Sms(); // same
}
public string SiteTitle { get; set; }
public Dms Dms { get; set; }
public Sms Sms { get; set; }
}
public class Dms
{
public string Url { get; set; }
public int MaxRetries { get; set; }
}
public class Sms
{
public string FromNumber { get; set; }
public string ApiKey { get; set; }
}
We then load the configuration into an instance of IConfigurationSourceRoot and then set values of AppSettings using GetSubKey. The best practice would be to do this in ConfigureServices and add it to the DI Container.
public class Startup
{
public Startup(IHostingEnvironment env)
{
// Setup configuration sources.
var configuration = new Configuration()
.AddJsonFile("config.json")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true);
}
public void ConfigureServices(IServiceCollection services)
{
// Add Application settings to the services container.
services.Configure<AppSettings>(Configuration.GetSubKey("AppSettings"));
//Notice we can also reference elements directly from Configuration using : notation
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
}
}
We can now provide access in a controller through the constructor. I set the setting values explicitly the constructor but you could use the entire IOptions
public class HomeController : Controller
{
private string _title;
private string _fromNumber;
private int _maxRetries;
public HomeController(IOptions<AppSettings> settings)
{
_title = settings.Options.SiteTitle;
_fromNumber = settings.Options.Sms.FromNumber;
_maxRetries = settings.Options.Dms.MaxRetries;
}
If you wanted to keep everything flat and use a pseudo hierarchy like you have been doing, you can, but ":" isn't a valid symbol for a variable name. You would need to use a valid symbol like "_" or "-" instead.

Categories

Resources