Domanda

I have 2 classes, Member.cs and Customer.cs and using table-per-type inheritance mapping described here.

This question poses the same problem, but with no answer.

Customer.cs

public class Customer
{
}

Member.cs

public class Member : Customer
{
    public Member(Customer customer)
    {
        CreateFromCustomer(customer);
    }

    private void CreateFromCustomer(Customer customer)
    {
        // Here I assume I'll assign the Id so NHibernate wouldn't have to create a new Customer and know what Customer to be referred
        Id = customer.Id;
    }
}

CustomerMap.cs

public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {
        Id(x => x.Id)
            .GeneratedBy.GuidComb();
    }
}

MemberMap.cs

public class MemberMap : SubclassMap<Member>
{
    public MemberMap()
    {
        KeyColumn("Id");
    }
}

I tried several test case :

Test1.cs

[Test]
public void CanAddCustomer()
{
    var customerRepo = /* blablabla */;

    using (var tx = NHibernateSessionManager.GetSession().BeginTransaction())
    {
        var customer = new Customer()

        customerRepo.RegisterCustomer(customer);

        tx.Commit();
    }

    using (var tx = NHibernateSessionManager.GetSession().BeginTransaction())
    {
        /* Get the persisted customer */
        var customer = customerRepo.GetCustomerByWhatever();

        var member = customerRepo.RegisterMember(new Member(customer));

        tx.Commit();
    }
}

I'm expecting to have :

1 customer and 1 member which is a child of that customer

Instead I have :

2 customers (1 that is what was correctly created and 1 with all null columns) and 1 member that Id referred to all null columns Customer.

Is it the expected behavior?

I understand if we were wanted to create a child object from a transient parent object, this is a correct behavior.

But what if we were to create a child object that refers to an existing parent object?

The link I provided doesn't cover any persistence example, neither does googling.

È stato utile?

Soluzione

Short Answer

No, it is not possible to "upgrade" an already persisted object to its subclass. Nhibernate simply doesn't support this. That's why you see 2 customers and one member entry. This is actually the expected behavior because Nhibernate simply creates a copy with a new ID of the object instead of creating the reference to Member...

So basically you could do either

  1. Copy the data of Customer into Member, delete customer and save Member
  2. Use a different object structure without subclasses where Member is a different table with it's own ID and a reference to Customer
  3. Use native sql to insert the row into Member...

Some example:

Your classes could look like this

public class Customer
{
    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
}
public class Member : Customer
{
    public virtual string MemberSpecificProperty { get; set; }
}

Basically, Member could have additional properties, but will have the same properties as Customer of cause, too.

public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {
        Id(x => x.Id)
            .GeneratedBy.GuidComb();

        Map(x => x.Name);
    }
}

and for the sub class, you have to map additional properties only!

public class MemberMap : SubclassMap<Member>
{
    public MemberMap()
    {
        Map(x => x.MemberSpecificProperty);
    }
}

testing it

{
    session.Save(new Customer()
    {
        Name ="Customer A"
    });

    session.Save(new Member()
    {
        Name = "Customer B",
        MemberSpecificProperty = "something else"
    });

    session.Flush();
}

This will create 2 entries in the customer table and one row into Member table. So this is as expected, because we created one customer and one member...

Now the "upgrade" from Customer A to Member:

using (var session = NHibernateSessionFactory.Current.OpenSession())
{
    session.Save(new Customer()
    {
        Name ="Customer A"
    });

    session.Flush();
}

using (var session = NHibernateSessionFactory.Current.OpenSession())
{
    var customer = session.Query<Customer>().FirstOrDefault();
    //var member = customer as Member;
    var member = new Member()
    {
        Name = customer.Name,
        MemberSpecificProperty = "something else"
    };
    session.Delete(customer);
    session.Save(member);

    session.Flush();
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top