views:

3094

answers:

2

Suppose I have three classes. It is valid to instantiate A, but there are also special cases B and D which subclass A, adding additional information.

How would I do the mapping files for this in (fluent) NHibernate?

public class A
{
    public int ID { get; set;}
    public string CommonProperty1 { get; set; }
    public string CommonProperty2 { get; set; }
}

public class B : A
{
    public string BSpecificProperty1 { get; set; } //not null
    public string BSpecificProperty2 { get; set; } //not null
}

public class D : A
{
    public string DSpecificProperty { get; set; } //not null
}

I tried the following, but it doesn't work at all:

public class AMap : ClassMap<A>
{
    public AMap()
    {
        Id(x => x.ID);

        Map(x => x.CommonProperty1);
        Map(x => x.CommonProperty2);
    }
}

public class BMap : ClassMap<B>
{
    public BMap()
    {
        References(x => x.ID);
        Map(x => x.BSpecificProperty1)
            .CanNotBeNull();
        Map(x => x.BSpecificProperty2)
            .CanNotBeNull();
    }
}

public class DMap : ClassMap<D>
{
    public DMap()
    {
        References(x => x.ID);

        Map(x => x.DSpecificProperty)
            .CanNotBeNull();
    }
}
+6  A: 

I'm not sure I understand what you mean by "map a subclass one-to-one", but if you want to map inheritance where the subclasses have properties that are not nullable, you can do like this in Fluent-NHibernate:

// Domain classes
public class Animal
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
}

public class Cat : Animal
{
    public virtual int WhiskerLength { get; set; }
    public virtual int ClawCount { get; set; }
}

public class Dog : Animal
{
    public virtual int TailWagRate { get; set; }
}



// Mapping file
public class AnimalMap : ClassMap<Animal>
{
    public AnimalMap()
    {
        Id(x => x.Id)
            .WithUnsavedValue(0)
            .GeneratedBy.Native();

        Map(x => x.Name);

        var catMap = JoinedSubClass<Cat>("CatId", sm => sm.Map(x => x.Id));

        catMap.Map(x => x.WhiskerLength)
            .CanNotBeNull();
        catMap.Map(x => x.ClawCount)
            .CanNotBeNull();

        JoinedSubClass<Dog>("DogId", sm => sm.Map(x => x.Id))
            .Map(x => x.TailWagRate)
                .CanNotBeNull();
    }
}

Since you want the subclasses' properties to be not-null, you have to use the table-per-class (joined-subclass) way of modeling the inheritance. This is because table-per-hierarchy requires all subclass properties to be nullable.

I hope it helps.

/Erik

Erik Öjebo
Erik, great post.
Berryl
+4  A: 

The syntax may have changed in FNH since Erik's post, but his example is right on target. Here's some code I used based on Erik's post to work through FNH the two FNH subclass strategies I know of right now (SubClass (the commented out code below, and JoinedSubClass). As an aside, I've seen other names used to describe the same strategies, including in NHibernate docs, which is a bit confusing when this is new to you. (https://www.hibernate.org/hib_docs/nhibernate/html/inheritance.html).

// Domain classes
public class Animal : Entity
{
    public virtual string Name { get; set; }
    public virtual string Unwanted { get; set; }
}

public class Cat : Animal
{
    public virtual int WhiskerLength { get; set; }
    public virtual int ClawCount { get; set; }
}

public class Dog : Animal
{
    public virtual int TailWagRate { get; set; }
}

public class Boxer : Dog
{
    public string DroolBucket { get; set; }
}

public class AnimalMapJoinedSubclassOverride : IAutoMappingOverride<Animal>
{
    public void Override(AutoMap<Animal> mapping) {
        mapping.Map(x => x.Name);

        mapping.IgnoreProperty(x => x.Unwanted);

        mapping.JoinedSubClass("CatId", CatMap.AsJoinedSubClass());
        mapping.JoinedSubClass("DogId", DogMap.AsJoinedSubClass());
        //mapping.DiscriminateSubClassesOnColumn("Type")
        //    .SubClass<Cat>("CatId", CatMap.AsSubClass())
        //    .SubClass<Dog>("CatId", DogMap.AsSubClass());
    }
}

public class CatMap
{
    public static Action<JoinedSubClassPart<Cat>> AsJoinedSubClass()
    {
        return part =>
        {
            part.Map(x => x.ClawCount).Not.Nullable();
            part.Map(x => x.WhiskerLength).Not.Nullable();
        };
    }

    public static Action<SubClassPart<Cat>> AsSubClass()
    {
        return part =>
        {
            part.Map(x => x.ClawCount);
            part.Map(x => x.WhiskerLength);
        };
    }
}

public class DogMap
{
    public static Action<JoinedSubClassPart<Dog>> AsJoinedSubClass()
    {
        return sub =>
        {
            sub.Map(x => x.TailWagRate).Not.Nullable();
        };
    }

    public static Action<SubClassPart<Dog>> AsSubClass()
    {
        return sub =>
        {
            sub.Map(x => x.TailWagRate);
        };
    }
}

public class BoxerMap
{
    public static Action<JoinedSubClassPart<Boxer>> AsJoinedSubClass()
    {
        return sub =>
        {
            sub.Map(x => x.DroolBucket);
        };
    }

    public static Action<SubClassPart<Boxer>> AsSubClass()
    {
        return sub =>
        {
            sub.Map(x => x.DroolBucket);
        };
    }
}
Berryl