Domanda

I have defined this mapping:

public class Mapping : ConventionModelMapper
{
    public Mapping()
    {
        IsRootEntity((type, declared) =>
                         {
                             return !type.IsAbstract &&
                                    new[] { typeof(Entity<Guid>), typeof(CommonEntity) }.Contains(type.BaseType);
                         });

        IsEntity((x, y) => typeof(Entity<Guid>).IsAssignableFrom(x) && !x.IsAbstract && !x.IsInterface);

        IsSet((mi, wasDeclared) =>
                  {
                      var propertyType = mi.GetPropertyOrFieldType();
                      return propertyType.IsGenericType && typeof(System.Collections.Generic.ISet<>).IsAssignableFrom(propertyType.GetGenericTypeDefinition());
                  });

        IsManyToMany((mi, wasDeclared) =>
                         {
                             var propertyType = mi.GetPropertyOrFieldType();
                             var containingType = mi.ReflectedType;

                             if (typeof(System.Collections.Generic.ISet<>).IsAssignableFrom(propertyType.GetGenericTypeDefinition()))
                             {
                                 var referenceType = propertyType.GetGenericArguments()[0];
                                 return true;
                                 return !referenceType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Any(p => p.PropertyType.IsAssignableFrom(containingType));
                             }
                             return false;
                         });

        Class<Entity<Guid>>(x =>
                                {
                                    x.Id(c => c.Id, m => m.Generator(Generators.GuidComb));
                                    x.Version(c => c.Version, (vm) => { });
                                });

        BeforeMapClass += OnBeforeMapClass;
        BeforeMapManyToOne += OnBeforeMapManyToOne;
        BeforeMapSet += OnBeforeMapSet;
        BeforeMapManyToMany += OnBeforeMapManyToMany;

        Class<CommonEntity>(x =>
        {
            x.Property(c => c.DateCreated, m => m.Type<UtcDateTimeType>());
            x.Property(c => c.DateModified, m => m.Type<UtcDateTimeType>());
        });
    }

    private void OnBeforeMapManyToMany(IModelInspector modelInspector, PropertyPath member, IManyToManyMapper collectionRelationManyToManyCustomizer)
    {
        collectionRelationManyToManyCustomizer.Column(member.LocalMember.GetPropertyOrFieldType().GetGenericArguments()[0].Name + "Id");
    }

    private void OnBeforeMapSet(IModelInspector modelInspector, PropertyPath member, ISetPropertiesMapper propertyCustomizer)
    {
        propertyCustomizer.Key(k=>k.Column(member.GetContainerEntity(modelInspector).Name + "Id"));
        propertyCustomizer.Cascade(Cascade.Persist);
        if (modelInspector.IsManyToMany(member.LocalMember))
        {
            propertyCustomizer.Table(member.GetContainerEntity(modelInspector).Name +
                                     member.LocalMember.GetPropertyOrFieldType().GetGenericArguments()[0].Name);
        }
    }

    private void OnBeforeMapManyToOne(IModelInspector modelInspector, PropertyPath member, IManyToOneMapper propertyCustomizer)
    {
        propertyCustomizer.Column(member.LocalMember.Name + "Id");
    }

    private void OnBeforeMapClass(IModelInspector modelInspector, Type type, IClassAttributesMapper classCustomizer)
    {
        classCustomizer.Table('['+ type.Name + ']');
    }
}

An I am having a problem with a many to many relationship. I have User, UserPermission and Permission. When I am saving a user after attaching a Permission to it it generates this SQL:

exec sp_executesql N'UPDATE [Permission] SET UserId = @p0 WHERE Id = @p1',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='57A2CD87-4A79-4131-B9CE-A1060168D520',@p1='9D99D340-1B63-4291-B55A-6127A8F34FC9'

When it should be like:

exec sp_executesql N'INSERT INTO UserPermission (UserId, PermissionId) VALUES (@p0, @p1)',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='2C670A01-C2E6-46A3-A412-A1060168F976',@p1='9D99D340-1B63-4291-B55A-6127A8F34FC9'

When I add a specific class mapping for User:

        Class<User>(classMapper =>
                        {
                            classMapper.Set(x => x.Permissions, map =>
                                                        {
                                                            //map.Key(k => k.Column("UserId"));
                                                            //map.Table("UserPermission");
                                                        }, r => r.ManyToMany(m => {}));
                        });

I can leave out the key and table definition and include the ManyToMany without the column call and it works. But it does the same same thing as my BeforeManyToMany event handler. If I drop the whole Class thing the BeforeMapManyToMany event is not fired and nHibernate thinks I've got a UserId on my Permission table.

Heres User:

public class User : CommonEntity
{
    protected User()
    {
        Permissions = new HashSet<Permission>();
    }

    public User(User createdBy) : base(createdBy)
    {
        Permissions = new HashSet<Permission>();
    }

    public ISet<Permission> Permissions { get; protected set; }
}
È stato utile?

Soluzione

After poking around in the source code I realised the problem was that IsOneToMany was checked against the property when defining the set before IsManyToMany. I just needed to define IsOneToMany and it worked without any explicit mappings.

        IsOneToMany((mi, wasDeclared) =>
        {
            var propertyType = mi.GetPropertyOrFieldType();

            return ModelInspector.IsEntity(propertyType);
        });
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top