2009-09-14 7 views
3

J'ai une simple table de trois DB avec relation many-to-many.Comment faire pour enregistrer en cascade avec CompositeId dans NHibernate?

A(id, Name) 
B(id, Name) 
AB(AId, BId) references A and B 

Les classes correspondantes:

public class A 
{ 
    public virtual int Id { get; set; } 
    public virtual string Name { get; set; } 
} 

public class B 
{ 
    public virtual int Id { get; set; } 
    public virtual string Name { get; set; } 
} 

public class AB 
{ 
    public virtual A A { get; set; } 
    public virtual B B { get; set; } 
    public override bool Equals(object obj) { /* routine */ } 
    public override int GetHashCode() { /* routine */ } 
} 

J'ai fait les correspondances avec Fluent NHibernate:

public class AMap : ClassMap<A> 
{ 
    public AMap() 
    { 
     Id(x => x.Id).GeneratedBy.Identity(); 
     Map(x => x.Name); 
    } 
} 

public class BMap : ClassMap<B> { /* The same as for A */ } 

public class ABMap : ClassMap<AB> 
{ 
    public ABMap() 
    { 
     CompositeId() 
      .KeyReference(x => x.A, "AId") 
      .KeyReference(x => x.B, "BId"); 
    } 
} 

Alors maintenant, je veux être en mesure de faire quelque chose comme ça

var a = new A { Name = "a1" };  
var b = new B { Name = "b1" };  
var ab = new AB { A = a, B = b }; 

//session.SaveOrUpdate(a); 
//session.SaveOrUpdate(b); 
session.SaveOrUpdate(ab); 

Mais sur SaveOrUpdate Je reçois TransientObjectException. Donc, pour passer au-dessus je dois SaveOrUpdate A et B avant d'enregistrer l'AB. Mais il semble qu'il devrait y avoir l'autre moyen de persister ces objets dans un seul SaveOrUpdate.

Y a-t-il un moyen de pointer dans le mappage AB vers Cascade A et B lors de la sauvegarde?

MISE À JOUR:

J'ai retiré la liste des liens AB dans une classe pour plus de clarté. À l'origine, c'était:

public class A 
{ 
    public A() 
    { 
     LinkToB = new List<AB>(); 
    } 

    public virtual int Id { get; set; } 
    public virtual string Name { get; set } 
    public virtual IList<AB> LinkToB { get; private set; } 
} 

public class AMap : ClassMap<A> 
{ 
    public AMap() 
    { 
     Id(x => x.Id).GeneratedBy.Identity(); 
     Map(x => x.Name); 

     HasMany(x => x.LinkToB) 
      .KeyColumn("AId") 
      .Inverse() 
      .Cascade.All() 
      .AsBag(); 
    } 
} 

// inside the transaction 
var a = new A { Name = "a1" }; 
var b = new B { Name = "b1" }; 

a.LinkToB.Add(new AB { A = a, B = b }); 
// session.SaveOrUpdate(b); 
session.SaveOrUpdate(a); 

Merci!

Répondre

2

J'ai posé cette question à nhibernate user group. Et la réponse était qu'il n'y a aucun moyen de cascader une opération en utilisant composite-id (peut-être dans les prochaines versions, il sera possible).

J'ai donc fait une solution de contournement. J'ai placé deux références (many-to-one avec cascade) au lieu de CompositeId et j'ai ajouté Id à la table AB et à l'entité AB. Maintenant, cela fonctionne et les entités A et B sont en cascade.

0

Juste une note, mais pourquoi avez-vous créé une classe AB? Si la relation entre ces 2 classes n'a pas de propriétés supplémentaires, alors votre classe 'AB' n'est pas nécessaire ...

Pour répondre à votre question: vous pouvez définir des options en cascade sur une relation dans NHibernate.

En Fluent NHibernate, je suppose que vous aurez à le faire comme ceci:

public class FooMap : ClassMap<Foo> 
{ 
    public FooMap() 
    { 
     HasMany(x => x.Bars).Cascade.AllDeleteOrphan(); 
    } 
} 

(notez que vous devez l'appliquer à votre situation, mais le collectionmapping renvoie une propriété Cascade, de sorte que vous pouvez définir quelle technique en cascade vous souhaitez appliquer)

+0

Merci pour la réponse rapide. J'ai enlevé cette relation pour la simplicité. Où dois-je entrer cette règle Cascade? – mipi

+0

Je n'ai pas encore vraiment utilisé Fluent, mais lorsque vous utilisez des fichiers de mappage XML, l'attribut cascade doit être défini dans la collection. –

+0

Et pour ma situation, il n'y a aucun moyen de mettre en cascade l'opération de sauvegarde ou est-ce là? – mipi

0

J'ai effectivement réussi à le faire fonctionner très facilement, et le code semble trop sec!

Voici ce que mon cas ressemblait à:

public class MappingSample : ClassMap<DomainClass> 
{ 
    public MappingCvTheme() 
    { 
     CompositeId() 
      .KeyProperty(x => x.SomeProperty) 
      .KeyReference(x => x.SomeReferencedObjectProperty); 

     //here comes the trick, repeat the reference code: 
     References(x => x.SomeReferencedObjectProperty).Cascade.All(); 
    } 
} 

J'espère que cela aide quelqu'un à la recherche de la même question à l'avenir.

+0

One mauvais effet secondaire bien que vous ayez deux colonnes créées pour SomeReferencedObjectProperty. Un appelé "SomeReferencedObjectPropertyID", un autre appelé juste "SomeReferencedObjectProperty". Ils seront toujours synchronisés, mais il est toujours moche, et si vous essayez de définir le KeyReference et les références au même nom de colonne, INSERT échouera car NH enverra 2 paramètres bien qu'il y ait une colonne! – Meligy

+0

* dans le commentaire précédent J'utilise NH pour créer votre schéma – Meligy

Questions connexes