views:

51

answers:

1

I'm a fluent nhibernate newbie and I'm struggling mapping a hierarchy of polymorhophic objects. I've produced the following Model that recreates the essence of what I'm doing in my real application.

I have a ProductList and several specialised type of products;

public class MyProductList
{
    public virtual int Id { get; set; }
    public virtual string Name {get;set;}
    public virtual IList<Product> Products { get; set; }

    public MyProductList()
    {
        Products = new List<Product>();   
    }
}

public class Product
{
    public virtual int Id { get; set; }
    public virtual string ProductDescription {get;set;}
}

public class SizedProduct : Product
{
    public virtual decimal Size {get;set;}
}

public class BundleProduct : Product
{
    public virtual Product BundleItem1 {get;set;}
    public virtual Product BundleItem2 {get;set;}
}

Note that I have a specialised type of Product called BundleProduct that has two products attached.

I can add any of the specialised types of product to MyProductList and a bundle Product can be made up of any of the specialised types of product too.

Here is the fluent nhibernate mapping that I'm using;

public class MyListMap : ClassMap<MyList>
{
    public MyListMap()
    {
        Id(ml => ml.Id);
        Map(ml => ml.Name);
        HasManyToMany(ml => ml.Products).Cascade.All();
    }
}

public class ProductMap : ClassMap<Product>
{
    public ProductMap()
    {
        Id(prod => prod.Id);
        Map(prod => prod.ProductDescription);
    }
}

public class SizedProductMap : SubclassMap<SizedProduct>
{
    public SizedProductMap()
    {
        Map(sp => sp.Size);
    }
}

public class BundleProductMap : SubclassMap<BundleProduct>
{
    public BundleProductMap()
    {
        References(bp => bp.BundleItem1).Cascade.All();
        References(bp => bp.BundleItem2).Cascade.All();
    }
}

I haven't configured have any reverse mappings, so a product doesn't know which Lists it belongs to or which bundles it is part of.

Next I add some products to my list;

        MyList ml = new MyList() { Name = "Example" };

        ml.Products.Add(new Product() { ProductDescription = "PSU" });
        ml.Products.Add(new SizedProduct() { ProductDescription = "Extension Cable", Size = 2.0M });

        ml.Products.Add(new BundleProduct()
        {
            ProductDescription = "Fan & Cable",
            BundleItem1 = new Product() { ProductDescription = "Fan Power Cable" },
            BundleItem2 = new SizedProduct() { ProductDescription = "80mm Fan", Size = 80M }
        });

When I persist my list to the database and reload it, the list itself contains the items I expect ie MyList[0] has a type of Product, MyList[1] has a type of SizedProduct, and MyList[2] has a type of BundleProduct - great!

If I navigate to the BundleProduct, I'm not able to see the types of Product attached to the BundleItem1 or BundleItem2 instead they are always proxies to the Product - in this example BundleItem2 should be a SizedProduct.

Is there anything I can do to resove this either in my model or the mapping?

Thanks in advance for your help.

A: 

As it stands, the BundleItem1 and BundleItem2 properties will always have a Product proxy because NH creates your proxies without touching the database, so it doesn't know if they are Products or some derived type. But when you call a method on your bundle items, NH should hit the DB and load the correct record, and you should get polymorphic behavior.

You could test this out. Add an override of ToString to your SizedProduct:

public override string ToString()
{
   return "I'm a sized product!";
}

Then load your BundleProduct and do this:

Debug.WriteLine(bp.BundleItem1.ToString());
Debug.WriteLine(bp.BundleItem2.ToString());

You should find that the second call prints out "I'm a sized product!", and this will demonstrate that you have working polymorphism.

Assuming this all worked as I've described, its time to tackle the real question: what exactly do you want to do? Maybe you could provide some code that doesn't actually work as you would like it to.

Paul Batum
Paul, thanks for taking the time to answer this question. I've added the ToString overrides and its working as you describe. When I look at BundleItem2 I can see its a sized product, but how do I actually get at its additional properties, I can't seem to cast?`Console.WriteLine(bp.BundleItem2.ProductDescription); SizedProduct sp = bp.BundleItem2 as SizedProduct; SizedProduct sp1 = (SizedProduct)bp.BundleItem2;`Here, the ProductDescription is output, the first cast returns null and the second attempt gives invalid cast error.
Andy Baker
What I really need to be able to do is get to the .Size property on the bundled item.This relates to my real task in that I'm building an object that represents a query, similar to the ExpressionTree, where each node needs to be a specialised type of a base object. It appears to persist OK when I look at the tables, but on reload I'm not able to see the properties on the nodes in my tree other than those that belong to the base object.
Andy Baker
..also I do accept that I didn't mean polymorphism :)
Andy Baker
Ahh well if you are trying to cast then you should take a look at this: http://stackoverflow.com/questions/413237/getting-proxies-of-the-correct-type-in-nhibernate
Paul Batum

related questions