Brook
Brook

Reputation: 6009

Why are my Fluent NHibernate SubClass Mappings generating redundant columns?

I have the following entities

public abstract class Card
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }

    public virtual Product Product { get; set; }
    public virtual Sprint Sprint { get; set; }
}
public class Story:Card
{
    public virtual double Points { get; set; }
    public virtual int Priority { get; set; }
}
public class Product
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }

    public virtual IList<Story> Stories { get; private set; }

    public Product()
    {
         Stories = new List<Story>();
    }
}

And the following mappings

public class CardMap:ClassMap<Card>
{
    public CardMap()
    {
        Id(c => c.Id)
            .Index("Card_Id");

        Map(c => c.Name)
            .Length(50)
            .Not.Nullable();
        Map(c => c.Description)
            .Length(1024)
            .Not.Nullable();

        References(c=>c.Product)
            .Not.Nullable();

        References(c=>c.Sprint)
            .Nullable();

    }
}
public class StoryMap : SubclassMap<Story>
{
    public StoryMap()
    {
        Map(s => s.Points);
        Map(s => s.Priority);

    }
}
public class ProductMap:ClassMap<Product>
{
    public ProductMap()
    {
        Id(p => p.Id)
            .Index("Product_Id");

        Map(p => p.Name)
            .Length(50)
            .Not.Nullable();

        HasMany(p => p.Stories)
            .Inverse();
    }

When I generate my Schema, the tables are created as follows

Card
---------
Id
Name
Description
Product_id
Sprint_id

Story
------------
Card_id
Points
Priority
Product_id
Sprint_id

What I would have expected would have been to see the columns Product_id and Sprint_id ONLY in the Card table, not the Story table.

What am I doing wrong or misunderstanding?

Upvotes: 3

Views: 1114

Answers (2)

fostandy
fostandy

Reputation: 4402

NB: Tested on the NH2 project only

Well, you are probably going to want to chew on a door once you read this, but the TLDR reason is because the Product_id and Spring_id columns in your Story table are not redundant - they exist for the HasMany(x => x.Stories) relations in your SpringMap and ProductMap. They just happen to be share the same naming convention as the CardMap References(x => x.Product and References(x => x.Sprint).

Validate this for yourself by commenting out ProductMap.cs:24-25 and SprintMap.cs:22 and rebuilding.

If the above does not make sense, let me know and I will try to explain in further detail.

So, it should work fine as is. If you want to clarify the columns, you could explicitly define the column names like so:

ProductMap.cs
        HasMany(p => p.Stories)
            .KeyColumn("ProductOwner_id")
            .Inverse();

SprintMap.cs
        HasMany(s => s.Stories)
            .KeyColumn("SprintOwner_id")
            ;

CardMap.cs
        References(c=>c.Product)
            .Column("Product_id")
            .Not.Nullable();

        References(c=>c.Sprint)
            .Column("Sprint_id")
            .Nullable();

Here I am guessing that the 1:N relationships between a Story and a Product/Sprint are an "owner". You would want to rename it to whatever is appropriate semantically.

One other thing. I would have thought the last changes (the changes to CardMap.cs) would be unnecessary - but they seem to be for some reason, or the Sprint_id column becomes SprintOwner_id. I have no idea why this would happen - I would speculate that this is some sort of bidirectional relationship inferencing on fluent/nhibernates part gone awry, but I'd put very little money on that.

Upvotes: 1

gillyb
gillyb

Reputation: 8910

I see that the Story entity inherits from the Card entity you created, but you don't know why you have Product_Id and Sprint_Id properties in the Story table Schema, since they're virtual properties in the Card class.

I'm guessing that this happens because in NHibernate, all properties need to be virtual but only at first. They don't really stay virtual. The NHibernate framework overrides them, and probably because of this, this is happening to you.

Upvotes: 0

Related Questions