I am attempting to use code first migrations for Entity Framework 6.0
This is for a desktop (WPF) app which is currently in use at a couple of sites with their own copy of the database, so running update-database from the package manager console is of no use.
I had been hitherto using automatic migrations, but I now need to apply a more complex migration than this will allow. The new migration does get applied properly if
update-database -force
option is used (of course, restoring the database to its pre-updated state before testing what follows)
The DbConfiguration (which had been working with AutomaticMigrations) had been set up like so:
[DbConfigurationType(typeof(ContextCeConfiguration))]
public class TrialDataContext : DbContext, ITrialDataContext
{
...
}
public class ContextCeConfiguration : DbConfiguration
{
public ContextCeConfiguration()
{
SetProviderServices(
SqlCeProviderServices.ProviderInvariantName,
SqlCeProviderServices.Instance);
SetDatabaseInitializer<TrialDataContext>(new DataContextInitialiser());
}
}
class DataContextInitialiser : MigrateDatabaseToLatestVersion<TrialDataContext,TrialDataConfiguration>
{
}
internal sealed class TrialDataConfiguration : DbMigrationsConfiguration<BlowTrial.Domain.Providers.TrialDataContext>
{
public TrialDataConfiguration()
{
AutomaticMigrationsEnabled = true;
}
}
I have changed the DbMigrationsConfiguration class to:
internal sealed class TrialDataConfiguration : DbMigrationsConfiguration<BlowTrial.Domain.Providers.TrialDataContext>
{
public TrialDataConfiguration()
{
AutomaticMigrationsEnabled = false;
DbMigrator migrator = new DbMigrator(this);
if (migrator.GetPendingMigrations().Any())
{
migrator.Update();
}
}
However, the line:
DbMigrator migrator = new DbMigrator(this);
Is causing the exception:
ValueFactory attempted to access the Value property of this instance.
Can someone please suggest how/where I should instantiate the instance of DbMigrator so that updates can be programatically applied
Related
I have an Azure function running on .NET Core 3.1. I have a .NET Standard 2.1 library that contains an EF Core 3.1 DbContext.
I'm trying to add migrations from Visual Studio and I'm getting the following errors:
If I run PM> Add-Migration Initial selecting as default the function project I get the error 'No DbContext was found in assembly 'SimonApp' (this is my function project). Ensure that you're using the correct assembly and that the type is neither abstract nor generic.'
If I run the same command against the library where EF Core is installed, I get No parameterless constructor defined for type 'SimonApp.Core.Data.ClinikoEntitiesContext'.
I have found lots of posts and questions that are similar on SO but none of them fix my problem.
I have tried creating a parameterless constructor on the context without luck, I get the same errors. My context looks like this:
public class ClinikoEntitiesContext : DbContext
{
public ClinikoEntitiesContext()
{}
public ClinikoEntitiesContext(DbContextOptions<ClinikoEntitiesContext> options)
: base(options)
{ }
My Startup.cs looks like:
class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var configuration = builder.Services.BuildServiceProvider().GetService<IConfiguration>();
var IsDevelopment = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT")?.Equals("Development", StringComparison.OrdinalIgnoreCase);
var connString = configuration.GetConnectionString("SqlCliniko");
builder.Services.AddDbContext<ClinikoEntitiesContext>(options => options.UseSqlServer(connString)
.UseLazyLoadingProxies()
.EnableSensitiveDataLogging(IsDevelopment.HasValue && IsDevelopment.Value == true));
builder.Services.AddLogging(loggingBuilder =>
{
loggingBuilder.AddConsole()
.AddFilter(DbLoggerCategory.Database.Command.Name, LogLevel.Warning);
});
}
}
I am using Entity Framework Core tools to create Entities and DBContext from the existing database.
Scaffold-DbContext "Server=XXXXXX;Database=MyDB;User ID=xxxx;Password=xxxxxxx" Microsoft.EntityFrameworkCore.SqlServer -ContextDir .-OutputDir Entities -Force
This working. But is there any way to scaffold all the entities with known interface? So i have interface IEntityBase and i want all the entities to have this interface
Note
This question is specific to EF Core 2+ and scaffolding
Update 1
So as per the SO Suggesion i have created CSharpEntityTypeGenerator and IDesignTimeServices The accepted answer is not valid for EF Core > 2.* so i am using the suggession from #Chris Peacock
public class MyEntityTypeGenerator: CSharpEntityTypeGenerator
{
public MyEntityTypeGenerator(ICSharpHelper cSharpHelper)
: base(cSharpHelper)
{
}
public override string WriteCode(IEntityType entityType, string #namespace, bool useDataAnnotations)
{
string code = base.WriteCode(entityType, #namespace, useDataAnnotations);
var oldString = "public partial class " + entityType.Name;
var newString = "public partial class " + entityType.Name + " : EntityBase";
return code.Replace(oldString, newString);
}
}
public class MyDesignTimeServices: IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<ICSharpEntityTypeGenerator, MyEntityTypeGenerator>();
}
}
There is one change i had to make. CSharpEntityTypeGenerator constructor takes ICSharpHelper as parameter instead of ICSharpUtilities
These two classes are in Data assembly which is not a startup project.
At package manager console i executed the scaffolding command again. However i do not see generated entities have base class.
How scaffolding framework would know to use my custom generator? I am adding generator in serviceCollection but looks like the code never get executed
Am i missing something?
You can use EF Core Power Tools with Handlebars templates to achieve this.
https://github.com/ErikEJ/EFCorePowerTools/wiki/Reverse-Engineering#customize-code-using-handlebars
I think these options should have been provided as as a part of EF Scaffolding tool by default.
But here is my code. I am using Microsoft.EntityFrameworkCore.Design 2.2.4 and Microsoft.EntityFrameworkCore.SqlServer 2.2.4 and based on my experience with .NET Core this may change in future version
CSharpEntityTypeGenerator
public class MyEntityTypeGenerator: CSharpEntityTypeGenerator
{
public MyEntityTypeGenerator(ICSharpHelper cSharpUtilities)
: base(cSharpUtilities)
{
}
public override string WriteCode(IEntityType entityType, string #namespace, bool useDataAnnotations)
{
Console.WriteLine(entityType.Name);
string code = base.WriteCode(entityType, #namespace, useDataAnnotations);
var oldString = "public partial class " + entityType.Name;
var newString = "public partial class " + entityType.Name + " : EntityBase";
return code.Replace(oldString, newString);
}
}
By default scaffolding generates classes with the same name as Table name. In our case the table names are Pluralize, but we want class name Singularize. So you have to implement Microsoft.EntityFrameworkCore.Design.IPluralizer. I used Inflator utility. However, note that i could not add Inflector using Nuget in .Net Core project. Looks like it does not support .NET Core yet. So i have added the single code file instead in my project.
IPluralizer
public class MyPluralizer : IPluralizer
{
public string Pluralize(string identifier)
{
return Inflector.Pluralize(identifier) ?? identifier;
}
public string Singularize(string identifier)
{
return Inflector.Singularize(identifier) ?? identifier;
}
}
IDesignTimeServices
You need to add this class in startup project. Initially, i had this class in the same project as other classes but that did not work. I moved this class in startup project, (In my case that is ASP.NET Core project) and it worked
public class MyDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
{
// Start debugger
System.Diagnostics.Debugger.Launch();
serviceCollection.AddSingleton<ICSharpEntityTypeGenerator, MyEntityTypeGenerator>();
serviceCollection.AddSingleton<IPluralizer, MyPluralizer>();
}
}
By default the tables's schema of Identity Server 4 is dbo, i want change it to security, so i create ConfigurationContext which inherit from ConfigurationDbContext:
public class ConfigurationContext : ConfigurationDbContext
{
public ConfigurationContext(DbContextOptions<ConfigurationDbContext> options, ConfigurationStoreOptions storeOptions) : base(options, storeOptions)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("Security");
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var relationalOptions = RelationalOptionsExtension.Extract(optionsBuilder.Options);
relationalOptions.MigrationsHistoryTableSchema = "Security";
}
}
and in add-migration i use ConfigurationContext :
Add-Migration -c ConfigurationContext
but i got this error:
No parameterless constructor was found on 'ConfigurationContext'. Either add a parameterless constructor to 'ConfigurationContext' or add an implementation of 'IDbContextFactory' in the same assembly as 'ConfigurationContext'.
what is the problem?
IdentityServer4 provides this option. In ConfigureServices,
services.AddIdentityServer()
.AddOperationalStore(builder => builder.UseSqlServer(cnStr, options =>
options.MigrationsAssembly(migAssembly)),
storeOption => storeOption.DefaultSchema = "security")
This way, you can continue to use the IDbContextFactory as suggested in the quickstarts.
I know this is quite an old question, but I recently had a similar issue; June Lau's answer does provide some of the info you need to resolve this, but the important part is that migrations don't inspect the database context at runtime, so you need to define the schema before you create your database migration.
Don't worry about extending ConfigurationDbContext either, as that's not needed, just add something like this to your ConfigureServices method in Startup.cs:
var identityServerBuilder = services.AddIdentityServer(options =>
{
// ...
});
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
identityServerBuilder.AddConfigurationStore(options =>
{
options.DefaultSchema = "config";
options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
});
Once you've added that code, create a migration for the relevant database context:
Add-Migration CreateInitialSchema -Context ConfigurationDbContext
You should see that the created migration starts like this:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "config");
migrationBuilder.CreateTable(
name: "ApiResources",
schema: "config",
columns: table => new ...
The problem is that Add-Migration -c ConfigurationContext command does not startup your application and thus does not know how to resolve the classes in your constructor:
public ConfigurationContext( //How do i resolve this, i dont know?
DbContextOptions<ConfigurationDbContext> options,
ConfigurationStoreOptions storeOptions)
: base(options, storeOptions)
{ }
You need to add a parameterless constructor, as the error suggests:
public ConfigurationContext()
: base(/* todo default static logic here */)
{ /* and here */ }
Why
The database migration tries to create an instance of the ConfigurationContext to determine the 'desired' state (the state you want your database to be after the database migration has been executed).
This migration is a static file inside your project saying which Columns and which indexes etc need to be added or removed to the database to create the 'desired' state.
This Add-Migration command simply reflects your code to find the right context, it does not go through your startup class to see which dependencies you have the find (this would become way to complex since there could also be runtime dependencies or dependencies based on App-settings, etc)
I made a DLL to include in an ASP (MVC) project, in the DLL there is some 'base' functionality and ofcourse, I'm including it in other MVC projects.
The problem is I have a EF - Code first project now and it's using a Configuration class for the migrations:
internal sealed class Configuration : DbMigrationsConfiguration<Project.Models.DbContextTest>
{
public Configuration()
{
//AutomaticMigrationsEnabled = true;
}
protected override void Seed(Project.Models.DbContextTest context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
When I'm using this configuration i get the following exception:
Sequence contains more than one matching element
On the following function:
return orderBy != null ? orderBy(query).ToList() : query.ToList();
The function is not the problem, because when i remove the configuration class everything is working fine.
The exception is thrown on a query where I get a list of a table, it only has a Primary Key and no related tables (Foreign Keys).
The configuration class is empty, so I'm wondering what it's doing to make my Dll broken?
Thanks in advance,
Stefan
It was a combination of starting the project and doing manual migration.
When starting the project it's auto migrating the code-first models, this in combination with the manual migration gives this errors / exceptions.
I am trying to use mvc-mini-profiler for db-first EF, but it is not working properly.
(note that I'm using objectcontext, not dbcontext)
Here is the list of stackoverflows I've tried:
Setup of mvc-mini-profiler for EF-db- first
How to get MVC-mini-profiler working on EF 4.1 Database First
versions:
Entity Framework: 4.3.1
MiniProfiler: 2.0.2
MiniProfiler.ef: 2.0.3
This is how I setup miniprofiler:
I've added the following stuff in Global.asax
protected void Application_BeginRequest(
{
MiniProfiler.Start();
}
protected void Application_EndRequest()
{
MiniProfiler.Stop();
}
protected void Application_Start()
{
...
MiniProfilerEF.Initialize_EF42();
}
Then configure an objectcontext,
var entityConnection = new EntityConnection(ConnectionString);
var profiledDbConnection = new EFProfiledDbConnection(entityConnection, MiniProfiler.Current);
var context = profiledDbConnection.CreateObjectContext<MyContext>();
var list = context.MyEntities.ToList();
If I execute this, the following exception occurs when running "context.MyEntities.ToList()"
[System.Data.EntityCommandCompliationException]
the message in the inner exception says:
EntityClient cannot be used to create a command definition from a store command tree.
Have I configured wrong? Any help?
thanks,
I use MiniProfiler and database first Entity Framework and it does work well. You may need to turn off the database initialization strategy inside of your database context as per this related answer: https://stackoverflow.com/a/9762989/325727
public class EmployeeContext : DbContext
{
static EmployeeContext() { Database.SetInitializer<EmployeeContext>(null); }
public IDbSet<Employee> Employees { get; set; }
}
The parameter null turns off database initialization by making sure that there is no initializer available.