From: <fab...@us...> - 2011-05-24 12:18:35
|
Revision: 5869 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5869&view=rev Author: fabiomaulo Date: 2011-05-24 12:18:28 +0000 (Tue, 24 May 2011) Log Message: ----------- Fix NH-2728 Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Mapping/ByCode/ExplicitlyDeclaredModel.cs trunk/nhibernate/src/NHibernate/Mapping/ByCode/IModelInspector.cs trunk/nhibernate/src/NHibernate/Mapping/ByCode/Impl/CollectionElementRelation.cs trunk/nhibernate/src/NHibernate/Mapping/ByCode/ModelMapper.cs trunk/nhibernate/src/NHibernate/Mapping/ByCode/SimpleModelInspector.cs trunk/nhibernate/src/NHibernate/NHibernate.csproj trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj Added Paths: ----------- trunk/nhibernate/src/NHibernate/Mapping/ByCode/Impl/ManyToAnyMapper.cs trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/ trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/ trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/Cat.cs trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/Dog.cs trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/IAnimal.cs trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/SampleTest.cs trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/Toy.cs Modified: trunk/nhibernate/src/NHibernate/Mapping/ByCode/ExplicitlyDeclaredModel.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Mapping/ByCode/ExplicitlyDeclaredModel.cs 2011-05-24 05:29:15 UTC (rev 5868) +++ trunk/nhibernate/src/NHibernate/Mapping/ByCode/ExplicitlyDeclaredModel.cs 2011-05-24 12:18:28 UTC (rev 5869) @@ -66,6 +66,11 @@ return OneToManyRelations.Contains(member); } + public bool IsManyToAny(MemberInfo member) + { + return ManyToAnyRelations.Contains(member); + } + public virtual bool IsAny(MemberInfo member) { return Any.Contains(member); Modified: trunk/nhibernate/src/NHibernate/Mapping/ByCode/IModelInspector.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Mapping/ByCode/IModelInspector.cs 2011-05-24 05:29:15 UTC (rev 5868) +++ trunk/nhibernate/src/NHibernate/Mapping/ByCode/IModelInspector.cs 2011-05-24 12:18:28 UTC (rev 5869) @@ -18,6 +18,8 @@ bool IsManyToOne(MemberInfo member); bool IsManyToMany(MemberInfo member); bool IsOneToMany(MemberInfo member); + bool IsManyToAny(MemberInfo member); + bool IsAny(MemberInfo member); bool IsPersistentId(MemberInfo member); Modified: trunk/nhibernate/src/NHibernate/Mapping/ByCode/Impl/CollectionElementRelation.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Mapping/ByCode/Impl/CollectionElementRelation.cs 2011-05-24 05:29:15 UTC (rev 5868) +++ trunk/nhibernate/src/NHibernate/Mapping/ByCode/Impl/CollectionElementRelation.cs 2011-05-24 12:18:28 UTC (rev 5869) @@ -48,7 +48,9 @@ public void ManyToAny(System.Type idTypeOfMetaType, Action<IManyToAnyMapper> mapping) { - throw new NotImplementedException(); + var hbm = new HbmManyToAny(); + mapping(new ManyToAnyMapper(collectionElementType, idTypeOfMetaType, hbm, mapDoc)); + elementRelationshipAssing(hbm); } #endregion Added: trunk/nhibernate/src/NHibernate/Mapping/ByCode/Impl/ManyToAnyMapper.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Mapping/ByCode/Impl/ManyToAnyMapper.cs (rev 0) +++ trunk/nhibernate/src/NHibernate/Mapping/ByCode/Impl/ManyToAnyMapper.cs 2011-05-24 12:18:28 UTC (rev 5869) @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Type; + +namespace NHibernate.Mapping.ByCode.Impl +{ + public class ManyToAnyMapper: IManyToAnyMapper + { + private const string DefaultIdColumnNameWhenNoProperty = "ReferencedId"; + private const string DefaultMetaColumnNameWhenNoProperty = "ReferencedClass"; + private readonly System.Type elementType; + private readonly System.Type foreignIdType; + private readonly ColumnMapper classColumnMapper; + private readonly ColumnMapper idColumnMapper; + + private readonly HbmManyToAny manyToAny; + private readonly HbmMapping mapDoc; + + public ManyToAnyMapper(System.Type elementType, System.Type foreignIdType, HbmManyToAny manyToAny, HbmMapping mapDoc) + { + if (elementType == null) + { + throw new ArgumentNullException("elementType"); + } + if (foreignIdType == null) + { + throw new ArgumentNullException("foreignIdType"); + } + if (manyToAny == null) + { + throw new ArgumentNullException("manyToAny"); + } + this.elementType = elementType; + this.foreignIdType = foreignIdType; + this.manyToAny = manyToAny; + this.mapDoc = mapDoc; + this.manyToAny.idtype = this.foreignIdType.GetNhTypeName(); + var idHbmColumn = new HbmColumn(); + idColumnMapper = new ColumnMapper(idHbmColumn, DefaultIdColumnNameWhenNoProperty); + var classHbmColumn = new HbmColumn(); + classColumnMapper = new ColumnMapper(classHbmColumn, DefaultMetaColumnNameWhenNoProperty); + manyToAny.column = new[] { classHbmColumn, idHbmColumn }; + } + + public void MetaType(IType metaType) + { + if (metaType != null) + { + CheckMetaTypeImmutability(metaType.Name); + manyToAny.metatype = metaType.Name; + } + } + + public void MetaType<TMetaType>() + { + MetaType(typeof(TMetaType)); + } + + public void MetaType(System.Type metaType) + { + if (metaType != null) + { + string nhTypeName = metaType.GetNhTypeName(); + CheckMetaTypeImmutability(nhTypeName); + manyToAny.metatype = nhTypeName; + } + } + + public void IdType(IType idType) + { + if (idType != null) + { + CheckIdTypeImmutability(idType.Name); + manyToAny.idtype = idType.Name; + } + } + + public void IdType<TIdType>() + { + IdType(typeof(TIdType)); + } + + public void IdType(System.Type idType) + { + if (idType != null) + { + string nhTypeName = idType.GetNhTypeName(); + CheckIdTypeImmutability(nhTypeName); + manyToAny.idtype = nhTypeName; + } + } + + public void Columns(Action<IColumnMapper> idColumnMapping, Action<IColumnMapper> classColumnMapping) + { + idColumnMapping(idColumnMapper); + classColumnMapping(classColumnMapper); + } + + public void MetaValue(object value, System.Type entityType) + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + if (entityType == null) + { + throw new ArgumentNullException("entityType"); + } + if (value is System.Type) + { + throw new ArgumentOutOfRangeException("value", "System.Type is invalid meta-type (you don't need to set meta-values)."); + } + if(!elementType.IsAssignableFrom(entityType)) + { + throw new ArgumentOutOfRangeException("entityType", string.Format("A {0} is not assignable to the collection's elements which type is {1}", entityType, elementType)); + } + System.Type metavalueType = value.GetType(); + if (manyToAny.metavalue == null) + { + manyToAny.metavalue = new HbmMetaValue[0]; + } + Dictionary<string, string> values = manyToAny.metavalue.ToDictionary(mv => mv.value, mv => mv.@class); + MetaType(metavalueType); + string newClassMetavalue = entityType.GetShortClassName(mapDoc); + string metavalueKey = value.ToString(); + string existingClassMetavalue; + if (values.TryGetValue(metavalueKey, out existingClassMetavalue) && existingClassMetavalue != newClassMetavalue) + { + throw new ArgumentException( + string.Format( + "Can't set two different classes for same meta-value (meta-value='{0}' old-class:'{1}' new-class='{2}')", + metavalueKey, existingClassMetavalue, newClassMetavalue)); + } + values[metavalueKey] = newClassMetavalue; + manyToAny.metavalue = values.Select(vd => new HbmMetaValue { value = vd.Key, @class = vd.Value }).ToArray(); + } + + private void CheckMetaTypeImmutability(string nhTypeName) + { + if (manyToAny.metavalue != null && manyToAny.metavalue.Length > 0 && manyToAny.metatype != nhTypeName) + { + throw new ArgumentException(string.Format("Can't change the meta-type (was '{0}' trying to change to '{1}')", manyToAny.metatype, nhTypeName)); + } + } + + private void CheckIdTypeImmutability(string nhTypeName) + { + if (manyToAny.metavalue != null && manyToAny.metavalue.Length > 0 && manyToAny.idtype != nhTypeName) + { + throw new ArgumentException(string.Format("Can't change the id-type after add meta-values (was '{0}' trying to change to '{1}')", manyToAny.idtype, nhTypeName)); + } + } + } +} \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Mapping/ByCode/ModelMapper.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Mapping/ByCode/ModelMapper.cs 2011-05-24 05:29:15 UTC (rev 5868) +++ trunk/nhibernate/src/NHibernate/Mapping/ByCode/ModelMapper.cs 2011-05-24 12:18:28 UTC (rev 5869) @@ -1264,6 +1264,10 @@ { return new ComponentRelationMapper(property, ownerType, collectionElementType, membersProvider, modelInspector, customizerHolder, this); } + if (modelInspector.IsManyToAny(property)) + { + return new ManyToAnyRelationMapper(propertyPath, customizerHolder, this); + } return new ElementRelationMapper(propertyPath, customizerHolder, this); } @@ -1619,6 +1623,35 @@ #endregion + #region Nested type: ManyToAnyRelationMapper + + private class ManyToAnyRelationMapper : ICollectionElementRelationMapper + { + private readonly ICustomizersHolder customizersHolder; + private readonly ModelMapper modelMapper; + private readonly PropertyPath propertyPath; + + public ManyToAnyRelationMapper(PropertyPath propertyPath, ICustomizersHolder customizersHolder, ModelMapper modelMapper) + { + this.propertyPath = propertyPath; + this.customizersHolder = customizersHolder; + this.modelMapper = modelMapper; + } + + #region Implementation of ICollectionElementRelationMapper + + public void Map(ICollectionElementRelation relation) + { + relation.ManyToAny(typeof(int), x => customizersHolder.InvokeCustomizers(propertyPath, x)); + } + + public void MapCollectionProperties(ICollectionPropertiesMapper mapped) { } + + #endregion + } + + #endregion + #region Nested type: OneToManyRelationMapper private class OneToManyRelationMapper : ICollectionElementRelationMapper Modified: trunk/nhibernate/src/NHibernate/Mapping/ByCode/SimpleModelInspector.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Mapping/ByCode/SimpleModelInspector.cs 2011-05-24 05:29:15 UTC (rev 5868) +++ trunk/nhibernate/src/NHibernate/Mapping/ByCode/SimpleModelInspector.cs 2011-05-24 12:18:28 UTC (rev 5869) @@ -78,6 +78,11 @@ return OneToManyRelations.Contains(member); } + public bool IsManyToAny(MemberInfo member) + { + return ManyToAnyRelations.Contains(member); + } + public bool IsAny(MemberInfo member) { return Any.Contains(member); @@ -173,6 +178,7 @@ private Func<MemberInfo, bool, bool> isDynamicComponent = (m, declared) => declared; private Func<MemberInfo, bool, bool> isAny = (m, declared) => declared; private Func<MemberInfo, bool, bool> isManyToMany = (m, declared) => declared; + private Func<MemberInfo, bool, bool> isManyToAny = (m, declared) => declared; private Func<MemberInfo, bool, bool> isManyToOne; private Func<MemberInfo, bool, bool> isMemberOfNaturalId = (m, declared) => declared; private Func<MemberInfo, bool, bool> isOneToMany; @@ -713,6 +719,12 @@ return isOneToMany(member, declaredResult); } + bool IModelInspector.IsManyToAny(MemberInfo member) + { + bool declaredResult = DeclaredPolymorphicMatch(member, m => declaredModel.IsManyToAny(m)); + return isManyToAny(member, declaredResult); + } + bool IModelInspector.IsAny(MemberInfo member) { bool declaredResult = DeclaredPolymorphicMatch(member, m => declaredModel.IsAny(m)); @@ -910,6 +922,15 @@ isOneToMany = match; } + public void IsManyToAny(Func<MemberInfo, bool, bool> match) + { + if (match == null) + { + return; + } + isManyToAny = match; + } + public void IsAny(Func<MemberInfo, bool, bool> match) { if (match == null) Modified: trunk/nhibernate/src/NHibernate/NHibernate.csproj =================================================================== --- trunk/nhibernate/src/NHibernate/NHibernate.csproj 2011-05-24 05:29:15 UTC (rev 5868) +++ trunk/nhibernate/src/NHibernate/NHibernate.csproj 2011-05-24 12:18:28 UTC (rev 5869) @@ -311,6 +311,7 @@ <Compile Include="Mapping\ByCode\Impl\CustomizersImpl\DynamicComponentCustomizer.cs" /> <Compile Include="Mapping\ByCode\Impl\CustomizersImpl\ManyToAnyCustomizer.cs" /> <Compile Include="Mapping\ByCode\Impl\DynamicComponentMapper.cs" /> + <Compile Include="Mapping\ByCode\Impl\ManyToAnyMapper.cs" /> <Compile Include="Mapping\ByCode\PropertyGeneration.cs" /> <Compile Include="Mapping\ByCode\PropertyToField.cs" /> <Compile Include="Mapping\ByCode\SimpleModelInspector.cs" /> Added: trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/Cat.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/Cat.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/Cat.cs 2011-05-24 12:18:28 UTC (rev 5869) @@ -0,0 +1,11 @@ +using System; + +namespace NHibernate.Test.MappingByCode.IntegrationTests.NH2728 +{ + public class Cat : IAnimal + { + public virtual int Id { get; set; } + public virtual string Name { get; set; } + public virtual DateTime Born { get; set; } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/Dog.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/Dog.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/Dog.cs 2011-05-24 12:18:28 UTC (rev 5869) @@ -0,0 +1,10 @@ + +namespace NHibernate.Test.MappingByCode.IntegrationTests.NH2728 +{ + public class Dog : IAnimal + { + public virtual int Id { get; set; } + public virtual string Name { get; set; } + public virtual int Walks { get; set; } + } +} Added: trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/IAnimal.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/IAnimal.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/IAnimal.cs 2011-05-24 12:18:28 UTC (rev 5869) @@ -0,0 +1,9 @@ + +namespace NHibernate.Test.MappingByCode.IntegrationTests.NH2728 +{ + public interface IAnimal + { + int Id { get; set; } + string Name { get; set; } + } +} Added: trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/SampleTest.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/SampleTest.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/SampleTest.cs 2011-05-24 12:18:28 UTC (rev 5869) @@ -0,0 +1,111 @@ +using System; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NUnit.Framework; + +namespace NHibernate.Test.MappingByCode.IntegrationTests.NH2728 +{ + [TestFixture] + public class SampleTest : TestCaseMappingByCode + { + protected override HbmMapping GetMappings() + { + var mapper = new ConventionModelMapper(); + // Working Example + //mapper.Class<Toy>(rc => rc.Set(x => x.Animals, cmap => { }, rel => rel.ManyToAny<int>(meta => + // { + // meta.MetaValue(1, typeof (Cat)); + // meta.MetaValue(2, typeof (Dog)); + // }))); + + // User needs + mapper.Class<Toy>(rc => rc.Set(x => x.Animals, cmap => + { + cmap.Table("Animals_Toys"); + cmap.Key(km => km.Column("Cat_Id")); + }, rel => rel.ManyToAny<int>(meta => + { + meta.MetaValue(1, typeof(Cat)); + meta.MetaValue(2, typeof(Dog)); + meta.Columns(cid => + { + cid.Name("Animal_Id"); + cid.NotNullable(true); + }, ctype => + { + ctype.Name("Animal_Type"); + ctype.NotNullable(true); + }); + }))); + var mappings = mapper.CompileMappingFor(new[] { typeof(Cat), typeof(Dog), typeof(Toy) }); + //Console.WriteLine(mappings.AsString()); // <=== uncomment this line to see the XML mapping + return mappings; + } + + protected override void OnSetUp() + { + base.OnSetUp(); + using (ISession session = this.OpenSession()) + { + var cat1 = new Cat() + { + Id = 1, + Name = "Cat 1", + Born = DateTime.Now, + }; + session.Save(cat1); + + var cat2 = new Cat() + { + Id = 2, + Name = "Cat 2", + Born = DateTime.Now, + }; + session.Save(cat2); + + var dog1 = new Dog() + { + Id = 1, + Name = "Dog 1", + Walks = 11, + }; + session.Save(dog1); + + var toy1 = new Toy() + { + Id = 1, + Name = "Toy 1", + }; + toy1.Animals.Add(cat1); + toy1.Animals.Add(cat2); + toy1.Animals.Add(dog1); + session.Save(toy1); + + session.Flush(); + } + } + + protected override void OnTearDown() + { + base.OnTearDown(); + using (ISession session = this.OpenSession()) + { + string hql = "from System.Object"; + session.Delete(hql); + session.Flush(); + } + } + + [Test] + public void ShouldBeAbleToGetFromToyToAnimals() + { + using (ISession session = this.OpenSession()) + { + var toy1 = session.Get<Toy>(1); + + Assert.AreEqual(1, toy1.Id); + Assert.AreEqual(3, toy1.Animals.Count); + } + } + } +} Added: trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/Toy.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/Toy.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/MappingByCode/IntegrationTests/NH2728/Toy.cs 2011-05-24 12:18:28 UTC (rev 5869) @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace NHibernate.Test.MappingByCode.IntegrationTests.NH2728 +{ + public class Toy + { + public Toy() + { + Animals = new HashSet<IAnimal>(); + } + + public virtual int Id { get; set; } + public virtual string Name { get; set; } + + public virtual ICollection<IAnimal> Animals { get; protected set; } + } +} Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2011-05-24 05:29:15 UTC (rev 5868) +++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2011-05-24 12:18:28 UTC (rev 5869) @@ -552,6 +552,11 @@ <Compile Include="MappingByCode\ExpliticMappingTests\SubclassPropertiesSplitsTests.cs" /> <Compile Include="MappingByCode\ExpliticMappingTests\VersionTests.cs" /> <Compile Include="MappingByCode\For.cs" /> + <Compile Include="MappingByCode\IntegrationTests\NH2728\Cat.cs" /> + <Compile Include="MappingByCode\IntegrationTests\NH2728\Dog.cs" /> + <Compile Include="MappingByCode\IntegrationTests\NH2728\IAnimal.cs" /> + <Compile Include="MappingByCode\IntegrationTests\NH2728\SampleTest.cs" /> + <Compile Include="MappingByCode\IntegrationTests\NH2728\Toy.cs" /> <Compile Include="MappingByCode\MappersTests\AnyMapperTest.cs" /> <Compile Include="MappingByCode\MappersTests\IdMapperTest.cs" /> <Compile Include="MappingByCode\MappersTests\ManyToOneMapperTest.cs" /> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |