I have the following classes defined:
And these tables in my database:
My fluent NHibernate mappings are:
public class BusinessUnitMap : ClassMap<BusinessUnit>
{
public BusinessUnitMap()
{
Table("BusinessUnits");
Id(x => x.Id);
Map(x => x.Code);
Map(x => x.Name);
Map(x => x.ParentId);
Map(x => x.Type).Column("Type").CustomType<BusinessUnitType>();
}
}
public class CompanyMap : SubclassMap<Company>
{
public CompanyMap()
{
Table("CompanyData");
KeyColumn("BusinessUnitID");
Map(x => x.Something);
}
}
public class FranchiseeMap : SubclassMap<Franchisee>
{
public FranchiseeMap()
{
Table("FranchiseeData");
KeyColumn("BusinessUnitID");
Map(x => x.SomethingDifferent);
}
}
public class StoreMap : SubclassMap<Store>
{
public StoreMap()
{
Table("StoreData");
KeyColumn("BusinessUnitID");
Map(x => x.SomethingElse);
}
}
Question #1 As far as I can tell, my code and database are setup the same as every example I've been able to find. According to those articles, NHibernate is supposed to be smart enough to determine what subclass to instantiate when I query for a particular subclass. But, when I execute the following statement:
var result = Session.QueryOver<BusinessUnit>()
.Where(x => x.Code == "Acme")
.SingleOrDefault();
an exception is thrown because it can't create an instance of the abstract BusinessUnit class. The only way I can get this to work is to specify Company as the type argument for QueryOver.
I've confirmed that using a discriminator breaks since NHibernate is looking for all of the columns to exist in a single table. Without it, though, I struggle to see how NHibernate would know what type to instantiate.
What am I doing wrong? Is the problem in my mappings, the way I'm querying, ...?
Question #2 When I change the query to something like this:
public T WithCode<T>(String code)
where T : BusinessUnit
{
var result = Session.QueryOver<T>()
.Where(x => x.Code == code)
.SingleOrDefault();
return result;
}
I get an exception indicating that the UPDATE statement conflicts with a foreign key constraint. Update statement!!!! Clearly something is still not right. How can a QueryOver call result in an UPDATE statement? What am I missing?
it looks like your data is not consistent. It might be better to use discrimnator mapping with optional. If you dont really need a BusinessUnitType property in code then just delete everything around the property Type
public enum BusinessUnitType
{
Company,
Franchisee
}
public abstract class BusinessUnit
{
public virtual int Id { get; set; }
public virtual string Code { get; set; }
public virtual string Name { get; set; }
public virtual BusinessUnit Parent { get; set; }
public abstract BusinessUnitType Type { get; }
}
public class Company : BusinessUnit
{
public virtual string Something { get; set; }
public override BusinessUnitType Type { get { return BusinessUnitType.Company; } }
}
public class Franchisee : BusinessUnit
{
public virtual string SomethingDifferent { get; set; }
public override BusinessUnitType Type { get { return BusinessUnitType.Franchisee; } }
}
public class BusinessUnitMap : ClassMap<BusinessUnit>
{
public BusinessUnitMap()
{
Table("BusinessUnits");
Id(x => x.Id);
Map(x => x.Code);
Map(x => x.Name);
References(x => x.Parent);
DiscriminateSubClassesOnColumn("Type");
Map(x => x.Type, "Type")
.Access.None()
.CustomType<BusinessUnitType>().ReadOnly();
}
}
public class CompanyMap : SubclassMap<StrangeTablePerSubclass.Company>
{
public CompanyMap()
{
DiscriminatorValue((int)new Company().Type);
Join("CompanyData", join =>
{
join.KeyColumn("BusinessUnitID");
join.Optional();
join.Map(x => x.Something);
});
}
}
public class FranchiseeMap : SubclassMap<Franchisee>
{
public FranchiseeMap()
{
DiscriminatorValue((int)new Franchisee().Type);
Join("FranchiseeData", join =>
{
join.KeyColumn("BusinessUnitID");
join.Optional();
join.Map(x => x.SomethingDifferent);
});
}
}