FluentNHibernate: автоматическое отображение отношения OneToMany с использованием атрибута и соглашения

Это очень похоже на мой предыдущий вопрос: FluentNHibernate: Как перевести 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");
    }
}

Это правильно решило проблему. Но я хочу получить тот же результат, используя атрибут и соглашение. Я пытался:

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

Любые идеи? Спасибо.


Обновление:

Тестовый проект можно загрузить с здесь.


person Community    schedule 23.05.2011    source источник


Ответы (2)


Вздох ... Изучение NHibernate действительно увлекательно.

В любом случае, я думаю, что наконец-то понял, как решить эту проблему: просто удалите SimpleForeignKeyConvention, и все будет работать нормально.

Кажется, SimpleForeignKeyConvention конфликтует как с ReferenceKeyConvention, так и 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";
    }
}
person Community    schedule 24.05.2011
comment
позаботьтесь принять ответ, чтобы другие знали, что это решение вашей проблемы - person Firo; 24.05.2011
comment
@ Firo: Я получил сообщение Вы можете принять свой ответ через x часов (на данный момент x = 16). Я попробую еще раз завтра. - person ; 24.05.2011

Я тестировал ваши классы с помощью функции автоматического сопоставления FHN, и она не создает второй PersonId в таблице адресов. Я использую FHN v1.2.0.721 из здесь

person VahidN    schedule 23.05.2011
comment
Спасибо за тестирование! Вы включили PersonMappingOverride в конфигурацию? Если да, то второй PersonId в Address таблице не создается. Но его предполагается исключить из конфигурации, потому что я хочу получить тот же результат, используя KeyColumnAttribute & KeyColumnConvention. Я обновил свой вопрос ссылкой на загрузку моего тестового проекта, который должен воспроизвести проблему. - person ; 23.05.2011
comment
Теперь вы изменили названия свойств, верно? (чтобы доказать, что ...) Итак, пожалуйста, прочтите этот похожий поток (Чтобы избежать двух разных столбцов внешнего ключа ...): stackoverflow.com/questions/310641/ - person VahidN; 23.05.2011