ServiceStack OrmLite如何检测代码模型的更改并重新创建数据库?

 mobiledu2502857177 发布于 2023-01-31 17:40

是否有一种首选方法来检测代码模型中的更改并自动重新创建数据库?我不需要迁移数据,如果有变化,我可以完全删除所有表,从模型重新创建表,并在代码中填充初始数据集的新表.

与此相关:有没有办法使用ServiceStack的ORMLite版本获取数据库中所有表的列表?

目前我正在使用我自己的班级,但如果已经有了这方面的话,我不想发明一个轮子.

Web.config文件:


    

DatabaseUtil:

public class DatabaseUtil
{
    private const int CURR_VERSION = 1;
    private static string connString = WebConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString;

    public static void Init(Funq.Container container)
    {
        using (var db = connString.OpenDbConnection())
        {
            using (IDbConnection dbConn = connString.OpenDbConnection())
            {
                var createScript = !db.TableExists(typeof(ZatabaseConfig).Name.ToString());
                if (!createScript)
                {
                    var first = dbConn.FirstOrDefault("");
                    createScript = first == null || first.Version < CURR_VERSION;
                }

                if (createScript)
                {
                    DropAndCreateDatabase(dbConn);
                }
            }
            // db.InsertAll(SeedData);
        }
    }

    private static void DropAndCreateDatabase(IDbConnection dbConn)
    {
        var tables = new[] { typeof(Table1), typeof(Table2), typeof(Table3), typeof(ZatabaseConfig) };

        // running drop tables only once doesn't remove tables that are referenced
        using (var dbDrop = createConnection())
            dbDrop.ExecuteSql(DROP_EVERYTHING_CONSTRAINT);

        for (int i = 0; i < 5; i++)
        {
            // dropping table 5 times to eliminate foreign constraints
            try
            {
                using (var dbNew = createConnection())
                    dbNew.ExecuteSql(DROP_EVERYTHING_TABLES);
            }
            catch
            {
            }
        }           

        //Drop and re-create all Auth and registration tables
        //var authRepo = (OrmLiteAuthRepository)container.Resolve();
        //authRepo.DropAndReCreateTables();

        dbConn.CreateTables(true, tables);
        dbConn.Insert(new ZatabaseConfig { ConfigId = (int)ZatabaseConfigIds.Version, Name = CURR_VERSION });
    }

    private static string DROP_EVERYTHING_CONSTRAINT = @"
        SELECT 'ALTER TABLE ' + OBJECT_NAME(f.parent_object_id)+
        ' DROP CONSTRAINT ' + f.name 
        FROM .sys.foreign_keys AS f
        INNER JOIN .sys.foreign_key_columns AS fc
        ON f.OBJECT_ID = fc.constraint_object_id
        ";
    private static string DROP_EVERYTHING_TABLES = @"
        exec sp_MSforeachtable 'DROP TABLE ?'
        ";
}

Scott.. 12

我没有意识到这种内置机制.但你可以自己扮演角色:

创建单独的模型程序集:

如果您发现自己经常更换模型,那么我建议您将模型创建为单独的组件.因此,在您的解决方案中创建一个新的库项目并将模型移动到那里.然后参考主项目中的项目.无论如何,这是良好的组织实践.

然后在您的Properties/AssemblyInfo.cs(模型中)确保AssemblyVersion使用通配符内部版本号设置并删除[assembly: AssemblyFileVersion ...]它是否存在.

[assembly: AssemblyVersion("1.0.*")]

所以我的模型:类看起来像这样:

using System;
using ServiceStack.Model;
using ServiceStack.DataAnnotations;

namespace Blog
{
    public static class Model
    {
        public class Article : IHasId
        {
            [AutoIncrement, PrimaryKey]
            public int Id { get; set; }
            ...

请注意,我使用外部静态类Model.这使我的所有表格都易于在我的项目中引用.

创建一个方法来检测模型程序集的更改:

既然我们有一个程序集,当我们为它构建新版本时,版本号会自动递增,我们需要能够检测应用程序中程序集的更改,以便我们可以重新创建表.

以下代码执行以下操作:

    确定模型程序集的类型.因为它可以使用反射来确定当前程序集的版本号.

    检查上次创建的数据库模型版本的应用程序配置设置.

    如果找不到配置设置或版本号不匹配,则解析数据库连接.

    然后从程序集中推断出具有Model的表.这样做的好处是我们可以向Model程序集添加更多表,而不必更改drop/create代码.

    数据库删除并创建表.

    将保存新的程序集版本号.

    如果在不更改模型的情况下重新启动应用程序,则数据库将保持不变,但是进行更改并重新启动并重新创建数据库.

public static void Init(Funq.Container container)
{
    ...
    CheckDatabaseModel(typeof(Model));
}

public void CheckDatabaseModel(Type modelType)
{
    // Get the referenced model version
    string modelVersion = Assembly.GetAssembly(modelType).GetName().Version.ToString();

    // Determine the last model version number from the configuration
    var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    var lastModelVersion = config.AppSettings.Settings["DatabaseModelVersion"];

    // Determine if the model has changed
    if(lastModelVersion == null || lastModelVersion.Value != modelVersion)
    {
        Console.WriteLine("Model has changed");
        using(var db = Resolve().OpenDbConnection())
        {
            // Drop and recreate the tables

            // Determine the tables from the model assembly
            var tables = modelType.GetNestedTypes();

            db.DropAndCreateTables(tables);

            // Repopulate database with initial data.

            // Save the model version to settings
            if(lastModelVersion == null)
                config.AppSettings.Settings.Add("DatabaseModelVersion", modelVersion);
            else 
                config.AppSettings.Settings["DatabaseModelVersion"].Value = modelVersion;     
            config.Save(ConfigurationSaveMode.Modified);
        }
    } else {
        // The model numbers matched
        Console.WriteLine("Model is current");
    }
}

处理表创建顺序

您的数据库可能具有外键约束,然后您将发现需要按特定顺序创建表,否则数据库将不满意.

当您为db.DropAndCreateTables之前手动创建表数组时,您将指定创建顺序以满足任何约束.但因为我们使用modelTypes.GetNestedTypes()的订单不再受我们的控制.有几种方法可以解决这个问题.

1:禁用密钥检查约束(不推荐)

最基本的,是指示我们的数据库忽略约束,同时我们创建表.在MySQL中,代码将是:

db.ExecuteSql("SET foreign_key_checks = 0;");
db.DropAndCreateTables(tables);
db.ExecuteSql("SET foreign_key_checks = 1;");

MSSQL或其他数据库中所需的代码会有所不同,有些代码可能无法实现.但这最终是一种危险的做事方式.毕竟,限制是有原因的.

2:在我们的模型中定义表创建顺序(推荐):

我们可以创建一个简单的属性来装饰我们的表,告诉我们的数据库设置代码执行的顺序是什么.这样做的好处是我们不必转向约束,并且维护人员明白订单的事情会发生在.

属性:

public class TableCreationOrderAttribute : Attribute
{
    public int Order { get; private set; }

    public TableCreationOrderAttribute(int order)
    {
        Order = order;
    }
}

装饰模型:

public static class Model
{
    [TableCreationOrder(3)]
    public class Article : IHasId
    {
        [AutoIncrement, PrimaryKey]
        public int Id { get; set; }
        ...

所以现在我们需要告诉数据库设置代码如何使用此顺序正确创建表.用这个替换这一行db.DropAndCreateTables(tables);:

var orderedTables = new Dictionary();
var unorderedTables = new List(); // Tables without the attribute will be created last, but in no specific order
foreach(var table in tables)
{
    var order = Attribute.GetCustomAttribute(table, typeof(TableCreationOrderAttribute)) as TableCreationOrderAttribute;
    if(order != null)
        orderedTables.Add(order.Order, table);
    else
        unorderedTables.Add(table);
}

foreach(var table in orderedTables.OrderBy(t=>t.Key))
    db.DropAndCreateTable(table.Value);

foreach(var table in unorderedTables)
    db.DropAndCreateTable(table);

摘要

它可能看起来很多,但事实并非如此,该CheckDatabaseModel方法可以缩小到少于35行代码.它是通用的,因此您可以将其添加到实用程序库,并在其他项目中使用单个调用再次重用它.您永远不必担心手动导致触发数据库刷新.

该方法的完整源代码可以在这里找到.

包括简化的分步指南,因为这个答案包括许多额外的解释.


有没有办法使用ServiceStack的ORMLite版本获取数据库中所有表的列表?

直接内置于ORMLite:不.

您可以使用db.ExecSql查询数据库来获取此信息.每个数据库都有不同的命令来执行此操作.以这种方式使用原始SQL肯定是可能的.

希望这可以帮助.

撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有