Как увеличить ID перед любой вставкой с помощью NHibernate - PullRequest
4 голосов
/ 25 октября 2011

Похоже, что NH получает MAX (ID) только один раз, сначала вставляет, а затем сохраняет это значение внутри, это вызывает у меня некоторые проблемы, когда другие процессы вставляют данные.Тогда у меня нет фактического идентификатора, и исключение дубликат ключа выдается.

Давайте представим, что у нас есть таблица Cats

CREATE TABLE Cats(ID int, Name varchar(25))

Затем мы выполним соответствующее сопоставление с помощью FluentNhibernate

public class CatMap : ClassMap<Cat>
{
    public CatMap()
    {
      Id(m=>m.ID).GeneratedBy.Increment();
      Map(m=>.Name);
    }
}

Все, чего я хочу добиться, это вставить свой Cat записей с идентификаторами, сгенерированными NHibernate с использованием SELECT MAX(ID) FROM Cats перед любой вставкой.Выполнение Session.Flush после любого коммита.Я провел некоторое исследование с использованием профилировщика SQL Server, и эта инструкция SQL выполняется только один раз (при первой вставке) - другие вставки не заставляют возвращать фактический MAX (ID).Я знаю, что другие алгоритмы, такие как HiLo, лучше, но я не могу заменить его.

1 Ответ

3 голосов
/ 25 октября 2011

Как вы выяснили, генератор идентификатора приращения NHibernate не предназначен для использования в многопользовательской среде. Вы заявляете, что использование генератора HiLo не является опцией, поэтому у вас остаются следующие опции:

  • используйте собственный генератор и измените столбец id, чтобы использовать поддерживаемый базой данных механизм идентификации

  • используйте назначенный генератор и введите код для определения следующего действительного идентификатора

  • создайте пользовательский генератор, в котором вы реализуете интерфейс IIdentifierGenerator , чтобы делать то, что вам нужно

Ниже приведен пример кода для пользовательского генератора, который использует обобщенный процесс для получения идентификатора для данной таблицы. Основная проблема с этим подходом заключается в том, что вы должны обернуть код в что-то вроде шаблона Unit of Work , чтобы гарантировать, что «select max (id) ...» и вставка покрыты одной и той же транзакцией базы данных. Ссылка IIdentifierGenerator содержит отображение XML, необходимое для подключения этого пользовательского генератора.

using System;
using System.Collections.Generic;
using System.Data;
using NHibernate.Dialect;
using NHibernate.Engine;
using NHibernate.Id;
using NHibernate.Persister.Entity;
using NHibernate.Type;

namespace YourCompany.Stuff
{
    public class IdGenerator : IIdentifierGenerator, IConfigurable
    {
        private string _tableName;
        // The "select max(id) ..." query will go into this proc:
        private const string DefaultProcedureName = "dbo.getId";

        public string ProcedureName { get; protected set; }
        public string TableNameParameter { get; protected set; }
        public string OutputParameter { get; protected set; }

        public IdGenerator()
        {
            ProcedureName = DefaultProcedureName;
            TableNameParameter = "@tableName";
            OutputParameter = "@newID";
        }

        public object Generate(ISessionImplementor session, object obj)
        {
            int newId;
            using (var command = session.Connection.CreateCommand())
            {
                var tableName = GetTableName(session, obj.GetType());

                command.CommandType = CommandType.StoredProcedure;
                command.CommandText = ProcedureName;

                // Set input parameters
                var parm = command.CreateParameter();
                parm.Value = tableName;
                parm.ParameterName = TableNameParameter;
                parm.DbType = DbType.String;

                command.Parameters.Add(parm);

                // Set output parameter
                var outputParameter = command.CreateParameter();
                outputParameter.Direction = ParameterDirection.Output;
                outputParameter.ParameterName = OutputParameter;
                outputParameter.DbType = DbType.Int32;

                command.Parameters.Add(outputParameter);

                // Execute the stored procedure
                command.ExecuteNonQuery();

                var id = (IDbDataParameter)command.Parameters[OutputParameter];

                newId = int.Parse(id.Value.ToString());

                if (newId < 1)
                    throw new InvalidOperationException(
                        string.Format("Could not retrieve a new ID with proc {0} for table {1}",
                                      ProcedureName,
                                      tableName));
            }

            return newId;
        }

        public void Configure(IType type, IDictionary<string, string> parms, Dialect dialect)
        {
            _tableName = parms["TableName"];
        }

        private string GetTableName(ISessionImplementor session, Type objectType)
        {
            if (string.IsNullOrEmpty(_tableName))
            {
                //Not set by configuration, default to the mapped table of the actual type from runtime object:
                var persister = (IJoinable)session.Factory.GetClassMetadata(objectType);

                var qualifiedTableName = persister.TableName.Split('.');
                _tableName = qualifiedTableName[qualifiedTableName.GetUpperBound(0)]; //Get last string
            }

            return _tableName;
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...