Trying to create a code first project using Entity Framework and MySQL.
When I use context.Database.EnsureCreated(); the tables are created correctly, but I would like to use migrations so the code changes to: context.Database.Migrate(); and that is when I get the error:
MySqlException: Table 'library.publishers' doesn't exist
I do see that the database was created and there is an empty table: __efmigrationshistory but that is the only table, no publishers like it does with the EnsureCreated.
What Am I missing here?
Here is the minimal code reproducing the error:
using Microsoft.EntityFrameworkCore;
namespace mySqlEFcore
{
class Program
{
static void Main(string[] args)
{
using (var context = new LibraryContext())
{
context.Database.Migrate();
context.Publishers.Add(new Publisher { ID = 1 });
context.SaveChanges();
}
}
private class Publisher
{
public int ID { get; set; }
}
private class LibraryContext : DbContext
{
public DbSet<Publisher> Publishers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySQL("server=localhost;database=library;user=root;password=123456;SslMode=none;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Publisher>(entity => { entity.HasKey(e => e.ID); });
}
}
}
}
Tried running Add-Migration InitialCreate but ran into more errors ...
Added a reference Microsoft.EntityFrameworkCore.Design and now the InitialCreate shows:
System.MissingMethodException: Method not found: 'Void Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory..ctor(Microsoft.EntityFrameworkCore.Diagnostics.IDiagnosticsLogger'1, Microsoft.EntityFrameworkCore.Storage.IRelationalTypeMapper)'.
at MySql.Data.EntityFrameworkCore.Storage.Internal.MySQLCommandBuilderFactory..ctor(IDiagnosticsLogger'1 logger, IRelationalTypeMapper typeMapper)
I had the same issue. The comment from #Helder Sepulveda solved my issue.
The steps that I followed:
Open Package Manager Console.
Enable-Migrations
Add-Migration InitialCreate (or whatever name that you wish.)
Update-Database
The tables were then created in my MySQL database.
Related
Environment: .Net Core 3.1 REST API / EntityFrameworkCore.InMemory 3.1.6 / XUnit 2.4.1
In a Database First Setup I have a model mapped to a Sql View.
During Code Generation (with EF Core PowerTools 2.4.51) this entity is marked in DbContext with .HasNoKey()
When I try to test the endpoint accessing the DbSet mapped to the Sql View it throws exception:
Unable to track an instance of type '*' because it does not have a primary key. Only entity types with primary keys may be tracked.
Follows some code snippets with highlights of what I have tries so far.
Auto generated DbContext: ViewDemoAccountInfo is the entity mapped to a Sql View. Other entities are mapped to regular Sql Tables
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
namespace Demo.Data.Entities
{
public partial class DemoDbContext : DbContext
{
public DemoDbContext(){}
public DemoDbContext(DbContextOptions<DemoDbContext> options): base(options){}
public virtual DbSet<ViewDemoAccountInfo> ViewDemoAccountInfo { get; set; }
// multiple other entities
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ViewDemoAccountInfo>(entity =>
{
entity.HasNoKey();
entity.ToView("ViewDemoAccountInfo");
entity.Property(e => e.AccountType).IsUnicode(false);
});
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
}
Attempt #1:
The test
public class MyIntegrationTests : BaseIntegrationTest {
// throws "Unable to track an instance of type 'ViewDemoAccountInfo'
// because it does not have a primary key. Only entity types with primary keys may be tracked."
[Fact]
public async Task SetupData_WhenKeylessEntity_ThenShouldNotThrow() {
using (var scope = _testServer.Host.Services.CreateScope()) {
var dbContext = scope.ServiceProvider.GetService<DemoDbContext>();
await dbContext.ViewDemoAccountInfo.AddAsync(MockedAccountInfo); // setup some data
await dbContext.SaveChangesAsync();
}
var endpointUrl = $"{ControllerRootUrl}/account-info";
var response = await _testClient.GetAsync(endpointUrl);
// Assertions
}
}
Helpers
public class BaseIntegrationTest {
protected readonly HttpClient _testClient;
protected readonly TestServer _testServer;
public BaseIntegrationTest() {
var builder = new WebHostBuilder()
.UseEnvironment("Test")
.ConfigureAppConfiguration((builderContext, config) => {
config.ConfigureSettings(builderContext.HostingEnvironment);
});
builder.ConfigureServices(services => {
services.ConfigureInMemoryDatabases(new InMemoryDatabaseRoot());
});
builder.UseStartup<Startup>();
_testServer = new TestServer(builder);
_testClient = _testServer.CreateClient();
}
}
// Regular DbContext
internal static class IntegrationExtensions {
public static void ConfigureInMemoryDatabases(this IServiceCollection services, InMemoryDatabaseRoot memoryDatabaseRoot) {
services.AddDbContext<DemoDbContext>(options =>
options.UseInMemoryDatabase("DemoApp", memoryDatabaseRoot)
.EnableServiceProviderCaching(false));
}
}
The simplest solution is to change the Auto generated DbContext and remove the .HasNoKey() config, but it would be removed each time the schema structure will be generated with EF Core PowerTools.
Search for other solutions which would not require changes in Auto generated files
Found: how to test keyless entity - github discussion planned for EF Core 5 and stackoverflow source
Attemp #2 - Try to create another DbContext and override the entity setup by adding explicitly a key when Database.IsInMemory
public class TestingDemoDbContext : DemoDbContext {
public TestingDemoDbContext(){}
public TestingDemoDbContext(DbContextOptions<DemoDbContext> options): base(options){}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ViewDemoAccountInfo>(entity => {
if (Database.IsInMemory()) {
entity.HasKey(e => new { e.AccountType, e.AccountStartDate });
}
});
}
}
In BaseIntegrationTest use the "extended" TestingDemoDbContext in ConfigureInMemoryDatabases method.
internal static class IntegrationExtensions {
public static void ConfigureInMemoryDatabases(this IServiceCollection services, InMemoryDatabaseRoot memoryDatabaseRoot) {
services.AddDbContext<TestingDemoDbContext>(options =>
options.UseInMemoryDatabase("DemoApp", memoryDatabaseRoot)
.EnableServiceProviderCaching(false));
}
}
The test is similar, with a small difference: var dbContext = scope.ServiceProvider.GetService<TestingDemoDbContext>();
Result - strange, but it throws The string argument 'connectionString' cannot be empty. - even I do use the InMemoryDatabase
Attemp #3 - Try to use the OnModelCreatingPartial method to add a Key for that keyless entity.
So, in the same namespace with the Regular DbContext, create the partial DbContext meant to enrich the existing config
namespace Demo.Data.Entities {
public partial class DemoDbContext : DbContext {
partial void OnModelCreatingPartial(ModelBuilder builder) {
builder.Entity<ViewDemoAccountInfo>(entity => {
// try to set a key when Database.IsInMemory()
entity.HasKey(e => new { e.AccountType, e.AccountStartDate }));
});
}
}
}
Result - The key { e.AccountType, e.AccountStartDate } cannot be added to keyless type 'ViewDemoAccountInfo'.
Any hints on how to add some mock data for Keyless entities (mapped to Sql View), with InMemoryDatabase, for testing purpose (with XUnit) would be grateful appreciated.
As well, if something is wrong or is considered bad practice in the setup I have listed here - would appreciate to receive improvement suggestions.
I know this is an old post, but I wanted to share the solution I ended up using for this in case anyone else comes across here.
In my model class, I added a [NotMapped] field named 'UnitTestKey'.
KeylessTable.cs
[Table("KeylessTable", Schema = "dbo")]
public class KeylessTable
{
[NotMapped]
public int UnitTestKey { get; set; }
[Column("FakeColName")]
public string FakeColumn { get; set; }
}
In my DbContext class, I use IHostEnvironment and used that to set HasKey() or HasNoKey() depending on if we are in the "Unit Testing" environment.
This example is using .NET 5. If using .NET Core 3.1 like in the original question, you would want to use IWebHostEnvironment.
ContextClass.cs
public class ContextClass : DbContext
{
private readonly IHostEnvironment _environment;
public ContextClass(DbContextOptions<ContextClass> options, IHostEnvironment environment) : base(options)
{
_environment = environment;
}
public DbSet<KeylessTable> KeylessTable => Set<KeylessTable>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<KeylessTable>(entity => {
if (_environment.EnvironmentName == "UnitTesting")
entity.HasKey(x => x.UnitTestKey);
else
entity.HasNoKey();
});
}
}
Then in my unit test, I mock the environment and set the name to be "UnitTesting".
UnitTest.cs
[Fact]
public async void GetKeylessTable_KeylessTableList()
{
// Arrange
var environment = new Mock<IHostEnvironment>();
environment.Setup(e => e.EnvironmentName).Returns("UnitTesting");
var options = new DbContextOptionsBuilder<ContextClass>().UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()).Options;
var contextStub = new ContextClass(options, environment.Object);
contextStub.Database.EnsureDeleted();
contextStub.Database.EnsureCreated();
contextStub.Set<KeylessTable>().AddRange(_keylessTablelMockData);
contextStub.SaveChanges();
var repository = new Repo(contextStub);
// Act
var response = await repository.GetKeylessData();
// Assert
response.Should().BeEquivalentTo(_keylessTablelMockData);
}
(I apologize for my English)
UPDATE
Well, now I'm feeling so dumb, I Forgot install the package of
Microsoft.Entityframeworkcore.tools
In my console app Project. I install the package and it run correctly. I don't know if this can be helpful for someone but I'll let the post open. Saludos!!!
I'm doing some tests with Entity Framework Core in a .Net Framework console application. I have my solution split into three projects: one for my models, one for my data context and one for the console app.
I'm using Mysql server for database, and create the database with migrations so easily but when I'm trying to insert some data in the database, when I call the SaveChanges method, my app throws an exception.
I need to build an enterprise app, and I need to migrate the existing database (.dbf) to a Mysql. I'm trying to use EF Core in a console application in which and doing some tests.
Disposing transaction.
Closing connection to database 'EntityFrameworkCore' on server 'localhost'.
Closed connection to database 'EntityFrameworkCore' on server 'localhost'.
An exception occurred in the database while saving changes for context type 'EntityFrameworkCore.Data.TiendaContext'.
Microsoft.EntityFrameworkCore.DbUpdateException:
An error occurred while updating the entries. See the inner exception for details.
---> System.MissingFieldException: Campo no encontrado: 'Microsoft.EntityFrameworkCore.Metadata.Internal.EntityMaterializerSource.ThrowReadValueExceptionMethod'.
This is my program class
class Program
{
static void Main(string[] args)
{
InsertCliente();
}
private static void InsertCliente()
{
using (var ctx = new TiendaContext())
{
var cliente = new Cliente { Nombre = "Pedro" };
ctx.GetService<ILoggerFactory>().AddProvider(new MyLoggerProvider());
ctx.Clientes.Add(cliente);
ctx.SaveChanges();
}
}
}
My DbContext class
public class TiendaContext:DbContext
{
public DbSet<Cliente> Clientes { get; set; }
public DbSet<Producto> Productos { get; set; }
public DbSet<Transaccion> Trasacciones { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ProductosTransacciones>()
.HasKey(k => new { k.ProductoId, k.TransaccionId });
base.OnModelCreating(modelBuilder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySQL(
"server=localhost;port=3306;database=EntityFrameworkCore;uid=root;password=408792");
}
}
From the exception you are reporting it appears the entity you are trying to save; doesnt match the database structure. Compare the entities to the tables to make sure they match.
You should update your ef core nuget package. After that, hope error will gone
I'm trying to create a database through EF, to store members and their locations.
I've had a look at other examples, but no matter what I do, when I run my program, my database doesn't get created.
I've noticed that the example I followed created a DataInitializer class, which puts some records in the database. I haven't done this yet, since I would like to check if my database gets created with the right columns.
Could not having data in the database be the problem?
public class ApplicationDbContext : DbContext
{
public DbSet<Lid> Leden { get; set; }
public DbSet<Locatie> Locaties { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfiguration(new LidConfiguration());
builder.ApplyConfiguration(new LocatieConfiguration());
}
}
Appsettings.json:
{ "ConnectionStrings": {
"DefaultConnection": "Server=.\\sqlexpress;Database=Taijitan;Trusted_Connection=True;MultipleActiveResultSets=true"},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
In the example I followed, startup.cs also contains
services.AddScoped<BeerhallDataInitializer>();
and all the way at the bottom
beerhallDataInitializer.InitializeData();
I don't suppose this is what's causing the problem, but I'm not so sure anymore
EDIT:
public class BeerhallDataInitializer
{
private readonly ApplicationDbContext _dbContext;
public BeerhallDataInitializer(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public void InitializeData()
{
_dbContext.Database.EnsureDeleted();
if (_dbContext.Database.EnsureCreated())
{ *create new objects and add them to _dbcontext*
_dbContext.SaveChanges();
}
}
In order to run a migration you first need to create the migration with the command:
'dotnet ef migrations add InitialCreate'
Secondly, then you need to run your migration with the command:
'dotnet ef database update' or by creating some code that run missing migrations which i can assume it's what beerhallDataInitializer.InitializeData() does but can't know for sure since I have no more information about it but to be able to migrate via code you need the reference to the IApplicationBuilder like so in the startup.cs file:
DbInitializer.Migrate<ApplicationDbContext>(app);
and my dbinitilizer is :
public static class DbInitializer
{
public static void Migrate<T>(IApplicationBuilder app) where T : DbContext
{
using (var scope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
scope.ServiceProvider.GetRequiredService<T>().Database.Migrate();
}
}
}
Hope it helps anything just comment bellow but give more information about what you have done
Edit:
try changing your connection string to:
Data Source=localhost\\SQLEXPRESS;Initial Catalog=Taijitan;Integrated Security=True
i have three tier application in Asp.net Mvc Core and use EF core,
now i want create auto migration ,
i have DAL layer that my context available here
public class AdminContext : DbContext
{
public AdminContext(DbContextOptions<AdminContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<AdsAdsCategory>()
.HasKey(bc => new { bc.AdsId, bc.AdsCategoryId });
modelBuilder.Entity<AdsAdsCategory>()
.HasOne(bc => bc.Ads)
.WithMany(b => b.AdsAdsCategories)
.HasForeignKey(bc => bc.AdsId);
modelBuilder.Entity<AdsAdsCategory>()
.HasOne(bc => bc.Category)
.WithMany(c => c.AdsAdsCategories)
.HasForeignKey(bc => bc.AdsCategoryId);
}
public DbSet<Ads> Adses { get; set; }
public DbSet<AdsCategory> AdsCategories { get; set; }
public DbSet<AdsPosition> AdsPositions { get; set; }
public DbSet<AdsCustomer> AdsCustomers { get; set; }
}
and in my application startup
i write this code
var context = app.ApplicationServices.GetService<AdminContext>();
if (!context.Database.EnsureCreated())
context.Database.Migrate();
when i run application database was created and table generate but __migrationhistory doesn't exist and migration not generate,
when in start up i remove this line code
if (!context.Database.EnsureCreated())
database was created and __migrationhistory table generated,but my model table not generate,
how i can solve this problem ?
and run auto migration in EF Core in three tier application?
You need to do the following to enable Migration in MVC .NET Core.
1-open the Package Manager Console in Visual Studio. Type and execute this code.
add-migration ClassName
pm> add-migration FirstInitialize
2-After executing the code, the migration classes will be created for your models
public partial class FirstInitialize : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
//After executing the code, this section will be automatically generated for your models
}
}
3-Then, with the following code you enter in the class section of the program.cs main method, your models will be built into a database.
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<YouDbContext>();
context.Database.Migrate();
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred while seeding the atabase.");
}
}
4-Each time you change your models or add a new one, you have to repeat the steps. Choose a new name for your migration every time.
Sample:
pm> add-migration SecondInitialize
*I can't speak english well
Automatic migration like in EF6 do not exist in EF core. You either have to generate your migrations before starting and then use
context.Database.Migrate();
or you drop your whole database on each launch and use
context.Database.EnsureCreated();
to recreate the updated database.
The second one wont allow you to add any migrations later on, so you have to recreate entire database each time. To delete database you can use
context.Database.EnsureDeleted();
I have a DatabaseInitializer class
public class DatabaseInitializer : CreateDatabaseIfNotExists<DatabaseContext>
{
protected override void Seed
(
DatabaseContext databaseContext
)
{
// Seed the hash methods.
var defaultHashMethod = new HashMethod
{
Description = "Default",
CreateDate = DateTime.Now
};
databaseContext.HashMethod.Add(defaultHashMethod);
databaseContext.SaveChanges();
}
}
In my DatabaseContext class I set the initializer
public DatabaseContext() : base("DatabaseContext")
{
InitializeDatabase();
}
private void InitializeDatabase()
{
Database.SetInitializer(new DatabaseInitializer());
if (!Database.Exists())
{
Database.Initialize(true);
}
}
As far as I can understand the seed method is only invoked once you perform an operation such as a query. My database is created successfully and I'm querying the table, but the seed method is never called.
Update:
It seems like the problem is caused because of a class that is inheriting from my DatabaseContext class, when using this class to perform database operations, the seed method is not called. When using my DatabaseContext class, everything works as expected
public DbSet<TestEntity> TestEntity { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
You need to call Update-Database from the Package Manager Console.
The only way I could get this to work was to call the seed method myself
Here are the methods for my DatabaseContext class
public DatabaseContext() : base("DatabaseContext")
{
InitializeDatabase();
}
public DatabaseContext(string connectionString) : base(connectionString)
{
Database.Connection.ConnectionString = connectionString;
InitializeDatabase();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
Here I changed my InitializeDatabase method from
private void InitializeDatabase()
{
Database.SetInitializer(new DatabaseInitializer());
if (!Database.Exists())
{
Database.Initialize(true);
}
}
to
protected virtual void InitializeDatabase()
{
if (!Database.Exists())
{
Database.Initialize(true);
new DatabaseInitializer().Seed(this);
}
}
This can happen if your Update-Database command does not run successfully, and this does not necessarily mean that it errors out. There might be changes that EF recognizes as "outstanding" that need to be added to a migration.
Try calling "Add-Migration {migrationNameHere}" and then try "Update-Database" again.
to get Seed method to be called when you are not using AutomaticMigration, you should use MigrateDatabaseToLatestVersion initializer for your code-first database.
like this:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<YourContext,YourConfiguration>());
this way, Seed method will be called every time the migration is done successfully.
I had this issue and the problem was my Context constructor did not use the same name as in my web.config.
If you are using Code-First then you can populate the data when the application runs for the first time.
Create a DbInitializer
public class MyDbInitializer : IDatabaseInitializer<MyDbContext>
{
public void InitializeDatabase(MyDbContext context)
{
if (context.Database.Exists())
{
if (!context.Database.CompatibleWithModel(true))
{
context.Database.Delete();
}
}
context.Database.Create();
User myUser = new User()
{
Email = "a#b.com",
Password = "secure-password"
};
context.Users.AddOrUpdate<User>(p => p.Email, myUser);
context.SaveChanges();
}
}
Register this DbInitializer in your Global.asax.cs Application_Start method
Database.SetInitializer(new My.namespace.MyDbInitializer());
My seed was not being executed either. However, it was because I added a column to a model that I had no intention of using in my actual database and forgot to use the [NotMapped] annotation.
[NotMapped]
public string Pair { get; set; }
There was no error message relating to this being the cause at all. Just a null reference to my repository obj when I tried to query data that should have been there.