如何在使用NHibernate进行任何插入之前增加ID

如何在使用NHibernate进行任何插入之前增加ID,nhibernate,auto-increment,Nhibernate,Auto Increment,看起来NH只获取MAX(ID)一次,首先插入,然后在内部存储该值,这会在其他进程插入数据时给我带来一些问题。然后我没有实际的ID,并引发重复密钥异常 让我们想象一下我们有一张桌子Cats CREATE TABLE Cats(ID int, Name varchar(25)) 然后我们用FluentNhibernate做了相应的映射 public class CatMap : ClassMap<Cat> { public CatMap() { Id(m=&

看起来NH只获取MAX(ID)一次,首先插入,然后在内部存储该值,这会在其他进程插入数据时给我带来一些问题。然后我没有实际的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);
    }
}
公共类CatMap:ClassMap
{
公共CatMap()
{
Id(m=>m.Id).GeneratedBy.Increment();
Map(m=>.Name);
}
}

我只想在任何插入之前插入我的
Cat
记录,其中的ID由NHibernate使用
SELECT MAX(ID)FROM Cats
生成。在任何提交dosnt工作后执行Session.Flush。我使用SQL Server profiler做了一些调查,这个SQL语句只执行一次(在第一次插入时)-其他插入不强制检索实际的MAX(ID)。我知道像HiLo这样的其他算法更好,但我无法取代它。

正如您所发现的,NHibernate增量id生成器不适用于多用户环境。您声明使用HiLo生成器不是一个选项,因此剩下这些选项:

  • 使用本机生成器并更改id列以使用数据库支持的标识机制

  • 使用分配的生成器和写入代码确定下一个有效id

  • 创建一个自定义生成器,在其中实现接口以执行所需操作

下面是一个自定义生成器的示例代码,该生成器使用一个通用过程来获取给定表的ID。这种方法的主要问题是,您必须将代码包装成某种模式,以确保“select max(id)…”和insert被同一数据库事务覆盖。该链接具有连接此自定义生成器所需的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;
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用系统数据;
使用NHibernate方言;
使用NHibernate.发动机;
使用NHibernate.Id;
使用NHibernate.Persister.Entity;
使用NHibernate.Type;
我是你的公司
{
公共类IdGenerator:IIIdentifierGenerator,可配置
{
私有字符串_tableName;
//“选择最大值(id)…”查询将进入此过程:
private const string DefaultProcedureName=“dbo.getId”;
公共字符串过程重命名{get;protected set;}
公共字符串TableNameParameter{get;protected set;}
公共字符串OutputParameter{get;protected set;}
公共IdGenerator()
{
ProcedureName=默认ProcedureName;
TableNameParameter=“@tableName”;
OutputParameter=“@newID”;
}
公共对象生成(ISessionImplementor会话,对象obj)
{
int-newId;
使用(var命令=session.Connection.CreateCommand())
{
var tableName=GetTableName(会话,obj.GetType());
command.CommandType=CommandType.storedProcess;
command.CommandText=ProcedureName;
//设置输入参数
var parm=command.CreateParameter();
参数值=表名;
parm.ParameterName=表名参数;
parm.DbType=DbType.String;
command.Parameters.Add(parm);
//设置输出参数
var outputParameter=command.CreateParameter();
outputParameter.Direction=ParameterDirection.Output;
outputParameter.ParameterName=outputParameter;
outputParameter.DbType=DbType.Int32;
command.Parameters.Add(outputParameter);
//执行存储过程
command.ExecuteNonQuery();
var id=(IDbDataParameter)命令。参数[OutputParameter];
newId=int.Parse(id.Value.ToString());
if(newId<1)
抛出新的InvalidOperationException(
Format(“无法检索表{1}的proc{0}的新ID”,
程序重命名,
表名);
}
返回newId;
}
公共void配置(IType类型、IDictionary参数、方言)
{
_tableName=parms[“tableName”];
}
私有字符串GetTableName(ISessionImplementor会话,类型objectType)
{
if(string.IsNullOrEmpty(_tableName))
{
//未按配置设置,默认为运行时对象中实际类型的映射表:
var persister=(IJoinable)session.Factory.GetClassMetadata(objectType);
var qualifiedTableName=persister.TableName.Split('.');
_tableName=qualifiedTableName[qualifiedTableName.GetUpperBound(0)];//获取最后一个字符串
}
返回_tableName;
}
}
}

通过实现自定义ID生成器完成。@Dariusz我遇到了一个类似的问题,您能提供一些关于如何实现此功能的其他信息吗?正如我所说,我已经实现了自定义ID生成器@nozzleman