Efcore: IMigrationsAssembly vs MigrationProviderStore for storing migrations !

Created on 12 Jul 2018  Β·  5Comments  Β·  Source: dotnet/efcore

Hello fellows !

I have researched the Imigrator. and Migration commands !
https://github.com/aspnet/EntityFrameworkCore/blob/04607ac00983705b3b1d4d4ca9834364edd36a01/src/EFCore.Relational/Migrations/IMigrator.cs

And my main target is to store Migration not in Cs files!
I want to store migrations files in database or in some xml files!
And apply migrations with out relaunching the main app!

As I said before is need for enterprise scenarios, when application must work in 24/7 servicemodel!
It'is very sad that dotnet/coreclr still could not provide unloading of assembly!
https://github.com/dotnet/coreclr/pull/18476

I have a few conceptual and technicals questions !

1.Its possible to Realize IMigrationsAssembly that will store migrations in database or xml files, not in cs files ?
https://github.com/aspnet/EntityFrameworkCore/blob/04607ac00983705b3b1d4d4ca9834364edd36a01/src/EFCore.Relational/Migrations/IMigrationsAssembly.cs
The migration architecture is hard binded to work with assembly!
I think is not right that with architecure principes, that the migrations are stored in Cs files.
I understand, that it is very nice that U inspired with Django and Ruby_on_Rails frameworks, with there simplicity to work with migrations!

Maybe U shall overview the architecture of migrations ?
Maybe must be some kind of MigrationProvider, that will store all migrations ?
By option it could be cs files,xml files,json files,or database storage ?

Fake migrations
https://github.com/aspnet/EntityFrameworkCore/blob/04607ac00983705b3b1d4d4ca9834364edd36a01/test/EFCore.Relational.Tests/RelationalDatabaseFacadeExtensionsTest.cs

 protected virtual void PopulateMigrations(
            IEnumerable<string> appliedMigrationEntries,
            string targetMigration,
            out IReadOnlyList<Migration> migrationsToApply,
            out IReadOnlyList<Migration> migrationsToRevert)
        {
            // ΠŸΡ€ΠΈΠΌΠ΅Π½Π΅Π½Π½Ρ‹Π΅ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ
            var appliedMigrations = new Dictionary<string, TypeInfo>();
            // НС ΠŸΡ€ΠΈΠΌΠ΅Π½Π΅Π½Π½Ρ‹Π΅ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ
            var unappliedMigrations = new Dictionary<string, TypeInfo>();

            var appliedMigrationEntrySet = new HashSet<string>(appliedMigrationEntries, StringComparer.OrdinalIgnoreCase);

            //Если Π½Π΅Ρ‚Ρƒ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΉ Π² _migrationsAssembly Ρ‚ΠΎ пишСм Ρ‡Ρ‚ΠΎ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Ρ‹ 
            if (_migrationsAssembly.Migrations.Count == 0)
            {
                _logger.MigrationsNotFound(this, _migrationsAssembly);
            }

            /// ΠŸΠ΅Ρ€Π΅Π±ΠΈΡ€Π°Π΅ΠΌ всС ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ Π² _migrationsAssembly
            foreach (var migration in _migrationsAssembly.Migrations)
            {
                // Если appliedMigrationEntrySet содСрТит ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΡŽ _migrationsAssembly
                // Ρ‚ΠΎ добавляСм ΠΊ ΠΏΡ€ΠΈΠΌΠ΅Π½Π΅Π½Ρ‹ΠΌ 
                if (appliedMigrationEntrySet.Contains(migration.Key))
                {
                    appliedMigrations.Add(migration.Key, migration.Value);
                }
                else
                { //Если Π½Π΅Ρ‚ Ρ‚ΠΎ добавляСм Π½Π΅ ΠΊ ΠΏΡ€ΠΈΠΌΠ΅Π½Π΅Π½Π½Ρ‹ΠΌ
                    unappliedMigrations.Add(migration.Key, migration.Value);
                }
            }

            // Если targetMigration Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Π° Ρ‚ΠΎ Π² migrationsToApply Π»ΠΎΠΆΠΈΠΌ Π½Π΅ ΠΏΡ€ΠΈΠΌΠ΅Π½Π΅Π½Π½Ρ‹Π΅ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ
            if (string.IsNullOrEmpty(targetMigration))
            {
                migrationsToApply = unappliedMigrations
                    .Select(p => _migrationsAssembly.CreateMigration(p.Value, _activeProvider))
                    .ToList();
                migrationsToRevert = Array.Empty<Migration>();
            }
            else if (targetMigration == Migration.InitialDatabase)
            {
                migrationsToApply = Array.Empty<Migration>();
                migrationsToRevert = appliedMigrations
                    .OrderByDescending(m => m.Key)
                    .Select(p => _migrationsAssembly.CreateMigration(p.Value, _activeProvider))
                    .ToList();
            }
            else
            { // Π’ Else случаС Π½Π°Ρ…ΠΎΠ΄ΠΈΠΌ targetMigration Π² Π½Π΅ΠΏΡ€ΠΈΠΌΠ΅Π½Π΅Π½Π½Ρ‹Ρ… миграциях ΠΈ Π»ΠΎΠΆΠΈΠΌ ΠΈΡ… Π² migrationsToApply
                targetMigration = _migrationsAssembly.GetMigrationId(targetMigration);
                migrationsToApply = unappliedMigrations
                    .Where(m => string.Compare(m.Key, targetMigration, StringComparison.OrdinalIgnoreCase) <= 0)
                    .Select(p => _migrationsAssembly.CreateMigration(p.Value, _activeProvider))
                    .ToList();
                migrationsToRevert = appliedMigrations
                    .Where(m => string.Compare(m.Key, targetMigration, StringComparison.OrdinalIgnoreCase) > 0)
                    .OrderByDescending(m => m.Key)
                    .Select(p => _migrationsAssembly.CreateMigration(p.Value, _activeProvider))
                    .ToList();
            }
        }       

2.The second question is similar to reloading assembly !
If it is not possible to reload asseblyes that stores model class, as i mentioned before!
Then it is possible to make models with dynamic injection in runtime, injection in dbcontext class ?

When litle time application go to service mode, and all models that changed (for example added one column)
all models are reloading and injecting in dbcontext ?

3.And last one!
I understand that many things in ef core realized with interface principals!
And if shall realize some custom Imigrator or IMigrationsAssembly how to make it work when many source files have no documentation and,
writed coments that api may changed ?

 /// <summary>
    ///     This API supports the Entity Framework Core infrastructure and is not intended to be used
    ///     directly from your code. This API may change or be removed in future releases.
    /// </summary>

I wanna say that Ure have some plans to release more modern and fashionable things!
But where is border of source code, when api changeable or not gonna change in future?
Maybe must be some council(soviet) that shall approve architecture and future of EF CORE ?
I Could realize some library that will dependent on EF CORE but, there have some guarantee that feature that I realized will work in future releases of ef core ?

closed-question customer-reported

All 5 comments

I wonder how coupled it really is... Have you tried just passing dummy reflection instances around?

class MyMigrationsAssembly : IMigrationsAssembly
{
    class DummyTypeInfo : TypeInfo
    {
        public DummyTypeInfo(string migrationId)
        {
            MigrationId = migrationId
        }

        public string MigrationId { get; }
    }

    public IReadOnlyDictionary<string, TypeInfo> Migrations { get; }
        = new Dictionary<string, TypeInfo>()
        {
            // TODO: Get this list from XML, etc.
            ["00000000000000_MyMigration"] = new DummyTypeInfo("00000000000000_MyMigration");
        };

    public Migration CreateMigration(TypeInfo migrationClass, string activeProvider)
    {
        var migrationId = ((DummyTypeInfo)migrationClass).MigrationId;

        return new MyMigration(migrationId);
    }

    class MyMigration : Migration
    {
        string _id;

        public MyMigration(string id)
        {
            _id = id;
        }

        protected override void Up(MigrationBuilder migrationBuilder)
        {
            // TODO: Deserialize migration operations from XML, etc.
        }
    }
}

Actually i want to use already ready types and classes , u just want is to do another storage for migration, all other componets of ef core still be in place !

I wanna say, is all i need is to deserilize MigrationComands from xml and aplly them !

@bricelam for now i dint try it, I am still diving in sources ! Tnks for example! The main actually problem that I need it not only for MS sql, i need it for other providers!

  1. @bricelam U wanna say that the typeinfo is some custom type of migration, that u left for extensibility of migrations ? It very hard to understand architecture, because U must understnad abstract and fact classes that responsibiliti of fact of migrationcommand!

Is need not for 1 database.
This kind migration is depended on Ms sql specific
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
I'am trying to say when command add-migration executes.
Its code that generates is dependable on 1 database.

Maybe have some abstract class, not depend on specific RDBMS ?
With out .Annotation("SqlServer:ValueGenerationStrategy", or it is impossible ???


        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Authors",
                columns: table => new
                {
                    AuthorId = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                    FirstName = table.Column<string>(nullable: true),
                    LastName = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Authors", x => x.AuthorId);
                });

            migrationBuilder.CreateTable(
                name: "Books",
                columns: table => new
                {
                    BookId = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                    Title = table.Column<string>(nullable: true),
                    AuthorId = table.Column<int>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Books", x => x.BookId);
                    table.ForeignKey(
                        name: "FK_Books_Authors_AuthorId",
                        column: x => x.AuthorId,
                        principalTable: "Authors",
                        principalColumn: "AuthorId",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateIndex(
                name: "IX_Books_AuthorId",
                table: "Books",
                column: "AuthorId");
        }

thanks for feedback !

Was this page helpful?
0 / 5 - 0 ratings