在同一数据库上使用多个 FluentMigrator 程序集

问题描述 投票:0回答:2

假设我在同一个数据库中有两个独立的表,表 Book 和 Cup。我仅使用主键 (int) 创建这两个键,称为 Id。然后,为了保持整洁,我将它们分成不同的项目,并为这两个项目创建 FluentMigrations,它将驻留在

Book.Migrations.dll
Cup.Migrations.dll
中。

现在我意识到也许我的书应该能够有一个名称,并创建一个新的迁移来添加一个名为 name 的列。我将其版本设置为 201408111220(因此是撰写本文时的时间戳),并将其命名为 AddNameToBook。我应用此迁移并相应更新数据库。

然后我意识到也许杯子应该有颜色。因此,我在另一个项目中创建了一个新的迁移,版本为 201408111221,并将其命名为 AddColorToCup。我再次运行迁移,并且数据库已更新。

据我所知,到目前为止一切都应该工作得很好。我不确定的是,如果我现在向 Book 添加另一个迁移(例如 201408111224),应用它,然后尝试回滚。由于现在来自其他程序集的版本

201408111221
存在于 VersionInfo 表中,FluentMigrator 将如何处理这个问题?它会在我面前抛出错误,还是因为当前程序集对此一无所知而忽略该行?

也欢迎有关以这种方式使用 FluentMigrator(一个数据库使用多个程序集)的其他评论。

c# database-versioning fluent-migrator
2个回答
8
投票

我不推荐这种方法。回滚或向上迁移时,VersionInfo 表中保存的迁移版本将与 FluentMigrator 程序集中的迁移版本不匹配。如果运气不好的话,这会崩溃。

如果您想要有两个独立的项目,那么您需要两个 VersionInfo 表。其中一个项目可以使用默认的 VersionInfo 表,但另一个项目必须创建一个自定义版本信息表。请参阅此处了解如何为 VersionInfo 表创建自定义元数据

对于 FluentMigrator,这与拥有两个数据库相同。这种方法在与模式结合时很常见。在您的示例中,您将在数据库中创建一个 Book 架构和一个 Cup 架构,并将它们彼此分开。

根据我的经验,大多数数据库都不够大,不足以证明这种额外的开销是合理的,但如果您有数百或数千个迁移,那么您绝对应该将其划分为不同的项目(并将数据库划分为不同的模式)。


4
投票

我编写了一个迁移加载器来帮助我做到这一点

public class MultiAssemblyMigrationLoader : IMigrationInformationLoader
{
    public MultiAssemblyMigrationLoader(IMigrationConventions conventions, IEnumerable<Assembly> assemblies, string @namespace, IEnumerable<string> tagsToMatch)
        : this(conventions, assemblies, @namespace, false, tagsToMatch)
    {
    }

    public MultiAssemblyMigrationLoader(IMigrationConventions conventions, IEnumerable<Assembly> assemblies, string @namespace, bool loadNestedNamespaces, IEnumerable<string> tagsToMatch)
    {
        this.Conventions = conventions;
        this.Assemblies = assemblies;
        this.Namespace = @namespace;
        this.LoadNestedNamespaces = loadNestedNamespaces;
        this.TagsToMatch = tagsToMatch ?? new string[0];
    }

    public IMigrationConventions Conventions { get; private set; }

    public IEnumerable<Assembly> Assemblies { get; private set; }

    public string Namespace { get; private set; }

    public bool LoadNestedNamespaces { get; private set; }

    public IEnumerable<string> TagsToMatch { get; private set; }

    public SortedList<long, IMigrationInfo> LoadMigrations()
    {
        var sortedList = new SortedList<long, IMigrationInfo>();

        IEnumerable<IMigration> migrations = this.FindMigrations();
        if (migrations == null) return sortedList;

        foreach (IMigration migration in migrations)
        {
            IMigrationInfo migrationInfo = this.Conventions.GetMigrationInfo(migration);
            if (sortedList.ContainsKey(migrationInfo.Version))
                throw new DuplicateMigrationException(string.Format("Duplicate migration version {0}.", migrationInfo.Version));
            sortedList.Add(migrationInfo.Version, migrationInfo);
        }
        return sortedList;
    }

    private IEnumerable<IMigration> FindMigrations()
    {
        IEnumerable<Type> types = new Type[] { };
        foreach (var assembly in Assemblies)
        {
            types = types.Concat(assembly.GetExportedTypes());
        }

        IEnumerable<Type> source = types.Where(t =>
                                                    {
                                                        if (!Conventions.TypeIsMigration(t))
                                                            return false;
                                                        if (!Conventions.TypeHasMatchingTags(t, this.TagsToMatch))
                                                            return !Conventions.TypeHasTags(t);
                                                        return true;
                                                    });
        if (!string.IsNullOrEmpty(Namespace))
        {
            Func<Type, bool> predicate = t => t.Namespace == Namespace;
            if (LoadNestedNamespaces)
            {
                string matchNested = Namespace + ".";
                predicate = t =>
                                {
                                    if (t.Namespace != Namespace)
                                        return t.Namespace.StartsWith(matchNested);
                                    return true;
                                };
            }
            source = source.Where(predicate);
        }
        return source.Select(matchedType => (IMigration)matchedType.Assembly.CreateInstance(matchedType.FullName));
    }

}

要使用此类,您只需将其连接到 MigrationRunner

var runner = new MigrationRunner(mainAssembly, runnerContext, processor);
runner.MigrationLoader = new MultiAssemblyMigrationLoader(runner.Conventions, assemblies, runnerContext.Namespace, runnerContext.NestedNamespaces, runnerContext.Tags);
runner.MigrateUp(true);
© www.soinside.com 2019 - 2024. All rights reserved.