I'm working on a project with NHibernate that classes similar to the following:
public class Parent {
public IList Children {get;set;}
// ...
}
public class Child {
// ...
}
I've got the Children property set to cascade all / delete orphan. Since I'm using the aggregate pattern and instances of Child class will only ever be referenced in the context of a Parent, I don't have a ChildRepository to delete the children directly - only a ParentRepository. However, when I retrieve the Parent object and call Parent.Children.Clear(), the children from the database are never deleted. How can I achieve this?
Deleting child entities is this easy - just remove them from the collection and then save the parent entity. The collection should be mapped with all-delete-orphans.
parent.Children.Clear();
session.Save( parent );
// or
parent.Children.RemoveAt(0);
session.Save( parent );
You can do this without Save() calls as well, unless your FlushMode is Never.
session.BeginTransaction();
parent.Children.Clear();
session.Transaction.Commit();
Using #Chris's UnitOfWork abstractionm this could look like:
using (var uow = new UnitOfWork()) {
parent.Children.Clear();
}
Related
When I have an entity object with a one-to-many child collection, and I need to query for a specific child object, is there a feature or some clever pattern I haven't come up with yet to avoid that NHibernate fetches the entire child collection?
Example:
class Parent
{
public virtual int Id { get; proteced set; } // generated PK
public virtual IEnumerable<Child> Children { get; proteced set; }
}
class Child
{
public virtual int Id { get; protected set; } // generated PK
public virtual string Name { get; protected set; }
public virtual Parent Parent { get; protected set; }
}
// mapped with Fluent
class Service
{
private readonly ISessionFactory sessionFactory;
public Service(ISessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
void DoSomethingWithChildrenNamedBob(int parentId)
{
using(var session = sessionFactory.OpenSession())
{
var parent = session.Get<Parent>(parentId);
// Will cause lazy fetch of all children!
var childrenNamedBob = parent.Children.Where(c => c.Name == "Bob");
// do something with the children
}
}
}
I know it's not the best example because in this case one would probably just query the Child entities directly, but I have encountered situations where I already had a Parent object and needed to traverse specific sub-trees through it.
Short answer: no. Longer answer: you can make it do this, with some sleight of hand.
Rippo's answer above shows how you would do it the 'proper' NHibernate way (whether it's with Linq or QueryOver or HQL doesn't really matter - the point is you have to step outside the parent -> child relationship to do a query). You can take this a step further and disguise this behind a façade. But to do so, you have to remove the mapped relationship entirely and replace it with a query at all times. You'd take out the Parent -> Children mapping, but leave the Child -> Parent mapping intact; then re-write the property on Parent to look like this:
public virtual IQueryable<Child> Children
{
get
{
// somehow get a reference to the ISession (I use ambient context), then
return session.Query<Child>().Where(c => c.Parent == this);
}
}
Now, when you use Parent.Children you get back a queryable collection, so you could then write
IEnumerable<Child> childrenNamedBob = parent.Children.Where(c => c.Name == "Bob");
The only way you could do this and preserve the mapping is to amend NHibernate's collection objects (or inject your own). Diego Mijelshon (who is around these parts) wrote a spike of exactly that, adding IQueryable support to NHibernate collections so you could do
IEnumerable<Child> childrenNamedBob = parent.Children.AsQueryable().Where(c => c.Name == "Bob");
But from what I can see, this never went any further and there's no apparent plan to add this capability to NH. I have run Diego's code and it does work, but obviously it's not production quality and hasn't been tested, and I don't think it's ever been officially 'released' even as a private patch.
Here's the link to the discussion on the NH issue tracker: https://nhibernate.jira.com/browse/NH-2319
I believe NH should support this out of the box, as it's a natural way for most .NET devs to want to interact with pretty much anything enumerable, now that we have Linq, and not being able to do it without the side-effect of loading an unbounded collection into RAM sucks. But the traditional NH model is session -> query and that's what 99% of people use.
I asked the same question on NHusers a few weeks ago and didn't get an answer so I suspect the answer is you will always get all the parents children and then perform a in-memory filter. In many cases this might be the correct way in seeing it.
In your case I would rewrite the query to be:-
var childrenNamedBob = session.Query<Children>()
.Where(w => w.Parent.Id == parentId && w.Name == "Bob");
Then simply to get parent (if childrenNamedBob has results) you could call:-
var parent = childrenNamedBob.First().Parent;
or as you rightly pointed out:-
var parent = session.Get<Parent>(parentId);
You can now do that with NHibernate 5 directly without specific code !
See https://github.com/nhibernate/nhibernate-core/blob/master/releasenotes.txt
Build 5.0.0
=============================
** Highlights
...
* Entities collections can be queried with .AsQueryable() Linq extension without being fully loaded.
...
I've searched so many places and still can't find the a answer I'm looking for.
I'm using NHibernate 3.2 - Mapping by Code
I Have the following Mapping files:
public class ParentMapping: EntityMapping<Parent>
{
public ParentMapping()
{
Set(x => x.Children, map =>
{
map.Cascade(Cascade.All);
map.Inverse(true);
}, r => r.OneToMany());
}
}
public class ChildMapping: JoinedSubclassMapping<Child> // This is a subclass of something else.
{
public RequiredSkillMapping()
{
ManyToOne(x => x.Parent, map => { map.NotNullable(true); });
}
}
The cascade save works fine.
session.Save(parent) will save the children and associate them correctly.
When I try to call:
var parent = session.Get<Parent>(1);
parent.Children.Clear();
session.Save(parent); or session.SaveOrUpdate(parent) or session.Update(parent)
The entities stay associated to the parent.
I had it working by calling:
foreach(var child in parent.Children)
{
session.Delete(child);
}
parent.Children.Clear();
I was hoping that there would be a way to just save the parent?
Cheers,
James
Cascade means that operations on the parent are cascaded to the child.
When inserting the parent, the children are inserted too.
When deleting the parent, the children are deleted too.
Update is special in NH, but it also means when calling update with the parent, that the children are "updated" too.
and so on
The collection itself belongs to the parent, so changes on the collection are stored with the parent. But there is no reason to delete the children when they are not in the collection anymore. NH can't know if you need them for anything else.
There is cascade-delete-orphans.
map.Cascade(Cascade.All | Cascade.DeleteOrphans)
It means that items that had been in the collection, and are removed from the collection get deleted. It may be useful in your case. Note that it is not possible to use items for anything after removing from the collection. You can't even add them to another parent.
To make NH remove unused items automatically correctly in every case would require persistent garbage collection. This is highly inefficient and is not implemented by NH.
"foreach child delete" is OK. That's something you may have to do to tell NH when the children are not used anymore and need deletion.
I'm sending a JSON collection from Javascript through a REST web service to be Deserialized using Json.NET and then finally updated in the DB using NHibernate (I'm using Fluent).
My Json is:
{
"ID": 1,
"Name": "ObjectName",
"Keys": [
{
"ID": 6,
"Name": "ID"
}
]
}
My difficulty is that when I remove a child from the 'Key' collection in Javascript, the subsequent update only nulls the foreign key of the child - it doesn't actually delete it. I have setup what I think is the correct mappings on both the parent and child:
Object one-to-many mapping:
// one-to-many
HasMany(x => x.Keys)
.KeyColumn("ObjectID")
.Cascade.AllDeleteOrphan();
Key many-to-one mapping:
// many-to-one
References(x => x.Object)
.Cascade.None();
NHibernate code that performs the update:
using (var transaction = Session.BeginTransaction())
{
Session.SaveOrUpdate(entity);
transaction.Commit();
}
Session.Flush();
Although this example is bidirectional, I have also attempted uni-directional mappings, but so far this has had no effect; the child record persists in the database, although the association itself is broken (FK set to null).
Anything obvious that I'm missing?
Ok, the problem here lies in the fact that using the Json.NET Deserializer in a normal capacity essentially creates a new object - this object is then persisted by NHibernate. The end result is the record in the database is kept, as are all the child objects that exist in the new child object collection - however children that were removed are orphaned - since the where not removed directly from the persisted NHibernate object using .Remove or .Clear.
The solution is in two parts. Firstly, we have to use the CustomCreationConverter of Json.NET to pass in an instance of the existing object to be worked on (merge).
public static T Deserialize<T>(T existingObject, string json)
{
return JsonConvert.DeserializeObject<T>(json, new ObjectConverter<T>(existingObject));
}
public class ObjectConverter<T> : CustomCreationConverter<T>
{
public T ExistingObject { get; set; }
public ObjectConverter(T existingObject)
{
ExistingObject = existingObject;
}
public override T Create(Type objectType)
{
return ExistingObject;
}
}
This alone will not work however, as the exiting objects child collections will be added to by the children in the json collection. The remedy this, and to ensure that NHibernate knows what to do when receiving the resulting object, we need to do a bit of a Json.NET hack.
Json.Net > Serialization > JsonSerializerInternalReader.cs
private object PopulateList(IWrappedCollection wrappedList, JsonReader reader, string reference, JsonArrayContract contract)
{
// Edit // Clear the collection
wrappedList.Clear();
After re-compiling, and re-adding the DLL - it works - children that are removed in javascript are finally removed from the DB as well.
I have a situation where I'm using a table per subclass for association. The mappings are fine until I try and delete a record from the subclass table. The delete cause's not only the subclass to be deleted, but also the parent. I can understand this feature may be by design, but is there anyway to just delete the subclass?
Here's my sample code.
public class ParentClassMap : ClassMap<Parent>
{
public ParentClassMap ()
{
Table("tblParent");
Id(x => x.ParentId).Column("ParentId").GeneratedBy.Identity()
... other properties
}
}
public class ChildClassMap : SubClassMap<Child>
{
public ChildClassMap()
{
Table("tblChild");
KeyColumn("ParentId");
... other properties
}
}
Now when I query for a record, everything seems fine
Child child = session.CreateCriteria<Parent>().Add(Restrictions.Eq("ParentId", 1)).UniqueResult<Parent>();
But when I delete the child, the sql executed includes updates to all tables that reference either Parent or Child, then the deletion of Child then Parent.
session.Delete(child);
I only want to delete the Child Object, is this possible?
No, it's not possible.
Think about it in OOP terms: if you have an object of the class Dog that inherits from Animal, does it makes sense to "delete the dog, but leave the animal"?
Which brings me to the next point: if you can delete "part" of an object, then you should not be using a subclass, but an association (probably one-to-one or many-to-one)
If I have two entities, Parent and Child, Parent needs to know about all of its Child children, and every Child instance needs to know about its parent Parent instance, how do I do this properly (in terms of DDD etc)?
The easy way would be to do parent.addChild(new Child(parent)), but this seems ugly - as well as:
parent.addChild(new Child()); // Then call some setParent method on child, which needs to be public
Do I need to use a factory here? And if so, how?
Thanks
One option would be to have a relevant instance method on the Parent class. A static Factory is probably not necessary here since you are operating on existing objects and simply need to connect object with another.
public class Parent {
// ...
public Child createChild() {
Child c = new Child(this);
this.addChild(c);
return c;
}
protected void addChild(c) {
// ...
}
// ...
}
public class Child {
public Child(Parent p) {
// ...
this.addParent(p);
}
protected addParent(Parent p) {
// ...
}
}
If the Child constructor needs arguments, you can pass those to the createChild method.
You are not limited to a single one approach. You should use the approach that fits you.
In SWT the child is linked to parent in constructor:
new Label(parentComposite, SWT.NONE);
After that parentComposite knows its child.
Note: SWT requires parent on creation which limits some functionality - you can't create child without specifying a child. This is limitation of the SWT.
In Swing you can create child widget and then add it to the parent.
Those above just the examples. Your personal solution will be based on your needs.
I would consider to use less methods, more consistency (don't leave your childs unlinked from parent)
Talking about code I would use following method:
Parent {
addChild(Child child) {
children.add(child);
child.setParent(this);
}
}
Hope that helps. Happy designing!
How about to do something like this:
public class Child {
Child (Parent parent) {
...
this.parent = parent;
parent.addChild(this);
}
}
So you can set parent to Child only while creating child.