2009-05-10 2 views
25

제출물로 채워졌으며 Fluent NHibernate (이전 NHibernate 경험 없음)를 배우기 시작했습니다. 내 프로젝트에서 나는 커플 링 등을 줄이기 위해 인터페이스를 프로그래밍하고있다. 즉, "모든 것"은 구체적인 유형 (메시지 대신 IMessage) 대신 인터페이스를 의미한다. 이것의 뒤에있는 생각은 의존성을 조롱 할 수있게 됨으로써 그것을 더 테스트 가능하게 만드는 것을 돕는 것입니다.Fluent NHibernate와 맵핑하는 동안 인터페이스 프로그래밍

그러나 (Fluent) NHibernate는 구체적인 클래스 대신 인터페이스에 매핑하려고 할 때이를 좋아하지 않습니다. 문제는 간단합니다 - 유창함 위키에 따르면, 전형적인 자동 생성 된 기본 키를 얻기 위해 예를

int Id { get; private set; } 

에 대한 나의 클래스의 ID 필드를 정의하는 스마트입니다. 내가

int Id { get; set; } 

로 같은 줄이있는 인터페이스에 액세스 레벨을 지정할 수 없으며, 그합니다 (콘크리트 클래스에서 세터 개인을 부정 생각 - 그러나, 단지 구체적인 클래스와 함께 작동 아이디어는 NHibernate가 DB에 의해 할당 된 ID를 설정해야한다는 것입니다).

지금 당장은 공개 자로 하여금 글쓰기의 유혹을 피하려고 노력할 것입니다.하지만 누구에게도 "적절한"최상의 모범 사례가 될 수있는 아이디어가 있습니까? Hibernate만이 인터페이스로 프로그래밍하는 동안 쓸 수있는 적절한 기본 키 필드?

내가 mookid 제임스 그레고리에서 아래의 두 가지 답변 후, 내가 아니라 잘못된 궤도에있을 수 있습니다 이해하는 바로는

업데이트 - 나 개체 당 인터페이스를하는 이유가 안된다 내가 지금 가지고있는 것처럼. 그게 다 잘되고 잘됐다. 내 질문에 그때가 될 것 같아요 - 어떤 엔터티에 대한 인터페이스에 대해 100 % 프로그램에 대한 이유가 없습니까? 그리고 이것이 정당화 될 수있는 단일 상황이 있다면, (Fluent) NHibernate로 이것을 수행 할 수 있습니까?

나는 중요하지 않기 때문에 묻습니다. 답변 주셔서 감사합니다. "

public interface ISomeEntity 
{ 
    int Id { get; } 
} 

귀하의 구체적인 클래스는 여전히뿐만 아니라 세터를 구현할 수 있으며, 당신이 당신의 인터페이스에 프로그래밍되어 있기 때문에 당신은 세터를 호출하지 않습니다 : :)

+3

내가 존경하는 일부 개발자가 실수로 엔티티 인터페이스 방지 패턴을 사용했기 때문에 재미있다. 루키들보다 프로가 더 많은 실수를 범한 이유는 루키가 처음부터 인터페이스가 중요한 이유를 간신히 알고 있기 때문입니다. –

+1

엔티티의 인터페이스를 만들지 않으면 엔티티의 동작을 어떻게 테스트합니까? 보다 구체적으로, 당신은 어떻게 그 개체의 의존성을 조롱합니까? –

+2

사람들은 인터페이스가 프로그래밍이 아니라 구현이라고 말하면 인터페이스 언어 구조를 의미하지는 않습니다. 그들은 가능한 한 많이 사용하는 코드를 블랙 박스에 넣으려고합니다. 나는. 사전에 열거자를 사용할 때 movenext가 호출되기 전에 Current가 throw되지 않는다고 가정하지 않습니다. 정의되지 않았으며 변경 될 수 있습니다. – stonemetal

답변

8

유창한 인터페이스 fluent-nhibernate가 제공하는 union-subclass를 사용하는 UPDATE :은 지원되지 않습니다. 일반 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> 

참고. NHibernate는 이것을 요구했다. 또한 IAccountManager 테이블이 실제로 존재하지 않습니다.

NHibernate의 Implicit Polymorphism (table-per-concrete-class 전략 아래에 설명되어 있음)을 활용할 수도 있지만 제한이 있습니다.

+6

유니온 서브 클래스는 이제 Fluent NHibernate 1에서 지원됩니다.1 : 기본 ClassMap에서 UseUnionSubclassForInheritanceMapping() 호출 – cbp

10

당신은 단지 게터를 포함하기 위해 인터페이스를 조정할 수 있습니다 사고로".

구체적인 인스턴스에 대한 참조를 보유하는 경우에도 id 설정을 허용하지 않으려면 설정자를 구현하지 말고 NHibernate가 속성 대신 해당 필드에 액세스하도록 할 수 있습니다. 즉, NHibernate는 일부 인스턴스를 사용할 수 있습니다. 멋진 속성을 호출하는 대신 ID 필드를 직접 설정하는 멋진 속임수. 당신의 Id 속성이 해당 id 필드에 의해 백업해야

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

하는 경우 : 그럼 당신은이 같은 ID를 매핑 할 수 있습니다. 더 많은 명명 규칙이 있습니다 (예 : 비공개 필드 접두사로서 밑줄을 선호하는 경우.

+0

답변 해 주셔서 감사합니다! 귀하의 ISomeEntity 내가 어떻게 했어,하지만 NHibernate 인터페이스에 매핑하는 경우 setter에 대해 불평. 구체적인 클래스에 매핑하면 작동하지만, 저에게 약간의 냄새가 나는 것 같습니다. 그래서 두 번째 옵션으로 가서 필드 액세스를 위해 이동한다고 생각합니다. 덜 깨끗한,하지만 현실은 내가 할 수있는 일을 제한 같아요. :) –

+0

좋아, 노력 했어 Id (e => e.Id) .Access.AsCamelCaseField(); 비트이지만 역방향 ID 필드가 인터페이스에 있어야합니다. 나는 내가 한 일을 성취하기위한 쉬운 방법이 없을지도 모른다는 것을 안다. 그러나 나는 또한 내가 한 일이 옳은 일이 아닐 수도 있다는 James Gregory의 대답을 본다. 그래도 고마워! –

12

저는 이것이 전환이라고 생각합니다. 귀하의 질문에 대한 대답이 아닙니다. (나는 미키가 그 문제를 다루었다고 생각하지만).

도메인 엔터티의 인터페이스가 실제로 가치있는 것을 제공하는지 여부를 실제로 평가해야합니다. 실제로이 작업을 수행해야하는 상황을 찾는 것은 드뭅니다.

예 : IMessage에 의존하는 것이 둘 다 (거의) 의심 할 여지없이 동일한 서명을 공유 할 때 Message에 의존하는 것보다 얼마나 덜 의존적입니까? 엔티티를 조롱 할 필요는 없습니다. 왜냐하면 조롱 당할 필요가있는 행동은 드물기 때문입니다.

+0

와우, 모선에서 답하십시오. :) 고마워, 알았어. 분명히 혼자서 인터페이스를 사용하는 것을 정당화 할 수는 없지만 충분한 블로그를 읽었을뿐입니다. 항상 인터페이스로 프로그래밍해야하며, 결코 구체적인 구현을해서는 안된다고 주장합니다. 나는 당신의 통찰력 덕분에 그것을 버리 겠지만 TDD/mock/IoC/ORM 미친 세상에서 이것에 대해 더 많이 보게 될 것입니다. ;) –

+1

행동을 확인하기 위해 모의하고 구현시 결합을 피하기 위해 주입합니다. 엔티티는 거의 데이터 구조 이상이 아니기 때문에 추상화 (인터페이스)가 필요한 동작 (따라서 구현의 변동성이 없음)이 없습니다. NH-core가 지원하는 것처럼이 디자인은 Fluent NHibernate에서 계속 지원해야하므로 문제가 될 것입니다. –

+0

통찰력과 문제 모두에 대해 James에게 감사드립니다. :) –

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) 

매핑합니다 (깔끔한 솔루션)에 대한 '캐스트'에는 해당이 없다가 그 위에서 언급 한 문제를 던졌습니다 ID는 설정을 위해 최상위 클래스에 있어야하며 인터페이스에 setter가 필요합니다.

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

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

지금 당장 유일한 해결책은 모든 인터페이스 ID 필드에 설정 도구를 추가하는 것입니다. Id는 하위 클래스 내부에 존재할 수 없거나 인터페이스에서 유형을 캐스팅 한 것이 부끄러운 일입니다.

+2

References (x => x.House)를 사용할 수 없습니다 .Class ();'? – fostandy

4

다른 사람들의 답변에 대한 의견을 말할만큼 충분한 평판을 얻지 못한 것 같습니다. 그렇기 때문에 나는이 답변을 자신의 권리로 만들어야 할 것입니다.

참고 자료에는 이제 TheGecko가 답변에서 찾던 캐스트를 허용하는 일반적인 오버로드가 있습니다.