I'm currently working with entityName-only mapping, specifically focusing on Chapter 4.4, Dynamic Models. My approach involves creating criteria based on entityName to query entities within the database.
When working with primary key mappings, the setup functions as expected. However, I've encountered some unexpected behavior with composite key mappings.
To elaborate, it retrieves entity data without including the composite key elements, and when criteria restrictions are applied on composite ID properties, an exception is thrown, often stating "Property not found or not mapped."
Upon further investigation, I noticed differences between Map and POCO-based mappings. It appears that the root of the exception lies within the AbstractEntityPersister class, specifically in the following member:
protected readonly BasicEntityPropertyMapping propertyMapping;
In the AbstractPropertyMapping class, the members below seem to vary between modes:
private readonly Dictionary<string, IType> typesByPropertyPath = new Dictionary<string, IType>();
private readonly Dictionary<string, string[]> columnsByPropertyPath = new Dictionary<string, string[]>();
private readonly Dictionary<string, string[]> formulaTemplatesByPropertyPath = new Dictionary<string, string[]>();
The number of dictionary elements changes when switching between POCO and Map mode. For example:
public class Sample
{
public virtual int MyId { get; set; }
public virtual string GlobalId { get; set; }
public virtual string Name { get; set; }
}
In POCO mode, the dictionary mappings are structured as follows:
In Map-based mode, however, the mappings look like this:
Initially, I thought the issue could be due to an error in my XML mapping. However, I later discovered that it may be due to how the HBM mapping is deserialized and bound into RootClass instances. During binding, composite key elements are mapped differently depending on whether they are embedded properties or separate components. In embedded cases, the properties are mapped to the same type as the component class, and the IsEmbedded flag is set to true.
In Map-based mode, however, this flag seems to be ignored, and instead, IsDynamic is set to true. Consequently, the composite key properties aren’t treated as embedded, resulting in them being skipped during BasicEntityPropertyMapping.InitPropertyPaths(). This different behavior is observed across Map and POCO modes.
The call chain leading to this behavior is as follows:
Within InitIdentifierPropertyPaths, the conditions are as follows:
private void InitIdentifierPropertyPaths(IMapping mapping)
{
string idProp = IdentifierPropertyName;
if (idProp != null)
{
propertyMapping.InitPropertyPaths(idProp, IdentifierType, IdentifierColumnNames, null, mapping);
}
if (entityMetamodel.IdentifierProperty.IsEmbedded)
{
propertyMapping.InitPropertyPaths(null, IdentifierType, IdentifierColumnNames, null, mapping);
}
if (!entityMetamodel.HasNonIdentifierPropertyNamedId)
{
propertyMapping.InitPropertyPaths(EntityPersister.EntityID, IdentifierType, IdentifierColumnNames, null, mapping);
}
}
When entityMetamodel.IdentifierProperty.IsEmbedded is true, the method should proceed accordingly, but in Map mode, this condition is skipped. Ideally, if the type is dynamic, the composite ID properties should also be treated as embedded. When a composite ID has a defined class type in the application domain, the process follows the standard execution path.
(Actually, I was adding a New Line IsEmbedded = true)
Another question I have is: if I proceed with EntityMode.Map along with composite key entities, how should I handle cases like GetHashCode and Equals?
I understand that the result is a Dictionary<string, object>. So far, I’ve managed insert and update operations by creating an anonymous type object containing separate ID elements and data into Dictionary<string, object>.