9
votes

Comment dois-je concevoir mon modèle d'objet afin que mon dal puisse peupler des champs en lecture seule?

Afin de séparer les préoccupations, sur mon projet actuel, j'ai décidé de séparer complètement mes objets DAL et BLL / Business dans des assemblées distinctes. Je voudrais garder mes objets métier comme des structures simples sans aucune logique pour garder les choses extrêmement simples. Je voudrais si je pouvais garder la logique d'entreprise séparée de mon dal aussi. Donc, mon application indiquera à mon DAL de charger mes objets, mon DAL fonctionnera à la base de données et obtenez les données, remplissez l'objet avec les données, puis transmettez-le à mon BLL.

question - comment puis-je avoir mon dal dans un assemblage séparé et pousser des données dans les champs en lecture seule?

  • Si je définis le getter tel que protégé, les objets hérités peuvent y accéder, ce qui n'est pas vraiment ce que je veux, car je retournerais les types d'objets hérités, pas les types d'objets d'origine.
  • Si je réglais le getter comme interne, mon DAL doit résider dans le même assemblage que mon BLL que je ne veux pas.
  • Si je fixe le getter comme public, alors tout le monde peut y lire / écrire quand il devrait être lu uniquement.

    EDIT: Je remarque que je peux avoir un type de retour de l'objetBase, mais renvoie réellement un objet ou une collection d'objets dérivés de formulaire ObjectBase, de sorte que le monde extérieur (en dehors de mon dal) les propriétés serait en lecture seule, mais mes types dérivés (uniquement accessibles à l'intérieur de My DAL) Les propriétés sont en réalité lues / écriture.


5 commentaires

Il y a une raison, si vous voulez faire lire les propriétés uniquement sur vos objets professionnels, que vous ne les définissez pas dans le constructeur?


Si vous allez construire votre DLL à partir de zéro, je vous recommande de lire "Microsoft .NET: Applications d'architecture de l'entreprise" de Dino Esposito & Andrea Salterello Amazon.com/...


@CSHARPATL Cela ne ressemble tout simplement pas à la bonne approche pour moi.


@BobtheBuilder Définition des valeurs d'objet Business via le constructeur est de la manière dont les objets immuables sont créés davantage sur la rubrique: weblogs.asp.net/bleroy/archive/2008/01/16/...


Après avoir lu votre question et vos commentaires, je pense que vous n'avez pas accepté qu'il n'y a pas une solution parfaite pour cela. Votre exigence de conception est devenue un désirement - vous allez avoir à accepter un trafic d'une manière ou une autre.


5 Réponses :


7
votes

Vous pouvez définir la propriété en lecture seule via un constructeur.


8 commentaires

C'est ce que j'ai pensé, mais je dois dire que je n'aime pas cela mieux non plus. Il met une aide au bandage sur la question, il ne réalise pas le problème sous-jacent.


Les objets immuables peuvent être une douleur à travailler lorsqu'il n'y a aucun avantage. c'est-à-dire les utiliser dans une application Web.


@Chals Conway - mais n'oubliez pas, la couche logique de l'entreprise est probablement dans une DLL séparée qui peut non seulement être utilisée dans une webApp. Personnellement, lorsque je crée un BLL, je l'utilise également dans d'autres outils dérivés.


Après avoir vu la réponse de Ben ci-dessous, je vois que l'utilisation d'un constructeur dans un cadre contrôlé est en fait assez utile. Si vous l'utilisez dans un objet hérité, vous pouvez réellement encapsuler le constructeur à l'intérieur du DAL afin que le BLL ne puisse pas y voir réellement. Donc, cela peut réellement fonctionner.


@Bobthebuilder - quel est exactement le problème sous-jacent que vous voyez?


@Jeff Sternal - Je n'aime pas le fait que tout développeur puisse instancier l'objet et définir des champs ne devrait pas être réglable sauf dans certaines circonstances très spéciales, par exemple lors du chargement des données d'une base de données ou désériorialisation d'un objet. Ces champs sont calculés dans la base de données et, en tant que tels, vous ne devriez pas être capable de les manipuler dans des circonstances normales - à des fins d'utilisation de l'API, elles devraient être immuables.


@BobtheBuilder qui n'explique toujours pas ce qui vous a fait penser que les paramétrages sur le constructeur ne résout pas le problème --- Cela permet à ce que vous venez de dire: définissez des champs lors du chargement de l'objet qui ne peut pas être modifié.


@Jeff Sternal - Je pensais à cela du mauvais angle; Avoir le constructeur sur mon objet de base n'est pas ce que je voulais. Je me rends compte maintenant que je puisse avoir un objet dérivé à l'intérieur de mon dal avec un constructeur qui définit les propriétés que le BLL n'a qu'un accès en lecture. C'était une question de mon interprétation erronée où ce constructeur devrait aller - il devrait être sur mon objet dérivé, pas mon objet de base.



0
votes

Que diriez-vous de vivre avec elle?

Mise en œuvre avec ces directives, mais n'ajoutez pas une telle contrainte difficile dans votre modèle. Disons que vous le faites, mais ensuite venir un autre req où vous devez le sérialiser ou faire autre chose, puis vous êtes attaché avec.

Comme vous l'avez dit dans d'autres commentaires, vous voulez des pièces interchangeables ... Donc, fondamentalement, vous ne voulez pas quelque chose qui est attaché dans des relations spécifiques.


Mise à jour 1: peut-être "vient de vivre avec elle" était trop simpliste, mais je dois toujours insister sur que tu ne devrais pas aller trop profondément dans ces choses. En utilisant des directives simples, en gardant votre code propre et solide son meilleur que vous puissiez faire au début. Cela ne va pas dans la voie du progrès tout en refactorisant quand tout est plus réglé n'est pas difficile.

Ne faites aucune erreur, je ne suis pas du tout une personne qui va écrire du code sans y penser. Mais, je suis allé avec de telles approches et seulement dans des cas poignées, sans indication que vous n'auriez pas un résultat similaire en passant simple et en évoluant.

IMHO Celui-ci ne s'intègre pas dans des problèmes d'architecture importants qui doivent être traités au tout début.

Suivi préventif: méfiez-vous si vous ne pouvez pas faire confiance à votre équipe en suivant des directives simples. Assurez-vous également de commencer par une certaine structure, choisissez un couple de scénarios qui définissaient une structure avec de vraies choses, l'équipe saura bien mieux quand il y a quelque chose simple là.


2 commentaires

Parce que "vivre avec elle" est la mauvaise réponse. Vous ne devriez pas être capable de définir des valeurs sur des champs basés sur des champs calculés dans la base de données. Je comprends les problèmes de sérialisation, mais il doit y avoir des modèles pour ces scénarios. Je suis désolé, mais je vais devoir dire que «vivre avec elle», ce n'est tout simplement pas l'attitude que je prends pour programmer. Je préférerais faire quelque chose de juste que de vivre avec ça. Peut-être que mon modèle SOC a besoin d'une revue si cela ne peut pas être fait.


Je peux voir les deux côtés ... Il y a des moments où le design «idéal» est trop extrême, une convention bien communiquée peut suffire dans de nombreux cas. Cela dit - à condition que le motif de référentiel soit suivi pour la DAL - Trouver un moyen de limiter le jeu des propriétés des référentiels semble parfaitement raisonnable.



1
votes

C'est une situation sans balle d'argent; Les options les plus simples sont limitées ou ne répondent pas à vos besoins et que les solutions approfondies commencent à avoir des odeurs ou commencent à quitter la simplicité.

Peut-être que l'option la plus simple est celle que je n'ai pas vue mentionnée ici: Garder les champs / Propriétés privées et les transmettent comme out code> / byref code> paramètres sur DAL. Bien que cela ne fonctionnerait pas pour un grand nombre de champs, ce serait simple pour un petit nombre. P>

(Je ne l'ai pas testé, mais je pense que cela vaut la peine d'explorer). P>

public class MyObject()
{
    private int _Id;
    public int Id { get { return _Id; } } // Read-only

    public string Name { get; set; }

    // This method is essentially a more descriptive constructor, using the repository pattern for seperation of Domain and Persistance
    public static MyObject GetObjectFromRepo(IRepository repo)
    {
        MyObject result = new MyObject();
        return repo.BuildObject(result, out _Id);            
    }
}

public class MyRepo : IRepository
{
    public MyObject BuildObject(MyObject objectShell, out int id)
    {
        string objectName;
        int objectId;

        // Retrieve the Name and Value properties
        objectName = "Name from Database";
        objectId = 42;
        //

        objectShell.Name = objectName;
        Console.WriteLine(objectShell.Id); // <-- 0, as it hasn't been set yet
        id = objectId; // Setting this out parameter indirectly updates the value in the resulting object
        Console.WriteLine(objectShell.Id); // <-- Should now be 42
    }
}


2 commentaires

Après avoir donné une pensée, je ne suis pas sûr que j'aime bien le décès des propriétés de refait dans ce cas, il se sent boueux et potentiellement déroutant - même si je comprends ce que votre code en reçoit, il ajoute une confusion inutile pour les futurs développeurs qui peuvent être Maintenir ce code.


C'est vrai, les propriétés Ref ont toujours un peu une odeur et ne sont jamais intuitives. Cependant, j'ai évalué des solutions à ce problème exacte et c'est la seule façon de constater que cela empêche explicitement les objets externes de définir des valeurs et ne laissant aucune dépendance sur des référentiels spécifiques. Toutes les autres solutions ne sont pas appliquées au moment de la compilation (comme définissant une propriété «isocké» sur l'objet et de lancer une exception si une tentative est faite pour définir la propriété après le verrouillage de l'objet), ou n'abandonnez simplement pas l'exigence .



1
votes

Ceci garde votre modèle de SOC bien, il ne peut pas ajouter trop de complexité, cela empêche l'écriture de lire des champs en lecture seule et vous pouvez utiliser un modèle très similaire pour les préoccupations de sérialisation. Vos champs en lecture seule peuvent toujours être écrits par votre DAL, de même que votre sérialisateur s'il est utilisé de la même manière - cela signifie que les efforts conscients doivent être pris par un développeur pour écrire à un champ en lecture seule qui empêche toute utilisation abusive non intentionnelle. < p> modèle de modèle xxx

projet de données < / p> xxx


3 commentaires

Hmm, je ne suis toujours pas vraiment sûr que j'aime bien le constructeur que vous avez utilisé, cependant, il cache le constructeur du monde extérieur à toutes fins intensifs, donc je pense que j'aime cette approche. J'aime le fait que ce modèle aborde la sérialisation et conserve le SOC et j'aime que les champs en lecture seule sont réellement conservés comme tels. Merci +1


@BobtheBuilder - Ce serait bien si vous pouviez remplacer le constructeur protégé de l'objet de base, mais malheureusement que ce n'est pas possible. Par conséquent, il n'y a que deux façons de définir les propriétés: via un constructeur ou une méthode. La voie la plus succincte étant le constructeur.


@BobtheBuilder - au moins cette méthode signifie que vous devez décider consciemment que je sais que ces propriétés sont protégées et que je dois le faire afin de leur écrire. Il empêche donc des erreurs non intentionnelles.



0
votes

À mon avis, le meilleur moyen de gérer cela est d'avoir les objets métier et le DAL dans le même assemblage séparé par l'espace de noms. Cela sépare de manière logique les préoccupations et vous permet d'utiliser des setters internes. Je ne peux penser à aucun avantage à les séparer dans leurs propres assemblées, car on est inutile sans l'autre.


3 commentaires

C'est un point de vue que je suppose. Cependant, mon point de vue est que la couche logique ne doit pas nécessiter une couche de données utile; La couche de données ne doit pas nécessiter que la couche logique soit utile; La couche d'objet ne doit pas nécessiter une couche logique ni une couche de données car elle devrait simplement être des données; Mais la couche de données et la couche logique nécessiteront à la fois la couche d'objet car elles ont besoin d'objets pour travailler. Par conséquent, les objets doivent être de leur propre montage, la logique doit être dans son propre assemblage et la couche de données doit être dans sa propre assemblée.


Mettez ces morceaux ensemble et vous avez construit une application qui espère être utile. La séparation logiquement des pièces par espace de noms remplit la séparation des préoccupations. La séparation par assemblage rend considérablement plus de travail pour créer une application et n'a aucun avantage pratique.


À mon avis, le DAL devrait être un assemblage complètement séparé, de cette façon, lorsque votre magasin de données doit être échangé, il suffit de remplacer une seule DLL, vous n'avez pas besoin de recompiler quoi que ce soit d'autre.