Programmer vers les interfaces tout en cartographiant avec NHibernate Fluent

j'ai été poussé dans la soumission et j'ai commencé à apprendre le NHibernate couramment (pas d'expérience antérieure du NHibernate). Dans mon projet, je programmais vers des interfaces pour réduire le couplage, etc. Cela signifie à peu près "tout" se réfère à l'interface au lieu du type concret (IMessage au lieu du Message). La pensée derrière ceci est d'aider à le rendre plus testable en étant capable de se moquer des dépendances.

Cependant, (Courant) NHibernate n'aime pas quand je l'ai essayer pour mapper aux interfaces au lieu de classes concrètes. Le problème est simple - selon le Wiki courant, il est intelligent de définir le champ ID de ma classe comme par exemple

int Id { get; private set; }

pour obtenir une clé primaire générée automatiquement. Cependant, cela fonctionne seulement avec des classes concrètes - Je ne peux pas spécifier un niveau d'accès sur une interface, où la même ligne doit être

int Id { get; set; }

et je suppose que cela nie rendre le setter privé dans la classe de béton (l'idée étant que seulement NHibernate devrait jamais définir L'ID comme assigné par le DB).

pour l'instant, je suppose que je vais juste rendre le setter public et essayer d'éviter la tentation de lui écrire.. Mais est-ce que quelqu'un a une idée de ce qui serait la "bonne", la meilleure pratique pour créer un champ primaire-clé que seul NHibernate peut écrire tout en programmant seulement aux interfaces?

mise à JOUR

De ce que je comprends après les deux réponses ci - dessous de mookid et James Gregory, je peux bien être sur la mauvaise voie-il ne devrait pas y avoir une raison pour moi d'avoir une interface par entité comme je l'ai maintenant. C'est bien et bon. Je suppose que ma question devient alors - n'y a-t-il aucune raison de programmer 100% contre une interface pour des entités? Et s'il n'y a qu'une seule situation où cela pourrait être justifié, est-il possible de le faire avec le NHibernate (couramment)?

je demande parce que Je ne sais pas, de ne pas être critique. Merci pour les réponses. :)

25
demandé sur Rune Jacobsen 2009-05-10 19:32:26

5 réponses

mise à jour: utilisation de l'union-la sous-classe n'est pas prise en charge par l'intermédiaire de l'interface fluent-nhibernate fournit. Vous devrez utiliser un fichier de cartographie hbm et l'ajouter.

moi aussi j'essaie de le faire avec du NHibernate. Je ne pense pas que ça devrait être un problème de mappage des interfaces. Vous voulez utiliser une stratégie d'héritage, en particulier le table-par-classe de béton .

essentiellement, vous créez une définition de mappage pour la classe de base (dans ce cas votre interface) et spécifiez comment NHibernate devrait traiter avec les implémenteurs en utilisant union-subclass.

ainsi, par exemple, cela devrait vous permettre de faire des associations polymorphiques:

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

notez que la Id est la même pour tous les réalisateurs de béton. NHibernate. En outre, iaccountmanager table n'existe pas réellement.

vous pouvez également essayer de tirer Le polymorphisme implicite du NHibernate (documenté sous le tableau-stratégie par classe de béton)-mais il a des tonnes de limites.

8
répondu Dane O'Connor 2009-05-13 01:18:32

je me rends compte qu'il s'agit d'une diversion, et non d'une réponse à votre question (bien que je pense que mookid a obtenu ce couvert).

vous devriez vraiment évaluer si les interfaces sur vos entités de domaine fournissent réellement quelque chose de Valable; il est rare de trouver une situation où vous avez réellement besoin de le faire.

par exemple: comment utiliser IMessage moins couplé que Message , quand ils deux (presque) sans doute partager des signatures identiques? Vous ne devriez pas avoir besoin de vous moquer d'une entité, parce qu'il est rare qu'elle ait assez de comportement pour exiger d'être moquée.

12
répondu James Gregory 2009-05-10 17:28:25

vous pouvez ajuster votre interface pour ne contenir qu'un getter:

public interface ISomeEntity
{
    int Id { get; }
}

votre classe de béton peut encore implémenter un setter, et puisque vous programmez sur vos interfaces, vous n'appellerez jamais le setter "par accident".

si vous voulez refuser de définir l'id même si vous tenez une référence à une instance concrète, vous pouvez vous abstenir d'implémenter un setter, et puis laisser NHibernate accéder au champ au lieu de la propriété - C'est vrai, NHibernate peut utiliser une ruse de réflexion astucieuse pour définir votre champ d'identification directement au lieu d'invoquer la propriété. Alors vous pourriez cartographier l'id comme ceci:

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

dans ce cas, votre propriété Id doit être soutenue par un champ correspondant id . Il y a plus de conventions de nommage, par exemple si vous préférez les underscores comme préfixe de champ privé.

10
répondu mookid8000 2009-05-10 17:14:16

j'ai exactement le même problème. Malheureusement, j'ai une raison valable pour utiliser les interfaces entity; le modèle entity sera implémenté de différentes façons et avec différentes correspondances par client.

le modèle entier doit être lu en lecture seule, donc les interfaces sont du style:

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

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

implémentations concrètes puis les mettre en œuvre avec des setters internes:

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

j'ai suivi la route de la cartographie pour les classes concrètes. Tout va bien jusqu'à ce que vous créiez des relations qui retournent des interfaces et doivent être moulées à des implémentations concrètes.

HasMany(x => x.Accounts)

peut devenir

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

Mais il n'y a pas d'équivalent "cast" pour

References(x => x.House)

Mapping to the interfaces (the neater solution) soulève le problème mentionné ci-dessus en ce que L'Id doit exister sur la classe supérieure pour le réglage et nécessite un setter sur le interface.

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

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

pour l'instant, ma seule solution est d'ajouter des setters à tous les champs d'ID d'interface. C'est une honte L'Id ne peut pas exister à l'intérieur d'une sous-classe ou avoir son type moulé à partir de l'interface.

10
répondu theGecko 2009-07-15 11:27:41

on dirait que je n'ai pas assez de réputation pour commenter les réponses d'autres personnes encore en tant que tel je vais devoir faire ceci une réponse en elle-même.

Références a maintenant une surcharge générique pour permettre à la distribution que theGecko cherchait dans sa réponse.

4
répondu BenCr 2010-07-20 13:51:27