Entity Framwork Core Add-Migration fails - c#

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.

Related

Register EF Core in Prism in full .NET Framework

I want to use EF.Core 2.2.6 in a .Net Framework (4.6.2) project. I have created a separate project for the database.
I want to register the DbContext in the main project using dependency injection over the Prism framework (Unity).
var optionsBuilder = new DbContextOptionsBuilder<DbContext>();
optionsBuilder.UseSqlite(#"Data Source=CustomerDB.db");
containerRegistry.GetContainer().RegisterType<DbContext, CrossSettingContext>();
containerRegistry.GetContainer().RegisterType<DbContext>(new InjectionConstructor(optionsBuilder));
The database context:
public class CrossSettingContext : DbContext
{
private static Action<DbContextOptionsBuilder> onConfigure;
#pragma warning restore 649
public CrossSettingContext(DbContextOptions<CrossSettingContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.EnableSensitiveDataLogging(true);
onConfigure?.Invoke(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Setting>().Map();
}
}
I get the following exception. The question is if I am using the right approach for registering EF Core.
System.InvalidOperationException: "No member matching data has been found.
Error in: RegisterType(Invoke.Constructor(Microsoft.EntityFrameworkCore.DbContextOptionsBuilder`1[Microsoft.EntityFrameworkCore.DbContext]))
I found this question -> but I need EF Core 2.2.6 and not Entity Framework 6.0
The answer from Haukinger is partially correct, but missing quite a bit of important information.
What you will want to do something like:
var optionsBuilder = new DbContextOptionsBuilder<CrossSettingContext>();
optionsBuilder.UseSqlite( #"Data Source=CustomerDB.db" );
However you do not need anything Container Specific AT ALL here. All you really need is:
containerRegistry.RegisterInstance(optionsBuilder.Options);
You do not actually need to register the CrossSettingContext if you want a new instance each time it's resolved. Though you could do the following if you want a singleton:
containerRegistry.RegisterSingleton<CrossSettingContext>();
When you want to use it you can just inject the context in your ViewModel/Services like:
public class ViewAViewModel
{
public ViewAViewModel(CrossSettingContext context)
{
// Do Stuff
}
}

DbContext in separate class library project with dependency injection

I'm currently creating a WebAPI for a school project.
Before is worked with DI but i can't seem to get it working in the current context.
for the project i'm using multiple projects for the layer where my DAL needs to get the dbContext through DI.
DbContext:
public FICTIEContext(DbContextOptions<FICTIEContext> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
}
}
startup register:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<FICTIEContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Database")));
}
Call to the DAL Constructor:
public class LoginBL
{
private LoginDAL _LoginDAL;
public bool LoginValidation(string login, string password)
{
_LoginDAL = new LoginDAL(); //Gives the error. As i remember given that dependency injection works correctly it should work like this.
return _LoginDAL.LoginValidation(login, password); ;
}
}
for my DAL i'm using a c# class library project with a class where i have a constructor which has the DbContext as parameter:
private readonly FICTIEContext _Database;
public LoginDAL(FICTIEContext database)
{
_Database = database;
}
When using this code i can't seem to call the constructor without getting errors about not giving any parameters with the call to the DAL.
Which part am i missing withing my solution and how can i fix it.
Of course you need to provide an instance of FICTIEContext when creating an instance of a LoginDAL given your current implementation. That's the purpose of using dependency injection.
The question is whether you actually need or want dependency injection in this case? I doubt it. There should be no point of using nor testing the DAL class without a DbContext anyway.
In this case you could simply let the DAL class create an instance of the FICTIEContext internally. You may of course also provide an overload of the constructor that accepts a context if you want to:
private readonly FICTIEContext _Database;
public LoginDAL()
{
_Database = new FICTIEContext();
}
public LoginDAL(FICTIEContext database)
{
_Database = database;
}
Then the consumer of the class can choose whether to supply a custom FICTIEContext or let the LoginDAL class create a default one.

Can't create an instance of the DbContext in AspNetCore 2.0 application using DI for options

According to the docs I'm supposed to use the following statement to access my data in the DB (which always works great when using the non-DI based context approach).
using (Context context = new Context())
return context.Workers.ToList()
However, now I'm trying to follow the docs for how to deploy EF context based on DI provided by the service configuration like this.
public void ConfigureServices(IServiceCollection services)
{
...
string connection = "...";
services.AddDbContext<Context>(_ => _.UseSqlServer(connection));
}
This seems to be working when manipulating the database schema but produces an error when trying to query for data (the first statement above) due to the fact that there's no default constructor in the context class, which looks like this.
public class Context : DbContext
{
public Context(DbContextOptions<Context> options) : base(options) { }
public DbSet<Worker> Workers { get; set; }
protected override void OnModelCreating(ModelBuilder builder) { ... }
}
Am I supposed to introduce a parameterless constructor in additional to the one i already have? Or should I provide the options object by myself? (Frankly speaking, I was expecting the DI to provide it for me automagically, so I feel a bit confused if I've understood the concept correctly.)
I couldn't find any info on this particular issue in the docs.
Now that I understood what you meant, this is completely wrong:
using (Context context = new Context())
return context.Workers.ToList();
You are not supposed to ever create instances yourself when using Dependency Injection. This is how your code should look like:
public class HomeController : Controller
{
private readonly Context context;
public HomeController(Context ctx)
{
context = ctx;
}
public List<Worker> GetWorkers()
{
return context.Workers.ToList();
}
}
I'd suggest you to follow this Microsoft Docs article on DI which explains both DI and how to work with it (as well as configuring Entity Framework Core) on ASP.NET Core.

Configuring DbContext Constructor

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; }
//...
}

EF code first: inherited dbcontext creates two databases

I'm trying to create a base dbcontext that contains all the common entities that will always be reused in multiple projects, like pages, users, roles, navigation etc.
In doing so I have a ContextBase class that inherits DbContext and defines all the DbSets that I want. Then I have a Context class that inherits ContextBase where I define project specific DbSets. The classes are defined as follows:
public class ContextBase : DbContext
{
public virtual DbSet<User> Users { get; set; }
//more sets
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UsersConfiguration());
//add more configurations
}
}
public class Context : ContextBase
{
public DbSet<Building> Buildings { get; set; }
//some more project specific sets
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new BuildingsConfiguration());
//add more project specific configs
}
}
In my global.asax:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Configuration>());
where Configuration referes to a class inheriting DbMigrationsConfiguration and overriding the Seed method.
The two context classes are defined in the same namespace, but cross assembly (in order that I may update the base project in multiple existing projects without touching the project specific code) - not sure if this is relevant.
MY PROBLEM:
When running this code, it works fine, but when looking in the Database, it actually creates two different databases!! One containing all the base entity tables and one containing BOTH base and custom tables. CRUD operations are only performed on the custom version (which is obviousely what I want), but why does it create the schema of the other one as well?
Any help is appreciated, thanks!
UPDATE:
The following code is what I ended up with. It isn't ideal, but it works. I would still love to get feedback on ways to improve this, but in the meantime I hope this helps further the process. I REALLY DO NOT RECOMMEND DOING THIS! It is extremely error prone and very frustrating to debug. I'm merely posting this to see if there is any better ideas or implementations to achieve this.
One (but not the only) issue still existing is that the MVC views have to be manually added to projects. I've added it to the Nuget package, but it takes 2 to 3 hours to apply a nuget package with so many files when VS is connected to TFS. With some more work and a custom View engine the views can be precompiled (http://blog.davidebbo.com/2011/06/precompile-your-mvc-views-using.html).
The solution is split into the Base Framework projects and the Custom projects (each category includes its own models and repository pattern). The framework projects are packaged up in a Nuget package and then installed in any custom projects allowing the common functionality of any project like user, role and permission management, content management, etc (often referred to as the Boiler Plate) to be easily added to any new projects. This allows any improvements of the boilerplate to be migrated in any existing custom projects.
Custom Database Initializer:
public class MyMigrateDatabaseToLatestVersion : IDatabaseInitializer<Context>
{
public void InitializeDatabase(Context context)
{
//create the base migrator
var baseConfig = new FrameworkConfiguration();
var migratorBase = new DbMigrator(baseConfig);
//create the custom migrator
var customConfig = new Configuration();
var migratorCustom = new DbMigrator(customConfig);
//now I need to check what migrations have not yet been applied
//and then run them in the correct order
if (migratorBase.GetPendingMigrations().Count() > 0)
{
try
{
migratorBase.Update();
}
catch (System.Data.Entity.Migrations.Infrastructure.AutomaticMigrationsDisabledException)
{
//if an error occured, the seed would not have run, so we run it again.
baseConfig.RunSeed(context);
}
}
if (migratorCustom.GetPendingMigrations().Count() > 0)
{
try
{
migratorCustom.Update();
}
catch (System.Data.Entity.Migrations.Infrastructure.AutomaticMigrationsDisabledException)
{
//if an error occured, the seed would not have run, so we run it again.
customConfig.RunSeed(context);
}
}
}
}
Framework's DB Migrations Configuration:
public class FrameworkConfiguration: DbMigrationsConfiguration<Repository.ContextBase>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
public void RunSeed(Repository.ContextBase context)
{
Seed(context);
}
protected override void Seed(Repository.ContextBase context)
{
// This method will be called at every app start so it should use the AddOrUpdate method rather than just Add.
FrameworkDatabaseSeed.Seed(context);
}
}
Custom Project's DB Migrations Configuration:
public class Configuration : DbMigrationsConfiguration<Repository.Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
public void RunSeed(Repository.Context context)
{
Seed(context);
}
protected override void Seed(Repository.Context context)
{
// This method will be called at every app start so it should use the AddOrUpdate method rather than just Add.
CustomDatabaseSeed.Seed(context);
}
}
The custom DbContext
//nothing special here, simply inherit ContextBase, IContext interface is purely for DI
public class Context : ContextBase, IContext
{
//Add the custom DBsets, i.e.
public DbSet<Chart> Charts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//Assign the model configs, i.e.
modelBuilder.Configurations.Add(new ChartConfiguration());
}
}
Framework DbContext:
//again nothing special
public class ContextBase: DbContext
{
//example DbSet's
public virtual DbSet<Models.User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder);
}
In the global.asax AppStart:
//first remove the base context initialiser
Database.SetInitializer<ContextBase>(null);
//set the inherited context initializer
Database.SetInitializer(new MyMigrateDatabaseToLatestVersion());
In the web.config:
<connectionStrings>
<!--put the exact same connection string twice here and name it the same as the base and overridden context. That way they point to the same database. -->
<add name="Context" connectionString="Data Source=.\SQLEXPRESS; Initial Catalog=CMS2013; Integrated Security=SSPI;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient"/>
<add name="ContextBase" connectionString="Data Source=.\SQLEXPRESS; Initial Catalog=CMS2013; Integrated Security=SSPI;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient"/>
</connectionStrings>
(from the comments)
You're creating ContextBase objects directly, apparently as new T() in a generic method with ContextBase as a generic type argument, so any initialisers for ContextBase also run. To prevent creating ContextBase objects (if it should never be instantiated directly, if the derived context should always be used), you can mark the class as abstract.
Your ContextBase seems to have an initializer as well.. You can remove this by
Database.SetInitializer<ContextBase>(null);

Categories