views:

778

answers:

2

Hi everyone, I have a few classes that look like this

public class Token
{
    public int Id
    {
        get;
        set;
    }

    public ITokenInstance Instance
    {
        get;
        set;
    }
}

public interface ITokenInstance
{
    int Id
    {
        get;
        set;
    }

    Token Token
    {
        get;
        set;
    }
}

and mapping files


<class name="Token" >
   <id name="Id" >
      <generator class="hilo" />
   </id>

   <any name="Instance" meta-type="class" id-type="Int32" cascade="all-delete-orphan">
      <column  name="instance_type" />        
      <column name="instance_id" />
   </any>
</class>

<class name="TokenInstanceOne" >
   <id name="Id" >
      <generator class="hilo" />
   </id>

   <many-to-one name="Token" class="Token" column="token_id"/>
</class>

I have various implementations of the ITokenInstance interface, all looking in different tables but all using the same baisc structure as shown in the mapping. The problem is that whilst i can add a new ITokenInstance to a Token that has no instance set (null) and it will update correctly I can NOT add a new Instance to a Token that has already got an instance and then Update it, NHibernate will add the new instance i provide but not delete the now un-assigned instance. For example

Token token = Session.Get<Token>(4);
var instance = Session.Get<TokenInstanceOne>(1);
Assert.AreSame(token.Instance, instance);

var newInstance = new TokenInstanceOne();
token.Instance = newInstance;
newInstance.Token = token;
instance.Token = null;
Session.Flush();

This fires SQL to insert the new TokenInstance, and updates the token table to point at it, it does NOT delete the instance that was originaly set to the token. Does anyone know how I can instruct NHibernate to delete the original TokenInstance from the database

EIDT: I had missed something off that is now included in the code example (setting original TokenInstance's Token reference to null). Also just to clarify this is the SQL NHibernate is producing;

  1. INSERT INTO TokenInstanceOne (token_id, Id) VALUES (@p0, @p1); @p0 = '4', @p1 = '32768'
  2. UPDATE Token SET instance_type = @p0, instance_id = @p1 WHERE Id = @p2; @p0 = 'ClassLibrary1.TokenInstanceOne', @p1 = '32768', @p2 = '4'
  3. UPDATE TokenInstanceOne SET token_id = @p0 WHERE Id = @p1; @p0 = '', @p1 = '1'

Notice the last Update is setting token_id = '', what i need is for NHibernate to delete the row instead.

A: 

Sorry, I misunderstood your question.

Have you tried setting inverse="true" on your any end? Alternatively move the cascade to th the other class' mapping.

Read

Krzysztof Koźmic
I take it you're refering to the cascade options, which i have set on the any tag, which doesn't work. What are you suggesting instead?
Gareth
I tried moving the cascade to the other side but that didn't work, unfortunately neither any or many-to-one support inverse. Thanks for your suggestions though - much apprieciated
Gareth
+1  A: 

NHibernate does not implement a so called persistent garbage collection. There are situations where you need to remove entities explicitly. The cascade is for the case when you delete the Token.

This is your code:

var token = Session.Get<Token>(4);
Assert.IsNotNull(token.Instance);

// remove the old token
Session.Delete(token.Instance);

// assign the new token
var newInstance = new TokenInstance();
token.Instance = newInstance;
newInstance.Token = token;

// don't need to call update, the token is in the session.
// (except you turned off session flush)
// Session.Update(token);
Stefan Steinegger