NHibernate数据库版本控制:对象级架构和数据升级

NHibernate数据库版本控制:对象级架构和数据升级,nhibernate,version-control,Nhibernate,Version Control,我想从一个不同的方向来探讨NHibernate中的数据库版本控制和自动升级,而不是提出的大多数策略 由于每个对象都是由XML映射定义的,所以我希望为每个映射文件/配置获取大小和校验和,并将其与潜在的自定义更新脚本一起存储在文档数据库(raven或其他东西)中。如果未找到脚本,请使用NHibernate DDL生成器更新对象架构。通过这种方式,我可以检测到更改,如果我需要在DDL之外进行DML更改,或者执行仔细排序的转换,理论上我可以以可控的、可测试的方式进行。这还应该保持某种程度的持久层不可知性

我想从一个不同的方向来探讨NHibernate中的数据库版本控制和自动升级,而不是提出的大多数策略

由于每个对象都是由XML映射定义的,所以我希望为每个映射文件/配置获取大小和校验和,并将其与潜在的自定义更新脚本一起存储在文档数据库(raven或其他东西)中。如果未找到脚本,请使用NHibernate DDL生成器更新对象架构。通过这种方式,我可以检测到更改,如果我需要在DDL之外进行DML更改,或者执行仔细排序的转换,理论上我可以以可控的、可测试的方式进行。这还应该保持某种程度的持久层不可知性,尽管我认为脚本仍然必须是特定于数据库系统的

诀窍是,从数据库生成“旧”映射文件,并将其与当前映射文件进行比较。我不知道这是否可能。我也不知道我是否遗漏了任何其他东西,这将使这一战略变得不切实际


那么,我的问题是:这个策略有多实际,为什么?我做了什么来解决这个问题

  • 在名为SchemaVersion的表中对数据库进行版本设置
  • 查询该表以查看架构是否是最新的(DAL中存储的必需版本),如果是,转到6
  • 从resources/webservices/获取version==versionFromBb的updatescript
  • 运行脚本,该脚本也会将schemaversation更改为新版本
  • 转到2
  • 运行应用程序
  • 为了生成脚本,我使用了两个选项

  • 支持一个rdbms:运行SchemaUpdate导出到文件中,手动添加DML语句
  • 支持多个rdbms:使用Nhibernate类表在运行时生成ddl,以添加/更改/删除使用会话DML的表和代码
  • 更新:

    “您使用什么方法存储当前版本?”

    小例子

    像这样的

    public static class Constants
    {
        public static readonly Version DatabaseSchemaVersion = new Version(1, 2, 3, 4);
    }
    
    public class DBMigration
    {
        private IDictionary<Version, Action> _updates = new Dictionary<Version, Action>();
        private Configuration _config;
        private Dialect _dialect;
        private IList<Action<ISession>> _actions = new List<Action<ISession>>(16);
        private string _defaultCatalog;
        private string _defaultSchema;
    
        private void CreateTable(string name, Action<Table> configuretable)
        {
            var table = new Table(name);
            configuretable(table);
    
            string createTable = table.SqlCreateString(_dialect, _config.BuildMapping(), _defaultCatalog, _defaultSchema);
            _actions.Add(session => session.CreateSQLQuery(createTable).ExecuteUpdate());
        }
    
        private void UpdateVersionTo(Version version)
        {
            _actions.Add(session => { session.Get<SchemaVersion>(1).Value = version; session.Flush(); });
        }
    
        private void WithSession(Action<session> action)
        {
            _actions.Add(action);
        }
    
        public void Execute(Configuration config)
        {
            _actions.Clear();
            _defaultCatalog = config.Properties[NH.Environment.DefaultCatalog];
            _defaultSchema = config.Properties[NH.Environment.DefaultSchema];
            _config = config;
            _dialect = Dialect.GetDialect(config.Properties);
    
            using (var sf = _config.BuildSessionFactory())
            using (var session = sf.OpenSession())
            using (var tx = session.BeginTransaction())
            {
                Version dbVersion = session.Get<SchemaVersion>(1).Value;
                while (dbVersion < Constants.DatabaseSchemaVersion)
                {
                    _actions.Clear();
                    _updates[dbVersion].Invoke(); // init migration, TODO: error handling
                    foreach (var action in _actions)
                    {
                        action.Invoke(session);
                    }
                    tx.Commit();
                    session.Clear();
                    dbVersion = session.Get<SchemaVersion>(1).Value;
                }
            }
        }
    
        public DBMigration()
        {
            _updates.Add(new Version(1, 0, 0, 0), UpdateFromVersion1);
            _updates.Add(new Version(1, 0, 1, 0), UpdateFromVersion1);
            ...
        }
    
        private void UpdateFromVersion1()
        {
            AddTable("Users", table => table.AddColumn(...));
            WithSession(session => session.CreateSqlQuery("INSERT INTO ..."));
            UpdateVersionTo(new Version(1,0,1,0));
        }
    
        ...
    }
    
    公共静态类常量
    {
    公共静态只读版本DatabaseSchemaVersion=新版本(1,2,3,4);
    }
    公共类数据库迁移
    {
    私有IDictionary_updates=新字典();
    私有配置_config;
    私人方言(u方言),;
    私人IList_行动=新名单(16);
    私有字符串_defaultCatalog;
    私有字符串_defaultSchema;
    私有void CreateTable(字符串名称、操作配置表)
    {
    var表=新表(名称);
    配置表(table);
    string createTable=table.SqlCreateString(_方言,_config.BuildMapping(),_defaultCatalog,_defaultSchema);
    _添加(session=>session.CreateSQLQuery(createTable.ExecuteUpdate());
    }
    私有void UpdateVersionTo(版本)
    {
    _actions.Add(session=>{session.Get(1).Value=version;session.Flush();});
    }
    私隐无效(诉讼)
    {
    _动作。添加(动作);
    }
    公共void执行(配置)
    {
    _动作。清除();
    _defaultCatalog=config.Properties[NH.Environment.defaultCatalog];
    _defaultSchema=config.Properties[NH.Environment.defaultSchema];
    _config=config;
    _dialent=dialent.getdialent(config.Properties);
    使用(var sf=_config.BuildSessionFactory())
    使用(var session=sf.OpenSession())
    使用(var tx=session.BeginTransaction())
    {
    Version dbVersion=session.Get(1).Value;
    while(dbVersiontable.AddColumn(…);
    WithSession(session=>session.CreateSqlQuery(“插入…”);
    UpdateVersionTo(新版本(1,0,1,0));
    }
    ...
    }
    
    我不确定我是否理解您从中获得的好处。如果您所追求的只是数据库结构维护,那么为什么不让nHiberntae为您更新模式,您可以编写一个小型的.Net应用程序作为DML脚本—这在很多情况下是行不通的。有时,数据更改需要在升级过程中进行复杂的操作;DDL必须发生在特定点。此外,我还没有在NHibernate的任何更新中取得过巨大的成功,这些更新可能会改变数据。。。这可能是一件好事。另外,我真的希望将其实现为一种自动化,它在应用程序启动时检查模式,并根据需要进行更新。我不希望运行另一个进程来更新数据库。能否在第二次会话中扩展#2?什么意思?你认为你能举个小例子吗?另外,您使用什么方法存储当前版本?