Cette discussion est consacrée à l'article intitulé "Quelle est la différence entre un DTO et un POCO ?" qui est la traduction d'un article de Rudy Lacovara.
Postez ici vos commentaires concernant cette publication.
Cette discussion est consacrée à l'article intitulé "Quelle est la différence entre un DTO et un POCO ?" qui est la traduction d'un article de Rudy Lacovara.
Postez ici vos commentaires concernant cette publication.
Excellent article ! très utile. Maintenant, j'ai une petite question :
Quid du DTO pour lequel on est obligé d'insérer la logique de sérialisation car il implémente IXmlSerializable ? c'est un DTO, un POCO, ni l'un ni l'autre ?
Sinon, concernant les différents schémas d'implantation, en voici un que j'utilise tous les jours :
J'ai ma couche d'accès aux données branchée via l'ORM Entitiy Framework.
J'ai donc une partie DAL me permettant d'effectuer les requetes linq to entities.
Ensuite, j'ai une parti BLL qui me permet de faire tous les mix de requetes dont j'ai besoin et ressortir un résultat métier.
Entre la DAL et la BLL, je transforme (de façon bilatérale) mes Entities en DTO et inversement.
Le tout est au final utilisable avec n'importe quel projet et peut même etre partagé entre projets de types différents.
Dans mon cas, j'ai branché un webservice WCF à la BLL et il va y avoir une partie ASP.Net MVC2 qui viendra plus tard se brancher sur cette meme BLL.
Ce qu'il y a d'intéressant avec les DTO, c'est que derriere ils peuvent être importés dans tous les projets se servant de ma BLL (ou meme de mon webservice).
En tout cas, merci pour l'explication
chez moi toutes les couches d'une application ont accès aux DTO , seul la couche "business" a acces aux POCO ( ou pojo , etc ... ).
un POCO est forcément lié au business layer,puisqu'il a un comportement.
Un DTO peut par contre alimenter un template , le modèle , ou n'importe quoi d'autre.
Article clair et didactique !
Je viens donc de réaliser que je suis depuis plus de dix ans un M. Jourdain du POCO/DTO.
Dites moi, dans vos projets, comment faites vous la translation POCO<->DTO ?
Avez vous des petits outils, des patterns ou autres qui permettent d'automatiser un peu la transformation ? ou bien gérez vous la copie des propriétés à la mano ?
Qu'entends-tu par translation/transformation ?
Le lien entre un DTO et son POCO correspondant est qu'un POCO a un DTO, les propriétés ne sont pas "copiées" :
Les propriétés du DTO sont accessibles dans le POCO via la propriété Data.
Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 public class PersonDTO { // Propriétés du DTO... } public class PersonPOCO { public PersonDTO Data { get; set; } public PersonPOCO(PersonDTO dto) { this.Data = dto; } }
Voir l'héritage "a un", dans le paragraphe "Alors qu'est-ce qu'un POCO ?"
Je me joins aux autres pour te féliciter de ton choix de traduction, l'article choisi est excellent, et la traduction de qualité
bravo
Confronté à un problème d'architecture, je me suis penché sur cet article.
Et je me demande comment gérer la composition/agrégation dans ce modèle ?
En effet, mes objets ont quelques propriétés de type primitif, mais aussi un gros paquet d'instances, voire de collections, d'autre classes.
Pour être précis sur ma question : à quel niveau gère-t-on la composition ?
Au niveau des POCO tu peux utiliser tous les concepts de la POO sans problèmes mais pour les DTO tu peux aussi le faire mais il faut avant tout savoir que les DTO c'est pour faire transiter des données être un pont entre deux parties ou doit contenir des informations minimalistes dont on a besoin quand on travaille par exemple avec des services telles que WCF. Bref un DTO ne doit pas contenir la complexité de ton modèle ou domaine. Si tu te mets à te poser des questions de composition excédant deux niveaux alors là je ne vois plus l'intérêt d'utiliser les DTO autant utiliser directement ton modèle pour ne pas avoir à tout le recréer en intégrant sa complexité dans les DTO.
Hello,
Comme tout le monde l'a déjà dit, il s'agit là d'un excellent article (comme tous les autres (ou presque) du même auteur).
Par contre cela me pose un souci.
Avant, la couche BLL passait directement les DTO ou les listes de DTO à la couche GUI qui les utilisait et les renvoyait vers BLL.
A la lumière de cet article, GUI utilise maintenant des objets de la BLL (des POCO donc) qui contiennent les DTO.
Mais du coup, comment on fait pour affecter les propriétés DisplayMember et ValueMember des classes du genre ComboBox ou ListBox (pour DisplayMember, j'ai triché en surchargeant la méthode ToString du POCO mais c'est du bricolage non ?)
Et je n'ai pas encore testé mais j'imagine que je vais avoir le même problème pour affecter la propriété DataPropertyName de la classe DataGridViewColumn.
Comme faites-vous donc ?
Salut,
Dans notre projet (Silverlight / WCF) on avait cette problématique étant donné que le fait de recevoir d'une part, une liste de DTO pour alimenter l'itemssource du combobox et, d'autre part, le DTO correspondant à l'élement actuellement sélectionné via le webservice faisait que les deux DTO identiques (celui qui est la sélection courante et celui qui fait partie de la liste et qui en base correspondent a la meme ligne) n'avaient pas la meme adresse mémoire.
Du coup, en surchargeant la méthode Equals pour faire matcher les property Id plutot que les références mémoire a résolu le problème.
Euh... J'ai pas compris
Comment utilises tu ta combo, peux tu envoyer un exemple ? parce que je ne sais meme pas dans quel type de projet tu bosses (winforms, wpf, silverlight, win8, wp8 ?)
Je fais du winforms.
Mais je pense que je faisais de la merde (j'ai contourné le problème en faisant autrement et ça va mieux maintenant ^^).
Exemple de ce que je faisais :
J'avais un poco Person avec une propriété DTO as PersonDTO et un poco Persons qui héritait de List(Of Person).
Du coup, dans la GUI, j'utilisais des objets du type Persons pour manipuler des listes. Mais ce genre de truc, c'est pas pratique à passer en datasource.
Maintenant, le poco Persons n'hérite plus de List(Of PersonDTO) mais a une propriété DTO as List(Of PersonDTO). Et ça va tout de suite mieux ^^
Désolé de déterrer ce topic que je découvre tardivement.
Est-ce que par hasard un développeur fainéant comme moi aurait créé un outil pour automatiser la création des classes décrites dans cet article très intéressant pour une base de données entière ?
Qui ne tente rien n'a rien
Il y a des outils qui existent déjà comme entity framework mais personnellement, je n'aime pas.
Dans mon ancien taff, j'avais développé ça en me basant sur le catalogue de la db relationnelle utilisée mais cela nécessite d'avoir une db fortement normalisée pour avoir quelque chose de propre. Ca m'avait pris 1/2 journée pour avoir qqch de fonctionnelle donc rien d'insurmontable
Justement le but est de ne pas utiliser EF, que je connais mal, mais que beaucoup de développeurs n'aiment pas, sûrement pour de bonnes raisons.
Du coup j'ai trouvé un script pour créer les DTO à partir de la base de données.
Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143 DECLARE @Schema VARCHAR(MAX) = N'dbo' DECLARE @TableName VARCHAR(MAX) = N'ENT' DECLARE @Namespace VARCHAR(MAX) = N'MonApplication.DTO' DECLARE @Chemin VARCHAR(MAX) = 'C:\MonApplication\' + @TableName + '.cs' DECLARE @CRLF VARCHAR(2) = CHAR(13) + CHAR(10); DECLARE @result VARCHAR(max) = ' ' DECLARE @constructor VARCHAR(max) = ' ' DECLARE @PrivateProp VARCHAR(100) = @CRLF + --' /// <summary>' + @CRLF + --' /// ' + @CRLF + --' /// </summary>' + @CRLF + CHAR(9) + CHAR(9) + 'public <ColumnType> <ColumnName> { get; set; }' + @CRLF; DECLARE @LigneAffectation VARCHAR(100) = ' <ColumnName> = CommonBase.<ColumnType>;' + @CRLF; SET @result = 'using System;' + @CRLF + @CRLF + 'using MonApplication.COMMON;' + @CRLF + @CRLF + 'namespace ' + @Namespace + @CRLF + '{' + @CRLF + --' /// <summary>' + @CRLF + --' /// ' + @CRLF + --' /// </summary>' + @CRLF + ' public class ' + @TableName + ' : DTOBase' + @CRLF + ' {' + @CRLF + ' #region Instance Properties' + @CRLF SET @constructor = '///<summary>' + @CRLF + ' /// Constructeur' + @CRLF + ' /// Aucun paramètre et tous les types sont initialisés à leur' + @CRLF + ' /// valeurs nulles telles que définies dans CommonBase.' + @CRLF + ' ///</summary>' + @CRLF + ' public ' + @TableName + ' ()' + @CRLF + ' {' + @CRLF SELECT @constructor = @constructor + REPLACE( REPLACE(@LigneAffectation , '<ColumnName>', ColumnNameC) , '<ColumnType>', ColumnTypeC) FROM ( SELECT cc.COLUMN_NAME AS ColumnNameC , CASE cc.DATA_TYPE WHEN 'bigint' THEN 'Int_NullValue' WHEN 'binary' THEN 'Int_NullValue' WHEN 'bit' THEN 'Int_NullValue' WHEN 'char' THEN 'String_NullValue' WHEN 'date' THEN 'DateTime_NullValue' WHEN 'datetime' THEN 'DateTime_NullValue' WHEN 'datetime2' THEN 'DateTime_NullValue' WHEN 'datetimeoffset' THEN 'DateTimeOffSet_NullValue' WHEN 'decimal' THEN 'Decimal_NullValue' WHEN 'float' THEN 'Float_NullValue' WHEN 'image' THEN 'ByteTab_NullValue' WHEN 'int' THEN 'Int_NullValue' WHEN 'money' THEN 'Decimal_NullValue' WHEN 'nchar' THEN 'String_NullValue' WHEN 'ntext' THEN 'String_NullValue' WHEN 'numeric' THEN 'Decimal_NullValue' WHEN 'nvarchar' THEN 'String_NullValue' WHEN 'real' THEN 'Double_NullValue' WHEN 'smalldatetime' THEN 'DateTime_NullValue' WHEN 'smallint' THEN 'Int_NullValue' WHEN 'smallmoney' THEN 'Decimal_NullValue' WHEN 'text' THEN 'String_NullValue' WHEN 'time' THEN 'TimeSpan_NullValue' WHEN 'timestamp' THEN 'DateTime_NullValue' WHEN 'tinyint' THEN 'Byte_NullValue' WHEN 'uniqueidentifier' THEN 'Guid_NullValue' WHEN 'varbinary' THEN 'ByteTab_NullValue' WHEN 'varchar' THEN 'String_NullValue' ELSE 'Object' END AS ColumnTypeC , cc.ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS cc WHERE cc.TABLE_NAME = @TableName AND ISNULL(@Schema, cc.TABLE_SCHEMA) = cc.TABLE_SCHEMA ) tc ORDER BY tc.ORDINAL_POSITION SELECT @result = @result + REPLACE( REPLACE(@PrivateProp , '<ColumnName>', ColumnName) , '<ColumnType>', ColumnType) FROM ( SELECT c.COLUMN_NAME AS ColumnName , CASE c.DATA_TYPE WHEN 'bigint' THEN 'Int64' WHEN 'binary' THEN 'Byte[]' WHEN 'bit' THEN 'Boolean' WHEN 'char' THEN 'String' WHEN 'date' THEN 'DateTime' WHEN 'datetime' THEN 'DateTime' WHEN 'datetime2' THEN 'DateTime' WHEN 'datetimeoffset' THEN 'DateTimeOffset' WHEN 'decimal' THEN 'Decimal' WHEN 'float' THEN 'Single' WHEN 'image' THEN 'Byte[]' WHEN 'int' THEN 'Int32' WHEN 'money' THEN 'Decimal' WHEN 'nchar' THEN 'String' WHEN 'ntext' THEN 'String' WHEN 'numeric' THEN 'Decimal' WHEN 'nvarchar' THEN 'String' WHEN 'real' THEN 'Double' WHEN 'smalldatetime' THEN 'DateTime' WHEN 'smallint' THEN 'Int16' WHEN 'smallmoney' THEN 'Decimal' WHEN 'text' THEN 'String' WHEN 'time' THEN 'TimeSpan' WHEN 'timestamp' THEN 'DateTime' WHEN 'tinyint' THEN 'Byte' WHEN 'uniqueidentifier' THEN 'Guid' WHEN 'varbinary' THEN 'Byte[]' WHEN 'varchar' THEN 'String' ELSE 'Object' END AS ColumnType , c.ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = @TableName AND ISNULL(@Schema, c.TABLE_SCHEMA) = c.TABLE_SCHEMA ) t ORDER BY t.ORDINAL_POSITION SELECT @result = @result + @CRLF + CHAR(9) + ' #endregion Instance Properties' + @CRLF + CHAR(9) + @constructor + @CRLF + CHAR(9) + '}' + @CRLF + CHAR(9) + '}' + @CRLF + @CRLF + '}' --SELECT @result EXEC USP_SaveFile @Result,@Chemin print CAST(@Result AS TEXT)
Maintenant si quelqu'un en a un plus complet pour générer les classes des couches supérieures décrites dans l'article, ça m'intéresse au plus haut point
Je n'ai rien de tout fait, mais tu peux aussi regarder du cote des templates T4. Ca permet de coder (en C# / VB.NET) la generation de tes classes : Code Generation and T4 Text Templates.
Partager