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

发布于 2024-11-09 01:30:55 字数 4268 浏览 1 评论 0原文

这与我之前的问题非常相似: FluentNHibernate: How to Translation HasMany( x => x.Addresses).KeyColumn("PersonId") 进入自动映射


假设我有这些模型:

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

这正确解决了问题。但我想使用 attribute & 获得相同的结果习俗。我尝试过:

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();

有什么想法吗?谢谢。


更新:

测试项目可以从

This is very similar to my previous question: FluentNHibernate: How to translate HasMany(x => x.Addresses).KeyColumn("PersonId") into automapping


Say I have these models:

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

I want FluentNHibernate to create the following tables:

Person
    PersonId
Address
    AddressId
    OwnerId

This can be easily achieved by using fluent mapping:

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

I want to get the same result by using auto mapping. I tried the following conventions:

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

But it created the following tables:

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

So I added a AutoMappingOverride:

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

This correctly solved the problem. But I want to get the same result using attribute & convention. I tried:

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

But it created these tables:

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

Below is the rest of my code:

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();

Any ideas? Thanks.


Update:

The test project can be downloaded from here.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

旧城空念 2024-11-16 01:30:55

唉……学习NHibernate真是一件揪心的经历。

不管怎样,我想我终于找到了解决这个问题的方法:只需删除 SimpleForeignKeyConvention ,一切都会正常工作。

似乎 SimpleForeignKeyConventionReferenceKeyConventionReferenceKeyConvention 都冲突。 KeyColumnConvention。它的优先级高于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";
    }
}

Sigh... Learning NHibernate is really a hair pulling experience.

Anyway I think I finally figured out how to solve this problem: Just remove the SimpleForeignKeyConvention and everything will work fine.

It seems the SimpleForeignKeyConvention conflicts with both ReferenceKeyConvention & KeyColumnConvention. It has higher priority than KeyColumnConvention but lower priority than 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";
    }
}
最冷一天 2024-11-16 01:30:55

我已经使用 FHN 的自动映射功能测试了您的类,它不会在地址表上创建第二个 PersonId。
我使用的是 此处 的 FHN v1.2.0.721

I've tested your classes with FHN's auto-mapping feature and it does not create that second PersonId on Address table.
I'm using FHN v1.2.0.721 from here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文