nHibernate ByCode Mapping - 如何按照惯例完全映射ManyToMany?

时间:2012-11-11 11:06:37

标签: nhibernate mapping-by-code

我已定义此映射:

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 + ']');
    }
}

我遇到了多对多关系的问题。我有User,UserPermission和Permission。当我在为其附加权限后保存用户时,它会生成此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'

应该是这样的:

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'

当我为User添加特定的类映射时:

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

我可以省略键和表定义并包含没有列调用的ManyToMany,它可以工作。但它与我的BeforeManyToMany事件处理程序完全相同。如果我删除整个Class事件,则不会触发BeforeMapManyToMany事件,并且nHibernate认为我的Permission表上有UserId。

Heres用户:

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; }
}

1 个答案:

答案 0 :(得分:2)

在源代码中探讨之后,我发现问题是在IsManyToMany之前定义集合时,IsOneToMany是针对属性进行检查的。我只需要定义IsOneToMany,它没有任何明确的映射。

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

            return ModelInspector.IsEntity(propertyType);
        });