- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Not Getting desired result for complex property when trying to set through custom EntityStaticMetaData
Joined: 03-May-2024
Setting EntityStaticMetaData custom
SetEntityCoreInfo("" + ObjectInfo.Name + "", ObjectInfo.IsInInheritanceChain
? InheritanceHierarchyType.TargetPerEntity
: InheritanceHierarchyType.None, false, (int)ObjectInfo.EntityType, ObjectInfo.Type, typeof(EntityFactory), false);
MethodInfo[] methods = typeof(EntityStaticMetaDataBase).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(m => m.Name == "AddNavigatorMetaData").ToArray();
Type currentEntityType = ObjectInfo.StaticType != null ? ObjectInfo.StaticType : ObjectInfo.Type;
var propertyInfos = currentEntityType.GetProperties();
foreach (var property in ObjectInfo.Properties.OfType<IReferenceProperty>())
{
var propertyInfo = propertyInfos.Where(p => p.Name == property.Name).FirstOrDefault();
if (propertyInfo != null)
{
Type relatedEntityType = property.NetType;
if (property.Relation != null)
{
if (property.Relation.Type == RelationType.OneToMany && property.Relation.RelationProperties.Count() > 0)
{
var relationProperties = new List<IRelationProperty>(property.Relation.RelationProperties);
foreach (IRelationProperty relationProperty in relationProperties)
{
if (relationProperty.StartProperty != null && relationProperty.EndProperty != null)
{
// Filter by parameter types
MethodInfo methodInfo = methods.Single(m =>
m.GetParameters().Length == 7 &&
m.GetParameters()[0].ParameterType == typeof(string) &&
m.GetParameters()[1].ParameterType.IsGenericType && m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>) &&
m.GetParameters()[2].ParameterType.IsGenericType && m.GetParameters()[2].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>) &&
m.GetParameters()[3].ParameterType.IsGenericType && m.GetParameters()[3].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>) &&
m.GetParameters()[4].ParameterType == typeof(Func<IEntityRelation>) &&
m.GetParameters()[5].ParameterType == typeof(Type) &&
m.GetParameters()[6].ParameterType == typeof(int)
);
// Create an instance of the class
// object entityStaticMetaDataInstance = Activator.CreateInstance(currentEntityType);
var instance = new EntityStaticMetaDataBase();
// Make the method generic with the dynamic types
MethodInfo genericMethod = methodInfo.MakeGenericMethod(currentEntityType, relatedEntityType);
string navigator = property.Name;
var memberGetter = CreateGetterDelegate(currentEntityType, property.Name, relatedEntityType);
var memberSetter = CreateMemberSetterDelegate(currentEntityType, relatedEntityType, property.Name);
var propertyGetter = CreateGetterDelegate(currentEntityType, property.Name, relatedEntityType); // reuse
Func<IEntityRelation> relationCreatorFunc = () => RelationFactory.CreateFor(property.Relation);// CreateRelationFactory(property.Relation.GetType());
object typeOfRelatedEntity = relatedEntityType;
object relatedEntityEnumTypeValue = property.Type.EntityType;
//// Invoke the method dynamically
genericMethod.Invoke(this, new object[]
{
navigator,
memberGetter,
memberSetter,
propertyGetter,
relationCreatorFunc,
typeOfRelatedEntity,
relatedEntityEnumTypeValue
});
}
}
}
else if ((property.Relation.Type == RelationType.ManyToMany || property.Relation.Type == RelationType.OneToMany) && property.Relation.SecondRelation != null)
{
MethodInfo methodInfo = methods.Single(m =>
{
var p = m.GetParameters();
return p.Length == 10 &&
p[0].ParameterType == typeof(string) &&
p[1].ParameterType.IsGenericType && p[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>) &&
p[2].ParameterType.IsGenericType && p[2].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>) &&
p[3].ParameterType.IsGenericType && p[3].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>) &&
p[4].ParameterType == typeof(Func<IEntityRelation>) &&
p[5].ParameterType == typeof(Func<IEntityRelation>) &&
p[6].ParameterType == typeof(string) &&
p[7].ParameterType == typeof(string) &&
p[8].ParameterType == typeof(Type) &&
p[9].ParameterType == typeof(int);
});
// Make method generic
MethodInfo genericMethod = methodInfo.MakeGenericMethod(currentEntityType, relatedEntityType);
// Set up values
string navigator = property.Name;
var memberGetter = CreateGetterDelegate(currentEntityType, property.Name, relatedEntityType);
var memberSetter = CreateMemberSetterDelegate(currentEntityType, relatedEntityType, property.Name);
var propertyGetter = CreateGetterDelegate(currentEntityType, property.Name, relatedEntityType);
Func<IEntityRelation> relationCreatorFunc = () => RelationFactory.CreateFor(property.Relation.FirstRelation);
Func<IEntityRelation> secondRelationCreatorFunc = () => RelationFactory.CreateFor(property.Relation.SecondRelation);
string aliasStartEntity = property.Object.Name + "__";
string aliasIntermediateEntity = property.Relation.FirstRelation.EndObject.Name + "_";
object typeOfRelatedEntity = relatedEntityType;
object relatedEntityEnumTypeValue = property.Type.EntityType;
// Invoke
genericMethod.Invoke(this, new object[]
{
navigator,
memberGetter,
memberSetter,
propertyGetter,
relationCreatorFunc,
secondRelationCreatorFunc,
aliasStartEntity,
aliasIntermediateEntity,
typeOfRelatedEntity,
relatedEntityEnumTypeValue
});
}
else if (property.Relation.Type == RelationType.ManyToOne && property.Relation.RelationProperties.Count() > 0)
{
var relationProperties = new List<IRelationProperty>(property.Relation.RelationProperties);
foreach (IRelationProperty relationProperty in relationProperties)
{
if (relationProperty.StartProperty != null && relationProperty.EndProperty != null)
{
MethodInfo methodInfo = methods.Single(m =>
{
var p = m.GetParameters();
return p.Length == 12 &&
p[0].ParameterType == typeof(string) &&
p[1].ParameterType == typeof(string) &&
p[2].ParameterType.IsGenericType && p[2].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>) &&
p[3].ParameterType.IsGenericType && p[3].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>) &&
p[4].ParameterType.IsGenericType && p[4].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>) &&
p[5].ParameterType == typeof(IEntityRelation) &&
p[6].ParameterType == typeof(Func<IEntityRelation>) &&
p[7].ParameterType == typeof(string[]) &&
p[8].ParameterType == typeof(int[]) &&
p[9].ParameterType.IsGenericType && p[9].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>) &&
p[10].ParameterType == typeof(bool) &&
p[11].ParameterType == typeof(int);
});
MethodInfo genericMethod = methodInfo.MakeGenericMethod(currentEntityType, relatedEntityType);
string navigator = property.Name;
string navigatorInRelatedEntity = property.ReferencePropertyInRelatedObject != null ? property.ReferencePropertyInRelatedObject.ToString() : "";//dummy
var memberSetterFunc = CreateMemberSetterDelegate(currentEntityType, relatedEntityType, property.Name);
var memberGetterFunc = CreateGetterDelegate(currentEntityType, property.Name, relatedEntityType);
var propertySetterFunc = CreateMemberSetterDelegate(currentEntityType, relatedEntityType, property.Name);
IEntityRelation staticRelationship = RelationFactory.CreateFor(property.Relation);
Func<IEntityRelation> relationshipCreatorFunc = () => RelationFactory.CreateFor(property.Relation);
string[] forfFieldNames = null;
List<int> fieldarray = new List<int>();
foreach (var field in property.Relation.RelationProperties) { fieldarray.Add(field.StartProperty.FieldIndex); }
int[] fkFieldIndices = fieldarray.ToArray();
object forfPropertyChangedHandlerFunc = null; //CreatePropertyChangedHandlerGetterDelegate(currentEntityType, property.ToString());//dummy
bool containingIsOnPkSide = true;
object relatedEntityEnumTypeValue = property.Type.EntityType;
genericMethod.Invoke(this, new object[]
{
navigator,
navigatorInRelatedEntity,
memberSetterFunc,
memberGetterFunc,
propertySetterFunc,
staticRelationship,
relationshipCreatorFunc,
forfFieldNames,
fkFieldIndices,
forfPropertyChangedHandlerFunc,
containingIsOnPkSide,
relatedEntityEnumTypeValue
});
}
}
}
}
}
}
}
public static object CreateGetterDelegate(Type containingType, string propertyName, Type relatedType)
{
var param = LinqExpression.Parameter(containingType, "x");
var propertyInfo = containingType
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.FirstOrDefault(p => p.Name == propertyName)
?? throw new InvalidOperationException(
$"Property '{propertyName}' not found on type {containingType.FullName}.");
var propertyExpr = LinqExpression.Property(param, propertyInfo);
bool isCollection =
typeof(IEntityCollectionCore).IsAssignableFrom(propertyInfo.PropertyType)
|| (propertyInfo.PropertyType.IsGenericType &&
propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IBusinessObjectCollection<>));
Type returnType;
LinqExpression converted;
if (isCollection)
{
// Return as IEntityCollectionCore to match LLBLGen expectations
returnType = typeof(IEntityCollectionCore);
converted = LinqExpression.Convert(propertyExpr, returnType);
}
else
{
// Single entity: return actual relatedType (child entity)
returnType = relatedType;
converted = LinqExpression.Convert(propertyExpr, returnType);
}
var funcType = typeof(Func<,>).MakeGenericType(containingType, returnType);
return LinqExpression.Lambda(funcType, converted, param).Compile();
}
public static Delegate CreateSetterDelegate(Type containingType, Type relatedType, string propertyName)
{
var containingParam = LinqExpression.Parameter(containingType, "a");
var relatedParam = LinqExpression.Parameter(relatedType, "b");
// Find SetRelatedEntityProperty(string, IEntityCore) on BusinessObject / IEntityCore
var boType = typeof(BusinessObject);
var methodInfo = boType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.FirstOrDefault(m => m.Name == "SetRelatedEntityProperty"
&& m.GetParameters().Length == 2
&& m.GetParameters()[0].ParameterType == typeof(string)
&& typeof(IEntityCore).IsAssignableFrom(m.GetParameters()[1].ParameterType));
if (methodInfo == null)
throw new InvalidOperationException($"SetRelatedEntityProperty method not found on type '{boType.FullName}'.");
// Call a.SetRelatedEntityProperty("PropertyName", (IEntityCore)b)
var call = LinqExpression.Call(
LinqExpression.Convert(containingParam, boType), // ensure we invoke on BusinessObject
methodInfo,
LinqExpression.Constant(propertyName),
LinqExpression.Convert(relatedParam, typeof(IEntityCore))
);
var lambda = LinqExpression.Lambda(call, containingParam, relatedParam).Compile();
return lambda;
}
public static Delegate CreateMemberSetterDelegate(Type containingType, Type relatedType, string propertyName)
{
var propertyInfo = containingType
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.FirstOrDefault(p => p.Name == propertyName)
?? throw new InvalidOperationException(
$"Property '{propertyName}' not found on type {containingType.FullName}.");
bool isCollection = typeof(IEntityCollectionCore).IsAssignableFrom(propertyInfo.PropertyType)
|| (propertyInfo.PropertyType.IsGenericType);
if (!isCollection)
{
// reuse CreateSetterDelegate for single-valued navs
return CreateSetterDelegate(containingType, relatedType, propertyName);
}
// For collection navigators, the setter should be Action<TContaining, TChild>
var parentParam = LinqExpression.Parameter(containingType, "parent");
var childParam = LinqExpression.Parameter(relatedType, "child");
// SetRelatedEntityProperty on the child: child.SetRelatedEntityProperty("NavigatorName", parent);
var entityCoreType = typeof(IEntityCore);
var setRelatedOnChild = entityCoreType.GetMethod("SetRelatedEntityProperty",
BindingFlags.Instance | BindingFlags.Public);
if (setRelatedOnChild == null)
{
// fallback: search on BusinessObject
setRelatedOnChild = typeof(BusinessObject).GetMethod("SetRelatedEntityProperty",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
}
var call = LinqExpression.Call(
LinqExpression.Convert(childParam, setRelatedOnChild.DeclaringType),
setRelatedOnChild,
LinqExpression.Constant(propertyName),
LinqExpression.Convert(parentParam, typeof(IEntityCore)) // many implementations accept object for parent
);
var actionType = typeof(Action<,>).MakeGenericType(containingType, relatedType);
return LinqExpression.Lambda(actionType, call, parentParam, childParam).Compile();
}
What is the problem in this method I am getting error in unit test for complex property
You posted a lengthy piece of code and I have to figure out where some vague issue is?
I'm sorry but you're not even describing what the 'desired result' is. Have you tried stepping through the code with a debugger? Enabled tracing https://www.llblgen.com/Documentation/5.12/LLBLGen%20Pro%20RTF/Using%20the%20generated%20code/gencode_troubleshootingdebugging.htm to see what queries are generated?
Joined: 03-May-2024
Otis wrote:
You posted a lengthy piece of code and I have to figure out where some vague issue is?
I'm sorry but you're not even describing what the 'desired result' is. Have you tried stepping through the code with a debugger? Enabled tracing https://www.llblgen.com/Documentation/5.12/LLBLGen%20Pro%20RTF/Using%20the%20generated%20code/gencode_troubleshootingdebugging.htm to see what queries are generated?
So, the Idea here to replicate LLBLGen's inbuild EntityStaticMetaData constructor's AddNavigatorMetaData method initialization. This code works fine for Single Navigator property but fails for Collection/Complex Property to initialize properly. I just want you to analysis and suggest how should we achieve this replication for custom code.
For Example we have a property with one-to-many relation with ParentProperty (ReferencePropertyInRelatedObject) such as
public virtual IBusinessObjectCollection<NewEntity> ChildProperty { get { return (IBusinessObjectCollection<NewEntity>)GetPropertyValue("ChildProperty"); } }
is not initializing when we try to set the value through EntityStaticMetaData's AddNavigatorMetaData method.
when we are accessing this property I am getting NullReference Exception (Object Not set to an instance of an object)
Why would you want to replicate the AddNavigatorMetaData method? I'm not going to analyze pages of your code for a crash somewhere of which you don't specify where it happens