FluentNHibernate:使用属性和约定自动化OneToMany关系

时间:2011-05-23 00:03:58

标签: nhibernate fluent-nhibernate automapping

这与我之前的问题非常相似:FluentNHibernate: How to translate HasMany(x => x.Addresses).KeyColumn("PersonId") into automapping


说我有这些模型:

public class Person
{
    public virtual int Id { get; private set; }
    public virtual ICollection<Address> Addresses { get; private set; }
}

public class Address
{
    public virtual int Id { get; private set; }
    public virtual Person Owner { get; set; }
}

我希望FluentNHibernate创建以下表格:

Person
    PersonId
Address
    AddressId
    OwnerId

这可以通过使用流畅的映射轻松实现:

public class PersonMapping : ClassMap<Person>
{
    public PersonMapping()
    {
        Id(x => x.Id).Column("PersonId");
        HasMany(x => x.Addresses).KeyColumn("OwnerId");
    }
}

public class AddressMapping : ClassMap<Address>
{
    public AddressMapping()
    {
        Id(x => x.Id).Column("AddressId");
        References(x => x.Person).Column("OwnerId");
    }
}

我想通过使用自动映射获得相同的结果。我尝试了以下约定:

class PrimaryKeyNameConvention : IIdConvention
{
    public void Apply(IIdentityInstance instance)
    {
        instance.Column(instance.EntityType.Name + "Id");
    }
}

class ReferenceNameConvention : IReferenceConvention
{
    public void Apply(IManyToOneInstance instance)
    {
        instance.Column(string.Format("{0}Id", instance.Name));
    }
}

// Copied from @Fourth: https://stackoverflow.com/questions/6091290/fluentnhibernate-how-to-translate-hasmanyx-x-addresses-keycolumnpersonid/6091307#6091307
public class SimpleForeignKeyConvention : ForeignKeyConvention
{
    protected override string GetKeyName(Member property, Type type)
    {
        if(property == null)
            return type.Name + "Id";
        return property.Name + "Id";
    }
}

但它创建了以下表格:

Person
    PersonId
Address
    AddressId
    OwnerId
    PersonId // this column should not exist

所以我添加了一个AutoMappingOverride:

public class PersonMappingOverride : IAutoMappingOverride<Person>
{
    public void Override(AutoMapping<Person> mapping)
    {
        mapping.HasMany(x => x.Addresses).KeyColumn("OwnerId");
    }
}

这正确地解决了这个问题。但是我希望使用属性&amp;获得相同的结果。惯例。我试过了:

public class Person
{
    public virtual int Id { get; private set; }

    [KeyColumn("OwnerId")]
    public virtual ICollection<Address> Addresses { get; private set; }
}

class KeyColumnAttribute : Attribute
{
    public readonly string Name;

    public KeyColumnAttribute(string name)
    {
        Name = name;
    }
}

class KeyColumnConvention: IHasManyConvention
{
    public void Apply(IOneToManyCollectionInstance instance)
    {
        var keyColumnAttribute = (KeyColumnAttribute)Attribute.GetCustomAttribute(instance.Member, typeof(KeyColumnAttribute));
        if (keyColumnAttribute != null)
        {
            instance.Key.Column(keyColumnAttribute.Name);
        }
    }
}

但它创建了这些表:

Person
    PersonId
Address
    AddressId
    OwnerId
    PersonId // this column should not exist

以下是我的其余代码:

ISessionFactory sessionFactory = Fluently.Configure()
    .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString))
    .Mappings(m =>
                m.AutoMappings.Add(AutoMap.Assemblies(typeof(Person).Assembly)
                    .Conventions.Add(typeof(PrimaryKeyNameConvention))
                          .Conventions.Add(typeof(PrimaryKeyNameConvention))
                          .Conventions.Add(typeof(ReferenceNameConvention))
                          .Conventions.Add(typeof(SimpleForeignKeyConvention))
                          .Conventions.Add(typeof(KeyColumnConvention)))

                //m.FluentMappings
                //    .Add(typeof (PersonMapping))
                //    .Add(typeof (AddressMapping))
    )
    .ExposeConfiguration(BuildSchema)
    .BuildConfiguration()
    .BuildSessionFactory();

有什么想法吗?感谢。


更新

可以从here下载测试项目。

2 个答案:

答案 0 :(得分:7)

叹息......学习NHibernate真的是一种拔毛的经历。

无论如何,我想我终于想出了如何解决这个问题:只需删除SimpleForeignKeyConvention,一切都会正常。

似乎SimpleForeignKeyConventionReferenceKeyConventionKeyColumnConvention冲突KeyColumnConvention。它的优先级高于ReferenceKeyConvention,但优先级低于public class SimpleForeignKeyConvention : ForeignKeyConvention { protected override string GetKeyName(Member property, Type type) { if(property == null) // This line will disable `KeyColumnConvention` return type.Name + "Id"; // This line has no effect when `ReferenceKeyConvention` is enabled. return property.Name + "Id"; } }

{{1}}

答案 1 :(得分:1)

我已经使用FHN的自动映射功能测试了您的类,并且它没有在地址表上创建第二个PersonId。 我正在使用here

中的FHN v1.2.0.721