I have an ASP .NET MVC 6 and Entity Framework 6, divided into layers , how do I get the connection string in the DAL layer in my DbContext ?
The connection string is in appsettings.json file like this:
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Verbose",
"System": "Information",
"Microsoft": "Information"
}
},
"Data": {
"DefaultConnection": {
"ConnectionString": "",
}
}
}
If you have the connection string in appsettings.json you want to build a configuration object first:
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
var configuration = builder.Build();
This should probably be in Startup's ctor. You can then store the configuration object in a field. Let's say a _configuration field.
Then you can do
// _connectionString is also a field.
_connectionString = _configuration["Data:DefaultConnection"];
Your DbContext:
public class AppDbContext : DbContext
{
public AppDbContext(string connectionString) : base(connectionString)
{
}
}
The you can register your AppDbContext in ConfigureServices as:
services.AddScoped(_ => new AppDbContext(_connectionString));
Related
I have a rather simple(for now) Odata controller that does not work in the way I expect.
Backend store is a Mongo database, but I don't think that is relevant.
Model class:
public class EventModel
{
[Key]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string EventName { get; set; }
public DateTime EventTime { get; set; }
// Fields omitted to shorten sample
public IDictionary<string,object?> Data { get; set; }
}
OData controller is added like so:
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Repository.Model.EventModel>("Events");
services.AddControllers()
.AddOData(options => options
.Select()
.Filter()
.OrderBy()
.Expand()
.Count()
.SetMaxTop(null)
.AddRouteComponents("eventservice/odata", builder.GetEdmModel()));
And this is the controller:
public class EventODataController : ODataController
{
private readonly IEventRepository _repository;
public EventODataController(IEventRepository repository)
{
_repository = repository;
}
[HttpGet("eventservice/odata/Events")]
[EnableQuery]
public ActionResult Get()
{
var data = _repository.GetAsQueryable();
return Ok(data);
}
Expected result when doing a HTTP get to eventservice/odata/Events$top=100 should be along the lines of this:
{
"#odata.context": "http://127.0.0.1:7125/eventservice/odata/$metadata#Events",
"#odata.count": 1080302,
"value": [
{
"Id": "63dbbcc9920829279f559025",
"EventName": "Asset",
"EventTime": "2022-09-27T09:14:15.398+02:00",
// Omitted field data here
"Data": {
"Data1":"Foo",
"Data2":"Bar",
"SomeMore":4
}
}
But it turns out that OData somehow flattens/inlines the dictionary, so the result is this:
"#odata.context": "http://127.0.0.1:7125/eventservice/odata/$metadata#Events",
"#odata.count": 1080302,
"value": [
{
"Id": "63dbbcc9920829279f559025",
"EventName": "Asset",
"EventTime": "2022-09-27T09:14:15.398+02:00",
"AssetId#odata.type": "#Int64",
"AssetId": 1258,
"UniqueCode": "9404120007",
"DbId#odata.type": "#Int64",
"DbId": 1038118,
"SomeData": "ABC",
"MoreData": "108",
"AreaName": "Area51,
...
},
Simple question: How do I stop OData from behaving like this, and do what I expect?
This is the appsetting.json file
{
"Connection Strings": {
"EmployeeDBConnection": "server=(localhost)\\SQLEXPRESS ; database=EmployeeDB; Trusted_Connection =true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"MyKey": "Value of MYKey from appsettings.json"
}
This is the startup.cs file
namespace EmployeeManagement
{
public class Startup
{
private IConfiguration _config;
public Startup(IConfiguration config)
{
_config = config;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IEmployeeRepository, SQLEmployeeRepository>();
services.AddControllersWithViews();
services.AddDbContextPool<AppDbContext>(options => options.UseSqlServer(_config.GetConnectionString("EmployeeDBConnection")));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World");
});
}
}
}
I have checked that "Connection Strings" has s in the end,
Changed the placement of the connection strings in the appsetting.json file
Instead of just writing the name of the connection string in Configuration.GetConnectionString() in Startup.cs, I put the entire ConnectionString there
I am still having this error.
Your key "Connection Strings" contains space. Change to "ConnectionStrings" for the intended behavior.
"Connection Strings" does not match your parameter "connectionStrings". You need to change that in appsettings.cs. It could also need to be "connectionString" instead, I am a bit confused by your wording. Regardless, your program is looking for "connectionString", not "Connection Strings".
I want to access the "BaseURL" key value in my user defined class instead of controller. Please help me how to do the same
Below is My AppSettings.json
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"BaseURL": "http://192.168.0.72/mehcrm/CMS/public/api/v1/"`
}
Need your assistance.
You can take "BaseURL" value in Startup.cs like mentioned in below code.
public class Startup
{
public static string BaseUrl { get; private set; }
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);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
BaseUrl = Configuration.GetSection("BaseURL").Value;
}
}
public static string GetBaseUrl()
{
return Startup.BaseUrl;
}
Use it in other classes in the project like shown below:
string BaseUrl= Startup.GetBaseUrl();
I have my "appsettings.json" file content
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"appData": {
"applicationDeveloper": "El Bayames"
}
}
And a class that read from appsettings and more
public class learningDIGlobalVariables
{
private String _applicationDeveloper;
private String _webRootFolderPath;
public String applicationDeveloper
{
get
{
return _applicationDeveloper;
}
}
public String webRootFolderPath
{
get
{
return _webRootFolderPath;
}
}
public learningDIGlobalVariables(IConfigurationRoot auxConfRoot, Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment)
{
_applicationDeveloper = auxConfRoot["appData:applicationDeveloper"];
_webRootFolderPath = hostingEnvironment.WebRootPath;
}
}
And within the settings container I have
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddSingleton<IConfigurationRoot>(Configuration);
}
Within the controller I have
public class HomeController : Controller
{
private learningDIGlobalVariables _learningDIGlobalVariables;
public HomeController(IConfigurationRoot auxConfRoot, Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment)
{
_learningDIGlobalVariables = new learningDIGlobalVariables(auxConfRoot, hostingEnvironment);
}
}
How do I use that class "learningDIGlobalVariables" within the controller without instantiate it?. Doesn't that class get instantiated by the framework?. If I have to add that class to the Services, How can I do it and use it latter on?
If you don't want to instantiate the calss than you can make the it static, remove the constructor and add a SetGlobals method:
public class learningDIGlobalVariables
{
public static SetGlobals(IConfigurationRoot auxConfRoot, Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment)
{
_applicationDeveloper = auxConfRoot["appData:applicationDeveloper"];
_webRootFolderPath = hostingEnvironment.WebRootPath;
}
}
In your startup, in the Configure function you can setup your globals:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// Add framework services.
services.AddMvc();
services.AddSingleton<IConfigurationRoot>(Configuration);
learningDIGlobalVariables.SetGlobals(app.ApplicationServices.GetService<IConfigurationRoot>(), env);
}
Create a simply POCO to represent your JSON config.
public class AppSetting
{
public AppData AppData {set;get;}
}
public class AppData
{
public string ApplicationDeveloper { set;get;}
}
Now in the Startup.cs class ConfigureServices method.
services.Configure<AppSettings>(Configuration);
Now in your controller, you will use constructor injection. You do not need to new up any object here. The framework will inject the dependency for you.
public class HomeController : Controller
{
private IOptions<AppSetting> _settings;
public HomeController(IOptions<AppSetting> settings)
{
this._settings= settings;
}
}
Inside the controller, you can use this._settings.Value.AppData.ApplicationDeveloper to get the value you set in your json.
I'm currently facing a problem with setting up RavenDB with dotnet core for multiple environments.
In StartUp class I have configured Raven as a Singleton and used the IOptions pattern to bind the settings Raven to the RavenSettings object.
public virtual void ConfigureServices(IServiceCollection services)
{
Services.AddMvc()
//Add functionality to inject IOptions<T>
services.AddOptions();
// App Settings
services.Configure<RavenSettings>(Configuration.GetSection("Raven"));
//services.Configure<RavenSettings>(settings => Configuration.GetSection("Raven").Bind(settings));
// .NET core built in IOC
services.AddSingleton(DocumentStoreHolder.Store);
services.AddSingleton<IConfiguration>(Configuration);
}
This is my default app settings.
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"Raven": {
"Url": "x",
"DefaultDatabase": "x"
}
}
This is the class that I have attempted to bind the setting from appsettings to ...
public class RavenSettings
{
public string Url { get; set; }
public string DefaultDatabase { get; set; }
}
The class below follows the Raven documents in generating the Raven document store. Because I am using a singleton, I am not hitting the constructor to inject the settings. Can anyone advise a way around this?
public sealed class DocumentStoreHolder
{
private static RavenSettings _ravenSettings;
public DocumentStoreHolder(IOptions<RavenSettings> ravenSettings)
{
_ravenSettings = ravenSettings.Value;
}
public static IDocumentStore Store => DocStore.Value;
private static readonly Lazy<IDocumentStore> DocStore = new Lazy<IDocumentStore>(CreateStore);
private static IDocumentStore CreateStore()
{
var store = new DocumentStore
{
Url = _ravenSettings.Url,
DefaultDatabase = _ravenSettings.DefaultDatabase
}.Initialize();
return store;
}
}
Building on Set's answer, I was able to resolve it by creating an IDocumentStoreHolder interface, and registering that in the IOC.
DocumentStoreHolder.cs
public class DocumentStoreHolder : IDocumentStoreHolder
{
public DocumentStoreHolder(IOptions<RavenSettings> ravenSettings)
{
var settings = ravenSettings.Value;
Store = new DocumentStore
{
Url = settings.Url,
DefaultDatabase = settings.DefaultDatabase
}.Initialize();
}
public IDocumentStore Store { get; }
}
IDocumentStoreHolder.cs
public interface IDocumentStoreHolder
{
IDocumentStore Store { get; }
}
Startup.cs (ConfigureServices)
services.AddSingleton<IDocumentStoreHolder, DocumentStoreHolder>();
SomeClass.cs
private readonly IDocumentStore _store;
public SomeClass(IDocumentStoreHolder documentStoreHolder)
{
_store = documentStoreHolder.Store;
}
ASP.NET Core DI already uses lazy loading with singleton:
Singleton lifetime services are created the first time they are requested (or when ConfigureServices is run if you specify an instance there) and then every subsequent request will use the same instance.
and moreover:
If your application requires singleton behavior, allowing the services container to manage the service's lifetime is recommended instead of implementing the singleton design pattern and managing your object's lifetime in the class yourself.
Therefore, you can do something like this:
services.AddSingleton<DocumentStoreHolder>();
by removing your own singleton implementation from DocumentStoreHolder class
public sealed class DocumentStoreHolder
{
public DocumentStoreHolder(IOptions<RavenSettings> ravenSettings)
{
var _ravenSettings = ravenSettings.Value;
Store = new DocumentStore
{
Url = _ravenSettings.Url,
DefaultDatabase = _ravenSettings.DefaultDatabase
}.Initialize();
}
public IDocumentStore Store {get; private set;}
}