2009-05-10 8 views
25

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

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

int Id { get; private set; } 

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

int Id { get; set; } 

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

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

ОБНОВЛЕНО

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

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

+3

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

+1

Если вы не создаете интерфейсы для своих объектов, то как вы проверяете поведение своих объектов? Более конкретно, как вы издеваетесь над зависимостями этого объекта? –

+2

Когда люди говорят, что программа для интерфейсов не реализуется, это не означает конструкцию языка интерфейса. Они означают, что вы пытаетесь использовать черный код, который вы используете, насколько это возможно. То есть При использовании Enumerator в словаре не предполагается, что Current не будет выбрасываться до вызова movenext, он не определен и может измениться. – stonemetal

ответ

8

ОБНОВЛЕНИЕ: с использованием подкласса union не поддерживается через свободный интерфейс, обеспечивающий свободный доступ. Вам нужно будет использовать обычный файл сопоставления hbm и добавить его.

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

По существу, вы создаете определение отображения для базового класса (в данном случае ваш интерфейс) и указываете, как 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> 

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

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

+6

Союзный подкласс теперь поддерживается Fluent NHibernate 1.1: Вызов UseUnionSubclassForInheritanceMapping() из базы ClassMap – cbp

10

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

public interface ISomeEntity 
{ 
    int Id { get; } 
} 

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

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

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

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

+0

Спасибо за ответ! Ваша ISomeEntity - это то, как я это сделал, но затем NHibernate жалуется на отсутствие сеттера, если я сопоставляю интерфейс. Если я сопоставляю конкретный класс, он работает, но мне кажется, что он немного пахнет. Поэтому я думаю, что поеду на второй вариант и подойду для доступа к полю. Менее чистый, что я хотел бы, но я предполагаю, что реальность ограничивает то, что я могу сделать. :) –

+0

Хорошо, я попытался сделать Id (e => e.Id) .Access.AsCamelCaseField(); бит, но для этого требуется также, чтобы поле идентификатора поддержки также находилось в интерфейсе. Я вижу, что может быть нелегкий способ выполнить то, что я хочу, но я также вижу из ответа Джеймса Грегори, что то, что я хочу, может не быть Правильной Вещью (tm). Спасибо хоть! –

12

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

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

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

+0

Вау, ответ от материнства. :) Спасибо, и я вижу. Я, конечно, не могу оправдывать использование интерфейсов самостоятельно, я только что достаточно читал блоги и т. Д., Которые утверждают, что я всегда должен программировать интерфейсы, а не конкретные реализации. Я откажусь от этого, благодаря вашему (и мужику) прозрению, но было бы замечательно узнать об этом в сумасшедшем мире TDD/mock/IoC/ORM. ;) –

+1

Вы издеваетесь над тем, чтобы проверить поведение, и вы вставляете, чтобы избежать связи с реализациями. Объекты редко бывают чем-то большим, чем структуры данных, и поэтому не имеют поведения (и, следовательно, не изменчивости в реализации), чтобы требовать абстракции (интерфейсы). Этот дизайн все еще поддерживается Fluent NHibernate, поскольку он поддерживается NH-core, поэтому я обязательно создам для него проблему. –

+0

Спасибо Джеймсу, как за понимание, так и за эту проблему. :) –

10

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

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

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) 

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

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

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

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

+2

Не можете использовать 'Ссылки (x => x.House) .Class ();'? – fostandy

4

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

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