I am trying to write my connection string in my appsettings.json file and bring it into my startup file through an extension class but I keep getting a
Value cannot be null. Parameter name: connectionString.
I have been using various examples but can't seem to see this new setup with ASP.NET Core 2.2 startup class.`And I'm using PostgreSql
appsetting.json file
{
"PostgreConnectionString": {
"DefaultConnection": "User ID=1;Password=1234;Host=localhost;Port=5432;Database=Demo;Pooling=true;Integrated Security=true;",
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
}
ServiceExtension.cs
public static void ConfigurePostgreSQL(this IServiceCollection services, IConfiguration config)
{
var connectionString = config["PostgreConnectionString:DefaultConnection"];
services.AddDbContext<RepositoryContext>(options => options.UseNpgsql(config.GetConnectionString(connectionString))); --Error in this line value cannot be null
}
Startup.cs
services.ConfigurePostgreSQL(Configuration);
For .NET Core I use:
_connectionString = config.GetConnectionString("PostgreConnectionString");
config
{
"ConnectionStrings": {
"PostgreConnectionString": "string"
}
}
it works
the problem is the nesting brackets ! Appsettings.json generate a ConnectionStrings object imbricated in "Logging" object.
Put ConnectionStrings out and your Configuration.GetConnectionStrings will works !
Related
Looking to understand if this is possible. If so, then how? If not, then why?
I would like to have a single azure queue function process messages like this:
{
"tenanatDb": Db_Name_3102,
"command": "getCountryCurrencies",
"args": "US"
}
I'm using DI so in my Function I have a Startup
internal class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddDbContext<MyDbContext>(options =>
options
.UseSqlServer("<ConnectionString>"));
builder.Services.AddSingleton<IMyService>((s) => {
return new MyService();
});
}
}
I can provide a connection string, however, what can I do so that a standard connection string is used, but the Database is specified at runtime from the Queue Message:
Server=myServerAddress;Database=***Db_Name_3102***;User Id=myUsername;Password=myPassword;
Thanks
I have been able to get this working in this way, however, it does not use Dependency Injection.
SomeClass obj = new SomeClass("DatabaseName");
obj.getCoutnryCurrecies(args);
In the SomeClass constructor, I'm setting up the database connection string.
I've created an ASP.NET core 5 Solution and it consists of different projects. The Startup class and DataContext class are not in the same project so when I'm going to add a new migration on the Data project where DataContext is located I get the following error.
Unable to create an object of type 'DataContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
It is the structure of the Solution:
I managed to solve the problem by adding the Startup project's name while adding a new migration, but this way seems annoying to add the project's name every time creating a new migration.
dotnet ef migrations add initialcreation -s ..\API\API.csproj
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 application service provider. If 'AddDbContext' is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
I also add a new constructor with no parameter in the DataContext class and the result was the following error:
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 application service provider.
If 'AddDbContext' is used, then also ensure that your DbContext type
accepts a DbContextOptions object in its constructor and
passes it to the base constructor for DbContext.
Finally, I added the OnConfiguring function on DataContext and now it is like this:
public class DataContext : DbContext
{
public DataContext() {}
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
this.ChangeTracker.LazyLoadingEnabled = false;
}
public DbSet<AppUser> Users { get; set; }
public DbSet<UserPhoto> Photos { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlite("name=DefaultConnection");
}
}
}
And again I got a new error while creating a new migration:
A named connection string was used, but the name 'DefaultConnection'
was not found in the application's configuration. Note that named
connection strings are only supported when using 'IConfiguration' and
a service provider, such as in a typical ASP.NET Core application. See
https://go.microsoft.com/fwlink/?linkid=850912 for more information.
UPDATE:
Here is appsettings.json file:
"ConnectionStrings": {
"DefaultConnection": "Data Source=database1.db"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
I've reached a bit of a brick-wall with my current project.
I have three normalised databases, one of which I want to dynamically connect to; these are:
Accounts: For secure account information, spanning clients
Configuration: For managing our clients
Client: Which will be atomic for each of our clients & hold all of their information
I need to use data stored in the "Configuration" database to modify the ConnectionString that will be used to connect to the "Client" database, but this is the bit I'm getting stuck on.
So far I've generated the entities from the databases into a project by hooking up EntityFrameWorkCore Tools and using the "Scaffold-DbContext" command & can do simple look-ups to make sure that the databases are being connected to okay.
Now I'm trying to register the databases by adding them to the ServiceCollection, I have them added in the StartUp class as follows:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
services.AddDbContext<Accounts>( options =>
options.UseSqlServer(Configuration.GetConnectionString("Accounts"))
);
services.AddDbContext<Support>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Configuration"))
);
// Erm?
SelectClientDatabase(services);
}
Obviously the next stage is to dip into the "Configuration" database, so I've been trying to keep that contained in "SelectClientDatabase()", which just takes the IServiceCollection as a parameter and is for all intents and purposes empty for now. Over the last few days I've found some excellent write-ups on EFC and I'm currently exploring a CustomConfigurationProvider as a possible route, but I must admit I'm a little lost on starting out in ASP.Net Core.
Is it possible to hook into the freshly added DbContext within the ConfigureServices method? Or can/must I add this database to the service collection at a later point?
Thanks!
Edit 1:
I just found this post, which mentions that a DbContext cannot be used within OnConfiguring as it's still being configured; which makes a lot of sense. I'm now wondering if I can push all three DbContexts into a custom middleware to encapsulate, configure and make the connections available; something new to research.
Edit 2:
I've found another post, describing how to "Inject DbContext when database name is only know when the controller action is called" which looks like a promising starting point; however this is for an older version of ASP.Net Core, according to https://learn.microsoft.com "DbContextFactory" has been renamed so I'm now working to update the example given into a possible solution.
So, I've finally worked it all out. I gave up on the factory idea as I'm not comfortable enough with asp.net-core-2.0 to spend time working it out & I'm rushing head-long into a deadline so the faster options are now the better ones and I can always find time to refactor the code later (lol).
My appsettings.json file currently just contains the following (the relevant bit of appsettings.Developments.json is identical):
{
"ConnectionStrings" : {
"Accounts": "Server=testserver;Database=Accounts;Trusted_Connection=True;",
"Client": "Server=testserver;Database={CLIENT_DB};Trusted_Connection=True;",
"Configuration": "Server=testserver;Database=Configuration;Trusted_Connection=True;"
},
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
}
}
I've opted to configure the two static databases in the ConfigureServices method of StartUp, these should be configured and ready to use by the time the application gets around to having to do anything. The code there is nice & clean.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.Configure<MvcOptions>(options =>
{
//options.Filters.Add(new RequireHttpsAttribute());
});
services.AddDbContext<AccountsContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Accounts"))
);
services.AddDbContext<ConfigContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Configuration"))
);
services.AddSingleton(
Configuration.GetSection("ConnectionStrings").Get<ConnectionStrings>()
);
}
It turns out that one can be spoilt for choice in how to go about accessing configuration options set in the appsettings.json, I'm currently trying to work out how I've managed to get it to switch to the release version instead of the development one. I can't think what I've done to toggle that...
To get the placeholder config setting I'm using a singleton to hold the string value. This is just dipping into the "ConnectionStrings" group and stuffing that Json into the "ClientConnection" object (detailed below).
services.AddSingleton(
Configuration.GetSection("ConnectionStrings").Get<ClientConnection>()
);
Which populates the following structure (that I've just bunged off in its own file):
[DataContract(Name = "ConnectionStrings")]
public class ClientConnection
{
[DataMember]
public string Client { get; set; }
}
I only want this holding the connection string for the dynamically assigned database, so it's not too jazzy. The "Client" DataMember is what is selecting the correct key in the Json, if I wanted a different named node in the Json I'd rename it to "Accounts", for instance.
Another couple of options I tested, before settling on the Singleton option, are:
services.Configure<ConnectionStrings>(Configuration.GetSection("ConnectionStrings"));
and
var derp = Configuration.GetSection("ConnectionStrings:Client");
Which I discounted, but it's worth knowing other options (they'll probably be useful for loading other configuration options later).
I'm not keen on the way the Controller dependencies work in ASP.Net Core 2, I was hoping I'd be able to hide them in a BaseController so they wouldn't have to be specified in every single Controller I knock out, but I've not found a way to do this yes. The dependencies needed in the Controllers are passed in the constructor, these weirded me out for a while because they're auto-magically injected.
My BaseController is set up as follows:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.EntityFrameworkCore.Internal;
using ServiceLayer.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ServiceLayer.Controllers
{
public class BaseController : Controller
{
private readonly ClientConnection connectionStrings;
private readonly AccountsContext accountsContext;
private readonly ConfigurationContext configContext;
public ClientTemplateContext clientContext;
private DbContextServices DbContextServices { get; set; }
public BaseController(AccountsContext accounts, ConfigContext config, ClientConnection connection) : base()
{
accountsContext = accounts;
configContext = config;
connectionStrings = connection;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
}
}
}
The code for selecting the database then goes in the "OnActionExecuting()" method; this proved to be a bit of a pain as well, trying to ensure that the dbcontext was set up properly, in the end I settled on:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ServiceLayer.Controllers
{
public class BaseController : Controller
{
private readonly ClientConnection connectionStrings;
private readonly AccountsContext accountsContext;
private readonly ConfigurationContext configContext;
public ClientTemplateContext clientContext;
private DbContextServices DbContextServices { get; set; }
public BaseController(AccountsContext accounts, ConfigurationContext config, ClientConnection connection) : base()
{
accountsContext = accounts;
configContext= config;
connectionStrings = connection;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Temporary selection identifier for the company
Guid cack = Guid.Parse("827F79C5-821B-4819-ABB8-819CBD76372F");
var dataSource = (from c in configContext.Clients
where c.Cack == cack
join ds in configContext.DataStorage on c.CompanyId equals ds.CompanyId
select ds.Name).FirstOrDefault();
// Proto-connection string
var cs = connectionStrings.Client;
if (!string.IsNullOrEmpty(cs) && !string.IsNullOrEmpty(dataSource))
{
// Populated ConnectionString
cs = cs.Replace("{CLIENT_DB}", dataSource);
clientContext = new ClientTemplateContext().Initialise(cs);
}
base.OnActionExecuting(context);
}
}
}
new ClientTemplateContext().Initialise() is a bit messy but I'll clean it up when I refactor everything else. "ClientTemplateContext" is the entity-framework-core generated class that ties together all the entities it generated, I've added the following code to that class (I did try putting it in a separate file but couldn't get that working, so it's staying in there for the moment)...
public ClientTemplateContext() {}
private ClientTemplateContext(DbContextOptions options) : base(options) {}
public ClientTemplateContext Initialise(string connectionString)
{
return new ClientTemplateContext().CreateDbContext(new[] { connectionString });
}
public ClientTemplateContext CreateDbContext(string[] args)
{
if (args == null && !args.Any())
{
//Log error.
return null;
}
var optionsBuilder = new DbContextOptionsBuilder<ClientTemplateContext>();
optionsBuilder.UseSqlServer(args[0]);
return new ClientTemplateContext(optionsBuilder.Options);
}
I also included using Microsoft.EntityFrameworkCore.Design; and added the IDesignTimeDbContextFactory<ClientTemplateContext> interface to the class. So it looks like this:
public partial class ClientTemplateContext : DbContext, IDesignTimeDbContextFactory<ClientTemplateContext>
This is where the CreateDbContext(string[] args) comes from & it allows us to create a new instance of a derived context at design-time.
Finally, the code for my test controller is as follows:
using Microsoft.AspNetCore.Mvc;
using ServiceLayer.Entities;
using System.Collections.Generic;
using System.Linq;
namespace ServiceLayer.Controllers
{
[Route("api/[controller]")]
public class ValuesController : BaseController
{
public ValuesController(
AccountsContext accounts,
ConfigurationContext config,
ClientConnection connection
) : base(accounts, config, connection) {}
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
var herp = (from c in clientContext.Usage
select c).FirstOrDefault();
return new string[] {
herp.TimeStamp.ToString(),
herp.Request,
herp.Payload
};
}
}
}
This successfully yields data from the database dynamically selected from the DataSource table within the Configuration database!
["01/01/2017 00:00:00","derp","derp"]
If anyone can suggest improvements to my solution I'd love to see them, my solution is mashed together as it stands & I want to refactor it as soon as I feel I'm competent enough to do so.
We have an existing .NET Framework library with Entity Framework 6 and static methods like this:
public class OrderManager
{
public static OrderDTO GetOrderByOrderId(int oid)
{
var entities = new MyEntities();
....
}
}
where MyEntities have a hardcoded connectionstring name
internal partial class MyEntities : DbContext
{
public SSE3Entities() : base("name=MyEntities") {}
}
When used in a ASP.NET application, the web.config have a connectionstrings defined like this:
<connectionStrings>
<add name="MyEntities" connectionString="metadata=res://*/M..." providerName="System.Data.EntityClient" />
</connectionStrings>
But how can I reuse this library in a aspnet core application. Tried:
{
"ConnectionStrings": {
"MyEnties": "metadata=res://*/M...",
},
"Logging": {
...
}
}
I know passing the connectionstring into the OrderManager constructor is what we should have done years ago, but changing it now requiers a lot of work.
Are there any ways we can make EF read the new configurations system?
Or could we maybe write some settings to the old ConfigurationManager?
I tried dropping in a web.config without any luck
add System.Configuration.ConfigurationManager from nuget
add a file app.config
.net core applictaion is a consle app, so it find app.config first
app.config:
<appSettings>
<add key="webpages:Version" value="2.0.0.0"/>
</appSettings>
This solution will work for ASPNET Core MVC 2.0, 2.2 and 3.0 In your app root go find the appsetting.json file and modify the connection string.
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=DESKTOP-3R4MR9H\\SQLSERVER3R4MR9;Initial
Catalog=ASPNetCoreMVC;Persist Security Info=True;User
ID=sa;Password=********;"
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
Is there a way how to specify which DataProvider(SQL Server) and ConnectionString to use just for generating a migrations(Add-Migration) and updating a database(Update-Database)? I do not want to hardcode the data provider selection and connection string loading into DbContext (.UseSqlServer()).
I think EF6 can pick a connection string directly from web.config, is there something similar in EF7?
No you have to use optionsBuilder:
example:
string oldConnectionString =#"Server = .\;Initial Catalog=EFTutorial; AttachDbFilename=|DataDirectory|\EFTutorial.mdf; Trusted_Connection = True; MultipleActiveResultSets = true";
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(oldConnectionString );
base.OnConfiguring(optionsBuilder);
}
you can also load as json and load it directly from app.config or web.config.
If you are using .Net Core:
you can define in your appsettings.json the conenction string as following:
{
{
....
},
"Data": {
"ConnectionString": "..."
}
}
on App starting you have to load it:
// Set up configuration sources.
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
builder.AddEnvironmentVariables();
Configuration = builder.Build().ReloadOnChanged("appsettings.json");
After that you also have to configure Entity Framework in Startup.cs.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<AppContext>(options =>
{
options.UseSqlServer(Configuration "Data:ConnectionString"]); });
services.AddScoped<AppContext, AppContext>();
...
}
}
The improtant thing here:
services.AddScoped((_) => new AppContext(Configuration["Data:DefaultConnection:ConnectionString"]));
https://docs.asp.net/en/latest/data/entity-framework-6.html
Examples to conenction string in Entity Framework Core 1.0 / 7:
https://docs.asp.net/en/latest/fundamentals/configuration.html
The old way(You can do that also with EF Core) we have load the conenctionString from App.config or web.config
and by the Migration process Add-Migration/Update-Database, EF will automatically found the Connection string.
But you can also provide the conenction string to Add-Migration in the NuGet comamnd line as parameter.
I hope this will solve your Problem!