NHibernate - Вставить с включенным identity_insert

У меня проблема, когда я пытаюсь повторно вставить объект в свою базу данных. Это проиллюстрировано следующим модульным тестом:

        // entity mapped to dbo.IdentityInsertTest table
        // dbo.IdentityInsertTest has an IDENTITY Primary Key, Id
        var id = (long)NHibernateSession1.Save(new IdentityInsertTest());
        NHibernateSession1.Flush();

        // delete previously created row
        ExecuteNonQuery("DELETE FROM dbo.IdentityInsertTest");

        try
        {
            // set entity insert off so that I can re-insert
            NHibernateSession2.CreateSQLQuery("SET IDENTITY_INSERT dbo.IdentityInsertTest ON").UniqueResult();

            // re-create deleted row with explicit Id
            NHibernateSession2.Save(new IdentityInsertTest { Id = id });
            NHibernateSession2.Flush();

            Assert.AreEqual(1, ExecuteScalar("SELECT COUNT(1) FROM dbo.IdentityInsertTest"));

            // this assert fails: expected 1, actual 2
            Assert.AreEqual(id, ExecuteScalar("SELECT TOP 1 [Id] FROM dbo.IdentityInsertTest"));
        }
        finally
        {
            NHibernateSession2.CreateSQLQuery("SET IDENTITY_INSERT dbo.IdentityInsertTest OFF").UniqueResult();
        }

Мое отображение довольно простое:

<class name="IdentityInsertTest" table="IdentityInsertTest">
    <id name="Id" type="long">
        <generator class="native" />
    </id>

    <property name="Data" type="int" not-null="false" />
</class>

Насколько я понимаю, проблема в том, что генератор NHibernate все еще каким-то образом вызывает генерацию идентификаторов из SQL, хотя я отключил его. Есть ли способ обойти это?

Изменить: я изначально забыл выполнить «UniqueResult ()» при установке IDENTITY_INSERT, но это не похоже на корень ошибки. По-прежнему получаю те же результаты


person Shane    schedule 16.11.2012    source источник
comment
Вы хотите добавить объект где-нибудь в середине таблицы ИЛИ хотите добавить объект в конце? Я действительно не понимаю твоей логики, зачем тебе это нужно?   -  person Rippo    schedule 16.11.2012
comment
Как сказано в другом комментарии, это реализует логику отмены / повтора в облачном приложении с отсутствием естественного ключа.   -  person Shane    schedule 16.11.2012
comment
Если ВСЕ таблицы являются столбцами идентификаторов и у вас есть целостность базы данных, то почему ключ должен возвращаться в исходное состояние? Также мой комментарий был до вашего разъяснения ... Извините, но я все еще не понимаю; пойми свою логику   -  person Rippo    schedule 16.11.2012
comment
Если я изменю идентификатор, мне придется передать его обратно клиенту, и все может стать очень запутанным. Клиенты, работающие только для чтения, также не могли бы выполнять работу.   -  person Shane    schedule 16.11.2012


Ответы (5)


вы на самом деле не выполняете свой SQLQuery, это должно помочь

 IQuery sqlQry = NHibernateSession2.CreateSQLQuery("SET IDENTITY_INSERT dbo.IdentityInsertTest ON");
 object ret = sqlQry.UniqueResult();
person marc.d    schedule 16.11.2012
comment
Я изменил свой код, чтобы включить это, но это не повлияло на результат. Спасибо, что указали на это, позже это стало бы проблемой. - person Shane; 16.11.2012

Просто интересно, ваша логика здесь относительно удаления / повторного добавления, а не НЕ удаления, а просто обновления ....

Однако, если NHibernate мешает вам, и вы не можете изменить столбец remove Identity, тогда есть некоторые ужасные обходные пути ...

Если вы хотите добавить запись в bottom, вы можете попробовать следующее: -

var sql = "DECLARE @id long = 0;
    SELECT @id = MAX(Id) + 1 FROM IdentityInsertTest;
    DBCC CHECKIDENT(IdentityInsertTest, RESEED, @id);";

    NHibernateSession2.CreateSqlQuery(sql).ExecuteUpdate();

    ... now save the entity normally

ИЛИ Если вы хотите добавить запись где-нибудь в middle таблицы, вам придется вручную построить SQL: -

var sql = "SET IDENTITY_INSERT dbo.IdentityInsertTest ON; 
    INSERT INTO IdentityInsertTest(Id, Data) Values (:id, :data)
    VALUES (:id, :data);
    SET IDENTITY_INSERT dbo.IdentityInsertTest OFF;";

    NHibernateSession2.CreateSqlQuery(sql)
      .SetInt64("id", id)
      .SetInt32("data", data)
      .ExecuteUpdate();
person Rippo    schedule 16.11.2012
comment
Этот ответ указал мне правильное направление. Проблема (или одна из них) заключалась в том, что NHibernate не выполнял все это в параллельном операторе, как у вас выше - person Shane; 16.11.2012

ПРИМЕЧАНИЕ: я отметил это как ответ, поскольку он напрямую отвечает на вопрос, однако в конце концов я выбрал вариант мягкого удаления, как указано выше.

Проблема заключалась в том, что

  1. Я не указывал идентификатор явно в методе сохранения
  2. даже если бы это было так, набор identity_insert был бы выполнен в другом запросе. Этот фиксируется с помощью транзакции

    // entity mapped to dbo.IdentityInsertTest table
    // dbo.IdentityInsertTest has an IDENTITY Primary Key, Id
    var id = (long)NHibernateSession1.Save(new IdentityInsertTest());
    NHibernateSession1.Flush();
    
    // delete previously created row
    ExecuteNonQuery("DELETE FROM dbo.IdentityInsertTest");
    
    try
    {
        NHibernate.ITransaction txn;
        using (txn = SelectSession1.BeginTransaction())
        {
            // set entity insert off so that I can re-insert
            NHibernateSession2.CreateSQLQuery("SET IDENTITY_INSERT dbo.IdentityInsertTest ON").UniqueResult();
    
            // re-create deleted row with explicit Id
            NHibernateSession2.Save(new IdentityInsertTest(), id);
            NHibernateSession2.Flush();
    
            txn.Commit();
        }
    
        Assert.AreEqual(1, ExecuteScalar("SELECT COUNT(1) FROM dbo.IdentityInsertTest"));
    
        // this assert fails: expected 1, actual 2
        Assert.AreEqual(id, ExecuteScalar("SELECT TOP 1 [Id] FROM dbo.IdentityInsertTest"));
    }
    finally
    {
        NHibernateSession2.CreateSQLQuery("SET IDENTITY_INSERT dbo.IdentityInsertTest OFF").UniqueResult();
    }
    
person Shane    schedule 16.11.2012

Выберите другую стратегию генерации ключей, то, что вы пытаетесь сделать, - действительно плохая идея. Столбец идентичности - это искусственный первичный ключ, и он не должен иметь никакого значения.

person Jamie Ide    schedule 16.11.2012
comment
На данном этапе это довольно большой шаг, и я не совсем понимаю ваш аргумент. Столбец идентификаторов - это искусственный первичный ключ. Знаете ли вы какие-либо статьи, которые я мог бы прочитать по этому поводу для разъяснения? - person Shane; 16.11.2012
comment
Суррогат Google против естественного первичного ключа. Естественные первичные ключи, такие как номер социального страхования, редко используются в современных системах. Столбец идентификаторов SQL - это суррогатный ключ; этот тип ключа не имеет значения, кроме однозначной идентификации записи. Хорошо спроектированная система не должна вставлять запись с определенным суррогатным ключом. Какую проблему вы пытаетесь решить? - person Jamie Ide; 16.11.2012
comment
Я понимаю, к чему вы клоните, но у этих данных нет естественного ключа. Вся проблема возникает из-за реализации шаблона отмены сувенира в облачном (на основе браузера) приложении. Я использую личность как естественный ключ, потому что у меня нет других - person Shane; 16.11.2012
comment
Хорошо, тогда я бы предложил вместо этого использовать мягкое удаление или фактически не удалять, пока у пользователя больше не будет возможности отменить операцию. - person Jamie Ide; 17.11.2012

Надо сказать, что этот вопрос меня долгое время блокировал. Даже если я выполняю sql "SET IDENTITY_INSERT dbo.IdentityInsertTest ON" перед запуском кода Nihbernate, он все еще не работает. есть 2 пункта, требующие большего внимания.

Во-первых, вы должны использовать транзакцию в своем коде.

NHibernate.ITransaction txn;
using (txn = SelectSession1.BeginTransaction())
{
    NHibernateSession2.CreateSQLQuery("SET IDENTITY_INSERT dbo.IdentityInsertTest ON").UniqueResult();

    ...

    NHibernateSession2.Flush();
    txn.Commit();
}

Во-вторых, вы должны использовать "Id (x => x.Id) .GeneratedBy. Assigned () .Column (" Id ");" в вашем разделе картографии.

person Dave Wang    schedule 30.07.2015