I'm trying to use EF Core tools to manage an SqlServer database I'm designing in a C# class library. It's in a class library because I need to use the database schema in both an MVC6 website and some command line tools.
I had to convert the class library to being a netapp because the current version of the tooling doesn't support class libraries, but I don't think that's the source of my problem.
My DbContext class looks like this:
public class ConnellDbContext : IdentityDbContext<ConnellUser>
{
public ConnellDbContext( DbContextOptions<ConnellDbContext> options )
{
}
// core tables
public DbSet<Ballot> Ballots { get; set; }
public DbSet<Campaign> Campaigns { get; set; }
//...
}
When I run "dotnet ef migrations list" on the Package Manager Console, I get the following error message:
No parameterless constructor was found on 'ConnellDbContext'. Either
add a parameterless constructor to 'ConnellDbContext' or add an
implementation of 'IDbContextFactory' in the same
assembly as 'ConnellDbContext'.
I'm not quite sure how to resolve this. It's easy enough to insert a parameterless constructor, but when I do I get 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.
I >>think<< this means the console commands are not picking up the connection string information in my appsettings.json file:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-ConnellCampaigns;Trusted_Connection=True;MultipleActiveResultSets=true;AttachDbFilename=e:\\SqlServer\\Data\\ConnellCampaigns.mdf;"
}
}
I'm missing something about how the EF tooling accesses the source code to do its magic. Any pointers or leads would be much appreciated.
Additional Info
Thanx to Mr. Anderson I've made a bit of progress. I added a parameterless constructor and overrode the OnConfiguring() method in my DbContext class:
protected override void OnConfiguring( DbContextOptionsBuilder optionsBuilder )
{
var builder = new ConfigurationBuilder()
.AddJsonFile( "appsettings.json", optional: true, reloadOnChange: true );
IConfigurationRoot config = builder.Build();
optionsBuilder.UseSqlServer(config.GetConnectionString("DefaultConnection") );
}
That didn't work, but explicitly including the actual connection string in the call to UseSqlServer() did. Thoughts on why the call based on "DefaultConnection" didn't work?
public class ConnellDbContext : IdentityDbContext<ConnellUser>
{
internal static string connection_string
{
get
{
return System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
}
}
public ConnellDbContext() : base(connection_string)
{
}
// core tables
public DbSet<Ballot> Ballots { get; set; }
public DbSet<Campaign> Campaigns { get; set; }
//...
}
Related
I'm trying to use Ef Core in my project.
The structure is a little different, in the sense that I'm not using EfCore insite the WebApi.csproj. In fact I have a different dll. and a DependenciesResolver.dll that handles all my dependency injection.
In my EfCore.dll I've installed both
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore.SqlServer
Now when I try to run the command (the dll in which I'm running is the EfCore.dll)
Add-Migration Name
I get this :
An error occurred while accessing the IWebHost on class 'Program'.
Continuing without the application service provider. Error: Object
reference not set to an instance of an object. Unable to create an
object of type 'StoreContext'. Add an implementation of
'IDesignTimeDbContextFactory' to the project, or see
https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns
supported at design time.
The structure of the sln is like this
WebApi | EfCore.dll | DependencyResolver.dll and I want to keep it this way, don't want to permit using EfCore in my WebApi.
What is the resolution for this issue ?
If this helps within the EfCore.dll I have this.
public sealed partial class StoreContext : DbContext, IStoreContext
{
private string _connectionString;
public StoreContext(string connectionString) : base()
{
_connectionString = connectionString;
Database.EnsureCreated();
}
/// db.tbls
public DbSet<Order> Orders { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.AddOrderConfiguration();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString);
}
}
which is called by DependencyResolver like this
private DependenciesResolver RegisterInfrastructure()
{
_serviceCollection.AddScoped<StoreContext>(factory => new StoreContext(_connectionString));
return this;
}
and the DependencyResolver is then called by the WebApi
Please have a look at this: https://learn.microsoft.com/en-us/ef/core/miscellaneous/cli/dbcontext-creation
The error message clearly specifies the EF Core tools can't create an instance of your Context at design-time.
If you can define a constructor with no parameters for your StoreContext that would work, otherwise you need tell the tools how to create an instance of your context at design-time by defining a class that implements the IDesignTimeDbContextFactory interface.
I'm attempting to register my own custom options. I have, in my ASP.Net project (Kimble.API), an appsettings.json file. It looks like this:
{
"NotificationHub": {
"AccountName": "my-notification-hub-name",
"ConnectionString": "my-secret-connection-string"
}
}
In my .Net Standard library (Kimble.Core), which the API project references, I have a class NotificationHubOptions:
public class NotificationHubOptions
{
public string AccountName { get; set; }
public string ConnectionString { get; set; }
}
Back to the API project.
In my Startup.cs file, in the ConfigureServices method, I register the options:
services.Configure<NotificationHubOptions>(configuration.GetSection("NotificationHub"));
I've checked, and the registration does show up in the services collection.
My controller's constructor looks like this:
public MyController(NotificationHubOptions options)
{
_notificationHubOptions = options;
}
However, when I try to call a method on the controller, I always get an exception:
System.InvalidOperationException: 'Unable to resolve service for type 'Kimble.Core.Options.NotificationHubOptions' while attempting to activate 'Kimble.API.Controllers.MyController'.'
This all worked before I moved the NotificationHubOptions class to my Core project. However, I can't see why that should matter at all.
You need to inject IOptions<TOptions>, like so:
public MyController(IOptions<NotificationHubOptions> options)
{
_notificationHubOptions = options.Value;
}
When you use Configure, you are registering a callback to configure the options instance for that type when it creates it. In this case using the configuration section to bind data to the options object.
So the options class itself is not in DI.
If you wanted that you could do this:
var options = Configuration.GetSection("NotificationHub").Get<NotificationHubOptions>();
services.AddSingleton<NotificationHubOptions>(options);
To access App Keys in a class library, do we need to do the following code in every class library and class where we need to access a AppKey?
public static IConfigurationRoot Configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
This is what I found in Microsoft docs, but this looks very redundant.
Startup class in a project as below
public class Startup
{
public IConfigurationRoot Configuration { get; set; }
public Startup()
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework().AddEntityFrameworkSqlServer()
.AddDbContext<DbContext>(options =>
options.UseSqlServer(Configuration["Data:MyDb:ConnectionString"]));
}
}
Then how should I inject this "IConfigurationRoot" in each class of a project. And do I have to repeat this Startup class in each class Library? Why is this not part of .NET Core Framework?
The recommended way is to use the options pattern, provided by Microsoft and used heavily in ASP.NET Core.
Basically you create a strong typed class and configure it in the Startup.cs class.
public class MySettings
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
and initialize it in the Startup class.
// load it directly from the appsettings.json "mysettings" section
services.Configure<MySettings>(Configuration.GetSection("mysettings"));
// do it manually
services.Configure<MySettings>(new MySettings
{
Value1 = "Some Value",
Value2 = Configuration["somevalue:from:appsettings"]
});
then inject these options everywhere you need it.
public class MyService : IMyService
{
private readonly MySettings settings;
public MyService(IOptions<MySettings> mysettings)
{
this.settings = mySettings.Value;
}
}
By the principle of Information Hiding in Object-Oriented Programming, most classes should not need to have access to your application configuration. Only your main application class should need to directly have access to this information. Your class libraries should expose properties and methods to alter their behavior based on whatever criteria their callers deem necessary, and your application should use its configuration to set the right properties.
For example, a DateBox shouldn't need to know how timezone information is stored in your application configuration file - all it needs to know is that it has a DateBox.TimeZone property that it can check at runtime to see what timezone it is in.
I have an ASP.NET 5 MVC 6 application. It has a Data Access library which needs a connection string to make a connection to the database.
Currently I am passing a strongly typed configuration settings class with connection string as a public property all the way up from the MVC controllers (Where it is received through DI) to the Data Access Class library.
I want to know if there is a better way for a class library to access strongly typed configuration settings using dependency injection or any other mechanism ?
Thank you.
EDIT : Code Example
This is a generic DbTransaction class which is called from the business layer.
public class DbTransactions<TEntity> where TEntity : DbEntity, new()
{
private readonly Query _query;
public DbTransactions(string connectionString)
{
_query = new Query(connectionString);
}
public TEntity GetById(long id)
{
var sqlGenerator = new SqlGenerator<TEntity>();
var sql = sqlGenerator.GetSelectByIdQuery();
var mapper = new NMapper.Mapper<TEntity>();
var cmd = _query.GetNpgsqlCommand(sql, new { id });
return mapper.GetObject(cmd);
}
}
The query class creates the connection object from the connection string that is provided to it.
I agree with #Steven that using IOptions<T> is a bad idea. You can however use the ConfigurationBinder extensions to read out a specific section of configuration into a strongly-typed POCO class. Just make sure you have this somewhere in your project.json's dependencies section:
"dependencies": {
[other dependencies],
"Microsoft.Extensions.Configuration.Binder": "1.0.0-rc1-final",
[other dependencies]
}
Just build up your configuration as normal. For example, say you had a Database.json configuration file that looked like this:
{
"Database": {
"ConnectionInfo": {
"connectionString": "myConnectionString"
}
}
}
You can build your configuration from the Startup method in Startup.cs:
public IConfiguration Configuration { get; private set; }
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) {
IConfigurationBuilder configBuilder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("Database.json")
.AddEnvironmentVariables()
Configuration = configBuilder.Build();
}
Now we can make a POCO class to match the "Database:ConnectionInfo" section of the JSON configuraiton file. You can match it to an interface as #janhartmann suggests, but it may or may not be necessary.
public class DatabaseConnectionInfo {
public string ConnectionString { get; set; }
}
Now, how can we get that DatabaseConnectionInfo class populated with the data from the JSON config file? One way is to use the IOptions<T> framework type, but I don't like using framework types when I can avoid them. Instead, you can get an instance like so:
DatabaseConnectionInfo dbConnInfo = Configuration
.GetSection("Database:ConnectionInfo")
.Get<DatabaseConnectionInfo>();
Now you can just register the dbConnInfo type as a singleton of the type DatabaseConnectionInfo (or as a singleton of an interface type if you prefer to have an immutable configuration settings object). Once it's registered in the IoC container, you can constructor inject it where needed:
public class DbTransactions<TEntity> where TEntity : DbEntity, new()
{
private readonly Query _query;
public DbTransactions(DatabaseConnectionInfo dbConnInfo)
{
_query = new Query(dbConnInfo.ConnectionString);
}
public TEntity GetById(long id) { ... }
}
You can let your service class depend on a an interface, e.g.:
public interface IConnectionFactory {
string ConnectionString();
}
public class MyDataAccessClass {
private readonly IConnectionFactory _connectionFactory
public MyDataAccessClass(IConnectionFactory connectionFactory) {
_connectionFactory = connectionFactory;
}
public void Whatever() {
var connectionString = _connectionFactory.ConnectionString();
}
}
And then make an implementation of it (as near to your composition root as possible):
public class SqlConnectionFactory : IConnectionFactory {
public string ConnectionString() {
return "myConnectionString";
}
}
Let the interface have the methods or properties you need.
Wire like:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConnectionFactory, SqlConnectionFactory>();
}
I use a similar method to some of those listed earlier, but I think its sufficiently different to warrant another answer.
Firstly I define an interface with all the configuration that my class needs. In this case
public interface IDbTransactionsConfiguration {
string ConnectionString { get; }
}
Then I alter my class to take this configuration via constructor injection
public class DbTransactions<TEntity> where TEntity : DbEntity, new() {
public DbTransactions(IDbTransactionsConfiguration configuration) {
...
}
}
Then I define a class that handles all the configuration for my application.
public class MyApplicationConfiguration : IDbTransactionsConfiguration, ISomeOtherConfiguration, etc {
public string ConnectionString { get; }
... other configuration
}
Then I pass this class into all classes that need it using some kind of Depenendency Injection (normally Castle Windsor or AutoFac for me).
If it is too difficult to construct DbTransactions for legacy type reasons, I define a static version of MyApplicationConfiguration and access this directly.
More details on this blog post.
I am starting a vNext project, and I'm having some issues kicking it off the ground. I have added a table to the ApplicationDbContext class, and it successfully created the table in the db (which in my case is in Azure). However, I can't seem to correctly instantiate a dbContext to use in my Controllers.
In my experience with previous ASP.NET EF projects, I could instantiate the ApplicationDbContext class without passing it any parameters, but in the case of vNext however, it seems to expect a number of things (IServiceProvider, and IOptionsAccessor<DbContextOptions>). I have tried creating a parameter-less constructor, but the App breaks due to not knowing what connection strings to use. My code is below -- as you see in the OnConfiguring(DbContextOptions options) override, I force the connection string in via the DbContextOptions, but that's obviously not ideal, and I feel like I'm just not understanding where those two IServiceProvider, and IOptionsAccessor parameters need to come from.
Thanks for any help!
namespace Project.Models
{
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser
{
public string CompanyName { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
private static bool _created = false;
public DbSet<Business> Businesses { get; set; }
public ApplicationDbContext()
: base()
{
if (!_created)
{
Database.EnsureCreated();
_created = true;
}
}
protected override void OnConfiguring(DbContextOptions options)
{
var configuration = new Configuration();
configuration.AddJsonFile("config.json");
configuration.AddEnvironmentVariables();
options.UseSqlServer(configuration.Get("Data:DefaultConnection:ConnectionString"));
}
public ApplicationDbContext(IServiceProvider serviceProvider, IOptionsAccessor<DbContextOptions> optionsAccessor)
: base(serviceProvider, optionsAccessor.Options)
{
// Create the database and schema if it doesn't exist
// This is a temporary workaround to create database until Entity Framework database migrations
// are supported in ASP.NET vNext
if (!_created)
{
Database.EnsureCreated();
_created = true;
}
}
}
}
IServiveProvider and IOptionAccessor are injected by the Dependency Injection
the ASP.Net Core DI has limitation, you cannot have more than one constructor.
Read this: http://blogs.msdn.com/b/webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx