Reputation: 283
I have a simple three table DB with many-to-many relation.
A(id, Name)
B(id, Name)
AB(AId, BId) references A and B
The corresponding classes:
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 */ }
}
I have made mappings with 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");
}
}
So now I want to be able to do something like this
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);
But on SaveOrUpdate I do get TransientObjectException. So to pass over it I need to SaveOrUpdate A and B before saving the AB. But it seems that there should be the other way to persist these objects in a single SaveOrUpdate.
Is there any way to point in AB mapping to Cascade A and B on save operation?
UPDATE:
I removed the List of AB links in A class for clarity. Originally it was:
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);
Thank you!
Upvotes: 3
Views: 2839
Reputation: 36594
I actually managed to get it working very easily, and the code looks too much NOT DRY!
Here is what my case looked like:
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();
}
}
I hope it helps somebody looking for the same issue in the future.
Upvotes: 0
Reputation: 283
I did ask this question at nhibernate user group. And the answer was that there is no way to cascade any operation using composite-id (maybe in the future releases it will be possible).
So I made a workaround. I placed two References (many-to-one with cascading) instead of CompositeId and added Id to the AB table and AB entity. Now it's working and A and B entities are cascading.
Upvotes: 2
Reputation: 56934
Just a side-note, but why did you create a class AB ? If the relationship between these 2 classes does not have any extra properties, then your class 'AB' is not necessary ...
To answer your question: you can define cascade options on a relationship in NHibernate.
In Fluent NHibernate, I guess you'll have to do it like this:
public class FooMap : ClassMap<Foo>
{
public FooMap()
{
HasMany(x => x.Bars).Cascade.AllDeleteOrphan();
}
}
(note that you have to apply it to your situation, but the collectionmapping returns a property Cascade, so you can define which cascading technique you wish to apply)
Upvotes: 0