2009-05-10 17 views
25

Byłem ubrany w uległość i rozpocząłem naukę Fluent NHibernate (bez wcześniejszego doświadczenia NHibernate). W moim projekcie programuję interfejsy w celu zmniejszenia sprzężenia itp. Oznacza to, że prawie wszystko odnosi się do interfejsu zamiast konkretnego typu (wiadomość zamiast wiadomości). Myślą, która kryje się za tym, jest pomoc w sprawieniu, by był on bardziej testowalny dzięki możliwości kpić z zależności.Programowanie do interfejsów podczas mapowania z Fluent NHibernate

Jednak (Fluent) NHibernate nie podoba się, gdy próbuję mapować na interfejsy zamiast konkretnych klas. Sprawa jest prosta - według Fluent Wiki, jest inteligentny, aby określić pole ID z mojej klasy, jak na przykład

int Id { get; private set; } 

aby uzyskać typową automatycznie wygenerowany klucz podstawowy. Jednak, że działa tylko z konkretnych klas - Nie mogę określić poziom dostępu na interfejsie, gdzie ta sama linia musi być

int Id { get; set; } 

i myślę, że neguje czyniąc seter prywatny w konkretnej klasy (the Pomysł polega na tym, że tylko NHibernate powinien kiedykolwiek ustawić ID przypisany przez DB).

Chyba po prostu rozgłaszam publiczność i spróbuję uniknąć pokusy pisania na jej temat. Ale czy ktoś ma pojęcie, co byłoby "właściwym", najlepszym sposobem na stworzenie właściwe pole klucza podstawowego, do którego może pisać tylko NHibernate, a jednocześnie programować tylko do interfejsów?

AKTUALIZACJA

Z tego co rozumiem, po dwie odpowiedzi poniżej ze mookid i James Gregory, mogę równie dobrze być na niewłaściwym torze - nie powinno być powodem do mnie, aby mieć interfejs do jednego podmiotu tak jak mam teraz. Wszystko dobrze i dobrze. Domyślam się, że moje pytanie staje się - czy nie ma powodu, aby programować w 100% przeciwko interfejsowi dla dowolnych podmiotów? A jeśli istnieje nawet jedna sytuacja, w której może to być uzasadnione, czy można to zrobić za pomocą (Fluent) NHibernate?

Pytam, ponieważ nie wiem, nie krytycznie. Dzięki za odpowiedzi. :)

+3

To zabawne, ponieważ niektórzy programiści, których szanuję przypadkowo, przyjęli anty-wzór interfejsu użytkownika. Bardziej błędem jest to, co robią zawodowcy niż nowicjuszami, ponieważ debiutanci ledwo wiedzą, dlaczego interfejsy są najważniejsze. –

+1

Jeśli nie tworzysz interfejsów dla swoich podmiotów, to jak testujesz zachowanie swoich jednostek? Dokładniej, jak wyśmiewać zależności tego bytu? –

+2

Kiedy ludzie mówią, że programują interfejsy, a nie implementacje, nie mają na myśli konstrukcji języka interfejsu. Chodzi o to, że starają się zaciemnić kod, którego używasz tak często, jak to tylko możliwe. To znaczy. Gdy używasz modułu wyliczającego w słowniku, nie zakładaj, że Current nie wyrzuci przed wywołaniem movenext, to jest niezdefiniowane i może się zmienić. – stonemetal

Odpowiedz

8

AKTUALIZACJA: używanie podklasy związków nie jest obsługiwane przez płynny interfejs zapewniający płynność-nhibernate. Będziesz musiał użyć zwykłego pliku mapowania hbm i dodać go.

Ja też próbuję zrobić to płynnie NHibernate. Nie sądzę, że powinien to być interfejs mapowania problemów. Chcesz użyć strategii dziedziczenia, w szczególności: table-per-concrete-class strategy.

Zasadniczo tworzy się definicję odwzorowania dla klasy bazowej (w tym przypadku interfejsu użytkownika) i określa, w jaki sposób NHibernate powinien zajmować się programistami za pomocą podklasy związków.

Tak więc, na przykład, powinno to pozwolić na wykonywanie polimorficzne skojarzenia:

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

Uwaga jak id jest taka sama dla wszystkich konkretnych realizatorów. NHibernate tego wymaga. Ponadto tabela IAccountManager w rzeczywistości nie istnieje.

Można również próbować wykorzystać polimorfizm NHibernate's Implicit Polimorphism (udokumentowany poniżej strategii typu table-per-concrete) - ale ma on mnóstwo ograniczeń.

+6

Podklasa Unii jest teraz obsługiwana przez Fluent NHibernate 1.1: Call UseUnionSubclassForInheritanceMapping() z bazy ClassMap – cbp

10

Można dostosować interfejs zawiera tylko getter:

public interface ISomeEntity 
{ 
    int Id { get; } 
} 

Twój beton klasy można jeszcze zaimplementować setter, jak również, a ponieważ jesteś programowania do interfejsów nigdy nie wywołać setter " przez przypadek".

Jeśli chcesz zabronić ustawiania identyfikatora, nawet jeśli posiadasz odwołanie do konkretnej instancji, możesz powstrzymać się od implementowania narzędzia ustawiającego, a następnie pozwolić NHibernate uzyskać dostęp do pola zamiast właściwości - tak, NHibernate może użyć niektórych sprytne oszustwo do odrysowania, aby ustawić pole id bezpośrednio, zamiast wywoływać właściwość. Następnie można zmapować identyfikator takiego:

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

w takim przypadku nieruchomość Id muszą być poparte odpowiednią id dziedzinie. Istnieje więcej konwencji nazewnictwa, np. jeśli wolisz podkreślenia jako prywatny prefiks pola.

+0

Dzięki za odpowiedź! Twoja ISomeEntity jest taka, jak ja to miałem, ale potem NHibernate narzeka na brak ustawiacza, jeśli mapuję do interfejsu. Jeśli mapuję do konkretnej klasy, to działa, ale wydaje mi się, że to trochę pachnie. Myślę więc, że wybiorę drugą opcję i przejdę do pola. Mniej czysty, jaki chciałbym, ale sądzę, że rzeczywistość ogranicza to, co mogę zrobić. :) –

+0

OK, próbowałem zrobić Id (e => e.Id) .Access.AsCamelCaseField(); bit, ale to wymaga, aby pole identyfikatora kopii również znajdowało się w interfejsie. Widzę, że nie może być łatwego sposobu na osiągnięcie tego, czego pragnę, ale widzę też, z odpowiedzi Jamesa Gregory'ego, że to, czego szukam, może nie być właściwą rzeczą (tm). W każdym razie dzięki! –

12

Zdaję sobie sprawę, że jest to urozmaicenie, a nie odpowiedź na twoje pytanie (choć myślę, że mookid ma to pokryte).

Powinieneś naprawdę ocenić, czy interfejsy na twoich domenieach domeny dostarczają czegoś, co jest warte; rzadko zdarza się znaleźć sytuację, w której rzeczywiście trzeba to zrobić.

Na przykład: w jaki sposób poleganie na IMessage jest mniej powiązane, niż poleganie na Message, gdy obaj (prawie) niewątpliwie mają identyczne podpisy? Nie powinieneś kpić z istoty, ponieważ rzadko zdarza się, aby jej zachowanie wymagało drwiny.

+0

Wow, odpowiedź od statku-matki. :) Dzięki, i widzę. Z pewnością nie mogę usprawiedliwić korzystania z interfejsów przeze mnie, właśnie przeczytałem wystarczającą liczbę blogów itp., Które twierdzą, że zawsze powinienem programować na interfejsy, nigdy na konkretne implementacje. Porzucę to dzięki twojemu (i mookidzkiemu) wglądowi, ale byłoby wspaniale zobaczyć więcej na ten temat w szalonym świecie TDD/mock/IoC/ORM. ;) –

+1

Wyśmiewasz się w celu sprawdzenia zachowania i wstrzykujesz, aby uniknąć sprzężenia na implementacjach. Jednostki rzadko są czymś więcej niż strukturami danych i jako takie nie mają zachowań (a więc nie wymagają żadnej zmienności w implementacji), aby wymagały abstrakcji (interfejsów). W związku z tym, projekt ten powinien być nadal wspierany przez Fluent NHibernate, ponieważ jest obsługiwany przez rdzeń NH, więc na pewno stworzę dla niego problem. –

+0

Dzięki James, zarówno za wgląd i problem. :) –

10

Mam dokładnie ten sam problem. Niestety mam ważny powód używania interfejsów encji; model podmiotu zostanie wdrożony na różne sposoby iz różnymi mapowaniami przypadającymi na klienta.

całego modelu musi być tylko do odczytu, więc interfejsy są w stylu:

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

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

Konkretne implementacje następnie wdrożyć je z ustawiaczy wewnętrznych:

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

I spadły trasie mapowania do konkretnych klas. Wszystko jest w porządku, dopóki nie utworzysz relacji, które zwrócą interfejsy i będą wymagały rzutowania na konkretne implementacje.

HasMany(x => x.Accounts) 

może stać

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

Ale nie ma odpowiednika 'cast' dla

References(x => x.House) 

mapowania interfejsów (roztwór neater) rzuca się problem, o którym mowa powyżej, że Id musi istnieć w najwyższej klasie do ustawienia i wymaga settera w interfejsie.

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

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

Na razie jedynym moim rozwiązaniem jest dodanie seterów do wszystkich pól identyfikatora interfejsu. Szkoda, że ​​Id nie może istnieć w podklasie lub jego typ jest rzutowany z interfejsu.

+2

Nie możesz użyć 'References (x => x.House) .Class ();'? – fostandy

4

Wygląda na to, że nie mam wystarczającej reputacji, aby komentować odpowiedzi innych ludzi, ale jako takie będę musiał zrobić to samo z siebie.

Odniesienia mają teraz ogólne przeładowanie, aby umożliwić obsadę, której szukał Gecko w swojej odpowiedzi.

Powiązane problemy