I have an abstract base class which inherits Sharp Arch's Entity class:
/// <summary>
/// defines an entity that will ne indexed by a search crawler and offered up as full-text searchable
/// </summary>
public abstract class IndexedEntity : Entity
{
[DocumentId]
public override int Id
{
get { return base.Id; }
protected set { base.Id = value; }
}
}
This is to a legacy db and actually the Id column is called "HelpPageID", so I have some mapping override as:
mapping.Id(x => x.Id, "HelpPageID");
The generated sql for querying HelpPage works fine when I simply inherit Entity. But inheriting IndexedEntity, when translated to sql, the column name override is ignored and instead Id is used for the column, thus failing.
Edit
Seems a general issue with an override as placing the override directly in the class has the same net effect
mapping overrides are only executed for the exact type not types which subclass the type in the mappingoverride. you have to specify an override for the subclass.
Related
I´m using Fluent Nhibernate with automapping and having problem setting up a bi-directional HasMany relationship because of my current inheritance.
I simplified version of my code looks like this
public abstract class BaseClass
{
public BaseClass Parent { get; set; }
}
public class ClassA : BaseClass
{
public IList<ClassB> BChilds { get; protected set; }
public IList<ClassC> CChilds { get; protected set; }
}
public class ClassB : BaseClass
{
public IList<ClassD> DChilds { get; protected set; }
}
public class ClassC : BaseClass
{
}
public class ClassD : BaseClass
{
}
Every class can have one parent and some parents can have childs of two types. I´m using table-per-type inheritance which result in the tables
"BaseClass"
"ClassA"
"ClassB"
"ClassC"
"ClassD"
To get a working bi-directional mapping I have made the following overrides
(one example from ClassA)
mapping.HasMany<BaseType>(x => x.BChilds).KeyColumn("Parent_Id");
mapping.HasMany<BaseType>(x => x.CChilds).KeyColumn("Parent_Id");
This works fine on classes with only one type of children, but ClassA with two child types will get all subtypes of BaseType in each list which ofcourse will end up in an exception. I have looked at two different workarounds tho none of them feels really sufficient and I really believe there is a better way to solve it.
Workaround 1: Point to the concrete subtype in the HasMany mapping. (Updated with more info)
mapping.HasMany<ClassB>(x => x.BChilds).KeyColumns("Parent_Id");
(BaseType replaced with ClassB)
With this mapping NHibernate will in some cases look in the ClassB table for a column named Parent_Id, obviously there is no such column as it belongs to the BaseClass table. The problem only occurs if you add a statement based on BChilds during a ClassA select. e.g loading an entity of ClassA then calling ClassA.BChilds seems to work, but doing a query (using NhibernateLinq) something like
Query<ClassA>().Where(c => c.BChilds.Count == 0)
the wrong table will be used. Therefore I have to manually create a new column in this table with the same name and copy all the values. It works but it´s risky and not flexible at all.
Workaround 2: Add a column to the BaseClass that tells the concrete type and add a where statement to the HasMany mapping.
(after my update to workaround1 I´m no longer sure if this could be a workable solution)
By adding a column they same way as it´s done when using table-per-hierarchy inheritance with a discriminatorValue. i.e BaseType table will get a new column with a value of ClassA, ClassB... Tho given how well NHibernate handles the inheritance overall and by reading the NHibernate manual I believe that the discriminator shouldn´t be needed in a table-per-type scenario, seems like Nhibernate already doing the hardpart and should be able to take care of this in a clean way to without adding a new column, just can´t figure out how.
What's your base class mapping and what does your subclass map look like?
You should be able to do
mapping.HasMany(x => x.BChilds);
And with the correct mapping, you shouldn't have a problem.
If it's fluent nhibernate, look into
UseUnionSubclassForInheritanceMapping();
I have a Fluent NHibernate project I'm working on, and doing some testing I have run into a very strange error:
The entity '<>c__DisplayClass3' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id).
The related entity reported is:
{Name = "<>c__DisplayClass3" FullName = "TPLLCPortal.Domain.Account+<>c__DisplayClass3"}
I don't have any class named DisplayClass, but I do have an Account entity. I'm using a primary key convention that looks like this:
public class PrimaryKeyConvention : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
instance.GeneratedBy.GuidComb();
}
}
My Account class inherits from an EntityBase class that declares the ID as:
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public virtual Guid Id { get; protected internal set; }
I'm confident that I'm setting up the configuration properly and that the conventions are being picked up, but just in case I added an override and specifically mapped the ID for the Account class. No dice.
Any ideas what's going on here?
I'm using FNH 1.3.0.733 with NHibernate 3.3.1.4000 (both loaded off NuGet).
Looks like I figured it out. This SO answer had the key. Because some of the methods on the class use lambdas, the compiler creates classes that you can exclude in the DefaultAutomappingConfiguration by specifying !type.IsDefined(typeof(CompilerGeneratedAttribute), false) as part of the ShouldMap override.
I have a convention for my ids, which automatically maps properties with a name of Id as the identifier. As requirements are being fleshed out I need to tweak a domain model so naturally I went online and found that I need to create a class that inherits from IAutoMappingOverride<T>.
My convention:
public class PrimaryKeyConvention : IIdConvention, IIdConventionAcceptance
{
public void Apply(IIdentityInstance instance)
{
instance.Column("Id");
instance.GeneratedBy.SeqHiLo(instance.Name, "10");
}
public void Accept(IAcceptanceCriteria<IIdentityInspector> criteria)
{
criteria.Expect(x => x.Generator, Is.Not.Set);
}
}
My override:
public class LocateMappingOverride : IAutoMappingOverride<Locate>
{
public void Override(AutoMapping<Locate> mapping)
{
mapping.Map(x => x.SendTo).Not.Nullable();
}
}
The convention does work as expected if I remove my override.
The exception I get is The entity 'LocateMappingOverride' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id)..
Is it possible to use conventions in conjunction with mapping overrides?
The answer is - yes, automapping can work with overrides.
Look what the error said. The problem is not with Locate entity, but with LocateMappingOverride entity, and that class should not be treated as entity, of course. You must have IAutomappingConfiguration configured so that FluentNHibernate's rule what to treat as entity includes LocateMappingOverride, too. And it does not have an Id mapped, indeed.
You should either:
change your IAutomappingConfiguration so that classes that implements IAutoMappingOverride<> are excluded
move the override outside the scope that is searched for entities
or introduce a common marker interface that all entities need to implement, i.e. IEntity and change IAutomappingConfiguration rules respectively.
By implementing ISubclassConvention, I can change the Discriminator Value for the subclasses in my class hierarchy. I'm now looking for a way to set the Discriminator Value for my base classes as well. Is there a way to change it with a convention override or do I have to add a manual mapping for my hierarchy?
(The IClassConvention provides the DiscriminatorValue property but it is read-only, so no luck there.)
The only way I know is to make simple mapping override just for base class.
public class DepotMappingOverride : IAutoMappingOverride<Depot>
{
/// <summary>
/// Alter the auto mapping for this type
/// </summary>
/// <param name="mapping">Auto mapping</param>
public void Override(AutoMapping<Depot> mapping)
{
mapping.DiscriminateSubClassesOnColumn("Type", "BaseDepot");
}
}
Now "BaseDepot" will be discriminator value for Depot class.
I have a Oracle database and one of the fields is a date range field. It is basically just stored in the database as a VARCHAR(40) in the format YYYY/MM/DD-YYYY/MM/DD. I want to map it in nHibernate to a custom class I have created like this
public class DateTimeRange
{
public DateTimeRange(DateTime fromTime, DateTime toTime)
{
FromTime = fromTime;
ToTime = toTime;
}
public override string ToString()
{
return String.Format("{0} to {1}", FromTime.ToString("HH:mm:ss"), ToTime.ToString("HH:mm:ss"));
}
public DateTime FromTime { get; set; }
public DateTime ToTime { get; set; }
}
How can I map to custom classes like this?
You need to implement your own IUserType.
See this blog post for details. I'll also paste the relevant section below in case the blog disappears.
In NHibernate, a custom mapping type is a class that derives from either the IUserType or ICompositeUserType interfaces. These interfaces contain several methods that must be implemented, but for our purposes here, we’re going to focus on 2 of them. Consider the following.
public class TypeClassUserType : IUserType
{
object IUserType.NullSafeGet(IDataReader rs,
string[] names,
object owner) {
string name = NHibernateUtil.String.NullSafeGet(rs,
names[0]) as string;
TypeClassFactory factory = new TypeClassFactory();
TypeClass typeobj = factory.GetTypeClass(name);
return typeobj;
}
void IUserType.NullSafeSet(IDbCommand cmd,
object value,
int index) {
string name = ((TypeClass)value).Name;
NHibernateUtil.String.NullSafeSet(cmd, name, index);
}
}
Having created this class, I can now explicitly map the association between ActualClass and TypeClass as a simple property on the ActualClass mapping.
<property
name="Type"
column="TypeName"
type="Samples.NHibernate.DataAccess.TypeClassUserType,
Samples.NHibernate.DataAccess" />
As NHibernate is in the process of saving an instance of ActualType, it will load and create a new instance of TypeClassUserType and call the NullSafeSet method. As you can see from the method body, I am simply extracting the name from the mapped property (passed in as the value parameter) and setting the extracted name as the value of the parameter to be set in the database. The net result is that although the Type property of ActualClass is TypeClass in the domain model, only the Name property of the TypeClass object gets stored in the database. The converse is also true. When NHibernate is loading an instance of ActualType from the database and the finds a property of my custom mapping type, it loads my custom type and calls the NullSafeGet method. As you can see, my method gets the name from the returned data, calls my flyweight factory to get the correct instance of TypeClass, and then actually returns that instance. The type resolution process happens transparently to my data access classes (and even to NHibernate itself for that matter).