-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
1338 lines (1179 loc) · 60.2 KB
/
index.html
File metadata and controls
1338 lines (1179 loc) · 60.2 KB
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
<!DOCTYPE html>
<!--[if IE]><![endif]-->
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Welcome to RDO.Net </title>
<meta name="viewport" content="width=device-width">
<meta name="title" content="Welcome to RDO.Net ">
<meta name="generator" content="docfx 2.43.2.0">
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" href="styles/docfx.vendor.css">
<link rel="stylesheet" href="styles/docfx.css">
<link rel="stylesheet" href="styles/main.css">
<meta property="docfx:navrel" content="toc.html">
<meta property="docfx:tocrel" content="toc.html">
</head>
<body data-spy="scroll" data-target="#affix" data-offset="120">
<div id="wrapper">
<header>
<nav id="autocollapse" class="navbar navbar-inverse ng-scope" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">
<img id="logo" class="svg" src="logo.svg" alt="">
</a>
</div>
<div class="collapse navbar-collapse" id="navbar">
<form class="navbar-form navbar-right" role="search" id="search">
<div class="form-group">
<input type="text" class="form-control" id="search-query" placeholder="Search" autocomplete="off">
</div>
</form>
</div>
</div>
</nav>
<div class="subnav navbar navbar-default">
<div class="container hide-when-search" id="breadcrumb">
<ul class="breadcrumb">
<li></li>
</ul>
</div>
</div>
</header>
<div role="main" class="container body-content hide-when-search">
<div class="article row grid">
<div class="col-md-10">
<article class="content wrap" id="_content" data-uid="">
<h1 id="welcome-to-rdonet">Welcome to RDO.Net</h1>
<p>RDO.Net (Relational Data Objects for .Net) is an open source framework under MIT license to handle data in .Net platform, consists of the following libraries and tools:</p>
<p><img src="/images/RdoNetOverview2.jpg" alt="image"></p>
<h2 id="why-rdonet">Why RDO.Net</h2>
<p>Enterprise application, typically backed by a relational database, has decades of history. Today's enterprise applications are unnecessarily complex and heavyweight, due to the following technical constraints:</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Object-relational_mapping">Object-Relational Mapping (ORM, O/RM, and O/R mapping tool)</a>, as the core of enterprise applications, is still <a href="http://blogs.tedneward.com/post/the-vietnam-of-computer-science/">The Vietnam of Computer Science</a>. Particularly, these difficulties are referred to as the <a href="https://en.wikipedia.org/wiki/Object-relational_impedance_mismatch">object-relational impedance mismatch</a>.</li>
<li>Database testing, still stays on principles and guidelines. No widely practical use yet. Refactoring or changing an enterprise application is time consuming and error prone.</li>
<li>Existing data presentation solutions are far from ideal. Taking <a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel">MVVM</a> for example: it can be overkill for simple UI; in bigger cases, it can be hard to design the ViewModel up front in order to get the right amount of generality. Refactoring or changing data presentation code is time consuming and error prone.</li>
</ul>
<p>The above challenges impose great burdens for developing and further changing an enterprise application. Many frameworks are trying to solve these problems however they are all far from ideal. RDO.Net is the first solution to these problems in an integral, not an after-thought way (strongly recommended reading through):</p>
<ul>
<li><a class="xref" href="articles/overview/enterprise_application_the_direction.html">Enterprise Application, the Direction</a></li>
<li><a class="xref" href="articles/overview/data_and_business_layer_the_new_way.html">Data and Business Layer, the New Way</a></li>
<li><a class="xref" href="articles/overview/presentation_layer_the_new_way.html">Presentation Layer, the New Way</a></li>
</ul>
<p>In the end, your application follows your business in a no-more-no-less basis - it adapts to your business, not vice versa:</p>
<ul>
<li>Your application is 100% strongly typed from database to GUI, all in clean C#/VB.Net code. Refactoring or changing your code is much easier than ever before.</li>
<li>Your data and business layer is best balanced for both programmability and performance. Rich set of data objects such as <code>Model</code>, <code>Db</code>, <code>DbTable</code>, <code>DbQuery</code> and <code>DataSet</code> are provided, no more <a href="https://en.wikipedia.org/wiki/Object-relational_impedance_mismatch">object-relational impedance mismatch</a>.</li>
<li>Data and business layer testing is a first class citizen which can be performed easily - your application can be much more robust and adaptive to change.</li>
<li>A one-for-all, fully customizable data presenter is provided to handle presentation logic including layout, data binding and data validation, all consumed in clean C#/VB.Net code (no XAML needed). You don't need complex controls such as <code>ListBox</code>, <code>TreeView</code>, <code>DataGrid</code> any more. You UI code is greatly simplified because you can reuse all the presentation logic.</li>
<li>And much more with a lightweight runtime - you only need to add several dlls into your application, size ranged from tens to several hundereds KBs.</li>
</ul>
<h2 id="a-taste-of-rdonet">A Taste of RDO.Net</h2>
<p>A fully featured sample application, <a href="https://github.com/DevZest/AdventureWorksLT">AdventureWorksLT</a>, together with others, is provided to demonstrate the use of RDO.Net:</p>
<p><img src="/images/samples_adventureworkslt.wpfapp.jpg" alt="image"></p>
<h3 id="the-model">The Model</h3>
<div class="tabGroup" id="tabgroup_CeZOj-G++Q">
<ul role="tablist">
<li role="presentation">
<a href="#tabpanel_CeZOj-G++Q_cs" role="tab" aria-controls="tabpanel_CeZOj-G++Q_cs" data-tab="cs" tabindex="0" aria-selected="true">C#</a>
</li>
<li role="presentation">
<a href="#tabpanel_CeZOj-G++Q_vb" role="tab" aria-controls="tabpanel_CeZOj-G++Q_vb" data-tab="vb" tabindex="-1">VB.Net</a>
</li>
</ul>
<section id="tabpanel_CeZOj-G++Q_cs" role="tabpanel" data-tab="cs">
<pre><code class="lang-csharp" name="SalesOrderDetail">using DevZest.Data;
using DevZest.Data.Annotations;
using DevZest.Data.SqlServer;
namespace DevZest.Samples.AdventureWorksLT
{
[Computation(nameof(ComputeLineTotal))]
[CheckConstraint(nameof(CK_SalesOrderDetail_OrderQty), typeof(UserMessages), nameof(UserMessages.CK_SalesOrderDetail_OrderQty), Description = "Check constraint [OrderQty] > (0)")]
[CheckConstraint(nameof(CK_SalesOrderDetail_UnitPrice), typeof(UserMessages), nameof(UserMessages.CK_SalesOrderDetail_UnitPrice), Description = "Check constraint [UnitPrice] >= (0.00)")]
[CheckConstraint(nameof(CK_SalesOrderDetail_UnitPriceDiscount), typeof(UserMessages), nameof(UserMessages.CK_SalesOrderDetail_UnitPriceDiscount), Description = "Check constraint [UnitPriceDiscount] >= (0.00)")]
[DbIndex(nameof(IX_SalesOrderDetail_ProductID), Description = "Nonclustered index.")]
public class SalesOrderDetail : BaseModel<SalesOrderDetail.PK>
{
[DbPrimaryKey("PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID", Description = "Clustered index created by a primary key constraint.")]
public sealed class PK : CandidateKey
{
public PK(_Int32 salesOrderID, _Int32 salesOrderDetailID)
: base(salesOrderID, salesOrderDetailID)
{
}
}
public class Key : Key<PK>
{
static Key()
{
Register((Key _) => _.SalesOrderID, _SalesOrderID);
Register((Key _) => _.SalesOrderDetailID, _SalesOrderDetailID);
}
protected override PK CreatePrimaryKey()
{
return new PK(SalesOrderID, SalesOrderDetailID);
}
public _Int32 SalesOrderID { get; private set; }
public _Int32 SalesOrderDetailID { get; private set; }
}
public static readonly Mounter<_Int32> _SalesOrderID = RegisterColumn((SalesOrderDetail _) => _.SalesOrderID);
public static readonly Mounter<_Int32> _SalesOrderDetailID = RegisterColumn((SalesOrderDetail _) => _.SalesOrderDetailID);
public static readonly Mounter<_Int16> _OrderQty = RegisterColumn((SalesOrderDetail _) => _.OrderQty);
public static readonly Mounter<_Int32> _ProductID = RegisterColumn((SalesOrderDetail _) => _.ProductID);
public static readonly Mounter<_Decimal> _UnitPrice = RegisterColumn((SalesOrderDetail _) => _.UnitPrice);
public static readonly Mounter<_Decimal> _UnitPriceDiscount = RegisterColumn((SalesOrderDetail _) => _.UnitPriceDiscount);
public static readonly Mounter<_Decimal> _LineTotal = RegisterColumn((SalesOrderDetail _) => _.LineTotal);
public SalesOrderDetail()
{
}
protected sealed override PK CreatePrimaryKey()
{
return new PK(SalesOrderID, SalesOrderDetailID);
}
private SalesOrderHeader.PK _fk_salesOrderHeader;
public SalesOrderHeader.PK FK_SalesOrderHeader
{
get { return _fk_salesOrderHeader ?? (_fk_salesOrderHeader = new SalesOrderHeader.PK(SalesOrderID)); }
}
private Product.PK _fk_product;
public Product.PK FK_Product
{
get { return _fk_product ?? (_fk_product = new Product.PK(ProductID)); }
}
[DbColumn(Description = "Primary key. Foreign key to SalesOrderHeader.SalesOrderID.")]
public _Int32 SalesOrderID { get; private set; }
[Identity]
[DbColumn(Description = "Primary key. One incremental unique number per product sold.")]
public _Int32 SalesOrderDetailID { get; private set; }
[Required]
[DbColumn(Description = "Quantity ordered per product.")]
public _Int16 OrderQty { get; private set; }
[Required]
[DbColumn(Description = "Product sold to customer. Foreign key to Product.ProductID.")]
public _Int32 ProductID { get; private set; }
[Required]
[SqlMoney]
[DbColumn(Description = "Selling price of a single product.")]
public _Decimal UnitPrice { get; private set; }
[Required]
[SqlMoney]
[DefaultValue(typeof(decimal), "0", Name = "DF_SalesOrderDetail_UnitPriceDiscount")]
[DbColumn(Description = "Discount amount.")]
public _Decimal UnitPriceDiscount { get; private set; }
[Required]
[SqlMoney]
[DbColumn(Description = "Per product subtotal. Computed as UnitPrice * (1 - UnitPriceDiscount) * OrderQty.")]
public _Decimal LineTotal { get; private set; }
[_Computation]
private void ComputeLineTotal()
{
LineTotal.ComputedAs((UnitPrice * (_Decimal.Const(1) - UnitPriceDiscount) * OrderQty).IfNull(_Decimal.Const(0)));
}
[_CheckConstraint]
private _Boolean CK_SalesOrderDetail_OrderQty
{
get { return OrderQty > _Decimal.Const(0); }
}
[_CheckConstraint]
private _Boolean CK_SalesOrderDetail_UnitPrice
{
get { return UnitPrice >= _Decimal.Const(0); }
}
[_CheckConstraint]
private _Boolean CK_SalesOrderDetail_UnitPriceDiscount
{
get { return UnitPriceDiscount >= _Decimal.Const(0); }
}
[_DbIndex]
private ColumnSort[] IX_SalesOrderDetail_ProductID => new ColumnSort[] { ProductID };
}
}
</code></pre></section>
<section id="tabpanel_CeZOj-G++Q_vb" role="tabpanel" data-tab="vb" aria-hidden="true" hidden="hidden">
<pre><code class="lang-vb" name="SalesOrderDetail"><Computation("ComputeLineTotal")>
<CheckConstraint("CK_SalesOrderDetail_OrderQty", GetType(My.UserMessages), NameOf(My.UserMessages.CK_SalesOrderDetail_OrderQty), Description:="Check constraint [OrderQty] > (0)")>
<CheckConstraint("CK_SalesOrderDetail_UnitPrice", GetType(My.UserMessages), NameOf(My.UserMessages.CK_SalesOrderDetail_UnitPrice), Description:="heck constraint [UnitPrice] >= (0.00)")>
<CheckConstraint("CK_SalesOrderDetail_UnitPriceDiscount", GetType(My.UserMessages), NameOf(My.UserMessages.CK_SalesOrderDetail_UnitPriceDiscount), Description:="Check constraint [UnitPriceDiscount] >= (0.00)")>
<DbIndex("IX_SalesOrderDetail_ProductID", Description:="Nonclustered index.")>
Public Class SalesOrderDetail
Inherits BaseModel(Of PK)
<DbPrimaryKey("PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID", Description:="Clustered index created by a primary key constraint.")>
Public NotInheritable Class PK
Inherits CandidateKey
Public Sub New(salesOrderID As _Int32, salesOrderDetailID As _Int32)
MyBase.New(salesOrderID, salesOrderDetailID)
End Sub
End Class
Public Class Key
Inherits Key(Of PK)
Shared Sub New()
Register(Function(x As Key) x.SalesOrderID, _SalesOrderID)
Register(Function(x As Key) x.SalesOrderDetailID, _SalesOrderDetailID)
End Sub
Protected Overrides Function CreatePrimaryKey() As PK
Return New PK(SalesOrderID, SalesOrderDetailID)
End Function
Private m_SalesOrderID As _Int32
Public Property SalesOrderID As _Int32
Get
Return m_SalesOrderID
End Get
Private Set
m_SalesOrderID = Value
End Set
End Property
Private m_SalesOrderDetailID As _Int32
Public Property SalesOrderDetailID As _Int32
Get
Return m_SalesOrderDetailID
End Get
Private Set
m_SalesOrderDetailID = Value
End Set
End Property
End Class
Public Shared ReadOnly _SalesOrderID As Mounter(Of _Int32) = RegisterColumn(Function(x As SalesOrderDetail) x.SalesOrderID)
Public Shared ReadOnly _SalesOrderDetailID As Mounter(Of _Int32) = RegisterColumn(Function(x As SalesOrderDetail) x.SalesOrderDetailID)
Public Shared ReadOnly _OrderQty As Mounter(Of _Int16) = RegisterColumn(Function(x As SalesOrderDetail) x.OrderQty)
Public Shared ReadOnly _ProductID As Mounter(Of _Int32) = RegisterColumn(Function(x As SalesOrderDetail) x.ProductID)
Public Shared ReadOnly _UnitPrice As Mounter(Of _Decimal) = RegisterColumn(Function(x As SalesOrderDetail) x.UnitPrice)
Public Shared ReadOnly _UnitPriceDiscount As Mounter(Of _Decimal) = RegisterColumn(Function(x As SalesOrderDetail) x.UnitPriceDiscount)
Public Shared ReadOnly _LineTotal As Mounter(Of _Decimal) = RegisterColumn(Function(x As SalesOrderDetail) x.LineTotal)
Protected NotOverridable Overrides Function CreatePrimaryKey() As PK
Return New PK(SalesOrderID, SalesOrderDetailID)
End Function
Private m_FK_SalesOrderHeader As SalesOrderHeader.PK
Public ReadOnly Property FK_SalesOrderHeader As SalesOrderHeader.PK
Get
If m_FK_SalesOrderHeader Is Nothing Then
m_FK_SalesOrderHeader = New SalesOrderHeader.PK(SalesOrderID)
End If
Return m_FK_SalesOrderHeader
End Get
End Property
Private m_FK_Product As Product.PK
Public ReadOnly Property FK_Product As Product.PK
Get
If m_FK_Product Is Nothing Then
m_FK_Product = New Product.PK(ProductID)
End If
Return m_FK_Product
End Get
End Property
Private m_SalesOrderID As _Int32
<DbColumn(Description:="Primary key. Foreign key to SalesOrderHeader.SalesOrderID.")>
Public Property SalesOrderID As _Int32
Get
Return m_SalesOrderID
End Get
Private Set
m_SalesOrderID = Value
End Set
End Property
Private m_SalesOrderDetailID As _Int32
<Identity>
<DbColumn(Description:="Primary key. One incremental unique number per product sold.")>
Public Property SalesOrderDetailID As _Int32
Get
Return m_SalesOrderDetailID
End Get
Private Set
m_SalesOrderDetailID = Value
End Set
End Property
Private m_OrderQty As _Int16
<Required>
<DbColumn(Description:="Quantity ordered per product.")>
Public Property OrderQty As _Int16
Get
Return m_OrderQty
End Get
Private Set
m_OrderQty = Value
End Set
End Property
Private m_ProductID As _Int32
<Required>
<DbColumn(Description:="Product sold to customer. Foreign key to Product.ProductID.")>
Public Property ProductID As _Int32
Get
Return m_ProductID
End Get
Private Set
m_ProductID = Value
End Set
End Property
Private m_UnitPrice As _Decimal
<Required>
<SqlMoney>
<DbColumn(Description:="Selling price of a single product.")>
Public Property UnitPrice As _Decimal
Get
Return m_UnitPrice
End Get
Private Set
m_UnitPrice = Value
End Set
End Property
Private m_UnitPriceDiscount As _Decimal
<Required>
<SqlMoney>
<DefaultValue(GetType(Decimal), "0", Name:="DF_SalesOrderDetail_UnitPriceDiscount")>
<DbColumn(Description:="Discount amount.")>
Public Property UnitPriceDiscount As _Decimal
Get
Return m_UnitPriceDiscount
End Get
Private Set
m_UnitPriceDiscount = Value
End Set
End Property
Private m_LineTotal As _Decimal
<Required>
<SqlMoney>
<DbColumn(Description:="Per product subtotal. Computed as UnitPrice * (1 - UnitPriceDiscount) * OrderQty.")>
Public Property LineTotal As _Decimal
Get
Return m_LineTotal
End Get
Private Set
m_LineTotal = Value
End Set
End Property
<_Computation>
Private Sub ComputeLineTotal()
LineTotal.ComputedAs((UnitPrice * (_Decimal.[Const](1) - UnitPriceDiscount) * OrderQty).IfNull(_Decimal.[Const](0)))
End Sub
<_CheckConstraint>
Private ReadOnly Property CK_SalesOrderDetail_OrderQty As _Boolean
Get
Return OrderQty > _Decimal.Const(0)
End Get
End Property
<_CheckConstraint>
Private ReadOnly Property CK_SalesOrderDetail_UnitPrice As _Boolean
Get
Return UnitPrice >= _Decimal.Const(0)
End Get
End Property
<_CheckConstraint>
Private ReadOnly Property CK_SalesOrderDetail_UnitPriceDiscount As _Boolean
Get
Return UnitPriceDiscount >= _Decimal.Const(0)
End Get
End Property
<_DbIndex>
Private ReadOnly Property IX_SalesOrderDetail_ProductID As ColumnSort()
Get
Return New ColumnSort() {ProductID}
End Get
End Property
End Class
</code></pre></section>
</div>
<p>The code of model can be manipulated in Model Visualizer tool window in Visual Studio:</p>
<p><img src="/images/SalesOrderDetailModelVisualizer.jpg" alt="image"></p>
<h3 id="the-database">The Database</h3>
<div class="tabGroup" id="tabgroup_CeZOj-G++Q-1">
<ul role="tablist">
<li role="presentation">
<a href="#tabpanel_CeZOj-G++Q-1_cs" role="tab" aria-controls="tabpanel_CeZOj-G++Q-1_cs" data-tab="cs" tabindex="0" aria-selected="true">C#</a>
</li>
<li role="presentation">
<a href="#tabpanel_CeZOj-G++Q-1_vb" role="tab" aria-controls="tabpanel_CeZOj-G++Q-1_vb" data-tab="vb" tabindex="-1">VB.Net</a>
</li>
</ul>
<section id="tabpanel_CeZOj-G++Q-1_cs" role="tabpanel" data-tab="cs">
<pre><code class="lang-csharp" name="Db">using DevZest.Data;
using DevZest.Data.Annotations;
using DevZest.Data.SqlServer;
using System;
using System.Data.SqlClient;
namespace DevZest.Samples.AdventureWorksLT
{
public partial class Db : SqlSession
{
public Db(string connectionString, Action<Db> initializer = null)
: base(CreateSqlConnection(connectionString))
{
initializer?.Invoke(this);
}
private static SqlConnection CreateSqlConnection(string connectionString)
{
if (string.IsNullOrEmpty(connectionString))
throw new ArgumentNullException(nameof(connectionString));
return new SqlConnection(connectionString);
}
#if !DEPLOY
// For unit tests
public Db(SqlVersion sqlVersion)
: base(new SqlConnection())
{
SqlVersion = sqlVersion;
}
#endif
public Db(SqlConnection sqlConnection)
: base(sqlConnection)
{
}
private DbTable<Address> _address;
[DbTable("[SalesLT].[Address]", Description = "Street address information for customers.")]
public DbTable<Address> Address
{
get { return GetTable(ref _address); }
}
private DbTable<Customer> _customer;
[DbTable("[SalesLT].[Customer]", Description = "Customer information.")]
public DbTable<Customer> Customer
{
get { return GetTable(ref _customer); }
}
private DbTable<CustomerAddress> _customerAddress;
[DbTable("[SalesLT].[CustomerAddress]", Description = "Cross-reference table mapping customers to their address(es).")]
[Relationship(nameof(FK_CustomerAddress_Customer_CustomerID), Description = "Foreign key constraint referencing Customer.CustomerID.")]
[Relationship(nameof(FK_CustomerAddress_Address_AddressID), Description = "Foreign key constraint referencing Address.AddressID.")]
public DbTable<CustomerAddress> CustomerAddress
{
get { return GetTable(ref _customerAddress); }
}
[_Relationship]
private KeyMapping FK_CustomerAddress_Customer_CustomerID(CustomerAddress _)
{
return _.FK_Customer.Join(Customer._);
}
[_Relationship]
private KeyMapping FK_CustomerAddress_Address_AddressID(CustomerAddress _)
{
return _.FK_Address.Join(Address._);
}
private DbTable<ProductCategory> _productCategory;
[DbTable("[SalesLT].[ProductCategory]", Description = "High-level product categorization.")]
[Relationship(nameof(FK_ProductCategory_ProductCategory_ParentProductCategoryID_ProductCategoryID), Description = "Foreign key constraint referencing ProductCategory.ProductCategoryID.")]
public DbTable<ProductCategory> ProductCategory
{
get { return GetTable(ref _productCategory); }
}
[_Relationship]
private KeyMapping FK_ProductCategory_ProductCategory_ParentProductCategoryID_ProductCategoryID(ProductCategory _)
{
return _.FK_ParentProductCategory.Join(_);
}
private DbTable<ProductModel> _productModel;
[DbTable("[SalesLT].[ProductModel]")]
public DbTable<ProductModel> ProductModel
{
get { return GetTable(ref _productModel); }
}
private DbTable<ProductDescription> _productDescription;
[DbTable("[SalesLT].[ProductDescription]", Description = "Product descriptions in several languages.")]
public DbTable<ProductDescription> ProductDescription
{
get { return GetTable(ref _productDescription); }
}
private DbTable<ProductModelProductDescription> _productModelProductDescription;
[DbTable("[SalesLT].[ProductModelProductDescription]", Description = "Cross-reference table mapping product descriptions and the language the description is written in.")]
[Relationship(nameof(FK_ProductModelProductDescription_ProductModel_ProductModelID), Description = "Foreign key constraint referencing ProductModel.ProductModelID.")]
[Relationship(nameof(FK_ProductModelProductDescription_ProductDescription_ProductDescriptionID), Description = "Foreign key constraint referencing ProductDescription.ProductDescriptionID.")]
public DbTable<ProductModelProductDescription> ProductModelProductDescription
{
get { return GetTable(ref _productModelProductDescription); }
}
[_Relationship]
private KeyMapping FK_ProductModelProductDescription_ProductModel_ProductModelID(ProductModelProductDescription _)
{
return _.FK_ProductModel.Join(ProductModel._);
}
[_Relationship]
private KeyMapping FK_ProductModelProductDescription_ProductDescription_ProductDescriptionID(ProductModelProductDescription _)
{
return _.FK_ProductDescription.Join(ProductDescription._);
}
private DbTable<Product> _product;
[DbTable("[SalesLT].[Product]", Description = "Products sold or used in the manfacturing of sold products.")]
[Relationship(nameof(FK_Product_ProductModel_ProductModelID))]
[Relationship(nameof(FK_Product_ProductCategory_ProductCategoryID))]
public DbTable<Product> Product
{
get { return GetTable(ref _product); }
}
[_Relationship]
private KeyMapping FK_Product_ProductModel_ProductModelID(Product _)
{
return _.FK_ProductModel.Join(ProductModel._);
}
[_Relationship]
private KeyMapping FK_Product_ProductCategory_ProductCategoryID(Product _)
{
return _.FK_ProductCategory.Join(ProductCategory._);
}
private DbTable<SalesOrderHeader> _salesOrderHeader;
[DbTable("[SalesLT].[SalesOrderHeader]", Description = "General sales order information.")]
[Relationship(nameof(FK_SalesOrderHeader_Customer_CustomerID))]
[Relationship(nameof(FK_SalesOrderHeader_Address_BillTo_AddressID))]
[Relationship(nameof(FK_SalesOrderHeader_Address_ShipTo_AddressID))]
public DbTable<SalesOrderHeader> SalesOrderHeader
{
get { return GetTable(ref _salesOrderHeader); }
}
[_Relationship]
private KeyMapping FK_SalesOrderHeader_Customer_CustomerID(SalesOrderHeader _)
{
return _.FK_Customer.Join(Customer._);
}
[_Relationship]
private KeyMapping FK_SalesOrderHeader_Address_BillTo_AddressID(SalesOrderHeader _)
{
return _.FK_BillToCustomerAddress.Join(CustomerAddress._);
}
[_Relationship]
private KeyMapping FK_SalesOrderHeader_Address_ShipTo_AddressID(SalesOrderHeader _)
{
return _.FK_ShipToCustomerAddress.Join(CustomerAddress._);
}
private DbTable<SalesOrderDetail> _salesOrderDetail;
[DbTable("[SalesLT].[SalesOrderDetail]", Description = "Individual products associated with a specific sales order. See SalesOrderHeader.")]
[Relationship(nameof(FK_SalesOrderDetail_SalesOrderHeader))]
[Relationship(nameof(FK_SalesOrderDetail_Product))]
public DbTable<SalesOrderDetail> SalesOrderDetail
{
get { return GetTable(ref _salesOrderDetail); }
}
[_Relationship]
private KeyMapping FK_SalesOrderDetail_SalesOrderHeader(SalesOrderDetail _)
{
return _.FK_SalesOrderHeader.Join(SalesOrderHeader._);
}
[_Relationship]
private KeyMapping FK_SalesOrderDetail_Product(SalesOrderDetail _)
{
return _.FK_Product.Join(Product._);
}
}
}
</code></pre></section>
<section id="tabpanel_CeZOj-G++Q-1_vb" role="tabpanel" data-tab="vb" aria-hidden="true" hidden="hidden">
<pre><code class="lang-vb" name="Db">Imports System.Data.SqlClient
Partial Public Class Db
Inherits SqlSession
Public Sub New(connectionString As String, Optional initializer As Action(Of Db) = Nothing)
MyBase.New(CreateSqlConnection(connectionString))
If initializer IsNot Nothing Then initializer(Me)
End Sub
Private Shared Function CreateSqlConnection(connectionString As String) As SqlConnection
If String.IsNullOrEmpty(connectionString) Then Throw New ArgumentNullException(NameOf(connectionString))
Return New SqlConnection(connectionString)
End Function
Public Sub New(ByVal sqlVersion As SqlVersion)
MyBase.New(New SqlConnection())
sqlVersion = sqlVersion
End Sub
Public Sub New(ByVal sqlConnection As SqlConnection)
MyBase.New(sqlConnection)
End Sub
Private m_Address As DbTable(Of Address)
<DbTable("[SalesLT].[Address]", Description:="Street address information for customers.")>
Public ReadOnly Property Address As DbTable(Of Address)
Get
Return GetTable(m_Address)
End Get
End Property
Private m_Customer As DbTable(Of Customer)
<DbTable("[SalesLT].[Customer]", Description:="Customer information.")>
Public ReadOnly Property Customer As DbTable(Of Customer)
Get
Return GetTable(m_Customer)
End Get
End Property
Private m_CustomerAddress As DbTable(Of CustomerAddress)
<DbTable("[SalesLT].[CustomerAddress]", Description:="Cross-reference table mapping customers to their address(es).")>
<Relationship(NameOf(FK_CustomerAddress_Customer_CustomerID), Description:="Foreign key constraint referencing Customer.CustomerID.")>
<Relationship(NameOf(FK_CustomerAddress_Address_AddressID), Description:="Foreign key constraint referencing Address.AddressID.")>
Public ReadOnly Property CustomerAddress As DbTable(Of CustomerAddress)
Get
Return GetTable(m_CustomerAddress)
End Get
End Property
<_Relationship>
Private Function FK_CustomerAddress_Customer_CustomerID(x As CustomerAddress) As KeyMapping
Return x.FK_Customer.Join(Customer.Entity)
End Function
<_Relationship>
Private Function FK_CustomerAddress_Address_AddressID(x As CustomerAddress) As KeyMapping
Return x.FK_Address.Join(Address.Entity)
End Function
Private m_ProductCategory As DbTable(Of ProductCategory)
<DbTable("[SalesLT].[ProductCategory]", Description:="High-level product categorization.")>
<Relationship(NameOf(FK_ProductCategory_ProductCategory_ParentProductCategoryID_ProductCategoryID), Description:="Foreign key constraint referencing ProductCategory.ProductCategoryID.")>
Public ReadOnly Property ProductCategory As DbTable(Of ProductCategory)
Get
Return GetTable(m_ProductCategory)
End Get
End Property
<_Relationship>
Private Function FK_ProductCategory_ProductCategory_ParentProductCategoryID_ProductCategoryID(x As ProductCategory) As KeyMapping
Return x.FK_ParentProductCategory.Join(x)
End Function
Private m_ProductModel As DbTable(Of ProductModel)
<DbTable("[SalesLT].[ProductModel]")>
Public ReadOnly Property ProductModel As DbTable(Of ProductModel)
Get
Return GetTable(m_ProductModel)
End Get
End Property
Private m_ProductDescription As DbTable(Of ProductDescription)
<DbTable("[SalesLT].[ProductDescription]", Description:="Product descriptions in several languages.")>
Public ReadOnly Property ProductDescription As DbTable(Of ProductDescription)
Get
Return GetTable(m_ProductDescription)
End Get
End Property
Private m_ProductModelProductDescription As DbTable(Of ProductModelProductDescription)
<DbTable("[SalesLT].[ProductModelProductDescription]", Description:="Cross-reference table mapping product descriptions and the language the description is written in.")>
<Relationship(NameOf(FK_ProductModelProductDescription_ProductModel_ProductModelID), Description:="Foreign key constraint referencing ProductModel.ProductModelID.")>
<Relationship(NameOf(FK_ProductModelProductDescription_ProductDescription_ProductDescriptionID), Description:="Foreign key constraint referencing ProductDescription.ProductDescriptionID.")>
Public ReadOnly Property ProductModelProductDescription As DbTable(Of ProductModelProductDescription)
Get
Return GetTable(m_ProductModelProductDescription)
End Get
End Property
<_Relationship>
Private Function FK_ProductModelProductDescription_ProductModel_ProductModelID(x As ProductModelProductDescription) As KeyMapping
Return x.FK_ProductModel.Join(ProductModel.Entity)
End Function
<_Relationship>
Private Function FK_ProductModelProductDescription_ProductDescription_ProductDescriptionID(x As ProductModelProductDescription) As KeyMapping
Return x.FK_ProductDescription.Join(ProductDescription.Entity)
End Function
Private m_Product As DbTable(Of Product)
<DbTable("[SalesLT].[Product]", Description:="Products sold or used in the manfacturing of sold products.")>
<Relationship(NameOf(FK_Product_ProductModel_ProductModelID))>
<Relationship(NameOf(FK_Product_ProductCategory_ProductCategoryID))>
Public ReadOnly Property Product As DbTable(Of Product)
Get
Return GetTable(m_Product)
End Get
End Property
<_Relationship>
Private Function FK_Product_ProductModel_ProductModelID(x As Product) As KeyMapping
Return x.FK_ProductModel.Join(ProductModel.Entity)
End Function
<_Relationship>
Private Function FK_Product_ProductCategory_ProductCategoryID(x As Product) As KeyMapping
Return x.FK_ProductCategory.Join(ProductCategory.Entity)
End Function
Private m_SalesOrderHeader As DbTable(Of SalesOrderHeader)
<DbTable("[SalesLT].[SalesOrderHeader]", Description:="General sales order information.")>
<Relationship(NameOf(FK_SalesOrderHeader_Customer_CustomerID))>
<Relationship(NameOf(FK_SalesOrderHeader_Address_BillTo_AddressID))>
<Relationship(NameOf(FK_SalesOrderHeader_Address_ShipTo_AddressID))>
Public ReadOnly Property SalesOrderHeader As DbTable(Of SalesOrderHeader)
Get
Return GetTable(m_SalesOrderHeader)
End Get
End Property
<_Relationship>
Private Function FK_SalesOrderHeader_Customer_CustomerID(x As SalesOrderHeader) As KeyMapping
Return x.FK_Customer.Join(Customer.Entity)
End Function
<_Relationship>
Private Function FK_SalesOrderHeader_Address_BillTo_AddressID(x As SalesOrderHeader) As KeyMapping
Return x.FK_BillToCustomerAddress.Join(CustomerAddress.Entity)
End Function
<_Relationship>
Private Function FK_SalesOrderHeader_Address_ShipTo_AddressID(x As SalesOrderHeader) As KeyMapping
Return x.FK_ShipToCustomerAddress.Join(CustomerAddress.Entity)
End Function
Private m_SalesOrderDetail As DbTable(Of SalesOrderDetail)
<DbTable("[SalesLT].[SalesOrderDetail]", Description:="Individual products associated with a specific sales order. See SalesOrderHeader.")>
<Relationship(NameOf(FK_SalesOrderDetail_SalesOrderHeader))>
<Relationship(NameOf(FK_SalesOrderDetail_Product))>
Public ReadOnly Property SalesOrderDetail As DbTable(Of SalesOrderDetail)
Get
Return GetTable(m_SalesOrderDetail)
End Get
End Property
<_Relationship>
Private Function FK_SalesOrderDetail_SalesOrderHeader(x As SalesOrderDetail) As KeyMapping
Return x.FK_SalesOrderHeader.Join(SalesOrderHeader.Entity)
End Function
<_Relationship>
Private Function FK_SalesOrderDetail_Product(x As SalesOrderDetail) As KeyMapping
Return x.FK_Product.Join(Product.Entity)
End Function
End Class
</code></pre></section>
</div>
<p>The code of database can be manipulated via Db Visualizer tool window in Visual Studio:</p>
<p><img src="/images/AdventureWorksLTDbVisualizer.jpg" alt="image"></p>
<h3 id="data-and-business-layer">Data and Business Layer</h3>
<div class="tabGroup" id="tabgroup_CeZOj-G++Q-2">
<ul role="tablist">
<li role="presentation">
<a href="#tabpanel_CeZOj-G++Q-2_cs" role="tab" aria-controls="tabpanel_CeZOj-G++Q-2_cs" data-tab="cs" tabindex="0" aria-selected="true">C#</a>
</li>
<li role="presentation">
<a href="#tabpanel_CeZOj-G++Q-2_vb" role="tab" aria-controls="tabpanel_CeZOj-G++Q-2_vb" data-tab="vb" tabindex="-1">VB.Net</a>
</li>
</ul>
<section id="tabpanel_CeZOj-G++Q-2_cs" role="tabpanel" data-tab="cs">
<pre><code class="lang-csharp" name="DbApi">
private async Task EnsureConnectionOpenAsync(CancellationToken ct)
{
if (Connection.State != ConnectionState.Open)
await OpenConnectionAsync(ct);
}
public async Task<DataSet<SalesOrderInfo>> GetSalesOrderInfoAsync(_Int32 salesOrderID, CancellationToken ct = default(CancellationToken))
{
var result = CreateQuery((DbQueryBuilder builder, SalesOrderInfo _) =>
{
builder.From(SalesOrderHeader, out var o)
.LeftJoin(Customer, o.FK_Customer, out var c)
.LeftJoin(Address, o.FK_ShipToAddress, out var shipTo)
.LeftJoin(Address, o.FK_BillToAddress, out var billTo)
.AutoSelect()
.AutoSelect(c, _.Customer)
.AutoSelect(shipTo, _.ShipToAddress)
.AutoSelect(billTo, _.BillToAddress)
.Where(o.SalesOrderID == salesOrderID);
});
await result.CreateChildAsync(_ => _.SalesOrderDetails, (DbQueryBuilder builder, SalesOrderInfoDetail _) =>
{
builder.From(SalesOrderDetail, out var d)
.LeftJoin(Product, d.FK_Product, out var p)
.AutoSelect()
.AutoSelect(p, _.Product)
.OrderBy(d.SalesOrderDetailID);
}, ct);
return await result.ToDataSetAsync(ct);
}
public async Task<int?> CreateSalesOrderAsync(DataSet<SalesOrderInfo> salesOrders, CancellationToken ct)
{
await EnsureConnectionOpenAsync(ct);
using (var transaction = BeginTransaction())
{
salesOrders._.ResetRowIdentifiers();
await SalesOrderHeader.InsertAsync(salesOrders, true, ct);
var salesOrderDetails = salesOrders.GetChild(_ => _.SalesOrderDetails);
salesOrderDetails._.ResetRowIdentifiers();
await SalesOrderDetail.InsertAsync(salesOrderDetails, ct);
await transaction.CommitAsync(ct);
return salesOrders.Count > 0 ? salesOrders._.SalesOrderID[0] : null;
}
}
public async Task UpdateSalesOrderAsync(DataSet<SalesOrderInfo> salesOrders, CancellationToken ct)
{
await EnsureConnectionOpenAsync(ct);
using (var transaction = BeginTransaction())
{
salesOrders._.ResetRowIdentifiers();
await SalesOrderHeader.UpdateAsync(salesOrders, ct);
await SalesOrderDetail.DeleteAsync(salesOrders, (s, _) => s.Match(_.FK_SalesOrderHeader), ct);
var salesOrderDetails = salesOrders.GetChild(_ => _.SalesOrderDetails);
salesOrderDetails._.ResetRowIdentifiers();
await SalesOrderDetail.InsertAsync(salesOrderDetails, ct);
await transaction.CommitAsync(ct);
}
}
public Task<int> DeleteSalesOrderAsync(DataSet<SalesOrderHeader.Key> dataSet, CancellationToken ct)
{
return SalesOrderHeader.DeleteAsync(dataSet, (s, _) => s.Match(_), ct);
}
</code></pre></section>
<section id="tabpanel_CeZOj-G++Q-2_vb" role="tabpanel" data-tab="vb" aria-hidden="true" hidden="hidden">
<pre><code class="lang-vb" name="DbApi">Imports System.Data
Imports System.Threading
Partial Class Db
Private Async Function EnsureConnectionOpenAsync(ct As CancellationToken) As Task
If Connection.State <> ConnectionState.Open Then
Await OpenConnectionAsync(ct)
End If
End Function
Public Async Function GetSalesOrderInfoAsync(salesOrderID As _Int32, Optional ct As CancellationToken = Nothing) As Task(Of DataSet(Of SalesOrderInfo))
Dim result = CreateQuery(
Sub(builder As DbQueryBuilder, x As SalesOrderInfo)
Dim o As SalesOrderHeader = Nothing, c As Customer = Nothing, shipTo As Address = Nothing, billTo As Address = Nothing
builder.From(SalesOrderHeader, o).
LeftJoin(Customer, o.FK_Customer, c).
LeftJoin(Address, o.FK_ShipToAddress, shipTo).
LeftJoin(Address, o.FK_BillToAddress, billTo).
AutoSelect().
AutoSelect(c, x.Customer).
AutoSelect(shipTo, x.ShipToAddress).
AutoSelect(billTo, x.BillToAddress).
Where(o.SalesOrderID = salesOrderID)
End Sub)
Await result.CreateChildAsync(Function(x) x.SalesOrderDetails,
Sub(builder As DbQueryBuilder, x As SalesOrderInfoDetail)
Dim d As SalesOrderDetail = Nothing, p As Product = Nothing
builder.From(SalesOrderDetail, d).LeftJoin(Product, d.FK_Product, p).AutoSelect().AutoSelect(p, x.Product)
End Sub, ct)
Return Await result.ToDataSetAsync(ct)
End Function
Public Function DeleteSalesOrderAsync(dataSet As DataSet(Of SalesOrderHeader.Key), ct As CancellationToken) As Task(Of Integer)
Return SalesOrderHeader.DeleteAsync(dataSet, Function(s, x) s.Match(x), ct)
End Function
Public Async Function UpdateSalesOrderAsync(salesOrders As DataSet(Of SalesOrderInfo), ct As CancellationToken) As Task
Await EnsureConnectionOpenAsync(ct)
Using transaction = BeginTransaction()
salesOrders.Entity.ResetRowIdentifiers()
Await SalesOrderHeader.UpdateAsync(salesOrders, ct)
Await SalesOrderDetail.DeleteAsync(salesOrders, Function(s, x) s.Match(x.FK_SalesOrderHeader), ct)
Dim salesOrderDetails = salesOrders.GetChild(Function(x) x.SalesOrderDetails)
salesOrderDetails.Entity.ResetRowIdentifiers()
Await SalesOrderDetail.InsertAsync(salesOrderDetails, ct)
Await transaction.CommitAsync(ct)
End Using
End Function
Public Async Function CreateSalesOrderAsync(salesOrders As DataSet(Of SalesOrderInfo), ct As CancellationToken) As Task(Of Integer?)
Await EnsureConnectionOpenAsync(ct)
Using transaction = BeginTransaction()
salesOrders.Entity.ResetRowIdentifiers()
Await SalesOrderHeader.InsertAsync(salesOrders, True, ct)
Dim salesOrderDetails = salesOrders.GetChild(Function(x) x.SalesOrderDetails)
salesOrderDetails.Entity.ResetRowIdentifiers()
Await SalesOrderDetail.InsertAsync(salesOrderDetails, ct)
Await transaction.CommitAsync(ct)
If salesOrders.Count > 0 Then
Return salesOrders.Entity.SalesOrderID(0)
Else
Return Nothing
End If
End Using
End Function
End Class
</code></pre></section>
</div>
<h3 id="data-presentation">Data Presentation</h3>
<div class="tabGroup" id="tabgroup_CeZOj-G++Q-3">
<ul role="tablist">
<li role="presentation">
<a href="#tabpanel_CeZOj-G++Q-3_cs" role="tab" aria-controls="tabpanel_CeZOj-G++Q-3_cs" data-tab="cs" tabindex="0" aria-selected="true">C#</a>
</li>
<li role="presentation">
<a href="#tabpanel_CeZOj-G++Q-3_vb" role="tab" aria-controls="tabpanel_CeZOj-G++Q-3_vb" data-tab="vb" tabindex="-1">VB.Net</a>
</li>
</ul>
<section id="tabpanel_CeZOj-G++Q-3_cs" role="tabpanel" data-tab="cs">
<pre><code class="lang-csharp" name="DetailPresenter">using DevZest.Data.Presenters;
using DevZest.Data.Views;
using DevZest.Data;
using System.Windows;
using System;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Diagnostics;
namespace DevZest.Samples.AdventureWorksLT
{
partial class SalesOrderWindow