diff --git a/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs b/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs index 457107d5a4e..3b9bf5c07fd 100644 --- a/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs +++ b/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs @@ -6,7 +6,7 @@ using NHibernate.Mapping.ByCode; using NUnit.Framework; -namespace NHibernate.Test.MappingByCode.ExpliticMappingTests +namespace NHibernate.Test.MappingByCode.ExplicitMappingTests { [TestFixture] public class DynamicComponentMappingTests @@ -33,6 +33,141 @@ public IDictionary Info } } + private class PersonWithDynamicInfo + { + public int Id { get; set; } + public dynamic Info { get; set; } + } + + [Test] + public void WhenMapDynCompoByDictionaryThenMapItAndItsProperties() + { + //NH-3704 + var mapper = new ModelMapper(); + mapper.Class( + map => + { + map.Id(x => x.Id, idmap => { }); + map.Component( + x => x.Info, + new Dictionary + {{"MyInt", typeof(int)}, {"MyDate", typeof(DateTime)}}, + z => { z.Property("MyInt", pm => pm.Column("MY_COLUMN")); }); + }); + + var hbmMapping = mapper.CompileMappingFor(new[] { typeof(Person) }); + var hbmClass = hbmMapping.RootClasses[0]; + var hbmDynamicComponent = hbmClass.Properties.OfType().SingleOrDefault(); + Assert.That(hbmDynamicComponent, Is.Not.Null); + Assert.That( + hbmDynamicComponent.Properties.Select(x => x.Name), + Is.EquivalentTo(new[] { "MyInt", "MyDate" })); + } + + [Test] + public void WhenMapDynCompoByDictionaryThenMapItAndItsPropertiesGeneric() + { + //NH-3704 + var mapper = new ModelMapper(); + mapper.Class( + map => + { + map.Id(x => x.Id, idmap => { }); + map.Component( + x => x.Info, + new Dictionary + {{"MyInt", typeof(int)}, {"MyDate", typeof(DateTime)}}, + z => { z.Property("MyInt", pm => pm.Column("MY_COLUMN")); }); + }); + + var hbmMapping = mapper.CompileMappingFor(new[] { typeof(PersonWithGenericInfo) }); + var hbmClass = hbmMapping.RootClasses[0]; + var hbmDynamicComponent = hbmClass.Properties.OfType().SingleOrDefault(); + Assert.That(hbmDynamicComponent, Is.Not.Null); + Assert.That( + hbmDynamicComponent.Properties.Select(x => x.Name), + Is.EquivalentTo(new[] { "MyInt", "MyDate" })); + } + + [Test] + public void WhenMapDynCompoByDictionaryThenMapItAndItsPropertiesDynamic() + { + //NH-3704 + var mapper = new ModelMapper(); + mapper.Class( + map => + { + map.Id(x => x.Id, idmap => { }); + map.Component( + nameof(PersonWithDynamicInfo.Info), + new Dictionary + {{"MyInt", typeof(int)}, {"MyDate", typeof(DateTime)}}, + z => + { + z.Property("MyInt", pm => pm.Column("MY_COLUMN")); + z.Component("MyDate"); + }); + }); + + var hbmMapping = mapper.CompileMappingFor(new[] { typeof(PersonWithDynamicInfo) }); + var hbmClass = hbmMapping.RootClasses[0]; + var hbmDynamicComponent = hbmClass.Properties.OfType().SingleOrDefault(); + Assert.That(hbmDynamicComponent, Is.Not.Null); + Assert.That( + hbmDynamicComponent.Properties.Select(x => x.Name), + Is.EquivalentTo(new[] { "MyInt", "MyDate" })); + } + + [Test] + public void WhenMapPrivateDynCompoByDictionaryThenMapItAndItsProperties() + { + //NH-3704 + var mapper = new ModelMapper(); + mapper.Class( + map => + { + map.Id(x => x.Id, idmap => { }); + map.Component( + "Info", + new Dictionary + {{"MyInt", typeof(int)}, {"MyDate", typeof(DateTime)}}, + z => { z.Property("MyInt", pm => pm.Column("MY_COLUMN")); }); + }); + + var hbmMapping = mapper.CompileMappingFor(new[] { typeof(Person) }); + var hbmClass = hbmMapping.RootClasses[0]; + var hbmDynamicComponent = hbmClass.Properties.OfType().SingleOrDefault(); + Assert.That(hbmDynamicComponent, Is.Not.Null); + Assert.That( + hbmDynamicComponent.Properties.Select(x => x.Name), + Is.EquivalentTo(new[] { "MyInt", "MyDate" })); + } + + [Test] + public void WhenMapPrivateDynCompoByDictionaryThenMapItAndItsPropertiesGeneric() + { + //NH-3704 + var mapper = new ModelMapper(); + mapper.Class( + map => + { + map.Id(x => x.Id, idmap => { }); + map.Component( + "Info", + new Dictionary + {{"MyInt", typeof(int)}, {"MyDate", typeof(DateTime)}}, + z => { z.Property("MyInt", pm => pm.Column("MY_COLUMN")); }); + }); + + var hbmMapping = mapper.CompileMappingFor(new[] { typeof(PersonWithGenericInfo) }); + var hbmClass = hbmMapping.RootClasses[0]; + var hbmDynamicComponent = hbmClass.Properties.OfType().SingleOrDefault(); + Assert.That(hbmDynamicComponent, Is.Not.Null); + Assert.That( + hbmDynamicComponent.Properties.Select(x => x.Name), + Is.EquivalentTo(new[] { "MyInt", "MyDate" })); + } + [Test] public void WhenMapDynCompoThenMapItAndItsProperties() { diff --git a/src/NHibernate/Mapping/ByCode/IPlainPropertyContainerMapper.cs b/src/NHibernate/Mapping/ByCode/IPlainPropertyContainerMapper.cs index 9441c73676f..2565d66c4ca 100644 --- a/src/NHibernate/Mapping/ByCode/IPlainPropertyContainerMapper.cs +++ b/src/NHibernate/Mapping/ByCode/IPlainPropertyContainerMapper.cs @@ -42,10 +42,29 @@ public interface IBasePlainPropertyContainerMapper : IMinimalPlainPr { void Component(Expression> property, Action> mapping); void Component(Expression> property); + /// + /// Maps a non-generic dictionary property as a dynamic component. + /// + /// The property to map. + /// The template for the component. It should either be a (usually + /// anonymous) type having the same properties than the component, or an + /// IDictionary<string, System.Type> of property names with their type. + /// The mapping of the component. + /// The type of the template. void Component(Expression> property, TComponent dynamicComponentTemplate, Action> mapping); void Component(string notVisiblePropertyOrFieldName, Action> mapping); void Component(string notVisiblePropertyOrFieldName); + /// + /// Maps a property or field as a dynamic component. The property can be a C# dynamic or a dictionary of + /// property names to their value. + /// + /// The property or field name to map. + /// The template for the component. It should either be a (usually + /// anonymous) type having the same properties than the component, or an + /// IDictionary<string, System.Type> of property names with their type. + /// The mapping of the component. + /// The type of the template. void Component(string notVisiblePropertyOrFieldName, TComponent dynamicComponentTemplate, Action> mapping); void Any(Expression> property, System.Type idTypeOfMetaType, Action mapping) where TProperty : class; @@ -61,6 +80,17 @@ public interface IPlainPropertyContainerMapper : IBasePlainPropertyC public static class BasePlainPropertyContainerMapperExtensions { //6.0 TODO: Merge into IBasePlainPropertyContainerMapper<> interface + /// + /// Maps a generic IDictionary<string, object> property as a dynamic component. + /// + /// The mapper. + /// The property to map. + /// The template for the component. It should either be a (usually + /// anonymous) type having the same properties than the component, or an + /// IDictionary<string, System.Type> of property names with their type. + /// The mapping of the component. + /// The type of the mapped class. + /// The type of the template. public static void Component( this IBasePlainPropertyContainerMapper mapper, Expression>> property, diff --git a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/ComponentCustomizer.cs b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/ComponentCustomizer.cs index 470b5a7b020..b8d96369928 100644 --- a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/ComponentCustomizer.cs +++ b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/ComponentCustomizer.cs @@ -39,7 +39,7 @@ public void Parent(Expression> parent) wh public void Parent(string notVisiblePropertyOrFieldName, Action parentMapping) { - MemberInfo member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); + MemberInfo member = GetRequiredPropertyOrFieldByName(notVisiblePropertyOrFieldName); AddCustomizer(m => m.Parent(member, parentMapping)); } diff --git a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/DynamicComponentCustomizer.cs b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/DynamicComponentCustomizer.cs index d409c6d57ee..fdeff23a056 100644 --- a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/DynamicComponentCustomizer.cs +++ b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/DynamicComponentCustomizer.cs @@ -1,21 +1,40 @@ using System; +using System.Reflection; namespace NHibernate.Mapping.ByCode.Impl.CustomizersImpl { - public class DynamicComponentCustomizer : PropertyContainerCustomizer, IDynamicComponentMapper + public class DynamicComponentCustomizer : PropertyContainerCustomizer, + IDynamicComponentMapper { - public DynamicComponentCustomizer(IModelExplicitDeclarationsHolder explicitDeclarationsHolder, ICustomizersHolder customizersHolder, PropertyPath propertyPath) + private readonly System.Type _componentType; + + public DynamicComponentCustomizer( + IModelExplicitDeclarationsHolder explicitDeclarationsHolder, + ICustomizersHolder customizersHolder, + PropertyPath propertyPath) + : this(typeof(TComponent), explicitDeclarationsHolder, customizersHolder, propertyPath) + { + } + + internal DynamicComponentCustomizer( + System.Type componentType, + IModelExplicitDeclarationsHolder explicitDeclarationsHolder, + ICustomizersHolder customizersHolder, + PropertyPath propertyPath) : base(explicitDeclarationsHolder, customizersHolder, propertyPath) { if (propertyPath == null) { - throw new ArgumentNullException("propertyPath"); + throw new ArgumentNullException(nameof(propertyPath)); } + if (explicitDeclarationsHolder == null) { - throw new ArgumentNullException("explicitDeclarationsHolder"); + throw new ArgumentNullException(nameof(explicitDeclarationsHolder)); } - explicitDeclarationsHolder.AddAsDynamicComponent(propertyPath.LocalMember, typeof(TComponent)); + + _componentType = componentType; + explicitDeclarationsHolder.AddAsDynamicComponent(propertyPath.LocalMember, _componentType); } #region IDynamicComponentMapper Members @@ -51,5 +70,15 @@ public void Unique(bool unique) } #endregion + + protected override MemberInfo GetRequiredPropertyOrFieldByName(string memberName) + { + var result = _componentType.GetPropertyOrFieldMatchingName(memberName); + if (result == null) + { + throw new MappingException(string.Format("Member not found. The member '{0}' does not exists in type {1}", memberName, _componentType.FullName)); + } + return result; + } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/PropertyContainerCustomizer.cs b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/PropertyContainerCustomizer.cs index b76e28f6eee..41bd43ad3f9 100644 --- a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/PropertyContainerCustomizer.cs +++ b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/PropertyContainerCustomizer.cs @@ -1,9 +1,9 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Reflection.Emit; namespace NHibernate.Mapping.ByCode.Impl.CustomizersImpl { @@ -55,7 +55,7 @@ protected virtual void RegisterNoVisiblePropertyMapping(string notVisiblePropert { // even seems repetitive, before unify this registration with the registration using Expression take in account that reflection operations // done unsing expressions are faster than those done with pure reflection. - MemberInfo member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); + MemberInfo member = GetRequiredPropertyOrFieldByName(notVisiblePropertyOrFieldName); MemberInfo memberOf = member.GetMemberFromReflectedType(typeof(TEntity)); RegistePropertyMapping(mapping, memberOf); } @@ -94,14 +94,48 @@ protected void RegisterComponentMapping(Action + /// Maps a non-generic dictionary property as a dynamic component. + /// + /// The property to map. + /// The template for the component. It should either be a (usually + /// anonymous) type having the same properties than the component, or an + /// IDictionary<string, System.Type> of property names with their type. + /// The mapping of the component. + /// The type of the template. public void Component(Expression> property, TComponent dynamicComponentTemplate, Action> mapping) { - RegisterDynamicComponentMapping(property, mapping); + if (dynamicComponentTemplate is IEnumerable> template) + { + var componentType = CreateDynamicComponentTypeFromTemplate(template); + RegisterDynamicComponentMapping(property, componentType, mapping); + } + else + { + RegisterDynamicComponentMapping(property, mapping); + } } + /// + /// Maps a generic IDictionary<string, object> property as a dynamic component. + /// + /// The property to map. + /// The template for the component. It should either be a (usually + /// anonymous) type having the same properties than the component, or an + /// IDictionary<string, System.Type> of property names with their type. + /// The mapping of the component. + /// The type of the template. public void Component(Expression>> property, TComponent dynamicComponentTemplate, Action> mapping) where TComponent : class { - RegisterDynamicComponentMapping(property, mapping); + if (dynamicComponentTemplate is IEnumerable> template) + { + var componentType = CreateDynamicComponentTypeFromTemplate(template); + RegisterDynamicComponentMapping(property, componentType, mapping); + } + else + { + RegisterDynamicComponentMapping(property, mapping); + } } protected virtual void RegisterDynamicComponentMapping(Expression> property, Action> mapping) @@ -110,6 +144,13 @@ protected virtual void RegisterDynamicComponentMapping(Expression(Expression> property, System.Type componentType, Action> mapping) + { + MemberInfo member = TypeExtensions.DecodeMemberAccessExpression(property); + MemberInfo memberOf = TypeExtensions.DecodeMemberAccessExpressionOf(property); + RegisterDynamicComponentMapping(componentType, mapping, member, memberOf); + } protected virtual void RegisterDynamicComponentMapping(Expression>> property, Action> mapping) where TComponent : class { @@ -118,6 +159,21 @@ protected virtual void RegisterDynamicComponentMapping(Expression(Expression>> property, System.Type componentType, Action> mapping) + { + MemberInfo member = TypeExtensions.DecodeMemberAccessExpression(property); + MemberInfo memberOf = TypeExtensions.DecodeMemberAccessExpressionOf(property); + RegisterDynamicComponentMapping(componentType, mapping, member, memberOf); + } + + protected void RegisterDynamicComponentMapping(System.Type componentType, Action> mapping, params MemberInfo[] members) + { + foreach (var member in members) + { + mapping(new DynamicComponentCustomizer(componentType, explicitDeclarationsHolder, CustomizersHolder, new PropertyPath(PropertyPath, member))); + } + } + protected void RegisterDynamicComponentMapping(Action> mapping, params MemberInfo[] members) { foreach (var member in members) @@ -126,6 +182,50 @@ protected void RegisterDynamicComponentMapping(Action> template) + { + var assemblyName = new AssemblyName("MyDynamicAssembly"); + var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); + var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); + var typeBuilder = moduleBuilder.DefineType( + "MyDynamicType", + TypeAttributes.Public | TypeAttributes.Serializable); + + foreach (var property in template) + { + var propertyBuilder = typeBuilder.DefineProperty( + property.Key, + PropertyAttributes.HasDefault, + property.Value, + null); + var getMethodBuilder = typeBuilder.DefineMethod( + "get_" + property.Key, + MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, + property.Value, + System.Type.EmptyTypes); + var setMethodBuilder = typeBuilder.DefineMethod( + "set_" + property.Key, + MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, + typeof(void), + new[] { property.Value }); + + var getILGenerator = getMethodBuilder.GetILGenerator(); + getILGenerator.Emit(OpCodes.Ldarg_0); + getILGenerator.Emit(OpCodes.Nop); + getILGenerator.Emit(OpCodes.Ret); + + var setILGenerator = setMethodBuilder.GetILGenerator(); + setILGenerator.Emit(OpCodes.Ldarg_0); + setILGenerator.Emit(OpCodes.Ldarg_1); + setILGenerator.Emit(OpCodes.Ret); + + propertyBuilder.SetGetMethod(getMethodBuilder); + propertyBuilder.SetSetMethod(setMethodBuilder); + } + + return typeBuilder.CreateTypeInfo(); + } + public void ManyToOne(Expression> property, Action mapping) where TProperty : class { @@ -165,7 +265,7 @@ public void OneToOne(Expression> property, A public void OneToOne(string notVisiblePropertyOrFieldName, Action> mapping) where TProperty : class { - var member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); + var member = GetRequiredPropertyOrFieldByName(notVisiblePropertyOrFieldName); var propertyOrFieldType = member.GetPropertyOrFieldType(); if (typeof(TProperty) != propertyOrFieldType) { @@ -362,7 +462,7 @@ protected virtual void RegisterIdBagMapping(Action(string notVisiblePropertyOrFieldName, Action> collectionMapping, Action> mapping) { - MemberInfo member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); + MemberInfo member = GetRequiredPropertyOrFieldByName(notVisiblePropertyOrFieldName); AssertCollectionElementType(notVisiblePropertyOrFieldName, member); MemberInfo memberOf = member.GetMemberFromReflectedType(typeof(TEntity)); @@ -392,7 +492,7 @@ public void Set(string notVisiblePropertyOrFieldName, Action(string notVisiblePropertyOrFieldName, Action> collectionMapping, Action> mapping) { - MemberInfo member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); + MemberInfo member = GetRequiredPropertyOrFieldByName(notVisiblePropertyOrFieldName); AssertCollectionElementType(notVisiblePropertyOrFieldName, member); MemberInfo memberOf = member.GetMemberFromReflectedType(typeof(TEntity)); @@ -406,7 +506,7 @@ public void Bag(string notVisiblePropertyOrFieldName, Action(string notVisiblePropertyOrFieldName, Action> collectionMapping, Action> mapping) { - MemberInfo member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); + MemberInfo member = GetRequiredPropertyOrFieldByName(notVisiblePropertyOrFieldName); AssertCollectionElementType(notVisiblePropertyOrFieldName, member); MemberInfo memberOf = member.GetMemberFromReflectedType(typeof(TEntity)); @@ -420,7 +520,7 @@ public void List(string notVisiblePropertyOrFieldName, Action(string notVisiblePropertyOrFieldName, Action> collectionMapping, Action> keyMapping, Action> mapping) { - MemberInfo member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); + MemberInfo member = GetRequiredPropertyOrFieldByName(notVisiblePropertyOrFieldName); var propertyOrFieldType = member.GetPropertyOrFieldType(); var keyType = propertyOrFieldType.DetermineDictionaryKeyType(); var collectionElementType = propertyOrFieldType.DetermineDictionaryValueType(); @@ -445,7 +545,7 @@ public void Map(string notVisiblePropertyOrFieldName, Action(string notVisiblePropertyOrFieldName, Action> collectionMapping, Action> mapping) { - MemberInfo member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); + MemberInfo member = GetRequiredPropertyOrFieldByName(notVisiblePropertyOrFieldName); AssertCollectionElementType(notVisiblePropertyOrFieldName, member); MemberInfo memberOf = member.GetMemberFromReflectedType(typeof(TEntity)); @@ -459,7 +559,7 @@ public void IdBag(string notVisiblePropertyOrFieldName, Action(string notVisiblePropertyOrFieldName, Action mapping) where TProperty : class { - MemberInfo member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); + MemberInfo member = GetRequiredPropertyOrFieldByName(notVisiblePropertyOrFieldName); var propertyOrFieldType = member.GetPropertyOrFieldType(); if (!typeof(TProperty).Equals(propertyOrFieldType)) { @@ -472,7 +572,7 @@ public void ManyToOne(string notVisiblePropertyOrFieldName, Action(string notVisiblePropertyOrFieldName, Action> mapping) { - MemberInfo member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); + MemberInfo member = GetRequiredPropertyOrFieldByName(notVisiblePropertyOrFieldName); var propertyOrFieldType = member.GetPropertyOrFieldType(); if (!typeof(TComponent).Equals(propertyOrFieldType)) { @@ -488,16 +588,35 @@ public void Component(string notVisiblePropertyOrFieldName) Component(notVisiblePropertyOrFieldName, x => { }); } + /// + /// Maps a property or field as a dynamic component. The property can be a C# dynamic or a dictionary of + /// property names to their value. + /// + /// The property or field name to map. + /// The template for the component. It should either be a (usually + /// anonymous) type having the same properties than the component, or an + /// IDictionary<string, System.Type> of property names with their type. + /// The mapping of the component. + /// The type of the template. public void Component(string notVisiblePropertyOrFieldName, TComponent dynamicComponentTemplate, Action> mapping) { - MemberInfo member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); + MemberInfo member = GetRequiredPropertyOrFieldByName(notVisiblePropertyOrFieldName); MemberInfo memberOf = member.GetMemberFromReflectedType(typeof(TEntity)); - RegisterDynamicComponentMapping(mapping, member, memberOf); + + if (dynamicComponentTemplate is IEnumerable> template) + { + var componentType = CreateDynamicComponentTypeFromTemplate(template); + RegisterDynamicComponentMapping(componentType, mapping, member, memberOf); + } + else + { + RegisterDynamicComponentMapping(mapping, member, memberOf); + } } public void Any(string notVisiblePropertyOrFieldName, System.Type idTypeOfMetaType, Action mapping) where TProperty : class { - MemberInfo member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); + MemberInfo member = GetRequiredPropertyOrFieldByName(notVisiblePropertyOrFieldName); var propertyOrFieldType = member.GetPropertyOrFieldType(); if (!typeof(TProperty).Equals(propertyOrFieldType)) { @@ -508,6 +627,14 @@ public void Any(string notVisiblePropertyOrFieldName, System.Type idT RegisterAnyMapping(mapping, idTypeOfMetaType, member, memberOf); } + protected virtual MemberInfo GetRequiredPropertyOrFieldByName(string memberName) + { +#pragma warning disable 618 + return GetPropertyOrFieldMatchingNameOrThrow(memberName); +#pragma warning restore 618 + } + + [Obsolete("Please use GetRequiredPropertyOrFieldByName instead.")] public static MemberInfo GetPropertyOrFieldMatchingNameOrThrow(string memberName) { var result = typeof(TEntity).GetPropertyOrFieldMatchingName(memberName);