Подтвердить что ты не робот

Программирование на интерфейсы при сопоставлении с Fluent NHibernate

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

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

int Id { get; private set; }

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

int Id { get; set; }

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

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

ОБНОВЛЕНО

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

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

4b9b3361

Ответ 1

UPDATE: с использованием union-subclass не поддерживается через свободный интерфейс, поддерживающий свободный доступ. Вам нужно будет использовать обычный файл сопоставления hbm и добавить его.

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

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

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

<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 Implicit Polymorphism (задокументированный ниже стратегия таблицы за конкретный класс), но он имеет множество ограничений.

Ответ 2

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

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

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

Ответ 3

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

public interface ISomeEntity
{
    int Id { get; }
}

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

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

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

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

Ответ 4

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

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

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)

Но для

нет эквивалента 'cast'
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);       
        });
    }
}

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

Ответ 5

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

В настоящее время у ссылок есть общая перегрузка, позволяющая выполнять бросок, который Гекко искал в своем ответе.