NHibernate: Exception with collection mapped as cascade all-delete-orphan - nhibernate

I receive the following exception intermittently:
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: Domain.Foo.Bars
The majority of the results on Google for this exception indicate that the problem occurs when you dereference a collection, instead of calling Clear() on the existing collection then adding new entities. However, this is not my problem.
Here is all the relevant code:
public class Foo
{
public int Id { get; set; }
private Iesi.Collections.Generic.ISet<Bar> _bars = new HashedSet<Bar>();
public virtual ICollection<Bar> Bars
{
get { return _bars; }
}
}
public class Bar
{
public int Id { get; set; }
public DateTime Expiry { get; set; }
}
public class FooDbMap : ClassMap<Foo>
{
public FooDbMap
{
Id(x => x.Id);
HasMany(x => x.Bars)
.Access.CamelCaseField(Prefix.Underscore)
.KeyColumn("FooId")
.LazyLoad()
.Where("Expiry > (select getdate())")
.AsSet()
.Cascade.AllDeleteOrphan();
}
}
You will see with this code that it is impossible to dereference the Bars collection, i.e. by doing this:
foo.Bars = new List<Boo>();
What could be causing the error?

you should never mess with collection references created by NHibernate. First of all, NH creates proxy objects for lazy loading - replace that proxy with a List<> and NH has no way to either lazy load the contents OR detect wether any children have been removed. Secondly, NH watches mapped collections for changes (new entities, deletes etc.). Just replacing a collection with a new one that NH does know nothing about is not a good idea. NH will still have a reference to the managed collection, especially if you have NH monitor this collection and cascade all changes to contained children.
I would simply clear the collection to remove all entities instead of replacing the entire collection.

Related

DDD - updating nested collection of value objects throws NHibernate exception

TLDR version: I'm having trouble getting my DDD domain model to work with NHibernate. If my value object itself contains a collection of value objects, I can't assign a new value without getting an NHibernate exception, and want to know what the best practice is in this situation.
Longer version:
Say I have an entity which contains a value object as a property, ValueObjectA, which itself contains a set of a different value objects of type ValueObjectB.
ValueObjectB only exists meaningfully as a property of ValueObjectA, i.e. if myEntity.ValueObjectA == null, it doesn't make sense for ValueObjectB to exist either.
I've written some example code to illustrate what I mean, with simplifications for brevity.
public class Entity
{
public int Id { get; private set; }
public ValueObjectA ValueObjectA { get; set; }
// Constructor: public Entity(ValueObjectA valueObjectA)
}
public class ValueObjectA : IEquatable<ValueObjectA>
{
public string X { get; private set; }
public ISet<ValueObjectB> ValueObjectBs { get; private set; }
// Constructor: public ValueObjectA(string x, ISet<ValueObjectB> valueObjectBs)
// Implementation of Equals/GetHahcode
}
public class ValueObjectB : IEquatable<ValueObjectB>
{
public int Y { get; private set; }
public int Z { get; private set; }
// Constructor: public ValueObjectB(int y, int z)
// Implementation of Equals/GetHahcode
}
I have a corresponding mapping class using mapping by code:
public class EntityMap : ClassMapping<Entity>
{
public EntityMap()
{
Table("Entity");
Id(x => x.Id, map => map.Generator(Generators.Identity));
Component(x => x.ValueObjectA, c =>
{
c.Property(x => x.X);
// Component relation is equilavent to <composite-element> in xml mappings
c.Set(x => x.ValueObjectBs, map =>
{
map.Table("ValueObjectB");
map.Inverse(true);
map.Cascade(Cascade.All | Cascade.DeleteOrphans);
map.Key(k => k.Column("Id"));
}, r => r.Component(ce =>
{
ce.Property(x => x.Y);
ce.Property(x => x.Z);
}));
});
}
}
The properties of ValueObjectA are mapped to the Entity table, but the properties of ValueObjectA.ValueObjectB are mapped to another table, since it is a one to many relationship. When a ValueObjectB is removed, I want that row to be deleted in the ValueObjectB table.
Since value objects are immutable, when I change the properties of entity.ValueObjectA, I should create a new instance of ValueObjectA. The problem is that the set of ValueObjectBs is a reference type, so when I try to save the entity with a different ValueObjectA, NHibernate will throw an exception because the original set that NHibernate is tracking is no longer referenced:
A collection with cascade="all-delete-orphan" was no longer referenced
by the owning entity instance.
Consider the following code:
var valueObjectBs_1 = new HashSet<ValueObjectB>
{
new ValueObjectB(1, 2),
new ValueObjectB(3, 4)
};
var valueObjectA_1 = new ValueObjectA("first", valueObjectBs_1);
var entity = new Entity(valueObjectA_1);
// Save entity, reload entity
var valueObjectBs_2 = new HashSet<ValueObjectB>
{
new ValueObjectB(1, 2)
};
var valueObjectA_2 = new ValueObjectA("second", valueObjectBs_2);
entity.ValueObjectA = valueObjectA_2;
// Save entity again
// NHIBERNATE EXCEPTION
I've managed to get around this by creating another ValueObjectA in order to preserve the reference to the set, e.g.
valueObjectA_1.ValueObjectBs.Remove(new ValueObjectB(3, 4));
entity.ValueObjectA = new ValueObjectA(valueObjectA_2.X, valueObjectA_1.ValueObjectBs);
However... that feels like a code smell - even if I wrote a custom setter for Entity.ValueObjectA, the implementation is starting to get complicated where the design is supposed to be simple.
public class Entity
{
// ...
private ValueObjectA valueObjectA;
public ValueObjectA ValueObjectA
{
// get
set
{
// Add/Remove relevant values from ValueObjectA.ValueObjectBs
valueObjectA = new ValueObjectA(value.X, ValueObjectA.ValueObjectBs);
}
}
}
What is the best practice in this type of situation? Or is this a sign that I'm trying to do something which violates the principles of DDD?
What you have is an anemic domain model.
You should replace public setters of the entity with methods that have meaningful names from the Ubiquitous language, that check the invariants and that do all the necessary cleanup in case of value objects replacements.
Although it may seem that things are more complicated this is payed back by the fact the now the entity is in full control about what happens with its internals. You now have full encapsulation.

Backing field for collection is null when using LinFu ProxyFactoryFactory in NHibernate

I am having a problem when I try switching from the Castle ProxyFactoryFactory to the LinFu ProxyFactoryFactory in NHibernate.
I have an entity like this:
public class Foo
{
private ISet<Bar> _bars = new HashedSet<Bar>();
public virtual void AddBar(Bar bar)
{
if (!_bars.Contains(bar)
_bars.Add(bar);
bar.Foo = this;
}
}
This is mapped with Fluent NHibernate like this:
public class FooDbMap : ClassMap<Foo>
{
public FooDbMap()
{
HasMany(x => x.Bars)
.Access.CamelCaseField(Prefix.Underscore)
.LazyLoad()
.KeyColumn("FooId")
.AsSet()
.Cache.ReadWrite();
}
}
The relationship is bi-directional and is mapped as such on the Bar side too.
The problem occurs when I call the AddBar method. The _bars collection is null, and a NullReferenceException is thrown.
The problem goes away if I switch back to the Castle ProxyFactoryFactory.
The error doesn't occur with all mapped collections, just this one instance.
The problem still occurs even if I change _bars to readonly! So someone is managing to set a readonly field back to null, even after the field has been assigned.
Any ideas?

Optional SubclassMap (left join) with NHibernate 3.0 GA & FNH

Disclaimer: I'm fairly new to NH & ORM in general.
Disclaimer: I'm working with a build of FNH from here in order to use with NH3.0GA.
The problem in a nutshell is that I would like to use FNH's SubclassMap as a way to map a LEFT JOIN, table-per-subclass scenario to my object hierarchy which is defined as:
public class MyBaseClass {
public virtual int Id { get; set; }
}
public class MySubClass : MyBaseClass {
public virtual string SubClassVal { get; set; }
}
This is mapped via FNH as:
public class MyBaseClassMap : ClassMap<MyBaseClass> {
public MyBaseClassMap() {
Table("BaseClass");
Id(x => x.Id, "Id").GeneratedBy.Assigned();
}
}
public class MySubClassMap : SubclassMap<MySubClass> {
public MySubClassMap() {
Table("SubClass");
KeyColumn("Id");
Map(x => x.SubClassVal);
}
}
And I retrieve via:
public class Repository {
ISession session; //assume properly initialized ISession
public IList<T> GetAll<T>() where T: class {
return session.CreateCriteria<T>().List<T>();
}
}
And in my database, I've got 1 record in my BaseClass table, 0 records in SubClass.
Now, what I would like to do is pull the entity out as a MySubClass instance by doing something like this:
var rep = new Repository();
var subclasses = rep.GetAll<MySubClass>();
And of course, there are no instances in the returned collection as this is presumably performing an INNER JOIN underneath it all. This is where I'm stuck. I've managed to discover that specifying an 'optional' join is what I'm supposed to do. I've attempted to modify MySubClassMap to:
public class MySubClassMap : SubclassMap<MySubClass> {
public MySubClassMap() {
Join("SubClass", j => {
j.KeyColumn("Id");
j.Optional();
j.Map(x => x.SubClassVal); // note that I've tried the map outside the Join() below, to no avail
});
//Map(x => x.SubClassVal);
}
}
Compiling/running this presents me with the following (innermost) exception:
The element 'joined-subclass' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'join' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'property, many-to-one, one-to-one, component, dynamic-component, properties, any, map, set, list, bag, idbag, array, primitive-array, joined-subclass, loader, sql-insert, sql-update, sql-delete, resultset, query, sql-query' in namespace 'urn:nhibernate-mapping-2.2'.
I'll save posting the stack trace, but the jist of it is:
MyApp -->
FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory() -->
NHibernate.Cfg.FluentConfiguration.BuildConfiguration()
I think that's all the relevant info. I suspect I may be bumping into a breaking change between this very new version of NH and version of FNH that isn't so new. But, as mentioned earlier, I am a rookie, and could well be doing something stupid. If this is the case, I'd very much appreciate somebody smacking me over the head with what probably should be obvious.
Thanks in advance.
Entities have one type, which doesn't change. If you have a record in your BaseClass table only, that entity is and will always be a MyBaseClass.
If entities can change their "type", you shouldn't use inheritance but composition.

How to add a read-only collection property in (Fluent) NHibernate?

I want to control my domain's interaction with a collection, so I thought I'd make the collection protected and provide a read-only wrapper around it so that the contents are visible, but I can ensure that items are not added directly to the collection.
So I have the following code:
public class MyClass
{
public virtual ICollection<Thread> Threads
{
get { return new ReadOnlyWrappedCollection<Thread>(this.ThreadsInternal); }
}
protected virtual ICollection<Thread> ThreadsInternal { get; private set; }
}
I tried this:
this.Map(c => c.Threads)
.Access.None();
The result was a MappingException: Could not determine type for: System.Collections.Generic.ICollection'1[[Thread]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, for columns: NHibernate.Mapping.Column(Threads)
I tried this:
this.HasMany(c => c.Threads)
.Access.None();
The result was an InvalidOperationException: Tried to add collection 'Threads' when already added
If I omit the mapping, I get PropertyNotFoundException: Could not find a setter for property 'Threads' in class 'MyClass'
How can I persuade NHibernate to ignore this property in the mapping? I'm using Fluent NHibernate, but please post examples in hbm too.
I don't think you can map an ICollection. Regardless, I'm following a similar pattern and I've found that the best way to map it is to map a private IList.
Class:
public class Invoice
{
private IList<InvoiceItem> _items;
public Invoice()
{
_items = new List<InvoiceItem>();
}
public virtual IEnumerable<InvoiceItem> Items
{
get { return _items; }
}
}
Mapping:
public class InvoiceMap : ClassMap<Invoice>
{
public InvoiceMap()
{
Table("Invoice");
HasMany(x => x.Items).KeyColumn("InvoiceId")
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.AllDeleteOrphan()
.Inverse()
.AsBag().LazyLoad();
}
}
The key line in the mapping is .Access.CamelCaseField(Prefix.Underscore) which tells NHibernate to use the private field _items. Note that The collection can still be cast to IList, but you could wrap it in a read only collection if needed.

No persister for... {SUBCLASS} NHibernate with Fluent NHibernate

How do I let NHibernate ignore extra properties of a subclass of my model?
class SuperModel { // hot I know
{
public Guid Id { get; private set; }
public string FirstName { get; set; }
}
class SubModel : SuperModel {
public string FavoriteColor { get; set; }
}
I really only want to store the SuperModel data using my repository and use the FavoriteColor elsewhere, but I get
No persister for: SubModel
even though I save it with my repository as
void Store(SuperModel model) {
using (var session = Session){
session.SaveOrUpdate(model); // <<<< The exception is thrown here
}
}
and some where else I use
void WhatToDo(SubModel model) {
doSomething(model.FavoriteColor);
}
And I use it as such
var model = new SubModel { FirstName = "Miranda", FavoriteColor = "Green" };
modelRepository.Store(model);
someService.WhatToDo(model);
Any one know how I can fluently configure this? Thanks.
FYI- implicit and explicit casting has no effect.
Edit
My mappings are like this
class SuperModelMap : ClassMap<SuperModel>
{
public SuperModelMap()
{
WithTable("SuperModels");
Id(x => x.Id);
Map(x => x.FirstName);
}
}
Edit 2
I figure/found out that I could do this, but in my database, I have to have a dummy table, which would just be inefficient. It works but there has to be a better way...
In my SuperModelMap...
JoinedSubClass<SubModel>("SubModel", MapSubModel);
private void MapSubModel(JoinedSubClassPart<SubModel> part)
{
// Leave this empty
}
Edit 3
I'm closer, but I still get a different error on selection.
I tried this.
DiscriminateSubClassesOnColumn("Id")
.SubClass<SubModel>(m => { });
InnerException {"Object with id:
5586b075-47f1-49c8-871c-9c4d013f7220
was not of the specified subclass:
SuperUser (Discriminator was:
'1000')"} System.Exception
{NHibernate.WrongClassException}
You can refine this solution to make it more reusable. As I understand, you don't like mapping duplication. This can be avoided:
I have created a SuperModelMapHelper class that contains an extension method:
public static class SuperModelMapHelper
{
public static void MapSuperModel<T>(this ClassMap<T> classMap)
where T : SuperModel
{
classMap.WithTable("SuperModels");
classMap.Id(x => x.Id);
classMap.Map(x => x.FirstName);
}
}
As you can see - it's generic and will accept any of SuperModel's subclasses. Then, there are two mappings:
public class SuperModelMap : ClassMap<SuperModel>
{
public SuperModelMap()
{
MapSuperModel();
}
}
public class SubModelMap : ClassMap<SubModel>
{
public SubModelMap()
{
MapSuperModel();
}
}
I've used extension method to preserve convention of FluentNHibernate, you can make it simple static method and pass class map as a parameter.
And this code:
Guid id;
using (var session = sf.OpenSession())
using (var transaction = session.BeginTransaction())
{
var subModel = new SubModel()
{FavoriteColor = "blue", FirstName = "Jane"};
session.Save(subModel);
id = subModel.Id;
transaction.Commit();
}
using (var session = sf.OpenSession())
using (var transaction = session.BeginTransaction())
{
var superModel = session.Get<SuperModel>(id);
Console.WriteLine(superModel.GetType().Name);
Console.WriteLine(superModel.FirstName);
transaction.Commit();
}
Works as intended - type is SuperClass. Note that I've created second session. You'd have to flush your session before trying to load entity in the same session you saved it, because NHibernate defers query execution.
Using this solution there is very little duplication. You can investigate AutoMapping feature of FluentNHibernate to reduce it even more - perhaps creating own convention would let you to automatically map such classes.
NHibernate assumes that you'd like to retrieve exactly the same object as you persist. So, even though you don't care about additional properties, you might care about the type of object. If you don't, the simplest solution would be to make a shallow copy of SubModel object, but instead of creating SubModel object, create SuperModel object.
I assume you thought about this and didn't like it. If you'd like to avoid dummy table, but can live with dummy column, I'd suggest you call:
DiscriminateSubClassesOnColumn("dummycolumn")
.SubClass<SubModel>(m => { });
This column would be used by NHibernate to store information about persisted object's type. When you load object from the db, it will be SubModel or SuperModel, depending on what it was when you persisted it.
Your solution with calling DiscriminateSubClassesOnColumn didn't work, because NHibernate couldn't determine which class to use based on id column.
Another idea: I'm not sure if it will work, but you could add another mapping, for SubModel, exactly the same as for SuperModel. Then, NHibernate should persist SubModel to the same table as SuperModel, and when you request your object it should fetch SuperModel object. Unfortunately I can't test this solution right now, maybe you can get it to work. No SubClass in this solution - two "parallel" mappings.