2009-06-15 6 views
5

Ich arbeite mit einer Legacy-Datenbank, die zusammengesetzte Schlüssel verwendet. Und ich versuche, NHibernate zu verwenden, um einen neuen Datensatz in die Datenbank einzufügen. NHibernate gibt an, dass ich die Id manuell erstellen, aber wenn ich versuche, mit dieser ID einfügen erhalte ich die Meldung:Einfügen eines Datensatzes mit einem zusammengesetzten Schlüssel mit NHibernate

System.Data.SqlClient.SqlException: Cannot insert explicit value for identity column in table 'tablename' when IDENTITY_INSERT is set to OFF. 

ich keine der db-Einstellungen berühren kann, wie sie durch Hauptsitz in den USA verwaltet werden.

fand ich, dass ich über einen db Einsatz kann:

insert into tablename (tablename_country_id, /*extra fields here*/) values (16, /*extra values here*/) 

und die tablename_id Spalte wird automatisch erhöht.

Ist es möglich, eine Art von Handler zu schreiben, der es mir erlaubt, ein ID Objekt mit dem CountryId-Set zu erstellen und es die Id Eigenschaft automatisch zu inkrementieren.

Prost.


Beispielcode:

Tabelle Definition:

CREATE TABLE [dbo].[tablename](
    [tablename_country_id] [int] NOT NULL, 
    [tablename_id] [int] IDENTITY(1,1) NOT NULL, 

    -- more fields here 

    CONSTRAINT [pk_tablename] PRIMARY KEY 
    (
     [tablename_country_id] ASC, 
     [tablename_id] ASC 
    ) 
) 

Klassendateien:

public class ModelObject 
{ 
    public ID { get; set; } 
    // more properties here 
} 

public class ID : INHibernateProxy 
{ 
    public int Id { get; set; } 
    public int CountryId { get; set; } 
    public ILazyInitializer HibernateLazyInitializer { get { throw new ApplicationException(); } } 
} 

Mapping-Datei:

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Model" assembly="API"> 
    <class name="ModelObject" table="dbname.dbo.tablename" lazy="false"> 
     <composite-id name="Id" class="ID"> 
      <key-property name="Id" column="tablename_id" type="int" /> 
      <key-property name="CountryId" column="tablename_country_id" type="int" /> 
     </composite-id> 
     <!-- more properties here --> 
    </class> 
</hibernate-mapping> 

Antwort

0

Ich denke, Sie können eine Identitätsspalte für einen Ihrer Schlüssel im zusammengesetzten Schlüssel festlegen.

ich weiß, dass in FluentNHibernate i für eine omposite Schlüssel eine Identitätsspalte Namen haben

+0

Wie wirkt sich dies auf das Caching von Objekten aus? Insbesondere die 'session.Get (id);' mit der Möglichkeit von Duplikaten in der Spalte 'tablename_id'. – zonkflut

+0

Diese Option löst eine FKUnmatchingColumnsException aus "muss die gleiche Anzahl von Spalten wie der referenzierte Primärschlüssel haben" – zonkflut

0

vielleicht dies funktionieren kann. Wenn Sie die update-Anweisung explizit hinzufügen, können Sie dies auch tun, wenn Sie sie in ein sProc ändern müssen.

https://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html/querysql.html#querysql-cud 

Hier ist ein Beispiel für das Sproc-Konzept.

<class name="Person"> 
    <id name="id"> 
     <generator class="increment"/> 
    </id> 
    <property name="name" not-null="true"/> 
    <sql-insert>exec createPerson ?, ?</sql-insert> 
    <sql-delete>exec deletePerson ?</sql-delete> 
    <sql-update>exec updatePerson ?, ?</sql-update> 
</class> 
0

Ich verstehe, dass Sie nicht die Datenbanktabelle oder Einschränkungen ändern können, aber sind Sie absolut sicher, dass die Datenbank wirklich Objekte bedeuten, dass Sie eine NHibernate zusammengesetzten Schlüssel verwenden?

Ein NHibernate-Verbundschlüssel sollte für solche Tabellen verwendet werden, bei denen die Eindeutigkeit einer Zeile von mehr als einer Spalte bestimmt wird. In dem von Ihnen bereitgestellten Beispiel stellt sich heraus, dass die Eindeutigkeit jeder Zeile tatsächlich nur durch die einzelne Spalte tablename_id bestimmt wird. Der Primärschlüssel erweist sich als überflüssig, das heißt, er kann nicht verletzt werden, weil eines seiner Bestandteile eine Identitätsspalte ist, die immer unterschiedlich sein wird.

Vielleicht wurde der Primärschlüssel dort von Ihren Kollegen aus einem anderen Grund platziert (Indexierung usw.).

Das bedeutet, dass Ihr NHibernate-Mapping wirklich auf etwas ganz Einfaches reduziert werden sollte: ein normales POCO mit einer einzelnen ID, die zu tablename_id mapping, unter Verwendung eines einfachen nativen id-Generators von NHibernate. Die Eigenschaft "CountryID" fügt dann Ihre Eigenschaften als normale Eigenschaft zu Ihrem Ziel hinzu: CountryID wird mit der von der Datenbank automatisch erhöhten ID festgelegt.

public class ModelObject 
{ 
    public int ID { get; set; } 
    public int CountryID { get; set; } 
    // more properties here 
} 

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Model" assembly="API"> 
    <class name="ModelObject" table="dbname.dbo.tablename" lazy="false"> 
     <id name="ID" column="tablename_id" type="int"> 
     <generator class="native" /> 
    </id> 
     <property name="CountryID" column="tablename_country_id" type="int" /> 
     <!-- more properties here --> 
    </class> 
</hibernate-mapping> 
+0

Die CountryID ist erforderlich, da sie zur eindeutigen Identifizierung der Instanz der Datenbank (d. H. Datenbank des Landes) bei der Replikation verwendet wird. Dies war die ursprüngliche Lösung für den Umgang mit verteilten Datenbanken. Um richtig zu bleiben, muss ich den zusammengesetzten Schlüssel verwenden :( – zonkflut

0

wie in fluent nhibernate auto increment non key (Id) property erwähnt, kann es nicht notwendig sein, einen zusammengesetzten Schlüssel zu verwenden, um die erzeugte Natur des Feldes zu erhalten. Erwägen Sie, dieses oder ähnlich zu verwenden:

Zuordnung (x => x.Field) .Column ("Feld"). Generated.Always(). ReadOnly(); ReadOnly();