Программирование интерфейсов при отображении с помощью Fluent NHibernate

Я был взбитым в подчинение и начал изучать свободно NHibernate (без предыдущего опыта NHibernate). В моем проекте я программирую интерфейсы для уменьшения сцепления и т. д. Это означает, что в значительной степени" все " относится к интерфейсу вместо конкретного типа (IMessage вместо сообщения). Мысль об этом заключается в том, чтобы помочь сделать его более проверяемым, имея возможность издеваться над зависимостями.

однако (свободно) NHibernate не любит, когда я пытаюсь сопоставить с интерфейсы вместо конкретных классов. Проблема проста - согласно Fluent Wiki, разумно определить поле ID моего класса, например

int Id { get; private set; }

чтобы получить типичный автоматически сгенерированный первичный ключ. Однако это работает только с конкретными классами - я не могу указать уровень доступа на интерфейсе, где одна и та же строка должна быть

int Id { get; set; }

и я думаю, что это отрицает, что делает сеттер частным в конкретном классе (идея заключается в том, что только NHibernate должны указать идентификатор, присвоенный ДБ).

пока, я думаю, я просто сделаю сеттера публичным и попытаюсь избежать соблазна написать ему.. Но есть ли у кого-нибудь идея о том, что было бы "правильным", лучшим способом создать правильное поле первичного ключа, в которое только NHibernate может писать, все еще программируя только интерфейсы?

обновлено

из того, что я понимаю после двух ответов ниже от mookid и James Грегори, возможно, я на неверном пути - у меня не должно быть причин иметь интерфейс для каждой сущности, как у меня сейчас. Все это очень хорошо. Думаю, мой вопрос тогда становится - нет ли причин программировать 100% против интерфейса для любых объектов? И если есть хотя бы одна ситуация, когда это может быть оправдано, можно ли это сделать с (беглым) NHibernate?

Я спрашиваю, потому что я не знаю, не критиковать. Спасибо за ответы. :)

5 ответов


обновление: использование union-подкласса не поддерживается через интерфейс fluent fluent-nhibernate. Вам нужно будет использовать обычный файл сопоставления hbm и добавить его.

Я тоже пытаюсь сделать это с беглым NHibernate. Я не думаю, что это должно быть проблемой сопоставления интерфейсов. Вы хотите использовать стратегию наследования, в частности таблица на конкретный класс стратегии.

по существу, вы создаете определение отображения для базового класса (в этом случае ваш интерфейс) и укажите, как NHibernate должен иметь дело с реализаторами, используя подкласс union.

Так, например, это должно позволить вам создавать полиморфные ассоциации:

<class name="IAccountManager"
                abstract="true"
                table="IAccountManager">

        <id name="Id">
                <generator class="hilo"/>
        </id>

        <union-subclass
                table="DefaultAccountManager"
                name="DefaultAccountManager">
                <property name="FirstName"/>
        </union-subclass>

        <union-subclass
                table="AnotherAccountManagerImplementation"
                name="AnotherAccountManagerImplementation">
                <property name="FirstName"/>
        </union-subclass>
        ...
</class> 

обратите внимание, что идентификатор является одинаковым для всех конкретных исполнителей. NHibernate требовал этого. Кроме того, таблица IAccountManager фактически не существует.

вы также можете попробовать использовать неявный полиморфизм NHibernate (документированный ниже таблицы на конкретный класс стратегия) - но она имеет массу ограничений.


Я понимаю, что это отвлекающий маневр, а не ответ на ваш вопрос (хотя я думаю, что mookid это покрыл).

вы должны действительно оценить, действительно ли интерфейсы на ваших доменных сущностях предоставляют что-либо стоящее; редко можно найти ситуацию, когда вам действительно нужно это сделать.

например: как полагаться на IMessage любой менее связанный, чем полагаться на Message, когда они оба (почти), безусловно, имеют одинаковые подписи? Вам не нужно издеваться над сущностью, потому что редко у нее достаточно поведения, чтобы требовать издевательства.


вы можете настроить свой интерфейс, чтобы содержать только геттер:

public interface ISomeEntity
{
    int Id { get; }
}

ваш конкретный класс все еще может реализовать сеттер, а также, поскольку вы программируете на свои интерфейсы, вы никогда не будете называть сеттера "случайно".

если вы хотите запретить установку id, даже если вы держите ссылку на конкретный экземпляр, вы можете воздержаться от реализации сеттера, а затем позволить NHibernate получить доступ к полю вместо свойства-это правильно, NHibernate может используйте некоторые ловкие отражения трюк, чтобы установить поле id непосредственно вместо вызова свойства. Затем вы можете отобразить id следующим образом:

Id(e => e.Id).Access.AsCamelCaseField();

в этом случае Id свойство должно быть подкреплено соответствующим id поле. Существует больше соглашений об именах, например, если вы предпочитаете подчеркивания в качестве префикса частного поля.


у меня точно такая же проблема. К сожалению, у меня есть веская причина для использования интерфейсов сущностей; модель сущностей будет реализована по-разному и с разными сопоставлениями для каждого клиента.

вся модель должна быть только для чтения, поэтому интерфейсы типа:

public interface IAccount
{
    long AccountId { get; }
    IHouse House { get; }
}

public interface IHouse
{
    long HouseId { get; }
    HouseStatus Status { get; }
    IList<IAccount> Accounts { get; }
}

конкретные реализации затем реализуют их с помощью внутренних сеттеров:

public class Account: IAccount
{
    public virtual long AccountId { get; internal set; }
    public virtual IHouse House { get; internal set; }
}

public class House: IHouse
{
    public virtual long HouseId { get; internal set; }
    public virtual HouseStatus Status { get; internal set; }
    public virtual IList<IAccount> Accounts { get; internal set; }
}

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

HasMany(x => x.Accounts)

может стать

HasMany<Account>(x => x.Accounts)

но нет эквивалентного "броска" для

References(x => x.House)

сопоставление с интерфейсами (более аккуратное решение) вызывает проблему, упомянутую выше, в том, что идентификатор должен существовать в самом верхнем классе для настройки и требует сеттера на интерфейсе.

public sealed class AccountMap : ClassMap<IAccount>
{
    public PokerPlayerMap()
    {
        Id(x => x.AccountId, "account_id");

        DiscriminateSubClassesOnColumn("Type").SubClass<Account>(s =>  
        {
            References(x => x.House);       
        });
    }
}

на данный момент мое единственное решение-добавить сеттеры ко всем идентификатор поля интерфейса. Жаль, что идентификатор не может существовать внутри подкласса или иметь его тип, отлитый из интерфейса.


похоже, у меня недостаточно репутации, чтобы комментировать ответы других людей, но как таковые я собираюсь сделать этот ответ своим собственным правом.

ссылки теперь имеют общую перегрузку, чтобы разрешить бросок, который thegecko искал в своем ответе.