diff --git a/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs b/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs index 6b2c1749171..78249339d73 100644 --- a/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs +++ b/src/NHibernate.Test/MappingByCode/ExplicitMappingTests/DynamicComponentMappingTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Linq; using NHibernate.Cfg.MappingSchema; using NHibernate.Mapping.ByCode; @@ -20,6 +21,42 @@ public IDictionary Info } } + [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 => { }); + }); + + 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 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 => { }); + }); + + 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 WhenMapDynCompoThenMapItAndItsProperties() { diff --git a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/DynamicComponentCustomizer.cs b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/DynamicComponentCustomizer.cs index d409c6d57ee..a813f3fd765 100644 --- a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/DynamicComponentCustomizer.cs +++ b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/DynamicComponentCustomizer.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; namespace NHibernate.Mapping.ByCode.Impl.CustomizersImpl { @@ -18,6 +21,55 @@ public DynamicComponentCustomizer(IModelExplicitDeclarationsHolder explicitDecla explicitDeclarationsHolder.AddAsDynamicComponent(propertyPath.LocalMember, typeof(TComponent)); } + public DynamicComponentCustomizer(IDictionary template, IModelExplicitDeclarationsHolder explicitDeclarationsHolder, ICustomizersHolder customizersHolder, PropertyPath propertyPath) + : base(explicitDeclarationsHolder, customizersHolder, propertyPath) + { + if (propertyPath == null) + { + throw new ArgumentNullException("propertyPath"); + } + if (explicitDeclarationsHolder == null) + { + throw new ArgumentNullException("explicitDeclarationsHolder"); + } + + var componentType = this.CreateType(template); + + explicitDeclarationsHolder.AddAsDynamicComponent(propertyPath.LocalMember, componentType); + } + + private System.Type CreateType(IDictionary properties) + { + var assemblyName = new AssemblyName("MyDynamicAssembly"); + var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); + var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); + var typeBuilder = moduleBuilder.DefineType("MyDynamicType", TypeAttributes.Public | TypeAttributes.Serializable); + + foreach (var property in properties) + { + 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 System.Type[] { 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); + } + + var type = typeBuilder.CreateType(); + + return type; + } + #region IDynamicComponentMapper Members public void Access(Accessor accessor) diff --git a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/PropertyContainerCustomizer.cs b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/PropertyContainerCustomizer.cs index b3119f6244a..d2f031fa547 100644 --- a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/PropertyContainerCustomizer.cs +++ b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/PropertyContainerCustomizer.cs @@ -95,7 +95,14 @@ protected void RegisterComponentMapping(Action(Expression> property, TComponent dynamicComponentTemplate, Action> mapping) { - RegisterDynamicComponentMapping(property, mapping); + if (dynamicComponentTemplate is IDictionary) + { + RegisterDynamicComponentMapping(property, dynamicComponentTemplate as IDictionary, mapping); + } + else + { + RegisterDynamicComponentMapping(property, mapping); + } } protected virtual void RegisterDynamicComponentMapping(Expression> property, Action> mapping) @@ -105,6 +112,21 @@ protected virtual void RegisterDynamicComponentMapping(Expression(Expression> property, IDictionary template, Action> mapping) + { + MemberInfo member = TypeExtensions.DecodeMemberAccessExpression(property); + MemberInfo memberOf = TypeExtensions.DecodeMemberAccessExpressionOf(property); + RegisterDynamicComponentMapping(template, mapping, member, memberOf); + } + + protected void RegisterDynamicComponentMapping(IDictionary template, Action> mapping, params MemberInfo[] members) + { + foreach (var member in members) + { + mapping(new DynamicComponentCustomizer(template, explicitDeclarationsHolder, CustomizersHolder, new PropertyPath(PropertyPath, member))); + } + } + protected void RegisterDynamicComponentMapping(Action> mapping, params MemberInfo[] members) { foreach (var member in members) @@ -479,7 +501,15 @@ public void Component(string notVisiblePropertyOrFieldName, TCompone { MemberInfo member = GetPropertyOrFieldMatchingNameOrThrow(notVisiblePropertyOrFieldName); MemberInfo memberOf = member.GetMemberFromReflectedType(typeof(TEntity)); - RegisterDynamicComponentMapping(mapping, member, memberOf); + + if (dynamicComponentTemplate is IDictionary) + { + RegisterDynamicComponentMapping(dynamicComponentTemplate as IDictionary, mapping, member, memberOf); + } + else + { + RegisterDynamicComponentMapping(mapping, member, memberOf); + } } public void Any(string notVisiblePropertyOrFieldName, System.Type idTypeOfMetaType, Action mapping) where TProperty : class diff --git a/src/NHibernate/NHibernate.csproj b/src/NHibernate/NHibernate.csproj index 19b538de969..7950b5ab0a4 100644 --- a/src/NHibernate/NHibernate.csproj +++ b/src/NHibernate/NHibernate.csproj @@ -90,6 +90,7 @@ +