In the process of upgrading our version of Entity Framework from 2.2.6 to 6.0.12 many of the integration tests have broken. These use an in memory sqlite database.
The error revolves around migrations and a database function that was added in a migration. This function is used to define a Computed Column in the dbContext OnModelCreating() method. During unit test set up we run
_dbContext.Database.EnsureDeleted();
_dbContext.Database.EnsureCreated();
Calling EnsureCreated results in this exception being thrown:
Microsoft.Data.Sqlite.SqliteException : SQLite Error 1: 'near "(": syntax error'.
Stack tracing this, it occurs OnModelCreating when we call.
modelBuilder.Entity<MyObject>()
.Property(p => p.LinkId)
.HasComputedColumnSql("Schema.fn_link_id([Id])");
The syntax for this appears to be correct, so despite the exception error message, this seems to be occurring because the function does not exist as EnsureCreated() does not run a migration.
So my question is what is the best method to update an in memory sqlite database to include a database function that was added during a migration and fix this error?
Things I've attempted:
Replacing the call to .EnsureCreated() with a call to .Migrate(). This doesn't work because of the limitations of SqLite migrations, being unable to AlterColumns etc. A work around exists where I could rewrite all of the existing migrations by hand to accommodate sqlite, but I'd like to avoid having to do this for 5+ years of migrations.
Use .Migrate() and also change the database provider to a local instance of SqlServer. This resolves the issue and the unit tests pass locally. However, this would result in additional overhead and considerations in our CICD pipeline which we'd like to avoid.
Create the function in the test itself to bypass a need for migrations - Results in the same unhelpful syntax error:
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
connection.CreateFunction("Schema.fn_link_id", (int Id) => Guid.NewGuid());
var options = new DbContextOptionsBuilder<MyDbContext>()
.UseSqlite(connection)
.Options;
Related
Wanted Result: Run integration tests in parallel with entity framework configured to use some kind of database that enforces constraints.
Current Situation: I have a project with some integration tests, and by that I mean test set up with WebApplicationFactory<Startup>. Currently I use it with EF Core setup to use In Memory Database
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
ConfigureWebHostBuilder(builder);
builder.ConfigureTestServices(services =>
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
services.AddDbContext<AppDbContext>(options =>
{
options.UseInMemoryDatabase("testDb");
options.UseInternalServiceProvider(serviceProvider);
options.EnableDetailedErrors();
options.EnableSensitiveDataLogging();
});
});
}
While this works, the problem is that it doesn't enforce SQL queries and I already ran in multiple cases where integration tests show the feature works, but when trying to reproduce it with actual project running and having EF connected to SQL Server database, the feature fails because of some failed database constraint.
Ideas:
At first I thought of using SQLite in memory database. Didn't work, I think it's some concurrency issue, because at the start of every test case, I wipe the database with .EnsureDeleted() .EnsureCreated() .Migrate()
Then SQLite with files with random names. Didn't work, because I couldn't manage to wipe out internal DbContext Cache. I was sure that file was recreated and clean, but DbContext simply had cached entities.
Finally after some migrations I released that I wouldn't probably be able to use SQLite at all, because it doesn't support a lot of migration types (Removing FK, altering a column, etc.)/
I have read this blog: which explains the difference between Database Initializer seeds and Migration seed methods.
I am working on a project, that uses code-first Entity Framework, with migrations enabled. A Database Initializer seed is present and it will execute on my local database, when the database is created.
But in the production environment, I do not have the rights to add databases myself. I need to run update-database -script from package manager console on an empty newly created database and then execute the migrations manually.
As I do not have direct access from my development environment to the production database server, I am thinking out loud: there is no gain in rewriting the Database Initializer to a Migrations Seed.
How can I obtain the script (SQL), that is the insert statements, defined in the Database Initializer seed method?
Of course I can :
run update-database on an empty existing database. (no seed method is executed)
run update-database on a non-existing database. (seed method is executed)
Do a SQL compare between the 2 databases and get the INSERT queries that way.
But there should be an easier way right?
Why does the update-database -script not show the INSERT statements from the database initializer seed method in the first place?
I am not sure if the logging described in Entity Framework Logging and Intercepting Database Operations will catch the SQL from the DatabaseInitializer. If not you can try to find a 3rd party profiler to catch your SQL. Personally, I use ORM Profiler and it catches everything, however it is not free.
And yes, there should be an easier way.
I'm using EF6 and I'm now setting up some tests for my aggregates. I've decided to use Effort.EF6 because I'd like to have those tests run without having to install an entire database engine.
My DbContext uses migrations and a seeding method that inserts some data. Can Effort.EF6 make use of that or should I use Effort's methods of seeding data ?
The migrations take place automatically. I call the normal context seed method when I need populated data. Note that depending on the scope of your context (per test, or per test assembly) you may be running lots and lots of queries to do your seeding. That has both performance implications, and debugging issues, since any seeding bugs will start showing up as bugs in your tests, and any logging that happens during seeding will log as part of each test.
var connection = Effort.DbConnectionFactory.CreateTransient();
var context = new DbContext(connection);
context.Seed();
I have been playing around with EF6 CodeFirst and Migrations. So far all good. But there is one thing I don't understand (conceptually):
In my specific case I am determining the database to connect to at runtime (via some external config XML / JSON or similar). So during development I am a) not certain what database it is I am working with and b) do not necessarily have one available. But EF still requires one, because otherwise I cannot use Update-Database. And if I cannot use Update-Database, I cannot scaffold further migrations.
Is there something I am missing or do I necessarily need a dev-database to develop something using EF6?
The real problem I face is that Update-Database fails with an error telling me that connecting to a DB failed.
What am I doing wrong. I have got a user DbContext setup and working when I originally created the Code-First with powershell it all worked fine.
I implemented Database Initializer as expected on application start.
Database.SetInitializer<UserDbContext>(new CreateDatabaseIfNotExists<UserDbContext>());
Just to test out if it really creates the database I actually dropped the database and now I am stuck the database will not be created. I am using SQL Server 2012, any idea what could be wrong.
The error message I am getting is
System.InvalidOperationException: Migrations is enabled for context 'UserDbContext' but the database does not exist or contains no mapped tables. Use Migrations to create the database and its tables, for example by running the 'Update-Database' command from the Package Manager Console.
I have tried the same from Package Manager console and it is still give me the same message.
Finally figured the solutions, not sure why or what. Changed my Database initializer to MigrateDatabaseToLatestVersion instead of CreateDatabaseIfNotExists worked.
Database.SetInitializer<UserDbContext>(new MigrateDatabaseToLatestVersion<UserDbContext, Configuration>());
Edit:
With your new error message the problem comes from you having migrations enabled and already ran a migration (probably the first creation of the database) and since you dropped the DB the migration history has been lost. If your not using Automatic migrations you can not go in and make changes to the database your self and expect code-first to know about it. Try disabling migrations and re-enabling them to see if that clears out the migration history.
You will need to make a call into the DB either as a read or insert of data for the DB to initially be created. The code you use only tells EF how to deal with a database if one does not exist when it tries to find it. For me I use the method outlined at http://www.codeproject.com/Tips/411288/Ensure-Your-Code-First-DB-is-Always-Initialized