简体   繁体   中英

Mapping by code in NHibernate does not delete child elements in many to one mapping

I've got the following two C# classes:

public class Pet
{
    public virtual string Id { get; set; }

    public virtual string Name { get; set; }
}

public class PetOwner
{
    public virtual string Id { get; set; }
    public virtual Pet Pet { get; set; }
}

And I'm mapping them by code.

In my PetOwner class I have the following ManyToOne mapping for Pet:

    ManyToOne<Pet>("Pet", map =>
         {
             map.Lazy(LazyRelation.Proxy);
             map.Column("Pet_Id");
             map.Cascade(Cascade.Persist | Cascade.DeleteOrphans); 
             map.Insert(true);
             map.Update(true);
         });

Now when I call delete on my PetOwner class as follows, it works and the Pet is also deleted:

var owner = session.QueryOver<PetOwner>().Take(1).List().FirstOrDefault();
session.Delete(owner);

But the following code does not delete the pet at all (and does not give errors either).

session.Query<PetOwner>().Delete();

Now this is just in my unit tests, and in real life there will be a where clause and I probably won't delete all items, but why is this delete different from the explicit delete using the actual database entity?

Is there a way to get this to work as I'm expecting?

There is difference between your QueryOver and Query versions.

The QueryOver is deleting just a single instance. To do that, it is first loading the entity in memory - honor first level cache. That is why, your Cascade are working correctly.

The Query version is deleting all the records and is bulk delete operation. It does not load instances in memory - does not honor first level cache.
The Query interface example in your question actually work as DML. It does not load entities in memory before operating (UPDATE/DELETE) on them.

 /// Delete all entities selected by the specified query. The delete operation is performed in the database without reading the entities out of it. /// </summary> /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam> /// <param name="source">The query matching the entities to delete.</param> /// <returns>The number of deleted entities.</returns> public static int Delete<TSource>(this IQueryable<TSource> source) 

Replies to your comments:

I understand that, but does it not figure out it should also go and delete those owned items?

As first level cache is not utilized, there is actually nothing "owned".

so NHibernate does not automatically run something like "delete from PETS where Id in (select Pet_Id from PetOwners)".

With Query API, you are actually doing bulk delete -- it works on DML queries. So; no NHibernate does not generate the SQL you are asking for.

Or is that the type of information that comes from the level 1 cache?

Not First level cache; but the API you choose. This type of information is defined in mappings. But, how to generate the query also depends on what API you are using. You are using Query API here.

So out of curiosity, even if I used hbm mapping files, i would end up with the same scenario?

HBM or code mapping does not matter. Both are same.

Is there maybe a way I can at least get the delete to fail if the user does not manually also delete the pets? Using mapping by code that is.

Well... use QueryOver ; but it does not support bulk operations. Any API that support bulk operations will not generate the query you are expecting. Add necessary constraints on database level; that way, you will get exception thrown.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM