EntityFramework, Migrations and MySql. Schema name being misinterpreted. How to solve?

3

I'm having a problem using migrations with MySql .

When I try to create the first version with Migrations it is making the schema / bank name part of the table name:

public partial class Initial : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "vendas.produto",  // Aqui
            c => new
                {
                    ...
                })
            .PrimaryKey(t => t.Id);

        CreateTable(
            "vendas.vendaitem", // aqui
            c => new
                {
                    ...
                })
            ....
            .Index(t => t.ProdutoId);

        CreateTable(
            "vendas.venda", // e aqui
            c => new
                {
                    ...
                })
            .PrimaryKey(t => t.Id);            
    }

I'm trying to reverse engineer an existing base for a Code First mapping with EntityFramework , so I've created another, simpler base , just to simulate the problem:

CREATE DATABASE 'vendas' /*!40100 DEFAULT CHARACTER SET utf8 */;

CREATE TABLE 'venda' (
  'Id' int(11) NOT NULL,
  'DataVenda' date NOT NULL,
  'SubTotal' double NOT NULL DEFAULT '0',
  'Desconto' double NOT NULL DEFAULT '0',
  'Total' double NOT NULL DEFAULT '0',
  PRIMARY KEY ('Id')
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

... entre outras
Using the EntityFramework Power Tools I did the mapping that generated the simple classes and mappings by Fluent API .

Example of class Venda :

public class venda
{
    public venda()
    {
        vendaitems = new List<vendaitem>();
    }

    public int Id { get; set; }
    public DateTime DataVenda { get; set; }
    public double SubTotal { get; set; }
    public double Desconto { get; set; }
    public double Total { get; set; }
    public virtual ICollection<vendaitem> vendaitems { get; set; }
}

And your mapping:

public class vendaMap : EntityTypeConfiguration<venda>
{
    public vendaMap()
    {
        // Primary Key
        HasKey(t => t.Id);

        // Properties
        Property(t => t.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

        // Table & Column Mappings
        ToTable("venda", "vendas"); // <-- Aqui está o schema/database name

        Property(t => t.Id).HasColumnName("Id");
        Property(t => t.DataVenda).HasColumnName("DataVenda");
        Property(t => t.SubTotal).HasColumnName("SubTotal");
        Property(t => t.Desconto).HasColumnName("Desconto");
        Property(t => t.Total).HasColumnName("Total");
    }
}

By the way I understand it, for the first version with Migrations , the mapping equivalent to the existing database database update (with update-database in the Package Manager Console ) should generate the Up and Down empty methods and only the Migrations table would be added to the database.

However, Migrations of all tables are created and the Update-Database command using the Migrations generated causes other tables to be created with the names vendas.venda, vendas.produto, venda.vendaitem .

To use the EntityFramework Power Tools to do the mapping I configured my App.config with the following code:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" requirePermission="false" 
    type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, 
    EntityFramework, Version=6.0.0.0, Culture=neutral,PublicKeyToken=b77a5c561934e089" />
  </configSections>
  <connectionStrings>
    <add name="VendasContext" providerName="MySql.Data.MySqlClient"
    connectionString="server=localhost;user id=USUARIO; password=SENHA; 
    persistsecurityinfo=True; database=Vendas" />
  </connectionStrings>
  <entityFramework>
    <defaultConnectionFactory type="MySql.Data.Entity.MySqlConnectionFactory, 
    MySql.Data.Entity.EF6" />
    <providers>
      <provider invariantName="MySql.Data.MySqlClient" 
       type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6" />
    </providers>
  </entityFramework>
  <system.data>
    <DbProviderFactories>
      <remove invariant="MySql.Data.MySqlClient" />
      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" 
        description=".Net Framework Data Provider for MySQL"
        type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data />
    </DbProviderFactories>
  </system.data>
</configuration>

I installed via NuGet:

  • EntityFramework
  • MySql.Data
  • MySql.Data.Entity.EF6
  • The EntityFramework version is 6.1.3 with .Net 4.5 . The version of MySql.Data and MySql.Data.Entity.EF6 is 6.9.6 .

    My class Configuration of Migrations :

    internal sealed class Configuration : DbMigrationsConfiguration<Models.VendasContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = false;
            SetSqlGenerator("MySql.Data.MySqlClient", 
                new MySql.Data.Entity.MySqlMigrationSqlGenerator()); // necessário para usar 
                    // Migrations com MySql
        }
    }
    

    Configuration in the Context class:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new produtoMap());
        modelBuilder.Configurations.Add(new vendaMap());
        modelBuilder.Configurations.Add(new vendaitemMap());
    
        modelBuilder.Properties<string>().Configure(p => p.HasColumnType("varchar"));
    }
    

    Having this information, how can I solve the problem with Migrations ?

    Adding more details:

    This is the database I'm mapping with EntityFramework Power Tools :

    SoafterusingreverseengineeringwiththeEntityFrameworkPowerToolsIaddamigrations(Add-MigrationInitial)thatgeneratesthefirstcodesnippetofDbMigrationofthatquestion.

    SeethenamehegavetheSalestable("vendas.venda" ).

    So I do Update-Database and my bank looks like this:

    Finally,theMigrationsgeneratedcausesothertablestobecreatedwiththenamesvendas.venda,vendas.produto,venda.vendaitem.

    Iimaginethattheprefix/schema/banknameshouldnotbeaddedtothetablenamewhencreatingtheversionofMigrations(firstcodesnippetofthequestion).AnditwouldonlycreatetheUpandDownemptymethods,andbydoingthistheUpdate-Databasecommandonlythe_Migrationstablewouldbecreatedinthedatabasewiththefirstversionrecord.

    WhenUpdate-Databasewascreatingthe_Migrationstable,youalsorananerror:

      

    Specifiedkeywastoolong;maxkeylengthis767bytes

    Thiswillleaveyouforanotherquestion.

    Extractingtheschemafromthemappingconfigurationclasses(classeswithFluent)

    Ialsotriedtogettheschemanamefromtheconfigurationclasses:

    From:

    //Table&ColumnMappingsToTable("venda", "vendas"); // <-- Aqui está o schema/database name
    

    To:

    ToTable("venda"); // <-- Aqui está o schema/database name
    

    But then migrations are generated with the schema dbo. .

    Example:

        CreateTable(
            "dbo.produto",  // <-- schema dbo.
            c => new
                {
                    ...
                })
            .PrimaryKey(t => t.Id);
    

    And so when trying to apply the migration to the database it gives an error. This time he accused that the tables already exist.

      

    Table 'product' already exists

    It implies that it ignores dbo. but does not treat as an existing table.

        
    asked by anonymous 03.07.2015 / 00:38

    2 answers

    4

    You can customize the code generator for Migrations :

    public class MeuProprioMySqlMigrationSqlGenerator : MySqlMigrationSqlGenerator
    {
        protected override MigrationStatement Generate(AddForeignKeyOperation addForeignKeyOperation)
        {
            addForeignKeyOperation.PrincipalTable = addForeignKeyOperation.PrincipalTable.Replace("vendas.", "");
            addForeignKeyOperation.DependentTable = addForeignKeyOperation.DependentTable.Replace("vendas.", "");
            MigrationStatement ms = base.Generate(addForeignKeyOperation);
            return ms;
        }
    }
    

    Register it on your Migrations/Configuration.cs :

    internal sealed class Configuration : DbMigrationsConfiguration<MeuProjeto.Models.MeuProjetoContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
            SetSqlGenerator("MySql.Data.MySqlClient", new MeuProprioMySqlMigrationSqlGenerator());
        }
    
        ...
    }
    

    EDIT

    I reimplemented the wrong class. Also add the following class:

    public class CustomMySqlMigrationCodeGenerator : MySqlMigrationCodeGenerator
    {
        protected override void Generate(CreateTableOperation createTableOperation, IndentedTextWriter writer)
        {
            // Descomente para debugar
            // if (System.Diagnostics.Debugger.IsAttached == false)
            //    System.Diagnostics.Debugger.Launch();
    
            var create = new CreateTableOperation(createTableOperation.Name.Replace("Vendas.", ""));
    
            foreach (var item in createTableOperation.Columns)
                create.Columns.Add(item);
    
            create.PrimaryKey = createTableOperation.PrimaryKey;
    
            base.Generate(create, writer);
        }
    }
    

    If you want to exchange createTableOperation.Name.Replace("Vendas.", "") for a generic solution, feel free to.

    The configuration looks like this:

        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = false;
            SetSqlGenerator("MySql.Data.MySqlClient", new CustomMySqlMigrationSqlGenerator());
            CodeGenerator = new CustomMySqlMigrationCodeGenerator();
            SetHistoryContextFactory("MySql.Data.MySqlClient", (conn, schema) => new MySqlHistoryContext(conn, schema));
        }
    

    I made a pull request with the modifications in your GitHub repository .

        
    06.07.2015 / 20:39
    1

    Summarizing and synthesizing my problem, the confusions in itself that I committed and what was done.

    EntityFramework Power Tools , from what I've noticed, do not make database distinction when generating mapping classes.

    When generating the mapping classes from my existing database he was adding the database name as schema (although the term schema in MySQL also, but I understand schema in MySQL as another thing, other than SQL SQL ).

    Mapping example:

    public class ProdutoMap : EntityTypeConfiguration<Produto>
    {
        public ProdutoMap()
        {
            ToTable("Produto", "Vendas");  // isso que ele faz
            ....
        }
    }
    

    I believe this was the main confusion because MySQL does not work with schemas like SQL Server and , when generating the Migrations classes, the bank name was appended with a period to the table name as schema strong> is done on SQL Server .

    Then I added the T4 .tt ) files to the project, to edit, and no longer add the database name as schema . This is necessary for any other MySQL basis that will be imported, especially with many tables.

    And then the mapping classes now have the ToTable method written in this way by EF Power Tools reverse engineering:

    public class ProdutoMap : EntityTypeConfiguration<Produto>
    {
        public ProdutoMap()
        {
            ToTable("Produto");  // sem um nome para o esquema.
            ....
        }
    }
    

    However ...

    Until the illustrious and patient help of the colleague Gypsy I had not done so, I was leaving the name of the bank as the name of schema there in the mapping class and wanting to generate a Migration correctly.

    The colleague Cigano breaking head to understand my confusion attacked with the MySQL generation classes and the generation of code (from the Migrations ).

    Okay, that's it! =]

    Thank you, and I have already marked the accepted response and the most deserved reward was given.

    I was still thinking, as I was overwriting more methods of CustomMySqlMigrationSqlGenerator and CustomMySqlMigrationCodeGenerator inherited from MySqlMigrationSqlGenerator and MySqlMigrationCodeGenerator (the example can be seen in this history of GitHub in the file

    08.07.2015 / 01:52