IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 Delphi Discussion :

Organisation des fichiers d'un gros projet


Sujet :

Delphi

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2013
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2013
    Messages : 21
    Points : 14
    Points
    14
    Par défaut Organisation des fichiers d'un gros projet
    Bonjour,

    Je vous explique mon cas. Je vais bientôt démarrer un projet de mise à jour d'une application existante sous Delphi. Plusieurs points doivent être revu pour en améliorer la maintenance future :
    - dans un premier temps, je souhaiterai développer l'application de façon modulaire, pour que chaque partie des applications soient indépendante. Cela pourrait faciliter la maintenance si un problème survient dans un module spécifique. Je trouve très peu de documentation sur le sujet en Delphi. Est ce faisable ?
    - deuxièmement, je souhaiterai revoir l'architecture des fichiers de développement. J'ai une certaine culture du MVC pour mes projets webs perso mais un tel pattern est il adaptable à un projet Delphi ? Pour information, l'application finale sera une application VCL qui à accès à une base de données. Où alors, que me conseillez vous pour architecture les fichiers ? (*.pas, *.dfm, unités, datamodule, ...)

    J'espère que vos réponses pourront éclaircir mes recherches

    Florian

  2. #2
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 445
    Points
    28 445
    Par défaut
    il existe des projets MVC pour Delphi (cf Google), mais ce n'est pas la démarche la plus naturelle.

    par contre ce qu'il manque souvent c'est une séparation des traitements et de l'UI. Une façon relativement efficace d'y parvenir est d'utiliser des DataModule (si possible sans rattachement à la VCL) pour placer les composants d'accès aux données et les méthodes métiers, quitte à ajouter des unités hors VCL avec des objets métiers. Cette approche facilite la refonte de 'l'UI, voir le passage à Firemonkey, ou la création de services web, traitements de fond, sans UI à partir du même code.

    c'est en fait l'usage des DB controls qui va mettre à défaut MVC, mais il est assez dommage de s'en passer.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2013
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2013
    Messages : 21
    Points : 14
    Points
    14
    Par défaut
    Merci de votre réponse.

    Actuellement, les applications possèdent un DataModule qui concentre tous les liens vers la base de données (un Singleton). Chaque fenêtres possède son *.pas et *.dfm et on a quelques unités qui permettent de regrouper certaines fonctions.
    N'y a-t-il pas un moyen de mieux organiser cela ? Aucun Framework existant ? Cela me parait bizarre qu'on ne puisse pas mieux organiser un projet Delphi.

    Florian

  4. #4
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 263
    Points
    3 263
    Par défaut
    Bonjour,

    Cogimaflorian : Chaque fenêtres possède son *.pas et *.dfm et on a quelques unités qui permettent de regrouper certaines fonctions.
    N'y a-t-il pas un moyen de mieux organiser cela ? Aucun Framework existant ? Cela me parait bizarre qu'on ne puisse pas mieux organiser un projet Delphi.
    Bin, tout dépend de la façon avec laquelle ces quelques unités regroupent le fonctions :
    Dans un gros projet on peut placer toutes les routines qui peuvent être appelées depuis plusieurs (>=2) *.dfm dans une unité uGlobal.pas dont le nom est déclaré dans le uses du *.pas associé au *.dfm.
    Les routines qui ne sont appelées que depuis un seul *.dfm on peut les placer au début de la partie implémentation du *.pas associé. (Cela facilite la lisibilité, donc la maintenance)
    On peut aussi regrouper des fonctions dans une *.DLL.

    A+.
    N'oubliez pas de consulter les FAQ Delphi et les cours et tutoriels Delphi

  5. #5
    Membre averti Avatar de Moez.B
    Homme Profil pro
    Développeur Delphi
    Inscrit en
    Mars 2006
    Messages
    219
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Tunisie

    Informations professionnelles :
    Activité : Développeur Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2006
    Messages : 219
    Points : 370
    Points
    370
    Par défaut
    Bonjour,

    Pour grouper les fichiers d'un grand projet Delphi, il faudra peut être séparer les fichiers par modules. Exemple: dans une gestion commerciale, on sépare la gestion des articles de celle de la gestion des opérations commerciales. C'est une séparation contextuelle selon l'axe du métier dans lequel on est entrain de développer.
    Ensuite, c'est du cas pas cas: un MVC en Delphi même si ce n'est pas trop naturel doit englober cette hiérarchie : tout ce qui est visuel et IHM c'est des répertoires "vue", tout ce qui est modèle c'est dans des répertoires "Model" et la partie contrôleur dans "Controller" par exemple :

    • ./MonProjet/Gestion des opérations commerciales/Vente/VenteVue
    • ./MonProjet/Gestion des opérations commerciales/Vente/VenteModel
    • ./MonProjet/Gestion des opérations commerciales/Vente/VenteController


    Si on dispose d'une fiche qui gère des ventes particulières, par exemple, on a :

    • ./MonProjet/Gestion des opérations commerciales/Vente/VenteVue/GestionVenteParticuliereVue.pas
    • ./MonProjet/Gestion des opérations commerciales/Vente/VenteVue/GestionVenteParticuliereVue.dfm
    • ./MonProjet/Gestion des opérations commerciales/Vente/VenteVue/GestionVenteParticuliereModel.pas
    • ./MonProjet/Gestion des opérations commerciales/Vente/VenteVue/GestionVenteParticuliereModel.dfm
    • ./MonProjet/Gestion des opérations commerciales/Vente/VenteVue/GestionVenteParticuliereController.dfm
    • ./MonProjet/Gestion des opérations commerciales/Vente/VenteVue/GestionVenteParticuliereController.dfm


    On peut avoir des types en communs par exemple pour ceux qui pratiquent l'héritage des forms ou bien les objets ancêtres : on peut le gérer dans des endroits comme :

    • ./MonProjet/Communs/GeneralVue.pas
    • ./MonProjet/Communs/GeneralVue.dfm
    • ./MonProjet/Communs/GeneralModel.dfm
    • ./MonProjet/Communs/GeneralModel.dfm
    • ./MonProjet/Communs/GeneralController.dfm
    • ./MonProjet/Communs/GeneralController.dfm


    on peut externaliser des fonctions appelées dans d'autres programmes ou par des WebServices dans des fichiers dll à la racine du projet.
    on peut créer des fichiers ressources, par ailleurs, pour regrouper des fichiers images qui servent à des icônes, des glyphs etc ...
    Si on utilise les DataModules, on peut les grouper avec les fichiers en liaisons du contexte : ça peut servir dans les cas on n'utilise pas une architecture en MVC mais qu'on utilise des composants DB-Aware
    :

    • ./MonProjet/Gestion des opérations commerciales/Vente/DataMod/DMVenteParticuliere.pas
    • ./MonProjet/Gestion des opérations commerciales/Vente/DataMod/DMVenteParticuliere.dfm


    Bonne journée
    "L'homme supérieur est celui qui a une bienveillance égale pour tous, et qui est sans égoïsme et sans partialité." [Confucius]
    "Celui qui n'évolue pas disparaît." [Charles Darwin]
    “Without requirements or design, programming is the art of adding bugs to an empty text file.” [Louis Srygley]

  6. #6
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2013
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2013
    Messages : 21
    Points : 14
    Points
    14
    Par défaut
    Merci de vos réponses, j'y vois déjà un peu plus clair dans l'organisation que pourrait prendre notre projet.

    Moez.B : Auriez-vous un ou deux exemples de code sources de projet Delphi correctement architecturé? Éventuellement avec une séparation des vues, des controllers et des models?

    Gilbert Geyer : L'architecture que vous me proposez est plus ou moins déjà celle utilisé. L'utilisation de *.dll me faisant peur, après avoir déjà des retours négatifs sous environnement Citrix (une histoire d'optimisation de DLL ).

    Merci d'avance


  7. #7
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Sur Citrix, tant qu'un EXE est ouvert (et ses DLL), le fichier est verrouillé, donc on ne pas le mettre à jour !
    Plusieurs exe pouvant utiliser la même DLL, Windows lui même ne duplique la DLL autant de fois que nécessaire mais ça je le laisse les pro de la mémoire et de l'OS en dire plus ...

    Un collègue a évoqué l'existence d'un cache Citrix mais le service infrastructure qui gère les fermes Citrix n'a pas confirmé cette chose !

    Où je travaille, on a souvent des utilisateurs qui ne ferme pas leur session, du coup, le déploiement est pénible (kill de remote session ...)


    Pour la structure, pour le MVC, c'est ce que je fais !
    Et comme cela a été dit ce n'est pas naturel
    J'ai ma propre couche Entity et un truc genre LinQ que j'ai bricolé,
    lorsque j'ai montré ça à mes collègues habitués au DataModule avec 50 Query posées dessus, euh disons que l'accueil fut assez froid !

    Tu peux voir le DFM comme la Vue
    Le DataModule comme le Model\Controller fusionné, oui c'est plus du MVC mais ... le RAD n'aide pas !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  8. #8
    Membre averti Avatar de Moez.B
    Homme Profil pro
    Développeur Delphi
    Inscrit en
    Mars 2006
    Messages
    219
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Tunisie

    Informations professionnelles :
    Activité : Développeur Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2006
    Messages : 219
    Points : 370
    Points
    370
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message

    Pour la structure, pour le MVC, c'est ce que je fais !
    Et comme cela a été dit ce n'est pas naturel
    J'ai ma propre couche Entity et un truc genre LinQ que j'ai bricolé,
    lorsque j'ai montré ça à mes collègues habitués au DataModule avec 50 Query posées dessus, euh disons que l'accueil fut assez froid !

    Tu peux voir le DFM comme la Vue
    Le DataModule comme le Model\Controller fusionné, oui c'est plus du MVC mais ... le RAD n'aide pas !
    Si la couche Entity correspond après au modèle, comment tu as fais pour concevoir une couche de Link entre les composants et les entités générées et gérées après ?
    Par exemple, un datasource est branché à un dataset et les composants DB placés sur une fiche sont branchés à ce datasource.
    Pour ce qui est entité maintenant, est ce que tu reprends directement le dataSet auquel tu as mappé ton entité ou bien tu crées de nouveaux composants qui sont basés sur l'entité en elle même ?
    Comment gérer le AfterScroll par exemple ou bien le BeforeDelete qui sont sur un DataSet sur des composants orientés modèle si c'est le cas ?
    Je dis ça parce que je me positionne auprès de tes collègues pour me demander justement sur le après des choses.
    Et la question que je me pose à chaque fois : si un modèle MVC séparant les différences couches est le concept de base pour toute architecture d'une nouvelle application, pourquoi Embarcadero ne pense pas à mettre en place son propre FrameWork MVC et remplacer le DataSet et les composants orientés base de données vers des DataObject par exemple et des composants orientés entité ?

    Merci
    "L'homme supérieur est celui qui a une bienveillance égale pour tous, et qui est sans égoïsme et sans partialité." [Confucius]
    "Celui qui n'évolue pas disparaît." [Charles Darwin]
    “Without requirements or design, programming is the art of adding bugs to an empty text file.” [Louis Srygley]

  9. #9
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Citation Envoyé par Moez.B Voir le message
    Si la couche Entity correspond après au modèle, comment tu as fais pour concevoir une couche de Link entre les composants et les entités générées et gérées après ?
    Par exemple, un datasource est branché à un dataset et les composants DB placés sur une fiche sont branchés à ce datasource
    Mon speudo LINQ to SQL c'est juste la génération de SQL, ce n'est pas du LinQ To Entity
    C'est plus proche d'un Zend DB Select
    Au final, j'ai effectivement un TDataSet (même si les objets peuvent fonctionner avec un mode sans DataSet)

    exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ClientList := TClient.CreateAsSet(); // Create c'est un objet mono-ligne, CreateAsSet c'est un objet list
    ClientList.Select().Where(TClient.Schema.FieldNames[TClient.TProperties.ChampTruc], opEgal, 'Bidule');
    DataSource1.DataSet := ClientList.DataSet;
    Il va générer le SELECT ... avec les champs nécessaires qui correspond au champ published dans l'objet



    Citation Envoyé par Moez.B Voir le message
    pourquoi Embarcadero ne pense pas à mettre en place son propre FrameWork MVC et remplacer le DataSet et les composants orientés base de données vers des DataObject par exemple et des composants orientés entité ?
    BOLD en Delphi 7
    ECO en Delphi.NET

    Oui cela manque le Entity et MVC, surtout avec les LiveBindings on pourrait avoir un truc sympa !

    Si tu es curieux
    Voici mon nouveau délire de 2012 sous DXE2 sous le nom de TSliteDBEntity \ TSLTDBEntityEngine
    c'est une fusion de ma persitance codé en D7 : TEpcPersistant et celle en BCB2007 : TShaiORPersistent

    Code : 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
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    587
    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    627
    628
    629
    630
    631
    632
    633
    634
    635
    636
    637
    638
    639
    640
    641
    642
    643
    644
    645
    646
    647
    648
    649
    650
    651
    652
    653
    654
    655
    656
    657
    658
    659
    660
    661
    662
    663
    664
    665
    666
    667
    668
    669
    670
    671
    672
    673
    674
    675
    676
    677
    678
    679
    680
    681
    682
    683
    684
    685
    686
    687
    688
    689
    690
    691
    692
    693
    694
    695
    696
    697
    698
    699
    700
    701
    702
    703
    704
    705
    706
    707
    708
    709
    710
    711
    712
    713
    714
    715
    716
    717
    718
    719
    720
    721
    722
    723
    724
    725
    726
    727
    728
    729
    730
    731
    732
    733
    734
    735
    736
    737
    738
    739
    740
    741
    742
    743
    744
    745
    746
    747
    748
    749
    750
    751
    752
    753
    754
    755
    756
    757
    758
    759
    760
    761
    762
    763
    764
    765
    766
    767
    768
    769
    770
    771
    772
    773
    774
    775
    776
    777
    778
    779
    780
    781
    782
    783
    784
    785
    786
    787
    788
    789
    790
    791
    792
    793
    794
    795
    796
    797
    798
    799
    800
    801
    802
    803
    804
    805
    806
    807
    808
    809
    810
    811
    812
    813
    814
    815
    816
    817
    818
    819
    820
    821
    822
    823
    824
    825
    826
    827
    828
    829
    830
    831
    832
    833
    834
    835
    836
    837
    838
    839
    840
    841
    842
    843
    844
    845
    846
    847
    848
    849
    850
    851
    852
    853
    854
    855
    856
    857
    858
    859
    860
    861
    862
    863
    864
    865
    866
    867
    868
    869
    870
    871
    872
    873
    874
    875
    876
    877
    878
    879
    880
    881
    882
    883
    884
    885
    886
    887
    888
    889
    890
    891
    892
    893
    894
    895
    896
    897
    898
    899
    900
    901
    902
    903
    904
    905
    906
    907
    908
    909
    910
    911
    912
    913
    914
    915
    916
    917
    918
    919
    920
    921
    922
    923
    924
    925
    926
    927
    928
    929
    930
    931
    932
    933
    934
    935
    936
    937
    938
    939
    940
    941
    942
    943
    944
    945
    946
    947
    948
    949
    950
    951
    952
    953
    954
    955
    956
    957
    958
    959
    960
    961
    962
    963
    964
    965
    966
    967
    968
    969
    970
    971
    972
    973
    974
    975
    976
    977
    978
    979
    980
    981
    982
    983
    984
    985
    986
    987
    988
    989
    990
    991
    992
    993
    994
    995
    996
    997
    998
    999
    1000
    1001
    1002
    1003
    1004
    1005
    1006
    1007
    1008
    1009
    1010
    1011
    1012
    1013
    1014
    1015
    1016
    1017
    1018
    1019
    1020
    1021
    1022
    1023
    1024
    1025
    1026
    1027
    1028
    1029
    1030
    1031
    1032
    1033
    1034
    1035
    1036
    1037
    1038
    1039
    1040
    1041
    1042
    1043
    1044
    1045
    1046
    1047
    1048
    1049
    1050
    1051
    1052
    1053
    1054
    1055
    1056
    1057
    1058
    1059
    1060
    1061
    1062
    1063
    1064
    1065
    1066
    1067
    1068
    1069
    1070
    1071
    1072
     
    //------------------------------------------------------------------------------
    (*                SoLuTions is an Versatile Library for Delphi                 -
     *                                                                             -
     *  Version alternative publiée sur "www.developpez.net"                       -
     *  Post : "[BDD] Séparation de couche de données [Résolu]"                    -
     *  Post Number : 2899656                                                      -
     *  Post URL = "http://www.developpez.net/forums/d481448/environnements-developpement/delphi/bases-donnees/bdd-separation-couche-donnees/#post2899656"
                                                                                   -
     *  Copyright ou © ou Copr. "SLT Solutions", (2006)                            -
     *  contributeur : ShaiLeTroll (2009) - Refonte architecturale (Séparation de l'ORM : l'EPC "Engine of Persistence Component" gère uniquement une ORM très rigide, la nouvelle persistance sépare la partie RTTI de la gestion DB)
     *  contributeur : ShaiLeTroll (2012) - Renommage Fichier et Correction XE2    -
     *  contributeur : ShaiLeTroll (2012) - Documentation Insight                  -
     *  contributeur : ShaiLeTroll (2013) - Fusion de la SLT<2006> sous Delphi 7, SLT<2009> sous C++Builder 2007, SLT<2012> sous C++Builder XE2/XE3 vers la SLT<2013> sous Delphi XE2
     *                                                                             -
     * ShaiLeTroll@gmail.com                                                       -
     *                                                                             -
     * Ce logiciel est un programme informatique servant à aider les développeurs  -
     * Delphi avec une bibliothèque polyvalente, adaptable et fragmentable.        -
     *                                                                             -
     * Ce logiciel est régi par la licence CeCILL-C soumise au droit français et   -
     * respectant les principes de diffusion des logiciels libres. Vous pouvez     -
     * utiliser, modifier et/ou redistribuer ce programme sous les conditions      -
     * de la licence CeCILL-C telle que diffusée par le CEA, le CNRS et l'INRIA    -
     * sur le site "http://www.cecill.info".                                       -
     *                                                                             -
     * En contrepartie de l'accessibilité au code source et des droits de copie,   -
     * de modification et de redistribution accordés par cette licence, il n'est   -
     * offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,   -
     * seule une responsabilité restreinte pèse sur l'auteur du programme,  le     -
     * titulaire des droits patrimoniaux et les concédants successifs.             -
     *                                                                             -
     * A cet égard  l'attention de l'utilisateur est attirée sur les risques       -
     * associés au chargement,  à l'utilisation,  à la modification et/ou au       -
     * développement et à la reproduction du logiciel par l'utilisateur étant      -
     * donné sa spécificité de logiciel libre, qui peut le rendre complexe à       -
     * manipuler et qui le réserve donc à des développeurs et des professionnels   -
     * avertis possédant  des  connaissances  informatiques approfondies.  Les     -
     * utilisateurs sont donc invités à charger  et  tester  l'adéquation  du      -
     * logiciel à leurs besoins dans des conditions permettant d'assurer la        -
     * sécurité de leurs systèmes et ou de leurs données et, plus généralement,    -
     * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.          -
     *                                                                             -
     * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez      -
     * pris connaissance de la licence CeCILL-C, et que vous en avez accepté les   -
     * termes.                                                                     -
     *                                                                             -
     *----------------------------------------------------------------------------*)
    unit SLT.Persistence.DBEntity;
     
    interface
     
    {$IFDEF DEBUG}
    {$DEFINE DEBUG_SLT_DB_ENTITY_TIME}
    {$ENDIF DEBUG}
     
    {$IFDEF DEBUG_SLT_DB_ENTITY_TIME}
      {$DEFINE DEBUG_SLT_DB_ENTITY_USES}
    {$ENDIF DEBUG_SLT_DB_ENTITY_TIME}
     
    uses System.Classes, System.SysUtils, System.Contnrs, Data.DB,
      SLT.Persistence.Interfaces, SLT.Persistence.ORMInterfaces,
      SLT.DB.Provider;
     
    type
      { Forward class declarations }
      TSLTDBEntityEngine = class;
     
      /// <summary>Référence de Classe sur TSLTDBEntityEngine</summary>
      TSLTDBEntityEngineClass = class of TSLTDBEntityEngine;
     
      /// <summary>TSLTDBEntityNotifyEvent est la notification de base du TSLTDBEntityEngine émis lorsqu'un événement s'est produit.</summary>
      TSLTDBEntityNotifyEvent = procedure(Entity: TSLTDBEntityEngine; NotifyData: Pointer) of object;
     
      /// <summary>Erreur de base liée à l'utilisation des Entités mappant une Table d'une DB</summary>
      ESLTDBEntityError = class(Exception);
     
      { Aliases interface declarations }
     
      /// <summary>Associations entre les noms des champs d'une Table d'une DB et leurs propriétés associées dans une Entité DB</summary>
      TSLTDBEntityFieldPropertyAssociation = TSLTPersistentORMEntityFieldPropertyAssociation;
      TSLTDBEntityFieldPropertyAssociations = TSLTPersistentORMEntityFieldPropertyAssociations;
      TSLTDBEntityFieldThesaurusAssociation = TSLTPersistentORMEntityFieldThesaurusAssociation;
      TSLTDBEntityFieldName = TSLTPersistentORMEntityFieldName;
      TSLTDBEntityFieldNames = TSLTPersistentORMEntityFieldNames;
     
      /// <summary>Interface fournissant la valeur d'une propriété contenues dans une Entité DB</summary>
      ISLTDBEntityPropertyValue = ISLTPersistentPropertyValue;
     
      /// <summary>Type de propriétés "Integer" acceptant la valeur nulle</summary>
      ISLTDBEntityNullableInteger = ISLTPersistentNullableInteger;
     
      /// <summary>Type de propriétés "Integer" géré comme un Auto Inc généralement associé à la Clé Primaire d'une Table d'une DB</summary>
      ISLTDBEntityAutoIncrementValue = ISLTPersistentORMEntityAutoIncrementValue;
     
      /// <summary>Type de propriétés "Integer" contenant le numéro de l'entité active au sein de l'ensemble d'entité généralement associé à une Clé Primaire composite d'une Table d'une DB</summary>
      ISLTDBEntityActiveNumberValue = ISLTPersistentSetActiveNumberValue;
     
      /// <summary>Type de propriétés "Integer" géré comme une valeur Auto Inc présente dans une autre Entité DB généralement associé à la Clé Etrangère d'une Table d'une DB</summary>
      ISLTDBEntityExternalAutoIncrementValue = ISLTPersistentORMEntityExternalAutoIncrementValue;
     
      /// <summary>Type de propriétés "string" acceptant la valeur nulle</summary>
      ISLTDBEntityNullableString = ISLTPersistentNullableString;
     
      /// <summary>Type de propriétés "Double" acceptant la valeur nulle</summary>
      ISLTDBEntityNullableDouble = ISLTPersistentNullableDouble;
     
      /// <summary>Type de propriétés "TDateTime" acceptant la valeur nulle</summary>
      ISLTDBEntityNullableDateTime = ISLTPersistentNullableDateTime;
     
      /// <summary>TSLTDBEntityPropertyValueChangeEvent est une notification émise lors de la modification de la valeur des propriétés d'une Entité DB</summary>
      TSLTDBEntityPropertyValueChangeEvent = procedure(Sender: TObject; Value: ISLTDBEntityPropertyValue) of object;
     
      /// <summary>Moteur de persistance fournissant un accès aux données d'une Table d'une DB</summary>
      /// <remarks>La classe TSLTDBEntityEngine a pour but de remplacer les classes TEpcPersistant (Engine of Persistence Component SLT&lt;2006&gt;) en y incoporant plus de souplesse en terme d'implémentation ainsi que diverses simplifications.
      /// <para>Conçu pour une utilisation par composition contrairement au TEpcPersistant qui devait être utilisé par héritage ce qui parait plus simple au départ mais nuit à l'évolution,
      /// le TSLTDBEntityEngine ne peut pas être utilisé par héritage et doit être utiliser par composition pour conserver une liberté d'héritage dans les classes métiers tant qu'elles héritent soit de TPersistent ou de TObject compilé en $M+ ou {$TYPEINFO ON}.</para>
      /// <para>- Réduction de l'utilisation de Variant dans les propriétés publiées au profit d'accessseur fortement typé (type simple ou interface), cela simplifie la gestion des champs n'acceptant pas la valeur nulle en utilisant des types simples mais simplifie aussi l'accès au champ pouvant être null en garantissant un typage fort via des accesseurs similaires au TField à travers une série d'interface,
      /// le compilateur de Delphi 7 provoquait régulièrement des erreurs internes sur les published property Variant avec spécificateur d'indice.</para>
      /// <para>- Externalisation de la Connexion sur la base de données, évolution du TepcDB vers ISLTDBProvider et ISLTDBConnection.</para>
      /// <para>- Gestion des générateurs de requête SQL en fonction du Provider de la Connexion DB, évolution du TepcQueryAssistant vers ISLTDBEntityQueryAssistant.</para>
      /// <para>- Gestion des Clés primaires et Clés étrangères qui peuvent être au choix des AutoInc ainsi que des Clés composites (ceci n'existait en EPC), évolution du TepcQueryAssistant vers ISLTDBEntityQueryIdentityAssistant et ISLTDBEntityQueryCompositeAssistant.</para>
      /// <para>- Les Relations ne seront plus représentées par une Collection d'Objet mais par un Ensemble d'Entité (au comportement proche d'un TDataSet) car cela accroit l'efficience avec une volumétrie importante,
      /// une Collection d'Objet par nature est encombrante, implique la duplication de nombreux éléments, le TEpcPersistant héritant du TComponent, l'utilisation du Owner pouvant paraître pratique était consommatrice en CPU.
      /// Un Ensemble d'Entité n'est pas une Liste d'Entité mais plutôt un Curseur sur lot d'enregistrement mappé comme une Entité DB, les données actuels de l'Entité dépendent de la position du Curseur dans l'Ensemble d'Entité,
      /// selon les implémentations, les données d'un Ensemble d'Entité sont stockées selon une structure interne mais il est possible que l'Ensemble d'Entité ne soit juste qu'une encapsulation d'un TDataSet.</para>
      /// La classe TSLTDBEntityEngine est une traduction du TShaiORPersistent qui a été écrit originellement en C++Builder 2007 SLT&lt;2009&gt; et XE3 SLT&lt;2012&gt;,
      /// durant la traduction C++Builder vers Delphi, de nombreuses fonctionnalités qui existait à l'époque du TEpcPersistant SLT&lt;2006&gt; ont été ré-intégrées, si il possible d'utiliser du code Delphi en C++Builder, l'inverse n'est malheureusement pas possible.</remarks>
      TSLTDBEntityEngine = class sealed(TObject)
      public
        // Type publique
        type
          /// <summary>Indique si le nom contenu dans CriterionName est un nom de propriété ou un nom de champ</summary>
          TCriterionNameType = (cntPropertyName, cntFieldName);
          /// <summary>Critère de chargement d'une Entité DB</summary>
          TCriterion = record
            CriterionName: string;
            CriterionNameType: TCriterionNameType;
            CriterionValue: Variant;
            CriterionDisjunction: Boolean;
            /// <summary>Création d'un critère vide de chargement d'une Entité DB prévu pour un nom de propriété</summary>
            class function Create(): TCriterion; overload; static;
            /// <summary>Création d'un critère de chargement d'une Entité DB avec en paramètre un nom de propriété et une valeur</summary>
            constructor Create(const ACriterionName: string; ACriterionValue: Variant; ACriterionDisjunction: Boolean = False); overload;
            /// <summary>Création d'un critère de chargement d'une Entité DB avec en paramètre un nom, un type de nom et une valeur</summary>
            constructor Create(const ACriterionName: string; ACriterionNameType: TCriterionNameType; ACriterionValue: Variant; ACriterionDisjunction: Boolean = False); overload;
          end;
          /// <summary>Ensemble de critère pour le chargement d'une Entité DB</summary>
          TCriteria = array of TCriterion;
     
          /// <summary>Indique si le nom contenu dans CriterionName est un nom de propriété ou un nom de champ</summary>
          TSelectionFormulaType = (sftPropertyName, sftFieldName, sftFormula);
          /// <summary>Elément sélectionné pour le chargement d'une Entité DB</summary>
          TSelectionItem = record
            SelectionFormula: string;
            SelectionFormulaType: TSelectionFormulaType;
            /// <summary>Création d'une selection vide pour le chargement d'une Entité DB prévu pour un nom de propriété</summary>
            class function Create(): TSelectionItem; overload; static;
            /// <summary>Création d'une selection pour le chargement d'une Entité DB avec une résolution sur un nom de propriété</summary>
            constructor Create(const ASelectionFormula: string); overload;
            /// <summary>Création d'une selection pour le chargement d'une Entité DB avec un nom et un type pour la résolution du nom</summary>
            constructor Create(const ASelectionFormula: string; ASelectionFormulaType: TSelectionFormulaType); overload;
          end;
          /// <summary>Ensemble de éléments sélectionnés pour le chargement d'une Entité DB</summary>
          TSelection = array of TSelectionItem;
     
          /// <summary>Type de Jointure</summary>
          TJoinType = TSLTPersistentORMDBSQLJoinType;
          /// <summary>Type de Jointure</summary>
          TJoinTable = TSLTPersistentORMDBSQLJoinTable;
          /// <summary>Description d'une des jointures nécessaires pour les données auxiliaires chargées en plus de celle de l'Entité DB</summary>
          TJoin = record
            JoinMaster: TJoinTable;
            JoinDetail: TJoinTable;
            JoinType: TJoinType;
            JoinMasterFields: TSLTDBEntityFieldNames;
            JoinDetailFields: TSLTDBEntityFieldNames;
            /// <summary>Création d'une jointure vide</summary>
            class function Create(): TJoin; overload; static;
            /// <summary>Création d'une jointure complète y compris les alias</summary>
            constructor Create(const AMaster: string; const AMasterAlias: string; const ADetail: string; const ADetailAlias: string; const AMasterFields: array of TSLTDBEntityFieldName; const ADetailFields: array of TSLTDBEntityFieldName; const AJoinType: TJoinType = jtInner); overload;
            /// <summary>Permute le Maître comme Detail et réciproquement, le type de jointure résultant sera symétriquement permuté</summary>
            procedure InterchangeMasterDetail(); overload;
            /// <summary>Permute le Maître comme Detail et réciproquement en forçant le type de jointure</summary>
            procedure InterchangeMasterDetail(const AJoinType: TJoinType); overload;
          end;
     
          /// <summary>Ensemble des jointures nécessaires pour les données auxiliaires chargées en plus de celle de l'Entité DB</summary>
          TJoins = array of TJoin;
     
          /// <summary>Indique si le nom contenu dans OrderItemName est un nom de propriété ou un nom de champ</summary>
          TOrderNameType = (ontPropertyName, ontFieldName);
          TOrderOrientationType = (ootAscending, ootDescending);
          /// <summary>Ordre de chargement d'une Entité DB</summary>
          TOrderItem = record
            OrderName: string;
            OrderNameType: TOrderNameType;
            OrderOrientationType: TOrderOrientationType;
            /// <summary>Création d'un ordre ascendant vide de chargement d'une Entité DB prévu pour un nom de propriété</summary>
            class function Create(): TOrderItem; overload; static;
            /// <summary>Création d'un ordre ascendant de chargement d'une Entité DB avec en paramètre un nom de propriété</summary>
            constructor Create(const AOrderName: string); overload;
            /// <summary>Création d'un ordre de chargement d'une Entité DB avec en paramètre un nom de propriété et son orientation</summary>
            constructor Create(const AOrderName: string; AOrderOrientationType: TOrderOrientationType); overload;
            /// <summary>Création d'un ordre de chargement d'une Entité DB avec en paramètre un nom, son type de nom et son orientation</summary>
            constructor Create(const AOrderName: string; AOrderNameType: TOrderNameType; AOrderOrientationType: TOrderOrientationType = ootAscending); overload;
          end;
          /// <summary>Ensemble de critère pour le chargement d'une Entité DB</summary>
          TOrder = array of TOrderItem;
     
        // Constante publique
        const
          /// <summary>SELECTION_ALL est gérée différemment selon TSelectionFormulaType, sftPropertyName = toutes les propriétés de l'Entité DB, sftFieldName = tous les champs de la table associée</summary>
          /// <remarks>ALL est synonyme de tous les champs, ce n'est pas le ALL qui est le contraire du DISTINCT</remarks>
          SELECTION_ALL: array[TSelectionFormulaType] of string = ('!', '*', '*'); // Do not localize
          /// <summary>SELECTION_DISTINCT active le mode DISTINCT</summary>
          SELECTION_DISTINCT = '! DISTINCT !'; // Do not localize
      private
        // Membres privés de Classe
        class var
          FDefaultInternalORMEntityClass: TClass;
          FDefaultInternalORMEntitySetClass: TClass;
          FDefaultInternalORMEntityMarshallingEngineClass: TClass;
      private
        // Membres privés
        FPropertyPublisher: TObject;
        FORMEntityInternalIntf: IInterface;
        FParent: TSLTDBEntityEngine;
        FForceUpdate: Boolean;
        FIgnoreChanged: Boolean;
        FReadOnly: Boolean;
        // Membres privés - Evenement
        FBeforeAdd: TSLTDBEntityNotifyEvent;
        FAfterAdd: TSLTDBEntityNotifyEvent;
        FBeforeAppend : TNotifyEvent;
        FAfterAppend : TNotifyEvent;
        FOnSaveProgress: TNotifyEvent;
        FOnLoadProgress: TNotifyEvent;
     
        FMonitorPropertyValueProxies: TObjectList;
     
        // Méthodes
        function InternalORMEntityIntfFactory(): IInterface;
        function InternalORMEntitySetIntfFactory(): IInterface;
     
        procedure LockDataSetReadOnly();
     
        function GetCursorInterface(): ISLTPersistentSetPropertiesData;
        procedure FreeEventProxies();
     
        // Accesseurs
        function GetConnection(): ISLTDBConnection;
        procedure SetConnection(const Value: ISLTDBConnection);
        function GetTableName(): string;
     
        function GetProperty(Index: Integer): Variant;
        procedure SetProperty(Index: Integer; const Value: Variant);
        function GetPropertyByName(const PropertyName: string): Variant;
        procedure SetPropertyByName(const PropertyName: string; const Value: Variant);
        function GetPropertyName(Index: Integer): string;
        function GetPropertyChange(Index: Integer): Boolean;
        function GetPropertyIsNull(Index: Integer): Boolean;
        function GetPropertyMetaData(Index: Integer): ISLTPersistentPropertyMetaData;
     
        function GetPropertyFieldName(Index: Integer): string;
        function GetPropertyByFieldName(const FieldName: string): Variant;
        procedure SetPropertyByFieldName(const FieldName: string; const Value: Variant);
        function GetPropertyNameByFieldName(const FieldName: string): string;
        function GetFieldNameByPropertyName(const PropertyName: string): string;
        function GetPropertyCount(): Integer;
     
        function GetEoF(): Boolean;
        function GetDataSet(): TDataSet;
        procedure SetDataSet(const Value: TDataSet);
        function GetOwnsDataSet(): Boolean;
        procedure SetOwnsDataSet(const Value: Boolean);
        procedure SetReadOnly(const Value: Boolean);
        function GetEntityCount(): Integer;
     
        function GetLoaded(): Boolean;
        procedure SetLoaded(const Value: Boolean);
     
        // Méthodes privées - Lanceur d'Evenement
        procedure DoBeforeAdd(AEntity: TSLTDBEntityEngine);
        procedure DoAfterAdd(AEntity: TSLTDBEntityEngine);
        procedure DoBeforeAppend();
        procedure DoAfterAppend();
     
        // Méthodes privées - Gestion de l'etat Changed
        procedure ResetChanges(OnlyActive: Boolean; AChanged: Boolean = False);
        // Méthodes privées - Manipulation de données
        procedure ClearValues();
      public
        // Accesseurs - Type de propriétés simples
        function GetIntegerPropByIndex(PropertyIndex: Integer): Integer;
        procedure SetIntegerPropByIndex(PropertyIndex: Integer; const Value: Integer);
        function GetStringPropByIndex(PropertyIndex: Integer): string;
        procedure SetStringPropByIndex(PropertyIndex: Integer; const Value: string);
        function GetDoublePropByIndex(PropertyIndex: Integer): Double;
        procedure SetDoublePropByIndex(PropertyIndex: Integer; const Value: Double);
        function GetDateTimePropByIndex(PropertyIndex: Integer): TDateTime;
        procedure SetDateTimePropByIndex(PropertyIndex: Integer; const Value: TDateTime);
        function GetBooleanPropByIndex(PropertyIndex: Integer): Boolean;
        procedure SetBooleanPropByIndex(PropertyIndex: Integer; const Value: Boolean);
     
        // Accesseurs - Type de propriétés acceptant la valeur nulle
        function GetNullableIntegerPropByIndex(PropertyIndex: Integer): ISLTDBEntityNullableInteger;
        function GetNullableStringPropByIndex(PropertyIndex: Integer): ISLTDBEntityNullableString;
        function GetNullableDoublePropByIndex(PropertyIndex: Integer): ISLTDBEntityNullableDouble;
        function GetNullableDateTimePropByIndex(PropertyIndex: Integer): ISLTDBEntityNullableDateTime;
     
        // Accesseurs - Type de propriétés spéciales
        function GetAutoIncrementValuePropByIndex(PropertyIndex: Integer): ISLTDBEntityAutoIncrementValue;
        function GetActiveNumberValuePropByIndex(PropertyIndex: Integer): ISLTDBEntityActiveNumberValue;
        function GetExternalAutoIncrementValuePropByIndex(PropertyIndex: Integer): ISLTDBEntityExternalAutoIncrementValue;
     
      public
        // Constructeurs de Classe
        class constructor Create();
     
        // Méthodes des Classes
        class procedure RegisterFieldPropertyAssociations(APropertyPublisherClass: TClass; const AAssociations: array of TSLTDBEntityFieldPropertyAssociation);
        class procedure RegisterTableName(APropertyPublisherClass: TClass; const ATableName: TSLTPersistentORMEntityTableName);
        class procedure RegisterPrimaryKey(APropertyPublisherClass: TClass; const APrimaryKeyNames: array of TSLTPersistentORMEntityFieldName);
        class procedure RegisterForeignKey(APropertyPublisherClass: TClass; const AForeignKeyNames: array of TSLTPersistentORMEntityFieldName);
        class procedure RegisterFieldSequence(APropertyPublisherClass: TClass; const AFieldNames: array of TSLTPersistentORMEntityFieldName; const ASequenceNames: array of string);
        class procedure RegisterFieldValueGenerator(APropertyPublisherClass: TClass; const AFieldNames: array of TSLTPersistentORMEntityFieldName; const AValueGeneratorFormulas: array of string);
        class procedure RegisterFieldDefaultValue(APropertyPublisherClass: TClass; const AFieldNames: array of TSLTPersistentORMEntityFieldName; const ADefaultValues: array of Variant);
        class procedure RegisterFieldType(APropertyPublisherClass: TClass; const AFieldNames: array of TSLTPersistentORMEntityFieldName; const AFieldTypes: array of TFieldType);
        class procedure RegisterClassAlias(APropertyPublisherClass: TClass; APropertyPublisherClassAlias: TClass);
        class function FindPrimaryKeyFieldNames(APropertyPublisherClass: TClass; out AFieldNames: TSLTPersistentORMEntityFieldNames): Boolean;
        class function FindPrimaryKeyPropertyNames(APropertyPublisherClass: TClass; out APropertyNames: TSLTPersistentORMEntityPropNames): Boolean;
        class function GetFieldPropertyAssociations(APropertyPublisherClass: TClass): TSLTPersistentORMEntityFieldPropertyAssociations;
     
        // Propriétés de Classe
        /// <summary>Indique la classe par défaut utilisée comme Implémentation de ISLTDBEntity et des interfaces liées aux Entités DB</summary>
        class property DefaultORMEntityClass: TClass read FDefaultInternalORMEntityClass write FDefaultInternalORMEntityClass;
        /// <summary>Indique la classe par défaut utilisée comme Implémentation de ISLTDBEntitySet et des interfaces liées aux Ensemble d'Entités DB</summary>
        class property DefaultORMEntitySetClass: TClass read FDefaultInternalORMEntitySetClass write FDefaultInternalORMEntitySetClass;
        /// <summary>Indique la classe par défaut utilisée comme Implémentation des interfaces présentes dans ISLTPersistentMarshallable fournissant l'interaction entre une DB et les Entités DB</summary>
        class property DefaultORMEntityMarshallingEngine: TClass read FDefaultInternalORMEntityMarshallingEngineClass write FDefaultInternalORMEntityMarshallingEngineClass;
     
      public
        // Constructeurs
        /// <summary>Crée l'assistant de persistance pour mapper l'objet indiqué par le paramètre APersistent</summary>
        /// <param name="APropertyPublisher">Indique l'objet contenant les propriétés publiées</param>
        constructor Create(APropertyPublisher: TObject);
        /// <summary>Crée l'assistant de persistance pour mapper l'objet indiqué par le paramètre APersistent comme un ensemble de données comparable à un TDataSet</summary>
        /// <param name="APropertyPublisher">Indique l'objet contenant les propriétés publiées</param>
        /// <param name="AParent">Indique l'objet Parent d'une pseudo-collection d'Entité, le parent partage sa connexion avec sa collection d'enfant, typique d'une relation Maitre-Détail</param>
        constructor CreateAsSet(APropertyPublisher: TObject; AParent: TSLTDBEntityEngine = nil);
        destructor Destroy(); override;
     
        // Méthodes
        /// <summary>Ajoute un élement à l'ensemble et y recopie les données de l'entité passée en paramètre</summary>
        /// <param name="AEntity">Entité contenant les données qui seront recopié dans l'ensemble formant une pseudo-collection d'Entité DB</param>
        /// <remarks>AEntity n'est pas liée, ses données ont été recopiées selon un mécansime proche d'un Assign</remarks>
        function Add(AEntity: TSLTDBEntityEngine): Boolean;
     
        // Méthodes
        function Load(const ACriteria: TCriteria = nil; const ASelection: TSelection = nil; const AOrder: TOrder = nil; const AJoins: TJoins = nil): Boolean;
        function Select(): ISLTDBQuerySQLSelectDescriptor;
        function Save(OnlyActive: Boolean = False): Boolean;
        function Delete(const ACriteria: TCriteria = nil): Boolean;
     
        // Méthodes sur Ensemble
        procedure Append();
        procedure Remove();
        procedure First();
        procedure Next();
        function IsEmpty(): Boolean;
        procedure Clear();
        procedure SetAllLoaded(const Value: Boolean);
     
        // Méthodes Diverses
        procedure MonitorPropertyValue(AValue: ISLTDBEntityPropertyValue; APropertyValueChangeEventHandler: TSLTDBEntityPropertyValueChangeEvent); overload;
        procedure MonitorPropertyValue(PropertyIndex: Integer; APropertyValueChangeEventHandler: TSLTDBEntityPropertyValueChangeEvent); overload;
     
        // Propriétés DB
        /// <summary>Indique la connexion en cours sur l'une Base de Données</summary>
        property Connection: ISLTDBConnection read GetConnection write SetConnection;
        /// <summary>Nom de la Table associée à cette entité DB</summary>
        property TableName: string read GetTableName;
     
        // Propriétés ORM
        /// <summary>Indique l'instance d'objet persistant contenant les propriétés publiées accessibles via RTTI qui est manipulé par l'entité DB</summary>
        property PropertyPublisher: TObject read FPropertyPublisher;
        /// <summary>Nombre de propriétés présentes dans l'entité DB</summary>
        property PropertyCount: Integer read GetPropertyCount;
        /// <summary>Données de la propriété présente dans l'entité DB</summary>
        property Properties[Index: Integer]: Variant read GetProperty write SetProperty;
        /// <summary>Données de la propriété présente dans l'entité DB</summary>
        property PropertyByName[const PropertyName: string]: Variant read GetPropertyByName write SetPropertyByName;
        /// <summary>Nom de la propriété présentes dans l'entité DB</summary>
        property PropertyNames[Index: Integer]: string read GetPropertyName;
        /// <summary>Indique une modification de la propriété présentes dans l'entité DB</summary>
        property PropertyChanges[Index: Integer]: Boolean read GetPropertyChange;
        /// <summary>Indique une valeur nulle dans la propriété présente dans l'entité DB</summary>
        property PropertyIsNull[Index: Integer]: Boolean read GetPropertyIsNull;
        /// <summary>Meta-Données de la propriété présente dans l'entité DB</summary>
        property PropertiesMetaData[Index: Integer]: ISLTPersistentPropertyMetaData read GetPropertyMetaData;
     
        // Propriétés Entité
        /// <summary>Indique que l'entité DB a été chargée, un Save réussi peut passer cette propriété aussi à True. Si c'est un ensemble cette valeur est différente pour chaque entité de l'ensemble.</summary>
        property Loaded: Boolean read GetLoaded write SetLoaded;
        /// <summary>Indique que l'entité DB sera mise à jour, si l'entité n'existe pas, cela ne tentera pas l'insertion</summary>
        property ForceUpdate: Boolean read FForceUpdate write FForceUpdate;
        /// <summary>Indique que lorsque l'entité DB sera insérée ou mise à jour, peut importe les propriétés modifiées, on prend le tout peu importe l'état de PropertyChanges</summary>
        property IgnoreChanged: Boolean read FIgnoreChanged write FIgnoreChanged;
        /// <summary>Indique que l'entité DB n'autorise pas de mise à jour</summary>
        property ReadOnly: Boolean read FReadOnly write SetReadOnly;
     
        // Propriétés DB - ORM
        /// <summary>Nom du champ associé à la propriété</summary>
        property PropertyFieldNames[Index: Integer]: string read GetPropertyFieldName;
        /// <summary>Données de la propriété présente dans l'entité DB en fonction d'un nom de champ présent dans la Table associée</summary>
        property PropertyByFieldName[const FieldName: string]: Variant read GetPropertyByFieldName write SetPropertyByFieldName;
        /// <summary>Nom de la propriété présente dans l'entité DB en fonction d'un nom de champ présent dans la Table associée</summary>
        property PropertyNameByFieldName[const FieldName: string]: string read GetPropertyNameByFieldName;
        /// <summary>Nom d'un nom de champ présent dans la Table associée à l'entité DB en fonction d'un nom de propriété</summary>
        property FieldNameByPropertyName[const FieldName: string]: string read GetFieldNameByPropertyName;
     
        // Propriétés Ensemble d'Entité
        /// <summary>Indique si le dernier objet de l'ensemble de données est déjà actif et que l'on ne peut pas se déplacer plus loin vers l'avant</summary>
        property EoF: Boolean read GetEoF;
        /// <summary>Fourni un accès au DataSet interne si il existe</summary>
        property DataSet: TDataSet read GetDataSet write SetDataSet;
        /// <summary>Fourni un accès au DataSet interne si il existe</summary>
        property OwnsDataSet: Boolean read GetOwnsDataSet write SetOwnsDataSet;
        /// <summary>Renvoie le nombre d'objet présent dans l'ensemble de données</summary>
        property EntityCount: Integer read GetEntityCount;
     
        // Propriétés Evenement
        /// <summary>Notification se produisant avant d'effectuer le Add. Abort permet d'annuler l'opération avant qu'elle ne se produise</summary>
        property BeforeAdd: TSLTDBEntityNotifyEvent read FBeforeAdd write FBeforeAdd;
        /// <summary>Notification se produisant après avoir effectué avec succès le Add</summary>
        property AfterAdd: TSLTDBEntityNotifyEvent read FAfterAdd write FAfterAdd;
        /// <summary>Notification se produisant avant d'effectuer le Append. Abort permet d'annuler l'opération avant qu'elle ne se produise</summary>
        property BeforeAppend: TNotifyEvent read FBeforeAppend write FBeforeAppend;
        /// <summary>Notification se produisant après avoir effectué avec succès le Append</summary>
        property AfterAppend: TNotifyEvent read FAfterAppend write FAfterAppend;
        /// <summary>Notification se produisant avant d'effectuer le Next</summary>
        property OnSaveProgress: TNotifyEvent read FOnSaveProgress write FOnSaveProgress;
        /// <summary>Notification se produisant après avoir effectué le Next</summary>
        property OnLoadProgress: TNotifyEvent read FOnLoadProgress write FOnLoadProgress;
     
      end;
     
    implementation
     
    uses
      System.Variants, System.TypInfo,
      Winapi.Windows,
      SLT.Common.SystemEx, SLT.Common.RTTI, {$IFDEF DEBUG_SLT_DB_ENTITY_USES}SLT.Common.Tracing, {$ENDIF DEBUG_SLT_DB_ENTITY_USES}
      SLT.Persistence.ORMImpl, SLT.Persistence.Consts;
     
    const
      ERR_ADD_FIELD_VALUE_GENERATOR = 'FieldNames and ValueGeneratorFormula must have same length'; // Do not localize
      ERR_ADD_FIELD_SEQUENCE = 'FieldNames and SequenceNames must have same length'; // Do not localize
      ERR_ADD_FIELD_DEFAULT_VALUE = 'FieldNames and DefaultValues must have same length'; // Do not localize
      ERR_ADD_FIELD_TYPE = 'FieldNames and FieldTypes must have same length'; // Do not localize
      ERR_ADD_CLASS_ALIAS_FMT = 'Class "%s" have already alias "%s", can''t attach with "%s"'; // Do not localize
      ERR_NO_CONNECTION = 'Connection needed !';
      ERR_UNLOADABLE = 'Entity is not loadable';
      ERR_UNSAVEABLE = 'Entity is not saveable';
      ERR_UNDELETEABLE = 'Entity is not deleteable';
      ERR_READONLY = 'Entity is read only';
      PROP_READONLY = 'ReadOnly';
      ERR_LOADSTATEUNINSPECTABLE = 'Entity load state is not inspectable';
     
    {$IFDEF DEBUG_SLT_DB_ENTITY_TIME}
    procedure OutputDebugElapsedTime(const Msg: string; const AElapsedMilliSec: Double); inline; forward;
    function GetElapsedTimeMilliSec(const TickStart: Int64): Double; inline; forward;
    const
      DEBUG_MARK = '[SLT.DB.Entity]';
    {$ENDIF DEBUG_SLT_DB_ENTITY_TIME}
     
    { TSLTDBEntityEngine }
     
    //------------------------------------------------------------------------------
    class constructor TSLTDBEntityEngine.Create();
    begin
      // Par défaut, on utilise l'implémentation naturelle du ISLTPersistentORMEntity : le TSLTPersistentORMEntity
      DefaultORMEntityClass := TSLTPersistentORMEntity;
      // Par défaut, on utilise l'implémentation naturelle du ISLTPersistentORMEntitySet : le TSLTPersistentORMEntitySet
      DefaultORMEntitySetClass := TSLTPersistentORMEntitySet;
      // Par défaut, on utilise l'implémentation naturelle des interfaces liées au ISLTPersistentMarshallable : le TSLTPersistentORMDBSQLMarshaller
      DefaultORMEntityMarshallingEngine := TSLTPersistentORMDBSQLMarshallingEngine;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.Add(AEntity: TSLTDBEntityEngine): Boolean;
    var
      Src: ISLTPersistentPropertiesData;
      Dest: ISLTPersistentSetPropertiesData;
    begin
      Result := False;
      if FReadOnly then
        raise ESLTDBEntityError.Create(Self.PropertyPublisher.ClassName() + ' - ' + ERR_READONLY);
     
      if Supports(FORMEntityInternalIntf, ISLTPersistentSetPropertiesData, Dest) then
      begin
        if Supports(AEntity.FORMEntityInternalIntf, ISLTPersistentPropertiesData, Src) then
        begin
          try
            DoBeforeAdd(AEntity);
            Result := Dest.AppendData(Src);
            if Result then
              DoAfterAdd(AEntity);
          except
            on E: EAbort do
              Result := False;
          end;
        end;
      end;
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTDBEntityEngine.Append();
    begin
      if FReadOnly then
        raise ESLTDBEntityError.Create(Self.PropertyPublisher.ClassName() + ' - ' + ERR_READONLY);
     
      DoBeforeAppend();
      GetCursorInterface().Append();
      DoAfterAppend();
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTDBEntityEngine.Clear();
    begin
      if FReadOnly then
        raise ESLTDBEntityError.Create(Self.PropertyPublisher.ClassName() + ' - ' + ERR_READONLY);
     
      GetCursorInterface().Clear();
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTDBEntityEngine.ClearValues();
    var
      I: Integer;
      Prop: ISLTPersistentPropertyValue;
      NullableProp: ISLTPersistentNullablePropertyValue;
    begin
      if FReadOnly then
        raise ESLTDBEntityError.Create(Self.PropertyPublisher.ClassName() + ' - ' + ERR_READONLY);
     
      with FORMEntityInternalIntf as ISLTPersistentPropertiesData do
      begin
        for I := 0 to PropertyCount - 1 do
        begin
          Prop := Properties[I].NewValue;
          if Supports(Prop, ISLTPersistentNullablePropertyValue, NullableProp) then
            NullableProp.IsNull := True
          else
            Prop.AsVariant := Unassigned;
        end;
      end;
    end;
     
    //------------------------------------------------------------------------------
    constructor TSLTDBEntityEngine.Create(APropertyPublisher: TObject);
    begin
      inherited Create();
      FPropertyPublisher := APropertyPublisher;
     
      // Libération gérée par compteur de référence !
      FORMEntityInternalIntf := InternalORMEntityIntfFactory();
     
      // Liste contenant les implémentations pour chaque propriété surveillée de ISLTPersistentEventHandler gérant une ISLTPersistentPropertyValueChangeEvent
      // Tout les proxy de surveillance seront libérés automatiquement
      FMonitorPropertyValueProxies := TObjectList.Create(True);
    end;
     
    //------------------------------------------------------------------------------
    constructor TSLTDBEntityEngine.CreateAsSet(APropertyPublisher: TObject; AParent: TSLTDBEntityEngine = nil);
    begin
      inherited Create();
      FPropertyPublisher := APropertyPublisher;
     
      // Libération gérée par compteur de référence !
      FORMEntityInternalIntf := InternalORMEntitySetIntfFactory();
     
      // Liste contenant les implémentations pour chaque propriété surveillée de ISLTPersistentEventHandler gérant une ISLTPersistentPropertyValueChangeEvent
      // Tout les proxy de surveillance seront libérés automatiquement
      FMonitorPropertyValueProxies := TObjectList.Create(True);
     
      FParent := AParent;
      if Assigned(FParent) then
        Connection := AParent.Connection;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.Delete(const ACriteria: TCriteria = nil): Boolean;
    var
      ConnectionIntf: ISLTDBConnection;
      QueryIntf: ISLTDBQuery;
      MarshallingEngine: IInterface;
      DestinationIntf: ISLTPersistentDestination;
      DBDestinationIntf: ISLTPersistentORMDBQueryDestination;
      MarshallerIntf: ISLTPersistentMarshaller;
      DeleteCriteria: ISLTPersistentORMDBSQLWhereCriteria;
      InternalDeleteCriteria: TSLTPersistentORMEntityPropNames;
      CriterionPropName: string;
      I: Integer;
      EntitySet: ISLTPersistentSetPropertiesData;
      SaveableIntf: ISLTPersistentMarshallable;
    {$IFDEF DEBUG_SLT_DB_ENTITY_TIME}
      TickStart: TLargeInteger;
    {$ENDIF DEBUG_SLT_DB_ENTITY_TIME}
    begin
    {$IFDEF DEBUG_SLT_DB_ENTITY_TIME}
      QueryPerformanceCounter(TickStart);
    {$ENDIF DEBUG_SLT_DB_ENTITY_TIME}
     
      Result := False;
      if FReadOnly then
        raise ESLTDBEntityError.Create(Self.PropertyPublisher.ClassName() + ' - ' + ERR_READONLY);
     
      ConnectionIntf := GetConnection();
      if not Assigned(ConnectionIntf) then
        raise ESLTDBEntityError.Create(Self.PropertyPublisher.ClassName() + ' - ' + ERR_UNDELETEABLE + ' : ' + ERR_NO_CONNECTION);
     
      QueryIntf := TSLTDBQuery.Create(ConnectionIntf);
      with FORMEntityInternalIntf as ISLTPersistentMarshalling do
      begin
        TSLTInterfacedObjectFactory.IntfFactory(DefaultORMEntityMarshallingEngine, IInterface, MarshallingEngine);
        try
          if Supports(MarshallingEngine, ISLTPersistentDestination, DestinationIntf) then
            Destination := DestinationIntf;
          if Supports(MarshallingEngine, ISLTPersistentORMDBQueryDestination, DBDestinationIntf) then
          begin
            DBDestinationIntf.Query := QueryIntf;
            DBDestinationIntf.MarshallingOperation := moDelete;
          end;
          if Supports(MarshallingEngine, ISLTPersistentMarshaller, MarshallerIntf) then
            Marshaller := MarshallerIntf;
     
          if Supports(MarshallingEngine, ISLTPersistentORMDBSQLWhereCriteria, DeleteCriteria) then
          begin
            // Suppression selon des critères externes
            if Length(ACriteria) > 0 then
            begin
              for I := Low(ACriteria) to High(ACriteria) do
                with ACriteria[I] do
                  DeleteCriteria.AddCriterion(CriterionName, CriterionValue, CriterionNameType = cntPropertyName, CriterionDisjunction);
            end
            else
            begin
              // Suppression de l'entité courante qui fournira ses propres critères
              if (FindPrimaryKeyPropertyNames(FPropertyPublisher.ClassType(), InternalDeleteCriteria)) then
              begin
                for I := Low(InternalDeleteCriteria) to High(InternalDeleteCriteria) do
                begin
                  CriterionPropName := InternalDeleteCriteria[I];
                  DeleteCriteria.AddCriterion(CriterionPropName, GetPropertyByName(CriterionPropName), True, False); // comme si c'était cntPropertyName et une Conjunction !!!
                end;
              end;
            end;
          end;
     
          // On lance le Save qui effectuera une suppression que l'on considère comme une variante de sérialisation
          if Supports(FORMEntityInternalIntf, ISLTPersistentMarshallable, SaveableIntf) then
            Result := SaveableIntf.Save()
          else
            raise ESLTDBEntityError.Create(Self.PropertyPublisher.ClassName() + ' ' + Self.PropertyPublisher.ClassName() + ' - ' + ERR_UNDELETEABLE);
     
          // Seul une suppression de la ligne en cours peut entrainer un Remove, c'est important dans la gestion du Loaded
          // Pour toutes suppressions personnalisées, on ne peut supposer d'aucun remove sachant que l'on peut faire un delete avec critère sur un objet ou ensemble NON chargé !
          if Result and (Length(ACriteria) = 0) then
          begin
            // Au passage, si l'on a fait un Delete sans critère explicite c'est que l'on a supprimé l'entité courante en DB et qu'il faut donc la supprimer en mémoire !
            if Supports(FORMEntityInternalIntf, ISLTPersistentSetPropertiesData, EntitySet) then
              Remove()
            else
              ClearValues();
          end;
        finally
          Marshaller := nil;
          Destination := nil;
          MarshallingEngine := nil;
        end;
      end;
     
    {$IFDEF DEBUG_SLT_DB_ENTITY_TIME}
      OutputDebugElapsedTime('Delete', GetElapsedTimeMilliSec(TickStart));
    {$ENDIF DEBUG_SLT_DB_ENTITY_TIME}
    end;
     
    //------------------------------------------------------------------------------
    destructor TSLTDBEntityEngine.Destroy();
    begin
      FreeEventProxies();
     
      // Les Interfaces sont gérées par compteur de référence,
      // L'affectation à nil doit les libérer (si plus aucun autre objet les utilises, ce qui DOIT être le cas)
      FORMEntityInternalIntf := nil;
     
      inherited Destroy();
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTDBEntityEngine.DoAfterAdd(AEntity: TSLTDBEntityEngine);
    begin
      if Assigned(FAfterAdd) then
        FAfterAdd(Self, AEntity);
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTDBEntityEngine.DoAfterAppend();
    begin
      if Assigned(FAfterAppend) then
        FAfterAppend(Self);
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTDBEntityEngine.DoBeforeAdd(AEntity: TSLTDBEntityEngine);
    begin
      if Assigned(FBeforeAdd) then
        FBeforeAdd(Self, AEntity);
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTDBEntityEngine.DoBeforeAppend();
    begin
      if Assigned(FBeforeAppend) then
        FBeforeAppend(Self);
    end;
     
    //------------------------------------------------------------------------------
    class function TSLTDBEntityEngine.FindPrimaryKeyFieldNames(APropertyPublisherClass: TClass; out AFieldNames: TSLTPersistentORMEntityFieldNames): Boolean;
    var
      I, L: Integer;
    begin
      with TSLTPersistentORMDBClassMapping.Instance.Keys[APropertyPublisherClass] do
      begin
        L := PrimaryKeyCount;
        if L > 0 then
        begin
          Result := True;
          SetLength(AFieldNames, L);
          for I := 0 to L - 1 do
            AFieldNames[I] := PrimaryKeyFieldNames[I];
        end
        else
          Result := False;
      end;
    end;
     
    //------------------------------------------------------------------------------
    class function TSLTDBEntityEngine.FindPrimaryKeyPropertyNames(APropertyPublisherClass: TClass; out APropertyNames: TSLTPersistentORMEntityPropNames): Boolean;
    var
      I, L: Integer;
    begin
      with TSLTPersistentORMDBClassMapping.Instance.Keys[APropertyPublisherClass] do
      begin
        L := PrimaryKeyCount;
        if L > 0 then
        begin
          Result := True;
          SetLength(APropertyNames, L);
          for I := 0 to L - 1 do
            APropertyNames[I] := PrimaryKeyPropertyNames[I];
        end
        else
          Result := False;
      end;
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTDBEntityEngine.First();
    begin
      GetCursorInterface().First();
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTDBEntityEngine.FreeEventProxies();
    begin
      FreeAndNil(FMonitorPropertyValueProxies);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetActiveNumberValuePropByIndex(PropertyIndex: Integer): ISLTDBEntityActiveNumberValue;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentSpecialPropertyByIndexAccessors).GetActiveNumberValuePropByIndex(PropertyIndex);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetAutoIncrementValuePropByIndex(PropertyIndex: Integer): ISLTDBEntityAutoIncrementValue;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentORMEntitySpecialPropertyByIndexAccessors).GetAutoIncrementValuePropByIndex(PropertyIndex);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetBooleanPropByIndex(PropertyIndex: Integer): Boolean;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentPropertyByIndexAccessors).GetBooleanPropByIndex(PropertyIndex);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetConnection(): ISLTDBConnection;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentORMEntity).Connection;
      if not Assigned(Result) and Assigned(FParent) then
        Result := FParent.Connection;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetCursorInterface(): ISLTPersistentSetPropertiesData;
    begin
      try
        Result := (FORMEntityInternalIntf as ISLTPersistentORMEntitySet).Cursor;
      except
        on E: Exception do
          raise ExceptClass(E.ClassType()).Create(Self.PropertyPublisher.ClassName() + ' is not use as Set : ' + E.Message);
      end;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetDataSet(): TDataSet;
    var
      DataSetProxy: ISLTPersistentORMEntityDataSetProxy;
    begin
      if Supports(FORMEntityInternalIntf, ISLTPersistentORMEntityDataSetProxy, DataSetProxy) then
        Result := DataSetProxy.DataSet
      else
        Result := nil;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetDateTimePropByIndex(PropertyIndex: Integer): TDateTime;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentPropertyByIndexAccessors).GetDateTimePropByIndex(PropertyIndex);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetDoublePropByIndex(PropertyIndex: Integer): Double;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentPropertyByIndexAccessors).GetDoublePropByIndex(PropertyIndex);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetEntityCount(): Integer;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentSetCountable).PersistentCount;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetEoF(): Boolean;
    begin
      Result := GetCursorInterface().EoF;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetExternalAutoIncrementValuePropByIndex(PropertyIndex: Integer): ISLTDBEntityExternalAutoIncrementValue;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentORMEntitySpecialPropertyByIndexAccessors).GetExternalAutoIncrementValuePropByIndex(PropertyIndex);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetPropertyFieldName(Index: Integer): string;
    begin
      Result := (PropertiesMetaData[Index] as ISLTPersistentORMEntityPropertyMetaData).FieldName;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetFieldNameByPropertyName(const PropertyName: string): string;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentORMEntityPropertiesMetaData).FieldNameByPropertyName[PropertyName];
    end;
     
    //------------------------------------------------------------------------------
    class function TSLTDBEntityEngine.GetFieldPropertyAssociations(APropertyPublisherClass: TClass): TSLTPersistentORMEntityFieldPropertyAssociations;
    var
      FieldMap: TSLTPersistentORMEntityFieldPropertyMap;
      it: TSLTPersistentORMEntityFieldPropertyMapIterator;
      I: Integer;
    begin
      with TSLTPersistentORMDBClassMapping.Instance do
      begin
        FieldMap := PublishedPropertiesFieldsMaps[APropertyPublisherClass];
        SetLength(Result, FieldMap.Count);
     
        I := 0;
        for it in FieldMap do
        begin
          Result[I].FieldName := it.Key;
          Result[I].PropertyName := it.Value;
     
          Inc(I);
        end;
      end;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetIntegerPropByIndex(PropertyIndex: Integer): Integer;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentPropertyByIndexAccessors).GetIntegerPropByIndex(PropertyIndex);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetLoaded(): Boolean;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentLoadStateInspector).Loaded;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetNullableDateTimePropByIndex(PropertyIndex: Integer): ISLTDBEntityNullableDateTime;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentNullablePropertyByIndexAccessors).GetNullableDateTimePropByIndex(PropertyIndex);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetNullableDoublePropByIndex(PropertyIndex: Integer): ISLTDBEntityNullableDouble;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentNullablePropertyByIndexAccessors).GetNullableDoublePropByIndex(PropertyIndex);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetNullableIntegerPropByIndex(PropertyIndex: Integer): ISLTDBEntityNullableInteger;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentNullablePropertyByIndexAccessors).GetNullableIntegerPropByIndex(PropertyIndex);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetNullableStringPropByIndex(PropertyIndex: Integer): ISLTDBEntityNullableString;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentNullablePropertyByIndexAccessors).GetNullableStringPropByIndex(PropertyIndex);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetOwnsDataSet(): Boolean;
    var
      DataSetProxy: ISLTPersistentORMEntityDataSetProxy;
    begin
      if Supports(FORMEntityInternalIntf, ISLTPersistentORMEntityDataSetProxy, DataSetProxy) then
        Result := DataSetProxy.OwnsDataSet
      else
        Result := False;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetProperty(Index: Integer): Variant;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentPropertiesData).Properties[Index].NewValue.AsVariant;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetPropertyByFieldName(const FieldName: string): Variant;
    var
      PropertyName: string;
    begin
      PropertyName := (FORMEntityInternalIntf as ISLTPersistentORMEntityPropertiesMetaData).PropertyNameByFieldName[FieldName];
      if PropertyName <> '' then
        Result := GetPropertyByName(PropertyName);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetPropertyByName(const PropertyName: string): Variant;
    var
      PropertyData: ISLTPersistentPropertyData;
    begin
      PropertyData := (FORMEntityInternalIntf as ISLTPersistentPropertiesData).PropertyDataByName[PropertyName];
      if Assigned(PropertyData) then
        Result := PropertyData.NewValue.AsVariant;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetPropertyChange(Index: Integer): Boolean;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentPropertiesData).Properties[Index].Changed;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetPropertyCount(): Integer;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentORMEntityPropertiesMetaData).MetaData.PropertyCount;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetPropertyIsNull(Index: Integer): Boolean;
    begin
      Result := VarIsNull(Properties[Index]);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetPropertyMetaData(Index: Integer): ISLTPersistentPropertyMetaData;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentORMEntityPropertiesMetaData).MetaData.Properties[Index];
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetPropertyName(Index: Integer): string;
    begin
      Result := PropertiesMetaData[Index].PropertyName;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetPropertyNameByFieldName(const FieldName: string): string;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentORMEntityPropertiesMetaData).PropertyNameByFieldName[FieldName];
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetStringPropByIndex(PropertyIndex: Integer): string;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentPropertyByIndexAccessors).GetStringPropByIndex(PropertyIndex);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.GetTableName(): string;
    begin
      Result := (FORMEntityInternalIntf as ISLTPersistentORMEntityPropertiesMetaData).TableName;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.InternalORMEntityIntfFactory(): IInterface;
    var
      EntityIntf: ISLTPersistentORMEntity;
      EntityIntfWithRTTI: ISLTPersistentRTTIAccess;
    begin
      Result := TSLTPersistentORMEntity.EntityFactory(DefaultORMEntityClass);
     
      if Supports(Result, ISLTPersistentORMEntity, EntityIntf) then
      begin
        // Indique au moteur de persistence quel objet contient les RTTI
        EntityIntf.PublishedPropertiesDescriptor := FPropertyPublisher.ClassType();
        if Supports(Result, ISLTPersistentRTTIAccess, EntityIntfWithRTTI) then
          EntityIntfWithRTTI.PublishedPropertiesContainer := FPropertyPublisher;
      end;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.InternalORMEntitySetIntfFactory(): IInterface;
    var
      EntityIntf: ISLTPersistentORMEntitySet;
      EntityIntfWithRTTI: ISLTPersistentRTTIAccess;
    begin
      Result := TSLTPersistentORMEntitySet.EntityFactory(DefaultORMEntitySetClass);
     
      if Supports(Result, ISLTPersistentORMEntitySet, EntityIntf) then
      begin
        // Indique au moteur de persistence quel objet contient les RTTI
        EntityIntf.PublishedPropertiesDescriptor := FPropertyPublisher.ClassType();
        if Supports(Result, ISLTPersistentRTTIAccess, EntityIntfWithRTTI) then
          EntityIntfWithRTTI.PublishedPropertiesContainer := FPropertyPublisher;
      end;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.IsEmpty(): Boolean;
    begin
       Result := GetCursorInterface().IsEmpty();
    end;
     
     
    //------------------- TMarshallableProgressEventHandlerProxy --------------------
    type
      TMarshallableProgressEventHandlerProxy = class(TSLTInterfacedReferencableObject, ISLTPersistentEventHandler)
      private
        FSender: TObject;
        FMarshallingProgressEvent: ISLTPersistentSetMarshallableProgressEvent;
        FMarshallableProgressEventHandler: TNotifyEvent;
        procedure DoEvent();
      public
        constructor Create(Sender: TObject; AMarshallingProgressEvent: IInterface; AMarshallableProgressEventHandler: TNotifyEvent);
        destructor Destroy(); override;
        procedure HandleEvent(AEvent: ISLTPersistentEvent);
      end;
     
    //------------------------------------------------------------------------------
    function TSLTDBEntityEngine.Load(const ACriteria: TCriteria = nil; const ASelection: TSelection = nil; const AOrder: TOrder = nil; const AJoins:
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  10. #10
    Membre éprouvé
    Avatar de Andry
    Profil pro
    Informaticien
    Inscrit en
    Juillet 2002
    Messages
    1 164
    Détails du profil
    Informations personnelles :
    Localisation : Madagascar

    Informations professionnelles :
    Activité : Informaticien

    Informations forums :
    Inscription : Juillet 2002
    Messages : 1 164
    Points : 1 181
    Points
    1 181
    Par défaut
    Waaaaoooo ShaiLeTroll, chapeau !!!
    J'ai eu accès au source d'un ORM et je peux dire que c'est pas de la tarte quoi.
    En tout cas, je vais jetter un oeil à ton source.

    Andry
    On progresse .....

  11. #11
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Ce n'est qu'une petite partie
    Si tu veux le code tel qu'il est aujourd'hui : SoLuTions.zip
    Il faut faut ODAC pour Oracle,
    je n'ai pas eu le temps de ré-écrire la version Delphi7 MyDAC pour MySQL ni la version DevExpress Sybase : persistanceOLD.zip

    j'ai commencé une version ADO mais j'ai pas le temps

    tout ça c'est du bricolage,
    c'est fun à faire mais d'un ennui à utiliser !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

Discussions similaires

  1. Réponses: 2
    Dernier message: 02/02/2010, 22h57
  2. Organisation des fichiers dans un projet maven
    Par kimlaw95 dans le forum Maven
    Réponses: 3
    Dernier message: 27/01/2010, 07h51
  3. Organisation des fichiers du programme
    Par greg13 dans le forum Linux
    Réponses: 2
    Dernier message: 16/03/2007, 15h25
  4. Réponses: 11
    Dernier message: 13/03/2006, 17h51
  5. Réponses: 8
    Dernier message: 03/09/2003, 00h47

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo