C# Ado - Net 2010
C# Ado - Net 2010
ADO.NET
ﻟﻤﺒﺭﻤﺠﻲ ﺴﻲ ﺸﺎﺭﺏ ٢٠١٠
ﺒﻘﻠﻡ:
٢
ﻟﻬﺫﺍ ﺃﺭﺠﻭ ﻤﻥ ﻜل ﻤﻥ ﻴﺴﺘﻔﻴﺩ ﺒﻪ ﺃﻥ ﻴﺘﺫﻜﺭ ﺃﻥ ﺃﺒﻲ ﻫﻭ ﺍﻟـﺫﻱ ﺭﺒـﺎﻨﻲ ﻭﻋﻠﻤﻨـﻲ
ﻭﻟﻭﻻﻩ ﺒﻌﺩ ﺘﻭﻓﻴﻕ ﺍﷲ ﻤﺎ ﺨﺭﺝ ﺇﻟﻰ ﺍﻟﻭﺠﻭﺩ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ﻭﻏﻴﺭﻩ ﻤﻥ ﺍﻟﻜﺘﺏ.
ﻓﺎﺩﻋﻭﺍ ﻟﻪ ﺒﺎﻟﺭﺤﻤﺔ ﻭﺍﻟﻤﻐﻔﺭﺓ
ﻭﻤﻥ ﻜﺎﻥ ﻤﻨﻜﻡ ﻓﻲ ﺍﻟﺤﺭﻤﻴﻥ ﺍﻟﺸﺭﻴﻔﻴﻥ ﻭﻜﺎﻥ ﻗﺎﺩﺭﺍ ﻋﻠﻰ ﻋﻤل ﻋﻤﺭﺓ ﻟﻪ ،ﻓﺠﺯﺍﻩ
ﺍﷲ ﺨﻴﺭﺍ.
ﺃﺩﻋﻭ ﺍﷲ ﺃﻥ ﻴﻜﻭﻥ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ﻭﺒﺎﻗﻲ ﻜﺘﺒﻲ ﻤﻥ ﺍﻟﻌﻠﻡ ﺍﻟﺫﻱ ﻴﻨﺘﻔﻊ ﺒﻪ ،ﻭﺃﻥ ﻴﺠﻌل
ﺍﷲ ﻷﺒﻲ ﻨﺼﻴﺒﺎ ﻤﻥ ﺜﻭﺍﺒﻪ ،ﻓﻴﻜﻭﻥ ﻤﻥ ﻋﻤﻠﻪ ﺍﻟﺫﻱ ﻻ ﻴﻨﻘﻁﻊ ﺒﻤﻭﺘﻪ.
ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ ﻭﻗﻪ ﻤﻥ ﻋﺫﺍﺏ ﺍﻟﻘﺒﺭ ﻭﻗﻪ ﻤﻥ ﻋﺫﺍﺏ ﺍﻟﻨﺎﺭ،
ﻭﺃﺩﺨﻠﻪ ﺍﻟﺠﻨﺔ ﻭﺃﻋلِ ﻤﻨﺯﻟﺘﻪ ﻓﻴﻬﺎ
ﻭﺍﺤﻔﻅ ﻭﺍﻟﺩﺘﻲ ﻭﺒﺎﺭﻙ ﻓﻲ ﻋﻤﺭﻫﺎ
ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﻭﺍﻟﺩﻱ ﻜﻤﺎ ﺭﺒﻴﺎﻨﻲ ﺼﻐﻴﺭﺍ
ﺁﻤﻴﻥ ﻴﺎ ﺭﺏ ﺍﻟﻌﺎﻟﻤﻴﻥ
٣
ﻟﻠﺘﻭﺍﺼل ﻤﻊ ﺍﻟﻜﺎﺘﺏ:
-ﺒﺭﻴﺩﻱ ﺍﻻﻟﻜﺘﺭﻭﻨﻲ:
[email protected]
-ﻤﺩﻭﻨﺘﻲ:
http://mhmdhmdy.blogspot.com
-ﻗﻨﺎﺘﻲ ﻋﻠﻰ ﻴﻭﺘﻴﻭﺏ )ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺇﻟﻘﺎﺀ ﺃﻜﺜﺭ ﻤﻥ ٦٠ﻗﺼﻴﺩﺓ ﺒﺼﻭﺘﻲ(:
http://www.youtube.com/user/mhmdhmdy
-ﺼﻔﺤﺘﻲ ﺍﻷﺩﺒﻴﺔ ﻋﻠﻰ ﻓﻴﺴﺒﻭﻙ:
https://www.facebook.com/Poet.Mhmd.Hmdy
-ﻜﺘﺒﻲ ﻓﻲ ﻤﺠﺎل ﺍﻟﺒﺭﻤﺠﺔ ﺒﻠﻐﺘﻲ ﻓﻴﺠﻭﺍل ﺒﻴﺯﻴﻙ ﻭﺴﻲ ﺸﺎﺭﺏ:
https://drive.google.com/drive/folders/1J21xi8Aw15BFSv-
GUgVOElLuYM6zoNct
-ﺼﻔﺤﺔ ﻓﻴﺠﻭﺍل ﺒﻴﺯﻴﻙ ﻭﺴﻲ ﺸﺎﺭﺏ ﻋﻠﻰ ﻓﻴﺴﺒﻭﻙ:
https://www.facebook.com/vbandcsharp
٤
ﻜﺘﺏ ﻤﻁﺒﻭﻋﺔ ﻟﻠﻜﺎﺘﺏ:
ـﻴﻥ
ـﺩﻯ ﺍﻟﻠﻐﺘـ
ـﻥ ﺇﺤـ
ـﺎل ﻤـ
ـﺭ ﻟﻼﻨﺘﻘـ
ـﻙ ﺍﻟﻤﺨﺘﺼـ
ـﺎﺭﺏ :ﻁﺭﻴﻘـ
ـﻲ ﺸـ
ـﻙ ﻭﺴـ
ـﻭﺍل ﺒﻴﺯﻴـ
.١ﻓﻴﺠﻴـ
ﺇﻟﻰ ﺍﻷﺨﺭﻯ.
.٢ﻤﻥ ﺍﻟﺼﻔﺭ ﺇﻟﻰ ﺍﻻﺤﺘﺭﺍﻑ :ﻓﻴﺠﻴﻭﺍل ﺒﻴﺯﻴﻙ ﺩﻭﺕ ﻨﺕ .٢٠١٧
.٣ﻤﻥ ﺍﻟﺼﻔﺭ ﺇﻟﻰ ﺍﻻﺤﺘﺭﺍﻑ :ﺴﻲ ﺸﺎﺭﺏ .٢٠١٧
.٤ﻤﻥ ﺍﻟﺼﻔﺭ ﺇﻟﻰ ﺍﻻﺤﺘﺭﺍﻑ :ﺒﺭﻤﺠﺔ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﻟﻤﺒﺭﻤﺠﻲ ﻓﻴﺠﻴـﻭﺍل ﺒﻴﺯﻴـﻙ ﺩﻭﺕ ﻨـﺕ ﻭﺴـﻲ
ﺸﺎﺭﺏ.
.٥ﻤﻥ ﺍﻟﺼﻔﺭ ﺇﻟﻰ ﺍﻻﺤﺘﺭﺍﻑ :ﺒﺭﻤﺠﺔ ﻨﻤﺎﺫﺝ ﺍﻟﻭﻴﻨﺩﻭﺯ ﻟﻤﺒﺭﻤﺠﻲ ﻓﻴﺠﻴﻭﺍل ﺒﻴﺯﻴﻙ ﺩﻭﺕ ﻨـﺕ ﻭﺴـﻲ
ﺸﺎﺭﺏ.
.٦ﺍﻟﻤﺩﺨل ﺍﻟﻌﻤﻠﻲ ﺍﻟﺴﺭﻴﻊ ﺇﻟﻰ ﻓﻴﺠﻴﻭﺍل ﺒﻴﺯﻴﻙ ﺩﻭﺕ ﻨﺕ .٢٠١٧
.٧ﺍﻟﻤﺩﺨل ﺍﻟﻌﻤﻠﻲ ﺍﻟﺴﺭﻴﻊ ﺇﻟﻰ ﺴﻲ ﺸﺎﺭﺏ .٢٠١٧
.٨ﺃﺴﺎﺴﻴﺎﺕ WPFﻟﻤﺒﺭﻤﺠﻲ ﻓﻴﺠﻴﻭﺍل ﺒﻴﺯﻴﻙ ﺩﻭﺕ ﻨﺕ.
.٩ﺃﺴﺎﺴﻴﺎﺕ WPFﻟﻤﺒﺭﻤﺠﻲ ﺴﻲ ﺸﺎﺭﺏ.
٥
ﻟﻘﺭﺍﺀﺓ ﻤﻘﺩﻤﺔ ﻭﻓﻬﺭﺱ ﻜل ﻜﺘﺎﺏ:
https://drive.google.com/drive/folders/1J21xi8Aw15BFSv-
GUgVOElLuYM6zoNct
ﻟﺸﺭﺍﺀ ﻫﺫﻩ ﺍﻟﻜﺘﺏ ،ﻴﺘﻡ ﺘﺤﻭﻴل ﺍﻟﺜﻤﻥ ﺒﺤﻭﺍﻟﺔ ﺒﺭﻴﺩﻴﺔ ﺩﺍﺨل ﻤﺼﺭ ،ﺃﻭ ﺒﻭﻴﺴﺘﺭﻥ ﻴﻭﻨﻴﻭﻥ ﻤﻥ
ﺨﺎﺭﺝ ﻤﺼﺭ ،ﻭﻴﺘﻡ ﺇﺭﺴﺎل ﺍﻟﻜﺘﺏ ﺒﻁﺭﺩ ﺒﺎﻟﺒﺭﻴﺩ ﺍﻟﺴﺭﻴﻊ ..ﻟﻤﺯﻴﺩ ﻤﻥ ﺍﻟﺘﻔﺎﺼﻴل ﺃﺭﺴل ﺭﺴﺎﻟﺔ
ﺒﺎﻟﻜﺘﺏ ﺍﻟﻤﻁﻠﻭﺒﺔ ﺇﻟﻰ:
[email protected]
ﺴﺠﻠﻭﺍ ﺇﻋﺠﺎﺒﻜﻡ ﺒﺼﻔﺤﺘﻲ ﺍﻟﺒﺭﻤﺠﻴﺔ ﻟﻤﺘﺎﺒﻌﺔ ﺼـﺩﻭﺭ ﻫـﺫﻩ ﺍﻟﻜﺘـﺏ ﺒـﺈﺫﻥ ﺍﷲ ،ﻭﺍﻻﺴـﺘﻔﺎﺩﺓ
ﺒﺎﻟﻤﻼﺤﻅﺎﺕ ﺍﻟﺒﺭﻤﺠﻴﺔ ﺍﻟﻌﻤﻠﻴﺔ ﺍﻟﺘﻲ ﺃﻨﺸﺭﻫﺎ ﻋﻠﻰ ﺍﻟﺼﻔﺤﺔ:
https://www.facebook.com/vbandcsharp
٦
ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻜﺘﺎﺏ
١٤ · ﻤﻘﺩﻤﺔ
١٦ · ﻟﻤﻥ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ
:ﻤﻠﺤﻭﻅﺔ
:ﺘﻡ ﻨﺸﺭ ﺍﻟﻔﺼﻭل ﺍﻷﺍﺭﺒﻌﺔ ﺍﻷﻭﻟﻰ ﻓﻲ ﻜﺘﺎﺏ ﻤﺴﺘﻘل ﺒﻌﻨﻭﺍﻥ
SQL ﺇﻨﺸﺎﺀ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻜﺘﺎﺒﺔ ﺍﺴﺘﻌﻼﻤﺎﺕ
:ﻴﻤﻜﻨﻡ ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻫﻨﺎ
https://drive.google.com/file/d/1H3FqC-jEXihVI5fx-
7ZBFmVGJm2D7Oi3/edit?fbclid=IwAR31PkLyHT1QTN1xNoozTWsvkwQ 5-
uowwQzNarL4EhPULQsy4-CGBOl1Cv0
-٥-
ADO.NET ﺘﻘﻨﻴﺔ
-٦-
Connection Object ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل
-٧-
Command Object ﻜﺎﺌﻥ ﺍﻷﻤﺭ
-٨-
DataReader ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ
-٩-
DataAdapter ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ
٩
-١٠-
ﻤﺼﺎﻨﻊ ﺍﻟﻤﺯﻭﺩﺍﺕ Provider Factories
-١١-
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ DataSet
-١٢-
ﺍﻟﺠﺩﺍﻭل ﻭﺍﻟﻌﻼﻗﺎﺕ ﻭﺍﻟﻘﻴﻭﺩ
١٠
DataColumn Class ﻓﺌﺔ ﻋﻤﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ
DataTableReader Class ﻓﺌﺔ ﻗﺎﺭﺉ ﺠﺩﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ
DataRelationCollection Class ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻌﻼﻗﺎﺕ
DataRelation Class ﻓﺌﺔ ﺍﻟﻌﻼﻗﺔ
ConstraintCollection Class ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻘﻴﻭﺩ
Constraint Class ﻓﺌﺔ ﺍﻟﻘﻴﺩ
UniqueConstraint Class ﺩﻓﺌﺔ ﻗﻴﺩ ﺍﻟﺘﻔﺭ
ForeignKeyConstraint Class ﻓﺌﺔ ﻗﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺜﺎﻨﻭﻱ
-١٣-
Data Views ﻋﺭﻭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ
-١٤-
Data Binding ﺭﺒﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ
:ﻤﻠﺤﻭﻅﺔ
: ﻓﻲ ﻜﺘﺎﺏ ﻤﺴﺘﻘل ﺒﻌﻨﺎﻭﺍﻥ١ ﻭﺍﻟﻤﻠﺤﻕ١٧ ﻭ١٦ ﻭ١٥ ﺘﻡ ﻨﺸﺭ ﺍﻟﻔﺼﻭل
DataGridView ﺠﺩﻭل ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ
:ﻴﻤﻜﻨﻡ ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻫﻨﺎ
https://drive.google.com/file/d/1tm27L0yGX1RA__vxXng0t5ny3XuJUFv_/view?fbclid=IwAR0IM7zdX9dqkWPXttQyQU6s-
FPna2m0hshLq9itiog9SS6PISFdes7Gm_0
١٢
ﻤﻠﺤﻕ ٢
ﺃﻨﻭﺍﻉ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺍﻟﻤﺩﺍﺭﺓ
Managed SQL Data Types
ﻤﻠﺤﻕ٣ :
ﺇﻋﺩﺍﺩ ﺘﻁﺒﻴﻕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل
١٣
ﻤﻘﺩﻤﺔ
١٤
-ﻜﻴـــﻑ ﺘﻌـــﺭﻑ ﺍﻟﻤﻌـــﺎﻤﻼﺕ Parametersﻭﺍﻟﻤﻌـــﺎﻤﻼﺕ ﺍﻟﺠﺩﻭﻟﻴـــﺔ
،Table-Valued Parametersﻭﻜﻴﻑ ﺘﺴﺘﺨﺩﻤﻬﺎ ﻟﺘﻤﺭﻴﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﺍﻹﺠﺭﺍﺀﺍﺕ
ﺍﻟﻤﺨﺯﻨﺔ ﻓﻲ .Sql Server 2008
-ﻜﻴﻑ ﺘﺤﻤﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤـﻥ ﺍﻟﻘﺭﺍﺼـﻨﺔ ﺍﻟـﺫﻴﻥ ﻴﺤـﺎﻭﻟﻭﻥ ﺩﺱ ﺍﻻﺴـﺘﻌﻼﻤﺎﺕ
.SQL Injection
-ﻜﻴﻑ ﺘﻘﺭﺃ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ﻭﺍﻟﻨﺼﻴﺔ ﺍﻟﻀﺨﻤﺔ ﺘﺘﺎﺒﻌﻴﺎ Sequentiallyﻋﻠـﻰ ﺼـﻭﺭﺓ
ﺃﺠﺯﺍﺀ ﻓﻲ .SQL Server 2008
-ﻜﻴﻑ ﺘﻨﺸﺊ ﺍﻹﺠﺭﺍﺀﺍﺕ ﺍﻟﻤﺨﺯﻨﺔ ﻓﻲ .Access
-ﻜﻴﻑ ﺘﺤﻔﻅ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻤﻠﻑ XMLﻭﻜﻴﻑ ﺘﺴﺘﻌﻴﺩﻫﺎ ﻤﻨﻪ ﻤﺭﺓ ﺃﺨﺭﻯ.
-ﻜﻴﻑ ﺘﺴﺘﺨﺩﻡ ﻤﺨﻁﻁ XMLﻹﻨﺸﺎﺀ ﻤﺠﻤﻭﻋﺎﺕ ﺒﻴﺎﻨﺎﺕ ﺨﺎﺼﺔ Custom DataSet
ﻻ ﺘﻌﺘﻤﺩ ﻋﻠﻰ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ.
-ﻜﻴﻑ ﺘﺘﻌﺎﻤل ﻤﻊ ﻋﻼﻗﺔ ﻭﺍﺤﺩ ﺒﻤﺘﻌﺩﺩ ،One-To-Many Relationﻭﻋﻼﻗﺔ ﻤﺘﻌـﺩﺩ
ﺒﻤﺘﻌﺩﺩ ،Many-To-Many Relationﻭﺍﻟﻌﻼﻗﺔ ﺍﻟﺫﺍﺘﻴﺔ .Self Relation
-ﻜﻴﻑ ﺘﺴﺘﺨﺩﻡ ﻤﺼﺎﻨﻊ ﺍﻟﻤﺯﻭﺩﺍﺕ Provider Factoriesﻟﻜﺘﺎﺒﺔ ﻓﺌﺎﺕ ﻋﺎﻤـﺔ ﻗـﺎﺩﺭﺓ
ﻋﻠﻰ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺃﻱ ﻨﻭﻉ ﻤﻥ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻤﺎ ﻴﺨﺘﺼﺭ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﺘﻜﺘﺒﻪ ،ﻭﻴﻤﻬـﺩ
ﻟﻙ ﺍﻟﻁﺭﻴﻕ ﻹﻨﺸﺎﺀ ﻤﺸﺎﺭﻴﻊ ﻤﺘﻌﺩﺩﺓ ﺍﻟﻁﺒﻘﺎﺕ .N-Tier Applications
-ﻜﻴﻑ ﺘﺤل ﻤﺸﺎﻜل ﺘﺼﺎﺭﻉ ﺃﻜﺜﺭ ﻤﻥ ﻤﺴﺘﺨﺩﻡ ﻋﻠﻰ ﺤﻔﻅ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻨﻔـﺱ ﺍﻟﻠﺤﻅـﺔ
ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﻔﺎﺌل .Optimistic Concurrency
-ﻜﻴﻑ ﺘﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﻼﻓﺘﺎﺕ ﻭﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺹ ﻭﺍﻟﻘﻭﺍﺌﻡ ﻭﺍﻟﺠﺩﺍﻭل ،ﻭﻜﻴﻑ ﺘﺭﺒﻁ
ﻜل ﻫﺫﻩ ﺍﻟﻌﻨﺎﺼﺭ ﻤﻌﺎ.
ﻭﻏﻴﺭ ﻫﺫﺍ ﺍﻟﻜﺜﻴﺭ.
***
ﻭﻴﻐﻁﻲ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ﺒﺎﻟﺘﻔﺼﻴل ﺤﻭﺍﻟﻲ ١٣٥ﻭﺍﺠﻬﺔ ﻭﻓﺌﺔ ﻭﺴﺠﻼ ﻤﻥ ﻤﻜﺘﺒـﺔ ﺇﻁـﺎﺭ ﺍﻟﻌﻤـل،
ﻤﺨﺼﺼﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺘﻁﺒﻴﻘﺎﺕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺸﺎﺭﺤﺎ ﺨﺼﺎﺌﺹ ﻭﻭﺴـﺎﺌل ﻭﺃﺤـﺩﺍﺙ ﻫـﺫﻩ
ﺍﻟﻤﻜﻭﻨﺎﺕ ﺒﺎﻟﺘﻔﺼﻴل ..ﻟﻬﺫﺍ ﻴﻌﺘﺒﺭ ﺍﻟﻜﺘﺎﺏ ﻤﺭﺠﻌﺎ ﻤﻔﺼﻼ ﻤﺒﻭﺒﺎ ،ﻴﻤﻜﻥ ﻟﻘﺎﺭﺌﻪ ﺍﻟﺭﺠﻭﻉ ﺇﻟﻴﻪ ﻋﻨﺩ
١٥
ﺍﻟﺒﺤﺙ ﻋﻥ ﺘﻔﺎﺼﻴل ﺃﻱ ﻓﺌﺔ ﺃﻭ ﺨﺎﺼﻴﺔ ﺃﻭ ﻭﺴﻴﻠﺔ ﺃﻭ ﺤﺩﺙ ،ﻓﻲ ﻨﻔﺱ ﺍﻟﻭﻗـﺕ ﺍﻟـﺫﻱ ﻴﺠﻌﻠـﻪ
ﺼﺎﻟﺤﺎ ﻟﻠﻘﺭﺍﺀﺓ ﻜﻜﺘﺎﺏ ﺘﻌﻠﻴﻤﻲ ﻋﻤﻠﻲ ﻤﺭﺘﺏ ﻤﻥ ﺍﻷﺴﻬل ﺇﻟﻰ ﺍﻷﺼﻌﺏ ،ﻴﻨﻘل ﺇﻟﻰ ﺍﻟﻤﺒﺭﻤﺞ ﻓﻲ
ﺼﻔﺤﺎﺕ ﻤﻌﺩﻭﺩﺍﺕ ﺨﺒﺭﺓ ﺴﻨﻭﺍﺕ ﻓﻲ ﺒﺭﻤﺠﺔ ﺘﻁﺒﻴﻘﺎﺕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻴﺭﺸﺩﻩ ﺇﻟﻰ ﻜﻴﻔﻴﺔ ﺤل
ﺍﻟﻤﺸﻜﻼﺕ ﻏﻴﺭ ﺍﻟﻤﺘﻭﻗﻌﺔ ﺍﻟﺘﻲ ﺘﻭﺍﺠﻬﻪ ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﺠﺎل ،ﻭﻜﻴﻑ ﻴﺤﺴﻥ ﺃﺩﺍﺀ ﺒﺭﻨﺎﻤﺠـﻪ ﺒﺘـﻭﻓﻴﺭ
ﺃﻜﺒﺭ ﻗﺩﺭ ﻤﻥ ﺍﻟﺫﺍﻜﺭﺓ ،ﻭﻜﻴﻑ ﻴﺤﺎﻓﻅ ﻋﻠﻰ ﻜﻔﺎﺀﺓ ﺨﺎﺩﻡ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺒﺘﻘﻠﻴل ﻋﺩﺩ ﺍﻻﺘﺼﺎﻻﺕ ﻭﻭﻗﺕ
ﻜل ﺍﺘﺼﺎل ﺒﻘﺩﺭ ﺍﻹﻤﻜﺎﻥ.
ﺒﺎﺨﺘﺼﺎﺭ :ﻫﺫﺍ ﻫﻭ ﺍﻟﻜﺘﺎﺏ ﺍﻟﺫﻱ ﺘﺒﺤﺙ ﻋﻨﻪ.
ﻭﺍﷲ ﻭﻟﻲ ﺍﻟﺘﻭﻓﻴﻕ
ﺴﺠل .Structure
ﻓﺌﺔ .Class
ﻭﺍﺠﻬﺔ .Interface
ﺜﺎﺒﺕ .Constant
ﺨﺎﺼﻴﺔ Propertyﻴﻤﻜﻨﻙ ﻗﺭﺍﺀﺓ ﺃﻭ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺘﻬﺎ.
ﺨﺎﺼﻴﺔ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ .Read Only Property
ﻭﺴﻴﻠﺔ .Method
ﻤﻌﺎﻤل .Operator
ﺤﺩﺙ .Event
ﻫﺫﺍ ﺍﻟﻌﻨﺼﺭ ﺜﺎﺒﺕ ،Staticﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻋﺒﺭ ﺍﺴﻡ ﺍﻟﻔﺌﺔ ﻤﺒﺎﺸﺭﺓ.
١٧
:ﻤﻠﺤﻭﻅﺔ
:ﺘﻡ ﻨﺸﺭ ﺍﻟﻔﺼﻭل ﺍﻷﺍﺭﺒﻌﺔ ﺍﻷﻭﻟﻰ ﻓﻲ ﻜﺘﺎﺏ ﻤﺴﺘﻘل ﺒﻌﻨﻭﺍﻥ
SQL ﺇﻨﺸﺎﺀ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻜﺘﺎﺒﺔ ﺍﺴﺘﻌﻼﻤﺎﺕ
:ﻴﻤﻜﻨﻡ ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻫﻨﺎ
https://drive.google.com/file/d/1H3FqC-jEXihVI5fx-
7ZBFmVGJm2D7Oi3/edit?fbclid=IwAR31PkLyHT1QTN1xNoozTWsvkwQ 5-
uowwQzNarL4EhPULQsy4-CGBOl1Cv0
١٨
-٥-
ﺘﻘﻨﻴﺔ ADO.NET
٢٠
ﻤﺜل ﻫﺫﺍ ﺍﻟﺘﻨﻅﻴﻡ ﻴﻀﻤﻥ ﺘﺨﻔﻴﻑ ﻋﺏﺀ ﻫﺎﺌل ﻤﻥ ﻋﻠﻰ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﻭﺘﻘﻠﻴل ﺠﻤل SQLﺍﻟﺘـﻲ
ﻴﻨﻔﺫﻫﺎ ،ﻭﺒﺎﻟﺘﺎﻟﻲ ﻴﻭﻓﺭ ﻗﺩﺭﺓ ﺍﻟﻤﺸﻐل ﺍﻟﺩﻗﻴﻕ Processorﻭﺍﻟﺫﺍﻜﺭﺓ RAMﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﺤﺎﺴﻭﺏ
ﺍﻟﺫﻱ ﻴﻌﻤل ﻋﻠﻴﻪ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ،ﻟﻴﺴﺘﻁﻴﻊ ﺘﻨﻔﻴﺫ ﻋﻤﻠﻴﺎﺕ ﺃﺨﺭﻯ.
ﻜﻤﺎ ﺃﻥ ﻫﻨﺎﻙ ﺒﻌﺽ ﺍﻟﺘﺤ ﺩﻴﺎﺕ ﺍﻟﺘﻲ ﺘﻭﺍﺠﻪ ﺍﻟﻤﺒﺭﻤﺞ ﻭﻫﻭ ﻴﻜﺘﺏ ﺒﺭﻨﺎﻤﺠﺎ ﻴﺘﻌﺎﻤل ﻤـﻊ ﺍﻟﺨـﺎﺩﻡ،
ﻤﺜل ﺍﻟﺘﻌﺎﺭﺽ ﺍﻟﺫﻱ ﻴﻤﻜﻥ ﺃﻥ ﻴﻨﺘﺞ ﻋﻨﺩﻤﺎ ﻴﺤﺫﻑ ﺃﺤﺩ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﺒﻌﺽ ﺍﻟﺴـﺠﻼﺕ ،ﺒﻴﻨﻤـﺎ
ﻤﺴﺘﺨﺩﻡ ﺁﺨﺭ ﻴﺤﺩﺙ ﻗﻴﻤﻬﺎ! ..ﺃﻭ ﻋﻨﺩﻤﺎ ﻴﺤﺎﻭل ﺃﻜﺜﺭ ﻤﻥ ﻤﺴﺘﺨﺩﻡ ﺘﺤـﺩﻴﺙ ﻨﻔـﺱ ﺍﻟﺴـﺠﻼﺕ
ﺒﻁﺭﻕ ﻤﺨﺘﻠﻔﺔ ﻓﻲ ﻨﻔﺱ ﺍﻟﻭﻗﺕ ..ﻭﺴﻨﺭﻯ ﻜﻴﻑ ﻨﻭﺍﺠﻪ ﻤﺜل ﻫﺫﺍ ﺍﻷﻤﺭ ﻻﺤﻘﺎ.
ﺘﻘﻨﻴﺔ :ADO.NET
ـﺎل"
ـﺎﺕ ﺍﻟﻔﻌــ
ـﺎﺌﻥ ﺍﻟﺒﻴﺎﻨــ
ـﻁﻠﺢ "ﻜــ
ـﺎﺭ ﺍﻟﻤﺼــ
ـﻲ ﺍﺨﺘﺼــ
ـﺭﻑ ADOﻫــ
ﺍﻷﺤــ
،ActiveX Data Objectﻭﻫﻲ ﺘﻘﻨﻴﺔ ﺒﺭﻤﺠﻴﺔ ﻅﻬﺭﺕ ﻓﻲ ﻓﻴﺠﻴﻭﺍل ﺴﺘﺩﻴﻭ ،٦ﺘﻘﺩﻡ ﺠﻤﻴـﻊ
ﺍﻟﻔﺌﺎﺕ Classesﺍﻟﻼﺯﻤﺔ ﻟﻼﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻁﻠﺏ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻨﻬـﺎ ﻭﺤﻔﻅﻬـﺎ ﻓﻴﻬـﺎ..
ﻭﺘﻔﺘﺭﺽ ﻫﺫﻩ ﺍﻟﺘﻘﻨﻴﺔ ﺃﻥ ﺍﻟﻌﻤﻴل ﺴﻴﻅل ﻋﻠﻰ ﺍﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻁﻭﺍل ﻤ ﺩﺓ ﺘﻌﺎﻤﻠﻪ ﻤﻌﻬـﺎ
ﺴﻭﺍﺀ ﻋﻠﻰ ﻨﻔﺱ ﺍﻟﺠﻬﺎﺯ ﺃﻭ ﻋﺒﺭ ﺍﻟﺸﺒﻜﺔ ،ﺤﻴﺙ ﻴﺤﺼل ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺃﻱ ﺠـﺩﻭل ﻴﺭﻴـﺩﻩ
ﻭﻴﺤﺩﺜﻬﺎ ﻋﺒﺭ ﻨﻔﺱ ﺍﻻﺘﺼﺎل.
ﻟﻜﻥ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﻜﺎﻨﺕ ﻋﻘﻴﻤﺎ ،ﻓﻔﺘﺢ ﺍﻻﺘﺼﺎل ﻁﻭﺍل ﺍﻟﻭﻗﺕ ﻤﻊ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﺒـﺭ ﺍﻟﺸـﺒﻜﺔ
ﻏﻴﺭ ﻋﻤﻠﻲ ﻷﻨﻪ ﻴﻌﻁل ﻤﺴﺘﺨﺩﻤﻴﻥ ﺁﺨﺭﻴﻥ ﻋﻥ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻨـﺩ ﺘﺠـﺎﻭﺯ ﻋـﺩﺩ
ﺍﻟﻤﺘﺼﻠﻴﻥ ﻓﻲ ﻨﻔﺱ ﺍﻟﻠﺤﻅﺔ ﺍﻟﺤﺩ ﺍﻟﻤﺴﻭﺡ ﺒﻪ ،ﻜﻤﺎ ﺃﻥ ﺇﺠﺭﺍﺀ ﺍﻟﻌﻤﻠﻴﺎﺕ ﻋﺒﺭ ﺍﻟﺸﺒﻜﺔ ﺃﺒﻁﺄ ﻤـﻥ
ﺇﺠﺭﺍﺌﻬﺎ ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻤﺴﺘﺨﺩﻡ ..ﻜل ﻫﺫﺍ ﺠﻌل ﺍﻟﻤﺒﺭﻤﺠﻴﻥ ﻴﺴﺘﺨﺩﻤﻭﻥ ﺘﻘﻨﻴـﺔ ADOﺒﻁﺭﻴﻘـﺔ
ﻏﺭﻴﺒﺔ ،ﻓﻘﺩ ﻜﺎﻨﻭﺍ ﻴﺘﺼﻠﻭﻥ ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻴﻨﺴﺨﻭﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻁﻠﻭﺒﺔ ﺇﻟﻰ ﺃﺠﻬـﺯﺘﻬﻡ ،ﺜـﻡ
ﻴﻐﻠﻘﻭﻥ ﺍﻻﺘﺼﺎل ﻭﻴﺠﺭﻭﻥ ﺍﻟﻌﻤﻠﻴﺎﺕ ﺍﻟﻤﻁﻠﻭﺒﺔ ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺃﺠﻬﺯﺘﻬﻡ ..ﻭﺇﺫﺍ ﻜﺎﻨﺕ ﻫﻨﺎﻙ
ﺘﻐﻴﻴﺭﺍﺕ ﻴﺠﺏ ﺤﻔﻅﻬﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻴﺘﺼﻠﻭﻥ ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤ ﺭﺓ ﺃﺨـﺭﻯ ﻭﻴﺤﻔﻅـﻭﻥ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﺜﻡ ﻴﻐﻠﻘﻭﻥ ﺍﻻﺘﺼﺎل.
ﻭﻗﺩ ﺃﺨﺫﺕ ﻤﻴﻜﺭﻭﺴﻭﻓﺕ ﻫﺫﺍ ﺍﻷﻤﺭ ﺒﻌﻴﻥ ﺍﻻﻋﺘﺒﺎﺭ ،ﻭﻁﻭﺭﺕ ﺘﻘﻨﻴﺔ ADOﻤﻊ ﻅﻬﻭﺭ ﻓﻴﺠﻴﻭﺍل
ﺴﺘﺩﻴﻭ ﺩﻭﺕ ﻨﺕ ،٢٠٠٢ﻭﺼﺎﺭﺕ ﺍﻟﺘﻘﻨﻴﺔ ﺍﻟﺠﺩﻴـﺩﺓ ﺘﺤﻤـل ﺍﻻﺴـﻡ ،ADO.NETﻓﺼـﺎﺭ
٢١
ﺒﺎﻹﻤﻜﺎﻥ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻌﺩ ﺇﻏﻼﻕ ﺍﻻﺘﺼﺎل ﻓﻴﻤﺎ ﻋـﺭﻑ ﺒﺎﺴـﻡ "ﺍﻟﺘﻌﺎﻤـل ﺍﻟﻤﻨﻔﺼـل"
،Disconnected Modeﺤﻴﺙ ﻴﺘﺼل ﺍﻟﻤﺴﺘﺨﺩﻡ ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻴﻘﻭﻡ ﺒﺘﺤﻤﻴـل ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻤﻨﻬﺎ ﺇﻟﻰ ﺍﻟﺫﺍﻜﺭﺓ ﻭﻴﻐﻠﻕ ﺍﻻﺘﺼﺎل ،ﻟﻴﺘﻌﺎﻤل ﻤﻊ ﻫﺫﻩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺠﻬﺎﺯﻩ ،ﻭﻋﻨﺩﻤﺎ ﻴﺭﻴﺩ ﺤﻔـﻅ
ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ،ﻴﻔﺘﺢ ﺍﻻﺘﺼﺎل ﻤﺭﺓ ﺃﺨﺭﻯ ﻟﻨﻘل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﻓﻴﻤﺎ ﻴﻠﻲ ﺘﻠﺨﻴﺹ ﻟﻠﺨﻁﻭﺍﺕ ﺍﻟﺘﻲ ﺘﻘﻭﻡ ﺒﻬﺎ ﻋﺒﺭ ﺘﻘﻨﻴﺔ ADO.NETﻟﻠﺤﺼﻭل ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻭﺘﺤﺩﻴﺜﻬﺎ:
-ﻓﻲ ﺍﻟﺒﺩﺍﻴﺔ ﻋﻠﻴﻙ ﺃﻥ ﺘﻘﻭﻡ ﺒﺎﻻﺘﺼﺎل ﺒﻘﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺒﻭﺍﺴـﻁﺔ ﻜـﺎﺌﻥ ﺍﻻﺘﺼـﺎل
..Connection objectﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ ﻴﺘﻴﺢ ﻟﻙ ﺘﻭﻀﻴﺢ ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻭﻜﻴﻔﻴـﺔ
ﺍﻻﺘﺼﺎل ﺒﻬﺎ.
-ﺒﻌﺩ ﻫﺫﺍ ﻋﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ،Command Objectﺍﻟﺫﻱ ﻴﺘﻭﻟﻰ ﺘﻨﻔﻴﺫ ﺠﻤﻠـﺔ
ﺍﻻﺴﺘﻌﻼﻡ SQL Queryﻋﺒﺭ ﺍﻻﺘﺼﺎل ﺍﻟﻤﻔﺘﻭﺡ.
-ﺒﻌﺩ ﻫﺫﺍ ﻴﻜﻭﻥ ﺃﻤﺎﻤﻙ ﺃﺤﺩ ﺍﺨﺘﻴﺎﺭﻴﻥ:
.١ﻓﺈﻤﺎ ﺃﻥ ﺘﺴﺘﺨﺩﻡ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ Data Readerﻟﻘﺭﺍﺀﺓ ﻨﺘﺎﺌﺞ ﺍﻻﺴﺘﻌﻼﻡ ﻤﺒﺎﺸـﺭﺓ
ﺩﻭﻥ ﺤﻔﻅﻬﺎ ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل.
.٢ﻭﺇﻤﺎ ﺃﻥ ﺘﺴﺘﺨﺩﻡ "ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ" Data Adapterﻟﺤﻔﻅ ﻨﺘﺎﺌﺞ ﺍﻻﺴﺘﻌﻼﻡ ﻋﻠـﻰ
ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،Data Setﺍﻟﺘﻲ ﻴﻤﻜﻥ ﺍﻟﻘﻭل ﺇﻨﹼﹼﻬـﺎ ﺼـﻭﺭﺓ
ﻤﺼﻐﹼﹼﺭﺓ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺘﺤﺘﻭﻱ ﻋﻠـﻰ ﺍﻟﺠـﺩﺍﻭل ﻭﺍﻟﻌﻼﻗـﺎﺕ ،Relations
ﻭﺍﻟﻘﻴﻭﺩ Constraintsﺍﻟﻤﻔﺭﻭﻀﺔ ﻋﻠﻰ ﻗﻴﻡ ﺍﻟﺤﻘﻭل ،ﺒﻤﺎ ﻓﻲ ﺫﻟﻙ ﻓﺭﺽ ﺍﻟﺘﻜﺎﻤل
ﺍﻟﻤﺭﺠﻌﻲ Referential Integrityﺒﻴﻥ ﺍﻟﺠﺩﺍﻭل.
-ﺒﻌﺩ ﻫﺫﺍ ﻋﻠﻴﻙ ﺇﻏﻼﻕ ﺍﻻﺘﺼﺎل ،ﻭﻤﻌﺎﻟﺠﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻙ ..ﻭﻴﻤﻜﻨـﻙ ﻋـﺭﺽ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻟﻘﺭﺍﺀﺘﻬﺎ ﺃﻭ ﺘﻌﺩﻴﻠﻬﺎ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺃﺩﻭﺍﺕ ﺭﺒﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ Data Bound
.Controls
-ﻭﺇﺫﺍ ﻜﺎﻨﺕ ﻫﻨﺎﻙ ﺃﻴﺔ ﺘﻐﻴﻴﺭﺍﺕ ﺃﺠﺭﺍﻫﺎ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺘﺭﻴـﺩ ﺤﻔﻅﻬـﺎ ﻓـﻲ
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﻌﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﻤﺭﺓ ﺃﺨﺭﻯ ﻟﻼﺘﺼﺎل ﺒﻬﺎ ،ﻭﺍﺴـﺘﺨﺩﺍﻡ
ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﺃﻭ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﺘﻨﻔﻴﺫ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴﺙ.
٢٢
ﺴﻴﻡ ﺍﻟﻌﻤل ﺇﻟﻰ ﻁﺒﻘﺎﺕ Layersﻤﺴﺘﻘﻠﹼﹼﺔ ﻓـﻲ ﻭﻅﻴﻔﺘﻬـﺎ،
ﻜﻤﺎ ﺘﺭﻯ :ﻴﻌﺘﻤﺩ ﻫﺫﺍ ﺍﻟﺘﻨﻅﻴﻡ ﻋﻠﻰ ﺘﻘ
ﻭﺒﻬﺫﺍ ﻴﺴﻬل ﻋﻠﻴﻙ ﺍﻟﺘﻌﺩﻴل ﻓﻲ ﺃﻱ ﻁﺒﻘﺔ ﺩﻭﻥ ﻫﺩﻡ ﺍﻟﻁﺒﻘﺎﺕ ﺍﻟﺴﺎﺒﻘﺔ ﺃﻭ ﺍﻟﺘﺎﻟﻴﺔ ﻟﻬﺎ ،ﻤﻤﺎ ﻴـﻭﻓﺭ
ﺍﻟﻭﻗﺕ ﻭﺍﻟﺠﻬﺩ.
ﻭﺍﻟﺸﻜل ﺍﻟﺘﺎﻟﻲ ﻴﻠﺨﹼﹼﺹ ﻫﺫﻩ ﺍﻟﺘﻘﻨﻴﺔ:
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ
Database
ﺍﻟﺨـــﺎﺩﻡ Server
ﺍﻟﻌﻤــــﻴل Client
ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل
Connection Object
ﻜﺎﺌﻥ ﺍﻷﻤﺭ
Command Object
ﻤﻭﺼل ﺍﻟﺒﻴﺎﻨﺎﺕ
Data Adapter
ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ
Data Reader
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ
Data Set
٢٣
ﻭﺘﻭﺠﺩ ﻓﺌﺎﺕ ADO.NETﻓﻲ ﻨﻁﺎﻗﺎﺕ ﺍﻷﺴﻤﺎﺀ Namespacesﺍﻟﺘﺎﻟﻴﺔ:
- System.Data
- System.Transactions
- Microsoft.SqlServer.Server
ﻟﻐﺔ :XML
ﺍﻟﺤــﺭﻭﻑ XMLﻫــﻲ ﺍﺨﺘﺼــﺎﺭ ﻟﻠﺘﻌﺒﻴــﺭ "ﻟﻐــﺔ ﺍﻟﺘﻭﺼــﻴﻑ ﺍﻟﻘﺎﺒﻠــﺔ ﻟﻠﺘﻤﺩﻴــﺩ"
،Extensible Markup Languageﻭﻫﻲ ﻁﺭﻴﻘﺔ ﻟﺘﻤﺜﻴل ﺃﻱ ﺒﻴﺎﻨﺎﺕ ﻟﻬﺎ ﺘﺭﻜﻴـﺏ ﻤـﻨﻅﻡ،
ﻭﺫﻟﻙ ﺒﺘﺤﻭﻴﻠﻬﺎ ﺇﻟﻰ ﻨﺹ ﻴﻌ ﺒﺭ ﻋﻨﻬﺎ ..ﻟﻬﺫﺍ ﺘﺼﻠﺢ ﻟﻐﺔ XMLﻟﻠﺘﻌﺒﻴﺭ ﻋﻥ ﺃﻱ ﻨﻭﻉ ﻤﻥ ﺃﻨﻭﺍﻉ
ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻜﺎﻟﺠﺩﺍﻭل ﻭﺍﻟﺼﻭﺭ ﻭﻏﻴﺭﻫﻤﺎ.
ﻭﺭﻏﻡ ﺃﻥ ﺍﻟﻤﻠ ﹼﻔﹼﺎﺕ ﺍﻟﻨﺼ ﻴﺔ ﺃﻜﺒﺭ ﺤﺠﻤﺎ ﻤﻥ ﺍﻟﻤﻠ ﹼﻔﹼﺎﺕ ﺍﻟﺜﻨﺎﺌﻴـﺔ ،Binary Filesﺇﻻ ﺇﻥ ﺍﻷﻭﻟـﻰ
ﺼﺎﻟﺤﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺃﻱ ﺒﺭﻨﺎﻤﺞ ﺒل ﻤﻊ ﺃﻱ ﻨﻅﺎﻡ ﺘﺸﻐﻴل ،ﺩﻭﻥ ﺍﻟﻭﻗﻭﻉ ﻓﻲ ﻤﺸـﺎﻜل ﺍﺨـﺘﻼﻑ
ﻁﺭﻕ ﺘﻤﺜﻴل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ..ﻟﻬﺫﺍ ﺼﺎﺭﺕ ﻟﻐﺔ XMLﻓﻲ ﺍﻟﺴﻨﻭﺍﺕ ﺍﻷﺨﻴﺭﺓ ﺃﻨﺴﺏ ﻭﺴـﻴﻠﺔ
ﻟﻨﻘل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﺒﺭ ﺍﻹﻨﺘﺭﻨﺕ ،ﻷﻨﻬﺎ ﺘﺘﺠﺎﻭﺯ ﻤﺸﺎﻜل ﻋﺩﻡ ﺍﻟﺘﻭﺍﻓـﻕ ﺒـﻴﻥ ﺍﻟﺘﻁﺒﻴﻘـﺎﺕ ﻭﺃﻨﻅﻤـﺔ
ﺍﻟﺘﺸﻐﻴل ﺍﻟﻤﺨﺘﻠﻔﺔ ..ﻭﻟﻬﺫﺍ ﺘﺴﺘﺨﺩﻡ ﺘﻘﻨﻴﺔ ADO.NETﻟﻐﺔ XMLﻓﻲ ﻨﻘـل ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺒـﻴﻥ
ﺍﻟﺨﺎﺩﻡ ﻭﺍﻟﻌﻤﻴل.
ﻭﺭﻏﻡ ﺃﻥ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﻴﻘﺩﻡ ﺩﻋﻤﺎ ﻜﺎﻤﻼ ﻟﻠﻐﺔ XMLﻭﻴﺘﻴﺢ ﻟﻙ ﻜﺘﺎﺒﺘﻬﺎ ﻭﻗﺭﺍﺀﺘﻬﺎ ،ﺇﻻ ﺃﻨﻙ ﻟـﻥ
ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﻫﺫﺍ ﻋﻨﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻷﻥ ﺘﻘﻨﻴﺔ ADO.NETﺘﺴﺘﺨﺩﻡ ﻟﻐﺔ XML
ﻜﻁﺒﻘﺔ ﺩﺍﺨﻠﻴﺔ ،ﻓﻬﻲ ﺘﻨﺘﺠﻬﺎ ﻭﺘﻘﺭﺃ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻨﻬﺎ ﺒﻁﺭﻴﻘﺔ ﺁﻟﻴﺔ.
ﻭﻤﻥ ﺍﻹﻤﻜﺎﻨﻴﺎﺕ ﺍﻟﺘﻲ ﺘﺘﻴﺤﻬﺎ ﻟﻙ ﻟﻐﺔ ،XMLﻗﺩﺭﺘﻙ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻹﻨﺸﺎﺀ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ
DataSetﺒﺩﻭﻥ ﺍﻻﻋﺘﻤﺎﺩ ﻋﻠﻰ ﺃﻱ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ..ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﺘﻭﻀـﻊ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻓـﻲ
ﺍﻟﺫﺍﻜﺭﺓ ،ﻭﻟﻭ ﺸﺌﺕ ﺍﻻﺤﺘﻔﺎﻅ ﺒﻬﺎ ،ﻓﻴﻤﻜﻨﻙ ﺤﻔﻅﻬﺎ ﻓﻲ ﻤﻠﻑﹼﹼ ،ﺜﻡ ﺇﻋﺎﺩﺓ ﺘﺤﻤﻴﻠﻬـﺎ ﻤـﺭﺓ ﺃﺨـﺭﻯ
ﺤﻴﻨﻤﺎ ﺘﺭﻴﺩ ..ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﺍ ﺒﺘﻔﺼﻴل ﺍﻜﺒﺭ ﻻﺤﻘﺎ.
٢٤
ﻤﺯﻭﺩﺍﺕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ :Database Providers
ﺘﻭﻓﺭ ﺘﻘﻨﻴﺔ ADO.NETﻋﺩﺓ ﻤﺯﻭﺩﺍﺕ Providersﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺃﻨﻭﺍﻉ ﻤﺨﺘﻠﻔﺔ ﻤـﻥ ﻗﻭﺍﻋـﺩ
ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻫﺫﻩ ﺍﻟﻤﺯﻭﺩﺍﺕ ﻫﻲ:
:ODBC -١
ﺍﺴﻡ ﻫﺫﺍ ﺍﻟﻤﺯﻭﺩ ﻫﻭ ﺍﺨﺘﺼﺎﺭ ﻟﻠﻤﺼﻁﻠﺢ "ﺍﻟﺘﻭﺍﺼل ﺍﻟﻤﻔﺘﻭﺡ ﻤﻊ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ":
Open Database Connectivity
ﻭﻗﺩ ﻁﻭﺭﺕ ﻤﻴﻜﺭﻭﺴﻭﻓﺕ ﻫﺫﻩ ﺍﻟﺘﻘﻨﻴﺔ ـ ﺒﺎﻟﺘﻌﺎﻭﻥ ﻤﻊ ﺁﺨﺭﻴﻥ ،ﻋﺎﻡ ،١٩٩٢ﻟﺘـﻭﻓﺭ
ﻁﺭﻴﻘﺔ ﻋﺎﻤﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻐﺽ ﺍﻟﻨﻅﺭ ﻋﻥ ﻟﻐﺔ ﺍﻟﺒﺭﻤﺠﺔ ﺍﻟﻤﺴـﺘﺨﺩﻤﺔ
ﻭﻨﻅﺎﻡ ﺍﻟﺘﺸﻐﻴل ﺍﻟﺫﻱ ﺘﻌﻤل ﻋﻠﻴﻪ ،ﻭﺘﻁﺒﻴﻕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺴﺘﺨﺩﻡ.
ﻭﺘﻭﺠﺩ ﻓﺌﺎﺕ ﻫﺫﺍ ﺍﻟﻤﺯﻭﺩ ﻓﻲ ﺍﻟﻨﻁﺎﻕ:
System.Data.ODBC
:OLE DB -٢
ﺍﺴﻡ ﻫﺫﺍ ﺍﻟﻤﺯﻭﺩ ﻫﻭ ﺍﺨﺘﺼﺎﺭ ﻟﻠﻤﺼﻁﻠﺢ "ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺭﺒﻁ ﻭﺘﻀﻤﻴﻥ ﺍﻟﻜﺎﺌﻨﺎﺕ":
Object Linking and Embedding Database
ﻭﻫﻭ ﻤﺯﻭﺩ Providerﺒﻨﺘﻪ ﻤﻴﻜﺭﻭﺴﻭﻓﺕ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺘﻘﻨﻴﺔ COMﻜﺘﻁﻭﻴﺭ ﻭﺘﺤﺴـﻴﻥ
ﻟﺘﻘﻨﻴﺔ ،ODBCﻟﻠﺘﻌﺎﻤل ﺒﻁﺭﻴﻘﺔ ﻋﺎﻤﺔ ﻤﻊ ﺃﻱ ﻨﻭﻉ ﻤﻥ ﺃﻨﻭﺍﻉ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻟﻬـﺫﺍ
ﺘﺴﺘﻁﻴﻊ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺁﻜﺴﻴﺱ )ﻓﻠﻴﺱ ﻟﻘﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺘﻪ ﻤـﺯﻭﺩ ﺨـﺎﺹ ﺒﻬـﺎ(،
ﻭﻜﺫﻟﻙ ﻤﻊ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻭﺃﻭﺭﺍﻜل )ﺭﻏﻡ ﺃﻥ ﻟﻜـل ﻤﻨﻬﻤـﺎ ﻤـﺯﻭﺩﺍ
ﺨﺎﺼﺎ ﺒﻬﻤﺎ( ،ﻭﻤﻊ ﺃﻱ ﻨﻭﻉ ﺁﺨﺭ ﻤﻥ ﺃﻨﻭﺍﻉ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺤﺘﻰ ﻭﻟﻭ ﻟﻡ ﺘﻜﻥ ﺘـﺩﻋﻡ
ﺍﺴﺘﺨﺩﺍﻡ ﻟﻐﺔ SQLﻤﺜل ﺍﻟﺠﺩﺍﻭل ﺍﻟﺸﺎﻤﻠﺔ Spreadsheetsﺍﻟﺨﺎﺼﺔ ﺒﺘﻁﺒﻴﻕ ﺇﻜﺴـﻴل
.Excel
ﻭﺘﻭﺠﺩ ﻓﺌﺎﺕ ﻫﺫﺍ ﺍﻟﻤﺯﻭﺩ ﻓﻲ ﺍﻟﻨﻁﺎﻕ:
System.Data.OleDb
٢٥
:SQL Server -٣
ﺘﻭﻓﺭ ﺩﻭﺕ ﻨﺕ ﺩﻋﻤﺎ ﺨﺎﺼﺎ ﻟﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﺒﺎﻋﺘﺒﺎﺭﻩ ﺃﻫﻡ ﺘﻁﺒﻴﻘﺎﺕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺍﻟﺘﻲ ﺃﻨﺘﺠﺘﻬﺎ ﻤﻴﻜﺭﻭﺴﻭﻓﺕ ﺒﻌﺩ ﺍﻨﺘﺸﺎﺭ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺸﺒﻜﺎﺕ ﻭﺍﻹﻨﺘﺭﻨﺕ ﻓﻲ ﻋﺎﻟﻡ ﺍﻟﺘﺠﺎﺭﺓ
ﻭﺍﻷﻋﻤﺎل.
ﻭﺘﻭﺠﺩ ﻓﺌﺎﺕ ﻫﺫﺍ ﺍﻟﻤﺯﻭﺩ ﻓﻲ ﺍﻟﻨﻁﺎﻗﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ:
ﻭﻋﻠﻴﻙ ﺃﻥ ﺘﻼﺤﻅ ﺃﻥ ﺠﻤﻴﻊ ﻫﺫﻩ ﺍﻟﻤﺯﻭﺩﺍﺕ ﺘﻭﻓﺭ ﻨﻔﺱ ﺃﺩﻭﺍﺕ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ )ﻜـﺎﺌﻥ
ﺍﻻﺘﺼﺎل ،Connectionﻜـﺎﺌﻥ ﺍﻷﻤـﺭ ،Commandﻤﻬﻴـﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،DataAdapter
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،DataSetﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ...DataReaderﺇﻟﺦ( ،ﻟﻜﻥ ﻜﻼ ﻤﻨﻬـﺎ ﻴﺒـﺩﺃ
ﺒﺎﺨﺘﺼﺎﺭ ﻴﻭﻀﺢ ﻨﻭﻉ ﺍﻟﻤﺯﻭﺩ ،ﻤﺜل:
SqlCeConnection
SqlConnection
OdbcConnection ﻜﺎﺌﻨﺎﺕ ﺍﻻﺘﺼﺎل
OleDbConnection
OracleConnection
SqlCeCommand
SqlCommand
OdbcCommand ﻜﺎﺌﻨﺎﺕ ﺍﻷﻤﺭ
OleDbCommand
OracleCommand
SqlCeDataAdapter
SqlDataAdapter
OdbcDataAdapter ﻤﻬﻴﺌﺎﺕ ﺍﻟﺒﻴﺎﻨﺎﺕ
OleDbDataAdapter
OracleDataAdapter
SqlDataSet
OdbcDataSet
ﻤﺠﻤﻭﻋﺎﺕ ﺍﻟﺒﻴﺎﻨﺎﺕ
OleDbDataSet
OracleDataSet
٢٧
SqlCeDataReader
SqlDataReader
OdbcDataReader ﻗﺎﺭﺌﺎﺕ ﺍﻟﺒﻴﺎﻨﺎﺕ
OleDbDataReader
OracleDataReader
ﻭﻨﻅﺭﺍ ﻷﻨﻪ ﻻ ﺘﻭﺠﺩ ﻓﺭﻭﻕ ﺘﺫﻜﺭ ﺒﻴﻥ ﺃﻨﻭﺍﻉ ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﺨﺎﺼﺔ ﺒﺄﺤـﺩ ﺍﻟﻤـﺯﻭﺩﺍﺕ ﻭﺍﻟﻜﺎﺌﻨـﺎﺕ
ﺍﻟﺨﺎﺼﺔ ﺒﻨﻭﻉ ﺁﺨﺭ ،ﻓﺴﻨﻘﺘﺼﺭ ﻓﻲ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ﻋﻠﻰ ﺸـﺭﺡ ﻤـﺯﻭﺩ ﺴـﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ ،ﻷﻥ
ﺍﺴﺘﺨﺩﺍﻤﻙ ﻟﺒﺎﻗﻲ ﺃﻨﻭﺍﻉ ﺍﻟﻤﺯﻭﺩﺍﺕ ﻟﻥ ﻴﺨﺘﻠﻑ ﻓﻲ ﺸﻲﺀ ،ﺴﻭﻯ ﻓﻲ ﺘﻐﻴﻴﺭ ﻨﻁﺎﻕ ﺍﻻﺴﻡ ﻭﺍﻟﺒﺎﺩﺌﺔ
ﺍﻟﺘﻲ ﺘﺴﺒﻕ ﺍﺴﻡ ﻜل ﻜﺎﺌﻥ ﻤﻥ ﻜﺎﺌﻨﺎﺕ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ!
٢٨
-٦-
ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل
Connection Object
ﻴﺘﻴﺢ ﻟﻙ ﻫﺫﻩ ﺍﻟﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺘﻌﻤل ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ..ﻭﻜﻤﺎ ﺫﻜﺭﻨﺎ ﺴﺎﺒﻘﺎ ،ﺴـﻨﺭﻜﺯ
ﻫﻨﺎ ﻋﻠﻰ ﺩﺭﺍﺴﺔ ﺍﻟﻔﺌﺔ ،sqlConnectionﻟﻬﺫﺍ ﻻ ﺘﻨﺱ ﺇﻀﺎﻓﺔ ﺠﻤﻠﺔ ﺍﻻﺴﺘﺨﺩﺍﻡ ﺍﻟﺘﺎﻟﻴﺔ ﺃﻋﻠـﻰ
ﺼﻔﺤﺔ ﺍﻟﻜﻭﺩ:
;using System.Data.SqlClient
ﻭﺘﺨﺘﻠﻑ ﺒﻌﺽ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﻤﺭﺴﻠﺔ ﻋﺒﺭ ﻨﺹ ﺍﻻﺘﺼﺎل ،ﺘﺒﻌﺎ ﻟﻨﻭﻉ ﻤـﺯﻭﺩ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺍﻟﻤﺴﺘﺨﺩﻡ ..ﻭﻗﺩ ﻜﺎﻨﺕ ﻜﺘﺎﺒﺔ ﻨﺹ ﺍﻻﺘﺼﺎل ﺘﻤﺜل ﻤﺸﻜﻠﺔ ﻗﺒل ﻅﻬﻭﺭ ﺍﻹﺼﺩﺍﺭ ﺍﻟﺜﺎﻨﻲ ﻤﻥ ﺇﻁﺎﺭ
ﺍﻟﻌﻤل ﻤﻊ ﺩﻭﺕ ﻨﺕ ،٢٠٠٥ﺤﻴﺙ ﻭﻓﺭ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﻓﺌﺔ ﺨﺎﺼﺔ ﺘﺴﻤﻰ "ﺒﺎﻨﻲ ﻨﺹ ﺍﻻﺘﺼـﺎل"
،DbConnectionStringBuilderﻭﻤﻨﻬﺎ ﺘﻡ ﺍﺸﺘﻘﺎﻕ ﻓﺌﺔ ﻟﺒﻨﺎﺀ ﻨﺹ ﺍﺘﺼﺎل ﻜل ﻤﺯﻭﺩ ﻤـﻥ
ﻤﺯﻭﺩﺍﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻫﻲ:
ﻭﻅﻴﻔﺘﻬﺎ ﺍﻟﻔﺌﺔ
ﺒﺎﻨﻲ ﻨﺹ ﺍﺘﺼﺎل ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ. SqlConnectionStringBuilder
ﺒﺎﻨﻲ ﻨﺹ ﺍﺘﺼﺎل .OLEDB OleDbConnectionStringBuilder
ﺒﺎﻨﻲ ﻨﺹ ﺍﺘﺼﺎل .ODBC OdbcConnectionStringBuilder
ﺒﺎﻨﻲ ﻨﺹ ﺍﺘﺼﺎل ﺃﻭﺭﺍﻜل. OracleConnectionStringBuilder
٣٠
ﻓﺌﺔ ﺒﺎﻨﻲ ﻨﺹ ﺍﻻﺘﺼﺎل
DbConnectionStringBuilder Class
٣١
ﻨﺹ ﺍﺘﺼﺎل ﻗﺎﺒل ﻟﻠﺘﺼﻔﺢ :BrowsableConnectionString
ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ،trueﻓﺴﻴﺘﻡ ﻋﺭﺽ ﻨﺹ ﺍﻻﺘﺼﺎل ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼـﺎﺌﺹ
ﻋﻨﺩﻤﺎ ﺘﺴﺘﺨﺩﻡ ﺍﻷﺩﺍﺓ PropertyGridﻟﻌﺭﺽ ﺨﺼﺎﺌﺹ ﺒﺎﻨﻲ ﻨﺹ ﺍﻻﺘﺼﺎل.
ﻭﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﻭﺴﺎﺌل ﺍﻟﻘﺎﻤﻭﺱ ﺍﻟﺸﻬﻴﺭﺓ ،ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ:
٣٢
ﻤﺴﺎﻭٍ ﻟـ :EquivalentTo
ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﻔﺌﺔ DbConnectionStringBuilderﻟﻤﻘﺎﺭﻨﺘﻬـﺎ
ﺒﺎﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ﻤﻥ ﺍﻟﻔﺌﺔ ..DbConnectionStringBuilderﻭﺘﺘﻡ ﺍﻟﻤﻘﺎﺭﻨـﺔ ﺒﺎﻟﺘﺄﻜـﺩ
ﻤﻥ ﺃﻥ ﻜل ﻤﻔﺘﺎﺡ ﻓﻲ ﺍﻟﻘﺎﻤﻭﺱ ﺍﻷﻭل ﻟﻪ ﻤﺎ ﻴﻨﺎﻅﺭﻩ ﻓﻲ ﺍﻟﻘﺎﻤﻭﺱ ﺍﻟﺜﺎﻨﻲ )ﺒﻐـﺽ ﺍﻟﻨﻅـﺭ
ﻋﻥ ﺍﻟﺘﺭﺘﻴﺏ( ،ﻭﺃﻥ ﺍﻟﻘﻴﻤﺘﻴﻥ ﺍﻟﻤﺤﻔﻭﻅﺘﻴﻥ ﻓﻲ ﻜﻠﻴﻬﻤﺎ ﻤﺘﺴـﺎﻭﻴﺘﺎﻥ ..ﻻﺤـﻅ ﺃﻥ ﻤﻘﺎﺭﻨـﺔ
ﺍﻟﻤﻔﺎﺘﻴﺢ ﻻ ﺘﺭﺍﻋﻲ ﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ ،ﺒﻴﻨﻤﺎ ﻤﻘﺎﺭﻨﺔ ﺍﻟﻘﻴﻡ ﺘﺭﺍﻋﻲ ﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ ..ﻭﻓﻲ ﺤﺎﻟﺔ
ﻨﺠﺎﺡ ﺍﻟﻤﻘﺎﺭﻨﺔ ﻴﻌﺘﺒﺭ ﻨﺼﺎ ﺍﻻﺘﺼﺎل ﺍﻟﻤﻭﺠﻭﺩﻴﻥ ﻓﻲ ﺍﻟﻘﺎﻤﻭﺴﻴﻥ ﻤﺘﺴﺎﻭﻴﻴﻥ ،ﻭﺘﻌﻴـﺩ ﻫـﺫﻩ
ﺍﻟﻭﺴﻴﻠﺔ ..trueﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﻓـﻲ ﺍﻟـﺯﺭ EquivalentToﻓـﻲ
ﺍﻟﻤﺸﺭﻭﻉ .ConStrBuilder
٣٣
ﻭﻟﻠﻭﺴﻴﻠﺔ TryGetValueﻤﻌﺎﻤﻼﻥ :ﺍﻷﻭل ﻤﻌﺎﻤل ﻨﺼﻲ ﻴﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻤﻔﺘﺎﺡ ،ﻭﺍﻟﺜـﺎﻨﻲ
ﻤﻌﺎﻤل ﺇﺨﺭﺍﺝ outﻤﻥ ﺍﻟﻨﻭﻉ ،Objectﻴﻌﻴﺩ ﺇﻟﻴﻙ ﻗﻴﻤﺔ ﺍﻟﻤﻔﺘﺎﺡ ﺇﻥ ﻭﺠﺩ ..ﻭﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ
ﻫﻭ ﺇﻋﺎﺩﺓ ﻜﺘﺎﺒﺔ ﻟﻠﻤﺜﺎل ﺍﻟﺴﺎﺒﻕ ﺒﺎﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ:
;object Value = null
))if (CsB.TryGetValue("AttachDbFilename", out Value
MessageBox.Show(CsB["AttachDbFilename"].
ToString ( )); // C:\Books.mdf
٣٤
ﻓﺌﺔ ﺒﺎﻨﻲ ﻨﺹ ﺍﺘﺼﺎل ﺴﻴﻜﻴﻭل
SqlConnectionStringBuilder Class
٣٥
ﺍﻟﻔﻬﺭﺱ ﺍﻷﺴﺎﺴﻲ :InitialCatalog
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﻻﺘﺼﺎل ﺒﻬﺎ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ..ﻭﺘﺨﺘﻠـﻑ ﻫـﺫﻩ
ﺍﻟﺨﺎﺼﻴﺔ ﻋﻥ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺴﺎﺒﻘﺔ ﻓﻲ ﺃﻨﻬﺎ ﺘﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻤﺘﺼﻠﺔ ﺒﺎﻟﺨـﺎﺩﻡ ﻓﻌـﻼ
ﻓﻲ ﻫﺫﻩ ﺍﻟﻠﺤﻅﺔ ،ﻟﻬﺫﺍ ﻴﺘﻡ ﺫﻜﺭ ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻘﻁ ﺒﺩﻭﻥ ﺍﻟﻤﺴﺎﺭ ﻭﺍﻻﻤﺘـﺩﺍﺩ )ﻤﺜـل
..(Booksﺒﻴﻨﻤﺎ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ AttachDBFilenameﻴﺘﻡ ﺫﻜﺭ ﻤﺴﺎﺭ ﻤﻠـﻑ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﺘﻭﺼﻴﻠﻬﺎ ﺒﺎﻟﺨﺎﺩﻡ ﺜﻡ ﺍﻻﺘﺼﺎل ﺒﻬﺎ.
ﻭﺘﻨﺎﻅﺭ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻤﻔﺘﺎﺡ databaseﺃﻭ Initial Catalogﻓﻲ ﻨـﺹ ﺍﻻﺘﺼـﺎل..
ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ﻨﺼﺎ ﻓﺎﺭﻏﺎ.
٣٦
ﺤﻤﺎﻴﺔ ﻤﺘﻜﺎﻤﻠﺔ :IntegratedSecurity
ﺘﻨــﺎﻅﺭ ﺍﻟﻤﻔﺘــﺎﺡ trusted_connectionﺃﻭ Integrated Securityﻓــﻲ ﻨــﺹ
ﺍﻻﺘﺼﺎل ..ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ،falseﻤﻤﺎ ﻴﻌﻨـﻲ ﺃﻥ ﻋﻠﻴـﻙ ﺇﻤـﺩﺍﺩ
ﺍﻻﺘﺼﺎل ﺒﺎﺴﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻭﻜﻠﻤﺔ ﺍﻟﻤﺭﻭﺭ ..ﺃﻤﺎ ﻟﻭ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ ،trueﻓﺴـﻴﺘﻡ ﺍﺴـﺘﺨﺩﺍﻡ
ﺤﺴﺎﺏ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻋﻠﻰ ﺍﻟﻭﻴﻨﺩﻭﺯ ﻟﻼﺘﺼﺎل ..ﻫﺫﺍ ﻤﻔﻴﺩ ﻋﻨﺩ ﺍﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ ﺍﻟﻤﺤﻠـﻲ ،ﺃﻭ
ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺩﺍﺨل ﺸﺭﻜﺔ ﺘﺴﺘﺨﺩﻡ ﺸﺒﻜﺔ ﺩﺍﺨﻠﻴﺔ ،LANﻓﻔﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻴﻘـﻭﻡ
ﻤﺩﻴﺭ ﻨﻅﺎﻡ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ System Administratorﺒﺘﻌﺭﻴﻑ ﺤﺴـﺎﺒﺎﺕ ﺍﻟﻭﻴﻨـﺩﻭﺯ
ﺍﻟﺨﺎﺼﺔ ﺒﺄﺠﻬﺯﺓ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﺍﻟﻤﺘﺼﻠﺔ ﺒﺎﻟﺸﺒﻜﺔ ﻭﺍﻟﻤﺴﻤﻭﺡ ﻟﻬﺎ ﺒﺎﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ ،ﻭﺒﻬـﺫﺍ
ﻴﻜﻔﻲ ﻤﺠﺭﺩ ﺘﺴﺠﻴل ﺍﻟﺩﺨﻭل ﻋﻠﻰ ﺍﻟﻭﻴﻨﺩﻭﺯ ﻟﻀﻤﺎﻥ ﺴﺭﻴﺔ ﺍﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ.
٣٧
ﺇﺒﻘﺎﺀ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺴﺭﻴﺔ :PersistSecurityInfo
ـﺹ
ـﻲ ﻨـ
ـﺎﺡ Persist Security Infoﺃﻭ persistsecurityinfoﻓـ
ـﺎﻅﺭ ﺍﻟﻤﻔﺘـ
ﺘﻨـ
ﺍﻻﺘﺼﺎل ..ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ،falseﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﻋﻠﻴـﻙ ﺇﺭﺴـﺎل
ﺍﺴﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻭﻜﻠﻤﺔ ﺍﻟﺴﺭ ﻜﻠﻤﺎ ﺃﺭﺩﺕ ﻓﺘﺢ ﺍﻻﺘﺼﺎل ..ﺃﻤﺎ ﻟـﻭ ﺠﻌﻠـﺕ ﻗﻴﻤﺘﻬـﺎ ،true
ﻓﻴﻤﻜﻨﻙ ﺇﺭﺴﺎل ﻫﺫﻩ ﺍﻟﻤﻌﻠﻭﻤﺎﺕ ﻋﻨﺩ ﻓﺘﺢ ﺍﻻﺘﺼﺎل ﻷﻭل ﻤﺭﺓ ﻓﻘﻁ ،ﻭﺴﻴﺘﻡ ﺍﻻﺤﺘﻔﺎﻅ ﺒﻬـﺎ
ﻻﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﻓﺘﺢ ﺍﻻﺘﺼﺎل ﺒﻌﺩ ﻫﺫﺍ.
٣٨
ﺍﻟﺘﺸﻔﻴﺭ :Encrypt
ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ Encryptﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل ..ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜـﻭﻥ ﻗﻴﻤﺘﻬـﺎ
falseﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﻟﻥ ﻴﺸﻔﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺭﺴﻠﺔ ﺒﻴﻨﻪ ﻭﺒﻴﻥ ﺍﻟﻌﻤﻴل ..ﻭﻟـﻭ
ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ ،trueﻓﺴﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻡ ﻨﻭﻉ ﻤﻥ ﺍﻟﺘﺸﻔﻴﺭ ﻴﺴﻤﻰ SSL Encryptionﺍﻟـﺫﻱ
ﻴﻌﻨﻲ "ﺘﺸﻔﻴﺭ ﻁﺒﻘﺔ ﻤﻘﺎﺒﺱ ﺍﻻﺘﺼﺎل ﺍﻵﻤﻨﺔ" ،Secure Sockets Layer Encryption
ﻭﻫﻭ ﻴﺴﺘﺨﺩﻡ ﻤﻔﺘﺎﺤﻴﻥ :ﻤﻔﺘﺎﺤﺎ ﻋﺎﻤﺎ Public Keyﻴﺴﺘﺨﺩﻡ ﻟﺘﺸﻔﻴﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﻭﻤﻔﺘﺎﺤـﺎ
ﺨﺎﺼﺎ Private Keyﻴﺴﺘﺨﺩﻡ ﻟﻔﻙ ﺘﺸﻔﻴﺭﻫﺎ.
٣٩
ﻭﻴﻨﺼــﺢ ﺒﺠﻌــل ﻗﻴﻤــﺔ ﻫــﺫﻩ ﺍﻟﺨﺎﺼــﻴﺔ falseﻻﺴــﺘﺨﺩﺍﻡ ﺍﻻﺘﺼــﺎل ﺍﻟﻌــﺎﺩﻱ
،Regular Connectionﻭﺫﻟﻙ ﻋﻨﺩ ﺍﻻﺘﺼﺎل ﺒﺨﺎﺩﻡ ﺒﻌﻴﺩ )ﻏﻴـﺭ ﻤﺤﻠـﻲ( Remote
.Server
ﻭﺍﻟﺸﻜل ﺍﻟﺘﺎﻟﻲ ﻴﻠﺨﺹ ﺍﻟﻔﺎﺭﻕ ﺒﻴﻥ ﻫﺫﻴﻥ ﺍﻟﻨﻭﻋﻴﻥ ﻤﻥ ﺍﻻﺘﺼﺎل ..ﻻﺤـﻅ ﺃﻥ ﺍﻻﺘﺼـﺎل
ﺒﺎﻟﻤﺤﺘﻭﻯ ﻴﺘﺠﺎﻫل ﺍﻟﻌﺩﻴﺩ ﻤﻥ ﻁﺒﻘﺎﺕ ﺍﻻﺘﺼـﺎل ﻋﺒـﺭ ﺍﻟﺸـﺒﻜﺔ ،Network Layers
ﻭﻴﺴﺘﺨﺩﻡ ﻭﺍﺠﻬﺔ ﺍﻻﺘﺼﺎل ﺍﻟﻤﺒﺎﺸﺭ .In-Process Interface
٤٠
ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ :Enlist
ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ Enlistﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل ..ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀـﻲ ﺘﻜـﻭﻥ ﻗﻴﻤﺘﻬـﺎ
،trueﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﺍﻻﺘﺼﺎل ﺍﻟﺤﺎﻟﻲ ﺴﻴﻭﻀﻊ ﻓﻲ ﻗﺎﺌﻤﺔ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻟﻤﺤﺘﻭﻯ
ﺍﻟﺘﻌﺎﻤﻼﺕ ﺍﻟﺤﺎﻟﻲ ..Current Transaction Contextﻫﺫﺍ ﻴﺴﻤﺢ ﺒﺠﻌل ﺃﻜﺜـﺭ ﻤـﻥ
ﺍﺘﺼﺎل ﺘﺘﺸﺎﺭﻙ ﻓﻲ ﺘﻌﺎﻤل Transactionﻭﺍﺤﺩ ،ﺒﺤﻴﺙ ﻟﻭ ﻓﺸﻠﺕ ﺃﻱ ﻋﻤﻠﻴﺔ ﻋﻠـﻰ ﺃﻱ
ﺍﺘﺼﺎل ﻤﻨﻬﺎ ﻴﺘﻡ ﺇﻟﻐﺎﺀ ﺍﻟﻌﻤﻠﻴﺎﺕ ﺍﻟﺘﻲ ﺘﻤﺕ ﻋﻠﻰ ﺒﺎﻗﻲ ﺍﻻﺘﺼـﺎﻻﺕ ..ﻟـﻥ ﻨﺘﻌﻤـﻕ ﻓـﻲ
ﻤﻭﻀﻭﻉ ﺍﻟﺘﻌﺎﻤﻼﺕ Transactionsﻓﻲ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ،ﻭﺴـﻨﺘﻌﺭﻑ ﻋﻠﻴـﻪ ﻤـﻊ ﺒـﺎﻗﻲ
ﺍﻟﻤﻭﺍﻀﻴﻊ ﺍﻟﻤﺘﻘﺩﻤﺔ ﻓﻲ ﺒﺭﻤﺠﺔ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﻜﺘﺎﺏ ﺍﻟﻘﺎﺩﻡ ﺒﺈﺫﻥ ﺍﷲ.
ﻤﺴﺎﻫﻤﺔ :Pooling
ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ Poolingﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل ..ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜـﻭﻥ ﻗﻴﻤﺘﻬـﺎ
،trueﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﻫﺫﺍ ﺍﻻﺘﺼـﺎل ﺴﻴﻨﻀـﻡ ﺇﻟـﻰ ﺭﺼـﻴﺩ ﺍﻻﺘﺼـﺎﻻﺕ ﺍﻟﻤﺴـﺎﻫﻤﺔ
Connection Poolﺍﻟﺘﻲ ﺘﻅل ﻤﻔﺘﻭﺤﺔ ﺩﺍﺌﻤﺎ ﻻﺴﺘﺨﺩﺍﻤﻪ ﻓﻭﺭ ﺍﻟﺤﺎﺠﺔ ﺇﻟﻴﻬﺎ ..ﺃﻤـﺎ ﻟـﻭ
ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ falseﻓﺴﻴﻌﻨﻲ ﻫﺫﺍ ﺃﻥ ﻫﺫﺍ ﺍﻻﺘﺼﺎل ﺴﻴﺘﻡ ﻓﺘﺤﻪ ﻭﺇﻏﻼﻗـﻪ ﻤﺒﺎﺸـﺭﺓ ﺒﻌـﺩ
ﺍﻨﺘﻬﺎﺀ ﺍﺴﺘﺨﺩﺍﻤﻪ ،ﻭﻋﻨﺩ ﺍﻻﺤﺘﻴﺎﺝ ﺇﻟﻴﻪ ﻤﺠﺩﺩﺍ ﻴﺘﻡ ﻓﺘﺤﻪ ﻤﻥ ﺠﺩﻴﺩ ..ﻭﻫﻜﺫﺍ.
٤١
ﺃﻗﺼﻰ ﺤﺠﻡ ﻟﻠﻤﺴﺎﻫﻤﺔ :MaxPoolSize
ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ Max Pool Sizeﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل ..ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜـﻭﻥ
ﻗﻴﻤﺘﻬﺎ ،١٠٠ﻤﻤﺎ ﻴﻌﻨﻲ ﺍﻻﺤﺘﻔﺎﻅ ﻓﻲ ﺭﺼﻴﺩ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺴﺎﻫﻤﺔ ﺍﻟﻤﺘﺎﺤﺔ ﻟﻼﺴـﺘﺨﺩﺍﻡ،
ﺒﻤﺌﺔ ﺍﺘﺼﺎل ـ ﻜﺤﺩ ﺃﻗﺼﻰ ـ ﻤﻔﺘﻭﺤﺔ ﺒﻴﻥ ﺍﻟﺨﺎﺩﻡ ﻭﺍﻟﻌﻤﻴل.
٤٢
ﻤﺠﻤﻭﻋﺎﺕ ﺍﻟﻨﺘﺎﺌﺞ ﺍﻟﻔﻌﺎﻟﺔ ﺍﻟﻤﺘﻌﺩﺩﺓ:MultipleActiveResultSets
ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ MultipleActiveResultSetsﻓﻲ ﻨﺹ ﺍﻻﺘﺼـﺎل ..ﻭﻓـﻲ ﺍﻟﻭﻀـﻊ
ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ،falseﻭﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺍﺴـﺘﺨﺩﺍﻡ "ﻤﺠﻤﻭﻋـﺔ ﺍﻟﻨﺘـﺎﺌﺞ ﺍﻟﻌﺎﺩﻴـﺔ"
،Default Result Setﻭﻓﻴﻬﺎ ﻴﺘﻡ ﺇﺭﺴﺎل ﻨﺘﺎﺌﺞ ﺍﻻﺴﺘﻌﻼﻡ ﻤﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﺇﻟﻰ ﺠﻬﺎﺯ
ﺍﻟﻌﻤﻴل ،ﺤﻴﺙ ﻴﺘﻡ ﺤﻔﻅﻬﺎ ﻓﻲ ﻤﺨﺯﻥ ﻭﺴﻴﻁ Bufferﻓـﻲ ﺍﻟـﺫﺍﻜﺭﺓ ،ﻭﻋﻨـﺩﻤﺎ ﻴﺤﺘـﺎﺝ
ﺒﺭﻨﺎﻤﺠﻙ ﺇﻟﻰ ﻋﺭﻀﻬﺎ ﻟﻠﻤﺴﺘﺨﺩﻡ ،ﻴﺘﻡ ﺍﻟﻤﺭﻭﺭ ﻋﺒﺭﻫﺎ ﺴﺠﻼ ﺒﺴﺠل ..ﻭﻻ ﻴﺴﺘﻁﻴﻊ ﺍﻟﻌﻤﻴل
ﺍﺴﺘﺨﺩﺍﻡ ﺍﻻﺘﺼﺎل ﺍﻟﻤﻔﺘﻭﺡ ﻤﻊ ﺍﻟﺨﺎﺩﻡ ﻓﻲ ﺘﺤﺩﻴﺙ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻗﺒل ﺃﻥ ﻴﻨﺘﻬﻲ ﻤﻥ ﺍﻟﺘﻌﺎﻤل ﻤﻊ
ﻜل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺃﺭﺴﻠﻬﺎ ﺍﻟﺨﺎﺩﻡ ﺃﻭﻻ ،ﺃﻭ ﻗﺒل ﺃﻥ ﻴﺭﺴل ﺇﻟﻰ ﺍﻟﺨﺎﺩﻡ ﻁﻠﺒﺎ ﻹﻟﻐﺎﺀ ﺇﺭﺴـﺎل
ﺒﺎﻗﻲ ﺍﻟﻨﺘﺎﺌﺞ ..ﻭﺘﻌﺘﺒﺭ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﺃﻜﺜﺭ ﻜﻔﺎﺀﺓ ﻓﻲ ﺍﺴﺘﻐﻼل ﺍﻻﺘﺼﺎل ،ﻷﻥ ﺍﻟﺨﺎﺩﻡ ﻴﺭﺴل
ﺃﻜﺒﺭ ﻜﻡ ﻤﻤﻜﻥ ﻤﻥ ﺍﻟﻨﺘﺎﺌﺞ ﻋﺒﺭ ﺤـﺯﻡ ﺍﻟﺒﻴﺎﻨـﺎﺕ Packetsﺍﻟﻤﺭﺴـﻠﺔ ﻋﺒـﺭ ﺍﻟﺸـﺒﻜﺔ
.Network
ﻭﻟﻭ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ،trueﻓﺴﻴﺘﻡ ﺍﺴـﺘﺨﺩﺍﻡ "ﻤﺠﻤﻭﻋـﺎﺕ ﺍﻟﻨﺘـﺎﺌﺞ ﺍﻟﻔﻌﺎﻟـﺔ
ﺍﻟﻤﺘﻌﺩﺩﺓ" Multiple Active Result Setsﺃﻭ ﺍﺨﺘﺼﺎﺭﺍ ،MARSﻭﻫﻲ ﻤﺘﺎﺤﺔ ﻓﻘﻁ
ﻤﻊ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ٢٠٠٥ﻭﻤﺎ ﻴﻠﻴﻪ ﻤﻥ ﺇﺼﺩﺍﺭﺍﺕ ،ﻭﻓﻴﻬﺎ ﻴﺴﻤﺢ ﻟﻠﻌﻤﻴل ﺒﺎﺴﺘﺨﺩﺍﻡ ﺃﻜﺜـﺭ
ﻤﻥ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ SqlDataReaderﻓﻲ ﻨﻔﺱ ﺍﻟﻭﻗﺕ.
٤٣
AppleTalk dbmsadsn
VIA dbmsgnet
Shared Memory dbmslpcn
IPX/SPX dbmsspxn
TCP/IP dbmssocn
ﻭﻓﻲ ﺤﺎﻟﺔ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺨﺎﺩﻡ ﻤﺤﻠﻲ ﻭﺘﺭﻙ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻓﺎﺭﻏـﺔ ،ﻴـﺘﻡ ﺍﺴـﺘﺨﺩﺍﻡ
ﺍﻟﻤﻜﺘﺒﺔ .(Shared Memory) dbmslpcn
٤٤
ﻓﻙ ﺍﺭﺘﺒﺎﻁ ﻀﻤﻨﻲ :ﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀـﻴﺔ ،ﻭﻓﻴﻬـﺎ ﻴـﺅﺩﻱ Implicit
Unbind
Current ﺇﻏﻼﻕ ﺍﻻﺘﺼﺎل ﺇﻟﻰ ﻓﺼﻠﻪ ﻋﻥ ﺍﻟﺘﻌﺎﻤﻼﺕ ﺍﻟﺠﺎﺭﻴﺔ
.Transactions
ﻓﻙ ﺍﺭﺘﺒﺎﻁ ﺼﺭﻴﺢ :ﻴﺠﺏ ﻋﻠﻴﻙ ﻓﻙ ﺍﻻﺭﺘﺒﺎﻁ ﺒـﻴﻥ ﺍﻻﺘﺼـﺎل Explicit
Unbind
ﻭﺍﻟﺘﻌﺎﻤﻼﺕ ﺍﻟﺠﺎﺭﻴﺔ ﺒﻁﺭﻴﻘﺔ ﺼﺭﻴﺤﺔ ﻗﺒل ﺇﻏـﻼﻕ ﺍﻻﺘﺼـﺎل،
ﻭﺇﻻ ﺤﺩﺙ ﺨﻁﺄ.
٤٥
ﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺨﺎﺼﻴﺔ ﺒﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ .٢٠٠٨ SQL Server
2008
ﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻡ ﺃﺤﺩﺙ ﺇﺼﺩﺍﺭ ﻤﻥ ﺴﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ ﻴﻤﻜـﻥ ﻟﻠﺨـﺎﺩﻡ Latest
ﻭﺍﻟﻌﻤﻴل ﺍﻟﺘﻌﺎﻤل ﻤﻌﻪ.
ﻭﻻ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﻴﺔ ﻭﺴﺎﺌل Methodsﻏﻴﺭ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ.
ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﺭﻴﻙ ﻜﻴﻑ ﺘﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻟﺘﻜﻭﻴﻥ ﻨﺹ ﺍﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﻜﺘﺏ ﻋﻠﻰ ﺍﻟﺨـﺎﺩﻡ
ﺍﻟﻤﺤﻠﻲ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺤﻤﺎﻴﺔ ﺍﻟﻤﺘﻜﺎﻤﻠﺔ:
;) (var CnStrBldr = new SqlConnectionStringBuilder
;"CnStrBldr.DataSource = ".\\SQLEXPRESS
;"CnStrBldr.InitialCatalog = "Books
;CnStrBldr.IntegratedSecurity = true
;var CnStr = CnStrBldr.ConnectionString
;)MessageBox.Show(CnStr
٤٦
ﺤﻔﻅ ﻨﺹ ﺍﻻﺘﺼﺎل ﻓﻲ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﺒﺭﻨﺎﻤﺞ :Settings
ﻋﻨﺩ ﻜﺘﺎﺒﺔ ﺒﺭﻨﺎﻤﺞ ﻴﺘﻌﺎﻤل ﻤﻊ ﻗﻭﺍﻋﺩ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻴﻠﺠﺄ ﺍﻟﻤﺒﺭﻤﺞ ﻓﻲ ﻤﻌﻅﻡ ﺍﻷﺤﻭل ﺇﻟـﻰ ﺇﻨﺸـﺎﺀ
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺠﻬﺎﺯﻩ ،ﺃﻭ ﻴﻨﺴﺦ ﺠﺯﺀﺍ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤـﻥ ﺍﻟﺨـﺎﺩﻡ ﺇﻟـﻰ ﺠﻬـﺎﺯﻩ،
ﻟﻴﺠﻌﻠﻬﺎ ﺘﻌﻤل ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ﺍﻟﻤﺤﻠﻲ ،ﻭﻤﻥ ﺜﻡ ﻴﺘﺼل ﺒﻬﺎ ﻤﻥ ﺒﺭﻨﺎﻤﺠـﻪ ..ﻫـﺫﺍ ﻴﺠﻌـل ﻜﺘﺎﺒـﺔ
ﻭﺍﺨﺘﺒﺎﺭ ﺍﻟﻜﻭﺩ ﻭﺘﺼﺤﻴﺤﻪ ﺃﺴﺭﻉ ﻤﻥ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺨﺎﺩﻡ ﺤﻘﻴﻘﻲ ﻋﺒﺭ ﺸﺒﻜﺔ ﺍﻹﻨﺘﺭﻨﺕ ،ﻜﻤﺎ ﺃﻨـﻪ
ﻴﻀﻤﻥ ﻋﺩﻡ ﺘﺨﺭﻴﺏ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺭﺌﻴﺴﻴﺔ ﻋﻨﺩ ﺇﻀﺎﻓﺔ ﺃﻭ ﺤﺫﻑ ﺍﻟﺴﺠﻼﺕ ﻟﻼﺨﺘﺒﺎﺭ.
ﻭﺒﻌــﺩ ﺍﻻﻨﺘﻬــﺎﺀ ﻤــﻥ ﺍﻟﺒﺭﻨــﺎﻤﺞ ،ﻴــﺘﻡ ﺭﻓــﻊ ﻗﺎﻋــﺩﺓ ﺍﻟﺒﻴﺎﻨــﺎﺕ ﺇﻟــﻰ ﺍﻟﺨــﺎﺩﻡ
)ﺇﻥ ﻟﻡ ﺘﻜﻥ ﻤﻭﺠﻭﺩﺓ ﻋﻠﻴﻪ( ،ﻭﺘﺼﺤﻴﺢ ﻨﺼﻭﺹ ﺍﻻﺘﺼﺎل ﻟﺘﺸﻴﺭ ﺇﻟﻰ ﺍﻟﺨﺎﺩﻡ ﺍﻟﺤﻘﻴﻘﻲ ﺒﺩﻻ ﻤـﻥ
ﺍﻟﺨﺎﺩﻡ ﺍﻟﻤﺤﻠﻲ ،ﻟﻜﻲ ﻴﺒﺩﺃ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻋﻤﻠﻪ ﻓﻲ ﺼﻭﺭﺘﻪ ﺍﻟﻨﻬﺎﺌﻴﺔ.
ﻭﻨﻅﺭﺍ ﻷﻥ ﺍﻟﺒﺭﺍﻤﺞ ﺍﻟﻌﻤﻠﻴﺔ ﻗﺩ ﺘﺘﻌﺎﻤل ﻤﻊ ﺃﻜﺜﺭ ﻤﻥ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ،ﻜﻤـﺎ ﺃﻥ ﻋﻨـﻭﺍﻥ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻗﺩ ﻴﺘﻐﻴﺭ ﻓﻲ ﺃﻱ ﻟﺤﻅﺔ ﻟﻭ ﺘﻡ ﻨﻘﻠﻬﺎ ﻤﻥ ﺨﺎﺩﻡ ﺇﻟﻰ ﺁﺨﺭ ﻋﻠﻰ ﺍﻹﻨﺘﺭﻨﺕ ،ﻴﺼـﻴﺭ ﻤـﻥ
ﻏﻴﺭ ﺍﻟﻌﻤﻠﻲ ﻜﺘﺎﺒﺔ ﻨﺹ ﺍﻻﺘﺼﺎل ﻓﻲ ﺍﻟﻜﻭﺩ ،ﻷﻥ ﺍﻟﺒﺤﺙ ﻋﻨﻪ ﻭﺘﻐﻴﻴﺭﻩ ﻓﻲ ﻜل ﺍﻟﻤﻭﺍﻀﻊ ﺃﻤـﺭ
ﻤﺭﻫﻕ ﻭﻋﺭﻀﺔ ﻟﻠﺨﻁﺄ ..ﻟﻬﺫﺍ ﻴﻔﻀل ﻜﺘﺎﺒﺔ ﻨﺼﻭﺹ ﺍﻻﺘﺼﺎل ﻓﻲ ﻤﻠﻑ ﺨﺎﺭﺝ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻤـﻊ
ﺍﺴﺘﺨﺩﺍﻡ ﺇﺤﺩﻯ ﻁﺭﻕ ﺍﻟﺘﺸﻔﻴﺭ ﻟﺤﻤﺎﻴﺔ ﺍﻟﺘﻔﺎﺼﻴل ﺍﻟﻤﻜﺘﻭﺒﺔ ﺒﻪ )ﻜﺎﺴﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻭﻜﻠﻤﺔ ﺍﻟﺴـﺭ(..
ﻭﻴﻤﻜﻥ ﻓﻌل ﻫﺫﺍ ﻴﺩﻭﻴﺎ ،ﺃﻭ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺇﺤﺩﻯ ﺍﻟﻁـﺭﻕ ﺍﻟﺠـﺎﻫﺯﺓ ﺍﻟﺘـﻲ ﺘﻤﻨﺤﻬـﺎ ﺩﻭﺕ ﻨـﺕ،
ﻜﺎﻹﻋﺩﺍﺩﺍﺕ Settingsﺍﻟﺘﻲ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﺒﺎﻟﺘﻔﺼﻴل ﺍﻟﻤﻤـل ﻓـﻲ ﻤﺭﺠـﻊ "ﺒﺭﻤﺠـﺔ ﻨﻤـﺎﺫﺝ
ﺍﻟﻭﻴﻨﺩﻭﺯ" ،ﻭﻗﻠﻨﺎ ﻫﻨﺎﻙ ﺇﻥ ﺩﻭﺕ ﻨﺕ ﺘﻘﺩﻡ ﺭﻋﺎﻴﺔ ﺨﺎﺼﺔ ﻟﻨﺼﻭﺹ ﺍﻻﺘﺼﺎل ،ﻓﻘﺩ ﺨﺼﺼﺕ ﻟﻬﺎ
ـﺔ
ـﻲ ﺍﻟﻔﺌـ
ـﻪ ،ﻫـ
ـل ﻤﻌـ
ـﺔ ﺘﺘﻌﺎﻤـ
ـﺎ ﻓﺌـ
ـﺩﺍﺩﺍﺕ ،ﻭﻤﻨﺤﺘﻨـ
ـﻑ ﺍﻹﻋـ
ـﻲ ﻤﻠـ
ـﺎ ﻓـ
ـﺎ ﺨﺎﺼـ
ﻤﻘﻁﻌـ
..ConnectionStringsSectionﻭﻟﻘﺩ ﺃﺭﺠﺄﻨﺎ ﺸﺭﺡ ﻫﺫﺍ ﺍﻟﻤﻭﻀﻭﻉ ﺇﻟﻰ ﺤﻴﻥ ﺍﻟﺘﻌﺭﻑ ﻋﻠﻰ
ﻗﻭﺍﻋﺩ ،ﻭﻫﺎ ﻨﺤﻥ ﺃﻭﻻﺀ . J
ﻹﻀﺎﻓﺔ ﻨﺹ ﺍﺘﺼﺎل ﺇﻟﻰ ﺍﻹﻋﺩﺍﺩﺍﺕ ﺒﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ﺍﺘﺒﻊ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ:
-ﺍﻓﺘﺢ ﻤﺘﺼﻔﺢ ﺍﻟﻤﺸﺎﺭﻴﻊ ،Solution Explorerﻭﺍﻨﻘﺭ ﻤﺭﺘﻴﻥ ﺒﺎﻟﻔﺄﺭﺓ ﻓـﻭﻕ ﺍﻟﻌﻨﺼـﺭ
..Propertiesﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻟﻰ ﻓﺘﺢ ﻨﺎﻓﺫﺓ ﺨﺼﺎﺌﺹ ﺍﻟﻤﺸﺭﻭﻉ.
-ﺍﻀﻐﻁ ﺍﻟﻌﻨﺼﺭ Settingsﻤﻥ ﺍﻟﻬﺎﻤﺵ ﺍﻷﻴﺴﺭ ﻟﻔﺘﺢ ﺼﻔﺤﺔ ﻤﺼﻤﻡ ﺍﻹﻋـﺩﺍﺩﺍﺕ ﻜﻤـﺎ
ﺘﻌﻠﻤﻨﺎ ﻤﻥ ﻗﺒل.
٤٧
-ﻓﻲ ﺍﻟﻌﻤﻭﺩ Nameﺍﻜﺘﺏ ﺍﺴﻡ ﺨﺎﺼﻴﺔ ﺍﻹﻋﺩﺍﺩ ،ﻭﻟﺘﻜﻥ .BooksConStr
-ﻓﻲ ﺍﻟﻌﻤﻭﺩ Typeﺍﻀﻐﻁ ﺯﺭ ﺍﻹﺴﺩﺍل ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ﺍﺨﺘﺭ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺨـﺎﺹ
) ..(Connection Stringﻫﺫﺍ ﺴﻴﻐﻴﺭ ﻨﻁﺎﻕ ﺨﺎﺼﻴﺔ ﺍﻹﻋﺩﺍﺩ Scopeﻟﻴﺼـﻴﺭ ﻋﻠـﻰ
ﻤﺴﺘﻭﻯ ﺍﻟﺘﻁﺒﻴﻕ .Application
-ﺍﻀﻐﻁ ﺍﻟﺯﺭ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺨﺎﻨﺔ ﺍﻟﻘﻴﻤﺔ ..Valueﺴﻴﻌﺭﺽ ﻟـﻙ ﻫـﺫﺍ ﻤﺭﺒـﻊ ﺤـﻭﺍﺭ
ﺨﺼﺎﺌﺹ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﺍﺴﺘﺨﺩﻤﻨﺎﻩ ﻤﻥ ﻗﺒل ﻹﻨﺸﺎﺀ ﺍﺘﺼـﺎل ﻤـﻥ ﻤﺘﺼـﻔﺢ ﺍﻟﺨـﻭﺍﺩﻡ
..Server Explorerﻫﺫﺍ ﻴﺘﻴﺢ ﻟﻙ ﺘﻜﻭﻴﻥ ﻨﺹ ﺍﻻﺘﺼﺎل ﺒﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ﺴﻬﻠﺔ ..ﺤـﺩﺩ
ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﺨﺘﻴﺎﺭﺍﺕ ﺍﻟﺤﻤﺎﻴﺔ ..ﻭﻟﻭ ﺃﺭﺩﺕ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﺯﻴـﺩ ﻤـﻥ
ﻤﻔﺎﺘﻴﺢ ﻨﺹ ﺍﻻﺘﺼﺎل ،ﻓﺎﻀﻐﻁ ﺍﻟﺯﺭ ..Advancedﺴﻴﻌﺭﺽ ﻟﻙ ﻫﺫﺍ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ:
٤٨
ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﺘﻌﺭﺽ ﺨﺼﺎﺌﺹ ﻨﺹ ﺍﻻﺘﺼﺎل ،ﻭﻫﻲ ﻨﻔﺱ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﻲ ﺸﺭﺤﻨﺎﻫﺎ ﻓﻲ
ﺍﻟﻔﺌﺔ ..SqlConnectionStringBuilderﻭﻴﻤﻜﻨﻙ ﺘﻐﻴﻴﺭ ﺍﻟﻘـﻴﻡ ﺍﻻﻓﺘﺭﺍﻀـﻴﺔ ﻟﻬـﺫﻩ
ﺍﻟﺨﺼﺎﺌﺹ ،ﻭﻜل ﺨﺎﺼﻴﺔ ﺴﺘﻐﻴﺭﻫﺎ ﺴﺘﻅﻬﺭ ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺘﻜﻭﻴﻨﻪ.
-ﺍﻀﻐﻁ OKﻹﻏﻼﻕ ﺍﻟﻨﺎﻓﺫﺘﻴﻥ ..ﺴﻴﻅﻬﺭ ﻨﺹ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﺘﻡ ﺘﻜﻭﻴﻨـﻪ ﻓـﻲ ﺍﻟﺨﺎﻨـﺔ
.Value
-ﻭﺇﺫﺍ ﻜﻨﺕ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﻫﺫﺍ ،ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﻨﺼﻭﺹ ﺍﺘﺼﺎل ﺃﺨـﺭﻯ ﺇﻟـﻰ ﺍﻹﻋـﺩﺍﺩﺍﺕ،
ﺒﺎﻟﻜﺘﺎﺒﺔ ﻓﻲ ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﺎﻟﻴﺔ ،ﻭﺒﻬﺫﺍ ﺘﺠﻤﻊ ﻓﻲ ﻤﻜﺎﻥ ﻭﺍﺤﺩ ،ﻜـل ﻨﺼـﻭﺹ ﺍﻻﺘﺼـﺎل
ﺍﻟﻼﺯﻤﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻜل ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﻟﺨﻭﺍﺩﻡ ﺍﻟﺘﻲ ﺘﺤﺘﺎﺠﻬﺎ ﻓـﻲ ﺒﺭﻨﺎﻤﺠـﻙ ،ﻤﻤـﺎ
ﻴﺴﻬل ﻋﻠﻴﻙ ﺘﻌﺩﻴﻠﻬﺎ ﻓﻲ ﺃﻱ ﻟﺤﻅﺔ.
-ﺍﻀﻐﻁ ﺯﺭ ﺍﻟﺤﻔﻅ ﻤﻥ ﺸﺭﻴﻁ ﺍﻷﺩﻭﺍﺕ ﻟﺤﻔﻅ ﻫﺫﻩ ﺍﻟﺘﻐﻴﻴـﺭﺍﺕ ﻓـﻲ ﻤﻠـﻑ ﺇﻋـﺩﺍﺩﺍﺕ
ﺍﻟﻤﺸﺭﻭﻉ.
ﺍﻵﻥ ،ﺘﻡ ﺘﻭﻟﻴﺩ ﺨﺎﺼﻴﺔ ﺍﺴﻤﻬﺎ BooksConStrﻓﻲ ﻓﺌﺔ ﺍﻹﻋﺩﺍﺩﺍﺕ Settingsﻓـﻲ ﺍﻟﻨﻁـﺎﻕ
،Propertiesﻭﺘﺴﺘﻁﻴﻊ ﻗﺭﺍﺀﺓ ﻗﻴﻤﺘﻬﺎ ﻓﻲ ﺃﻱ ﻭﻗﺕ ﺒﻤﻨﺘﻬﻰ ﺍﻟﺒﺴﺎﻁﺔ ..ﻤﺜﻼ:
;)MessageBox.Show(Properties.Settings.Default.BooksConStr
ﻻﺤﻅ ﺃﻨﻙ ﻻ ﺘﺴﺘﻁﻴﻊ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ BooksConStrﻷﻨﻬﺎ ﻤﻌﺭﻓﺔ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ ،ﻟﻜـﻥ
ﻤﺎ ﺯﺍل ﺒﻭﺴﻌﻙ ﻓﺘﺢ ﻤﺼﻤﻡ ﺍﻹﻋﺩﺍﺩﺍﺕ ﻓﻲ ﺃﻱ ﻟﺤﻅﺔ ﻟﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﻨـﺹ ﺍﻻﺘﺼـﺎل ،ﺃﻭ ﻓـﺘﺢ
ﺍﻟﻤﻠﻑ app.configﻤﻥ ﻤﺘﺼﻔﺢ ﺍﻟﻤﺸﺎﺭﻴﻊ ،ﻭﺘﺤﺭﻴﺭ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ BooksConStrﺍﻟﺘـﻲ
ﺴﺘﺠﺩﻫﺎ ﺘﺤﺕ ﺍﻟﻤﻘﻁﻊ > ،<connectionStringsﺩﻭﻥ ﺍﻟﺤﺎﺠﺔ ﺇﻟـﻰ ﺘﻐﻴﻴـﺭ ﺃﻱ ﻜـﻭﺩ ﻓـﻲ
ﺒﺭﻨﺎﻤﺠﻙ.
٤٩
ﻓﺌﺔ ﻤﻘﻁﻊ ﻨﺼﻭﺹ ﺍﻻﺘﺼﺎل
ConnectionStringsSection Class
ـﺔ
ـﺭﺙ ﺍﻟﻔﺌـ
ـﻲ ﺘـ
ـﺎﻕ ،System.Configurationﻭﻫـ
ـﻲ ﺍﻟﻨﻁـ
ـﻭﺩﺓ ﻓـ
ـﺔ ﻤﻭﺠـ
ـﺫﻩ ﺍﻟﻔﺌـ
ﻫـ
ConfigurationSectionﺍﻟﺘﻲ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻓﻲ ﻜﺘﺎﺏ ﺒﺭﻤﺠﺔ ﺍﻟﻭﻴﻨﺩﻭﺯ.
ﻭﻻ ﺠﺩﻴﺩ ﻓﻲ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ،ﺴﻭﻯ ﺍﻤﺘﻼﻜﻬﺎ ﻟﻠﺨﺎﺼﻴﺔ ﺍﻟﺘﺎﻟﻴﺔ:
٥٠
ﻓﺌﺔ ﺇﻋﺩﺍﺩﺍﺕ ﻨﺹ ﺍﻻﺘﺼﺎل
ConnectionStringSettings Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﻓﺌﺔ ﻋﻨﺼﺭ ﺍﻟﺘﻬﻴﺌﺔ ،ConfigurationElement Classﺍﻟﺘﻲ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬـﺎ
ﻓﻲ ﻜﺘﺎﺏ ﺒﺭﻤﺠﺔ ﻨﻤﺎﺫﺝ ﺍﻟﻭﻴﻨﺩﻭﺯ.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺜﻼﺙ ﺼﻴﻎ:
-١ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ.
-٢ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ :ﺍﺴﻡ ﺨﺎﺼﻴﺔ ﺍﻹﻋﺩﺍﺩ ﺍﻟﺘﻲ ﺴﺘﺤﻔﻅ ﻨﺹ ﺍﻻﺘﺼـﺎل ،ﻭﻨـﺹ
ﺍﻻﺘﺼﺎل ﻨﻔﺴﻪ.
-٣ﻭﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻟـﺙ ،ﻴﺴـﺘﻘﺒل ﺍﺴـﻡ ﻤـﺯﻭﺩ ﺍﻟﺒﻴﺎﻨـﺎﺕ
Providerﺍﻟﺫﻱ ﺴﻴﺴﺘﺨﺩﻡ ﻨﺹ ﺍﻻﺘﺼﺎل.
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ،ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
ﺍﻻﺴﻡ :Name
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﺨﺎﺼﻴﺔ ﺍﻹﻋﺩﺍﺩ ﺍﻟﺘﻲ ﺴﺘﺤﻔﻅ ﻨﺹ ﺍﻻﺘﺼﺎل.
ﻭﻴﻤﻜﻨﻙ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﺇﻋﺩﺍﺩﺍﺕ ﻨﺼﻭﺹ ﺍﻻﺘﺼﺎل ﺍﻟﺨﺎﺼﺔ ﺒـﺎﻟﺘﻁﺒﻴﻕ ،ﺒﺎﺴـﺘﺨﺩﺍﻡ
ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻤﺸﺘﺭﻜﺔ Static Propertyﺍﻟﺘﺎﻟﻴﺔ:
٥١
;var CnStrSett = ConfigurationManager.ConnectionStrings
ﻤﻠﺤﻭﻅﺔ:
ﻴﺠﺏ ﻋﻠﻴﻙ ﺤﻤﺎﻴﺔ ﻨﺹ ﺍﻻﺘﺼﺎل ﺒﺘﺸﻔﻴﺭﻩ ،ﻭﺫﻟﻙ ﻷﻥ ﻤﻠﻑ ﺍﻹﻋـﺩﺍﺩﺍﺕ ﻴـﺘﻡ ﺘﻭﺯﻴﻌـﻪ ﻤـﻊ
ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻤﻤﺎ ﻴﺠﻌل ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻗﺎﺩﺭﻴﻥ ﻋﻠﻰ ﻗﺭﺍﺀﺘﻪ ﻭﺃﺨﺫ ﻜﻠﻤﺎﺕ ﺍﻟﻤﺭﻭﺭ ﻤﻨﻪ.
ﻭﻴﻤﻜﻨﻙ ﺘﺸﻔﻴﺭ ﻤﻘﻁﻊ ﻨﺼﻭﺹ ﺍﻻﺘﺼﺎل > <ConnectionStringsﻓﻲ ﻤﻠـﻑ ﺍﻹﻋـﺩﺍﺩﺍﺕ،
ﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺸﺭﺤﻨﺎﻫﺎ ﻓﻲ ﻜﺘﺎﺏ ﺒﺭﻤﺠـﺔ ﺍﻟﻭﻴﻨـﺩﻭﺯ ،ﻭﺍﺴـﺘﺨﺩﻤﻨﺎﻫﺎ ﻓـﻲ ﺍﻟﺒﺭﻨـﺎﻤﺞ
AddAppSettingsﺍﻟﻤﺭﻓﻕ ﺒﺫﻟﻙ ﺍﻟﻜﺘﺎﺏ.
٥٢
ﻭﺍﺠﻬﺔ ﺍﻻﺘﺼﺎل ﺒﻘﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ
IDbConnection Interface
ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ،IDisposableﻭﻫﻲ ﺘﺘﻴﺢ ﻟﻙ ﺇﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺍﺘﺼﺎل ﺨـﺎﺹ ﺒـﻙ،
ﻭﺫﻟﻙ ﺒﻜﺘﺎﺒﺔ ﻓﺌﺔ ﺘﻤﺜﻠﻬﺎ ..Implements the interfaceﻫﺫﺍ ﻴﺴﻬل ﻋﻠﻴﻙ ﻜﺘﺎﺒﺔ ﻤﺯﻭﺩ ﺠﺩﻴـﺩ
ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻨﻭﻉ ﻤﻌﻴﻥ ﻤﻥ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻏﻴﺭ ﻤﺘﺎﺡ ﻓﻲ ﺇﻁﺎﺭ ﺍﻟﻌﻤل.
ﻭﺘﻤﺘﻠﻙ ﺍﻟﻭﺍﺠﻬﺔ IDbConnectionﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
٥٣
ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ﻴﺠﻌل ﺍﺴﺘﺠﺎﺒﺘﻪ ﻟﻤﺤﺎﻭﻻﺕ ﺍﻻﺘﺼﺎل ﺒﻁﻴﺌﺔ ﺃﻭ ﻤﺘﺄﺨﺭﺓ ،ﻓﻀﻊ ﻗﻴﻤﺔ ﺃﻜﺒﺭ ﻓـﻲ
ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ )ﻤﺜل ٦٠ﺃﻭ ٩٠ﻤﺜﻼ(.
ﺍﻟﺤﺎﻟﺔ :State
ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ConnectionStateﺍﻟﺘﻲ ﺘﻌﺒﺭ ﻋﻥ ﺤﺎﻟﺔ ﺍﻻﺘﺼـﺎل ﻓـﻲ ﻫـﺫﻩ
ﺍﻟﻠﺤﻅﺔ ،ﻭﻫﻲ:
ﻓﺘﺢ :Open
ـﻴﺔ
ـﻲ ﺍﻟﺨﺎﺼـ
ـﻭﺩﺓ ﻓـ
ـﺎﺕ ﺍﻟﻤﻭﺠـ
ـﺎ ﻟﻠﺒﻴﺎﻨـ
ـﺎﺕ ،ﺘﺒﻌـ
ـﺩﺓ ﺍﻟﺒﻴﺎﻨـ
ـﺎل ﺒﻘﺎﻋـ
ـﺘﺢ ﺍﻻﺘﺼـ
ﺘﻔـ
.ConnectionString
٥٤
..Databaseﻻﺤﻅ ﺃﻥ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﺘﺎﺡ ﻓﻘﻁ ﺃﺜﻨﺎﺀ ﻓﺘﺢ ﺍﻻﺘﺼـﺎل ﺒﺎﻟﺨـﺎﺩﻡ،
ﻭﺇﻻ ﺤﺩﺙ ﺨﻁﺄ ﻴﺨﺒﺭﻙ ﺃﻥ ﺍﻻﺘﺼﺎل ﻤﻐﻠﻕ! ..ﺍﻟﺤﻜﻤﺔ ﻤﻥ ﻫﺫﺍ ،ﻫـﻭ ﺍﺴـﺘﻐﻼل ﻨﻔـﺱ
ﺍﻻﺘﺼﺎل ﺍﻟﻤﻔﺘﻭﺡ ﻤﻊ ﺍﻟﺨﺎﺩﻡ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺃﻜﺜﺭ ﻤﻥ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ،ﻟﺘﻭﻓﻴﺭ ﻭﻗـﺕ ﺇﻏـﻼﻕ
ﺍﻻﺘﺼﺎل ﻭﺇﻋﺎﺩﺓ ﻓﺘﺢ ﺍﺘﺼﺎل ﺠﺩﻴﺩ.
ﺇﻏﻼﻕ :Close
ﺘﻘﻭﻡ ﺒﺎﻟﺘﺭﺍﺠﻊ Rollbackﻋﻥ ﺃﻱ ﺘﻌﺎﻤﻼﺕ Transactionsﻟﻡ ﻴﺘﻡ ﺇﺤﺎﻟﺘﻬﺎ ﺇﻟﻰ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ..Committedﺜﻡ ﺘﻐﻠﻕ ﺍﻻﺘﺼﺎل.
٥٥
ﻻﺤﻅ ﺃﻨﻪ ﻓﻲ ﺤﺎﻟﺔ ﺘﻔﻌﻴل ﺨﺎﺼﻴﺔ ﺍﻟﻤﺴﺎﻫﻤﺔ ،Poolingﻓﺈﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﻴﺤﺎﻓﻅ ﻋﻠـﻰ
ﻋﺩﺩ ﻤﺤﺩﺩ ﻤﻥ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﻔﺘﻭﺤﺔ ﺒﻴﻨﻪ ﻭﺒﻴﻥ ﺒﺭﻨﺎﻤﺠﻙ ،ﻭﺫﻟﻙ ﻟﺘﻭﻓﻴﺭ ﻭﻗـﺕ ﺇﻏـﻼﻕ
ﻭﺇﻋﺎﺩﺓ ﻓﺘﺢ ﺍﻻﺘﺼﺎﻻﺕ ﺒﻴﻨﻬﻤﺎ ..ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻻ ﺘﻘـﻭﻡ ﺍﻟﻭﺴـﻴﻠﺔ Closeﺒـﺈﻏﻼﻕ
ﺍﻻﺘﺼﺎل ،ﺒل ﺘﺘﺭﻙ ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺤﺎ ،ﻭﺘﻀﻴﻔﻪ ﺇﻟـﻰ ﺭﺼـﻴﺩ ﺍﻻﺘﺼـﺎﻻﺕ ﺍﻟﻤﺴـﺎﻫﻤﺔ
،Connection Poolﻟﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻤﺒﺎﺸﺭﺓ ﻋﻨﺩ ﺍﻻﺤﺘﻴﺎﺝ ﺇﻟﻴﻪ.
٥٦
ﻓﺌﺔ ﺍﻻﺘﺼﺎل DbConnection Class
٥٧
ﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻗﺎﺌﻤﺔ ﺍﻟﺘﻌﺎﻤﻼﺕ :EnlistTransaction
ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺍﻟﺘﻌـﺎﻤﻼﺕ Transaction Objectﺍﻟـﺫﻱ ﺘﺭﻴـﺩ ﻀـﻡ
ﺘﻌﺎﻤﻼﺕ ﺍﻻﺘﺼﺎل ﺍﻟﺤﺎﻟﻲ ﺇﻟﻴﻪ ،ﻟﺘﻜﻭﻴﻥ ﺘﻌﺎﻤﻼﺕ ﻤﻨﺘﺸـﺭﺓ ،Distributed Transaction
ﻭﻫﻲ ﺘﻌﺎﻤﻼﺕ ﺘﻨﻔﺫ ﻋﻤﻠﻴﺎﺕ ﻋﻠﻰ ﺃﻜﺜﺭ ﻤﻥ ﻤﺼﺩﺭ ﻭﺃﻜﺜﺭ ﻤﻥ ﺍﺘﺼﺎل ،ﻭﻻ ﻴﻨﺠﺢ ﺘﻨﻔﻴﺫﻫﺎ
ﺇﻻ ﺇﺫﺍ ﻨﺠﺤﺕ ﻜل ﺃﺠﺯﺍﺌﻬﺎ ..ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻜﺎﺌﻥ ﺍﻟﺘﻌﺎﻤﻼﺕ ﻻﺤﻘﺎ.
٥٨
ﻭﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ :DbConnection
.SqlConnection .١
.OdbcConnection .٢
.OleDbConnection .٣
.OracleConnection .٤
ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻨﻬﺎ ﺠﻤﻴﻌﺎ ﺘﻤﺘﻠﻙ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﻫﺫﻩ ﺍﻟﻔﺌﺔ ..ﻭﺴﻨﺘﻌﺭﻑ ﺍﻵﻥ ﻋﻠﻰ ﻭﺍﺤﺩﺓ ﻤـﻥ
ﻫﺫﻩ ﺍﻟﻔﺌﺎﺕ ،ﻭﻫﻲ ﺍﻟﻔﺌﺔ .SqlConnection
٥٩
ﻓﺌﺔ ﺍﺘﺼﺎل ﺴﻴﻜﻴﻭل SqlConnection Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ،DbConnectionﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻨﻬﺎ ﻀﻤﻨﻴﺎ ﺘـﺭﺙ ﺍﻟﻔﺌـﺔ Component
ﻭﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ .IDbConnection
ﻭﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﻲ ﺘﺭﺜﻬﺎ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ،ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
٦١
ﺇﻟﻐﺎﺀ ﺍﻟﻤﺴﺎﻫﻤﺔ :ClearPool
ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل SqlConnection Objectﻹﻟﻐـﺎﺀ ﺍﻻﺘﺼـﺎل
ﺍﻟﺫﻱ ﻴﻤﺜﻠﻪ ﻤﻥ ﺭﺼﻴﺩ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺴـﺎﻫﻤﺔ ..Connection Poolﻻﺤـﻅ ﺃﻥ ﻫـﺫﺍ
ﺍﻻﺘﺼﺎل ﻗﺩ ﻴﻜﻭﻥ ﻤﺴﺘﺨﺩﻤﺎ ﻓﻲ ﺘﻠﻙ ﺍﻟﻠﺤﻅﺔ ﻷﺩﺍﺀ ﺒﻌﺽ ﺍﻻﺴﺘﻌﻼﻤﺎﺕ ،ﻟﻬﺫﺍ ﻴﺘﻡ ﺇﻨﻬـﺎﺅﻩ
ﻓﻲ ﺍﻟﺤﺎل ،ﻭﺴﻴﻅل ﻤﺴﺘﺨﺩﻤﺎ ﺇﻟﻰ ﺤﻴﻥ ﺇﻏﻼﻗﻪ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ،Closeﻭﻋﻨـﺩﻫﺎ ﻟـﻥ
ﻴﻌﻭﺩ ﺇﻟﻰ ﺭﺼﻴﺩ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺴﺎﻫﻤﺔ ،ﺒل ﺴﻴﻐﻠﻕ ﻓﻲ ﺍﻟﺤﺎل ..ﻤﺜﺎل:
;)SqlConnection.ClearPool(Cn
٦٣
ﻓﻲ ﺃﻨﻨﺎ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﺨﺎﺼﻴﺔ Connectionﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻷﻤﺭ ﻹﻏﻼﻕ ﺍﻻﺘﺼﺎل ﻓﻲ ﺤـﺩﺙ
ﺇﻏﻼﻕ ﺍﻟﻨﻤﻭﺫﺝ ﻜﺎﻟﺘﺎﻟﻲ:
;) (Cmd.Connection.Close
ﻻﺤﻅ ﺃﻥ ﺘﺭﻙ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺤﺎ ﻁﻭﺍل ﺍﻟﻭﻗﺕ ﻋﻤﻠﻲ ﻓﻘﻁ ﻓﻲ ﺤﺎﻟﺘﻨﺎ ﻫﺫﻩ ،ﻷﻨﻨﺎ ﻨﺘﻌﺎﻤـل
ﻫﻨﺎ ﻤﻊ ﺨﺎﺩﻡ ﻤﺤﻠﻲ ،ﻭﻫﻨﺎﻙ ﻨﺴﺨﺔ ﻭﺍﺤﺩﺓ ﻓﻘﻁ ﻤﻥ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺘﺘﻌﺎﻤل ﻤﻌﻪ ..ﻟﻜﻥ ﻓﻲ ﺍﻟﺒـﺭﺍﻤﺞ
ﺍﻟﻌﻤﻠﻴﺔ ﺍﻟﺘﻲ ﺘﺘﻌﺎﻤل ﻤﻊ ﺨﺎﺩﻡ ﺤﻘﻴﻘﻲ ،ﻗﺩ ﻴﺅﺩﻱ ﺘﺭﻙ ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺤـﺎ ﺇﻟـﻰ ﺘﻘﻠﻴـل ﻜﻔـﺎﺀﺓ
ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﺨﺎﺼﺔ ﺇﺫﺍ ﻜﺎﻥ ﻫﻨﺎﻙ ﻋﺩﺩ ﻜﺒﻴﺭ ﻤﻥ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻴﺘﻌﺎﻤﻠﻭﻥ ﻤﻊ ﺒﺭﻨﺎﻤﺠﻙ ﻓﻲ ﻨﻔـﺱ
ﺍﻟﻠﺤﻅﺔ.
ﺍﻓﺘﺭﺽ ﻤﺜﻼ ﺃﻨﻙ ﺘﺼﻤﻡ ﺒﺭﻨﺎﻤﺠﺎ ﻟﺸﺭﻜﺔ ﻴﻌﻤل ﺒﻬﺎ ٢٥٠ﻤﻭﻅﻔﺎ ،ﻭﺃﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﻤﺠﻬـﺯ
ﻻﺴﺘﻘﺒﺎل ١٠٠ﺍﺘﺼﺎل ﻓﻘﻁ ﻓﻲ ﻨﻔﺱ ﺍﻟﻠﺤﻅﺔ ..ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻟﻭ ﺠﻌﻠﺕ ﺒﺭﻨﺎﻤﺠﻙ ﻴﻔﺘﺢ ﻨﻔـﺱ
ﺍﻻﺘﺼﺎل ﺒﺼﻭﺭﺓ ﺩﺍﺌﻤﺔ ﻁﻭﺍل ﺘﺸﻐﻴل ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﻪ ،ﻓﺈﻥ ﺃﻭل ١٠٠ﻤﻭﻅﻑ ﻴﻔﺘﺤﻭﻥ ﺍﻟﺒﺭﻨـﺎﻤﺞ
ﻋﻠﻰ ﺃﺠﻬﺯﺘﻬﻡ ﺴﻴﻤﻨﻌﻭﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﻤﻥ ﺍﻻﺴﺘﺠﺎﺒﺔ ﻟﻤﺌﺔ ﻭﺨﻤﺴﻴﻥ ﻤﻭﻅﻔﺎ ﺁﺨـﺭﻴﻥ ﺇﻟـﻰ ﺃﻥ
ﻴﻐﻠﻕ ﺒﻌﺽ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﺍﻟﺒﺭﻨﺎﻤﺞ!
ﻟﻘﺩ ﺃﺤﺒﺒﺕ ﺃﻥ ﺃﺭﻴﻙ ﻜﻴﻑ ﻴﻤﻜﻥ ﺃﻥ ﻴﺅﺩﻱ ﺍﻟﺘﺼﻤﻴﻡ ﺍﻟﺨﺎﻁﺊ ﻟﺒﺭﻨﺎﻤﺠﻙ ﺇﻟـﻰ ﻨﺘـﺎﺌﺞ ﻜﺎﺭﺜﻴـﺔ،
ﻭﻴﻌﻁل ﺍﻟﻌﻤل ﻭﻻ ﺘﺠﻨﻲ ﻤﻥ ﻭﺭﺍﺌﻪ ﺴﻭﻯ ﺍﻟﺴﺨﻁ ! J
ﻭﺭﻏﻡ ﺃﻥ ﻫﺫﺍ ﻤﺜﺎل ﺍﻓﺘﺭﺍﻀﻲ ﻟﺘﻘﺭﻴﺏ ﺍﻟﻔﻜﺭﺓ ،ﺤﻴﺙ ﺇﻥ ﺴﻴﻜﻴﻭل ﺴﻴﺭﻓﺭ ﻴﺴﺘﻁﻴﻊ ﻓﻌﻠﻴﺎ ﺨﺩﻤـﺔ
ﺒﻀﻊ ﻤﺌﺎﺕ ﻤﻥ ﺍﻟﻌﻤﻼﺀ ﻭﺭﺒﻤﺎ ﺃﻜﺜﺭ ﻓﻲ ﻨﻔﺱ ﺍﻟﻭﻗﺕ ،ﺇﻻ ﺃﻥ ﻫﺫﺍ ﺍﻟﻌﺩﺩ ﻤﻬﻤﺎ ﺒﺩﺍ ﻜﺒﻴﺭﺍ ﻟﻙ ﻓﻬﻭ
ﻤﺤﺩﻭﺩ ،ﻭﻴﻤﻜﻥ ﺘﺠﺎﻭﺯﻩ ﻋﻤﻠﻴﺎ ﻓﻲ ﺍﻟﻤﺅﺴﺴﺎﺕ ﺍﻟﻀﺨﻤﺔ ﻜﺎﻟﺤﻜﻭﻤﺔ ﺍﻻﻟﻜﺘﺭﻭﻨﻴـﺔ ﻤـﺜﻼ )ﻫـل
ﺘﺘﺨﻴل ﻜﻡ ﻋﺩﺩ ﺍﻟﻤﻭﻅﻔﻴﻥ ﻓﻲ ﺍﻟﻭﺯﺍﺭﺍﺕ ﺍﻟﻤﺨﺘﻠﻔﺔ ﺍﻟﺫﻴﻥ ﻴﺘﻌﺎﻤﻠﻭﻥ ﻤﻊ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻨﻔـﺱ
ﺍﻟﻠﺤﻅﺔ؟( ،ﺃﻭ ﻓﻲ ﻤﻭﺍﻗﻊ ﺍﻹﻨﺘﺭﻨﺕ ﺍﻟﺘﻲ ﺘﺤﻔﻅ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺘﻬﺎ ﻋﻠﻰ ﺴﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ ،ﻭﺃﻨـﺕ
ﺘﻌﺭﻑ ﺃﻥ ﺒﻌﺽ ﻫﺫﻩ ﺍﻟﻤﻭﺍﻗﻊ ﻴﺼل ﺯﻭﺍﺭﻫﺎ ﺇﻟﻰ ﻋﺩﺓ ﻤﻼﻴﻴﻥ ﻴﻭﻤﻴﺎ ،ﻤﻤﺎ ﻴـﺅﺩﻱ ﺇﻟـﻰ ﺒـﻁﺀ
ﺍﺴﺘﺠﺎﺒﺔ ﺍﻟﺨﺎﺩﻡ ،ﻭﺍﻋﺘﺫﺍﺭﻩ ﻟﻠﻜﺜﻴﺭ ﻤﻥ ﺍﻟﻌﻤﻼﺀ ﻋﻥ ﻭﺠﻭﺩ ﻀﻐﻁ ﻜﺒﻴﺭ ﺒﺠﺒﺭﻫﻡ ﻋﻠﻰ ﺍﻻﻨﺘﻅـﺎﺭ
)ﻻ ﺭﻴﺏ ﺃﻨﻙ ﻭﺍﺠﻬﺕ ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ﻤﻊ ﺨﺎﺩﻡ ﺒﺭﻴﺩ ﻫﻭﺘﻤﻴل ﻓﻲ ﺒﻌﺽ ﺍﻷﻭﻗﺎﺕ(!
ﻭﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻨﺎ ،ﻋﻠﻴﻙ ﺃﻥ ﺘﻨﻘل ﺍﻟﻜﻭﺩ ﻤﻥ ﺤﺩﺙ ﺘﺤﻤﻴـل ﺍﻟﻨﻤـﻭﺫﺝ ﻭﺤـﺩﺙ
ﺇﻏﻼﻗﻪ ﺇﻟﻰ ﺤﺩﺙ ﻀﻐﻁ ﺍﻟﺯﺭ ،ﻟﻴﺘﻡ ﻓﺘﺢ ﺍﻻﺘﺼﺎل ﻭﺇﻏﻼﻗﻪ ﻓﻘﻁ ﻋﻨﺩ ﺍﻟﺤﺎﺠﺔ ..ﻭﻻ ﺘﻘﻠﻕ ﻤـﻥ
٦٤
ﻜﺜﺭﺓ ﻓﺘﺢ ﺒﺭﻨﺎﻤﺠﻙ ﻟﻼﺘﺼﺎل ﻭﺇﻏﻼﻗـﻪ ،ﻓﺨﺎﺼـﻴﺔ ﻤﺴـﺎﻫﻤﺔ ﺍﻻﺘﺼـﺎﻻﺕ Connection
Poolingﺍﻟﺘﻲ ﻴﺩﻋﻤﻬﺎ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﺘﻐﻨﻴﻙ ﻋﻥ ﺃﻱ ﻋﻨﺎﺀ ﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ،ﺤﻴﺙ ﻴﺘﻡ ﺘـﺭﻙ
ﺒﻌﺽ ﺍﻻﺘﺼﺎﻻﺕ ﻤﻔﺘﻭﺤﺔ ﻟﻀﻤﺎﻥ ﺴﺭﻋﺔ ﺍﻻﺴﺘﺠﺎﺒﺔ ﻟﻼﺴـﺘﻌﻼﻤﺎﺕ ﺍﻟﻤﺘﻜـﺭﺭﺓ ،ﺩﻭﻥ ﺇﻋﺎﻗـﺔ
ﺒﻌﺽ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻋﻥ ﺍﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ ..ﻭﻟﺤﺴﻥ ﺍﻟﺤﻅ ﻓـﺈﻥ ﺘﻘﻨﻴـﺔ ﺍﻟﻤﺴـﺎﻫﻤﺔ Pooling
ﺘﻜﻭﻥ ﻓﻌﺎﻟﺔ ﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ،ﻤﺎ ﻟﻡ ﺘﻁﻠﺏ ﺃﻨﺕ ﺇﻴﻘﺎﻓﻬﺎ ﺼﺭﺍﺤﺔ ﻋﺒﺭ ﻨـﺹ ﺍﻻﺘﺼـﺎل
ﻜﻤﺎ ﻋﺭﻓﻨﺎ ﺴﺎﺒﻘﺎ.
ﻭﺴﺘﺠﺩ ﻜﻭﺩ ﺍﻻﺘﺼﺎل ﺍﻟﻤﺤﺴﻥ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ﺍﻟﻤﺴﻤﻰ .AuthorBooks_Reader2
٦٥
ﻓﺌﺔ ﺨﻁﺄ ﺴﻴﻜﻴﻭل SqlError Class
ﺘﺤﺘﻭﻱ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻋﻠﻰ ﻤﻌﻠﻭﻤﺎﺕ ﻋﻥ ﺭﺴﺎﻟﺔ ﺍﻟﺨﻁﺄ )ﺃﻭ ﺍﻟﺘﺤﺫﻴﺭ( ﺍﻟﺘﻲ ﺃﺭﺴﻠﻬﺎ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل..
ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
ﺍﻟﺭﺘﺒﺔ :Class
ﺘﻌﻴﺩ ﺭﻗﻤﺎ ﻤﻥ ٠ﺇﻟﻰ ٢٥٥ﻴﻤﺜل ﺩﺭﺠﺔ ﺨﻁﻭﺭﺓ ﺍﻟﺨﻁﺄ ..ﻭﻗﻴﻤﺘﻬﺎ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ .٠
ﺍﻟﺨﺎﺩﻡ :Server
ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﻨﺴﺨﺔ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﺍﻟﺘﻲ ﺃﺭﺴﻠﺕ ﺍﻟﺨﻁﺄ.
ﺍﻟﻤﺼﺩﺭ :Source
ﺘﻌﻴﺩ ﺍﺴﻡ ﺍﻟﻤﺯﻭﺩ Providerﺍﻟﺫﻱ ﺘﺴﺒﺏ ﻓﻲ ﺍﻟﺨﻁﺄ.
ﺍﻹﺠﺭﺍﺀ :Procedure
ﺘﻌﻴﺩ ﺍﺴﻡ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ﺍﻟﺫﻱ ﺘﺴﺒﺏ ﻓﻲ ﺍﻟﺨﻁﺄ.
ﺍﻟﺭﺴﺎﻟﺔ :Message
ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﺼﻑ ﺭﺴﺎﻟﺔ ﺍﻟﺨﻁﺄ .. ..ﻭﻴﻤﻜﻨـﻙ ﺃﻴﻀـﺎ ﺍﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ ToString
ﺍﻟﺨﺎﺼﺔ ﺒﻬﺫﺍ ﺍﻟﻜﺎﺌﻥ ﻟﻌﺭﺽ ﻨﺹ ﻫﺫﻩ ﺍﻟﺭﺴﺎﻟﺔ.
ﺍﻟﺭﻗﻡ :Number
ﺘﻌﻴﺩ ﺭﻗﻡ ﺍﻟﺨﻁﺄ.
ﺍﻟﺤﺎﻟﺔ :State
ﺘﻌﻴﺩ ﺭﻗﻤﺎ ﻤﻥ ٠ﺇﻟﻰ ٢٥٥ﻴﻤﺜل ﺍﻟﻜﻭﺩ ﺍﻟﺭﻗﻤﻲ ﻟﻠﺨﻁﺄ.
٦٦
ﻤﻠﺤﻭﻅﺔ:
ﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻓﺌﺔ ﺍﺴﺘﺜﻨﺎﺀ ﺴﻴﻜﻭﻴل SqlExceptionﻤﻥ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﻤﻥ ﺍﻟﻔﺌﺔ
ﺍﻷﻡ SystemExceptionﻭﺍﻟﻔﺌﺔ ﺍﻷﻡ ،Exceptionﻓﺈﻨﻬﺎ ﺘﻤﺘﻠﻙ ﻨﻔـﺱ ﺨﺼـﺎﺌﺹ ﺍﻟﻔﺌـﺔ
،SqlErrorﻤﻤﺎ ﻴﻤﻨﺤﻙ ﺍﻟﻘﺩﺭﺓ ﻋﻠﻰ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻨﻔـﺱ ﺍﻟﻤﻌﻠﻭﻤـﺎﺕ ،ﺴـﻭﺍﺀ ﺍﺴـﺘﺨﺩﻤﺕ
ﺍﻟﺤﺩﺙ InfoMessageﺃﻡ ﺍﺴﺘﺨﺩﻤﺕ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻘﻠﻴﺩﻴـﺔ ﻟﻤﻌﺎﻟﺠـﺔ ﺍﻷﺨﻁـﺎﺀ Exception
.Handling
٦٧
-٧-
ﻜــﺎﺌـــــﻥ ﺍﻷﻤــــــﺭ
Command Object
ﻴﺘﻌﺎﻤل ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﻤﻊ ﺍﺴﺘﻌﻼﻡ SQLﺃﻭ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻥ ،Stored Procedureﻤﻊ ﺍﻤﺘﻼﻜـﻪ
ﺍﻟﻭﺴﺎﺌل ﺍﻟﻼﺯﻤﺔ ﻟﺘﻨﻔﻴﺫﻫﻤﺎ ﻋﺒﺭ ﺍﺘﺼﺎل ﻤﻔﺘﻭﺡ ،ﻭﺍﺴﺘﻼﻡ ﺍﻟﻨﺘﻴﺠﺔ ﻤﻥ ﺍﻟﺨﺎﺩﻡ.
ﻭﺴﻨﺘﻌﺭﻑ ﻓﻲ ﻫﺫﺍ ﺍﻟﻔﺼل ﻋﻠﻰ ﻜﻴﻔﻴﺔ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻜﺎﺌﻥ ﺍﻷﻤﺭ.
ﺍﻻﺘﺼﺎل :Connection
ﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺃﻱ ﻜـﺎﺌﻥ ﺍﺘﺼـﺎل ﻴﻤﺜـل ﺍﻟﻭﺍﺠﻬـﺔ ،IDbConnectionﻟﻴـﺘﻡ
ﺍﺴﺘﺨﺩﺍﻤﻪ ﻓﻲ ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ..ﻭﻴﺸﺘﺭﻁ ﻓﺘﺢ ﺍﻻﺘﺼﺎل ﺃﻭﻻ ﻗﺒل ﻤﺤﺎﻭﻟﺔ ﺘﻨﻔﻴـﺫ ﺍﻷﻤـﺭ ،ﻭﺇﻻ
ﺤﺩﺙ ﺨﻁﺄ.
٦٨
ﺍﻷﻤﺭ ﻴﺘﻜﻭﻥ ﻤﻥ ﺠﻤﻠﺔ ..SQLﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ. Text
StoredProcedureﺍﻷﻤﺭ ﻴﺘﻜﻭﻥ ﻤﻥ ﺍﺴﻡ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻥ ﻴﺭﺍﺩ ﺘﻨﻔﻴﺫﻩ.
ﺍﻷﻤﺭ ﻴﺘﻜﻭﻥ ﻤﻥ ﺍﺴﻡ ﺠﺩﻭل ﻴﺭﺍﺩ ﺘﺤﻤﻴل ﻜل ﺒﻴﺎﻨﺎﺘﻪ ﻜﺎﻤﻠﺔ ﻤﻥ TableDirect
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺒﺎﺸﺭﺓ ..ﻫﺫﻩ ﺍﻟﻘﻴﻤﺔ ﻏﻴﺭ ﻤﻘﺒﻭﻟﺔ ﻤﻊ ﻗﻭﺍﻋـﺩ
ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ،ﻟﻜﻥ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻤـﻊ ﻗﻭﺍﻋـﺩ
ﺒﻴﺎﻨﺎﺕ ﺁﻜﺴﻴﺱ.
ﺍﻟﻤﻌﺎﻤﻼﺕ :Parameters
ﺘﻌﻴـــﺩ ﺃﻱ ﻜـــﺎﺌﻥ ﻴﻤﺜـــل ﻭﺍﺠﻬـــﺔ "ﻤﺠﻤﻭﻋـــﺔ ﻤﻌـــﺎﻤﻼﺕ ﺍﻟﺒﻴﺎﻨـــﺎﺕ"
،IDataParameterCollectionﺍﻟﺘﻲ ﺘﺭﺙ ﻭﺍﺠﻬـﺔ ﺍﻟﻘﺎﺌﻤـﺔ ..IListﻭﺘﺘـﻴﺢ ﻟـﻙ
٦٩
ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺇﻀﺎﻓﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﻁﻠﻭﺏ ﺍﻟﺘﻌﻭﻴﺽ ﺒﻬﺎ ﻓﻲ ﺠﻤﻠﺔ ﺍﻻﺴـﺘﻌﻼﻡ ﺃﻭ
ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ..ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ.
ﺍﻟﺘﻌﺎﻤل :Transaction
ﺘﺴﺘﻘﺒل ﺃﻱ ﻜﺎﺌﻥ ﻤﻥ ﻨﻭﻉ ﻭﺍﺠﻬﺔ ﺘﻌﺎﻤﻼﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،IDbTransactionﻟﻴـﺘﻡ
ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﻨﻁﺎﻗﻪ.
ﺘﺠﻬﻴﺯ :Prepare
ﺘﻨﺸﺊ ﻨﺴﺨﺔ ﻤﺤﺴﻨﺔ ﻤﺠﻬﺯﺓ ﻤﻥ ﺍﻷﻤﺭ ﻭﺘﺤﻔﻅﻬﺎ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ،ﻟﻴﻜﻭﻥ ﺘﻨﻔﻴـﺫﻫﺎ ﺃﺴـﺭﻉ..
ﻭﻻ ﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻼﺕ ،ﻭﻟﻴﺱ ﻟﻬﺎ ﻗﻴﻤﺔ ﻋﺎﺌﺩﺓ ،ﻭﻻ ﻴﻜﻭﻥ ﻟﻬـﺎ ﺃﻱ ﺘـﺄﺜﻴﺭ ﺇﻥ
ﻜﺎﻨﺕ ﻟﻠﺨﺎﺼﻴﺔ CommandTypeﺍﻟﻘﻴﻤﺔ .TableDirect
ﻻﺤﻅ ﺃﻥ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺼﺎﺭ ﻋﺩﻴﻡ ﺍﻟﻘﻴﻤـﺔ ﺘﻘﺭﻴﺒـﺎ ،ﻷﻥ ﺇﺼـﺩﺍﺭﺍﺕ ﺴـﻴﻜﻭﻴل
ﺴﻴﺭﻓﺭ ٢٠٠٠ﻭ ٢٠٠٥ﻭ ٢٠٠٨ﺘﻘﻭﻡ ﺒﺘﺠﻬﻴﺯ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﻠﻘﺎﺌﻴﺎ ﻋﻨﺩ ﺍﻟﻠـﺯﻭﻡ ،ﻟﺘﺤﺴـﻴﻥ
ﺍﻷﺩﺍﺀ.
٧١
ﻻﺤﻅ ﺃﻥ ﻜﻭﺩ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺘﻨﻔﻴﺫ ﺍﻷﻭﺍﻤﺭ ﻴﺘﻜﺭﺭ ﻜﺜﻴﺭﺍ ،ﻭﻫﻭ ﻴﺒﺩﻭ ﻁـﻭﻴﻼ
ﻤﻊ ﻭﺠﻭﺩ ﺘﻌﺩﻴﻼﺕ ﻁﻔﻴﻔﺔ ..ﻟﻬﺫﺍ ﺴﻴﻜﻭﻥ ﻤﻥ ﺍﻷﺫﻜﻰ ﻟﻭ ﺠﻤﻌﻨﺎ ﺍﻟﻜﻭﺩ ﺍﻟﻤﺘﺸﺎﺒﻪ ﻓﻲ ﺇﺠﺭﺍﺀ
ﻭﺃﺭﺴﻠﻨﺎ ﺇﻟﻴﻪ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﻲ ﺘﻨﺎﺴﺏ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺫﻱ ﻨﻨﻔﺫﻩ ..ﻭﻟﻘﺩ ﻓﻌﻠﻨﺎ ﻫﺫﺍ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ
،DbTasksﺤﻴﺙ ﻋﺭﻓﻨﺎ ﻓﻴﻪ ﻓﺌﺔ ﺍﺴﻤﻬﺎ ،MyDbConnectorﻭﺃﻀﻔﻨﺎ ﺇﻟﻴﻬﺎ ﺨﺎﺼـﻴﺔ
ﺍﺴﻤﻬﺎ ConnectionStrﺘﺴﺘﻘﺒل ﻨﺹ ﺍﻻﺘﺼﺎل ،ﻭﺤﺩﺙ ﺇﻨﺸﺎﺀ Constuctorﻴﺴـﺘﻘﺒل
ﻨﺹ ﺍﻻﺘﺼﺎل ﺃﻴﻀﺎ ﻭﻴﻀﻌﻪ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻋﻠﻰ ﺴﺒﻴل ﺍﻻﺨﺘﺼﺎﺭ ..ﻭﻗﺩ ﻋﺭﻓﻨﺎ ﻓـﻲ
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻋﺩﺩﺍ ﻤﻥ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﻲ ﺘﺘﻴﺢ ﻟﻨﺎ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻤﻥ ﺒﻴﻨﻬـﺎ ﺩﺍﻟـﺔ
ﺍﺴﻤﻬﺎ ..ExcuteCommandﻫﺫﻩ ﺍﻟﺩﺍﻟﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ:
-ﻨﺹ ﺍﻷﻤﺭ CommandTextﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺘﻨﻔﻴﺫﻩ ..ﻭﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺭﺴل ﺇﻟـﻰ ﻫـﺫﺍ
ﺍﻟﻤﻌﺎﻤل ﺍﺴﺘﻌﻼﻡ SQLﺃﻭ ﺍﺴﻡ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻥ ،ﺤﻴﺙ ﺴﻨﻘﻭﻡ ﺒﺎﺴﺘﻨﺘﺎﺝ ﻨﻭﻉ ﺍﻷﻤﺭ
ﺒﺤﻴﻠﺔ ﺼﻐﻴﺭﺓ ،ﻓﻠﻭ ﻜﺎﻥ ﺍﻟﻨﺹ ﻴﺒﺩﺃ ﺒﺄﻱ ﻤـﻥ ﺃﻭﺍﻤـﺭ SQLﻤﺜـل " "insertﺃﻭ
" ..."updateﺇﻟﺦ ،ﻓﻤﻌﻨﻰ ﻫﺫﺍ ﺃﻨـﻪ ﻨـﻭﻉ ﺍﻷﻤـﺭ ..CommandType.Text
ﻻﺤﻅ ﺃﻨﻨﺎ ﻭﻀﻌﻨﺎ ﻤﺴﺎﻓﺔ ﺒﻌﺩ ﺍﺴﻡ ﺍﻟﻜﻠﻤﺔ ،ﺘﻼﻓﻴﺎ ﻻﺤﺘﻤﺎل ﺃﻥ ﺘﻜﻭﻥ ﻫـﺫﻩ ﺍﻟﻜﻠﻤـﺔ
ﺠﺯﺀﺍ ﻤﻥ ﺍﺴﻡ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻥ )ﻤﺜل ،(UpdateAuthorsﻓﻨﺤﻥ ﻭﺍﺜﻘﻭﻥ ﺃﻥ ﺍﺴـﻡ
ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ﻻ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻤﺴﺎﻓﺎﺕ ،ﺒﻴﻨﻤﺎ ﺍﻻﺴـﺘﻌﻼﻤﺎﺕ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ
ﻤﺴــــﺎﻓﺎﺕ ..ﻏﻴــــﺭ ﻫــــﺫﺍ ﻴﻜــــﻭﻥ ﻨــــﻭﻉ ﺍﻷﻤــــﺭ
.CommandType.StroresProcedure
-ﻤﺼﻔﻭﻓﺔ ﻨﺼﻴﺔ ﺜﻨﺎﺌﻴﺔ ﺍﻟﺒﻌﺩ ) ،String(,ﻴﺘﻜﻭﻥ ﻜل ﻋﻨﺼﺭ ﻓﻴﻬﺎ ﻤﻥ ﺍﺴﻡ ﺍﻟﻤﻌﺎﻤل
ﻭﻗﻴﻤﺘﻪ ..ﻭﺒﻬﺫﺍ ﻴﻤﻜﻨﻙ ﺘﻤﺭﻴﺭ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻤﺒﺎﺸﺭﺓ ﺇﻟﻰ ﺍﻟﺩﺍﻟﺔ ،ﺤﻴﺙ ﺴﻨﻀﻴﻑ ﻫـﺫﻩ
ﺍﻟﻤﻌﺎﻤﻼﺕ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﻤﻌﺎﻤﻼﺕ ﺍﻷﻤﺭ ..Command.Parametersﻭﺇﺫﺍ ﻟﻡ
ﻴﻜﻥ ﻟﻸﻤﺭ ﻤﻌﺎﻤﻼﺕ ،ﻓﺄﺭﺴل ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ﻟﻠﺩﺍﻟﺔ .null
ﻭﺘﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﺩﺍﻟﺔ ﺍﻟﻭﺴﻴﻠﺔ Command.ExecuteNonQueryﻟﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ،ﻭﺘﻌﻴـﺩ
trueﺇﺫﺍ ﻟﻡ ﻴﺤﺩﺙ ﺨﻁﺄ ،ﻭﺘﻌﻴﺩ falseﺇﺫﺍ ﺤﺩﺙ ﺨﻁﺄ.
ﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﺩﺍﻟﺔ ﻓﻲ ﻨﻔﺱ ﺍﻟﻤﺸﺭﻭﻉ ،ﺤﻴﺙ ﻭﻀﻌﻨﺎ ﻤﺭﺒﻌـﻲ ﻨـﺹ
ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ﻻﺴﺘﻘﺒﺎل ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ﻭﻨﺒﺫﺓ ﻋﻨﻪ ،ﻭﻋﻨﺩ ﻀﻐﻁ ﺍﻟﺯﺭ ﻴﺘﻡ ﺘﻨﻔﻴـﺫ ﺍﺴـﺘﻌﻼﻡ
٧٢
ﻹﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ..ﺍﻨﻅﺭ ﻜﻴﻑ ﺴﻴﻜﻭﻥ ﺍﻟﻜﻭﺩ ﻓﻲ ﻤﻨﺘﻬﻰ ﺍﻟﺒﺴﺎﻁﺔ ﻭﺍﻻﺨﺘﺼـﺎﺭ
ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻔﺌﺔ :MyDbConnector
(var DbBooks = new MyDbConnector
;)Properties.Settings.Default.BooksConStr
var SQL = @"INSERT INTO Authors
)(Author, CountryID, About
;")VALUES (@Author, 21, @About
var Params = new[,] { { "@Author", TxtAuthor.Text },
;} } { "@About", TxtAbout.Text
))if (DbBooks.ExcuteCommand(SQL, Params
{
;) (TxtAuthor.Clear
;) (TxtAbout.Clear
}
ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻔﺌﺔ MyDbConnectorﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺃﻱ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ،ﻭﺘﻨﻔﻴـﺫ ﺃﻱ
ﺃﻤﺭ ﻋﻠﻴﻬﺎ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻹﺠﺭﺍﺀ ..ExcuteCommandﺃﻟﻴﺱ ﻫﺫﺍ ﺸﻴﺌﺎ ﻤﺭﻴﺤﺎ؟
ﺍﻟﺴﻠﻭﻙ ﺍﻟﻌﺎﺩﻱ ،ﺤﻴﺙ ﻴﻤﻜﻥ ﺃﻥ ﻴﺅﺩﻱ ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ﺇﻟـﻰ ﺍﻟﺤﺼـﻭل Default
ﻋﻠﻰ ﺃﻜﺜﺭ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﻤﻥ ﻤﺠﻤﻭﻋﺎﺕ ﺍﻟﻨﺘﺎﺌﺞ ) Result Setsﻜﻤﺎ
ﻴﺤﺩﺙ ﻓﻲ ﺤﺎﻟﺔ ﺘﻨﻔﻴﺫ ﺃﻜﺜﺭ ﻤﻥ ﺠﻤﻠﺔ SQLﻤـﻥ ﺩﺍﺨـل ﺇﺠـﺭﺍﺀ
ﻤﺨﺯﻥ( ..ﻫﺫﺍ ﻤﻜﺎﻓﺊ ﻻﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ExecuteReaderﺒـﺩﻭﻥ
ﻤﻌﺎﻤﻼﺕ.
٧٣
ﻴﺅﺩﻱ ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ ﺇﻟﻰ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﻨﺘـﺎﺌﺞ ﻭﺍﺤـﺩﺓ SingleResult
ﻓﻘﻁ.
SchemaOnlyﻴﺅﺩﻱ ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ ﺇﻟﻰ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻷﻋﻤﺩﺓ ﻓﻘـﻁ
ﺒﺩﻭﻥ ﺃﻱ ﺴﺠﻼﺕ ..ﻫﺫﺍ ﻴﻌﻨﻲ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﺠـﺩﻭل ﻓـﺎﺭﻍ ﺒـﻪ
ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﻓﻘﻁ ..ﻫﺫﺍ ﻤﻤﺎﺜل ﻻﺴﺘﺨﺩﺍﻡ ﻤﺯﻭﺩ ﺴﻴﻜﻭﻴل ﻟﻠﺨﻴﺎﺭ:
SET FMTONLY ON
ﻴﺅﺩﻱ ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ ﺇﻟﻰ ﺍﻟﺤﺼـﻭل ﻋﻠـﻰ ﻤﻌﻠﻭﻤـﺎﺕ ﺍﻷﻋﻤـﺩﺓ KeyInfo
ﻭﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ .Primary Key
ﻴﺅﺩﻱ ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ ﺇﻟﻰ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﺴﺠل ﻭﺍﺤﺩ ﻓﻘـﻁ ،ﻭﻟـﻭ SingleRow
ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺃﻜﺜﺭ ﻤﻥ ﻗﻴﻤﺔ ﻤﻥ ﻫﺫﻩ ﺍﻟﻘﻴﻡ ،ﺒﺭﺒﻁﻬﺎ ﻤﻌﺎ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﻌﺎﻤل |.
ﻭﻗﺩ ﺃﻀﻔﻨﺎ ﻭﺴﻴﻠﺔ ﺍﺴﻤﻬﺎ GetReaderﺇﻟﻰ ﺍﻟﻔﺌﺔ MyDbConnectorﻓﻲ ﺍﻟﻤﺸـﺭﻭﻉ
،DbTasksﻤﻬﻤﺘﻬﺎ ﺘﻨﻔﻴﺫ ﺍﺴﺘﻌﻼﻡ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ExecuteReaderﻭﺇﻋﺎﺩﺓ ﻗـﺎﺭﺉ
ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻻﺤﻅ ﺃﻨﻙ ﻟﻭ ﺃﻏﻠﻘﺕ ﺍﻻﺘﺼﺎل ﻓﻲ ﻨﻬﺎﻴﺔ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﺴـﻴﺤﺩﺙ ﺨﻁـﺄ ﻋﻨـﺩ
ﻤﺤﺎﻭﻟﺘﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺃﻋﺎﺩﺘﻪ ﺇﻟﻴﻙ ،ﻷﻥ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﻴﺴﺘﺨﺩﻤﻪ ﻗﺩ ﺘـﻡ
ﺇﻏﻼﻗــﻪ ..ﻟﻬــﺫﺍ ﻋﻠﻴــﻙ ﻋــﺩﻡ ﺇﻏــﻼﻕ ﺍﻻﺘﺼــﺎل ،ﻭﺇﺭﺴــﺎل ﺍﻟﻘﻴﻤــﺔ
CommandBehavior.CloseConnectionﺇﻟــــﻰ ﻤﻌﺎﻤــــل ﺍﻟﻭﺴــــﻴﻠﺔ
ExecuteReaderﻟﺠﻌل ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﻐﻠﻕ ﺍﻻﺘﺼﺎل ﺒﻨﻔﺴﻪ ﻋﻨﺩﻤﺎ ﻴﺘﻡ ﺇﻏﻼﻗﻪ ..ﻭﻗـﺩ
ﺠﻌﻠﻨﺎ ﻟﻠﻭﺴﻴﻠﺔ GetReaderﻤﻌﺎﻤﻼ ﺍﺨﺘﻴﺎﺭﻴـﺎ ﺍﺴـﻤﻪ Sequentialﺇﺫﺍ ﺠﻌﻠﺘـﻪ true
ﻓﺴﺘﺤﺼل ﻋﻠﻰ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ﺘﺘﺎﺒﻌﻲ ﻻﺴﺘﺨﺩﺍﻤﻪ ﻓﻲ ﻗـﺭﺍﺀﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﻀـﺨﻤﺔ ﻋﻠـﻰ
ﺃﺠﺯﺍﺀ ،ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻫﻲ falseﻟﺘﺤﺼل ﻋﻠﻰ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ﻋﺎﺩﻱ.
ﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻻﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﻨﻔﺱ ﺍﻟﻤﺸﺭﻭﻉ ﻓﻲ ﺯﺭ "ﺍﻟﻜﺘـﺏ" ..ﻫـﺫﺍ ﺍﻟـﺯﺭ
ﻴﻌﺭﺽ ﻜﺘﺏ ﺍﻟﻤﺅﻟﻑ ﺍﻟﺫﻱ ﻜﺘﺒﺕ ﺍﺴﻤﻪ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺍﻟﻌﻠﻭﻱ.
٧٥
ﺘﻨﻔﻴﺫ ﻗﻴﻤﺔ :ExecuteScalar
ﺘﻨﻔﺫ ﺍﻷﻤﺭ ،ﻭﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ Objectﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﻴﻤﺔ ﺍﻟﺨﺎﻨﺔ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺼـﻑ ﺍﻷﻭل
ﻤﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻷﻭل ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﻨﺎﺘﺞ ،ﻭﺘﺘﺠﺎﻫل ﺒﺎﻗﻲ ﺍﻟﺨﺎﻨﺎﺕ.
ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻟﺘﻨﻔﻴﺫ ﺩﻭﺍل ﺍﻟﺘﺠﻤﻴﻊ .Aggregate Functions
ﻭﺍﻟﻤﺸﺭﻭﻉ AvgPriceﻴﺭﻴﻙ ﻤﺜﺎﻻ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻟﻤﻌﺭﻓﺔ ﻤﺘﻭﺴﻁ ﺃﺴـﻌﺎﺭ
ﺍﻟﻜﺘﺏ.
ﻭﻗﺩ ﺃﻀﻔﻨﺎ ﻭﺴﻴﻠﺔ ﺍﺴﻤﻬﺎ GetValueﺇﻟﻰ ﺍﻟﻔﺌﺔ MyDbConnectorﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ
،DbTasksﻤﻬﻤﺘﻬﺎ ﺘﻨﻔﻴﺫ ﺍﺴﺘﻌﻼﻡ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ExecuteScalarﻭﺇﻋﺎﺩﺓ ﺍﻟﻘﻴﻤـﺔ
ﺍﻟﻨﺎﺘﺠﺔ ،ﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻻﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﻨﻔﺱ ﺍﻟﻤﺸﺭﻭﻉ ﻓﻲ ﺍﻟﺯﺭ "ﻤﻌﺭﻓﺔ ﻋﺩﺩ ﺍﻟﻤﺅﻟﻔﻴﻥ".
ﺇﻟﻐﺎﺀ :Cancel
ﺘﺤﺎﻭل ﺇﻟﻐﺎﺀ ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ..ﻭﻻ ﻴﺤﺩﺙ ﺨﻁﺄ ﺇﻥ ﻟﻡ ﻴﻜﻥ ﺍﻷﻤﺭ ﻗﻴﺩ ﺍﻟﺘﻨﻔﻴـﺫ ﺤﺎﻟﻴـﺎ ،ﺃﻭ ﺇﻥ
ﻓﺸﻠﺕ ﻓﻲ ﺇﻴﻘﺎﻑ ﺘﻨﻔﻴﺫﻩ.
٧٦
ﻓﺌﺔ ﺃﻤﺭ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ DbCommand Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺴﺎﺴﻴﺔ ﻤﺠﺭﺩﺓ ،Abstract Base Classﻭﻫﻲ ﺘﺭﺙ ﻓﺌﺔ ﺍﻟﻤﻜﻭﻥ Component
،Classﻜﻤﺎ ﺃﻨﻬﺎ ﺘﻤﺜـل ﺍﻟﻭﺍﺠﻬـﺔ IDbCommandﻭﺒﺎﻟﺘـﺎﻟﻲ ﺘﻤﺘﻠـﻙ ﺠﻤﻴـﻊ ﻭﺴـﺎﺌﻠﻬﺎ
ﻭﺨﺼﺎﺌﺼﻬﺎ.
ﻭﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﺨﺼﺎﺌﺹ ﺍﻟﻭﺍﺠﻬﺔ ،IDbCommandﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺎﺼﻴﺔ
ﺍﻟﺘﺎﻟﻴﺔ:
ﻭﻻ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﻱ ﻭﺴﺎﺌل ﺠﺩﻴﺩﺓ ﻏﻴﺭ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﻭﺴﺎﺌل ﺍﻟﻭﺍﺠﻬﺔ .IDbCommand
ﻻﺤﻅ ﺃﻥ ﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ :DbCommand
.OdbcCommand Class .١
.OleDbCommand Class .٢
.SqlCommand Class .٣
.OracleCommand Class .٤
ﻭﺴﻨﻜﺘﻔﻲ ﻫﻨﺎ ﺒﺎﻟﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ .SqlCommand
٧٧
ﻓﺌﺔ ﺃﻤﺭ ﺴﻴﻜﻭﻴل SqlCommand Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ،DbCommandﻭﻫﻲ ﻤﺨﺼﺼﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺍﻷﻭﺍﻤﺭ ﺍﻟﺘﻲ ﻴﺘﻡ ﺘﻨﻔﻴـﺫﻫﺎ
ﻋﻠﻰ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺭﺒﻊ ﺼﻴﻎ ﻤﺨﺘﻠﻔﺔ:
.١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ.
.٢ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺎ ﻤﻌﺎﻤل ﻭﺍﺤﺩ ،ﻴﺴﺘﻘﺒل ﻨﺹ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺫﻱ ﺴﻴﻭﻀﻊ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ
.CommandText
.٣ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ ﻤﻥ ﺍﻟﻨﻭﻉ ،SqlConnection
ﻴﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ﻤﻥ ﺨﻼﻟﻪ.
.٤ﻭﺍﻟﺼﻴﻐﺔ ﺍﻷﺨﻴﺭﺓ ﺘﺯﻴـﺩ ﻋﻠـﻰ ﺍﻟﺼـﻴﻐﺔ ﺍﻟﺴـﺎﺒﻘﺔ ﺒﻤﻌﺎﻤـل ﺜﺎﻟـﺙ ﻤـﻥ ﺍﻟﻨـﻭﻉ
،SqlTransactionﻴﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﺘﻌﺎﻤل ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ﻓﻲ ﻨﻁﺎﻗﻪ.
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ،ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
ﺍﻟﺘﻨﺒﻴﻪ :Notification
ﺘﺤﺩﺩ ﻜﺎﺌﻥ ﻁﻠﺏ ﺍﻟﺘﻨﺒﻴﻪ SqlNotificationRequestﺍﻟﺫﻱ ﺴﻴﺴﺘﺨﺩﻤﻪ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﻓﻲ
ﺘﻠﻘﻲ ﺍﻟﺘﻨﺒﻴﻬـﺎﺕ ﻤـﻥ ﺍﻟﺨـﺎﺩﻡ ﻋﻨـﺩ ﺘﻨﻔﻴـﺫ ﺍﻻﺴـﺘﻌﻼﻡ ..ﻭﺴـﻨﺘﻌﺭﻑ ﻋﻠـﻰ ﺍﻟﻔﺌـﺔ
SqlNotificationRequestﻻﺤﻘﺎ.
٧٨
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ،ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ:
ﻨﺴﺦ :Clone
ﺘﻌﻴﺩ ﻜﺎﺌﻥ SqlCommandﺠﺩﻴﺩﺍ ﻤﻤﺎﺜﻼ ﻓﻲ ﻜل ﺸﻲﺀ ﻟﻠﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ.
٨٠
ﺘﻤﺭﻴﺭ ﺍﻟﻘﻴﻡ ﺇﻟﻰ ﺠﻤل ﺍﻻﺴﺘﻌﻼﻡ:
ﺍﻓﺘﺭﺽ ﺃﻨﻙ ﺘﺭﻴﺩ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻜﺘﺏ "ﺘﻭﻓﻴﻕ ﺍﻟﺤﻜﻴﻡ" ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻓﻲ ﻫـﺫﻩ ﺍﻟﺤﺎﻟـﺔ
ﻴﻤﻜﻨﻙ ﻭﻀﻊ ﺠﻤﻠﺔ SQLﺍﻟﺘﺎﻟﻴﺔ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ CommandTextﻟﻜﺎﺌﻥ ﺍﻷﻤﺭ )ﻭﻟﻴﻜﻥ ﺍﺴﻤﻪ
:(Cmd
Cmd.CommandText = @"SELECT Books.Book
FROM Authors, Books
WHERE Authors.ID = AuthorID
;"'ﺘﻭﻓﻴﻕ ﺍﻟﺤﻜﻴﻡ' = AND Authors.Author
ﻭﻟﻜﻥ ،ﻫل ﺘﻅﻥ ﺃﻨﻙ ﺴﺘﻜﺘﺏ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺴﺎﺒﻘﺔ ﻓﻲ ﺃﻱ ﺘﻁﺒﻴﻕ ﻋﻤﻠﻲ ﻓﻌـﻼ؟ ..ﻫـل ﺴﺘﻘﺘﺼـﺭ
ﻭﻅﻴﻔﺔ ﺒﺭﻨﺎﻤﺠﻙ ﻋﻠﻰ ﻋﺭﺽ ﻜﺘﺏ ﻤﺅﻟﻑ ﻭﺍﺤﺩ ﻓﻘﻁ ،ﺃﻡ ﺃﻨﻙ ﺴﺘﺴـﻤﺢ ﻟﻠﻤﺴـﺘﺨﺩﻡ ﺒﺎﺨﺘﻴـﺎﺭ
ﺍﻟﻤﺅﻟﻑ ﺍﻟﺫﻱ ﻴﺭﺩﻩ ﻟﻴﻌﺭﺽ ﻟﻪ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻜﺘﺏ ﻫﺫﺍ ﺍﻟﻤﺅﻟﻑ؟
ﺍﻟﻤﻨﻁﻘﻲ ﻭﺍﻟﻌﻤﻠﻲ ،ﻫﻭ ﺃﻥ ﺘﻀﻊ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ﻤﺭﺒﻊ ﻨـﺹ )ﻭﻟـﻴﻜﻥ ﺍﺴـﻤﻪ (TxtAuthor
ﻟﻴﻜﺘﺏ ﻓﻴﻪ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ،ﻭﻤﻥ ﺜﻡ ﺘﻌﺭﺽ ﻟﻪ ﻜﺘﺒﻪ ..ﻓﻲ ﻤﺜل ﻫـﺫﻩ ﺍﻟﺤﺎﻟـﺔ ،ﻋﻠﻴـﻙ
ﺘﻌﺩﻴل ﻨﺹ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺴﺎﺒﻕ ﻟﻴﺼﻴﺭ ﻜﺎﻟﺘﺎﻟﻲ:
Cmd.CommandText = @"SELECT Books.Book
FROM Authors, Books
WHERE Authors.ID = AuthorID
;"'"AND Authors.Author = '" + TxtAuthor.Text +
ﺤﻴﺙ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻁﺭﻴﻘﺔ ﺘﺸﺒﻴﻙ ﺍﻟﻨﺼﻭﺹ ﻓﻲ ﺍﻟﻜﻭﺩ ،ﻹﻀﺎﻓﺔ ﺍﻟﻨﺹ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ
ﺇﻟﻰ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ،ﻭﺒﻬﺫﺍ ﺤﺼﻠﻨﺎ ﻋﻠﻰ ﺠﻤﻠﺔ ﺍﺴﺘﻌﻼﻡ ﻤﺭﻨﺔ ،ﺘﺴﺘﻁﻴﻊ ﺍﻟﺒﺤﺙ ﻋـﻥ ﺍﺴـﻡ ﺃﻱ
ﻤﺅﻟﻑ ﻴﺭﻴﺩﻩ ﺍﻟﻤﺴﺘﺨﺩﻡ.
ﻟﻜﻥ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺜﻐﺭﺓ ﻗﺎﺘﻠﺔ ،ﺘﺴﻤﺢ ﻟﻸﺸﻘﻴﺎﺀ ﺒﺘﺩﻤﻴﺭ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺘﻙ ﻭﺭﺒﻤﺎ ﻨﻅـﺎﻡ
ﺍﻟﺘﺸﻐﻴل ﺍﻟﺫﻱ ﻴﻭﺠﺩ ﻋﻠﻴﻪ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﻟﻭ ﺃﺭﺍﺩﻭﺍ!
ﻜﻴﻑ؟ ..ﻫﺫﺍ ﻫﻭ ﻤﻭﻀﻭﻉ ﺍﻟﻔﻘﺭﺓ ﺍﻟﺘﺎﻟﻴﺔ.
٨١
ﺩﺱ ﺍﻻﺴﺘﻌﻼﻤﺎﺕ :SQL Injection
ﻓﻲ ﺍﻟﻤﺜﺎل ﺍﻟﺴﺎﺒﻕ ،ﺴﻤﺤﻨﺎ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺒﻜﺘﺎﺒﺔ ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ﻓﻲ ﻤﺭﺒﻊ ﻨﺹ ،ﺜﻡ ﺃﺩﺭﺠﻨﺎ ﻤﺤﺘـﻭﻯ
ﺍﻟﻨﺹ ﺩﺍﺨل ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﻜﺠﺯﺀ ﻤﻥ ﺸﺭﻁ ﺍﻟﻔﻘﺭﺓ ..WHEREﻭﻗﺩ ﺘﻔﺘﻕ ﺫﻫـﻥ ﺒﻌـﺽ
ﺍﻟﻌﺒﺎﻗﺭﺓ ﻋﻥ ﻓﻜﺭﺓ ﺸﺭﻴﺭﺓ ،ﻭﻫﻲ ﻜﺘﺎﺒﺔ ﺒﻌﺽ ﺠﻤل ﺍﻻﺴﺘﻌﻼﻡ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺒﺩﻻ ﻤﻥ ﺍﺴـﻡ
ﺍﻟﻤﺅﻟﻑ ،ﻭﺒﻬﺫﺍ ﻴﺴﺘﻁﻴﻌﻭﻥ ﺤﻘﻥ ﺍﺴﺘﻌﻼﻤﺎﺕ ﻤﺩﺴﻭﺴﺔ ﺨﺎﺼﺔ ﺒﻬﻡ ﺩﺍﺨـل ﺠﻤﻠـﺔ ﺍﻻﺴـﺘﻌﻼﻡ
ﺍﻟﺨﺎﺼﺔ ﺒﻙ ،ﻓﻴﻘﻭﻡ ﺒﺭﻨﺎﻤﺠﻙ ﺒﺘﻨﻔﻴﺫ ﻤﺎ ﻴﺭﻴﺩﻭﻥ ﻋﻠﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻫﻭ ﺃﻤﺭ ﻴﺸـﺒﻪ ﺴـﻠﻭﻙ
ﺍﻟﻔﻴﺭﻭﺴﺎﺕ ﺍﻟﺘﻲ ﺘﺤﻘﻥ ﻤﺎﺩﺘﻬﺎ ﺍﻟﻭﺭﺍﺜﻴﺔ ﻓﻲ ﻨﻭﺍﺓ ﺍﻟﺨﻠﻴﺔ ﻭﺘﺘﺭﻜﻬﺎ ﺘﻨﻔـﺫﻫﺎ ﻹﻨﺘـﺎﺝ ﻓﻴﺭﻭﺴـﺎﺕ
ﺠﺩﻴﺩﺓ!
ﻭﺨﻁﻭﺭﺓ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ،ﻫﻲ ﺃﻨﻬﺎ ﺘﺘﻴﺢ ﻟﻠﻤﺨﺘﺭﻗﻴﻥ ﺍﻻﺴﺘﻌﻼﻡ ﻋﻥ ﺒﻌﺽ ﺤﺴـﺎﺒﺎﺕ ﺍﻟﻤـﺩﻴﺭﻴﻥ
ﻭﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ،ﻭﻤﻌﺭﻓﺔ ﺘﺭﻜﻴﺏ ﺍﻟﺠﺩﺍﻭل ،ﺒل ﻭﺘﻨﻔﻴﺫ ﺒﻌﺽ ﺃﻭﺍﻤﺭ ﻏﻼﻑ ﺍﻟﻭﻴﻨﺩﻭﺯ Shellﻤﻥ
ﺨﻼل ﺨﺎﺩﻡ ﺴﻜﻴﻭﻴل ،ﻤﻤﺎ ﻗﺩ ﻴﻀﺭ ﺒﺎﻟﺠﻬﺎﺯ ﺍﻟﺫﻱ ﻴﻌﻤل ﻋﻠﻴﻪ ﺍﻟﺨﺎﺩﻡ!
ﻭﻟﻜﻥ ﻜﻴﻑ ﺘﺘﻡ ﻋﻤﻠﻴﺔ ﺍﻟﺤﻘﻥ Injection؟
-١ﺃﻭل ﺸﻲﺀ ،ﻴﺘﻭﻗﻊ ﺍﻟﻘﺭﺼﺎﻥ Hackerﻀﺭﻭﺭﺓ ﻭﺠﻭﺩ ﺍﻟﻨﺹ ﺒﻴﻥ ﻋﻼﻤﺘﻲ ﺘﻨﺼﻴﺹ،
ﻭﻻ ﺒﺩ ﺃﻨﻙ ﻭﻀﻌﺕ ﻋﻼﻤﺔ ﺘﻨﺼﻴﺹ ﺒﺎﺩﺌﺔ ﻗﺒل ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﺴﻴﺄﺘﻲ ﻤﻥ ﻤﺭﺒﻊ ﺍﻟـﻨﺹ،
ﻟﻬﺫﺍ ﻴﺠﺏ ﻋﻠﻰ ﺍﻟﻤﺨﺘﺭﻕ ﺃﻥ ﻴﻜﺘﺏ ﺃﻱ ﻜﻠﻤﺔ ،ﺜﻡ ﻴﺘﺒﻌﻬﺎ ﺒﺎﻟﻌﻼﻤﺔ ' ﻹﻏﻼﻕ ﻋﻼﻤﺘـﻲ
ﺍﻟﺘﻨﺼﻴﺹ ،ﻭﺒﻬﺫﺍ ﻴﻀﻤﻥ ﻋﺩﻡ ﺤﺩﻭﺙ ﺨﻁﺄ ﻓﻲ ﺼﻴﻐﺔ ﺠﻤﻠﺔ .SQL
-٢ﺒﻌﺩ ﻫﺫﺍ ﻴﻀﻊ ﺍﻟﻘﺭﺼﺎﻥ ﻓﺎﺼﻠﺔ ﻤﻨﻘﻭﻁﺔ ; ﻟﻴﺴﺘﻁﻴﻊ ﻜﺘﺎﺒﺔ ﺃﻤﺭ SQLﺠﺩﻴﺩ ﺨﺎﺹ ﺒﻪ،
ﻭﻫﻨﺎ ﺘﻜﻭﻥ ﻟﺩﻴﻪ ﺍﻟﺤﺭﻴﺔ ﻓﻲ ﻜﺘﺎﺒﺔ ﺍﻷﻤﺭ ﺍﻟﺫﻱ ﻴﺭﻴﺩﻩ!
-٣ﻨﻅﺭﺍ ﻷﻥ ﺍﻟﻘﺭﺼﺎﻥ ﻴﺘﻭﻗﻊ ﻤﻨﻙ ﺇﻀﺎﻓﺔ ﺘﻜﻤﻠﺔ ﻟﺠﻤﻠﺔ SQLﺒﻌﺩ ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﻜﺘﺒﻪ ﻓـﻲ
ﻤﺭﺒﻊ ﺍﻟﻨﺹ ،ﻓﺈﻨﻪ ﻴﻀﻊ ﻓﻲ ﻨﻬﺎﻴﺔ ﺍﻟﻜﻭﺩ ﺍﻟﻤﺩﺴﻭﺱ ﺍﻟﺭﻤﺯ --ﻟﻴﺠﻌل ﺃﻱ ﻨﺹ ﺘﺎل ﻟﻪ
ﻤﺠﺭﺩ ﺘﻌﻠﻴﻕ ،ﻭﺒﻬﺫﺍ ﻴﻠﻐﻲ ﺃﻱ ﺘﻜﻤﻠﺔ ﺨﺎﺼﺔ ﺒﻙ ﻟﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ،ﻭﻴﻀـﻤﻥ ﺴـﻼﻤﺔ
ﺼﻴﻐﺔ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ!
ﻭﺍﻵﻥ ،ﺩﻋﻨﺎ ﻨﺭﻯ ﻤﺎﺫﺍ ﺴﻴﺤﺩﺙ ﻟﻭ ﻜﺘﺏ ﺍﻟﻘﺭﺼﺎﻥ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ:
Ahamd'; drop table Books--
ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﺘﺼﺒﺢ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﺒﻌﺩ ﺇﻀﺎﻓﺔ ﻫﺫﻩ ﺍﻟﺠﻤﻠﺔ ﻜﺎﻟﺘﺎﻟﻲ:
٨٢
SELECT Books.Book
FROM Authors, Books
WHERE Authors.ID = AuthorID
'AND Authors.Author = 'Ahamd'; drop table Books--
ﻜﻤﺎ ﺘﺭﻯ :ﺼﺎﺭ ﻟﺩﻴﻨﺎ ﺍﺴﺘﻌﻼﻤﺎﻥ ﺼﺤﻴﺤﺎﻥ ﻭﺘﻌﻠﻴﻕ:
-ﺍﻻﺴﺘﻌﻼﻡ ﺍﻷﻭل ﻻ ﻗﻴﻤﺔ ﻟﻪ ،ﻭﻫﻭ ﻴﺒﺤﺙ ﻋﻥ ﻜﺘﺏ ﻤﺅﻟﻑ ﺍﺴﻤﻪ .Ahmad
-ﻭﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺜﺎﻨﻲ ﺃﻤﺭ ﺤﺫﻑ ﻴﻁﻠﺏ ﺤﺫﻑ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﻜﺎﻤﻼ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ..
ﻻﺤﻅ ﺃﻥ ﺍﻟﻘﺭﺼﺎﻥ ﻻ ﻴﻌﺭﻑ ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ،ﻭﻟﻜﻥ ﺘﻭﻗﻊ ﺍﺴﻡ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﻟﻥ ﻴﻜﻭﻥ
ﻋﺴﻴﺭﺍ ،ﻭﻟﻥ ﻴﻴﺄﺱ ﺍﻟﻘﺭﺼﺎﻥ ﻤﻥ ﺘﺠﺭﺒﺔ ﻋﺸﺭﺍﺕ ﺍﻷﺴﻤﺎﺀ ﺍﻟﻤﺤﺘﻤﻠﺔ ،ﻤﺎ ﺩﺍﻡ ﻋﺯﻤﻪ ﻗﺩ
ﻗﺭ ﻋﻠﻰ ﺘﺩﻤﻴﺭ ﺒﺭﻨﺎﻤﺠﻙ!
-ﻭﻓﻲ ﺍﻟﻨﻬﺎﻴﺔ ﻴﻭﺠﺩ ﺘﻌﻠﻴﻕ ﺼﻐﻴﺭ ،ﻫﻭ ﺍﻟﻌﻼﻤﺔ ' ﺍﻟﺨﺎﺼﺔ ﺒﻙ ،ﻭﺍﻟﺘﻲ ﺍﺴﺘﻁﺎﻉ ﺍﻟﻘﺭﺼﺎﻥ
ﺘﻬﻤﻴﺸﻬﺎ ﺒﺤﻴﻠﺔ ﺼﻐﻴﺭﺓ ﺒﺎﺭﻋﺔ!
ﻴﺒﺩﻭ ﺍﻷﻤﺭ ﻤﻔﺯﻋﺎ ،ﺃﻟﻴﺱ ﻜﺫﻟﻙ؟
ﺇﻥ ﻫﺫﻩ ﺍﻟﺜﻐﺭﺓ ﺘﺘﻴﺢ ﻟﻠﻘﺭﺍﺼﻨﺔ ﺘﺩﻤﻴﺭ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺩﺨﻭل ﺤﺴـﺎﺒﺎﺕ ﺍﻟﻤﺴـﺘﺨﺩﻤﻴﻥ ،ﺩﻭﻥ
ﺍﻟﺤﺎﺠﺔ ﺇﻟﻰ ﻜﺘﺎﺒﺔ ﺍﺴﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺃﻭ ﻜﻠﻤﺔ ﺍﻟﻤﺭﻭﺭ ،ﻭﺍﻟﻜﺜﻴﺭ ﻤﻥ ﺍﻟﻜﻭﺍﺭﺙ ﺍﻟﺘﻲ ﺘﻜﻔـﻲ ﻟﺘﻁﻴـﺭ
ﺍﻟﻨﻭﻡ ﻤﻥ ﻋﻴﻭﻥ ﺍﻟﻤﺒﺭﻤﺠﻴﻥ .J
ﻓﻜﻴﻑ ﺇﺫﻥ ﻴﻤﻜﻥ ﺇﻏﻼﻕ ﻫﺫﻩ ﺍﻟﺜﻐﺭﺓ ﺍﻟﻘﺎﺘﻠﺔ؟
ﻓﻲ ﺍﻟﺤﻘﻴﻘﺔ ﻫﻨﺎﻙ ﻋﺩﺓ ﻨﺼﺎﺌﺢ ﻫﺎﻤﺔ ﻓﻲ ﻫﺫﺍ ﺍﻟﺼﺩﺩ:
-١ﺍﻟﺘﻘﻠﻴل ﻤﻥ ﺍﺴﺘﺨﺩﺍﻡ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺹ ،ﻭﺍﻻﺴﺘﻌﺎﻀﺔ ﻋﻨﻬﺎ ﺒﺄﺩﻭﺍﺕ ﺘﺘﻴﺢ ﺍﺨﺘﻴـﺎﺭ ﺍﻟﻘـﻴﻡ،
ﻤﺜل ﺍﻟﻘﻭﺍﺌﻡ Listsﺇﻥ ﻜﺎﻥ ﻫﺫﺍ ﻤﻤﻜﻨﺎ.
-٢ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼﻴﺔ MaxLengthﺍﻟﺨﺎﺼﺔ ﺒﻤﺭﺒﻊ ﺍﻟـﻨﺹ ﻟﺘﺤﺩﻴـﺩ ﻁـﻭل ﺍﻟـﻨﺹ
ﺍﻟﻤﺴﻤﻭﺡ ﺒﻜﺘﺎﺒﺘﻪ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ..ﻫﺫﺍ ﺴﻴﺤﺩ ﻤﻥ ﻗﺩﺭﺓ ﺍﻟﻘﺭﺼﺎﻥ ﻋﻠﻰ ﻜﺘﺎﺒﺔ ﺃﻭﺍﻤـﺭ
ﻤﺩﺴﻭﺴﺔ.
-٣ﺇﺠﺭﺍﺀ ﺒﻌﺽ ﺍﻟﻔﺤﻭﺼﺎﺕ ﺍﻟﺼﻐﻴﺭﺓ ﻋﻠﻰ ﻗﻴﻤﺔ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ،ﻟﻠﺘﺄﻜﺩ ﻤﻥ ﺨﻠـﻭ ﺍﻟـﻨﺹ
ﺍﻟﺫﻱ ﻜﺘﺒﻪ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻤﻥ ﺍﻟﻌﻼﻤﺎﺕ ﺍﻟﻤﺭﻴﺒﺔ ﻤﺜـل ; ' .. */ /* --ﻫـﺫﺍ ﺴﻴﺸـل
٨٣
ﺤﺭﻜﺔ ﺍﻟﻘﺭﺼﺎﻥ ﺘﻤﺎﻤﺎ ..ﻭﺍﻷﻓﻀل ﺃﻥ ﺘﻤﻨﻊ ﻜﺘﺎﺒﺔ ﻫﺫﻩ ﺍﻟﺤﺭﻭﻑ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﻤـﻥ
ﺍﻟﻤﻨﺒﻊ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺤﺩﺙ .KeyPress
-٤ﻋﻠﻴﻙ ﺃﻴﻀﺎ ﺃﻥ ﺘﻤﻨﻊ ﺍﻟﻜﻠﻤﺎﺕ ﺍﻟﺩﺍﻟﺔ ﻋﻠﻰ ﺃﻭﺍﻤﺭ SQLﻓﻲ ﻤﺭﺒـﻊ ﺍﻟـﻨﺹ ،ﺨﺎﺼـﺔ
DROPﻭ DELETEﻭ UPDATEﻭ .INSERT
-٥ﻻ ﺘﻘﺒل ﺃﻴﺎ ﻤﻥ ﺍﻟﻜﻠﻤﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ ﻓﻲ ﻤﺭﺒﻊ ﻨﺹ ﻴﺩﺨل ﻓﻴﻪ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﺴﻡ ﻤﻠﻑ:
AUX, CLOCK$, CON, CONFIG$, NUL, PRN
COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8
LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8
-٦ﻤﻥ ﺍﻟﻤﻬﻡ ﺃﻴﻀﺎ ﺃﻥ ﺘﺤﺩﺩ ﺼـﻼﺤﻴﺎﺕ ﻤﺴـﺘﺨﺩﻤﻲ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﻭﺃﻻ ﺘﻌﻁـﻲ
ﺍﻟﺼﻼﺤﻴﺎﺕ ﺍﻟﺨﻁﻴﺭﺓ )ﻜﺤﺫﻑ ﺍﻟﺠﺩﺍﻭل ﺃﻭ ﺇﻨﺸﺎﺌﻬﺎ( ﺇﻻ ﻟﻠﻤﺩﻴﺭﻴﻥ ،ﻭﻋﻠﻴﻙ ﺃﻥ ﺘﺼـﻨﻊ
ﻨﺴﺨﺔ ﺨﺎﺼﺔ ﻤﻥ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻟﻬﺅﻻﺀ ﺍﻟﻤﺩﻴﺭﻴﻥ ﺒﺤﻴﺙ ﻻ ﻴﺘﻡ ﺘـﺩﺍﻭﻟﻬﺎ ﺇﻻ ﺒﻴـﻨﻬﻡ ..ﺃﻤـﺎ
ﺍﻟﻤﺴﺘﺨﺩﻤﻭﻥ ﺍﻟﻌﺎﺩﻴﻭﻥ ،ﻓﻌﻠﻴﻙ ﺃﻥ ﺘﺼﻨﻊ ﻟﻬﻡ ﻨﺴﺨﺔ ﺃﺨﺭﻯ ﻤﻥ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻭﺃﻥ ﺘﺘﺼـل
ﻫﺫﻩ ﺍﻟﻨﺴﺨﺔ ﺒﺎﻟﺨﺎﺩﻡ ﻤﻥ ﺨﻼل ﺤﺴﺎﺏ ﻤﺴﺘﺨﺩﻡ ﻤﺤﺩﻭﺩ ﺍﻟﺼﻼﺤﻴﺎﺕ ،ﻭﺒﻬﺫﺍ ﻟﻭ ﻨﺠـﺢ
ﺃﻱ ﻗﺭﺼﺎﻥ ﻓﻲ ﺘﺠﺎﻭﺯ ﺨﻁﻭﻁ ﺩﻓﺎﻋﻙ ﻋﺒﺭ ﻫﺫﻩ ﺍﻟﻨﺴﺨﺔ ،ﻻ ﻴﺠﺩ ﺍﻟﻜﺜﻴﺭ ﻤﻤﺎ ﻴﺴـﺘﻁﻴﻊ
ﻓﻌﻠﻪ!
-٧ﻜﻥ ﺤﺫﺭﺍ ﻤﻥ ﺍﻟﻜﻠﻤﺎﺕ ﺍﻟﺘﻲ ﺘﺒﺩﺃ ﺒـ _ ،xpﻷﻨﻬﺎ ﺍﻟﺒﺎﺩﺌـﺔ ﺍﻟﺘـﻲ ﻴـﺘﻡ ﺒﻬـﺎ ﺘﺴـﻤﻴﺔ
ﺍﻹﺠـــﺭﺍﺀﺍﺕ ﺍﻟﻤﺨﺯﻨـــﺔ ﺍﻹﻀـــﺎﻓﻴﺔ ﻟﻤﺨﻁـــﻁ ﻗﺎﻋـــﺩﺓ ﺍﻟﺒﻴﺎﻨـــﺎﺕ
،Catalog-extended stored proceduresﻤﺜل ﺍﻹﺠﺭﺍﺀ .xp_cmdshell
-٨ﺍﺴﺘﺨﺩﺍﻡ ﺍﻹﺠﺭﺍﺀﺍﺕ ﺍﻟﻤﺨﺯﻨﺔ Stored Proceduresﻓﻲ ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻤﺎﺕ ،ﻷﻨﻬـﺎ
ﺘﻜﻭﻥ ﻤﺤﻔﻭﻅﺔ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ،ﻭﻤﻥ ﺜﻡ ﻴﻘﻭﻡ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﺒﻔﺤﺹ
ﻗﻴﻡ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﺭﺴﻠﺔ ﺇﻟﻰ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ،ﻭﺍﻟﺘﺄﻜﺩ ﻤﻥ ﺃﻨﻬﺎ ﻤﻥ ﺍﻟﻨﻭﻉ ﺍﻟﺼـﺤﻴﺢ
ﻭﺒﺎﻟﻁﻭل ﺍﻟﻤﺤﺩﺩ.
-٩ﺍﺭﻓﺽ ﻜﺘﺎﺒﺔ ﺍﻟﻜﻠﻤﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ،ﻷﻨﻬﺎ ﺘﺴﻤﺢ ﺒﺤﻘﻥ ﺍﻷﻜﻭﺍﺩ ﺍﻟﻤﺩﺴﻭﺴـﺔ
ﻓﻲ ﺍﻹﺠﺭﺍﺀﺍﺕ ﺍﻟﻤﺨﺯﻨﺔ:
EXECUTE, EXEC, sp_executesql
٨٤
-١٠ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﻌﺎﻤﻼﺕ Parametersﻟﺘﻤﺭﻴﺭ ﺍﻟﻘﻴﻡ ﺇﻟﻰ ﺠﻤل ﺍﻻﺴﺘﻌﻼﻡ ،ﺒـﺩﻻ ﻤـﻥ
ﺍﺴﺘﺨﺩﺍﻡ ﻁﺭﻴﻘﺔ ﺘﺸﺒﻴﻙ ﺍﻟﻨﺼـﻭﺹ ،Concatinationﻷﻥ ﺍﻟﻤﻌـﺎﻤﻼﺕ ﺘﻀـﻤﻥ
ﺍﻟﺘﺤﻘﻕ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﻌﺎﻤل ﻭﺍﻟﻁﻭل ﺍﻟﻤﺴﻤﻭﺡ ﺒﻪ ﻟﻘﻴﻤﺘﻪ ..ﻟﻜﻥ ﻨﺼﻴﺤﺔ :ﻻ ﺘﺘﺨل ﻋﻥ
ﻓﺤﺹ ﺍﻟﻨﺼﻭﺹ ﺍﻟﺘﻲ ﻴﻜﺘﺒﻬﺎ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓـﻲ ﻤﺭﺒﻌـﺎﺕ ﺍﻟﻨﺼـﻭﺹ ،ﺤﺘـﻰ ﻟـﻭ
ﺍﺴﺘﺨﺩﻤﺕ ﺍﻟﻤﻌﺎﻤﻼﺕ ،ﻓﻘﺩ ﺘﺴﻤﺢ ﺒﻌﺽ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻨﺼﻴﺔ ﺍﻟﻁﻭﻴﻠﺔ ﺒﻌﺒﻭﺭ ﺒﻌـﺽ
ﺍﻟﻜﻭﺩ ﺍﻟﻤﺩﺴﻭﺱ.
ﻭﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ AuthorBooks_Readerﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻹﺠﺭﺍﺀ SqlInjectionﻟﻠﺘﺄﻜﺩ ﻤﻥ ﺃﻥ
ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﻜﺘﺒﻪ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﻻ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺃﻴﺔ ﺭﻤﻭﺯ ﺃﻭ ﻜﻠﻤﺎﺕ ﻤﺭﻴﺒﺔ ،ﻜﻤـﺎ
ﺍﺴﺘﺨﺩﻤﻨﺎ ﻤﻌﺎﻤﻼ ﻟﺘﻤﺭﻴﺭ ﺍﻟﻨﺹ ﺇﻟﻰ ﺍﻻﺴﺘﻌﻼﻡ ﻟﻤﺯﻴﺩ ﻤﻥ ﺍﻟﺤﻤﺎﻴﺔ.
ﻜﻤﺎ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﺩﺍﻟﺔ SqlInjectionﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ DbTasksﻟﻠﺘﺄﻜﺩ ﻤﻥ ﺃﻥ ﻗﻴﻡ ﺍﻟﻤﻌـﺎﻤﻼﺕ
ﻻ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﺴﺘﻌﻼﻤﺎﺕ ﻤﺩﺴﻭﺴﺔ.
ﻭﺴﻨﺘﻌﺭﻑ ﻓﻴﻤﺎ ﻴﻠﻲ ﻋﻠﻰ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻭﻜﻴﻔﻴﺔ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ.
٨٥
ﺍﻟﻤﻌﺎﻤﻼﺕ :Parameters
ﺍﻟﻤﻌﺎﻤل ﻫﻭ ﻋﻼﻤﺔ ﻤﻭﻀﻌﻴﺔ Placeholderﺘﻭﻀﻊ ﻓﻲ ﺠﻤﻠﺔ SQLﻟﺘﺸﻴﺭ ﺇﻟـﻰ ﺃﻥ ﻫﻨـﺎﻙ
ﻗﻴﻤﺔ ﺴﻴﺘﻡ ﺍﻟﺘﻌﻭﻴﺽ ﺒﻬﺎ ﺒﺩﻻ ﻤﻨﻬﺎ ..ﻭﻴﻤﻜﻨﻙ ﺘﻌﺭﻴﻑ ﺃﻱ ﻋﺩﺩ ﺘﺭﻴﺩﻩ ﻤﻥ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻓﻲ ﺠﻤﻠـﺔ
ﺍﻻﺴﺘﻌﻼﻡ.
ﻭﺘﺨﺘﻠﻑ ﺼﻴﻐﺔ ﻫﺫﻩ ﺍﻟﻌﻼﻤﺔ ﺘﺒﻌﺎ ﻟﻨﻭﻉ ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﻤﺯﻭﺩ ﺴﻴﻜﻭﻴل ﻴﺴـﺘﺨﺩﻡ ﺍﻟﺭﻤـﺯ @
ﻟﺘﻤﻴﻴــﺯ ﺍﻟﻤﻌﺎﻤــل ،ﻴﺘﺒﻌــﻪ ﺍﺴــﻡ ﻤﺘﻐﻴــﺭ ﺒــﺩﻭﻥ ﺃﻥ ﻴﻔﺼــل ﺒﻴﻨﻬﻤــﺎ ﻤﺴــﺎﻓﺎﺕ
)ﻤﺜل ..(@UserNameﻟﻬﺫﺍ ﻨﺴﺘﻁﻴﻊ ﻜﺘﺎﺒﺔ ﺍﻻﺴﺘﻌﻼﻡ ﻋﻥ ﻜﺘﺏ ﺃﺤﺩ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻜﺎﻟﺘﺎﻟﻲ:
Cmd.CommandText = @"SELECT Books.Book
FROM Authors, Books
WHERE Authors.ID = AuthorID
;"AND Authors.Author = @Author
ﻫﺫﺍ ﻴﺒﺩﻭ ﻜﺄﻨﻨﺎ ﻋﺭﻓﻨﺎ ﻤﺘﻐﻴﺭﺍ ﺍﺴﻤﻪ @Authorﻭﺍﺴﺘﺨﺩﻤﻨﺎﻩ ﻓـﻲ ﺠﻤﻠـﺔ ﺍﻻﺴـﺘﻌﻼﻡ ،ﻟﻴـﺘﻡ
ﺍﻟﺘﻌﻭﻴﺽ ﻋﻨﻪ ﻋﻨﺩ ﺘﻨﻔﻴﺫﻫﺎ.
ﺃﻤﺎ ﻓﻲ ﻤﺯﻭﺩ OLEDBﻭ ODBCﻓﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻡ ﻋﻼﻤﺔ ﺍﻻﺴﺘﻔﻬﺎﻡ ﺍﻹﻨﺠﻠﻴﺯﻴـﺔ ? ﻟﻺﺸـﺎﺭﺓ
ﺇﻟﻰ ﻭﺠﻭﺩ ﻤﻌﺎﻤل ،ﺩﻭﻥ ﻤﻨﺢ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﺃﻱ ﺍﺴﻡ:
Cmd.CommandText = @"SELECT Books.Book
FROM Authors, Books
WHERE Authors.ID = AuthorID
;"? = AND Authors.Author
ﻻﺤﻅ ﺃﻥ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ﺁﻜﺴﻴﺱ ﺼﺎﺭﺕ ﺘﻘﺒل ﺘﺴﻤﻴﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ )ﻭﻤﺎ ﺯﺍﻟﺕ ﺘﻘﺒـل ﺍﻟﻌﻼﻤـﺔ ?
ﺃﻴﻀﺎ( ،ﻟﻜﻨﻬﺎ ﻻ ﺘﻤﻴﺯ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﺴﻤﺎﺓ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺃﻱ ﻋﻼﻤﺔ ﺨﺎﺼﺔ ..ﻴﻤﻜﻨﻙ ﻤﺜﻼ ﺃﻥ ﺘﻜﺘﺏ
ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺴﺎﺒﻕ ﻜﻤﺎ ﻴﻠﻲ:
Cmd.CommandText = @"SELECT Books.Book
FROM Authors, Books
WHERE Authors.ID = AuthorID
;"AND Authors.Author = AuthorValue
ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﺘﻌﺘﺒﺭ ﺁﻜﺴﻴﺱ ﺃﻥ AuthorValueﻫﻭ ﺍﺴﻡ ﻤﻌﺎﻤل ..ﺒل ﻴﻤﻜﻨﻙ ﺃﻴﻀﺎ ﺃﻥ ﺘﺴـﺘﺨﺩﻡ
ﺍﻻﺴﻡ @Authorﻓﻲ ﺍﻻﺴﺘﻌﻼﻡ ،ﻭﺴﺘﻘﺒﻠﻪ ﺁﻜﺴﻴﺱ ﻜﺎﺴﻡ ﻤﻌﺎﻤل ،ﻭﻫﺫﺍ ﻴﺴﺎﻋﺩﻙ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻨﻔﺱ
ﺍﺴﺘﻌﻼﻤﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻤﻊ ﺁﻜﺴﻴﺱ!
٨٦
ﻟﻜﻥ ﻫﺫﺍ ﺍﻟﻤﺭﻭﻨﺔ ﻤﻥ ﺁﻜﺴﻴﺱ ﺘﺴﺒﺏ ﻤﺸﻜﻠﺔ ﻏﺭﻴﺒﺔ ﻓﻲ ﺒﻌﺽ ﺍﻷﺤﻴﺎﻥ ،ﻓﻠﻭ ﺃﺨﻁﺄﺕ ﻤﺜﻼ ﻓـﻲ
ﻜﺘﺎﺒﺔ ﺍﺴﻡ ﺍﻟﺤﻘل AuthorIDﻓﻲ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺴﺎﺒﻕ ،ﻭﻜﺘﺒﺘﻪ ﻤﺜﻼ ،AutherIDﻓﺴﺘﻌﺘﺒﺭﻩ ﺁﻜﺴـﻴﺱ
ﺍﺴﻡ ﻤﻌﺎﻤل ،ﻭﺒﺩﻻ ﻤﻥ ﺃﻥ ﺘﺤﺼل ﻓﻲ ﺒﺭﻨﺎﻤﺠﻙ ﻋﻠﻰ ﺭﺴﺎﻟﺔ ﺘﺨﺒﺭﻙ ﺃﻥ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻟﻴﺱ ﻤﻭﺠﻭﺩﺍ ﻓﻲ
ﺍﻟﺠﺩﻭل ،ﺴﺘﺤﺼل ﻋﻠﻰ ﺭﺴﺎﻟﺔ ﺘﺨﺒﺭﻙ ﺒﺄﻥ ﻗﻴﻡ ﺒﻌﺽ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻤﻔﻘﻭﺩﺓ ..ﻫﺫﺍ ﻫـﻭ ﺍﻟﺴـﺒﺏ ﺍﻟـﺫﻱ
ﺴﻴﺠﻌﻠﻙ ﺘﺭﻯ ﺁﻻﻑ ﺍﻷﺴﺌﻠﺔ ﻤﻥ ﺍﻟﻤﺒﺭﻤﺠﻴﻥ ﻋﻥ ﺴﺒﺏ ﻅﻬﻭﺭ ﻫـﺫﻩ ﺍﻟﺭﺴـﺎﻟﺔ ﺍﻟﻐﺭﺒﻴـﺔ ﻓـﻲ
ﺒﺭﻨﺎﻤﺠﻬﻡ:
"No value given for one or more required parameters
ﺭﻏﻡ ﺃﻨﻬﻡ ﻻ ﻴﺴﺘﺨﺩﻤﻭﻥ ﺍﺴﺘﻌﻼﻤﺎﺕ ﻓﻴﻬﺎ ﻤﻌﺎﻤﻼﺕ ،ﺃﻭ ﺃﻨﻬﻡ ﻤﺭﺭﻭﺍ ﻗﻴﻡ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺼﺤﻴﺤﺔ
ﻓﻌﻼ! ..ﻓﻜل ﻤﺎ ﻫﻨﺎﻙ ،ﺃﻨﻬﻡ ﺃﺨﻁﺄﻭﺍ ﻓﻲ ﻜﺘﺎﺒﺔ ﺍﺴﻡ ﺃﺤﺩ ﺍﻟﺤﻘﻭل ،ﻓﺘﻡ ﺍﻋﺘﺒﺎﺭﻩ ﻤﻌﺎﻤﻼ!
ﻭﻟﻜﻥ ،ﻜﻴﻑ ﻴﻤﻜﻥ ﺍﻟﺘﻌﻭﻴﺽ ﻋﻥ ﻗﻴﻡ ﺍﻟﻤﻌﺎﻤﻼﺕ؟
ﻟﻔﻌــل ﻫــﺫﺍ ،ﻋﻠﻴــﻙ ﺍﺴــﺘﺨﺩﺍﻡ ﻤﺠﻤﻭﻋــﺔ ﺍﻟﻤﻌــﺎﻤﻼﺕ ﺍﻟﺨﺎﺼــﺔ ﺒﻜــﺎﺌﻥ ﺍﻷﻤــﺭ
DbCommand.Parametersﻟﺘﻌﺭﻴﻑ ﻜﺎﺌﻨﺎﺕ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻭﻭﻀﻊ ﺍﻟﻘﻴﻡ ﻓﻴﻬﺎ ،ﺤﻴﺙ ﺴـﻴﻘﻭﻡ
ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﺒﺘﻤﺭﻴﺭﻫﺎ ﺇﻟﻰ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﻋﻨﺩ ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ..ﻭﻋﻠﻴﻙ ﺃﻥ ﺘﻨﺘﺒﻪ ﺠﻴـﺩﺍ ﺇﻟـﻰ ﺃﻥ
ﻭﻀﻊ ﺭﻤﺯ ﺍﻟﻤﻌﺎﻤل ﻓﻲ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﻻ ﻴﻨﺸﺊ ﻤﻌﺎﻤﻼﺕ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺘﻠﻘﺎﺌﻴـﺎ،
ﻓﻬﺫﺍ ﺍﻟﺭﻤﺯ ﻴﺤﺩﺩ ﻓﻘﻁ ﻤﻭﻀﻊ ﺍﻟﺘﻌﻭﻴﺽ ﻋﻥ ﺍﻟﻤﻌﺎﻤل ،ﺒﻴﻨﻤﺎ ﺘﻅل ﺃﻨﺕ ﻤﺴﺌﻭﻻ ﻋـﻥ ﺘﻌﺭﻴـﻑ
ﻤﻌﺎﻤل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻟﺘﺤﺩﻴﺩ ﻨﻭﻉ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﻴﻘﺒﻠﻬﺎ ﺍﻟﻤﻌﺎﻤل ،ﻭﺘﻤﺭﻴﺭ ﺍﻟﻘﻴﻤـﺔ ﻤـﻥ
ﺨﻼﻟﻪ ﺇﻟﻰ ﻜﺎﺌﻥ ﺍﻷﻤﺭ.
ﻭﻴﺸﺘﺭﻁ ﻓﻲ ﺤﺎﻟﺔ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﺃﻥ ﻴﻜﻭﻥ ﻟﻜل ﻤﻥ ﺍﻟﻤﻌﺎﻤل ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻨـﺹ ﺍﻻﺴـﺘﻌﻼﻡ
ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻨﻔﺱ ﺍﻻﺴﻡ ..ﻓﺈﺫﺍ ﻋﺭﻓﺕ ﻓﻲ ﻨـﺹ ﺍﻻﺴـﺘﻌﻼﻡ
ﻤﻌﺎﻤﻼ ﺍﺴﻤﻪ ،@Authorﻓﻴﺠـﺏ ﺃﻥ ﻴﻜـﻭﻥ ﺍﺴـﻤﻪ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﻤﻌـﺎﻤﻼﺕ ﺃﻴﻀـﺎ
.@Author
ﺃﻤﺎ ﻤﻌﺎﻤﻼﺕ OLEDBﻭ ODBCﻓﻜﻠﻬﺎ ﻤﻤﺜﻠﺔ ﺒﺎﻟﺭﻤﺯ ? ﻤﻤﺎ ﻴﺠﻌﻠﻙ ﻤﻀﻁﺭﺍ ﺇﻟﻰ ﺘﻌﺭﻴﻔﻬﺎ
ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ DbCommand.Parametersﺒﻨﻔﺱ ﺘﺭﺘﻴﺏ ﻅﻬﻭﺭﻫﺎ ﻓـﻲ ﻨـﺹ
ﺍﻻﺴﺘﻌﻼﻡ ..ﻭﺤﺘﻰ ﻟﻭ ﻜﻨﺕ ﺘﺴﺘﺨﺩﻡ ﻤﻌﺎﻤﻼﺕ ﻤﺴﻤﺎﺓ ﻓﻲ ﺁﻜﺴﻴﺱ ،ﻓﻤﺎ ﺯﻟـﺕ ﻤﻀـﻁﺭﺍ ﺇﻟـﻰ
ﺍﻟﻤﺤﺎﻓﻅﺔ ﻋﻠﻰ ﺍﻟﺘﺭﺘﻴﺏ ﺍﻟﺼﺤﻴﺢ ﻟﻬﺫﻩ ﺍﻟﻤﻌﺎﻤﻼﺕ ،ﻜﻤﺎ ﺃﻥ ﺍﺴﻡ ﻜل ﻤﻌﺎﻤل ﻤﺎ ﺯﺍل ﻏﻴﺭ ﻤﻬـﻡ،
٨٧
ﻟﻬﺫﺍ ﺘﺴﺘﻁﻴﻊ ﺘﻌﺭﻴﻑ ﻤﻌﺎﻤل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﺴﻤﻪ Xﻟﻴﻌﻭﺽ ﻋﻥ ﻤﻌﺎﻤل ﻓﻲ ﻨـﺹ
ﺍﻻﺴﺘﻌﻼﻡ ﺍﺴﻤﻪ ..@Authorﻓﻜل ﻤﺎ ﻴﻬﻡ ﺤﻘﺎ ﻫﻭ ﺍﻟﺘﺭﺘﻴﺏ ﻭﻟﻴﺱ ﺍﻻﺴﻡ ..ﻴﻤﻜﻨﻙ ﺍﻋﺘﺒـﺎﺭ ﺃﻥ
ﺁﻜﺴﻴﺱ ﻴﻤﺤﻭ ﺍﺴﻡ ﺍﻟﻤﻌﺎﻤل ﻭﻴﻀﻊ ﺒﺩﻻ ﻤﻨﻪ ﻋﻼﻤﺔ ﺍﺴﺘﻔﻬﺎﻡ ? ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﺘﺴﻬﻴل ﻋﻠﻴﻙ.
ﻫﺫﺍ ﻫﻭ ﻤﺎ ﺠﻌل ﻤﻥ ﺍﻟﻤﻤﻜﻥ ﺃﻥ ﻨﺴﺘﺨﺩﻡ ﻨﻔﺱ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ،ﻭﻨﻔﺱ ﻜﻭﺩ ﺘﻌﺭﻴﻑ ﺍﻟﻤﻌﺎﻤﻼﺕ
ﻓﻲ ﺤﺩﺙ ﻀﻐﻁ ﺯﺭ "ﺍﻟﻜﺘﺏ" ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ Factoriesﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻜﺘﺏ ﺃﺤﺩ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻤﻥ
ﻗﺎﻋﺩﺓ ﺍﻟﻜﺘﺏ ﻓﻲ ﺁﻜﺴﻴﺱ ﺃﻭ ﻗﺎﻋﺩﺓ ﺍﻟﻜﺘﺏ ﻓﻲ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ.
ﻭﺍﻵﻥ ،ﺩﻋﻨﺎ ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻭﺍﻟﻔﺌﺎﺕ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻤﻌﻬﺎ.
٨٨
ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﻤﻌﺎﻤﻼﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ
DbParameterCollection Class
٨٩
ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﻤﻌﺎﻤﻼﺕ ﺴﻴﻜﻭﻴل
SqlParameterCollection Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ،DbParameterCollectionﻭﻫﻲ ﻻ ﺘﺨﺘﻠﻑ ﻋﻨﻬﺎ ﻜﺜﻴﺭﺍ ﺇﻻ ﻓـﻲ ﺃﻥ
ﻋﻨﺎﺼﺭﻫﺎ ﻤﻥ ﻨﻭﻉ ﺍﻟﻔﺌﺔ ، SqlParameterﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﺒﻌﺩ ﻗﻠﻴل.
ﻜﻤﺎ ﺃﻥ ﻟﻠﻭﺴﻴﻠﺔ Addﺍﻟﺨﺎﺼﺔ ﺒﻬﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻌﺩﻴﺩ ﻤﻥ ﺍﻟﺼﻴﻎ ﺍﻟﺘﻲ ﻤﻥ ﺍﻟﻤﻔﻴﺩ ﺃﻥ ﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ:
ﺇﻀﺎﻓﺔ :Add
ﺘﻀﻴﻑ ﻤﻌﺎﻤﻼ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ،ﻭﻟﻬﺎ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻭﺍﺤﺩﺍ ﻤﻥ ﺍﻟﻨﻭﻉ .SqlParameter
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻨﺼـﺎ ﻴﻤﺜـل ﺍﺴـﻡ ﺍﻟﻤﻌﺎﻤـل ﻭﺇﺤـﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ
SqlDbTypeﺍﻟﺘﻲ ﺘﻭﻀﺢ ﻨﻭﻉ ﺍﻟﻤﻌﺎﻤل.
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻟﺙ ﻤـﻥ ﺍﻟﻨـﻭﻉ ،Integer
ﻴﺴﺘﻘﺒل ﺤﺠﻡ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺴﺘﻭﻀﻊ ﻓﻲ ﺍﻟﻤﻌﺎﻤل.
-٤ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ،ﻴﺴﺘﻘﺒل ﺍﺴـﻡ ﻋﻤـﻭﺩ ﻓـﻲ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ DataSetﻟﻴﺭﺒﻁ ﺍﻟﻤﻌﺎﻤل ﺒﻪ.
-٥ﻭﻫﻨﺎﻙ ﺼﻴﻐﺔ ﺨﺎﻤﺴﺔ ﻟﻜﻨﻬﺎ ﻟﻡ ﻴﻌﺩ ﻤﻥ ﺍﻟﻤﻨﺼـﻭﺡ ﺍﺴـﺘﺨﺩﺍﻤﻬﺎ ،ﺘﺴـﺘﻘﺒل ﺍﺴـﻡ
ﺍﻟﻤﻌﺎﻤل ،ﻭﻜﺎﺌﻨﺎ Objectﻴﺤﻤل ﻗﻴﻤﺘﻪ ،ﻭﺫﻟﻙ ﺒﺴـﺒﺏ ﺍﻟﺘﻌـﺎﺭﺽ ﺒﻴﻨﻬـﺎ ﻭﺒـﻴﻥ
ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ،ﻟﻬﺫﺍ ﺘﻡ ﺇﻀﺎﻓﺔ ﻭﺴﻴﻠﺔ ﺠﺩﻴﺩﺓ ﺍﺴﻤﻬﺎ AddWithValueﻜﺒـﺩﻴل
ﻟﻬﺫﻩ ﺍﻟﺼﻴﻐﺔ.
٩٠
ﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺃﻥ ﺘﺭﺴل ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤـل ﺍﻟﺜـﺎﻨﻲ ﻟﻬـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﻗـﺎﺭﺉ ﺒﻴﺎﻨـﺎﺕ
DataReaderﺃﻭ ﺠﺩﻭل ﺒﻴﺎﻨﺎﺕ ،DataTableﻟﺘﺘﻡ ﻗﺭﺍﺀﺓ ﻜل ﺍﻟﺼـﻔﻭﻑ ﺍﻟﻤﻭﺠـﻭﺩﺓ
ﻓﻴﻬﻤﺎ ﻭﻭﻀﻌﻬﺎ ﻓﻲ ﺍﻟﻤﻌﺎﻤل ..ﻫﺫﺍ ﻤﻔﻴﺩ ﺇﺫﺍ ﻜﻨﺕ ﺘﺘﻌﺎﻤل ﻤﻊ ﺇﺠـﺭﺍﺀ ﻤﺨـﺯﻥ ﻴﺴـﺘﻘﺒل
ﻤﻌﺎﻤﻼ ﺠﺩﻭﻻ ،Table-Valued Parameterﻭﺘﺭﻴﺩ ﺃﻥ ﺘﺭﺴل ﺇﻟﻴﻪ ﺠـﺩﻭﻻ ﻜـﺎﻤﻼ..
ﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫـﺫﺍ ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ ..TableValuedParametersﻓـﻲ ﻫـﺫﺍ
ﺍﻟﻤﺸﺭﻭﻉ ﻨﻘﺭﺃ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻓﻲ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺍﻟﻨﻭﻉ ،OledbDataReaderﺜـﻡ
ﻨﺭﺴﻠﻪ ﻜﻤﻌﺎﻤل ﺜﺎﻥ ﺇﻟﻰ ﺍﻟﻭﺴﻴﻠﺔ AddWithValueﻻﺴـﺘﺨﺩﺍﻤﻪ ﻜﻤﻌﺎﻤـل ﻟﻺﺠـﺭﺍﺀ
ﺍﻟﻤﺨﺯﻥ ،InsertAuthorsﻭﺒﻬﺫﺍ ﻨﺴﺘﻁﻴﻊ ﺇﻀﺎﻓﺔ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻤﻥ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨـﺎﺕ ﺁﻜﺴـﻴﺱ
ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ.
ﻻﺤﻅ ﺃﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺠﺏ ﺃﻥ ﻴﻅل ﻤﻔﺘﻭﺤﺎ ﻫﻭ ﻭﺍﻻﺘﺼـﺎل ﺍﻟـﺫﻱ ﻴﺴـﺘﻨﺨﺩﻤﻪ ،ﻷﻥ
ﺍﻟﻭﺴﻴﻠﺔ AddWithValueﻻ ﺘﻨﺴﺦ ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻌﻠﻴﺎ ،ﻭﻻ ﻴﺘﻡ ﻨﺴـﺦ
ﻫﺫﻩ ﺍﻟﺴﺠﻼﺕ ﺇﻻ ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ExecuteNonQueryﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻷﻤـﺭ
ﺍﻟﺫﻱ ﻴﻨﻔﺫ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ﻭﻴﺭﺴل ﺇﻟﻪ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺠﺩﻭل.
٩١
ﻭﺍﺠﻬﺔ ﻤﻌﺎﻤل ﺍﻟﺒﻴﺎﻨﺎﺕ IDataParameter Interface
ﺍﻻﺘﺠﺎﻩ :Direction
ﺘﺤﺩﺩ ﺍﺘﺠﺎﻩ ﺍﻟﻤﻌﺎﻤل ،ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ParameterDirectionﺍﻟﺘﺎﻟﻴﺔ:
ﺍﻟﻘﻴﻤﺔ :Value
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺍﻟﻤﻌﺎﻤل ،ﻭﻫﻲ ﻤﻥ ﺍﻟﻨﻭﻉ Objectﻟﺘﻘﺒل ﺃﻱ ﻨﻭﻉ ﻤﻥ ﺍﻟﻘﻴﻡ ،ﻟﻜﻥ ﻫـﺫﺍ
ﻻ ﻴﻌﻨﻲ ﺃﻥ ﻜل ﺍﻟﻘﻴﻡ ﻤﺴﻤﻭﺡ ﺒﻬﺎ ،ﻷﻥ ﺍﻟﺨﺎﺼﻴﺔ DbTypeﺘﺤﺩﺩ ﻨﻭﻉ ﺍﻟﻘﻴﻡ ﺍﻟﻤﺴـﻤﻭﺡ
ﺒﻬﺎ.
٩٢
ﻭﻴﺠﺏ ﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻗﺒل ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ ﻤﻥ ﺨﻼل ﻜﺎﺌﻥ ﺍﻷﻤـﺭ ،ﻭﺇﺫﺍ
ﻜﺎﻥ ﺍﻟﻤﻌﺎﻤل ﻟﻺﺨﺭﺍﺝ ،ﻓﺈﻥ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻌﺎﺌﺩﺓ ﻤﻥ ﺍﻟﺨﺎﺩﻡ ﺘﻭﻀﻊ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺒﻌﺩ ﺘﻨﻔﻴﺫ
ﺍﻷﻤﺭ ﺃﻭ ﺒﻌﺩ ﺇﻏﻼﻕ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ DataReaderﺇﻥ ﻜﻨﺕ ﺘﺴﺘﺨﺩﻤﻪ.
ﻭﻟﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ DBNullﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ،ﺍﺴﺘﺨﺩﻡ ﺍﻟﻔﺌﺔ DBNullﻜﺎﻟﺘﺎﻟﻲ:
;P.Value = DBNull.Value
ﺤﻴﺙ ﺇﻥ Valueﻫﻲ ﺨﺎﺼﻴﺔ ﺜﺎﺒﺘـﺔ ﻟﻠﻘـﺭﺍﺀﺓ ﻓﻘـﻁ Static ReadOnly Property
ﻤﻌﺭﻓﺔ ﻓﻲ ﺍﻟﻔﺌﺔ DBNullﻟﺘﻌﻴﺩ ﻨﺴﺨﺔ ﺠﺩﻴﺩﺓ ﻤﻥ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﺍﻟﻘﻴﻤﺔ .DBNull
ﺍﻟﻨﻭﻉ :DbType
ﺘﺤﺩﺩ ﻨﻭﻉ ﺍﻟﻤﻌﺎﻤل ،ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ DbTypeﺍﻟﺘﺎﻟﻴﺔ:
٩٣
ﻤﺠﺎل ﺍﻟﻘﻴﻡ ﺃﻭ ﻁﻭل ﺍﻟﻤﺘﻐﻴﺭ ﻤﻌﻨﺎﻩ ﺍﻟﺜﺎﺒﺕ
ﻤﻥ ٩٢٢٣٣٧٢٠٣٦٨٥٤٧٧٥٨٠٨- ﻋﺩﺩ ﻁﻭﻴل Int64
ﺇﻟﻰ ٩٢٢٣٣٧٢٠٣٦٨٥٤٧٧٥٨٠٧
ﻤﻥ ٠ﺇﻟﻰ ﻋﺩﺩ ﻁﻭﻴل ﻤﻭﺠﺏ UInt64
١٨٤٤٦٧٤٤٠٧٣٧٠٩٥٥١٦١٥
ﻤﻥ (٤٥- ^ ١٠) × ١,٥ ﻋﺩﺩ ﻤﻔﺭﺩ Single
ﺇﻟﻰ (٣٨^١٠) × ٣,٤
ﺒﺩﻗﺔ ٧ﺨﺎﻨﺎﺕ ﻋﺸﺭﻴﺔ.
ﻤﻥ (٣٢٤-^١٠) × ٥,٠ ﻋﺩﺩ ﻤﺯﺩﻭﺝ Double
ﺇﻟﻰ (٣٠٨^١٠) × ١,٧
ﺒﺩﻗﺔ ١٥ﺨﺎﻨﺔ ﻋﺸﺭﻴﺔ.
ﻤﻥ ٦٣ ^ ٢-ﺇﻟﻰ )١- (٦٣ ^ ٢ ﻋﻤﻠﺔ Currency
ﺒﺩﻗﺔ ٤ﺨﺎﻨﺎﺕ ﻋﺸﺭﻴﺔ.
ﻤﻥ (٢٨-^١٠) × ١,٠ ﻋﺩﺩ ﻋﺸﺭﻱ Decimal
ﺇﻟﻰ (٢٨^١٠) × ٧,٩
ﺒﺩﻗﺔ ﺘﺼل ﺇﻟﻰ ٢٨ﺨﺎﻨﺔ ﻋﺸﺭﻴﺔ.
ﻗﻴﻤﺔ ﺭﻗﻤﻴﺔ ﻤﺘﻐﻴﺭﺓ ﺍﻟﻁﻭل ،ﺘﻘﺒل ﺃﻴﺎ ﻤﻥ ﺭﻗﻡ ﻤﺘﻐﻴﺭ VarNumeric
ﺍﻷﻨﻭﺍﻉ ﺍﻟﺴﺎﺒﻘﺔ.
ﻤﻌــﺭﻑ ﻋــﺎﻡ ﻤﺘﻔــﺭﺩ Guid
ﻭﻗﺕ ﺒﺩﻭﻥ ﺘﺎﺭﻴﺦ ﻭﻗﺕ Time
ﺘﺎﺭﻴﺦ ﺒﺩﻭﻥ ﻭﻗﺕ ﺘﺎﺭﻴﺦ Date
ﺘﻘﺒل ﺘﻭﺍﺭﻴﺦ ﺒﻴﻥ ١٧٥٣/١/١ ﺘﺎﺭﻴﺦ ﻭﻭﻗﺕ DateTime
ﻭ ٩٩٩٩/١٢/٣١
ﺘﻘﺒل ﺘﻭﺍﺭﻴﺦ ﺒﻴﻥ ١/١/١ ﺘﺎﺭﻴﺦ ﻭﻭﻗﺕ DateTime2
ﻭ ٩٩٩٩/١٢/٣١
٩٤
ﻤﺠﺎل ﺍﻟﻘﻴﻡ ﺃﻭ ﻁﻭل ﺍﻟﻤﺘﻐﻴﺭ ﻤﻌﻨﺎﻩ ﺍﻟﺜﺎﺒﺕ
DateTimeO
ﺇﺯﺍﺤﺔ ﺍﻟﻭﻗﺕ ﻭﺍﻟﺘﺎﺭﻴﺦ
ffset
ﻨﺹ ﻤﺘﻐﻴﺭ ﺍﻟﻁﻭل ،ﻤﻜﻭﻥ ﻤﻥ ﺤﺭﻭﻑ ﻨﺹ String
ﻤﻭﺴﻌﺔ .Unicode
ﻨﺹ ﺜﺎﺒﺕ ﺍﻟﻁﻭل ،ﻤﻜﻭﻥ ﻤﻥ ﺤﺭﻭﻑ ﻨﺹ ﺜﺎﺒﺕ ﺍﻟﻁﻭل StringFixed
Length
ﻤﻭﺴﻌﺔ .Unicode
ﻨﺹ ﻤﺘﻐﻴﺭ ﺍﻟﻁﻭل ،ﻤﻜﻭﻥ ﻤﻥ ﺤﺭﻭﻑ ﻨﺹ ﻗﻴﺎﺴﻲ AnsiString
ﻗﻴﺎﺴﻴﺔ ﺒﺘﺭﻤﻴﺯ .ASCII
ﻨﺹ ﺜﺎﺒﺕ ﺍﻟﻁﻭل ،ﻤﻜﻭﻥ ﻤﻥ ﺤﺭﻭﻑ ﻨﺹ ﻗﻴﺎﺴﻲ ﺜﺎﺒﺕ AnsiString
Fixed
ﻗﻴﺎﺴﻴﺔ ﺒﺘﺭﻤﻴﺯ .ASCII ﺍﻟﻁﻭل Length
٩٦
ﻭﺍﺠﻬﺔ ﻤﻌﺎﻤل ﺒﻴﺎﻨﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ
IDbDataParameter Interface
ﺍﻟﺤﺠﻡ :Size
ﺘﺤﺩﺩ ﺃﻗﺼﻰ ﺤﺠﻡ ﻤﺴﻤﻭﺡ ﺒﻪ ﻟﻠﻤﻌﺎﻤل ﺒﺎﻟﻭﺤﺩﺓ ﺍﻟﺜﻨﺎﺌﻴﺔ ..Byteﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ
ﺍﻟﺨﺎﺼﻴﺔ ﺘﺴﺘﻨﺘﺞ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﻌﺎﻤل ،ﻓﺈﻥ ﻜﺎﻥ ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜـﺎل ،ﺘﻜـﻭﻥ
ﻗﻴﻤﺘﻬﺎ ،٤ﻭﺇﻥ ﻜﺎﻥ ﺍﻟﻤﻌﺎﻤل ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻤﺼﻔﻭﻓﺔ ﻓﺈﻥ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﺘﺄﺨـﺫ ﻁـﻭل
ﺍﻟﻤﺼﻔﻭﻓﺔ ..ﻻﺤﻅ ﺃﻨﻙ ﻟﻭ ﺼﻐﺭﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻋﻥ ﺤﺠﻡ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﺴـﻴﺘﻡ ﺃﺨـﺫ
ﺠﺯﺀ ﻤﻥ ﻫﺫﻩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻘﻁ ﻭﺇﺴﻘﺎﻁ ﺍﻟﺠﺯﺀ ﺍﻟﺯﺍﺌﺩ.
ﺍﻟﺩﻗﺔ :Precision
ﺘﺤﺩﺩ ﺃﻜﺒﺭ ﻋﺩﺩ ﻤﺴﻤﻭﺡ ﺒﻪ ﻤﻥ ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﺭﻗﻤﻴﺔ ﻓﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﻴﻘﺒﻠﻬﺎ ﺍﻟﻤﻌﺎﻤل ..ﻭﺍﻟﻘﻴﻤﺔ
ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻫﻲ ،٠ﻭﻫﻲ ﺘﻌﻨﻲ ﻋﺩﻡ ﻓﺭﺽ ﻗﻴﻭﺩ ﻋﻠﻰ ﻋﺩﺩ ﺍﻟﺨﺎﻨﺎﺕ ،ﻭﺘﺭﻙ ﺫﻟﻙ ﻟﻤـﺯﻭﺩ
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﺍﻟﻤﻘﻴﺎﺱ :Scale
ﺘﺤﺩﺩ ﺃﻜﺒﺭ ﻋﺩﺩ ﻤﺴﻤﻭﺡ ﺒﻪ ﻤﻥ ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﻌﺸﺭﻴﺔ ﻓﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﻴﻘﺒﻠﻬﺎ ﺍﻟﻤﻌﺎﻤل ..ﻭﻟـﻭ
ﺯﺍﺩ ﻋﺩﺩ ﺍﻟﺨﺎﻨﺎﺕ ﻋﻥ ﻫﺫﺍ ﺍﻟﺭﻗﻡ ﻴﺘﻡ ﺘﻘﺭﻴﺒﻪ ..ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻫﻲ . ٠
٩٧
ﻓﺌﺔ ﻤﻌﺎﻤل ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ DbParameter Class
٩٨
ﻓﺌﺔ ﻤﻌﺎﻤل ﺴﻴﻜﻭﻴل SqlParameter Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ،DbParameterﻭﻫﻲ ﺘﻤﺜل ﻤﻌﺎﻤل ﺍﺴـﺘﻌﻼﻡ ﻤﻭﺠـﻪ ﺇﻟـﻰ ﺴـﻴﻜﻭﻴل
ﺴﻴﺭﻓﺭ.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺴﺒﻊ ﺼﻴﻎ ،ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ ،ﻭﺍﻟﺼﻴﻎ ﺍﻷﺨﺭﻯ ﺘﺘـﻴﺢ
ﻟﻙ ﺇﻤﺩﺍﺩ ﺍﻟﻤﻌﺎﻤل ﺒﺒﻌﺽ ﻗﻴﻡ ﺨﺼﺎﺌﺼﻪ ،ﻤﺜل ﺍﺴﻡ ﺍﻟﻤﻌﺎﻤل ﻭﻨﻭﻋﻪ ﻭﺍﺘﺠﺎﻫﻪ ..ﺇﻟـﺦ ..ﻋﻠـﻰ
ﺴﺒﻴل ﺍﻟﻤﺜﺎل ،ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻌﺔ ١٣ﻤﻌﺎﻤﻼ ﻫﻲ ﺒﺎﻟﺘﺭﺘﻴﺏ:
-ﺍﺴﻡ ﺍﻟﻤﻌﺎﻤل.
-ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ SqlDbTypeﺘﻭﻀﺢ ﻨﻭﻉ ﺍﻟﻤﻌﺎﻤل.
-ﻋﺩﺩ ﺼﺤﻴﺢ ﻴﻭﻀﺢ ﻁﻭل ﺍﻟﻤﻌﺎﻤل ﺇﺫﺍ ﻜﺎﻥ ﻨﺼﺎ ﺃﻭ ﻭﺤﺩﺍﺕ ﺜﻨﺎﺌﻴﺔ ..Bytesﺒﺎﻟﻨﺴـﺒﺔ
ﻟﻠﻤﻌﺎﻤﻼﺕ ﺍﻟﻌﺩﺩﻴﺔ ﺍﺴﺘﺨﺩﻡ ﺍﻟﻘﻴﻤﺔ ﺼﻔﺭ ..ﻭﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻌﺎﻤل ﻤﻥ ﺍﻷﻨـﻭﺍﻉ ﺍﻟﻘﺼـﻭﻯ
) ،(MAXﻓﺎﺴﺘﺨﺩﻡ ﺍﻟﻘﻴﻤﺔ .١-
-ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ParameterDirectionﺍﻟﺘﻲ ﺘﻭﻀﺢ ﺍﺘﺠﺎﻩ ﺍﻟﻤﻌﺎﻤل.
-ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ Byteﺘﻭﻀﺢ ﻋﺩﺩ ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﻌﺸـﺭﻴﺔ ﻟﻠﻤﻌﺎﻤـل ﺍﻟﺭﻗﻤـﻲ )ﺍﻟﺨﺎﺼـﻴﺔ
.(Precision
-ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ Byteﺘﻭﻀﺢ ﻋﺩﺩ ﺨﺎﻨﺎﺕ ﺍﻟﺘﻘﺭﻴﺏ ﺍﻟﻌﺸﺭﻱ ﻟﻠﻤﻌﺎﻤل ﺍﻟﺭﻗﻤﻲ )ﺍﻟﺨﺎﺼـﻴﺔ
.(Scale
-ﺍﺴﻡ ﻋﻤﻭﺩ ﺍﻟﻤﺼﺩﺭ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺍﻟﺫﻱ ﺴﻴﺄﺨﺫ ﻤﻨﻪ ﺍﻟﻤﻌﺎﻤل ﻗﻴﻤﺘﻪ.
-ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ،DataRowVersionﺘﻭﻀﺢ ﺇﺼﺩﺍﺭ ﺍﻟﻌﻤـﻭﺩ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﻤﺭﻗﻡ ﻓﻲ ﻓﺼل ﻻﺤﻕ.
-ﻗﻴﻤﺔ ﻤﻨﻁﻘﻴﺔ ،ﺇﺫﺍ ﺠﻌﻠﺘﻬﺎ ،trueﻓﺴﺘﻜﻭﻥ ﻭﻅﻴﻔﺔ ﻤﻌﺎﻤل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻥ ﻴﺘﺄﻜﺩ ﺃﻥ ﺍﻟﻌﻤـﻭﺩ
ﻟﻴﺱ ﻓﺎﺭﻏﺎ Nullﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺤﻴﺙ ﺘﻜﻭﻥ ﻗﻴﻤﺔ ﺍﻟﻤﻌﺎﻤل ٠ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻌﻤﻭﺩ
ﻓﺎﺭﻏﺎ ،ﻭﺘﻜﻭﻥ ﻗﻴﻤﺘﻪ ١ﺇﺫﺍ ﻟﻡ ﻴﻜﻥ ﻓﺎﺭﻏﺎ.
-ﻜﺎﺌﻥ Objectﻴﺤﻤل ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺘﻤﺭﻴﺭﻫﺎ ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ..ﻭﻋﻠﻴـﻙ ﺃﻥ ﺘﺴـﺘﺨﺩﻡ
ﺍﻟﻘﻴﻤﺔ ،nullﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻌﺎﻤل ﺴﻴﻘﺭﺃ ﺍﻟﻘﻴﻤﺔ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
٩٩
-ﻨﺹ ﺘﺭﺴل ﻗﻴﻤﺘﻪ ﺇﻟـﻰ ﺍﻟﺨﺎﺼـﻴﺔ XmlSchemaCollectionDatabaseﺍﻟﺘـﻲ
ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ.
-ﻨﺹ ﺘﺭﺴل ﻗﻴﻤﺘﻪ ﺇﻟﻰ ﺍﻟﺨﺎﺼﻴﺔ XmlSchemaCollectionOwningSchemaﺍﻟﺘـﻲ
ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ.
-ﻨﺹ ﺘﺭﺴل ﻗﻴﻤﺘﻪ ﺇﻟﻰ ﺍﻟﺨﺎﺼﻴﺔ XmlSchemaCollectionNameﺍﻟﺘﻲ ﺴـﻨﺘﻌﺭﻑ
ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ.
ﻭﻫﻨﺎﻙ ﺼﻴﻐﺔ ﺘﺴﺘﻘﺒل ﻓﻘﻁ ﺃﻭل ﺃﺭﺒﻌﺔ ﻤﻌﺎﻤﻼﺕ ﻤﻥ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ،ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻴﻜـﻭﻥ
ﺍﺘﺠﺎﻩ ﺍﻟﻤﻌﺎﻤل ﻟﻺﺩﺨﺎل ،ﻭﻴﻘﺭﺃ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺤﺎﻟﻴﺔ ﻟﻠﺴـﺠل ..Current Valueﻭﻫﻨـﺎﻙ ﺼـﻴﻐﺔ
ﺃﺨﺭﻯ ﺘﺴﺘﻘﺒل ﻜل ﻤﻌﺎﻤﻼﺕ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﻤﺎ ﻋﺩﺍ ﺁﺨﺭ ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ.
ﻭﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﻑ ﻤﻌﺎﻤﻼ ﻴﻘﺭﺃ ﻗﻴﻤﺘﻪ ﻤﻥ ﺍﻟﺤﻘل Bookﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ:
var PrmBook = new SqlParameter("@Book",
;)"SqlDbType.NVarChar, 0, "Book
ﻭﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﻑ ﻤﻌﺎﻤﻼ ﻴﺄﺨﺫ ﻗﻴﻤﺘﻪ ﻤﻥ ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼـﻠﻴﺔ ﻟﻠﺤﻘـل IDﻓـﻲ ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ:
var PrmID = new SqlParameter("@Original_ID",
SqlDbType.Int, 0, ParameterDirection.Input, false, 0,
;)0, "ID", DataRowVersion.Original, null
ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻴﻥ ﺍﻟﻤﻌﺎﻤﻠﻴﻥ ﻟﺘﻌﺭﻴﻑ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ Updateﻜﺎﻟﺘﺎﻟﻲ:
;) (SqlCommand UpdateCmd = new SqlCommand
UpdateCmd.CommandText = @"UPDATE Books
SET Book = @Book
;"WHERE ID = @Original_ID
;UpdateCmd.Connection = SqlConnection1
(UpdateCmd.Parameters.AddRange
;)}new SqlParameter[] {PrmBook, PrmID
ﻭﻴﻤﻜﻨﻙ ﺠﻌل ﻫﺫﺍ ﺍﻷﻤﺭ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﺍﻟﺨﺎﺹ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﺎﻟﺘﺎﻟﻲ:
;DaAuthorBooks.UpdateCommand = UpdateCmd
ﻭﺴــﺘﺠﺩ ﻫــﺫﺍ ﺍﻟﻜــﻭﺩ ﻓــﻲ ﺤــﺩﺙ ﺘﺤﻤﻴــل ﺍﻟﻨﻤــﻭﺫﺝ Loadﻓــﻲ ﺍﻟﻤﺸــﺭﻭﻉ
..ViewAndEditBooksﻭﻴﻤﻜﻨﻙ ﻨﻘل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ
١٠٠
ﺒﻀﻐﻁ ﺯﺭ ﺍﻟﺤﻔﻅ ،ﺤﻴﺙ ﻴﻘﻭﻡ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ DaAuthorBooksﺒﺘﻨﻔﻴﺫ ﺍﻟﻭﺴﻴﻠﺔ ،Update
ﺍﻟﺘﻲ ﺘﺴﺘﺨﺩﻡ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﻲ ﻋﺭﻓﻨﺎﻫﺎ.
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ،ﺘﻤﺘﻠﻙ ﺍﻟﻔﺌﺔ SqlParameterﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
ﺍﻹﺯﺍﺤﺔ :Offset
ﺘﺤﺩﺩ ﻤﻭﻀﻊ ﺒﺩﺍﻴﺔ ﺍﻟﻘﺭﺍﺀﺓ ﻤﻥ ﺍﻟﺨﺎﺼﻴﺔ ..Valueﻭﺘﻘﺎﺱ ﺍﻹﺯﺍﺤـﺔ ﺒﻌـﺩﺩ ﺍﻟﻭﺤـﺩﺍﺕ
ﺍﻟﺜﻨﺎﺌﻴﺔ Bytesﻋﻨﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺒﻴﺎﻨﺎﺕ ﺜﻨﺎﺌﻴﺔ ،ﻭﺒﻌـﺩﺩ ﺍﻟﺤـﺭﻭﻑ Charactersﻋﻨـﺩ
ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻨﺼﻭﺹ ..ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻫﻲ ،٠ﺃﻱ ﺃﻥ ﺍﻟﻘـﺭﺍﺀﺓ ﺘـﺘﻡ ﻤـﻥ ﺒﺩﺍﻴـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺘﺤﺩﺩ ﺍﻟﺨﺎﺼﻴﺔ Sizeﻋﺩﺩ ﺍﻟﺤﺭﻭﻑ ﺃﻭ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ﺍﻟﺘﻲ ﺴﺘﺘﻡ ﻗﺭﺍﺀﺘﻬﺎ ﺒﺩﺀﺍ ﻤـﻥ
ﺍﻟﻤﻭﻀﻊ ..Offsetﻫﺫﺍ ﻤﻔﻴﺩ ﺇﺫﺍ ﻭﻀﻌﺕ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ Valueﻜﻤﺎ ﻜﺒﻴﺭﺍ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ،
ﻭﺃﺭﺩﺕ ﺘﺠﺯﺌﺘﻬﺎ ﺩﻭﻥ ﺍﻟﺤﺎﺠﺔ ﺇﻟﻰ ﻤﺘﻐﻴﺭﺍﺕ ﻭﺴﻴﻁﺔ ﻭﺤﻴل ﺒﺭﻤﺠﻴﺔ ﻤﻠﺘﻭﻴﺔ ..ﻓـﻲ ﻫـﺫﻩ
ﺍﻟﺤﺎﻟﺔ ﺴﺘﻌﻁﻲ ﻟﻠﺨﺎﺼﻴﺔ Offsetﻤﺒﺩﺌﻴﺎ ﺍﻟﻘﻴﻤﺔ ﺼﻔﺭ ﻭﻟﻠﺨﺎﺼـﻴﺔ Sizeﺍﻟﻁـﻭل ﺍﻟـﺫﻱ
ﺘﺭﻴﺩﻩ )ﻭﻟﻴﻜﻥ ..(١٠٠ﺒﻌﺩ ﻫﺫﺍ ﺘﺴﺘﺨﺩﻡ ﺤﻠﻘﺔ ﺘﻜﺭﺍﺭ Loopﺘﻨﻔﺫ ﻓﻴﻬﺎ ﺍﻷﻤﺭ ﻋﻠﻰ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ )ﺤﻴﺙ ﺴﻴﻘﺭﺃ ﺍﻷﻤﺭ ﺍﻟﺠﺯﺀ ﺍﻟﻤﺤﺩﺩ ﻓﻘﻁ ﻓﻲ ﺍﻟﻤﻌﺎﻤل ﺒﺩﺀﺍ ﻤﻥ ﺍﻟﻤﻭﻀـﻊ Offset
١٠١
ﻭﺒﺎﻟﻁﻭل ،(Sizeﻭﻤﻥ ﺜﻡ ﺘﺯﻴﺩ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ Offsetﺒﻤﻘﺩﺍﺭ ١٠٠ﻟﻘﺭﺍﺀﺓ ﺠﺯﺀ ﺘﺎل..
ﻭﻴﺴﺘﻤﺭ ﺍﻟﺩﻭﺭﺍﻥ ﻭﺘﻨﻔﻴﺫ ﻫﺫﻩ ﺍﻟﻌﻤﻠﻴﺔ ﺇﻟﻰ ﺃﻥ ﺘﺘﺠﺎﻭﺯ ﻁـﻭل ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓـﻲ
ﺍﻟﺨﺎﺼﻴﺔ ..Valueﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘـﺔ ﻤﻨﺎﺴـﺒﺔ ﻟﻼﺴـﺘﺨﺩﺍﻡ ﻓﻘـﻁ ﻤـﻊ ﺍﻷﻤـﺭ
..Update .Writeﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫـﺫﺍ ﻓـﻲ ﺍﻟـﺯﺭ Parameter.Offsetﻓـﻲ
ﺍﻟﻤﺸﺭﻭﻉ ،Write Large Dataﻭﻫﻭ ﻴﺴﻤﺢ ﻟﻙ ﺒﺈﻀﺎﻓﺔ ﺼﻭﺭﺓ ﺸﻌﺎﺭ ﻓـﻲ ﺍﻟﻌﻤـﻭﺩ
Logo2ﻟﻠﻨﺎﺸﺭ ﺍﻟﺜﺎﻨﻲ ..ﻻﺤﻅ ﺃﻨﻨﺎ ﺤﻤﻠﻨﺎ ﻜل ﻤﺤﺘﻭﻴﺎﺕ ﻤﻠﻑ ﺍﻟﺼـﻭﺭﺓ ﻤـﺭﺓ ﻭﺍﺤـﺩﺓ
ﻭﻭﻀﻌﻨﺎﻫﺎ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ Valueﻟﻠﻤﻌﺎﻤل ،ﻭﻤﻥ ﺜﻡ ﺃﺭﺴﻠﻨﺎﻫﺎ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻋﻠـﻰ
ﺃﺠﺯﺍﺀ ﺼﻐﻴﺭﺓ ..ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻴﺏ ﻜﺒﻴﺭ ،ﻭﻫـﻭ ﺃﻥ ﺍﻟﺼـﻭﺭﺓ ﺇﺫﺍ ﻜﺎﻨـﺕ
ﻀﺨﻤﺔ ﺠﺩﺍ ،ﻓﺴﺘﺴﺘﻬﻠﻙ ﻤﺴﺎﺤﺔ ﻜﺒﻴﺭﺓ ﻤﻥ ﺍﻟﺫﺍﻜﺭﺓ ﻭﻗﺩ ﺘﺴﺒﺏ ﺒﻁﺀ ﺍﻟﺒﺭﻨـﺎﻤﺞ ..ﻟﻬـﺫﺍ ﻻ
ﺘﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﺇﻻ ﺇﺫﺍ ﻜﺎﻥ ﺤﺠﻡ ﺍﻟﺼﻭﺭﺓ ﻤﻌﻘﻭﻻ ،ﺃﻤﺎ ﺇﺫﺍ ﻜﺎﻥ ﻀﺨﻤﺎ ،ﻓﺎﻷﻓﻀـل
ﺃﻥ ﺘﺤﻤل ﺃﺠﺯﺍﺀ ﻤﻥ ﺍﻟﻤﻠﻑ ﻭﺘﺭﺴﻠﻬﺎ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺍﺴﺘﺨﺩﻤﻨﺎﻫﺎ ﻓـﻲ
ﺍﻟﺯﺭ Update .Writeﻓﻲ ﻨﻔﺱ ﺍﻟﺒﺭﻨﺎﻤﺞ.
١٠٣
ﺍﺴﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﺨﻁﻁﺎﺕ :XmlSchemaCollectionName
ﺘﻌﻴﺩ ﺍﺴﻡ ﻤﺠﻤﻭﻋﺔ ﻤﺨﻁﻁﺎﺕ .XML
:XmlSchemaCollectionOwningSchema
ﺘﻌﻴﺩ ﻤﺨﻁﻁ ﺍﻟﻌﻼﻗﺎﺕ ﺍﻟﺭﺌﻴﺴﻲ ،ﺍﻟﺫﻱ ﻴﺤﺩﺩ ﻤﻭﻀﻊ ﻤﺠﻤﻭﻋﺔ ﻤﺨﻁﻁﺎﺕ .XML
١٠٤
-٨-
ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ DataReader
١٠٥
-٢ﻟﻭ ﻜﻨﺕ ﺴﺘﻘﺭﺃ ﻜل ﺴﺠل ﻤﺭﺓ ﻭﺍﺤﺩﺓ ﻓﻘﻁ ،ﻭﻻ ﻴﻌﻨﻴﻙ ﺍﻟﺭﺠﻭﻉ ﺇﻟﻴﻪ ﻤﺭﺓ ﺃﺨﺭﻯ.
-٣ﻟﻭ ﻜﺎﻨﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻭﺠﻭﺩﺓ ﻋﻠﻰ ﻨﻔﺱ ﺍﻟﺠﻬﺎﺯ ،ﻤﻤﺎ ﻴﻌﻨﻲ ﺴﺭﻋﺔ ﺍﻟﺤﺼﻭل ﻋﻠﻰ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻨﻬﺎ ﻤﺒﺎﺸﺭﺓ ،ﺩﻭﻥ ﺍﻟﺤﺎﺠﺔ ﺇﻟﻰ ﺘﺤﻤﻴﻠﻬﺎ ﻓﻲ ﺍﻟﺫﺍﻜﺭﺓ.
-٤ﻋﻨﺩﻤﺎ ﺘﺭﻴﺩ ﻗﺭﺍﺀﺓ ﺍﻟﻨﺘﺎﺌﺞ ﺩﻭﻥ ﺍﻟﺤﺎﺠﺔ ﺇﻟﻰ ﺘﻐﻴﻴﺭ ﺃﻱ ﺠﺯﺀ ﻤﻨﻬﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
١٠٦
ﻭﺍﺠﻬﺔ ﺴﺠل ﺍﻟﺒﻴﺎﻨﺎﺕ IDataRecord Interface
ﺘﻘﺩﻡ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺨﺼﺎﺌﺹ ﻭﺍﻟﻭﺴﺎﺌل ﺍﻟﻼﺯﻤﺔ ﻟﻘﺭﺍﺀﺓ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻓـﻲ ﻗـﺎﺭﺉ
ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
ﺍﻟﻤﻔﻬﺭﺱ :Indexer
ﻴﺴﺘﻘﺒل ﺭﻗﻡ ﺍﻟﻌﻤﻭﺩ ﺃﻭ ﺍﺴﻤﻪ ﻜﻤﻌﺎﻤل ،ﻭﻴﻌﻴـﺩ ﻜﺎﺌﻨـﺎ Objectﻴﺤﺘـﻭﻱ ﻋﻠـﻰ ﺍﻟﻘﻴﻤـﺔ
ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ..ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﺽ ﻗﻴﻤﺔ ﺍﻟﺨﺎﻨﺔ ﺍﻻﻭﻟﻰ
ﻓﻲ ﺍﻟﺼﻑ:
;)) (MessageBox.Show(Reader[0].ToString
١٠٨
-ﻋﺩﺩ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ Bytesﺍﻟﻤﻁﻠﻭﺏ ﻗﺭﺍﺀﺘﻬﺎ ﻤﻥ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻌﻤﻭﺩ ،ﺒﺩﺀﺍ ﻤـﻥ
ﺍﻟﻤﻭﻀﻊ ﺍﻟﻤﺤﺩﺩ ﻓﻲ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ..ﻻﺤﻅ ﺃﻥ ﺨﻁﺄ ﺴﻴﺤﺩﺙ ﻟﻭ ﻜـﺎﻥ ﺍﻟﻁـﻭل
ﺍﻟﻤﻁﻠﻭﺏ ﺃﻜﺒﺭ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺘﺒﻘﻴﺔ ﻓﻲ ﺍﻟﻌﻤﻭﺩ.
ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺘﻲ ﺘﻡ ﻨﺴﺨﻬﺎ ﺇﻟﻰ ﺍﻟﻤﺼﻔﻭﻓﺔ ،ﻭﻟﻭ ﺃﺭﺴﻠﺕ ﺇﻟﻰ ﻫـﺫﻩ
ﺍﻟﻭﺴﻴﻠﺔ ﻤﺼﻔﻭﻓﺔ ﻓﺎﺭﻏﺔ ،nullﻓﺴﺘﻌﻴﺩ ﺇﻟﻴﻙ ﺍﻟﻌﺩﺩ ﺍﻹﺠﻤﺎﻟﻲ ﻟﻠﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴـﺔ ﺍﻟﻤﺘﺎﺤـﺔ
ﻓﻲ ﺍﻟﺨﺎﻨﺔ.
ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ReadLargeDataﻟﻘﺭﺍﺀﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺼـﻭﺭﺓ،
ﺤﻴﺙ ﻨﻘﺭﺃ ١٠٠ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ Byteﻤﻥ ﺒﺩﺍﻴﺔ ﺍﻟﺼﻭﺭﺓ ﻭﻨﺤﻔﻅﻬﺎ ﻓﻲ ﺍﻟﻤﻠﻑ ،ﺜـﻡ ﻨﻘـﺭﺃ
١٠٠ﻭﺤﺩﺓ ﺘﺎﻟﻴﺔ ﻭﻨﺤﻔﻅﻬﺎ ﻓﻲ ﺍﻟﻤﻠﻑ ،ﻭﻨﺴﺘﻤﺭ ﻓﻲ ﻓﻌل ﻫـﺫﺍ ﺇﻟـﻰ ﺃﻥ ﻨﻜﻤـل ﻗـﺭﺍﺀﺓ
ﺍﻟﺼﻭﺭﺓ ..ﻻﺤﻅ ﺃﻥ ﺸﺭﻁ ﺍﻟﺘﻭﻗﻑ ﻋﻥ ﺍﻟﻘﺭﺍﺀﺓ ،ﻫﻭ ﺃﻥ ﺘﻜﻭﻥ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻌﺎﺌﺩﺓ ﻤﻥ ﺍﻟﻭﺴـﻴﻠﺔ
GetBytesﺃﺼﻐﺭ ﻤﻥ ﻋﺩﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﻁﻠﺒﻨﺎ ﻗﺭﺍﺀﺘﻪ ،ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﻫﺫﻩ ﻫـﻲ ﺁﺨـﺭ
ﺒﻴﺎﻨﺎﺕ ﻤﺘﺎﺤﺔ ﻓﻲ ﺍﻟﺨﺎﻨﺔ.
ﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﻓﺸﻠﺕ ﻓﻲ ﺘﺤﻭﻴل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﺍﻟﻨﻭﻉ ﺍﻟﻤﻁﻠﻭﺏ.
١١٠
ﻓﺌﺔ ﺴﺠل ﺍﻟﺒﻴﺎﻨﺎﺕ DbDataRecord Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ،IDataRecordﻭﻫﻲ ﺘﻤﺘﻠﻙ ﻜل ﻭﺴﺎﺌﻠﻬﺎ ﻭﺨﺼﺎﺌﺼﻬﺎ ﺩﻭﻥ ﺃﻥ ﺘﺯﻴﺩ
ﻋﻠﻴﻬﺎ ﺸﻴﺌﺎ.
ﻭﺘﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻤﻊ ﻭﺍﺠﻬﺔ ﺍﻟﻌﺩﺍﺩ ﻟﻠﻤﺭﻭﺭ ﻋﺒﺭ ﺴﺠﻼﺕ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻜﻤﺎ ﺴﻨﺭﻯ ﻻﺤﻘﺎ.
١١١
ﻭﺍﺠﻬﺔ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ IDataReader Interface
ﺍﻟﻌﻤﻕ :Depth
ﺘﻌﻴﺩ ﺭﻗﻤﺎ ﻴﻤﺜل ﻋﻤﻕ ﺍﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ ،ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﻨﺘﻴﺠﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺠـﺩﺍﻭل ﻤﺘﺩﺍﺨﻠـﺔ
)ﺨﺎﻨﺎﺕ ﺒﻬﺎ ﺠﺩﺍﻭل ،ﺒﻬﺎ ﺨﺎﻨﺎﺕ ﺒﻬﺎ ﺠﺩﺍﻭل ...ﺇﻟﺦ( ،ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﺍﻟﺠﺩﻭل ﺍﻟﺨـﺎﺭﺠﻲ
ﻴﻜﻭﻥ ﻋﻤﻘﻪ ﺼﻔﺭﺍ ،ﻭﺃﻭل ﺠﺩﻭﻻ ﺩﺍﺨﻠﻲ ﻋﻤﻘﻪ ... ١ﻭﻫﻜﺫﺍ.
١١٢
ﻗﺭﺍﺀﺓ :Read
ﺘﺠﻌل ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﻨﺘﻘل ﺇﻟﻰ ﺍﻟﺴﺠل ﺍﻟﺘﺎﻟﻲ ،ﻭﺘﻌﻴﺩ ..trueﺃﻤﺎ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠل ﺍﻟﺤـﺎﻟﻲ
ﻫﻭ ﺁﺨﺭ ﺴﺠل ﻭﻻ ﻴﻭﺠﺩ ﺴﺠل ﺘﺎل ،ﻓﺈﻨﻬﺎ ﺘﻌﻴﺩ ،falseﻭﻋﻠﻴﻙ ﺍﻟﺘﻭﻗﻑ ﻋﻥ ﺍﻟﻘﺭﺍﺀ ﻓـﻲ
ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ،ﻭﺇﻻ ﺤﺩﺙ ﺨﻁﺄ.
ﻻﺤﻅ ﺃﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺸﻴﺭ ﻤﺒﺩﺌﻴﺎ ﺇﻟﻰ ﺍﻟﺴﺠل ﺭﻗﻡ ،١-ﺃﻱ ﺃﻨﻪ ﻴﺸﻴﺭ ﺇﻟـﻰ ﺍﻟﺴـﺠل
ﺍﻟﺴﺎﺒﻕ ﻷﻭل ﺴﺠل ،ﻭﻫﺫﺍ ﺴﻴﺠﻌل ﻤﺤﺎﻭﻟﺔ ﺍﻟﻘﺭﺍﺀﺓ ﺘﺴﺒﺏ ﺨﻁﺄ ﻓـﻲ ﺍﻟﺒﺭﻨـﺎﻤﺞ ،ﺤﻴـﺙ
ﺴﺘﺨﺒﺭﻙ ﺭﺴﺎﻟﺔ ﺍﻟﺨﻁﺄ ﺃﻥ ﻫﺫﻩ ﻤﺤﺎﻭﻟﺔ ﻏﻴﺭ ﻤﺴﻤﻭﺡ ﺒﻬﺎ ﻟﻠﻘﺭﺍﺀﺓ ﺒﻴﻨﻤﺎ ﻻ ﺘﻭﺠﺩ ﺒﻴﺎﻨـﺎﺕ
ﺤﺎﻟﻴﺎ:
Invalid attempt to read when no data is present.
ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ Readﺃﻭﻻ ﻟﻼﻨﺘﻘﺎل ﺇﻟﻰ ﺃﻭل ﺴﺠل ﻭﻗﺭﺍﺀﺘﻪ ،ﺜﻡ ﺍﻻﺴﺘﻤﺭﺍﺭ
ﻓﻲ ﺍﺴﺘﺩﻋﺎﺌﻬﺎ ﺇﻟﻰ ﺃﻥ ﺘﻌﻴﺩ ،falseﻭﺫﻟﻙ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺘﺎﻟﻴﺔ:
{ )) (while (Reader.Read
ﺍﻟﻜﻭﺩ ﺍﻟﻼﺯﻡ ﻟﻘﺭﺍﺀﺓ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ //
}
ﺇﻏﻼﻕ :Close
ﺘﻐﻠﻕ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻫﺫﺍ ﻀﺭﻭﺭﻱ ﻟﺘﺤﺭﻴﺭ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﻤﺭﺘﺒﻁ ﺒﻘـﺎﺭﺉ ﺍﻟﺒﻴﺎﻨـﺎﺕ،
ﻷﻨﻙ ﻟﻥ ﺘﺴﺘﻁﻴﻊ ﺍﺴﺘﺨﺩﺍﻡ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﻓﻲ ﺃﻱ ﻋﻤﻠﻴﺔ ﺃﺨﺭﻯ ﻁﺎﻟﻤﺎ ﻜﺎﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ
ﻴﺴﺘﺨﺩﻤﻪ.
ﻭﻜﻤﺎ ﺫﻜﺭﻨﺎ ﻤﻥ ﻗﺒل ،ﻟﻭ ﺃﻨﺸﺄﺕ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ ExecuteReader
ﺒﺎﻟﺼﻴﻐﺔ ﺍﻟﺘﺎﻟﻴﺔ )ﺤﻴﺙ Cmdﻫﻭ ﺍﺴﻡ ﻜﺎﺌﻥ ﺍﻷﻤﺭ(:
(var Dr = Cmd.ExecuteReader
;)CommandBehavior.CloseConnection
ﻓﺈﻥ ﻜﺎﺌﻥ ﺍﻷﻤﺭ Drﺴﻴﻘﻭﻡ ﺒﺈﻏﻼﻕ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﻠﻘﺎﺌﻴﺎ ﺒﻤﺠـﺭﺩ ﺍﺴـﺘﺩﻋﺎﺀ
ﺍﻟﻭﺴﻴﻠﺔ .Close
١١٤
ﻓﺌﺔ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ DbDataReader Class
١١٦
ﻭﺴﻨﻜﺘﻔﻲ ﻫﻨﺎ ﺒﺎﻟﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ ،SqlDataReaderﻭﺴﻨﺘﻌﺭﻑ ﻓﻲ ﻓﺼل ﻻﺤﻕ ﻋﻠﻰ ﺍﻟﻔﺌﺔ
.DataTableReader
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ DbDataReaderﺒﻜل ﻭﺴﺎﺌﻠﻬﺎ ﻭﺨﺼﺎﺌﺼﻬﺎ ،ﻭﻫﻲ ﺘﻤﻜﻨﻙ ﻤﻥ ﻗـﺭﺍﺀﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻘﺎﺩﻤﺔ ﻤﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل.
ﻭﻟﻴﺱ ﻟﻬﺫﻩ ﺍﻟﻔﺌﺔ ﺤﺩﺙ ﺇﻨﺸﺎﺀ ،ﻭﻟﻜﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻨﺴﺨﺔ ﻤﻨﻬﺎ ﺒﺎﺴـﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ
ExecuteReaderﺍﻟﺨﺎﺼﺔ ﺒﺄﻤﺭ ﺴﻴﻜﻭﻴل .SqlCommand
ﻭﻻ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﻴﺔ ﺨﺼﺎﺌﺹ ﺠﺩﻴﺩﺓ ﻏﻴﺭ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ،ﻭﻟﻜﻨﻬﺎ ﺘﻤﺘﻠﻙ ﺍﻟﻌﺩﻴﺩ ﻤﻥ
ﺍﻟﻭﺴﺎﺌل ﺍﻟﺠﺩﻴﺩﺓ ،ﻭﻫﻲ ﺘﻘﻭﻡ ﺒﻘﺭﺍﺀﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﺃﺭﺴﻠﺕ ﺭﻗﻤﻪ ﺇﻟﻴﻬـﺎ ﻜﻤﻌﺎﻤـل،
ﻭﺘﺤﻭﻟﻬﺎ ﺇﻟﻰ ﻨﻭﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻁﻠﻭﺏ ..ﻭﻷﺴﻤﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺼﻴﻐﺔ ﺍﻟﻌﺎﻤﺔ ،GetXﺤﻴـﺙ
Xﻫﻭ ﺍﺴﻡ ﻨﻭﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺍﻟﺘﺤﻭﻴل ﺇﻟﻴﻪ ..ﻭﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﻫﻲ:
GetSqlBoolean GetSqlBinary
GetSqlBytes GetSqlByte
GetSqlDateTime GetSqlChars
GetSqlDouble GetSqlDecimal
GetSqlInt16 GetSqlGuid
GetSqlInt64 GetSqlInt32
GetSqlSingle GetSqlMoney
GetSqlValue GetSqlString
GetSqlXml GetSqlValues
GetTimeSpan
١١٧
،(Logoﻭﺤﻔﻅﻬﺎ ﻓﻲ ﻤﻠﻑ ..ﻻﺤﻅ ﺃﻥ ﻋﻤﻠﻴﺔ ﺍﻟﻘﺭﺍﺀﺓ ﺴﺘﺘﻡ ﻫﻨـﺎ ﺒﻁﺭﻴﻘـﺔ ﻤﺒﺎﺸـﺭﺓ )ﻏﻴـﺭ
ﺘﺘﺎﺒﻌﻴﺔ( ،ﻭﺃﻥ ﺤﺠﻡ ﺍﻟﺼﻭﺭﺓ ﺴﻴﺅﺜﺭ ﻋﻠﻰ ﻜﻔﺎﺀﺓ ﻫﺫﻩ ﺍﻟﻌﻤﻠﻴﺔ ،ﻓﻠﻭ ﻜﺎﻨﺕ ﻀﺨﻤﺔ ﻓﺴﻴﺄﺨﺫ ﻨﻘﻠﻬـﺎ
ﻭﻗﺘﺎ ﻁﻭﻴﻼ ،ﻭﺴﻴﺘﻡ ﺘﺤﻤﻴﻠﻬﺎ ﻓﻲ ﺍﻟﺫﺍﻜﺭﺓ ﻜﺎﻤﻠﺔ ﻗﺒل ﺤﻔﻅﻬﺎ ﻓﻲ ﺍﻟﻤﻠﻑ!
ﺒﻬﺫﺍ ﻨﻜﻭﻥ ﻗﺩ ﺃﻜﻤﻠﻨﺎ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻰ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻟﻠﺘﺩﺭﻴﺏ ﻋﻠﻰ ﻤـﺎ ﺘﻌﻠﻤﻨـﺎﻩ ﺤﺘـﻰ ﺍﻵﻥ،
ﻴﻤﻜﻨﻙ ﻓﺤﺹ ﺍﻟﻤﺸﺭﻭﻉ ..AuthorBooks_Readerﻓﻲ ﻫﺫﺍ ﺍﻟﻤﺸﺭﻭﻉ ﻨﺴـﻤﺢ ﻟﻠﻤﺴـﺘﺨﺩﻡ
ـﺯﺭ
ـﻐﻁ ﺍﻟــ
ـﺩﻤﺎ ﻴﻀــ
ـﺹ ،ﻭﻋﻨــ
ـﻊ ﻨــ
ـﻲ ﻤﺭﺒــ
ـﻑ ﻓــ
ـﻡ ﺍﻟﻤﺅﻟــ
ـﺔ ﺍﺴــ
ﺒﻜﺘﺎﺒــ
"ﻋﺭﺽ ﺍﻟﻜﺘﺏ" ،ﻨﺴﺘﺨﺩﻡ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻜﺘﺎﺒﺔ ﻜﺘﺏ ﻫﺫﺍ ﺍﻟﻤﺅﻟﻑ ﻓـﻲ ﻤﺭﺒـﻊ ﻨـﺹ ﻤﺘﻌـﺩﺩ
ﺍﻷﺴﻁﺭ.
ﻻ ﺘﻨﺱ ﻨﺴﺦ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ ﻤﻥ ﺍﻟﻘﺭﺹ ﺍﻟﻀﻭﺌﻲ ﺇﻟﻰ ﺍﻟﻤﺤﺭﻙ C:ﻭﺍﻟﺘﺄﻜـﺩ ﻤـﻥ ﺃﻨﻬـﺎ
ﻟﻴﺴﺕ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ ،ﻟﻜﻲ ﻴﻌﻤل ﺍﻟﻤﺜﺎل ﺒﺸﻜل ﺼﺤﻴﺢ.
ﻤﻠﺤﻭﻅﺔ:
ﻻﺴﺘﺨﺩﺍﻡ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ GetAuthorBooksﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻜﺘﺏ ﺍﻟﻤﺅﻟﻑ ﺍﻟﺫﻱ ﺘﺭﺴـل
ـﺭﻭﻉ
ـﻰ ﺍﻟﻤﺸـ
ـﺔ ﻋﻠـ
ـﺩﻴﻼﺕ ﺍﻟﺘﺎﻟﻴـ
ـﺭ ﺍﻟﺘﻌـ
ـل ،ﺃﺠـ
ـﻤﻪ ﻜﻤﻌﺎﻤـ
ـﺭﺍﺀ ﺍﺴـ
ـﺫﺍ ﺍﻹﺠـ
ـﻰ ﻫـ
ﺇﻟـ
:AuthorBooks_Reader
-ﻀﻊ ﻓﻲ ﺍﻟﺨﺎﺼ ﻴﺔ CommandTextﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻷﻤﺭ ﺍﺴـﻡ ﺍﻹﺠـﺭﺍﺀ ﺍﻟﻤﺨـ ﺯﻥ
.GetAuthorBooks
-ﻀــﻊ ﻓــﻲ ﺍﻟﺨﺎﺼــﻴﺔ CommandTypeﺍﻟﺨﺎﺼــﺔ ﺒﻜــﺎﺌﻥ ﺍﻷﻤــﺭ ﺍﻟﻘﻴﻤــﺔ
.StoredProcedure
-ﻻ ﹸﺘﹸﺠﺭِ ﺃﻴﺔ ﺘﻌﺩﻴﻼﺕ ﻋﻠﻰ ﺍﻟﻤﻌﺎﻤل @Authorﺍﻟﺫﻱ ﺃﻀﻔﻨﺎﻩ ﺇﻟﻰ ﻤﺠﻤﻭﻋـﺔ ﻤﻌـﺎﻤﻼﺕ
ﻜﺎﺌﻥ ﺍﻷﻤﺭ.
ﻫﺫﺍ ﻓﻘﻁ ﻫﻭ ﻜل ﺍﻟﻤﻁﻠﻭﺏ ،ﻭﺴﻴﻌﻤل ﺍﻟﺒﺭﻨﺎﻤﺞ ﺒﺸﻜل ﺴﻠﻴﻡ ،ﻭﺴﻴﻌﻁﻲ ﻨﻔﺱ ﺍﻟﻨﺘﺎﺌﺞ ﺍﻟﺘﻲ ﻜـﺎﻥ
ﻴﻌﻁﻴﻬﺎ ﺴﺎﺒﻘﺎ ،ﻤﻊ ﺍﺨﺘﻼﻑ ﻭﺍﺤﺩ :ﺃﻨﻪ ﻴﺴﺘﺨﺩﻡ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ﺒﺩﻻ ﻤﻥ ﺠﻤﻠﺔ .SQL
ﻭﺍﻟﻤﺸﺭﻭﻉ AuthorBooks_Reader2ﻴﺤﺘﻭﻱ ﺒﺎﻟﻔﻌل ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﺘﻌﺩﻴﻼﺕ.
١١٨
-٩-
ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ DataAdapter
ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻫﻭ ﻤﺠﺭﺩ ﺤﻠﻘﺔ ﻭﺼل ﺒﻴﻥ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل Connection Objectﻭﻤﺠﻤﻭﻋﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ ،DataSetﻭﻤﻬﻤﺘﻪ ﻫﻲ ﺇﻴﺼﺎل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺍﻟﺨـﺎﺩﻡ ﺇﻟـﻰ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺃﻭ
ﺍﻟﻌﻜﺱ.
ﻭﻴﺘﻜــﻭﻥ ﻤﻬﻴــﺊ ﺍﻟﺒﻴﺎﻨــﺎﺕ ﻓــﻲ ﺍﻟﺤﻘﻴﻘــﺔ ﻤــﻥ ﺃﺭﺒﻌــﺔ ﻜﺎﺌﻨــﺎﺕ ﺃﻭﺍﻤــﺭ
،Command Objectsﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺠﻤل SQLﺍﻟﻼﺯﻤﺔ ﻟﺘﺤﺩﻴﺩ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤـﻥ ﺍﻟﺨـﺎﺩﻡ
،SELECTﻭﺘﺤــــﺩﻴﺜﻬﺎ UPDATEﻭﺇﺩﺭﺍﺝ ﺴــــﺠﻼﺕ ﺠﺩﻴــــﺩﺓ INSERT
ﻭﺤﺫﻑ ﺴﺠﻼﺕ ﻤﻭﺠﻭﺩﺓ ،DELETEﻭﺒﻬﺫﺍ ﻴﺘﻴﺢ ﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺤﺭﻴـﺔ ﺍﻟﺘﻌﺎﻤـل ﻤـﻊ
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻜﻼ ﺍﻻﺘﺠﺎﻫﻴﻥ )ﺍﻻﺴﺘﻘﺒﺎل ﻭﺍﻹﺭﺴـﺎل( ،ﻋﻠـﻰ ﻋﻜـﺱ ﻗـﺎﺭﺉ ﺍﻟﺒﻴﺎﻨـﺎﺕ
،DataReaderﺍﻟﺫﻱ ﻴﻘﺭﺃ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻘﻁ.
ﻓﻲ ﺍﻟﺤﻘﻴﻘﺔ ،ﻓﺈﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺴﺘﺨﺩﻡ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ Data Readerﺩﺍﺨﻠﻴﺎ ﻟﺘﻨﻔﻴـﺫ ﺃﻤـﺭ
ﺍﻟﺘﺤﺩﻴﺩ ..ﻟﻜﻥ ﻫﺫﺍ ﻤﺠﺭﺩ ﺠﺯﺀ ﻤﻥ ﻗﺩﺭﺍﺕ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﻬﻭ ﻴﺴﺘﻁﻴﻊ ﺇﺭﺴﺎل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻤﻥ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻜﻤﺎ ﻴﺘﻴﺢ ﻟﻙ ﻋﻤل ﺨﺭﺍﻁ ﻟﻠﺠﺩﺍﻭل Table Mapping
،ﻭﺫﻟﻙ ﺒﺈﻋﺎﺩﺓ ﺘﺴﻤﻴﺔ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺒﺄﺴﻤﺎﺀ ﺨﺎﺼﺔ ﺒﻙ ،ﻭﺭﺒﻁﻬﺎ ﺒﺎﻷﺴﻤﺎﺀ ﺍﻟﺤﻘﻴﻘﻴﺔ ﻓـﻲ
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻫﻭ ﻤﺎ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻪ ﺒﺎﻟﺘﻔﺼﻴل ﻓﻲ ﻨﻬﺎﻴﺔ ﻫﺫﺍ ﺍﻟﻔﺼل.
ﺩﻋﻨﺎ ﺇﺫﻥ ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻭﺍﺠﻬﺎﺕ ﻭﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﻲ ﺘﺼﻨﻊ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ.
١١٩
ﻭﺍﺠﻬﺔ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ IDataAdapter Interface
ﺘﻘﺩﻡ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻷﺴﺎﺴﻴﺔ ﻟﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻫﻲ ﺘﻤﺘﻠﻙ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
١٢٠
ﻤلﺀ :Fill
ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ DataSetﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻤﻸﻫـﺎ ﺒﺎﻟﺠـﺩﺍﻭل
ﻭﺍﻟﺴﺠﻼﺕ ﺍﻟﻨﺎﺘﺠﺔ ﻤﻥ ﺘﻨﻔﻴﺫ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ SELECTﺍﻟﺨﺎﺹ ﺒﻤﻬﻴـﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ ..ﻭﺇﺫﺍ
ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺴﺠﻼﺕ ﻓﻌﻼ ،ﻓﺴﻴﺤﺩﺙ ﺃﺤﺩ ﺍﻻﺤﺘﻤﺎﻟﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ:
-١ﺇﺫﺍ ﻜــﺎﻥ ﻫﻨــﺎﻙ ﻤﻔﺘــﺎﺡ ﺃﺴﺎﺴــﻲ Primary Keyﺃﻭ ﻗﻴــﺩ ﺘﻔــﺭﺩ
Unique Constraintﻟﺴﺠﻼﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﺴﻴﺘﻡ ﺇﻨﻌﺎﺸﻬﺎ ﻤـﻥ ﺠﺩﻴـﺩ
ﺒﺄﺤﺩﺙ ﻗﻴﻡ ﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-٢ﺇﺫﺍ ﻟﻡ ﻴﻜﻥ ﻫﻨﺎﻙ ﻤﺎ ﻴﻤﻴﺯ ﻜل ﺴﺠل ﻭﻴﻤﻨﻊ ﺘﻜﺭﺍﺭﻩ ،ﻓﺴﺘﻀـﺎﻑ ﺍﻟﺴـﺠﻼﺕ ﺇﻟـﻰ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺭﺓ ﺃﺨﺭﻯ ،ﻤﻤﺎ ﻴﺠﻌﻠﻬﺎ ﻤﻜﺭﺭﺓ!
ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﻤﺕ ﺇﻀﺎﻓﺘﻬﺎ ﺃﻭ ﺘﺤﺩﻴﺜﻬﺎ.
ﻻﺤﻅ ﺃﻨﻙ ﻻ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﻓﺘﺢ ﺍﻻﺘﺼﺎل ﻤﻊ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻭﻻ ،ﻓﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺘﻘﻭﻡ ﺒﻔﺘﺤﻪ
ﺇﻥ ﻜﺎﻥ ﻤﻐﻠﻘﺎ ﺜﻡ ﺘﻌﻴﺩ ﺇﻏﻼﻗﻪ ..ﺃﻤﺎ ﻟﻭ ﻜﺎﻥ ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺤﺎ ﻗﺒل ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ،
ﻓﺈﻨﻬﺎ ﺘﺴﺘﺨﺩﻤﻪ ﺜﻡ ﺘﺘﺭﻜﻪ ﻤﻔﺘﻭﺤﺎ ﻜﻤﺎ ﻫﻭ.
ﻭﻴﺴﻤﻰ ﻜل ﺠﺩﻭل ﻴﻀﺎﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺘﺒﻌـﺎ ﻟﺨﺭﻴﻁـﺔ ﺍﻟﺠـﺩﻭل Table
Mappingﺇﻥ ﻭﺠﺩﺕ ..ﻓﺈﻥ ﻟﻡ ﺘﻭﺠﺩ ﻫﺫﻩ ﺍﻟﺨﺭﻴﻁﺔ ،ﺘﺴﺘﺨﺩﻡ ﻗﻭﺍﻋﺩ ﺍﻟﺘﺴﻤﻴﺔ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺇﺫﺍ ﻜﺎﻥ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ﻴﻌﻴﺩ ﺴﺠﻼﺕ ﺠﺩﻭل ﻭﺍﺤﺩ ﻓﻘﻁ ،ﻓﺈﻨﻬﺎ ﺘﻀﺎﻑ ﻓﻲ ﺠﺩﻭل ﻴﺴﻤﻰ
.Table
-٢ﺇﺫﺍ ﻜﺎﻥ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ﻴﻨﻔﺫ ﺃﻜﺜﺭ ﻤﻥ ﺍﺴﺘﻌﻼﻡ ﻭﻴﻌﻴﺩ ﺴﺠﻼﺕ ﺃﻜﺜﺭ ﻤﻥ ﺠﺩﻭل ،ﻓﺈﻥ ﻜل
ﻨﺘﻴﺠﺔ ﻤﻨﻬﺎ ﺘﻭﻀﻊ ﻓﻲ ﺠﺩﻭل ﻤﺴﺘﻘل ،ﻭﻴﺘﻡ ﺘﺴﻤﻴﺔ ﻫﺫﻩ ﺍﻟﺠﺩﺍﻭل ﺒﺎﻟﺘﺭﺘﻴﺏ Tableﻭ
Table1ﻭ ...Table2ﻭﻫﻜﺫﺍ ..ﻻﺤﻅ ﺃﻥ ﺤﺩﻭﺙ ﺃﻱ ﺨﻁـﺄ ﻓـﻲ ﺃﻱ ﺍﺴـﺘﻌﻼﻡ،
ﺴﻴﻤﻨﻊ ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻤﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ ﻟﻪ ﻭﻟﻥ ﺘﻭﻀﻊ ﺒﺎﻗﻲ ﺍﻟﻨﺘﺎﺌﺞ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-٣ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺃﻜﺜﺭ ﻤﻥ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﻟﻤلﺀ ﻨﻔﺱ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﻟﺠﺩﺍﻭل ..ﻓﻲ
ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﻴﻀﻴﻑ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﻭل ﺠﺩﻭﻻ ﺍﺴﻤﻪ ،Tableﻭﺴﻴﺤﺎﻭل ﻤﻬﻴـﺊ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺜﺎﻨﻲ ﺃﻥ ﻴﻀﻴﻑ ﺠﺩﻭﻻ ﺍﺴﻤﻪ Tableﺃﻴﻀﺎ ،ﻟﻜﻥ ﻨﻅﺭﺍ ﻷﻨـﻪ ﻤﻭﺠـﻭﺩ،
ﻓﺴﺘﺭﻓﻀﻪ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻟﻥ ﺘﺘﻡ ﺇﻀﺎﻓﺘﻪ ،ﻟﻜﻥ ﻟﻥ ﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ!..
١٢١
ﻫﺫﺍ ﻴﻭﻀﺢ ﻟﻙ ﻀﺭﻭﺭﺓ ﺍﺴﺘﺨﺩﺍﻡ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل ﻟﺘﺴﻤﻴﺔ ﻜل ﻤﻥ ﺍﻟﺠﺩﻭﻟﻴﻥ ﺒﺎﺴﻤﻴﻥ
ﻤﺨﺘﻠﻔﻴﻥ ﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ.
-٤ﻴﺴﻤﻰ ﻜل ﻋﻤﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺒﻨﻔﺱ ﺍﺴﻤﻪ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ.
-٥ﺇﺫﺍ ﻜﺎﻨــﺕ ﺍﻟﻨﺘﻴﺠــﺔ ﺘﺤﺘــﻭﻱ ﻋﻠــﻰ ﺃﻜﺜــﺭ ﻤــﻥ ﻋﻤــﻭﺩ ﺒــﻨﻔﺱ ﺍﻻﺴــﻡ
)ﺒﺴﺒﺏ ﺍﺴﺘﺨﺩﺍﻡ ﺍﺴﺘﻌﻼﻡ ﻴﺠﻤﻌﻬﺎ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﺠﺩﻭل ﻤﻥ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ( ﻓﺈﻨﻬـﺎ
ﺘﻭﻀﻊ ﻓﻲ ﺍﻟﺠﺩﻭل ﺒﻌﺩ ﺇﻀﺎﻓﺔ ﺍﻷﺭﻗﺎﻡ ) ( ... ٣ ،٢ ،١ﺇﻟﻰ ﻨﻬﺎﻴﺔ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻟﻤﻨﻊ
ﺍﻟﺘﺸﺎﺒﻪ.
-٦ﺇﺫﺍ ﻜﺎﻨﺕ ﺒﻌﺽ ﺍﻷﻋﻤﺩﺓ ﺒﺩﻭﻥ ﺃﺴﻤﺎﺀ )ﻷﻨﻬﺎ ﻨﺎﺘﺠﺔ ﻋﻥ ﺩﻭﺍل ﺘﺠﻤﻴﻊ ﻤـﺜﻼ( ﻓﺈﻨﻬـﺎ
ﺘﻌﻁﻰ ﺍﻷﺴﻤﺎﺀ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ Column1ﻭ Column2ﻭ ...Column3ﻭﻫﻜﺫﺍ.
ﻭﻴﺠﺏ ﻋﻠﻴﻙ ﺃﻻ ﺘﻀﻴﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺠﺩﺍﻭل ﺃﻭ ﺃﻋﻤﺩﺓ ﺨﺎﺼﺔ ﺒـﻙ ﻭﺘﺴـﻤﻴﻬﺎ
ﺒﻬﺫﻩ ﺍﻷﺴﻤﺎﺀ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ،ﻜﻲ ﻻ ﻴﺤﺩﺙ ﺃﻱ ﺘﻌﺎﺭﺽ ﺃﻭ ﺨﻁﺄ ﺒﺴﺒﺒﻬﺎ ..ﻭﺴـﺘﺠﺩ ﻤﺜـﺎﻻ
ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ Fillﻓﻲ ﺍﻟﻤﺸـﺭﻭﻉ ..DataGridViewAuthorBooksﻓـﻲ
ﻫﺫﺍ ﺍﻟﻤﺸﺭﻭﻉ ﻨﺴﺘﺘﺨﺩﻡ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺍﺴﻤﻪ DAAuthorsﻟﺘﺤﻤﻴل ﺴﺠﻼﺕ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻤﻥ
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻜﻤﺎ ﻨﺴﺘﺨﺩﻡ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺍﺴﻤﻪ DABooksﻟﺘﺤﻤﻴل ﺴـﺠﻼﺕ ﺍﻟﻜﺘـﺏ
ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻟﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺍﺴﻤﻬﺎ Dsﺒﺴﺠﻼﺕ ﺍﻟﻤـﺅﻟﻔﻴﻥ ﻭﺍﻟﻜﺘـﺏ،
ﻨﺴﺘﺨﺩﻡ ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻓﻲ ﺤﺩﺙ ﺘﺤﻤﻴل ﺍﻟﻨﻤﻭﺫﺝ :Form1_Load
;)DAAuthors.Fill(Ds
;)DABooks.Fill(Ds
-ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ Primary Keyﻤﻭﺠﻭﺩﺍ ﻀﻤﻥ ﺃﻋﻤﺩﺓ ﺍﻟﻨﺘﻴﺠـﺔ ،ﻴـﺘﻡ
ﺍﺴﺘﺨﺩﺍﻤﻪ ﻜﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ ﻟﻠﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﺇﺫﺍ ﻟﻡ ﻴﻭﺠـﺩ ﻤﻔﺘـﺎﺡ
ﺃﺴﺎﺴﻲ ﻭﻜﺎﻥ ﻫﻨﺎﻙ ﺤﻘل ﻤﺘﻔﺭﺩ ﺍﻟﻘﻴﻤﺔ ،Uniqueﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻜﻤﻔﺘﺎﺡ ﺃﺴﺎﺴـﻲ
ﻟﻠﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺒﺸـﺭﻁ ﺃﻻ ﻴﻜـﻭﻥ ﻤﺴـﻤﻭﺤﺎ ﺒﺘﺭﻜـﻪ ﻓﺎﺭﻏـﺎ
) ..(AllowDbNull = Falseﺃﻤﺎ ﺇﺫﺍ ﻜـﺎﻥ ﺍﻟﺤﻘـل ﺍﻟﻤﺘﻔـﺭﺩ ﻴﻘﺒـل ﺍﻟﻘﻴﻤـﺔ
،NULLﻓﻠﻥ ﻴﺴﺘﺨﺩﻡ ﻜﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ ،ﻭﺴﺘﻜﺘﻔﻲ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺒﺈﻀﺎﻓﺔ ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ
١٢٣
UniqueConstraintﺍﻟﺨــﺎﺹ ﺒﻬــﺫﺍ ﺍﻟﻌﻤــﻭﺩ ﺇﻟــﻰ ﻤﺠﻤﻭﻋــﺔ ﺍﻟﻘﻴــﻭﺩ
ConstrainsCollectionﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﺠﺩﻭل.
-ﺃﻱ ﻗﻴﻭﺩ ﺃﺨﺭﻯ ﻏﻴﺭ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻭﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ ﻻ ﺘﻀـﺎﻑ ﺇﻟـﻰ ﺍﻟﺠـﺩﻭل،
ﻭﻋﻠﻴﻙ ﺇﻀﺎﻓﺘﻬﺎ ﺒﻨﻔﺴﻙ!
١٢٤
ﻤﻌﺭﻓﺔ ﻤﻌﺎﻤﻼﺕ ﺍﻟﻤلﺀ :GetFillParameters
ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﻤﻌﺎﻤﻼﺕ ،IDataParameter Arrayﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻤﻌـﺎﻤﻼﺕ ﺍﻟﺘـﻲ
ﺍﺴﺘﺨﺩﻤﻬﺎ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﻋﻨﺩ ﺘﻨﻔﻴﺫ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ SELECTﺍﻟﺨﺎﺹ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﺘﺤﺩﻴﺙ :Update
ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ DataSetﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻨﻘل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ
ﺤﺩﺜﺕ ﻋﻠﻰ ﺴﺠﻼﺘﻬﺎ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﻨﺠـﺢ
ﺘﺤﺩﻴﺜﻬﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺘﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺃﻭﺍﻤﺭ ﺍﻹﻀﺎﻓﺔ ﻭﺍﻟﺤﺫﻑ ﻭﺍﻟﺘﺤﺩﻴﺙ ﺍﻟﺨﺎﺼﺔ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻟﻨﻘل
ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺘﺒﻌﺎ ﻟﻠﺘﻐﻴﻴـﺭ ﺍﻟـﺫﻱ ﺤـﺩﺙ ﻟﻜـل
ﺴﺠل ..ﻭﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﺇﺫﺍ ﻟﻡ ﻴﻭﻓﺭ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﻤﺭ ﺍﻟﻤﻁﻠﻭﺏ ﻤﻥ ﻫـﺫﻩ
ﺍﻷﻭﺍﻤﺭ.
ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺫﻜﻴﺔ ،ﻓﻬﻲ ﺘﻤﺭ ﻋﺒﺭ ﻜل ﺴﺠل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺘـﺭﻯ ﺇﻥ
ﻜﺎﻥ ﻫﻨﺎﻙ ﺃﻱ ﺘﻐﻴﻴﺭ ﻗﺩ ﺤﺩﺙ ﻟﻬﺫﺍ ﺍﻟﺴﺠل ،ﻭﻤﻥ ﺜﻡ ﺘﺴﺘﺨﺩﻡ ﺍﻷﻤﺭ ﺍﻟﻤﻨﺎﺴﺏ ﻹﺭﺴﺎل ﻫﺫﺍ
ﺍﻟﺘﻐﻴﻴﺭ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﺃﻤﺎ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﻟﻡ ﻴﺤـﺩﺙ ﺒﻬـﺎ ﺃﻱ ﺘﻐﻴﻴـﺭ ،ﻓﻴـﺘﻡ
ﺘﺠﺎﻫﻠﻬﺎ ..ﻭﺒﻬﺫﺍ ﻻ ﺘﻀﻴﻊ ﻫﺫﻩ ﻟﻭﺴﻴﻠﺔ ﺃﻱ ﻭﻗﺕ ﻓﻲ ﻤﺤﺎﻭﻟﺔ ﺤﻔﻅ ﺴﺠﻼﺕ ﻟﻡ ﻴﺤﺩﺙ ﻓﻴﻬﺎ
ﺘﻐﻴﻴﺭ ..ﻟﺫﺍ ﻓﺄﻨﺕ ﻻ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺍﻟﻘﻴﺎﻡ ﺒﺄﻴﺔ ﺨﻁﻭﺍﺕ ﺨﺎﺼﺔ ﻟﺘﺤﺴﻴﻥ ﻭﻅﻴﻔﺔ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ.
ﻟﻜﻥ ،ﻟﻭ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﻜﺜﺭ ﻤﻥ ﺠﺩﻭل ،ﻓﺄﻴﻬﺎ ﻴـﺎ ﺘـﺭﻯ ﺴـﻴﺘﻡ
ﺘﺤﺩﻴﺜﻪ؟
ﻴﺤﺩﺩ ﻫﺫﺍ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺘﺴﺘﺨﺩﻤﻪ ..ﻤﺜﻼ :ﻟﻭ ﺍﺴـﺘﺨﺩﻤﺕ ﻤﻬﻴـﺊ ﺒﻴﺎﻨـﺎﺕ ﻟﻤـلﺀ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﻓﺈﻥ ﺍﻟﻭﺴﻴﻠﺔ Updateﺍﻟﺨﺎﺼﺔ ﺒﻪ ﺴـﺘﺘﻌﺎﻤل ﻤـﻊ
ﺴﺠﻼﺕ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ..ﻭﻟﻭ ﺍﺴﺘﺨﺩﻤﺕ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﻟﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺠـﺩﻭل
ﺍﻟﻜﺘﺏ ،ﻓﺈﻥ ﺍﻟﻭﺴﻴﻠﺔ Updateﺍﻟﺨﺎﺼﺔ ﺒﻪ ﺴﺘﺘﻌﺎﻤل ﻤﻊ ﺴﺠﻼﺕ ﺠﺩﻭل ﺍﻟﻜﺘـﺏ ..ﻟﻬـﺫﺍ
ﺘﺤﺘﺎﺝ ﺍﻟﺠﻤﻠﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ ﻟﺤﻔﻅ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ:
١٢٥
;)DaAuthors.Update(Ds
;)DaBooks.Update(Ds
ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﺼﺤﻴﺢ ﻭﻟﻜﻨﻪ ﻗﺩ ﻴﺴﺒﺒﺏ ﻤﺸﺎﻜل ﻓﻲ ﺒﻌﺽ ﺍﻟﺤﺎﻻﺕ ،ﻭﺍﻷﻓﻀـل ﺃﻥ
ﺘﺤﺎﻭل ﺘﺤﺩﻴﺙ ﺍﻟﺠﺩﻭل ﺍﻟﻔﺭﻋﻲ ﻗﺒل ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ ،ﻓﻠﻭ ﻜﻨﺕ ﺤﺫﻓﺕ ﻤﺅﻟﻔﺎ ﻭﻜﺘﺒﻪ ،ﻓﺈﻥ
ﻤﺤﺎﻭﻟﺔ ﺘﺤﺩﻴﺙ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﺃﻭﻻ ﺴﺘﺤﺎﻭل ﺤﺫﻑ ﺴﺠل ﻫﺫﺍ ﺍﻟﻤﺅﻟﻑ ،ﻭﻫﺫﺍ ﺴﻴﺴـﺒﺏ
ﺨﻁﺄ ﺇﺫﺍ ﻜﻨﺕ ﻓﺭﻀﺕ ﻗﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋـﻲ ،Foreign Key Constraintﻷﻥ ﻜﺘـﺏ
ﻫﺫﺍ ﺍﻟﻤﺅﻟﻑ ﺴﺘﺸﻴﺭ ﺇﻟﻰ ﻤﺅﻟﻑ ﻏﻴﺭ ﻤﻭﺠﻭﺩ ..ﺒﻴﻨﻤﺎ ﻟﻭ ﻋﻜﺴﺕ ﺍﻟﻌﻤﻠﻴﺔ ،ﻭﺤﺩﺜﺕ ﺠـﺩﻭل
ﺍﻟﻜﺘﺏ ﺃﻭﻻ ،ﻓﺴﻴﺘﻡ ﺤﺫﻑ ﻜﺘﺏ ﺍﻟﻤﺅﻟﻑ ﺒﻼ ﻤﺸﺎﻜل ،ﻭﻤﻥ ﺜﻡ ﻴﺘﻡ ﺤﺫﻑ ﺍﻟﻤﺅﻟﻑ ﻨﻔﺴﻪ ﻋﻨﺩ
ﺘﺤﺩﻴﺙ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ..ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻓﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ:
;)DaBooks.Update(Ds
;)DaAuthors.Update(Ds
ﻭﻫﺫﺍ ﻫﻭ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﺍﺴﺘﺨﺩﻤﻨﺎﻩ ﻓﻲ ﺍﻟﺯﺭ "ﺤﻔﻅ ﻓﻲ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ" ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ
.DataSetSample
١٢٦
ﻭﺍﺠﻬﺔ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ
IDbDataAdapter Interface
ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺘﺭﺙ ﺍﻟﻭﺍﺠﻬﺔ ،IDataAdapterﻭﻫﻲ ﺘﻤﺩ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒـﺎﻷﻭﺍﻤﺭ ﺍﻟﻼﺯﻤـﺔ
ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻷﻡ ،ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
ﻤﻠﺤﻭﻅﺔ:
ﻹﻨﻌﺎﺵ ﺍﻟﺴﺠل ﺍﻟﻤﻌﺭﻭﺽ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻴﻤﻜﻨﻙ ﺍﺴـﺘﺨﺩﺍﻡ ﺠﻤﻠـﺔ SELECT
ﺒﻌﺩ ﺍﺴﺘﻌﻼﻡ ﺍﻹﺩﺭﺍﺝ ﺃﻭ ﺍﻟﺘﺤﺩﻴﺙ ﺍﻟﺨﺎﺹ ﺒﻘﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ،ﻭﺫﻟﻙ ﺒﻭﻀﻊ
ﻓﺎﺼﻠﺔ ﻤﻨﻘﻭﻁﺔ ; ﺒﻴﻥ ﺍﻷﻤﺭﻴﻥ ..ﻤﺜﻼ:
INSERT INTO Authors
)(Author, CountryID, Phone, About
;)VALUES (@Author, @CountryID, @Phone, @About
SELECT * FROM Authors
) (WHERE ID = SCOPE_IDENTITY
ﻫﺫﺍ ﻤﻔﻴﺩ ﻓﻲ ﺒﻌﺽ ﺍﻟﺤﺎﻻﺕ ..ﻤﺜﻼ :ﻟﻭ ﻜﺎﻥ ﺍﻟﺴﺠل ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻤﻭﺩ ﺘﺭﻗﻴﻡ ﺘﻠﻘـﺎﺌﻲ،
ﻓﺈﻥ ﺍﻟﺭﻗﻡ ﺍﻟﺫﻱ ﺴﺘﻌﻁﻴﻪ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻠﺴﺠل ﻫﻭ ﻤﺠﺭﺩ ﺍﻗﺘﺭﺍﺡ ﻻ ﺘﻌﻤـل ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻪ )ﻟﻬﺫﺍ ﻟﻴﺴﺕ ﻤﺸﻜﻠﺔ ﺃﻥ ﺘﻀﻊ ﻓﻴﻪ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ٠ﺃﻭ ،(١-ﺤﻴﺙ ﺘﻌﻁﻲ
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻠﺴﺠل ﺍﻟﺭﻗﻡ ﺍﻟﺼﺤﻴﺢ ﻓﻲ ﺍﻟﺘﺭﻗﻴﻡ ﻋﻨﺩ ﺇﻀﺎﻓﺘﻪ ﺇﻟﻴﻬﺎ ،ﻟﻬﺫﺍ ﺘﻜﻭﻥ ﺠﻤﻠﻴـﺔ
SELECTﻤﻔﻴﺩﺓ ﻟﻌﺭﺽ ﺍﻟﺘﺭﻗﻴﻡ ﺍﻟﺼﺤﻴﺢ ﻟﻠﺴﺠل ﻓﻲ ﺒﺭﻨﺎﻤﺠﻙ.
١٢٨
ﺃﻤﺎ ﺇﺫﺍ ﻟﻡ ﺘﺠﺩ ﺩﺍﻋﻴﺎ ﻹﻨﻌﺎﺵ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﻼ ﺘﺴﺘﺨﺩﻡ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ.
ﻻﺤﻅ ﺃﻥ ﺍﻟﺩﺍﻟﺔ SCOPE_IDENTITYﺘﻌﻴﺩ ﺁﺨﺭ ﻤﻌﺭﻑ ﺘﻡ ﺘﻭﻟﻴﺩﻩ ﻓـﻲ ﻨﻁـﺎﻕ ﺍﻟﺘﻨﻔﻴـﺫ
ﺍﻟﺤﺎﻟﻲ ،ﻭﻫﻭ ﺒﺎﻟﻁﺒﻊ ﻤﻌﺭﻑ ﺍﻟﺴﺠل ﺍﻟـﺫﻱ ﺃﻀـﻔﻨﺎﻩ ﻟﻠﺘـﻭ ..ﻭﻻ ﻴﻨﺼـﺢ ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﺩﺍﻟـﺔ
@@IDENTITYﻷﻨﻬﺎ ﻗﺩ ﺘﺘﺄﺜﺭ ﺒﻌﻭﺍﻤل ﺃﺨﺭﻯ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺜـل ﺍﻟﻤﻨﺒﻬـﺎﺕ
،Triggersﻤﻤﺎ ﻴﺠﻌﻠﻬﺎ ﺘﻌﻴﺩ ﻤﻌﺭﻓﺎ ﻏﻴﺭ ﺍﻟﻤﻌﺭﻑ ﺍﻟﺨﺎﺹ ﺒﺎﻟﺴﺠل ﺍﻟﺫﻱ ﺃﻀﻔﺘﻪ.
ﻤﻠﺤﻭﻅﺔ:
ﺇﺫﺍ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ ،ﻭﻜﻨﺕ ﻗﺩ ﻭﻀﻌﺕ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ
ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ،SelectCommandﻓﻠﺴﺕ ﻤﺠﺒﺭﺍ ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻋﻠﻰ ﺇﻨﺸـﺎﺀ ﺃﻭﺍﻤـﺭ
ﺍﻟﺤﺫﻑ ﻭﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺘﺤﺩﻴﺙ ﺒﻨﻔﺴﻙ ،ﻓﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺴﺘﻁﻴﻊ ﺇﻨﺘﺎﺝ ﻫﺫﻩ ﺍﻷﻭﺍﻤـﺭ ﺁﻟﻴـﺎ
ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ Updateﺍﻟﺨﺎﺼﺔ ﺒﻪ ،ﻭﻫﻭ ﻴﺴﺘﺨﺩﻡ ﻟﻔﻌل ﻫﺫﺍ ﻓﺌﺎﺕ ﺒﻨﺎﺀ ﺍﻷﻭﺍﻤﺭ
CommandBuildersﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ ﻓﻲ ﻫﺫﺍ ﺍﻟﻔﺼل.
١٢٩
ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ DataAdapter Class
١٣٠
-٣ﺘﺴﺨﺩﻡ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺁﺨﺭ ﻟﺘﺤﺩﻴﺙ ﺍﻟﺠﺩﻭل ﺍﻟﺠﺩﻴﺩ ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ ،Update
ﻤﻊ ﺍﺴﺘﺨﺩﺍﻡ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺤﺫﻑ ﺍﻟﻤﻨﺎﺴﺒﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻫﺫﺍ ﺍﻟﺠﺩﻭل
ﺍﻟﺠﺩﻴﺩ.
ﻭﺍﻟﻤﺸﺭﻭﻉ CopyAuthorsﻴﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﻟﻨﺴﺦ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻤـﻥ ﻗﺎﻋـﺩﺓ ﺒﻴﻨـﺎﺕ
ﺁﻜﺴﻴﺱ ﻭﺇﻀﺎﻓﺘﻬﻡ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ..ﻻﺤﻅ ﺃﻥ ﺍﻟﻭﺴﻴﻠﺔ Updateﻟـﻥ
ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ﻭﺍﻟﺤﺫﻑ ﻭﺍﻟﺘﺤﺩﻴﺙ ﻓﻲ ﺤﺎﻟﺘﻨﺎ ﻫﺫﻩ ،ﻟﻬﺫﺍ ﻟـﻥ ﻨﻌﺭﻓﻬـﺎ ،ﻭﻟـﻥ
ﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ.
١٣٢
ﺍﺨﺘﻴﺎﺭ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺘﻨﺎﺴـﺒﻙ ﺃﻜﺜـﺭ ﻟﻤﻌﺎﻟﺠـﺔ ﻤﺜـل ﻫـﺫﻩ ﺍﻻﺨﻁـﺎﺀ ..ﻭﺍﻟﻤﺸـﺭﻭﻉ
UpdateErrorsﻴﺘﻴﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺍﺨﺘﻴﺎﺭ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺘﻨﺎﺴﺒﻪ ﻤﻥ ﺨﻼل ﻤﺭﺒﻊ ﺍﻻﺨﺘﻴـﺎﺭ
Check Boxﺍﻟﻤﻭﻀﻭﻉ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ..ﻓﻠﻭ ﺍﺨﺘﺎﺭ "ﻤﻭﺍﺼﻠﺔ ﺍﻟﺘﺤﺩﻴﺙ ﺭﻏـﻡ ﺤـﺩﻭﺙ
ﺃﺨﻁﺎﺀ" ،ﻓﺴﻨﻌﺭﺽ ﻟﻪ ﺘﻘﺭﻴﺭﺍ ﺒﺎﻷﺨﻁﺎﺀ ﺍﻟﺘﻲ ﺤﺩﺜﺕ ﻭﻨﻁﻠﺏ ﻤﻨﻪ ﺇﺼﻼﺤﻬﺎ ﻜﻤﺎ ﺘﻭﻀـﺢ
ﺍﻟﺼﻭﺭﺓ.
ﻻﺤﻅ ﺃﻥ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻴﻤﻨﻊ ﻤﻌﻅﻡ ﺍﻷﺨﻁﺎﺀ ،ﻓﻬﻭ ﻴﺭﻓﺽ ﺘﺭﻙ ﺨﺎﻨﺔ ﻓﺎﺭﻏﺔ ﺇﺫﺍ ﻜﺎﻨﺕ
ﻻ ﺘﻘﺒل ﺍﻟﻘﻴﻤﺔ ،Nullﻜﻤﺎ ﻴﺭﻓﺽ ﺃﻱ ﻨﺹ ﻴﺘﻜﻭﻥ ﻤﻥ ﻋﺩﺩ ﻤﻥ ﺍﻟﺤﺭﻭﻑ ﺃﻁـﻭل ﻤﻤـﺎ
ﺘﻘﺒﻠﻪ ﺍﻟﺨﺎﻨﺔ ..ﻟﻬﺫﺍ ﻟﻭ ﺃﺭﺩﺕ ﺍﺨﺘﺒﺎﺭ ﻫﺫﺍ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻓﻠﻴﺱ ﺃﻤﺎﻤﻙ ﺇﻻ ﺃﻥ ﺘﻀﻊ ﻓﻲ ﺍﻟﻌﻤﻭﺩ
CountryIDﺭﻗﻤﺎ ﺃﻜﺒﺭ ﻤﻥ ،٢٢ﻷﻥ ﻗﻴـﺩ ﺍﻟﻤﻔﺘـﺎﺡ ﺍﻟﻔﺭﻋـﻲ FOREIGN KEY
constraintﺒﻴﻥ ﺠﺩﻭل ﺍﻟﺩﻭل ﻭﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﻴﻤﻨﻌﻙ ﻤﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺭﻗﻡ ﺩﻭﻟـﺔ ﻟـﻴﺱ
ﻤﻭﺠﻭﺩﺍ ﻓﻲ ﺠﺩﻭل ﺍﻟﺩﻭل.
١٣٣
ﺃﻤﺎ ﺇﻥ ﺍﺨﺘﺎﺭ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺇﻴﻘﺎﻑ ﺍﻟﺘﺤﺩﻴﺙ ﻋﻨـﺩ ﺤـﺩﻭﺙ ﺃﺨﻁـﺎﺀ ،ﻓﺴﻨﺴـﺘﺨﺩﻡ ﺍﻟﻤﻘﻁـﻊ
Try Catchﻟﻨﻌﺭﺽ ﻟﻪ ﺭﺴﺎﻟﺔ ﺨﻁﺄ ﻋﻨﺩ ﻓﺸل ﺘﺤﺩﻴﺙ ﺃﻱ ﺴﺠل ،ﻭﻨﺤﺩﺩ ﻟﻪ ﻫﺫﺍ ﺍﻟﺴﺠل
ﻓﻲ ﺃﺩﻭﺍﺕ ﺍﻟﻌﺭﺽ ﻟﻴﻘﻭﻡ ﺒﺘﺼﺤﻴﺤﻪ ﻭﺇﻋﺎﺩﺓ ﺍﻟﻤﺤﺎﻭﻟﺔ.
ﻻﺤﻅ ﺃﻥ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ ﻟﻬﺫﺍ ﺍﻟﺴﺠل ﻟﻡ ﺘﺤﻔﻅ ﺤﺘﻰ ﺍﻵﻥ ،ﻭﻋﻨﺩ ﺘﻜﺭﺍﺭ ﻤﺤﺎﻭﻟﺔ ﺍﻟﺤﻔـﻅ
ﺴﻴﺘﻡ ﺤﻔﻅﻬﺎ ،ﻭﻗﺩ ﺘﻅﻬﺭ ﺃﺨﻁﺎﺀ ﺠﺩﻴﺩﺓ ﻓﻲ ﺃﻱ ﺴﺠل ﻤﻨﻬﺎ ﻓﻲ ﺘﻠﻙ ﺍﻟﻠﺤﻅﺔ ،ﺤﻴﺙ ﻴﺘﻭﺠﺏ
ﻋﻠﻰ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺘﺼﺤﻴﺤﻬﺎ ﺃﻴﻀﺎ ﻭﺇﻋﺎﺩﺓ ﺍﻟﻤﺤﺎﻭﻟﺔ ....ﻭﻫﻜﺫﺍ ..ﻭﺭﻏﻡ ﺃﻥ ﻫﺫﺍ ﻴﺒﺩﻭ ﻤﻤﻼ،
ﺇﻷ ﺃﻨﻪ ﺃﺴﻬل ﻤﻥ ﻜﺘﺎﺒﺔ ﺘﻘﺭﻴﺭ ﻁﻭﻴل ﻭﺘﺭﻙ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻴﺒﺤﺙ ﻋﻥ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﺫﻜﻭﺭﺓ ﻓﻲ
ﻫﺫﺍ ﺍﻟﺘﻘﺭﻴﺭ ﻟﺘﺼﺤﻴﺤﻬﺎ.
ﻫﺫﺍ ﻫﻭ ﻜﻭﺩ ﺯﺭ ﺍﻟﺤﻔﻅ ،ﻤﻊ ﺍﺴﺘﺜﻨﺎﺀ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﻴﺠﻤﻊ ﺃﺨﻁﺎﺀ ﺍﻟﺴـﺠﻼﺕ ﻓـﻲ ﺍﻟﺤﺎﻟـﺔ
ﺍﻷﻭﻟﻰ ،ﻷﻨﻨﺎ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻪ ﻓﻲ ﻓﺼل ﻻﺤﻕ:
)if (ChkContinue.Checked
{
ﺍﺴﺘﻤﺭﺍﺭ ﺍﻟﺘﺤﺩﻴﺙ ﺭﻏﻡ ﺤﺩﻭﺙ ﺃﺨﻁﺎﺀ //
;DaAuthors.ContinueUpdateOnError = true
;)DaAuthors.Update(Ds
ﻋﻠﻴﻨﺎ ﺠﻤﻊ ﺍﻷﺨﻁﺎﺀ ﺍﻟﺘﻲ ﺤﺩﺜﺕ ﻭﻋﺭﻀﻬﺎ ﻓﻲ ﺘﻘﺭﻴﺭ ﻟﻠﻤﺴﺘﺨﺩﻡ //
// ………………………….
}
١٣٤
ﺇﻴﻘﺎﻑ ﺍﻟﺘﺤﺩﻴﺙ ﻋﻨﺩ ﺤﺩﻭﺙ ﺃﻱ ﺨﻁﺄ else //
{
;DaAuthors.ContinueUpdateOnError = false
try
{
;)DaAuthors.Update(Ds
}
)catch (SqlException ex
{
;)MessageBox.Show(ex.Message
}
}
ﻻﺤﻅ ﺃﻨﻨﺎ ﻟﻡ ﻨﻜﺘﺏ ﺃﻱ ﻜﻭﺩ ﻟﻭﻀﻊ ﻋﻼﻤﺔ ﺍﻟﺨﻁﺄ ﺍﻟﺘﻲ ﺘﻅﻬﺭ ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﺒﺠـﻭﺍﺭ
ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﺴﺒﺏ ﺍﻟﻤﺸﻜﻠﺔ ،ﻓﻬﻲ ﺘﻅﻬﺭ ﺘﻠﻘﺎﺌﻴﺎ ﺒﺴﺒﺏ ﻭﺠﻭﺩ ﺭﺒﻁ Bindingﺒﻴﻥ ﺠـﺩﻭل
ﺍﻟﻌﺭﺽ ﻭﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺘﻘﻨﻴﺔ ﺭﺒﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﻟﺘﻔﺼﻴل ﻓﻲ ﻓﺼـل
ﻻﺤﻕ.
ﻭﻟﻌﻠﻙ ﺘﻠﺠﺄ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻙ ﺇﻟﻰ ﺘﻌﺭﻴﺏ ﺃﻫﻡ ﺭﺴﺎﺌل ﺍﻟﺨﻁﺄ ﺍﻟﺘﻲ ﺘﺘﻭﻗﻊ ﺤـﺩﻭﺜﻬﺎ ،ﻟﺘﻌـﺭﺽ
ﻟﻠﻤﺴﺘﺨﺩﻡ ﺍﻟﻌﺭﺒﻲ ﺭﺴﺎﺌل ﻴﺴﺘﻁﻴﻊ ﻓﻬﻤﻬﺎ ،ﻭﻜﺫﻟﻙ ﻟﻤﻨﻊ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻤـﻥ ﻤﻌﺭﻓـﺔ ﺃﺴـﻤﺎﺀ
ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺍﻟﺘـﻲ ﻗـﺩ ﻴﺴـﺘﺨﺩﻤﻬﺎ ﺃﻱ ﻗﺭﺼـﺎﻥ
ﻟﻤﺤﺎﻭﻟﺔ ﺤﻘﻥ ﺍﺴﺘﻌﻼﻤﺎﺕ ﻏﻴﺭ ﻤﺭﻏﻭﺒﺔ ﻭﺘﺨﺭﻴﺏ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻓﻲ ﺍﻟﻤﺜـﺎل ﺍﻟﺴـﺎﺒﻕ
ﻤﺜﻼ ،ﻜل ﻤﺎ ﻴﻬﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺃﻥ ﻴﻌﺭﻓﻪ ﻫﻭ" :ﻻ ﺘﻭﺠﺩ ﺩﻭﻟﺔ ﻟﻬﺎ ﺍﻟﺭﻗﻡ ﺍﻟﺫﻱ ﺃﺩﺨﻠﺘﻪ" ..ﻁﺒﻌﺎ
ﺴﻴﺤﺘﺎﺝ ﻤﻨﻙ ﺍﻷﻤﺭ ﺇﻟﻰ ﺒﻌﺽ ﺍﻟﺠﻬﺩ ﻟﻜﺘﺎﺒﺔ ﻜﻭﺩ ﻴﺤﻠل ﻨﺹ ﺍﻟﺭﺴـﺎﻟﺔ ﻭﻴﺒﻨـﻲ ﺍﻟـﻨﺹ
ﺍﻟﻌﺭﺒﻲ ﺒﻨﺎﺀ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻴﻬﺎ ..ﻟﻬﺫﺍ ﻓﻤﻥ ﺍﻷﺫﻜﻰ ﺃﻥ ﺘﻘﻠـل
ﺍﺤﺘﻤﺎﻻﺕ ﺍﻟﺨﻁﺄ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻙ ﺇﻟﻰ ﺃﻗﺼﻰ ﺤﺩ ..ﻓﻔﻲ ﺍﻟﺒـﺭﺍﻤﺞ ﺍﻟﺤﻘﻴﻘﻴـﺔ ،ﻟـﻴﺱ ﻋﻠـﻰ
ﺍﻟﻤﺴﺘﺨﺩﻡ ﺃﻥ ﻴﻜﺘﺏ ﺃﺭﻗﺎﻡ ﺍﻟﺩﻭل ،ﺒل ﻋﻠﻴﻙ ﺃﻥ ﺘﻌﺭﺽ ﻟﻪ ﻗﺎﺌﻤﺔ ﻤﻨﺴـﺩﻟﺔ ﻓﻴﻬـﺎ ﺃﺴـﻤﺎﺀ
ﺍﻟﺩﻭل ،ﻭﻫﻭ ﻴﺨﺘﺎﺭ ﻤﻨﻬﺎ ﻤﺒﺎﺸﺭﺓ ،ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ ..ﻫﺫﺍ ﻻ ﻴﺴـﻬل ﻋﻠﻴـﻪ
ﺍﻟﺤﻴﺎﺓ ﻓﻘﻁ ،ﺒل ﻴﻤﻨﻌﻪ ﻤﻥ ﺇﺤﺩﺍﺙ ﺃﺨﻁﺎﺀ ﻻ ﻟﺯﻭﻡ ﻟﻬﺎ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ..ﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻋﻠـﻰ
ﻫﺫﺍ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ،UpdateErrors2ﻭﺍﻟﺫﻱ ﺴﻴﺘﻌﺫﺭ ﻋﻠﻴﻙ ﻓﻴﻪ ﺭﺅﻴﺔ ﺃﻱ ﻤﺜـﺎل ﻋﻠـﻰ
ﺃﺨﻁﺎﺀ ﺍﻟﺘﺤﺩﻴﺙ ..ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﺸﺭﻭﻉ ﺃﻴﻀﺎ ﺘﺨﻠﺼﻨﺎ ﻤﻥ ﺭﺴـﺎﻟﺔ ﺍﻟﺨﻁـﺄ ﺍﻟﻌﻘـﻴﻡ ﺍﻟﺘـﻲ
ﻴﻌﺭﻀﻬﺎ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻋﻨﺩ ﺘﺭﻙ ﺼﻑ ﻓﻴﻪ ﺃﺨﻁﺎﺀ ،ﻭﻋﺭﻀﻨﺎ ﺭﺴﺎﻟﺔ ﺨﻁﺄ ﺨﺎﺼﺔ ﺒﻨـﺎ
١٣٥
ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺤﺩﺙ ،DataGridView.DataErrorﻤﻤﺎ ﻴﻤﻨﻊ ﺍﻟﻤﺴـﺘﺨﺩﻡ ﻤـﻥ ﻤﻌﺭﻓـﺔ
ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
١٣٧
ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﻜﺎﺌﻨﺎﺕ ،Object Arrayﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻘـﻴﻡ Values
ﺍﻟﻤﻭﺠﻭﺩﺓ ﺒﺎﻟﺼﻑ ﺍﻟﺫﻱ ﺤﺩﺙ ﺒﻪ ﺍﻟﺨﻁﺄ.
ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ ،Trueﻓﺴﻴﺴﺘﻤﺭ ﻤلﺀ ﺍﻟﺠﺩﻭل ﺒﺎﻟﺴـﺠﻼﺕ Continue
ﻭﺘﺠﺎﻫل ﺍﻟﺨﻁﺄ ..ﺃﻤﺎ ﺇﻥ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ Falseﻓﺴﻴﺘﻭﻗﻑ ﻤلﺀ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﺤﺎل.
١٣٨
ﻓﺌﺔ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ DbDataAdapter Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺴﺎﺴﻴﺔ ﻤﺠﺭﺩﺓ ﺘﺠﺏ ﻭﺭﺍﺜﺘﻬﺎ ،ﻭﻫﻲ ﺘﺭﺙ ﺍﻟﻔﺌﺔ .DataAdapter
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ،ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
ﻜﻤﺎ ﺃﻥ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻀﻴﻑ ﻋﺩﺓ ﺼﻴﻎ ﺠﺩﻴﺩﺓ ﻟﻜل ﻤﻥ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ Fillﻭ ..FillSchemaﺩﻋﻨـﺎ
ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﺼﻴﻎ:
ﻤلﺀ :Fill
ﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺜﻼﺙ ﺼﻴﻎ ﺠﺩﻴﺩﺓ ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ،ﻭﻫﻲ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﺠﺩﻭل ﺒﻴﺎﻨـﺎﺕ DataTableﻟـﺘﻤﻸﻩ ﺒﺎﻟﺴـﺠﻼﺕ ..ﻭﻗـﺩ
ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫـﺫﻩ ﺍﻟﺼـﻴﻐﺔ ﻓـﻲ ﺍﻟﻭﺴـﻴﻠﺔ MyDbConnector.GetTableﻓـﻲ
ﺍﻟﻤﺸﺭﻭﻉ DbTasksﻟﻤـلﺀ ﻜـﺎﺌﻥ ﺠـﺩﻭل DataTableﺒﺎﻟﺒﻴﺎﻨـﺎﺕ ﻭﺇﻋﺎﺩﺘـﻪ
ﻟﻠﻤﺴﺘﺨﺩﻡ ..ﺒﻌﺩ ﻫﺫﺍ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨـﺎﺕ ،ﺃﻭ ﻋـﺭﺽ
ﺒﻴﺎﻨﺎﺘﻪ ﻤﺒﺎﺸﺭﺓ ﻓﻲ ﺠﺩﻭل ﻋﺭﺽ ،ﺃﻭ ﺘﻨﻔﻴﺫ ﺃﻱ ﻋﻤﻠﻴﺔ ﺘﺭﻴﺩﻫﺎ ﻋﻠﻴﻪ ..ﻭﺴـﺘﺠﺩ ﻓـﻲ
ﻨﻔﺱ ﺍﻟﻤﺸﺭﻭﻉ ﻤﺜﺎﻻ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ،GetTableﻭﺫﻟـﻙ ﺒﻀـﻐﻁ ﺍﻟـﺯﺭ
"ﻋﺭﺽ ﺍﻟﻤﺅﻟﻔﻴﻥ" ،ﺍﻟﺫﻱ ﻴﻌﺭﺽ ﻨﻤﻭﺫﺠﺎ ﺠﺩﻴﺩﺍ ﻋﻠﻴﻪ ﺠﺩﻭل ﻓﻴﻪ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺅﻟﻔﻴﻥ.
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺭﺍﺩ ﻤﻠﺌﻬﺎ ،ﻭﻨﺼﺎ ﻴﻤﺜل ﺍﺴـﻡ ﺍﻟﺠـﺩﻭل
ﺍﻟﻤﻀﺎﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻤﺜﻼ ﺴﻴﻀﻴﻑ ﺠﺩﻭل ﺍﻟﻜﺘـﺏ ﺇﻟـﻰ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﻻﺴﻡ :TblBooks
;)"DaBooks.Fill(Ds, "TblBooks
MessageBox.Show(Ds.Tables[0].TableName); //TblBooks
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﻟﻬﺎ ﺃﺭﺒﻌﺔ ﻤﻌﺎﻤﻼﺕ ،ﻫﻲ ﺒﺎﻟﺘﺭﺘﻴﺏ:
-ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-ﺭﻗﻡ ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺍﻟﻘﺭﺍﺀﺓ ﺒﺩﺀﺍ ﻤﻨﻬﺎ ،ﻋﻠﻤﺎ ﺒﺄﻥ ﺃﻭل ﺴـﺠل ﻓـﻲ ﺍﻟﺠـﺩﻭل
ﺭﻗﻤﻪ ﺼﻔﺭ.
١٤٠
-ﺃﻗﺼﻰ ﻋﺩﺩ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﺘﺭﻴﺩ ﻗﺭﺍﺀﺘﻪ ﻤﻥ ﺍﻟﺠﺩﻭل ..ﻭﻟﻥ ﻴﺤﺩﺙ ﺨﻁﺄ ﺇﺫﺍ ﻜﺎﻥ
ﺍﻟﺠﺩﻭل ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻋﺩﺩ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﺃﻗل ﻤﻥ ﻫﺫﺍ ﺍﻟﻌﺩﺩ.
-ﻨﺹ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-٤ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﻔﻴﺩﻙ ﻋﻨﺩﻤﺎ ﺘﺭﻴﺩ ﺃﺨﺫ ﺠﺯﺀ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﺃﻤﺭ ﺘﺤﺩﻴـﺩ ﻴﻌﻴـﺩ
ﺃﻜﺜﺭ ﻤﻥ ﺠﺩﻭل ،ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ:
-ﺭﻗﻡ ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺍﻟﻘﺭﺍﺀﺓ ﺒﺩﺀﺍ ﻤﻨﻬﺎ.
-ﺃﻗﺼﻰ ﻋﺩﺩ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﺘﺭﻴﺩ ﻗﺭﺍﺀﺘﻪ ﻤﻥ ﻜل ﺠﺩﻭل.
DataTable -ﻤﺼﻔﻭﻓﺔ ﻤﻌﺎﻤﻼﺕ ParamArayﺘﺴﺘﻘﺒل ﻤﺼـﻔﻭﻓﺔ ﺠـﺩﺍﻭل
،Arrayﻟﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺭﺴل ﺇﻟﻴﻬﺎ ﺍﻟﺠﺩﺍﻭل ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻤﻸﻫﺎ ﺒﺎﻟﺴﺠﻼﺕ.
ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﻤﺕ ﺇﻀﺎﻓﺘﻬﺎ ﺃﻭ ﺘﺤﺩﻴﺜﻬﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
١٤١
ﻓﺌﺔ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل SqlDataAdapter Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ،DbDataAdapterﻭﻫﻲ ﺘﻌﻤل ﻜﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﻤﺨﺼﺹ ﻟﻠﺘﻌﺎﻤل ﻤـﻊ
ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺭﺒﻊ ﺼﻴﻎ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ.
-٢ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ SelectCommandﺍﻟﺫﻱ ﺴﻴﺴـﺘﺨﺩﻤﻪ ﻤﻬﻴـﺊ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-٣ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ:
-ﻨﺹ ﺠﻤﻠﺔ ﺍﻟﺘﺤﺩﻴﺩ .SELECT
-ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل SqlConnectionﺍﻟﺫﻱ ﺴﻴﺴﺘﺨﺩﻡ ﻓـﻲ ﺍﻻﺘﺼـﺎل ﺒﻘﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻻﺤﻅ ﺃﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺴﻴﻘﻭﻡ ﺒﺈﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺃﻤﺭ SqlCommandﻭﺴﻴﻀـﻊ ﺠﻤﻠـﺔ
ﺍﻟﺘﺤﺩﻴﺩ SELECTﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ CommandTextﺍﻟﺨﺎﺼﺔ ﺒﻪ ،ﻜﻤﺎ ﺴﻴﻀﻊ ﻜﺎﺌﻥ
ﺍﻻﺘﺼﺎل ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ Connectionﺍﻟﺨﺎﺼﺔ ﺒﻪ ..ﺒﻌﺩ ﻫﺫﺍ ﺴﻴﻭﻀﻊ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﻓﻲ
ﺍﻟﺨﺎﺼﺔ SelectCommandﺍﻟﺨﺎﺼﺔ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻤﻌﻨﻰ ﻫﺫﺍ ﺃﻥ ﻫﺫﻩ ﺍﻟﺼـﻴﻐﺔ
ﺘﺨﺘﺼﺭ ﻋﻠﻴﻙ ﺍﻟﻜﺜﻴﺭ ﻤﻥ ﺍﻟﺨﻁﻭﺍﺕ.
-٤ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ،ﻭﻟﻜﻥ ﻤﻌﺎﻤﻠﻬﺎ ﺍﻟﺜﺎﻨﻲ ﻴﺴﺘﻘﺒل ﻨﺹ ﺍﻻﺘﺼـﺎل
Connection Stringﺍﻟﻼﺯﻡ ﻟﻼﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻟﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻓـﻲ ﺇﻨﺸـﺎﺀ
ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل .SqlCommand
ﻭﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ﻤﻥ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﻭﺃﺤﺩﺍﺙ ،ﺘﻤﺘﻠـﻙ ﻫـﺫﻩ ﺍﻟﻔﺌـﺔ
ﺍﻟﺤﺩﺜﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ:
١٤٢
ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﺍﻟﺴﺠل :RowUpdating
ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ Updateﺍﻟﺨﺎﺼﺔ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﺈﻨﻬﺎ ﺘﻘﻭﻡ ﺒﺎﻟﻤﺭﻭﺭ ﻋﺒﺭ ﻜـل
ﺴﺠل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻻﺴﺘﺨﺩﺍﻤﻪ ﻓﻲ ﺘﺤﺩﻴﺙ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻴﻨﻁﻠﻕ ﻫﺫﺍ ﺍﻟﺤﺩﺙ
ﻗﺒل ﺍﺴﺘﺨﺩﺍﻡ ﻜل ﺴﺠل ﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ.
ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ eﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨـﻭﻉ ،SqlRowUpdatingEventArgsﻭﻫـﻭ
ﻴﻤﺘﻠﻙ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
١٤٣
:ErrorsOccurred -ﺘﻁﻠﺏ ﻤﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻌﺎﻤل ﻤﻊ
ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ ﻜﺄﻨﻬﺎ ﺘﺴﺒﺒﺕ ﻓﻲ ﺤﺩﻭﺙ ﺨﻁﺄ ..ﻓـﻲ ﻫـﺫﻩ
ﺍﻟﺤﺎﻟﺔ ﺴﻴﻨﻁﻠﻕ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻓﻌﻼ ﻓﻲ ﺍﻟﺴـﻁﺭ ﺍﻟـﺫﻱ
ﺍﺴﺘﺩﻋﻴﺕ ﻓﻴﻪ ﺍﻟﻭﺴﻴﻠﺔ Updateﺍﻟﺨﺎﺼﺔ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ،
ﻭﻋﻠﻴﻙ ﻤﻌﺎﻟﺠﺔ ﻫﺫﺍ ﺍﻟﺨﻁﺄ ﺒﻤﻘﻁﻊ .Try Catch
:SkipCurrentRow -ﺘﺠــﺎﻭﺯ ﺍﻟﺴــﺠل ﺍﻟﺤــﺎﻟﻲ ﺩﻭﻥ
ﺍﺴﺘﺨﺩﺍﻤﻪ ﻓﻲ ﺘﺤﺩﻴﺙ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻤﻊ ﺍﺴﺘﻤﺭﺍﺭ ﺘﺤﺩﻴﺙ
ﺒﺎﻗﻲ ﺍﻟﺴﺠﻼﺕ.
:SkipAllRemainingRows -ﺘﺠﺎﻭﺯ ﺍﻟﺴـﺠل ﺍﻟﺤـﺎﻟﻲ
ﻭﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ ﻟﻪ ﻭﺇﻴﻘﺎﻑ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ ﻓﻲ ﺍﻟﺤﺎل.
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺨﺭﻴﻁﺔ ﺍﻟﺠـﺩﻭل ،DataTableMappingﺍﻟـﺫﻱ Table
Mapping
ﻴﺴﺘﺨﺩﻡ ﻟﻠﺭﺒﻁ ﺒﻴﻥ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﻟﺠـﺩﻭل
ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
١٤٥
ﻤﻠﺤﻭﻅﺔ:
ـﻴﻠﺔ
ـﺩﺙ RowUpdatingﺍﻟﻭﺴـ
ـل ﺍﻟﺤـ
ـﻙ ﻤﻌﺎﻤـ
ـﺎﺫﺍ ﻻ ﻴﻤﺘﻠـ
ـﺎﺀل ﻟﻤـ
ـﻙ ﺘﺘﺴـ
ﻟﻌﻠـ
..CopyToRowsﺍﻟﺴﺒﺏ ﻓﻲ ﻫﺫﺍ ﺃﻥ ﺍﻟﺤﺩﺙ RowUpdatingﻴﻨﻁﻠﻕ ﺩﺍﺌﻤﺎ ﻗﺒـل
ﺘﺤﺩﻴﺙ ﻜل ﺼﻑ ﻋﻠﻰ ﺤﺩﺓ ،ﺤﺘﻰ ﻟﻭ ﻜﺎﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺴﻴﺴﺘﺨﺩﻡ ﻤﺠﻤﻭﻋﺔ ﺃﻭﺍﻤـﺭ
Batch SQLﻟﺘﺤﺩﻴﺙ ﻤﺠﻤﻭﻋﺔ ﺼﻔﻭﻑ ﺩﻓﻌﺔ ﻭﺍﺤﺩﺓ ..ﻫﺫﺍ ﻤﻨﻁﻘـﻲ ،ﻷﻥ ﻤﻬﻴـﺊ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﻘﺭﺃ ﺴـﺠﻼ ﺘﻠـﻭ ﺴـﺠل ﻤـﻥ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ )ﻭﻴﻁﻠـﻕ ﺍﻟﺤـﺩﺙ
RowUpdatingﻟﻜل ﺴﺠل( ،ﻭﺒﻌﺩ ﻫﺫﺍ ﻴﻜ ﻭﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤﺠﻤﻭﻋـﺔ ﺃﻭﺍﻤـﺭ
ﻟﺘﺤﺩﻴﺙ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﻗﺭﺃﻫﺎ ،ﻭﻴﺭﺴل ﻫﺫﻩ ﺍﻷﻭﺍﻤﺭ ﺍﻟﻤﺠﻤﻌﺔ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺜﻡ
ﻴﻁﻠﻕ ﺍﻟﺤﺩﺙ RowUpdatedﺒﻌﺩ ﺘﻨﻔﻴﺫﻫﺎ.
١٤٦
ﺍﻟﺘﺼﺎﺭﻉ ﻋﻠﻰ ﺘﺤﺩﻴﺙ ﺍﻟﺒﻴﺎﻨﺎﺕ:
ﻫﻨﺎﻙ ﻤﺸﻜﻠﺔ ﺭﺌﻴﺴﻴﺔ ﺴﺘﻭﺍﺠﻬﻙ ﻋﻨﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻴﺴﺘﺨﺩﻤﻬﺎ ﺃﻜﺜﺭ ﻤﻥ ﻤﻭﻅﻑ ﻓﻲ
ﻨﻔﺱ ﺍﻟﻭﻗﺕ ،ﻭﻫﻲ ﺍﻟﺘﻀﺎﺭﺏ ﺒﻴﻥ ﺍﻟﺘﻌﺩﻴﺭﺕ ﺍﻟﺘﻲ ﻴﺠﺭﻴﻬﺎ ﺃﻜﺜﺭ ﻤـﻥ ﻤﻭﻅـﻑ ﻋﻠـﻰ ﻨﻔـﺱ
ﺍﻟﺴﺠل ..ﺘﺨﻴل ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ:
-ﻗﺎﻡ ﻤﺴﺘﺨﺩﻡ ﺒﺭﻨﺎﻤﺠﻙ ﺒﺘﺤﻤﻴل ﺴﺠﻼﺕ ﺍﻟﻜﺘﺏ ،ﻭﻗﺎﻡ ﺒﺘﻌﺩﻴل ﺴﻌﺭ ﻜﺘﺎﺏ "ﻋﻀﺎ ﺍﻟﺤﻜﻴﻡ"
ﻤﻥ ٥ﺠﻨﻴﻬﺎﺕ ﺇﻟﻰ ٧ﺠﻨﻴﻬﺎﺕ.
-ﻋﻨﺩ ﻤﺤﺎﻭﻟﺔ ﺒﺭﻨﺎﻤﺠﻙ ﺤﻔﻅ ﻫﺫﻩ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻜﺎﻥ ﻤﺴﺘﺨﺩﻡ ﺁﺨﺭ ﻗـﺩ
ﻏﻴﺭ ﻋﺩﺩ ﺍﻟﻨﺴﺦ ﺍﻟﻤﺘﺎﺤﺔ ﺍﻟﻤﺘﺎﺤﺔ ﻤﻥ ﻜﺘﺎﺏ "ﻋﺼﺎ ﺍﻟﺤﻜﻴﻡ" ﻤﻥ ٢٠٠٠ﺇﻟﻰ .٣٠٠٠
ﺍﻟﺴﺅﺍل ﺍﻵﻥ ﻫﻭ :ﻤﺎﺫﺍ ﻨﻔﻌل ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ؟
ﻟﻭ ﺤﻔﻅ ﺒﺭﻨﺎﻤﺠﻙ ﺴﺠل ﺍﻟﻜﺘﺎﺏ "ﻋﻀﺎ ﺍﻟﺤﻜﻴﻡ" ﻓﺴﻴﻌﺩل ﺴﻌﺭﻩ ﺇﻟﻰ ٧ﺠﻨﻴﻬﺎﺕ ،ﻟﻜﻨـﻪ ﺴـﻴﻌﻴﺩ
ﻋﺩﺩ ﺍﻟﻨﺴﺦ ﺍﻟﻤﺘﺎﺤﺔ ﻤﻨﻪ ﺇﻟﻰ !٢٠٠٠
ﺃﻤﺎ ﻟﻭ ﺃﺒﻘﻴﻨﺎ ﻋﻠﻰ ﺍﻟﺘﻌﺩﻴﻼﺕ ﺍﻟﺘﻲ ﺃﺠﺭﺍﻫﺎ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻵﺨﺭ ،ﻓﻬﺫﺍ ﻤﻌﻨﺎﻩ ﺍﻹﺒﻘﺎﺀ ﻋﻠـﻰ ﺍﻟﺘﻌـﺩﻴل
ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﻋﺩﺩ ﺍﻟﻨﺴﺦ ،ﻟﻜﻥ ﺍﻟﺴﻌﺭ ﺴﻴﻅل ٥ﺠﻨﻴﻬﺎﺕ!
ﻁﺒﻌﺎ ﻓﻲ ﻜﻠﺘﺎ ﺍﻟﺤﺎﻟﺘﻴﻥ ﺴﺘﺤﺩﺙ ﻤﺸﻜﻠﺔ ﻓﻲ ﺍﻟﻌﻤل ..ﻭﻋﻨﺩﻤﺎ ﺴﻴﺤﺎﻭل ﺍﻟﻤﺩﻴﺭ ﻤﻌﺎﻗﺒـﺔ ﻤﺴـﺌﻭل
ﺍﻟﻤﺨﺎﺯﻥ ﻓﻲ ﺍﻟﺤﺎﻟﺔ ﺍﻷﻭﻟﻰ ﻓﺴﻴﻘﺴﻡ ﻟﻪ ﺒﺄﻏﻠﻅ ﺍﻷﻴﻤﺎﻥ ﺇﻨﻪ ﻏﻴﺭ ﻋﺩﺩ ﺍﻟﻨﺴﺦ ﺍﻟﻤﺘﺎﺤـﺔ ،ﻭﻋﻨـﺩﻤﺎ
ﺴﻴﺤﺎﻭل ﻤﻌﺎﻗﺒﺔ ﻤﺴﺌﻭل ﺍﻟﻤﺒﻴﻌﺎﺕ ﻓﻲ ﺍﻟﺤﺎﻟﺔ ﺍﻟﺜﺎﻨﻴﺔ ،ﻓﺴﻴﻘﺴﻡ ﻟﻪ ﺇﻨـﻪ ﻏﻴـﺭ ﺜﻤـﻥ ﺍﻟﻨﺴـﺨﺔ،
ﻭﻜﻼﻫﻤﺎ ﺼﺎﺩﻕ ﻓﻲ ﻗﺴﻤﻪ ،ﻭﺃﻨﺕ ﺍﻟﺫﻱ ﺨﺭﺒﺕ ﺒﻴﺘﻪ!
ﺘﺴﻤﻰ ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ﺒﺎﺴﻡ ﻤﺸﻜﻠﺔ ﺍﻟﺘﻁﺎﺒﻕ ..Concurrency Problemﻭﻴﻤﻜﻥ ﻋﻼﺠﻬﺎ ﺒﺄﺤﺩ
ﺍﻟﺤﻠﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ:
١٤٧
ﺁﺨﺭ ﻤﻥ ﺘﻐﻴﻴﺭﻫﺎ ﺇﻟﻰ ﺃﻥ ﻴﺘﻡ ﻴﻐﻠﻕ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻷﻭل ﺍﻻﺘﺼﺎل ﻭﺘـﺘﻡ ﺇﺯﺍﻟـﺔ ﺍﻹﻏـﻼﻕ..
ﻭﻴﻤﻜﻥ ﺘﻨﻔﻴﺫ ﻫﺫﺍ ﺍﻟﺤل ﻓﻲ ﺩﻭﺕ ﻨﺕ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺘﻌﺎﻤﻼﺕ ،Transactionsﻟﻬﺫﺍ ﺴﻨﺅﺠل
ﺘﻁﺒﻴﻘﻪ ﺇﻟﻰ ﺍﻟﻜﺘﺎﺏ ﺍﻟﻘﺎﺩﻡ ﺇﻥ ﺸﺎﺀ ﺍﷲ.
ﻭﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﺍ ﺍﻟﺤل ،ﺘﻜﻭﻥ ﺠﻤﻠﺔ ﺍﻟﺘﺤﺩﻴﺙ ﺒﺴﻴﻁﺔ ﻟﻠﻐﺎﻴﺔ ،ﻷﻨـﻙ ﺘﺴـﺘﺨﺩﻡ ﺍﻟﻤﻔﺘـﺎﺡ
ﺍﻷﺴﺎﺴﻲ ﻟﻠﺤﻘل ﻟﻠﻌﺜﻭﺭ ﻋﻠﻴﻪ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻤﻥ ﺜﻡ ﺘﻐﻴﺭ ﻗﻴﻤﻪ ﻤﺒﺎﺸﺭﺓ ،ﻷﻨﻙ ﻭﺍﺜـﻕ
ﺃﻨﻪ ﻟﻡ ﻴﺘﻐﻴﺭ ﻤﻨﺫ ﺃﻥ ﻗﻤﺕ ﺒﺘﺤﻤﻴﻠﻪ ..ﻫﻜﺫﺍ ﻤﺜﻼ ﺴﺘﻜﻭﻥ ﺠﻤﻠﺔ ﺘﺤﺩﻴﺙ ﺴﺠﻼﺕ ﺍﻟﻤﺅﻟﻔﻴﻥ:
UPDATE Authors
SET Author = @Author,
CountryID = @CountryID,
Phone = @Phone,
About = @About
;WHERE ID = @ID
ﻻﺤﻅ ﺃﻥ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻫﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ ﺘﺄﺨﺫ ﻗﻴﻤﻬﺎ ﻤﻥ ﺨﺎﻨﺎﺕ ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﻴﺘﻡ
ﺘﺤﺩﻴﺜﻪ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﻴﻌﺘﺒﺭ ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﺸﺎﺌﻡ ﺤﻼ ﺤﺎﺴﻤﺎ ﻟﻠﻤﺸﻜﻠﺔ ،ﻷﻥ ﺃﻱ ﻤﺴﺘﺨﺩﻡ ﺁﺨـﺭ ﺴـﻴﺤﺎﻭل ﺘﻌـﺩﻴل
ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﺘﻨﺎﺯﻉ ﻋﻠﻴﻬﺎ ﺴﻴﺤﺼل ﻋﻠﻰ ﺭﺴﺎﻟﺔ ﺨﻁﺄ ﺘﺨﺒﺭﻩ ﺒﺄﻨﻬﺎ ﻤﻐﻠﻘﺔ ﻤﻥ ﻗﺒل ﻤﺴﺘﺨﺩﻡ
ﺁﺨﺭ ..ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻋﻠﻴﻙ ﺃﻥ ﺘﺠﻌل ﺒﺭﻨﺎﻤﺠﻙ ﻴﻨﺘﻅﺭ ﺍﻨﺘﻬﺎﺀ ﺍﻹﻏﻼﻕ ،ﻭﻤﻥ ﺜﻡ ﻴﻌﺭﺽ
ﻟﻠﻤﺴﺘﺨﺩﻡ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﻴﺤﺎﻭل ﺘﺤﺩﻴﺜﻬﺎ ،ﻟﻴﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺘﻤﺕ ﻋﻠﻴﻬﺎ ،ﻭﻤﻥ
ﺜﻡ ﻴﻘﺭﺭ ﻜﻴﻑ ﻴﻭﺍﺌﻡ ﺒﻴﻨﻬﺎ ﻭﺒﻴﻥ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺃﺠﺭﺍﻫﺎ ،ﺜﻡ ﻴﻌﻴـﺩ ﺤﻔﻅﻬـﺎ ﻓـﻲ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻟﻜﻥ ﺍﻟﻤﺸﻜﻠﺔ ﻫﻲ ﺃﻥ ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﺸﺎﺌﻡ ﺴﻴﻬﺒﻁ ﺒﻜﻔﺎﺀﺓ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﺍﺴـﺘﻤﺭ ﺇﻏـﻼﻕ ﻜـل
ﺴﺠل ﻟﻔﺘﺭﺍﺕ ﺯﻤﻨﻴﺔ ﻁﻭﻴﻠﺔ ،ﺃﻭ ﺇﺫﺍ ﺘﻡ ﺘﺤﺩﻴﺙ ﺃﻋﺩﺍﺩ ﻀﺨﻤﺔ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﻋﻠﻰ ﺍﻟﺘﺘـﺎﺒﻊ،
ﻭﺫﻟﻙ ﻷﻥ ﺇﻏﻼﻕ ﺍﻟﺴﺠﻼﺕ ﻴﺴﺘﻬﻠﻙ ﺠﺯﺀﺍ ﻤﻥ ﻭﻗﺕ ﺘﺸﻐﻴل ﻭﺫﺍﻜﺭﺓ ﺍﻟﺨﺎﺩﻡ ،ﻜﻤـﺎ ﺃﻨـﻪ
ﻴﺤﺘﺎﺝ ﺇﻟﻰ ﺇﺒﻘﺎﺀ ﻗﻨﻭﺍﺕ ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺤﺔ ﻤﻊ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﺍﻟﺫﻴﻥ ﻗﺎﻤﻭﺍ ﺒﻌﻤﻠﻴﺔ ﺍﻹﻏـﻼﻕ،
ﻤﻤﺎ ﻴﺤﺭﻡ ﻤﺴﺘﺨﺩﻤﻴﻥ ﺁﺨﺭﻴﻥ ﻤﻥ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺫﻟﻙ ﺍﻟﻭﻗﺕ ..ﻟﻜـﻥ ﻴﻅـل
ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﺸﺎﺌﻡ ﺍﻟﺤل ﺍﻷﻓﻀل ﻋﻨﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻴﺘﺼل ﺒﻬﺎ ﻋﺩﺩ ﻜﺒﻴﺭ ﻤـﻥ
ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻓﻲ ﻨﻔﺱ ﺍﻟﻠﺤﻅﺔ ،ﻭﻴﺘﺼﺎﺭﻋﻭﻥ ﻋﻠﻰ ﺘﺤﺩﻴﺙ ﻨﻔﺱ ﺍﻟﺴﺠﻼﺕ ،ﻷﻥ ﺍﺴـﺘﺨﺩﺍﻡ
١٤٨
ﻜﺜﺭﺓ ﻋﻤﻠﻴﺎﺕ ﺍﻟﺘﺭﺍﺠﻊ ﻋﻥ ﺍﻟﺘﻌـﺎﻤﻼﺕ Transactions Rollbackﻻﺴـﺘﻌﺎﺩﺓ ﺍﻟﻘـﻴﻡ
ﺍﻷﺼﻠﻴﺔ ﻗﺒل ﺍﻟﺘﻀﺎﺭﺏ ،ﺘﺴﺘﻬﻠﻙ ﺍﻟﺨﺎﺩﻡ ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺒﺄﻜﺜﺭ ﻤﻤﺎ ﺘﻔﻌل ﻋﻤﻠﻴﺎﺕ ﺍﻹﻏﻼﻕ.
١٤٩
ﻟﻌﻠﻙ ﺘﻼﺤﻅ ﻓﻲ ﻫﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ ﻭﺠﻭﺩ ﻤﻌﺎﻤﻠﻴﻥ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻜل ﺤﻘل ..ﻤﺜﻼ ،ﻴـﺘﻡ
ﺍﻟﺘﻌﺎﻤــل ﻤــﻊ ﺤﻘــل ﺍﻟﻤــﺅﻟﻔﻴﻥ ﻤــﻥ ﺨــﻼل ﺍﻟﻤﻌــﺎﻤﻠﻴﻥ @Author
ﻭ ..@Original_Authorﻓﻤﺎ ﻫﻭ ﺍﻟﻔﺎﺭﻕ ﺒﻴﻨﻬﻤﺎ؟
ﻟﻜﻲ ﺘﻔﻬﻡ ﻫﺫﺍ ﺍﻟﻔﺎﺭﻕ ،ﻋﻠﻴﻙ ﺃﻥ ﺘﻌﺭﻑ ﺃﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ DataSetﺘﺤـﺘﻔﻅ
ﺒﻨﺴﺨﺘﻴﻥ ﻤﻥ ﻜل ﺴﺠل ﻴﺘﻡ ﻭﻀﻌﻪ ﻓﻴﻬﺎ:
-ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ :Original Version
ﻭﻫﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﻜﻤﺎ ﺘﻡ ﺘﺤﻤﻴﻠﻬـﺎ ﻤـﻥ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻻﺴﺘﺨﺩﺍﻤﻬﺎ ﺒﻌﺩ ﺫﻟﻙ ﻓﻲ ﺍﻟﺒﺤﺙ ﻋﻥ ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﺘﺤﺩﻴﺜﻪ.
-ﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ :Current Version
ﻭﻫﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺴﺠل ﺒﻌﺩ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺃﺠﺭﺍﻫﺎ ﺍﻟﻤﺴـﺘﺨﺩﻡ
ﻋﻠﻴﻬﺎ ،ﻭﺫﻟﻙ ﻻﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﺘﺤﺩﻴﺙ ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﻜل ﻤﺎ ﻴﻔﻌﻠﻪ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴﺙ ﺍﻟﺴﺎﺒﻕ ،ﻫﻭ ﺘﻌﺭﻴﻑ ﻤﻌﺎﻤﻠﻴﻥ ﻟﻜل ﺤﻘل ،ﺃﺤـﺩﻫﻤﺎ
ﻴﻘﺭﺃ ﻗﻴﻤﺘﻪ ﺍﻟﺤﺎﻟﻴﺔ )ﻤﺜل (@Authorﻭﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻟﺤﻔﻅ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻓﻲ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺍﻵﺨﺭ ﻴﻘﺭﺃ ﻗﻴﻤﺘﻪ ﺍﻷﺼﻠﻴﺔ )ﻤﺜـل (@Original_Authorﻭﻴﺴـﺘﺨﺩﻡ
ﻟﻠﺒﺤﺙ ﻋﻥ ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻴﺘﻡ ﺍﻟﺘﻔﺭﻴﻕ ﺒﻴﻥ ﻫﺫﻴﻥ ﺍﻟﻤﻌﺎﻤﻠﻴﻥ
ﺒﺎﺴــﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼــﻴﺔ SourceVersionﺍﻟﺨﺎﺼــﺔ ﺒﻜــﺎﺌﻥ ﺍﻟﻤﻌﺎﻤــل
،DataParameterﻭﺍﻟﺫﻱ ﻴﻤﻜﻥ ﺇﺭﺴﺎل ﻗﻴﻤﺘﻪ ﻤﻥ ﺨﻼل ﺍﻟﻤﻌﺎﻤل ﺍﻟﺘﺎﺴﻊ ﻟﺤـﺩﺙ
ﺍﻹﻨﺸﺎﺀ ..Newﻫﻜﺫﺍ ﻤﺜﻼ ﻴﺘﻡ ﺘﻌﺭﻴﻑ ﺍﻟﻤﻌﺎﻤل ..@Authorﻻﺤـﻅ ﺃﻨﻨـﺎ ﻟـﻥ
ﻨﺭﺴل ﻗﻴﻤﺔ ﺍﻟﻤﻌﺎﻤل ،SourceVersionﻟﻬـﺫﺍ ﺴـﻴﺘﻡ ﺴـﻴﻘﺭﺃ ﺍﻟﻘﻴﻤـﺔ ﺍﻟﺤﺎﻟﻴـﺔ
ﺍﻓﺘﺭﺍﻀﻴﺎ:
SqlParameter P2 = new SqlParameter("@Author",
;)"SqlDbType.NVarChar, 0, "Author
ﻭﻫﻜﺫﺍ ﻴﺘﻡ ﺘﻌﺭﻴﻑ ﺍﻟﻤﻌﺎﻤل :@Original_Author
١٥٠
var P2 = new SqlParameter("@Original_Author",
SqlDbType.NVarChar, 0,
ParameterDirection.Input, false, 0, 0,
;)"Author", DataRowVersion.Original, null
ﻟﻜﻥ ..ﻟﻤﺎﺫﺍ ﻻ ﻴﻭﺠﺩ ﺸﺭﻁ ﻋﻠﻰ ﺍﻟﺤﻘل Aboutﻓﻲ ﺍﻟﻤﻘﻁﻊ Where؟
ﺍﻟﺴﺒﺏ ﻓﻲ ﻫﺫﺍ ﺃﻨﻨﺎ ﻋﺭﻓﻨﺎ ﻫﺫﺍ ﺍﻟﺤﻘل ﻤﻥ ﺍﻟﻨﻭﻉ ) ،nvarchar(MAXﻭﻫﺫﺍ ﻤﻌﻨﺎﻩ
ﺃﻨﻪ ﻴﺘﺴﻊ ﻟﻨﺹ ﻗﺩ ﻴﺼل ﺇﻟﻰ ٢ﻤﻠﻴﺎﺭ ﺤﺭﻑ ،ﻭﻫﺫﺍ ﺤﺠﻡ ﻫﺎﺌل ،ﻭﺴﺘﻜﻭﻥ ﻤﻘﺎﺭﻨـﺔ
ﻫﺫﺍ ﺍﻟﺤﻘل ﻤﻀﻴﻌﺔ ﻟﻠﻭﻗﺕ ..ﻟﻜﻥ ﻟﻭ ﻜﻨﺕ ﻤﺼﺭﺍ ،ﻓﻴﻤﻜﻨﻙ ﺘﻌـﺩﻴل ﺍﻻﺴـﺘﻌﻼﻡ ..ﻻ
ﺃﻨﺼﺤﻙ ﺒﻔﻌل ﻫﺫﺍ ﻤﻥ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ،ﻷﻨﻬﺎ ﺴـﺘﻌﺠﺯ ﻋـﻥ ﺇﻨﺸـﺎﺀ ﺍﻟﻤﻌﺎﻤـل
@Original_Aboutﺒﺸﻜل ﺼﺤﻴﺢ ،ﻭﺒﻼ ﻤﻥ ﻫﺫﺍ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﻓﻲ
ﺒﺩﺍﻴﺔ ﺤﺩﺙ ﺘﺤﻤﻴل ﺍﻟﻨﻤﻭﺫﺝ:
ﺇﻀﺎﻓﺔ ﺸﺭﻁ ﺇﻟﻰ ﻨﻬﺎﻴﺔ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴﺙ //
ﺴﻴﺤﺩﺙ ﺨﻁﺄ ﻟﻭ ﻜﺎﻥ ﻫﻨﺎﻙ ﺍﺴﺘﻌﻼﻡ ﺘﺤﺩﻴﺩ ﻓﻲ ﻨﻬﺎﻴﺔ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴﺙ//
=DaAuthors.UpdateCommand.CommandText +
;"" And About = @Original_About
ﺘﻌﺭﻴﻑ ﻤﻌﺎﻤل ﺠﺩﻴﺩ //
var P = new SqlParameter("@Original_About",
SqlDbType.NVarChar, -1,
ParameterDirection.Input, false, 0, 0, "About",
;)DataRowVersion.Original, null
ﺇﻀﺎﻓﺔ ﺍﻟﻤﻌﺎﻤل ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﻤﻌﺎﻤﻼﺕ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ //
;)DaAuthors.UpdateCommand.Parameters.Add(P
ﺃﻭ ﻴﻤﻜﻨﻙ ﻓﺘﺢ ﻤﻠﻑ ﺘﺼﻤﻴﻡ ﺍﻟﻨﻤﻭﺫﺝ ،Form1_Designer.csﻭﺘﻌﺩﻴل ﺍﺴـﺘﻌﻼﻡ
ﺍﻟﺘﺤﺩﻴﺙ ﻤﺒﺎﺸﺭﺓ ،ﻭﺇﻥ ﻜﻨﺕ ﻻ ﺃﻨﺼﺢ ﺒﻬﺫﺍ.
ﻻﺤﻅ ﺃﻥ ﻤﻥ ﺍﻷﻓﻀل ﺘﻐﻴﻴﺭ ﻨﻭﻉ ﺍﻟﺤﻘل Aboutﻟﻴﻜﻭﻥ ﺃﻜﺜﺭ ﻤﻼﺀﻤﺔ ﻟﻭﻅﻴﻔﺘـﻪ..
ﻴﻤﻜﻨﻙ ﺍﻓﺘﺭﺍﺽ ﺃﻥ ﺃﻁﻭل ﻨﺒﺫﺓ ﻻ ﺘﺯﻴﺩ ﻋﻥ ٥٠٠ﺤﺭﻑ ﻤﺜﻼ ،ﻭﺘﻌﺭﻴﻑ ﻫﺫﺍ ﺍﻟﺤﻘل
ﻤﻨﻥ ﺍﻟﻨﻭﻉ ).nvarchar(50
١٥١
ﺩﻋﻨﺎ ﻨﻌﺩ ﺇﻟﻰ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴﺙ ﺍﻟﺴﺎﺒﻕ ،ﻓﻤﺎﺯﺍل ﻫﻨﺎﻙ ﻨﻭﻉ ﺜﺎﻟﺙ ﻤﻥ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻟﻡ
ﻨﺘﻁﺭﻕ ﺇﻟﻴﻪ ..ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻤﺨﺼﺹ ﻟﻠﻠﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻘﻴﻡ ﺍﻟﻤﻨﻌﺩﻤﺔ ) NULLﻤﺜـل
ﺍﻟﻤﻌﺎﻤل ..(@IsNull_Phoneﻭﺴﺒﺏ ﺍﺤﺘﻴﺎﺠﻨﺎ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤـل ،ﻫـﻭ ﺃﻥ ﺃﻱ
ﻋﻤﻠﻴﺔ ﻤﻘﺎﺭﻨﺔ ﻤﻊ ﺨﺎﻨﺔ ﻤﻨﻌﺩﻤﺔ ﺘﻌﻁﻲ ﺩﺍﺌﻤﺎ ،Falseﻟﻬﺫﺍ ﻟﻭ ﻜﺎﻨﺕ ﺨﺎﻨﺔ ﺍﻟﻬـﺎﺘﻑ
ﻓﺎﺭﻏﺔ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻜﺎﻨﺕ ﻓﺎﺭﻏﺔ ﺃﻴﻀﺎ ﻓﻲ ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ ﻤﻥ ﺍﻟﺴـﺠل،
ﻓﺈﻥ ﻤﻘﺎﺭﻨﺘﻬﻤﺎ ﺴﺘﻌﻁﻲ ،Falseﻭﻫﺫﺍ ﻴﻌﻨﻲ ﺃﻥ ﺒﺭﻨﺎﻤﺠﻙ ﻟﻥ ﻴﺴﺘﻁﻴﻊ ﺘﺤﺩﻴﺙ ﺨﺎﻨﺔ
ﺍﻟﻬﺎﺘﻑ ﺃﺒﺩﺍ!
ﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ،ﻨﺴﺘﺨﺩﻡ ﺍﻟﺸﺭﻁ ﺍﻟﺘﺎﻟﻲ:
)(@IsNull_Phone = 1) AND (Phone IS NULL
)OR (ID = @Original_ID
ﻫﺫﺍ ﺍﻟﺸﺭﻁ ﻴﺘﺄﻜﺩ ﻤﻥ ﺃﻥ ﺨﺎﻨﺔ ﺍﻟﻬﺎﺘﻑ ﻓﺎﺭﻏﺔ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺃﻨﻬﺎ ﻓﺎﺭﻏـﺔ
ﺃﻴﻀﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺃﻭ ﺃﻥ ﺍﻟﺨﺎﻨﺘﻴﻥ ﻓﻴﻬﻤﺎ ﻗﻴﻤﺘﺎﻥ ﻤﺘﺴﺎﻭﻴﺘﺎﻥ.
ﻭﻴﺘﻡ ﺘﻌﺭﻴﻑ ﺍﻟﻤﻌﺎﻤل (@IsNull_Phoneﺒﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ Trueﻓـﻲ ﺍﻟﺨﺎﺼـﻴﺔ
SourceColumnNullMappingﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻟﻤﻌﺎﻤل ،ﻭﻫﻭ ﻤﺎ ﻴﻤﻜﻥ ﻓﻌﻠﻪ
ﺒﺈﺭﺴﺎل ﺍﻟﻘﻴﻤﺔ Trueﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺘﺎﺴﻊ ﻓﻲ ﺇﺤﺩﻯ ﺼﻴﻎ ﺤﺩﺙ ﺍﻹﻨﺸـﺎﺀ New
ﻜﺎﻟﺘﺎﻟﻲ:
var P3 = new SqlParameter("@IsNull_Phone",
SqlDbType.Int, 0, ParameterDirection.Input,
0, 0, "Phone", DataRowVersion.Original,
;)"" true, null, "", "",
ﻻﺤﻅ ﺃﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺴﺘﺨﺩﻡ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴﺙ ﺍﻟﺴﺎﺒﻕ ﺒﺼﻭﺭﺓ ﺍﻓﺘﺭﺍﻀﻴﺔ ،ﻟﻜﻥ
ﻫﺫﺍ ﻗﺩ ﻴﻬﺒﻁ ﺒﻜﻔﺎﺀﺓ ﺒﺭﻨﺎﻤﺠﻙ ،ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺠﺩﻭل ﻴﺤﺘﻭﻱ ﻋﻠـﻰ ﻋـﺩﺩ ﻜﺒﻴـﺭ ﻤـﻥ
ﺍﻟﺴﺠﻼﺕ ،ﻤﻤﺎ ﻴﻌﻘﺩ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴﺙ ،ﻭﻴﺴﺘﻬﻠﻙ ﻭﻗﺘﺎ ﻤﻠﻤﻭﺴﺎ ﻤﻥ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ
ﻟﻠﺒﺤﺙ ﻋﻥ ﺍﻟﺴﺠل ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻷﻨﻪ ﺴﻴﻘﺎﺭﻥ ﻫﻨﺎ ﻜل ﺍﻟﺨﺎﻨﺎﺕ ،ﻭﻟﻴﺱ ﻤـﻥ
ﺍﻟﻤﺘﻭﻗﻊ ﻭﺠﻭﺩ ﻓﻬﺎﺭﺱ ﻟﻜل ﺃﻋﻤﺩﺓ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﺏ .ﻋﻨﺩ ﺘﺤﺩﻴﺙ ﺍﻟﺴﺠل ،ﻴﺘﻡ ﺍﻟﺒﺤﺙ ﻋﻨﻪ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻭﺍﺴﻁﺔ ﻤﻔﺘﺎﺤﻪ ﺍﻷﺴﺎﺴـﻲ
ﻓﻘﻁ )ﻜﻤﺎ ﻓﻌﻠﻨﺎ ﻓﻲ ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﺸﺎﺌﻡ( ..ﻤﻴﺯﺓ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﺃﻨﻬﺎ ﺘﺒﺴـﻁ ﺍﺴـﺘﻌﻼﻡ
١٥٢
ﺍﻟﺘﺤﺩﻴﺙ ،ﻭﺘﺠﻌل ﺍﻟﻌﺜﻭﺭ ﻋﻠﻰ ﺍﻟﺴﺠل ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺃﺴـﺭﻉ ﻷﻥ ﺍﻟﻤﻔﺘـﺎﺡ
ﺍﻷﺴﺎﺴﻲ ﻤﻔﻬﺭﺱ ،Indexedﻭﻫﻲ ﻤﻴﺯﺓ ﻫﺎﺌﻠﺔ ﻓﻲ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﻀـﺨﻤﺔ..
ﻟﻜﻥ ﻋﻴﺏ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﻫﻲ ﺃﻨﻬﺎ ﺘﺴﺘﺨﺩﻡ ﻤﺒﺩﺃ "ﺁﺨﺭ ﺘﺤﺩﻴﺙ ﻴﻜﺴﺏ!" ..ﺤﻴـﺙ ﺇﻥ
ﺍﻟﺴﺠﻼﺕ ﻴﺘﻡ ﺤﻔﻅﻬﺎ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺤ ﹼﺘﹼﻰ ﻭﻟﻭ ﻜﺎﻨﺕ ﻫﻨـﺎﻙ ﺘﻌـﺩﻴﻼﺕ ﻗـﺩ
ﺃﺠﺭﺍﻫﺎ ﻤﺴﺘﺨﺩﻡ ﺁﺨﺭ ﻋﻠﻴﻬﺎ ..ﺇﻨﹼﹼﻙ ﺘﻔﺭﺽ ﺴﺠﻼﺘﻙ ﻋﻠﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺭﻏﻡ ﺃﻨﻑ
ﺍﻟﺠﻤﻴﻊ )ﻭﻫﺫﺍ ﺴﻴﺨﺭﺏ ﺒﻴﺕ ﻤﺩﻴﺭ ﺍﻟﻤﺨﺎﺯﻥ ﻋﻨﺩ ﺘﻌﺩﻴل ﺴﻌﺭ ﻜﺘﺎﺏ ﻋﺼﺎ ﺍﻟﺤﻜـﻴﻡ(..
ﻟﻜﻥ ﺃﺤﻴﺎﻨﺎ ﺘﻜﻭﻥ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﻤﻘﺒﻭﻟﺔ ،ﻜﻤﺎ ﻓﻲ ﺃﻨﻅﻤﺔ ﺤﺠﺯ ﺭﺤـﻼﺕ ﺍﻟﻁﻴـﺭﺍﻥ،
ﻷﻥ ﺁﺨﺭ ﺘﻌﺩﻴل ﻓﻲ ﻤﻭﺍﻋﻴﺩ ﺍﻟﺤﺠﺯ ﻫﻭ ﺍﻷﻭﻟﻰ ﺒﺎﻻﻋﺘﺒﺎﺭ.
ﺝ .ﻋﻨﺩ ﺘﺤﺩﻴﺙ ﺍﻟﺴﺠل ،ﻴﺘﻡ ﺍﻟﺒﺤﺙ ﻋﻨﻪ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻭﺍﺴﻁﺔ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ
ﻭﻁﺎﺒﻊ ﺍﻟﻭﻗﺕ ..Timestampﻟﻔﻌل ﻫﺫﺍ ﻋﻠﻴـﻙ ﺇﻀـﺎﻓﺔ ﻋﻤـﻭﺩ ﻤـﻥ ﺍﻟﻨـﻭﻉ
timestampﺇﻟﻰ ﺍﻟﺠﺩﻭل ..ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻴﺘﻐﻴﺭ ﺘﻠﻘﺎﺌﻴﺎ ﻜﻠﻤﺎ ﺘـﻡ ﺘﻌـﺩﻴل ﺍﻟﺴـﺠل،
ﻭﺒﻬﺫﺍ ﻟﻭ ﻜﺎﻥ ﻁﺎﺒﻊ ﺍﻟﻭﻗﺕ ﺍﻟﺫﻱ ﺘﺤﺘﻔﻅ ﺒﻪ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺨﺘﻠﻔﺎ ﻋـﻥ ﻁـﺎﺒﻊ
ﺍﻟﻭﻗﺕ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﻬﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﺍﻟﺴﺠل ﻗﺩ ﺘﻐﻴـﺭ ،ﻭﻓـﻲ ﻫـﺫﻩ
ﺍﻟﺤﺎﻟﺔ ﻟﻥ ﻴﺤﻔﻅ ﺒﺭﻨﺎﻤﺠﻙ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻓﻲ ﻫﺫﺍ ﺍﻟﺴﺠل ..ﻻﺤﻅ ﺃﻥ ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ
ﻟﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﻨﺘﺞ ﺃﻭﺍﻤﺭ ﺘﺤﺩﻴﺙ ﺘﻌﺘﻤﺩ ﻋﻠﻰ ﻁﺎﺒﻊ ﺍﻟﻭﻗﺕ ﺇﺫﺍ ﻭﺠﺩﻩ ﻓﻲ ﺍﺴﺘﻌﻼﻡ
ﺍﻟﺘﺤﺩﻴﺩ ،ﺃﻤﺎ ﺇﺫﺍ ﻟﻡ ﻴﺠﺩﻩ ،ﻓﺈﻨﻪ ﻴﻨﺘﺞ ﺃﻭﺍﻤﺭ ﺘﺤﺩﻴﺙ ﺘﻘﺎﺭﻥ ﻜل ﺍﻟﺤﻘـﻭل ﻜﻤـﺎ ﻓـﻲ
ﺍﻟﻁﺭﻴﻘﺔ ﺃ.
ﻻﺤﻅ ﺃﻥ ﺍﻟﻁﺭﻴﻘﺘﻴﻥ ﺃ ﻭ ﺝ ﻫﻤﺎ ﺍﻷﻜﺜﺭ ﺸﻴﻭﻋﺎ ،ﻟﻜﻥ ﺒﻬﻤـﺎ ﻤﺸـﻜﻠﺔ ﻜﺒﻴـﺭﺓ ،ﻭﻫـﻲ ﺃﻥ
ﺒﺭﻨﺎﻤﺠﻙ ﺴﻴﻜﺘﻔﻲ ﺒﻌﺭﺽ ﺭﺴﺎﻟﺔ ﺨﻁﺄ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺘﺨﺒﺭﻩ ﺒﺄﻥ ﺃﺤﺩ ﺍﻟﺼﻔﻭﻑ ﻴﺘﻌﺎﺭﺽ ﻤـﻊ
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺩﻭﻥ ﺃﻥ ﻴﻌﺭﻑ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺤﺩﺜﺕ ﻓـﻲ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﻭﺩﻭﻥ ﺃﻥ
ﻴﺴﺘﻁﻴﻊ ﺇﻋﺎﺩﺓ ﺤﻔﻅ ﺍﻟﺴﺠل ،ﻷﻥ ﻨﻔﺱ ﺍﻟﺨﻁﺄ ﺴﻴﺴﺘﻤﺭ ﻓﻲ ﺍﻟﺤﺩﻭﺙ!!
ﻭﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ،ﻋﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺤﺩﺙ RowUpdatedﺍﻟﺨﺎﺹ ﺒﻤﻬﻴـﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺒﺎﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﺎﻟﻴﺔ:
١٥٣
-ﺇﺫﺍ ﻜﺎﻨﺕ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ e.RecordsAffectedﺘﺴﺎﻭﻱ ﺼﻔﺭﺍ ،ﻓﻬﺫﺍ ﻤﻌﻨـﺎﻩ ﺃﻥ
ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﻟﻡ ﻴﺅﺜﺭ ﻋﻠﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻷﻨﻪ ﻟﻡ ﻴﺠﺩ ﺍﻟﺴﺠل ﺍﻟﻤﻁﻠﻭﺏ ﺘﺤﺩﻴﺜﻪ،
ﺇﻤﺎ ﻷﻥ ﻤﺴﺘﺨﺩﻤﺎ ﺁﺨﺭ ﺤﺫﻓﻪ ﺃﻭ ﻋﺩل ﺒﻴﺎﻨﺎﺘﻪ ..ﻫﺫﺍ ﻫﻭ ﺍﻟﺘﻌﺎﺭﺽ ﺍﻟـﺫﻱ ﻨﺒﺤـﺙ
ﻋﻨﻪ ..ﻻﺤﻅ ﺃﻥ ﻭﺠﻭﺩ ﺠﻤﻠﺔ SELECTﻓﻲ ﻨﻬﺎﻴـﺔ ﺃﻤـﺭ ﺍﻟﺘﺤـﺩﻴﺙ ﺴـﻴﺠﻌل
ﺍﻟﺨﺎﺼﻴﺔ RecordsAffectedﺘﻌﻴﺩ ﺍﻟﺭﻗﻡ ٠ﺩﺍﺌﻤﺎ ..ﻟﻬﺫﺍ ﺇﺫﺍ ﺃﺭﺩﺕ ﺃﻥ ﺘﺴـﺘﻔﻴﺩ
ﻤﻥ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻓﻲ ﻤﻌﺭﻓﺔ ﺇﻥ ﻜﺎﻥ ﺍﻟﺘﺤﺩﻴﺙ ﻗﺩ ﺘﻡ ﺃﻡ ﻻ ،ﻓﻌﻠﻴﻙ ﺃﻥ ﺘﺯﻴل ﺠﻤﻠﺔ
ﺍﻟﺘﺤﺩﻴﺩ ﻤﻥ ﻨﻬﺎﻴﺔ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ..ﻭﺴﻨﻌﺭﻑ ﻜﻴﻑ ﻨﻔﻌل ﻫﺫﺍ ﻤﻥ ﺨـﻼل ﺍﻟﻤﻌـﺎﻟﺞ
ﺍﻟﺴﺤﺭﻱ ﻟﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻌﺩ ﻗﻠﻴل.
-ﻀﻊ ﻨﺼﺎ ﻴﺩل ﻋﻠﻰ ﺤﺩﻭﺙ ﺨﻁﺄ ﻓـﻲ ﺍﻟﺨﺎﺼـﻴﺔ ،e.Row.RowErrorﻤﺜـل
"ﺤﺩﺙ ﺘﻌﺎﺭﺽ ﻤﻊ ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﻷﻥ ﺃﺤﺩ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻗﺎﻡ ﺒﺘﻌﺩﻴﻠﻪ ﺃﻭ ﺤﺫﻓﻪ"..
ﻫﺫﺍ ﺴﻴﺠﻌل ﺃﻴﻘﻭﻨﺔ ﺍﻟﺨﻁﺄ ﺘﻅﻬﺭ ﺒﺠﻭﺍﺭ ﺍﻟﺴﺠل ﻓـﻲ ﺠـﺩﻭل ﺍﻟﻌـﺭﺽ ،ﻭﻋﻨـﺩ
ﺍﻟﺘﺤﻠﻴﻕ ﻓﻭﻗﻬﺎ ﺒﺎﻟﻔﺄﺭﺓ ﺴﻴﻅﻬﺭ ﺘﻠﻤﻴﺢ ﻋﻠﻰ ﺍﻟﺸﺎﺸﺔ ﻴﻌﺭﺽ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺍﻟﻨﺹ ﺍﻟـﺫﻱ
ﻜﺘﺒﺘﻪ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ.
-ﺇﺫﺍ ﺃﺭﺩﺕ ﺃﻥ ﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻓﻲ ﺴﻁﺭ ﺍﺴـﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ Update
ـﻴﺔ e.Statusﺍﻟﻘﻴﻤــﺔ
ـﻲ ﺍﻟﺨﺎﺼـ
ـﻊ ﻓـ
ـﺔ ،ﻓﻀـ
ـﻙ ﺍﻟﺨﺎﺼـ
ـﻪ ﺒﻁﺭﻴﻘﺘـ
ﻟﺘﻌﺎﻟﺠـ
..ErrorsOccurredﺃﻤﺎ ﺇﺫﺍ ﺃﺭﺩﺕ ﻤﻭﺍﺼﻠﺔ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ ،ﻓﻀﻊ ﻓﻴﻬﺎ ﺍﻟﻘﻴﻤـﺔ
Continueﺃﻭ ..SkipCurrentRowﺩﻋﻨﺎ ﻨﺴﺘﺨﺩﻡ ﺍﻟﻘﻴﻤﺔ ﺍﻷﺨﻴﺭﺓ.
-ﻴﺒﺩﻭ ﺍﻷﻤﺭ ﺭﺍﺌﻌﺎ ﺤﺘﻰ ﺍﻵﻥ ،ﻭﺴﻴﻼﺤﻅ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻅﻬﻭﺭ ﺃﻴﻘﻭﻨﺎﺕ ﺍﻟﺨﻁﺄ ﺒﺠـﻭﺍﺭ
ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﻓﺸل ﺘﺤﺩﻴﺜﻬﺎ ..ﻟﻜﻥ ﺍﻟﻤﺸﻜﻠﺔ ﺃﻥ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻻ ﻴﻌﺭﻑ ﺍﻟﺘﻌﺩﻴل ﺍﻟﺫﻱ
ﺃﺩﺨﻠﻪ ﺍﻟﻤﺴﺘﺨﺩﻤﻭﻥ ﺍﻵﺨﺭﻭﻥ ﻋﻠﻰ ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ..ﻟﻬـﺫﺍ
ﺴﻨﻠﺠﺄ ﺇﻟﻰ ﻁﺭﻴﻘﺔ ﻤﺒﺘﻜﺭﺓ ،ﻭﻫﻲ ﺍﺴﺘﺨﺩﺍﻡ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺍﺴـﻤﻪ DaErrAuthor
ﻟﺘﺤﻤﻴل ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﻤﺭﺓ ﺃﺨﺭﻯ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺨﺎﺼﺔ ﺍﺴﻤﻬﺎ ،DsErr
ﻭﻋﺭﻀﻪ ﻓﻲ ﺠﺩﻭل ﻋﺭﺽ ﺁﺨﺭ ﺍﺴﻤﻪ ،DgErrorsﻟﻴﻘـﺎﺭﻥ ﺍﻟﻤﺴـﺘﺨﺩﻡ ﺒـﻴﻥ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﻴﺭﻴﺩ ﺤﻔﻅﻬﺎ ،ﻭﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺤﻔﻅﻬﺎ ﻤﺴﺘﺨﺩﻡ ﺁﺨﺭ ،ﻭﻴﺘﺨـﺫ ﻗـﺭﺍﺭﻩ
ﺒﻨﺎﺀ ﻋﻠﻰ ﻫﺫﺍ ،ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ.
١٥٤
ﻻﺤﻅ ﺃﻥ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺤﺫﻓﻬﺎ ﻤﺴﺘﺨﺩﻡ ﺁﺨﺭ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻥ ﺘﻅﻬﺭ ﻓـﻲ
ﺠﺩﻭل ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻌﺩﻟﺔ ..ﻤﻥ ﺍﻟﺴﻬل ﺃﻥ ﻴﻔﻬﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺃﻥ ﺍﻟﺴﺠل ﻗﺩ ﺤﺫﻑ ﺇﺫﺍ
ﻟﻡ ﻴﺠﺩﻩ ،ﻟﻜﻨﻨﺎ ﺃﻴﻀﺎ ﻨﺴﺘﻁﻴﻊ ﺍﻟﺘﺴﻬﻴل ﻋﻠﻴﻪ ،ﺒﺘﻐﻴﻴﺭ ﺭﺴﺎﻟﺔ ﺍﻟﺨﻁﺄ ﺇﺫﺍ ﻜﺎﻥ ﺍﺴﺘﻌﻼﻡ
ﺍﻟﺘﺤﺩﻴﺙ ﻻ ﻴﻌﻴﺩ ﺃﻴﺔ ﺴﺠﻼﺕ ،ﻭﺫﻟﻙ ﻜﺎﻟﺘﺎﻟﻲ:
)if (DaErrAuthor.Fill(DsErr, "Authors") > 0
" +ﺤﺩﺙ ﺘﻌﺎﺭﺽ ﻤﻊ ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ" = e.Row.RowError
;"ﻷﻥ ﺃﺤﺩ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻗﺎﻡ ﺒﺘﻌﺩﻴﻠﻪ ﺃﻭ ﺤﺫﻓﻪ "
else
" +ﻫﺫﺍ ﺍﻟﺴﺠل ﺤﺫﻓﻪ ﻤﺴﺘﺨﺩﻡ" = e.Row.RowError
;"ﺁﺨﺭ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ "
-ﺒﻘﻴﺕ ﺃﻤﺎﻤﻨﺎ ﺨﻁﻭﺓ ﺃﺨﻴﺭﺓ ،ﻭﻫﻲ :ﻜﻴﻑ ﻨﺴﻤﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺒﺘﻌﺩﻴل ﺍﻟﺴﺠل ﺍﻟﻤﻌﺩل ﺃﻭ
ﺇﻋﺎﺩﺓ ﺍﻟﺴﺠل ﺍﻟﻤﺤﺫﻭﻑ ،ﺇﻥ ﻗﺭﺭ ﻫﺫﺍ؟ ..ﺃﻨﺎ ﺃﺭﻯ ﺃﻥ ﺃﻓﻀل ﺤل ،ﻫـﻭ ﻋـﺭﺽ
ﻗﺎﺌﻤﺔ ﻤﻭﻀﻌﻴﺔ ﺤﻴﻨﻤﺎ ﻴﻀﻐﻁ ﺍﻟﺼﻑ ﺍﻟﺫﻱ ﺒﻪ ﺨﻁﺄ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﻌﻠـﻭﻱ ،ﻭﻤـﻥ
ﻫﺫﻩ ﺍﻟﻘﺎﺌﻤﺔ ﻴﺨﺘﺎﺭ ﻤﺎ ﻴﻨﺎﺴﺒﻪ ﻤﻤﺎ ﻴﻠﻲ:
١٥٥
ﺃ .ﺍﻷﻤﺭ "ﺃﺭﻴﺩ ﺤﻔﻅ ﺘﻌﺩﻴﻼﺘﻲ":
ﺴﻨﺴﺘﺨﺩﻡ ﻫﺫﺍ ﺍﻷﻤﺭ ﻋﻨﺩﻤﺎ ﻴﻐﻴﺭ ﻤﺴﺘﺨﺩﻡ ﺁﺨﺭ ﺍﻟﺴﺠل ،ﻭﻫـﻭ ﻴﻨﺴـﺦ ﺍﻟﻘـﻴﻡ
ﺍﻷﺼﻠﻴﺔ ﻤﻥ ﺴﺠل ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻌﺩل ﻭﻴﺠﻌﻠﻬﺎ ﺍﻟﻘـﻴﻡ ﺍﻷﺼـﻠﻴﺔ ﻟﻠﺴـﺠل
ﺍﻟﺨﺎﺹ ﺒﺎﻟﻤﺴﺘﺨﺩﻡ ،ﻭﻫﺫﺍ ﺤﺘﻰ ﻴﻨﺠﺢ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴﺙ ﻓـﻲ ﺍﻟﻌﺜـﻭﺭ ﻋﻠـﻰ
ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻤﻥ ﺜﻡ ﻟﻭ ﻀﻐﻁ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺯﺭ ﺍﻟﺤﻔـﻅ
ﻴﺘﻡ ﺤﻔﻅ ﺘﻌﺩﻴﻼﺘﻪ ..ﻭﺍﻟﻤﺴﺘﺨﺩﻡ ﻫﻭ ﺍﻟﻤﺴﺌﻭل ﻋﻥ ﻨﻘل ﺃﻴﺔ ﻗﻴﻤﺔ ﻴـﺩﻭﻴﺎ ﻤـﻥ
ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﺍﻟﻤﻌﺭﻭﺽ ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﺍﻟﺴﻔﻠﻲ ﺇﻟﻰ ﺍﻟﺴﺠل ﺍﻟﺨـﺎﺹ
ﺒﻪ ﻗﺒل ﻀﻐﻁ ﺯﺭ ﺍﻟﺤﻔﻅ ..ﻭﺍﻟﺴﺠل ﺍﻟﺫﻱ ﺘﻨﺠﺢ ﻤﺤﺎﻭﻟﺔ ﺤﻔﻅﻪ ﻤﺭﺓ ﺃﺨـﺭﻯ،
ﻋﻠﻴﻨﺎ ﺃﻥ ﻨﺯﻴل ﺴﺠل ﺍﻟﺨﻁﺄ ﺍﻟﻤﻨﺎﻅﺭ ﻟﻪ ﻤﻥ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﺍﻟﺴﻔﻠﻲ.
ﻻﺤﻅ ﺃﻨﻙ ﻓﻲ ﺍﻟﺒﺭﺍﻤﺞ ﺍﻟﻌﻤﻠﻴﺔ ﻗﺩ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﻤﺭﺍﻋﺎﺓ ﺃﻭﻟﻭﻴﺎﺕ ﺍﻟﻤﺴـﺘﺨﺩﻤﻴﻥ
ﻓﻲ ﺇﺠﺭﺍﺀ ﺍﻟﺘﻐﻴﻴﺭ ..ﻤﺜﻼ :ﻟﻭ ﻜﺎﻥ ﺍﻟﻤﺩﻴﺭ ﻫﻭ ﻤـﻥ ﻗـﺎﻡ ﺒﺘﻌـﺩﻴل ﺍﻟﺴـﺠل،
ﻓﺴﻴﺴﺘﺸﻴﻁ ﻏﻀﺒﺎ ﻟﻭ ﻗﺎﻡ ﺃﺤﺩ ﺍﻟﻤﻭﻅﻔﻴﻥ ﺒﺈﻟﻐﺎﺀ ﺘﻌﺩﻴﻠﻪ! ..ﻟﻬﺫﺍ ﻗﺩ ﺘﺤﺘﺎﺝ ﺇﻟـﻰ
ﺇﻀﺎﻓﺔ ﺤﻘل ﺍﺴﻤﻪ UserIDﺇﻟﻰ ﺍﻟﺠﺩﻭل ،ﻟﻴﺭﺒﻁـﻪ ﺒﺠـﺩﻭل ﺍﻟﻤﺴـﺘﺨﺩﻤﻴﻥ
،Usersﺒﺤﻴﺙ ﺘﻀﻊ ﺭﻗﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻟﺫﻱ ﺃﺠﺭﻯ ﺁﺨﺭ ﺘﻌﺩﻴل ،ﻭﻋﻨﺩ ﺤـﺩﻭﺙ
ﺍﻟﺘﻌﺎﺭﺽ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻻ ﺘﺴﻤﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺒﺎﺘﺨﺎﺫ ﻗﺭﺍﺭ ﺤﻔﻅ ﺘﻌﺩﻴﻼﺘـﻪ ﺇﻻ
ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻵﺨﺭ ﺃﻗل ﺃﻭﻟﻭﻴﺔ ﻤﻨﻪ ﺃﻭ ﻋﻠﻰ ﺍﻷﻗل ﻟﻪ ﻨﻔﺱ ﺍﻷﻭﻟﻭﻴﺔ ﻓﻲ
ﺇﺠﺭﺍﺀ ﺍﻟﺘﻌﺩﻴﻼﺕ ..ﻭﻴﻤﻜﻨﻙ ﻤﻌﺭﻓﺔ ﺃﻭﻟﻭﻴﺎﺕ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻤﻥ ﺍﻟﺠﺩﻭل ،User
ﺍﻟﺫﻱ ﻻ ﺒﺩ ﺃﻥ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻤﻭﺩ ﻴﻭﻀﺢ ﻭﻅﻴﻔﺔ ﺍﻟﻤﺴﺘﺨﺩﻡ ،ﺃﻭ ﻋﻤﻭﺩ ﻴﻭﻀﺢ
ﺘﺭﺘﻴﺒﻪ ﻓﻲ ﺍﻟﺴﻠﻡ ﺍﻟﻭﻅﻴﻔﻲ ﺃﻭ ﻤﺩﻯ ﺼﻼﺤﻴﺎﺘﻪ.
١٥٦
ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻤﻤﺎ ﻴﻠﻐﻲ ﺘﻌﺩﻴﻼﺘﻪ ،ﻭﻴﺤﺎﻓﻅ ﻋﻠﻰ ﺍﻟﺘﻌـﺩﻴل ﺍﻟﻘـﺎﺩﻡ ﻤـﻥ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﺝ .ﺍﻷﻤﺭ "ﺃﺭﻴﺩ ﺇﻋﺎﺩﺓ ﺇﺩﺭﺍﺝ ﺍﻟﺴﺠل ﺍﻟﻤﺤﺫﻭﻑ":
ﺴﻨﺴﺘﺨﺩﻡ ﻫﺫﺍ ﺍﻷﻤﺭ ﺇﺫﺍ ﺤﺫﻑ ﻤﺴﺘﺨﺩﻡ ﺁﺨﺭ ﺍﻟﺴﺠل ﻤﻥ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ..
ﻭﻜل ﻤﺎ ﻴﻔﻌﻠﻪ ﻫﺫﺍ ﺍﻷﻤﺭ ،ﻫﻭ ﺘﻐﻴﻴﺭ ﺤﺎﻟﺔ ﺍﻟﺴـﺠل ﺍﻟﺤـﺎﻟﻲ ﺇﻟـﻰ ،Added
ﻟﻴﻌﺘﺒﺭﻩ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﺴﺠﻼ ﺠﺩﻴﺩﺍ ﻭﻴﻀﻴﻔﻪ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺴــﺘﺠﺩ ﺍﻟﻜــﻭﺩ ﺍﻟﻜﺎﻤــل ﺍﻟــﺫﻱ ﻴﻨﻔــﺫ ﻜــل ﻫــﺫﻩ ﺍﻷﻓﻜــﺎﺭ ﻓــﻲ ﺍﻟﻤﺸــﺭﻭﻉ
..OptimisticConcurrencyﻭﺘﻭﺠــﺩ ﻨﺴــﺨﺔ ﺃﺨــﺭﻯ ﻤﻨــﻪ ﻓــﻲ ﺍﻟﻤﺸــﺭﻭﻉ
،OptimisticConcurrencyWithTimeStampﻨﺴﺘﺨﺩﻡ ﻓﻴﻬﺎ ﻁﺎﺒﻊ ﺍﻟﻭﻗـﺕ ،ﺤﻴـﺙ
ﻋﺭﻓﻨﺎ ﻋﻤﻭﺩﺍ ﺍﺴﻤﻪ RowVersionﻓﻲ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻨﻭﻋﻪ ..Timstampﻻﺤﻅ ﺃﻥ
ﻋﺭﺽ ﻁﺎﺒﻊ ﺍﻟﻭﻗﺕ ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ DatagridViewﻴﺴﺒﺏ ﺃﺨﻁﺎﺀ ﻷﻨـﻪ ﻴﺤـﺎﻭل
ﺭﺴﻡ ﻁﺎﺒﻊ ﺍﻟﻭﻗﺕ ﺒﺎﻋﺘﺒﺎﺭﻩ ﺼﻭﺭﺓ! ..ﻭﻟﺤل ﺍﻟﻤﺸﻜﻠﺔ ،ﻋﻠﻴﻙ ﺇﺨﻔﺎﺀ ﻋﻤﻭﺩ ﻁﺎﺒﻊ ﺍﻟﻭﻗـﺕ،
ﻓﻼ ﻴﻭﺠﺩ ﻤﺒﺭﺭ ﺃﺼﻼ ﻟﻌﺭﻀﻪ ﻟﻠﻤﺴﺘﺨﺩﻡ! ..ﻟﻔﻌل ﻫﺫﺍ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ:
;DgAuthors.Columns["RowVersion"].Visible = false
ﻻﺤﻅ ﺃﻥ ﻫﻨﺎﻙ ﻤﺸﻜﻠﺔ ﺴﺘﻭﺍﺠﻬﻨﺎ ﻓﻲ ﻫﺫﺍ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﺒﺴﺒﺏ ﻋﺩﻡ ﺍﺴﺘﺨﺩﺍﻤﻨﺎ ﺠﻤﻠـﺔ ﺘﺤﺩﻴـﺩ
Selectﺒﻌﺩ ﺠﻤﻠﺔ ﺍﻟﺘﺤﺩﻴﺙ ،Updateﻭﺫﻟﻙ ﻷﻥ ﻁﺎﺒﻊ ﺍﻟﻭﻗﺕ ﻴﺘﻐﻴﺭ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺒﺎﺴﺘﻤﺭﺍﺭ ﺒﻌﺩ ﻜل ﻋﻤﻠﻴﺔ ﺘﺤﺩﻴﺙ ،ﻭﻟﻭ ﻟﻡ ﻨﻨﻌﺵ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒـﺎﻟﻘﻴﻡ ﺍﻟﺠﺩﻴـﺩﺓ ﻟـﻪ،
ﻓﺴﺘﺤﺩﺙ ﻤﺸﻜﻠﺔ ﺘﻁﺎﺒﻕ ﺒﻼ ﺩﺍﻉ ..ﻭﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ،ﺍﺴﺘﺨﺩﻤﻨﺎ ﻤﻬﻴﺊ ﺒﻴﺎﻨـﺎﺕ ﺍﺴـﻤﻪ
،DaTimestampﻤﻬﻤﺘﻪ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﺘﻡ ﺘﺤﺩﻴﺜﻪ ،ﻭﻭﻀﻌﻪ ﻓﻲ ﻤﺠﻤﻭﻋﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻹﻨﻌﺎﺸﻬﺎ ..ﻟﻬﺫﺍ ﻴﺴﺘﺨﺩﻡ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ﺍﻟﺨﺎﺹ ﺒﻬﺫﺍ ﺍﻟﻤﻬﻴﺊ ﺠﻤﻠﺔ ﺍﻟﺘﺤﺩﻴﺩ ﺍﻟﺘﺎﻟﻴﺔ:
١٥٧
Select * From Authors
Where ID = @ID
ﻭﺃﻨﺴﺏ ﻤﻜﺎﻥ ﻻﺴﺘﺨﺩﺍﻡ ﻫﺫﺍ ﺍﻟﻤﻬﻴﺊ ،ﻫﻭ ﺍﻟﺤﺩﺙ ،RowUpdatedﻷﻨﻪ ﻴﻨﻁﻠﻕ ﻤﺒﺎﺸـﺭﺓ
ﺒﻌﺩ ﺘﺤﺩﻴﺙ ﺍﻟﺼﻑ ،ﻟﻬﺫﺍ ﻴﻤﻜﻨﻨﺎ ﺃﻥ ﻨﻘﺭﺃ ﺍﻟﺼﻑ ﻤﺭﺓ ﺃﺨـﺭﻯ ﺒﻌـﺩ ﺃﻥ ﻏﻴـﺭﺕ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻁﺎﺒﻊ ﺍﻟﻭﻗﺕ ﺍﻟﺨﺎﺹ ﺒﻪ ..ﻟﻬﺫﺍ ﻁﻭﺭﻨﺎ ﺠﻤﻠﺔ ﺍﻟﺸﺭﻁ ﺍﻟﺘﻲ ﻨﺴـﺘﺨﺩﻤﻬﺎ ﻓـﻲ ﻫـﺫﺍ
ﺍﻟﺤﺩﺙ ،ﺒﺈﻀﺎﻓﺔ ﺍﻟﻤﻘﻁﻊ Elseﻜﺎﻟﺘﺎﻟﻲ:
)if (e.RecordsAffected == 0
{
ﺍﻟﻜﻭﺩ ﺍﻟﻤﻨﺎﺴﺏ ﻟﺤل ﻤﺸﻜﻠﺔ ﺍﻟﺘﻁﺎﺒﻕ //
}
)else if (e.StatementType != StatementType.Delete
{
;]"TimestampCmd.Parameters[0].Value = e.Row["ID
;)"DaTimestamp.Fill(Ds, "Authors
}
ﻻﺤﻅ ﺃﻨﻨﺎ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺸﺭﻁﺎ ﻻﺴﺘﺜﻨﺎﺀ ﺤﺎﻟﺔ ﺤﺫﻑ ﺴﺠل ،ﻓﺎﻟﺴﺠل ﺴﻴﺤﺫﻑ ﻤـﻥ ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﻤﺎ ﺤﺫﻑ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻟﻴﺴﺕ ﻟﺩﻴﻨﺎ ﻤﺸﻜﻠﺔ.
ـﺭﻭﻉ
ـﺫﺍ ﺍﻟﻤﺸـﺭﻭﻉ ﻋـﻥ ﺍﻟﻤﺸـ
ـﺩ ﺃﻱ ﺍﺨـﺘﻼﻑ ﻓـﻲ ﻜـﻭﺩ ﻫـ
ﻏﻴـﺭ ﻫـﺫﺍ ﻟـﻥ ﺘﺠـ
،OptimisticConcurrencyﻓﺎﻟﻔﺭﻭﻕ ﻜﻠﻬﺎ ﺘﻨﺤﺼﺭ ﻓﻲ ﺼﻴﻐﺔ ﺍﺴﺘﻌﻼﻤﺎﺕ ﺍﻟﺘﺤـﺩﻴﺙ،
ﺍﻟﺘﻲ ﺘﺯﻴﺩ ﻜﻔﺎﺀﺓ ﺒﺭﻨﺎﻤﺠﻙ ﺒﺴﺒﺏ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﻁﺎﺒﻊ ﺍﻟﻭﻗﺕ ،ﺒﺩﻴﻼ ﻋﻥ ﻤﻘﺎﺭﻨﺔ ﻜـل ﺍﻟﻘـﻴﻡ
ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺨﺎﻨﺎﺕ ﺍﻟﺼﻑ.
١٥٨
ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﻹﻋﺩﺍﺩ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ
Data Adapter Configuration Wizard
ﺍﺴﺘﺨﺩﻡ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻟﺞ ﻟﺘﺴﻬﻴل ﻀﺒﻁ ﻭﻅﻴﻔﺔ ﻭﺨﺼﺎﺌﺹ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻴﻤﻜﻨﻙ ﺘﺸـﻐﻴل ﻫـﺫﺍ
ﺍﻟﻤﻌﺎﻟﺞ ﺒﺎﺘﺒﺎﻉ ﺃﻱ ﻤﻤﺎ ﻴﻠﻲ:
-ﺍﻟﻨﻘﺭ ﻤﺭﺘﻴﻥ ﻋﻠﻰ ﺃﻴﻘﻭﻨﺔ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ SqlDataAdapterﻓﻲ ﺼـﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ
.ToolBox
-ﺴﺤﺏ ﺃﻴﻘﻭﻨﺔ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ ﻭﺇﻟﻘﺎﺌﻬﺎ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ.
-ﻀــﻐﻁ ﻤﻬﻴــﺊ ﺍﻟﺒﻴﺎﻨــﺎﺕ ﺒﻌــﺩ ﺇﻀــﺎﻓﺘﻪ ﺇﻟــﻰ ﺼــﻴﻨﻴﺔ ﻤﻜﻭﻨــﺎﺕ ﺍﻟﻨﻤــﻭﺫﺝ
Component Trayﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀـﻌﻴﺔ ﻀـﻐﻁ ﺍﻷﻤـﺭ
.Configure Data Adapter
ﻭﻴﺒﺩﺃ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻟﺞ ﺒﻨﺎﻓﺫﺓ ﺘﻁﻠﺏ ﻤﻨﻙ ﺍﺨﺘﻴﺎﺭ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﻻﺘﺼﺎل ﺒﻬﺎ:
١٥٩
ﻓﻲ ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ،ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ﻻﺨﺘﻴﺎﺭ ﺍﺴﻡ ﺇﺤﺩﻯ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨـﺎﺕ ﺴـﻴﻜﻭﻴل
ﺴﻴﺭﻓﺭ ﺍﻟﺘﻲ ﺃﻀﻔﺕ ﺍﺘﺼﺎﻻ ﺒﻬﺎ ﻤﻥ ﻗﺒل ﻓﻲ ﻤﺘﺼﻔﺢ ﺍﻟﺨﻭﺍﺩﻡ .Server Explorer
ﻭﻟﻭ ﺃﺭﺩﺕ ﺇﻨﺸﺎﺀ ﺍﺘﺼﺎل ﺠﺩﻴﺩ ﺒﻘﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺃﺨﺭﻯ ،ﻓﺎﻀـﻐﻁ ﺍﻟـﺯﺭNew Connection
ﻟﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﺇﻀﺎﻓﺔ ﺍﺘﺼﺎل Add Connectionﺍﻟﺘﻲ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻤﻥ ﻗﺒل ﻓﻲ ﻤﺘﺼـﻔﺢ
ﺍﻟﺨﻭﺍﺩﻡ.
ﻭﻟﻭ ﻀﻐﻁﺕ ﺍﻟﻌﻼﻤﺔ +ﺍﻟﻤﺠﺎﻭﺭﺓ ﻟﻠﺠﻤﻠﺔ Connection Stringﻓﻲ ﺍﻟﺠـﺯﺀ ﺍﻟﺴـﻔﻠﻲ ﻤـﻥ
ﺍﻟﻨﺎﻓﺫﺓ ،ﻓﺴﻴﺘﻡ ﻋﺭﺽ ﻤﺭﺒﻊ ﻨﺹ ﻗﺎﺒل ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ ،ﺒﻪ ﻨﺹ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﺘـﻲ
ﺍﺨﺘﺭﺘﻬﺎ ..ﻭﻴﻤﻜﻨﻙ ﻨﺴﺦ ﻫﺫﺍ ﺍﻟﻨﺹ ﻻﺴﺘﺨﺩﺍﻤﻪ ﻓﻲ ﺃﻱ ﻤﻭﻀﻊ ﺁﺨﺭ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻟﻭ ﺃﺭﺩﺕ.
ﺠﺭﺏ ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜﺎل ﺍﺨﺘﻴﺎﺭ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ ،Books.mdfﻭﺍﻀﻐﻁ ﺍﻟﺯﺭ .Next
ﻓﻲ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ ﻴﻤﻜﻨﻙ ﺍﺨﺘﻴﺎﺭ ﻨﻭﻉ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺫﻱ ﺴﺘﺴﺘﺨﺩﻤﻪ ﻹﺤﻀﺎﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ:
١٦٠
ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﺘﺘﻴﺢ ﻟﻙ ﺍﺨﺘﻴﺎﺭ ﻭﺍﺤﺩ ﻤﻤﺎ ﻴﻠﻲ:
oﺍﺴﺘﺨﺩﺍﻡ ﺠﻤل .(Use SQL Statements) SQL
oﺇﻨﺸﺎﺀ ﺇﺠﺭﺍﺀﺍﺕ ﻤﺨﺯﻨﺔ ﺠﺩﻴﺩﺓ ).(Create New Stored Procedures.
oﺍﺴــﺘﺨﺩﺍﻡ ﺇﺠــﺭﺍﺀﺍﺕ ﻤﺨﺯﻨــﺔ ﻤﻭﺠــﻭﺩ ﺴــﺎﺒﻘﺎ ﻓــﻲ ﻗﺎﻋــﺩﺓ ﺍﻟﺒﻴﺎﻨــﺎﺕ
).(Use Existing Stored Procedures
ﻭﻴﺤﺩﺩ ﺍﺨﺘﻴﺎﺭﻙ ،ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ ﺍﻟﺘﻲ ﺴﺘﻅﻬﺭ ﻋﻨﺩﻤﺎ ﺘﻀﻐﻁ ﺍﻟﺯﺭ ،Nextﻭﺫﻟﻙ ﻜﺎﻟﺘﺎﻟﻲ:
-ﺇﺫﺍ ﺍﺨﺘﺭﺕ ﺍﺴﺘﺨﺩﺍﻡ ﺠﻤﻠﺔ SQLﺃﻭ ﺇﻨﺸﺎﺀ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻥ ﺠﺩﻴﺩ ،ﻓﺴﺘﻅﻬﺭ ﻟﻙ ﺍﻟﻨﺎﻓـﺫﺓ
ﺍﻟﺘﺎﻟﻴﺔ:
ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﺘﻘﺩﻡ ﻟﻙ ﻤﺭﺒﻊ ﻨﺹ ﺘﺴﺘﻁﻴﻊ ﺃﻥ ﺘﻀﻴﻑ ﻓﻴﻪ ﻴﺩﻭﻴﺎ ﺃﻭ ﺒﺎﻟﻠﺼﻕ ،ﻨﺹ ﺠﻤﻠﺔ
ﺍﻻﺴﺘﻌﻼﻡ ﺃﻭ ﺠﻤﻠﺔ ﺇﻨﺸﺎﺀ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ﺍﻟﺠﺩﻴﺩ.
ﻭﻴﻤﻜﻨﻙ ﻀﻐﻁ ﺍﻟﺯﺭ Query Builder ﻻﺴﺘﺨﺩﺍﻡ ﺒﺎﻨﻲ ﺍﻻﺴﺘﻌﻼﻡ ﻓﻲ ﺇﻨﺸـﺎﺀ ﺠﻤﻠـﺔ
،SQLﻭﻋﻨﺩ ﺇﻏﻼﻕ ﺒﺎﻨﻲ ﺍﻻﺴﺘﻌﻼﻡ ﺴﺘﺠﺩ ﻫﺫﻩ ﺍﻟﺠﻤﻠﺔ ﻤﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺭﺒـﻊ ﺍﻟـﻨﺹ..
١٦١
ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﺍﻟﺯﺭ ﻤﻭﺠﻭﺩ ﺃﻴﻀﺎ ﻓﻲ ﺤﺎﻟﺔ ﺇﻨﺸﺎﺀ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻥ ﺠﺩﻴﺩ ،ﻷﻨﻙ ﻗﺩ ﺘﺤﺘﺎﺝ
ﺇﻟﻰ ﺇﻀﺎﻓﺔ ﺠﻤﻠﺔ ﺍﺴﺘﻌﻼﻡ ﺩﺍﺨل ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ،ﻟﻬﺫﺍ ﻤﻥ ﺍﻷﺴﻬل ﺃﻥ ﺘﻨﺸـﺊ ﻫـﺫﻩ
ﺍﻟﺠﻤﻠﺔ ﺒﺒﺎﻨﻲ ﺍﻻﺴﺘﻌﻼﻡ ،ﺜﻡ ﺘﻀﻴﻑ ﺇﻟﻰ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺒﻌﺩ ﻫﺫﺍ ﺼﻴﻐﺔ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ
ﺍﻟﺫﻱ ﻴﺤﺘﻭﻴﻬﺎ ..ﻭﺇﻥ ﻜﻨﺕ ﺃﻨﺼﺤﻙ ﺒﻌﺩﻡ ﺇﻨﺸﺎﺀ ﺍﻹﺠﺭﺍﺀﺍﺕ ﺍﻟﻤﺨﺯﻨﺔ ﺒﻬـﺫﻩ ﺍﻟﻁﺭﻴﻘـﺔ،
ﻷﻥ ﺇﻨﺸﺎﺀ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ﺒﺎﺴﺘﺨﺩﺍﻡ ﻤﺘﺼﻔﺢ ﺍﻟﺨـﻭﺍﺩﻡ Sever Explorerﺃﺴـﻬل
ﻭﺃﻓﻀل ﺘﻨﺴﻴﻘﺎ ،ﻭﻴﺘﻴﺢ ﻟﻙ ﺍﻴﻀﺎ ﺍﺴﺘﺨﺩﺍﻡ ﺒﺎﻨﻲ ﺍﻻﺴﺘﻌﻼﻡ ،ﻤﻊ ﻤﺭﺍﺠﻌﺔ ﺼﻴﺎﻏﺔ ﺠﻤـل
ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ،ﻭﺍﺨﺘﺒﺎﺭ ﻨﺘﺎﺌﺠﻪ.
ﻤﻠﺤﻭﻅﺔ:
ﻻ ﻴﺴﻤﺢ ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﺒﻜﺘﺎﺒﺔ ﺃﻜﺜﺭ ﻤﻥ ﺠﻤﻠﺔ SQLﻤﻔﺼﻭﻟﺔ ﺒﺎﻟﻌﻼﻤﺔ ; ﺠﺭﺏ
ﻤﺜﻼ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ ﻟﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺠﺩﻭﻟﻲ ﺍﻟﻤـﺅﻟﻔﻴﻥ ﻭﺍﻟﻜﺘـﺏ
ﻜﺎﻤﻠﻴﻥ:
;SELECT * FROM Authors
SELECT * FROM Books
ﻟﻭ ﻀﻐﻁﺕ ﺍﻟﺯﺭ Nextﻓﺴﺘﻅﻬﺭ ﻨﺎﻓﺫﺓ ﺘﺨﺒﺭﻙ ﺒﻭﺠﻭﺩ ﺨﻁﺄ ﻓﻲ ﺠﻤﻠﺔ ﺍﻻﺴـﺘﻌﻼﻡ،
ﻭﺴﺘﺭﻓﺽ ﻤﻭﺍﺼﻠﺔ ﺍﻟﺨﻁﻭﺍﺕ ﻤﺎ ﻟﻡ ﺘﺼﺤﺢ ﻫﺫﺍ ﺍﻟﺨﻁﺄ.
ﻟﻜﻥ ﻟﻭ ﻜﻨﺕ ﻤﺼﺭﺍ ﻋﻠﻰ ﻭﻀﻊ ﺃﻜﺜﺭ ﻤﻥ ﺠﻤﻠﺔ ﺍﺴﺘﻌﻼﻡ ﻓﻲ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻟﻴﻘـﻭﻡ
ﺒﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺄﻜﺜﺭ ﻤﻥ ﺠﺩﻭل ،ﻓﺎﺘﺒﻊ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ:
-ﺤﺩﺩ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺼﻴﻨﻴﺔ ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤﻭﺫﺝ ،ﻭﺍﻀﻐﻁ F4ﻟﻌﺭﺽ ﻨﺎﻓﺫﺓ
ﺍﻟﺨﺼﺎﺌﺹ.
-ﺤﺩﺩ ﺍﻟﺨﺎﺼﻴﺔ SelectCommandﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ،ﻭﺍﻀﻐﻁ ﺍﻟﻌﻼﻤـﺔ
+ﺍﻟﻤﺠﺎﻭﺭﺓ ﻟﻬﺎ ﻹﺴﺩﺍل ﺨﺼﺎﺌﺹ ﻜﺎﺌﻥ ﺍﻷﻤﺭ.
-ﺤﺩﺩ ﺍﻟﺨﺎﺼﻴﺔ ،CommandTextﻭﺍﻜﺘﺏ ﻓﻲ ﻗﻴﻤﺘﻬـﺎ ﺠﻤﻠـﺔ ﺍﻻﺴـﺘﻌﻼﻡ
ﺍﻟﻤﻜﻭﻨﺔ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﺃﻤﺭ.
-ﺍﻀﻐﻁ ﺯﺭ ﺍﻟﺤﻔﻅ ﺃﻭ ﺍﻨﺘﻘل ﺇﻟﻰ ﺃﻱ ﺨﺎﺼﻴﺔ ﺃﺨﺭﻯ ﺃﻭ ﺇﻟﻰ ﺍﻟﻨﻤﻭﺫﺝ ..ﺴﺘﻅﻬﺭ
ﺭﺴﺎﻟﺔ ﺘﺴﺄﻟﻙ ﺇﻥ ﻜﻨﺕ ﺘﺭﻴﺩ ﺘﺤﺩﻴﺙ ﻤﻌـﺎﻤﻼﺕ ﻫـﺫﺍ ﺍﻻﻤـﺭ ..ﺍﻀـﻐﻁ ﺯﺭ
ﺍﻟﻤﻭﺍﻓﻘﺔ.
١٦٢
ﺍﻵﻥ ﺴﻴﻜﻭﻥ ﻜل ﺸﻲﺀ ﻋﻠﻰ ﻤﺎ ﻴﺭﺍﻡ ،ﻭﺴﻴﻤﻸ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺒﺠﺩﻭﻟﻲ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺍﻟﻜﺘﺏ!
ﺍﻟﻁﺭﻴﻑ ﺃﻨﻙ ﻟﻭ ﺃﻋﺩﺕ ﻓﺘﺢ ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﻓﺴﺘﺠﺩ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﻤﺭﻜﺒﺔ ﻤـﻥ
ﺃﻜﺜﺭ ﻤﻥ ﺃﻤﺭ ﻤﻌﺭﻭﻀﺔ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ،ﻭﻟﻜﻨﻙ ﺴﺘﻅل ﺘﺤﺼل ﻋﻠﻰ ﺨﻁـﺄ ﻟـﻭ
ﺤﺎﻭﻟﺕ ﺍﻻﻨﺘﻘﺎل ﺇﻟﻰ ﺍﻟﺨﻁﻭﺓ ﺍﻟﺘﺎﻟﻴﺔ!
١٦٣
ﺍﻟﻤﻨﺎﺴﺏ ﻹﻏﻼﻕ Lockﺴﺠﻼﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻋﻠﻴﻙ ﺃﻥ ﺘﻜﺘﺏ ﻫـﺫﺍ ﺍﻟﻜـﻭﺩ
ﺒﻨﻔﺴﻙ ﻤﻥ ﺨﻼل ﻜﺎﺌﻥ ﺍﻟﺘﻌﺎﻤﻼﺕ .Transaction Object
-ﺃﻤﺎ ﻟﻭ ﺍﺨﺘﺭﺕ ﺍﺴﺘﺨﺩﺍﻡ ﺇﺠﺭﺍﺀﺍﺕ ﻤﺨﺯﻨﺔ ﻤﻭﺠﻭﺩﺓ ﺴﺎﺒﻘﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﺴﺘﻅﻬﺭ
ﻟﻙ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ:
١٦٤
ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﺒﻬﺎ ﺃﺭﺒﻊ ﻗﻭﺍﺌﻡ ﻤﻨﺴﺩﻟﺔ ،ﺘﻌﺭﺽ ﻜل ﻤﻨﻬﺎ ﺃﺴـﻤﺎﺀ ﺍﻹﺠـﺭﺍﺀﺍﺕ ﺍﻟﻤﺨﺯﻨـﺔ
ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻟﺘﺨﺘﺎﺭ ﻤﻨﻬﺎ ﺇﺠﺭﺍﺀ ﻟﻠﺘﺤﺩﻴـﺩ Selectﻭﺍﻹﺩﺭﺍﺝ Insert
ﻭﺍﻟﺘﺤﺩﻴﺙ Updateﻭﺍﻟﺤﺫﻑ .Delete
ﻭﻴﻭﺠﺩ ﻋﻠﻰ ﻴﻤﻴﻥ ﺍﻟﻨﺎﻓﺫﺓ ﺠﺩﻭل ﻴﻌـﺭﺽ ﺒﻌـﺽ ﺍﻟﺘﻔﺎﺼـﻴل ﺍﻟﺨﺎﺼـﺔ ﺒـﺎﻹﺠﺭﺍﺀ
ﺍﻟﻤﺨﺯﻥ ..ﻓﺒﺎﻟﻨﺴﺒﺔ ﻟﻺﺠﺭﺍﺀ ﺍﻟﺨﺎﺹ ﺒﺎﻟﺘﺤﺩﻴﺩ ،ﻴﻌﺭﺽ ﺍﻟﺠﺩﻭل ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘـﻲ
ﻴﻌﻴﺩ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ﻤﺤﺘﻭﻴﺎﺘﻬﺎ )ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜﺎل :ﺍﻹﺠﺭﺍﺀ GetAuthorBooks
ﻴﻌﻴﺩ ﺍﻟﻌﻤﻭﺩ ،Bookﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺍﻟﻜﺘﺏ ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﻤﺅﻟﻑ ﺍﻟﻤﻁﻠﻭﺏ.
ﺃﻤﺎ ﺒﺎﻟﻨﺴﺒﺔ ﻹﺠﺭﺍﺀﺍﺕ ﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻟﺤﺫﻑ ،ﻓﻴﻌﺭﺽ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ﻋﻤـﻭﺩﻴﻥ،
ﺃﻭﻟﻬﻤﺎ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﻌﺭﻓﺔ ﻓﻲ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ،ﻭﺍﻟﺜﺎﻨﻲ ﻴﺤﺘﻭﻱ ﻋﻠـﻰ
ﺃﺴﻤﺎﺀ ﺤﻘﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺍﻟﺘﻲ ﺴﻴﺘﻡ ﻤلﺀ ﻫﺫﻩ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻤﻥ ﻗﻴﻤﻬﺎ ﻹﺭﺴـﺎﻟﻬﺎ
ﺇﻟﻰ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ..ﻭﺘﺤﺘﻭﻱ ﻜل ﺨﺎﻨﺔ ﻓﻲ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﻤﻨﺴﺩﻟﺔ ﻴﻤﻜﻨـﻙ
ﺍﺨﺘﻴﺎﺭ ﺍﺴﻡ ﺍﻟﺤﻘل ﻤﻨﻬﺎ.
ﻭﺒﻌﺩ ﺍﻻﻨﺘﻬﺎﺀ ﻤﻥ ﺃﻱ ﻤﻥ ﺍﻟﻨﺎﻓﺫﺘﻴﻥ ﺍﻟﺴﺎﺒﻘﺘﻴﻥ ،ﺴﻴﺅﺩﻱ ﻀﻐﻁ ﺍﻟﺯﺭ Nextﺇﻟﻰ ﻅﻬـﻭﺭ ﻨﺎﻓـﺫﺓ
ﺘﻌﺭﺽ ﻤﻠﺨﹼﹼﺼﺎ ﻟﺨﻴﺎﺭﺍﺘﻙ ..ﺍﻀﻐﻁ ﺍﻟﺯﺭ Finishﻹﻨﻬﺎﺀ ﺍﻟﻤﻌـﺎﻟﺞ ﺍﻟﺴـﺤﺭﻱ ﻭﺘﻨﻔﻴـﺫ ﻫـﺫﻩ
ﺍﻻﺨﺘﻴﺎﺭﺍﺕ ،ﺃﻭ ﺍﻀﻐﻁ Cancelﻹﻟﻐﺎﺀ ﺍﻟﻌﻤﻠﻴﺔ.
ﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﻓﻲ ﻜل ﻨﺎﻓﺫﺓ ﻤﻥ ﻨﻭﺍﻓﺫ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻟﺞ ،ﺍﻟﺭﺠﻭﻉ ﺇﻟﻰ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻀـﻐﻁ
ﺍﻟﺯﺭ .Previous
ﺒﻌﺩ ﺍﻨﺘﻬﺎﺀ ﺍﻟﻤﻌﺎﻟﺞ ﺴﺘﺠﺩ ﺃﻥ ﺨﺼﺎﺌﺹ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻗﺩ ﺘﻡ ﻀﺒﻁﻬﺎ ﻟﺘﻭﺍﻓﻕ ﺍﺨﺘﻴﺎﺭﺍﺘﻙ ،ﻜﻤـﺎ
ﺴﺘﺠﺩ ﻜﺎﺌﻥ ﺍﺘﺼﺎل SqlConnectionﺍﺴﻤﻪ ﺍﻻﻓﺘﺭﺍﻀﻲ SqlConnection1ﻗـﺩ ﺃﻀـﻴﻑ
ﺇﻟﻰ ﺼﻴﻨﻴﺔ ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤﻭﺫﺝ ،ﻟﻴﺴﺘﺨﺩﻤﻪ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
١٦٥
ﻓﺌﺔ ﺒﺎﻨﻲ ﺃﻭﺍﻤﺭ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ
DbCommandBuilder Class
ﻫـﺫﻩ ﺍﻟﻔﺌـﺔ ﻫـﺫﻩ ﺍﻟﻔﺌـﺔ ﺃﺴﺎﺴـﻴﺔ ﻤﺠـﺭﺩﺓ ،Abstract Base Classﺘﺠـﺏ ﻭﺭﺍﺜﺘﻬـﺎ
،MustInheritﻭﻫﻲ ﺘﺭﺙ ﻓﺌﺔ ﺍﻟﻤﻜﻭﻥ ،Component Classﻭﻴﻤﻜﻨﻙ ﺍﺴـﺘﺨﺩﺍﻤﻬﺎ ﻟﺒﻨـﺎﺀ
ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺤﺫﻑ ﺍﻟﻼﺯﻤﺔ ﻟﻨﻘل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻤﻥ ﺃﺤﺩ ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ
DataSetﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻟﻜﻲ ﺘﻔﻌل ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻫﺫﺍ ،ﻋﻠﻴﻙ ﺭﺒﻁﻬﺎ ﺒﻤﻬﻴـﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ،
ﺒﺸﺭﻁ ﺃﻥ ﻴﺤﺘﻭﻱ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ..SELECT Commandﻭﺘﻘـﻭﻡ ﻫـﺫﻩ
ﺍﻟﻔﺌﺔ ﺒﺎﻻﺴﺘﺠﺎﺒﺔ ﻟﻠﺤﺩﺙ RowUpdatingﺍﻟﺨـﺎﺹ ﺒﻤﻬﻴـﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﺤﻴـﺙ ﺘﺴـﺘﺨﻠﺹ
ﺍﻟﻤﻌﻠﻭﻤﺎﺕ ﺍﻷﺴﺎﺴﻴﺔ ﻋﻥ ﺘﺭﻜﻴـﺏ ﺍﻟﺼـﻑ ﻤـﻥ ﺃﻤـﺭ ﺍﻟﺘﺤﺩﻴـﺩ SELECT Command
)ﻜﺎﺴﻡ ﺍﻟﺠﺩﻭل ﻭﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ( ،ﻭﺘﺒﻨﻲ ﺍﻷﻤﺭ ﺍﻟﻤﻨﺎﺴﺏ ﻟﺘﺤﺩﻴﺙ ﺃﻭ ﺤﺫﻑ ﺃﻭ ﺇﺩﺭﺍﺝ ﺍﻟﺴـﺠل
ﺍﻟﺫﻱ ﺃﻁﻠﻕ ﺍﻟﺤﺩﺙ.
ﻭﻴﺘﻡ ﺭﺒﻁ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﻭﺍﺤﺩ ﻓﻘﻁ ﺒﺒﺎﻨﻲ ﺃﻭﺍﻤﺭ ﻭﺍﺤﺩ ﻓﻘﻁ.
١٦٩
ﻤﻌﺭﻓﺔ ﺃﻤﺭ ﺍﻹﺩﺭﺍﺝ :GetInsertCommand
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻷﻤﺭ DbCommandﺍﻟﺫﻱ ﺘﻡ ﺇﻨﺘﺎﺠﻪ ﻹﺩﺭﺍﺝ ﺼـﻑ ﺠﺩﻴـﺩ ﻓـﻲ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻫﻲ ﻤﻤﺎﺜﻠﺔ ﻓﻲ ﺼﻴﻐﺘﻴﻬﺎ ﻟﻠﻭﺴﻴﻠﺔ .GetUpdateCommand
١٧٠
ﻓﺌﺔ ﺒﺎﻨﻲ ﺃﻭﺍﻤﺭ ﺴﻴﻜﻭﻴل
SqlCommandBuilder Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ،DbCommandBuilderﻭﻫﻲ ﺘﻤﺘﻠﻙ ﻨﻔﺱ ﺨﺼﺎﺌﺼﻬﺎ ﻭﻭﺴﺎﺌﻠﻬﺎ ،ﻤﻊ
ﻓﺎﺭﻕ ﺒﺴﻴﻁ ﺃﻨﻬﺎ ﻤﺨﺼﺼﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻭﺃﻭﺍﻤﺭﻩ .SqlCommand
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺼﻴﻐﺘﺎﻥ:
-١ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ.
-٢ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ SqlDataAdapterﺍﻟﺫﻱ ﺴﻴﺭﺘﺒﻁ ﺒﻪ ﺒﺎﻨﻲ ﺍﻷﻭﺍﻤﺭ.
ﻭﺍﻟﻤﺸﺭﻭﻉ CommandBuilderﻴﺭﻴﻙ ﻤﺜﺎﻻ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻹﻨﺘﺎﺝ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ
ﻭﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺤﺫﻑ.
١٧١
ﺨﺭﺍﺌﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ :Data Mapping
ﻴﺘﻴﺢ ﻟﻙ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻤل ﺨﺭﺍﻁ ﻟﻠﺠـﺩﺍﻭل ،Table Mappingﻭﺫﻟـﻙ ﺒﺈﻋـﺎﺩﺓ ﺘﺴـﻤﻴﺔ
ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺒﺄﺴﻤﺎﺀ ﺨﺎﺼﺔ ﺒﻙ ،ﻭﺭﺒﻁﻬﺎ ﺒﺎﻷﺴﻤﺎﺀ ﺍﻟﺤﻘﻴﻘﻴﺔ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ..ﻫـﺫﺍ
ﻴﺤﻘﻕ ﻟﻙ ﺍﻟﻔﻭﺍﺌﺩ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺘﻐﻴﻴﺭ ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﺃﻭ ﺍﻷﻋﻤﺩﺓ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﻤﺎ ﻴﻨﺎﺴﺏ ﺒﺭﻨﺎﻤﺠـﻙ ،ﺩﻭﻥ
ﺍﻟﻌﺒﺙ ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﺼﻠﻴﺔ.
-٢ﻋﺭﺽ ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺒﺎﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺘﻨﺎﺴﺒﻙ.
-٣ﺇﺫﺍ ﻜﺎﻥ ﻟﺩﻴﻙ ﺠﺩﻭﻻﻥ ﻟﻬﻤﺎ ﻨﻔﺱ ﺍﻻﺴﻡ ﻓﻲ ﻗﺎﻋﺩﺘﻲ ﺒﻴﺎﻨﺎﺕ ﻤﺨﺘﻠﻔﺘﻴﻥ ،ﻓـﺈﻥ ﺒﺈﻤﻜﺎﻨـﻙ
ﺇﻀﺎﻓﺘﻬﻤﺎ ﺇﻟﻰ ﻨﻔﺱ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺫﻟﻙ ﺒﺘﻐﻴﻴﺭ ﺍﺴﻤﻴﻬﻤﺎ ﻤـﻥ ﺨـﻼل ﺨﺭﻴﻁـﺔ
ﺍﻟﺠﺩﻭل ﺍﻟﺨﺎﺼﺔ ﺒﻜل ﻤﻨﻬﻤﺎ.
١٧٢
)ﻜﻤﺎ ﻓﻲ ﺤﺎﻟﺔ ﺍﻟﺭﺒﻁ (Joiningﺃﻭ ﻗﺩ ﻴﻌﻴﺩ ﻨﺘﺎﺌﺞ ﻤﺤﺴﻭﺒﺔ ﻤﻥ ﺠﺩﻭل ﺃﻭ ﺃﻜﺜﺭ )ﻜﻤـﺎ
ﻓﻲ ﺤﺎﻟﺔ ﺍﻟﺘﺠﻤﻴﻊ ..(Aggregationﻟﻬﺫﺍ ﻴﺭﻴﺢ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻨﻔﺴﻪ ﻤﻥ ﻜـل ﻫـﺫﻩ
ﺍﻻﺤﺘﻤﺎﻻﺕ ﺍﻟﻤﻌﻘﺩﺓ ،ﻭﻴﺴﻤﻲ ﺍﻟﺠﺩﺍﻭل ﺍﻟﻨﺎﺘﺠﺔ ﻤﻥ ﺍﻻﺴـﺘﻌﻼﻡ ﺒﺄﺴـﻤﺎﺀ ﺍﻓﺘﺭﺍﻀـﻴﺔ،
ﻭﻴﺘﺭﻙ ﻟﻙ ﺤﺭﻴﺔ ﺇﻨﺸﺎﺀ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﺍﻭل ﺍﻟﺘﻲ ﺘﺼﺤﺢ ﻓﻴﻬﺎ ﺍﻷﺴﻤﺎﺀ ﺒﻁﺭﻴﻘﺘﻙ.
ﻭﺍﻟﻤﺸﺭﻭﻉ Mappingﻴﺭﻴﻙ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫﺫﺍ ..ﻓﻨﺤﻥ ﻨﺴﺘﺨﺩﻡ ﺍﺴﺘﻌﻼﻡ ﺭﺒـﻁ ﻴﻌﻴـﺩ
ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﻜﺘﺒﻬﻡ ..ﻨﺘﻴﺠﺔ ﻫﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ ﺴﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺠﺩﻭل ﻤﺨﻠﻕ ،ﺴﻴﻌﻁﻴﻪ ﻤﻬﻴـﺊ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ ،Tableﻟﻬﺫﺍ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل ﻹﻋﺎﺩﺓ ﺘﺴـﻤﻴﺘﻪ
.Authors-Books
١٧٣
ﻻﺤﻅ ﺃﻨﻙ ﺒﻌﺩ ﻋﻤل ﺨﺭﺍﺌﻁ ﺍﻟﺭﺒﻁ ،ﺴﺘﺴﺘﺨﺩﻡ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻟﺠﺩﻴﺩ ﻭﺍﺴﻤﻲ ﺍﻟﻌﻤﻭﺩﻴﻥ ﺍﻟﻌﺭﺒﻴﻴﻥ
ﻓﻲ ﺍﻟﻜﻭﺩ ﻋﻨﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻬﻤﺎ ﻤﻥ ﺨﻼل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻤﺜﻼ:
;]"var T = Ds.Tables["Authors-Books
;)) ("].MaxLength.ToStringﺍﻟﻤﺅﻟﻑ"[MeesageBox.Show(T.Columns
ﻓﻲ ﺍﻟﺤﻘﻴﻘﺔ ﻫﻨﺎﻙ ﺤل ﺁﺨﺭ ﻟﻌﺭﺽ ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﺒﺄﺴﻤﺎﺀ ﻋﺭﺒﻴـﺔ ﺩﻭﻥ ﺍﺴـﺘﺨﺩﺍﻡ ﺨﺭﻴﻁـﺔ
ﺍﻷﻋﻤﺩﺓ ،ﻭﺫﻟﻙ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺨﺼﺎﺌﺹ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻨﻔﺴﻪ ﻹﻋﺎﺩﺓ ﺘﺴﻤﻴﺔ ﻋﻨﻭﺍﻥ ﺍﻟﻌﻤﻭﺩ ،ﻜﻤـﺎ
ﺴﻨﺭﻯ ﻓﻴﻬﺎ ﺒﻌﺩ.
ﻭﺍﻵﻥ ،ﻓﻠﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ،ﻭﻜﻴﻔﻴﺔ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ..ﻭﺴـﻨﺘﻔﻕ ﻫﻨـﺎ ﻋﻠـﻰ
ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺘﻌﺒﻴﺭ "ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ" ﻟﻺﺸﺎﺭﺓ ﺇﻟﻰ ﺍﻻﺴﻡ ﺍﻟـﺫﻱ ﻴﻤﻨﺤـﻪ ﻤﻬﻴـﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻟﻠﺠﺩﻭل ،ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﻫﺫﺍ ﺍﻻﺴﻡ ﺤﺴﺎﺱ ﻟﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ ..ﻜﻤﺎ ﺴﻨﺴـﺘﺨﺩﻡ ﺍﻟﺘﻌﺒﻴـﺭ "ﺍﺴـﻡ
ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ" ﻟﻺﺸﺎﺭﺓ ﺇﻟﻰ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺃﻭ ﺍﻻﺴﻡ ﺍﻟﺒﺩﻴل ﺍﻟﺫﻱ ﺴﻤﺎﻩ ﺒـﻪ
ﻤﻬﻴﺌﺎ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻥ ﺤﺩﺙ ﺘﻌﺎﺭﺽ ﺒﻴﻥ ﻋﻤﻭﺩﻴﻥ ،ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﻫﺫﺍ ﺍﻻﺴﻡ ﻏﻴﺭ ﺤﺴﺎﺱ ﻟﺤﺎﻟﺔ
ﺍﻷﺤﺭﻑ.
١٧٤
ﻭﺍﺠﻬﺔ ﻤﺠﻤﻭﻋﺔ ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل
ITableMappingCollection Interface
ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺘﺭﺙ ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ،IListﻭﻫﻲ ﻗﺎﺌﻤﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل.
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ﻤﻥ ﻋﻨﺎﺼﺭ ،ﺘﻤﺘﻠﻙ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻭﺤﻴﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ:
ﻜﻤﺎ ﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺼﻴﻐﺔ ﺃﺨﺭﻯ ﻟﺒﻌﺽ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺘﻘﻠﻴﺩﻴﺔ ،ﻤﺜل:
ﺍﻟﻌﻨﺼﺭ :Item
ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﺴﻡ ﺍﻟﺠـﺩﻭل ﺍﻷﺼـﻠﻲ )ﻭﻫـﻭ ﺤﺴـﺎﺱ ﻟﺤﺎﻟـﺔ
ﺍﻷﺤﺭﻑ( ،ﻭﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ Objectﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺨﺭﻴﻁﺔ ﻫـﺫﺍ ﺍﻟﺠـﺩﻭل ﺇﻥ ﻭﺠـﺩﺕ ﻓـﻲ
ﺍﻟﻘﺎﺌﻤﺔ ،ﻭﺇﻥ ﻟﻡ ﺘﻭﺠﺩ ﻓﺴﻴﺤﺩﺙ ﺨﻁﺄ.
ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻟﺘﻐﻴﻴﺭ ﻜﺎﺌﻥ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل ،ﻓﻬـﻲ ﻗﺎﺒﻠـﺔ ﻟﻠﻘـﺭﺍﺀﺓ
ﻭﻟﻠﻜﺘﺎﺒﺔ ﺃﻴﻀﺎ.
ﺇﻀﺎﻓﺔ :Add
ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻠﻴﻥ ﻨﺼﻴﻴﻥ ،Stringsﺃﻭﻟﻬﺎ ﻫﻭ ﺍﺴﻡ ﺍﻟﺠـﺩﻭل
ﺍﻷﺼﻠﻲ )ﻭﻫﻭ ﺤﺴﺎﺱ ﻟﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ ،(Case-Sensitiveﻭﺜﺎﻨﻴﻬﻤﺎ ﻫﻭ ﺍﺴﻡ ﺍﻟﺠـﺩﻭل
ﻓﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ..ﻭﺘﻘـﻭﻡ ﻫـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺒﺈﻨﺸـﺎﺀ ﻜـﺎﺌﻥ ﺨﺭﻴﻁـﺔ ﺠـﺩﻭل
DataTableMappingﻴﻤﺜل ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺠﺩﻭﻟﻴﻥ ﻭﺘﻀﻴﻔﻪ ﺇﻟﻰ ﺍﻟﻘﺎﺌﻤﺔ ،ﻭﺘﻌﻴﺩ ﻨﺴـﺨﺔ
ﻤﻥ ﺍﻟﻭﺍﺠﻬﺔ ITableMappingﺘﺸﻴﺭ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ.
١٧٥
ﺘﺤﺘﻭﻱ ﻋﻠﻰ :Contains
ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ،ﻭﺘﻌﻴﺩ Trueﺇﺫﺍ ﻜﺎﻨﺕ ﻫﻨـﺎﻙ
ﺨﺭﻴﻁﺔ ﻟﻬﺫﺍ ﺍﻟﺠﺩﻭل ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ.
ﻤﻠﺤﻭﻅﺔ:
ﻫﺫﻩ ﺍﻟﺼﻴﻐﺔ ﺘﺒﺩﻭ ﻤﺨﺘﻠﻔﺔ ﻓﻲ ﻭﻅﻴﻔﺘﻬﺎ ﻋﻥ ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺍﻟﻤﺄﻟﻭﻓﺔ ،ﺍﻟﺘﻲ ﺘﺴﺘﻘﺒل ﺭﻗـﻡ
ﺨﺎﻨﺔ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﻭﺘﺤﺫﻓﻬﺎ ﻹﺯﺍﻟﺔ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل ﺍﻟﻤﻭﺠﻭﺩﺓ ﺒﻬﺎ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ..ﻭﺇﻥ ﺸﺌﺕ
ﺭﺃﻴﻲ ،ﻜﺎﻥ ﺍﻟﻤﻨﻁﻘﻲ ﺃﻥ ﺘﻜﻭﻥ ﻫﺫﻩ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺠﺩﻴﺩﺓ ﻫـﻲ ﺍﻟﺼـﻴﻐﺔ ﺍﻟﺜﺎﻨﻴـﺔ ﻟﻠﻭﺴـﻴﻠﺔ
Removeﻭﻟﻴﺱ RemoveAtﻤﻨﻌﺎ ﻟﻼﻟﺘﺒﺎﺱ!!
١٧٦
ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل
DataTableMappingCollection Class
١٧٧
ﺭﻗﻡ ﺠﺩﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ :IndexOfDataSetTable
ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻟﺘﻌﻴﺩ ﺇﻟﻴـﻙ ﺭﻗـﻡ ﺨﺭﻴﻁـﺔ
ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺨﺭﺍﺌﻁ ﺍﻟﺤﺎﻟﻴﺔ ..ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ١-ﺇﺫﺍ ﻟـﻡ ﺘﻌﺜـﺭ ﻋﻠـﻰ
ﺨﺭﻴﻁﺔ ﻫﺫﺍ ﺍﻟﺠﺩﻭل.
١٧٨
ﻭﺍﺠﻬﺔ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل
ITableMapping Interface
ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﻼﺯﻤﺔ ﻟﺭﺴﻡ ﺨﺭﻴﻁﺔ ﺍﻟﺭﺒﻁ ﺒﻴﻥ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ﻭﺍﻟﺠـﺩﻭل
ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻫﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ ﻫﻲ:
١٧٩
ﻓﺌﺔ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل DataTableMapping Class
١٨٠
ﻴﺘﻡ ﺘﺠﺎﻫل ﺍﻟﻌﻤﻭﺩ. Ignore
ﻴــــﺘﻡ ﺇﻁــــﻼﻕ ﺨﻁــــﺄ ﻤــــﻥ ﺍﻟﻨــــﻭﻉ Error
.InvalidOperationException
١٨١
ﻭﺍﺠﻬﺔ ﻤﺠﻤﻭﻋﺔ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ
IColumnMappingCollection Interface
ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺘﺭﺙ ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ،IListﻭﻫﻲ ﺘﻤﻠﻙ ﻭﺴﻴﻠﺔ ﻭﺤﻴﺩﺓ ﺠﺩﻴﺩﺓ ،ﻭﻫﻲ:
ﻭﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺼﻴﻐﺔ ﺃﺨﺭﻯ ﻟﺒﻌﺽ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺘﻘﻠﻴﺩﻴﺔ ،ﻤﺜل:
ﺍﻟﻌﻨﺼﺭ :Item
ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ )ﻭﻫﻭ ﺤﺴﺎﺱ ﻟﺤﺎﻟﺔ ﺍﻷﺤـﺭﻑ
،(Case-Sensitiveﻭﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ Objectﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺨﺭﻴﻁﺔ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﺇﻥ ﻭﺠـﺩﺕ
ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ،ﻭﺇﻥ ﻟﻡ ﺘﻭﺠﺩ ﻓﺴﻴﺤﺩﺙ ﺨﻁﺄ.
ﺇﻀﺎﻓﺔ :Add
ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻠﻴﻥ ﻨﺼﻴﻴﻥ ،Stringsﺃﻭﻟﻬﺎ ﻫﻭ ﺍﺴـﻡ ﺍﻟﻌﻤـﻭﺩ
ﺍﻷﺼﻠﻲ ،ﻭﺜﺎﻨﻴﻬﻤﺎ ﻫﻭ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﺘﻘﻭﻡ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺒﺈﻨﺸـﺎﺀ
ﻜﺎﺌﻥ ﺨﺭﻴﻁﺔ ﺃﻋﻤﺩﺓ DataColumnMappingﻴﻤﺜل ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﻌﻤﻭﺩﻴﻥ ﻭﺘﻀـﻴﻔﻪ
ﺇﻟﻰ ﺍﻟﻘﺎﺌﻤﺔ ،ﻭﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﻭﺍﺠﻬﺔ IColumnMappingﺘﺸﻴﺭ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ.
١٨٢
ﺘﺤﺘﻭﻱ ﻋﻠﻰ :Contains
ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ )ﻭﻫﻭ ﺤﺴﺎﺱ ﻟﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ(،
ﻭﺘﻌﻴﺩ Trueﺇﺫﺍ ﻜﺎﻨﺕ ﻫﻨﺎﻙ ﺨﺭﻴﻁﺔ ﻟﻬﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ.
١٨٣
ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ
DataColumnMappingCollection Class
١٨٤
ﻭﺍﺠﻬﺔ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ
IColumnMapping Interface
ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺨﺎﺼﻴﺘﻴﻥ ﻓﻘﻁ ،ﺘﺴﺘﺨﺩﻤﺎﻥ ﻟﺭﺒﻁ ﻋﻤﻭﺩ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﺒـﺎﻟﻌﻤﻭﺩ
ﺍﻷﺼﻠﻲ ،ﻭﻫﻤﺎ:
١٨٥
ﻓﺌﺔ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ
DataColumnMapping Class
١٨٦
-١٠-
ﻤﺼﺎﻨﻊ ﺍﻟﻤﺯﻭﺩﺍﺕ Provider Factories
ﻟﻌﻠﻙ ﺸﻌﺭﺕ ﺒﺎﻻﺴﺘﻴﺎﺀ ﻤﻥ ﻭﺠﻭﺩ ﺃﻜﺜﺭ ﻤﻥ ﻨﻭﻉ ﻤﻥ ﻨﻔﺱ ﺍﻟﻜﺎﺌﻥ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻤﺯﻭﺩﺍﺕ ﻗﻭﺍﻋـﺩ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺨﺘﻠﻔﺔ ،ﻤﺜل:
-ﻜﺎﺌﻨـــﺎﺕ ﺍﻻﺘﺼـــﺎل ﻤﺜـــل OleDbConnectionﻭ SqlConnection
ﻭ .OracleConnection
-ﻜﺎﺌﻨــــﺎﺕ ﺍﻷﻤــــﺭ ﻤﺜــــل OleDbCommandﻭ SqlCommand
ﻭ .OracleCommand
-ﻜﺎﺌﻨــﺎﺕ ﻗــﺭﺍﺀﺓ ﺍﻟﺒﻴﺎﻨــﺎﺕ ﻤﺜــل OleDbDataReaderﻭ SqlDataReader
ﻭ .OracleDataReader
-ﻤﻬﻴﺌـــﺎﺕ ﺍﻟﺒﻴﺎﻨـــﺎﺕ ﻤﺜـــل OleDbDataAdapterﻭ SqlDataAdapter
ﻭ .OracleDataAdapter
ﻓﻬﺫﺍ ﻴﺠﻌﻠﻙ ﺘﻜﺘﺏ ﻜﻭﺩﺍ ﻤﺨﺘﻠﻔﺎ ﻟﻜل ﻨﻭﻉ ﻤﻥ ﺃﻨﻭﺍﻉ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﺭﻏـﻡ ﺃﻥ ﺍﻻﺨـﺘﻼﻑ
ﻴﻨﺤﺼﺭ ﻓﻘﻁ ﻓﻲ ﺠﻤل ﺘﻌﺭﻴﻑ ﺍﻟﻜﺎﺌﻨﺎﺕ ،ﻭﻟﻴﺱ ﻓﻲ ﻓﻜﺭﺓ ﺍﻟﻜﻭﺩ!
ﻭﻟﻘﺩ ﻗﺩﻤﺕ ﺩﻭﺕ ﻨﺕ ٢٠٠٥ﺤﻼ ﻟﻬﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ﺒﺈﻀﺎﻓﺘﻴﻥ ﻫﺎﻤﺘﻴﻥ:
-١ﺘﻌﺭﻴﻑ ﺍﻟﻔﺌﺎﺕ ﺍﻟﻌﺎﻤﺔ ﻓﻲ ﺍﻟﻨﻁﺎﻕ ،System.Data.Commonﻤﺜل:
-ﺍﻟﻔﺌﺔ DbConnectionﺍﻟﺘﻲ ﺘﺸﺘﻕ ﻤﻨﻬﺎ ﺠﻤﻴﻊ ﻜﺎﺌﻨﺎﺕ ﺍﻻﺘﺼﺎل.
-ﺍﻟﻔﺌﺔ DbCommandﺍﻟﺘﻲ ﺘﺸﺘﻕ ﻤﻨﻬﺎ ﺠﻤﻴﻊ ﻜﺎﺌﻨﺎﺕ ﺍﻷﻭﺍﻤﺭ.
-ﺍﻟﻔﺌﺔ DbDataReaderﺍﻟﺘﻲ ﺘﺸﺘﻕ ﻤﻨﻬﺎ ﻜل ﻗﺎﺭﺌﺎﺕ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-ﺍﻟﻔﺌﺔ DbDataAdapterﺍﻟﺘﻲ ﺘﺸﺘﻕ ﻤﻨﻬﺎ ﻜل ﻤﻬﻴﺌﺎﺕ ﺍﻟﺒﻴﺎﻨﺎﺕ.
١٨٧
ﻫﺫﺍ ﻴﺠﻌل ﻤﻥ ﺍﻟﻤﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺃﻱ ﻨﻭﻉ ﻤـﻥ ﺃﻨـﻭﺍﻉ ﺍﻟﻔﺌـﺎﺕ
ﺍﻟﻤﺸـــﺘﻘﺔ ﻤﻨﻬـــﺎ )ﺭﺍﺠـــﻊ ﻤﻔﻬـــﻭﻡ ﺍﻟﻔﺌـــﺎﺕ ﺍﻷﺴﺎﺴـــﻴﺔ ﺍﻟﻤﺠـــﺭﺩﺓ
Abstract Base Classesﻭﺘﻌﺩﺩ ﺍﻷﺴﻤﺎﺀ Polymorphismﻓﻲ ﻓﺼل ﺍﻟﻭﺭﺍﺜـﺔ
ﻓﻲ ﻜﺘﺎﺏ "ﻤﻥ ﺍﻟﺼﻔﺭ ﺇﻟﻰ ﺍﻻﺤﺘﺭﺍﻑ :ﺴﻲ ﺸﺎﺭﺏ"(.
-٢ﺇﻀﺎﻓﺔ ﺍﻟﻔﺌﺘﻴﻥ DbProviderFactoriesﻭ DbProviderFactoryﺇﻟﻰ ﺍﻟﻨﻁـﺎﻕ
،System.Data.Commonﻹﻤﺩﺍﺩﻙ ﺒﻤﺼﻨﻊ ﺨﺎﺹ ﺒﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟـﺫﻱ ﺘﺭﻴـﺩ
ﺍﻟﺘﻌﺎﻤل ﻤﻌﻪ ،ﻤﻤﺎ ﻴﻜﻤل ﻗﺩﺭﺘﻙ ﻋﻠﻰ ﺘﻌﻤﻴﻡ ﺍﻟﻜﻭﺩ ،ﻜﻤﺎ ﺴﻨﺭﻯ ﺒﻌﺩ ﻗﻠﻴل.
ﻭﺴﻨﺘﻌﻠﻡ ﻓﻲ ﻫﺫﺍ ﺍﻟﻔﺼل ﻜﻴﻑ ﻨﺴﺘﺨﺩﻡ ﻫﺎﺘﻴﻥ ﺍﻹﻤﻜﺎﻨﻴﺘﻴﻥ ﻟﻜﺘﺎﺒﺔ ﻜﻭﺩ ﻭﺍﺤﺩ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺃﻨـﻭﺍﻉ
ﻤﺨﺘﻠﻔﺔ ﻤﻥ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺴﻨﺴﺘﺨﺩﻤﻪ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ ﻓﻲ ﻜل ﻤﻥ ﺴﻴﻜﻭﻴل
ﺴﻴﺭﻓﺭ ﻭﺁﻜﺴﻴﺱ.
١٨٨
ﻓﺌﺔ ﻤﺼﺎﻨﻊ ﺍﻟﻤﺯﻭﺩﺍﺕ DbProviderFactories Class
ﺘﻌﺘﺒﺭ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻤﺠﺭﺩ ﻤﺩﺨل ﻻﺴﺘﺨﺩﺍﻡ ﺍﻟﻔﺌﺔ ،DbProviderFactoryﻭﻫـﻲ ﻻ ﺘﻤﺘﻠـﻙ ﺇﻻ
ﻭﺴﻴﻠﺘﻴﻥ ﻤﺸﺘﺭﻜﺘﻴﻥ ،ﻫﻤﺎ:
١٨٩
-٢ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﺼﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ DataRowﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺘﻔﺎﺼـﻴل
ﺍﻟﻤﺯﻭﺩ ..ﻭﻴﻤﻜﻨﻙ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﺼﻑ ﻤﻥ ﺍﻟﺠﺩﻭل ﺍﻟﻌﺎﺌﺩ ﻤـﻥ ﺍﻟﻭﺴـﻴﻠﺔ
.GetFactoryClasses
ﺩﻋﻨﺎ ﺇﺫﻥ ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ .DbProviderFactory
١٩٠
ﻓﺌﺔ ﻤﺼﻨﻊ ﺍﻟﻤﺯﻭﺩ DbProviderFactory Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺴﺎﺴﻴﺔ ﻤﺠﺭﺩﺓ ﺘﺠﺏ ﻭﺭﺍﺜﺘﻬﺎ ،ﻭﻫﻲ ﺘﻤﺘﻠﻙ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻼﺯﻤﺔ ﻟﻠﺘﻌﺎﻤـل ﻤـﻊ ﻤـﺯﻭﺩ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺨﺼﺼﺕ ﻟﻠﺘﻌﺎﻤل ﻤﻌﻪ.
ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﺎﻟﻴﺔ:
١٩١
ﺇﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺃﻤﺭ :CreateCommand
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺃﻤﺭ ﻤﻥ ﺍﻟﻨﻭﻉ ﺍﻟﻌﺎﻡ ،DbCommandﻟﻜﻨﻪ ﻴﻜﻭﻥ ﻤﺨﺼﺼـﺎ ﻟﻠﺘﻌﺎﻤـل ﻤـﻊ
ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺃﻨﺸﺄﺕ ﺍﻟﻤﺼﻨﻊ ﺍﻟﺤﺎﻟﻲ ﻟﻠﺘﻌﺎﻤل ﻤﻌﻪ ..ﻭﺃﻨﺕ ﺘﻌﺭﻑ ﺃﻨـﻙ ﺘﺴـﺘﻁﻴﻊ
ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ .ExecuteReader
ﻻﺤﻅ ﺃﻥ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﺍﻟﺫﻱ ﺴﺘﺤﺼل ﻋﻠﻴﻪ ﻟﻴﺱ ﻤﺭﺘﺒﻁﺎ ﺒﺄﻱ ﺍﺘﺼﺎل ،ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺭﺒﻁـﻪ
ﺒﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﺤﺼﻠﺕ ﻋﻠﻴﻪ ﻤﻥ ﺍﻟﻭﺴﻴﻠﺔ ،CreateConnectionﻭﻫﻭ ﻤﺎ ﻓﻌﻠﻨـﺎﻩ
ﻓﻲ ﺍﻟﺩﺍﻟﺔ CreateCommandﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ Factoriesﻜﺎﻟﺘﺎﻟﻲ:
;) (var Command = Fac.CreateCommand
;Command.Connection = Cn
ﻭﻴﻤﻜﻨﻙ ﺃﺩﺍﺀ ﻨﻔﺱ ﻭﻅﻴﻔﺔ ﻫـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ،ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ CreateCommand
ﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ،ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﺘﺨﺘﺼﺭ ﺍﻟﺴﻁﺭ ﺍﻟﺜﺎﻨﻲ ﻤﻥ ﺍﻟﻜﻭﺩ ﺍﻟﺴﺎﺒﻕ:
;) (var Command = Cn.CreateCommand
ﻭﺨﻴﺭ ﻁﺭﻴﻘﺔ ﻹﺩﺭﺍﻙ ﻋﺒﻘﺭﻴﺔ ﻤﺼﺎﻨﻊ ﺍﻟﻤﺯﻭﺩﺍﺕ ،ﻫﻲ ﺃﻥ ﻨﻌﻴﺩ ﻜﺘﺎﺒﺔ ﺍﻟﻤﺸـﺭﻭﻉ DbTasks
ﺒﻁﺭﻴﻘﺔ ﻋﺎﻤﺔ ،ﺘﺴﻤﺢ ﺒﺎﻟﺘﻌﺎﻤل ﻤﻊ ﺃﻱ ﻤﺯﻭﺩ ﺒﻴﺎﻨﺎﺕ ..ﻜﻤﺎ ﺘﺫﻜﺭ ،ﻓﻘﺩ ﺃﻨﺸﺄﻨﺎ ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﺸﺭﻭﻉ
ﻓﺌﺔ ﺍﺴﻤﻬﺎ MyDbConnectorﺘﺴﻬل ﻋﻠﻴﻨﺎ ﺇﺠﺭﺍﺀ ﺃﻱ ﻋﻤﻠﻴﺔ ﻋﻠﻰ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ﺴـﻴﻜﻭﻴل
ﺴﻴﺭﻓﺭ ..ﺍﻵﻥ ﺤﺎﻥ ﺍﻟﻭﻗﺕ ﻟﻨﻌﻤﻡ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ،ﺒﺤﻴﺙ ﻨﺴﺘﻁﻴﻊ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺒﺎﻗﻲ ﺃﻨﻭﺍﻉ
ﺍﻟﻤﺯﻭﺩﺍﺕ ﺍﻟﺘﻲ ﻴﺩﻋﻬﺎ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ..ﻫﺫﺍ ﻫﻭ ﻤﺎ ﻓﻌﻠﻨﺎﻩ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ،Factoriesﺍﻟﺫﻱ ﻫـﻭ
ﻨﺴﺨﺔ ﻁﺒﻕ ﺍﻷﺼل ﻤﻥ ﺍﻟﻤﺸﺭﻭﻉ ،DbTasksﻟﻜﻨﻪ ﻴﻌﺭﺽ ﻋﻠـﻰ ﺍﻟﻨﻤـﻭﺫﺝ ﺯﺭﻱ ﺘﺤﻭﻴـل
Radio Buttonsﻟﻴﺴﺘﻁﻴﻊ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﺨﺘﻴﺎﺭ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺁﻜﺴـﻴﺱ ﺃﻭ ﻗﺎﻋـﺩﺓ
ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ،ﻭﺍﻟﺭﺍﺌﻊ ﺤﻘﺎ ﺃﻥ ﻜﻭﺩ ﺍﻷﺯﺭﺍﺭ ﺍﻟﻤﻭﻀﻭﻋﺔ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ﻅل ﻜﻤﺎ ﻫـﻭ
ﺒﺩﻭﻥ ﺘﻐﻴﻴﺭ ،ﺒﻔﻀل ﺍﺴﺘﺨﺩﺍﻡ ﻤﺼﺎﻨﻊ ﺍﻟﻤﺯﻭﺩﺍﺕ!
ﻟﻜﻨﻨﺎ ﺒﺎﻟﻁﺒﻊ ﺃﺠﺭﻴﻨﺎ ﺒﻌﺽ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﻀﺭﻭﺭﻴﺔ ﻋﻠﻰ ﻜﻭﺩ ﺍﻟﻔﺌﺔ ،MyDbConnectorﻓﻘـﺩ
ﻋﺭﻓﻨﺎ ﻓﻴﻬﺎ ﻤﺭﻗﻤﺎ ﺍﺴﻤﻪ ،Providersﻭﺍﺴﺘﺨﺩﻤﻨﺎﻩ ﻓﻲ ﺘﻌﺭﻴﻑ ﻤﻌﺎﻤل ﺜﺎﻥ ﻟﺤـﺩﺙ ﺍﻹﻨﺸـﺎﺀ
،Newﻟﻴﺭﺴل ﺍﻟﻤﺴﺘﺨﺩﻡ ﻋﻨﺩ ﺇﻨﺸﺎﺀ ﻨﺴﺨﺔ ﻤﻥ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ،ﻨﺹ ﺍﻻﺘﺼﺎل ﻭﻨﻭﻉ ﺍﻟﻤﺯﻭﺩ ﺍﻟـﺫﻱ
ﻴﺭﻴﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻪ.
ﻜﻤﺎ ﻋﺭﻓﻨﺎ ﺩﺍﻟﺔ ﺍﺴﻤﻬﺎ ،GetProviderNameﺘﺴﺘﻘﺒل ﻗﻴﻤـﺔ ﺍﻟﻤـﺭﻗﻡ ،Providersﻭﺘﻌﻴـﺩ
ـﻴﻠﺔ
ـﻰ ﺍﻟﻭﺴــ
ـﻠﻪ ﺇﻟــ
ـﺯﻭﺩ ،ﻟﻨﺭﺴــ
ـﺫﺍ ﺍﻟﻤــ
ـﻡ ﻫــ
ـل ﺍﺴــ
ـﺫﻱ ﻴﻤﺜــ
ـﻨﺹ ﺍﻟــ
ﺍﻟــ
DbProviderFactories.GetFactorﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻤﺼﻨﻊ ﺍﻟﻤﺯﻭﺩ ﺍﻟﺫﻱ ﻴﺭﻴﺩ ﺍﻟﻤﺴـﺘﺨﺩﻡ
ﺍﻟﺘﻌﺎﻤل ﻤﻌﻪ ..ﺒﻌﺩ ﻫﺫﺍ ﻴﺼﻴﺭ ﻤﻥ ﺍﻟﺴﻬل ﺍﺴﺘﺨﺩﺍﻡ ﻭﺴﺎﺌل ﻫﺫﺍ ﺍﻟﻤﺼﻨﻊ ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻜـﺎﺌﻥ
ﺍﻻﺘﺼﺎل ﻭﻜﺎﺌﻥ ﺍﻷﻤﺭ ﻭﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻼﺯﻤﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﻟﻭ ﻨﻅﺭﺕ ﺇﻟﻰ ﻜﻭﺩ ﺍﻟﻔﺌﺔ MyDbConnectorﻓﻲ ﻫﺫﺍ ﺍﻟﻤﺸﺭﻭﻉ ،ﻓﺴـﺘﺠﺩ ﺃﻥ ﺍﻟﺘﻌـﺩﻴﻼﺕ
ﺍﻟﺘﻲ ﺃﺩﺨﻠﻨﺎﻫﺎ ﻁﻔﻴﻔﺔ ،ﻟﻜﻥ ﺘﺄﺜﻴﺭﻫﺎ ﻫﺎﺌل ،ﻓﻘﺩ ﺼﺎﺭﺕ ﻟﺩﻴﻨﺎ ﻓﺌﺔ ﻋﺎﻤﺔ ﺘﺴﺘﻁﻴﻊ ﺃﺩﺍﺀ ﻤﻌﻅـﻡ ـ
١٩٤
ﺇﻥ ﻟﻡ ﻴﻜﻥ ﻜل ـ ﺍﻟﻭﻅﺎﺌﻑ ﺍﻟﺘﻲ ﻨﺭﻴﺩﻫﺎ ﻋﻠﻰ ﺃﻱ ﻨﻭﻉ ﻤﻥ ﺃﻨﻭﺍﻉ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻤﻤﺎ ﻴﺘﻴﺢ ﻟﻙ
ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﻤﺸﺎﺭﻴﻌﻙ ﻟﺘﻘﻠﻴل ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﺘﻜﺘﺒﻪ ﺇﻟﻰ ﺃﻗل ﺤﺩ ﻤﻤﻜﻥ!
ﻻﺤﻅ ﺃﻨﻨﺎ ﻨﻐﻴﺭ ﻨﻭﻉ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘـﻲ ﻨﺘﻌﺎﻤـل ﻤﻌﻬـﺎ ،ﻓـﻲ ﺤـﺩﺙ ﺘﻐﻴـﺭ ﺍﻻﺨﺘﻴـﺎﺭ
CheckedChangedﺍﻟﺨﺎﺹ ﺒﺯﺭﻱ ﺍﻟﺘﺤﻭﻴل ،ﻭﺫﻟﻙ ﺒﺎﻟﻜﻭﺩ ﺍﻟﺒﺴﻴﻁ ﺍﻟﺘﺎﻟﻲ:
)if (RdSql.Checked
(DbBooks = new MyDbConnector
Properties.Settings.Default.BooksMdfConStr,
)MyDbConnector.Providers.SqlServer
else
(DbBooks = new MyDbConnector
Properties.Settings.Default.BooksMdbConStr,
)MyDbConnector.Providers.OleDb
ﺤﻴﺙ DbBooksﻫﻭ ﻤﺘﻐﻴﺭ ﻤﻌﺭﻑ ﻋﻠﻰ ﻤﺴـﺘﻭﻯ ﺍﻟﻨﻤـﻭﺫﺝ ،ﻨﻀـﻊ ﻓﻴـﻪ ﻨﺴـﺨﺔ ﺍﻟﻔﺌـﺔ
MyDbConnectorﺍﻟﺘﻲ ﻨﺴﺘﺨﺩﻤﻬﺎ ﻟﺘﻨﻔﻴﺫ ﻭﻅﺎﺌﻑ ﺍﻷﺯﺭﺍﺭ.
ﻻﺤﻅ ﺃﻴﻀﺎ ﺃﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺯﺭ "ﺍﻟﻜﺘﺏ "١ﻻﺴﺘﺩﻋﺎﺀ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ،ﻴﺴـﺘﻠﺯﻡ ﻤﻨـﻙ ﺃﻭﻻ ﺃﻥ
ﺘﺴــﺘﺨﺩﻡ ﺍﻟﻤﺸــﺭﻭﻉ AccessStoredProcedureﻹﻀــﺎﻓﺔ ﺍﻹﺠــﺭﺍﺀ ﺍﻟﻤﺨــﺯﻥ
GetAuthorBooksﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ ﺍﻟﺨﺎﺼﺔ ﺒﺂﻜﺴﻴﺱ.
١٩٥
ﺍﻟﻁﺒﻘﺎﺕ ﺍﻟﻤﺘﻌﺩﺩﺓ :N-Tiers
ﻟﻌل ﺍﻟﻤﺸﺭﻭﻉ ﺍﻟﺴﺎﺒﻕ ﻴﻜﺸﻑ ﻟﻙ ﺃﻫﻤﻴﺔ ﺘﻘﺴﻴﻡ ﻤﺸﺎﺭﻴﻊ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻁﺒﻘـﺎﺕ Layers
ﻤﺴﺘﻘﻠﺔ ﻋﻥ ﺒﻌﻀﻬﺎ ..ﻫﺫﺍ ﻴﺴﻬل ﻋﻠﻴﻙ ﺘﻁﻭﻴﺭ ﺃﻱ ﻁﺒﻘﺔ ﺩﻭﻥ ﺘﻐﻴﻴﺭ ﺃﻱ ﺸﻲﺀ ﻓـﻲ ﺍﻟﻁﺒﻘـﺎﺕ
ﺍﻷﺨﺭﻯ ..ﻓﻨﺤﻥ ﻫﻨﺎ ﻤﺜﻼ ﻋﺩﻟﻨﺎ ﻜﻭﺩ ﺍﻟﻔﺌﺔ MyDbConnectorﺩﻭﻥ ﺃﻥ ﻨﻐﻴـﺭ ﺃﻱ ﺸـﻲﺀ
ﺘﻘﺭﻴﺒﺎ ﻓﻲ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﻴﺴﺘﺨﺩﻤﻬﺎ ،ﻭﻫﻲ ﻤﻴﺯﺓ ﺘﺘﻀﺢ ﻓﻭﺍﺌﺩﻫﺎ ﺍﻟﻬﺎﺌﻠﺔ ﻓﻲ ﺍﻟﻤﺸﺎﺭﻴﻊ ﺍﻟﻀـﺨﻤﺔ،
ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﻻﺴﺘﻔﺎﺩﺓ ﻤﻥ ﺍﻟﺘﻁﻭﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺘﺤﺩﺙ ﻓﻲ ﺘﻘﻨﻴﺎﺕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺩﻭﻥ ﺇﻋﺎﺩﺓ ﻜﺘﺎﺒـﺔ
ﺍﻟﻜﻭﺩ ﻜﻠﻪ ﻤﻨﺫ ﺍﻟﺒﺩﺍﻴﺔ ..ﻓﻠﻭ ﺃﻥ ﻫﺫﻩ ﺍﻟﻤﺸﺎﺭﻴﻊ ﻤﻘﺴﻤﺔ ﺇﻟﻰ ﻁﺒﻘﺎﺕ ،ﻓﺴﻴﻨﺤﺼﺭ ﺍﻟﺘﻁﻭﻴﺭ ﻋﻠـﻰ
ﻁﺒﻘﺔ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻼﺴﺘﻔﺎﺩﺓ ﻤﻥ ﺍﻟﺘﻘﻨﻴﺎﺕ ﺍﻟﺠﺩﻴﺩﺓ ،ﺒﻴﻨﻤـﺎ ﺴـﺘﻅل ﺍﻟﻁﺒﻘـﺔ ﺍﻟﺘـﻲ
ﺘﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻜﻤﺎ ﻫﻲ ﺒﺩﻭﻥ ﺘﻐﻴﻴﺭ ﻴﺫﻜﺭ ..ﻭﺘﺴﻤﻰ ﺍﻟﺒﺭﺍﻤﺞ ﺍﻟﺘﻲ ﺘﺴـﺘﺨﺩﻡ ﻫـﺫﺍ
ﺍﻟﺘﻨﻅــﻴﻡ ﺒﺎﺴــﻡ ﺍﻟﺘﻁﺒﻴﻘــﺎﺕ ﻤﺘﻌــﺩﺩﺓ ﺍﻟﻁﺒﻘــﺎﺕ Multi-Tier Applicationsﺃﻭ
،n-Tier Applicationsﻭﺘﺴـــﻤﻰ ﺃﻴﻀـــﺎ ﺒﺎﺴـــﻡ ﺍﻟﺘﻁﺒﻴﻘـــﺎﺕ ﺍﻟﻤﻭﺯﻋـــﺔ
Distributed Applicationsﻷﻨﻬﺎ ﻤﻘﺴﻤﺔ ﻋﻠﻰ ﺃﻜﺜﺭ ﻤﻥ ﻁﺒﻘـﺔ ..ﻭﺍﻟﺸـﻬﻴﺭ ﺃﻥ ﺘﻜﺘـﺏ
ﻤﺸﺎﺭﻴﻊ ﻗﻭﺍﻋﺩ ﻋﻠﻰ ﺜﻼﺙ ﻁﺒﻘﺎﺕ:
-١ﻁﺒﻘﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ :Data Tier
ﻭﻫﻲ ﺍﻟﻁﺒﻘﺔ ﺍﻟﺘﻲ ﺘﻭﺠﺩ ﻓﻴﻬﺎ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻤﺎ ﻓﻴﻬﺎ ﻤﻥ ﺠﺩﺍﻭل ﻭﻋﻼﻗﺎﺕ ﻭﺇﺠﺭﺍﺀﺍﺕ
ﻤﺨﺯﻨﺔ ،ﻭﻤﺴﺘﺨﺩﻤﻴﻥ ﻭﺼﻼﺤﻴﺎﺕ ﻭﻗﻭﺍﻋﺩ ﺴﺭﻴﺔ ﻭﺤﻤﺎﻴـﺔ ﻭﺼـﻴﺎﻨﺔ ﻭﺤﻔـﻅ ﻨﺴـﺦ
ﺍﺤﺘﻴﺎﻁﻴﺔ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﺇﻟﺦ ..ﻭﻓﻲ ﺍﻟﺸﺭﻜﺎﺕ ﺍﻟﻜﺒﻴﺭﺓ ﻴﻜﻭﻥ ﻫﻨﺎﻙ ﻤﻭﻅﻔﻭﻥ ﻤﺴﺌﻭﻟﻭﻥ
ﻋﻥ ﺇﺩﺍﺭﺓ ﻫﺫﻩ ﺍﻟﻁﺒﻘﺔ.
-٢ﻁﺒﻘﺔ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﺒﻴﺎﻨﺎﺕ :Data Access Tier
ﻓﻲ ﻫﺫﻩ ﺍﻟﻁﺒﻘﺔ ،ﻴﻭﺠﺩ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﻴﺘﺼل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻴﺤﻀـﺭ ﺍﻟﻨﺘـﺎﺌﺞ ﻤﻨﻬـﺎ..
ﻭﺍﻟﻔﺌﺔ MyDbConnectorﻫﻲ ﻤﺠﺭﺩ ﻤﺜﺎل ﻤﺒﺴﻁ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻁﺒﻘﺔ ،ﻟﻜﻥ ﺩﻭﺕ ﻨﺕ
ﺘﻤﻨﺤﻙ ﺇﻤﻜﺎﻨﻴﺎﺕ ﺃﻗﻭﻯ ﻟﺘﺼﻤﻴﻡ ﻫﺫﻩ ﺍﻟﻁﺒﻘﺔ ﻤﺜل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤﺤـﺩﺩﺓ ﺍﻟﻨـﻭﻉ
ﻭﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﻘﺎﺩﻡ ،ﻭﻤﺜـل LinQ-To-SQL
ﻭﻏﻴﺭ ﺫﻟﻙ.
١٩٦
ﻭﺘﻤﺘﺎﺯ ﻫﺫﻩ ﺍﻟﻁﺒﻘﺔ ﺒﺄﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﺃﻜﺜﺭ ﻤﻥ ﻤﺸﺭﻭﻉ ،ﻤﻤﺎ ﻴـﻭﻓﺭ ﻟـﻙ
ﺍﻟﻭﻗﺕ ﻭﺍﻟﺠﻬﺩ ،ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺘﻁﻭﻴﺭﻫﺎ ﺩﻭﻥ ﺍﻟﺤﺎﺠﺔ ﺇﻟﻰ ﺇﻋﺎﺩﺓ ﻜﺘﺎﺒﺔ ﺍﻟﻤﺸـﺎﺭﻴﻊ ﺍﻟﺘـﻲ
ﺘﻌﺘﻤﺩ ﻋﻠﻴﻬﺎ.
-٣ﻁﺒﻘﺔ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ :Data Display Tier
ﻓﻲ ﻫﺫﻩ ﺍﻟﻁﺒﻘﺔ ﻴﻭﺠﺩ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﻴﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻠﻤﺴﺘﺨﺩﻡ ،ﻭﺍﻟﻤﻔﺭﻭﺽ ﺃﻻ ﻴﻭﺠـﺩ
ﻓﻲ ﻫﺫﻩ ﺍﻟﻁﺒﻘﺔ ﺃﻱ ﻜﻭﺩ ﻴﺘﺼل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﺘﻁﺒﻴﻘﺎﺕ ﻤﺘﻌﺩﺩﺓ ﺍﻟﻁﺒﻘﺎﺕ ﺒﺈﺫﻥ ﺍﷲ ﺒﺼﻭﺭﺓ ﺃﺸﻤل ،ﻓﻲ ﺍﻟﻜﺘﺎﺏ ﺍﻟﻤﺨﺼـﺹ
ﻟﻠﻤﻭﺍﻀﻴﻊ ﺍﻟﻤﺘﻘﺩﻤﺔ ﻓﻲ ﺒﺭﻤﺠﺔ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ.
١٩٧
ﻓﺌﺔ ﻋﺩﺍﺩ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ
DbDataSourceEnumerator Class
ـﺔ
ـﺎ ﺇﻻ ﺍﻟﻔﺌــ
ـﻰ ﺍﻵﻥ ﻻ ﺘﺭﺜﻬــ
ـﻥ ﺤﺘــ
ـﺭﺩﺓ ،ﻟﻜــ
ـﻴﺔ ﻤﺠــ
ـﺔ ﺃﺴﺎﺴــ
ـﺫﻩ ﺍﻟﻔﺌــ
ﻫــ
،SqlDataSourceEnumeratorﻷﻥ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻫﻲ ﺍﻟﺘﻲ ﺘﻌﻤل ﻋﻠـﻰ
ﺨﺎﺩﻡ ،ﺴﻭﺍﺀ ﺃﻜﺎﻥ ﺨﺎﺩﻤﺎ ﻤﺤﻠﻴﺎ Localﺃﻭ ﺒﻌﻴﺩﺍ .Remote
ﻭﺘﺘﻴﺢ ﻟﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤﻌﻠﻭﻤﺎﺕ ﻋﻥ ﺍﻟﺨﻭﺍﺩﻡ ﺍﻟﻤﺘﻭﻓﺭﺓ ﺤﺎﻟﻴﺎ ﻋﻠﻰ ﺍﻟﺸـﺒﻜﺔ ﺍﻟﺘـﻲ
ﻴﺘﺼل ﺒﻬﺎ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل.
ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻭﺤﻴﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ:
ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻟﺘﻌﺭﺽ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻗﺎﺌﻤﺔ ﺒﺄﺴﻤﺎﺀ ﺍﻟﺨﻭﺍﺩﻡ ﺍﻟﻤﺘﺎﺤﺔ ،ﻟﻴﺨﺘـﺎﺭ
ﺍﻟﺨﺎﺩﻡ ﺍﻟﺫﻱ ﻴﺭﻴﺩ ﺃﻥ ﻴﺘﺼل ﺒﻪ ..ﻟﻜﻥ ﻋﻠﻴﻙ ﺃﻥ ﺘﻼﺤﻅ ﻤﺎ ﻴﻠﻲ:
-١ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺘﺴﺘﻬﻠﻙ ﻭﻗﺘﺎ ﻋﻨﺩ ﺘﻨﻔﻴﺫﻫﺎ ،ﺒﺴﺒﺏ ﺒﺤﺜﻬﺎ ﻋﻥ ﺍﻟﺨﻭﺍﺩﻡ ﺍﻟﻤﺘﺎﺤﺔ ﻋﻠـﻰ
ﺍﻟﺸﺒﻜﺔ.
١٩٨
-٢ﻨﺎﺘﺞ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻗﺩ ﻴﺨﺘﻠﻑ ﻤﻥ ﻤﺭﺓ ﺇﻟﻰ ﺃﺨﺭﻯ ،ﺒﺴﺒﺏ ﻅﻬﻭﺭ ﺒﻌﺽ ﺍﻟﺨﻭﺍﺩﻡ ﺃﻭ
ﺍﺨﺘﻔﺎﺌﻬﺎ!
-٣ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻗﺩ ﻻ ﺘﻌﻴﺩ ﻜل ﺍﻟﺨﻭﺍﺩﻡ ﺍﻟﻤﺘﺎﺤﺔ ﻓﻌـﻼ ،ﻟﻬـﺫﺍ ﻋﻠﻴـﻙ ﺃﻥ ﺘﻌـﺭﺽ
ﻟﻠﻤﺴﺘﺨﺩﻡ ﻤﺭﺒﻊ ﻨﺹ ﺃﻴﻀﺎ ،ﻟﻴﻜﺘﺏ ﺍﺴﻡ ﺍﻟﺨﺎﺩﻡ ﺒﻨﻔﺴﻪ ﺇﺫﺍ ﻟﻡ ﻴﺠﺩﻩ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ.
ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﻤﺸـﺭﻭﻉ DataProvidersﻟﻨﻌـﺭﺽ ﻓـﻲ ﺍﻟﺠـﺩﻭل
ﺍﻟﺴﻔﻠﻲ ،ﺍﻟﺨﻭﺍﺩﻡ ﺍﻟﻤﺘﺎﺤﺔ ﻋﻠﻰ ﺍﻟﻤﺯﻭﺩ ﺍﻟﻤﺤﺩﺩ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﻌﻠﻭﻱ ،ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓـﻲ
ﺍﻟﺼﻭﺭﺓ:
ﻟﻔﻌل ﻫﺫﺍ ،ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﺤﺩﺙ RowEnterﺍﻟﺨﺎﺹ ﺒﺠﺩﻭل ﺍﻟﻌﺭﺽ ،ﻭﻓﻴﻪ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺭﻗﻡ
ﺍﻟﺼﻑ ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻜﺎﺌﻥ ﺼﻑ ﺍﻟﺒﻴﺎﻨـﺎﺕ DataRowﺍﻟﻤﻨـﺎﻅﺭ ﻟـﻪ ﻓـﻲ ﺠـﺩﻭل
ﺍﻟﻤﺯﻭﺩﺍﺕ ،ﻭﺃﺭﺴﻠﻨﺎ ﻫﺫﺍ ﺍﻟﺼﻑ ﺇﻟﻰ ﺍﻟﻭﺴﻴﻠﺔ DbProviderFactories.GetFactory
ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻤﺼﻨﻊ ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ:
;]var R = TblProviders.Rows[e.RowIndex
;)var Pf = DbProviderFactories.GetFactory(R
١٩٩
ﺒﻌﺩ ﻫﺫﺍ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﻭﺴﻴﻠﺔ CanCreateDataSourceEnumeratorﻟﻠﺘﺄﻜﺩ ﻤﻥ ﺃﻥ ﺍﻟﻤـﺯﻭﺩ
ﻴﺘﻴﺢ ﻋﺭﺽ ﺍﻟﺨـﻭﺍﺩﻡ ،ﻭﻤـﻥ ﺍﺴـﺘﺨﺩﻤﻨﺎ ﺍﻟﻭﺴـﻴﻠﺔ CreateDataSourceEnumerator
ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻋﺩﺍﺩ ﺍﻟﺨﻭﺍﺩﻡ ،ﻭﻤﻨﻪ ﺤﺼﻠﻨﺎ ﻋﻠﻰ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺘﻔﺎﺼـﻴل ﻫـﺫﻩ
ﺍﻟﺨﻭﺍﺩﻡ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ GetDataSourcesﻭﻋﺭﻀﻨﺎﻩ ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ:
)if (Pf.CanCreateDataSourceEnumerator
{
;) (var Se = Pf.CreateDataSourceEnumerator
;) (var TblServers = Se.GetDataSources
;DgServers.DataSource = TblServers
}
else
;DgServers.DataSource = null
ﻋﻨﺩ ﺘﺠﺭﺒﺔ ﻫﺫﺍ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻋﻠﻰ ﺠﻬﺎﺯﻙ ،ﻟﻥ ﺘﻅﻬﺭ ﺃﻴﺔ ﺨﻭﺍﺩﻡ ﺇﻻ ﻋﻨـﺩ ﺍﺨﺘﻴـﺎﺭ ﻤـﺯﻭﺩ
ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ،ﺤﻴﺙ ﺴﻴﻅﻬﺭ ﺍﻟﺨﺎﺩﻡ ﺍﻟﻤﺤﻠﻲ Local Serverﺍﻟﻤﻌﺭﻑ ﻋﻠﻰ ﺠﻬـﺎﺯﻙ
)ﻭﻫﻭ ﻴﻤﺘﻠﻙ ﻨﻔﺱ ﺍﺴﻡ ﺠﻬﺎﺯﻙ( ﻭﻓﻲ ﺍﻟﻐﺎﻟﺏ ﻟﻥ ﻴﻅﻬﺭ ﺍﻟﺨﺎﺩﻡ SQLEXPRESSﺍﻟﺫﻱ
ﻴﻌﻤل ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﺨﺎﺩﻡ ﺍﻟﻤﺤﻠﻲ!
٢٠٠
ﻓﺌﺔ ﻋﺩﺍﺩ ﻤﺼﺎﺩﺭ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ
SqlDataSourceEnumerator Class
ﺍﻟﻨﺴﺨﺔ :Instance
ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﺠﺩﻴﺩﺓ ﻤﻥ ﺍﻟﻔﺌﺔ ،SqlDataSourceEnumeratorﻤﻤﺎ ﻴﻐﻨﻴﻙ ﻋﻥ ﺍﺴﺘﺨﺩﺍﻡ
ﻤﺼﻨﻊ ﺍﻟﻤﺯﻭﺩ ﺃﻭﻻ ﻟﻠﻭﺼﻭل ﺇﻟﻴﻬﺎ.
ﻭﺍﻟﻤﺸﺭﻭﻉ SqlServersﻴﺭﻴﻙ ﻜﻴﻑ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻟﻌـﺭﺽ ﺍﻟﺨـﻭﺍﺩﻡ
ﺍﻟﻤﺘﻭﻓﺭﺓ ﻋﻠﻰ ﺠﻬﺎﺯﻙ ،ﻭﻫﻭ ﻻ ﻴﺤﺘﺎﺝ ﻟﻔﻌل ﻫﺫﺍ ،ﺇﻻ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﺴﻁﺭ ﺍﻟﻭﺤﻴﺩ ﻤﻥ ﺍﻟﻜﻭﺩ:
= DgServers.DataSource
;) (SqlDataSourceEnumerator.Instance.GetDataSources
٢٠١
-١١-
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ DataSet
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻫﻲ ﻭﻋﺎﺀ ﻤﺼﻐﺭ ﻟﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻙ ،ﻴﺘﻴﺢ ﻟﻙ ﺃﻥ ﺘﺤﻤـل ﻓـﻲ
ﺍﻟﺫﺍﻜﺭﺓ ،ﺒﻌﺽ ﺍﻟﺠﺩﺍﻭل ﺃﻭ ﺃﺠﺯﺍﺀ ﻤﻨﻬﺎ )ﺘﺒﻌﺎ ﻟﻼﺴﺘﻌﻼﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ( ﻤﻊ ﻗﺩﺭﺘﻙ ﺇﻨﺸﺎﺀ ﺍﻟﻌﻼﻗﺎﺕ
ﺒﻴﻨﻬﺎ ﻭﻭﻀﻊ ﺍﻟﻘﻴﻭﺩ ﻋﻠﻴﻬﺎ ،ﻭﺒﻬﺫﺍ ﻴﻤﻜﻨﻙ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻫﺫﻩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺠﻬـﺎﺯﻙ ﺒﻌـﺩ ﻗﻁـﻊ
ﺍﻻﺘﺼﺎل ﻤﻊ ﺍﻟﺨﺎﺩﻡ.
ﻭﺘﻤﺘﺎﺯ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺄﻨﻬﺎ ﻋﺎﻤﺔ ،ﻓﻬﻲ ﺘﺴﺘﻁﻴﻊ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺃﻱ ﻨﻭﻉ ﻤﻥ ﺃﻨـﻭﺍﻉ ﻗﻭﺍﻋـﺩ
ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻴﻤﻜﻨﻬﺎ ﺍﺴﺘﻴﻌﺎﺏ ﺍﻟﺠﺩﺍﻭل ﺩﻭﻥ ﺃﻥ ﻴﻌﻨﻴﻬﺎ ﻤﺼﺩﺭﻫﺎ ،ﺒﻴﻨﻤﺎ ﺘﺘﺭﻙ ﻤﻬﻤﺔ ﺍﻟﺘﻌﺎﻤل ﻤـﻊ
ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ..ﻟﻬﺫﺍ ﻴﻭﺠﺩ ﻓﻲ ﺩﻭﺕ ﻨﺕ ﻨﻭﻉ ﻭﺍﺤﺩ ﻓﻘﻁ ﻤﻥ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻋﻠﻰ ﻋﻜﺱ ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﺘﻲ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﺴﺎﺒﻘﺎ ،ﻭﺍﻟﺘﻲ ﻴﻭﺠـﺩ ﻤﻨـﻪ ﻨـﻭﻉ
ﺨﺎﺹ ﺒﻜل ﻤﺯﻭﺩ.
ﻭﺘﺴﺘﺨﺩﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺩﺍﺨﻠﻴﺎ ﻜـﻭﺩ XMLﻟﺤﻔـﻅ ﻤﺨﻁﻁـﺎﺕ ﺍﻟﺠـﺩﺍﻭل ﻭﺍﻷﻋﻤـﺩﺓ
،Schemaﻭﺒﻴﺎﻨﺎﺕ ﺍﻟﺼﻔﻭﻑ ..ﻭﻴﺘﻴﺢ ﻟﻙ ﻫﺫﺍ ﺤﻔﻅ ﻤﺨﻁﻁـﺎﺕ ﻭﺒﻴﺎﻨـﺎﺕ ﺍﻟﺠـﺩﺍﻭل ﻤـﻥ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﺠﻬﺎﺯﻙ ﻓﻲ ﺼﻭﺭﺓ ﻭﺜﺎﺌﻕ ،XMLﻭﻤـﻥ ﺜـﻡ ﺇﻋـﺎﺩﺓ ﺘﺤﻤﻴﻠﻬـﺎ ﻓـﻲ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺭﺓ ﺃﺨﺭﻯ ﻻﺤﻘﺎ.
ﻭﻜﻤﺎ ﺫﻜﺭﻨﺎ ﻤﻥ ﻗﺒل ،ﺘﺤﺘﻔﻅ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻨﺴﺨﺘﻴﻥ ﻤﻥ ﻜل ﺴﺠل:
-١ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ Original Versionﺍﻟﺘﻲ ﺘﻡ ﺘﺤﻤﻴﻠﻬﺎ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-٢ﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ Current Versionﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺴﺠل ﺒﻌﺩ ﺤﺩﻭﺙ ﺘﻐﻴﻴـﺭﺍﺕ
ﺒﻪ.
٢٠٢
ﻭﻴﻤﺘﻠﻙ ﻜل ﺴﺠل ﺍﻟﺨﺎﺼﻴﺔ RowStateﺍﻟﺘﻲ ﺘﻭﻀﺢ ﺤﺎﻟﺘﻪ ،ﻭﻫل ﺩﺨﻠﺕ ﻋﻠﻴﻪ ﺘﻐﻴﻴﺭﺍﺕ ﻭﻫل
ﺘﻡ ﺤﻔﻅ ﻫﺫﻩ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻡ ﻻ ..ﻭﺒﻬﺫﺍ ﺍﻟﺘﻨﻅﻴﻡ ﺘﺴﺘﻁﻴﻊ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻨﻘل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺘﺤﺩﻴﺜﻬﺎ ،ﺒﺎﻻﻋﺘﻤﺎﺩ ﻋﻠﻰ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺍﻟﺫﻱ ﻴﻌﻴـﺩ ﻓـﺘﺢ
ﺍﻻﺘﺼﺎل ﻤﻊ ﺍﻟﺨﺎﺩﻡ ..ﻫﺫﺍ ﻴﺠﻌل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻓﻀل ﻤﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓـﻲ ﺍﻟﺤـﺎﻻﺕ
ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻴﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻜﺜﻴﺭ ﻤﻥ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻟﺴﺠﻼﺕ ،ﻭﻴﺴﺘﺨﺩﻤﻬﺎ ﺃﻜﺜـﺭ ﻤـﻥ
ﻤﺭﺓ ﺒﺩﻭﻥ ﺘﺭﺘﻴﺏ ﻤﻌﻴﻥ ..ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻴﻜﻭﻥ ﺍﻟﻤﺭﻭﺭ ﻋﺒﺭﻫﺎ ﻋﻠﻰ ﺍﻟﺘﻭﺍﻟﻲ ﺒﺎﺴﺘﺨﺩﺍﻡ
ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻤﺭﺍ ﻏﻴﺭ ﻋﻤﻠﻲ.
-٢ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻁﻠﻭﺏ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻭﺍﻟﺴﻤﺎﺡ ﻟﻪ ﺒﺎﻟﺘﻌﺎﻤل ﻤﻌﻬـﺎ ﻭﺘﻌـﺩﻴﻠﻬﺎ
ﺒﺤﺭﻴﺔ ،ﻭﺍﻹﻀﺎﻓﺔ ﺇﻟﻴﻬﺎ ﻭﺍﻟﺤﺫﻑ ﻤﻨﻬﺎ ..ﺃﻨﺕ ﺘﻌـﺭﻑ ﺃﻥ ﻗـﺎﺭﺉ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻻ ﻴﻘـﻭﻡ
ﺒﺘﺤﺩﻴﺙ ﺍﻟﺴﺠﻼﺕ ،ﻓﻬﻭ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ.
-٣ﺇﺫﺍ ﻜﺎﻨﺕ ﻫﻨﺎﻙ ﻋﻼﻗﺎﺕ ﺒﻴﻥ ﺍﻟﺠﺩﺍﻭل ﻭﻗﻴﻭﺩ ﻤﻔﺭﻭﻀﺔ ﻋﻠﻴﻬﺎ ،ﻭﻤﻥ ﺍﻟﻤﻬـﻡ ﺍﻟﺘﻌﺎﻤـل
ﻤﻌﻬﺎ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻋﻨﺩ ﺍﻹﻀﺎﻓﺔ ﻭﺍﻟﺤﺫﻑ ،ﻓﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺴﻤﺢ ﺒﺎﻟﺘﻌﺎﻤـل ﻤـﻊ
ﺍﻟﻌﻼﻗﺎﺕ ﻭﺍﻟﻘﻴﻭﺩ ،ﻭﻫﺫﺍ ﻏﻴﺭ ﻤﺘﻭﻓﺭ ﻓﻲ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻟﻜﻥ ﻋﻠﻰ ﺍﻟﺠﺎﻨﺏ ﺍﻵﺨﺭ ،ﺘﻌﺎﻨﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺍﻟﻌﻴﺒﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ:
-١ﺘﻌﺘﺒﺭ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﺒﺌﺎ ﻋﻠﻰ ﺫﺍﻜﺭﺓ ﺍﻟﺠﻬﺎﺯ ،ﻟﻬﺫﺍ ﻴﺠﺏ ﻋﻠﻴﻙ ﺘﺤﻤﻴﻠﻬﺎ ﺒﺄﻗل ﻗﺩﺭ
ﻤﻤﻜﻥ ﺘﺤﺘﺎﺠﻪ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻻ ﺘﻀﻊ ﻓﻴﻬﺎ ﺍﻟﺠﺩﺍﻭل ﺒﻜﺎﻤل ﺼـﻔﻭﻓﻬﺎ ﺒـﺩﻭﻥ ﻓﺎﺌـﺩﺓ،
ﻭﺒﺩﻻ ﻤﻥ ﻫﺫﺍ ﺍﺴﺘﺨﺩﻡ ﺸﺭﻁﺎ ﻓﻲ ﺠﻤﻠﺔ ﺍﻟﺘﺤﺩﻴﺩ SELECTﻟﺘﺤﺼل ﻋﻠﻰ ﺍﻟﺴـﺠﻼﺕ
ﺍﻟﻤﻁﻠﻭﺒﺔ ﺒﺎﻟﻀﺒﻁ .ﺃﻴﻀﺎ ،ﻻ ﺘﺤﻤل ﻤﻥ ﺍﻟﺠﺩﺍﻭل ﺃﻋﻤﺩﺓ ﻻ ﻴﺤﺘﺎﺠﻬﺎ ﺍﻟﻤﺴﺘﺨﺩﻡ.
-٢ﻗﺩ ﺘﺴﺒﺏ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺸﺎﻜل ﻋﻨﺩ ﺘﺤﺩﻴﺙ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﻭﺫﻟـﻙ ﺇﺫﺍ ﻜـﺎﻥ
ﻤﺴﺘﺨﺩﻤﻭﻥ ﺁﺨﺭﻭﻥ ﻗﺩ ﻏﻴﺭﻭﺍ ﻗﻴﻡ ﺒﻌﺽ ﺍﻟﺴﺠﻼﺕ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺃﺜﻨـﺎﺀ ﻗﻁـﻊ
ﺍﻻﺘﺼﺎل ﻭﺘﻌﺎﻤﻠﻙ ﻤﻌﻬﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﻓﻴﻤـﺎ ﻴﺴـﻤﻰ ﺒﻤﺸـﺎﻜل ﺍﻟﺘﻁـﺎﺒﻕ
..Concurrency Violationsﻭﻗﺩ ﺭﺃﻴﻨﺎ ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺴﺎﺒﻕ ﻜﻴﻑ ﻴﻤﻜﻥ ﺤل ﻫـﺫﻩ
ﺍﻟﻤﺸﻜﻠﺔ.
ﻭﺍﻵﻥ ،ﺩﻋﻨﺎ ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
٢٠٣
ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ DataSet Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻭﺠـﺩ ﻓـﻲ ﺍﻟﻨﻁـﺎﻕ System.Dataﻭﻫـﻲ ﺘﻤﺜـل ﻭﺍﺠﻬـﺔ ﻤﺼـﺩﺭ ﺍﻟﻘﺎﺌﻤـﺔ
IListSourceﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺼﻴﻐﺘﺎﻥ ﺍﻟﺘﺎﻟﻴﺘﺎﻥ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ.
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻨﺼﻴﺎ ،ﻴﻤﺜل ﺍﺴﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺍﻟـﺫﻱ ﺴﻴﺴـﺘﺨﺩﻡ
ﻋﻨﺩ ﺤﻔﻅ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻤﻠﻑ .XML
ﺍﻟﺒﺎﺩﺌﺔ :Prefix
ﺘﺤﺩﺩ ﺍﻟﺒﺎﺩﺌﺔ ﺍﻟﺘﻲ ﺴﺘﺴﺘﺨﺩﻡ ﻟﺘﻤﻴﻴﺯ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﺘﻲ ﺘﻨﺘﻤﻲ ﺇﻟﻰ ﻨﻁﺎﻕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ..
ﻫﺫﺍ ﻤﻔﻴﺩ ﺇﺫﺍ ﻜﺎﻥ ﻤﻠﻑ XMLﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻨﻁﺎﻕ ﺍﻻﺴﻡ ﻓﻴﻪ ﺃﻜﺜﺭ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ،
ﻭﺘﺭﻴﺩ ﺘﻤﻴﻴﺯ ﻜل ﻤﻨﻬﺎ ﺘﺤﺕ ﻨﻁﺎﻕ ﻓﺭﻋﻲ ﺨﺎﺹ ﺒﻬﺎ.
٢٠٤
ﻤﻠﺤﻭﻅﺔ:
ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ReadXmlﻭ ReadXmlSchemaﻟﺘﺤﻤﻴـل ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺃﻭ
ﺍﻟﻤﺨﻁﻁ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﺈﻨﻬﻤﺎ ﺘﺒﺤﺜﺎﻥ ﻓﻲ ﻤﻠﻑ XMLﻋـﻥ ﻨﻁـﺎﻕ ﺍﻻﺴـﻡ
ﺍﻟﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺨﺎﺼـﻴﺔ ،DataSetNameﻭﻤﺠﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﻤﻤﻴـﺯﺓ ﺒﺎﻟﺒﺎﺩﺌـﺔ
ﺍﻟﻤﻭﻀﺤﺔ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ،Prefixﻓﺈﺫﺍ ﻟﻡ ﺘﻌﺜﺭ ﻓﻲ ﺍﻟﻤﻠﻑ ﻋﻥ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺘﺤﻘـﻕ
ﻫﺫﻴﻥ ﺍﻟﺸﺭﻁﻴﻥ ،ﻻ ﻴﺘﻡ ﺘﺤﻤﻴل ﺃﻱ ﺸﻲﺀ ﻤﻥ ﺍﻟﻤﻠﻑ.
٢٠٥
ﺍﻟﻤﺤل :Locale
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺜﻘﺎﻓﺔ ،CultureInfoﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺘﻔﺎﺼـﻴل ﺍﻟﻠﻐـﺔ
ﺍﻟﺘﻲ ﺘﺴﺘﺨﺩﻡ ﻟﻤﻘﺎﺭﻨﺔ ﻭﺘﺭﺘﻴﺏ ﺍﻟﻨﺼﻭﺹ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻻﺤﻅ ﺃﻥ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ،ﺴﻴﻐﻴﺭ ﺘﻠﻘﺎﺌﻴﺎ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ Localﺍﻟﺨﺎﺼﺔ ﺒﻜـل
ﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﺍﻟﺠﺩﺍﻭل :Tables
ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺠﺩﺍﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ ،DataTableCollectionﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠـﻰ ﻜﺎﺌﻨـﺎﺕ
ﺍﻟﺠﺩﺍﻭل DataTable Objectsﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﺴـﻨﺘﻌﺭﻑ ﻋﻠـﻰ
ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﺎﻟﺘﻔﺼﻴل ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺘﺎﻟﻲ.
ﻤﺎ ﻴﻌﻨﻴﺎ ﻫﻨﺎ ﻫﻭ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺘﺤﺭﻴﺭ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﻤﻥ ﺨﻼل ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ..ﻓﻠـﻭ
ﻀﻐﻁﺕ ﺯﺭ ﺍﻻﻨﺘﻘﺎل ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺨﺎﻨﺔ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ،ﻓﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﻤﺤـﺭﺭ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل Table Collection Editorﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
٢٠٦
ﻓﻲ ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﺠﺩﺍﻭل ﺠﺩﻴﺩ ﺒﻀﻐﻁ ﺍﻟﺯﺭ ،Addﺜﻡ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺼـﺎﺌﺹ
ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﻘﺴﻡ ﺍﻷﻴﻤﻥ ﻤﻥ ﺍﻟﻨﺎﻓﺫﺓ ﻟﺘﻐﻴﻴﺭ ﺍﺴﻡ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ﻭﻁﺭﻴﻘﺔ ﻋﺭﻀﻪ ﻭﺍﻷﻋﻤﺩﺓ
ﺍﻟﻤﻭﺠﻭﺩﺓ ﺒﻪ ..ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ ﺒﺎﻟﺘﻔﺼﻴل ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﻼﺤﻕ.
ﺍﻟﻌﻼﻗﺎﺕ :Relations
ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﻋﻼﻗﺎﺕ ﺍﻟﺒﻴﺎﻨﺎﺕ ،DataRelationCollectionﺍﻟﺘـﻲ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ
ﻜﺎﺌﻨﺎﺕ ﺍﻟﻌﻼﻗﺎﺕ DataRelation Objectsﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ..
ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﺎﻟﺘﻔﺼﻴل ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺘﺎﻟﻲ.
ﻻﺤﻅ ﺃﻥ ﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﻟﺠﺩﺍﻭل ﻭﺍﻟﺴﺠﻼﺕ ﻻ ﻴﻀﻴﻑ ﺍﻟﻌﻼﻗﺎﺕ ﺒﻴﻥ ﺍﻟﺠﺩﺍﻭل
ﺘﻠﻘﺎﺌﻴﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻌﻼﻗﺎﺕ ..Relationsﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺃﻥ ﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺎﺕ ﺒﻨﻔﺴﻙ
ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻌﻼﻗﺎﺕ ،ﺴﻭﺍﺀ ﻤﻥ ﺍﻟﻜﻭﺩ ﺃﻭ ﺒﺎﺴﺘﺨﺩﺍﻡ ﻨﺎﻓﺫﺓ ﺍﻟﻤﺨﻁﻁ ﻜﻤﺎ ﺴﻨﺭﻯ ﻻﺤﻘﺎ..
ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﺍﻟﻌﻼﻗﺎﺕ ﺒﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ﻤﻥ ﺨﻼل ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ..ﻓﻠﻭ ﻀـﻐﻁﺕ
٢٠٧
ﺯﺭ ﺍﻻﻨﺘﻘﺎل ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺨﺎﻨﺔ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ،ﻓﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﻤﺤﺭﺭ ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﻌﻼﻗﺎﺕ Relations Collection Editorﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
ﺍﻀﻐﻁ ﺍﻟﺯﺭ Addﻹﻀﺎﻓﺔ ﻋﻼﻗﺔ ﺠﺩﻴﺩﺓ ..ﺴﺘﻅﻬﺭ ﻨﺎﻓﺫﺓ ﺇﻨﺸﺎﺀ ﺍﻟﻌﻼﻗـﺔ ﻟﺘﺴـﻤﺢ ﻟـﻙ
ﺒﺘﺤﺩﻴﺩ ﺍﻟﺠﺩﻭﻟﻴﻥ ﻭﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﺸﺘﺭﻜﺔ ﻓﻲ ﺍﻟﻌﻼﻗﺔ ﻜﻤﺎ ﺘﻌﻠﻤﻨﺎ ﻤﻥ ﻗﺒل ..ﻭﺒﻌﺩ ﺃﻥ ﺘﻀـﻐﻁ
OKﻹﻏﻼﻕ ﻨﺎﻓﺫﺓ ﺍﻟﻌﻼﻗﺔ ،ﺴﺘﻅﻬﺭ ﺍﻟﻌﻼﻗﺔ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﺴﺭﻯ ،ﻭﺘﻅﻬﺭ ﺨﺼﺎﺌﺼﻬﺎ ﻓﻲ
ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﻤﻨﻰ ..ﻭﻟﻭ ﺃﺭﺩﺕ ﺘﻐﻴﻴﺭ ﻋﻨﺎﺼﺭ ﺍﻟﻌﻼﻗﺔ ،ﻓﺎﻀﻐﻁ ﺍﻟﺯﺭ Editﻟﻌﺭﺽ ﻨﺎﻓـﺫﺓ
ﺍﻟﻌﻼﻗﺔ ﻤﺭﺓ ﺃﺨﺭﻯ.
٢٠٨
ﺍﻟﺨﺼﺎﺌﺹ ﺍﻹﻀﺎﻓﻴﺔ :ExtendedProperties
ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺨﺼﺎﺌﺹ PropertyCollectionﺍﻟﺘﻲ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﺍﻟﺨﺼـﺎﺌﺹ
ﺍﻹﻀﺎﻓﻴﺔ ﺍﻟﺘﻲ ﺘﻀﻴﻔﻬﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺍﻟﻤﺠﻤﻭﻋﺔ PropertyCollectionﺘﺭﺙ ﺍﻟﺠﺩﻭل ﺍﻟﻤﺨﺘﻠﻁ ،Hashtableﻤﻤﺎ ﻴﺘﻴﺢ ﻟﻙ
ﺇﻀﺎﻓﺔ ﺍﺴﻡ ﺍﻟﺨﺎﺼﻴﺔ ﻜﻤﻔﺘﺎﺡ ،Keyﻭﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺤﻔﻅﻬﺎ ﻓﻴﻬﺎ ﻜﻘﻴﻤﺔ .Value
ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻀﻴﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺨﺎﺼﻴﺔ ﺇﻀﺎﻓﻴﺔ ﺘﺤـﺘﻔﻅ ﺒﺎﺴـﻡ ﺍﻟﺒﺭﻨـﺎﻤﺞ
ﺍﻟﺨﺎﺹ ﺒﻙ ،ﺜﻡ ﻴﻌﺭﺽ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻓﻲ ﺭﺴﺎﻟﺔ:
;)"Ds.ExtendedProperties.Add("ProgName", "MyProg
;))"Console.WriteLine(Ds.ExtendedProperties("ProgName
ﻤﺤﻭ :Clear
ﺘﻤﺤﻭ ﻜل ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﻜل ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻟﻜﻨﻬﺎ ﻻ ﺘﻤﺤﻭ ﺍﻟﺠﺩﺍﻭل ﻨﻔﺴـﻬﺎ،
ﻭﻻ ﺍﻟﻌﻼﻗﺎﺕ ﺒﻴﻨﻬﺎ ..ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺴﺘﺴﺒﺏ ﺨﻁﺄ ﻓـﻲ ﺍﻟﺒﺭﻨـﺎﻤﺞ ﻟـﻭ ﻜﺎﻨـﺕ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺘﻌﺎﻤل ﻤﻊ ﻭﺜﻴﻘﺔ XMLﻤﻥ ﺍﻟﻨﻭﻉ .XmlDataDocument
ﺘﺼﻔﻴﺭ :Reset
ﺘﻔﺭﻍ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺠﻤﻴﻊ ﻤﺤﺘﻭﻴﺎﺘﻬﺎ ،ﺒﻤﺎ ﻓﻲ ﺫﻟﻙ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻟﻌﻼﻗﺎﺕ ﻭﺍﻟﻘﻴﻭﺩ.
٢١٠
ﻨﺴﺦ :Clone
ﺘﻨﺴﺦ ﺘﺭﻜﻴﺏ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ )ﻤﺨﻁﻁﺎﺕ ﺍﻟﺠﺩﺍﻭل ،ﻭﺍﻟﻌﻼﻗﺎﺕ ﻭﺍﻟﻘﻴﻭﺩ( ﺇﻟﻰ ﻤﺠﻤﻭﻋـﺔ
ﺒﻴﺎﻨﺎﺕ ﺠﺩﻴﺩﺓ ﻭﺘﻌﻴﺩ ﻤﺭﺠﻌﺎ ﺇﻟﻴﻬﺎ ..ﻟﻜﻨﻬﺎ ﻻ ﺘﻨﺴﺦ ﺃﻱ ﺴﺠﻼﺕ.
ﻨﺴﺦ :Copy
ﺘﻨﺴﺦ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﺎﻤﻠﺔ )ﻤﺨﻁﻁﺎﺕ ﺍﻟﺠﺩﺍﻭل ،ﻭﺍﻟﻌﻼﻗـﺎﺕ ﻭﺍﻟﻘﻴـﻭﺩ ﻭﺍﻟﺴـﺠﻼﺕ
ﺃﻴﻀﺎ( ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻴﺩﺓ ﻭﺘﻌﻴﺩ ﻤﺭﺠﻌﺎ ﺇﻟﻴﻬﺎ.
ﺘﻡ ﺇﻨﺸﺎﺀ ﻫﺫﺍ ﺍﻟﺴﺠلّ ﻭﻟﻜﻨﻪ ﻟﻡ ﻴﻭﻀﻊ ﺒﻌﺩ ﻓﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺴـﺠﻼﺕ ﻤﺴﺘﻘلّ
Rowsﺍﻟﺨﺎﺼﺔ ﺒﺄﻱ ﺠﺩﻭل ،ﺃﻭ ﺃﻨﻪ ﺤﺫﻑ ﻟﻠﺘﻭ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺴﺠﻼﺕ Detached
ﺃﺤﺩ ﺍﻟﺠﺩﺍﻭل.
ﻟﻡ ﺘﺘﻐﻴﺭ ﺒﻴﺎﻨﺎﺕ ﻫﺫﺍ ﺍﻟﺴﺠلّّ ،ﻤﻨﺫ ﺃﻥ ﺘﻡ ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻭ
ﻟﻡ ﻴﺘﻐﻴﺭ
Unchangedﻤﻨﺫ ﺁﺨﺭ ﺍﺴﺘﺩﻋﺎﺀ ﻟﻠﻭﺴﻴﻠﺔ .AcceptChanges
٢١١
ﻫﺫﺍ ﺍﻟﺴﺠلّ ﻟﻴﺱ ﻤﻭﺠﻭﺩﺍ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺇﻨﻤﺎ ﺘﻤـﺕ ﺇﻀـﺎﻓﺘﻪ ﻤﻀﺎﻑ
ﻜﺴﺠل ﺠﺩﻴﺩ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ. Added
ﺘﻡ ﺤﺫﻑ ﻫﺫﺍ ﺍﻟﺴﺠلّ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻟﻜﻨﻪ ﻤﺎ ﺯﺍل ﻤﻭﺠـﻭﺩﺍ ﻤﺤﺫﻭﻑ
ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ. Deleted
لّ ،ﻭﻟﻜﻥ ﻟﻡ ﻴﺘﻡ ﺤﻔﻅ ﺍﻟﺘﻌﺩﻴﻼﺕ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ
ﺘﻡ ﺘﻌﺩﻴل ﻫﺫﺍ ﺍﻟﺴﺠ ّ ﻤﻌﺩل
ﺒﻌﺩ. Modified
ﻭﻴﻤﻜﻨﻙ ﺩﻤﺞ ﺃﻜﺜﺭ ﻤﻥ ﻗﻴﻤﺔ ﻤﻥ ﻗﻴﻡ ﻫﺫﺍ ﺍﻟﻤﺭﻗﻡ ﻤﻌﺎ ،ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﻌﺎﻤل .OR
ﺘﺤﻤﻴل :Load
ﺘﺘﻴﺢ ﻟﻙ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﺘﺨﺩﺍﻡ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨـﺎﺕ DataReaderﻹﻀـﺎﻓﺔ ﺍﻟﻤﺯﻴـﺩ ﻤـﻥ
ﺍﻟﺴﺠﻼﺕ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺜﻼﺙ ﺼﻴﻎ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ:
-ﻤﻌﺎﻤل ﻤﻥ ﻨﻭﻉ ﺍﻟﻭﺍﺠﻬﺔ IDataReaderﻴﺴﺘﻘﺒل ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ.
٢١٣
-ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ LoadOptionﺍﻟﺘﻲ ﺘﺤﺩﺩ ﻤﺎﺫﺍ ﺴﻴﺤﺩﺙ ﺇﺫﺍ ﻜﺎﻨﺕ ﺒﻌـﺽ
ﺍﻟﺴﺠﻼﺕ ﻤﻭﺠﻭﺩﺓ ﺴﺎﺒﻘﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻫل ﺴﻴﺘﻡ ﺘﺤﺩﻴﺙ ﺍﻟﻨﺴـﺨﺔ
ﺍﻷﺼــﻠﻴﺔ ﻤــﻥ ﺍﻟﺴــﺠل Original Versionﺃﻡ ﺍﻟﻨﺴــﺨﺔ ﺍﻟﺤﺎﻟﻴــﺔ
..Current Versionﻭﻗﺩ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﻤﺭﻗﻡ ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺴﺎﺒﻕ.
-ﻤﺼﻔﻭﻓﺔ ﺠﺩﺍﻭل ،DataTable Arrayﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﺒﻌـﺽ ﺍﻟﺠـﺩﺍﻭل
ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل ،DataSet.Tablesﻟﻴﺘﻡ ﻤﻠﺅﻫﺎ ﺒﺎﻟﺴﺠﻼﺕ
ﻤﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺤﻴﺙ ﺴﺘﻭﻀﻊ ﺴﺠﻼﺕ ﻜـل ﻤﺠﻤﻭﻋـﺔ ﻤـﻥ ﺍﻟﻨﺘـﺎﺌﺞ
ResultSetﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﻤﻨﺎﻅﺭ ﻟﻬﺎ ﻓﻲ ﺍﻟﺘﺭﺘﻴﺏ ﻓﻲ ﺍﻟﻤﺼﻔﻭﻓﺔ.
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ،ﺇﻻ ﺃﻥ ﻤﻌﺎﻤﻠﻬﺎ ﺍﻟﺜﺎﻟﺙ ﻴﺴـﺘﻘﺒل ﻤﺼـﻔﻭﻓﺔ
ﻨﺼﻴﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﺒﺩﻻ ﻤﻥ ﻜﺎﺌﻨﺎﺕ ﺍﻟﺠﺩﺍﻭل.
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﺒﻤﻌﺎﻤل ﺇﻀﺎﻓﻲ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ..ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻴﺄﺘﻲ ﻓﻲ
ﺍﻟﻤﻭﻀﻊ ﺍﻟﺜﺎﻟﺙ ﻓﻲ ﺘﺭﺘﻴﺏ ﺍﻟﻤﻌﺎﻤﻼﺕ ،ﻭﻫﻭ ﻤﻨـﺩﻭﺏ Delegateﻤـﻥ ﺍﻟﻨـﻭﻉ
،FillErrorEventHandlerﻭﻫﻭ ﺍﻟﻤﻨﺩﻭﺏ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓـﻲ ﺘﻌﺭﻴـﻑ ﺍﻟﺤـﺩﺙ
FillErrorﺍﻟﺨﺎﺹ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺭﺴل ﺇﻟـﻰ ﻫـﺫﺍ ﺍﻟﻤﻨـﺩﻭﺏ
ﻋﻨﻭﺍﻥ ﺇﺠﺭﺍﺀ ﻤﻨﺎﺴﺏ ،ﻟﻴﺘﻡ ﺍﺴﺘﺩﻋﺎﺅﻩ ﻟﻭ ﺤﺩﺙ ﺨﻁﺄ ﻋﻨﺩ ﺇﻀﺎﻓﺔ ﺃﺤﺩ ﺍﻟﺴـﺠﻼﺕ
ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﺩﻤﺞ :Merge
ﺘﻤﺯﺝ ﺒﻌﺽ ﺍﻟﺴﺠﻼﺕ ﺒﺴﺠﻼﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﺍﻟﻤﺯﺝ ﻴﻌﻨﻲ ﺃﻥ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺠﺩﻴﺩﺓ
ﺴﺘﺘﻡ ﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺃﻤﺎ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﺴـﺎﺒﻘﺎ ،ﻓﺴـﻴﺘﻡ ﻭﻀـﻊ
ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻀﺎﻓﺔ ﺒﺩﻻ ﻤﻨﻬﺎ ..ﻭﺘﺘﻡ ﻤﻁﺎﺒﻘﺔ ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﺨﻼل ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻟﻜـل
ﻤﻨﻬﺎ ،ﻟﻬﺫﺍ ﻴﺠﺏ ﺃﻥ ﻴﺤﺘﻭﻱ ﺠﺩﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﻤﻔﺘـﺎﺡ ﺃﺴﺎﺴـﻲ ،ﻭﺇﻻ ﺃﺩﺕ
ﻋﻤﻠﻴﺔ ﺍﻟﺩﻤﺞ ﺇﻟﻰ ﺘﻜﺭﺍﺭ ﻨﻔﺱ ﺍﻟﺼﻔﻭﻑ ﻤﺭﺘﻴﻥ.
ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻌﺩﻴﺩ ﻤﻥ ﺍﻟﺼﻴﻎ:
٢١٤
-١ﺒﻌﺽ ﺍﻟﺼﻴﻎ ﺫﺍﺕ ﻤﻌﺎﻤل ﻭﺍﺤﺩ ،ﻴﺴﺘﻘﺒل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺭﺍﺩ ﻤﺯﺠﻬﺎ ،ﺴـﻭﺍﺀ ﻜﺎﻨـﺕ
ﻗﺎﺩﻤﺔ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ،DataSetﺃﻭ ﺠـﺩﻭل DataTableﺃﻭ ﻤﺼـﻔﻭﻓﺔ
ﺴﺠﻼﺕ .DataRow Array
-٢ﺒﻌﺽ ﺍﻟﺼﻴﻎ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻎ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ ،ﺇﺫﺍ ﺠﻌﻠﺘﻪ Trueﻓﺴـﺘﺤﺘﻔﻅ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﺼﻠﻴﺔ ﺒﺎﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ﻟﻠﺴﺠﻼﺕ ،ﻭﺴﻴﺘﻡ ﺍﻟﻤﺯﺝ ﻓﻘـﻁ ﻋﻠـﻰ
ﻤﺴﺘﻭﻯ ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ ...ﺩﻋﻨﺎ ﻨﻔﻬﻡ ﻫﺫﺍ ﺒﻤﺜﺎل ﺼﻐﻴﺭ :ﺍﻓﺘﺭﺽ ﺃﻥ ﻟﺩﻴﻨﺎ ﺴﺠﻼ
ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﻴﻪ ﺨﺎﻨﺔ ﻗﻴﻤﺘﻬﺎ ﺍﻷﺼﻠﻴﺔ ،١ﻭﻗﻴﻤﺘﻬﺎ ﺍﻟﺤﺎﻟﻴﺔ ..٢ﻨﺭﻴﺩ ﺃﻥ
ﻨﻤﺯﺝ ﻫﺫﺍ ﺍﻟﺴﺠل ﺒﺴﺠل ﻤﻤﺎﺜل ﻟﻪ ،ﻟﻜﻥ ﺍﻟﻘﻴﻤﺔ ﺍﻷﺼﻠﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﻨﺔ ﻓﻴﻪ ﻫـﻲ ،٣
ﻭﻗﻴﻤﺘﻬﺎ ﺍﻟﺤﺎﻟﻴﺔ ﻫﻲ ..٤ﻟﻭ ﻜﺎﻨﺕ ﻗﻴﻤﺔ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤـل ،Trueﻓﺴﺘﺼـﻴﺭ ﺍﻟﻘﻴﻤـﺔ
ﺍﻷﺼﻠﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﻨﺔ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻌﺩ ﺍﻟﻤﺯﺝ ،٣ﻟﻜﻥ ﺴـﺘﻅل ﻗﻴﻤﺘﻬـﺎ
ﺍﻟﺤﺎﻟﻴﺔ ..٢ﺃﻤﺎ ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ ،Falseﻓﺴﺘﺼﻴﺭ ﺍﻟﻘﻴﻤﺔ ﺍﻷﺼﻠﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﻨـﺔ
ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻌﺩ ﺍﻟﻤﺯﺝ ،٣ﻭﻗﻴﻤﺘﻬﺎ ﺍﻟﺤﺎﻟﻴﺔ ..٤ﺍﻟﺠﺩﻭل ﺍﻟﺘﺎﻟﻲ ﻴﻠﺨـﺹ
ﻫﺫﺍ ﺍﻟﻤﺜﺎل:
ﻻﺤﻅ ﺃﻥ ﺠﻌل ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ،Trueﻫﻭ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﻭﺤﻴﺩﺓ ﺍﻟﺘﻲ ﺘﺴﺘﻁﻴﻊ ﺒﻬﺎ ﺘﻐﻴﻴﺭ
ﺍﻟﻘﻴﻤﺔ ﺍﻷﺼﻠﻴﺔ ﺩﻭﻥ ﺘﻐﻴﻴﺭ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺤﺎﻟﻴﺔ ،ﻷﻥ ﺼﻴﻎ ﺍﻟﻭﺴﻴﻠﺔ DataRow.Item
ﺍﻟﺘﻲ ﺘﺘﻴﺢ ﻟﻙ ﺘﺤﺩﻴﺩ ﺍﻟﻨﺴﺨﺔ ﺍﻟﺘﻲ ﺘﺘﻌﺎﻤل ﻤﻌﻬﺎ ،ﻗﺎﺒﻠﺔ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘـﻁ ،ﻭﻻ ﻴﻤﻜـﻥ
ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﻠﻜﺘﺎﺒﺔ!
٢١٥
ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﻭﺴﻴﻠﺔ Mergeﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ OptimisticConcurrency
ﻤﺭﺘﻴﻥ:
-ﻤﺭﺓ ﻓﻲ ﺤﺩﺙ ﻀﻐﻁ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ "ﺃﺭﻴﺩ ﺤﻔـﻅ ﺘﻌـﺩﻴﻼﺘﻲ" ،ﻭﻗـﺩ
ﺃﺭﺴﻠﻨﺎ ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻘﻴﻤﺔ Trueﻟﺘﻐﻴﻴـﺭ ﺍﻟﻨﺴـﺨﺔ
ﺍﻷﺼﻠﻴﺔ ﻟﻠﺴﺠل ﺍﻟﻤﺭﺍﺩ ﺇﻋﺎﺩﺓ ﺤﻔﻅﻪ ،ﻤﻊ ﺍﻻﺤﺘﻔﺎﻅ ﺒﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﻤﺴـﺘﺨﺩﻡ
ﻟﺤﻔﻅﻬﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-ﻭﻤﺭﺓ ﻓﻲ ﺤﺩﺙ ﻀﻐﻁ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ "ﺇﻟﻐﺎﺀ ﺘﻌﺩﻴﻼﺘﻲ" ،ﻭﻗﺩ ﺃﺭﺴـﻠﻨﺎ
ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻘﻴﻤﺔ Falseﻟﻠـﺘﺨﻠﺹ ﻤـﻥ ﺍﻟﺴـﺠل
ﺍﻟﻘﺩﻴﻡ ،ﻭﻭﻀﻊ ﺍﻟﺴﺠل ﺍﻟﻘﺎﺩﻡ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺩﻻ ﻤﻨﻪ )ﻴﺸـﻤل ﻫـﺫﺍ
ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ ﻭﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ﻟﻠﺴﺠل(.
-٣ﺒﻌﺽ ﺍﻟﺼﻴﻎ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻎ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻟﺙ ،ﻴﺤـﺩﺩ ﺭﺩ ﺍﻟﻔﻌـل ﺍﻟـﺫﻱ
ﺴﻴﺘﺨﺫ ﻟﻭ ﻜﺎﻥ ﺘﺭﻜﻴﺏ ﻤﺠﻤﻭﻋﺘﻲ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺨﺘﻠﻔﺎ )ﻜﻌﺩﻡ ﻭﺠﻭﺩ ﺒﻌﺽ ﺍﻟﺠﺩﺍﻭل ﺃﻭ
ﺍﻷﻋﻤﺩﺓ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺤﺎﻟﻴـﺔ( ،ﻭﻫـﻭ ﻴﺄﺨـﺫ ﺇﺤـﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ
MissingSchemaActionﺍﻟﺘﻲ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻤﻥ ﻗﺒـل ..ﺘـﺫﻜﺭ ﺃﻥ ﺍﻟﻘﻴﻤـﺔ
ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻓﻲ ﺍﻟﺼﻴﻎ ﺍﻟﺘﻲ ﻻ ﺘﺴﺘﻘﺒل ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻫﻲ ،Addﺒﻤﻌﻨـﻰ ﺇﻀـﺎﻓﺔ
ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺍﻟﻼﺯﻤﺔ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺤﺎﻟﻴـﺔ ﻻﺴـﺘﻘﺒﺎل ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺍﻟﺠﺩﻴﺩﺓ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻀﺎﻓﺔ.
ﺤﺔ ﺍﻟﻘﻴﻭﺩ ،Constrainsﺇﻻ ﺒﻌﺩ ﺍﻜﺘﻤﺎل ﻋﻤﻠﻴﺔ ﺍﻟﻤـﺯﺝ ..ﻓـﺈﺫﺍ
ﻭﻻ ﻴﺘﻡ ﺍﻟﺘﺤﻘﻕ ﻤﻥ ﺼ
ﻜﺎﻨﺕ ﻫﻨﺎﻙ ﺴﺠﻼﺕ ﺘﻌﺎﺭﺽ ﺍﻟﻘﻴﻭﺩ ﺍﻟﻤﻔﺭﻭﻀﺔ ،ﻴﺤﺩﺙ ﻤﺎ ﻴﻠﻲ:
-ﻴﻨﻁﻠﻕ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻤﻥ ﺍﻟﻨﻭﻉ .ConstraintException
-ﺘﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ Falseﻓﻲ ﺍﻟﺨﺎﺼـﻴﺔ DataSet.EnforceConstraintsﻹﻴﻘـﺎﻑ
ﺘﻁﺒﻴﻕ ﺍﻟﻘﻴﻭﺩ ،ﻭﺫﻟﻙ ﺤﺘﻰ ﻴﻤﻜﻥ ﺍﻻﺤﺘﻔﺎﻅ ﺒﺎﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻤﺯﻭﺠﺔ ﺇﻟﻰ ﺃﻥ ﺘﺭﻯ ﻜﻴﻑ
ﺘﺤل ﺍﻟﻤﺸﻜﻠﺔ.
-ﻴﻭﻀﻊ ﻨﺹ ﺍﻟﺨﻁﺄ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ RowErrorﺍﻟﺨﺎﺼﺔ ﺒﻜل ﺴﺠل ﻴﺘﻌﺎﺭﺽ ﻤـﻊ
ﺍﻟﻘﻴﻭﺩ ﺍﻟﻤﻔﺭﻭﻀﺔ ،ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﻓﺤﺹ ﻫﺫﻩ ﺍﻷﺨﻁﺎﺀ ﻭﺇﺼﻼﺤﻬﺎ ﺒﺎﻟﻁﺭﻴﻘﺔ ﺍﻟﻤﻨﺎﺴﺒﺔ،
٢١٦
ﻗﺒل ﻤﺤﺎﻭﻟﺔ ﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ Trueﻓـﻲ ﺍﻟﺨﺎﺼـﻴﺔ EnforceConstraintsﻤـﻥ
ﺠﺩﻴﺩ ﻟﺘﻁﺒﻴﻕ ﺍﻟﻘﻴﻭﺩ.
٢١٧
Typeﻭﺘﻌﻴﺩ ..Stringﻫﺫﺍ ﻤﻔﻴﺩ ﺇﺫﺍ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﻋﻤـﻭﺩ
ﻴﺘﻌﺎﻤل ﻤﻊ ﻨﻭﻉ ﺒﻴﺎﻨﺎﺕ ﻤﺭﻜﺏ ﻻ ﻴﻤﻜﻥ ﺘﺤﻭﻴﻠﻪ ﺇﻟﻰ ﻨﺹ ﻤﺒﺎﺸﺭﺓ ،ﻭﻫﻨﺎ ﻴﻤﻜﻨـﻙ ﻜﺘﺎﺒـﺔ
ﺩﺍﻟﺔ ﻤﻨﺎﺴﺒﺔ ﺘﻭﻀﺢ ﻜﻴﻑ ﻴﻤﻜﻥ ﺘﺤﻭﻴل ﺒﻴﺎﻨﺎﺘﻪ ﺇﻟﻰ ﻨﺹ ،ﻭﺘﺭﺴﻠﻬﺎ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل.
ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟـﺯﺭ "ﺤﻔـﻅ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻓـﻲ ﻤﻠـﻑ" ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ
،DataSetSampleﻭﺃﺭﺴﻠﻨﺎ ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤـل ﺍﻟﺜـﺎﻨﻲ ﺍﻟﻘﻴﻤـﺔ WriteSchemaﻟﺤﻔـﻅ
ﺍﻟﻤﺨﻁﻁ ﻤﻊ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻫﺫﺍ ﻴﻀﻤﻥ ﻟﻨﺎ ﺤﻔﻅ ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺠﺩﻭﻟﻴﻥ ﻭﺍﻟﻘﻴـﻭﺩ ﺍﻟﻤﻔﺭﻭﻀـﺔ
ﻋﻠﻴﻬﻤﺎ ،ﻭﺍﻟﻤﻔﺎﺘﻴﺢ ﺍﻷﺴﺎﺴﻴﺔ ﻭﺍﻟﻔﺭﻋﻴﺔ.
ﻤﻠﺤﻭﻅﺔ:
ـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴــﻴﻠﺔ
ـﻙ ﺍﺴـ
ـﻁ ،ﻓﻌﻠﻴـ
ـﻲ ﺘﻐﻴــﺭﺕ ﻓﻘـ
ـﺠﻼﺕ ﺍﻟﺘـ
ـﻅ ﺍﻟﺴـ
ﺇﺫﺍ ﺃﺭﺩﺕ ﺤﻔـ
DataSet.GetChangesﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻴﺩﺓ ﺒﻬﺎ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘـﻲ
ﺘﻐﻴﺭﺕ ﻓﻘﻁ ،ﻭﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ WriteXmlﺍﻟﺨﺎﺼﺔ ﺒﻬﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﻴﺩﺓ ﻟﺤﻔـﻅ
ﺴﺠﻼﺘﻬﺎ.
٢١٨
ﻗﺭﺍﺀﺓ ﻜﻭﺩ ﺍﻟﻤﺨﻁﻁ :ReadXmlSchema
ﻤﻤﺎﺜﻠﺔ ﻟﻠﻭﺴﻴﻠﺔ WriteXmlSchemaﻓﻲ ﻤﻌﺎﻤﻼﺘﻬﺎ ،ﻭﻟﻜﻨﻬﺎ ﺘﻘﻭﻡ ﺒﺎﻟﻭﻅﻴﻔﺔ ﺍﻟﻌﻜﺴـﻴﺔ،
ﺤﻴﺙ ﺘﻘﺭﺃ ﺍﻟﻤﺨﻁﻁ ﻤﻥ ﻤﻠﻑ XMLﻭﺘﺤﻤﻠﻪ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻻﺤـﻅ ﺃﻥ ﻫـﺫﻩ
ﺍﻟﻭﺴﻴﻠﺔ ﻗﺩ ﺘﺘﺴﺒﺏ ﻓﻲ ﺤﺩﻭﺙ ﺃﺨﻁﺎﺀ ﺇﺫﺍ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﺨﻁـﻁ
ﺒﺎﻟﻔﻌل ،ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺍﺴـﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ DataSet.Resetﺃﻭﻻ ﻟﻤﺤـﻭ ﻜـل ﺒﻴﺎﻨﺎﺘﻬـﺎ
ﻭﻤﺨﻁﻁﺎﺘﻬﺎ ﺃﻭﻻ ،ﻗﺒل ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ .ReadXmlSchema
٢١٩
ﻭﻴﺤﺩﺙ ﺨﻁﺄ ﺇﺫﺍ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﺨﻁﻁ ﺒﺎﻟﻔﻌـل،
ﻭﻜﺎﻥ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻤﻭﺩ ﺘﺘﻌﺎﺭﺽ ﺘﻔﺎﺼﻴﻠﻪ ﻤﻊ ﻋﻤـﻭﺩ ﻤﻭﺠـﻭﺩ ﻓـﻲ
ﺍﻟﻤﺨﻁﻁ ﺍﻟﻤﻀﺎﻑ.
ﻤﻤﺎﺜﻠﺔ ﻟﻠﻘﻴﻤﺔ ﺍﻟﺴﺎﺒﻘﺔ ،ﺇﻻ ﺃﻨﻬﺎ ﺘﺴﺘﻨﺞ ﻨﻭﻉ ﺒﻴﺎﻨﺎﺕ ﻜل ﻋﻤﻭﺩ ،ﻓﺈﻥ ﻓﺸﻠﺕ Infer
Typed
ﺘﻌﺘﺒﺭ ﺃﻥ ﻨﻭﻉ ﺍﻟﻌﻤﻭﺩ .String Schema
ﺘﻘﺭﺃ ﺍﻟﺴﺠﻼﺕ ﺍﻷﺼﻠﻴﺔ ﻭﺍﻟﺤﺎﻟﻴﺔ ﻤﻥ ﺍﻟﻤﻠﻑ ،ﻭﺫﻟﻙ ﺇﺫﺍ ﻜﻨﺕ ﺤﻔﻅﺘﻬﺎ ﻓﻴﻪ Diff
Gram
ﺴﺎﺒﻘﺎ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻘﻴﻤﺔ ..DiffGramﻭﺇﺫﺍ ﻜﺎﻨـﺕ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺴﺠﻼﺕ ﺒﺎﻟﻔﻌل ﻓﺴﺘﺤﺘﻔﻅ ﺒﻬﺎ ،ﻭﺴﺘﻀﺎﻑ ﺇﻟﻴﻬﺎ ﺍﻟﺴـﺠﻼﺕ
ﺍﻟﺠﺩﻴﺩﺓ.
Fragmentﺍﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻘﻴﻤﺔ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻠﻑ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺠﺯﺍﺀ ﻤﻥ ﻜﻭﺩ XML
ﻭﻟﻴﺱ ﻜﻭﺩ ﻭﺜﻴﻘﺔ ﻜﺎﻤﻠﺔ.
ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﺯﺭ "ﻗـﺭﺍﺀﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤـﻥ ﻤﻠـﻑ" ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ
،DataSetSampleﻭﺃﺭﺴﻠﻨﺎ ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤـل ﺍﻟﺜـﺎﻨﻲ ﺍﻟﻘﻴﻤـﺔ ReadSchemaﻟﻘـﺭﺍﺀﺓ
ﺍﻟﻤﺨﻁﻁ ﻤﻊ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻫﺫﺍ ﻴﻀﻤﻥ ﻟﻨﺎ ﺇﻨﺸﺎﺀ ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺠﺩﻭﻟﻴﻥ ﻓﻲ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻷﻥ ﻭﻅﻴﻔﺔ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺘﺤﺘﺎﺠﻬﺎ.
ﻻﺤﻅ ﺃﻥ ﺍﻟﻭﺴﻴﻠﺔ ReadXmlﻻ ﺘﺴﺘﺩﻋﻲ ﺍﻟﻭﺴـﻴﻠﺔ AcceptChangesﺘﻠﻘﺎﺌﻴـﺎ ﻜﻤـﺎ
ﺘﻔﻌل ﺍﻟﻭﺴﻴﻠﺔ ،DataAdapter.Fillﻟﻬﺫﺍ ﻓﺈﻥ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﻴﺘﻡ ﺘﺤﻤﻴﻠﻬﺎ ﻓﻲ ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﺴﺘﻌﺘﺒﺭ ﺴﺠﻼﺕ ﺠﺩﻴﺩﺓ ،Addeddﻭﻟﻭ ﻀﻐﻁﺕ ﺯﺭ ﺍﻟﺤﻔﻅ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ،
ﻓﺴﻴﺘﻡ ﺇﻀﺎﻓﺔ ﻜل ﻫﺫﻩ ﺍﻟﺴﺠﻼﺕ ﻤﺭﺓ ﺃﺨﺭﻯ ﺇﻟﻰ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺠﺩﻭل ﺍﻟﻜﺘﺏ ،ﻭﻫـﺫﺍ
ﺴﻴﺠﻌل ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤﻜـﺭﺭﺓ! ..ﻭﻟﺤـل ﻫـﺫﻩ ﺍﻟﻤﺸـﻜﻠﺔ ،ﻋﻠﻴـﻙ ﺍﺴـﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ
AcceptChangesﻤﺒﺎﺸﺭﺓ ﺒﻌﺩ ﺘﺤﻤﻴل ﺍﻟﺴﺠﻼﺕ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺒﻬـﺫﺍ ﻴـﺘﻡ
ﺍﻋﺘﺒﺎﺭ ﺃﻨﻬﺎ ﻟﻡ ﺘﺘﻐﻴﺭ ،ﻭﻻ ﻴﺘﻡ ﺤﻔﻅﻬﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻟﻜﻨﻙ ﻗﺩ ﺘﺭﻴﺩ ﺍﻋﺘﺒﺎﺭ ﺍﻟﺴﺠﻼﺕ ﺠﺩﻴﺩﺓ ﻓﻲ ﺒﻌﺽ ﺍﻟﻤﻭﺍﻗﻑ ،ﻭﺫﻟﻙ ﺇﺫﺍ ﻜﻨﺕ ﺘﻤﻠﻙ ﺍﻟﺒﻴﺎﻨﺎﺕ
ﻓﻲ ﻤﻠﻑ XMLﻭﺘﺭﻴﺩ ﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻓﺎﺭﻏﺔ.
٢٢٠
ﻭﻫﻨﺎﻙ ﻤﻼﺤﻅﺔ ﺒﺴﻴﻁﺔ ﺃﺨﺭﻯ ،ﻭﻫﻲ ﺃﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻻ ﻴﻬﻤﻬﺎ ﺍﻤﺘﺩﺍﺩ ﺍﻟﻤﻠﻑ ،ﺒل ﻴﻬﻤﻬـﺎ
ﻓﻘﻁ ﺼﺤﺔ ﻤﺤﺘﻭﻴﺎﺘﻪ ..ﻟﻬﺫﺍ ﻓﻘـﺩ ﺃﻋﻁﻴﻨـﺎ ﻟﻠﻤﻠﻔـﺎﺕ ﺍﻟﺨﺎﺼـﺔ ﺒﻨـﺎ ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ
CustomDataSetﺍﻻﻤﺘﺩﺍﺩ ،.dsfﻭﻫﻲ ﺍﻤﺘﺩﺍﺩ ﻤـﻥ ﺍﺨﺘﺭﺍﻋﻨـﺎ )ﺍﺨﺘﺼـﺎﺭ ﻟﻠﺘﻌﺒﻴـﺭ
،(DataSet Formatﻭﺠﻌﻠﻨﺎ ﻤﺭﺒﻊ ﺤﻭﺍﺭ ﻓﺘﺢ ﻤﻠﻑ ﻻ ﻴﻌﺭﺽ ﺴﻭﻯ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺘﻲ ﻟﻬـﺎ
ﻫﺫﺍ ﺍﻻﻤﺘﺩﺍﺩ ،ﻭﺒﻬﺫﺍ ﻨﻀﻤﻥ ﺃﻥ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺘﻲ ﻨﺤﺎﻭل ﻗﺭﺍﺀﺘﻬﺎ ﺴﻴﻜﻭﻥ ﻟﻬﺎ ﺍﻟﺼﻴﻐﺔ ﺍﻟﻤﻨﺎﺴﺒﺔ
ﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﻤﻠﻔﺎﺕ XMLﺘﺴﺘﻁﻴﻊ ﺤﻤل ﺃﻱ ﻨﻭﻉ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺒـﺄﻱ ﺘﻨﺴـﻴﻕ،
ﻟﻜﻨﻬﺎ ﻟﻥ ﺘﻜﻭﻥ ﺠﻤﻴﻌﺎ ﺼﺎﻟﺤﺔ ﻟﻠﻌﺭﺽ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻨﺎ.
٢٢١
ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﻹﻨﺸﺎﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ
Generate DataSet Wezard
ﺘﺘﻴﺢ ﻟﻙ ﺩﻭﺕ ﻨﺕ ﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ﻹﻨﺸﺎﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺁﻟﻴﺎ ..ﻟﻔﻌل ﻫـﺫﺍ ،ﺃﻀـﻑ ﻤﻬﻴـﺊ
ﺒﻴﺎﻨﺎﺕ Data Adapterﺇﻟﻰ ﺼﻴﻨﻴﺔ ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤﻭﺫﺝ ،ﻭﺍﻀﺒﻁ ﺨﺼﺎﺌﺼﻪ ﻜﻤﺎ ﺘﻌﻠﻤﻨـﺎ ﻤـﻥ
ﻗﺒل ،ﺜﻡ ﺍﻀﻐﻁﻪ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤﺭ "ﺇﻨﺘـﺎﺝ ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ" ..Generate Datasetﻭﺴﺘﺠﺩ ﻨﻔﺱ ﺍﻷﻤﺭ ﻓﻲ ﺍﻟﻘﺎﺌﻤـﺔ ﺍﻟﺭﺌﻴﺴـﻴﺔ Dataﺃﻋﻠـﻰ
ﺍﻟﻨﺎﻓﺫﺓ.
ﺴﻴﻅﻬﺭ ﻟﻙ ﻤﺭﺒﻊ ﺤﻭﺍﺭ "ﺇﻨﺘﺎﺝ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ" ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﺒﺎﻟﺼﻭﺭﺓ:
ﻓﻲ ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﻴﻤﻜﻨﻙ ﺍﺨﺘﻴﺎﺭ ﺇﻨﺸـﺎﺀ
ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻤﺨﻁﻁ ﻤﺠﻤﻭﻋﺔ
ﺒﻴﺎﻨﺎﺕ ﻤﻭﺠﻭﺩ ﺴﺎﺒﻘﺎ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﺃﻭ
ﺇﻨﺸــﺎﺀ ﻤﺨﻁــﻁ ﺠﺩﻴــﺩ ﺍﺴــﻤﻪ
..DataSet1ﻻﺤﻅ ﺃﻨـﻙ ﺘﺴـﺘﻁﻴﻊ
ﺘﻐﻴﻴﺭ ﻫﺫﺍ ﺍﻻﺴﻡ ،ﻭﺍﻷﻓﻀل ﺍﺨﺘﻴـﺎﺭ
ﺍﺴﻡ ﺃﻜﺜﺭ ﺘﻌﺒﻴﺭﺍ ﻋﻥ ﻭﻅﻴﻔﺔ ﻤﺠﻤﻭﻋﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺘﻌﺭﺽ ﻟﻙ ﺍﻟﻨﺎﻓﺫﺓ ﻗﺎﺌﻤـﺔ ﺒﺄﺴـﻤﺎﺀ
ﺍﻟﺠﺩﺍﻭل ﺍﻟﺘﻲ ﻴﻭﻓﺭﻫﺎ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ،
ﻟﻴﻤﻜﻨﻙ ﺍﺨﺘﻴﺎﺭ ﺇﻀﺎﻓﺘﻬﺎ ﺠﻤﻴﻌﺎ ﺇﻟـﻰ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻭ ﺤﺫﻑ ﺒﻌﻀﻬﺎ.
ﻭﻴﻭﺠﺩ ﺍﺨﺘﻴﺎﺭ ﺃﺴﻔل ﺍﻟﻨﺎﻓﺫﺓ ،ﻴﺤﺩﺩ ﺇﺫﺍ ﻜﻨﺕ ﺘﺭﻴﺩ ﺇﻀﺎﻓﺔ ﻨﺴﺨﺔ ﻤﻥ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺇﻟـﻰ
ﺍﻟﻨﻤﻭﺫﺝ ﺃﻡ ﻻ.
ﺒﻌﺩ ﺃﻥ ﺘﺤﺩﺩ ﺍﺨﺘﻴﺎﺭﺍﺘﻙ ﺍﻀﻐﻁ Okﻹﻏﻼﻕ ﺍﻟﻨﺎﻓﺫﺓ ..ﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻟﻰ ﻤﺎ ﻴﻠﻲ:
-ﺇﻀﺎﻓﺔ ﻤﻠﻑﹼ ﺍﺴﻤﻪ DataSet1.xsdﺇﻟﻰ ﻤﻠﻔﺎﺕ ﺍﻟﻤﺸﺭﻭﻉ ﺍﻟﺘـﻲ ﻴﻌﺭﻀـﻬﺎ ﻤﺘﺼـﻔﺢ
ﺍﻟﻤﺸﺎﺭﻴﻊ ..Solution Explorerﻭﺍﻻﻤﺘﺩﺍﺩ xsdﻫﻭ ﺍﺨﺘﺼﺎﺭ ﻟﻠﺘﻌﺒﻴﺭ "ﻟﻐﺔ ﺘﻌﺭﻴـﻑ
٢٢٢
ﺍﻟﻤﺨﻁﻁ" ،Xml Schema Definitionﻟﻬﺫﺍ ﻟﻭ ﻓﺘﺤﺕ ﻫـﺫﺍ ﺍﻟﻤﻠـﻑ ﻤـﻥ ﻤﺠﻠـﺩ
ﺍﻟﻤﺸﺭﻭﻉ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺒﺭﻨﺎﻤﺞ ،Notepadﻓﺴﺘﺠﺩﻩ ﻴﺤﺘﻭﻱ ﻋﻠـﻰ ﻜـﻭﺩ XMLﺍﻟـﺫﻱ
ﻴﻌﺭﻑ ﻤﺨﻁﻁ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ )ﺍﻟﺠـﺩﺍﻭل ﻭﺍﻷﻋﻤـﺩﺓ ﻭﺍﻟﻌﻼﻗـﺎﺕ ﻭﺍﻟﻘﻴـﻭﺩ ﺍﻟﺘـﻲ
ﺘﺤﺘﻭﻴﻬﺎ( ..ﺃﻤﺎ ﻟﻭ ﻨﻘﺭﺕ ﻫﺫﺍ ﺍﻟﻤﻠﻑ ﻤﺭﺘﻴﻥ ﺒﺎﻟﻔﺄﺭﺓ ﻓﻲ ﻤﺘﺼﻔﺢ ﺍﻟﻤﺸﺎﺭﻴﻊ ،ﻓﺴـﺘﻌﺭﺽ
ﻟﻙ ﺩﻭﺕ ﻨﺕ ﻨﺎﻓﺫﺓ ﻤﺼﻤﻡ ﺍﻟﻤﺨﻁﻁ ،Schema Designerﻭﺴﺘﺠﺩ ﻓﻴﻬﺎ ﺭﺴﻤﺎ ﻤﺒﺴﻁﺎ
ﻴﻤﺜل ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﻤﺨﻁﻁ ،ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﺒﺎﻟﺼﻭﺭﺓ:
٢٢٣
-ﺇﻀﺎﻓﺔ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺍﺴـﻤﻬﺎ DataSet11ﺇﻟـﻰ ﺼـﻴﻨﻴﺔ ﻤﻜﻭﻨـﺎﺕ ﺍﻟﻨﻤـﻭﺫﺝ
..Component Trayﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﻫﻲ ﻨﺴﺨﺔ ﻤﻌﺭﻓـﺔ ﻤـﻥ ﺍﻟﻔﺌـﺔ ،DataSet1
ﻭﺴﺘﺠﺩ ﺠﻤﻠﺔ ﺘﻌﺭﻴﻔﻬﺎ ﻓﻲ ﻤﻠﻑ ﺨﺼﺎﺌﺹ ﺍﻟﻨﻤﻭﺫﺝ ﻜﺎﻟﺘﺎﻟﻲ:
;internal DataSet1 DataSet11
ﻻﺤﻅ ﺃﻥ ﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ DataSet11ﻴﺸﻴﺭ ﺇﻟﻰ ﺃﻥ ﻫﺫﻩ ﻫﻲ ﺍﻟﻨﺴﺨﺔ ﺭﻗﻡ ١ﻤـﻥ
ﺍﻟﻔﺌــﺔ ..dataSet1ﻭﻟــﻭ ﻜﻨــﺕ ﺴــﻤﻴﺕ ﻤﺠﻤﻭﻋــﺔ ﺍﻟﺒﻴﺎﻨــﺎﺕ ﻤﻨــﺫ ﺍﻟﺒﺩﺍﻴــﺔ
DsAuthorBooksﻤﺜﻼ ،ﻟﻜﺎﻥ ﺍﺴﻡ ﻫﺫﻩ ﺍﻟﻨﺴﺨﺔ ﻫـﻭ DsAuthorBooks1ﺒـﺩﻻ
ﻤﻥ .DataSet11
-ﻅﻬﻭﺭ ﺃﺩﺍﺓ ﺠﺩﻴﺩﺓ ﺍﺴﻤﻬﺎ DataSet1ﻓﻲ ﺃﻋﻠﻰ ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ Toolboxﺘﺤـﺕ
ﺸﺭﻴﻁ ﺨﺎﺹ ﻴﺤﻤل ﺍﻻﺴﻡ:
ProjectName Components
ﺤﻴﺙ ProjectNameﻫﻭ ﺍﺴﻡ ﺍﻟﻤﺸﺭﻭﻉ.
ﻭﺒﻬﺫﺍ ﺘﺴﺘﻁﻴﻊ ﺇﻀﺎﻓﺔ ﻨﺴﺦ ﻤﻨﻬﺎ ﺇﻟﻰ ﺍﻟﻨﻤﻭﺫﺝ ﺒﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ.
ﻭﺍﻟﻤﺸﺭﻭﻉ TypedDataSetﻴﺭﻴﻙ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫﺫﺍ.
ﺩﻋﻨﺎ ﻨﺭ ﻤﺎﺫﺍ ﻓﻌﻠﻨﺎ ﺤﺘﻰ ﻫﺫﻩ ﺍﻟﻠﺤﻅﺔ ..ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺭﺌﻴﺴﻴﺔ ،Dataﺍﻀﻐﻁ ﺍﻷﻤﺭ Preview
) Dataﺴﺘﺠﺩ ﻫﺫﺍ ﺍﻷﻤﺭ ﺃﻴﻀﺎ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﻋﻨﺩ ﻀﻐﻁ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓـﻲ ﺼـﻴﻨﻴﺔ
ﺍﻟﻤﻜ ﻭﻨﺎﺕ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ( ..ﻫﺫﺍ ﺍﻷﻤﺭ ﺴﻴﻔﺘﺢ ﻨﺎﻓﺫﺓ ﺍﺴﺘﻌﺭﺍﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ:
ﺃﻋﻠﻰ ﻴﺴﺎﺭ ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ،ﺘﻭﺠﺩ ﻗﺎﺌﻤﺘﺎﻥ ﻤﻨﺴﺩﻟﺘﺎﻥ ،ﺘﺘﻴﺤﺎﻥ ﻟﻙ ﺘﺤﺩﻴﺩ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﺴﺘﻌﺭﺍﺽ ﺒﻴﺎﻨﺎﺘﻬﻤﺎ ،ﻭﺃﻋﻠﻰ ﺍﻟﻴﻤﻴﻥ ﺴﺘﺠﺩ ﺠﺩﻭﻻ ﻴﻌﺭﺽ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘـﻲ
ﺘﻡ ﺘﻌﺭﻴﻔﻬﺎ ﻓﻲ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ﻭﺍﻟﺘﺤﺩﻴﺙ ﺇﻥ ﻭﺠﺩﺕ ..ﺃﻤﺎ ﺍﻟﺠﺯﺀ ﺍﻟﺴﻔﻠﻲ ﻤﻥ ﺍﻟﻨﺎﻓﺫﺓ ،ﻓﻴﻌـﺭﺽ
ﺍﻟﺴﺠﻼﺕ ﺍﻟﻨﺎﺘﺠﺔ ﻤﻥ ﺘﻨﻔﻴﺫ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ،ﻭﻫﻭ ﺴﻴﻜﻭﻥ ﻓﺎﺭﻏﺔ ﻤﺒﺩﺌ ﻴﺎ ،ﺇﻟﻰ ﺃﻥ ﺘﻀـﻐﻁ ﺍﻟـﺯﺭ
.Preview
٢٢٤
٢٢٥
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ :Typed DataSet
ﺭﺃﻴﻨﺎ ﻜﻴﻑ ﻗﺎﻤﺕ ﺩﻭﺕ ﻨﺕ ﺒﺈﻨﺸﺎﺀ ﻓﺌﺔ ﺍﺴـﻤﻬﺎ DataSet1ﺁﻟﻴـﺎ ﺍﻋﺘﻤـﺎﺩﺍ ﻋﻠـﻰ ﺍﻟﻤﺨﻁـﻁ
ـﻭﻉ
ـﺩﺩﺓ ﺍﻟﻨـ
ـﺎﺕ ﻤﺤـ
ـﺔ ﺍﻟﺒﻴﺎﻨـ
ـﻡ ﻤﺠﻤﻭﻋـ
ـﺔ ﺒﺎﺴـ
ـﺫﻩ ﺍﻟﻔﺌـ
ـﻤﻰ ﻫـ
..DataSet1.xsdﻭﺘﺴـ
،Typed DataSetﻭﺫﻟﻙ ﻷﻨﻬﺎ ﺘﻘﻭﻡ ﺒﺘﻌﺭﻴﻑ ﺃﻨﻭﺍﻉ ﺨﺎﺼﺔ ﻟﺠـﺩﺍﻭل ﻭﺼـﻔﻭﻑ ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺘﺴﻤﺢ ﻟﻙ ﺒﺎﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺒﺄﺴﻤﺎﺌﻬﺎ ﻤﺒﺎﺸﺭﺓ ..ﻭﻟﻜﻲ ﻴﺤـﺩﺙ ﻫـﺫﺍ،
ﺘﻘﻭﻡ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺒﺘﻌﺭﻴﻑ ﺍﻟﻌﺩﻴﺩ ﻤﻥ ﺍﻟﻌﻨﺎﺼﺭ ..ﻭﻟﻭ ﻓﺘﺤﺕ ﺍﻟﻤﻠـﻑ DataSet1.Designer.cs
ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ TypedDataSetﻓﺴﺘﺠﺩ ﻓﻴﻪ ﺘﻌﺭﻴﻑ ﺍﻟﻔﺌﺔ ،DataSet1ﻭﺴﺘﺠﺩ ﻓﻴﻬﺎ ﺍﻟﻌﻨﺎﺼﺭ
ﺍﻟﺘﺎﻟﻴﺔ:
-١ﻓﺌﺔ ﺨﺎﺼﺔ ﻟﻜل ﺼﻑ ﻓﻲ ﻜل ﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻫﺫﻩ ﺍﻟﻔﺌﺎﺕ ﺘﺤﻤل ﺃﺴـﻤﺎﺀ
ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ،XRowﺤﻴﺙ Xﻫﻭ ﺍﺴﻡ ﺍﻟﺠﺩﻭل.
ﻭﺘﺭﺙ ﻓﺌﺔ ﺍﻟﺼﻑ ﻓﺌﺔ ﺼﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﻡ ،DataRowﻭﺒﺩﺍﺨل ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻴﺘﻡ ﺘﻌﺭﻴـﻑ
ﺨﺎﺼﻴﺔ ﺒﺎﺴﻡ ﻜل ﻋﻤﻭﺩ ﻤﻥ ﺃﻋﻤﺩﺓ ﺍﻟﺠﺩﻭل ،ﺘﻌﻴﺩ ﻗﻴﻤﺔ ﺍﻟﺨﺎﻨﺔ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ
ﻓﻲ ﻫﺫﺍ ﺍﻟﺼﻑ ..ﻓﻤﺜﻼ ،ﺴﺘﺠﺩ ﺩﺍﺨل ﺍﻟﻔﺌﺔ DataSet1ﻓﺌـﺔ ﺍﺴـﻤﻬﺎ AuthorsRow
ﺘﻤﺜل ﺼﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﻭﺴﺘﺠﺩ ﺒﺩﺍﺨﻠﻬﺎ ﺨﺎﺼـﻴﺘﻴﻥ ﻫﻤـﺎAuthor :
ﻭ Bookﺘﻌﻴﺩﺍﻥ ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ﻭﺍﺴﻡ ﺍﻟﻜﺘﺎﺏ ﻓﻲ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ.
-٢ﻓﺌﺔ ﻟﻜل ﺠﺩﻭل ﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻫﺫﻩ ﺍﻟﻔﺌﺎﺕ ﺘﺤﻤل ﺃﺴﻤﺎﺀ ﻋﻠﻰ ﺍﻟﺼـﻴﻐﺔ
،XDataTableﺤﻴﺙ Xﻫﻭ ﺍﺴﻡ ﺍﻟﺠﺩﻭل.
ﻭﺘﺭﺙ ﻓﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ﺍﻟﻔﺌﺔ ﻋﺎﻤـﺔ ﺍﻟﻨـﻭﻉ <TypedTableBase<Tﻭﺍﻟﺘـﻲ ﺘـﺭﺙ
ﺒﺩﻭﺭﻫﺎ ﻓﺌﺔ ﺍﻟﺠﺩﻭل ،DataTableﺤﻴﺙ Tﻫﻭ ﻨﻭﻉ ﺼﻔﻭﻑ ﺍﻟﺠﺩﻭل.
ﻓﻤﺜﻼ ،ﺴﺘﺠﺩ ﺩﺍﺨل ﺍﻟﻔﺌﺔ DataSet1ﻓﺌﺔ ﺍﺴﻤﻬﺎ AuthorsDataTableﺘﻤﺜـل ﺠـﺩﻭل
ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﻭﻫﻲ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ).TypedTableBase(Of AuthorsRow
ﻭﺒﺩﺍﺨل ﻓﺌﺔ ﺍﻟﺠﺩﻭل ،ﻴﺘﻡ ﺘﻌﺭﻴﻑ ﺨﺼﺎﺌﺹ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻜل ﻋﻤﻭﺩ ﺒﺎﻟﺠﺩﻭل ،ﻭﻫﻲ ﺘﻌﻴﺩ
ﻜﺎﺌﻨﺎﺕ ﻤﻥ ﻨﻭﻉ ﻓﺌﺔ ﺍﻟﻌﻤﻭﺩ ..DataColumn Classﻓﻤﺜﻼ ،ﺴـﺘﺠﺩ ﻓـﻲ ﺍﻟﺠـﺩﻭل
ـﻴﺘﻴﻥ AuthorColumnﻭ BookColumnﺍﻟﻠﺘــﻴﻥ
AuthorsDataTableﺍﻟﺨﺎﺼـ
ﺘﺘﻴﺤﺎﻥ ﻟﻙ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻋﻤﻭﺩﻱ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺍﻟﻜﺘﺏ.
٢٢٦
ﻜﻤﺎ ﻴﺘﻡ ﺘﻌﺭﻴﻑ ﻋﺩﺓ ﺃﺤﺩﺍﺙ ﻟﻔﺌﺔ ﺍﻟﺠﺩﻭل ﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌـﺔ ،DataTable
ﻭﻫﻲ:
-ﺍﻟﺼﻑ ﻴﺘﻐﻴﺭ .XRowChanging
-ﺍﻟﺼﻑ ﺘﻐﻴﺭ .XRowChanged
-ﺍﻟﺼﻑ ﻴﺤﺫﻑ .XRowDeleting
-ﺍﻟﺼﻑ ﺤﺫﻑ .XRowDeleted
ﺤﻴﺙ Xﻫﻭ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ..ﻓﻤﺜﻼ :ﻓﻲ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻴﺘﻡ ﺘﻌﺭﻴﻑ ﺍﻷﺤﺩﺍﺙ ﺍﻟﺘﺎﻟﻴﺔ:
AuthorsRowChanging, AuthorsRowChanged,
AuthorsRowDeleting, AuthorsRowDeleted.
-٣ﻋﺩﺓ ﺨﺼﺎﺌﺹ ﻋﻠﻰ ﻤﺴﺘﻭﻯ ﺍﻟﻔﺌﺔ DataSet1ﺘﺤﻤل ﺃﺴﻤﺎﺀ ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ،
ﻟﺘﺘﻴﺢ ﻟﻙ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻜﺎﺌﻥ ﻤﻥ ﻨﻭﻉ ﻓﺌﺔ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ..ﻓﻤـﺜﻼ ،ﺴـﺘﺠﺩ ﻓـﻲ ﺍﻟﻔﺌـﺔ
DataSet1ﺨﺎﺼﻴﺔ ﺍﺴﻤﻬﺎ ،Authorsﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﻔﺌﺔ ،AuthorsDataTable
ﻭﻴﻤﻜﻨﻙ ﻤﻥ ﺨﻼﻟﻬﺎ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ.
٢٢٧
ﻜﻨﺼﻭﺹ ،ﻭﻻ ﻴﺘﻡ ﺍﻜﺘﺸﺎﻑ ﺃﻱ ﺨﻁﺄ ﻓﻴﻬﻤﺎ ﺇﻻ ﺃﺜﻨﺎﺀ ﺘﺸﻐﻴل ﺍﻟﺒﺭﻨـﺎﻤﺞ ..ﺃﻤـﺎ ﻓـﻲ
ﺍﻟﺠﻤﻠﺔ ﺍﻟﺜﺎﻨﻴﺔ )ﺍﻟﻘﺼﻴﺭﺓ( ،ﻓﺈﻨﻙ ﺘﺘﻌﺎﻤل ﻤﻊ ﺨﺼـﺎﺌﺹ ﻤﻌﺭﻓـﺔ ﺴـﺎﺒﻘﺎ ﻓـﻲ ﺍﻟﻔﺌـﺔ
،DataSet1ﻭﻟﻥ ﻴﻘﺒل ﻤﺤﺭﺭ ﺍﻟﻜﻭﺩ ﺃﻱ ﺨﻁﺄ ﻓﻲ ﺃﺴﻤﺎﺌﻬﺎ ،ﻤﻤـﺎ ﻴﻌﻨـﻲ ﺍﻨﻌـﺩﺍﻡ ﺃﻱ
ﻓﺭﺼﺔ ﻟﻠﺨﻁﺄ.
-٣ﻻ ﺘﺤﺘﺎﺝ ﻋﻨﺩ ﻜﺘﺎﺒﺘﻬﺎ ﺇﻟﻰ ﺘﺫﻜﺭ ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺒﻨﻔﺴﻙ ،ﻭﻫﻭ ﺃﻤﺭ ﺘﺘﻀـﺢ
ﺃﻫﻤﻴﺘﻪ ﻓﻲ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻀﺨﻤﺔ ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠـﻰ ﻋﺸـﺭﺍﺕ ﺍﻟﺠـﺩﺍﻭل ،ﺍﻟﺘـﻲ
ﻴﺤﺘﻭﻱ ﻜل ﻤﻨﻬﺎ ﻋﻠﻰ ﻋﺸﺭﺍﺕ ﺍﻷﻋﻤﺩﺓ ،ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻨﻙ ﺴﺘﻀﻴﻊ ﺍﻟﻜﺜﻴﺭ ﻤﻥ ﺍﻟﻭﻗﺕ ﻟﻭ
ﺍﺴﺘﺨﺩﻤﺕ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻋﺎﺩﻴﺔ ،ﻷﻨﻙ ﺴﺘﻀﻁﺭ ﺇﻟﻰ ﺍﻟﻌﻭﺩﺓ ﺇﻟﻰ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻜﺜﻴﺭﺍ ﻟﺘﺫﻜﺭ ﺃﺴﻤﺎﺀ ﻋﻨﺎﺼﺭﻫﺎ ..ﺒﻴﻨﻤﺎ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﺘﺠﻌـل ﺍﻟﺤﻴـﺎﺓ
ﺠﻨﺔ ،ﻷﻥ ﺍﻻﺴﺘﺸﻌﺎﺭ ﺍﻟﺫﻜﻲ IntilliSenseﺴﻴﻌﺭﺽ ﻟﻙ ﻗﺎﺌﻤﺔ ﺍﻷﺴﻤﺎﺀ ﺒﻤﺠﺭﺩ ﻜﺘﺎﺒﺔ
ﺍﻟﻨﻘﻁﺔ .ﻟﺘﺨﺘﺎﺭ ﻤﻨﻬﺎ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺃﻭ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻪ.
ﻜل ﻫﺫﺍ ﻴﻭﻀﺢ ﻟﻙ ﻓﻭﺍﺌﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ،ﻭﻜﻴﻑ ﺘﺨﺘﺼﺭ ﻭﺘﺴﻬل ﻜﺘﺎﺒﺔ ﺍﻟﻜﻭﺩ
ﺒﺸﻜل ﻜﺒﻴﺭ.
ﻭﺍﻟﻤﺸﺭﻭﻉ DataSetContentsﻴﺭﻴﻙ ﻜﻴﻑ ﻴﻤﻜﻥ ﻋﺭﺽ ﻜل ﺠﺩﺍﻭل ﻭﻋﻼﻗـﺎﺕ ﻭﺒﻴﺎﻨـﺎﺕ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ،ﺒﺎﻟﻁﺭﻴﻘﺔ ﺍﻟﻤﻭﻀﺤﺔ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
٢٢٨
ﻤﻠﺤﻭﻅﺔ:
ﻴﻤﻜﻨﻙ ﺍﺴﺘﻌﺎﺭﺓ ﻤﺨﻁﻁ XMLﻤﻥ ﻤﺸﺭﻭﻉ ﺁﺨﺭ ،ﻭﺇﻨﺸﺎﺀ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﺒﻨﺎﺀ ﻋﻠﻴﻪ..
ﻟﻔﻌل ﻫﺫﺍ ،ﺍﺘﺒﻊ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ:
ﺃﻨﺸﺊ ﻤﺸﺭﻭﻋﺎ ﺠﺩﻴﺩﺍ. -
ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻌﻠﻭﻴﺔ ،Projectﺍﻀﻐﻁ ﺍﻷﻤﺭ .Add Existing Item -
ﺍﺴﺘﺨﺩﻡ ﻤﺭﺒﻊ ﺤﻭﺍﺭ ﻓﺘﺢ ﻤﻠﻑ ﻟﻠﻭﺼل ﺇﻟﻰ ﻤﺠﻠﺩ ﺍﻟﻤﺸـﺭﻭﻉ ،DataSetContentsﻭﺍﺨﺘـﺭ -
ﺍﻟﻤﻠﻑ ..DsAuthorsBooks.xsdﺴﻴﻀﺎﻑ ﻫﺫﺍ ﺍﻟﻤﻠﻑ ﺇﻟﻰ ﺍﻟﻤﺸﺭﻭﻉ.
ﺍﻋﺭﺽ ﺍﻟﻨﻤﻭﺫﺝ ،ﻭﺍﻓﺘﺢ ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ ،ﻭﺃﻀﻑ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺇﻟـﻰ ﺍﻟﻨﻤـﻭﺫﺝ ..ﻭﻓـﻲ -
ﻤﺭﺒﻊ ﺍﻟﺤﻭﺍﺭ ﺍﻟﺫﻱ ﺴﻴﻅﻬﺭ ﺍﺨﺘﺭ ..Typed DataSetﺴﺘﺠﺩ ﺃﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴـﺩﻟﺔ ﺘﻌـﺭﺽ
ﺍﻟﻌﻨﺼﺭ ،X.DsAuthorsBooksﺤﻴﺙ Xﻫﻭ ﺍﺴﻡ ﺍﻟﻤﺸﺭﻭﻉ ..ﺍﻀﻐﻁ .OK
ﺴﺘﻀﺎﻑ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺍﺴﻤﻬﺎ DsAuthorsBooks1ﺇﻟﻰ ﺼـﻴﻨﻴﺔ ﺍﻟﻤﻜﻭﻨـﺎﺕ ..ﻴﻤﻜﻨـﻙ -
ﺍﺴﺘﺨﺩﺍﻡ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ﻟﺘﻐﻴﻴﺭ ﺍﺴﻤﻬﺎ ﺇﻟﻰ ﺃﻱ ﺍﺴﻡ ﻤﻨﺎﺴﺏ ،ﻭﻟﻴﻜﻥ .Ds
ﺍﻀﻐﻁ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀـﻐﻁ ﺍﻷﻤـﺭ EditIin -
..DataSet Designerﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻟﻰ ﻓﺘﺢ ﻤﺨﻁﻁ ،XMLﻭﺴﺘﺠﺩ ﻓﻴﻪ ﻤﺨﻁـﻁ ﺠـﺩﻭل
ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﻭﻤﺨﻁﻁ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ،ﻭﺍﻟﻌﻼﻗﺔ ﺒﻴﻨﻬﻤﺎ.
٢٢٩
ﺇﻨﺸﺎﺀ ﻤﺠﻤﻭﻋﺎﺕ ﺒﻴﺎﻨﺎﺕ ﺨﺎﺼﺔ :Custom DataSet
ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﻘﻁﻊ ﺴﻨﻨﺸﺊ ﻤﺠﻤﻭﻋﺎﺕ ﺒﻴﺎﻨﺎﺕ ﺒﺩﻭﻥ ﺘﺤﻤﻴل ﺃﻴﺔ ﺘﻔﺎﺼﻴل ﻤﻥ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ..
ﺴﻨﻨﺸﺌﻬﺎ ﺒﺎﺴﺘﺨﺩﺍﻡ ﻤﺨﻁﻁ ،XMLﻭﺴﻨﺭﺒﻁﻬﺎ ﺒﺠﺩﻭل ﻋـﺭﺽ DataGridViewﺒﺤﻴـﺙ
ﻴﺴﺘﻁﻴﻊ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺇﺩﺨﺎل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻬﺎ ،ﻭﺴﻨﺴﻤﺢ ﻟﻪ ﺒﺤﻔﻅ ﻫﺫﻩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓـﻲ ﻤﻠـﻑﹼ ،XML
ﻭﺇﻋﺎﺩﺓ ﺘﺤﻤﻴﻠﻬﺎ ﺒﻌﺩ ﺫﻟﻙ ﻜﻤﺎ ﻴﺸﺎﺀ.
ﺍﺒﺩﺃ ﻤﺸﺭﻭﻋﺎ ﺠﺩﻴﺩﺍ ﺍﺴﻤﻪ ،CustomDataSetﻭﻤﻥ ﺍﻟﻘﺎﺌﻤـﺔ ﺍﻟﺭﺌﻴﺴـﻴﺔ Projectﺍﻀـﻐﻁ
ﺍﻷﻤﺭ Add New Itemﻟﻌﺭﺽ ﻨﺎﻓﺫﺓ ﺇﻀﺎﻓﺔ ﻋﻨﺼﺭ ..ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﺴﺭﻯ ﺍﺨﺘﺭ ﺍﻟﻌﻨﺼـﺭ
،Dataﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﻤﻨﻰ ﺍﺨﺘﺭ ﺍﻟﻌﻨﺼﺭ ،DataSetﻭﺤﺩﺩ ﺍﺴﻤﺎ ﻟﻬﺫﺍ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺠﺩﻴﺩ ﻭﻟﻴﻜﻥ
،MyDataSetﻭﺍﻀﻐﻁ ﺍﻟﺯﺭ.OK
ﺴﻴﻀﺎﻑ ﻤﺨﻁﹼﹼﻁ XMLﺇﻟﻰ ﺍﻟﻤﺸﺭﻭﻉ ﺍﺴﻤﻪ ..MyDataSet.xsdﺍﻨﻘﺭﻩ ﻤـﺭﺘﻴﻥ ﺒﺎﻟﻔـﺄﺭﺓ
ﻟﻌﺭﺽ ﻤﺼﻤﻡ ﺍﻟﻤﺨﻁﻁ.
ﻟﻭ ﻓﺘﺤﺕ ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ ﺍﻵﻥ ،ﻓﺴﺘﺠﺩ ﺒﻪ ﺃﺩﻭﺍﺕ ﺘﻨﺎﺴﺏ ﻤﺨﻁﻁ ،XMLﻭﺴﺘﻜﻭﻥ ﻤﺒﻭﺒـﺔ
ﺘﺤﺕ ﺍﻟﺸﺭﻴﻁ ..DataSetﺍﻨﻘﺭ ﻤﺭﺘﻴﻥ ﺒﺎﻟﻔﺄﺭﺓ ﻋﻠﻰ ﺍﻟﻌﻨﺼﺭ DataTableﻹﻀـﺎﻓﺔ ﺠـﺩﻭل
ﻁﹼﻁ ..ﻫﺫﺍ ﺍﻟﺠﺩﻭل ﺴﻴﻅﻬﺭ ﻋﻠﻰ ﺍﻟﻤﺨﻁﹼﹼﻁ ﻓﻲ ﺼﻭﺭﺓ ﻤﺴﺘﻁﻴل ﻓـﺎﺭﻍ ،ﻴﺤﻤـل
ﺠﺩﻴﺩ ﺇﻟﻰ ﺍﻟﻤﺨ ﹼ
ﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ ..DataTable1 ﻟﺘﻐﻴﺭ ﻫﺫﺍ ﺍﻻﺴﻡ ،ﺍﻀـﻐﻁﻪ ﺒﺎﻟﻔـﺄﺭﺓ ﻹﻅﻬـﺎﺭ ﻤﺭﺒـﻊ
ﺍﻟﺘﺤﺭﻴﺭ ،ﻭﺍﻜﺘﺏ ﺍﻻﺴﻡ ﺍﻟﺠﺩﻴﺩ ،Studentsﺜﻡ ﺍﻀﻐﻁ ..Enterﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺍﺴـﺘﺨﺩﺍﻡ ﻨﺎﻓـﺫﺓ
ﺍﻟﺨﺼﺎﺌﺹ ﻟﺘﻐﻴﻴﺭ ﺍﺴﻡ ﺍﻟﺠﺩﻭل.
ﻭﻹﻀﺎﻓﺔ ﻋﻤﻭﺩ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ،ﺍﻀﻐﻁﻪ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ
Addﺜﻡ ..Columnﺤﺭﺭ ﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ ﻟﻠﻌﻤﻭﺩ ﺍﻟﺠﺩﻴﺩ ،ﻭﺍﺠﻌل ﺍﺴـﻤﻪ ..IDﺍﻀـﻐﻁ
F4ﻟﻌﺭﺽ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ،ﻭﺍﺴﺘﺨﺩﻡ ﺍﻟﺨﺎﺼﻴﺔ DataTypeﻟﺠﻌﻠﻪ ﻤـﻥ ﺍﻟﻨـﻭﻉ ..Int32
ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺒﺎﻗﻲ ﺍﻟﺨﺼﺎﺌﺹ ﻟﻠﺘﺤﻜﻡ ﻓﻲ ﺍﻟﻌﻤﻭﺩ ﺒﺎﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺘﻨﺎﺴﺒﻙ ..ﻤـﺜﻼ :ﺍﺠﻌـل
ﻟﻠﺨﺎﺼﻴﺔ AutoIncrementﺍﻟﻘﻴﻤﺔ Trueﻟﺠﻌل ﻫﺫﺍ ﺍﻟﺤﻘل ﺘﺭﻗﻴﻤـﺎ ﺘﻠﻘﺎﺌﻴـﺎ ،ﻭﻻ ﺘـﻨﺱ ﺃﻥ
ﺘﺠﻌل ﻟﻠﺨﺎﺼﻴﺘﻴﻥ AutoIncrementSeedﻭ AutoIncrementStepﺍﻟﻘﻴﻤﺔ .١
ﺍﻀﻐﻁ ﺍﻟﻬﺎﻤﺵ ﺍﻷﻴﺴﺭ ﻟﻠﻌﻤﻭﺩ IDﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤـﺭ
Set Primary Keyﻟﺠﻌﻠﻪ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ.
٢٣٠
ﺃﻀﻑ ﺇﻟﻰ ﺍﻟﺠﺩﻭل ﻋﻤﻭﺩﺍ ﺠﺩﻴﺩﺍ ﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ ﻭﺍﺠﻌل ﺍﺴﻤﻪ ..Nameﺴﻴﻜﻭﻥ ﻨـﻭﻉ ﻫـﺫﺍ
ﺍﻟﻌﻤﻭﺩ Stringﺒﺼﻭﺭ ﺍﻓﺘﺭﺍﻀﻴﺔ ،ﻓﺎﺘﺭﻜﻪ ﻜﻤﺎ ﻫﻭ ..ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺤـﺩﺩ ﺍﻟﺨﺎﺼـﻴﺔ Unique
ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ﻭﺘﺠﻌل ﻗﻴﻤﺘﻬﺎ ،Trueﻟﺘﺠﻌل ﺍﺴﻡ ﺍﻟﺘﻠﻤﻴﺫ ﻤﺘﻔﺭﺩﺍ ﻏﻴﺭ ﻗﺎﺒـل ﻟﻠﺘﻜـﺭﺍﺭ..
ﻭﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻀﻊ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ MaxLengthﺍﻟﻘﻴﻤﺔ ٣٠ﻟﺭﻓﺽ ﺃﻱ ﺍﺴﻡ ﺃﻁـﻭل ﻤـﻥ ٣٠
ﺤﺭﻓﺎ.
ﻭﻟﻭ ﺃﺭﺩﺕ ﺇﺩﺭﺍﺝ ﺃﻱ ﻋﻤﻭﺩ ﻗﺒل ﺍﻟﻌﻤﻭﺩ ،Nameﻓﺎﻀﻐﻁ ﻫﺎﻤﺸﻪ ﺍﻷﻴﺴﺭ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ،
ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤﺭ ..Insert Columnﻭﺘﺴﺘﻁﻴﻊ ﺤﺫﻑ ﺃﻱ ﻋﻤﻭﺩ ﻓﻲ ﺃﻱ
ﻟﺤﻅﺔ ﺒﺘﺤﺩﻴﺩﻩ ﻭﻀﻐﻁ ﺍﻟﺯﺭ .Delete
ﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ ﻴﻤﻜﻨـﻙ ﺇﻀـﺎﻓﺔ ﺠـﺩﻭل ﺁﺨـﺭ ﺍﺴـﻤﻪ ،Subjectsﻓﻴـﻪ ﺍﻟﻌﻤـﻭﺩﺍﻥID :
ﻭ ..Nameﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﻨﺴﺦ ﺍﻟﺠﺩﻭل Studentsﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻷﻤـﺭ Copyﻭﻟﺼـﻕ
ﻨﺴﺨﺔ ﺠﺩﻴﺩﺓ ﻤﻨﻪ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻷﻤﺭ ،Pasteﺤﻴﺙ ﺴﻴﺄﺨﺫ ﺍﻟﺠﺩﻭل ﺍﻟﺠﺩﻴﺩ ﺍﻻﺴـﻡ ،Students1
ﻭﺍﻟﺫﻱ ﻴﻤﻜﻨﻙ ﺘﻐﻴﻴﺭﻩ ﺇﻟﻰ ..Subjectsﻫﺫﺍ ﻴﺴﻬل ﻋﻠﻴﻙ ﺇﻨﺸـﺎﺀ ﺍﻟﺠـﺩﺍﻭل ﺍﻟﻤﺘﺸـﺎﺒﻬﺔ ﻓـﻲ
ﺘﺭﻜﻴﺒﻬﺎ.
ﺃﻀﻑ ﺠﺩﻭﻻ ﺜﺎﻟﺜﺎ ﺍﺴﻤﻪ ،Gradesﻭﺃﻀـﻑ ﺇﻟﻴـﻪ ﺍﻷﻋﻤـﺩﺓ StudentIDﻭ SubjectID
ﻭ ،Gradeﻭﻻ ﺘﻨﺱ ﺃﻥ ﺘﻐﻴﺭ ﻨﻭﻉ ﺒﻴﺎﻨﺎﺘﻬﺎ ﺠﻤﻴﻌﺎ ﺇﻟﻰ .Int16
ﻭﺍﻀﺢ ﺃﻨﻨﺎ ﺴﻨﺴﺠل ﻓﻲ ﺍﻟﺠﺩﻭل Gradesﺩﺭﺠﺎﺕ ﻜل ﻁﺎﻟﺏ ﻓﻲ ﻜل ﺍﻟﻤﻭﺍﺩ ..ﻫـﺫﻩ ﻋﻼﻗـﺔ
ﻤﺘﻌﺩﺩ ﺒﻤﺘﻌﺩﺩ ،Many-to-Manyﻓﺎﻟﻁﺎﻟﺏ ﻤﺭﺘﺒﻁ ﺒﻜل ﺍﻟﻤـﻭﺍﺩ ،ﻭﺍﻟﻤـﺎﺩﺓ ﻤﺭﺘﺒﻁـﺔ ﺒﻜـل
ﺍﻟﻁﻼﺏ ..ﻫﺫﻩ ﻓﺭﺼﺔ ﻟﻨﺠﺭﺏ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ.
ﻭﻴﺠﺏ ﻫﻨﺎ ﺃﻥ ﻨﺠﻌل ﺍﻟﺤﻘﻠﻴﻥ StudentIDﻭ SubjectIDﻤﻌﺎ ﺯﻭﺠﺎ ﻤﺘﻔﺭﺩﺍ ،ﺤﺘﻰ ﻻ ﻨﻜﺭﺭ
ﺩﺭﺠﺔ ﻨﻔﺱ ﺍﻟﺘﻠﻤﻴﺫ ﻓﻲ ﻨﻔﺱ ﺍﻟﻤﺎﺩﺓ ..ﻟﻔﻌل ﻫﺫﺍ ،ﺤﺩﺩ ﻫﺫﻴﻥ ﺍﻟﺤﻘﻠﻴﻥ )ﺒﻀﻐﻁﻬﻤﺎ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﻤﻊ
ﻀﻐﻁ ﺍﻟﺯﺭ Ctrlﻤﻥ ﻟﻭﺤﺔ ﺍﻟﻤﻔﺎﺘﻴﺢ( ،ﺜﻡ ﺍﻨﻘﺭﻫﻤﺎ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻔﺭﻋﻴـﺔ
Addﺍﻀﻐﻁ ..Keyﺴﺘﻅﻬﺭ ﻨﺎﻓﺫﺓ ﺇﻀﺎﻓﺔ ﻗﻴﺩ ﺍﻟﺘﻔـﺭﺩ ،Unique Constraintﻜﻤـﺎ ﻫـﻭ
ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
٢٣١
ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺍﻟﻌﻠﻭﻱ ﺍﻜﺘﺏ ﺍﺴﻡ ﺍﻟﻘﻴﺩ ،ﻭﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺴﻔﻠﻴﺔ ﺘﺄﻜﺩ ﺃﻨﻙ ﺍﺨﺘﺭﺕ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘـﻲ
ﺴﻴﺘﻡ ﺘﻁﺒﻴﻕ ﺍﻟﻘﻴﺩ ﻋﻠﻴﻬﺎ )ﺴﺘﺠﺩ ﺍﻟﻌﻤﻭﺩﻴﻥ StudentIDﻭ SubjectIDﻤﺨﺘﺎﺭﻴﻥ ﻓﻌﻼ ﻷﻨـﻙ
ﺤﺩﺩﺘﻬﻤﺎ ﻗﺒل ﻓﺘﺢ ﺍﻟﻨﺎﻓﺫﺓ( ..ﻭﻟﻭ ﺃﺭﺩﺕ ﺠﻌل ﻫﺫﻴﻥ ﺍﻟﻌﻤﻭﺩﻴﻥ ﻤﻔﺘﺎﺤﺎ ﺃﺴﺎﺴﻴﺎ ﻟﻠﺠـﺩﻭل ﺃﻴﻀـﺎ،
ﻓﻀﻊ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﺃﻤﺎﻡ ﺍﻻﺨﺘﻴﺎﺭ Primary Keyﺃﺴﻔل ﺍﻟﻨﺎﻓﺫﺓ ..ﻟﻜﻨﻨﺎ ﻻ ﻨﺤﺘﺎﺝ ﺇﻟﻰ ﻫـﺫﺍ
ﻫﻨﺎ ..ﺍﻀﻐﻁ OKﻹﻨﺸﺎﺀ ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ.
ﻨﺭﻴﺩ ﺍﻵﻥ ﺃﻥ ﻨﻨﺸﺊ ﺍﻟﻌﻼﻗﺎﺕ ﺒﻴﻥ ﻫﺫﻩ ﺍﻟﺠﺩﺍﻭل ..ﻴﻤﻜﻨﻙ ﻨﻘـﺭ ﺍﻟﻌﻨﺼـﺭ Relationﻤـﺭﺘﻴﻥ
ﺒﺎﻟﻔﺄﺭﺓ ﻓﻲ ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ ﻟﻌﺭﺽ ﻨﺎﻓﺫﺓ ﺇﻨﺸﺎﺀ ﺍﻟﻌﻼﻗﺔ ..ﺃﻭ ﻴﻤﻜﻨـﻙ ﺃﻥ ﺘﺴـﺤﺏ ﺍﻟﻌﻤـﻭﺩ
ﺍﻷﺴﺎﺴﻲ ﻤﻥ ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ ،ﻭﺘﺴﻘﻁﻪ ﻋﻠﻰ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻔﺭﻋﻲ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﺘﺎﺒﻊ ..ﻭﻻ ﺘﻤـﺭ
ﻭﺃﻨﺕ ﺘﺴﺤﺏ ﺃﻱ ﻋﻤﻭﺩ ﻋﻠﻰ ﻋﻤﻭﺩ ﺁﺨﺭ ﻓﻲ ﻨﻔﺱ ﺍﻟﺠﺩﻭل ،ﻭﺇﻻ ﻓﺴﻴﺘﻡ ﺘﺤﺩﻴﺩﻩ ﻭﺍﻋﺘﺒﺎﺭﻩ ﺠﺯﺀﺍ
ﻤﻥ ﺍﻟﻌﻼﻗﺔ ..ﻟﻜﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺘﺼﺤﻴﺢ ﺫﻟﻙ ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﻌﻼﻗﺔ ﻋﻠﻰ ﺃﻱ ﺤﺎل ..ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﻤﺄﻟﻭﻓﺔ،
ﻭﻟﻥ ﺘﺠﺩ ﻓﻴﻬﺎ ﺃﻱ ﺠﺩﻴﺩ ﻟﻡ ﻨﺘﻌﺭﻑ ﻋﻠﻴﻪ ﺴﺎﺒﻘﺎ.
ﻨﺭﻴﺩ ﻫﻨﺎ ﺃﻥ ﻨﺭﺒﻁ ﺒﻴﻥ ﺍﻟﻌﻤﻭﺩﻴﻥ Students.IDﻭ ،Grades.StudentIDﻭﻜـﺫﻟﻙ ﺒـﻴﻥ
ﺍﻟﻌﻤﻭﺩﻴﻥ Subjects.IDﻭ ..Grades.SubjectIDﻭﻻ ﺘﻨﺱ ﺍﻟﻤﺤﺎﻓﻅـﺔ ﻋﻠـﻰ ﺍﻟﺘﻜﺎﻤـل
ﺍﻟﻤﺭﺠﻌﻲ ﻓﻲ ﻜل ﻋﻼﻗﺔ ،ﻭﺫﻟﻙ ﺒﺎﺨﺘﻴﺎﺭ:
Both Relation And Foreign Key Constraint
ﻤﻥ ﺍﻟﻘﺴﻡ ،Choose what to create :ﻋﻠﻰ ﺃﻥ ﺘﺠﻌـل ﺍﺨﺘﻴـﺎﺭﺍﺕ ﺍﻟﺤـﺫﻑ ﻭﺍﻟﺘﺤـﺩﻴﺙ
ﻭﺍﻟﺭﻓﺽ ..Cascadeﻫﺫﺍ ﺴﻴﺭﻴﺤﻨﺎ ﻤﻥ ﺍﻟﻤﺸﺎﻜل ﺍﻟﺘﻲ ﺘﺤﺩﺙ ﻋﻨﺩ ﺤـﺫﻑ ﺍﺴـﻡ ﻁﺎﻟـﺏ ،ﺃﻭ
٢٣٢
ﺘﻐﻴﻴﺭ ﺍﺴﻡ ﻤﺎﺩﺓ ،ﻓﺎﻟﺘﻜﺎﻤل ﺍﻟﻤﺭﺠﻌﻲ ﺴﻴﺤﺎﻓﻅ ﻋﻠﻰ ﺠﺩﻭل ﺍﻟﺩﺭﺠﺎﺕ ﺼﺤﻴﺤﺎ ﺩﺍﺌﻤﺎ.
ﺒﻌﺩ ﺇﻨﺠﺎﺯ ﻫﺫﺍ ،ﻴﺠﺏ ﺃﻥ ﻴﺒﺩﻭ ﺍﻟﻤﺨﻁﻁ ﻜﺎﻟﺘﺎﻟﻲ:
ﻨﺭﻴﺩ ﺍﻵﻥ ﺇﻨﺘﺎﺝ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻫﺫﺍ ﺍﻟﻤﺨﻁﻁ ..ﻟﻔﻌل ﻫﺫﺍ ﺍﻨﺘﻘل ﺇﻟـﻰ ﺍﻟﻨﻤـﻭﺫﺝ ،ﻭﺍﻓـﺘﺢ
ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ ﻭﺍﻨﻘﺭ ﻤﺭﺘﻴﻥ ﻋﻠﻰ ﺍﻟﻌﻨﺼﺭ ..DataSetﺴﻴﻅﻬﺭ ﻟﻙ ﻤﺭﺒﻊ ﺤـﻭﺍﺭ ﺇﻀـﺎﻓﺔ
ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ،ﻭﺴﺘﺠﺩ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻌﻠﻭﻴﺔ ﺍﺴﻡ ﺍﻟﻤﺨﻁﹼﹼـﻁ ..MyDataSetﺍﻀـﻐﻁ OK
ﻹﻨﺘﺎﺝ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﻤﻥ ﻫﺫﺍ ﺍﻟﻤﺨﻁﻁ ،ﺤﻴﺙ ﺴﺘﻀﺎﻑ ﻨﺴﺨﺔ ﻤﻨﻬـﺎ ﺍﺴـﻤﻬﺎ
MyDataSet1ﺇﻟﻰ ﺼﻴﻨﻴﺔ ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤﻭﺫﺝ ..ﺃﻗﺘﺭﺡ ﺘﻐﻴﻴﺭ ﺍﺴﻤﻬﺎ ﺇﻟﻰ .DsStudents
ﻤﻥ ﻫﺫﻩ ﺍﻟﻨﻘﻁﺔ ،ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘـﺔ ﺍﻟﺘـﻲ ﺍﻋﺘـﺩﺘﻬﺎ ﺴـﺎﺒﻘﺎ،
ﻭﺭﺒﻁﻬﺎ ﺒﺠﺩﻭل ﺍﻟﻌﺭﺽ ،ﻭﺤﻔﻅ ﻭﺘﺤﻤﻴل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﻟﻁﺭﻴﻘﺔ ﺍﻟﻤﺄﻟﻭﻓﺔ ..ﻭﺴﺘﺠﺩ ﺍﻟﻜﻭﺩ ﺍﻟﻜﺎﻤـل
ﺍﻟﺫﻱ ﻴﻔﻌل ﻫﺫﺍ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ .CustomDataSet
ﻻﺤﻅ ﺃﻥ ﺇﺠﺭﺍﺀ ﺃﻱ ﺘﻌﺩﻴل ﻋﻠﻰ ﻤﺨﻁﻁ ،XMLﻴﻨﻌﻜﺱ ﻤﺒﺎﺸﺭﺓ ﻋﻠﻰ ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ،ﻟﻬﺫﺍ ﻟﺴﺕ ﻓﻲ ﺤﺎﺠﺔ ﺇﻟﻰ ﺤﺫﻓﻬﺎ ﺜﻡ ﺇﻋﺎﺩﺓ ﺇﻨﺸﺎﺌﻬﺎ ،ﻓﻜل ﺸﻲﺀ ﻴﺘﻡ ﺘﻠﻘﺎﺌﻴﺎ ﺒﻤﻨﺘﻬـﻰ
ﺍﻟﺒﺴﺎﻁﺔ.
ﺠﺭﺏ ﻤﺜﻼ ﺇﻀﺎﻓﺔ ﻋﻤﻭﺩ ﻤﺤﺴﻭﺏ Calculated Columnﺇﻟﻰ ﺍﻟﺠﺩﻭل ..Gradesﻟﻔﻌـل
ﻫﺫﺍ ﺍﻓﺘﺢ ﺍﻟﻤﺨﻁﻁ ،ﻭﺍﻀﻐﻁ ﺍﻟﺠﺩﻭل Gradesﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤـﺔ ﺍﻟﻤﻭﻀـﻌﻴﺔ
ﺍﻀﻐﻁ ﺍﻷﻤﺭ ..Insert Columnﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺠﺩﻴﺩ ،Subjectﻭﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ﺤﺩﺩ
٢٣٣
ﺍﻟﺨﺎﺼﻴﺔ Expressionﻭﻀﻊ ﻓﻴﻬﺎ ﺍﻟﻨﺹ:
Parent(Subjects_Grades).Name
ﺒﻤﺠﺭﺩ ﺃﻥ ﺘﻔﻌل ﻫﺫﺍ ﺴﺘﺼﻴﺭ ﻟﻠﺨﺎﺼﻴﺔ ReadOnlyﺍﻟﻘﻴﻤﺔ ،Trueﻭﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﺍﻟﻤﺴـﺘﺨﺩﻡ
ﻻ ﻴﺴﺘﻁﻴﻊ ﺘﻌﺩﻴل ﻗﻴﻡ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ،ﻷﻨﻪ ﺴﻴﻌﺭﺽ ﻨﺎﺘﺠﺎ ﻤﺤﺴﻭﺒﺎ ﺒﻨﺎﺀ ﻋﻠﻰ ﻗﻴﻤﺔ ﻋﻤﻭﺩ ﺁﺨـﺭ..
ﻭﻓﻲ ﺤﺎﻟﺘﻨﺎ ﻫﺫﻩ ،ﺠﻌﻠﻨﺎ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻴﻌﺭﺽ ﺍﺴﻡ ﺍﻟﻤﺎﺩﺓ ﺍﻟﺩﺭﺍﺴﻴﺔ ،ﻭﺫﻟﻙ ﻤﻥ ﺨـﻼل ﺍﻟﻌﻼﻗـﺔ
Subjects_Gradesﺍﻟﺘﻲ ﺘﺭﺒﻁ ﺠﺩﻭل ﺍﻟﻤﻭﺍﺩ ﺒﺠﺩﻭل ﺍﻟﺩﺭﺠﺎﺕ ،ﺤﻴﺙ ﺴﻴﺴـﺘﺨﺩﻡ ﺍﻟﻌﻤـﻭﺩ
Subjectﻗﻴﻤﺔ ﺍﻟﺤﻘل ﺍﻟﻔﺭﻋﻲ ﻓﻲ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ )ﻭﻫﻭ ﺍﻟﺤﻘـل (SubjectIDﻟﻴﺤﻀـﺭ ﺍﺴـﻡ
ﺍﻟﻤﺎﺩﺓ ﺍﻟﺘﻲ ﻟﻬﺎ ﻨﻔﺱ ﺍﻟﺭﻗﻡ ﻤﻥ ﺠﺩﻭل ﺍﻟﻤﻭﺍﺩ ..ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﺤﺴـﻭﺒﺔ ﺒﺘﻔﺼـﻴل
ﺃﻜﺜﺭ ﻓﻲ ﻓﺼل ﺍﻟﺠﺩﺍﻭل.
ﺘﻼﺤﻅ ﻜﻤﺎ ﻫﻭ ﻭﺍﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ ،ﺇﻥ ﺇﻀﺎﻓﺔ ﺍﺴﻡ ﺍﻟﻤﺎﺩﺓ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ﺴﻴﺠﻌﻠﻬﺎ ﺘﻅﻬـﺭ
ﻓﻲ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﻲ ﺘﻌﺭﺽ ﺩﺭﺠﺎﺕ ﺍﻟﻁﺎﻟﺏ ،ﻭﻫﺫﺍ ﺃﻓﻀل ﻤﻥ ﺇﺭﺒﺎﻙ ﺍﻟﻤﺴـﺘﺨﺩﻡ ﺒﻌـﺭﺽ ﺭﻗـﻡ
ﺍﻟﻤﺎﺩﺓ ،ﻭﺍﻟﺩﺭﺠﺔ ﺍﻟﺘﻲ ﺤﺼل ﻋﻠﻴﻬﺎ ﺍﻟﻁﺎﻟﺏ ﻓﻴﻬﺎ.
٢٣٤
ﻻﺤﻅ ﺃﻨﻨﺎ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻗﺎﺌﻤﺔ ListBoxﻟﻌﺭﺽ ﺃﺴﻤﺎﺀ ﺍﻟﻁﻠﺒﺔ ..ﻫﺫﺍ ﻴﺭﻴﺢ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺃﺜﻨﺎﺀ ﺇﺩﺨﺎﻟﻪ
ﻟﺩﺭﺠﺎﺕ ﺍﻟﻁﻠﺏ ،ﺒﺴﺒﺏ ﺴﺭﻋﺔ ﺍﻻﻨﺘﻘﺎل ﻤﻥ ﻁﺎﻟﺏ ﺇﻟﻰ ﺁﺨﺭ.
ﻭﻟﻌﻠﻙ ﺘﺘﺴﺎﺀل :ﻟﻡ ﻻ ﻴﺩﺨل ﺍﻟﻤﺴﺘﺨﺩﻡ ﺃﺴﻤﺎﺀ ﺍﻟﻁﻠﺒﺔ ﻭﺩﺭﺠﺎﺘﻬﻡ ﻓﻲ ﻨﻔﺱ ﺍﻟﻨﻤﻭﺫﺝ؟
ﻫﺫﺍ ﻤﺠﺭﺩ ﻤﺜﺎل ﻤﺨﺘﺼﺭ ،ﻟﻜﻥ ﻓﻲ ﺍﻟﺒﺭﺍﻤﺞ ﺍﻟﺤﻘﻴﻘﻴﺔ ،ﺴﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺇﺩﺨـﺎل ﺒﻴﺎﻨـﺎﺕ ﺍﻟﻁﺎﻟـﺏ
ﻜﺎﻤﻠﺔ ﻭﻟﻴﺱ ﺍﺴﻤﻪ ﻓﻘﻁ ،ﻤﺜل ﻋﻤﺭﻩ ،ﻭﻓﺼﻠﻪ ﺍﻟﺩﺭﺍﺴﻲ ،ﻭﻋﻨﻭﺍﻨﻪ ،ﻭﻫﺎﺘﻔﻪ ...ﺇﻟﺦ ..ﻭﻜل ﻫـﺫﺍ
ﻴﺤﺘﺎﺝ ﺇﻟﻰ ﻤﺴﺎﺤﺔ ﻋﺭﺽ ﻜﺒﻴﺭﺓ ،ﻭﺍﻷﻓﻀل ﻋﻤل ﻨﻤﻭﺫﺝ ﻤﺴﺘﻘل ﻟﻪ ،ﻭﻫﻭ ﻤﺎ ﻓﻌﻠﻨﺎﻩ ﻓﻲ ﻫـﺫﺍ
ﺍﻟﻤﺸﺭﻭﻉ ،ﻟﻴﻤﻜﻨﻙ ﺍﻟﺒﻨﺎﺀ ﻋﻠﻴﻪ ﻓﻴﻤﺎ ﺒﻌﺩ.
ﺇﻟﻰ ﺍﻵﻥ ﻜل ﺸﻲﺀ ﺭﺍﺌﻊ ..ﻟﻜﻥ ﺘﺘﺒﻘﻰ ﻤﺸﻜﻠﺔ ﻓﻲ ﻫﺫﺍ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻭﻫﻲ ﺃﻥ ﺍﻟﻌﻤـﻭﺩ ﺍﻟﻤﺤﺴـﻭﺏ
Subjectﺴﻴﺘﻡ ﺤﻔﻅﻪ ﻓﻲ ﺍﻟﻤﻠﻑ ﻋﻨﺩ ﺤﻔﻅ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻫﻭ ﻤﺎ ﺴـﻴﺯﻴﺩ ﻤـﻥ ﺤﺠـﻡ
ﺍﻟﻤﻠﻑ ﺒﻼ ﻀﺭﻭﺭﺓ ..ﻟﻬﺫﺍ ﻋﻠﻴﻨﺎ ﺤﺫﻑ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻗﺒل ﺤﻔﻅﻬﺎ ..ﻟﻜـﻥ
ﻫﺫﺍ ﺴﻴﺅﺩﻱ ﺇﻟﻰ ﺤﺩﻭﺙ ﺃﺨﻁﺎﺀ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ!
ﻭﻴﻤﻜﻥ ﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ،ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ DataSet.Copyﻟﻨﺴﺦ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟـﻰ
ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺍﺤﺘﻴﺎﻁﻴﺔ ،ﺜﻡ ﺤﺫﻑ ﺍﻟﻌﻤﻭﺩ Subjectﻤﻥ ﻫـﺫﻩ ﺍﻟﻤﺠﻤﻭﻋـﺔ ﺍﻻﺤﺘﻴﺎﻁﻴـﺔ،
ﻭﺤﻔﻅﻬﺎ ﺒﻴﺎﻨﺎﺘﻬﺎ ﻓﻲ ﺍﻟﻤﻠﻑ ..ﻭﺒﻬﺫﺍ ﺘﻅل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﺼﻠﻴﺔ ﻜﻤﺎ ﻫﻲ ،ﺒﻴﻨﻤـﺎ ﻨﺤﺼـل
ﻋﻠﻰ ﻤﻠﻑ ﺃﺼﻐﺭ ﺤﺠﻤﺎ ..ﻭﻋﻨﺩ ﺘﺤﻤﻴل ﻫﺫﺍ ﺍﻟﻤﻠﻑ ،ﻟﻥ ﺘﺤﺩﺙ ﺃﻴﺔ ﻤﺸﻜﻠﺔ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺒﺴﺒﺏ
ﻏﻴﺎﺏ ﺍﻟﻌﻤﻭﺩ ،Subjectﻓﻬﻭ ﻋﻤﻭﺩ ﻤﺤﺴﻭﺏ ،ﻭﺴﻴﺴﺘﻨﺘﺞ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻗﻴﻤﺘﻪ ..ﻭﺴـﺘﺠﺩ ﺍﻟﻜـﻭﺩ
ﺍﻟﺫﻱ ﻴﻨﻔﺫ ﻫﺫﺍ ﻓﻲ ﺍﻟﺯﺭ "ﺤﻔﻅ ﺍﻟﺒﻴﺎﻨﺎﺕ".
ﻻﺤﻅ ﺃﻥ ﻋﻴﺏ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﻫﻭ ﺃﻥ ﻨﺴﺦ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻜﺎﻤﻠﻬﺎ ﻗﺩ ﻴﻜـﻭﻥ ﻜﺎﺭﺜـﺔ ﻋﻠـﻰ
ﺍﻟﺫﺍﻜﺭﺓ ﺇﺫﺍ ﻜﺎﻥ ﺤﺠﻡ ﺒﻴﺎﻨﺎﺘﻬﺎ ﻀﺨﻤﺎ ..ﻟﻬﺫﺍ ﻴﻤﻜﻥ ﺍﻟﻠﺠﻭﺀ ﺇﻟﻰ ﺤل ﺒﺩﻴل ،ﻭﻫﻭ ﺤﺫﻑ ﺍﻟﻌﻤـﻭﺩ
ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻗﺒل ﺤﻔﻅﻬﺎ ،ﺜﻡ ﺇﻨﺸﺎﺌﻪ ﻤﺭﺓ ﺃﺨﺭﻯ ﺒﻌﺩ ﺍﻟﺤﻔﻅ ﻤﺒﺎﺸﺭﺓ ..ﺃﺴﻬل ﻁﺭﻴﻘـﺔ
ﻟﻔﻌل ﻫﺫﺍ ﻫﻲ ﺍﻻﺤﺘﻔﺎﻅ ﺒﻤﺭﺠﻊ ﻟﻠﻌﻤﻭﺩ ﻓﻲ ﻤﺘﻐﻴﺭ ﻤﻥ ﺍﻟﻨﻭﻉ DataCoulmnﻗﺒل ﺤﺫﻓﻪ ﻤـﻥ
ﻤﺠﻤﻭﻋﺔ ﺃﻋﻤﺩﺓ ﺍﻟﺠﺩﻭل ،ﻭﻤﻥ ﺜﻡ ﺤﻔﻅ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﻤﻠﻑ ،ﺜﻡ ﺇﻀﺎﻓﺔ ﺍﻟﻌﻤﻭﺩ ﺍﻟـﺫﻱ ﻨﺤـﺘﻔﻅ
ﺒﻤﺭﺠﻌﻪ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ ﻤﺭﺓ ﺃﺨﺭﻯ ..ﻭﺴﺘﺠﺩ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﻴﻔﻌل ﻫﺫﺍ ﻓﻲ ﺍﻟـﺯﺭ "ﺤﻔـﻅ
ﺍﻟﺒﻴﺎﻨﺎﺕ ."٢
٢٣٥
ﺤﻔﻅ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺸﺠﺭﺓ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ:
ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ،CustomDataSetﺭﺃﻴﻨﺎ ﻤﺜﺎﻻ ﻋﻠﻰ ﻋﻼﻗﺔ ﻤﺘﻌﺩﺩ ﺒﻤﺘﻌﺩﺩ ..ﻟﻌﻠﻪ ﻴﻜﻭﻥ ﻤﻨﺎﺴﺒﺎ
ﺍﻵﻥ ﺃﻥ ﻨﺭﻯ ﻤﺜﺎﻻ ﻋﻠﻰ ﺍﻟﻌﻼﻗﺔ ﺍﻟﺫﺍﺘﻴﺔ ..Self-Relationﻟﻔﻌل ﻫﺫﺍ ،ﺴﻨﻨﺸﺊ ﻤﺸﺭﻭﻋﺎ ﺍﺴﻤﻪ
،SaveTreeNodesﻭﻫﻭ ﻴﻌﺭﺽ ﺸﺠﺭﺓ ﻭﻴﺘﻴﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺇﻀﺎﻓﺔ ﺍﻟﻌﻨﺎﺼﺭ ﺇﻟﻴﻬـﺎ ،ﻭﺘﻐﻴﻴـﺭ
ﻤﺴﺘﻭﻴﺎﺘﻬﺎ ،ﻭﻫﻲ ﻭﻅﺎﺌﻑ ﺘﻌﻠﻤﻨﺎ ﻜﻴﻑ ﻨﻨﺸﺌﻬﺎ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ TreeViewSampleﻓﻲ ﻜﺘـﺎﺏ
ﺒﺭﻤﺠﺔ ﺍﻟﻭﻴﻨﺩﻭﺯ ،ﻭﻟﻥ ﻨﻜﺭﺭ ﺸﺭﺤﻬﺎ ﻫﻨﺎ ..ﻭﺴﺘﺠﺩ ﺍﻟﻤﺸـﺭﻭﻉ SaveTreeNodesﻀـﻤﻥ
ﺃﻤﺜﻠﺔ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ.
ﻤﺎ ﻨﺭﻴﺩﻩ ﺍﻵﻥ ،ﻫﻭ ﺃﻥ ﻨﺴﻤﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺒﺤﻔﻅ ﻓﺭﻭﻉ ﺍﻟﺸﺠﺭﺓ ..ﻭﻨﻅﺭﺍ ،ﻷﻨﻪ ﻤﻥ ﻏﻴﺭ ﺍﻟﻌﻤﻠـﻲ
ﺇﻨﺸﺎﺀ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻜﺎﻤﻠﺔ ﻟﺤﻔﻅ ﺒﻌﺽ ﻋﻨﺎﺼﺭ ﺍﻟﺸﺠﺭﺓ ،ﻓﺴﻴﻜﻭﻥ ﻤﻥ ﺍﻟﻌﻤﻠﻲ ﻫﻨـﺎ ﺃﻥ ﻨﻨﺸـﺊ
ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺨﺎﺼﺔ ،ﻭﻨﺴﺘﺨﺩﻤﻬﺎ ﻟﺤﻔﻅ ﺍﻟﻌﻨﺎﺼﺭ ﻓﻲ ﻤﻠﻑ ..XMLﻭﺒﻬـﺫﺍ ﻨﻜـﻭﻥ ﻗـﺩ
ﺍﺴﺘﻔﺩﻨﺎ ﻤﻥ ﻗﺩﺭﺍﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻋﻼﻗﺎﺘﻬﺎ ،ﻭﻓﻲ ﻨﻔﺱ ﺍﻟﻭﻗﺕ ﺴﻨﺤﻔﻅ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻤﻠـﻑ
ﻤﺴﺘﻘل.
ﻟﻔﻌل ﻫﺫﺍ ،ﺃﻀﻔﻨﺎ ﺇﻟﻰ ﺍﻟﻤﺸﺭﻭﻉ SaveTreeNodesﻤﺨﻁﻁ ﻤﺠﻤﻭﻋـﺔ ﺒﻴﺎﻨـﺎﺕ ﺒﺎﻟﻁﺭﻴﻘـﺔ
ﺍﻟﻤﺄﻟﻭﻓﺔ ،ﻭﺃﺴﻤﻴﻨﺎﻩ ،TreeDataSetﻭﺃﻀﻔﻨﺎ ﺇﻟﻴﻪ ﺠﺩﻭﻻ ﺍﺴﻤﻪ ،TreeNodesﻭﺃﻀﻔﻨﺎ ﺇﻟﻴـﻪ
ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ:
ﻭﻗﺩ ﺃﻀﻔﻨﺎ ﻋﻼﻗﺔ ﺇﻟﻰ ﻤﺨﻁﻁ ﺍﻟﺠﺩﻭل ،ﻟﺘﺭﺒﻁ ﺒﻴﻥ ﺍﻟﺤﻘﻠﻴﻥ IDﻭ .ParentID
ﻫﻜﺫﺍ ﻴﺒﺩﻭ ﺸﻜل ﺍﻟﻤﺨﻁﻁ ..ﻻﺤﻅ ﻜﻴﻑ ﺘﺨﺭﺝ ﺍﻟﻌﻼﻗﺔ ﻤﻥ ﻨﻔﺱ ﺍﻟﺠﺩﻭل ﻭﺘﻌﻭﺩ ﺇﻟﻴﻪ:
٢٣٦
ﻭﻗﺩ ﺃﻀﻔﻨﺎ ﺇﻟﻰ ﺍﻟﻨﻤﻭﺫﺝ ﻨﺴﺨﺔ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺃﺴﻤﻴﻨﺎﻫﺎ ..TreeDsﻭﺤﺘـﻰ ﻻ ﻨﻌﻘـﺩ
ﺍﻷﻤﻭﺭ ﻋﻠﻰ ﺃﻨﻔﺴﻨﺎ ،ﻟﻥ ﻨﻤﻸ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻌﻨﺎﺼﺭ ﺍﻟﺸﺠﺭﺓ ﺇﻻ ﻋﻨﺩ ﻀﻐﻁ ﺯﺭ ﺍﻟﺤﻔـﻅ،
ﻓﻬﻲ ﻤﺠﺭﺩ ﻭﻋﺎﺀ ﻭﺴﻴﻁ ﻴﺘﻴﺢ ﻟﻨﺎ ﺤﻔﻅ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻤﻠﻑ ..XMLﻫﺫﺍ ﻫﻭ ﻜﻭﺩ ﻫﺫﺍ ﺍﻟﺯﺭ:
;) (TreeDs.Clear
{ )foreach (TreeNode Node in TreeView1.Nodes
;)var R = TreeDs.TreeNodes.AddTreeNodesRow(Node.Text, null
;)SaveChildren(Node, R
}
;)"TreeDs.WriteXml("C:\\TreeNodes.Xml
ﻓﻲ ﺍﻟﺒﺩﺍﻴﺔ ﺃﻓﺭﻏﻨﺎ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻤﺤﺘﻭﻴﺎﺘﻬﺎ ،ﺜﻡ ﻤﺭﺭﻨﺎ ﻋﺒﺭ ﺠﺫﻭﺭ ﺍﻟﺸﺠﺭﺓ ﻹﻀـﺎﻓﺘﻬﺎ
ﺇﻟﻰ ﺍﻟﺠﺩﻭل TreeNodesﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻻﺤـﻅ ﺃﻥ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﻗﺩ ﺃﻀﺎﻓﺕ ﺍﻟﻭﺴﻴﻠﺔ AddTreeNodesRowﺇﻟﻰ ﺍﻟﺠﺩﻭل ،ﻟﺘﺘﻴﺢ ﻟﻨـﺎ ﺇﻀـﺎﻓﺔ
ﺼﻑ ﺠﺩﻴﺩ ﺇﻟﻴﻪ ..ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ:
-ﻨﺼﺎ ﻴﻤﺜل ﻗﻴﻤﺔ ﺍﻟﺤﻘل Textﻓﻲ ﺍﻟﺼﻑ ﺍﻟﺠﺩﻴﺩ.
-ﻜﺎﺌﻥ ﺼﻑ ﻤﻥ ﺍﻟﻨﻭﻉ ،TreeDataSet.TreeNodesRowﻟﺘﺭﺴل ﺇﻟﻴـﻪ ﺍﻟﺼـﻑ
ﺍﻟﺭﺌﻴﺴﻲ ﻟﻠﺼﻑ ﺍﻟﺤﺎﻟﻲ ..ﻫﺫﺍ ﺃﺴﻬل ﻤﻥ ﺃﻥ ﺘﻀﻊ ﺒﻨﻔﺴﻙ ﺭﻗﻡ ﺍﻟﺼﻑ ﺍﻟﺭﺌﻴﺴﻲ ﻓـﻲ
ﺍﻟﺤﻘل ،ParentIDﻭﻫﺫﻩ ﺇﺤﺩﻯ ﺍﻟﺘﺴﻬﻴﻼﺕ ﺍﻟﺘﻲ ﻤﻨﺤﺘﻬﺎ ﻟﻙ ﺍﻟﻌﻼﻗﺔ ﺍﻟﺫﺍﺘﻴﺔ ..ﻭﻨﻅـﺭﺍ
ﻷﻥ ﺠﺫﻭﺭ ﺍﻟﺸﺠﺭﺓ ﻟﻴﺴﺕ ﻟﻬﺎ ﻓﺭﻭﻉ ﺭﺌﻴﺴﻴﺔ ،ﻓﺴﻨﺭﺴل ﺍﻟﻘﻴﻤﺔ Nothingﺇﻟـﻰ ﻫـﺫﺍ
ﺍﻟﻤﻌﺎﻤل ..ﻫﺫﺍ ﺴﻴﺘﺭﻙ ﺍﻟﺤﻘل ParentIDﻓﺎﺭﻏﺎ.
ﺒﻌﺩ ﻫﺫﺍ ،ﻴﺠﺏ ﺃﻥ ﻨﻀﻴﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﺭﻭﻉ ﻜل ﺠﺫﺭ ..ﻟﻔﻌل ﻫﺫﺍ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺇﺠﺭﺍﺀ
ﺍﺴﻤﻪ ،SaveChildrenﻭﻫﻭ ﻴﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ:
-ﻜﺎﺌﻥ ﺍﻟﻔﺭﻉ TreeNodeﺍﻟﺫﻱ ﺴﻨﻀﻴﻑ ﻋﻨﺎﺼﺭﻩ ﺍﻟﻔﺭﻋﻴﺔ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
٢٣٧
-ﻜﺎﺌﻥ ﺍﻟﺼﻑ TreeDataSet.TreeNodesRowﺍﻟﺫﻱ ﻴﻌﻤـل ﻜﺼـﻑ ﺭﺌﻴﺴـﻲ،
ﻟﻠﺼﻔﻭﻑ ﺍﻟﺘﻲ ﺴﻨﻀﻴﻔﻬﺎ ﺍﻟﺠﺩﻭل.
ﻫﺫﺍ ﻫﻭ ﻜﻭﺩ ﻫﺫﺍ ﺍﻹﺠﺭﺍﺀ ،ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻨﻪ ﺇﺠﺭﺍﺀ ﺍﺭﺘﺩﺍﺩﻱ Recursiveﻴﺴﺘﺩﻋﻲ ﻨﻔﺴﻪ ،ﻷﻥ
ﻜل ﻓﺭﻉ ﻗﺩ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻨﺎﺼﺭ ﻓﺭﻋﻴﺔ ،ﻜل ﻤﻨﻬﺎ ﻗﺩ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻨﺎﺼﺭ ﻓﺭﻋﻴﺔ ،ﻭﻫﻜﺫﺍ:
public void SaveChildren(TreeNode ParentNode,
{ )TreeDataSet.TreeNodesRow ParentRow
)foreach (TreeNode Node in ParentNode.Nodes
{
(var R = TreeDs.TreeNodes.AddTreeNodesRow
;)Node.Text, ParentRow
;)SaveChildren(Node, R
}
}
ﻭﺒﻌﺩ ﻭﻀﻊ ﺠﻤﻴﻊ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻔﺭﻭﻉ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺴﻴﺘﻡ ﺘﻨﻔﻴﺫ ﺁﺨﺭ ﺴـﻁﺭ ﻓـﻲ ﻜـﻭﺩ
ﻀﻐﻁ ﺯﺭ ﺍﻟﺤﻔﻅ ،ﻭﻫﻭ ﻴﺴﺘﺩﻋﻲ ﺍﻟﻭﺴﻴﻠﺔ WriteXMLﻟﺤﻔﻅ ﻤﺤﺘﻭﻴﺎﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻓﻲ ﺍﻟﻤﻠﻑ.
ﻭﻫﻜﺫﺍ ﻨﻜﻭﻥ ﻗﺩ ﺤﻔﻅﻨﺎ ﻓﺭﻭﻉ ﺍﻟﺸﺠﺭﺓ ﺒﺎﻟﻜﺎﻤل ..ﺒﻘﻲ ﺇﺫﻥ ﺃﻥ ﻨﻌﻴﺩ ﻗﺭﺍﺀﺘﻬﺎ ﻤﻥ ﺍﻟﻤﻠـﻑ ﻋﻨـﺩ
ﻀﻐﻁ ﺯﺭ ﺍﻟﺘﺤﻤﻴل ..ﻟﻔﻌل ﻫﺫﺍ ﺴﻨﻔﺭﻍ ﻜﻼ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﻟﺸﺠﺭﺓ ﻤﻥ ﻤﺤﺘﻭﻴﺎﺘﻬﻤـﺎ،
ﺜﻡ ﻨﻘﺭﺃ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻠﻑ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ :ReadXML
;) (TreeDs.Clear
;) (TreeView1.Nodes.Clear
;)"TreeDs.ReadXml("C:\TreeNodes.Xml
ﺒﻌﺩ ﻫﺫﺍ ﺴﻨﻨﻘل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﺍﻟﺸﺠﺭﺓ ..ﻟﻔﻌل ﻫﺫﺍ ﺴﻨﻀﻴﻑ ﺍﻟﺠﺫﻭﺭ ﺇﻟـﻰ
ﺍﻟﺸﺠﺭﺓ ﺃﻭﻻ ..ﻨﺤﻥ ﻨﻌﺭﻑ ﺃﻥ ﺍﻟﺠﺫﺭ ﻤﻤﺜل ﻓﻲ ﺍﻟﺠﺩﻭل ﺒﺼﻑ ﺘﻭﺠﺩ ﻓﻲ ﺍﻟﺨﺎﻨـﺔ ParentID
ﺍﻟﺨﺎﺼﺔ ﺒﻪ ﺍﻟﻘﻴﻤﺔ ..DbNullﻟﻬﺫﺍ ﺴﻨﻤﺭ ﻋﻠﻰ ﻜل ﺍﻟﺼﻔﻭﻑ ،ﻭﻨﺴﺘﺨﺩﻡ ﺍﻟﻭﺴـﻴﻠﺔ ﺍﻟﺠـﺎﻫﺯﺓ
IsParentIDNullﺍﻟﺘﻲ ﻋﺭﻓﺘﻬﺎ ﻟﻨﺎ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ،ﻟﻨﺭﻯ ﺇﻥ ﻜﺎﻨـﺕ ﻫـﺫﻩ
ﺍﻟﺨﺎﻨﺔ ﻓﺎﺭﻏﺔ ،ﻓﺈﻥ ﻜﺎﻨﺕ ﻜﺫﻟﻙ ،ﻋﺭﻓﻨﺎ ﻓﺭﻋﺎ ﺠﺩﻴﺩﺍ ،ﻭﻭﻀﻌﻨﺎ ﻓﻴﻪ ﺍﻟﻨﺹ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﺨﺎﻨـﺔ
،Textﻭﺃﻀﻔﻨﺎﻩ ﺇﻟﻰ ﺍﻟﺸﺠﺭﺓ ﻜﺠﺫﺭ ،ﺜـﻡ ﻨﺴـﺘﺩﻋﻲ ﺍﻹﺠـﺭﺍﺀ LoadChildrenﻟﺘﺤﻤﻴـل
ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻔﺭﻋﻴﺔ ﻓﻲ ﻫﺫﺍ ﺍﻟﺠﺫﺭ ..ﻫﺫﺍ ﻫﻭ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﻴﻔﻌل ﻫﺫﺍ:
٢٣٨
)foreach (var R in TreeDs.TreeNodes
{ )if (R.IsParentIDNull
;)var Node = TreeView1.Nodes.Add(R.Text
;)LoadChildren(Node, R
}
ﺃﻴﻀﺎ ،ﻴﺠﺏ ﺃﻥ ﻴﻜﻭﻥ ﺍﻹﺠﺭﺍﺀ LoadChildrenﺍﺭﺘـﺩﺍﺩﻴﺎ Recursiveﻴﺴـﺘﺩﻋﻲ ﻨﻔﺴـﻪ،
ﻟﺘﺤﻤﻴل ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻔﺭﻋﻴﺔ ﻟﻜل ﻓﺭﻉ ﻓﻲ ﺠﻤﻴﻊ ﺍﻟﻤﺴﺘﻭﻴﺎﺕ.
ﻭﻟﻜﻥ ﻜﻴﻑ ﻨﻌﺭﻑ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻔﺭﻋﻴﺔ؟
ﻫﺫﺍ ﺴﻬل ﺠﺩﺍ ،ﺒﻔﻀل ﺍﻟﻌﻼﻗﺔ ﺍﻟﺫﺍﺘﻴﺔ ﺍﻟﻤﻌﺭﻓﺔ ﻓﻲ ﺍﻟﺠﺩﻭل ،ﻓﻨﺤﻥ ﻨﺴﺘﻁﻴﻊ ﺍﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ
GetChildRowsﻟﻤﻌﺭﻓﺔ ﺍﻟﺼﻔﻭﻑ ﺍﻟﻔﺭﻋﻴﺔ ﺍﻟﺘﺎﺒﻌﺔ ﻷﻱ ﺼﻑ ﺭﺌﻴﺴﻲ ..ﻫﺫﺍ ﻫﻭ ﻜﻭﺩ ﻫـﺫﺍ
ﺍﻹﺠﺭﺍﺀ:
٢٣٩
ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل TableAdapter Class
ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻟﻴﺴﺕ ﻓﺌﺔ ﻤﻥ ﻓﺌﺎﺕ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ،ﻟﻜﻨﻬﺎ ﻓﺌﺔ ﻴﻘﻭﻡ ﻤﺼﻤﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ Typed DataSet Designerﺒﺈﻨﺸﺎﺌﻬﺎ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻙ ﻟﺘﺴـﻬﻴل ﺘﻌﺎﻤﻠـﻙ ﻤـﻊ
ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﻴﺸﺒﻪ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل TableAdapterﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ DataAdapterﻓﻲ ﻜل ﺸﻲﺀ ،ﻓﻬـﻭ
ﻴﺴﻤﺢ ﻟﻙ ﺒﺎﻟﺤﺼﻭل ﻋﻠﻰ ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻤلﺀ ﺃﺤﺩ ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ..
ﻭﻟﻜﻨﻪ ﻴﺘﻔﻭﻕ ﻋﻠﻰ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻗﺩﺭﺘﻪ ﻋﻠﻰ ﺘﻨﻔﻴﺫ ﻋﺩﺩ ﻜﺒﻴﺭ ﻤـﻥ ﺍﺴـﺘﻌﻼﻤﺎﺕ ﺍﻟﺘﺤﺩﻴـﺩ
SELECTﻟﻠﺤﺼﻭل ﻋﻠﻰ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺒﻁﺭﻕ ﻤﺨﺘﻠﻔﺔ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﺒﺸـﺭﻁ ﺃﻥ
ﻴﻜﻭﻥ ﺍﻟﻨﺎﺘﺞ ﻤﻼﺌﻤﺎ ﻟﺘﺭﻜﻴﺏ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﻴﺘﻡ ﻤﻠﺅﻩ ..ﺒﻴﻨﻤﺎ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻬﻴﺄ ﻟﻠﺘﻌﺎﻤـل ﻤـﻊ
ﺍﺴﺘﻌﻼﻡ ﻭﺍﺤﺩ ﻓﻘﻁ.
ﻭﻟﻜﻲ ﺘﻨﺸﺊ ﻤﻬﻴﺊ ﺠﺩﻭل ،ﻴﺠﺏ ﺃﻥ ﻴﺤﺘﻭﻱ ﺒﺭﻨﺎﻤﺠﻙ ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻤﺤـﺩﺩﺓ ﺍﻟﻨـﻭﻉ
ﺃﻭﻻ ..ﺍﺘﺒﻊ ﻫﺫﻩ ﺍﻟﺨﻁﻭﺍﺕ:
-ﺃﻨﺸﺊ ﻤﺸﺭﻭﻋﺎ ﺠﺩﻴﺩﺍ ﺍﺴﻤﻪ .TableAdapter
-ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻌﻠﻭﻴﺔ Projectﺍﻀﻐﻁ ﺍﻷﻤﺭ .Add New Item
-ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﺴﺭﻯ ﺍﺨﺘﺭ ﺍﻟﻌﻨﺼﺭ ،Dataﻭﻤﻥ ﺍﻟﻘﺎﺌﻤـﺔ ﺍﻟﻴﻤﻨـﻰ ﺍﺨﺘـﺭ ﺍﻟﻌﻨﺼـﺭ
،DataSetﻭﺍﻤﻨﺤﻬﺎ ﺍﻻﺴﻡ .DsAuthorsBooks.xsd
-ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﻤﺨﻁﻁ ،ﺍﻓﺘﺢ ﺼـﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ ،ﻭﺍﺴـﺤﺏ ﺍﻟﻌﻨﺼـﺭ TableAdapter
ﻭﺃﺴﻘﻁﻪ ﻋﻠﻰ ﻤﺼﻤﻡ ﺍﻟﻤﺨﻁﻁ ..ﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻟﻰ ﺒﺩﺀ ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﻟﺘﻬﻴﺌﺔ ﻤﻬﻴـﺊ
ﺍﻟﺠﺩﻭل:
TableAdapter Configuration wizard.
-ﺃﻭل ﻨﺎﻓﺫﺓ ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻟﺞ ،ﻫﻲ ﻨﺎﻓﺫﺓ ﺍﺨﺘﻴﺎﺭ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺭﺍﺩ ﺍﻻﺘﺼﺎل ﺒﻬﺎ ،ﻭﻗﺩ
ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻤﻥ ﻗﺒل ..ﺍﺨﺘﺭ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ Books.mdfﻤـﻥ ﺍﻻﺘﺼـﺎﻻﺕ
ﺍﻟﻤﺘﺎﺤﺔ ،ﺃﻭ ﺃﻨﺸﺊ ﺍﺘﺼﺎﻻ ﺠﺩﻴﺩﺍ ﺒﻬﺎ ،ﺜﻡ ﺍﻀﻐﻁ ﺍﻟﺯﺭ .Next
٢٤٠
-ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ ﺴﺘﺴﺄﻟﻙ ﺇﻥ ﻜﻨﺕ ﺘﺭﻴﺩ ﺤﻔﻅ ﻨﺹ ﺍﻻﺘﺼﺎل ﻓﻲ ﺇﻋـﺩﺍﺩﺍﺕ ﺍﻟﺒﺭﻨـﺎﻤﺞ..
ﻭﺍﻓﻕ ﻋﻠﻰ ﻫﺫﺍ ﻭﺩﻉ ﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀـﻲ BooksConnectionStringﻜﻤـﺎ ﻫـﻭ،
ﻭﺍﻀﻐﻁ .Next
-ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺘﻴﺢ ﻟﻙ ﺍﺨﺘﻴﺎﺭ ﻨﻭﻉ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴـﺩ ..ﺍﺨﺘـﺭ Use SQL Statement
ﻭﺍﻀﻐﻁ .Next
-ﻓﻲ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ ﺍﻜﺘﺏ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴﺩ ﺍﻟﺘﺎﻟﻲ:
SELECT * FROM Authors
ﻭﺍﻀﻐﻁ .Next
-ﻓﻲ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ ﻴﻤﻜﻨﻙ ﺍﺨﺘﻴﺎﺭ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﻲ ﺴﺘﻀﺎﻑ ﺇﻟﻰ ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ،ﻜﻤـﺎ
ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺨﺘﺎﺭ ﺇﻨﺸﺎﺀ ﻭﺴﻴﻠﺔ ﻟﻤلﺀ ﺠﺩﻭل ﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺴـﻴﻜﻭﻥ
ﺍﺴﻤﻬﺎ ﺍﻟﻤﺒﺩﺌﻲ Fillﻭﻴﻤﻜﻨﻙ ﻜﺘﺎﺒﺔ ﺃﻱ ﺍﺴﻡ ﺁﺨﺭ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ.
٢٤١
ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺨﺘﺎﺭ ﺇﻨﺸﺎﺀ ﻭﺴﻴﻠﺔ ﺘﻌﻴﺩ ﺠﺩﻭل ﺒﻴﺎﻨﺎﺕ DataTableﻤﻤﻠﻭﺀ ﺒﺎﻟﻨﺘـﺎﺌﺞ،
ﻟﺘﺴﺘﺨﺩﻤﻪ ﺃﻨﺕ ﺒﺎﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺘﻨﺎﺴﺒﻙ ،ﻭﺴﻴﻜﻭﻥ ﺍﺴﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﺒـﺩﺌﻴﺎ GetData
ﻭﻴﻤﻜﻨﻙ ﻜﺘﺎﺒﺔ ﺃﻱ ﺍﺴﻡ ﺁﺨﺭ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ.
ﺃﻤﺎ ﺍﻻﺨﺘﻴﺎﺭ ﺍﻷﺨﻴﺭ ،ﻓﻴﺠﻌل ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻴﻨﺸﺊ ﺍﻟﻭﺴﺎﺌل ﺍﻟﻼﺯﻤﺔ ﻟﺘﺤـﺩﻴﺙ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﺴﺘﺤﻤل ﺍﻷﺴﻤﺎﺀ Updateﻭ Insertﻭ .Delete
ﺒﻌﺩ ﺃﻥ ﺘﺤﺩﺩ ﺍﻻﺨﺘﻴﺎﺭﺍﺕ ﺍﻟﺘﻲ ﺘﻨﺎﺴﺒﻙ ،ﺍﻀﻐﻁ .Next
-ﺴﺘﻅﻬﺭ ﻨﺎﻓﺫﺓ ﺘﻠﺨﺹ ﺍﺨﺘﻴﺎﺭﺍﺘﻙ ..ﺍﻀﻐﻁ Finishﻹﻨﻬﺎﺀ ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﻭﺇﻨﺸـﺎﺀ
ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل.
ﺴﺘﺠﺩ ﻓﻲ ﻤﺼﻤﻡ ﺍﻟﻤﺨﻁﻁ ﺍﻟﻌﻨﺼﺭ ﺍﻟﻤﻭﻀﺢ ﻓـﻲ
ﺍﻟﺼﻭﺭﺓ ..ﻫﺫﺍ ﺍﻟﻌﻨﺼﺭ ﻴﻤﺜـل ﻤﺨﻁـﻁ ﺍﻟﺠـﺩﻭل
،Authorsﻭﻓﻲ ﺍﻟﺠﺯﺀ ﺍﻟﺴﻔﻠﻲ ﻤﻨﻪ ﻤﺨﻁﻁ ﻤﻬﻴـﺊ
ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﺴﻴﺴﺘﺨﺩﻡ ﻟﻠﺘﻌﺎﻤـل ﻤﻌـﻪ ،ﻭﺍﺴـﻤﻪ
ﺍﻻﻓﺘﺭﺍﻀﻲ .AuthorsTableAdapter
ﻭﻴﻤﻜﻨﻙ ﻀﻐﻁ ﺍﻟﺸﺭﻴﻁ ﺍﻟﺫﻱ ﻴﻌﺭﺽ ﺍﺴـﻡ ﻤﻬﻴـﺊ
ﺍﻟﺠﺩﻭل ﺒﺯﺭ ﺍﻟﻔـﺄﺭﺓ ﺍﻷﻴﻤـﻥ ،ﻭﺍﺨﺘﻴـﺎﺭ ﺍﻷﻤـﺭ
Propertiesﻟﻌﺭﺽ ﺨﺼﺎﺌﺹ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻓﻲ
ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ..ﻭﻫﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ ﻫﻲ:
ﺍﻻﺴﻡ :Name
ﺘﺤﺩﺩ ﺍﺴﻡ ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل.
ﺍﻟﻤﺠﺎل :Modifier
ﺘﺤﺩﺩ ﻤﺠﺎل ﻓﺌﺔ ﺍﻟﺠﺩﻭل ..ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻫﻲ Publicﻟﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻡ ﻫـﺫﻩ ﺍﻟﻔﺌـﺔ
ﺤﺘﻰ ﻤﻥ ﺨﺎﺭﺝ ﺍﻟﻤﺸﺭﻭﻉ ﺍﻟﺤﺎﻟﻲ.
٢٤٢
ﺍﻟﻔﺌﺔ ﺍﻷﻡ :Base Class
ﺘﺤﺩﺩ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ﺍﻟﺘﻲ ﺘﺭﺜﻬﺎ ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ..ﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻫﺫﻩ ﺍﻟﻔﺌـﺔ
ﻫﻲ ﻓﺌﺔ ﺍﻟﻤﻜﻭﻥ ..System.ComponentModel.Componentﻟﻜﻥ ﻻ ﻤﺎﻨﻊ ﻤـﻥ
ﺃﻥ ﺘﻜﺘﺏ ﺒﺩﻻ ﻤﻨﻬﺎ ﺃﻴﺔ ﻓﺌﺔ ﺃﺨﺭﻯ ﺒﺸﺭﻁ ﺃﻥ ﺘﻜﻭﻥ ﻤﺸﺘﻘﺔ ﻤـﻥ ﺍﻟﻔﺌـﺔ ..Component
ﻴﻤﻜﻨﻙ ﻤﺜﻼ ﺃﻥ ﺘﺭﺙ ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺃﻭ ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺭﺙ ﻓﺌﺔ ﻤﻬﻴﺊ ﺠﺩﻭل ﺁﺨﺭ!
ﺍﻻﺘﺼﺎل :Connection
ﺘﺤﺩﺩ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻴﻤﻜﻨﻙ ﺍﺨﺘﻴﺎﺭ ﺍﺘﺼﺎل ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ،ﺃﻭ ﻀـﻐﻁ
ﺍﻟﻌﻨﺼﺭ ﺍﻷﺨﻴﺭ ﻓﻴﻬﺎ ) (New Connectionﻹﻨﺸﺎﺀ ﺍﺘﺼﺎل ﺠﺩﻴﺩ.
٢٤٣
ﺃﻤﺭ ﺍﻟﺤﺫﻑ :DeleteCommand
ﺃﻤﺭ ﺍﻟﺤﺫﻑ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﺤﺫﻑ ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ..ﻻﺤﻅ ﺃﻥ ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺠـﺩﻭﻟﻴﻥ ﺴـﻴﺘﻡ
ﺇﻨﺸﺎﺅﻫﺎ ﺘﻠﻘﺎﺌﻴﺎ ﺒﻤﺠﺭﺩ ﺇﻀﺎﻓﺔ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ..ﻫﻜﺫﺍ ﺴﻴﻜﻭﻥ ﺍﻟﻤﺨﻁﻁ:
٢٤٤
Namespaceﺨﺎﺹ ﺒﻬﺎ ﻴﻜﻭﻥ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ،XTableAdaptersﺤﻴﺙ Xﻫﻭ ﺍﺴﻡ ﻓﺌـﺔ
ﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻫﻜﺫﺍ ﻤﺜﻼ ﺴﻴﻜﻭﻥ ﺍﻟﺸﻜل ﺍﻟﻌﺎﻡ ﻟﻤﻠﻑ ﻜﻭﺩ ﻓﺌﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ﻓـﻲ
ﻤﺸﺭﻭﻋﻨﺎ ﻫﺫﺍ:
{ public partial class DsAuthorsBooks :System.Data.DataSet
ﻜﻭﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ //
}
{ namespace DsAuthorsBooksTableAdapters
{ public partial class AuthorsTableAdapter : Component
ﻜﻭﺩ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ //
}
ﺍﻻﺘﺼﺎل :Connection
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﻴﺴﺘﺨﺩﻤﻪ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻟﻼﺘﺼﺎل ﺒﻘﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ..
ﻭﻴﻌﺘﻤﺩ ﻨﻭﻉ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻋﻠﻰ ﻨﻭﻉ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﺘﻌﺎﻤل ﻤﻌﻬﺎ ،ﻭﻓﻲ ﻤﺜﺎﻟﻨﺎ ﻫـﺫﺍ
ﺴﺘﻜﻭﻥ ﻤﻥ ﺍﻟﻨﻭﻉ .SqlConnection
ﻭﻴﺴﺘﺨﺩﻡ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺇﺠﺭﺍﺀﺍ ﺨﺎﺼﺎ Private Subﺍﺴﻤﻪ InitConnectionﻟﻀﺒﻁ
ﺨﺼﺎﺌﺹ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل.
٢٤٥
ﺍﻻﻨﺘﻘﺎﻻﺕ :Transaction
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﺍﻻﻨﺘﻘﺎﻻﺕ ﺍﻟﺫﻱ ﻴﺴﺘﺨﺩﻤﻪ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻟﻠﺘﺤﻜﻡ ﻓﻲ ﺍﻟﻌﻤﻠﻴﺎﺕ ﺍﻟﺘﻲ ﺘﺘﻡ
ﻋﻠﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ..ﻭﻓـﻲ ﻤﺜﺎﻟﻨـﺎ ﻫـﺫﺍ ﺴـﺘﻜﻭﻥ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﻤـﻥ ﺍﻟﻨـﻭﻉ
.SqlTransaction
ﻤﻠﺤﻭﻅﺔ:
ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻴﺴﺘﺨﺩﻡ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺩﺍﺨﻠﻴﺎ ﻟﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻟﻬﺫﺍ ﺴﺘﺠﺩ ﻓـﻲ
ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺨﺎﺼﻴﺔ ﻤﺤﻤﻴـﺔ Protected Propertyﺍﺴـﻤﻬﺎ ،Adapterﻟـﻥ
ﺘﺭﺍﻫﺎ ﻤﻥ ﺨﺎﺭﺝ ﺍﻟﻔﺌﺔ ،ﻟﻜﻥ ﺘﺴﺘﻁﻴﻊ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﺍﻟﻔﺌﺎﺕ ﺍﻟﺘـﻲ ﺘـﺭﺙ ﻓﺌـﺔ ﻤﻬﻴـﺊ
ﺍﻟﺠﺩﻭل ،ﺃﻭ ﻓﻲ ﺃﻱ ﻜﻭﺩ ﺇﻀﺎﻓﻲ ﺘﻜﺘﺒﻪ ﻓﻲ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺒﻨﻔﺴﻙ ..ﻭﻴﺴـﺘﺨﺩﻡ ﻤﻬﻴـﺊ
ﺍﻟﺠﺩﻭل ﺇﺠﺭﺍﺀ ﺨﺎﺼـﺎ private voidﺍﺴـﻤﻪ InitAdapterﻟﻭﻀـﻊ ﺍﻟﻘـﻴﻡ ﻓـﻲ
ﺨﺼﺎﺌﺹ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻜﻤــﺎ ﻴﺤﺘــﻭﻱ ﻤﻬﻴــﺊ ﺍﻟﺠــﺩﻭل ﻋﻠــﻰ ﺨﺎﺼــﻴﺔ ﻤﺤﻤﻴــﺔ ﺃﺨــﺭﻯ ﺍﺴــﻤﻬﺎ
،CommandCollectionﻭﻫﻲ ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻜﺎﺌﻨﺎﺕ ﺍﻷﻭﺍﻤـﺭ ﺍﻟﺘـﻲ
ﻴﺴﺘﺨﺩﻤﻬﺎ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ،ﻭﺒﻬﺫﺍ ﻴﺴﺘﻁﻴﻊ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺃﻜﺜﺭ ﻤﻥ ﺍﺴـﺘﻌﻼﻡ
ﻜﻤﺎ ﺴﻨﺭﻯ ﻻﺤﻘﺎ.
ﻭﻴﺴﺘﺨﺩﻡ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺇﺠﺭﺍﺀ ﺨﺎﺼﺎ ﺍﺴـﻤﻪ InitCommandCollectionﻟﻭﻀـﻊ
ﻜﺎﺌﻨﺎﺕ ﺍﻷﻭﺍﻤﺭ ﻓﻲ ﻫﺫﻩ ﺍﻟﻤﺼﻔﻭﻓﺔ ﻭﻀﺒﻁ ﺨﺼﺎﺌﺼﻬﺎ.
ﺘﺤﺩﻴﺙ :Update
ﺘﺤﻔﻅ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻻﺤﻅ ﺃﻥ ﻜل ﻤﺎ ﺘﻔﻌﻠﻪ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ،ﻫﻭ ﺍﺴـﺘﺩﻋﺎﺀ
ﺍﻟﻭﺴﻴﻠﺔ Updateﺍﻟﺨﺎﺼﺔ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺩﺍﺨﻠﻲ ..ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ﺍﻟﻤﺭﺍﺩ ﺤﻔﻅ ﺘﻐﻴﻴﺭﺍﺘﻪ.
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺤﻴﺙ ﻴﻘﻭﻡ ﻤﻬﻴﺊ ﺍﻟﺠـﺩﻭل ﺒﻘـﺭﺍﺀﺓ
ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻤﻥ ﺍﻟﺠﺩﻭل ﺍﻟﺨﺎﺹ ﺒﻪ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺩﻭﻥ ﻏﻴﺭﻩ ﻤﻥ ﺍﻟﺠﺩﺍﻭل..
ﻤﺜﻼ :ﺘﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﺼﻴﻐﺔ ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻓﻲ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ:
;)"return this.Adapter.Update(dataSet, "Authors
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺼﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ DataRowﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺤﻔﻅ ﺘﻐﻴﻴﺭﺍﺘـﻪ
ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-٤ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺼﻔﻭﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﺭﻴـﺩ ﺤﻔـﻅ
ﺘﻐﻴﻴﺭﺍﺘﻬﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-٥ﺍﻟﺼﻴﻐﺔ ﺍﻟﺨﺎﻤﺴﺔ ﺘﺴﺘﻘﺒل ﻗﻴﻡ ﺍﻟﺼﻑ ﺍﻟﻤﺭﺍﺩ ﺤﻔﻅﻪ ﻓﻲ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ..ﻭﻟﻬـﺫﻩ
ﺍﻟﺼﻴﻐﺔ ﻋﺩﺓ ﻤﻌﺎﻤﻼﺕ ،ﻜل ﻤﻨﻬﺎ ﻴﺴﺘﻘﺒل ﻗﻴﻤﺔ ﺃﺤﺩ ﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺼـﻑ..
ﻤﺜﻼ ،ﺴﺘﺤﺘﻭﻱ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤـﺅﻟﻔﻴﻥ ﻋﻠـﻰ ﻫـﺫﻩ ﺍﻟﻤﻌـﺎﻤﻼﺕ
٢٤٧
ﺒﺎﻟﺘﺭﺘﻴـــــﺏ،Original_ID ،About ،Phone ،CountryID ،Author :
.Original_RowVersion
-٦ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺩﺴﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤـل ﺇﻀـﺎﻓﻲ ﻴﺴـﺘﻘﺒل ﺍﻟﻤﻔﺘـﺎﺡ
ﺍﻷﺴﺎﺴﻲ ﻟﻠﺠﺩﻭل )ﺍﻟﺤﻘل IDﻓﻲ ﻤﺜﺎﻟﻨﺎ ﻫﺫﺍ(.
ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ ﻴﺨﺒﺭﻙ ﺒﻌﺩﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﻡ ﺘﺤـﺩﻴﺜﻬﺎ ﻓـﻲ ﻗﺎﻋـﺩﺓ
ـﺎﺒﻕ
ـﻜﻠﺔ ﺘﻁـ ـﺩﻭﺙ ﻤﺸــﺎﻩ ﺤــﺫﺍ ﻤﻌﻨـ
ـﻔﺭﺍ ،ﻓﻬـ ـﺎﺘﺞ ﺼــﺎﻥ ﺍﻟﻨـ
ـﺈﺫﺍ ﻜـ
ـﺎﺕ ،ﻓـ
ﺍﻟﺒﻴﺎﻨـ
.Concurrency Violation
ﺇﺩﺭﺍﺝ :Insert
ﺘﺩﺭﺝ ﺼﻔﺎ ﺠﺩﻴﺩﺍ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩﺍ ﻤﻥ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺒﻌﺩﺩ ﺃﻋﻤـﺩﺓ
ﺍﻟﺠﺩﻭل ،ﻻﺴﺘﻘﺒﺎل ﻗﻴﻡ ﺍﻟﺼﻑ ﺍﻟﻤﺭﺍﺩ ﺇﻀﺎﻓﺘﻪ.
ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ ﻴﺨﺒﺭﻙ ﺒﻌﺩﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘـﻲ ﺃﻀـﻴﻔﺕ ﺇﻟـﻰ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﺈﺫﺍ ﻜﺎﻥ ﺍﻟﻨﺎﺘﺞ ﺼﻔﺭﺍ ،ﻓﻬﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺭﻓﻀـﺕ ﺇﺩﺭﺍﺝ ﺍﻟﺼـﻑ
ﺒﺴﺒﺏ ﻭﺠﻭﺩ ﻤﺸﻜﻠﺔ ﻓﻲ ﻗﻴﻤﺔ ﺇﺤﺩﻯ ﺨﺎﻨﺎﺘﻪ.
ﺤﺫﻑ :Delete
ﺘﺤﺫﻑ ﺴﺠﻼ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﺘﻤﻴﺯ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺴﺠل ﺒﺎﺴﺘﻘﺒﺎل ﻤﻔﺘﺎﺤﻪ ﺍﻷﺴﺎﺴﻲ
Original_IDﻭﺇﺼﺩﺍﺭﻩ Original_RowVersionﻜﻤﻌـﺎﻤﻠﻴﻥ ..ﻻﺤـﻅ ﺃﻨﻨـﺎ ﻻ
ﻨﺴﺘﺨﺩﻡ ﺇﺼﺩﺍﺭ ﺍﻟﺴﺠل ﻓﻲ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ،ﻟﻬﺫﺍ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓـﻲ ﻤﻬﻴـﺊ ﺠـﺩﻭل
ﺍﻟﻜﺘﺏ ﻤﻌﺎﻤﻼﺕ ﺒﻌﺩﺩ ﺤﻘﻭل ﺍﻟﺠﺩﻭل ،ﻟﻠﺒﺤﺙ ﻋﻥ ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺒﺩﻻﻟﺔ ﻜل ﻗﻴﻤﻪ.
ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ ﻴﺨﺒﺭﻙ ﺒﻌﺩﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﻡ ﺤـﺫﻓﻬﺎ ﻤـﻥ ﻗﺎﻋـﺩﺓ
ـﺎﺒﻕ
ـﻜﻠﺔ ﺘﻁـ
ـﺩﻭﺙ ﻤﺸـ
ـﺎﻩ ﺤـ
ـﺫﺍ ﻤﻌﻨـ
ـﻔﺭﺍ ،ﻓﻬـ
ـﺎﺘﺞ ﺼـ
ـﺎﻥ ﺍﻟﻨـ
ـﺈﺫﺍ ﻜـ
ـﺎﺕ ،ﻓـ
ﺍﻟﺒﻴﺎﻨـ
.Concurrency Violation
٢٤٨
ﺇﻀﺎﻓﺔ ﺍﺴﺘﻌﻼﻤﺎﺕ ﺠﺩﻴﺩﺓ ﺇﻟﻰ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل:
ﺇﻟﻰ ﺍﻵﻥ ،ﻻ ﻴﺒﺩﻭ ﺃﻥ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻴﻘﺩﻡ ﺸﻴﺌﺎ ﺠﺩﻴﺩﺍ ﻴﻤﻴﺯﻩ ﻋﻥ ﻤﻬﻴـﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﻌـﺎﺩﻱ..
ﻓﺎﻟﺤﻘﻴﻘﺔ ﺃﻥ ﻤﺯﻴﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻴﺔ ،ﻫﻲ ﻗﺩﺭﺘﻙ ﻋﻠﻰ ﺇﻀـﺎﻓﺔ ﺃﻱ ﻋـﺩﺩ ﺘﺭﻴـﺩﻩ ﻤـﻥ
ﺍﻻﺴﺘﻌﻼﻤﺎﺕ ﺇﻟﻴﻪ ،ﻤﺎ ﺩﺍﻤﺕ ﺘﻠﺘﺯﻡ ﺒﺄﺤﺩ ﺍﻟﺸﺭﻁﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ:
-١ﺃﻥ ﺘﻌﻴﺩ ﺴﺠﻼﺕ ﻟﻬﺎ ﻨﻔﺱ ﺘﺭﻜﻴﺏ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﻴﺘﻌﺎﻤل ﻤﻌﻪ ﻤﻬﻴﺊ ﺍﻟﺠـﺩﻭل ..ﻟـﻴﺱ
ﻤﻥ ﺍﻟﻤﻨﻁﻘﻲ ﻤﺜﻼ ﺃﻥ ﺘﻀﻴﻑ ﺇﻟﻰ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﺍﺴﺘﻌﻼﻤﺎ ﻴﻌﻴـﺩ ﺴـﺠﻼﺕ
ﺍﻟﻜﺘﺏ.
-٢ﺃﻥ ﺘﻌﻴﺩ ﻗﻴﻤﺔ ﻤﻨﻔﺭﺩﺓ ..Scalar Valueﻴﻤﻜﻨﻙ ﻤﺜﻼ ﺃﻥ ﺘﻀﻴﻑ ﺇﻟﻰ ﻤﻬﻴـﺊ ﺠـﺩﻭل
ﺍﻟﻤﺅﻟﻔﻴﻥ ﺍﺴﺘﻌﻼﻤﺎ ﻴﻌﻴﺩ ﻋﺩﺩ ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﺃﻭ ﻋﺩﺩ ﻜﺘﺏ ﺃﺤﺩ ﺍﻟﻤﺅﻟﻔﻴﻥ.
ﻭﻹﻀﺎﻓﺔ ﺍﺴﺘﻌﻼﻡ ﺠﺩﻴﺩ ﺇﻟﻰ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ،ﺍﻀﻐﻁ ﺍﺴﻡ ﺍﻟﻤﻬﻴﺊ ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﻤﺼﻤﻡ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ
ﺍﻷﻴﻤﻥ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤﺭ ..Add Queryﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻟﻰ ﺒﺩﺀ ﺍﻟﻤﻌـﺎﻟﺞ
ﺍﻟﺴﺤﺭﻱ ﻟﺘﻬﻴﺌﺔ ﺍﺴﺘﻌﻼﻡ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل:
TableAdapter Query Configuration Wizard
ﺩﻋﻨﺎ ﻨﺴﺘﺨﺩﻡ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻟﺞ ﻹﻀﺎﻓﺔ ﺍﺴﺘﻌﻼﻡ ﺇﻟﻰ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ،ﻴﻌﻴﺩ ﻜﺘﺏ ﺍﻟﻤﺅﻟﻑ ﺍﻟـﺫﻱ
ﻨﺭﻴﺩﻩ:
-ﺍﻟﻨﺎﻓﺫﺓ ﺍﻷﻭﻟﻰ ﺘﺴﺄﻟﻙ ﻋﻥ ﻨﻭﻉ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟـﺫﻱ ﺘﺭﻴـﺩﻩ )ﺠﻤﻠـﺔ SQLﺃﻡ ﺇﺠـﺭﺍﺀ
ﻤﺨﺯﻥ( ..ﺍﺨﺘﺭ Use SQL Statementﻭﺍﻀﻐﻁ .Next
-ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺴﺄﻟﻙ ﻋﻥ ﻨﻭﻉ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺫﻱ ﺘﺭﻴﺩﻩ:
ﻴﻤﻜﻨﻙ ﺍﻻﺨﺘﻴﺎﺭ ﻤﻥ ﺒﻴﻥ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺘﺎﻟﻴﺔ:
ﺃ .ﺠﻤﻠﺔ ﺍﺴﺘﻌﻼﻡ ﺘﻌﻴﺩ ﺼﻔﻭﻓﺎ:
SELECT statement witch returns rows.
ﺏ .ﺠﻤﻠﺔ ﺍﺴﺘﻌﻼﻡ ﺘﻌﻴﺩ ﻗﻴﻤﺔ ﻤﻨﻔﺭﺩﺓ:
SELECT statement witch returns a single value.
ﺕ .ﺘﺤﺩﻴﺙ .UPDATE
ﺙ .ﺤﺫﻑ .DELETE
ﺝ .ﺇﺩﺭﺍﺝ .INSERT
٢٤٩
ﺍﺨﺘﺭ ﺃﻭل ﺍﺨﺘﻴﺎﺭ ،ﻭﺍﻀﻐﻁ .Next
٢٥٠
ﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﺘﻌﺭﻴﻑ ﻫﺎﺘﻴﻥ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﻗﺩ ﺃﻀﻴﻑ ﺇﻟﻰ ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ،ﻭﺴﻴﻜﻭﻥ ﻟﻜل ﻤﻨﻬﻤﺎ
ﻤﻌﺎﻤل ﻨﺼﻲ ﻴﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ..ﻭﻋﻤﻭﻤﺎ ،ﻴﻘﻭﻡ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﺒﺘﻌﺭﻴﻑ ﺍﻟﻤﻌـﺎﻤﻼﺕ
ﺍﻟﻤﻨﺎﺴﺒﺔ ﻟﻨﻭﻉ ﺍﻟﺤﻘل ﺍﻟﺫﻱ ﺘﺴﺘﻌﻠﻡ ﻋﻨﻪ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻻﺤﻅ ﺃﻨﻙ ﻟﻭ ﺍﺴﺘﺨﺩﻤﺕ ﺍﺴﺘﻌﻼﻤﺎ ﻴﻌﻴﺩ ﻨﺘﺎﺌﺞ ﻏﻴﺭ ﻤﺭﻏﻭﺒﺔ ،ﻓﺴﻴﻌﺭﺽ ﻟﻙ ﻤﻬﻴـﺊ ﺍﻟﺠـﺩﻭل
ﺭﺴﺎﻟﺔ ﺘﺤﺫﺭﻙ ﻤﻥ ﺃﻥ ﻨﺘﻴﺠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﻻ ﺘﻨﺎﺴﺏ ﻤﺨﻁﻁ ﺍﻟﺠـﺩﻭل ..ﻭﻟـﻭ ﺃﺭﺩﺕ ﺘﺼـﺤﻴﺢ
ﺍﻻﺴﺘﻌﻼﻡ ﻓﺎﻀﻐﻁ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ،ﻓﻭﻕ ﺍﻟﺼﻑ ﺍﻟﺫﻱ ﻴﻌﺭﺽ ﺍﺴﻤﻲ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺠـﺩﻴﺘﻴﻥ
ﻓﻲ ﻤﺨﻁﻁ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤـﺭ ..Configureﺴـﻴﻌﺭﺽ
ﻫﺫﺍ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﻲ ﺃﺩﺨﻠﺕ ﻓﻴﻬﺎ ﺍﻻﺴﺘﻌﻼﻡ ..ﻴﻤﻜﻨﻙ ﺘﺼﺤﻴﺤﻪ ﻜﻤﺎ ﺘﺭﻴﺩ ﻭﻀﻐﻁ ﺍﻟﺯﺭ .Finish
ﻭﻟﺤﺫﻑ ﺍﻻﺴﺘﻌﻼﻡ ،ﺤﺩﺩﻩ ﻓﻲ ﻤﺨﻁﻁ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ،ﻭﺍﻀﻐﻁ .Delete
ﺩﻋﻨﺎ ﺃﻴﻀﺎ ﻨﻨﺸﺊ ﺍﺴﺘﻌﻼﻤﺎ ﻓﻲ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﻴﻌﻴﺩ ﻟﻨﺎ ﻋﺩﺩ ﻜﺘﺏ ﻤﺅﻟﻑ ﻤﻌـﻴﻥ ..ﺩﻋﻨـﺎ
ﻨﺠﺭﺏ ﻁﺭﻴﻘﺔ ﺃﺨﺭﻯ ﻫﺫﻩ ﺍﻟﻤﺭﺓ ..ﻤﻥ ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ ﺍﺴﺤﺏ ﺍﻟﻌﻨﺼﺭ Queryﻭﺃﺴـﻘﻁﻪ
ﻓﻭﻕ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ..ﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻟﻰ ﺇﻁﻼﻕ ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ،ﺤﻴﺙ ﻴﻤﻜﻨـﻙ ﺍﺘﺒـﺎﻉ
ﻨﻔﺱ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺴﺎﺒﻘﺔ ،ﻟﻜﻥ ﻤﻊ ﺍﺨﺘﻴﺎﺭ:
SELECT Statement that returns a single value
ﻭﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻜﺘﺏ:
SELECT COUNT(BOOK) FROM Authors, Books
WHERE AuthorID = Authors.ID AND Author = @Author
ﻭﺍﻀﻐﻁ ..Nextﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﺘﺘﻴﺢ ﻟﻙ ﺘﺴﻤﻴﺔ ﺍﻟﺩﺍﻟﺔ ﺍﻟﺘﻲ ﺘﻨﻔﺫ ﻫﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ ..ﺴـﻴﻜﻭﻥ
ﻟﻬﺫﻩ ﺍﻟﺩﺍﻟﺔ ﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ ..ScalarQueryﻏﻴـﺭﻩ ﺇﻟـﻰ GetAuthorBooksCount
ﻭﺍﻀﻐﻁ ..Finishﺴﻴﻅﻬﺭ ﺍﻻﺴﻡ ﺍﻟﺠﺩﻴﺩ ﻓﻲ ﻤﺨﻁﻁ ﻤﻬﻴﺊ ﺍﻟﺠـﺩﻭل ﻜﻤـﺎ ﻓـﻲ ﺍﻟﺼـﻭﺭﺓ،
ﻭﺴﺘﻀﺎﻑ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺇﻟﻰ ﻓﺌـﺔ ﻤﻬﻴـﺊ
ﺍﻟﺠﺩﻭل ،ﺤﻴﺙ ﺴﺘﺴﺘﻘﺒل ﻨﺼﺎ ﻴﻤﺜل ﺍﺴـﻡ
ﺍﻟﻤﺅﻟﻑ ،ﻭﺘﻌﻴﺩ ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ ﻴﻤﺜل ﻋـﺩﺩ
ﻜﺘﺒﻪ.
٢٥١
ﻤﻠﺤﻭﻅﺔ:١
ﻋﻨﺩ ﺇﻨﺸﺎﺀ ﺍﻻﺴﺘﻌﻼﻡ ﻋﻥ ﺤﻘل ﻴﻤﻜﻥ ﺘﺭﻜﻪ ﻓﺎﺭﻏﺎ )ﻤﺜل ﺍﻟﺤﻘل Phoneﻓﻲ ﺠـﺩﻭل ﺍﻟﻜﺘـﺏ(،
ﻴﻘﻭﻡ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺘﻌﺭﻴﻑ ﻤﻌﺎﻤل ﺍﻟﻭﺴﻴﻠﺔ FillByﺒﺤﻴﺙ ﻴﻜﻭﻥ ﻗﺎﺒﻼ ﻟﻼﻨﻌـﺩﺍﻡ Nullable
)ﻤﺜﻼ :ﺴﻴﻜﻭﻥ ﻤﻌﺎﻤل ﺍﻟﻭﺴﻴﻠﺔ FillByPhoneﻤﻥ ﻨﻭﻉ ﺍﻟـﻨﺹ ﺍﻟﻤﻨﻌـﺩﻡ ? ..(Stringﻫـﺫﺍ
ﻴﺘﻴﺢ ﻟﻙ ﺇﺭﺴﺎل ﺍﻟﻘﻴﻤﺔ Nothingﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﻤﺎ ﺯﺍل ﻓﻴﻬﺎ ﻫﺫﺍ
ﺍﻟﺤﻘل ﻓﺎﺭﻏﺎ.
ﻤﻠﺤﻭﻅﺔ:٢
ﺇﺫﺍ ﺃﺭﺩﺕ ﺇﻀﺎﻓﺔ ﻭﺴﻴﻠﺔ ﻟﺘﻨﻔﻴﺫ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻥ ،ﻓﺎﺘﺒﻊ ﻨﻔﺱ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﻤﺄﻟﻭﻓﺔ ﻹﻀﺎﻓﺔ ﺍﺴﺘﻌﻼﻡ،
ﻟﻜﻥ ﻫﺫﻩ ﺍﻟﻤﺭﺓ ﺍﺨﺘﺭ ﻨﻭﻉ ﺍﻻﺴﺘﻌﻼﻡ:
Existing Stored Procedure
ﻭﺍﻀﻐﻁ ..Nextﺴﺘﻅﻬﺭ ﻨﺎﻓﺫﺓ ﺘﻌﺭﺽ ﻗﺎﺌﻤﺔ ﻤﻨﺴﺩﻟﺔ ﺒﻬﺎ ﺃﺴﻤﺎﺀ ﺍﻹﺠﺭﺍﺀﺍﺕ ﺍﻟﻤﺨﺯﻨـﺔ ﻓـﻲ
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﺍﺨﺘﺭ ﺍﻹﺠﺭﺍﺀ ..GetAuthorBooksﺴﻴﻌﺭﺽ ﺍﻟﻨﺼﻑ ﺍﻟﺴﻔﻠﻲ ﻤﻥ ﺍﻟﻨﺎﻓﺫﺓ
ﺒﻴﺎﻨﺎﺕ ﻫﺫﺍ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ :ﻋﻠﻰ ﺍﻟﻴﺴﺎﺭ ﺴﺘﻅﻬﺭ ﻤﻌﺎﻤﻼﺕ ﺍﻹﺠﺭﺍﺀ ،ﻭﻋﻠﻰ ﺍﻟﻴﻤﻴﻥ ﺴـﺘﻅﻬﺭ
ﺍﻷﻋﻤﺩﺓ ﺍﻟﻨﺎﺘﺠﺔ ﻋﻥ ﺘﻨﻔﻴﺫﻩ ،ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
٢٥٢
ﺍﻀﻐﻁ Nextﻟﻼﻨﺘﻘﺎل ﺇﻟﻰ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ ،ﻭﻫﻲ ﺘﺴﺄﻟﻙ ﻋﻥ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻌﺎﺌﺩﺓ ﻤﻥ ﺍﻟﻭﺴـﻴﻠﺔ ﺍﻟﺘـﻲ
ﺴﺘﻨﻔﺫ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ..ﻫﺫﻩ ﺍﻟﻘﻴﻤﺔ ﻗﺩ ﺘﻜﻭﻥ:
-ﻗﻴﻤﺔ ﺠﺩﻭﻟﻴﺔ ،Tabular Valueﺤﻴﺙ ﺘﻌﻴﺩ ﺍﻟﻭﺴﻴﻠﺔ ﻜـﺎﺌﻥ ﺠـﺩﻭل ﻴﺤﺘـﻭﻱ ﻋﻠـﻰ
ﺍﻟﺼﻔﻭﻑ ﺍﻟﻨﺎﺘﺠﺔ.
-ﻗﻴﻤﺔ ﻤﻨﻔﺭﺩﺓ ،Single Valueﺤﻴﺙ ﺘﻌﻴﺩ ﺍﻟﻭﺴﻴﻠﺔ ﻗﻴﻤﺔ ﺃﻭل ﺨﺎﻨﺔ ﻓﻲ ﺃﻭل ﻋﻤﻭﺩ ﻓﻲ
ﺍﻟﻨﺘﻴﺠﺔ.
-ﻭﻻ ﻗﻴﻤﺔ ،No Valueﺤﻴﺙ ﺴﺘﻜﻭﻥ ﺍﻟﻭﺴﻴﻠﺔ ﺒﺩﻭﻥ ﻗﻴﻤـﺔ ﻋﺎﺌـﺩﺓ ،ﻭﻫـﺫﺍ ﻤﻨﺎﺴـﺏ
ﻟﻺﺠﺭﺍﺀﺍﺕ ﺍﻟﻤﺨﺯﻨﺔ ﺍﻟﺘﻲ ﻻ ﺘﻌﻴﺩ ﻨﺎﺘﺠﺎ.
ﺍﺨﺘﺭ ﻤﺎ ﻴﻨﺎﺴﺒﻙ ﻭﺍﻀﻐﻁ ..Nextﺒﺎﻗﻲ ﺍﻟﺨﻁﻭﺍﺕ ﻻ ﺠﺩﻴﺩ ﻓﻴﻬﺎ.
٢٥٣
ﺇﻨﺸﺎﺀ ﺍﺴﺘﻌﻼﻤﺎﺕ ﻋﺎﻤﺔ :Global Queries
ﻴﻤﻜﻨﻙ ﺇﻨﺸﺎﺀ ﻤﻬﻴﺊ ﺠﺩﻭل ﻟﺘﻨﻔﻴﺫ ﺍﺴﺘﻌﻼﻤﺎﺕ ﻋﺎﻤﺔ ،ﻜﺤﺴﺎﺏ ﺩﺍﻟﺔ ﺘﺠﻤﻴﻊ ،ﺃﻭ ﺘﻨﻔﻴﺫ ﺍﺴﺘﻌﻼﻤﺎﺕ
ﺍﻟﺤﺫﻑ ﻭﺍﻹﺩﺭﺍﺝ ﺩﻭﻥ ﺃﻥ ﻴﻜﻭﻥ ﻤﺭﺘﺒﻁﺎ ﺒﺠﺩﻭل ﻤﻌﻴﻥ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻟﻔﻌـل ﻫـﺫﺍ،
ﺍﻀﻐﻁ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ﻓﻲ ﺃﻱ ﻤﻨﻁﻘﺔ ﺨﺎﻟﻴﺔ ﻤﻥ ﻤﺼﻤﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻤـﻥ ﺍﻟﻘﺎﺌﻤـﺔ
ﺍﻟﻔﺭﻋﻴﺔ Addﺍﻀﻐﻁ ﺍﻷﻤﺭ ..Queryﺃﻭ ﺍﺴﺤﺏ ﺍﻟﻌﻨﺼﺭ Queryﻤـﻥ ﺼـﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ
ﻭﺃﻟﻘﻪ ﻋﻠﻰ ﺃﻱ ﻤﻨﻁﻘﺔ ﻓﺎﺭﻏﺔ ﻤﻥ ﻤﺼﻤﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻟﻰ ﺇﻁﻼﻕ ﺍﻟﻤﻌـﺎﻟﺞ
ﺍﻟﺴﺤﺭﻱ ﻟﺘﻬﻴﺌﺔ ﺍﻻﺴﺘﻌﻼﻡ ،ﻟﻜﻨﻪ ﺴﻴﺒﺩﺃ ﻫﺫﻩ ﺍﻟﻤﺭﺓ ﺒﻨﺎﻓﺫﺓ ﺍﺨﺘﻴﺎﺭ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺜـﻡ
ﻴﺴﺘﻤﺭ ﺒﻨﻔﺱ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺴﺎﺒﻘﺔ ،ﻟﻜﻨﻙ ﻟﻥ ﺘﺴﺘﻁﻴﻊ ﺇﻨﺸﺎﺀ ﺍﺴﺘﻌﻼﻡ ﻴﻌﻴﺩ ﺴﺠﻼﺕ ﺒﻬﺫﺍ ﺍﻟﻤﻌﺎﻟﺞ..
ﻴﻤﻜﻨﻙ ﻓﻘﻁ ﺇﻨﺸﺎﺀ ﺍﺴﺘﻌﻼﻤﺎﺕ ﺘﻌﻴﺩ ﻗﻴﻤﺎ ﻤﻨﻔﺭﺩﺓ ،ﺃﻭ ﺍﺴﺘﻌﻼﻤﺎﺕ ﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻟﺤﺫﻑ ﻭﺍﻹﺩﺭﺍﺝ..
ﻭﺒﻌﺩ ﺃﻥ ﺘﻨﻬﻲ ﺍﻟﻤﻌﺎﻟﺞ ،ﺴﺘﺠﺩ ﻤﻬﻴﺊ ﺠﺩﻭل ﺠﺩﻴﺩ ﻗﺩ ﺃﻀﻴﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺴـﻴﻜﻭﻥ
ﺍﺴﻤﻪ QueriesTableAdapterﻭﻫﻭ ﺍﺴﻡ ﻻ ﻴﻤﻜﻨﻙ ﺘﻐﻴﻴﺭﻩ ..ﻭﺃﻴﺔ ﺍﺴﺘﻌﻼﻤﺎﺕ ﻋﺎﻤﺔ ﺃﺨﺭﻯ
ﺴﺘﻨﺸﺌﻬﺎ ﺴﺘﻀﺎﻑ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻤﻬﻴـﺊ ،.ﻭﻗـﺩ ﺃﻀـﻔﻨﺎ ﺇﻟﻴـﻪ ﻓـﻲ ﻤﺸـﺭﻭﻋﻨﺎ ﻫـﺫﺍ ﺍﻟﺩﺍﻟـﺔ
GetAuthorsCountﺍﻟﺘﻲ ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﻭﺍﻟﺩﺍﻟﺔ GetBooksCountﺍﻟﺘﻲ ﺘﻌﻴﺩ ﻋﺩﺩ
ﺍﻟﻜﺘﺏ.
ﺘﺫﻜﺭ ﻤﺭﺓ ﺃﺨﺭﻯ ،ﺃﻥ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻻﺴﺘﻌﻼﻤﺎﺕ ﻻ ﻴﺘﻌﺎﻤل ﻤـﻊ ﺍﺴـﺘﻌﻼﻤﺎﺕ ﺃﻭ ﺇﺠـﺭﺍﺀﺍﺕ
ﻤﺨﺯﻨﺔ ﺘﻌﻴﺩ ﺴﺠﻼﺕ ..ﻫﻭ ﻓﻘﻁ ﻴﺘﻌﺎﻤل ﻤﻊ ﺍﺴﺘﻌﻼﻤﺎﺕ ﺃﻭ ﺇﺠﺭﺍﺀﺍﺕ ﻤﺨﺯﻨﺔ ﺘﻌﻴﺩ ﻗﻴﻤﺎ ﻤﻔﺭﺩﺓ،
ﻭﺇﺫﺍ ﺍﺨﺘﺭﺕ ﺇﺠﺭﺍﺀﺍ ﻤﺨﺯﻨﺎ ﻴﻌﻴﺩ ﺴﺠﻼﺕ ،ﻓﺴﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻗﻴﻤﺔ ﺃﻭل ﺨﺎﻨﺔ ﻓﻲ ﺃﻭل ﺼﻑ
ﻓﻲ ﺍﻟﻨﺘﻴﺠﺔ!
٢٥٤
ﻤﻌﺎﻴﻨﺔ ﺒﻴﺎﻨﺎﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ:
ﻴﻤﻜﻨﻙ ﻤﻌﺎﻴﻨﺔ ﻨﺘﻴﺠﺔ ﺃﻱ ﺩﺍﻟﺔ ﻓﻲ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ،ﺒﺎﻟﻀﻐﻁ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ﻓﻲ ﺃﻱ ﻤﻭﻀـﻊ
ﺨﺎل ﻓﻲ ﻤﺼﻤﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺃﻭ ﻓﻭﻕ ﺘﺼﻤﻴﻡ ﺃﺤﺩ ﺍﻟﺠﺩﺍﻭل ﺃﻭ ﺃﺤﺩ ﻤﻬﻴﺌـﺎﺕ ﺍﻟﺠـﺩﺍﻭل،
ﻭﻀﻐﻁ ﺍﻷﻤﺭ Preview Dataﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ..ﻭﺴﺘﺠﺩ ﻨﻔﺱ ﺍﻷﻤـﺭ ﻓـﻲ ﺍﻟﻘﺎﺌﻤـﺔ
ﺍﻟﺭﺌﻴﺴﻴﺔ .Data
ﺴﺘﻅﻬﺭ ﻨﺎﻓﺫﺓ ﻋﺭﺽ ﺍﻟﻨﺘﺎﺌﺞ ،ﺤﻴﺙ ﻴﻤﻜﻨﻙ ﺇﺴﺩﺍل ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻌﻠﻭﻴﺔ ،ﻭﺍﺨﺘﻴـﺎﺭ ﻤﻬﻴـﺊ ﺍﻟﺠـﺩﻭل
ﺍﻟﺫﻱ ﺘﺭﻴﺩﻩ ،ﻭﺍﻟﺩﺍﻟﺔ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺘﻨﻔﻴﺫﻫﺎ ﻤﻨﻪ ،ﺜﻡ ﻭﻀﻊ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﻤﻭﺠـﻭﺩ ﻋﻠـﻰ
ﻴﻤﻴﻥ ﺍﻟﻨﺎﻓﺫﺓ ﺇﻥ ﻜﺎﻨﺕ ﺍﻟﺩﺍﻟﺔ ﺘﺤﺘﺎﺝ ﻤﻌﺎﻤﻼﺕ ،ﺜﻡ ﻀﻐﻁ ﺍﻟﺯﺭ Previewﻟﺭﺅﻴﺔ ﺍﻟﻨﺘﻴﺠﺔ.
٢٥٥
ﻓﺌﺔ ﻤﺩﻴﺭ ﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل TableAdapterManager
ﻅﻬﺭﺕ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻓﻲ ﺩﻭﺕ ﻨﺕ ،٢٠٠٨ﻭﻴﺘﻡ ﺇﻨﺘﺎﺠﻬﺎ ﺁﻟﻴﺎ ﻋﻨﺩ ﺇﻨﺸﺎﺀ ﻤﻬﻴﺌﺎﺕ ﺍﻟﺠـﺩﺍﻭل ﺍﻟﺘـﻲ
ﺘﺭﺒﻁﻬﺎ ﻋﻼﻗﺎﺕ ،ﻟﺘﺴﻤﺢ ﻟﻙ ﺒﺈﺠﺭﺍﺀ ﺍﻟﺘﺤﺩﻴﺙ ﺍﻟﻤﺘﺭﺍﻜﺏ ،Hierarchical Updateﻭﻓﻴﻪ ﻴـﺘﻡ
ﺘﺤﺩﻴﺙ ﺍﻟﺠﺩﺍﻭل ﺍﻟﻤﺘﺭﺍﺒﻁﺔ ﻤﻌﺎ ،ﻤﻊ ﻗﺩﺭﺘﻙ ﻋﻠﻰ ﺘﺤﺩﻴﺩ ﺍﻟﺘﺭﺘﻴﺏ ﺍﻟﺼﺤﻴﺢ ﻹﺠـﺭﺍﺀ ﻋﻤﻠﻴـﺎﺕ
ﺍﻟﺘﺤﺩﻴﺙ ،ﻟﻤﺭﺍﻋﺎﺓ ﺍﻟﻘﻴﻭﺩ ﺍﻟﻤﻔﺭﻭﻀﺔ ﻋﻠﻰ ﺍﻟﺠﺩﺍﻭل.
ﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﻤﻨﻊ ﺇﻨﺘﺎﺝ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ..ﻟﻔﻌل ﻫﺫﺍ ﺍﻀﻐﻁ ﺒﺯﺭ ﺍﻟﻔﺎﺭﺓ ﺍﻷﻴﻤﻥ ﻓﻲ ﺃﻱ ﻤﻭﻀـﻊ
ﻓﺎﺭﻍ ﻤﻥ ﻤﺨﻁﻁ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀـﻐﻁ ﺍﻷﻤـﺭ ،Properties
ﻭﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ﻏﻴﺭ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ Hierarchical Updateﺇﻟﻰ .False
ﺍﻻﺘﺼﺎل :Connection
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ.
٢٥٧
ﺇﻀﺎﻓﺔ ﺃﻜﻭﺍﺩ ﺨﺎﺼﺔ ﺒﻙ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﻟﺠﺩﺍﻭل ﻭﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل:
ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻀﻴﻑ ﺒﻌﺽ ﺍﻟﻭﺴﺎﺌل ﺇﻟﻰ ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺃﻭ ﻓﺌﺔ ﺍﻟﺠﺩﻭل ،ﺃﻭ ﻓﺌـﺔ ﻤﻬﻴـﺊ
ﺍﻟﺠﺩﻭل ..ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﻜﺘﺎﺒﺔ ﺇﺠﺭﺍﺀﺍﺕ ﺘﺴﺘﺠﻴﺏ ﻟﺒﻌﺽ ﺃﺤﺩﺍﺙ ﻓﺌﺔ ﺍﻟﺠﺩﻭل ،ﺴـﻭﺍﺀ ﺍﻷﺤـﺩﺍﺙ
ﺍﻟﻤﻌﺭﻓﺔ ﺩﺍﺨل ﻓﺌﺔ ﺍﻟﺠﺩﻭل ،ﺃﻭ ﺘﻠﻙ ﺍﻟﻤﻭﺭﻭﺜﺔ ﻤﻥ ﺍﻟﻔﺌﺔ ،DataTableﻭﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬـﺎ
ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺘﺎﻟﻲ.
ﻟﻜﻥ ﺍﻟﻤﺸﻜﻠﺔ ﺃﻨﻙ ﻟﻭ ﻜﺘﺒﺕ ﺃﻱ ﻜﻭﺩ ﻓﻲ ﺍﻟﻤﻠﻑ X.Designer.csﺍﻟﺫﻱ ﻓﻴـﻪ ﺘﻌﺭﻴـﻑ ﻫـﺫﻩ
ﺍﻟﻔﺌﺎﺕ )ﺤﻴﺙ Xﻫﻭ ﺍﺴﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ( ،ﻓﺴﻴﻜﻭﻥ ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﻋﺭﻀﺔ ﻟﻠﻀﻴﺎﻉ ﻋﻨﺩ ﻗﻴﺎﻤﻙ
ﺒﺄﻱ ﺘﻌﺩﻴﻼﺕ ﻓﻲ ﻤﺼﻤﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻷﻥ ﻫﺫﻩ ﺍﻟﺘﻌﺩﻴﻼﺕ ﺴﺘﻌﻴﺩ ﺇﻨﺘﺎﺝ ﻤﻠﻑ ﺍﻟﻜﻭﺩ ﻤـﻥ
ﺠﺩﻴﺩ ،ﻭﺴﺘﺘﺨﻠﺹ ﻤﻥ ﺃﻱ ﻜﻭﺩ ﺨﺎﺹ ﺒﻙ!
ﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ،ﺘﻡ ﺘﻌﺭﻴﻑ ﺍﻟﻔﺌﺎﺕ ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﻠﻑ ﺒﺎﻋﺘﺒﺎﺭﻫـﺎ ﺠﺯﺌﻴـﺔ ،Partialﻟﻴﻤﻜﻨـﻙ
ﺇﻀﺎﻓﺔ ﺍﻟﻜﻭﺩ ﺇﻟﻴﻬﺎ ﻓﻲ ﻤﻠﻑ ﺁﺨﺭ ..ﻟﻔﻌل ﻫﺫﺍ ،ﺍﻀﻐﻁ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤـﻥ ﻓـﻭﻕ ﺍﻟﺠـﺩﻭل ﺃﻭ
ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﺨﺘﺭ ﺍﻷﻤﺭ ،View Codeﻟﻔـﺘﺢ ﺘﻌﺭﻴـﻑ ﺠﺯﺌـﻲ
ﻤﺴﺘﻘل ﻟﻔﺌﺔ ﺍﻟﺠﺩﻭل ﺃﻭ ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ..ﻫﺫﺍ ﺍﻟﺘﻌﺭﻴﻑ ﺴﻴﻀﺎﻑ ﻓﻲ ﻤﻠـﻑ ﺠﺩﻴـﺩ ﺍﺴـﻤﻪ
) X.csﻓﻲ ﻤﺜﺎﻟﻨﺎ ﻫﺫﺍ ﺴﻴﻜﻭﻥ ﺍﺴﻤﻪ ..(DsAuthorsBooks.csﻭﺴﺘﺠﺩ ﻫﺫﺍ ﺍﻟﻤﻠﻑ ﻀـﻤﻥ
ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﻔﺭﻋﻴﺔ ﻟﻤﺨﻁﻁ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ .DsAuthorsBooks.xsd
ﻭﻋﻨﺩﻤﺎ ﺘﻔﺘﺢ ﻫﺫﺍ ﺍﻟﻤﻠﻑ ﻓﻲ ﻤﺤﺭﺭ ﺍﻟﻜﻭﺩ ،ﻴﻤﻜﻨﻙ ﺍﺨﺘﻴﺎﺭ ﺍﻟﻔﺌﺔ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻌﻠﻭﻴـﺔ ﺍﻟﻴﺴـﺭﻯ،
ﻭﺍﺨﺘﻴﺎﺭ ﺍﻟﺤﺩﺙ ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺇﻀﺎﻓﺘﻪ ﺇﻟﻴﻬﺎ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻌﻠﻭﻴﺔ ﺍﻟﻴﻤﻨﻰ ﻜﻤﺎ ﻫـﻭ ﻤـﺄﻟﻭﻑ ..ﻜﻤـﺎ
ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻀﻴﻑ ﺃﻴﺔ ﺩﺍﻟﺔ ﺘﺭﻴﺩﻫﺎ ﺇﻟﻰ ﺃﻴﺔ ﻓﺌﺔ ،ﺴـﻭﺍﺀ ﻜﺎﻨـﺕ ﺨﺎﺼـﺔ Privateﺃﻭ ﻋﺎﻤـﺔ
،Publicﻤﻊ ﻗﺩﺭﺘﻙ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻜل ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻤﻌﺭﻓﺔ ﻋﻠﻰ ﻤﺴﺘﻭﻯ ﺍﻟﻔﺌﺔ ﻓﻲ ﻜﺘﺎﺒـﺔ ﻜـﻭﺩ
ﻫﺫﻩ ﺍﻟﺩﺍﻟﺔ ،ﺴﻭﺍﺀ ﻜﺎﻨﺕ ﻫﺫﻩ ﺍﻟﻌﻨﺎﺼﺭ ﻤﺤﻤﻴﺔ Protectedﺃﻭ ﺨﺎﺼﺔ .Private
ﻜﻤﺎ ﻴﻘﺩﻡ ﻟﻙ ﻤﺼﻤﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺜﻴﺭ ﻤﻥ ﺍﻟﺘﺴﻬﻴﻼﺕ:
-ﻓﺎﻟﻨﻘﺭ ﻤﺭﺘﻴﻥ ﻓﻲ ﺃﻴﺔ ﻤﻨﻁﻘﺔ ﺨﺎﻟﻴﺔ ،ﻴﻔﺘﺢ ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-ﻭﺍﻟﻨﻘﺭ ﻤﺭﺘﻴﻥ ﻋﻠﻰ ﻋﻨﻭﺍﻥ ﺍﻟﺠﺩﻭل ﻴﻔﺘﺢ ﻓﺌﺔ ﺍﻟﺠﺩﻭل ،ﻭﻴﻀﻴﻑ ﺇﻟﻴﻬﺎ ﻤﺴﺘﺠﻴﺒﺎ ﻟﻠﺤـﺩﺙ
،XRowChangingﺤﻴﺙ Xﻫـﻭ ﺍﺴـﻡ ﺍﻟﺠـﺩﻭل ..ﻭﻗـﺩ ﺍﺴـﺘﺨﺩﻤﻨﺎ ﺍﻟﺤـﺩﺙ
٢٥٨
AuthorsRowDeletingﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ TableAdapterﻟﻌﺭﺽ ﺭﺴـﺎﻟﺔ ﺘﺄﻜﻴـﺩ
ﻗﺒل ﺤﺫﻑ ﺃﻱ ﺼﻑ ﻤﻥ ﺍﻟﺠﺩﻭل.
-ﻭﺍﻟﻨﻘﺭ ﻤﺭﺘﻴﻥ ﻋﻠﻰ ﺃﻱ ﺼﻑ ﻓﻲ ﺍﻟﺠﺩﻭل ،ﻴﻔﺘﺢ ﻓﺌﺔ ﺍﻟﺠﺩﻭل ،ﻭﻴﻀﻴﻑ ﺇﻟﻴﻬـﺎ ﺘﻌﺭﻴﻔـﺎ
ﻟﻠﺤﺩﺙ ،ColumnChangingﻭﺸﺭﻁﺎ ﻴﺘﺄﻜﺩ ﺃﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﺘﻐﻴﺭ ﻫﻭ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ
ﻨﻘﺭﺘﻪ ﺒﺎﻟﻔﺄﺭﺓ ..ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ TableAdapterﻟﻤﻨـﻊ
ﺍﻟﻤﺴﺘﺨﺩﻡ ﻤﻥ ﺘﺭﻙ ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ﻓﺎﺭﻏﺎ.
-ﻭﺍﻟﻨﻘﺭ ﻤﺭﺘﻴﻥ ﻋﻠﻰ ﻋﻨﻭﺍﻥ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻴﻔﺘﺢ ﻜﻭﺩ ﻓﺌﺘﻪ.
٢٦٠
ﻭﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻐﻴﺭ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﺘﺘﻌﺎﻤل ﻤﻌﻪ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ،ﻭﻴﻤﻜﻨـﻙ ﺃﻥ
ﺘﻐﻴﺭ ﺍﺴﻡ ﻭﺴﻴﻠﺔ ﺍﻟﻤلﺀ ﺒﺘﺤﺭﻴﺭﻫﺎ ﻓـﻲ ﻤﺭﺒـﻊ ﺍﻟـﻨﺹ )ﻭﺴﻨﺴـﺘﺨﺩﻡ ﻫﻨـﺎ ﺍﻻﺴـﻡ
،(FillByPublisherﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻜﺘﺏ ﺍﻻﺴﺘﻌﻼﻡ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟـﻨﺹ ﺍﻟﺴـﻔﻠﻲ..
ﻭﺇﺫﺍ ﺃﺭﺩﺕ ﺘﻌﺩﻴل ﺍﺴﺘﻌﻼﻡ ﻤﻭﺠﻭﺩ ﺴﺎﺒﻘﺎ ،ﻓﺎﻀـﻐﻁ ﺍﻻﺨﺘﻴـﺎﺭ Existing Query
Nameﻭﺍﺨﺘﺭ ﺍﺴﻡ ﺍﻻﺴﺘﻌﻼﻡ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ﻟﻌﺭﻀـﻪ ﻓـﻲ ﻤﺭﺒـﻊ ﺍﻟـﻨﺹ
ﺍﻟﺴﻔﻠﻲ ..ﻓﻲ ﺤﺎﻟﺘﻨﺎ ﻫﺫﻩ ،ﺴﻨﺴﺘﺨﺩﻡ ﺍﺴﺘﻌﻼﻤﺎ ﺠﺩﻴﺩﺍ ﻟﻠﺤﺼﻭل ﻋﻠـﻰ ﺍﻟﻜﺘـﺏ ﺍﻟﺘـﻲ
ﻨﺸﺭﻫﺎ ﻨﺎﺸﺭ ﻤﻌﻴﻥ.
ﺍﻀﻐﻁ Okﻹﻏﻼﻕ ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ..ﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻀﺎﻓﺔ ﺍﻟﻭﺴـﻴﻠﺔ FillByPublisher
ﺇﻟﻰ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ.
٢٦١
ﻭﺇﺫﺍ ﺃﺭﺩﺕ ﺇﻀﺎﻓﺔ ﺍﻟﻭﺴﻴﻠﺔ ،GetDataByPublisherﻓﺎﺘﺒﻊ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ:
-ﻀﻐﻁ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤﺭ
Edit Query In DataSetﻟﻌﺭﺽ ﻤﺨﻁﻁ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-ﺍﻀﻐﻁ ﺍﻻﺴﺘﻌﻼﻡ FillByPublisherﻓﻲ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﺒـﺯﺭ ﺍﻟﻔـﺄﺭﺓ
ﺍﻷﻴﻤﻥ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤـﺭ Configureﻟﻌـﺭﺽ ﻨﺎﻓـﺫﺓ
ﺘﺤﺭﻴﺭ ﺍﻻﺴﺘﻌﻼﻡ.
-ﺍﻀﻐﻁ Nextﻟﻌﺭﺽ ﻨﺎﻓﺫﺓ ﻭﺴﺎﺌل ﺍﻻﺴﺘﻌﻼﻡ ،ﻭﻀﻊ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴـﺎﺭ ﺃﻤـﺎﻡ
،Return a DataTableﻭﻏﻴﺭ ﺍﺴﻡ ﺍﻟﻭﺴـﻴﻠﺔ ﺇﻟـﻰ GetDataByPublisher
ﻭﺍﻀﻐﻁ .Finish
٢٦٢
ﻭﺘﺴﺘﻁﻴﻊ ﺘﻐﻴﻴﺭ ﻋﻨﻭﺍﻥ ﺍﻟﻼﻓﺘﺔ ﻭﺍﻟﺯﺭ ،ﻭﻋﺭﺽ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ﻤﻥ ﺍﻟﻴﻤﻴﻥ ﺇﻟﻰ ﺍﻟﻴﺴﺎﺭ
ﻭﺍﻟﻤﺸﺭﻭﻉ TableAdapterﻴﺭﻴﻙ ﺃﻤﺜﻠﺔ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ،ﻤﻊ ﺍﺴﺘﺨﺩﺍﻡ ﻤـﺩﻴﺭ
ﺍﻟﻤﻬﻴﺌﺎﺕ ﻓﻲ ﺤﺩﺙ ﻀﻐﻁ ﺍﻟﺯﺭ "ﺤﻔﻅ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ" ﻹﺭﺴﺎل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺃﺠﺭﺍﻫﺎ ﺍﻟﻤﺴـﺘﺨﺩﻡ
ﻋﻠﻰ ﺍﻟﺴﺠﻼﺕ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
٢٦٣
-١٢-
ﺍﻟﺠﺩﺍﻭل ﻭﺍﻟﻌﻼﻗﺎﺕ ﻭﺍﻟﻘﻴﻭﺩ
ﺴﻨﺘﻌﺭﻑ ﻓﻲ ﻫﺫﺍ ﺍﻟﻔﺼل ﻋﻠﻰ ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﺩﺍﺨﻠﺔ ﻓﻲ ﺘﻜـﻭﻴﻥ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،DataSet
ﻭﻫﻲ:
-ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل DataTableﻭﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﺩﺍﺨﻠﻴﺔ ﺍﻟﻤﻜﻭﻨﺔ ﻟـﻪ ﻤﺜـل ﻜـﺎﺌﻥ ﺍﻟﺼـﻑ
DataRowﻭﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ .DataCoulmn
-ﻜﺎﺌﻥ ﺍﻟﻌﻼﻗﺔ .DataRelation
-ﻜﺎﺌﻨﺎﺕ ﺍﻟﻘﻴﻭﺩ Constraintsﺍﻟﻤﺨﺘﻠﻔﺔ.
ﺇﻀﺎﻓﺔ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺎﺕ ﺍﻟﺘﻲ ﺘﺴﺘﺨﺩﻤﻬﺎ ﻫﺫﻩ ﺍﻟﻔﺌﺎﺕ.
٢٦٤
ﻓﺌﺔ ﺃﺴﺎﺱ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺩﺍﺨﻠﻴﺔ
InternalDataCollectionBase Class
ﻭﻻ ﺘﺯﻴﺩ ﻋﻠﻰ ﺨﺼﺎﺌﺼﻬﺎ ﻭﻭﺴﺎﺌﻠﻬﺎ ﺒﺸﻲﺀ،ICollection ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﻭﺍﺠﻬﺔ ﺍﻟﻤﺠﻤﻭﻋﺔ
.ﺠﺩﻴﺩ
:ﻭﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻫﻲ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ﻟﻜل ﻤﻥ ﺍﻟﻤﺠﻤﻭﻋﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ
DataTableCollection Class .١
DataColumnCollection Class .٢
DataRowCollection Class .٣
DataRelationCollection Class .٤
ConstraintCollection Class .٥
٢٦٥
ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل DataTableCollection Class
ﺍﻟﻤﻔﻬﺭﺱ :Indexer
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ DataTableﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻤﻭﻀﻊ ﻤﻌـﻴﻥ ﻓـﻲ ﺍﻟﻘﺎﺌﻤـﺔ ..ﻭﻟﻬـﺫﻩ
ﺍﻟﺨﺎﺼﻴﺔ ﺜﻼﺙ ﺼﻴﻎ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﺭﻗﻡ ﺍﻟﺠﺩﻭل ﻓﻲ ﺍﻟﻤﺠﻤﻭﻋﺔ.
-٢ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﺠﺩل.
-٣ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ ،ﻴﺴﺘﻘﺒل ﻨﺼﺎ ﻴﻤﺜل ﺍﺴـﻡ
ﺍﻟﻨﻁﺎﻕ Name Spaceﺍﻟﺫﻱ ﻴﻭﺠﺩ ﺘﺤﺘﻪ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ nullﺇﺫﺍ ﻟﻡ ﺘﺠﺩ ﺍﻟﺠﺩﻭل ﺍﻟﻤﻁﻠﻭﺏ ﻓﻲ ﺍﻟﻤﺠﻤﻭﻋﺔ.
ﺇﻀﺎﻓﺔ :Add
ﺘﻀﻴﻑ ﺠﺩﻭﻻ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻟﻬﺎ ﺃﺭﺒﻊ ﺼﻴﻎ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ ،ﻭﻫﻲ ﺘﻨﺸﺊ ﺠﺩﻭﻻ ﺒﺎﺴﻡ ﺍﻓﺘﺭﺍﻀﻲ Table1) ﺃﻭ
Table2ﻭﻫﻜﺫﺍ (...ﻭﺘﻀﻴﻔﻪ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
٢٦٦
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻨﺼﻴﺎ ،ﻫﻭ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺇﻨﺸﺎﺅﻩ ﻭﺇﻀﺎﻓﺘﻪ
ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ ..ﻭﻟﻭ ﺃﺭﺴﻠﺕ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻨﺼﺎ ﻓﺎﺭﻏﺎ "" ،ﻓﺴﻴﺴﻤﻰ ﺍﻟﺠـﺩﻭل
ﺒﺎﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ Table1ﺃﻭ Table2ﻭﻫﻜﺫﺍ ...ﻻﺤﻅ ﺃﻥ ﺇﻀـﺎﻓﺔ ﺠـﺩﻭل
ﺒﻨﻔﺱ ﺍﺴﻡ ﺠﺩﻭل ﻤﻭﺠﻭﺩ ﺴﺎﺒﻘﺎ ﺴﻴﺅﺩﻱ ﺇﻟﻰ ﺤﺩﻭﺙ ﺨﻁﺄ.
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ ،ﻴﻭﻀﺢ ﻨﻁﺎﻕ ﺍﻻﺴﻡ ﺍﻟﺫﻱ
ﺴﻴﻀﺎﻑ ﺇﻟﻴﻪ ﺍﻟﺠﺩﻭل ﺩﺍﺨل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻫﺫﺍ ﻴﺘﻴﺢ ﻟﻙ ﺇﻀﺎﻓﺔ ﺃﻜﺜـﺭ ﻤـﻥ
ﺠﺩﻭل ﺒﻨﻔﺱ ﺍﻻﺴﻡ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل ،ﻟﻜﻥ ﻜﻼ ﻤﻨﻬﺎ ﻴﻨﺘﻤـﻲ ﺇﻟـﻰ ﻨﻁـﺎﻕ
ﻤﺨﺘﻠﻑ ..ﻫﺫﺍ ﻤﻔﻴﺩ ﻋﻨﺩﻤﺎ ﺘﻤﻸ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺠﺩﺍﻭل ﻤﺘﺸﺎﺒﻬﺔ ﺍﻷﺴﻤﺎﺀ ﻤـﻥ
ﺃﻜﺜﺭ ﻤﻥ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ..ﻤﺜﻼ:
;)"Ds.Tables.Add("MyTable", "Db1
;)"Ds.Tables.Add("MyTable", "Db2
-٤ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺴـﺘﻘﺒل ﻜـﺎﺌﻥ ﺠـﺩﻭل ،DataTableﻟﺘـﺘﻡ ﺇﻀـﺎﻓﺘﻪ ﺇﻟـﻰ
ﺍﻟﻤﺠﻤﻭﻋﺔ.
ﻻﺤﻅ ﺃﻥ ﺍﻟﺼﻴﻎ ﺍﻟﺜﻼﺙ ﺍﻷﻭﻟﻰ ﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ﻤﻥ ﺍﻟﻨﻭﻉ DataTableﻴﻤﺜل ﺍﻟﺠﺩﻭل ﺍﻟـﺫﻱ
ﺘﻡ ﺇﻨﺸﺎﺅﻩ ،ﺒﻴﻨﻤﺎ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﻫﻲ ﺇﺠﺭﺍﺀ ﻻ ﻴﻌﻴﺩ ﺃﻴﺔ ﻗﻴﻤﺔ ،ﻭﺫﻟﻙ ﻷﻨﻙ ﺃﺭﺴﻠﺕ ﺇﻟﻴﻬـﺎ
ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ﺒﺎﻟﻔﻌل ،ﻭﻻ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﻤﺭﺠﻊ ﺁﺨﺭ ﻟﻪ.
٢٦٧
ﺤﺫﻑ :Remove
ﺘﺤﺫﻑ ﺍﻟﺠﺩﻭل ﺍﻟﻤﺭﺴل ﺇﻟﻴﻬﺎ ﻜﻤﻌﺎﻤل ،ﻭﻟﻬﺎ ﻨﻔﺱ ﺼﻴﻎ ﺍﻟﻭﺴﻴﻠﺔ Addﻤﺎ ﻋﺩﺍ ﺍﻟﺼـﻴﻐﺔ
ﺍﻷﻭﻟﻰ ﺍﻟﺘﻲ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ ..ﻻﺤﻅ ﺃﻨﻙ ﻻ ﺘﺴﺘﻁﻴﻊ ﺤﺫﻑ ﺠﺩﻭل ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ
ﺇﺫﺍ ﻜﺎﻥ ﺩﺍﺨﻼ ﻓﻲ ﻋﻼﻗﺔ ،ﻓﻬﺫﺍ ﺴﻴﺅﺩﻱ ﺇﻟﻰ ﺤﺩﻭﺙ ﺨﻁﺄ ﻓـﻲ ﺍﻟﺒﺭﻨـﺎﻤﺞ ..ﻭﺍﻟﻤﻘﺼـﻭﺩ
ﺒﺎﻟﻌﻼﻗﺔ ﻫﻨﺎ ،ﺍﻟﻌﻼﻗﺔ ﺍﻟﻤﻌﺭﻓﺔ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﻠﻭ ﻭﻀـﻌﺕ ﺠـﺩﻭﻟﻲ ﺍﻟﻤـﺅﻟﻔﻴﻥ
ﻭﺍﻟﻜﺘﺏ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺩﻭﻥ ﺇﻨﺸﺎﺀ ﻋﻼﻗﺔ ﺒﻴﻨﻬﻤﺎ ،ﻓﺴﻴﻤﻜﻨﻙ ﺤﺫﻑ ﺃﻴﻬﻤـﺎ ﺒـﺩﻭﻥ
ﻤﺸﺎﻜل ﺭﻏﻡ ﺃﻥ ﻫﻨﺎﻙ ﻋﻼﻗﺔ ﺒﻴﻨﻬﻤﺎ ﻓﻌﻼ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﺃﻤﺎ ﻟﻭ ﺃﻨﺸـﺄﺕ ﺍﻟﻌﻼﻗـﺔ
ﺒﻴﻨﻬﻤﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﻌﻠﻴﻙ ﺤﺫﻓﻬﺎ ﺃﻭﻻ ﻗﺒل ﻤﺤﺎﻭﻟﺔ ﺤﺫﻑ ﺃﻱ ﻤﻥ ﺍﻟﺠـﺩﻭﻟﻴﻥ..
ﻭﺍﻷﻓﻀل ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ CanRemoveﺃﻭﻻ ﻗﺒل ﻤﺤﺎﻭﻟﺔ ﺤﺫﻑ ﺍﻟﺠﺩﻭل ..ﻤﺜﺎل:
;]"var T = Ds.Tables["Authors
))if (Ds.Tables.CanRemove(T
;)Ds.Tables.Remove(T
ﺘﺤﺘﻭﻱ ﻋﻠﻰ :Contains
ﺘﻌﻴﺩ trueﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺠﺩﻭل ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل ﻤﻭﺠﻭﺩﺍ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ..ﻭﻟﻬـﺫﻩ
ﺍﻟﻭﺴﻴﻠﺔ ﺼﻴﻐﺘﺎﻥ ،ﺘﻤﺎﺜﻼﻥ ﺍﻟﺼﻴﻐﺘﻴﻥ ﺍﻟﺜﺎﻨﻴﺔ ﻭﺍﻟﺜﺎﻟﺜﺔ ﻟﻠﻭﺴﻴﻠﺔ .Add
٢٦٨
ﺍﻟﻤﺠﻤﻭﻋﺔ ﺘﺘﻐﻴﺭ :CollectionChanging
ﻴﻨﻁﻠﻕ ﻋﻨﺩﻤﺎ ﺘﻭﺸﻙ ﺠﺩﺍﻭل ﺍﻟﻤﺠﻤﻭﻋﺔ ﻋﻠﻰ ﺍﻟﺘﻐﻴﺭ ،ﻨﺘﻴﺠﺔ ﺇﻀﺎﻓﺔ ﺃﻭ ﺤـﺫﻑ ﺠـﺩﻭل..
ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ eﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨﻭﻉ ،CollectionChangeEventArgsﻭﻫـﻭ
ﻴﻤﺘﻠﻙ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
٢٦٩
ﻓﺌﺔ ﺠﺩﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ DataTable Class
ﺘﻌﻤل ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻜﻭﻋﺎﺀ ﻷﺤﺩ ﺍﻟﺠﺩﺍﻭل ﺒﻤﺎ ﻓﻴﻪ ﻤﻥ ﺃﻋﻤﺩﺓ ﻭﺼﻔﻭﻑ ،ﻭﻫـﻲ ﺘﻤﺜـل ﺍﻟﻭﺍﺠﻬـﺔ
،IListSourceﻜﻤﺎ ﺃﻨﻬﺎ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ،MarshalByValueComponentﻤﻤـﺎ ﻴﺘـﻴﺢ ﻟـﻙ
ﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﺼﻴﻨﻴﺔ ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤﻭﺫﺝ ،ﻭﺇﻥ ﻜـﺎﻥ ﻋﻠﻴـﻙ ﺃﻥ ﺘﻀـﻴﻔﻬﺎ ﺃﻭﻻ ﺇﻟـﻰ ﺼـﻨﺩﻭﻕ
ﺍﻷﺩﻭﺍﺕ ..ﻟﻜﻥ ﻻ ﺩﺍﻋﻲ ﻟﻬﺫﺍ ،ﻓﺄﻨﺕ ﺘﺴﺘﻁﻴﻊ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﺠﺩﻭل ﺒﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ﻓـﻲ ﻭﻗـﺕ
ﺍﻟﺘﺼﻤﻴﻡ ،ﺒﻌﺭﺽ ﺨﺼﺎﺌﺹ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻏﻴﺭ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ Un-typed DataSetﻓﻲ
ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ،ﻭﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼﻴﺔ Tablesﻹﻀﺎﻓﺔ ﺍﻟﺠﺩﺍﻭل ﻭﺘﻐﻴﻴﺭ ﺨﺼﺎﺌﺼﻬﺎ ﺒﻁﺭﻴﻘـﺔ
ﻤﺭﺌﻴــﺔ ..ﺃﻤــﺎ ﺇﺫﺍ ﻜﻨــﺕ ﺘﺘﻌﺎﻤــل ﻤــﻊ ﻤﺠﻤﻭﻋــﺔ ﺒﻴﺎﻨــﺎﺕ ﻤﺤــﺩﺩﺓ ﺍﻟﻨــﻭﻉ
،Typed DataSetﻓﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﺍﻟﺠﺩﺍﻭل ﻤﺒﺎﺸﺭﺓ ﺇﻟﻰ ﻤﺨﻁـﻁ XMLﺒـﺎﻟﻁﺭﻕ ﺍﻟﺘـﻲ
ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺴﺎﺒﻕ.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺭﺒﻊ ﺼﻴﻎ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ.
-٢ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻨﺼﻴﺎ ،ﻫﻭ ﺍﺴﻡ ﺍﻟﺠﺩﻭل.
-٣ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ ،ﻴﻭﻀﺢ ﻨﻁﺎﻕ ﺍﻻﺴـﻡ ﺍﻟـﺫﻱ
ﺴﻴﻀﺎﻑ ﺇﻟﻴﻪ ﺍﻟﺠﺩﻭل ﺩﺍﺨل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-٤ﻭﺍﻟﺼــﻴﻐﺔ ﺍﻟﺭﺍﺒﻌــﺔ ﺘﺴــﺘﻘﺒل ﻤﻌــﺎﻤﻠﻴﻥ ﻤــﻥ ﺍﻟﻨــﻭﻋﻴﻥ SerializationInfo
ﻭ StreamingContextﻻﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﺴﻠﺴﻠﺔ ﺍﻟﺠـﺩﻭل ..Serializationﻫـﺫﺍ
ﺍﻟﻤﻭﻀﻭﻉ ﺨﺎﺭﺝ ﻨﻁﺎﻕ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ.
ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﻑ ﻜﺎﺌﻥ ﺠﺩﻭل ﻭﻴﻀﻊ ﻓﻴﻪ ﺃﻭل ﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋـﺔ ﺠـﺩﺍﻭل ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ:
;]DataTable T = Ds.Tables[0
ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﻑ ﻜﺎﺌﻥ ﺠﺩﻭل ﺠﺩﻴﺩ ﻭﻴﻀﻴﻔﻪ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ:
;)"DataTable T = new DataTable("MyTable
;)Ds.Tables.Add(T
٢٧٠
ﻭﻴﻤﺘﻠﻙ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
ﺍﻟﺒﺎﺩﺌﺔ :Prefix
ﺘﺤﺩﺩ ﺍﻟﺒﺎﺩﺌﺔ ﺍﻟﺘﻲ ﺴﺘﻤﻴﺯ ﺍﻟﺠﺩﻭل ﻜﺎﺨﺘﺼﺎﺭ ﻻﺴﻡ ﻨﻁﺎﻗﻪ.
٢٧١
ﺍﻟﻤﺤل :Locale
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺜﻘﺎﻓﺔ ،CultureInfoﺍﻟﺫﻱ ﻴﻤﺜـل ﺍﻟﻠﻐـﺔ ﺍﻟﺘـﻲ ﺘﺭﻴـﺩ
ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﻤﻘﺎﺭﻨﺔ ﻭﺘﺭﺘﻴﺏ ﺍﻟﻨﺼﻭﺹ.
ﺍﻷﻋﻤﺩﺓ :Columns
ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ DataColumnCollectionﺍﻟﻤﻭﺠﻭﺩﺓ ﻓـﻲ ﻫـﺫﺍ ﺍﻟﺠـﺩﻭل..
ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ.
ﺍﻟﺼﻔﻭﻑ :Rows
ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ DataRowCollectionﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓـﻲ ﻫـﺫﺍ ﺍﻟﺠـﺩﻭل..
ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ.
٢٧٢
ﺍﻟﻌﻼﻗﺎﺕ ﺍﻟﻔﺭﻋﻴﺔ :ChildRelations
ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﻌﻼﻗـﺎﺕ ،DataRelationCollectionﺘﺤﺘـﻭﻱ ﻋﻠـﻰ
ﺍﻟﻌﻼﻗﺎﺕ ﺍﻟﻘﺎﺩﻤﺔ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﺠﺩﻭل )ﺍﻟﻌﻼﻗﺎﺕ ﺍﻟﺘﻲ ﻴﺩﺨل ﻓﻴﻬﺎ ﻜﺠﺩﻭل ﻓﺭﻋﻲ ﺃﻭ ﺠـﺩﻭل
ﺍﻟﺘﻔﺎﺼﻴل .(Details Table
ﺍﻟﻘﻴﻭﺩ :Constraints
ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻘﻴﻭﺩ ConstraintCollectionﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ..ﻭﺴﻨﺘﻌﺭﻑ
ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ.
٢٧٣
ﺘﻌﺒﻴﺭ ﺍﻟﻌﺭﺽ :DisplayExpression
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﻋﺭﻀﻪ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻜﻌﻨﻭﺍﻥ ﻟﻠﺠﺩﻭل ﻓـﻲ ﺃﺩﻭﺍﺕ ﻋـﺭﺽ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﺎﻷﺩﺍﺓ .DataGridView
ﻨﺴﺦ :Clone
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺠﺩﻭل DataTableﺠﺩﻴﺩﺍ ،ﻭﺘﻨﺴﺦ ﺇﻟﻴﻪ ﻤﺨﻁﻁ ﺍﻟﺠﺩﻭل ﺍﻟﺤـﺎﻟﻲ Schema
ﺒﻜلّ ﻤﺎ ﻓﻴﻪ ﻤﻥ ﺃﻋﻤﺩﺓ ﻭﻗﻴﻭﺩ ..ﻭﻟﻜﻥ ﺍﻟﺠﺩﻭل ﺍﻟﻨﺎﺘﺞ ﻴﻜﻭﻥ ﻓﺎﺭﻏﺎ ﻤﻥ ﺍﻟﺴﺠﻼﺕ.
ﻨﺴﺦ :Copy
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺠﺩﻭل DataTableﺠﺩﻴﺩﺍ ،ﻭﺘﻨﺴﺦ ﺇﻟﻴﻪ ﺍﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ ﺒﻤﺨﻁﻁﻪ ﻭﺴـﺠﻼﺘﻪ،
ﻟﻴﻜﻭﻥ ﻤﻤﺎﺜﻼ ﻟﻠﺠﺩﻭل ﺍﻷﺼﻠﻲ ﺘﻤﺎﻤﺎ.
٢٧٤
ﻤﺤﻭ :Clear
ﺘﻤﺤﻭ ﻜلّ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺠﺩﻭل.
ﺘﺼﻔﻴﺭ :Reset
ﺘﻔﺭﻍ ﺍﻟﺠﺩﻭل ﺘﻤﺎﻤﺎ ﻤﻥ ﻜل ﺃﻋﻤﺩﺘﻪ ﻭﺴﺠﻼﺘﻪ ﻭﻗﻴﻭﺩﻩ.
ﺍﺴﺘﻌﺎﺭﺓ ﺼﻑ :ImportRow
ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺼﻑ ،DaraRowﻟﺘﻨﺴﺨﻪ ﻭﺘﻀﻴﻔﻪ ﺇﻟﻰ ﺍﻟﺠـﺩﻭل ﺒﻜـلّ
ﺒﻴﺎﻨﺎﺘﻪ ﻭﺨﺼﺎﺌﺼﻪ ،ﺒﻤﺎ ﻓﻲ ﺫﻟـﻙ ﺍﻟﻨﺴـﺨﺔ ﺍﻷﺼـﻠﻴﺔ Original Versionﻭﺍﻟﻨﺴـﺨﺔ
ﺍﻟﺤﺎﻟﻴﺔ Current Versionﻟﻘﻴﻡ ﺨﺎﻨﺎﺘﻪ.
٢٧٥
ﺘﺤﻤﻴل ﺼﻑ :LoadDataRow
ﺍﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻟﺘﺤﺩﻴﺙ ﺃﺤﺩ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ،ﺃﻭ ﺇﻀـﺎﻓﺔ ﺴـﺠل ﺠﺩﻴـﺩ ﺇﻟﻴـﻪ..
ﻭﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻠﻴﻥ:
-ﻤﺼﻔﻭﻓﺔ ﻜﺎﺌﻨﺎﺕ ،Object Arrayﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻗﻴﻡ ﺨﺎﻨﺎﺕ ﺍﻟﺴﺠل ﺒﻨﻔﺱ ﺘﺭﺘﻴﺒﻬﺎ
ﻓﻲ ﺍﻟﺠﺩﻭل ..ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺴﺘﺒﺤﺙ ﻓﻲ ﺍﻟﺠﺩﻭل ،ﻟﺘﺭﻯ ﺇﻥ ﻜﺎﻥ ﺍﻟﻤﻔﺘـﺎﺡ
ﺍﻷﺴﺎﺴﻲ ﻷﻱ ﺤﻘل ﻟﻪ ﻨﻔﺱ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺨﺎﻨﺔ ﺍﻟﻤﻨﺎﻅﺭﺓ ﻓﻲ ﺍﻟﻤﺼﻔﻭﻓﺔ..
ﻓﺈﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻤﻭﺠﻭﺩﺍ ﻓﻲ ﺍﻟﺠﺩﻭل ،ﻴـﺘﻡ ﻨﺴـﺦ ﺒـﺎﻗﻲ ﺍﻟﻘـﻴﻡ ﻤـﻥ
ﺍﻟﻤﺼﻔﻭﻓﺔ ﺇﻟﻰ ﺒﺎﻗﻲ ﺤﻘﻭل ﺍﻟﺴﺠلّ ﻟﺘﺤﺩﻴﺜﻬﺎ ..ﻭﺇﺫﺍ ﻟﻡ ﻴﻜـﻥ ﺍﻟﻤﻔﺘـﺎﺡ ﺍﻷﺴﺎﺴـﻲ
ﻤﻭﺠﻭﺩﺍ ،ﻴﺘﻡ ﺇﻨﺸﺎﺀ ﺴﺠلّ ﺠﺩﻴﺩ ﻭﺘﻭﻀﻊ ﺒﺤﻘﻭﻟﻪ ﻗﻴﻡ ﺍﻟﻤﺼﻔﻭﻓﺔ.
ﻻﺤﻅ ﺃﻥ ﺘﺭﻙ ﺇﺤﺩﻯ ﺨﺎﻨﺎﺕ ﺍﻟﻤﺼﻔﻭﻓﺔ ﻓﺎﺭﻏﺔ ،ﺴـﻴﺅﺩﻱ ﺇﻟـﻰ ﻭﻀـﻊ ﺍﻟﻘﻴﻤـﺔ
ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻓﻲ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻤﻨﺎﻅﺭ ﻟﻬﺎ ﺇﻥ ﻜﺎﻨﺕ ﻟﻪ ﻗﻴﻤﺔ ﺍﻓﺘﺭﺍﻀﻴﺔ ،ﺃﻭ ﺴﻴﺘﻡ ﺘﻭﻟﻴـﺩ
ﺍﻟﺘﺭﻗﻴﻡ ﺍﻟﺘﻠﻘﺎﺌﻲ ﺇﺫﺍ ﻜﺎﻨﺕ ﻟﻠﺨﺎﺼـﻴﺔ AutoIncrementﻟﻬـﺫﺍ ﺍﻟﻌﻤـﻭﺩ ﺍﻟﻘﻴﻤـﺔ
..trueﻓﺈﺫﺍ ﻟﻡ ﻴﻜﻥ ﻫﺫﺍ ﺃﻭ ﺫﺍﻙ ،ﻭﻜﺎﻨﺕ ﺍﻟﺨﺎﻨـﺔ ﻻ ﺘﻘﺒـل ﺃﻥ ﺘﻅـل ﻓﺎﺭﻏـﺔ،
ﻓﺴﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ..ﻭﻴﺤﺩﺙ ﺨﻁﺄ ﺃﻴﻀﺎ ﺇﺫﺍ ﻜﺎﻥ ﻋﺩﺩ ﺨﺎﻨﺎﺕ ﺍﻟﻤﺼﻔﻭﻓﺔ
ﺃﻜﺒﺭ ﻤﻥ ﻋﺩﺩ ﺃﻋﻤﺩﺓ ﺍﻟﺠﺩﻭل.
-ﻤﻌﺎﻤل ﻤﻨﻁﻘﻲ ،Booleanﻟﻭ ﺠﻌﻠﺕ ﻗﻴﻤﺘـﻪ trueﻓﺴـﻴﺘﻡ ﺍﺴـﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ
AcceptChangesﺒﻌﺩ ﺇﻀﺎﻓﺔ ﺍﻟﺴﺠل ﺇﻟﻰ ﺍﻟﺠﺩﻭل ﻭﺒﻬﺫﺍ ﻴﻌﺘﺒﺭ ﻫـﺫﺍ ﺍﻟﺴـﺠل
ﺴﺠﻼ ﺃﺼﻠﻴﺎ ﻟﻡ ﻴﺤﺩﺙ ﻟﻪ ﺃﻱ ﺘﻐﻴﻴﺭ ..ﺃﻤﺎ ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫـﺫﺍ ﺍﻟﻤﻌﺎﻤـل ،false
ﻓﺴﻴﻌﺘﺒﺭ ﺍﻟﺴﺠل ﺍﻟﺠﺩﻴﺩ ﺴﺠﻼ ﻤﻀﺎﻓﺎ Addedﻭﻴﻌﺘﺒﺭ ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﺘـﻡ ﺘﺤﺩﻴﺜـﻪ
ﺴﺠﻼ ﻤﻌﺩﻻ .Modified
ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺼﻑﹼ DataRowﻴﺤﻤل ﻤﺭﺠﻌﺎ ﺇﻟﻰ ﺍﻟﺼﻑﹼ ﺍﻟﺫﻱ ﺘﻡ ﺘﺤﺩﻴﺜـﻪ
ﺃﻭ ﺇﻀﺎﻓﺘﻪ.
٢٧٦
ﻤﻠﺤﻭﻅﺔ:
٢٧٧
ﺘﻘﻭﻡ ﻓﺌﺔ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ،Typed DataSet Classﺒﺘﻌﺭﻴﻑ ﻋﺩﺓ ﻭﺴـﺎﺌل
ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﻓﻲ ﻜل ﺠﺩﻭل ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺼﻔﻭﻓﻪ ..ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜﺎل ،ﻟـﻭ ﻜـﺎﻥ ﻓـﻲ
ﺍﻟﻤﺠﻤﻭﻋﺔ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﻓﺌﺔ ﻟﺠﺩﻭل ﺍﻟﻤـﺅﻟﻔﻴﻥ ﺍﺴـﻤﻬﺎ ،AuthorsDataTableﻭﺘـﻡ
ﺘﻌﺭﻴﻑ ﻓﺌﺔ ﺍﺴﻤﻬﺎ AuthorsRowﺘﻤﺜل ﻨﻭﻉ ﺼﻔﻭﻑ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ،ﻓﺈﻥ ﻫﺫﺍ ﺍﻟﺠـﺩﻭل
ﺴﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ:
٢٧٨
ﺘﺤﺩﻴﺩ :Select
ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺼﻔﻭﻑ ،DataRow Arrayﺘﺤﺘﻭﻱ ﻋﻠـﻰ ﺒﻌـﺽ ﺃﻭ ﻜـلّ ﺼـﻔﻭﻑ
ﺍﻟﺠﺩﻭل ..ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ ،ﻭﻫﻲ ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻜل ﺴـﺠﻼﺕ
ﺍﻟﺠﺩﻭل.
ﺼﻴﺎ ،ﻴﻤﺜل ﺍﻟﺸﺭﻁ ﺍﻟﺫﻱ ﻋﻠﻰ ﺃﺴﺎﺴﻪ ﺴﻴﺘﻡ ﺍﺨﺘﻴﺎﺭ
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻨ
ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﺍﻟﺠﺩﻭل ،ﻭﻴﻤﻜﻨﻙ ﺼﻴﺎﻏﺔ ﻫﺫﺍ ﺍﻟﺸﺭﻁ ﺒﻨﻔﺱ ﻗﻭﺍﻋﺩ ﺼﻴﺎﻏﺔ ﺍﻟﻔﻘـﺭﺓ
WHEREﻓﻲ ﺍﺴﺘﻌﻼﻤﺎﺕ ..SQLﻭﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ ﺘﻌﻴﺩ ﻜلّ ﺍﻟﻜﺘﺏ ﺍﻟﺘـﻲ ﺘﺒـﺩﺃ
ﺒﺤﺭﻭﻑ ﺘﺴﺒﻕ ﺤﺭﻑ ﺍﻟﺜﺎﺀ ﻓﻲ ﺍﻟﺘﺭﺘﻴﺏ ﺍﻷﺒﺠﺩﻱ:
;)" 'ﺙ' < DataRow[ ] R = T.Select("Book
ﻭﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺴﺘﺨﺩﻡ ﻓﻲ ﺘﻜﻭﻴﻥ ﺍﻟﺸﺭﻁ ،ﺍﻟﺩﻭﺍل ﻭﺍﻟﻜﻠﻤﺎﺕ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻓﻲ ﺘﻜـﻭﻴﻥ
ﺸﺭﻁ ﺍﻟﺨﺎﺼﻴﺔ DataRow.Expressionﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ.
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﻨﺼﻲ ﻴﺤﺩﺩ ﺘﺭﺘﻴﺏ ﺍﻟﺼﻔﻭﻑ..
ﻭﻴﺘﻜﻭﻥ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻤﻥ ﺸﻘﻴﻥ:
ﺃ -ﺍﺴﻡ ﺍﻟﻌﻤـﻭﺩ ﺍﻟـﺫﻱ ﻴـﺘﻡ ﺍﻟﺘﺭﺘﻴـﺏ ﻋﻠـﻰ ﺃﺴﺎﺴـﻪ )ﻤﺜـل ،(Book
ﺃﻭ ﺃﻱ ﺘﻌﺒﻴﺭ ﻴﺠﻤـﻊ ﺒـﻴﻥ ﺃﻜﺜـﺭ ﻤـﻥ ﻋﻤـﻭﺩﻴﻥ ﻜﻨـﺎﺘﺞ ﻀـﺭﺒﻬﻤﺎ
)ﻤﺜل .(Copies_No * Price
ﺏ -ﻨﻭﻉ ﺍﻟﺘﺭﺘﻴﺏ ،ﻭﻫﻭ ﺇﺤﺩﻯ ﺍﻟﻜﻠﻤﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
:ASC -ﻟﻠﺘﺭﺘﻴﺏ ﺍﻟﺘﺼﺎﻋﺩﻱ ﻭﻫﻭ ﺍﻟﺘﺭﺘﻴﺏ ﺍﻻﻓﺘﺭﺍﻀﻲ ﻟﻬﺫﺍ ﻴﻤﻜـﻥ ﺃﻻ
ﺘﻜﺘﺏ ﻫﺫﻩ ﺍﻟﻜﻠﻤﺔ.
:DESC -ﻟﻠﺘﺭﺘﻴﺏ ﺍﻟﺘﻨﺎﺯﻟﻲ.
ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﺽ ﺃﺴﻤﺎﺀ ﺍﻟﻜﺘﺏ ﺍﻟﺘﻲ ﺘﺒﺩﺃ ﺒﺤﺭﻭﻑ ﺘﺴﺒﻕ ﺤﺭﻑ ﺍﻟﺜـﺎﺀ ﻓـﻲ
ﺍﻟﺘﺭﺘﻴﺏ ﺍﻷﺒﺠﺩﻱ ،ﻤﺭﺘﺒﺔ ﺘﻨﺎﺯﻟﻴﺎ ﻋﻠﻰ ﺤﺴﺏ ﺍﺴﻡ ﺍﻟﻜﺘﺎﺏ:
;)"' ", "Book DESCﺙ' < var R = T.Select ("Book
٢٧٩
-٤ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻟـﺙ ﻤـﻥ ﻨـﻭﻉ ﺍﻟﻤـﺭﻗﻡ
،DataViewRowStateﻤﻤﺎ ﻴﺘﻴﺢ ﻟﻙ ﺘﺤﺩﻴﺩ ﺤﺎﻟﺔ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺘﻁﺒﻴﻕ
ﺍﻟﺸﺭﻁ ﻋﻠﻴﻬﺎ ..ﻫﺫﺍ ﻴﻤﻜﻨﻙ ﻤﻥ ﺍﻟﺒﺤﺙ ﻓﻲ ﺍﻟﺴـﺠﻼﺕ ﺍﻟﻤﻀـﺎﻓﺔ ﺃﻭ ﺍﻟﻤﻌﺩﻟـﺔ ﺃﻭ
ﺍﻟﻤﺤﺫﻭﻓﺔ ...ﺇﻟﺦ ..ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻤﺭﻗﻡ DataViewRowStateﺒﺎﻟﺘﻔﺼـﻴل
ﻻﺤﻘﺎ.
ﺤﺴﺎﺏ :Compute
ﺘﺒﺤﺙ ﻓﻲ ﺍﻟﺠﺩﻭل ﻋﻥ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﺤﻘﻕ ﺍﻟﺸﺭﻁ ﺍﻟﻤﺭﺴـل ﺇﻟـﻰ ﺍﻟﻤﻌﺎﻤـل ﺍﻟﺜـﺎﻨﻲ،
ﻭﺘﺠﺭﻱ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﺴﺠﻼﺕ ﺩﺍﻟـﺔ ﺍﻟﺘﺠﻤﻴـﻊ Aggregate Functionﺍﻟﻤﺭﺴـﻠﺔ ﺇﻟـﻰ
ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ..ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻨﺎ Objectﻴﺤﻤل ﻨﺎﺘﺞ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺠﻤﻴـﻊ ..ﺩﻋﻨـﺎ
ﻨﺄﺨﺫ ﻤﺜﺎﻻ :ﺍﻓﺘﺭﺽ ﺃﻨﻙ ﺘﺭﻴﺩ ﺤﺴﺎﺏ ﻋﺩﺩ ﺍﻟﻜﺘﺏ ﺍﻟﺘﻲ ﺘﺒﺩﺃ ﺒﺤﺭﻭﻑ ﺘﺴﺒﻕ ﺤﺭﻑ ﺍﻟﻨـﻭﻥ
ﻓﻲ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ..ﻴﻤﻜﻨﻙ ﻓﻌل ﻫﺫﺍ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ Computeﻜﺎﻟﺘﺎﻟﻲ:
;)" 'ﻥ' < C = TblBooks.Compute("Count(Book)", "Book
ﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺇﺭﺴﺎل ﻨﺹ ﻓﺎﺭﻍ ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ،ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﻴﺘﻡ ﺘﻁﺒﻴـﻕ
ﺩﺍﻟﺔ ﺍﻟﺠﻤﻴﻊ ﻋﻠﻰ ﺠﻤﻴﻊ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ..ﻭﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ ﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻤﺠﻤﻭﻉ ﻨﺴﺦ ﺍﻟﻜﺘﺏ
ﻓﻲ ﺍﻟﺠﺩﻭل:
;)"" C = TblBooks.Compute("Sum(Copies_No)",
ﻭﻴﻭﺠﺩ ﻋﻴﺏ ﺨﻁﻴﺭ ﻓﻲ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ،ﻓﻬﻲ ﻻ ﺘﺴﺘﻁﻴﻊ ﺤﺴﺎﺏ ﺩﺍﻟﺔ ﺍﻟﺘﺠﻤﻴﻊ ﻋﻠـﻰ ﺃﻜﺜـﺭ
ﻤﻥ ﻋﻤﻭﺩ ﻤﺒﺎﺸﺭﺓ ..ﻓﺈﺫﺍ ﺃﺭﺩﺕ ﻤﺜﻼ ﺃﻥ ﺘﺤﺴﺏ ﻤﺠﻤﻭﻉ ﺃﺜﻤﺎﻥ ﻜل ﻨﺴﺦ ﺍﻟﻜﺘﺏ ﺍﻟﻤﻭﺠﻭﺩﺓ
ﻓﻲ ﺍﻟﺠﺩﻭل ،ﻓﺈﻥ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ ﻏﻴﺭ ﻤﻘﺒﻭﻟﺔ:
;)"" C = TblBooks.Compute("Sum(Copies_No * Price)",
ﻭﻟﺤلّ ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ،ﻋﻠﻴﻙ ﺇﻨﺸﺎﺀ ﻋﻤﻭﺩ ﺠﺩﻴﺩ ﻓﻲ ﺍﻟﺠﺩﻭل ،ﻭﺍﺴﺘﺨﺩﺍﻡ ﺨﺎﺼﻴﺔ "ﺍﻟﺼـﻴﻐﺔ"
ﺼﺔ ﺒﻪ ﻟﺘﻜﻭﻥ ﻗﻴﻡ ﺨﺎﻨﺎﺘﻪ ﻫﻲ ﺤﺎﺼل ﻀﺭﺏ ﺍﻟﻌﻤﻭﺩﻴﻥ ﺍﻟﻤﻁﻠـﻭﺒﻴﻥ،
Expressionﺍﻟﺨﺎ
ﺜﻡ ﺘﺠﺭﻱ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺤﺴﺎﺒﺎﺕ ﺍﻟﺘﻲ ﺘﺭﻴﺩﻫﺎ ..ﻭﺴـﻨﺘﻌﺭﻑ ﺍﻷﻋﻤـﺩﺓ ﺍﻟﻤﺤﺴـﻭﺒﺔ
ﺒﺎﻟﺘﻔﺼﻴل ﻋﻨﺩ ﺍﻟﺘﻌﺭﻑ ﻋﻠﻰ ﺨﺼﺎﺌﺹ ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ .DataCoulmn
٢٨٠
ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺤﺩﺙ ﻀـﻐﻁ ﺯﺭ ﺘﺤﻤﻴـل ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ
CustomDataSetﻟﺤﺴﺎﺏ ﻤﺠﻤﻭﻉ ﺩﺭﺠﺎﺕ ﻜل ﻁﺎﻟﺏ ﻭﻋﺭﻀﻪ ﻓﻲ ﻋﻤﻭﺩ "ﺍﻟﻤﺠﻤﻭﻉ"
ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ..ﺍﻟﺴﺒﺏ ﻓﻲ ﻫﺫﺍ ﺃﻥ ﻤﺠﻤﻭﻉ ﺩﺭﺠﺎﺕ ﻜل ﻁﺎﻟﺏ ﻻ ﻴﺘﻡ ﺤﻔﻅـﻪ ﻓـﻲ
ﺍﻟﻤﻠﻑ ،ﻻﻥ ﻋﻤﻭﺩ "ﺍﻟﻤﺠﻤﻭﻉ" ﻤﻀﺎﻑ ﺇﻟﻰ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻓﻘﻁ ﻭﻟـﻴﺱ ﻤﻭﺠـﻭﺩﺍ ﻓـﻲ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻟﻬﺫﺍ ﻋﻠﻴﻨﺎ ﺃﻥ ﻨﺤﺴﺏ ﻗﻴﻤﺘﻪ ﺒﺄﻨﻔﺴﻨﺎ ..ﻫﺫﺍ ﻫﻭ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﻴﻔﻌل ﻫﺫﺍ:
(SumCell.Value = DsStudents.Grades.Compute
"SUM(Grade)", "StudentID = " +
;)) (StdId.ToString( ).Trim
ﺤﻴﺙ StdIdﻫﻭ ﻤﺘﻐﻴﺭ ﻴﺤﻤل ﺭﻗﻡ ﺍﻟﻁﺎﻟﺏ ﺍﻟﻤﺭﺍﺩ ﺤﺴﺎﺏ ﻤﺠﻤﻭﻉ ﺩﺭﺠﺎﺘﻪ.
٢٨١
ﺭﻓﺽ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ :RejectChanges
ﺘﻘﻭﻡ ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ RejectChangesﺍﻟﺨﺎﺼﺔ ﺒﻜل ﺼﻑ ﻓﻲ ﺍﻟﺠﺩﻭل.
ﺩﻤﺞ :Merge
ﺘﻀﻴﻑ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻟﻤﺭﺴل ﺇﻟﻴﻬﺎ ﻜﻤﻌﺎﻤل ،ﺇﻟـﻰ ﺍﻟﺠـﺩﻭل ﺍﻟﺤـﺎﻟﻲ ،ﻭﺇﻥ ﻜﺎﻨـﺕ
ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻀﺎﻓﺔ ﻤﻭﺠﻭﺩﺓ ﺴﺎﺒﻘﺎ ،ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﺍﻟﺴـﺠﻼﺕ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﺒﻘـﻴﻡ ﺍﻟﺴـﺠﻼﺕ
ﺍﻟﻘﺎﺩﻤﺔ.
ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻨﻔﺱ ﺼﻴﻎ ﺍﻟﻭﺴﻴﻠﺔ ،DataSet.Mergeﻤﻊ ﺍﺨـﺘﻼﻑ ﻭﺍﺤـﺩ ،ﻫـﻭ ﺃﻥ
ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ﻓﻲ ﻫﺫﻩ ﺍﻟﺼﻴﻎ ﻫﻭ ﻜﺎﺌﻥ ﺠﺩﻭل ﺒﻴﺎﻨﺎﺕ DataTableﻭﻟﻴﺱ .DataSet
٢٨٢
ﺇﻨﺸﺎﺀ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ :CreateDataReader
ﺘﻌﻴﺩ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺠﺩﻭل ،DataTableReaderﺍﻟﺫﻱ ﻴﻤﻜﻨﻙ ﻤﻥ ﺨﻼﻟﻪ ﺍﻟﻤﺭﻭﺭ ﻋﺒﺭ
ﻜل ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ ..ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ DataTableReaderﻻﺤﻘﺎ ﻓـﻲ
ﻫﺫﺍ ﺍﻟﻔﺼل.
٢٨٤
ﺍﻟﻌﻤﻭﺩ ﻴﺘﻐﻴﺭ :ColumnChanging
ﻤﻤﺎﺜل ﻟﻠﺤﺩﺙ ﺍﻟﺴﺎﺒﻕ ،ﻭﻟﻜﻨﻪ ﻴﻨﻁﻠﻕ ﻋﻨﺩ ﻤﺤﺎﻭﻟﺔ ﺇﺠﺭﺍﺀ ﺍﻟﺘﻐﻴﻴﺭ ﻓﻲ ﺃﺤﺩ ﺃﻋﻤﺩﺓ ﺍﻟﺠـﺩﻭل
)ﺃﻱ ﻗﺒل ﺤﺩﻭﺙ ﺍﻟﺘﻐﻴﻴﺭ ﺒﺎﻟﻔﻌل( ..ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻹﻟﻐـﺎﺀ ﺘﻐﻴﻴـﺭ ﻗﻴﻤـﺔ
ﺍﻟﺨﺎﻨﺔ:
;]e.ProposedValue = e.Row[e.Column
ﻟﻜﻥ ﻋﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﺠﻤﻠﺔ ﺩﺍﺨل ﺸﺭﻁ ،ﻓﻠﻭ ﺍﺴﺘﺨﺩﻤﺘﻬﺎ ﻫﻜـﺫﺍ ﺒﻤﻔﺭﺩﻫـﺎ ﻓﺴـﺘﻤﻨﻊ
ﺍﻟﻤﺴﺘﺨﺩﻡ ﻤﻥ ﺘﻐﻴﻴﺭ ﺃﻱ ﺨﺎﻨﺔ ﻓﻲ ﺃﻱ ﻋﻤﻭﺩ ﻓﻲ ﺍﻟﺠﺩﻭل ..ﻟﻬﺫﺍ ﻓـﺎﻟﻌﻤﻠﻲ ﺃﻥ ﺘﺴـﺘﺨﺩﻤﻬﺎ
ﻟﻤﻨﻊ ﺒﻌﺽ ﺍﻟﻘﻴﻡ ﺍﻟﺨﺎﻁﺌﺔ ،ﻤﺜل ﺘﺭﻙ ﻋﻤﻭﺩ ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ﻓﺎﺭﻏﺎ ﻷﻥ ﻫﺫﺍ ﻏﻴﺭ ﻤﻘﺒﻭل ﻓـﻲ
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﺴﺘﺠﺩ ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﻓﻲ ﺍﻟﻔﺌﺔ ﺍﻟﺠﺯﺌﻴﺔ ﻟﺠﺩﻭل ﺍﻟﻤـﺅﻟﻔﻴﻥ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ :TableAdapter
)"" == if (e.Column == AuthorColumn && e.ProposedValue
;]e.ProposedValue = e.Row[e.Column
ﻭﻴﻤﻜﻨﻙ ﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ ،ﺇﻀﺎﻓﺔ ﺃﻱ ﺸﺭﻭﻁ ﺃﺨﺭﻯ ﻟﻠﺘﺄﻜﺩ ﻤﻥ ﺼﻼﺤﻴﺔ ﺍﻟﻘـﻴﻡ ﺍﻟﺘـﻲ ﻴـﺩﺨﻠﻬﺎ
ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﺒﺎﻗﻲ ﺍﻷﻋﻤﺩﺓ.
٢٨٦
ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻴﻨﻁﻠﻕ ﻭﺍﻟﺼﻑ ﻤﺎ ﺯﺍل ﻤﻭﺠﻭﺩﺍ ﻓﻌﻼ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ ﺼـﻔﻭﻑ
ﺍﻟﺠﺩﻭل ﻟﻜﻥ ﺤﺎﻟﺘﻪ ﺘﻜﻭﻥ ،DELETEDﻭﻫﻭ ﻤﺎ ﺴﻴﺴﺒﺏ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻟﻭ ﺤﺎﻭﻟﺕ
ﺇﻀﺎﻓﺔ ﺍﻟﺼﻑ ﻤﺭﺓ ﺃﺨﺭﻯ ﺇﻟﻰ ﺍﻟﺠﺩﻭل!!
٢٨٨
ﺘﺤﺫﻴﺭ ﻫﺎﻡ:
ﻻ ﺘﻌﺭﻑ ﻨﺴﺨﺔ ﺠﺩﻴﺩﺓ ﻤﻥ ﺍﻟﻤﺠﻤﻭﻋﺔ TempDsﻋﻠﻰ ﻤﺴﺘﻭﻯ ﻓﺌﺔ ﺠﺩﻭل ﺍﻟﻤـﺅﻟﻔﻴﻥ..
ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﻏﻴﺭ ﺼﺤﻴﺢ:
;) ( DsAuthorsBooks TempDs = new DsAuthorsBooks
ﺍﻟﺴﺒﺏ ﻓﻲ ﻫﺫﺍ ﺃﻨﻪ ﺴﻴﺅﺩﻱ ﺇﻟﻰ ﺘﻌﺭﻴﻑ ﺩﺍﺌﺭﻱ ﻴﻌﻁل ﺍﻟﺒﺭﻨﺎﻤﺞ ﻋﻥ ﺍﻟﻌﻤل ﺇﻟﻰ ﺃﻥ ﻴﺩﻤﺭ
ﻜل ﻤﺴﺎﺤﺔ ﺍﻟﺭﺼﺔ Stackﺍﻟﻤﺘﺎﺤﺔ ﻟﻪ ﻓﻲ ﺍﻟﺫﺍﻜﺭﺓ ..ﻓﻌﻨﺩ ﺘﻌﺭﻴﻑ ﻨﺴﺨﺔ ﺠﺩﻴـﺩﺓ ﻤـﻥ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،DsAuthorsBooksﺴﺘﻘﻭﻡ ﺒﺘﻌﺭﻴﻑ ﻨﺴﺨﺔ ﻤﻥ ﺠﺩﻭل ﺍﻟﻤـﺅﻟﻔﻴﻥ،
ﺍﻟﺘﻲ ﺴﺘﻘﻭﻡ ﺒﺘﻌﺭﻴﻑ ﻨﺴﺨﺔ ﺍﺤﺘﻴﺎﻁﻴﺔ ﻤﻥ ﺍﻟﻤﺠﻤﻭﻋﺔ TempDsﺍﻟﺘﻲ ﺴﺘﻘﻭﻡ ﺒﺘﻌﺭﻴـﻑ
ﻨﺴﺨﺔ ﺠﺩﻴﺩﺓ ﻤﻥ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﺍﻟﺘﻲ ﺴﺘﻘﻭﻡ ﺒﺘﻌﺭﻴﻑ ﻨﺴﺨﺔ ﺍﺤﺘﻴﺎﻁﻴﺔ ﻤﻥ ﺍﻟﻤﺠﻤﻭﻋﺔ
TempDsﻭﻫﻜﺫﺍ ﺇﻟﻰ ﻤﺎ ﻻ ﻨﻬﺎﻴﺔ!
ﻭﻫﺫﺍ ﻨﻔﺱ ﻤﺎ ﺴﻴﺤﺩﺙ ﺇﻥ ﺍﺴﺘﺨﺩﻤﺕ ﺠﺩﻭل ﻤﺅﻟﻔﻴﻥ ﺍﺤﺘﻴﺎﻁﻴﺎ ﻭﻋﺭﻓﺕ ﻨﺴﺨﺔ ﺠﺩﻴﺩﺓ ﻤﻨﻪ
ﻋﻠﻰ ﻤﺴﺘﻭﻯ ﺍﻟﻔﺌﺔ.
ﻭﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ،ﻋﺭﻑ ﺍﻟﻤﺘﻐﻴﺭ ﻋﻠﻰ ﻤﺴﺘﻭﻯ ﺍﻟﻔﺌﺔ ﺒﺩﻭﻥ ﺍﻟﻜﻠﻤﺔ :New
;DsAuthorsBooks TempDs
ﺜﻡ ﻀﻊ ﺍﻟﻨﺴﺨﺔ ﺍﻟﺠﺩﻴﺩﺓ ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﺘﻐﻴﺭ ﻓﻲ ﺍﻟﺤﺩﺙ :RowDeleting
;) ( TempDs = new DsAuthorsBooks
ﻟﻜﻥ ..ﻟﻤﺎﺫﺍ ﻻ ﻨﺴﺘﺨﺩﻡ ﻨﺭﻓﺽ ﺍﻟﺘﻐﻴﻴﺭ ﺍﻟﺫﻱ ﺤﺩﺙ ﻟﻠﺼﻑ ﺍﻟﻤﺤﺫﻭﻑ ﻟﻨﺴﺘﻌﻴﺩﻩ ﻤﺒﺎﺸـﺭﺓ
ﺒﺠﻤﻠﺔ ﻜﺎﻟﺘﺎﻟﻴﺔ:
;) (e.Row.RejectChanges
ﻓﻜﺭﺓ ﺠﻴﺩﺓ ،ﻟﻜﻥ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﺴﺘﻌﻤل ﻓﻘﻁ ﻤﻊ ﺍﻟﻤﺅﻟﻔﻴﻥ ﺍﻟﻘﺎﺩﻤﻴﻥ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺃﻤﺎ
ﺇﺫﺍ ﺃﻀﺎﻑ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻤﺅﻟﻔﺎ ،ﺜﻡ ﻗﺭﺭ ﺤﺫﻓﻪ ،ﺜﻡ ﻀـﻐﻁ Cancelﻟﻌـﺩﻡ ﺇﺘﻤـﺎﻡ ﻋﻤﻠﻴـﺔ
ﺍﻟﺤﺫﻑ ،ﻓﺴﺘﺴﺒﺏ ﺍﻟﻭﺴﻴﻠﺔ RejectChangesﺨﻁﺄ ،ﻷﻥ ﺍﻟﺴﺠل ﺍﻟﻤﻀﺎﻑ ﻴﻔﻘﺩ ﻜل ﻗﻴﻤﻪ
ﻓﻌﻠﻴﺎ ﻋﻨﺩ ﺤﺫﻓﻪ!! ..ﻫﺫﺍ ﺭﻏﻡ ﺃﻥ ﻫﺫﺍ ﺍﻟﺼﻑ ﻤﺎ ﺯﺍل ﻤﻭﺠﻭﺩﺍ ﻓﻲ ﺍﻟﺠﺩﻭل ،ﻭﺘﺴﺘﻁﻴﻊ ﺃﻥ
ﺘﺤﺼل ﻋﻠﻰ ﺭﻗﻤﻪ ﺒﺎﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ:
;)) (MessageBox.Show(this.Rows.IndexOf(e.Row).ToString
٢٨٩
ﻟﻜﻥ ﺤﺘﻰ ﻟﻭ ﻟﻡ ﻴﺤﺩﺙ ﻫﺫﺍ ﺍﻟﺨﻁﺄ ،ﻓﺴﻴﺅﺩﻱ ﺇﻟﻐﺎﺀ ﺘﻌﺩﻴﻼﺕ ﻫﺫﺍ ﺍﻟﺼﻑ ﺍﻟﻤﻀـﺎﻑ ﺇﻟـﻰ
ﺤﺫﻓﻪ ﻤﻥ ﺍﻟﺠﺩﻭل ،ﻭﻫﻜﺫﺍ ﻟﻥ ﺘﺴﺘﻌﻴﺩ ﺍﻟﺼﻑ ﻓﻲ ﻜل ﺍﻷﺤﻭﺍل!
ﺃﻴﻀﺎ ،ﻻ ﻴﻤﻜﻨﻙ ﺭﻓﺽ ﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺠﺩﻭل ﻜﻠﻪ:
;) (this.RejectChanges
ﻷﻥ ﻫﺫﺍ ﺴﻴﻀﻴﻊ ﻜل ﺍﻟﺘﻌﺩﻴﻼﺕ ﺍﻟﺘﻲ ﻗﺎﻡ ﺒﻬﺎ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻭﻟﻡ ﻴﺤﻔﻅﻬﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ،
ﻜﻤﺎ ﺃﻨﻪ ﺴﻴﻌﻴﺩ ﻜل ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺤﺫﻓﻬﺎ ﻤﻥ ﻗﺒل ،ﻭﻟﻴﺱ ﻓﻘﻁ ﺁﺨﺭ ﺴﺠل ﻤﺤﺫﻭﻑ!
ﻟﻬﺫﺍ ﺘﻅل ﻁﺭﻴﻘﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻻﺤﺘﻴﺎﻁﻴﺔ ﺃﻓﻀل ﻭﺃﺩﻕ ﻁﺭﻴﻘﺔ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ.
ﻭﺍﻀﺢ ﻁﺒﻌﺎ ﺃﻥ ﻤﻴﻜﺭﻭﺴﻭﻓﺕ ﻜﺎﻨﺕ ﺴﺘﺤﻴل ﺤﻴﺎﺘﻨﺎ ﺇﻟﻰ ﻨﻌـﻴﻡ ﻟـﻭ ﺃﻀـﺎﻓﺕ ﺍﻟﺨﺎﺼـﻴﺔ
e.Cancelﻓﻲ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻜﻤﺎ ﻫﻭ ﻤﺄﻟﻭﻑ!
ﺇﻀﺎﻓﺔ :Add
ﺘﻀﻴﻑ ﺼﻔﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ ،ﻭﻟﻬﺎ ﺼﻴﻐﺘﺎﻥ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﺼﻑ DataRowﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺇﻀﺎﻓﺘﻪ.
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺔ ﻜﺎﺌﻨﺎﺕ Object Arrayﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻘﻴﻡ ﺍﻟﺘﻲ
ﺘﺭﻴﺩ ﻭﻀﻌﻬﺎ ﻓﻲ ﺨﺎﻨﺎﺕ ﺍﻟﺴﺠل ..ﻻﺤﻅ ﺃﻨﻙ ﺘﺘﻌﺎﻤل ﻤﻊ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ ﻤـﻥ
ﺨﻼل ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ،DataTableﻟﻬﺫﺍ ﻓﺈﻥ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋـﺔ ﺘﻌـﺭﻑ ﺘﺭﻜﻴـﺏ
ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺴﺘﻀﻴﻔﻬﺎ ﺇﻟﻴﻬﺎ ،ﻭﻋﻠﻴﻙ ﻤﺭﺍﻋﺎﺓ ﺘﺭﺘﻴﺏ ﺍﻷﻋﻤﺩﺓ ﻭﺃﻨـﻭﺍﻉ ﺒﻴﺎﻨﺎﺘﻬـﺎ
ﻋﻨﺩ ﻭﻀﻌﻬﺎ ﻓﻲ ﺍﻟﻤﺼﻔﻭﻓﺔ ﺤﺘﻰ ﻻ ﻴﺤﺩﺙ ﺨﻁﺄ ،ﻭﻋﻠﻴـﻙ ﻜـﺫﻟﻙ ﺘـﺭﻙ ﺨﺎﻨـﺔ
ﺍﻟﻤﺼﻔﻭﻓﺔ ﺍﻟﻤﻨﺎﻅﺭﺓ ﻟﺨﺎﻨﺔ ﺍﻟﺘﺭﻗﻴﻡ ﺍﻟﺘﻠﻘﺎﺌﻲ ﻓﺎﺭﻏﺔ.
ﻭﺘﻘﻭﻡ ﻫﺫﻩ ﺍﻟﺼﻴﻐﺔ ﺒﺈﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺼﻑ ﺠﺩﻴﺩ ﻭﻭﻀﻊ ﺍﻟﻘـﻴﻡ ﺒـﻪ ﻭﺇﻀـﺎﻓﺘﻪ ﺇﻟـﻰ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ ،ﻭﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻜﺎﺌﻥ ﺼﻑ DataRowﻴﺸﻴﺭ ﺇﻟﻰ ﺍﻟﺼﻑ ﺍﻟﺫﻱ
ﺘﻤﺕ ﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ.
٢٩١
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺔ ﻜﺎﺌﻨﺎﺕ ،Objectsﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻗـﻴﻡ ﺍﻟﻤﻔﺘـﺎﺡ
ﺍﻷﺴﺎﺴﻲ ،ﻭﺫﻟﻙ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻟﻠﺠﺩﻭل ﻴﺘﻜﻭﻥ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﻋﻤﻭﺩ.
ﺍﻟﺒﺤﺙ ﻋﻥ :Find
ﻤﻤﺎﺜﻠﺔ ﻟﻠﻭﺴﻴﻠﺔ ﺍﻟﺴﺎﺒﻘﺔ ﻓﻲ ﺼﻴﻐﺘﻴﻬﺎ ،ﺇﻻ ﺃﻨﻬﺎ ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺼﻑ DataRowﺍﻟﺫﻱ ﻴﻤﻠﻙ
ﻤﻔﺘﺎﺤﺎ ﺃﺴﺎﺴﻴﺎ ﻤﺴﺎﻭﻴﺎ ﻟﻠﻘﻴﻤﺔ ﺍﻟﻤﺭﺴﻠﺔ ﻜﻤﻌﺎﻤل ،ﻭﺘﻌﻴﺩ nullﺇﺫﺍ ﻟﻡ ﺘﻌﺜﺭ ﻋﻠﻰ ﺍﻟﺼﻑ.
ﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﻟﻡ ﻴﻜﻥ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﺘﺒﺤﺙ ﻓﻴﻪ ﻴﺤﺘﻭﻱ ﻋﻠـﻰ
ﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ ..ﻴﻤﻜﻥ ﺃﻥ ﻴﺤﺩﺙ ﻫﺫﺍ ﺭﻏﻡ ﺍﻥ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ﻓـﻲ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ ،ﻭﺫﻟﻙ ﺇﺫﺍ ﺍﺴﺘﺨﺩﻤﺕ ﺍﻟﻭﺴﻴﻠﺔ Fillﻟﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ،
ﺩﻭﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ FillSchemaﺃﻭﻻ ،ﻓﻬﻲ ﺍﻟﺘﻲ ﺘﻨﺸﻲﺀ ﺍﻟﻤﻔﺘـﺎﺡ ﺍﻷﺴﺎﺴـﻲ ﻓـﻲ
ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
٢٩٢
ﻓﺌﺔ ﺼﻑﹼ ﺍﻟﺒﻴﺎﻨﺎﺕ DataRow Class
ﺘﺘﻌﺎﻤل ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻤﻊ ﺃﺤﺩ ﺼﻔﻭﻑ ﺍﻟﺠﺩﻭل ،ﻭﻫﻲ ﻻ ﺘﻤﺘﻠـﻙ ﺤـﺩﺙ ﺇﻨﺸـﺎﺀ ﻋﺎﻤـﺎ Public
،Constructorﻟﻬﺫﺍ ﻻ ﺘﺴﺘﻁﻴﻊ ﺇﻨﺸﺎﺀ ﻨﺴﺨﺔ ﺠﺩﻴﺩﺓ ﻤﻨﻬﺎ ﻤﺒﺎﺸﺭﺓ ،ﻭﺒﺩﻻ ﻤـﻥ ﻫـﺫﺍ ﻋﻠﻴـﻙ
ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ DataTable.NewRowﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻜﺎﺌﻥ ﺼﻑ ﺠﺩﻴﺩ ..ﺍﻟﺤﻜﻤﺔ ﻤـﻥ
ﻫﺫﺍ ،ﻫﻲ ﺃﻥ ﺍﻟﻭﺴﻴﻠﺔ NewRowﺘﺴﺘﺨﺩﻡ ﻤﺨﻁﻁ ﺍﻟﺠﺩﻭل ﻹﻨﺸﺎﺀ ﺼﻑ ﻟﻪ ﻨﻔـﺱ ﺍﻷﻋﻤـﺩﺓ
ﺒﻨﻔﺱ ﺃﻨﻭﺍﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻨﻔﺱ ﺍﻟﺘﺭﺘﻴﺏ ..ﻭﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻴﻌ ﺭﻑ ﺼﻔﹼﹼﺎ ﺠﺩﻴﺩﺍ ﻭﻴﻀﻴﻔﻪ ﺇﻟﻰ ﺠـﺩﻭل
ﺍﻟﻜﺘﺏ:
;]"var TblBooks = Ds.Tables["Books
;) (DataRow BooksRow = TblBooks.NewRow
;)TblBooks.Rows.Add(BooksRow
ﺍﻟﺠﺩﻭل :Table
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل DataTableﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺴﺠلّ ﺍﻟﺤـﺎﻟﻲ ..ﺘـﺫﻜﺭ ﺃﻨـﻙ ﻻ
ﺘﺴﺘﻁﻴﻊ ﺇﻨﺸﺎﺀ ﺴﺠل ﺠﺩﻴﺩ ﺒﺩﻭﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ NewRowﻤﻥ ﺃﺤﺩ ﺍﻟﺠﺩﺍﻭل ،ﻟﻬـﺫﺍ
ﺤﺘﻰ ﻟﻭ ﻟﻡ ﻴﻜﻥ ﺍﻟﺴﺠل ﻤﻀﺎﻓﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺼـﻔﻭﻑ ﺍﻟﺠـﺩﻭل ،Rowsﻓـﺈﻥ ﻫـﺫﻩ
ﺍﻟﺨﺎﺼﻴﺔ ﺴﺘﻅل ﺘﺸﻴﺭ ﺩﺍﺌﻤﺎ ﺇﻟﻰ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﺘﻡ ﺇﻨﺸﺎﺀ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻤﻨﻪ.
ﺍﻟﻌﻨﺼﺭ :Item
ﻫﺫﻩ ﻫﻲ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ،ﻭﻫﻲ ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﺤﻔﻭﻅﺔ ﻓﻲ ﺇﺤﺩﻯ ﺨﺎﻨـﺎﺕ
ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ..ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻥ ﺍﻟﻨﻭﻉ ،Objectﻟﻴﻤﻜﻨـﻙ ﺍﻟﺘﻌﺎﻤـل ﻤـﻊ
ﻤﺨﺘﻠﻑ ﺃﻨﻭﺍﻉ ﺍﻷﻋﻤﺩﺓ ..ﻭﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺒﻌﺽ ﺍﻟﺼﻴﻎ ﻟﻬﺎ ﻤﻌﺎﻤل ﻭﺍﺤﺩ ،ﻴﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺃﻭ ﺭﻗﻤﻪ ،ﺃﻭ ﻜـﺎﺌﻥ ﺍﻟﻌﻤـﻭﺩ
DataColumnﺍﻟﺫﻱ ﻴﻤﺜﻠﻪ ..ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻘﺭﺃ ﺍﺴﻡ ﺍﻟﻤﺅﻟـﻑ ﺍﻟﻤﻭﺠـﻭﺩ ﻓـﻲ
ﺍﻟﺼﻑﹼ ﺍﻟﺜﺎﻟﺙ ﻓﻲ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ )ﺍﻟﺼﻑﹼ ﺍﻷﻭل ﻫﻭ ﺍﻟﺼﻑ ﺭﻗﻡ ﺼﻔﺭ(:
٢٩٣
;]var R = Ds.Tables["Authors"].Rows[2
;]"var X = R["Author
ﻭﻫﻭ ﻤﺎ ﻴﻤﻜﻨﻙ ﻓﻌﻠﻪ ﻓﻲ ﺴﻁﺭ ﻭﺍﺤﺩ ﻜﺎﻟﺘﺎﻟﻲ:
;]"var X = Ds.Tables["Authors"].Rows[2]["Author
ﺤﻴﺙ ﻴﺒﺩﻭ ﺃﻨﻨﺎ ﻨﺘﻌﺎﻤل ﻤﻊ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ Rowsﻜﺄﻨﻬﺎ ﻤﺼﻔﻭﻓﺔ ﻤﺼﻔﻭﻓﺎﺕ.
ﺃﻤﺎ ﻟﻭ ﻜﻨﺕ ﺘﺘﻌﺎﻤل ﻤﻊ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ،ﻓﺴﻴﺨﺘﺼﺭ ﺍﻟﻜﻭﺩ ﺍﻟﺴـﺎﺒﻕ
ﺇﻟﻰ:
;]"var X = Ds.Authors[2]["Author
ﺃﻭ ﺒﺼﻭﺭﺓ ﺃﻓﻀل:
;var X = Ds.Authors[2].Author
-٢ﺒﻌﺽ ﺍﻟﺼﻴﻎ ﺘﺯﻴﺩ ﺒﻤﻌﺎﻤل ﺜﺎﻥ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﺭﻗﻡ ،DataRowVersionﻟﺘﺤـﺩﺩ
ﻤﻥ ﺨﻼﻟﻪ ﻨﺴﺨﺔ ﺍﻟﺴﺠل ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻬﺎ ..ﻟﻜﻥ ﻫﺫﻩ ﺍﻟﺼﻴﻎ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ،
ﻭﻻ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﺘﻐﻴﻴﺭ ﻗﻴﻡ ﺍﻟﺴﺠل ..ﻭﻴﻤﺘﻠﻙ ﺍﻟﻤـﺭﻗﻡ DataRowVersion
ﺍﻟﻘﻴﻡ ﺍﻟﺘﺎﻟﻴﺔ:
٢٩٤
ﻭﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻤﻊ ﺍﻟﺼﻴﻎ ﺍﻟﺘﻲ ﻻ ﺘﻤﺘﻠﻙ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ﻫﻲ .Default
ﺍﻨﻅﺭ ﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ:
ﻋﺭﺽ ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ //
MessageBox.Show(Row[0,
;)) ( DataRowVersion.Original].ToString
ﻋﺭﺽ ﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ //
MessageBox.Show(Row["Book",
;)) ( DataRowVersion.Current].ToString
ﻋﺭﺽ ﺍﻟﻨﺴﺨﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ //
;)) (MessageBox.Show(Row["Book"].ToString
٢٩٥
ﺨﻁﺄ ﺍﻟﺼﻑ :RowError
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﻴﺼﻑ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﻫﺫﺍ ﺍﻟﺼﻑﹼ ..ﻻﺤـﻅ ﺃﻥ ﻭﻀـﻊ
ﺃﻱ ﻨﺹ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻴﻐﻴﺭ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ HasErrorsﺇﻟﻰ ،trueﻭﻴﺠﻌل ﺠـﺩﻭل
ﺍﻟﻌﺭﺽ ﻴﻀﻊ ﺃﻴﻘﻭﻨﺔ ﺍﻟﺨﻁﺄ ﺒﺠﻭﺍﺭ ﻫﺫﺍ ﺍﻟﺼﻑ.
٢٩٦
ﺘﻐﻴﻴﺭ ﺍﻟﺤﺎﻟﺔ ﺇﻟﻰ ﻤﻌﺩل :SetModified
ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ RowStateﺇﻟﻰ ..Modifiedﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺨﻁـﺄ ﻓـﻲ
ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﻜﺎﻥ ﻜﺎﻨﺕ ﺤﺎﻟﺔ ﺍﻟﺴﺠل ﺘﺸﻴﺭ ﺇﻟﻰ ﺃﻨﻪ ﻤﻌﺩل ..ﻭﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸـﻜﻠﺔ ،ﻋﻠﻴـﻙ
ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ AcceptChangesﺃﻭﻻ.
٢٩٨
ﺇﻨﻬﺎﺀ ﺍﻟﺘﺤﺭﻴﺭ :EndEdit
ﺘﻨﻬﻲ ﻋﻤﻠ ﻴﺔ ﺍﻟﺘﺤﺭﻴﺭ ﺍﻟﺘﻲ ﺒﺩﺃﺕ ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ،Begin Editﻭﺘﻔﺤﺹ ﺍﻟﻘﻴﻡ ﺍﻟﺘﻲ ﺘﻡ
ﺇﺩﺨﺎﻟﻬﺎ ﻓﻲ ﺍﻟﺴﺠل ﺃﺜﻨﺎﺀ ﻭﻀﻊ ﺍﻟﺘﺤﺭﻴﺭ ،ﻓﺈﻥ ﻜﺎﻨﺕ ﺼﺤﻴﺤﺔ ﺘﻘﻭﻡ ﺒﺤﻔﻅ ﻨﺴﺨﺔ ﺍﻟﺴـﺠل
ﺍﻟﻤﻘﺘﺭﺤﺔ Proposed Versionﻓﻲ ﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ..Current Versionﻫﺫﺍ ﻤﻌﻨـﺎﻩ
ﺤﻔﻅ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺤﺩﺜﺕ ﻋﻠﻰ ﺍﻟﺴﺠل ﺃﺜﻨﺎﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺭﻴﺭ.
ﻭﻴﺘﻡ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺘﻠﻘﺎﺌﻴﺎ ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ .AcceptChanges
٢٩٩
ﻤﻌﺭﻓﺔ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺒﻬﺎ ﺃﺨﻁﺎﺀ :GetColumnsInError
ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺃﻋﻤﺩﺓ DataColumn Arrayﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺒﻬﺎ ﺃﺨﻁﺎﺀ ﻓﻲ
ﺍﻟﺴﺠلّ ﺍﻟﺤﺎﻟﻲ.
ﺤﺫﻑ :Delete
ﺘﻀﻊ ﺍﻟﻘﻴﻤﺔ Deletedﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ RowStateﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ..ﻫﺫﺍ ﻴﺘـﻴﺢ
ﻟﻙ ﺍﻟﺘﺭﺍﺠﻊ ﻋﻥ ﺤﺫﻑ ﻫﺫﺍ ﺍﻟﺴﺠل ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ RejectChangesﺃﻭ ﺤﺫﻓﻪ ﻓﻌﻼ
ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ .AcceptChanges
ﻻﺤﻅ ﺃﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ Deleteﻤـﻊ ﺴـﺠل ﻤﻀـﺎﻑ )(RowState = Added
ﺴﻴﺅﺩﻱ ﺇﻟﻰ ﺤﺫﻑ ﻫﺫﺍ ﺍﻟﺴﺠل ﻓﻲ ﺍﻟﺤﺎل.
٣٠١
ﻫل ﻫﻲ ﻋﺩﻡ :IsNull
ﺘﻌﻴﺩ trueﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﺨﺎﻨﺔ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺴﺠلّ ﺍﻟﺤﺎﻟﻲ ﻭﺍﻟﻌﻤـﻭﺩ ﺍﻟﻤﺭﺴـل ﻜﻤﻌﺎﻤـل
ﻓﺎﺭﻏﺔ ..DbNullﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺒﻌﺽ ﺍﻟﺼﻴﻎ ﻟﻬﺎ ﻤﻌﺎﻤل ﻭﺍﺤﺩ ،ﻴﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺃﻭ ﺭﻗﻤﻪ ﺃﻭ ﻜـﺎﺌﻥ ﺍﻟﻌﻤـﻭﺩ
DataCoulmnﺍﻟﺫﻱ ﻴﻤﺜﻠﻪ.
-٢ﻭﻫﻨﺎﻙ ﺼﻴﻎ ﻟﻬﺎ ﻤﻌﺎﻤل ﺜﺎﻥ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﺭﻗﻡ ،DataRowVersionﻟﻴﻤﻜﻨﻙ ﻤﻥ
ﺨﻼﻟﻪ ﺘﺤﺩﻴﺩ ﺍﻟﻨﺴﺨﺔ Versionﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻓﺤﺹ ﻗﻴﻤﻬﺎ.
٣٠٢
ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ DataColumnCollection Class
ﺍﻟﻌﻨﺼﺭ :Item
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻋﻤﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ DataColumnﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﻨﺎﺀ ﻋﻠﻰ ﺍﻟﻤﻌﺎﻤـل
ﺍﻟﻤﺭﺴل ﺇﻟﻴﻬﺎ ..ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺼﻴﻐﺘﺎﻥ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﺭﻗﻡ ﺍﻟﺨﺎﻨﺔ ﺍﻟﺘﻲ ﻴﻭﺠﺩ ﺒﻬﺎ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﻤﺠﻤﻭﻋﺔ ..ﻻﺤﻅ
ﺃﻥ ﺨﻁﺄ ﺴﻴﺤﺩﺙ ﻟﻭ ﺃﺭﺴﻠﺕ ﺭﻗﻡ ﺨﺎﻨﺔ ﻏﻴﺭ ﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﻤﺠﻤﻭﻋﺔ.
-٢ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﺠﺩﻭل ،ﻟﻴﺘﻡ ﺍﻟﺒﺤﺙ ﻋﻨﻪ ﻓـﻲ
ﺍﻟﻤﺠﻤﻭﻋﺔ ،ﻓﺈﻥ ﻜﺎﻥ ﻤﻭﺠﻭﺩﺍ ﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﻴﻤﺜﻠﻪ ،ﻭﺇﻥ ﻟﻡ
ﻴﻜﻥ ﻤﻭﺠﻭﺩﺍ ﻓﺈﻨﻬﺎ ﺘﻌﻴﺩ nullﻭﻻ ﻴﺤﺩﺙ ﺨﻁﺄ.
ﺇﻀﺎﻓﺔ :Add
ﺘﻀﻴﻑ ﻋﻤﻭﺩﺍ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ ،ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﺍﻷﻋﻤـﺩﺓ ﺍﻟﺘـﻲ ﺘﻀـﻴﻔﻬﺎ ﺇﻟـﻰ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻫﻲ ﺃﻋﻤﺩﺓ ﻤﺅﻗﺘﺔ ﺨﺎﺼﺔ ﺒﺎﻟﺒﺭﻨﺎﻤﺞ ﻓﻘـﻁ ،ﻭﻻ ﺘﻅﻬـﺭ ﻓـﻲ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺤﺘﻰ ﺒﻌﺩ ﺇﺠﺭﺍﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ ..Updateﻟﻜﻥ ﻟﻭ ﻜﻨﺕ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺇﻨﺸـﺎﺌﻬﺎ
ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﻌﻠﻴﻙ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺃﻭﺍﻤﺭ SQLﺍﻟﺨﺎﺼﺔ ﺒﺈﻨﺸﺎﺀ ﺃﻋﻤﺩﺓ ﺘﻨﺎﻅﺭ ﺍﻷﻋﻤـﺩﺓ
ﺍﻟﺠﺩﻴﺩﺓ ﺍﻟﺘﻲ ﺃﻀﻔﺘﻬﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
٣٠٣
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ ،ﻭﻫﻲ ﺘﻨﺸﺊ ﻋﻤﻭﺩﺍ ﺠﺩﻴﺩﺍ ﺒﺎﻻﺴـﻡ ﺍﻻﻓﺘﺭﺍﻀـﻲ
) Column1ﺃﻭ ... Column2ﺇﻟـﺦ( ..ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﺴﻴﺘﻌﺎﻤل ﻤﻊ ﺒﻴﺎﻨﺎﺕ
ﻨﺼﻴﺔ .String
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ DataColumnﻭﺘﻀﻴﻔﻪ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ.
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ،ﻭﺘﻘﻭﻡ ﺒﺈﻨﺸﺎﺌﻪ ﻭﺇﻀﺎﻓﺘﻪ ﺇﻟـﻰ ﺍﻟﻤﺠﻤﻭﻋـﺔ..
ﻭﺘﺴﺘﻁﻴﻊ ﺇﺭﺴﺎل ﻨﺹ ﻓﺎﺭﻍ ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ،ﻹﻨﺸﺎﺀ ﻋﻤﻭﺩ ﻟﻪ ﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ
) Column1ﺃﻭ ... Column2ﺇﻟﺦ(.
-٤ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ ﻤﻥ ﻨـﻭﻉ ﻓﺌـﺔ ﺍﻟﻨـﻭﻉ
،Typeﻟﻴﻤﻜﻨﻙ ﻤﻥ ﺨﻼﻟﻪ ﺘﺤﺩﻴﺩ ﻨﻭﻉ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻌﻤﻭﺩ ..ﻻﺤﻅ ﺃﻥ ﻨـﻭﻉ ﺍﻟﻌﻤـﻭﺩ
ﻴﻌﺘﺒﺭ ﻨﺼﻴﺎ Stringﻓﻲ ﺍﻟﺼﻴﻎ ﺍﻟﺘﻲ ﻻ ﺘﺴﺘﻘﺒل ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل.
-٥ﺍﻟﺼﻴﻐﺔ ﺍﻟﺨﺎﻤﺴﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻟﺙ ،ﻴﺴﺘﻘﺒل ﻨﺼـﺎ ﻴﻤﺜـل
ﺍﻟﺼﻴﻐﺔ ﺍﻟﺘﻲ ﺴﺘﻭﻀﻊ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ Expressionﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﻌﻤﻭﺩ ،ﻤﻤﺎ ﻴﺘـﻴﺢ
ﻟﻙ ﺇﻨﺸﺎﺀ ﻋﻤﻭﺩ ﻤﺤﺴﻭﺏ ..Calculated Columnﻭﺴـﻨﺘﻌﺭﻑ ﻋﻠـﻰ ﻫـﺫﻩ
ﺍﻟﺨﺎﺼﻴﺔ ﻋﻨﺩ ﺍﻟﺘﻌﺭﻑ ﻋﻠﻰ ﻓﺌﺔ ﻋﻤﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ .DataColumn
ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﺼﻴﻎ ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ DataColumnﺍﻟﺫﻱ ﺃﻀﻴﻑ ﺇﻟـﻰ ﻤﺠﻤﻭﻋـﺔ
ﺍﻷﻋﻤﺩﺓ ،ﻤﺎ ﻋﺩﺍ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻓﺄﻨﺕ ﺘﺭﺴل ﺇﻟﻴﻬﺎ ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ﺒﺎﻟﻔﻌل ﻟﻬﺫﺍ ﻟﻴﺴـﺕ ﻟﻬـﺎ
ﻗﻴﻤﺔ ﻋﺎﺌﺩﺓ.
ﻭﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﺍﻷﻋﻤﺩﺓ ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ ،ﻭﺫﻟﻙ ﻤﻥ
ﺨﻼل ﻨﺎﻓﺫﺓ ﺨﺼﺎﺌﺹ ﺍﻟﺠﺩﻭل ..ﻟﻔﻌل ﻫﺫﺍ ﻴﺠﺏ ﺃﻥ ﻴﻜﻭﻥ ﻟﺩﻴﻙ ﻜﺎﺌﻥ ﺠﺩﻭل ﻓﻲ ﺼـﻴﻨﻴﺔ
ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤﻭﺫﺝ )ﻭﻫﺫﺍ ﻏﻴﺭ ﺸﺎﺌﻊ( ،ﺃﻭ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨـﺎﺕ ﻋﺎﺩﻴـﺔ Un-
Typed DataSetﻤﻭﻀﻭﻋﺔ ﻓﻲ ﺼﻴﻨﻴﺔ ﺍﻟﻤﻜﻭﻨﺎﺕ ،ﻓﻠﻭ ﻋﺭﻀﺕ ﺨﺼﺎﺌﺼﻬﺎ ﻓﻲ ﻨﺎﻓﺫﺓ
ﺍﻟﺨﺼﺎﺌﺹ ،ﻓﺴﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼﻴﺔ Tablesﻟﻌﺭﺽ ﻤﺤﺭﺭ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠـﺩﺍﻭل،
ﻭﻟﻭ ﺤﺩﺩﺕ ﺃﻱ ﺠﺩﻭل ﻓﻲ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ،ﻓﺴﺘﻅﻬﺭ ﺨﺼﺎﺌﺼﻪ ﻓﻲ ﺍﻟﻘﺴﻡ ﺍﻷﻴﻤـﻥ ﻤـﻥ
ﺍﻟﻨﺎﻓﺫﺓ ،ﻭﺴﺘﺠﺩ ﺒﻴﻨﻬﺎ ﺍﻟﺨﺎﺼﻴﺔ ..Columnsﻭﻟﻭ ﻀﻐﻁﺕ ﺯﺭ ﺍﻻﻨﺘﻘﺎل ﺍﻟﻤﻭﺠـﻭﺩ ﻓـﻲ
ﺨﺎﻨﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ،ﻓﺴﺘﻅﻬﺭ ﻨﺎﻓﺫﺓ ﻤﺤﺭﺭ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ ،ﻜﻤﺎ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
٣٠٤
ﻓﻲ ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﻴﻤﻜﻨﻙ ﻀﻐﻁ ﺍﻟﺯﺭ Addﻹﻀﺎﻓﺔ ﻋﻤﻭﺩ ﺠﺩﻴﺩ ،ﺤﻴﺙ ﺴﺘﻅﻬﺭ ﺨﺼـﺎﺌﺹ
ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﻘﺴﻡ ﺍﻷﻴﻤﻥ ،ﻭﻴﻤﻜﻨﻙ ﺘﻐﻴﻴﺭﻫﺎ ﻜﻤﺎ ﺘﺸﺎﺀ.
ﺤﺫﻑ :Remove
ﺘﺤﺫﻑ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻤﺭﺴل ﺇﻟﻴﻬﺎ ﻜﻤﻌﺎﻤل ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ ..ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺼﻴﻐﺘﺎﻥ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ.
-٢ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ .DataColumn
٣٠٦
ﻓﺌﺔ ﻋﻤﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ DataColumn Class
ﺘﻤﺜــل ﻫــﺫﻩ ﺍﻟﻔﺌــﺔ ﻤﺨﻁــﻁ ﺃﺤــﺩ ﺃﻋﻤــﺩﺓ ﺍﻟﺠــﺩﻭل ،ﻭﻫــﻲ ﺘــﺭﺙ ﺍﻟﻔﺌــﺔ
،MarshalByValueComponentﻤﻤﺎ ﻴﺘﻴﺢ ﻟﻙ ﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﺼﻴﻨﻴﺔ ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤـﻭﺫﺝ،
ﻭﺇﻥ ﻜﺎﻥ ﻋﻠﻴﻙ ﺃﻥ ﺘﻀﻴﻔﻬﺎ ﺃﻭﻻ ﺇﻟﻰ ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫـﺫﻩ ﺍﻟﻔﺌـﺔ ﻨﻔـﺱ ﺼـﻴﻎ ﺍﻟﻭﺴـﻴﻠﺔ Addﺍﻟﺨﺎﺼـﺔ ﺒﻤﺠﻤﻭﻋـﺔ ﺍﻷﻋﻤـﺩﺓ
،DataColumnCollectionﻤﺎ ﻋﺩﺍ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ..ﻜﻤﺎ ﺘﻭﺠﺩ ﺼـﻴﻐﺔ ﺇﻀـﺎﻓﻴﺔ ﻟﺤـﺩﺙ
ﺍﻹﻨﺸﺎﺀ ،ﺘﺴﺘﻘﺒل ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ ﺒﺎﻟﺘﺭﺘﻴﺏ:
-ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ.
-ﻜﺎﺌﻥ ﻨﻭﻉ ،Type Objectﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻨﻭﻉ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻌﻤﻭﺩ.
-ﺼﻴﻐﺔ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺘﻲ ﺴﺘﻭﻀﻊ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ Expressionﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬـﺎ ﺒﻌـﺩ
ﻗﻠﻴل.
-ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ،MappingTypeﻟﺘﻭﻀـﻊ ﻓـﻲ ﺍﻟﺨﺎﺼـﻴﺔ ColumnMapping
ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﻌﻤﻭﺩ ،ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﺒﻌﺩ ﻗﻠﻴل.
ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﻑ ﻋﻤﻭﺩﺍ ﻨﺼﻴﺎ ﺍﺴﻤﻪ Tempﻭﻴﻀﻴﻔﻪ ﺇﻟﻰ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﺤﻤل ﺍﻻﺴﻡ :Ds
;))var Clmn = new DataColumn("Temp", typeof(string
;)Ds.Tables["Books"].Columns.Add(Clmn
ﺍﻟﺠﺩﻭل :Table
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل DataTableﺍﻟﺫﻱ ﻴﻨﺘﻤﻲ ﺇﻟﻴﻪ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ.
ﺍﻟﺒﺎﺩﺌﺔ :Prefix
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﺒﺎﺩﺌﺔ ﺍﻟﺘﻲ ﺘﻤﺜل ﻨﻁﺎﻕ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﻴﻨﺘﻤﻲ ﺇﻟﻴﻪ ﺍﻟﻌﻤﻭﺩ.
ﺍﻟﺭﺘﺒﺔ :Ordinal
ﺘﻌﻴﺩ ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ ﻴﻤﺜل ﺘﺭﺘﻴﺏ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ.
ﺍﻟﻌﻨﻭﺍﻥ :Caption
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﻭﺍﻥ ﺍﻟﻌﻤﻭﺩ ..ﻤﻥ ﺍﻟﻤﻔﺭﻭﺽ ﺃﻥ ﻴﺘﻡ ﻋﺭﺽ ﻫﺫﺍ ﺍﻟﻌﻨﻭﺍﻥ ﺒﺩﻻ ﻤﻥ ﺍﺴـﻡ
ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺃﺩﻭﺍﺕ ﺭﺒﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ ،Data-Bound Controlsﻟﻜﻨﻙ ﻟﻭ ﺠﺭﺒﺕ ﻫﺫﺍ ﻤـﻊ
ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻤﺜﻼ ،ﻓﻠﻥ ﺘﺠﺩ ﻟﻪ ﺘﺄﺜﻴﺭﺍ! ..ﻴﺒﺩﻭ ﺃﻥ ﻋﻠﻴﻙ ﺭﺒﻁ ﺃﺩﺍﺓ ﺍﻟﻌـﺭﺽ ﺤﺼـﺭﻴﺎ
ﺒﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ،ﻟﻜﻲ ﺘﺭﻯ ﺘﺄﺜﻴﺭﻫﺎ ،ﻜﻤﺎ ﺴﻨﺭﻯ ﻻﺤﻘﺎ.
٣٠٨
ﺃﻗﺼﻰ ﻁﻭل :MaxLength
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺃﻗﺼﻰ ﻋﺩﺩ ﻤﻥ ﺍﻟﺤﺭﻭﻑ ﻴﻤﻜﻨﻙ ﻜﺘﺎﺒﺘﻪ ﻓﻲ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﻴﺘﻌﺎﻤل ﻤﻊ ﺒﻴﺎﻨﺎﺕ
ﻨﺼﻴﺔ ..ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ١-ﻤﻤﺎ ﻴﻌﻨﻲ ﻋﺩﻡ ﻭﺠﻭﺩ ﻗﻴﻭﺩ ﻋﻠـﻰ ﻋـﺩﺩ
ﺍﻟﺤﺭﻭﻑ ..ﻻﺤﻅ ﺃﻥ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺴﻴﺘﻡ ﺘﺠﺎﻫﻠﻬﺎ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻌﻤﻭﺩ ﻴﺘﻌﺎﻤل ﻤﻊ ﺒﻴﺎﻨﺎﺕ
ﻤﻥ ﻨﻭﻉ ﺁﺨﺭ ﻏﻴﺭ ﺍﻟﻨﺼﻭﺹ.
ﻤﺘﻔﺭﺩ :Unique
ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼ ﻴﺔ ،trueﻓﻠﻥ ﻴﺴﻤﺢ ﺒﺘﻜﺭﺍﺭ ﻗﻴﻡ ﺨﺎﻨﺎﺕ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ.
ﻻﺤﻅ ﺃﻨﻙ ﻻ ﺘﺴﺘﻁﻴﻊ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺒﻌﺩ ﺇﻀﺎﻓﺔ ﺍﻟﺨﺎﻨﺎﺕ ﺇﻟﻰ ﺍﻟﻌﻤﻭﺩ ،ﺇﻻ ﻓﻲ
ﺤﺎﻟﺔ ﻭﺍﺤﺩﺓ :ﺇﺫﺍ ﻜﻨﺕ ﺘﺤﻭل ﻤﻥ ﺍﻟﻘﻴﻤﺔ Unspecifiedﺇﻟـﻰ UnspecifiedLocalﺃﻭ
ﺍﻟﻌﻜﺱ.
ﺍﻟﺘﻌﺒﻴﺭ :Expression
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﺼﻴﻐﺔ ﺍﻟﻨﺼﻴﺔ ﻟﻠﻌﻤﻭﺩ ،ﻭﺍﻟﺘﻲ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻴﻤﺎ ﻴﻠﻲ:
-ﺤﺴﺎﺏ ﻗﻴﻡ ﺨﺎﻨﺎﺕ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺤﺎﻟﻲ ،ﻤﻥ ﻨﺎﺘﺞ ﻋﻤﻠﻴﺔ ﺤﺴﺎﺒﻴﺔ ﻋﻠﻰ ﺃﻋﻤﺩﺓ ﺃﺨـﺭﻯ،
ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻴﺴﻤﻰ ﺒﺎﻟﻌﻤﻭﺩ ﺍﻟﻤﺤﺴﻭﺏ .Calculated Column
ـﻰ ﻜــل ﺴــﺠل ،ﻻﺴــﺘﺨﺩﺍﻡ ﺍﻟﻭﺴــﻴﻠﺔ
ـﺎﺏ ﻨــﺎﺘﺞ ﺸــﺭﻁ ﻤﻌــﻴﻥ ﻋﻠـ
-ﺤﺴـ
DataTable.Selectﺒﻌﺩ ﺫﻟﻙ ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﺍﻟﺴـﺠﻼﺕ ﺍﻟﺘـﻲ ﺤﻘﻘـﺕ ﻫـﺫﺍ
ﺍﻟﺸﺭﻁ ،ﻭﺴﻨﺭﻯ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫﺫﺍ ﺒﻌﺩ ﻗﻠﻴل.
ﻭﺍﻟﺠﺩﻭل ﺍﻟﺘﺎﻟﻲ ﻴﻠﺨﺹ ﻟﻙ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻭﺍﻟﺩﻭﺍلّ ﺍﻟﺘﻲ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﺘﻜـﻭﻴﻥ ﺼـﻴﻐﺔ
ﺍﻟﻌﻤﻭﺩ:
٣١٣
ﺘﺸﻴﺭ ﺇﻟﻰ ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ ﺍﻟﺫﻱ ﻴﺩﺨل ﻓﻲ ﻋﻼﻗﺔ ﻤـﻊ ﺍﻟﺠـﺩﻭل Parent
ﻻﺤﻅ ﺃﻥ ﻋﻠﻴﻙ ﻭﻀﻊ ﺍﻟﻨﺼﻭﺹ ﻭﺍﻟﺘﻭﺍﺭﻴﺦ ﺒﻴﻥ ﺍﻟﻌﻼﻤﺘﻴﻥ ' ' ﻤﺜل:
'ﻁﻭﻴل'
''1/1/2009
٣١٦
ﻓﺌﺔ ﻗﺎﺭﺉ ﺠﺩﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ DataTableReader Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﻓﺌﺔ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﻡ ،DbDataReader Classﻭﻫﻲ ﺘﺸﺒﻪ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ
ﺍﻟﻌﺎﺩﻱ ﻓﻲ ﻁﺭﻴﻘﺔ ﻋﻤﻠﻬﺎ ،ﻟﻜﻨﻬﺎ ﻻ ﺘﺴﺘﺨﺩﻡ ﻜﺎﺌﻥ ﺃﻤﺭ ﻟﻠﺤﺼﻭل ﻋﻠـﻰ ﺍﻟﺴـﺠﻼﺕ ﻤـﻥ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﻬﻲ ﺘﻘﺭﺃ ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺒﺎﺸﺭﺓ.
ـﻴﻠﺔ
ﻭﻹﻨﺸـﺎﺀ ﻗـﺎﺭﺉ ﺒﻴﺎﻨـﺎﺕ ﻴﻘـﺭﺃ ﺴـﺠﻼﺕ ﺃﺤـﺩ ﺍﻟﺠـﺩﺍﻭل ،ﻋﻠﻴـﻙ ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـ
CreateDataReaderﺍﻟﺨﺎﺼﺔ ﺒﻬﺫﺍ ﺍﻟﺠﺩﻭل ﻜﺎﻟﺘﺎﻟﻲ:
;) (var Tr = Ds.Tables["Authors"].CreateDataReader
)) (while (Tr.Read
{
;)) (MessageBox.Show(Tr["ID"].ToString
;)) (MessageBox.Show(Tr["Author"].ToString
}
ـﻴﻠﺔ
ـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـ
ـﻙ ﺒﺎﺴـ
ـﺩﺍﻭل ،ﻋﻠﻴـ
ـل ﺍﻟﺠــﺠﻼﺕ ﻜـ ـﺭﺃ ﺴـ ـﺎﺕ ﻴﻘـ
ـﺎﺭﺉ ﺒﻴﺎﻨـ
ـﺎﺀ ﻗـ
ﻭﻹﻨﺸـ
CreateDataReaderﺍﻟﺨﺎﺼﺔ ﺒﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ DataSetﻜﺎﻟﺘﺎﻟﻲ:
;) (DataTableReader Tr = Ds.CreateDataReader
do
{
)) (while (Tr.Read
{
;"" = string RowTxt
)for (var I = 0; I < Tr.FieldCount; I++
RowTxt += Tr.GetName(I) + " = " +
;"Tr[I].ToString( ) + "\r\n
;)MessageBox.Show(RowTxt
}
;)) (} while (Tr.NextResult
ﻻﺤﻅ ﺃﻨﻨﺎ ﻟﻡ ﻨﺴﺘﺨﺩﻡ ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﻋﻨﺩ ﻗﺭﺍﺀﺓ ﺨﺎﻨﺎﺕ ﻜل ﺴـﺠل ،ﻭﺫﻟـﻙ ﻷﻥ ﺍﻟﺴـﺠﻼﺕ
ﺴﺘﺨﺘﻠﻑ ﻤﻥ ﺠﺩﻭل ﺇﻟﻰ ﺁﺨﺭ ﻓﻲ ﻋﺩﺩ ﺍﻷﻋﻤﺩﺓ ﻭﺃﺴﻤﺎﺌﻬﺎ ..ﻭﺒﺩﻻ ﻤﻥ ﻫﺫﺍ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﺨﺎﺼـﻴﺔ
FieldCountﻹﻨﺸﺎﺀ ﺤﻠﻘﺔ ﺘﻜﺭﺍﺭ ﺘﻤﺭ ﻋﺒﺭ ﻜل ﺍﻷﻋﻤﺩﺓ ،ﻟﻘﺭﺍﺀﺓ ﻜل ﺨﺎﻨﺔ ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺭﻗـﻡ
ﺍﻟﻌﻤﻭﺩ ﺒﺩﻻ ﻤﻥ ﺍﺴﻤﻪ.
ﻭﻴﻤﻜﻨﻙ ﺘﺠﺭﺒﺔ ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ .DataTableReaderSample
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﺍﻟﻔﺌﺔ DataTableReaderﺍﻟﺼﻴﻐﺘﺎﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
٣١٧
-١ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل DataTableﺍﻟﺫﻱ ﺴﺘﻘﺭﺃ ﺴﺠﻼﺘﻪ.
-٢ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺔ ﺠﺩﺍﻭل DataTable Arrayﻟﺘﻘﺭﺃ ﺴﺠﻼﺘﻬﺎ.
ﻭﻻ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﻴﺔ ﺨﺼﺎﺌﺹ ﺃﻭ ﻭﺴﺎﺌل ﺠﺩﻴﺩﺓ ﻏﻴﺭ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ.
٣١٨
ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻌﻼﻗﺎﺕ
DataRelationCollection Class
ﺍﻟﻌﻨﺼﺭ :Item
ﻫﺫﻩ ﻫﻲ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ،ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ ﻜﻤﻌﺎﻤل ﺃﻭ ﺭﻗﻡ ﺍﻟﻌﻼﻗـﺔ ﻓـﻲ
ﺍﻟﻤﺠﻤﻭﻋﺔ ،ﻭﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻜﺎﺌﻥ ﺍﻟﻌﻼﻗﺔ DataRelationﺍﻟﺫﻱ ﻴﻤﺜﻠﻬﺎ ..ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ
ﺍﻟﺨﺎﺼﻴﺔ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ،DataSetContentsﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻜﺎﺌﻥ ﺍﻟﻌﻼﻗﺔ ﺍﻟﺘﻲ ﻴﻀﻐﻁ
ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﺴﻤﻬﺎ ﻓﻲ ﻗﺎﺌﻤﺔ ﺍﻟﻌﻼﻗﺎﺕ ،ﻟﻨﻌﺭﺽ ﺨﺼﺎﺌﺼﻬﺎ ﻓﻲ ﻤﺭﺒﻊ ﺭﺴﺎﻟﺔ.
ﺇﻀﺎﻓﺔ :Add
ﺘﻀﻴﻑ ﻋﻼﻗﺔ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ ،ﻭﻟﻬﺎ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﻌﻼﻗﺔ DataRelationﺍﻟﻤﺭﺍﺩ ﺇﻀﺎﻓﺘﻪ.
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ ﻤﻥ ﺍﻟﻨـﻭﻉ ،DataColumnﻴﻤـﺜﻼﻥ ﺍﻟﺤﻘـل
ﺍﻟﺭﺌﻴﺴﻲ ﻭﺍﻟﺤﻘل ﺍﻟﻔﺭﻋﻲ ﻋﻠﻰ ﺍﻟﺘﺭﺘﻴﺏ ،ﺤﻴﺙ ﺴـﻴﺘﻡ ﺇﻨﺸـﺎﺀ ﻋﻼﻗـﺔ ﺒﻴﻨﻬﻤـﺎ،
ﻭﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ.
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ،ﺇﻻ ﺃﻨﻬﺎ ﺘﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺘﻴﻥ ﻤـﻥ ﺍﻟﻨـﻭﻉ
،DataColumnﻭﺫﻟﻙ ﻟﻤﺭﺍﻋﺎﺓ ﺍﻟﺤﺎﻟﺔ ﺍﻟﺘﻲ ﻴﺘﻜﻭﻥ ﻓﻴﻬـﺎ ﻜـل ﻤـﻥ ﺍﻟﻤﻔﺘـﺎﺡ
ﺍﻷﺴﺎﺴﻲ ﻭﺍﻟﻔﺭﻋﻲ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﻋﻤﻭﺩ.
٣١٩
-٤ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺴﺘﻘﺒل ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ :ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ ،ﻭﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺭﺌﻴﺴـﻲ،
ﻭﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻔﺭﻋﻲ.
-٥ﺍﻟﺼﻴﻐﺔ ﺍﻟﺨﺎﻤﺴﺔ ﺘﺯﻴﺩ ﺒﻤﻌﺎﻤل ﻤﻨﻁﻘﻲ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ،ﺇﺫﺍ ﺠﻌﻠـﺕ ﻗﻴﻤﺘـﻪ
falseﻓﻠﻥ ﻴﺘﻡ ﺇﻨﺸﺎﺀ ﻗﻴﻭﺩ ﻋﻨﺩ ﺇﻨﺸﺎﺀ ﺍﻟﻌﻼﻗﺔ ..ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻓﻲ ﺍﻟﺼـﻴﻎ
ﺍﻟﺘﻲ ﻻ ﺘﺤﺘﻭﻱ ﻫـﺫﺍ ﺍﻟﻤﻌﺎﻤـل ﻫـﻲ ،trueﻟﻬـﺫﺍ ﻴـﺘﻡ ﺇﻨﺸـﺎﺀ ﻗﻴـﺩ ﺍﻟﺘﻔـﺭﺩ
UniqueConstraintﻋﻠﻰ ﺍﻟﺤﻘل ﺍﻷﺴﺎﺴﻲ ﺇﻥ ﻟﻡ ﻴﻜﻥ ﻤﻭﺠﻭﺩﺍ ،ﻭﺇﻨﺸـﺎﺀ ﻗﻴـﺩ
ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ ForeignKeyConstraintﻋﻠﻰ ﺍﻟﺤﻘل ﺍﻟﻔﺭﻋﻲ ﺇﻥ ﻟـﻡ ﻴﻜـﻥ
ﻤﻭﺠﻭﺩﺍ ،ﻭﻴﺘﻡ ﺇﻀﺎﻓﺘﻬﻤﺎ ﺇﻟﻰ ﻗﻴﻭﺩ ﺍﻟﺠﺩﻭل.
-٦ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺩﺴﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴـﺎﺒﻘﺔ ،ﺇﻻ ﺃﻥ ﻤﻌﺎﻤﻠﻴﻬـﺎ ﺍﻟﺜـﺎﻨﻲ ﻭﺍﻟﺜﺎﻟـﺙ
ﻴﺴﺘﻘﺒﻼﻥ ﻤﺼﻔﻭﻓﺔ ﺤﻘﻭل DataColumn Arrayﻟﻤﺭﺍﻋﺎﺓ ﺍﻟﺤﺎﻟﺔ ﺍﻟﺘﻲ ﻴﺘﻜـﻭﻥ
ﻓﻴﻬﺎ ﻜل ﻤﻥ ﺍﻟﻤﻔﺘﺎﺤﻴﻥ ﺍﻷﺴﺎﺴﻲ ﻭﺍﻟﻔﺭﻋﻲ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﺤﻘل.
٣٢٠
ﻓﺌﺔ ﺍﻟﻌﻼﻗﺔ DataRelation Class
٣٢١
ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﺭﻴﻙ ﻜﻴﻑ ﺘﻨﺸﺊ ﻋﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺤﻘـل IDﻓـﻲ ﺠـﺩﻭل ﺍﻟﻤـﺅﹼﻟﹼﻔﻴﻥ ،ﻭﺍﻟﺤﻘـل
AuthorIDﻓﻲ ﺠﺩﻭل ﺍﻟﻜﺘﺏ:
;]"DataColumn ID = Ds.Tables["Authors"].Columns["ID
;]"var AuthorID = Ds.Tables["Books"].Columns["AuthorID
;)var R = new DataRelation("AuthorsBooks", ID, AuthorID
ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ ﻟﻡ ﺘﻭﻀﻊ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ DataSetﺇﻟـﻰ ﺍﻵﻥ ،ﻟﻬـﺫﺍ ﻋﻠﻴـﻙ
ﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻌﻼﻗﺎﺕ ﺒﻨﻔﺴﻙ ﻜﺎﻟﺘﺎﻟﻲ:
;)Ds.Relations.Add(R
ﻭﺒﻌﺩ ﺘﻨﻔﻴﺫ ﺍﻟﺠﻤﻠﺔ ﺍﻷﺨﻴﺭﺓ ،ﺴﺘﻀﺎﻑ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ ﺘﻠﻘﺎﺌﻴﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﻌﻼﻗـﺎﺕ ﺍﻟﺭﺌﻴﺴـﻴﺔ
ParentRelationsﻟﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﻭﻤﺠﻤﻭﻋـﺔ ﺍﻟﻌﻼﻗـﺎﺕ ﺍﻟﻔﺭﻋﻴـﺔ ChildRelations
ﻟﺠﺩﻭل ﺍﻟﻜﺘﺏ.
٣٢٢
ﺍﻷﻋﻤﺩﺓ ﺍﻟﺭﺌﻴﺴﻴﺔ :ParentColumns
ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﻤﻥ ﺍﻟﻨﻭﻉ DataColumnﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻷﻋﻤـﺩﺓ ﺍﻟﺭﺌﻴﺴـﻴﺔ ﻓـﻲ ﻫـﺫﻩ
ﺍﻟﻌﻼﻗﺔ.
ﻤﺘﺩﺍﺨﻠﺔ :Nested
ﺘﻔﻴﺩ ﻋﻨﺩ ﺤﻔﻅ ﺴﺠﻼﺕ ﺍﻟﺠـﺩﻭل ﺍﻟﺭﺌﻴﺴـﻲ ﻓـﻲ ﻤﻠـﻑ XMLﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ
،WriteXmlﻓﻠﻭ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ،trueﻓﺴﻴﺘﻡ ﺤﻔﻅ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻔﺭﻋﻴﺔ ﻤـﻊ
ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﺍﻟﺫﻱ ﺘﺭﺒﻁﻬﺎ ﺒﻪ ﺍﻟﻌﻼﻗﺔ ﺍﻟﺤﺎﻟﻴﺔ.
٣٢٣
ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻘﻴﻭﺩ ConstraintCollection Class
ﺍﻟﻌﻨﺼﺭ :Item
ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺭﻗﻡ ﺍﻟﻘﻴﺩ ﻓﻲ ﺍﻟﻤﺠﻤﻭﻋﺔ ،ﺃﻭ ﻨﺼﺎ ﻴﺤﻤل ﺍﺴﻡ ﺍﻟﻘﻴﺩ ،ﻟﺘﻌﻴﺩ ﺇﻟﻴـﻙ
ﻜﺎﺌﻥ ﺍﻟﻘﻴﺩ Constraintﺍﻟﺫﻱ ﻴﻤﺜﻠﻪ.
ﺇﻀﺎﻓﺔ :Add
ﺘﻀﻴﻑ ﻗﻴﺩﺍ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ ،ﻭﻟﻬﺎ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﻘﻴﺩ Constraintﺍﻟﻤﺭﺍﺩ ﺇﻀﺎﻓﺘﻪ.
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﻨﺸﺊ ﻗﻴﺩ ﺘﻔﺭﺩ UniqueConstraintﻭﺘﻀﻴﻔﻪ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋـﺔ..
ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ:
-ﺍﺴﻡ ﺍﻟﻘﻴﺩ.
-ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ DataColumnﺍﻟﺫﻱ ﻴﺠﺏ ﺃﻥ ﻴﻜﻭﻥ ﻤﺘﻔﺭﺩﺍ.
-ﻗﻴﻤﺔ ﻤﻨﻁﻘﻴﺔ ﺇﺫﺍ ﺠﻌﻠﺘﻬﺎ trueﻓﺴﻴﺘﻡ ﺠﻌل ﺍﻟﻌﻤﻭﺩ ﺍﻟﻤﺭﺴل ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤـل
ﺍﻟﺜﺎﻨﻲ ﻤﻔﺘﺎﺤﺎ ﺃﺴﺎﺴﻴﺎ ﻟﻠﺠﺩﻭل.
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ،ﺇﻻ ﺃﻥ ﻤﻌﺎﻤﻠﻬﺎ ﺍﻟﺜﺎﻨﻲ ﻴﺴـﺘﻘﺒل ﻤﺼـﻔﻭﻓﺔ
ﺃﻋﻤﺩﺓ ،DataColumn Arrayﻭﺫﻟﻙ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻤﻁﻠـﻭﺏ ﺘﻔـﺭﺩﻩ ﻓـﻲ
ﺍﻟﺠﺩﻭل ﻴﺘﻜﻭﻥ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﻋﻤﻭﺩ.
-٤ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﻨﺸﺊ ﻗﻴﺩ ﻤﻔﺘﺎﺡ ﻓﺭﻋﻲ ForeignKeyConstraintﻭﺘﻀـﻴﻔﻪ
ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ ..ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ:
-ﺍﺴﻡ ﺍﻟﻘﻴﺩ.
٣٢٤
-ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ DataColumnﺍﻷﺴﺎﺴﻲ.
-ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ DataColumnﺍﻟﻔﺭﻋﻲ.
-٥ﺍﻟﺼﻴﻐﺔ ﺍﻟﺨﺎﻤﺴﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴـﺎﺒﻘﺔ ،ﺇﻻ ﺃﻥ ﻤﻌﺎﻤﻠﻴﻬـﺎ ﺍﻟﺜـﺎﻨﻲ ﻭﺍﻟﺜﺎﻟـﺙ
ﻴﺴﺘﻘﺒﻼﻥ ﻤﺼﻔﻭﻓﺔ ﺃﻋﻤﺩﺓ ،DataColumn Arrayﻭﺫﻟـﻙ ﺇﺫﺍ ﻜـﺎﻥ ﺍﻟﻤﻔﺘـﺎﺡ
ﺍﻷﺴﺎﺴﻲ ﻭﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ ﻴﺘﻜﻭﻨﺎﻥ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﻋﻤﻭﺩ.
ﻻﺤﻅ ﺃﻥ ﺠﻤﻴﻊ ﺍﻟﺼﻴﻎ ﻤﺎ ﻋﺩﺍ ﺍﻷﻭﻟﻰ ،ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﻘﻴﺩ Constraintﺍﻟﺫﻱ ﺘـﻡ ﺇﻨﺸـﺎﺅﻩ
ﻭﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ.
ﻭﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﺍﻟﻘﻴﻭﺩ ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ ،ﻭﺫﻟﻙ ﻤـﻥ
ﺨﻼل ﻨﺎﻓﺫﺓ ﺨﺼﺎﺌﺹ ﺍﻟﺠﺩﻭل ..ﻟﻔﻌل ﻫﺫﺍ ﻴﺠﺏ ﺃﻥ ﻴﻜﻭﻥ ﻟﺩﻴﻙ ﻜﺎﺌﻥ ﺠﺩﻭل ﻓﻲ ﺼـﻴﻨﻴﺔ
ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤﻭﺫﺝ )ﻭﻫﺫﺍ ﻏﻴﺭ ﺸﺎﺌﻊ( ،ﺃﻭ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨـﺎﺕ ﻋﺎﺩﻴـﺔ Un-
Typed DataSetﻤﻭﻀﻭﻋﺔ ﻓﻲ ﺼﻴﻨﻴﺔ ﺍﻟﻤﻜﻭﻨﺎﺕ ،ﻓﻌﻨﺩ ﻋﺭﺽ ﺨﺼﺎﺌﺼﻬﺎ ﻓﻲ ﻨﺎﻓـﺫﺓ
ﺍﻟﺨﺼﺎﺌﺹ ،ﺴﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼﻴﺔ Tablesﻟﻌﺭﺽ ﻤﺤﺭﺭ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺠـﺩﺍﻭل،
ﻭﻟﻭ ﺤﺩﺩﺕ ﺃﻱ ﺠﺩﻭل ﻓﻲ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ،ﻓﺴﺘﻅﻬﺭ ﺨﺼﺎﺌﺼﻪ ﻓﻲ ﺍﻟﻘﺴﻡ ﺍﻷﻴﻤـﻥ ﻤـﻥ
ﺍﻟﻨﺎﻓﺫﺓ ،ﻭﺴﺘﺠﺩ ﺒﻴﻨﻬﺎ ﺍﻟﺨﺎﺼﻴﺔ ..Constraintsﻭﻟﻭ ﻀﻐﻁﺕ ﺯﺭ ﺍﻻﻨﺘﻘﺎل ﺍﻟﻤﻭﺠﻭﺩ ﻓـﻲ
ﺨﺎﻨﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ،ﻓﺴﺘﻅﻬﺭ ﻨﺎﻓﺫﺓ ﻤﺤﺭﺭ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻘﻴﻭﺩ ،ﻜﻤﺎ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
٣٢٥
ﺍﻀﻐﻁ ﺍﻟﺯﺭ Addﻹﻀﺎﻓﺔ ﻗﻴﺩ ﺠﺩﻴﺩ ..ﺴﺘﻅﻬﺭ ﻟﻙ ﻗﺎﺌﻤﺔ ﻤﻭﻀﻌﻴﺔ ﻟﺘﺘﻴﺢ ﻟـﻙ ﺍﺨﺘﻴـﺎﺭ
ﻨﻭﻉ ﺍﻟﻘﻴﺩ ،ﻤﻥ ﺒﻴﻥ ﺍﻟﻨﻭﻋﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ:
٣٢٦
ﻭﺒﻌﺩ ﺃﻥ ﺘﻀﻴﻑ ﺍﻟﻘﻴﺩ ﺴﻴﻅﻬﺭ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﺴﺭﻯ ،ﻭﺴﺘﻅﻬﺭ ﺨﺼﺎﺌﺼـﻪ ﻓـﻲ ﺍﻟﻘﺎﺌﻤـﺔ
ﺍﻟﻴﻤﻨﻰ.
٣٢٧
ﺍﻟﻤﺠﻤﻭﻋﺔ ﺘﻐﻴﺭﺕ :CollectionChanged
ﻴﻨﻁﻠﻕ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻋﻨﺩﻤﺎ ﻴﺘﻐﻴﺭ ﻋﺩﺩ ﻋﻨﺎﺼﺭ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﻘﻴـﻭﺩ ،ﺴـﻭﺍﺀ ﺒﺎﻟﺤـﺫﻑ ﺃﻭ
ﺍﻹﻀـــﺎﻓﺔ ..ﻭﺍﻟﻤﻌﺎﻤـــل ﺍﻟﺜـــﺎﻨﻲ eﻟﻬـــﺫﺍ ﺍﻟﺤـــﺩﺙ ﻤـــﻥ ﺍﻟﻨـــﻭﻉ
CollectionChangeEventArgsﺍﻟﺫﻱ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻪ ﻤﻥ ﻗﺒل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠـﺩﺍﻭل
.DataTableCollection
٣٢٨
ﻓﺌﺔ ﺍﻟﻘﻴﺩ Constraint Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺴﺎﺴﺔ ﻤﺠﺭﺩﺓ Abstract Base Classﻭﺘﺠﺏ ﻭﺭﺍﺜﺘﻬﺎ ،ﻭﻫﻲ ﺘﻌﻤل ﻜﻔﺌﺔ ﺃﻡ ﻟﻜـل
ﻤــﻥ ﻓﺌــﺔ ﻗﻴــﺩ ﺍﻟﺘﻔــﺭﺩ UniqueConstraint Classﻭﻗﻴــﺩ ﺍﻟﻤﻔﺘــﺎﺡ ﺍﻟﻔﺭﻋــﻲ
.ForeignKeyConstraint Class
ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
ﺍﻟﺠﺩﻭل :Table
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل DataTableﺍﻟﺫﻱ ﻴﻨﻁﺒﻕ ﻋﻠﻴﻪ ﺍﻟﻘﻴﺩ.
٣٢٩
ﻓﺌﺔ ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ UniqueConstraint Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ،Constraintﻭﻫﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺘﻔﺎﺼﻴل ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ ﺍﻟﺫﻱ ﻴﻀﻤﻥ ﻋـﺩﻡ
ﺘﻜﺭﺍﺭ ﻗﻴﻡ ﺤﻘل ﺃﻭ ﻤﺠﻤﻭﻋﺔ ﻤﻥ ﺍﻟﺤﻘﻭل.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ DataColumnﺍﻟﺫﻱ ﺴﻴﻔﺭﺽ ﻋﻠﻴﻪ ﺍﻟﻘﻴﺩ.
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﻤﻨﻁﻘﻲ ،ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻪ trueﻴﺘﻡ
ﺠﻌل ﺍﻟﻌﻤﻭﺩ ﺍﻟﻤﺭﺴل ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ﻤﻔﺘﺎﺤﺎ ﺃﺴﺎﺴﻴﺎ ﻟﻠﺠﺩﻭل.
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﻤﺼـﻔﻭﻓﺔ ﺃﻋﻤـﺩﺓ DataColumn Arrayﺘﺤﺘـﻭﻱ ﻋﻠـﻰ
ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺴﻴﻔﺭﺽ ﻋﻠﻴﻬﺎ ﺍﻟﻘﻴﺩ.
-٤ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﻤﻨﻁﻘﻲ ،ﺇﺫﺍ ﺠﻌﻠﺘـﻪ trueﻓﺴـﻴﺘﻡ
ﺠﻌل ﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﺭﺴﻠﺔ ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ﻤﻔﺘﺎﺤﺎ ﺃﺴﺎﺴﻴﺎ ﻟﻠﺠﺩﻭل.
-٥ﻫﻨﺎﻙ ﺼﻴﻎ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻎ ﺍﻟﺴﺎﺒﻘﺔ ،ﻟﻜﻨﻬﺎ ﺘﺯﻴﺩ ﺒﻤﻌﺎﻤل ﺃﻭل ﻴﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻘﻴﺩ.
-٦ﺍﻟﺼﻴﻐﺔ ﺍﻷﺨﻴﺭﺓ ﺘﺴﺘﻘﺒل ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ:
-ﺍﺴﻡ ﺍﻟﻘﻴﺩ.
-ﻤﺼﻔﻭﻓﺔ ﻨﺼﻴﺔ ﺘﺴﺘﻘﺒل ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ.
-ﻤﻌﺎﻤل ﻤﻨﻁﻘﻲ ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻪ trueﻴﺘﻡ ﺠﻌل ﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﺭﺴﻠﺔ ﺇﻟـﻰ ﺍﻟﻤﻌﺎﻤـل
ﺍﻟﺜﺎﻨﻲ ﻤﻔﺘﺎﺤﺎ ﺃﺴﺎﺴﻴﺎ ﻟﻠﺠﺩﻭل.
ﻭﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﻑ ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ ﻋﻠﻰ ﺍﻟﺤﻘل IDﻟﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ:
;)"var ID = Ds.Tables("Authors").Columns("ID
;)var Uc = new UniqueConstraint("IDUnique", ID, true
ﺇﻟﻰ ﺍﻵﻥ ﻟﻡ ﻴﻭﻀﻊ ﻫﺫﺍ ﺍﻟﻘﻴﺩ ﻓﻲ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﻟﺫﺍ ﻋﻠﻴﻙ ﺃﻥ ﺘﻀﻴﻔﻪ ﻜﺎﻟﺘﺎﻟﻲ:
;)Ds.Tables("Authors").Constraints.Add(Uc
ﻟﻜﻥ ﻫﺫﺍ ﺍﻟﻘﻴﺩ ﻟﻥ ﻴﻌﻤل ،ﺇﻻ ﺇﺫﺍ ﺠﻌﻠﺕ ﻟﻠﺨﺎﺼﻴﺔ "ﻓـﺭﺽ ﺍﻟﻘﻴـﻭﺩ" EnforceConstraints
ﺼﺔ ﺒﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻘﻴﻤﺔ :true
ﺍﻟﺨﺎ
;Ds.EnforceConstraints = true
٣٣٠
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ،ﺘﻤﺘﻠﻙ ﻓﺌﺔ ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
ﺍﻷﻋﻤﺩﺓ :Columns
ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﻴﺅﺜﹼﹼﺭ ﻋﻠﻴﻬﺎ ﻫﺫﺍ ﺍﻟﻘﻴﺩ.
٣٣١
ﻓﺌﺔ ﻗﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺜﺎﻨﻭﻱ ForeignKeyConstraint Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ،Constraintﻭﻫﻲ ﺘﺤﺘﻭﻱ ﻋﻠـﻰ ﺘﻔﺎﺼـﻴل ﻗﻴـﺩ ﺍﻟﻤﻔﺘـﺎﺡ ﺍﻟﺜـﺎﻨﻭﻱ،
ﻱ ،ﻭﺍﻟﺫﻱ ﻴﻀﻤﻥ ﺼﺤﺔ ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺠـﺩﻭﻟﻴﻥ،
ﺍﻟﻤﻔﺭﻭﺽ ﻋﻠﻰ ﺍﻟﺴﺠﻼﺕ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﺜﺎﻨﻭ
ﻭﻴﻤﻨﻊ ﺤﺫﻑ ﺃﺤﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻷﺴﺎﺴﻴﺔ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ ﺇﺫﺍ ﻜﺎﻨﺕ ﻟﻪ ﺴﺠﻼﺕ ﻓﺭﻋﻴﺔ ﻓـﻲ
ﺍﻟﺠﺩﻭل ﺍﻟﺜﺎﻨﻭﻱ ،ﻜﻤﺎ ﻴﻤﻨﻊ ﺘﻌﺩﻴل ﻗﻴﻤﺔ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻓﻲ ﺃﻱ ﺴﺠل ﺇﺫﺍ ﻜﺎﻨﺕ ﻫـﺫﻩ ﺍﻟﻘﻴﻤـﺔ
ﻤﺴﺘﺨﺩﻤﺔ ﻓﻲ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ ﻟﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻟﺜﺎﻨﻭﻱ.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ DataColumnﺍﻟﺫﻱ ﻴﻤﺜل ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺭﺌﻴﺴﻲ ﻓﻲ
ﺍﻟﻌﻼﻗﺔ ،ﻭﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ DataColumnﺍﻟﺫﻱ ﻴﻤﺜل ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋـﻲ ﻓـﻲ ﺍﻟﻌﻼﻗـﺔ،
ﻟﻔﺭﺽ ﺍﻟﻘﻴﺩ ﻋﻠﻴﻬﻤﺎ.
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ،ﺇﻻ ﺃﻨﻬﺎ ﺘﺘﻌﺎﻤل ﻤﻊ ﻤﺼـﻔﻭﻓﺘﻴﻥ ﻤـﻥ ﺍﻟﻨـﻭﻉ
،DataColumnﻟﻤﺭﺍﻋﺎﺓ ﺍﻟﺤﺎﻟﺔ ﺍﻟﺘﻲ ﻴﺘﻜﻭﻥ ﻓﻴﻬﺎ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻭﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ
ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﻋﻤﻭﺩ.
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﻜل ﻤﻥ ﺍﻟﺼﻴﻐﺘﻴﻥ ﺍﻟﺴﺎﺒﻘﺘﻴﻥ ﺒﻤﻌﺎﻤل ﺃﻭل ،ﻴﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻘﻴﺩ.
-٤ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺴﺘﻘﺒل ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ ﺒﺎﻟﺘﺭﺘﻴﺏ:
-ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﻘﻴﺩ.
-ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ.
-ﻨﺼﺎ ﻴﻤﺜل ﻨﻁﺎﻕ ﺍﺴﻡ ﺍﻟﺠﺩﻭل.
-ﻤﺼﻔﻭﻓﺔ ﻨﺼﻴﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺘﻌﻤل ﻜﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ.
-ﻤﺼﻔﻭﻓﺔ ﻨﺼﻴﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺘﻌﻤل ﻜﻤﻔﺘﺎﺡ ﻓﺭﻋﻲ.
-ﺇﺤــﺩﻯ ﻗــﻴﻡ ﺍﻟﻤــﺭﻗﻡ AcceptRejectRuleﻟﻭﻀــﻌﻬﺎ ﻓــﻲ ﺍﻟﺨﺎﺼــﻴﺔ
AcceptRejectRuleﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﺒﻌﺩ ﻗﻠﻴل.
-ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ Ruleﻟﻭﻀﻌﻬﺎ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ DeleteRuleﺍﻟﺘـﻲ ﺴـﻨﺘﻌﺭﻑ
ﻋﻠﻴﻬﺎ ﺒﻌﺩ ﻗﻠﻴل.
٣٣٢
-ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ Ruleﻟﻭﻀﻌﻬﺎ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ UpdateRuleﺍﻟﺘﻲ ﺴـﻨﺘﻌﺭﻑ
ﻋﻠﻴﻬﺎ ﺒﻌﺩ ﻗﻠﻴل.
-٥ﺍﻟﺼﻴﻐﺔ ﺍﻟﺨﺎﻤﺴﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ،ﻟﻜﻥ ﻴﻨﻘﺼﻬﺎ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻟﺙ ﺍﻟﺫﻱ ﻴﺴـﺘﻘﺒل
ﻨﻁﺎﻕ ﺍﺴﻡ ﺍﻟﺠﺩﻭل.
ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﻑ ﻗﻴﺩﺍ ﻤﻥ ﻫﺫﺍ ﺍﻟﻨﻭﻉ:
;)"var ID = Ds.Tables("Authors").Columns("ID
;)"var AuthorID = Ds.Tables(Books").Columns("AuthorID
var Fkc = new ForeignKeyConstraint("AuthorIDCnst",
;)ID, AuthorID
ﻭﻴﺠﺏ ﺃﻥ ﺘﻀﻴﻑ ﻫﺫﺍ ﺍﻟﻘﻴﺩ ﺇﻟﻰ ﺍﻟﺠﺩﻭل ﺍﻟﺜﺎﻨﻭﻱ ،ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﺨﻁﺄ ﺴﻴﺤﺩﺙ ﻟـﻭ ﺤﺎﻭﻟـﺕ
ﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ ..ﻭﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻴﻀﻴﻑ ﺍﻟﻘﻴﺩ ﺍﻟﺫﻱ ﺃﻨﺸﺄﻨﺎﻩ ﺇﻟﻰ ﻤﺠﻤﻭﻋـﺔ ﻗﻴـﻭﺩ
ﺠﺩﻭل ﺍﻟﻜﺘﺏ:
;)Ds.Tables("Books").Constraints.Add(Fkc
ﻻﺤﻅ ﺃﻥ ﺘﻨﻔﻴﺫ ﻫﺫﻩ ﺍﻟﺠﻤﻠﺔ ﺴﻴﺅﺩﻱ ﺇﻟﻰ ﺇﻀﺎﻓﺔ ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ ﺘﻠﻘﺎﺌﻴﺎ ﻋﻠﻰ ﺍﻟﻌﻤﻭﺩ IDﻓﻲ ﺠـﺩﻭل
ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﻭﺫﻟﻙ ﺇﺫﺍ ﻟﻡ ﻴﻜﻥ ﻫﺫﺍ ﺍﻟﻘﻴﺩ ﻤﻭﺠﻭﺩﺍ ﻤﺴﺒﻘﺎ.
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ،ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
٣٣٣
ﺍﻷﻋﻤﺩﺓ :Columns
ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺃﻋﻤﺩﺓ ،DataColumn Arrayﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺘﻌﻤل ﻜﻤﻔﺘﺎﺡ
ﺜﺎﻨﻭﻱ ﻓﻲ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ.
ﺘﺘﺭﻙ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻔﺭﻋﻴﺔ ﻜﻤﺎ ﻫﻲ ﺒﺩﻭﻥ ﺘﻐﻴﻴﺭ ،ﻟﻜﻥ ﻫﺫﺍ ﻗﺩ ﻴﺅﺩﻱ ﺇﻟـﻰ None
ﺤﺩﻭﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻷﻥ ﺤﺫﻑ ﺃﻭ ﺘﻌـﺩﻴل ﺍﻟﺴـﺠل ﺍﻟﺭﺌﻴﺴـﻲ
ﺴﻴﻜﺴﺭ ﻗﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺜﺎﻨﻭﻱ ،ﻭﻴﺘﺭﻙ ﺒﻌﺽ ﺍﻟﺴـﺠﻼﺕ ﻓـﻲ ﺍﻟﺠـﺩﻭل
ﺍﻟﺜﺎﻨﻭﻱ ﺘﺸﻴﺭ ﺇﻟﻰ ﺴﺠل ﻏﻴﺭ ﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ.
ﻴﺅﺩﻱ ﺤﺫﻑ ﺃﻭ ﺘﻌﺩﻴل ﺍﻟﺴﺠلّ ﺍﻟﺭﺌﻴﺴﻲ ﺇﻟﻰ ﺤـﺫﻑ ﺃﻭ ﺘﻌـﺩﻴل ﻜـلّ Cascade
ﺍﻟﺴﺠﻼﺕ ﺍﻟﺜﺎﻨﻭﻴﺔ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﻪ ..ﻓﻤﺜﻼ ،ﺴﻴﺅﺩﻱ ﺤﺫﻑ ﺃﺤﺩ ﺍﻟﻤـﺅﻟﻔﻴﻥ
ﻤﻥ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﺇﻟﻰ ﺤﺫﻑ ﻜلّ ﻜﺘﺒﻪ ﻤﻥ ﺠﺩﻭل ﺍﻟﻜﺘﺏ.
٣٣٤
SetDefaultﻴﺅﺩﻱ ﺤﺫﻑ ﺃﻭ ﺘﻌﺩﻴل ﺍﻟﺴﺠلّ ﺍﻟﺭﺌﻴﺴﻲ ﺇﻟﻰ ﻭﻀﻊ ﺍﻟﻘـﻴﻡ ﺍﻻﻓﺘﺭﺍﻀـﻴﺔ
ﻓﻲ ﺤﻘﻭل ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺜﺎﻨﻭﻱ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﻪ.
ﻴﺅﺩﻱ ﺤﺫﻑ ﺃﻭ ﺘﻌﺩﻴل ﺍﻟﺴﺠلّ ﺍﻟﺭﺌﻴﺴﻲ ﺇﻟﻰ ﺇﻓـﺭﺍﻍ ﺤﻘـﻭل ﺍﻟﻤﻔﺘـﺎﺡ SetNull
ﺍﻟﺜﺎﻨﻭﻱ ﺍﻟﻤﺭﺘﺒﻁﺔ ﻟﺘﺼﻴﺭ ﺒﻬﺎ ﺍﻟﻘﻴﻤﺔ .DbNull
٣٣٥
-١٣-
ﻋـﺭﻭﺽ ﺍﻟﺒﻴـﺎﻨـﺎﺕ
Data Views
ﺘﺘﻴﺢ ﻟﻙ ﺍﻟﻌﺭﻭﺽ Viewﻋﺭﺽ ﺒﻌﺽ ﺃﻭ ﻜل ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺒﺎﻟﺘﺭﺘﻴﺏ ﺍﻟﺫﻱ ﺘﺭﻴـﺩﻩ ،ﺩﻭﻥ
ﺍﻟﺘﺄﺜﻴﺭ ﻋﻠﻰ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ..ﻫﺫﺍ ﻴﻤﻨﺤﻙ ﻤﺭﻭﻨﺔ ﻋﺎﻟﻴـﺔ ﻋﻨـﺩ ﻋـﺭﺽ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻟﻠﻤﺴﺘﺨﺩﻡ ،ﺩﻭﻥ ﺍﻟﺤﺎﺠﺔ ﺇﻟﻰ ﺇﻋﺎﺩﺓ ﺇﺭﺴﺎل ﺍﺴﺘﻌﻼﻤﺎﺕ ﻤﺨﺘﻠﻔﺔ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﻓﻜﺭﺓ ﺍﻟﻌﺭﺽ ﺒﺴﻴﻁﺔ ،ﻓﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻓﻬﺭﺱ Indexﻴﺸـﻴﺭ ﺇﻟـﻰ ﺴـﺠﻼﺕ
ﺍﻟﺠﺩﻭل ،ﻭﻻ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺴﺠﻼﺕ ﻨﻔﺴﻬﺎ ..ﻫﺫﺍ ﻴﺠﻌل ﻜـﺎﺌﻥ ﺍﻟﻌـﺭﺽ ﺴـﺭﻴﻌﺎ ﻓـﻲ ﺃﺩﺍﺀ
ﻋﻤﻠﻴﺎﺕ ﺍﻟﺘﺭﺸﻴﺢ Filteringﻭﺍﻟﺘﺭﺘﻴﺏ Sortingﻭﺍﻟﺒﺤـﺙ ،Searchingﺩﻭﻥ ﺃﻥ ﻴﺴـﺘﻬﻠﻙ
ﻤﺴﺎﺤﺔ ﻜﺒﻴﺭﺓ ﻓﻲ ﺍﻟﺫﺍﻜﺭﺓ!
ﻻﺤﻅ ﺃﻥ ﻓﻬﺭﺱ ﺍﻟﺴﺠﻼﺕ ﻴﺘﻡ ﺇﻨﺸﺎﺅﻩ ﻋﻨﺩ ﺇﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ،ﻭﻴﻌﺎﺩ ﺇﻨﺸﺎﺅﻩ ﻤﺭﺓ ﺃﺨﺭﻯ ﺇﺫﺍ
ﺘﻡ ﺘﻐﻴﻴﺭ ﻁﺭﻴﻘﺔ ﺍﻟﺘﺭﺘﻴﺏ ﺃﻭ ﺍﻟﺘﺭﺸﻴﺢ ..ﻟﺫﺍ ﻤـﻥ ﺍﻷﻓﻀـل ﺃﻥ ﺘﺤـﺩﺩ ﻤﻭﺍﺼـﻔﺎﺕ ﺍﻟﺘﺭﺘﻴـﺏ
ﻭﺍﻟﺘﺭﺸﻴﺢ ﻋﻨﺩ ﺇﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ﻟﺘﻭﻓﺭ ﻋﻠﻰ ﻨﻔﺴﻙ ﺍﻟﻭﻗﺕ ﺍﻟﻀـﺎﺌﻊ ﻓـﻲ ﺇﻋـﺎﺩﺓ ﺇﻨﺸـﺎﺀ
ﺍﻟﻔﻬﺭﺱ.
ﻭﻓﻲ ﻫﺫﺍ ﺍﻟﻔﺼل ﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﻲ ﺘﺘﻴﺢ ﻟﻨﺎ ﺇﻨﺸﺎﺀ ﺍﻟﻌﺭﻭﺽ ﻭﺍﻟﺘﻌﺎﻤل ﻤﻌﻬﺎ.
٣٣٦
ﻭﺍﺠﻬﺔ ﻗﺎﺌﻤﺔ ﺍﻟﺭﺒﻁ
IBindingList Interface
ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺘﺭﺙ ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ،IListﻭﻫﻲ ﺘﻘﺩﻡ ﺍﻟﻭﺴﺎﺌل ﺍﻟﻼﺯﻤﺔ ﻟﻠﺘﻌﺎﻤـل ﻤـﻊ ﻤﺼـﺩﺭ
ﺍﻟﺒﻴﺎﻨﺎﺕ Data Sourceﻤﻥ ﺨﻼل ﺃﺩﻭﺍﺕ ﺭﺒﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ .Data-Bound Controls
٣٣٧
ﺘﺩﻋﻡ ﺍﻟﺒﺤﺙ :SupportsSearching
ﺘﻌﻴﺩ trueﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﻘﺎﺌﻤﺔ ﺘﺘﻴﺢ ﺍﻟﺒﺤﺙ ﻓﻲ ﻋﻨﺎﺼﺭﻫﺎ ،ﻭﺘﺘﻴﺢ ﺘﺭﺘﻴﺒﻬﺎ.
٣٣٨
ﺇﻀﺎﻓﺔ ﻓﻬﺭﺱ :AddIndex
ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻜﺎﺌﻨﺎ ﻤـﻥ ﺍﻟﻨـﻭﻉ ،PropertyDescriptorﻟﺘﻘـﻭﻡ ﺒﺈﻀـﺎﻓﺔ
ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﻲ ﻴﺸﻴﺭ ﺇﻟﻴﻬﺎ ،ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻔﻬﺎﺭﺱ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻓﻲ ﺍﻟﺒﺤﺙ ﻓـﻲ ﻋﻨﺎﺼـﺭ
ﺍﻟﻘﺎﺌﻤﺔ.
٣٣٩
ﺒﺤﺙ :Find
ﺘﻘﻭﻡ ﺒﺎﻟﺒﺤﺙ ﻓﻲ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ ،ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ:
-ﻜﺎﺌﻥ ﻤﻥ ﺍﻟﻨﻭﻉ ،PropertyDescriptorﻴﻭﻀﺢ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﻲ ﺴـﻴﺘﻡ ﺍﻟﺒﺤـﺙ
ﻋﻥ ﻗﻴﻤﺘﻬﺎ.
-ﻜﺎﺌﻥ Objectﻴﺤﻤل ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﺭﺍﺩ ﺍﻟﺒﺤﺙ ﻋﻨﻬﺎ.
٣٤١
ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ
ITypedList Interface
ﺘﺤﺼل ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﻋﻠﻰ ﺨﺼﺎﺌﺹ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺍﻻﺭﺘﺒﺎﻁ Bindingﺒﻪ ،ﻭﻫﻲ ﺘﻤﺘﻠﻙ
ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
٣٤٢
ﻓﺌـﺔ ﻤﺩﻴـﺭ ﺍﻟﻌـﺭﺽ
DataViewManager Class
٣٤٤
ﻓﺌﺔ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻌﺭﺽ DataViewSetting Class
ﺘﺤﺘﻭﻱ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻋﻠﻰ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻌﺭﺽ ﺍﻻﻓﺘﺭﺍﻀ ﻴﺔ ﺍﻟﺘﻲ ﻴـﺘﻡ ﺍﺴـﺘﺨﺩﺍﻤﻬﺎ ﻤـﻊ ﺍﻟﺠـﺩﺍﻭل
ﺍﻟﻤﻌﺭﻭﻀﺔ.
ﻭﻟﻴﺱ ﻟﻬﺫﻩ ﺍﻟﻔﺌﺔ ﺤﺩﺙ ﺇﻨﺸﺎﺀ ،ﻭﻟﻜﻥ ﻴﻤﻜﻨﻙ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻨﺴﺨﺔ ﻤﻨﻬﺎ ﺨﺎﺼﺔ ﺒﺄﺤﺩ ﺍﻟﺠـﺩﺍﻭل
ﻤﻥ ﺨﻼل ﻤﺠﻤﻭﻋﺔ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻌﺭﺽ DataViewSettingsﻜﺎﻟﺘﺎﻟﻲ:
= DataViewSetting Vs
;]"Ds.DefaultViewManager.DataViewSettings["Authors
ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ:
:Table
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل DataTableﺍﻟﺫﻱ ﻴﻨﺘﻤﻲ ﺇﻟﻴﻪ ﻜﺎﺌﻥ ﺍﻹﻋﺩﺍﺩﺍﺕ ﺍﻟﺤﺎﻟﻲ.
٣٤٥
ﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ﻟﻠﺼﻔﻭﻑ .Current Version CurrentRows
ModifiedOriginalﺍﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ ﻟﻠﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﻡ ﺘﻌﺩﻴﻠﻬﺎ.
ModifiedCurrentﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ﻟﻠﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﻡ ﺘﻌﺩﻴﻠﻬﺎ.
ﺍﻟﺘﺭﺘﻴﺏ :Sort
ﺘﺤﺩﺩ ﺍﻟﻨﺹ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﺘﺭﺘﻴﺏ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻌﺭﻭﻀﺔ ،ﻭﻫﻭ ﻴﺘﻜﻭﻥ ﻤﻥ ﺍﺴـﻡ ﺍﻟﺤﻘـل
ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﺍﻟﺘﺭﺘﻴﺏ ،ﻤﺘﺒﻭﻋﺎ ﺒﺎﺘﺠﺎﻩ ﺍﻟﺘﺭﺘﻴﺏ ..ﻤﺜﻼ :ﻟﺘﺭﺘﻴﺏ ﺼﻔﻭﻑ ﺠـﺩﻭل ﺍﻟﻜﺘـﺏ
ﺘﻨﺎﺯﻟﻴﺎ ﻋﻠﻰ ﺤﺴﻴﺏ ﺍﺴﻡ ﺍﻟﻜﺘﺎﺏ ،ﺍﺴﺘﺨﺩﻡ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﺎﻟﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ:
;"Vs.Sort = "Book DESC
ﻭﻴﻌﺘﺒﺭ ﺍﻟﻤﺸﺭﻭﻉ Viewsﺘﺩﺭﻴﺒﺎ ﺠﻴﺩﺍ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻤﺩﻴﺭ ﺍﻟﻌﺭﻭﺽ ﻭﺇﻋـﺩﺍﺩﺍﺕ ﺍﻟﻌـﺭﺽ..
ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﺸﺭﻭﻉ ﻨﺘﻴﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻋﺭﺽ ﺠﺩﺍﻭل ﻗﺎﻋﺩﺓ ﺍﻟﻜﺘﺏ ،ﺒﺎﺨﺘﻴﺎﺭ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﻴﺭﻴـﺩﻩ
ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ "ﻋﺭﺽ ﺍﻟﺠﺩﻭل" ،ﻜﻤﺎ ﻨﺘﻴﺢ ﻟﻪ ﺍﺨﺘﻴﺎﺭ ﻤﺭﺸﺢ ﺍﻟﺼﻔﻭﻑ ﻭﻁﺭﻴﻘﺔ ﺍﻟﺘﺭﺘﻴﺏ
ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
٣٤٦
ﺩﻋﻨﺎ ﻨﻔﻬﻡ ﻜﻴﻑ ﻴﻌﻤل ﻫﺫﺍ ﺍﻟﻤﺸﺭﻭﻉ:
-ﻓﻲ ﺤﺩﺙ ﺘﺤﻤﻴل ﺍﻟﻨﻤﻭﺫﺝ ،Loadﻨﺴﺘﺨﺩﻡ ﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ﻟﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ،
ﺜﻡ ﻨﻀﻴﻑ ﺍﻟﺠﺩﺍﻭل ﺇﻟﻰ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﺭﻜﺒﺔ .LstTables
-ﻓﻲ ﺤﺩﺙ ﺘﻐﻴﺭ ﺍﻟﻌﻨﺼﺭ ﺍﻟﻤﺤﺩﺩ ﻓﻲ ﻗﺎﺌﻤـﺔ ﺍﻟﺠـﺩﺍﻭل ،SelectedIndexChanged
ﻨﻌﺭﺽ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﺍﺨﺘﺎﺭﻩ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ،ﻭﻨﻔـﺭﻍ ﻗﺎﺌﻤـﺔ
ﺍﻟﺤﻘﻭل LstFieldsﻭﻗﺎﺌﻤﺔ ﺍﻟﺘﺭﺘﻴﺏ LstSortﻤﻥ ﻤﺤﺘﻭﻴﺎﺘﻬﻤﺎ ،ﻭﻨﻀﻴﻑ ﺇﻟـﻰ ﻜـل
ﻤﻨﻬﻤﺎ ﺃﺴﻤﺎﺀ ﺃﻋﻤﺩﺓ ﺍﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ ..ﻭﻨﻅﺭﺍ ﻷﻥ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻗﺩ ﻴﺭﻴـﺩ ﻋـﺭﺽ ﻜـل
ﺍﻟﺴﺠﻼﺕ ﺒﺩﻭﻥ ﺘﺭﺘﻴﺏ ،ﻓﺴﻨﻀﻴﻑ ﺇﻟﻰ ﻜل ﻤﺠﻤﻭﻋﺔ ﻋﻨﺼﺭﺍ ﺇﻀﺎﻓﻴﺎ ﺍﺴﻤﻪ )(None
ﻭﺴﻨﺠﻌﻠﻪ ﺃﻭل ﻋﻨﺼﺭ ﻓﻲ ﻜل ﻤﻨﻬﻤﺎ.
ﻭﻟﻠﺘﺤﻜﻡ ﻓﻲ ﻁﺭﻴﻘﺔ ﻋﺭﺽ ﺍﻟﺠﺩﻭل ،ﺴﻨﺴﺘﺨﺩﻡ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻌـﺭﺽ ﺍﻟﺨﺎﺼـﺔ ﺒـﻪ..
ﻭﻟﺘﺴﻬﻴل ﺍﻟﻜﻭﺩ ﻓﻲ ﺒﺎﻗﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﺴﻨﻀﻊ ﻜﺎﺌﻥ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﻤﺘﻐﻴـﺭ
ﻤﻌﺭﻑ ﻋﻠﻰ ﻤﺴﺘﻭﻯ ﺍﻟﻨﻤﻭﺫﺝ ﺍﺴﻤﻪ :ViewSettings
٣٤٧
;DataTable T = LstTables.SelectedItem
;]ViewSettings = Ds.DefaultViewManager.DataViewSettings[T
-ﻓﻲ ﺤﺩﺙ ﺘﻐﻴﺭ ﺍﻟﻌﻨﺼﺭ ﺍﻟﻤﺤﺩﺩ SelectedIndexﻓﻲ ﻗﺎﺌﻤﺔ ﺍﻟﺤﻘـﻭل ،LstFields
ﺴﻨﻌﻁل ﻗﺎﺌﻤﺔ ﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﻘﺎﺭﻨﺔ LstOpﻭﻤﺭﺒﻊ ﻨﺹ ﺍﻟﻘﻴﻤﺔ ،TxtValueﻭﺫﻟﻙ ﺇﺫﺍ
ﺍﺨﺘﺎﺭ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻟﻌﻨﺼﺭ ) (Noneﻭﻫﻭ ﺍﻟﻌﻨﺼﺭ ﺭﻗﻡ ﺼﻔﺭ ﻓـﻲ ﺍﻟﻘﺎﺌﻤـﺔ ،ﺃﻤـﺎ ﺇﺫﺍ
ﺍﺨﺘﺎﺭ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺃﺤﺩ ﺃﻋﻤﺩﺓ ﺍﻟﺠﺩﻭل ،ﻓﺴﻨﻔﻌل ﻗﺎﺌﻤﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻭﺠﺩﻭل ﺍﻟﻘﻴﻤﺔ ﺤﺘـﻰ
ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻬﻤﺎ ﻓﻲ ﺘﻜﻭﻴﻥ ﻤﺭﺸﺢ ﺍﻟﺴﺠﻼﺕ ..ﻜل ﻫﺫﺍ ﻴﻤﻜـﻥ ﻓﻌﻠـﻪ ﺒﺎﻟﺴـﻁﺭﻴﻥ
ﺍﻟﺘﺎﻟﻴﻴﻥ ﻓﺤﺴﺏ:
;)LstOp.Enabled = (LstField.SelectedIndex != 0
;TxtValue.Enabled = LstOp.Enabled
-ﻓﻲ ﺤﺩﺙ ﺘﻐﻴﺭ ﺍﻟﻌﻨﺼﺭ ﺍﻟﻤﺤـﺩﺩ SelectedIndexﻓـﻲ ﻗﺎﺌﻤـﺔ ﺤﻘـل ﺍﻟﺘﺭﺘﻴـﺏ
:LstSortﺇﺫﺍ ﺍﺨﺘﺎﺭ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻟﻌﻨﺼﺭ ) (Noneﻭﻫﻭ ﺍﻟﻌﻨﺼﺭ ﺭﻗـﻡ ﺼـﻔﺭ ﻓـﻲ
ﺍﻟﻘﺎﺌﻤﺔ ،ﻓﺴـﻨﻌﻁل ﻗﺎﺌﻤـﺔ ﺍﺘﺠـﺎﻩ ﺍﻟﺘﺭﺘﻴـﺏ LstSortOrderﻭﻨﺠﻌـل ﻟﻠﺨﺎﺼـﻴﺔ
ApplyDefaultSortﺍﻟﺨﺎﺼﺔ ﺒﺈﻋﺩﺍﺩﺍﺕ ﺍﻟﻌﺭﺽ ﺍﻟﻘﻴﻤﺔ trueﻟﻌـﺭﺽ ﺍﻟﺴـﺠﻼﺕ
ﺒﺘﺭﺘﻴﺒﻬﺎ ﺍﻷﺼﻠﻲ ..ﺃﻤﺎ ﺇﺫﺍ ﺍﺨﺘﺎﺭ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺃﺤﺩ ﺃﻋﻤﺩﺓ ﺍﻟﺠﺩﻭل ،ﻓﺴﻨﻔﻌل ﻗﺎﺌﻤﺔ ﺍﺘﺠـﺎﻩ
ﺍﻟﺘﺭﺘﻴﺏ ﻭﻨﻀﻊ falseﻓﻲ ﺍﻟﺨﺎﺼـﻴﺔ ApplyDefaultSortﻟﻨﻌـﺭﺽ ﺍﻟﺴـﺠﻼﺕ
ﺒﺎﻟﺘﺭﺘﻴﺏ ﺍﻟﺫﻱ ﻴﺭﻴﺩﻩ ﺍﻟﻤﺴﺘﺨﺩﻡ ..ﻜل ﻫﺫﺍ ﻴﻤﻜﻥ ﻓﻌﻠﻪ ﺒﺎﻟﺴﻁﺭﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ ﻓﺤﺴﺏ:
;)LstSortOrder.Enabled = (LstSort.SelectedIndex != 0
;ViewSettings.ApplyDefaultSort = ! LstSortOrder.Enabled
-ﺃﺨﻴﺭﺍ ،ﻓﻲ ﺤﺩﺙ ﻀﻐﻁ ﺍﻟﺯﺭ "ﺘﻨﻔﻴﺫ" ،ﺴﻨﻘﻭﻡ ﺒﺘﻨﻔﻴﺫ ﺨﻴﺎﺭﺍﺕ ﺍﻟﻌﺭﺽ ﺍﻟﺘـﻲ ﺍﺨﺘﺎﺭﻫـﺎ
ﺍﻟﻤﺴﺘﺨﺩﻡ ،ﺤﻴﺙ ﺴﻨﻜﻭﻥ ﻤﺭﺸﺢ ﺍﻟﺴﺠﻼﺕ ﻜﺎﻟﺘﺎﻟﻲ:
)if (LstField.SelectedIndex == 0
;"" = ViewSettings.RowFilter
else
ViewSettings.RowFilter = LstField.Text + " " +
;) (LstOp.Text + " " + TxtValue.Text.Trim
ﻻﺤﻅ ﺃﻥ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻫﻭ ﺍﻟﻤﺴﺌﻭل ﻋﻥ ﻜﺘﺎﺒﺔ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻨﺎﺴﺒﺔ ﺒﺎﻟﺸﻜل ﺍﻟﺼﺤﻴﺢ ﻓﻲ ﻤﺭﺒﻊ
ﺍﻟﻨﺹ ..ﻓﻌﻠﻴﻪ ﻤﺜﻼ ﺃﻥ ﻴﻜﺘﺏ ﺭﻗﻤﺎ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺤﻘـل ﺍﻟـﺫﻱ ﺍﺨﺘـﺎﺭﻩ
٣٤٨
ﺭﻗﻤﻴﺎ ،ﻭﺃﻥ ﻴﻜﺘﺏ ﻨﺼﺎ ﻭﻴﻀﻌﻪ ﺒﻴﻥ ﻋﻼﻤﺘﻲ ﺍﻟﺘﻨﺼﻴﺹ ' ' ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺤﻘل ﻴﺘﻌﺎﻤل ﻤﻊ
ﻨﺼﻭﺹ ﺃﻭ ﺘﻭﺍﺭﻴﺦ ..ﻭﺇﺫﺍ ﺍﺨﺘﺎﺭ ﺍﻟﻤﻌﺎﻤل INﻓﻌﻠﻴﻪ ﺃﻥ ﻴﻔﺼل ﺒﻴﻥ ﺍﻟﻘﻴﻡ ﺒﺎﻟﻌﻼﻤـﺔ ,
ﻭﻴﻀﻊ ﻜل ﻤﺎ ﻜﺘﺒﻪ ﺒﻴﻥ ﻗﻭﺴﻴﻥ ) ( ..ﻭﻫﻜﺫﺍ.
ﻻﺤﻅ ﺃﻴﻀﺎ ﺃﻥ ﻭﻀﻊ ﺸﺭﻁ ﺨﺎﻁﺊ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ RowFilterﻻ ﻴﺴﺒﺏ ﺨﻁـﺄ ﻓـﻲ
ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻟﻜﻨﻪ ﻴﺠﻌل ﻤﺩﻴﺭ ﺍﻹﻋﺩﺍﺩﺍﺕ ﻴﺘﺠﺎﻫل ﻫﺫﻩ ﺍﻹﻋﺩﺍﺩﺍﺕ ﻭﻻ ﻴﻨﻔﺫﻫﺎ.
ﺒﻌﺩ ﻫﺫﺍ ﺴﻨﻜﻭﻥ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ Sortﺒﺎﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ:
)if (LstSort.SelectedIndex == 0
;"" = ViewSettings.Sort
else
ViewSettings.Sort = LstSort.Text + " " +
;)"((LstSortOrder.SelectedIndex == 0) ? "ASC" : "DESC
ﺃﺨﻴﺭﺍ ،ﺴﻨﻨﺸﺊ ﻜﺎﺌﻥ ﻋﺭﺽ ﻭﻨﻌﺭﻀﻪ ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ..ﻟﻔﻌل ﻫﺫﺍ ﺴﻨﺤﺼل ﻋﻠﻰ
ﻤﺩﻴﺭ ﺍﻟﻌﺭﻭﺽ ﻤﻥ ﻜﺎﺌﻥ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻌﺭﺽ ،ﻭﻨﺴﺘﺨﺩﻡ ﺍﻟﻭﺴﻴﻠﺔ CreateDataView
ﺍﻟﺨﺎﺼﺔ ﺒﻪ ﻜﺎﻟﺘﺎﻟﻲ:
var Dv = ViewSettings.DataViewManager.
;)CreateDataView(ViewSettings.Table
;Dgv.DataSource = Dv
ﻭﻫﻜﺫﺍ ،ﻭﺒﻬﺫﺍ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺍﻟﺒﺴﻴﻁ ،ﺍﻟﺫﻱ ﻟﻡ ﻨﻜﺘﺏ ﺒﻪ ﺍﻟﻜﺜﻴـﺭ ﻤـﻥ ﺍﻟﻜـﻭﺩ ،ﺼـﺎﺭ ﺒﺎﺴـﺘﻁﺎﻋﺔ
ﺍﻟﻤﺴﺘﺨﺩﻡ ﻋﺭﺽ ﺠﻤﻴﻊ ﺒﻴﺎﻨﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﻜﺘﺏ ،ﻭﺒﺄﻱ ﻁﺭﻴﻘﺔ ﻋﺭﺽ ﻴﺤﺒﻬﺎ!
٣٤٩
ﻭﺍﺠﻬﺔ ﺭﺒﻁ ﻗﺎﺌﻤﺔ ﺍﻟﻌﺭﺽ
IBindingListView Interface
ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺘﺭﺙ ﺍﻟﻭﺍﺠﻬﺔ ،IBindingListﻭﻫﻲ ﺘﻘﺩﻡ ﺇﻤﻜﺎﻨﻴـﺎﺕ ﻤﺘﻘﺩﻤـﺔ ﻓـﻲ ﺘﺭﺸـﻴﺢ
ﺍﻟﺴﺠﻼﺕ Filteringﻭﺘﺭﺘﻴﺒﻬﺎ .Sorting
ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
ﺍﻟﻤﺭﺸﺢ :Filter
ﺘﺤﺩﺩ ﺍﻟﺸﺭﻁ ﺍﻟﺫﻱ ﻴﺘﻡ ﻋﻠﻰ ﺃﺴﺎﺴﻪ ﺍﺨﺘﻴﺎﺭ ﺍﻟﺴﺠﻼﺕ ﻟﻌﺭﻀﻬﺎ ..ﻤﺜل:
""Price > 5
٣٥٠
ﻓﺌﺔ ﻭﺍﺼﻑ ﺘﺭﺘﻴﺏ ﺍﻟﻘﺎﺌﻤﺔ
ListSortDescription Class
ﺘﺤﺘﻭﻱ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻋﻠﻰ ﺍﻟﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﻼﺯﻤﺔ ﻟﺘﺭﺘﻴﺏ ﻋﻨﺎﺼﺭ ﻗﺎﺌﻤﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﻴﺴﺘﻘﺒل ﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻤﻌﺎﻤﻠﻴﻥ:
-ﻭﺍﺼﻑ ﺍﻟﺨﺎﺼﻴﺔ PropertyDescriptorﺍﻟﺘﻲ ﺴﻴﺘﻡ ﺍﻟﺘﺭﺘﻴﺏ ﻋﻠﻰ ﺃﺴﺎﺴﻬﺎ.
-ﺇﺤﺩﻯ ﻗﻴﻤﺘﻲ ﺍﻟﻤﺭﻗﻡ ListSortDirectionﺍﻟﺘﻲ ﺘﻭﻀﺢ ﺍﺘﺠﺎﻩ ﺍﻟﺘﺭﺘﻴﺏ.
٣٥١
ﻓﺌﺔ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ DataView Class
٣٥٢
var Dv = new DataView(Ds.Books,
'",ﺘﻭﻓﻴﻕ ﺍﻟﺤﻜﻴﻡ' = "Parent(FK_Books_Authors).Author
;)"Book", DataViewRowState.CurrentRows
ﻭﻴﻤﻜﻨﻙ ﺘﺠﺭﺒﺔ ﻫﺫﺍ ﺍﻟﻤﺜﺎل ﺒﻀﻐﻁ ﺍﻟﺯﺭ "ﻜﺘﺏ ﺘﻭﻓﻴﻕ ﺍﻟﺤﻜﻴﻡ" ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ .Views
ﻭﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﺨﺼﺎﺌﺹ ﺍﻟﻭﺍﺠﻬﺎﺕ IBindingListViewﻭ IBindingList
ﻭ ،ITypedListﺘﻤﺘﻠﻙ ﻓﺌﺔ ﺍﻟﻌﺭﺽ ﺒﻌﺽ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﻤﻤﺎﺜﻠـﺔ ﻓـﻲ ﺍﺴـﻤﻬﺎ ﻭﻭﻅﻴﻔﺘﻬـﺎ
ﻟﺨﺼﺎﺌﺹ ﻜﺎﺌﻥ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻌﺭﺽ ،DataViewSettingﻤﺜل:
ﺍﻟﺠﺩﻭل Table
ﻤﺩﻴﺭ ﺍﻟﻌﺭﺽ DataViewManager
ﻤﺭﺸﺢ ﺍﻟﺼﻔﻭﻑ RowFilter
ﻤﺭﺸﺢ ﺤﺎﻟﺔ ﺍﻟﺼﻔﻭﻑ RowStateFilter
ﺍﻟﺘﺭﺘﻴﺏ Sort
ﺘﻁﺒﻴﻕ ﺍﻟﺘﺭﺘﻴﺏ ﺍﻻﻓﺘﺭﺍﻀﻲ ApplyDefaultSort
ﻭﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻴﺠﻌل ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺍﻟﻜﺘﺏ ﺍﻟﺘﻲ ﺘﺒﺩﺃ ﺃﺴـﻤﺎﺅﻫﺎ ﺒـﺎﻟﺤﺭﻭﻑ
ﻤﻥ ﺍﻷﻟﻑ ﺇﻟﻰ ﺍﻟﺘﺎﺀ ﻓﻘﻁ ،ﻭﻴﺭﺘﺒﻬﺎ ﺘﻨﺎﺯﻟﻴﺎ ﻋﻥ ﻁﺭﻴﻕ ﺍﺴﻡ ﺍﻟﻜﺘﺎﺏ ﻭﺭﻗﻡ ﺍﻟﻤﺅﻟﹼﹼﻑ:
;]"DataTable T = Ds.Tables["Books
;DataView Dv = T.DefaultView
;" 'ﺙ' < Dv.RowFilter = " Book
;"Dv.Sort = "Book, AuthorID DESC
ﺍﻟﻤﻔﻬﺭﺱ :Indexer
ﻴﻌﻴﺩ ﻜﺎﺌﻥ ﻋﺭﺽ ﺍﻟﺼﻑ DataRowViewﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ﺍﻟﺤـﺎﻟﻲ ﻓـﻲ
ﺍﻟﻤﻭﻀﻊ ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل ..ﻤﺜﺎل:
٣٥٣
;]DataRowView R = Dv[0
ﻻﺤﻅ ﺃﻥ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ﻴﻌﻤل ﻜﻘﺎﺌﻤﺔ ،ﻟﻬﺫﺍ ﺘﺴﺘﻁﻴﻊ ﺍﻟﻤﺭﻭﺭ ﻋﺒﺭ ﻜـل ﺼـﻔﻭﻑ ﻜـﺎﺌﻥ
ﺍﻟﻌﺭﺽ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺤﻠﻘﺔ ﺍﻟﺘﻜﺭﺍﺭ foreachﻜﺎﻟﺘﺎﻟﻲ:
)foreach (DataRowView R in Dv
)) (MessageBox.Show(R["Book"].ToString
ﺤﺫﻑ :Delete
ﺘﺤﺫﻑ ﺍﻟﺴﺠلّ ﺍﻟﺫﻱ ﺘﺭﺴل ﺇﻟﻴﻬﺎ ﺭﻗﻤﻪ ﻜﻤﻌﺎﻤل ..ﻫﺫﺍ ﺍﻟﺴﺠل ﺴﻴﺘﻡ ﺤﺫﻓﻪ ﻓﻲ ﺍﻟﺤﺎل ﻤـﻥ
ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ،ﻭﺴﺘﻜﻭﻥ ﺃﻤﺎﻤﻨﺎ ﺤﺎﻟﺘﺎﻥ ﺒﺎﻟﻨﺴﺒﺔ ﻟﻠﺠﺩﻭل ﺍﻷﺼﻠﻲ:
-١ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠل ﻤﻀﺎﻓﺎ ﺇﻟﻰ ﺍﻟﺠﺩﻭل ) ،(RowState = Addedﻓﺴـﻴﺘﻡ ﺤﺫﻓـﻪ
ﻨﻬﺎﺌﻴــﺎ ﻤــﻥ ﺍﻟﺠــﺩﻭل ،ﻭﻟــﻥ ﻴﻤﻜﻨــﻙ ﺍﺴــﺘﻌﺎﺩﺘﻪ ﺒﺎﺴــﺘﺩﻋﺎﺀ ﺍﻟﻭﺴــﻴﻠﺔ
.DataTable.RejectChanges
٣٥٤
-٢ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠل ﺃﺤﺩ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻷﺴﺎﺴﻴﺔ ،ﻓﺴﻴﻅل ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ﻤﻊ
ﺠﻌل ﺤﺎﻟﺘﻪ ﻤﺤـﺫﻭﻓﺎ ) ..(RowState = Deletedﻭﻟـﻭ ﺍﺴـﺘﺩﻋﻴﺕ ﺍﻟﻭﺴـﻴﻠﺔ
DataTable.RejectChangesﻓﺴــﺘﻌﻴﺩ ﺍﻟﺴــﺠل ﺇﻟــﻰ ﻜــﺎﺌﻥ ﺍﻟﻌــﺭﺽ
،DataViewﻤﻊ ﺘﺤﻭﻴل ﺤﺎﻟﺘﻪ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ﺇﻟﻰ .Unchanged
ﻻﺤﻅ ﺃﻥ ﺘﺭﺘﻴﺏ ﺍﻟﺴﺠﻼﺕ ﻓﻲ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ﻗﺩ ﻴﺨﺘﻠﻑ ﻋﻥ ﺘﺭﺘﻴﺒﻬﺎ ﺍﻟﻤﺠﻤﻭﻋـﺔ Rows
ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ،ﺴﻭﺍﺀ ﺒﺴﺒﺏ ﺇﻋﺎﺩﺓ ﺘﺭﺘﻴﺒﻬﺎ ،ﺃﻭ ﺒﺴﺒﺏ ﺃﺨﺫ ﺠﺯﺀ ﻓﻘﻁ ﻤﻥ ﺴـﺠﻼﺕ
ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ﺘﺤﻘﻕ ﺸﺭﻁﺎ ﻤﻌﻴﻨﺎ ..ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺃﻥ ﺘﺴﺘﺨﺩﻡ ﺍﻟﻭﺴﻴﻠﺔ Findﻟﺘﺒﺤﺙ ﻓـﻲ
ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ﻋﻥ ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺤﺫﻓﻪ ﻟﺘﺤﺼل ﻋﻠﻰ ﺭﻗﻤﻪ ..ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻔﺘﺭﺽ
ﺃﻥ Dvﻫﻭ ﻜﺎﺌﻥ ﻋﺭﺽ ﻴﻌﺭﺽ ﺒﻌﺽ ﺴﺠﻼﺕ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ،ﻭﻴﺴﺘﺨﺩﻤﻪ ﻟﺤﺫﻑ ﺃﺤـﺩ
ﺍﻟﻜﺘﺏ:
;"Dv.Sort = "Book
;)"ﺍﻟﻁﻌﺎﻡ ﻟﻜل ﻓﻡ"(var I = Dv.Find
)if (I != -1
;)Dv.Delete(I
ﺒﺤﺙ :Find
ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻨﺎ ،Objectﻟﺘﺒﺤﺙ ﻋﻥ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﻴﺤﻤﻠﻬـﺎ ﻓـﻲ ﺍﻟﻌﻤـﻭﺩ
ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﺘﺭﺘﻴﺏ ﺍﻟﺴﺠﻼﺕ )ﻜﻤﺎ ﺘﺤﺩﺩﻩ ﺍﻟﺨﺎﺼﻴﺔ ..(Sortﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺭﻗـﻡ ﺃﻭل
ﺴﺠلّ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻁﻠﻭﺒﺔ ،ﻭﺇﺫﺍ ﻟﻡ ﺘﻌﺜﺭ ﻋﻠﻰ ﺃﻱ ﺴﺠلّّ ،ﺘﻌﻴﺩ .١-
ﻭﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ،ﺘﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺔ ﻜﺎﺌﻨﺎﺕ ،ﻭﺫﻟﻙ ﺇﺫﺍ ﻜﺎﻥ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ
ﻴﺴﺘﺨﺩﻡ ﺃﻜﺜﺭ ﻤﻥ ﻋﻤﻭﺩ ﻓﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺭﺘﻴﺏ.
ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ..CustomDataSetﻓـﻲ ﻫـﺫﺍ ﺍﻟﻤﺸـﺭﻭﻉ
ﻴﺴﺘﻁﻴﻊ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺇﺩﺨﺎل ﺃﺴﻤﺎﺀ ﺍﻟﻁﻼﺏ ﻓﻲ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺭﺌﻴﺴﻴﺔ ،ﻭﻀﻐﻁ ﺍﻟﺭﺍﺒﻁ ﺍﻟﻤﻭﺠـﻭﺩ
ﻓﻲ ﻋﻤﻭﺩ ﺍﻟﺩﺭﺠﺎﺕ ﻟﻌﺭﺽ ﻨﺎﻓﺫﺓ ﻓﻴﻬﺎ ﺩﺭﺠﺎﺕ ﺍﻟﻁﺎﻟﺏ ﻓﻲ ﺍﻟﻤﻭﺍﺩ ﺍﻟﻤﺨﺘﻠﻔﺔ ..ﻟﻔﻌل ﻫـﺫﺍ،
ﺍﺘﺒﻌﻨﺎ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ:
٣٥٥
-ﺤﺼﻠﻨﺎ ﻋﻠﻰ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺍﻟﺨـﺎﺹ ﺒﺠـﺩﻭل ﺍﻟﻁﻠﺒـﺔ ،ﺒﺎﺴـﺘﺨﺩﺍﻡ
ﺍﻟﺨﺎﺼﻴﺔ ،DsStudents.Students.DefaultViewﻭﻤﺭﺭﻨﺎ ﺇﻟﻴﻪ ﺭﻗﻡ ﺼـﻑ
ﺍﻟﺘﻠﻤﻴﺫ ﺍﻟﺤﺎﻟﻲ )ﻤﻤﺜﻼ ﺒﺎﻟﻤﺘﻐﻴﺭ ،(Idxﻟﻨﺤﺼل ﻋﻠﻰ ﻜﺎﺌﻥ ﻋﺭﺽ ﻫـﺫﺍ ﺍﻟﺼـﻑ
.DataRowView
[var StdRel = DsStudents.Students.ChildRelations
;0].RelationName
var RowView = DsStudents.Students.
;]DefaultView[Idx
-ﺃﺭﺴﻠﻨﺎ ﺇﻟﻰ ﺍﻟﻭﺴﻴﻠﺔ CreateChildViewﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻟﺼﻑ ،ﺍﺴﻡ ﺍﻟﻌﻼﻗـﺔ
ﺍﻟﺘﻲ ﺘﺭﺒﻁ ﺠﺩﻭل ﺍﻟﺘﻼﻤﻴﺫ ﺒﺠﺩﻭل ﺍﻟﺩﺭﺠﺎﺕ ،ﻟﻨﺤﺼل ﻋﻠﻰ ﻜﺎﺌﻥ ﻋﺭﺽ ﻴﺤﺘـﻭﻱ
ﻋﻠﻰ ﺩﺭﺠﺎﺕ ﺍﻟﺘﻠﻤﻴﺫ ﺍﻟﺤﺎﻟﻲ ،ﻟﻨﺴﺘﺨﺩﻤﻪ ﻜﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ﻟﺠﺩﻭل ﺍﻟﻌـﺭﺽ ﺍﻟـﺫﻱ
ﻴﻌﺭﺽ ﺍﻟﺩﺭﺠﺎﺕ ..ﻫﺫﺍ ﻴﺠﻌل ﺠﻤﻴﻊ ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﻲ ﺘﻀﺎﻑ ﺇﻟﻰ ﻫـﺫﺍ ﺍﻟﺠـﺩﻭل،
ﺘﻀﻊ ﺘﻠﻘﺎﺌﻴﺎ ﺭﻗﻡ ﻫﺫﺍ ﺍﻟﺘﻠﻤﻴﺫ ﻓﻲ ﺍﻟﻌﻤﻭﺩ :StudentID
;)var Grades = RowView.CreateChildView(StdRel
-ﻟﻭ ﻜﺎﻨﺕ ﻫﺫﻩ ﺃﻭل ﻤﺭﺓ ﻨﻌﺭﺽ ﻓﻴﻬﺎ ﺩﺭﺠﺎﺕ ﻫﺫﺍ ﺍﻟﺘﻠﻤﻴـﺫ ،ﻓﺴـﻴﻅﻬﺭ ﺍﻟﺠـﺩﻭل
ﻓﺎﺭﻏﺎ ..ﻫﺫﺍ ﻏﻴﺭ ﻤﻘﺒﻭل ،ﻷﻥ ﻋﻠﻴﻨﺎ ﺃﻥ ﻨﻌﺭﺽ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺃﺴﻤﺎﺀ ﺍﻟﻤﻭﺍﺩ ،ﻟﻴﻜﺘـﺏ
ﻫﻭ ﺩﺭﺠﺎﺕ ﺍﻟﻁﺎﻟﺏ ﺍﻟﻤﻨﺎﻅﺭﺓ ﻟﻬﺎ ﻤﺒﺎﺸﺭﺓ ..ﻭﻨﻅﺭﺍ ﻷﻨﻨﺎ ﻨﺴﻤﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺒﺘﺤﺭﻴﺭ
ﺠﺩﻭل ﺍﻟﻤﻭﺍﺩ ﺍﻟﺩﺭﺍﺴﻴﺔ ﺒﻀﻐﻁ ﺍﻟﺯﺭ "ﻋﺭﺽ ﺍﻟﻤﻭﺍﺩ" ،ﻓﻤﻥ ﺍﻟﻤﻤﻜـﻥ ﺃﻥ ﺘﻀـﺎﻑ
ﻤﻭﺍﺩ ﺠﺩﻴﺩﺓ ﻟﻡ ﻨﻘﻡ ﺒﺈﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﻨﺎﻓﺫﺓ ﺍﻟﺩﺭﺠﺎﺕ ﻤﻥ ﻗﺒل ..ﻟﻬﺫﺍ ﻋﻠﻴﻨﺎ ﺃﻥ ﻨﺴﺘﺨﺩﻡ
ﺍﻟﻭﺴﻴﻠﺔ Findﻟﻠﺘﺄﻜﺩ ﻤﻥ ﺃﻥ ﺭﻗﻡ ﺍﻟﻤﺎﺩﺓ ﻏﻴﺭ ﻤﻭﺠﻭﺩ ،ﻭﻤﻥ ﺜﻡ ﻨﻀﻴﻔﻬﺎ ..ﺍﻟﺴـﺒﺏ
ﻓﻲ ﻫﺫﺍ ﺃﻥ ﺘﻜﺭﺍﺭ ﻨﻔﺱ ﺍﻟﻤﺎﺩﺓ ﻤﻊ ﻨﻔﺱ ﺍﻟﻁﺎﻟﺏ ﺴﻴﺅﺩﻱ ﺇﻟﻰ ﺤﺩﻭﺙ ﺨﻁﺄ ﺒﺴـﺒﺏ
ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ ﺍﻟﻤﻔﺭﻭﺽ ﻋﻠﻰ ﻫﺫﻴﻥ ﺍﻟﺤﻘﻠﻴﻥ ..ﻟﻜﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ Findﻴﻭﺠـﺏ
ﻋﻠﻴﻨﺎ ﺃﻭﻻ ﺃﻥ ﻨﺴﺘﺨﺩﻡ ﺍﻟﺤﻘل SubjectIDﻜﻤﻔﺘﺎﺡ ﻟﻠﺘﺭﺘﻴﺏ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ :Sort
;"Grades.Sort = "SubjectID
ﺒﻌﺩ ﻫﺫﺍ ﺴﻨﻤﺭ ﻋﻠﻰ ﻜل ﻤﺎﺩﺓ ﻓﻲ ﺠﺩﻭل ﺍﻟﻤﻭﺍﺩ ،ﻭﻨﺒﺤﺙ ﻋﻥ ﺭﻗﻤﻬﺎ ﻓـﻲ ﺠـﺩﻭل
ﺍﻟﺩﺭﺠﺎﺕ ،ﻭﻟﻭ ﻟﻡ ﺘﻜﻥ ﻤﻭﺠﻭﺩﺓ )ﻨﺘﻴﺠﺔ ﺍﻟﺒﺤﺙ = ،(١-ﻋﻠﻴﻨﺎ ﺍﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ
AddNewﻹﻀﺎﻓﺔ ﺼﻑ ﺠﺩﻴﺩ ﺇﻟﻰ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ..ﻫﺫﺍ ﺍﻟﺼﻑ ﻴﻜﻭﻥ ﻓﺎﺭﻏـﺎ،
٣٥٦
ﻤﺎ ﻋﺩﺍ ﺍﻟﺤﻘل StudentIDﺍﻟﺫﻱ ﻴﺄﺨﺫ ﺘﻠﻘﺎﺌﻴﺎ ﺭﻗﻡ ﺍﻟﺘﻠﻤﻴﺫ ﺍﻟﺫﻱ ﻨﺘﻌﺎﻤـل ﻤﻌـﻪ..
ﻟﻬﺫﺍ ﻋﻠﻴﻨﺎ ﻨﻀﻴﻑ ﺭﻗﻡ ﺍﻟﻤﺎﺩﺓ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﺼﻑ ﺍﻟﺠﺩﻴﺩ ..ﻻﺤـﻅ ﺃﻥ ﺍﺴـﻡ ﺍﻟﻤـﺎﺩﺓ
ﺴﻴﻀﺎﻑ ﺘﻠﻘﺎﺌﻴﺎ ﻷﻨﻪ ﻋﻤﻭﺩ ﻤﺤﺴﻭﺏ ﻤﺒﻨﻲ ﻋﻠﻰ ﻗﻴﻤﺔ ﺍﻟﺤﻘل ..StudentIDﻟﻜﻥ
ﻫﺫﺍ ﻟﻥ ﻴﺤﺩﺙ ﻁﺎﻟﻤﺎ ﻅل ﻫﺫﺍ ﺍﻟﺼﻑ ﺍﻟﺠﺩﻴﺩ ﻫﻭ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ ..ﻟﺤﺴﻥ ﺍﻟﺤﻅ ﺃﻥ
ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ ﻴﺘﻐﻴﺭ ﺘﻠﻘﺎﺌﻴﺎ ﺇﺫﺍ ﺃﻀﻔﻨﺎ ﺼﻔﺎ ﺠﺩﻴﺩﺍ ﺒﻌﺩﻩ ،ﻟﻜﻥ ﺍﻟﻤﺸﻜﻠﺔ ﺘﻅـل ﻓـﻲ
ﺁﺨﺭ ﺼﻑ ﻨﻀﻴﻔﻪ ..ﻟﻬﺫﺍ ﻋﻠﻴﻨﺎ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ EndEditﻹﻨﻬـﺎﺀ ﺘﺤﺭﻴـﺭ
ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ ..ﻫﺫﺍ ﺴﻴﺠﺒﺭ ﺍﻟﻌﻤﻭﺩ Subjectﻋﻠﻰ ﺤﺴﺎﺏ ﻗﻴﻤﺘﻪ ،ﻭﻋﺭﺽ ﺍﺴﻡ
ﺍﻟﻤﺎﺩﺓ ﺘﻠﻘﺎﺌﻴﺎ:
;var SubjRows = DsStudents.Subjects.Rows
)for (var i = 0; i < SubjRows.Count; i++
{
;]"var SbjID = SubjRows[i]["ID
)if (Grades.Find(SbjID) == -1
{
;) (var Rv = Grades.AddNew
;Rv["SubjectID"] = SbjID
;) (Rv.EndEdit
}
}
ﻭﻤﻥ ﺍﻷﺫﻜﻰ ﺃﻥ ﺘﺨﻔﻲ ﺭﻗﻡ ﺍﻟﺘﻠﻤﻴﺫ ﻭﺭﻗﻡ ﺍﻟﻤﺎﺩﺓ ﻋﻥ ﺍﻟﻤﺴﺘﺨﺩﻡ ،ﻷﻨﻪ ﺴﻴﺘﻌﺎﻤل ﻓﻘﻁ
ﻤﻊ ﺍﻟﻤﺎﺩﺓ ﻭﺩﺭﺠﺔ ﺍﻟﻁﺎﻟﺏ ﻓﻴﻬﺎ ..ﻭﺴـﺘﺠﺩ ﻫـﺫﺍ ﺍﻟﻜـﻭﺩ ﻜـﺎﻤﻼ ﻓـﻲ ﺍﻹﺠـﺭﺍﺀ
ShowGradesﺍﻟﺫﻱ ﻴﺘﻡ ﺍﺴﺘﺩﻋﺎﺅﻩ ﻤـﻥ ﺍﻟﺤـﺩﺙ CellContentClickﻓـﻲ
ـﻭﺫﺝ
ـﻲ ﺍﻟﻨﻤـ
ـﺩﺙ SelectedIndexﻓـ
ـﻥ ﺍﻟﺤـ
ـﻭﺫﺝ ،FrmStudentﻭﻤـ
ﺍﻟﻨﻤـ
.FrmGrades
٣٥٧
ﺍﻟﺘﺤﻭﻴل ﺇﻟﻰ ﺠﺩﻭل :ToTable
ﺘﻨﺸﺊ ﺠﺩﻭﻻ ﺠﺩﻴﺩﺍ ﻭﺘﻨﺴﺦ ﺇﻟﻴﻪ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ﺍﻟﺤـﺎﻟﻲ ،ﻭﺘﻌﻴـﺩ
ﺇﻟﻴﻙ ﻜﺎﺌﻥ ﺠﺩﻭل DataTableﻴﺸﻴﺭ ﺇﻟﻴﻪ ..ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ.
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻨﺼﺎ ﻟﺘﺴﺘﺨﺩﻤﻪ ﻜﺎﺴﻡ ﻟﻠﺠﺩﻭل.
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ:
-ﻗﻴﻤﺔ ﻤﻨﻁﻘﻴﺔ ﺇﺫﺍ ﺠﻌﻠﺘﻬﺎ trueﻓﺴﻴﺘﻡ ﺍﺴﺘﺒﻌﺎﺩ ﺍﻟﺼﻔﻭﻑ ﺍﻟﻤﻜﺭﺭﺓ ﺍﻟﺘﻲ ﺘﺘﺸـﺎﺒﻪ
ﻗﻴﻡ ﺃﻱ ﻤﻥ ﺨﺎﻨﺎﺘﻬﺎ ﻤﻊ ﺃﻱ ﺼﻔﻭﻑ ﺃﺨﺭﻯ.
-ﻤﺼﻔﻭﻓﺔ ﻨﺼﻴﺔ ،ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻨﺴﺨﻬﺎ ﺇﻟﻰ ﺍﻟﺠﺩﻭل..
ﻭﺴﺘﻅﻬﺭ ﺍﻷﻋﻤﺩﺓ ﻓﻲ ﺍﻟﺠﺩﻭل ﺒﻨﻔﺱ ﺘﺭﺘﻴﺒﻬﺎ ﻓﻲ ﺍﻟﻤﺼﻔﻭﻓﺔ ..ﻻﺤﻅ ﺃﻥ ﻫـﺫﻩ
ﻫﻲ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﻭﺤﻴﺩﺓ ﺍﻟﺘﻲ ﺘﺴﺘﻁﻴﻊ ﺒﻬﺎ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﺠﺩﻭل ﻴﺨﺘﻠﻑ ﻓﻲ ﻋﺩﺩ
ﺃﻋﻤﺩﺘﻪ ﻭﺘﺭﺘﻴﺒﻬﺎ ﻋﻥ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ.
-٤ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﻌﺎﻤﻼﺕ ﺍﻟﺼﻴﻐﺘﻴﻥ ﺍﻟﺴﺎﺒﻘﺘﻴﻥ ﻤﻌﺎ.
٣٥٨
ﻭﺍﺠﻬﺔ ﺍﻟﻜﺎﺌﻥ ﺍﻟﻘﺎﺒل ﻟﻠﺘﺤﺭﻴﺭ
IEditableObject Interface
ـﺭ
ـﺔ ﺍﻟﺘﺤﺭﻴـ
ـﻲ ﺤﺎﻟـ
ـﻌﻪ ﻓـ
ـﻥ ﻭﻀـ
ـﺎﺌﻥ ﻴﻤﻜـ
ـﻊ ﻜـ
ـل ﻤـ
ـﺔ ﻟﻠﺘﻌﺎﻤـ
ـﺎﺌل ﺍﻟﻼﺯﻤـ
ـﺩﻡ ﺍﻟﻭﺴـ
ﺘﻘـ
)ﻤﺜل ﻜﺎﺌﻥ ﻋﺭﺽ ﺼﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ ،(DataRowViewﻤﻊ ﺍﻟﻘﺩﺭﺓ ﻋﻠﻰ ﺇﻟﻐﺎﺀ ﺍﻟﺘﻐﻴﻴـﺭﺍﺕ ﺃﻭ
ﻗﺒﻭﻟﻬﺎ.
ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ:
٣٥٩
ﻭﺍﺠﻬﺔ ﻤﻌﻠﻭﻤﺎﺕ ﺨﻁﺄ ﺍﻟﺒﻴﺎﻨﺎﺕ
IDataErrorInfo Interface
ﺘﻘﺩﻡ ﻤﻌﻠﻭﻤﺎﺕ ﻋﻥ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﺍﻟﺴﺠل ،ﻭﻫﻲ ﺘﻤﺘﻠﻙ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
ﺍﻟﺨﻁﺄ :Error
ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﺸﺭﺡ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﺍﻟﺴﺠل.
ﺘﻁﻠﻕ ﺍﻟﺤﺩﺙ PropertyChangedﻋﻨﺩ ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺨﺎﺼﻴﺔ ﻤﻌﻴﻨﺔ ..ﻭﺘﻤﺘﻠﻙ ﻫـﺫﻩ ﺍﻟﻭﺍﺠﻬـﺔ
ﺍﻟﺤﺩﺙ ﺍﻟﺘﺎﻟﻲ:
٣٦٠
ﻓﺌﺔ ﻋﺭﺽ ﺼﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ DataRowView Class
ﺍﻟﺴﺠل :Row
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺴﺠل ﺍﻟﺒﻴﺎﻨﺎﺕ DataRowﺍﻟﺫﻱ ﻴﻌﺭﻀﻪ ﻜﺎﺌﻥ ﻋﺭﺽ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ.
٣٦١
ﺍﻟﻤﻔﻬﺭﺱ :Indexer
ﻴﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺇﺤﺩﻯ ﺨﺎﻨﺎﺕ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ ،ﺍﻟﺘﻲ ﻴﺤﺩﺩﻫﺎ ﺍﺴـﻡ ﺍﻟﻌﻤـﻭﺩ ﺃﻭ ﺭﻗﻤـﻪ
ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل ..ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﺽ ﻗﻴﻡ ﻜل ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻜـل ﺼـﻔﻭﻑ
ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ :Dv
)foreach (DataRowView R in Dv
{
)for (var I = 0; I < Dv.Table.Columns.Count; I++
{
;)) (MessageBox.Show(R[I].ToString
}
}
ﺤﺫﻑ :Delete
ـﻴﻠﺔ
ـﺄﺜﻴﺭ ﺍﻟﻭﺴـ
ـﺱ ﺘـ
ـﺎ ﻨﻔـ
ـﺭﺽ ،ﻭﻟﻬـ
ـﺎﺌﻥ ﺍﻟﻌـ
ـﻥ ﻜـ
ـﺎﻟﻲ ﻤـ
ـﺠل ﺍﻟﺤـ
ـﺫﻑ ﺍﻟﺴـ
ﺘﺤـ
DataView.Deleteﻋﻠﻰ ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﻓﻲ ﺠﺩﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ.
٣٦٢
ﻋﻠﻰ ﻜلّ ﺍﻟﻜﺘﺏ ﺍﻟﺘﻲ ﺃﻟﻔﻬﺎ ﺃﻭل ﻤﺅﻟﻑ ﻓﻲ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ،ﺒـﺎﻓﺘﺭﺍﺽ ﺃﻥ ﺍﻟﻌﻼﻗـﺔ ﺒـﻴﻥ
ﺠﺩﻭﻟﻲ ﺍﻟﻜﺘﺏ ﻭﺍﻟﻤﺅﻟﻔﻴﻥ ﺍﺴﻤﻬﺎ :AuthorsBooks
;]DataRowView Rv = Ds.Tables["Authors"].DefaultView[0
;)"DataView Dv = Rv.CreateChildView("AuthorsBooks
ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ،DataSetSampleﻟﻌﺭﺽ ﻜﺘـﺏ ﺍﻟﻤﺅﻟـﻑ
ﺍﻟﻤﺤﺩﺩ ﺤﺎﻟﻴﺎ ..ﻓﻌﻨﺩ ﺘﻐﻴﺭ ﺍﻟﺴﺠل ﺍﻟﻤﺤﺩﺩ ﻓﻲ ﺠﺩﻭل ﻋﺭﺽ ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﻴﻨﻁﻠـﻕ ﺍﻟﺤـﺩﺙ
،DataGridView.RowEnterﻭﻓﻴﻪ ﻨﻔﻌل ﻤﺎ ﻴﻠﻲ:
-ﻨﺴﺘﺨﺩﻡ ﺍﻟﺨﺎﺼﻴﺔ e.RowIndexﻟﻤﻌﺭﻓﺔ ﺭﻗﻡ ﺍﻟﺼﻑ ﺍﻟﻤﺤﺩﺩ ﺤﺎﻟﻴﺎ.
-ﻨﻅﺭﺍ ﻷﻥ ﺭﻗﻡ ﺍﻟﺼﻑ ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ،ﻫﻭ ﻨﻔﺴﻪ ﺭﻗﻡ ﺍﻟﺼـﻑ ﻓـﻲ ﺠـﺩﻭل
ﺍﻟﻤﺅﻟﻔﻴﻥ ﺒﺴﺒﺏ ﺍﻟـﺭﺒﻁ ﺒﻴﻨﻬﻤـﺎ ،Bindingﻓﺴﻨﺭﺴـﻠﻪ ﺇﻟـﻰ ﻤﻔﻬـﺭﺱ ﺍﻟﻔﺌـﺔ
DefaultViewﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻜﺎﺌﻥ ﻋﺭﺽ ﻫﺫﺍ ﺍﻟﺼﻑ:
;]"var TblAuthors = Ds.Tables["Authors
;]var RowView = TblAuthors.DefaultView[e.RowIndex
ﻻﺤﻅ ﺃﻥ ﺭﻗﻡ ﻜل ﺼﻑ ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻴﻅل ﺜﺎﺒﺘﺎ ﻤﻬﻤـﺎ ﻏﻴـﺭ ﺍﻟﻤﺴـﺘﺨﺩﻡ
ﺘﺭﺘﻴﺏ ﺍﻟﺼﻔﻭﻑ ﺍﻟﻤﻌﺭﻭﻀﺔ!
-ﻨﺴﺘﺨﺩﻡ ﺍﻟﻭﺴﻴﻠﺔ CreateChildViewﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻜﺎﺌﻥ ﻋـﺭﺽ ﻴﺤﺘـﻭﻱ
ﻋﻠﻰ ﻜﺘﺏ ﺍﻟﻤﺅﻟﻑ ﺍﻟﻤﺤﺩﺩ ﺤﺎﻟﻴﺎ ،ﻭﻨﻌﺭﻀﻬﺎ ﻓﻲ ﺠﺩﻭل ﻋﺭﺽ ﺍﻟﻜﺘﺏ ﺍﻟﻤﻭﺠـﻭﺩ
ﻓﻲ ﺍﻟﻨﺼﻑ ﺍﻟﺴﻔﻠﻲ ﻤﻥ ﺍﻟﻨﺎﻓﺫﺓ:
= DataView DvBooks
;)"RowView.CreateChildView("AuthorsBooks
;DgBooks.DataSource = DvBooks
ﻻﺤﻅ ﺃﻨﻙ ﻋﻨﺩﻤﺎ ﺘﺒﺩﺃ ﺘﺤﺭﻴﺭ ﺍﻟﺴﺠل ﺍﻟﺠﺩﻴﺩ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻨﻬﺎﻴﺔ ﺠﺩﻭل ﻋـﺭﺽ ﺍﻟﻜﺘـﺏ،
ﻓﺈﻥ ﺍﻟﺨﺎﻨﺔ AuthorIDﺴﺘﺄﺨﺫ ﺘﻠﻘﺎﺌﻴﺎ ﺭﻗﻡ ﺍﻟﻤﺅﻟﻑ ﺍﻟﺫﻱ ﻴﻨﺘﻤﻲ ﺇﻟﻴـﻪ ﻜـﺎﺌﻥ ﺍﻟﻌـﺭﺽ
ﺍﻟﺤﺎﻟﻲ ..ﻫﺫﺍ ﻴﺭﻴﺤﻙ ﻤﻥ ﻜﺘﺎﺒﺔ ﺃﻱ ﻜﻭﺩ ﺇﻀﺎﻓﻲ ،ﻜﻤـﺎ ﻴﺴـﻤﺢ ﻟـﻙ ﺒﺈﺨﻔـﺎﺀ ﺍﻟﻌﻤـﻭﺩ
AuthorIDﻤﻥ ﺍﻟﺠﺩﻭل ﺩﻭﻥ ﻗﻠﻕ ،ﻓﻬﻭ ﺴﻴﺄﺨﺫ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺼﺤﻴﺤﺔ ﺁﻟﻴـﺎ ﺩﻭﻥ ﺃﻥ ﻴﺸـﻐل
ﺍﻟﻤﺴﺘﺨﺩﻡ ﺒﺎﻟﻪ ﺒﻬﺫﺍ ..ﻭﻹﺨﻔﺎﺀ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ،ﺃﻀﻑ ﻫﺫﺍ ﺍﻟﺴﻁﺭ ﺇﻟﻰ ﻨﻬﺎﻴﺔ ﺍﻟﻜﻭﺩ ﺍﻟﺴﺎﺒﻕ:
;DgBooks.Columns["AuthorID"].Visible = false
٣٦٣
-١٤-
ﺭﺒـﻁ ﺍﻟﺒﻴـﺎﻨـﺎﺕ
Data Binding
ﻓﻲ ﺘﻁﺒﻴﻘﺎﺕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻜﺜﻴﺭﺍ ﻤﺎ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﻋﺭﺽ ﺒﻴﺎﻨﺎﺕ ﺃﺤﺩ ﺍﻟﺴـﺠﻼﺕ ﻓـﻲ ﺃﺩﻭﺍﺕ
ﻤﻭﻀﻭﻋﺔ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ،ﻤﻊ ﻭﺠﻭﺩ ﺃﺯﺭﺍﺭ ﻟﻠﺘﺤﺭﻙ ﺇﻟﻰ ﺍﻟﺴﺠل ﺍﻟﺘﺎﻟﻲ ﺃﻭ ﺍﻟﺴـﺠل ﺍﻟﺴـﺎﺒﻕ،
ﻟﻴﺘﻤﻜﻥ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻤﻥ ﺍﻟﺘﺤﻜﻡ ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﻤﻌﺭﻭﺽ ﺤﺎﻟﻴﺎ.
ﻭﻨﻅﺭﺍ ﻷﻥ ﻓﻌل ﻫﺫﺍ ﻴﺩﻭﻴﺎ ﻗﺩ ﻴﺤﺘﺎﺝ ﺇﻟﻰ ﻜﻭﺩ ﻁﻭﻴل ﻭﻤﺭﻫﻕ ،ﻓﻘﺩ ﻗﺩﻤﺕ ﻟﻙ ﺩﻭﺕ ﻨـﺕ ﺁﻟﻴـﺔ
ﺠﺎﻫﺯﺓ ﺘﺴﻤﻰ ﺭﺒﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ ،Data Bindingﺘﺘﻴﺢ ﻟﻙ ﺭﺒﻁ ﺍﻟﻜﺎﺌﻨﺎﺕ ﺒﺄﺩﻭﺍﺕ ﺍﻟﻭﻴﻨﺩﻭﺯ ﺒﺄﻗـل
ﻗﺩﺭ ﻤﻥ ﺍﻟﻜﻭﺩ.
ﻭﻴﺴﻤﻰ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺫﻱ ﻴﺘﻡ ﺭﺒﻁﻪ ﺒﺎﻷﺩﺍﺓ ﺒﺎﺴﻡ ﻤﺼـﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ..Data Sourceﻭﺘﺸـﻤل
ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺘﺎﻟﻴﺔ:
٣٦٥
ﻭﺍﺠﻬﺔ ﺍﻟﻤﻜﻭﻥ ﺍﻟﻘﺎﺒل ﻟﻼﺭﺘﺒﺎﻁ
IBindableComponent Interfac
ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻷﺴﺎﺴﻴﺔ ﺍﻟﻼﺯﻤﺔ ﻟﺭﺒﻁ ﺍﻷﺩﻭﺍﺕ ﺒﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻫﻲ:
ﺍﻟﺠﺩﻴﺭ ﺒﺎﻟﺫﻜﺭ ﺃﻥ ﻓﺌﺔ ﺍﻷﺩﺍﺓ ﺍﻷﻡ Control Classﺍﻟﺘﻲ ﺘﺭﺜﻬﺎ ﺠﻤﻴﻊ ﺍﻷﺩﻭﺍﺕ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬـﺔ
،IBindableComponentﻭﻤﻥ ﺜﻡ ﻓﻬﻲ ﺘﻤﺘﻠﻙ ﺠﻤﻴﻊ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﺴـﺎﺒﻘﺔ ..ﻫـﺫﺍ ﻤﻌﻨـﺎﻩ ﺃﻥ
ﺠﻤﻴﻊ ﺃﺩﻭﺍﺕ ﺍﻟﻭﻴﻨﺩﻭﺯ ﺘﺼﻠﺢ ﻟﻼﺭﺘﺒﺎﻁ ﺒﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺘﺴــﻤﻰ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺘــﻲ ﻴــﺘﻡ ﺍﻻﺭﺘﺒــﺎﻁ ﺒﻬــﺎ ﺒﺎﺴــﻡ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﺭﺘﺒﻁــﺔ ﺒﺎﻟﺒﻴﺎﻨــﺎﺕ
،Data-Bound Controlsﻭﻨﻅﺭﺍ ﻷﻥ ﻜل ﺃﺩﺍﺓ ﺘﻤﺘﻠﻙ ﺍﻟﻌﺩﻴﺩ ﻤﻥ ﺍﻟﺨﺼـﺎﺌﺹ ،ﻓﻴﺠـﺏ
ﻋﻠﻴﻙ ﺃﻥ ﺘﺤﺩﺩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﻲ ﺘﺭﻴﺩﻫﺎ ﺃﻥ ﺘﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻻ ﻤﺎﻨﻊ ﻤﻥ ﺃﻥ ﺘﺭﺒﻁ ﺃﻜﺜﺭ ﻤـﻥ
٣٦٦
ﺨﺎﺼﻴﺔ ﻤﻥ ﺨﺼﺎﺌﺹ ﺍﻷﺩﺍﺓ ،ﺒﺄﻜﺜﺭ ﻤﻥ ﻋﻨﺼﺭ ﻤﻥ ﻋﻨﺎﺼﺭ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻤﺜﻼ :ﻴﻤﻜﻨـﻙ
ﺭﺒﻁ ﺍﻟﺨﺎﺼﻴﺔ Textﺍﻟﺨﺎﺼﺔ ﺒﺯﺭ ﺍﻻﺨﺘﻴﺎﺭ CheckBoxﺒﻌﻨﺼﺭ ﺒﻴﺎﻨـﺎﺕ ﻨﺼـﻲ ﻭﺭﺒـﻁ
ﺍﻟﺨﺎﺼﻴﺔ Checkedﺒﻌﻨﺼﺭ ﺒﻴﺎﻨﺎﺕ ﻤﻨﻁﻘﻲ ..Booleanﻭﺘﺴﻤﻰ ﺨﺎﺼﻴﺔ ﺍﻷﺩﺍﺓ ﺍﻟﺘـﻲ ﻴـﺘﻡ
ﺍﻻﺭﺘﺒﺎﻁ ﺒﻬﺎ ﺒﺎﺴﻡ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ،Display Memberﻷﻨﻬﺎ ﺘﻌـﺭﺽ ﻗﻴﻤـﺔ ﺨﺎﺼـﻴﺔ
ﺍﻟﻜﺎﺌﻥ.
ﻤﻠﺨﺹ:
· ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ :Data Source
ﻫﻭ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﻴﺘﻡ ﺭﺒﻁﻬﺎ ﺒﺎﻷﺩﺍﺓ ..ﻭﻤﺜـﺎل ﺫﻟـﻙ :ﺍﻟﺠـﺩﻭل
Booksﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﺘﺴﺘﺨﺩﻡ ﻓـﻲ ﺘﻘﻨﻴـﺔ ﺍﻟـﺭﺒﻁ ﻤﺠﻤﻭﻋـﺔ ﻤـﻥ ﺍﻟﻔﺌـﺎﺕ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓـﻲ ﻨﻁـﺎﻕ ﺍﻻﺴـﻡ
..System.Windows.Formsﺩﻋﻨﺎ ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻔﺌﺎﺕ.
٣٦٧
ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻻﺭﺘﺒﺎﻁﺎﺕ
BindingsCollection Class
٣٦٨
ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﺭﺘﺒﺎﻁﺎﺕ ﺍﻷﺩﺍﺓ
ControlBindingsCollection Class
٣٦٩
-٥ﺍﻟﺼﻴﻐﺔ ﺍﻟﺨﺎﻤﺴﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺴﺎﺩﺱ ،ﻭﻫﻭ ﻴﺴﺘﻘﺒل ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ
ﺘﺭﻴﺩ ﻭﻀﻌﻬﺎ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ﺇﺫﺍ ﻜﺎﻥ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﺎﺭﻏﺎ ..Nothingﻻﺤﻅ
ﺃﻥ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻤﻥ ﺍﻟﻨﻭﻉ Objectﻟﻴﺘﻴﺢ ﻟﻙ ﺇﺭﺴﺎل ﺃﻴﺔ ﻗﻴﻤﺔ ﺘﻨﺎﺴﺒﻙ.
-٦ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺩﺴﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺴﺎﺒﻊ ،ﻴﺘﻴﺢ ﻟـﻙ ﺇﺭﺴـﺎل ﻨـﺹ
ﻴﺤﻤل ﺍﻟﺘﻨﺴﻴﻕ Formatﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺘﻁﺒﻴﻘﻪ ﻋﻠﻰ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻨﺩ ﻭﻀـﻌﻬﺎ
ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ.
-٧ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻌﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤـل ﺜـﺎﻤﻥ ﻤـﻥ ﻨـﻭﻉ ﺍﻟﻭﺍﺠﻬـﺔ
،IFormatProviderﻟﻭﻀﻊ ﻗﻴﻤﺘﻪ ﻓﻲ ﺍﻟﺨﺎﺼـﻴﺔ FormatInfoﺍﻟﺘـﻲ ﺴـﻨﺘﻌﺭﻑ
ﻋﻠﻴﻬﺎ ﺒﻌﺩ ﻗﻠﻴل.
ﻭﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻜل ﻫﺫﻩ ﺍﻟﺼﻴﻎ ـ ﻤﺎ ﻋﺩﺍ ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟـﻰ ـ ﻤﺭﺠﻌـﺎ ﺇﻟـﻰ ﻜـﺎﺌﻥ ﺍﻻﺭﺘﺒـﺎﻁ
Bindingﺍﻟﺫﻱ ﺘﻡ ﺇﻨﺸﺎﺅﻩ ﻭﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ.
ﻭﺘﺴﺘﻁﻴﻊ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ Addﺃﻴﻀﺎ ﻹﻀﺎﻓﺔ ﺍﺭﺘﺒﺎﻁ ﺒﻜﺎﺌﻥ ﺒﺴﻴﻁ ﺤﺘﻰ ﻟـﻭ ﻜـﺎﻥ ﻤﺠـﺭﺩ
ﻤﺘﻐﻴﺭ ﻨﺼﻲ ،ﻤﺜل:
;"string Name = "Mohammad
;)"" TextBox1.DataBindings.Add("Text", Name,
ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﺴﻴﺠﻌل ﻤﺭﺒﻊ ﺍﻟﻨﺹ TextBox1ﻴﻌﺭﺽ ﺍﻟﻨﺹ .Mohammad
ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻹﻀﺎﻓﺔ ﺍﺭﺘﺒﺎﻁ ﺒﻜﺎﺌﻥ ﻤﻜﻭﻥ ﻤﻥ ﺃﻜﺜﺭ ﻤـﻥ ﻋﻨﺼـﺭ ،ﻤﺜـل ﺍﻟﻔﺌـﺎﺕ
ﻭﺍﻟﺴﺠﻼﺕ ..ﻤﺜﺎل:
;)Size Sz = new Size(100, 200
;)"TextBox2.DataBindings.Add("Text", Sz, "Width
ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﺴﻴﺠﻌل ﻤﺭﺒﻊ ﺍﻟﻨﺹ TextBox2ﻴﻌﺭﺽ ﺍﻟﺭﻗﻡ .١٠٠
ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ Addﻟﺭﺒﻁ ﺃﻜﺜﺭ ﻤﻥ ﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ﺒـﻨﻔﺱ ﺍﻷﺩﺍﺓ ..ﻫـﺫﺍ ﺍﻟﻜـﻭﺩ
ﺼﺤﻴﺢ:
;)"" TextBox1.DataBindings.Add("Text", Name,
;)"TextBox1.DataBindings.Add("Tag", Sz, "Width
ﻟﻜﻥ ﺍﻟﻭﺴﻴﻠﺔ Addﺴﺘﺴﺒﺏ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻟﻭ ﺤﺎﻭﻟﺕ ﺭﺒﻁ ﺃﻜﺜﺭ ﻤﻥ ﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ﺒـﻨﻔﺱ
ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ﻓﻲ ﻨﻔﺱ ﺍﻷﺩﺍﺓ ..ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﺨﺎﻁﺊ:
٣٧٠
;)"" TextBox1.DataBindings.Add("Text", Name,
;)"TextBox1.DataBindings.Add("Text", Sz, "Width
ﻟﻬﺫﺍ ﻗﺒل ﺃﻥ ﺘﻐﻴﺭ ﺍﺭﺘﺒﺎﻁ ﺍﻟﺨﺎﺼﻴﺔ ،ﻋﻠﻴﻙ ﺃﻥ ﺘﺯﻴل ﻜـﺎﺌﻥ ﺍﻻﺭﺘﺒـﺎﻁ ﺃﻭﻻ ﻤـﻥ ﺍﻟﻤﺠﻤﻭﻋـﺔ
ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ Removeﻜﺎﻟﺘﺎﻟﻲ:
;)"" var Bnd = TextBox1.DataBindings.Add("Text", Name,
;)TxtName.DataBindings.Remove(Bnd
;)"TextBox1.DataBindings.Add("Text", Sz, "Width
ﻻﺤﻅ ﻜﺫﻟﻙ ﺃﻥ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺠﺏ ﺃﻥ ﻴﻜﻭﻥ ﺨﺎﺼﻴﺔ ﻭﻟﻴﺱ ﻤﺘﻐﻴـﺭﺍ ..ﺒﻤﻌﻨـﻰ ﺁﺨـﺭ :ﻻ
ﻴﻤﻜﻨﻙ ﺭﺒﻁ ﺍﻷﺩﺍﺓ ﺒﺤﻘل Fieldﻤﻥ ﺤﻘﻭل ﺍﻟﻜﺎﺌﻥ ..ﺘﺫﻜﺭ ﺃﻥ ﺍﻟﺤﻘل ﻫﻭ ﻤﺘﻐﻴﺭ ﻋـﺎﻡ Public
Variableﻤﻌﺭﻑ ﻋﻠﻰ ﻤﺴﺘﻭﻯ ﺍﻟﻔﺌﺔ ،ﻤﺜل:
class Student
{
;public int ID
;public int Age
;public string Name
}
ﺍﻵﻥ ﻟﻭ ﻋﺭﻓﺕ ﻜﺎﺌﻨﺎ ﻤﻥ ﻓﺌﺔ ﺍﻟﻁﺎﻟﺏ ﻭﻟﻴﻜﻥ:
Student Std = new Student { ID = 1, Age = 15,
;} "Name = "Ahmad
ﻓﺈﻥ ﻤﺤﺎﻭﻟﺔ ﺭﺒﻁ ﺃﻱ ﺤﻘل ﺨﺎﺹ ﺒﺎﻟﻜﺎﺌﻥ ) Stdﻭﻟﻴﻜﻥ ﺍﻟﺤﻘل (Nameﺒﺄﻴﺔ ﺃﺩﺍﺓ ﺴﺘﺅﺩﻱ ﺇﻟـﻰ
ﺤﺩﻭﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ:
;)"TxtId.DataBindings.Add("Text", Std, "Name
ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺘﻐﻴﻴﺭ ﺍﻟﺤﻘﻭل ﺍﻟﻌﺎﻤﺔ ﻓﻲ ﻓﺌﺔ ﺍﻟﻁﺎﻟﺏ ﻟﺘﺼﻴﺭ ﺨﺼﺎﺌﺹ ..ﻜل ﻤﺎ ﻋﻠﻴﻙ ﻫﻭ ﺍﺴﺘﺨﺩﺍﻡ
ﺍﻟﺨﺼﺎﺌﺹ ﺫﺍﺘﻴﺔ ﺍﻟﺘﻌﺭﻴﻑ Auto Implemented Propertiesﺍﻟﺘﻲ ﻗﺩﻤﺘﻬﺎ ﺴـﻲ ﺸـﺎﺭﺏ
٢٠٠٨ﻜﺎﻟﺘﺎﻟﻲ:
class Student
{
};public int ID {get;set
};public int Age {get;set
} ;public string Name { get; set
}
٣٧١
ﺍﻵﻥ ﻟﻭ ﺠﺭﺒﺕ ﺭﺒﻁ ﺃﻱ ﺨﺎﺼﻴﺔ ﻤﻥ ﺨﺼﺎﺌﺹ ﺍﻟﻜﺎﺌﻥ ،Stdﻓﺴﻴﻌﻤل ﻜل ﺸـﻲﺀ ﻋﻠـﻰ ﻤـﺎ
ﻴﺭﺍﻡ ..ﻭﺍﻟﻤﺸﺭﻭﻉ BindingToObjectﻴﺭﻴﻙ ﺍﻟﻜﻭﺩ ﺍﻟﻜﺎﻤل ﻟﺭﺒﻁ ﺨﺼﺎﺌﺹ ﻓﺌـﺔ ﺍﻟﻁﺎﻟـﺏ
ﺒﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺼﻭﺹ ..ﻭﻋﻠﻴﻙ ﻋﻨﺩ ﻓﺤﺹ ﻫﺫﺍ ﺍﻟﻤﺸﺭﻭﻉ ﺃﻥ ﺘﻼﺤﻅ ﻤﺎ ﻴﻠﻲ:
-١ﺃﻥ ﺃﻱ ﺘﻐﻴﻴﺭ ﺘﺠﺭﻴﻪ ﻋﻠﻰ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺼﻭﺹ ﺴﻴﺅﺜﺭ ﻋﻠﻰ ﺍﻟﻜﺎﺌﻥ ﺍﻷﺼﻠﻲ .. Stdﻭﻟﻭ
ﺠﺭﺒﺕ ﺘﻐﻴﻴﺭ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻁﺎﻟﺏ ﻓﻲ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺼﻭﺹ ﻭﻀﻐﻁ ﺍﻟﺯﺭ "ﺒﻴﺎﻨﺎﺕ ﺍﻟﻁﺎﻟـﺏ"
ﻓﺴﺘﻌﺭﺽ ﺍﻟﺭﺴﺎﻟﺔ ﺍﻟﻘﻴﻡ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺼﻭﺹ ،ﺭﻏﻡ ﺃﻥ ﺍﻟﻜﻭﺩ ﺍﻟﻤﻜﺘـﻭﺏ
ﻓﻲ ﺤﺩﺙ ﻀﻐﻁ ﺍﻟﺯﺭ ﻴﻌﺭﺽ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺘﻐﻴﺭ .Std
-٢ﺇﺫﺍ ﺤﺎﻭﻟﺕ ﺃﻥ ﺘﻜﺘﺏ ﻗﻴﻤﺔ ﺨﺎﻁﺌﺔ ﻓﻲ ﺃﻱ ﻤﺭﺒﻊ ﻨﺹ ﻜﻜﺘﺎﺒﺔ ﺤﺭﻭﻑ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ
ﺍﻟﺨﺎﺹ ﺒﺭﻗﻡ ﺍﻟﻁﺎﻟﺏ ،ﻓﺴﻴﺘﻡ ﺇﻟﻐﺎﺅﻫﺎ ﺒﻤﺠﺭﺩ ﻤﻐﺎﺩﺭﺘﻪ ..ﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﺘﻘﻨﻴـﺔ ﺍﻟـﺭﺒﻁ
ﺘﺠﻴﺯ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﻠﻘﺎﺌﻴﺎ ﻗﺒل ﻤﻐﺎﺩﺭﺓ ﺍﻷﺩﺍﺓ ،ﻓﺈﺫﺍ ﻜﺎﻨﺕ ﺴﺘﺴﺒﺏ ﺨﻁﺄ ﻋﻨﺩ ﻭﻀـﻌﻬﺎ ﻓـﻲ
ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺘﻡ ﺇﻟﻐﺎﺀ ﺍﻟﻘﻴﻤﺔ ﻤﻥ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ﻭﺇﻋﺎﺩﺘﻪ ﺇﻟﻰ ﻗﻴﻤﺘﻪ ﺍﻟﺴﺎﺒﻘﺔ.
ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺃﻴﻀﺎ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻜﺎﺌﻨﺎﺕ ﻤﻌﻘﺩﺓ ،ﻤﺜل ﻤﺼﻔﻭﻓﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻨﺎﺼﺭ ﻤﻥ ﻨﻭﻉ ﻓﺌـﺔ
ﺍﻟﺘﻠﻤﻴﺫ ،ﺃﻭ ﻜﺎﺌﻨﺎﺕ ﺃﻜﺜﺭ ﺘﻌﻘﻴﺩﺍ ﻤﺜل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺠﺩﺍﻭل ،ﺒﻜـل ﻤﻨﻬـﺎ
ﺃﻋﻤﺩﺓ ﻴﻌﺘﺒﺭ ﻜل ﻋﻤﻭﺩ ﻤﻨﻬﺎ ﻤﺼﻔﻭﻓﺔ )ﻷﻥ ﺒﻪ ﺼﻔﻭﻓﺎ( ﻭﻴﺼﻠﺢ ﻜﻌﻨﺼﺭ ﺒﻴﺎﻨـﺎﺕ ..ﻭﺍﻟﻤﺜـﺎل
ﺍﻟﺘﺎﻟﻲ ﻴﻨﺸﺊ ﻜﺎﺌﻥ ﺍﺭﺘﺒﺎﻁ ﻭﻴﻀﻴﻔﻪ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﺭﺘﺒﺎﻁﺎﺕ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ،ﻟﻴﺠﻌﻠﻪ ﻴﻌﺭﺽ ﺍﺴـﻡ
ﺍﻟﻜﺘﺎﺏ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﺠﺩﻭل ﺍﻟﻜﺘﺏ:
Binding B = TextBox1.DataBindings.Add("Text",
;)"Ds, "Books.Book
ﻭﻴﻤﻜﻥ ﻓﻌل ﻨﻔﺱ ﺍﻟﺸﻲﺀ ﺃﻴﻀﺎ ﺒﺎﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ:
Binding B = TextBox1.DataBindings.Add("Text",
;)"Ds.Tables["Books"], "Book
ﻭﻴﺭﻴﻙ ﺍﻟﻤﺸﺭﻭﻉ BindingTextBoxﻤﺜﺎﻻ ﻁﺭﻴﻔﺎ ﻋﻠﻰ ﻫﺫﺍ ،ﺤﻴﺙ ﺴـﻨﺠﻌل ﻤﺭﺒـﻊ ﻨـﺹ
ﻴﻌﺭﺽ ﺍﺴﻡ ﺍﻟﻜﺘﺎﺏ ﺍﻟﺤﺎﻟﻲ ،ﻭﻤﺭﺒﻊ ﻨﺹ ﺁﺨﺭ ﻴﻌﺭﺽ ﺍﺴﻡ ﻤﺅﻟﻔﻪ ..ﻭﺴﻨﻌﺭﺽ ﺠﺩﻭل ﺍﻟﻜﺘـﺏ
ﻜﻠﻪ ﻓﻲ ﺠﺩﻭل ﻋﺭﺽ DataGridViewﺍﻟﺫﻱ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻁﺭﻴﻘﺔ ﺭﺒﻁﻪ ﻻﺤﻘﺎ.
٣٧٢
ﺍﻟﺠﻤﻴل ﻓﻲ ﺍﻷﻤﺭ ﺃﻥ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻜﻠﻤﺎ ﺍﻨﺘﻘل ﻤﻥ ﺼﻑ ﺇﻟﻰ ﺁﺨﺭ ﻓﻲ ﺠﺩﻭل ﺍﻟﻌـﺭﺽ ،ﻴﻌـﺭﺽ
ﻤﺭﺒﻌﺎ ﺍﻟﻨﺹ ﺍﺴﻡ ﺍﻟﻜﺘﺎﺏ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻫﺫﺍ ﺍﻟﺼﻑ ﻭﺍﺴﻡ ﻤﺅﻟﻔﻪ ﺘﻠﻘﺎﺌﻴﺎ ،ﻭﺒﺩﻭﻥ ﺃﻥ ﻨﻜﺘـﺏ ﺃﻱ
ﻜﻭﺩ! ..ﺍﻟﺴﺒﺏ ﻓﻲ ﻫﺫﺍ ﺃﻥ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻴﻐﻴﺭ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﻜـﺎﺌﻥ ﺍﻻﺭﺘﺒـﺎﻁ ،ﻓﻴﻘـﻭﻡ
ﺘﻠﻘﺎﺌﻴﺎ ﺒﺘﺤﺩﻴﺙ ﺍﻟﻘﻴﻡ ﺍﻟﻤﻌﺭﻭﻀﺔ ﻓﻲ ﺠﻤﻴﻊ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﻪ! ..ﻟﻜﻥ ﻟﻜـﻲ ﺘﻌﻤـل ﻫـﺫﻩ
ﺍﻟﻁﺭﻴﻘﺔ ،ﻴﺠﺏ ﺃﻥ ﻴﻜﻭﻥ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺭﺘﺒﻁ ﺒﻪ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻫﻭ ﻨﻔﺴﻪ ﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ
ﻤﺭﺒﻌﻲ ﺍﻟﻨﺹ ..ﻫﻜﺫﺍ ﻤﺜﻼ:
;DataGridView1.DataSource = Ds
;"DataGridView1.DataMember = "Books
;)"TxtBook.DataBindings.Add("Text", Ds, "Books.Book
;)"TxtAuthor.DataBindings.Add("Text", Ds, "Books.Author
ﺃﻭ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﻜﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ﻟﻼﺨﺘﺼﺎﺭ:
;DataGridView1.DataSource = Ds.Books
;)"TxtBook.DataBindings.Add("Text", Ds.Books, "Book
;)"TxtAuthor.DataBindings.Add("Text", Ds.Books, "Author
ﻟﻜﻥ ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻟﻥ ﻴﺠﻌل ﺍﻟﺒﺭﻨﺎﻤﺞ ﻴﻌﻤل ﺒﺸﻜل ﺼﺤﻴﺢ ،ﻷﻥ ﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻭل ﺍﻟﻌـﺭﺽ
)ﻭﻫﻭ (Ds.Booksﻤﺨﺘﻠﻑ ﻋﻥ ﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ﻤﺭﺒﻌﻲ ﺍﻟﻨﺹ )ﻭﻫﻭ :(Ds
;DataGridView1.DataSource = Ds.Books
;)"TxtBook.DataBindings.Add("Text", Ds, "Books.Book
;)"TxtAuthor.DataBindings.Add("Text", Ds, "Books.Author
٣٧٣
ﻜﺫﻟﻙ ﻓﺈﻥ ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﺃﻴﻀﺎ ﻟﻥ ﻴﺠﻌل ﺍﻟﺒﺭﻨﺎﻤﺞ ﻴﻌﻤل ﺒﺸﻜل ﺼﺤﻴﺢ ،ﻷﻥ ﻤﺼـﺩﺭ ﺒﻴﺎﻨـﺎﺕ
ﺠــﺩﻭل ﺍﻟﻌــﺭﺽ )ﻭﻫــﻭ (Dsﻤﺨﺘﻠــﻑ ﻋــﻥ ﻤﺼــﺩﺭ ﺒﻴﺎﻨــﺎﺕ ﻤﺭﺒﻌــﻲ ﺍﻟــﻨﺹ
)ﻭﻫﻭ :(Ds.Books
;DataGridView1.DataSource = Ds
;"DataGridView1.DataMember = "Books
;)"TxtBook.DataBindings.Add("Text", Ds.Books, "Book
;)"TxtAuthor.DataBindings.Add("Text", Ds.Books, "Author
ﺍﻷﺩﺍﺓ :Control
ﺘﻌﻴﺩ ﺍﻷﺩﺍﺓ ﺍﻟﺘﻲ ﺘﻨﺘﻤﻲ ﺇﻟﻴﻬﺎ ﻤﺠﻤﻭﻋﺔ ﺍﻻﺭﺘﺒﺎﻁﺎﺕ ﺍﻟﺤﺎﻟﻴﺔ.
٣٧٤
ﻓﺌﺔ ﺍﻻﺭﺘﺒﺎﻁ Binding Class
ﺘﻘﻭﻡ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺒﺭﺒﻁ ﻜﺎﺌﻥ ﻴﻌﻤل ﻜﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ،ﺒﺈﺤﺩﻯ ﺍﻷﺩﻭﺍﺕ ،ﺒﺤﻴـﺙ ﻴﺄﺨـﺫ ﻋﻨﺼـﺭ
ﺍﻟﻌﺭﺽ ﻓﻲ ﺍﻷﺩﺍﺓ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﻜﺎﺌﻥ ﺘﻠﻘﺎﺌﻴﺎ ،ﻭﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺃﺤﺩﻫﻤﺎ ﻜﻠﻤﺎ ﺘﻐﻴـﺭﺕ
ﻗﻴﻤﺔ ﺍﻵﺨﺭ.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻨﻔﺱ ﺼﻴﻎ ﺍﻟﻭﺴﻴﻠﺔ ،ControlBindingsCollection.Addﻤﺎ ﻋـﺩﺍ
ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺍﻟﺘﻲ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﺭﺘﺒﺎﻁ .Binding
ﺍﻓﺘﺭﺽ ﺃﻥ ﻟﺩﻴﻨﺎ ﻤﺼﻔﻭﻓﺔ ﺃﻋﺩﺍﺩ ﺼﺤﻴﺤﺔ ﻤﻌﺭﻓﺔ ﻋﻠﻰ ﻤﺴﺘﻭﻯ ﺍﻟﻨﻤﻭﺫﺝ ﻜﺎﻟﺘﺎﻟﻲ:
;} int[] A = { 1, 2, 3, 4
ﺴﻨﻌﺭﻑ ﺍﻵﻥ ﻜﺎﺌﻨﺎ ﻴﺭﺒﻁ ﺍﻟﺨﺎﺼ ﻴﺔ Textﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺒﻌﻨﺎﺼﺭ ﻫﺫﻩ ﺍﻟﻤﺼﻔﻭﻓﺔ:
;)"" var B = new Binding("Text", A,
;)TextBox1.DataBindings.Add(B
ﻻﺤﻅ ﺃﻥ ﺤﺩﺙ ﺍﻹﻨﺸﺎﺀ ﺍﺴﺘﻘﺒل ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ:
-ﺍﺴﻡ ﺨﺎﺼﻴﺔ ﺍﻷﺩﺍﺓ ﻭﻫﻲ ﻫﻨﺎ "."Text
-ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻫﻲ ﻫﻨﺎ ﺍﻟﻤﺼﻔﻭﻓﺔ .A
-ﺍﺴﻡ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ﻭﻗﺩ ﺘﺭﻜﻨﺎﻩ ﻓﺎﺭﻏﺎ ﻟﺭﺒﻁ ﺍﻟﻜﺎﺌﻥ ﻨﻔﺴﻪ )ﺍﻟﻤﺼﻔﻭﻓﺔ( ..ﻟﻜـﻥ ﻟـﻭ ﻜﻨـﺎ
ﻨﺘﻌﺎﻤل ﻤﻊ ﺴﺠل ﺍﻟﻁﺎﻟﺏ Student Structureﻤـﺜﻼ )ﻜﻤـﺎ ﻓﻌﻠﻨـﺎ ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ
،(BindingToArrayﻓﻴﻤﻜﻥ ﺃﻥ ﻨﺭﺴل ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﺍﺴﻡ ﺃﻱ ﺤﻘـل ﻤـﻥ ﺤﻘﻭﻟـﻪ،
ﻜﺎﻻﺴﻡ " "Nameﺃﻭ ﺍﻟﻌﻤﺭ " .."Ageﺃﻭ ﻏﻴﺭ ﺫﻟﻙ.
٣٧٥
ﺍﻷﺩﺍﺓ :Control
ﺘﻌﻴﺩ ﺍﻷﺩﺍﺓ Controlﺍﻟﺘﻲ ﻴﻨﺘﻤﻲ ﺇﻟﻴﻬﺎ ﺍﻻﺭﺘﺒﺎﻁ ﺍﻟﺤﺎﻟﻲ.
٣٧٦
ﻻ ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ﻋﻨـﺩ ﺘﻐﻴـﺭ ﻗﻴﻤـﺔ Never
ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ.
OnPropertyChangedﻴﺘﻡ ﺘﺤﺩﻴﺙ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ﻓﻭﺭ ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﻋﻨﺼـﺭ
ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻫﺫﻩ ﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ.
ﻻ ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻫﺫﺍ ﻴﻌﻨﻲ ﺃﻥ ﺃﻱ ﺘﻐﻴﻴﺭ ﻴﺤﺩﺙ ﻓﻲ Never
ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ )ﻜﺄﻥ ﻴﻜﺘﺏ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﻤﺜﻼ( ﻟـﻥ
ﻴﺅﺜﺭ ﻋﻠﻰ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻴﺘﻡ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻤﺠـﺭﺩ ﺘﻐﻴﻴـﺭ ﻗﻴﻤـﺔ ﻋﻨﺼـﺭ OnProperty
ﺍﻟﻌﺭﺽ ..ﻓﻤﺜﻼ ﻟﻭ ﻜﺘﺏ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ،ﻓﺈﻥ ﻤﺎ ﻜﺘﺒـﻪ Changed
ﻴﻭﻀﻊ ﻓﻭﺭﺍ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ.
OnValidationﻻ ﻴﺘﻡ ﻨﻘل ﺍﻟﺘﻐﻴﻴﺭ ﻤﻥ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ﺇﻟﻰ ﻋﻨﺼـﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺇﻻ
ﻋﻨــﺩ ﺍﻨﻁــﻼﻕ ﺤــﺩﺙ ﺘﻤــﺎﻡ ﺍﻟﺘﺤﻘــﻕ ﻤــﻥ ﺍﻟﺼــﺤﺔ
،Control.Validatedﻭﺫﻟﻙ ﻋﻨـﺩ ﻤﻐـﺎﺩﺭﺓ ﺍﻷﺩﺍﺓ ﺃﻭ ﻋـﺭﺽ
ﻋﻨﺼﺭ ﺁﺨﺭ ﻤﻥ ﻋﻨﺎﺼﺭ ﺍﻟﻜﺎﺌﻥ )ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻜـﺎﺌﻥ ﻴﺤﺘـﻭﻱ ﻋﻠـﻰ
ﻤﺠﻤﻭﻋﺔ ﻤﻥ ﺍﻟﻌﻨﺎﺼﺭ ﻤﺜل ﺍﻟﻤﺼـﻔﻭﻓﺎﺕ( ..ﻫـﺫﻩ ﻫـﻲ ﺍﻟﻘﻴﻤـﺔ
ﺍﻻﻓﺘﺭﺍﻀﻴﺔ.
٣٧٧
ﺍﻟﻘﻴﻤﺔ ﺍﻟﻔﺎﺭﻏﺔ ﻟﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ :DataSourceNullValue
ﺘﺤﺩﺩ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﺴﺘﻭﻀﻊ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺇﺫﺍ ﻜﺎﻨـﺕ ﻟﻌﻨﺼـﺭ ﺍﻟﻌـﺭﺽ ﺍﻟﻘﻴﻤـﺔ
..Nothingﻻﺤﻅ ﺃﻥ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﺴـﺘﻜﻭﻥ ﺒـﻼ ﻓﺎﺌـﺩﺓ ﺇﺫﺍ ﻜﺎﻨـﺕ ﻟﻠﺨﺎﺼـﻴﺔ
DataSourceUpdateModeﺍﻟﻘﻴﻤﺔ .None
٣٧٨
ﻗﺭﺍﺀﺓ ﺍﻟﻘﻴﻤﺔ :ReadValue
ﺘﺠﺒﺭ ﺍﻷﺩﺍﺓ ﻋﻠﻰ ﻋﺭﺽ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﺘﻨﺴﻴﻕ :Format
ﻴﻨﻁﻠﻕ ﻗﺒل ﻜﺘﺎﺒﺔ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ،ﻟﻴﺴﻤﺢ ﻟﻙ ﺒﺘﻨﺴﻴﻕ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻗﺒل ﺃﻥ ﺘﻌﺭﻀﻬﺎ ﺍﻷﺩﺍﺓ ..ﻭﺍﻟﻤﻌﺎﻤل eﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨـﻭﻉ ،ConvertEventArgs
ﻭﻫﻭ ﻴﻤﺘﻠﻙ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
ﺘﺤﻭﻴل :Parse
ﻴﻨﻁﻠﻕ ﻋﻨﺩﻤﺎ ﺘﺘﻐﻴﺭ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ..ﻓﺈﺫﺍ ﻜﻨﺕ ﻗـﺩ ﻏﻴـﺭﺕ ﺘﻨﺴـﻴﻕ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺤﺩﺙ ،Formatﻓﺎﺴﺘﺨﺩﻡ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻻﺴﺘﺨﻼﺹ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﺼﻠﻴﺔ ﻭﺇﻋﺎﺩﺘﻬﺎ
ﺇﻟﻰ ﺍﻟﻨﻭﻉ ﺍﻟﻤﻨﺎﺴﺏ ﻟﻭﻀﻌﻬﺎ ﻓﻲ ﺨﺎﺼﻴﺔ ﺍﻟﻜﺎﺌﻥ.
٣٧٩
ﻭﺍﻟﻤﻌﺎﻤل eﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻤﺎﺜل ﻟﺫﻟﻙ ﺍﻟﺨﺎﺹ ﺒﺎﻟﺤﺩﺙ ﺍﻟﺴﺎﺒﻕ ..ﺍﻨﻅـﺭ ﻜﻴـﻑ ﻨﺴـﺘﻌﻴﺩ
ﺍﻟﺘﺎﺭﻴﺦ ﻤﻥ ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﻨﺴﻘﻨﺎﻩ ﻓﻲ ﺍﻟﺤﺩﺙ ﺍﻟﺴﺎﺒﻕ:
;)) (e.Value = Date.Parse(e.Value.ToString
٣٨٠
ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﻑ ﻜﺎﺌﻥ ﺭﺒﻁ Binding Objectﺒﻴﻥ ﺍﻟﺨﺎﺼ ﻴﺔ Textﻟﻤﺭ ﺒﻊ ﻨﺹ ،ﻭﺒﻴﻥ
ﺍﻟﻌﻤﻭﺩ Bookﻓﻲ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ :Ds
;)"var B = new Binding("Text", Ds, "Books.Book
ﻫﺫﺍ ﺍﻻﺭﺘﺒﺎﻁ ﻟﻥ ﻴﺄﺨﺫ ﺤﻴـﺯﺍ ﻤـﻥ ﺍﻟﺘﻨﻔﻴـﺫ ﺇﻻ ﺇﺫﺍ ﺃﻀـﻔﻨﺎﻩ ﺇﻟـﻰ ﻤﺠﻤﻭﻋـﺔ ﺍﻻﺭﺘﺒﺎﻁـﺎﺕ
ﺼﺔ ﺒﻤﺭﺒﻊ ﺍﻟﻨﺹ ،ﻜﺎﻟﺘﺎﻟﻲ:
DataBindingsﺍﻟﺨﺎ
;)TextBox1.DataBindings.Add (B
ﻭﺴﻨﺭﻯ ﻻﺤﻘﺎ ﻜﻴﻑ ﻨﺘﺤﺭﻙ ﺒﺄﻨﻔﺴﻨﺎ ﻋﺒﺭ ﺼﻔﻭﻑ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﺍﻟﻤﺨﺘﻠﻔـﺔ ،ﻟﻨﻌـﺭﺽ ﺃﺴـﻤﺎﺀ
ﺍﻟﻜﺘﺏ ﺍﻟﻤﺨﺘﻠﻔﺔ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ..ﻭﻋﻠﻰ ﻜل ﺤـﺎل ،ﺴـﺘﺠﺩ ﻫـﺫﺍ ﻤﻁﺒﻘـﺎ ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ
.ViewAndEditBooks
٣٨١
ﺴﺠل ﻤﻌﻠﻭﻤﺎﺕ ﻋﻨﺼﺭ ﺍﻟﺭﺒﻁ
BindingMemberInfo Structure
ﻴﺤﺘﻭﻱ ﻫﺫﺍ ﺍﻟﺴﺠل ﻋﻠﻰ ﻤﻌﻠﻭﻤﺎﺕ ﻋﻥ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻻ ﺘﺘﻀﺢ ﻓﺎﺌﺩﺓ ﻫﺫﺍ ﺍﻟﺴـﺠل ﻋﻨـﺩ
ﺍﻻﺭﺘﺒﺎﻁ ﺒﻜﺎﺌﻨﺎﺕ ﺒﺴﻴﻁﺔ ،ﻭﺇﻨﻤﺎ ﺘﺘﻀﺢ ﻋﻨﺩ ﺍﻻﺭﺘﺒﺎﻁ ﻤﻊ ﻜﺎﺌﻨـﺎﺕ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﻜﺎﺌﻨـﺎﺕ
ﻤﺘﺩﺍﺨﻠﺔ ..ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻋﻠﻴﻙ ﺇﺭﺴﺎل ﻤﺴﺎﺭ ﺍﻟﻜﺎﻤل ﻻﺴﻡ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘـﻲ ﺘﻌﻤـل ﻜﻌﻨﺼـﺭ
ﺒﻴﺎﻨﺎﺕ ،ﺇﻟﻰ ﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ ..ﻤﺜﺎل:
;)"var Bmi = BindingMemberInfo ("Books.Book
ﺤﻴﺙ Booksﻫﻭ ﺍﺴﻡ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ،ﻭ Bookﻫﻭ ﺍﻟﺤﻘل ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﻋـﺭﺽ ﻗﻴﻤﺘـﻪ ﻓـﻲ
ﺍﻷﺩﺍﺓ ..ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﺍﻟﻤﺴﺎﺭ ﻻ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﺴﻡ ﺍﻟﻜﺎﺌﻥ )ﻭﻫﻭ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ Dsﻓـﻲ
ﻤﺜﺎﻟﻨﺎ ﻫﺫﺍ( ،ﻓﻬﺫﻩ ﺍﻟﻤﻌﻠﻭﻤﺔ ﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻜﺎﺌﻥ ﺍﻟﺭﺒﻁ Bindingﺍﻟﺫﻱ ﻴﻨﺘﻤـﻲ ﺇﻟﻴـﻪ ﺍﻟﺴـﺠل
..BindingMemberInfoﻭﻴﻤﻜﻨﻙ ﺃﻴﻀﺎ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻨﺴﺨﺔ ﻤﻥ ﻫﺫﺍ ﺍﻟﺴﺠل ﺒﺎﺴـﺘﺨﺩﺍﻡ
ﺍﻟﺨﺎﺼﻴﺔ .Binding.BindingMemberInfo
٣٨٢
ﻤﺴﺎﺭ ﺍﻟﺭﺒﻁ :BindingPath
ﺘﻌﻴﺩ ﻤﺴﺎﺭ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ )ﺒﺩﻭﻥ ﺍﺴﻡ ﺍﻟﺨﺎﺼﻴﺔ( ..ﻭﻟﻭ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﻤـﻊ
ﺍﻟﻤﺜﺎل ﺍﻟﺴﺎﺒﻕ ،ﻓﺴﺘﻌﻴﺩ ﺍﻟﻨﺹ .Books
ﻭﻟﻭ ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﺒﺴﻴﻁﺎ ﻭﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻴﺱ ﻟﻪ ﻤﺴﺎﺭ ،ﻓﻔﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﺘﻜﻭﻥ ﺍﻟﻘﻴﻤـﺔ
ﺍﻟﻌﺎﺌﺩﺓ ﻫﻲ ﺍﺴﻡ ﺍﻟﺨﺎﺼﻴﺔ ،ﻤﺜل Widthﻟﻭ ﻜﺎﻥ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻜﺎﺌﻥ ﺍﻟﺤﺠﻡ .Size
٣٨٣
ﻓﺌﺔ ﻤﺤﺘﻭﻯ ﺍﻟﺭﺒﻁ BindingContext Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﻭﺍﺠﻬﺔ ﺍﻟﻤﺠﻤﻭﻋﺔ ،ICollectionﻭﻫﻲ ﺘﻌﻤل ﻜﻤﺠﻤﻭﻋﺔ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ ﺘﺤﺘـﻭﻱ
ﻋﻠﻰ ﻜﺎﺌﻨﺎﺕ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ BindingManagerBaseﺍﻟﺨﺎﺼﺔ ﺒﺄﺩﺍﺓ ﻤﻌﻴﻨﺔ ..ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ
ﺍﻟﻔﺌﺔ BindingManagerBaseﺒﻌﺩ ﻗﻠﻴل.
ﻭﻴﻤﻜﻨــﻙ ﺍﻟﺤﺼــﻭل ﻋﻠــﻰ ﻨﺴــﺨﺔ ﻤــﻥ ﻫــﺫﻩ ﺍﻟﻔﺌــﺔ ﺒﺎﺴــﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼــﻴﺔ
..Control.BindingContextﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﻴﺩ ﺇﻟﻴﻙ ﻤﺠﻤﻭﻋﺔ ﺘﺤﺘﻭﻱ ﻋﻠـﻰ ﻜﺎﺌﻨـﺎﺕ
ﺃﺴﺎﺱ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ ﻟﻠﻨﻤﻭﺫﺝ:
;BindingContext BC = this.BindingContext
ﻻﺤﻅ ﺃﻥ ﻤﺠﻤﻭﻋﺔ ﻤﺤﺘﻭﻯ ﺍﻟﺭﺒﻁ BindingContextﺍﻟﺨﺎﺼﺔ ﺒﺎﻷﺩﺍﺓ ﺍﻟﺤﺎﻭﻴﺔ ﺘﺤﺘﻭﻱ ﻋﻠـﻰ
ﻜل ﻜﺎﺌﻨﺎﺕ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ BindingManagerBaseﺍﻟﺨﺎﺼﺔ ﺒﻜل ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻋﻠـﻰ
ﻫﺫﻩ ﺍﻷﺩﺍﺓ ﺍﻟﺤﺎﻭﻴﺔ ..ﻫﺫﺍ ﻴﻌﻨﻲ ﺃﻥ ﻤﺤﺘﻭﻯ ﺍﻟﺭﺒﻁ BCﻓﻲ ﺍﻟﻤﺜﺎل ﺍﻟﺴـﺎﺒﻕ ﺴـﻴﺤﺘﻭﻱ ﻋﻠـﻰ
ﻜﺎﺌﻨﺎﺕ ﺃﺴﺎﺱ ﺍﻟﺭﺒﻁ ﻟﻜل ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﻭﻀﻭﻋﺔ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ )ﺒﺸﺭﻁ ﺃﻥ ﺘﻜـﻭﻥ ﺩﺍﺨﻠـﺔ ﻓـﻲ
ﺍﺭﺘﺒﺎﻁﺎﺕ( ..ﻫﺫﺍ ﻴﻔﻴﺩﻙ ﻓﻲ ﺘﺴﻬﻴل ﻜﺘﺎﺒﺔ ﺍﻟﻜﻭﺩ ﻋﻨﺩﻤﺎ ﺘﻭﺠﺩ ﺍﻟﻌﺩﻴﺩ ﻤـﻥ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﺭﺘﺒﻁـﺔ
ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ.
ﺍﻟﻌﻨﺼﺭ :Item
ﻫﺫﻩ ﻫﻲ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ،ﻭﻫﻲ ﺘﻌﻴﺩ ﻤـﺩﻴﺭ ﺍﻟـﺭﺒﻁ BindingManagerBase
ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺘﺒﻌﺎ ﻟﻠﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﺭﺴﻠﺔ ..ﻭﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺼﻴﻐﺘﺎﻥ:
.١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﻟﻬﺎ ﻤﻌﺎﻤل ﻤﻥ ﺍﻟﻨﻭﻉ ،Objectﻴﺴﺘﻘﺒل ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺘﺭﻴﺩ
ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ ﺍﻟﺨﺎﺹ ﺒﻪ ..ﻭﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻴﻭﻗﻑ ﺍﻻﺭﺘﺒﺎﻁـﺎﺕ ﺒـﻴﻥ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ Dsﻭﻜل ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ:
;) (this.BindingContext[Ds].SuspendBinding
٣٨٤
.٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﻨﺼﻲ ،ﻴﺴﺘﻘﺒل ﻤﺴـﺎﺭ ﻋﻨﺼـﺭ
ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻫﺫﺍ ﻤﻔﻴﺩ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻌﺩﻴﺩ ﻤـﻥ ﻤﺼـﺎﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻭﺘﺭﻴﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻭﺍﺤﺩ ﻤﻨﻬﺎ ﻓﻘـﻁ ..ﻭﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ BindingToDataSet
ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ:
;]"var Bm = this.BindingContext[DsBooks, "Books
ﻻﺤﻅ ﺃﻥ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ Bmﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻴﺘﻌﺎﻤل ﻤﻊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ،ﻟﻬﺫﺍ ﻨﺴﺘﻁﻴﻊ
ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼﻴﺔ Bm.Positionﻟﻠﺘﺤﻜﻡ ﻓﻲ ﺍﻟﻜﺘـﺎﺏ ﺍﻟﻤﻌـﺭﻭﺽ ﺤﺎﻟﻴـﺎ ﻓـﻲ
ﺍﻷﺩﻭﺍﺕ ..ﺒﻴﻨﻤﺎ ﻟﻭ ﺍﺴﺘﺨﺩﻤﺕ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ:
;]var Bm = this.BindingContext[DsBooks
ﻓﺴﺘﺤﺼل ﻋﻠﻰ ﻤﺩﻴﺭ ﺭﺒﻁ ﻴﺘﻌﺎﻤل ﻤﻊ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻨﻔﺴـﻬﺎ ،ﻭﻨﻅـﺭﺍ ﻷﻨﻬـﺎ
ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻌﺩﻴﺩ ﻤﻥ ﺍﻟﻘﻭﺍﺌﻡ ﺍﻟﺩﺍﺨﻠﻴﺔ )ﻤﺜل ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺠـﺩﺍﻭل ﻭﻤﺠﻤﻭﻋـﺔ
ﺍﻟﻌﻼﻗﺎﺕ ﻭﻏﻴﺭﻫﻤﺎ( ،ﻓﻠﻥ ﻴﺴﺘﻁﻴﻊ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ Bmﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺍﻟﺘﻌﺎﻤـل ﻤـﻊ
ﺠﺩﻭل ﺍﻟﻜﺘﺏ ،ﻭﺴﺘﺸﻴﺭ ﺍﻟﺨﺎﺼﻴﺔ Bm.Countﺇﻟﻰ ﺃﻥ ﻫﻨﺎﻙ ﻋﻨﺼﺭﺍ ﻭﺍﺤﺩﺍ ﻓﻘـﻁ
ﻓﻲ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ )ﻭﻫﻭ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻨﻔﺴﻬﺎ( ،ﻭﻟﻬﺫﺍ ﻟﻥ ﺘﺴﺘﻁﻴﻊ ﺍﻻﻨﺘﻘﺎل ﺇﻟـﻰ
ﺴﺠﻼﺕ ﺃﺨﺭﻯ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼﻴﺔ .Bm.Position
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺴﺎﺴﻴﺔ ﻤﺠﺭﺩﺓ ،Abstract Base Classﻭﻤﻨﻬﺎ ﺘﺸﺘﻕ ﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﻲ ﺘﻌﻤل ﻜﻤـﺩﻴﺭ
ﻟﻠﺭﺒﻁ ..ﻭﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﺎﻟﻴﺘﺎﻥ ﺘﺭﺜﺎﻥ ﻫﺫﻩ ﺍﻟﻔﺌﺔ:
.١ﻓﺌﺔ ﻤﺩﻴﺭ ﺍﻟﺘﺴﻠﺴل .CurrencyManager Class
.٢ﻓﺌﺔ ﻤﺩﻴﺭ ﺍﻟﺨﺎﺼﻴﺔ .PropertyManager Class
ﻭﻟﺘﻌﺭﻴﻑ ﻤﺘﻐﻴﺭ ﻤﻥ ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ ،ﺍﺴﺘﺨﺩﻡ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺘﺎﻟﻴﺔ:
;BindingManagerBase BM
ﻭﻟﻭﻀﻊ ﻨﺴﺨﺔ ﺠﺩﻴﺩﺓ ﻤﻥ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ ﻓـﻲ ﻫـﺫﺍ ﺍﻟﻤﺘﻐﻴـﺭ ،ﻴﻤﻜﻨـﻙ ﺍﺴـﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼـﻴﺔ
BindingManagerBaseﻤﻥ ﻜﺎﺌﻥ ﺍﻻﺭﺘﺒﺎﻁ ..Bindingﻤﺜﺎل:
;)"" var Bnd = TextBox1.DataBindings.Add("Text", Obj,
;BM = Bnd.BindingManagerBase
ﻻﺤﻅ ﺃﻥ ﻨﻭﻉ ﺍﻟﻤﺩﻴﺭ ﺍﻟﺫﻱ ﺴﻴﻭﻀﻊ ﻓﻲ ﺍﻟﻤﺘﻐﻴﺭ BMﻴﺘﻭﻗﻑ ﻋﻠﻰ ﻨﻭﻉ ﺍﻟﻜﺎﺌﻥ ..ﻓﻠـﻭ ﻜـﺎﻥ
ﻜﺎﺌﻨﺎ ﺒﺴﻴﻁﺎ ﻓﺴﻴﺤﺘﻭﻱ ﺍﻟﻤﺘﻐﻴﺭ BMﻋﻠﻰ ﻨﺴﺨﺔ ﻤﻥ ﻤﺩﻴﺭ ﺍﻟﺨﺎﺼـﻴﺔ PropertyManager
ﺍﻟﺫﻱ ﻴﺘﺤﻜﻡ ﺒﺎﻻﺭﺘﺒﺎﻁ ﺒﻌﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﺃﻤﺎ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﻤﺭﻜﺒﺎ ﻭﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﻤـﻥ
ﺍﻟﻌﻨﺎﺼﺭ ،ﻓﺴﻴﺤﺘﻭﻱ ﺍﻟﻤﺘﻐﻴﺭ BMﻋﻠﻰ ﻨﺴﺨﺔ ﻤﻥ ﻤـﺩﻴﺭ ﺍﻟﺘﺴﻠﺴـل CurrencyManager
ﺍﻟﺫﻱ ﻴﺘﺤﻜﻡ ﻓﻲ ﺭﺒﻁ ﻗﺎﺌﻤﺔ ﻋﻨﺎﺼﺭ ﺍﻟﻜﺎﺌﻥ.
ﺍﻻﺭﺘﺒﺎﻁﺎﺕ :Bindings
ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋـﺔ ﺍﻻﺭﺘﺒﺎﻁـﺎﺕ BindingsCollectionﺍﻟﺘـﻲ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﺠﻤﻴـﻊ
ﺍﻻﺭﺘﺒﺎﻁﺎﺕ ﺍﻟﺘﻲ ﺘﺸﺘﺭﻙ ﻓﻴﻬﺎ ﺍﻷﺩﺍﺓ ﺍﻟﺤﺎﻟﻴﺔ.
٣٨٦
ﺍﻟﻌﺩﺩ :Count
ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻤﺸﺘﺭﻜﺔ ﻓﻲ ﺍﻻﺭﺘﺒﺎﻁ ..ﻫﺫﺍ ﺍﻟﻌﺩﺩ ﺴﻴﻜﻭﻥ ﺩﺍﺌﻤﺎ ١ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻜـﺎﺌﻥ
ﺒﺴﻴﻁﺎ ﻭﺘﻡ ﺍﻻﺭﺘﺒﺎﻁ ﺒﺈﺤﺩﻯ ﺨﺼﺎﺌﺼﻪ ..ﺃﻤﺎ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﻤﻌﻘﺩﺍ ﻭﺘﻡ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻘﺎﺌﻤـﺔ
ﻋﻨﺎﺼﺭ ﻤﻭﺠﻭﺩﺓ ﺩﺍﺨﻠﻪ ،ﻓﺈﻥ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺘﻌﻴﺩ ﻋﺩﺩ ﻋﻨﺎﺼﺭ ﻫﺫﻩ ﺍﻟﻘﺎﺌﻤﺔ )ﻤﺜـل ﻋـﺩﺩ
ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺇﺫﺍ ﻜﺎﻥ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻌﻤﻭﺩ ﻓﻲ ﺃﺤﺩ ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ(.
ﺍﻟﻤﻭﻀﻊ :Position
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻤﻭﻀﻊ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺫﻱ ﺘﻌﺭﻀﻪ ﺍﻷﺩﺍﺓ ﺤﺎﻟﻴﺎ ..ﻫﺫﺍ ﻤﻔﻴﺩ ﻋﻨﺩ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻜـﺎﺌﻥ
ﻤﻌﻘﺩ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﻤﻥ ﺍﻟﻌﻨﺎﺼﺭ ،ﻓﻔﻲ ﺒﺩﺀ ﺍﻻﺭﺘﺒﺎﻁ ﺴﺘﻭﻀﻊ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﻌـﺭﺽ
ﻗﻴﻤﺔ ﺃﻭل ﻋﻨﺼﺭ ﻓﻲ ﻫﺫﻩ ﺍﻟﻘﺎﺌﻤﺔ ،ﻭﻴﻤﻜﻨﻙ ﺒﻌﺩ ﻫﺫﺍ ﺃﻥ ﺘﺴـﺘﺨﺩﻡ ﺍﻟﺨﺎﺼـﻴﺔ Position
ﻟﻌﺭﺽ ﺃﻱ ﻋﻨﺼﺭ ﺁﺨﺭ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ..ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﺽ ﻓﻲ ﻤﺭﺒـﻊ ﺍﻟـﻨﺹ ﺜﺎﻟـﺙ
ﻜﺘﺎﺏ ﻓﻲ ﺠﺩﻭل ﺍﻟﻜﺘﺏ:
;)"Binding Bnd = new Binding("Text", Ds, "Books.Book
;)TxtBook.DataBindings.Add(Bnd
;Bnd.BindingManagerBase.Position = 2
ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻓﻲ ﺍﻟﺘﻁﺒﻴﻕ BindindSample2ﻟﻨﺘﻴﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺍﻟﺘﺤﺭﻙ
ﻋﺒﺭ ﺨﺎﻨﺎﺕ ﻤﺼﻔﻭﻓﺔ ﺍﻟﺘﻼﻤﻴﺫ ﻭﻋﺭﺽ ﺒﻴﺎﻨﺎﺕ ﻜل ﺘﻠﻤﻴﺫ ﻓﻲ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺼﻭﺹ ،ﻭﺫﻟـﻙ
ﺒﺎﺴﺘﺨﺩﺍﻡ ﺃﺯﺭﺍﺭ ﺍﻻﺘﺠﺎﻫﺎﺕ ﺃﺴﻔل ﺍﻟﻨﻤﻭﺫﺝ:
٣٨٧
ﺍﻟﻤﺭﻴﺢ ﻓﻲ ﺍﻷﻤﺭ ﺃﻨﻨﺎ ﻻ ﻨﺤﺘﺎﺝ ﺇﻟﻰ ﺘﻐﻴﻴﺭ ﺍﻟﻤﻭﻀﻊ ﻟﻜل ﻤﺭﺒﻊ ﻨﺹ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ،ﻓﻜـل
ﻤﺎ ﻋﻠﻴﻨﺎ ﻫﻭ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ ﺍﻟﺨﺎﺹ ﺒﻤﺼﻔﻭﻓﺔ ﺍﻟﺘﻼﻤﻴﺫ ﻤﻥ ﺨﻼل ﻤﺤﺘـﻭﻯ
ﺍﻟﺭﺒﻁ ﺍﻟﺨﺎﺹ ﺒﺎﻟﻨﻤﻭﺫﺝ ﻜﺎﻟﺘﺎﻟﻲ:
;]BindingManagerBase Bm = this.BindingContext[Std
ﻭﺒﻬﺫﺍ ﻴﺅﺩﻱ ﺘﻐﻴﻴﺭ ﻗﻴﻤـﺔ ﺍﻟﺨﺎﺼـﻴﺔ Bm.Positionﺇﻟـﻰ ﺘﻐﻴﻴـﺭ ﺍﻟﻌﻨﺼـﺭ ﺍﻟﺤـﺎﻟﻲ
ﺍﻟﻤﻌﺭﻭﺽ ﻓﻲ ﺠﻤﻴﻊ ﺃﺩﻭﺍﺕ ﺍﻟﻨﻤﻭﺫﺝ.
ﻜﻤﺎ ﻴﺘﻴﺢ ﺍﻟﺒﺭﻨﺎﻤﺞ BindindSample2ﻟﻠﻤﺴﺘﺨﺩﻡ ﻜﺘﺎﺒﺔ ﺭﻗﻡ ﺍﻟﺨﺎﻨﺔ ﻤﺒﺎﺸﺭﺓ ﻓﻲ ﻤﺭﺒـﻊ
ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﻴﺘﻭﺴﻁ ﺍﻷﺯﺭﺍﺭ )ﻭﺍﺴﻤﻪ ،(TxtPosﻭﻋﻨﺩﻤﺎ ﻴﻀـﻐﻁ Enterﻤـﻥ ﻟﻭﺤـﺔ
ﺍﻟﻤﻔﺎﺘﻴﺢ ﻴﺘﻡ ﻋﺭﺽ ﺍﻟﺘﻠﻤﻴﺫ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﻨﺔ ..ﻻﺤـﻅ ﺃﻥ ﺍﻟﺨﺎﺼـﻴﺔ Position
ﺘﺭﻓﺽ ﺃﻱ ﻤﻭﻀﻊ ﻏﻴﺭ ﺼﺤﻴﺢ ﺩﻭﻥ ﺃﻥ ﻴﺤﺩﺙ ﺨﻁﺎ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ..ﻟﻬﺫﺍ ﻟﻭ ﺠﺭﺒـﺕ ﺃﻥ
ﺘﻜﺘﺏ ﺍﻟﺭﻗﻡ ١٠ﻤﺜﻼ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﻭﺘﻀـﻐﻁ ،Enterﻓـﺈﻥ ﺍﻟﺨﺎﺼـﻴﺔ Position
ﺴﺘﻨﺘﻘل ﺘﻠﻘﺎﺌﻴﺎ ﺇﻟﻰ ﺁﺨﺭ ﺨﺎﻨﺔ ﻤﺴﻤﻭﺡ ﺒﻬﺎ ﻭﻫﻲ ﺍﻟﺨﺎﻨﺔ ﺭﻗﻡ ٤ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﺜﺎل.
ﺍﻟﺤﺎﻟﻲ :Current
ﺘﻌﻴﺩ ﺍﻟﻜﺎﺌﻥ Objectﺍﻟﻤﺭﺘﺒﻁ ﺤﺎﻟﻴﺎ ﺒﻌﻨﺼﺭ ﺍﻟﻌﺭﺽ ..ﻭﻓﻲ ﺤﺎﻟﺔ ﺍﻻﺭﺘﺒـﺎﻁ ﺒﻜـﺎﺌﻥ
ﺒﺴﻴﻁ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻋﺩﺓ ﺨﺼﺎﺌﺹ )ﻤﺜل ﻜﺎﺌﻥ ﺍﻟﺤﺠﻡ ،(Sizeﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫﺫﺍ
ﺍﻟﻜﺎﺌﻥ ،ﺃﻤﺎ ﻋﻨﺩ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻜﺎﺌﻥ ﻤﻌﻘﺩ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﻤﻥ ﺍﻟﻌﻨﺎﺼـﺭ ،ﻓـﺈﻥ ﻫـﺫﻩ
ﺍﻟﺨﺎﺼﻴﺔ ﺘﻌﻴﺩ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ )ﺍﻟﻤﻭﺠﻭﺩ ﻓـﻲ ﺍﻟﻤﻭﻀـﻊ ﺍﻟـﺫﻱ ﺘﺤـﺩﺩﻩ
ﺍﻟﺨﺎﺼﻴﺔ .(Position
٣٨٨
ﺇﻀﺎﻓﺔ ﺠﺩﻴﺩ :AddNew
ﺘﻀﻴﻑ ﻋﻨﺼﺭﺍ ﺠﺩﻴﺩ ﺇﻟﻰ ﻗﺎﺌﻤﺔ ﻋﻨﺎﺼﺭ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺨﻁـﺄ
ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻷﺩﺍﺓ ﻤﺭﺘﺒﻁﺔ ﺒﻜﺎﺌﻥ ﺒﺴﻴﻁ ﻻ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﺩﺍﺨﻠﻴـﺔ ،ﺃﻭ ﺇﺫﺍ
ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﻤﺼﻔﻭﻓﺔ ..ﻭﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻊ ﺼﻔﻭﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺘﻜـﻭﻥ ﺤﺎﻟـﺔ
ﺍﻟﺴﺠل ﺍﻟﺠﺩﻴﺩ ﻫﻲ ،RowState.Addedﻭﻟﻥ ﺘﺘﻡ ﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻻ ﻋﻨـﺩ
ﺘﺤﺩﻴﺜﻬﺎ.
٣٨٩
ﻤﻌﺭﻓﺔ ﺨﺼﺎﺌﺹ ﺍﻟﻌﻨﺼﺭ :GetItemProperties
ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﻭﺍﺼﻔﺎﺕ ﺍﻟﺨﺼﺎﺌﺹ PropertyDescriptorCollectionﺍﻟﺘﻲ ﺘﺼـﻑ
ﺨﺼﺎﺌﺹ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﺸﺘﺭﻜﺔ ﻓﻲ ﺍﻻﺭﺘﺒﺎﻁ ..ﻤﺜﻼ :ﻋﻨﺩ ﺍﻻﺭﺘﺒﺎﻁ ﺒﺤﻘل ﺍﺴﻡ ﺍﻟﻜﺘﺎﺏ ﻓﻲ
ﺠــﺩﻭل ﺍﻟﻜﺘــﺏ ،ﺴــﺘﺤﺘﻭﻱ ﻫــﺫﻩ ﺍﻟﻤﺠﻤﻭﻋــﺔ ﻋﻠــﻰ ﻭﺍﺼــﻑ ﺍﻟﺨﺼــﺎﺌﺹ
PropertyDescriptorﻟﻜل ﻋﻤﻭﺩ ﻓﻲ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ..ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﺽ ﺍﺴﻡ ﺃﻭل
ﺤﻘل ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ،ﻭﻴﻌﺭﺽ ﻗﻴﻤﺘﻪ:
;var BM = Bnd.BindingManagerBase
;]var PD = BM.GetItemProperties( )[0
MessageBox.Show(PD.Name); // ID
;)) ( MessageBox.Show(PD.GetValue(BM.Current).ToString
ﺤﻴﺙ ﺴﺘﻌﺭﺽ ﺍﻟﺭﺴﺎﻟﺔ ﺍﻷﻭﻟﻰ ﺍﺴﻡ ﺍﻟﺤﻘل IDﺒﻴﻨﻤﺎ ﺴﺘﻌﺭﺽ ﺍﻟﺭﺴـﺎﻟﺔ ﺍﻟﺜﺎﻨﻴـﺔ ﻗﻴﻤـﺔ
ﺍﻟﺤﻘل IDﻓﻲ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ.
٣٩٠
ﺍﻟﻤﻭﻀﻊ ﺘﻐﻴﺭ :PositionChanged
ﻴﻨﻁﻠﻕ ﺇﺫﺍ ﺘﻐﻴﺭﺕ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ..Positionﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ eﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨﻭﻉ
،EventArgsﺍﻟﺫﻱ ﻻ ﻴﺤﻤل ﺃﻴﺔ ﻤﻌﻠﻭﻤﺎﺕ ﻫﺎﻤﺔ ﻋﻥ ﺍﻟﺤﺩﺙ.
ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﺍ ﺍﻟﺤـﺩﺙ ﻓـﻲ ﺍﻟﺘﻁﺒﻴـﻕ ،BindingToArrayﻟﺘﺤـﺩﻴﺙ ﺍﻟﻤﻭﻀـﻊ
ﺍﻟﻤﻌﺭﻭﺽ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ TxtPosﺍﻟﻤﻭﺠﻭﺩ ﺃﺴﻔل ﺍﻟﻨﻤﻭﺫﺝ ،ﻜﻠﻤـﺎ ﺘﻐﻴـﺭ ﺍﻟﻤﻭﻀـﻊ
ﺍﻟﺤﺎﻟﻲ ﺒﺴﺒﺏ ﻀﻐﻁ ﺃﺯﺭﺍﺭ ﺍﻟﺘﺤﺭﻙ ..ﻻﺤﻅ ﺃﻨﻨﺎ ﺭﺒﻁﻨﺎ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﺒﺎﻹﺠﺭﺍﺀ ﺍﻟﻤﺴﺘﺠﻴﺏ
ﻟﻪ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺠﻤﻠﺔ AddHandlerﻓﻲ ﺤﺩﺙ ﺘﺤﻤﻴل ﺍﻟﻨﻤﻭﺫﺝ ﻜﺎﻟﺘﺎﻟﻲ:
;Bm.PositionChanged += Bm_PositionChanged
٣٩١
ﻓﺌﺔ ﻤﺩﻴﺭ ﺍﻟﺨﺎﺼﻴﺔ PropertyManager Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ،BindingManagerBaseﻭﻫﻲ ﺘﻌﻤل ﻜﻤﺩﻴﺭ ﻴﺘﺤﻜﻡ ﻓﻲ ﺭﺒـﻁ ﻜـﺎﺌﻥ
ﺒﺴﻴﻁ ﻟﻪ ﻋﺩﺓ ﺨﺼﺎﺌﺹ ﻭﻻ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﻋﻨﺎﺼﺭ ﺩﺍﺨﻠﻴﺔ.
ﻭﻻ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﻴﺔ ﺨﺼﺎﺌﺹ ﺃﻭ ﻭﺴﺎﺌل ﺃﻭ ﺃﺤﺩﺍﺙ ﺠﺩﻴﺩﺓ ﻏﻴﺭ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ.
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ،BindingManagerBaseﻭﻫﻲ ﺘﻌﻤل ﻜﻤﺩﻴﺭ ﻴﺘﺤﻜﻡ ﻓﻲ ﺭﺒـﻁ ﻜـﺎﺌﻥ
ﻤﺭﻜﺏ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﻋﻨﺎﺼﺭ ﺩﺍﺨﻠﻴﺔ.
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ،ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻌﻨﺼﺭﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ:
ﺍﻟﻘﺎﺌﻤﺔ :List
ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻥ ﻨﻭﻉ ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ،IListﻫﻲ ﺘﻌﻴﺩ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ ﺍﻟﺘـﻲ ﻴﺤﺘﻭﻴﻬـﺎ
ﺍﻟﻜﺎﺌﻥ.
ﺇﻨﻌﺎﺵ :Refresh
ﺘﻌﻴﺩ ﻤلﺀ ﻤﺼﻔﻭﻓﺔ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻤﺭﺘﺒﻁﺔ ..ﺍﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﻨﺩ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻜﺎﺌﻨـﺎﺕ ﻻ
ﺘﻌﻁﻲ ﺘﻨﺒﻴﻬﺎ ﻋﻨﺩ ﺘﻐﻴﺭ ﻋﻨﺎﺼﺭﻫﺎ ،ﻤﺜل ﺍﻟﻤﺼﻔﻭﻓﺎﺕ .Arrays
٣٩٢
ﺭﺒﻁ ﺍﻷﺩﻭﺍﺕ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ:
ﻴﻘﺩﻡ ﻟﻙ ﻤﺼﻤﻡ ﺍﻟﻨﻤﺎﺫﺝ Form Designerﻓﻲ ﺩﻭﺕ ﻨﺕ ﺘﺴﻬﻴﻼﺕ ﻜﺜﻴﺭﺓ ﻟﺭﺒﻁ ﺍﻷﺩﻭﺍﺕ ﻓﻲ
ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ ،ﻟﺘﻘﻠﻴل ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﺘﺤﺘﺎﺠﻪ ﻷﺩﺍﺀ ﻫﺫﻩ ﺍﻟﻌﻤﻠﻴﺔ ..ﻭﻟﻜﻲ ﺘﺭﻯ ﻫﺫﺍ ﻋﻤﻠﻴـﺎ ،ﺍﺒـﺩﺃ
ﻤﺸﺭﻭﻋﺎ ﺠﺩﻴﺩﺍ ﻭﺃﺴﻤﻪ ،ViewAndEditBooksﻭﺼﻤﻡ ﻭﺍﺠﻬﺔ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻜﻤﺎ ﻓﻲ ﺍﻟﺼﻭﺭﺓ
ﺍﻟﺘﺎﻟﻴﺔ:
٣٩٣
-ﺍﻀﻐﻁ ﺍﻟﻌﻼﻤﺔ +ﺍﻟﻤﺠﺎﻭﺭﺓ ﻟﻠﻤﻘﻁﻊ ،DataBindingﻹﺴﺩﺍل ﺨﺼﺎﺌﺹ ﺍﻷﺩﺍﺓ ﺍﻟﺘـﻲ
ﻴﻤﻜﻥ ﺭﺒﻁﻬﺎ ﺒﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﺒﺎﻟﻨﺴﺒﺔ ﻟﻤﺭﺒﻊ ﺍﻟﻨﺹ ،ﺴـﺘﺠﺩ ﺃﻥ ﺒﺈﻤﻜﺎﻨـﻙ ﺭﺒـﻁ
ﺍﻟﺨﺎﺼﻴﺘﻴﻥ Tagﻭ ..Textﻓﻲ ﺍﻟﻐﺎﻟﺏ ﻴﺘﻡ ﺭﺒﻁ ﺍﻟﺨﺎﺼﻴﺔ Tagﺒﺭﻗﻡ ﺍﻟﺴـﺠلّ ،ID
ﻭﺫﻟﻙ ﻟﺘﺴﻬﻴل ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﺤﻘل ﻋﻨﺩ ﺍﻟﺤﺎﺠﺔ ..ﻭﻟﻜﻥ ﻓﻲ ﻤﺜﺎﻟﻨﺎ ﻫﺫﺍ ،ﺴـﻨﻌﺭﺽ ﻫـﺫﺍ
ﺍﻟﺭﻗﻡ ﻓﻲ ﺍﻟﺨﺎﺼ ﻴﺔ Textﻟﺘﻅﻬﺭ ﻟﻠﻤﺴﺘﺨﺩﻡ.
-ﺤﺩﺩ ﺍﻟﺨﺎﺼ ﻴﺔ ،Textﻭﺍﻀﻐﻁ ﺯﺭ ﺍﻹﺴﺩﺍل ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺨﺎﻨﺔ ﺍﻟﻘﻴﻤﺔ ..ﺴﺘﻌﺭﺽ ﻟﻙ
ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ﺍﺨﺘﻴﺎﺭﻴﻥ:
:None -١ﻹﻟﻐﺎﺀ ﺭﺒﻁ ﺍﻟﺨﺎﺼﻴﺔ ﺒـﺄﻱ
ﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ.
:Other Data Sources -٢ﻭﻟــﻭ
ﺃﺴﺩﻟﺕ ﻋﻨﺎﺼﺭ ﻫﺫﺍ ﺍﻟﻔﺭﻉ ،ﻓﺴـﺘﺠﺩ
ﺘﺤﺘﻪ ﻋﻨﺼﺭﻴﻥ ﻓﺭﻋﻴﻴﻥ:
:Project Data Sources ﺃ.
ﺴﺘﺠﺩ ﺘﺤـﺕ ﻫـﺫﺍ ﺍﻟﻌﻨﺼـﺭ
ﻤﺼــﺎﺩﺭ ﺍﻟﺒﻴﺎﻨــﺎﺕ ﺍﻟﻌﺎﻤــﺔ
ﻟﻠﻤﺸــﺭﻭﻉ ،ﻤﺜــل ﻓﺌــﺎﺕ
ﻤﺠﻤﻭﻋﺎﺕ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤﺤـﺩﺩﺓ
ﺍﻟﻨﻭﻉ ﻤﺜل ..DsAuthorBooksﻭﻟﻭ ﺍﺨﺘﺭﺕ ﺃﻴﺎ ﻤﻥ ﻫـﺫﻩ ﺍﻟﻔﺌـﺎﺕ ،ﻓﺴـﻴﺘﻡ
ﺘﻌﺭﻴﻑ ﻨﺴﺨﺔ ﻤﻨﻬﺎ ﻻﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﺍﻟﻨﻤﻭﺫﺝ ﺍﻟﺤﺎﻟﻲ.
ﺏ :Form1 List Instances .ﺴﺘﺠﺩ ﺘﺤﺕ ﻫﺫﺍ ﺍﻟﻔﺭﻉ ﻨﺴﺦ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﻭﻀﻭﻋﺔ
ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ﺍﻟﺤﺎﻟﻲ ،ﻭﺍﻟﺘﻲ ﺘﺼﻠﺢ ﻟﻠﻌﻤل ﻜﻘﻭﺍﺌﻡ ﻭﻤﺼـﺎﺩﺭ ﺒﻴﺎﻨـﺎﺕ ..ﻭﻓـﻲ
ﻤﺸﺭﻭﻋﻨﺎ ﻫﺫﺍ ،ﺴﺘﺠﺩ ﺘﺤﺘﻪ ﻨﺴﺨﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ..DsAuthorBooks1
ﻟﻭ ﺃﺴﺩﻟﺕ ﻋﻨﺎﺼﺭ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ،ﻓﺴﺘﺠﺩ ﺘﺤﺘﻬﺎ ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﺍﻟﻤﻌﺭﻓﺔ ﻓـﻲ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ..ﻭﺴﺘﺠﺩ ﻓﻲ ﻤﺜﺎﻟﻨﺎ ﻫﺫﺍ ﺠﺩﻭﻻ ﻭﺍﺤـﺩﺍ ﺍﺴـﻤﻪ
،Authosﻭﺫﻟﻙ ﻷﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻗﺩ ﻤﻨﺢ ﺍﻟﺠﺩﻭل ﺍﻟﻨﺎﺘﺞ ﻤﻥ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺭﺒﻁ
٣٩٤
Join Queryﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ ..Authorsﺃﺴﺩل ﺤﻘـﻭل ﻫـﺫﺍ ﺍﻟﺠـﺩﻭل،
ﻭﺍﺨﺘﺭ ﺍﻟﺤﻘل .ID
ﺒﻬﺫﺍ ﻨﻜﻭﻥ ﻗﺩ ﺭﺒﻁﻨﺎ ﺍﻟﺨﺎﺼﻴﺔ Textﻟﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺒﺎﻟﺤﻘـل IDﻓـﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ..
ﻭﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ ﻴﻤﻜﻨﻙ ﺭﺒﻁ ﺍﻟﺨﺎﺼﻴﺔ Textﻟﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺍﻟﺜﺎﻨﻲ ﺒﺎﻟﺤﻘـل ،Bookﻭﻤﺭﺒـﻊ
ﺍﻟﻨﺹ ﺍﻟﺜﺎﻟﺙ ﺒﺎﻟﺤﻘل .Author
ﺍﻵﻥ ﺃﻨﻬﻴﻨﺎ ﺭﺒﻁ ﺃﺩﻭﺍﺘﻨﺎ ﺒﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺒﺤﻴﺙ ﻟﻭ ﻤﻸﻨﺎ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺒﺎﻟﺴـﺠﻼﺕ،
ﻓﺴﺘﻅﻬﺭ ﻗﻴﻡ ﺤﻘﻭل ﺍﻟﺴﺠلّ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺹ ،ﺒﺩﻭﻥ ﺃﻥ ﻨﻜﺘﺏ ﺃﻱ ﻜﻭﺩ ﻟﻔﻌـل ﻫـﺫﺍ..
ﻭﻜﹼﻠﹼﻤﺎ ﺘﺤﺭﻜﻨﺎ ﻤﻥ ﺴﺠلّ ﺇﻟﻰ ﺁﺨﺭ ،ﻴﺘﻡ ﻋﺭﺽ ﻗﻴﻡ ﺤﻘﻭل ﺍﻟﺴﺠلّ ﺍﻟﺠﺩﻴﺩ ﻓﻲ ﺍﻷﺩﻭﺍﺕ ﺁﻟﻴﺎ.
ﻋﻨﺩ ﻫﺫﻩ ﺍﻟﻨﻘﻁﺔ ،ﻟﻭ ﺸﻐﹼﹼﻠﺕ ﺍﻟﻤﺸﺭﻭﻉ ﻭﻀﻐﻁﺕ ﺯﺭ ﺘﺤﻤﻴل ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﺴﺘﻅﻬﺭ ﻗـﻴﻡ ﺍﻟﺴـﺠلّ
ﺍﻷﻭل ﻓﻲ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺹ ،ﻜلّ ﺤﻘل ﻓﻲ ﻤﺭﺒﻌﻪ ﺍﻟﺫﻱ ﺤ ﺩﺩﻨﺎﻩ.
ﻨﺭﻴﺩ ﺍﻵﻥ ﺃﻥ ﻨﻜﺘﺏ ﻜﻭﺩ ﺍﻷﺯﺭﺍﺭ ﺍﻟﺘﻲ ﺘﺴﻤﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺒﺎﻟﺘﻨﻘـل ﺒـﻴﻥ ﺴـﺠﻼﺕ ﻤﺠﻤﻭﻋـﺔ
ـﺭﺒﻁ
ـﺩﻴﺭ ﺍﻟـ
ـﺘﺨﺩﺍﻡ ﻤـ
ـﻭ ﺍﺴـ
ـﺎ ﻫـ
ـﺎ ﻋﻠﻴﻨـ
ـل ﻤـ
ـﻴﻁﺎ ،ﻓﻜـ
ـﺭ ﺒﺴـ
ـﻴﻜﻭﻥ ﺍﻷﻤـ
ـﺎﺕ ..ﺴـ
ﺍﻟﺒﻴﺎﻨـ
ـﻴﺔ
ـﻼل ﺍﻟﺨﺎﺼـ
ـﻥ ﺨـ
ـﻭل ﻋﻠﻴ ـﻪ ﻤـ
ـﺎ ﺍﻟﺤﺼـ
ـﺫﻱ ﻴﻤﻜﻨﻨـ
،BindingManagerBaseﻭﺍﻟـ
BindingContextﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﻨﻤﻭﺫﺝ ﻜﺎﻟﺘﺎﻟﻲ:
= BindingManagerBase Bm
;]"this.BindingContext[DsAuthorBooks1, "Authors
ﺍﻵﻥ ﺘﺴﺘﻁﻴﻊ ﺘﻐﻴﻴﺭ ﺍﻟﻤﻭﻀﻊ ﻜﻤﺎ ﺘﺭﻴﺩ ،ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ Positionﻭ Countﺍﻟﺘﺎﺒﻌﺘﻴﻥ
ﻟﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ ..ﻤﺜﻼ ،ﻓﻲ ﺯﺭ ﺍﻻﻨﺘﻘﺎل ﺇﻟﻰ ﺍﻟﺴﺠل ﺍﻟﺘﺎﻟﻲ ،ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﻜﻭﺩ:
)if (Bm.Position < Bm.Count - 1
{
;Bm.Position += 1
LbPosition.Text = (Bm.Position + 1).ToString( ) + " / " +
;) (Bm.Count.ToString
}
ﻻﺤﻅ ﺃﻥ ﻤﺤﺎﻭﻟﺔ ﺘﻐﻴﻴﺭ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻗﺩ ﺘﺅﺩﻱ ﺇﻟﻰ ﺤﺩﻭﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻭﺫﻟـﻙ ﻷﻥ
ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ ﺴﻴﻔﺤﺹ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺼﻭﺹ ،ﻓﺈﻥ ﻜﺎﻨﺕ ﺒﻌﺽ ﻗﻴﻤﻬﺎ ﺘﻐﻴﺭﺕ ،ﻓﺴﻴﺤﺎﻭل ﺤﻔﻅﻬـﺎ
ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﺴﻴﺤﺩﺙ ﺨﻁﺄ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻗﺩ ﺃﺩﺨل ﻗﻴﻤﺔ ﻏﻴﺭ ﻤﻨﺎﺴـﺒﺔ ﻷﺤـﺩ
ﺍﻟﺤﻘﻭل ..ﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ،ﺍﺴﺘﺨﺩﻡ ﺍﻟﻤﻘﻁﻊ Try Catchﻟﻤﻌﺎﻟﺠﺔ ﺃﻱ ﺨﻁﺄ ﻤﻥ ﻫﺫﺍ ﺍﻟﻨـﻭﻉ،
٣٩٥
ﻭﻓﻲ ﺍﻟﻤﻘﻁﻊ Catchﺍﺴﺘﺨﺩﻡ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ ﻹﻟﻐﺎﺀ ﺘﺤﺭﻴﺭ ﺍﻟﺴـﺠل ﺍﻟﺤـﺎﻟﻲ )ﺍﻟـﺫﻱ ﺴـﺒﺏ
ﺍﻟﻤﺸﻜﻠﺔ(:
;) (Bm.CancelCurrentEdit
ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﺴﻴﻌﻴﺩ ﻗﻴﻡ ﻜل ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺼﻭﺹ ﺇﻟﻰ ﻤﺎ ﻜﺎﻨﺕ ﻋﻠﻴـﻪ ..ﺴـﻴﻜﻭﻥ ﻫـﺫﺍ
ﻤﺴﺘﻔﺯﺍ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻟﻠﻐﺎﻴﺔ ﻟﻭ ﻜﺎﻥ ﻋﺩﺩ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺼﻭﺹ ﻜﺒﻴﺭﺍ ﻭﻜﺎﻥ ﺍﻟﺨﻁﺄ ﻨﺎﺘﺠﺎ ﻋﻥ ﻗﻴﻤـﺔ
ﺨﺎﻁﺌﺔ ﻓﻲ ﻭﺍﺤﺩ ﻤﻨﻬﺎ ﻓﻘﻁ ..ﻟﻬﺫﺍ ﺴﻴﻜﻭﻥ ﻤﻥ ﺍﻷﺫﻜﻰ ﺃﻥ ﺘﻠﻐﻲ ﺘﺤﺭﻴﺭ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﺴﺒﺏ
ﺍﻟﻤﺸﻜﻠﺔ ،ﺃﻭ ﺃﻥ ﺘﺘﺭﻙ ﺍﻟﻘﻴﻡ ﺍﻟﺤﺎﻟﻴﺔ ﻜﻤﺎ ﻫﻲ ،ﻭﺘﺘﺭﻙ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻤﻌﺭﻓﺔ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﺴﺒﺏ
ﺍﻟﻤﺸﻜﻠﺔ ﻤﻥ ﺨﻼل ﺭﺴﺎﻟﺔ ﺍﻟﺨﻁﺄ.
ﻭﺃﻟﻔﺕ ﻨﻅﺭﻙ ﻤﺠﺩﺩﺍ ﺇﻟﻰ ﺃﻥ ﻜل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﻴﺠﺭﻴﻬﺎ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻋﻠﻰ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺼـﻭﺹ
ﻴﺘﻡ ﺤﻔﻅﻬﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ )ﻭﻟﻴﺱ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ( ،ﻟﻬﺫﺍ ﻋﻠﻰ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻀـﻐﻁ ﺯﺭ
ﺍﻟﺤﻔﻅ ﻹﺭﺴﺎل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻫﺫﺍ ﺍﻟﺯﺭ ﻴﺴﺘﺨﺩﻡ ﺃﻤـﺭ
ﺍﻟﺘﺤﺩﻴﺙ Updateﺍﻟﺨﺎﺹ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻟﻜﻥ ﻫﺫﺍ ﻴﺤﺘﺎﺝ ﺇﻟﻰ ﺒﻌﺽ ﺍﻟﻌﻤل ﻤﻨﺎ ،ﻷﻥ ﻤﻬﻴﺊ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻻ ﻴﻨﺘﺞ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﺇﺫﺍ ﻜﺎﻥ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ﻴﻌﻴﺩ ﺤﻘﻭﻻ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﺠﺩﻭل ،ﺘﺎﺭﻜﺎ ﻟﻙ
ﺃﻨﺕ ﺍﻟﺘﺤﻜﻡ ﻓﻲ ﺍﻟﺤﻘﻭل ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺘﺤﺩﻴﺜﻬﺎ ﻭﻜﻴﻔﻴﺔ ﺘﺤﺩﻴﺜﻬﺎ ..ﻭﻨﻅﺭﺍ ﻷﻨﻨﺎ ﺴﻨﺴـﻤﺢ ﻓـﻲ ﻫـﺫﺍ
ﺍﻟﺒﺭﻨﺎﻤﺞ ﺒﺘﺤﺩﻴﺙ ﺍﻟﺤﻘل Books.Bookﻓﻘﻁ ،ﻓﺴﻨﺴﺘﺨﺩﻡ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﺍﻟﺘﺎﻟﻲ:
UPDATE Books
SET Book = @Book
WHERE ID = @Original_ID
ﻭﺴﺘﺠﺩ ﺘﻌﺭﻴﻑ ﻫﺫﺍ ﺍﻷﻤﺭ ﻭﻤﻌﺎﻤﻼﺘﻪ ﻓﻲ ﺤﺩﺙ ﺘﺤﻤﻴل ﺍﻟﻨﻤﻭﺫﺝ.
٣٩٦
ﺭﺒﻁ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻘﻭﺍﺌﻡ :Binding List Boxs
ﺭﺃﻴﻨﺎ ﺤﺘﻰ ﺍﻵﻥ ﻜﻴﻑ ﻨﺭﺒﻁ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺒﺴـﻴﻁﺔ ﻜﻤﺭﺒﻌـﺎﺕ ﺍﻟﻨﺼـﻭﺹ ﺒﻤﺼـﺎﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺍﻟﻤﺨﺘﻠﻔﺔ ..ﻟﻜﻥ ﻤﺎﺫﺍ ﻟﻭ ﺃﺭﺩﻨﺎ ﺭﺒﻁ ﺃﺩﻭﺍﺕ ﺃﻜﺜﺭ ﺘﻌﻘﻴـﺩﺍ ﻤﺜـل ﺍﻟﻘﺎﺌﻤـﺔ ListBoxﻭﺍﻟﻘﺎﺌﻤـﺔ
ﺍﻟﻤﺭﻜﺒﺔ ComboBoxﻭﻗﺎﺌﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ CkeckedListBox؟
ﻟﻭ ﺤﺎﻭﻟﺕ ﺍﺴﺘﺨﺩﺍﻡ ﻜﺎﺌﻥ ﺍﻟﺭﺒﻁ ﻟﺭﺒﻁ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﻟﺨﺎﺼﻴﺔ Itemsﻟﻬﺫﻩ ﺍﻷﺩﻭﺍﺕ ،ﻓﻜـل
ﻤﺎ ﺴﺘﺤﺼل ﻋﻠﻴﻪ ﻫﻭ ﺭﺴﺎﻟﺔ ﺨﻁﺎ ،ﺘﺨﺒﺭﻙ ﺃﻨﻪ ﻻ ﻴﻤﻜﻥ ﺍﻻﺭﺘﺒﺎﻁ ﺒﺎﻟﺨﺎﺼـﻴﺔ Itemsﻷﻨﻬـﺎ
ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ!
ﺇﺫﻥ ﻓﻤﺎ ﺍﻟﺤل؟
ﻓﻲ ﺍﻟﺤﻘﻴﻘﺔ ،ﻫﻨﺎﻙ ﻁﺭﻴﻘﺔ ﺃﺨﺭﻯ ﻟﺭﺒﻁ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻘﻭﺍﺌﻡ ﺒﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﻓﻬـﺫﻩ ﺍﻷﺩﻭﺍﺕ ﻻ
ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺍﻟﻤﺭﻭﺭ ﻤﻥ ﺴﺠل ﺇﻟﻰ ﺁﺨﺭ ،ﻓﻬﻲ ﻗﺎﺩﺭﺓ ﻋﻠﻰ ﻋﺭﺽ ﻜل ﺍﻟﺴﺠﻼﺕ ﺩﻓﻌﺔ ﻭﺍﺤـﺩﺓ،
ﻭﻤﻥ ﺃﺠل ﻫﺫﺍ ﻓﻬﻲ ﺘﻤﺘﻠﻙ ﺨﺼﺎﺌﺹ ﻤﺠﻬﺯﺓ ﻟﻬﺫﺍ ﺍﻟﻐﺭﺽ ،ﻭﻫﻲ:
٣٩٨
ﺘﻌﺭﻑ ﻁﺒﻌﺎ ﻜﻴﻑ ﺘﺭﺒﻁ ﻤﺭﺒﻌﻲ ﺍﻟﻨﺹ ﺍﻟﻠﺫﻴﻥ ﻴﻌﺭﻀﺎﻥ ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ﻭﺭﻗﻡ ﺍﻟﻜﺘﺎﺏ ..ﻤﺎ ﻴﻬﻤﻨـﺎ
ﺍﻵﻥ ﻫﻭ ﻜﻴﻔﻴﺔ ﺭﺒﻁ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﺭﻜﺒﺔ.
ﺤﺩﺩ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﺭ ﹼﻜﹼﺒﺔ ،ﻭﻤﻥ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ﺍﺨﺘﺭ ﺍﻟﺨﺎﺼـ ﻴﺔ ،DataSourceﻭﺍﻀـﻐﻁ ﺯﺭ
ﺍﻹﺴﺩﺍل ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺨﺎﻨﺔ ﻗﻴﻤﺘﻬﺎ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ﺍﺨﺘﺭ Other Data Sourcesﺜـﻡ
،Form1 List Instancesﺜﻡ .DsAuthorBooks1
ﺒﻌﺩ ﻫﺫﺍ ﺍﻨﺘﻘل ﺇﻟﻰ ﺍﻟﺨﺎﺼ ﻴﺔ DisplayMemberﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ،ﻭﺍﻀﻐﻁ ﺯﺭ ﺍﻹﺴﺩﺍل
ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺨﺎﻨﺔ ﻗﻴﻤﺘﻬﺎ ،ﻭﺍﺨﺘﺭ ﺍﻟﺠﺩﻭل ،Authorsﻭﻤﻥ ﺤﻘﻭﻟﻪ ﺍﺨﺘﺭ ﺍﻟﺤﻘل .Book
ﺇﺫﺍ ﺸﻐﹼﹼﻠﺕ ﺍﻟﺘﻁﺒﻴﻕ ﺍﻵﻥ ،ﻭﻀﻐﻁﺕ ﺯﺭ ﺘﺤﻤﻴل ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﺴﺘﺠﺩ ﺃﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﺭ ﹼﻜﹼﺒﺔ ﻗﺩ ﺍﻤﺘﻸﺕ
ﺒﺄﺴﻤﺎﺀ ﺍﻟﻜﺘﺏ ..ﺍﻟﻤﺩﻫﺵ ﺃﻨﹼﹼﻙ ﻟﻭ ﺍﺨﺘﺭﺕ ﺍﺴﻡ ﺃﻱ ﻜﺘﺎﺏ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ،ﻓﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻟﻰ ﺘﻐﻴﻴـﺭ
ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ،ﻭﻤﻥ ﺜﻡ ﺴﻴﻅﻬﺭ ﺭﻗﻤﻪ ﻭﺍﺴﻡ ﻤﺅﻟﻔﻪ ﻓﻲ ﻤﺭ ﺒﻌﻲ ﺍﻟﻨﺹ ﺘﻠﻘﺎﺌﻴﺎ ،ﻭﺒﺩﻭﻥ ﺃﻥ ﺘﻜﺘﺏ
ﺴﻁﺭﺍ ﻭﺍﺤﺩﺍ ﻤﻥ ﺍﻟﻜﻭﺩ ﻟﻔﻌل ﻫﺫﺍ!
ﺤﺴﻥ ..ﻨﺭﻴﺩ ﺍﻵﻥ ﺘﻁﻭﻴﺭ ﺍﻟﻤﺸﺭﻭﻉ ﺍﻟﺴﺎﺒﻕ ،ﺒﺤﻴﺙ ﻨﻌﺭﺽ ﺃﺴﻤﺎﺀ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻓﻲ ﻗﺎﺌﻤﺔ ﻤﺭﻜﺒﺔ،
ﻭﻨﻌﺭﺽ ﻜﺘﺏ ﺍﻟﻤﺅﻟﻑ ﺍﻟﻤﺤﺩﺩ ﺤﺎﻟﻴﺎ ﻓﻲ ﻗﺎﺌﻤﺔ ﻤﺭﻜﺒﺔ ﺃﺨﺭﻯ ،ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓـﻲ ﺍﻟﺼـﻭﺭﺓ
ﺍﻟﺘﺎﻟﻴﺔ:
٣٩٩
ﺍﺘﺒﻊ ﻫﺫﻩ ﺍﻟﺨﻁﻭﺍﺕ:
-١ﺃﻨﺸﺊ ﻤﺸﺭﻭﻋﺎ ﺠﺩﻴﺩﺍ ﺍﺴﻤﻪ .AuthorsBooks_Lists
-٢ﺃﻨﺸﺄ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺍﺴﻤﻪ DaAuthorsﻴﻌﻴﺩ ﺃﺴﻤﺎﺀ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺃﺭﻗﺎﻤﻬﻡ.
-٣ﺃﻨﺸﺄ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺍﺴﻤﻪ DaBooksﻴﻌﻴﺩ ﺃﺴﻤﺎﺀ ﺍﻟﻜﺘﺏ ﻭﺃﺭﻗﺎﻤﻬﺎ ﻭﺃﺴـﻌﺎﺭﻫﺎ ..ﻭﻴﻌﻴـﺩ
ﺃﻴﻀﺎ ﺍﻟﺤﻘل AuthorIDﻟﻜﻲ ﻨﺴﺘﺨﺩﻤﻪ ﻓﻲ ﺇﻨﺸﺎﺀ ﻋﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺠﺩﻭﻟﻴﻥ.
-٤ﺃﻨﺸﺊ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨـﻭﻉ ﺍﺴـﻤﻬﺎ DsAuthorsBooksﺘﺤﺘـﻭﻱ ﻋﻠـﻰ
ﺍﻟﺠﺩﻭﻟﻴﻥ.
-٥ﺍﻀﻐﻁ ﺍﻷﺩﺍﺓ DsAuthorsBooks1ﻓﻲ ﺼﻴﻨﻴﺔ ﺍﻟﻤﻜﻭﻨﺎﺕ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ،ﻭﻤـﻥ
ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤﺭ ..Edit in Dataset Designerﺴﺘﻅﻬﺭ ﻟـﻙ ﻨﺎﻓـﺫﺓ
ﻤﺨﻁﹼﹼﻁ ..XMLﺃﻨﺸﺊ ﻋﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺠﺩﻭﻟﻴﻥ ﺍﺴﻤﻬﺎ Authors_Booksﻜﻤﺎ ﻓﻌﻠﻨﺎ ﻤـﻥ
ﻗﺒل.
-٦ﺒﺎﻟﻨﺴﺒﺔ ﻟﻠﻘﺎﺌﻤﺔ ﺍﻟﻤﺭ ﹼﻜﹼﺒﺔ ﺍﻟﺘﻲ ﺴﺘﻌﺭﺽ ﺃﺴﻤﺎﺀ ﺍﻟﻤﺅﹼﻟﹼﻔﻴﻥ ،ﻭﻤﺭ ﺒﻊ ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﺴـﻴﻌﺭﺽ
ﺭﻗﻡ ﺍﻟﻤﺅﻟﻑ ،ﻟﻥ ﺘﺨﺘﻠﻑ ﻁﺭﻴﻘﺔ ﺭﺒﻁﻬﻤﺎ ﺒﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺸـﻲﺀ ﻋـﻥ ﺍﻟﻤﺜـﺎل
ﺍﻟﺴﺎﺒﻕ.
-٧ﺃﻤﺎ ﺒﺎﻟﻨﺴﺒﺔ ﻟﻠﻘﺎﺌﻤﺔ ﺍﻟﻤﺭ ﹼﻜﹼﺒﺔ ﺍﻟﺘﻲ ﺴﺘﻌﺭﺽ ﺃﺴﻤﺎﺀ ﺍﻟﻜﺘﺏ ﻓﺴﻴﺨﺘﻠﻑ ﺍﻷﻤﺭ ﻗﻠـﻴﻼ ..ﺤـﺩﺩ
ﺍﻟﺨﺎﺼــ ﻴﺔ DataSourceﻓــﻲ ﻨﺎﻓــﺫﺓ ﺍﻟﺨﺼــﺎﺌﺹ ،ﻭﻀــﻊ ﻓﻴﻬــﺎ ﺍﻟﻘﻴﻤــﺔ
ـﻐﻁ ﺯﺭ
..DsAuthorsBooks1ﺜـﻡ ﺤـﺩﺩ ﺍﻟﺨﺎﺼـﻴﺔ ،DisplayMemberﻭﺍﻀـ
ﺍﻹﺴﺩﺍل ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺨﺎﻨﺔ ﺍﻟﻘﻴﻤﺔ ..ﻫﺫﻩ ﺍﻟﻤﺭﺓ ﻻ ﺘﺨﺘﺭ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ،ﺒل ﺍﺨﺘﺭ ﺠـﺩﻭل
ﺍﻟﻤﺅﻟﹼﹼﻔﻴﻥ ..Authorsﺴـﺘﺠﺩ ﻀـﻤﻥ ﻋﻨﺎﺼـﺭﻩ ﺍﻟﻔﺭﻋﻴـﺔ ﻋﻨﺼـﺭﺍ ﺠﺩﻴـﺩﺍ ،ﻫـﻭ
..Authors_Booksﻫﺫﺍ ﺍﻟﻌﻨﺼﺭ ﻫﻭ ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ ﺍﻟﺘﻲ ﺃﻨﺸﺄﻨﺎﻫﺎ ..ﺃﺴﺩل ﻓﺭﻭﻉ ﻫـﺫﺍ
ﺍﻟﻌﻨﺼﺭ ..ﺴﺘﺠﺩ ﺘﺤﺘﻪ ﺃﺴﻤﺎﺀ ﺤﻘﻭل ﺠﺩﻭل ﺍﻟﻜﺘﺏ ..ﺍﺨﺘﺭ ﺍﻟﺤﻘـل ..Bookﺒﻬـﺫﺍ ﻟـﻥ
ﺘﻌﺭﺽ ﻗﺎﺌﻤﺔ ﺍﻟﻜﺘﺏ ﻜلّ ﺍﻟﻜﺘﺏ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺒل ﺴﺘﻌﺭﺽ ﻓﻘﻁ ﺍﻟﻜﺘـﺏ
ﺍﻟﺘﻲ ﺘﻨﺘﻤﻲ ﺇﻟﻰ ﺍﻟﻤﺅﻟﹼﹼﻑ ﺍﻟﺤﺎﻟﻲ ﻤﻥ ﺨﻼل ﺍﻟﻌﻼﻗﺔ ﺒﻴﻨﻬﻤﺎ.
ﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺃﺩﺍﺀ ﻫﺫﺍ ﻤﻥ ﺍﻟﻜﻭﺩ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ:
= CmbBook.DisplayMember
;""Authors.Authors_Books.Book
٤٠٠
-٨ﺒﺎﻟﻨﺴﺒﺔ ﻟﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﺴﻴﻌﺭﺽ ﺭﻗﻡ ﺍﻟﻜﺘﺎﺏ ،ﺍﺭﺒﻁ ﺍﻟﺨﺎﺼـﻴﺔ Textﺒﺎﻟﺤﻘـل ID
ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﻌﻼﻗﺔ Authors_Booksﺘﺤﺕ ﺠﺩﻭل ﺍﻟﻤـﺅﻟﹼﹼﻔﻴﻥ ..Authorsﻭﺍﻓﻌـل
ﺸﻴﺌﺎ ﻤﺸﺎﺒﻬﺎ ﻟﺭﺒﻁ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺍﻷﺨﻴﺭ ﺒﺎﻟﺤﻘل .Authors.Authors_Books.Price
ﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺃﺩﺍﺀ ﻫﺫﺍ ﻤﻥ ﺍﻟﻜﻭﺩ ﻜﻤﺎ ﻴﻠﻲ:
TextBox1.DataBindings.Add("Text",
;)"DsAuthorsBooks1, "Authors.Authors_Books.ID
TextBox2.DataBindings.Add("Text",
;)"DsAuthorsBooks1, "Authors.Authors_Books.Price
-٩ﻭﺃﺨﻴﺭﺍ ،ﺍﻜﺘﺏ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﻴﻤﻸ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺴﺠﻼﺕ ﺍﻟﺠﺩﻭﻟﻴﻥ ﻓﻲ ﺤﺩﺙ ﻀـﻐﻁ
ﺯﺭ ﺍﻟﺘﺤﻤﻴل:
;)"DaAuthors.Fill(DsAuthorsBooks1, "Authors
;)"DaBooks.Fill(DsAuthorsBooks1, "Books
ﺍﻵﻥ ﻟﻭ ﺠ ﺭﺒﺕ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻓﻼ ﺭﻴﺏ ﺃ ﹼﻨﹼﻙ ﺴﺘﺘﻴﻪ ﺩﻫﺸﺔﹰ ﻭﺴﻌﺎﺩ ﹰﺓﹰ ،ﻓﻠﺩﻴﻙ ﻭﺍﺠﻬﺔ ﺍﺴﺘﺨﺩﺍﻡ ﺭﺍﺌﻌـﺔ،
ﺘﻌﻤل ﺒﻁﺭﻴﻘﺔ ﻤﺜﺎﻟﻴﺔ ،ﻓﻲ ﺒﺭﻨﺎﻤﺞ ﻟﻡ ﻨﻜﺘﺏ ﻓﻴﻪ ﺃﻜﺜﺭ ﻤﻥ ﺴﻁﺭﻴﻥ ﻤﻥ ﺍﻟﻜﻭﺩ!
ﻻﺤﻅ ﺃﻨﻨﺎ ﻻ ﻨﻤﻠﻙ ﻁﺭﻴﻘﺔ ﻤﺒﺎﺸﺭﺓ ﻻﺴﺘﺨﺩﺍﻡ ﺍﻟﻌﻼﻗﺔ Authors_Booksﺒﻁﺭﻴﻘﺔ ﻋﻜﺴﻴﺔ ﻓـﻲ
ﻋﻤﻠﻴﺔ ﺍﻟﺭﺒﻁ ..ﻤﺜﻼ :ﻻ ﺘﺴﺘﻁﻴﻊ ﺃﻥ ﺘﺠﻌل ﻤﺭﺒﻊ ﻨﺹ ﻴﻌﺭﺽ ﻤﺅﻟﻑ ﺍﻟﻜﺘﺎﺏ ﺍﻟﺤﺎﻟﻲ ﺒﺎﻟﺠﻤﻠـﺔ
ﺍﻟﺘﺎﻟﻴﺔ:
TextBox1.DataBindings.Add("Text",
;)"Ds, "Books.Authors_Books.Author
ﻓﻬﺫﻩ ﺍﻟﺠﻤﻠﺔ ﺴﺘﺴﺒﺏ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻷﻥ ﺍﻟﻌﻨﺼﺭ Authors_Booksﻟﻴﺱ ﺠـﺯﺀﺍ ﻤـﻥ
ﺠﺩﻭل ﺍﻟﻜﺘﺏ! ..ﺇﻥ ﻋﻤﻠﻴﺔ ﺍﻟﺭﺒﻁ ﺘﻌﺘﺒﺭ ﺍﻟﻌﻼﻗﺔ ﺠﺯﺀﺍ ﻤﻥ ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴـﻲ ﻓﻘـﻁ ،ﻭﻟـﻴﺱ
ﺍﻟﺠﺩﻭل ﺍﻟﻔﺭﻋﻲ!
ﻭﻗﺩ ﻭﺍﺠﻬﺘﻨﺎ ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ BindingTextBoxﺍﻟﺫﻱ ﺃﻨﺸﺄﻨﺎﻩ ﻓـﻲ ﺒﺩﺍﻴـﺔ ﻫـﺫﺍ
ﺍﻟﻔﺼل ،ﻓـﻨﺤﻥ ﻓـﻲ ﻫـﺫﺍ ﺍﻟﻤﺸـﺭﻭﻉ ﻨﻌـﺭﺽ ﺠـﺩﻭل ﺍﻟﻜﺘـﺏ ﻓـﻲ ﺠـﺩﻭل ﻋـﺭﺽ
،DataGridViewﻭﻨﺭﻴﺩ ﺃﻥ ﻨﺭﺒﻁ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺒﺎﺴﻡ ﻤﺅﻟﻑ ﺍﻟﻜﺘﺎﺏ ﺍﻟﻤﺤﺩﺩ ﺤﺎﻟﻴﺎ ﻓﻲ ﺠﺩﻭل
ﺍﻟﻌﺭﺽ ..ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻻ ﻴﻤﻜﻨﻨﺎ ﺃﻥ ﻨﺴﺘﺨﺩﻡ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ:
٤٠١
TextBox1.DataBindings.Add("Text",
;)"Ds, "Authors.Authors_Books.Author
ﻷﻨﻬﺎ ﺴﺘﻌﺭﺽ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺍﺴﻡ ﺃﻭل ﻤﺅﻟﻑ ﻓﻘﻁ ،ﻭﻟﻥ ﻴﺘﻐﻴﺭ ﻤﻬﻤﺎ ﺘﻐﻴﺭ ﺍﻟﻜﺘﺎﺏ ﺍﻟﺤﺎﻟﻲ..
ﺍﻟﺴﺒﺏ ﻓﻲ ﻫﺫﺍ ﺃﻥ ﻜﺎﺌﻥ ﺍﻟﺭﺒﻁ ﺍﻟﺨﺎﺹ ﺒﺠﺩﻭل ﺍﻟﻌﺭﺽ ،ﻴﺘﻌﺎﻤل ﻤﻊ ﺴﺠﻼﺕ ﺠـﺩﻭل ﺍﻟﻜﺘـﺏ
ﻓﻘﻁ ،ﻭﻟﻴﺴﺕ ﻟﻪ ﺃﻱ ﻋﻼﻗﺔ ﺒﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ!
ﻭﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ،ﻋﺭﻓﻨﺎ ﻋﻤﻭﺩﺍ ﺇﻀﺎﻓﻴﺎ ﺍﺴﻤﻪ Authorﻭﺠﻌﻠﻨﺎ ﺨﻔﻴﺎ ،ﻭﺠﻌﻠﻨﺎﻩ ﻴﺤﻤل ﺍﺴـﻡ
ﻤﺅﻟﻑ ﺍﻟﻜﺘﺎﺏ ﺍﻟﺤﺎﻟﻲ ﻤﻥ ﺨﻼل ﺍﻟﻌﻼﻗﺔ ﺒﻴﻨﻬﻤﺎ ﻜﺎﻟﺘﺎﻟﻲ:
DataColumn Col = new DataColumn("Author",
;)typeof(string), "Parent.Author", MappingType.Hidden
ﺜﻡ ﺃﻀﻔﻨﺎ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﺇﻟﻰ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﻜﺎﻟﺘﺎﻟﻲ:
;)Ds.Books.Columns.Add(Col
ﺍﻵﻥ ﺼﺎﺭ ﻤﻥ ﺍﻟﺴﻬل ﺭﺒﻁ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺒﻬﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻜﺎﻟﺘﺎﻟﻲ:
;)"TxtAuthor.DataBindings.Add("Text", Ds, "Books.Author
ﻭﻟﻭ ﺠﺭﺒﺕ ﺍﻟﻤﺸﺭﻭﻉ ﻓﺴﺘﺠﺩﻩ ﻴﻌﻤل ﻋﻠﻰ ﻤﺎ ﻴﺭﺍﻡ.
٤٠٢
ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﻟﺘﻬﻴﺌﺔ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ
Data Source Configuration Wizard
ﻴﺴﺘﺨﺩﻡ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻟﺞ ﻹﻀﺎﻓﺔ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻤﺸﺭﻭﻋﻙ ..ﻭﻴﻤﻜﻨﻙ ﺘﺸﻐﻴﻠﻪ ﺒﻀﻐﻁ ﻗﺎﺌﻤـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ Data Menuﻤﻥ ﺸﺭﻴﻁ ﺍﻟﻘﻭﺍﺌﻡ ﺍﻟﺭﺌﻴﺴﻴﺔ ﺃﻋﻠﻰ ﻤﺼﻤﻡ ﺍﻟﻨﻤﻭﺫﺝ ،ﻭﻀـﻐﻁ ﺍﻷﻤـﺭ
..Add New Data Sourceﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﺍﺨﺘﻴﺎﺭ ﻨﻭﻉ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺍﻟﻤﻭﻀﺤﺔ ﻓﻲ
ﺍﻟﺼﻭﺭﺓ:
٤٠٣
-١ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ :DataBase
ﻴﺘﻴﺢ ﻟﻙ ﻫﺫﺍ ﺍﻟﻨﻭﻉ ﺇﻨﺸﺎﺀ ﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ﻴﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ،ﺤﻴﺙ ﻴـﺘﻡ ﺇﻨﺘـﺎﺝ
ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ Typed DataSetﻭﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ﺍﻟﻼﺯﻤﺔ ﻟﻠﺘﻌﺎﻤل
ﻤﻊ ﻜل ﺠﺩﻭل ﻤﻥ ﺠﺩﺍﻭﻟﻬﺎ.
-٢ﺨﺩﻤﺔ :Service
ﻴﺘﻴﺢ ﻟﻙ ﻫـﺫﺍ ﺍﻟﻨـﻭﻉ ﺇﻨﺸـﺎﺀ ﻤﺼـﺩﺭ ﺒﻴﺎﻨـﺎﺕ ﻴﺘﻌﺎﻤـل ﻤـﻊ ﺨﺩﻤـﺔ ﺇﻨﺘﺭﻨـﺕ
..Web Serviceﻫﺫﺍ ﺍﻟﻨﻭﻉ ﺨﺎﺭﺝ ﻨﻁﺎﻕ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ.
-٣ﻜﺎﺌﻥ :Object
ﻴﺘﻴﺢ ﻟﻙ ﻫﺫﺍ ﺍﻟﻨﻭﻉ ﺇﻨﺸﺎﺀ ﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ﻴﺘﻌﺎﻤل ﻤﻊ ﺃﻱ ﻜﺎﺌﻥ ﻓﻲ ﻤﺸﺭﻭﻋﻙ ..ﻤـﺜﻼ،
ﻟﻭ ﻋﺭﻓﺕ ﻓﺌﺔ ﺍﺴﻤﻬﺎ ،Studentsﻓﻴﻤﻜﻨﻙ ﺠﻌﻠﻬﺎ ﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ،ﺒﺎﺨﺘﻴﺎﺭ ﻫﺫﺍ ﺍﻟﻨـﻭﻉ
ﺜﻡ ﻀﻐﻁ Nextﻭﺍﺨﺘﻴﺎﺭﻫﺎ ﻤﻥ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
٤٠٤
ﻭﻴﻤﻜﻨﻙ ﺍﺨﺘﻴﺎﺭ ﺃﻱ ﻓﺌﺔ ﻤﻥ ﻓﺌﺎﺕ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﻻﺴﺘﺨﺩﺍﻤﻬﺎ ﻜﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ﻟﻭ ﺃﺭﺩﺕ..
ﻟﻔﻌـــل ﻫـــﺫﺍ ﺃﺯل ﻋﻼﻤـــﺔ ﺍﻻﺨﺘﻴـــﺎﺭ ﻤـــﻥ ﻤﺭﺒـــﻊ ﺍﻻﺨﺘﻴـــﺎﺭ
Hide System Assembliesﻟﺘﻅﻬﺭ ﻓﺌﺎﺕ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﻓﻲ ﺍﻟﻘﺎﺌﻤـﺔ ..ﻭﺇﺫﺍ ﺃﺭﺩﺕ
ﻋـــﺭﺽ ﻓﺌـــﺎﺕ ﻤـــﻥ ﺨـــﺎﺭﺝ ﻤﺸـــﺭﻭﻋﻙ ،ﻓﺎﻀـــﻐﻁ ﺍﻟـــﺯﺭ
Add Referenceﻭﺃﻀﻑ ﻤﺭﺠﻌﺎ ﺇﻟﻰ ﺍﻟﻤﻜﺘﺒﺎﺕ ﺍﻟﺘﻲ ﺘﻭﺠﺩ ﺒﻬﺎ ..ﻭﺒﻌﺩ ﺃﻥ ﺘﺨﺘـﺎﺭ
ﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ﺃﻭ ﺃﻜﺜﺭ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ،ﺍﻀﻐﻁ ﺍﻟﺯﺭ .Finish
-٤ﺘﻁﺒﻴﻕ :SharePoint
ﻴﺘﻴﺢ ﻟﻙ ﻫﺫﺍ ﺍﻟﻨﻭﻉ ﺇﻨﺸﺎﺀ ﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ﻴﻨﺎﺴﺏ ﺘﻁﺒﻴﻕ ..SharePoint 2010ﻫـﺫﺍ
ﺍﻟﻤﻭﻀﻭﻉ ﺨﺎﺭﺝ ﻨﻁﺎﻕ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ.
ﺩﻋﻨــﺎ ﺍﻵﻥ ﻨﺘﻌﺎﻤــل ﻤــﻊ ﺍﻟﻨــﻭﻉ ﺍﻷﻜﺜــﺭ ﻤﻼﺀﻤــﺔ ﻟﻨــﺎ ﻫﻨــﺎ ..ﺍﺨﺘــﺭ ﺍﻟﻨــﻭﻉ
Databaseﻭﺍﻀﻐﻁ ﺍﻟﺯﺭ ..Nextﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓـﺫﺓ ﺍﺨﺘﻴـﺎﺭ ﻨﻤـﻭﺫﺝ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ
Database Modelﻜﻤﺎ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
٤٠٥
ﺴﺘﺠﺩ ﻓﻲ ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﺨﻴﺎﺭﻴﻥ:
ﺃ:DataSet -
ﻴﺘﻡ ﺇﻨﺸﺎﺀ ﻤﺠﻤﻭﻋﻭ ﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ،ﻭﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻜﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ.
ﺏ:Entity Data Model -
ﻫــﺫﺍ ﺍﻻﺨﺘﻴــﺎﺭ ﻤﻨﺎﺴــﺏ ﻟﻠﻤﺸــﺎﺭﻴﻊ ﺍﻟﺘــﻲ ﺘﺴــﺘﺨﺩﻡ LinQ-To-SQL
ﻭ ،Entity Frameworkﻭﺴﻨﺅﺠﻠﻪ ﺇﻟﻰ ﺍﻟﻜﺘﺎﺏ ﺍﻟﻘﺎﺩﻡ ﺒﺈﺫﻥ ﺍﷲ.
ﺍﺨﺘﺭ DataSetﻭﺍﻀﻐﻁ .Next
ﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻗﺩ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻜﺜﻴﺭﺍ ﻤﻥ ﻗﺒل ..ﺍﺨﺘﺭ ﺍﻻﺘﺼـﺎل
ﺒﻘﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ ،Books.mdfﻭﺍﻀﻐﻁ .Next
ﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﺘﺴﺄﻟﻙ ﺇﻥ ﻜﻨﺕ ﺘﺭﻴﺩ ﺤﻔﻅ ﻨﺹ ﺍﻻﺘﺼﺎل ﻓﻲ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻤﺸﺭﻭﻉ Settings
ﺃﻡ ﻻ ..ﺍﺘﺭﻙ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﻜﻤﺎ ﻫﻲ ،ﻭﻋﺩل ﺍﻻﺴﻡ ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺃﻥ ﺘﺴـﺘﺨﺩﻤﻪ ﻟﺤﻔـﻅ ﻨـﺹ
ﺍﻻﺘﺼﺎل ﻓﻲ ﺍﻹﻋﺩﺍﺩﺍﺕ ﻟﻭ ﺃﺭﺩﺕ ،ﻭﺍﻀﻐﻁ .Next
ﺍﻨﺘﻅﺭ ﻟﺤﻅﺔ ﺇﻟﻰ ﺃﻥ ﻴﺘﻡ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺘﺤﻤﻴل ﻤﻜﻭﻨﺎﺘﻬﺎ ..ﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﺘﺘـﻴﺢ
ﺍﺨﺘﻴﺎﺭ ﻜﺎﺌﻨﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻬﺎ ،ﻭﺴﺘﺠﺩ ﻓﻴﻬﺎ ﻜل ﺍﻟﺠﺩﺍﻭل ﻭﺍﻟﻌـﺭﻭﺽ
ﻭﺍﻹﺠﺭﺍﺀﺍﺕ ﺍﻟﻤﺨﺯﻨﺔ ﺍﻟﻤﺘﺎﺤﺔ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻜﻤﺎ ﺘﺒﻴﻥ ﺍﻟﺼﻭﺭﺓ:
ﺍﺨﺘﺭ ﺠﺩﻭﻟﻲ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺍﻟﻜﺘﺏ ﻓﻲ ﻤﺜﺎﻟﻨﺎ ﻫﺫﺍ ..ﻻﺤﻅ ﺃﻥ ﻭﻀﻊ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﺒﺠﻭﺍﺭ ﺍﺴـﻡ
ﺍﻟﺠﺩﻭل ﻴﺤﺩﺩ ﻜل ﺃﻋﻤﺩﺘﻪ ..ﻟﻜﻨﻙ ﺘﺴﺘﻁﻴﻊ ﻀﻐﻁ ﺍﻟﻌﻼﻤﺔ +ﺍﻟﻤﺠﺎﻭﺭﺓ ﻻﺴﻡ ﺍﻟﺠﺩﻭل ﻹﺴـﺩﺍل
ﺃﻋﻤﺩﺘﻪ ،ﺤﻴﺙ ﻴﻤﻜﻨﻙ ﺇﺯﺍﻟﺔ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﺍﻟﻤﺠﺎﻭﺭﺓ ﻟﺒﻌﻀﻬﺎ ،ﻭﺒﻬﺫﺍ ﺘﻭﻓﺭ ﻋﻠـﻰ ﺒﺭﻨﺎﻤﺠـﻙ
ﺘﺠﻤﻴل ﺒﻴﺎﻨﺎﺕ ﻻ ﻀﺭﻭﺭﺓ ﻟﻬﺎ.
ﻭﻴﻤﻜﻨﻙ ﺘﻌﺩﻴل ﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ ﻟﻔﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ،ﻤﻥ ﺨﻼل ﻤﺭﺒﻊ ﺍﻟﻨﺹ
ﺍﻟﺴﻔﻠﻲ.
ﺘﺴﺘﻁﻴﻊ ﺍﻵﻥ ﺃﻥ ﺘﻀﻐﻁ Finishﻹﻨﻬﺎﺀ ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﻭﺇﻨﺸـﺎﺀ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﺃﻭ
ﻴﻤﻜﻨﻙ ﻭﻀﻊ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﺃﻤﺎﻡ ﺍﻻﺨﺘﻴﺎﺭ:
Enable Local Database Caching
٤٠٦
ﻫﺫﺍ ﺍﻻﺨﺘﻴﺎﺭ ﻴﺘﻴﺢ ﺤﻔﻅ ﺒﻌﺽ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺠﺩﺍﻭل ﻋﻠـﻰ ﺠﻬـﺎﺯ ﺍﻟﻤﺴـﺘﺨﺩﻡ ﻟﺘﻜـﻭﻥ ﺠـﺎﻫﺯﺓ
ﻟﻼﺴﺘﺨﺩﺍﻡ ،ﻭﺫﻟﻙ ﺇﺫﺍ ﻜﺎﻥ ﻤﻌﺩل ﺘﻐﻴﺭﻫﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻁﻴﺌﺎ ،ﻤﻤﺎ ﻴﻘﻠل ﻤﻥ ﻋﺩﺩ ﻤـﺭﺍﺕ
ﺍﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ ،ﻭﺒﺎﻟﺘﺎﻟﻲ ﻴﺤﺴﻥ ﺃﺩﺍﺀ ﻭﺴﺭﻋﺔ ﺍﻟﺒﺭﻨﺎﻤﺞ ..ﺇﺫﺍ ﺍﺨﺘﺭﺕ ﻫﺫﺍ ﺍﻟﺨﻴﺎﺭ ،ﻓﻌﻠﻴـﻙ ﺃﻥ
ﺘﻀﻐﻁ Nextﻟﻤﻭﺍﺼﻠﺔ ﺍﻟﻤﻌﺎﻟﺞ ..ﻟﻜﻨﻨﺎ ﺴﻨﺘﺭﻙ ﻫﺫﺍ ﺇﻟﻰ ﺍﻟﻜﺘـﺎﺏ ﺍﻟﻘـﺎﺩﻡ ..ﺍﻀـﻐﻁ Finish
ﻹﻨﻬﺎﺀ ﺍﻟﻤﻌﺎﻟﺞ.
ﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻟﻰ ﺇﻀﺎﻓﺔ ﺍﻟﻤﻠﻑ BooksDataSet.xsdﺇﻟﻰ ﺍﻟﻤﺸﺭﻭﻉ ..ﻭﻟـﻭ ﻨﻘـﺭﺕ ﻫـﺫﺍ
ﺍﻟﻤﻠﻑ ﻤﺭﺘﻴﻥ ،ﻓﺴﻴﻅﻬﺭ ﻤﺼﻤﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺍﻟﺫﻱ ﺴﻴﻌﺭﺽ ﻟﻙ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺠﺩﻭل
ﺍﻟﻜﺘﺏ ﻭﺍﻟﻌﻼﻗﺔ ﺒﻴﻨﻬﻤﺎ ،ﻜﻤﺎ ﺴﺘﺠﺩ ﻓﻴﻪ ﻤﻬﻴـﺊ ﺠـﺩﻭل ﺍﻟﻤـﺅﻟﻔﻴﻥ AuthorDataAdapter
ﻭﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ .BookDataAdapter
٤٠٧
ﻤﺘﺼﻔﺢ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ:
ﻟﻭ ﻓﺘﺤﺕ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺭﺌﻴﺴﻴﺔ Dataﻭﻀﻐﻁﺕ ﺍﻷﻤﺭ ،Show Data Sourcesﻓﺴـﻴﻅﻬﺭ ﻟـﻙ
ﻤﺘﺼﻔﺢ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ Data Sources Explorerﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ.
:Add New Data Sourceﻴﺅﺩﻱ ﻀﻐﻁ ﻫﺫﺍ ﺍﻟﺯﺭ ﺇﻟﻰ ﺘﺸﻐﻴل ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴـﺤﺭﻱ
ﻟﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ.
:Edit Data Source With Designerﻴﺅﺩﻱ ﻀﻐﻁ ﻫـﺫﺍ ﺍﻟـﺯﺭ ﻓـﺘﺢ ﻤﺼـﻤﻡ
ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﺘﺤﺭﻴﺭﻫﺎ.
:Configure Data Source With Wizardﻴﺅﺩﻱ ﻀﻐﻁ ﻫﺫﺍ ﺍﻟﺯﺭ ﺇﻟﻰ ﺘﺸـﻐﻴل
ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﻟﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻟﻜﻨﻪ ﻴﻌﺭﺽ ﻨﺎﻓﺫﺓ ﺍﺨﺘﻴﺎﺭ ﻜﺎﺌﻨﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻤﺒﺎﺸﺭﺓ ،ﻟﺘﺴﺘﻁﻴﻊ ﺇﻀﺎﻓﺔ ﺍﻟﺠﺩﺍﻭل ﺃﻭ ﺤﺫﻓﻬﺎ.
٤٠٨
:Refreshﻴﺅﺩﻱ ﻀﻐﻁ ﻫﺫﺍ ﺍﻟﺯﺭ ﺇﻟﻰ ﺇﻨﻌﺎﺵ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻟﻠﺘﻔﺎﻋـل ﻤـﻊ ﺃﻴـﺔ
ﺘﻐﻴﻴﺭﺍﺕ ﺤﺩﺜﺕ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﻭﻴﻘﺩﻡ ﻟﻙ ﻤﺘﺼﻔﺢ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺴﻬﻴﻼﺕ ﻫﺎﺌﻠﺔ ﻟﺘﺼﻤﻴﻡ ﺍﻟﻨﻤﺎﺫﺝ ﺍﻟﺘﻲ ﺘﻌـﺭﺽ ﺍﻟﺒﻴﺎﻨـﺎﺕ،
ﻓﺒﻤﺠﺭﺩ ﺴﺤﺏ ﺍﺴﻡ ﺃﻱ ﺤﻘل ﻤﻥ ﻤﺘﺼﻔﺢ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺇﻟﻘﺎﺌﻪ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ،ﻴﺘﻡ ﺇﻀـﺎﻓﺔ
ﺍﻟﻌﺩﻴﺩ ﻤﻥ ﺍﻷﺩﻭﺍﺕ ﺇﻟﻰ ﺍﻟﻨﻤﻭﺫﺝ ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
٤٠٩
-٥ﻨﺴﺨﺔ ﻤﻥ ﻤﺩﻴﺭ ﺍﻟﺘﻭﺼﻴل TableAdapterManagerﻟﻠﺘﺤﻜﻡ ﺘﺤـﺩﻴﺙ ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ.
-٦ﺃﺩﺍﺓ ﻤﺼﺩﺭ ﺍﻟﺭﺒﻁ BindingSourceﻻﺴﺘﺨﺩﺍﻤﻬﺎ ﻓـﻲ ﺭﺒـﻁ ﺍﻷﺩﻭﺍﺕ ﺒﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻷﺩﺍﺓ ﺒﻌﺩ ﻗﻠﻴل.
-٧ﻨﺴﺨﺔ ﻤﻥ ﺍﻷﺩﺍﺓ BindingNavigatorﻟﺘﺘﻴﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺍﻟﺘﺤﺭﻙ ﻋﺒﺭ ﺍﻟﺴـﺠﻼﺕ..
ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻷﺩﺍﺓ ﺒﻌﺩ ﻗﻠﻴل.
-٨ﻴﺘﻡ ﺇﻨﺘﺎﺝ ﻜﻭﺩ ﺘﺤﻤﻴل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺤﺩﺙ ﺘﺤﻤﻴل ﺍﻟﻨﻤﻭﺫﺝ Loadﺁﻟﻴﺎ ..ﻤﺜﻼ ،ﻴﺘﻡ ﺇﻨﺘﺎﺝ
ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻟﻴﻤﻸ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﺒﺎﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ:
;)this.AuthorsTableAdapter.Fill(this.BooksDataSet.Authors
-٩ﻴﻀﺎﻑ ﺯﺭ ﻟﺤﻔﻅ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺇﻟﻰ ﺸـﺭﻴﻁ ﻤﻭﺠـﻪ ﺍﻟـﺭﺒﻁ ،BindingNavigator
ﻭﻴﻀﺎﻑ ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﺇﻟﻰ ﺤﺩﺙ ﻀﻐﻁ ﻫﺫﺍ ﺍﻟﺯﺭ:
;) (this.Validate
;) (this.AuthorsBindingSource.EndEdit
;)this.TableAdapterManager.UpdateAll(this.BooksDataSet
ﺃﻟﻴﺱ ﺸﻴﺌﺎ ﺭﺍﺌﻌﺎ؟ ..ﺃﻨﺕ ﻻ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﻓﻌل ﺃﻱ ﺸﻲﺀ ﺘﻘﺭﻴﺒﺎ ،ﺴﻭﻯ ﺴﺤﺏ ﺍﻟﺤﻘـﻭل ﻭﺇﻟﻘﺎﺌﻬـﺎ
ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ﻟﺘﺤﺼل ﻋﻠﻰ ﺒﺭﻨﺎﻤﺞ ﻜﺎﻤل ﺍﻟﻭﻅﻴﻔﺔ!
ﻭﻴﻤﻜﻨﻙ ﺭﺒﻁ ﺍﻟﺤﻘل ﺒﺎﻷﺩﺍﺓ ﺒﻁﺭﻴﻘﺔ ﺃﺨﺭﻯ ،ﻭﺫﻟﻙ ﺒﻭﻀﻊ ﺍﻷﺩﺍﺓ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ﺃﻭﻻ ،ﺜﻡ ﺴـﺤﺏ
ﺍﻟﺤﻘل ﻤﻥ ﻨﺎﻓﺫﺓ ﺍﻟﻤﺼﺎﺩﺭ ﻭﺇﻟﻘﺎﺌﻪ ﻋﻠﻰ ﺍﻷﺩﺍﺓ ..ﻫﺫﺍ ﺴﻴﻀﺒﻁ ﺨﺼﺎﺌﺹ ﺍﻷﺩﺍﺓ ﺘﻠﻘﺎﺌﻴﺎ ﻟﺘﻌـﺭﺽ
ﻗﻴﻤﺔ ﻫﺫﺍ ﺍﻟﺤﻘل.
ﻜﻤﺎ ﺃﻨﻙ ﻟﺴﺕ ﻤﺠﺒﺭﺍ ﻋﻠﻰ ﻋﺭﺽ ﻗﻴﻤﺔ ﺍﻟﺤﻘل ﻓﻲ ﻤﺭﺒﻊ ﻨﺹ ،ﻓﻠﻭ ﺤﺩﺩﺕ ﺍﺴﻡ ﺍﻟﺤﻘـل ﻓـﻲ
ﻤﺘﺼﻔﺢ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﺴﻴﻅﻬﺭ ﺯﺭ ﺇﺴﺩﺍل ﺒﺠـﻭﺍﺭﻩ ،ﻭﻟـﻭ ﻀـﻐﻁﺘﻪ ﻓﺴـﺘﻅﻬﺭ ﻗﺎﺌﻤـﺔ
ﻤﻭﻀﻌﻴﺔ ،ﺒﻬﺎ ﺃﺴﻤﺎﺀ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺘﻲ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﻌﺭﺽ ﻗﻴﻤﺔ ﺍﻟﺤﻘـل ..ﻭﻟـﻭ ﺍﺨﺘـﺭﺕ
ﺍﻟﻘﻴﻤﺔ Noneﻓﻠﻥ ﻴﺘﻡ ﻭﻀﻊ ﺃﺩﻭﺍﺕ ﻟﻌﺭﺽ ﻫﺫﺍ ﺍﻟﺤﻘل ﻋﻨﺩ ﺇﻟﻘﺎﺌﻪ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ.
ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﻴﻜﻭﻥ ﻤﺭﺒﻊ ﺍﻟﻨﺹ TextBoxﻫﻭ ﺍﻷﺩﺍﺓ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻟﻌﺭﺽ ﻗﻴﻤـﺔ
ﺍﻟﺤﻘل ،ﻟﻜﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺍﺨﺘﻴﺎﺭ ﺃﻴﺔ ﺃﺩﺍﺓ ﺃﺨﺭﻯ ﻟﺠﻌﻠﻬﺎ ﺘﻌﺭﺽ ﻗﻴﻤﺘـﻪ ..ﻭﻟـﻭ ﻟـﻡ ﺘﺠـﺩ ﺍﻷﺩﺍﺓ
٤١٠
ﺍﻟﻤﻨﺎﺴﺒﺔ ﺒﻴﻥ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻅﺎﻫﺭﺓ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ،ﻓﺎﻀﻐﻁ ﺍﻷﻤﺭ Customizeﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻨﻬﺎﻴـﺔ
ﺍﻟﻘﺎﺌﻤﺔ ﻟﻌﺭﺽ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﻤﻭﻀﺤﺔ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
ﻓﻲ ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﻴﻤﻜﻨﻙ ﺍﺨﺘﻴﺎﺭ ﻨﻭﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ،Data Typeﻟﺘﻅﻬـﺭ ﻓـﻲ
ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺴﻔﻠﻴﺔ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺘﻲ ﻴﻤﻜﻨﻬﺎ ﻋﺭﺽ ﻫﺫﺍ ﺍﻟﻨﻭﻉ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺤﻴـﺙ ﺴـﺘﺠﺩ ﻋﻼﻤـﺔ
ﺍﻻﺨﺘﻴﺎﺭ ﺒﺠﻭﺍﺭ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﺴﻤﻭﺡ ﺒﺎﺴﺘﺨﺩﺍﻤﻬﺎ ،ﻭﻴﻤﻜﻨﻙ ﻭﻀﻊ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﺒﺠـﻭﺍﺭ ﺃﻴـﺔ
ﺃﺩﻭﺍﺕ ﺃﺨﺭﻯ ﺘﺭﻴﺩ ﺃﻥ ﺘﺴﻤﺢ ﺒﺎﺴﺘﺨﺩﺍﻤﻬﺎ ﻤﻊ ﻫﺫﺍ ﺍﻟﻨﻭﻉ ،ﺜﻡ ﺘﻀﻐﻁ .OK
ﻭﺘﺘﻴﺢ ﻟﻙ ﻨﺎﻓﺫﺓ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﺠﺩﻭل ﻜﻠﻪ ﺩﻓﻌﺔ ﻭﺍﺤﺩﺓ ..ﻓﻠـﻭ ﺤـﺩﺩﺕ ﺍﺴـﻡ
ﺍﻟﺠﺩﻭل Authorsﻓﻲ ﻤﺘﺼﻔﺢ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻓﺴﻴﻅﻬﺭ ﺯﺭ ﺇﺴﺩﺍل ﺒﺠﻭﺍﺭﻩ ،ﻭﻋﻨﺩ ﻀﻐﻁﻪ
ﺴﺘﻅﻬﺭ ﻗﺎﺌﻤﺔ ﻤﻭﻀﻌﻴﺔ ﺒﻬﺎ ﺍﻟﺨﻴﺎﺭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ:
:None -ﻻ ﻴﺘﻡ ﻭﻀﻊ ﺃﻴﺔ ﺃﺩﻭﺍﺕ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ﻋﻨﺩ ﺇﺴﻘﺎﻁ ﺍﻟﺠﺩﻭل ﻋﻠﻴﻪ.
:DataGridView -ﻟﻭ ﺍﺨﺘﺭﺕ ﻫﺫﺍ ﺍﻟﺨﻴﺎﺭ ،ﻭﺴﺤﺒﺕ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺃﻟﻘﻴﺘـﻪ ﻋﻠـﻰ
ﺍﻟﻨﻤﻭﺫﺝ ،ﻓﺴﻴﻀﺎﻑ ﺠﺩﻭل ﻋﺭﺽ ﺇﻟﻰ ﺍﻟﻨﻤﻭﺫﺝ ،ﻭﺴﻴﺤﺘﻭﻱ ﻋﻠـﻰ ﺃﻋﻤـﺩﺓ ﻟﻌـﺭﺽ
ﺤﻘﻭل ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ..ﻭﻴﺅﺩﻱ ﻀﻐﻁ ﺃﺯﺭﺍﺭ ﺍﻟﺘﺤﺭﻙ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻋﻠﻰ ﺸﺭﻴﻁ ﻤﻭﺠـﻪ
ﺍﻟﺭﺒﻁ ،ﺇﻟﻰ ﺘﻐﻴﻴﺭ ﺍﻟﺴﺠل ﺍﻟﻤﺤﺩﺩ ﺤﺎﻟﻴﺎ ﻓﻲ ﺠـﺩﻭل ﺍﻟﻌـﺭﺽ ،ﻜﻤـﺎ ﺃﻥ ﻀـﻐﻁ ﺯﺭ
٤١١
ﺍﻟﺤﺫﻑ ﺴﻴﺤﺫﻑ ﺍﻟﺴﺠل ﺍﻟﻤﺤﺩﺩ ﺤﺎﻟﻴﺎ ،ﻭﻀﻐﻁ ﺯﺭ ﺍﻹﻀﺎﻓﺔ ﺴﻴﻀﻴﻑ ﺴﺠﻼ ﺠﺩﻴـﺩﺍ
ﺇﻟﻰ ﻨﻬﺎﻴﺔ ﺠﺩﻭل ﺍﻟﻌﺭﺽ.
:Details -ﻟﻭ ﺍﺨﺘﺭﺕ ﻫﺫﺍ ﺍﻟﺨﻴﺎﺭ ،ﻭﺴﺤﺒﺕ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺃﻟﻘﻴﺘﻪ ﻋﻠـﻰ ﺍﻟﻨﻤـﻭﺫﺝ،
ﻓﺴﺘﻀﺎﻑ ﺃﺩﺍﺓ ﻋﺭﺽ ﺨﺎﺼﺔ ﺒﻜل ﺤﻘل ﻋﻠﻰ ﺤﺩﺓ ،ﻭﺒﺠﻭﺍﺭﻫﺎ ﻻﻓﺘﺔ ﺘﺤﻤل ﺍﺴﻡ ﻫـﺫﺍ
ﺍﻟﺤﻘل ..ﻭﻴﺨﺘﻠﻑ ﻨﻭﻉ ﺃﺩﺍﺓ ﺍﻟﻌﺭﺽ ﺍﻟﺨﺎﺼﺔ ﺒﻜل ﺤﻘل ﻋﻠﻰ ﺤﺴﺏ ﺍﻻﺨﺘﻴـﺎﺭ ﺍﻟـﺫﻱ
ﺤﺩﺩﺘﻪ ﻟﻜل ﺤﻘل )ﻜﻤﺎ ﺸﺭﺤﻨﺎ ﺴﺎﺒﻘﺎ( ..ﻤﺜﻼ :ﻗﺒل ﺃﻥ ﺘﺴﺤﺏ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﻋﻠﻴـﻙ
ﺃﻥ ﺘﻐﻴﺭ ﻨﻭﻉ ﺃﺩﺍﺓ ﻋﺭﺽ ﺍﻟﺤﻘل IDﺇﻟﻰ ﻻﻓﺘﺔ Lableﺤﺘـﻰ ﻻ ﺘﺴـﻤﺢ ﻟﻠﻤﺴـﺘﺨﺩﻡ
ﺒﺘﻐﻴﺭﻩ ،ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺍﺨﺘﻴﺎﺭ ﺍﻟﻌﻨﺼﺭ Noneﻤﻊ ﺍﻟﺤﻘل CountryIDﻟﻤﻨـﻊ ﻋـﺭﺽ
ﺭﻗﻡ ﺩﻭﻟﺔ ﺍﻟﻤﺅﻟﻑ ..ﺒﻌﺩ ﻫﺫﺍ ﻟﻭ ﺴﺤﺒﺕ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺃﻟﻘﻴﺘﻪ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ،ﻓﺴـﻴﺘﻡ
ﻭﻀﻊ ﺍﻷﺩﻭﺍﺕ ﻋﻠﻴﻪ ﻜﻤﺎ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
ﺍﻷﻤﺭ ﺭﺍﺌﻊ ﻓﻌﻼ ،ﻓﻘﺩ ﻜﺎﻥ ﺘﺼﻤﻴﻡ ﺍﻟﻨﻤﺎﺫﺝ ﻓﻲ ﻤﺸﺎﺭﻴﻊ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺴﺘﻬﻠﻙ ﻤﻌﻅـﻡ ﻭﻗـﺕ
ﺇﻨﺘﺎﺝ ﺍﻟﺒﺭﻨﺎﻤﺞ ..ﻟﻜﻥ ﺍﻵﻥ ﺼﺎﺭ ﺍﻷﻤﺭ ﻓﻲ ﻤﻨﺘﻬﻰ ﺍﻟﺒﺴﺎﻁﺔ ،ﻓﺄﻨﺕ ﺘﺤﺼل ﻋﻠﻰ ﻤﻌﻅﻡ ﺍﻟﻌﻤـل
ﺒﺎﻟﺴﺤﺏ ﻭﺍﻹﺴﻘﺎﻁ ،ﻤﻊ ﺍﻟﻜﺜﻴﺭ ﻤﻥ ﺍﻟﻜﻭﺩ ﺍﻟﻤﻭﻟﺩ ﺁﻟﻴﺎ!
ﻭﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﻨﻤﺎﺫﺝ ﺃﺨﺭﻯ ﺇﻟﻰ ﺍﻟﻤﺸﺭﻭﻉ ،ﻭﺇﺴﻘﺎﻁ ﺠﺩﺍﻭل ﺃﺨﺭﻯ ﻋﻠﻴﻬﺎ.
ﻭﺘﻤﺘﻠﻙ ﻨﺎﻓﺫﺓ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻴﺯﺓ ﺇﻀﺎﻓﻴﺔ ﻫﺎﻤﺔ ،ﻫـﻲ ﺍﻟﺴـﻤﺎﺡ ﻟـﻙ ﺒﻌـﺭﺽ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺍﻟﻤﺘﺭﺍﺒﻁﺔ ..ﻭﻟﻭ ﺃﺴﺩﻟﺕ ﻋﻨﺎﺼﺭ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ،ﻓﺴﺘﺠﺩ ﺁﺨﺭ ﻋﻨﺼﺭ ﻤﻨﻬﺎ ﺍﺴـﻤﻪ ..Books
ﻫﺫﺍ ﺍﻟﻌﻨﺼﺭ ﺃﻀﻴﻑ ﻟﻴﻤﺜل ﺍﻟﻌﻼﻗﺔ ﺍﻟﻤﻌﺭﻓﺔ ﺒﻴﻥ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺠﺩﻭل ﺍﻟﻜﺘﺏ ﻓﻲ ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ ..BooksDataSetﻭﻟﻭ ﺃﺴﺩﻟﺕ ﺍﻟﻌﻨﺼﺭ ،Booksﻓﺴﺘﺠﺩ ﺘﺤﺘﻪ ﻜل ﺤﻘﻭل ﺠـﺩﻭل
٤١٢
ﺍﻟﻜﺘﺏ ،ﻭﻟﻭ ﺴﺤﺒﺘﻬﺎ ﻭﺃﻟﻘﻴﺘﻬﺎ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ،ﻓﺴﺘﻌﺭﺽ ﺒﻴﺎﻨﺎﺕ ﻜﺘﺏ ﺍﻟﻤﺅﻟﻑ ﺍﻟﺤﺎﻟﻲ ..ﻻﺤـﻅ
ﺃﻨﻪ ﻤﻥ ﻏﻴﺭ ﺍﻟﻤﻨﻁﻘﻲ ﻋﺭﺽ ﻜل ﺤﻘل ﻓﺭﻋﻲ ﻋﻠﻰ ﺤﺩﺓ ﺇﺫﺍ ﻜﻨﺕ ﺘﺘﻌﺎﻤل ﻤﻊ ﻋﻼﻗـﺔ ﻭﺍﺤـﺩ
ﺒﻤﺘﻌﺩﺩ ..One-To-Manyﻓﺎﻟﻤﻨﺎﺴﺏ ﻓﻲ ﻤﺜﺎﻟﻨﺎ ﻫﺫﺍ ،ﺍﺴﺘﺨﺩﺍﻡ ﺠﺩﻭل ﻟﻌﺭﺽ ﻜﺘﺏ ﺍﻟﻤﺅﻟـﻑ
ﺍﻟﺤﺎﻟﻲ ،ﻜﻤﺎ ﺘﺭﻯ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
٤١٤
-ﺘﻭﺠﻪ ﺇﻟﻰ ﺍﻟﺨﺎﺼﻴﺔ ،DataMemberﻭﺍﻀﻐﻁ ﺯﺭ ﺍﻹﺴﺩﺍل ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﺨﺘﺭ ﺍﻟﺤﻘـل
..Bookﺍﻵﻥ ﺘﺄﻜﺩﻨﺎ ﺃﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﺭﻜﺒﺔ ﺴﺘﻌﺭﺽ ﻜﺘﺏ ﺍﻟﻤﺅﻟﻑ ﺍﻟﺤﺎﻟﻲ ،ﻷﻨﻨﺎ ﺭﺒﻁﻨﺎﻫـﺎ
ﻤﻥ ﺨﻼل ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺍﻟﻜﺘﺏ.
-ﻤﻥ ﻨﺎﻓﺫﺓ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﺴـﺤﺏ ﺍﻟﺤﻘـل Authors.Books.Priceﻭﺃﻟﻘـﻪ ﻋﻠـﻰ
ـﻴﺔ
ـﺘﺢ ﺍﻟﺨﺎﺼـ
ـﺎﺌﺹ ﻭﺍﻓـ
ـﺫﺓ ﺍﻟﺨﺼـ
ـﺘﺢ ﻨﺎﻓـ
ـﻨﺹ ﻭﺍﻓـ
ـﻊ ﺍﻟـ
ـﺩﺩ ﻤﺭﺒـ
ـﻭﺫﺝ ..ﺤـ
ﺍﻟﻨﻤـ
) ،(DataBindingsﻭﺘﻭﺠﻪ ﺇﻟﻰ ﺍﻟﺨﺎﺼﻴﺔ ..Textﺍﻀﻐﻁ ﺯﺭ ﺍﻹﺴﺩﺍل ،ﻭﺍﺨﺘﺭ ﺍﻟﻌﻨﺼﺭ
FKBooksAuthorsBindingSourceﻟﺭﺒﻁ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﻤﻥ ﺨﻼل ﺍﻟﻌﻼﻗﺔ.
-ﻴﻤﻜﻨﻙ ﺘﻜﺭﺍﺭ ﻫﺫﺍ ﻤﻊ ﺃﻜﺜﺭ ﻤﻥ ﺤﻘل ﻤﻥ ﺤﻘﻭل ﺠﺩﻭل ﺍﻟﻜﺘﺏ ..ﻤﺜﻼ ،ﻟﻭ ﺴـﺤﺒﺕ ﺍﻟﺤﻘـل
Publish_Dateﻭﺃﻟﻘﻴﺘﻪ ﻋﻠـﻰ ﺍﻟﻨﻤـﻭﺫﺝ ،ﻓﺴـﺘﻅﻬﺭ ﺃﺩﺍﺓ ﺍﺨﺘﻴـﺎﺭ ﺍﻟﺘـﺎﺭﻴﺦ ﻭﺍﻟﻭﻗـﺕ
DateDateTimePickerﻟﻌﺭﺽ ﻗﻴﻤﺘﻪ ..ﻭﺃﻴﻀﺎ ﻋﻠﻴﻙ ﺃﻥ ﺘﻐﻴـﺭ ﺍﺭﺘﺒـﺎﻁ ﺍﻟﺨﺎﺼـﻴﺔ
Valueﺍﻟﺨﺎﺼــﺔ ﺒﻬــﺫﻩ ﺍﻷﺩﺍﺓ ،ﻟﺘﺠﻌﻠﻬــﺎ ﺘــﺭﺘﺒﻁ ﻤــﻥ ﺨــﻼل ﺍﻟﻤﺼــﺩﺭ
FKBooksAuthorsBindingSourceﻜﻤﺎ ﻓﻌﻠﻨﺎ ﻤﻊ ﻤﺭﺒﻊ ﺍﻟﻨﺹ.
ﺍﻵﻥ ﺴﻴﻜﻭﻥ ﺸﻜل ﺍﻟﻨﻤﻭﺫﺝ ﻜﺎﻟﺘﺎﻟﻲ:
ﻟﻭ ﺸﻐﻠﺕ ﺍﻟﺒﺭﻨﺎﻤﺞ ،ﻓﺴﻴﻤﻜﻨﻙ ﺍﻟﺘﺤﺭﻙ ﻋﺒﺭ ﺍﻟﻤﺅﻟﻔﻴﻥ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺸﺭﻴﻁ ﻤﻭﺠﻪ ﺍﻟـﺭﺒﻁ ،ﺤﻴـﺙ
ﺴﺘﻅﻬﺭ ﻜﺘﺏ ﺍﻟﻤﺅﻟﻑ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﺭﻜﺒﺔ ،ﻭﻜﻠﻤﺎ ﺍﺨﺘﺭﺕ ﻜﺘﺎﺒﺎ ﻤﻨﻬﺎ ،ﻴﻅﻬـﺭ ﺘـﺎﺭﻴﺦ
٤١٥
ﻨﺸﺭﻩ ﻓﻲ ﺃﺩﺍﺓ ﺍﻟﺘﺎﺭﻴﺦ ،ﻭﺴﻌﺭﻩ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ..ﻭﺒﻬﺫﺍ ﻨﻜﻭﻥ ﺤﺼﻠﻨﺎ ﻋﻠﻰ ﻁﺭﻴﻘـﺔ ﺃﺨـﺭﻯ
ﻟﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺭﺌﻴﺴﻴﺔ ﻭﺍﻟﺘﻔﺎﺼﻴل ﻭﺘﻔﺎﺼﻴل ﺍﻟﺘﻔﺎﺼﻴل! ..ﺼـﺤﻴﺢ ﺃﻨﻨـﺎ ﺃﺠﺭﻴﻨـﺎ ﺒﻌـﺽ
ﺍﻟﺘﻌﺩﻴﻼﺕ ﺍﻟﻴﺩﻭﻴﺔ ﻫﺫﻩ ﺍﻟﻤﺭﺓ ،ﻭﻟﻜﻥ ﺼﺤﻴﺢ ﺃﻴﻀﺎ ﺃﻨﻨﺎ ﺇﻟﻰ ﺍﻵﻥ ﻟﻡ ﻨﻜﺘﺏ ﺴﻁﺭﺍ ﻭﺍﺤـﺩﺍ ﻤـﻥ
ﺍﻟﻜﻭﺩ ﺒﺄﻨﻔﺴﻨﺎ! ..ﻤﺭﺤﻰ ،ﻤﺎ ﺃﻤﺘﻊ ﺍﻟﻜﺴل!
٤١٦
ﻭﺍﺠﻬﺔ ﻤﺯﻭﺩ ﻤﺩﻴﺭ ﺍﻟﺘﺴﻠﺴل
ICurrencyManagerProvider Interface
٤١٧
ﻭﺍﺠﻬﺔ ﺇﻟﻐﺎﺀ ﺇﻀﺎﻓﺔ ﺍﻟﺠﺩﻴﺩ
ICancelAddNew Interface
ﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺇﻟﻰ ﺍﻟﻔﺌﺔ ﺍﻟﺘﻲ ﺘﻤﺜﻠﻬﺎ ﺍﻟﻘﺩﺭﺓ ﻋﻠﻰ ﻗﺒﻭل ﺍﻟﻌﻨﺼـﺭ ﺍﻟﺠﺩﻴـﺩ ﺍﻟﻤﻀـﺎﻑ ﺃﻭ
ﺍﻟﺘﺭﺍﺠﻊ ﻋﻥ ﺇﻀﺎﻓﺘﻪ ،ﻭﻫﻲ ﺘﻤﻠﻙ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
٤١٨
ﻓﺌﺔ ﻗﺎﺌﻤﺔ ﺍﻟﺭﺒﻁ ﻋﺎﻤﺔ ﺍﻟﻨﻭﻉ BindingList<T> Class
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ،ﻭﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﺍﻟﻭﺍﺠﻬﺎﺕ ﺍﻟﻤـﺫﻜﻭﺭﺓ،
ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺠﺩﻴﺩﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
٤١٩
ﻭﺍﺠﻬﺔ ﻤﺼﺩﺭ ﺍﻟﻘﺎﺌﻤﺔ
IListSource Interface
ﺘﻌﻤل ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﻜﻤﺼﺩﺭ ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻗﺎﺌﻤﺔ Listﻤﻥ ﻜﺎﺌﻨﺎﺕ ﻻ ﺘﻤﺜل ﻭﺍﺠﻬـﺔ ﺍﻟﻘﺎﺌﻤـﺔ
،IListﻤﻤﺎ ﻴﺠﻌل ﻤﻥ ﺍﻟﻤﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻜﺎﺌﻨﺎﺕ ﻜﻤﺼﺩﺭ ﻟﻠﺒﻴﺎﻨﺎﺕ DataSourceﻋﻨـﺩ
ﺭﺒﻁﻬﺎ ﺒﺄﺩﻭﺍﺕ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ.
٤٢٠
ﻓﺌﺔ ﻤﺼﺩﺭ ﺍﻟﺭﺒﻁ BindingSource Class
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﺨﺼﺎﺌﺹ ﺍﻟﻭﺍﺠﻬﺎﺕ ﺍﻟﻤﺫﻜﻭﺭﺓ ،ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
٤٢٢
ﻭﻴﻤﻜﻨﻙ ﻭﻀﻊ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺒﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ ،ﻭﺫﻟـﻙ ﺒﺎﺴـﺘﺨﺩﺍﻡ
ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ،ﺤﻴﺙ ﺴﻴﻌﺭﺽ ﻟﻙ ﺯﺭ ﺍﻹﺴﺩﺍل ﺸﺠﺭﺓ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻤﺘﺎﺤﺔ ..ﻓـﻲ ﻫـﺫﻩ
ﺍﻟﺸﺠﺭﺓ ﺴﺘﺠﺩ ﻋﻨﺼﺭﻴﻥ ﺭﺌﻴﺴﻴﻴﻥ:
:None -١ﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ،ﻭﻫﻲ ﺘﺠﻌل ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻘﻴﻤﺔ .Nothing
:Other Data Sources -٢ﻭﺘﺤﺘﻬﺎ ﺍﻻﺨﺘﻴﺎﺭﺍﻥ ﺍﻟﺘﺎﻟﻴﺎﻥ:
ﺃ :Project Data Sources .ﻭﻴﻭﺠﺩ ﺘﺤﺘﻬﺎ ﻜل ﻓﺌﺎﺕ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺘﺎﺤـﺔ
ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ﻜﻠﻪ ..ﻭﻴﺅﺩﻱ ﺍﺨﺘﻴﺎﺭ ﺃﻱ ﻓﺌﺔ ﻤﻥ ﻫﺫﻩ ﺍﻟﻔﺌﺎﺕ ،ﺇﻟﻰ ﺇﻨﺸﺎﺀ ﻨﺴـﺨﺔ
ﺠﺩﻴﺩﺓ ﻤﻨﻬﺎ ﻭﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﺍﻟﻨﻤﻭﺫﺝ.
ﺏ :Form Data Sources .ﻭﻴﻭﺠﺩ ﺘﺤﺘﻬﺎ ﻜل ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﻤﻌﺭﻓﺔ ﻓـﻲ ﺍﻟﻨﻤـﻭﺫﺝ
ﺍﻟﺤﺎﻟﻲ ﻭﺘﺼﻠﺢ ﻜﻤﺼﺎﺩﺭ ﺒﻴﺎﻨﺎﺕ ،ﻤﺜل ﺍﻟﻘـﻭﺍﺌﻡ Listsﻭﻤﺠﻤﻭﻋـﺎﺕ ﺍﻟﺒﻴﺎﻨـﺎﺕ
DataSetsﻭﻏﻴﺭﻫﺎ.
ﻭﻓﻲ ﺍﻟﻬﺎﻤﺵ ﺍﻟﺴﻔﻠﻲ ﻟﻠﻨﺎﻓﺫﺓ ﺍﻟﻤﺴﺩﻟﺔ ،ﻴﻭﺠﺩ ﺭﺍﺒﻁ ﺍﺴﻤﻪ:
Add Project data Source
ﻋﻨﺩ ﺍﻟﻀـﻐﻁ ﻋﻠﻴـﻪ ﻴـﺘﻡ ﺘﺸـﻐﻴل ﺍﻟﻤﻌـﺎﻟﺞ ﺍﻟﺴـﺤﺭﻱ ﻟﺘﻬﻴﺌـﺔ ﻤﺼـﺎﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ
،Data Source Configuration Wizardﻟﻴﻤﻜﻨﻙ ﺇﻨﺸﺎﺀ ﻤﺼـﺩﺭ ﺒﻴﺎﻨـﺎﺕ ﺠﺩﻴـﺩ
ﻭﺇﻀﺎﻓﺘﻪ ﺘﺤﺕ ﺍﻟﻔﺭﻉ .Project Data Sources
ﺍﻟﻘﺎﺌﻤﺔ :List
ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﻭﺍﺠﻬﺔ IListﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻌﻨﺎﺼﺭ
ﺍﻟﻤﺭﺘﺒﻁﺔ ..ﻻﺤﻅ ﺃﻥ ﻨﻭﻉ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻌﺎﺌﺩﺓ ﻴﺘﺤﺩﺩ ﺘﺒﻌﺎ ﻟﻤﺎ ﻴﻠﻲ:
٤٢٣
ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ
ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻌﺎﺌﺩﺓ ﻤﻥ ﺍﻟﺨﺎﺼﻴﺔ List
DataMember DataSource
ﻤﺼﻔﻭﻓﺔ ﻗﺎﺌﻤﺔ ArratListﻓﺎﺭﻏﺔ. ﻨﺹ ﻓﺎﺭﻍ null
ﻋﻨﺩ ﻗﺭﺍﺀﺓ ﺍﻟﺨﺎﺼﻴﺔ Listﺴﻴﺤﺩﺙ ﺨﻁﺄ ﻓـﻲ
ﺃﻱ ﻗﻴﻤﺔ null
ﺍﻟﺒﺭﻨﺎﻤﺞ.
ﻤﺼﻔﻭﻓﺔ .Array ﻤﺼﻔﻭﻓﺔ
ﺍﻟﻘﻴﻤـــﺔ ﺍﻟﻌﺎﺌـــﺩﺓ ﻤـــﻥ ﺍﻟﻭﺴـــﻴﻠﺔ ﻜﺎﺌﻥ ﻴﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ
IListSource.GetList IListSource
ﻜﺎﺌﻥ ﻴﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ
ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﻭﺍﺠﻬﺔ .IBindingList
IBindingList
ﻜﺎﺌﻥ ﻴﻤﺜل
ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﻭﺍﺠﻬﺔ .IList
ﺍﻟﻭﺍﺠﻬﺔ IList
ﻜﺎﺌﻥ ﺒﺴﻴﻁ ﻤﻥ
ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﻭﺍﺠﻬﺔ > IBindingList<Tﺒﻬـﺎ
ﺍﻟﻨﻭﻉ Tﻻ
ﻋﻨﺼﺭ ﻭﺍﺤﺩ.
ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ
ﻜﺎﺌﻥ ﻴﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ
ﻤﺼﻔﻭﻓﺔ ﻗﺎﺌﻤﺔ ArrayListﺒﻬﺎ ﻋﻨﺼﺭ ﻭﺍﺤﺩ. ICustomType
Descriptor
ﻤﺼﻔﻭﻓﺔ ﻗﺎﺌﻤـﺔ ArratListﻨﺴـﺨﺕ ﺇﻟﻴﻬـﺎ ﻜﺎﺌﻥ ﻴﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ
ﻋﻨﺎﺼﺭ ﺍﻟﻜﺎﺌﻥ. IEnumerable
ﻋﻨﺼﺭ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﺼﻔﻭﻓﺎﺕ
ﻨﺴﺨﺔ ﻓﺎﺭﻏﺔ ﻤﻥ ﺍﻟﻔﺌﺔ >.BindingList<T
ﺍﻟﻨﻭﻉ T Array Type
ﻨﻭﻉ ﻴﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ
IListSourceﺃﻭ
ﻨﺴﺨﺔ ﺠﻴﺩﺓ ﻓﺎﺭﻏﺔ ﻤﻥ ﻨﻭﻉ ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ.
ﺍﻟﻭﺍﺠﻬﺔ
ITypedList
٤٢٤
ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ
ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻌﺎﺌﺩﺓ ﻤﻥ ﺍﻟﺨﺎﺼﻴﺔ List
DataMember DataSource
ﻋﻨﺼﺭ ﻤﻥ ﻨﻭﻉ ﻴﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ
ﻨﺴﺨﺔ ﻓﺎﺭﻏﺔ ﻤﻥ ﺍﻟﻔﺌﺔ >.BindingList<T
ﺍﻟﻨﻭﻉ T IList
ﻨﻭﻉ ﺒﺴﻴﻁ ﻻ
ﻨﺴﺨﺔ ﻓﺎﺭﻏﺔ ﻤﻥ ﺍﻟﻔﺌﺔ >.BindingList<T
ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ
ﺍﻟﻤﻭﻀﻊ :Position
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻤﻭﻀﻊ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴـﺔ ﻟﻤﺼـﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﻭﻫـﻭ
ﺍﻟﻌﻨﺼﺭ ﺍﻟﺫﻱ ﻴﺘﻡ ﻋﺭﻀﻪ ﻓﻲ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﻤﺼﺩﺭ ﺍﻟﺭﺒﻁ.
ﺍﻟﺤﺎﻟﻲ :Current
ﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ Objectﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ ،ﻭﻫـﻭ ﺍﻟﻌﻨﺼـﺭ
ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﻤﻭﻀﻊ ﺍﻟﻤﺤﺩﺩ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ .Position
ﺍﻟﺘﺭﺘﻴﺏ :Sort
ﺘﺤﺩﺩ ﻁﺭﻴﻘﺔ ﺘﺭﺘﻴﺏ ﺍﻟﻌﻨﺎﺼﺭ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ،ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﻨﺼﺎ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﺴـﻡ ﺍﻟﻌﻤـﻭﺩ
ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﺍﻟﺘﺭﺘﻴﺏ ،ﻤﺘﺒﻭﻋﺎ ﺒﺎﺘﺠﺎﻩ ﺍﻟﺘﺭﺘﻴﺏ ) ASCﺃﻭ .(DESC
٤٢٥
ﺇﻁﻼﻕ ﺃﺤﺩﺍﺙ ﺘﻐﻴﺭ ﺍﻟﻘﺎﺌﻤﺔ :RaiseListChangedEvents
ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ) Trueﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀـﻴﺔ( ،ﻓﺴـﻴﻨﻁﻠﻕ ﺍﻟﺤـﺩﺙ
BindingSource.ListChangedﻋﻨﺩﻤﺎ ﻴﺤﺩﺙ ﺘﻐﻴﻴﺭ ﻓﻲ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ.
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﺃﺤﺩﺍﺙ ﺍﻟﻭﺍﺠﻬﺎﺕ ﺍﻟﺘﻲ ﺘﻤﺜﻠﻬﺎ ،ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻷﺤـﺩﺍﺙ ﺍﻟﺘﺎﻟﻴـﺔ،
ﻭﻜﻠﻬﺎ ﻤﺄﻟﻭﻑ ﻟﻨﺎ ﻟﻬﺫﺍ ﻟﻥ ﻨﻜﺭﺭ ﺸﺭﺤﻬﺎ ﻫﻨﺎ:
٤٢٨
ﻓﺌﺔ ﻤﺴﺎﻋﺩ ﺭﺒﻁ ﺍﻟﻘﻭﺍﺌﻡ ListBindingHelper Class
ﺘﺤﺘﻭﻱ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻋﻠﻰ ﺒﻌﺽ ﺍﻟﻭﺴﺎﺌل ﺍﻟﻤﺸﺘﺭﻜﺔ ،Shared Methodsﺍﻟﺘﻲ ﺘﺴﺘﺨﺩﻤﻬﺎ ﺍﻟﻔﺌـﺔ
BindingSourceﻓﻲ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﻫﻲ:
٤٢٩
-ﺍﺴﻡ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-ﻤﺼﻔﻭﻓﺔ ﻤﻥ ﻭﺍﺼﻔﺎﺕ ﺍﻟﺨﺼﺎﺌﺹ ،PropertyDescriptorﺍﻟﺘﻲ ﺘﺤﺩﺩ ﺍﻟﻘﺎﺌﻤـﺔ
ﺍﻟﻤﺭﺍﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻬﺎ.
ﻭﺘﻭﺠﺩ ﺼﻴﻐﺘﺎﻥ ﺃﺨﺭﻴﺎﻥ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ،ﺇﺤﺩﺍﻫﻤﺎ ﺘﺴﺘﻘﺒل ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ﻓﻘﻁ ،ﻭﺍﻷﺨـﺭﻯ
ﺘﺴﺘﻘﺒل ﺍﻟﻤﻌﺎﻤﻠﻴﻥ ﺍﻷﻭل ﻭﺍﻟﺜﺎﻟﺙ ﻓﻘﻁ.
٤٣٠
ﻓﺌﺔ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ BindingNavigator Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﻓﺌﺔ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ،ToolStrip Classﻟﻬﺫﺍ ﻓﻬﻲ ﺘﻌﻤل ﻜﺭﻑ ﺃﺩﻭﺍﺕ ﻴﻌﺭﺽ
ﻤﺠﻤﻭﻋﺔ ﻤﻥ ﺍﻷﺯﺭﺍﺭ ،ﺍﻟﺘﻲ ﺘﺘﻴﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺍﻟﺘﺤﺭﻙ ﻋﺒﺭ ﺴﺠﻼﺕ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻭﺤـﺫﻑ
ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﺃﻭ ﺇﻀﺎﻓﺔ ﺴﺠل ﺠﺩﻴﺩ ،ﻜل ﻫﺫﺍ ﺒﺩﻭﻥ ﺃﻥ ﺘﻜﺘﺏ ﺃﻨﺕ ﺤﺭﻓﺎ ﻤﻥ ﺍﻟﻜﻭﺩ!
ﻭﻴﺒﺩﻭ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺫﻱ ﻴﻌﺭﻀﻪ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ ﻜﻤﺎ ﻓﻲ ﺍﻟﺼﻭﺭﺓ:
ﻻﺤﻅ ﺃﻥ ﺁﺨﺭ ﺯﺭ ﻋﻠﻰ ﺍﻟﺸﺭﻴﻁ ﻴﺘﻴﺢ ﻟﻙ ﺇﻀﺎﻓﺔ ﺃﺯﺭﺍﺭ ﻭﺃﺩﻭﺍﺕ ﺠﺩﻴﺩﺓ ﺇﻟﻰ ﺍﻟﺸـﺭﻴﻁ ،ﺒـﻨﻔﺱ
ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺘﻌﻠﻤﻨﺎﻫﺎ ﻓﻲ ﻜﺘﺎﺏ ﺒﺭﻤﺠﺔ ﺍﻟﻭﻴﻨﺩﻭﺯ ..ﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺍﺴﺘﻐﻼل ﻤﺴـﺎﺤﺔ
ﺍﻟﺸﺭﻴﻁ ﻹﻀﺎﻓﺔ ﺃﺯﺭﺍﺭ ﻭﻗﻭﺍﺌﻡ ﻭﻤﺭﺒﻌﺎﺕ ﻨﺼﻭﺹ ﻭﻻﻓﺘﺎﺕ ﺘﺅﺩﻱ ﺃﻴﺔ ﻭﻅﺎﺌﻑ ﺃﺨﺭﻯ ﺨﺎﺼـﺔ
ﺒﻙ )ﻜﺎﻟﻘﺹ ﻭﺍﻟﻠﺼﻕ ﻭﻋﺭﺽ ﺤﺎﻟﺔ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻭﻏﻴﺭ ﻫﺫﺍ( ،ﻭﺒﻬﺫﺍ ﻻ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺇﻀـﺎﻓﺔ ﺭﻑ
ﺃﺩﻭﺍﺕ ﺁﺨﺭ ﺨﺎﺹ ﺒﻙ.
ﻭﺍﻟﺼﻭﺭﺓ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺭﻴﻙ ﻜﻴﻑ ﻴﺒﺩﻭ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ ﻋﻨﺩ ﺘﺸﻐﻴل ﺍﻟﻤﺸﺭﻭﻉ Navigatorﺍﻟﻤﺭﻓـﻕ
ﺒﺄﻤﺜﻠﺔ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ:
٤٣١
ﻤﺭﺓ ﺃﺨﺭﻯ ﺃﺫﻜﺭﻙ :ﻟﻭ ﺃﻀﻔﺕ ﺴﺠﻼ ﺠﺩﻴﺩ ﺒﻀﻐﻁ ﺯﺭ ﺍﻹﻀﺎﻓﺔ ﺍﻟﻤﻭﺠﻭﺩ ﻋﻠﻰ ﺸﺭﻴﻁ ﻤﻭﺠـﻪ
ﺍﻟﺭﺒﻁ ،ﺃﻭ ﺤﺫﻓﺕ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﺒﻀﻐﻁ ﺯﺭ ﺍﻟﺤﺫﻑ ،ﺃﻭ ﻏﻴﺭﺕ ﻗﻴﻤﺔ ﺃﻱ ﺤﻘل ﻓـﻲ ﺍﻟﺴـﺠل
ﺍﻟﺤﺎﻟﻲ ﺒﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﺃﺤﺩ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺹ ،ﻓﺈﻥ ﻫﺫﻩ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺴﺘﺅﺜﺭ ﻓﻘـﻁ ﻋﻠـﻰ ﻤﺠﻤﻭﻋـﺔ
ﺍﻟﺒﻴﺎﻨﺎﺕ ،DataSetﻟﻜﻥ ﺘﻅل ﻤﻬﻤﺔ ﺘﺤﺩﻴﺙ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺘﺭﻭﻜـﺔ ﻟـﻙ ..ﻭﺇﺫﺍ ﻜﻨـﺕ ﻻ
ﺘﺭﻏﺏ ﺃﻥ ﻴﻌﺒﺙ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺒﻘﻴﻡ ﺒﻌﺽ ﺍﻟﺤﻘﻭل ،ﻓﺎﺠﻌل ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺼـﻭﺹ ﺍﻟﻤﻨـﺎﻅﺭﺓ ﻟﻬـﺎ
ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ ،ﺃﻭ ﺍﺭﺒﻁ ﻫﺫﻩ ﺍﻟﺤﻘﻭل ﺒﻼﻓﺘﺎﺕ ﻤﻨﺫ ﺍﻟﺒﺩﺍﻴﺔ ..ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﺘﻐﻴﻴـﺭ ﺍﻟﻤﺴـﺘﺨﺩﻡ
ﻟﻘﻴﻤﺔ ﺍﻟﻤﻌﺭﻑ IDﻟﻥ ﺘﺅﺜﺭ ﻓﻲ ﺸﻲﺀ ،ﻷﻥ ﻫﺫﺍ ﺍﻟﺤﻘل ﻤﻭﻟﺩ ﺁﻟﻴﺎ ،ﻭﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻻ ﻴﺴﺘﻁﻴﻊ
ﺘﻐﻴﻴﺭﻩ.
ﻭﻟﻭ ﻟﻡ ﺘﻜﻥ ﺘﺭﻏﺏ ﻓﻲ ﺃﻥ ﻴﺤﺫﻑ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻟﺴﺠﻼﺕ ﺃﻭ ﻴﻀﻴﻑ ﺴﺠﻼﺕ ﺠﺩﻴـﺩﺓ ،ﻓﻴﻤﻜﻨـﻙ
ﺇﺯﺍﻟﺔ ﺯﺭ ﺍﻟﺤﺫﻑ ﺃﻭ ﺯﺭ ﺍﻹﻀﺎﻓﺔ ﻤﻥ ﻓﻭﻕ ﺍﻟﺸﺭﻴﻁ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ ،ﺃﻭ ﻴﻤﻜﻨﻙ ﺘﻌﻁﻴﻠﻬﻤـﺎ،
ﻭﺴﺘﺭﻯ ﻜﻴﻑ ﻨﻔﻌل ﻫﺫﺍ ﺒﻌﺩ ﻗﻠﻴل ﻭﻨﺤﻥ ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺨﺼﺎﺌﺹ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﺍﻟﻔﺌﺔ BindingNavigatorﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ.
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﻤﺼﺩﺭ ﺍﻟﺭﺒﻁ ،BindingSourceﺍﻟﺫﻱ ﺴـﻴﺘﺤﻜﻡ ﻤـﻥ
ﺨﻼﻟﻪ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ ﻓﻲ ﺴﺠﻼﺕ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-٣ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻤﻨﻁﻘﻴﺎ ،ﺇﺫﺍ ﺠﻌﻠﺘﻪ Falseﻓﻠﻥ ﻴﻌﺭﺽ ﻤﻭﺠﻪ ﺍﻟـﺭﺒﻁ
ﺃﺯﺭﺍﺭ ﺍﻟﺘﺤﻜﻡ ﺍﻟﻘﻴﺎﺴﻴﺔ )ﺃﺯﺭﺍﺭ ﺍﻻﻨﺘﻘﺎل ﻭﺯﺭ ﺍﻟﺤﺫﻑ ﻭﺯﺭ ﺍﻹﻀﺎﻓﺔ(.
-٤ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻨﺎ ﻤﻥ ﻨﻭﻉ ﺍﻟﻭﺍﺠﻬﺔ ) IContainerﻤﺜل ﺍﻟﻨﻤﻭﺫﺝ( ،ﻟﻴـﺘﻡ
ﻋﺭﺽ ﺸﺭﻴﻁ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ ﻋﻠﻴﻪ.
٤٣٣
ﻋﻨﺼﺭ ﺍﻟﺘﺤﺭﻙ ﺇﻟﻰ ﺍﻷﻭل :MoveFirstItem
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﺼﺭ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ToolStripItemﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﻼﻨﺘﻘﺎل ﺇﻟﻰ ﺃﻭل ﺴﺠل
ﻓﻲ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀـﻲ ﻴﻜـﻭﻥ ﻫـﺫﺍ ﺍﻟﻌﻨﺼـﺭ ﻤـﻥ ﺍﻟﻨـﻭﻉ
.ToolStripButton
٤٣٤
ﻋﻨﺼﺭ ﺍﻟﻌﺩ :CountItem
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﺼﺭ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ToolStripItemﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﻌﺭﺽ ﺍﻟﻌـﺩﺩ ﺍﻟﻜﻠـﻲ
ﻟﻠﺴﺠﻼﺕ ﻓﻲ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﺴـﺘﺨﺩﻡ ﻻﻓﺘـﺔ ﺭﻑ ﺃﺩﻭﺍﺕ
ـﻴﺔ
ـﺔ ﺍﻟﺨﺎﺼــ
ـﺭﺽ ﻗﻴﻤــ
ـﻲ ﺘﻌــ
ـﺭﺽ ،ﻭﻫــ
ـﺫﺍ ﺍﻟﻐــ
ToolStripLabelﻟﻬــ
.BindingSource.Count
٤٣٥
ﺇﺠﺎﺯﺓ :Validate
ﺘﺠﻌل ﺍﻟﻨﻤﻭﺫﺝ ﻴﻔﺤﺹ ﻗﻴﻡ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻋﻠﻴﻪ ،ﻭﺘﻌﻴـﺩ Trueﺇﺫﺍ ﻜﺎﻨـﺕ ﺒﻴﺎﻨﺎﺘﻬـﺎ
ﺼﺤﻴﺤﺔ.
٤٣٦
ﻤﻠﺤﻕ٢ :
ﺃﻨﻭﺍﻉ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺍﻟﻤﺩﺍﺭﺓ
Managed SQL Data Types
٤٣٧
ﺴﺠل ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻨﻁﻘﻴﺔ SqlBoolean Structure
ﻴﺴﺘﻁﻴﻊ ﻫﺫﺍ ﺍﻟﺴﺠل ﺃﻥ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﻴﻤﺔ ﻤﻨﻁﻘﻴﺔ true :ﺃﻭ .false
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﺍ ﺍﻟﺴﺠل ﺜﻼﺙ ﺼﻴﻎ:
.١ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ ،ﻭﻫﻲ ﺘﻨﺸﺊ ﻨﺴﺨﺔ ﻗﻴﻤﺘﻬﺎ .Null
.٢ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻗﻴﻤﺔ ﻤﻨﻁﻘﻴﺔ Booleanﻟﻭﻀﻌﻬﺎ ﻓﻲ ﺍﻟﺴﺠل ..ﻤﺜﺎل:
;)SqlBoolean Sb = new SqlBoolean(true
.٣ﻭﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ Integerﻟﻭﻀﻌﻪ ﻓﻲ ﻫﺫﺍ ﺍﻟﺴﺠل ،ﺤﻴﺙ ﻴﻌﺘﺒﺭ ﺍﻟﺼـﻔﺭ
falseﻭﺃﻱ ﻋﺩﺩ ﺁﺨﺭ .true
ﻭﻴﻤﺘﻠﻙ ﻫﺫﺍ ﺍﻟﺴﺠل ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
ﺨﻁﺄ :false
ﺘﻌﻴﺩ ﺴﺠﻼ ﻤﻨﻁﻘﻴﺎ SqlBooleanﻗﻴﻤﺘﻪ .false
ﺼﻭﺍﺏ :true
ﺘﻌﻴﺩ ﺴﺠﻼ ﻤﻨﻁﻘﻴﺎ SqlBooleanﻗﻴﻤﺘﻪ .true
ﻋﺩﻡ :Null
ﺘﻌﻴﺩ ﺴﺠﻼ ﻤﻨﻁﻘﻴﺎ SqlBooleanﻗﻴﻤﺘﻪ .Null
ﺼﻔﺭ :Zero
ﺘﻌﻴﺩ ﺴﺠﻼ ﻤﻨﻁﻘﻴﺎ SqlBooleanﻗﻴﻤﺘﻪ ) ٠ﻫﺫﺍ ﻴﻌﻨﻲ ﺃﻥ ﻗﻴﻤﺘﻪ .(false
ﻭﺍﺤﺩ :One
ﺘﻌﻴﺩ ﺴﺠﻼ ﻤﻨﻁﻘﻴﺎ SqlBooleanﻗﻴﻤﺘﻪ ) ١ﻫﺫﺍ ﻴﻌﻨﻲ ﺃﻥ ﻗﻴﻤﺘﻪ .(true
ﺍﻟﻘﻴﻤﺔ :Value
ﺘﻌﻴﺩ ﻗﻴﻤﺔ ﻤﻨﻁﻘﻴﺔ Booleanﺘﻌﺒﺭ ﻋﻥ ﻗﻴﻤﺔ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ..ﻭﺘﺴﺒﺏ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ
ﺨﻁﺄ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠل ﻤﻨﻌﺩﻤﺎ ،ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﻓﺤﺼﻪ ﺃﻭﻻ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼـﻴﺔ IsNullﻗﺒـل
ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ.
ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻤﻌﺎﻤل
And &
Or |
Xor ^
) OnesComplementﺍﻟﻤﻌﻜﻭﺱ ﺍﻟﺜﻨﺎﺌﻲ( !
Equals ==
NotEquals =!
GreaterThan >
٤٣٩
GreaterThanOrEquals =>
LessThan <
LessThanOrEquals =<
ﻤﻠﺤﻭﻅﺔ:
ﻜل ﺍﻷﻨﻭﺍﻉ ﺍﻟﺘﻲ ﺴﻨﺸﺭﺤﻬﺎ ﻓﻴﻤﺎ ﺒﻌﺩ ﻤﺯﻭﺩﺓ ﺒﺎﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺤﺴﺎﺒﻴﺔ )ﺍﻟﻁﺭﺡ ﻭﺍﻟﺠﻤﻊ ﻭﺍﻟﻀـﺭﺏ
ﻭﺍﻟﻘﺴﻤﺔ ﻭﺒﺎﻗﻲ ﺍﻟﻘﺴﻤﺔ( ﻭﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﻨﻁﻘﻴﺔ )& ﻭ | ﻭ ^ ﻭ !( ﻭﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﺤﻭﻴل ﺍﻟﻀﻤﻨﻲ
ﻭﺍﻟﺼﺭﻴﺢ ..ﻭﻻ ﻴﺤﺘﻭﻱ ﻜل ﻨﻭﻉ ﺇﻻ ﻋﻠﻰ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﻲ ﺘﻨﺎﺴـﺏ ﺍﻟﻘـﻴﻡ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓﻴـﻪ
)ﺍﻟﻨﺼﻭﺹ ﻤﺜﻼ ﻻ ﺘﻤﻠﻙ ﻤﻌﺎﻤﻼﺕ ﻤﻨﻁﻘﻴﺔ( ،ﻟﻬﺫﺍ ﻟﻥ ﻨﻜﺭﺭ ﺫﻜﺭ ﻫﺫﺍ ﻓﻲ ﺒﺎﻗﻲ ﺍﻷﻨﻭﺍﻉ ،ﺇﻻ ﺇﺫﺍ
ﻜﺎﻥ ﻫﻨﺎﻙ ﻤﻌﺎﻤل ﻴﻘﻭﻡ ﺒﻭﻅﻴﻔﺔ ﻤﺨﺘﻠﻔﺔ ﻋﻥ ﺍﻟﻤﺄﻟﻭﻑ.
٤٤٠
ﺴﺠل ﺍﻟﻭﺤﺩﺓ ﺍﻟﺜﻨﺎﺌﻴﺔ SqlByte Structure
ﻫﺫﺍ ﺍﻟﺴﺠل ﻴﺤﻔﻅ ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ ﺒﺩﻭﻥ ﺇﺸﺎﺭﺓ ،ﺃﻱ ﺃﻨﻪ ﻴﺘﻌﺎﻤل ﻤﻊ ﺍﻷﻋﺩﺍﺩ ﻤﻥ ٠ﺇﻟﻰ .٢٥٥
ﻭﻴﺴﺘﻘﺒل ﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﺍ ﺍﻟﺴﺠل ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ Byteﻟﻨﺴﺦ ﻗﻴﻤﺘﻬﺎ ﺇﻟﻴﻪ ..ﻤﺜﺎل:
;)SqlByte B = new SqlByte(5
ﺼﻔﺭ :Zero
ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﺴﺠل SqlByteﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻘﻴﻤﺔ ﺼﻔﺭ.
ﺍﻟﻌﺩﻡ :Null
ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﺴﺠل SqlByteﻻ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﻱ ﻗﻴﻤﺔ.
ﺍﻟﻘﻴﻤﺔ :Value
ﺘﻌﻴﺩ ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ Byteﺘﺤﻤل ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﺤﻔﻭﻅﺔ ﻓﻲ ﺍﻟﺴﺠل ..ﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺨﻁﺄ
ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠل ﻤﻨﻌﺩﻤﺎ ،ﻟﺫﺍ ﻋﻠﻴﻙ ﺃﻥ ﺘﺴﺘﺨﺩﻡ ﺍﻟﺨﺎﺼﻴﺔ IsNullﺃﻭﻻ ﻟﻠﺘﺄﻜﺩ
ﻤﻥ ﻭﺠﻭﺩ ﻗﻴﻤﺔ ﻓﻲ ﺍﻟﺴﺠل.
٤٤١
ﻻﺤﻅ ﺃﻥ ﻤﺎ ﻴﻨﻁﺒﻕ ﻋﻠﻰ ﺍﻟﺴﺠل SqlByteﻴﻨﻁﺒﻕ ﻋﻠﻰ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺭﻗﻤﻴﺔ ﺍﻷﺨـﺭﻯ ،ﻓﻬـﻲ
ﺘﻤﺘﻠﻙ ﻨﻔﺱ ﺍﻟﺨﺼﺎﺌﺹ ،ﻭﺘﺴﺘﻁﻴﻊ ﺤﻔﻅ ﻗﻴﻤﺔ ﺃﻭ ،Nullﻭﺍﻻﺨﺘﻼﻑ ﺍﻟﻭﺤﻴﺩ ﻫﻭ ﻨـﻭﻉ ﺍﻟﻘﻴﻤـﺔ
ﺍﻟﻤﺤﻔﻭﻅﺔ ..ﻟﺫﺍ ﻓﻼ ﺩﺍﻋﻲ ﻟﺘﻜﺭﺍﺭ ﻨﻔﺱ ﺍﻟﻜﻼﻡ ﻤﻊ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺘﺎﻟﻴﺔ ،ﻓﺄﻨﺕ ﺘﺴﺘﻁﻴﻊ ﻓﻬﻤﻬﺎ ﺒﻤﺠﺭﺩ
ﺍﻟﻨﻅﺭ:
٤٤٢
ﺴﺠل ﺍﻷﻋﺩﺍﺩ ﺍﻟﻌﺸﺭﻴﺔ SqlDecimal Structure
ﻴﺤﻔﻅ ﻫﺫﺍ ﺍﻟﺴﺠل ﺍﻷﻋﺩﺍﺩ ﺍﻟﻌﺸﺭﻴﺔ ،ﺒﻨﻔﺱ ﻁﺭﻴﻘﺔ ﺍﻟﺴﺠل ،Decimalﻜﻤﺎ ﺃﻥ ﺤـﺩﺙ ﺇﻨﺸـﺎﺀ
ﻫﺫﺍ ﺍﻟﺴﺠل ﻴﻤﺘﻠﻙ ﺼﻴﻐﺎ ﺸﺒﻴﻬﺔ ﺒﺤﺩﺙ ﺇﻨﺸﺎﺀ ﺍﻟﺴﺠل ،Decimalﺍﻟﺘﻲ ﻴﻤﻜﻨﻙ ﻤﺭﺍﺠﻌﺘﻬﺎ ﻓـﻲ
ﻜﺘﺎﺏ ﺒﺭﻤﺠﺔ ﺇﻁﺎﺭ ﺍﻟﻌﻤل.
ﻭﻴﺯﻴﺩ ﻫﺫﺍ ﺍﻟﺴﺠل ﻋﻠﻰ ﺴﺠﻼﺕ ﺍﻷﻨﻭﺍﻉ ﺍﻟﻌﺩﺩﻴﺔ ﺍﻷﺨﺭﻯ ﺒﺎﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ:
ﺍﻟﺒﻴﺎﻨﺎﺕ :Data
ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺃﻋﺩﺍﺩ ﺼﺤﻴﺤﺔ Integer Arrayﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺘﻤﺜﻴـل ﺍﻟﺜﻨـﺎﺌﻲ ﻟﻠﻌـﺩﺩ
ﺍﻟﻌﺸﺭﻱ.
ﺍﻟﺩﻗﺔ :Precision
ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﺼﺤﻴﺤﺔ ﻭﺍﻟﻌﺸﺭﻴﺔ ﻓﻲ ﺍﻟﻌﺩﺩ ﺍﻟﺤﺎﻟﻲ.
ﺍﻟﻤﻘﻴﺎﺱ :Scale
ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﻌﺸﺭﻴﺔ ﻓﻲ ﺍﻟﻌﺩﺩ ﺍﻟﺤﺎﻟﻲ.
٤٤٣
ﻓﺌﺔ ﺍﻟﺤﺭﻭﻑ SqlChars Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺘﻌﺎﻤل ﻤﻊ ﻤﺼﻔﻭﻓﺔ ﺤﺭﻭﻑ ،ﺒﺤﻴﺙ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺃﻨـﻭﺍﻉ ﺴـﻴﻜﻴﻭل
ﺍﻟﺘﺎﻟﻴﺔ ،varchar, nvarchar, char, nchar, text, ntext :ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﺃﻗﺼﻰ ﻋـﺩﺩ
ﻤﻥ ﺍﻟﺤﺭﻭﻑ ﻴﻤﻜﻥ ﻭﻀﻌﻪ ﻓﻲ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻫﻭ ﺃﻗﺼﻰ ﻗﻴﻤﺔ ﻟﻠﻌﺩﺩ ﺍﻟﺼﺤﻴﺢ )ﺃﻱ ﺤﻭﺍﻟﻲ ٢ﻤﻠﻴﺎﺭ
ﺤﺭﻑ(.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺜﻼﺙ ﺼﻴﻎ:
.١ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ ،ﻭﻫﻲ ﺘﻨﺸﺊ ﻨﺴﺨﺔ ﻗﻴﻤﺘﻬﺎ .Null
.٢ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺔ ﺤﺭﻭﻑ .Char Array
.٣ﻭﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﺴﺠل SqlStringﻷﺨﺫ ﺍﻟﺤﺭﻭﻑ ﻤﻥ ﺍﻟـﻨﺹ ﺍﻟﻤﻭﺠـﻭﺩ
ﻓﻴﻬﺎ.
ﺍﻟﻌﺩﻡ :Null
ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻓﺎﺭﻏﺔ ﻤﻥ ﺍﻟﻔﺌﺔ .SqlChars
ﺍﻟﻤﻔﻬﺭﺱ :Indexer
ﻴﻘﺭﺃ ﺃﻭ ﻴﻐﻴﺭ ﺍﻟﺤﺭﻑ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﻤﻭﻀﻊ ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل.
ﺍﻟﻁﻭل :Length
ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻟﺤﺭﻭﻑ ﺍﻟﻤﻭﺠﻭﺩﺓ ﺤﺎﻟﻴﺎ ﻓﻲ ﺍﻟﻜﺎﺌﻥ.
٤٤٤
ﺍﻟﺘﺨﺯﻴﻥ :Storage
ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ StorageStateﺍﻟﺘﻲ ﺘﻭﻀﺢ ﻨﻭﻉ ﺍﻟﻤﺨﺯﻥ ﺍﻟﺫﻱ ﻴﺘﻡ ﻓﻴـﻪ ﺤﻔـﻅ
ﺍﻟﺤﺭﻭﻑ ﺩﺍﺨل ﺍﻟﻜﺎﺌﻥ ،ﻭﻫﺫﻩ ﺍﻟﻘﻴﻡ ﻫﻲ:
ﺍﻟﻘﻴﻤﺔ :Value
ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺤﺭﻭﻑ ﺒﻬﺎ ﻨﺴﺨﺔ ﻤﻥ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻜﺎﺌﻥ ..ﻻﺤﻅ ﺃﻥ ﺃﻱ ﺘﻐﻴﻴﺭ ﻓـﻲ ﻫـﺫﻩ
ﺍﻟﻤﺼﻔﻭﻓﺔ ﻻ ﻴﺅﺜﺭ ﻋﻠﻰ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻜﺎﺌﻥ ،ﻋﻠﻰ ﻋﻜﺱ ﺍﻟﻤﺼﻔﻭﻓﺔ ﺍﻟﺘﻲ ﺘﻌﻴﺩﻫﺎ ﺍﻟﺨﺎﺼـﻴﺔ
.Buffer
٤٤٥
ﻭﻟﻭ ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﻴﺘﻌﺎﻤل ﻤﻊ ﻤﺨﺯﻥ ﻭﺴﻴﻁ ﻏﻴﺭ ﻤـﺩﺍﺭ Unmanaged Bufferﻓﺴـﻴﺘﻡ
ﺘﺤﻭﻴﻠﻪ ﺇﻟﻰ ﻤﺨﺯﻥ ﻤﺩﺍﺭ Managed Bufferﺒﻌﺩ ﺘﻨﻔﻴﺫ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ.
ﻜﺘﺎﺒﺔ :Write
ﺘﻨﺴﺦ ﻋﺩﺩﺍ ﻤﻥ ﺍﻟﺤﺭﻭﻑ ﻤﻥ ﻤﺼﻔﻭﻓﺔ ﺇﻟﻰ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ ﺒﺩﺀﺍ ﻤﻥ ﻤﻭﻀﻊ ﻤﻌﻴﻥ ..ﻭﻟﻬـﺎ
ﻨﻔﺱ ﻤﻌﺎﻤﻼﺕ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺴﺎﺒﻘﺔ.
ﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﻜﺘﺎﺒﺔ ﺤﺭﻭﻑ ﻓﻲ ﻤﻭﻀﻊ ﺘﺎل ﻵﺨﺭ ﺤﺭﻑ ﻓﻲ ﺍﻟﻜـﺎﺌﻥ ،ﺒﺸـﺭﻁ ﺃﻻ
ﺘﺘﺠﺎﻭﺯ ﺍﻟﻁﻭل ﺍﻷﻗﺼﻰ ﻟﻠﻜﺎﺌﻥ .MaxLength
ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻨﺴﺦ ﻜل ﺤﺭﻭﻑ ﺍﻟﻤﺼﻔﻭﻓﺔ ﺇﻟﻰ ﺍﻟﻜﺎﺌﻥ ﺒﺩﺀﺍ ﻤﻥ ﺍﻟﺤﺭﻑ ﺍﻟﺭﺍﺒﻊ:
٤٤٦
SqlChars Sc = new SqlChars("This is a test");
char[] X = { 'A', 'B', 'C', 'D', 'E' };
Sc.Write(3, X, 0, 5);
MessageBox.Show(new string(Sc.Value));
٤٤٧
ﺴﺠل ﺍﻟﻨﺹ SqlString Structure
ﻴﺨﺘﻠﻑ ﻨﺹ ﺴﻴﻜﻭﻴل ﻓﻲ ﻁﺭﻴﻘﺔ ﺘﻤﺜﻴﻠﻪ ﺍﻟﺩﺍﺨﻠﻴﺔ ،ﻋﻥ ﻓﺌﺔ ﺍﻟـﻨﺹ String Classﺍﻟﻌﺎﺩﻴـﺔ..
ﻓﻌﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜﺎل :ﻴﺄﺨﺫ ﺍﻟﻨﺹ ﺍﻟﻌﺎﺩﻱ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺜﻘﺎﻓﺔ ﻤﻥ ﺍﻟﻠﻐﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﺍﻟﻤﻌﺭﻓﺔ ﻋﻠـﻰ
ﺠﻬﺎﺯ ﺍﻟﻤﺴﺘﺨﺩﻡ ،ﺒﻴﻨﻤﺎ ﻻ ﻴﻔﻌل ﻨﺹ ﺴﻴﻜﻭﻴل ﻫﺫﺍ ،ﻓﻠﻭ ﻟﻡ ﺘﻤﺩﻩ ﺒﻤﻌﺭﻑ ﺍﻟﺜﻘﺎﻓﺔ ،ﻓﺈﻨﻪ ﻴﺴـﺘﺨﺩﻡ
ﻤﻘﺎﻴﻴﺱ ﺩﺍﺨﻠﻴﺔ ﺨﺎﺼﺔ ﺒﻪ ﻟﻤﻘﺎﺭﻨﺔ ﺍﻟﻨﺼﻭﺹ ..ﻭﻟﻭ ﺤﺎﻭﻟﺕ ﻤﻘﺎﺭﻨﺔ ﻨﺴﺨﺘﻴﻥ ﻤﻥ ﻨﺹ ﺴﻴﻜﻭﻴل
ﻟﻜل ﻤﻨﻬﻤﺎ ﻤﻌﺭﻑ ﺜﻘﺎﻓﺔ LCIDﻤﺨﺘﻠﻑ ﻋﻥ ﺍﻵﺨﺭ ،ﻓﺈﻥ ﺨﻁﺄ ﺴﻴﺤﺩﺙ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺒﺴـﺒﺏ
ﻋﺩﻡ ﻗﺩﺭﺘﻪ ﻋﻠﻰ ﺇﺠﺭﺍﺀ ﻋﻤﻠﻴﺔ ﺍﻟﻤﻘﺎﺭﻨﺔ.
)ﺃﻨﺼﺢ ﺒﻤﺭﺍﺠﻌﺔ ﻓﺼل ﺍﻟﻌﻭﻟﻤﺔ Globalizationﻓﻲ ﻤﺭﺠﻊ ﺒﺭﻤﺠﺔ ﺇﻁﺎﺭ ﺍﻟﻌﻤل(.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﺍ ﺍﻟﺴﺠل ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
.١ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ ،ﻭﻫﻲ ﺘﻨﺸﺊ ﻨﺴﺨﺔ ﻗﻴﻤﺘﻬﺎ .Null
.٢ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻨﺼﺎ Stringﻟﻨﺴﺨﻪ ﺇﻟﻰ ﺍﻟﺴﺠل.
.٣ﻭﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ ﻴﺴﺘﻘﺒل ﻤﻌﺭﻑ ﺍﻟﺜﻘﺎﻓﺔ LCIDﺍﻟـﺫﻱ
ﺘﺭﻴﺩ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻋﻨﺩ ﻤﻘﺎﺭﻨﺔ ﺍﻟﻨﺹ ﺒﺄﻱ ﻨﺹ ﺁﺨﺭ ..ﻤﺜﺎل:
", System.Globalization.ﻤﺤﻤﺩ"(SqlString Ss = new SqlString
;)CultureInfo.CurrentCulture.LCID
.٤ﻭﺍﻟﺭﺍﺒﻌﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻟـﺙ ،ﻴﺴـﺘﻘﺒل ﺇﺤـﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ
SqlCompareOptionsﺍﻟﺘﺎﻟﻴﺔ:
٤٤٨
ﺘﺘﺠﺎﻫل ﺍﻟﻤﻘﺎﺭﻨﺔ ﺍﻟﺭﻤﻭﺯ ﺍﻟﺼﻭﺘﻴﺔ ﻓﻲ ﺍﻟﻠﻐﺔ ﺍﻟﻴﺎﺒﺎﻨﻴﺔ. IgnoreKanaType
ﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺩﻤﺞ ﺃﻜﺜﺭ ﻤﻥ ﻗﻴﻤﺔ ﻤﻌﺎ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﻌﺎﻤل |.
ـﺔ
ـﺎﺭﺍﺕ ﺍﻟﻤﻘﺎﺭﻨــ
ـﺔ LCIDﻭﺨﻴــ
ـﺔ ﺍﻟﺜﻘﺎﻓــ
ـﺘﻘﺒل ﻤﻌﺭﻓــ
ـﺔ ﺘﺴــ
.٥ﻭﺍﻟﺨﺎﻤﺴــ
SqlCompareOptionsﻭﻤﺼﻔﻭﻓﺔ ﺜﻨﺎﺌﻴﺔ Byte Arrayﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﺍﻟﺘﻤﺜﻴـل
ﺍﻟﺜﻨﺎﺌﻲ ﻟﻠﻨﺹ.
.٦ﻭﺍﻟﺴﺎﺩﺴﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺭﺍﺒﻊ ،ﻋﻠﻴﻙ ﺠﻌﻠﻪ trueﺇﺫﺍ ﻜﺎﻥ ﺍﻟـﻨﺹ
ﻤﻤﺜﻼ ﺒﺎﻟﺘﺭﻤﻴﺯ ﺍﻟﻤﻭﺴﻊ .Unicode
.٧ﻭﺍﻟﺴﺎﺒﻌﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺨﺎﻤﺴﺔ ﺒﻤﻌﺎﻤل ﺭﺍﺒﻊ ﻴﺴﺘﻘﺒل ﻤﻭﻀﻊ ﺒﺩﺍﻴﺔ ﺍﻟﻘﺭﺍﺀﺓ ﻤـﻥ
ﺍﻟﻤﺼﻔﻭﻓﺔ ،ﻭﻤﻌﺎﻤل ﺨﺎﻤﺱ ﻴﺴﺘﻘﺒل ﻋﺩﺩ ﺍﻟﺤﺭﻭﻑ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻗﺭﺍﺀﺘﻬﺎ ﻤﻨﻬﺎ.
.٨ﻭﺍﻟﺜﺎﻤﻨﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺭﺍﺒﻊ ،ﻋﻠﻴﻙ ﺠﻌﻠﻪ trueﺇﺫﺍ ﻜﺎﻥ ﺍﻟـﻨﺹ
ﻤﻤﺜﻼ ﺒﺎﻟﺘﺭﻤﻴﺯ ﺍﻟﻤﻭﺴﻊ .Unicode
٤٤٩
ﺘﺭﺘﻴﺏ ﺜﻨﺎﺌﻲ :BinarySort2
ﺘﻌﻤل ﻜﺜﺎﺒﺕ ﻴﻌﻨﻲ ﺃﻥ ﺘﺭﺘﻴﺏ ﺍﻟﺤﺭﻭﻑ ﻴﺘﻡ ﺘﺒﻌﺎ ﻟﻘﻴﻤﻬﺎ ﺍﻟﺭﻗﻤﻴﺔ ﻓﻲ ﺍﻟﺘﺭﻤﻴﺯ.
ﺍﻟﻌﺩﻡ :Null
ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻓﺎﺭﻏﺔ ﻤﻥ ﺍﻟﺴﺠل .SqlString
٤٥٠
ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﻤﻘﺎﺭﻨﺔ :CompareInfo
ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﻤﻘﺎﺭﻨﺔ CompareInfoﺍﻟﺫﻱ ﻴﺴﺘﺨﺩﻤﻪ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﻤﻘﺎﺭﻨﺔ
ﺍﻟﻨﺼﻭﺹ.
ﺍﻟﻘﻴﻤﺔ :Value
ﺘﻌﻴﺩ Stringﻴﻤﺜل ﺍﻟﻨﺹ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ،ﺃﻭ ﺘﺴﺒﺏ ﺨﻁﺄ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴـﺠل
ﻓﺎﺭﻏﺎ.
ﺘﺸﺒﻴﻙ :Concat
ﺘﺩﻤﺞ ﻨﺴﺨﺘﻴﻥ ﻤﻥ ﻨﺹ ﺴﻴﻜﻴﻭل ﻓﻲ ﻨﺹ ﻭﺍﺤﺩ ،ﻭﺘﻌﻴﺩﻩ ﻜﺴﺠل ﺠﺩﻴﺩ ..ﻤﺜﺎل:
var LCID = System.Globalization.
;CultureInfo.CurrentCulture.LCID
;)", LCIDﻤﺤﻤﺩ"(SqlString Ss1 = new SqlString
;)", LCIDﻤﺤﻤﻭﺩ "(SqlString Ss2 = new SqlString
;)var Ss3 = SqlString.Concat(Ss1, Ss2
ﻤﺤﻤﺩ ﻤﺤﻤﻭﺩ MessageBox.Show(Ss3.Value); //
ﻭﻴﻤﻜﻨﻙ ﺇﻨﺠﺎﺯ ﻨﻔﺱ ﺍﻟﻤﻬﻤﺔ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ Addﻜﺎﻟﺘﺎﻟﻲ:
;)var Ss3 = SqlString.Add(Ss1, Ss2
ﺃﻭ ﺒﺎﺴﺘﺨﺩﺍﻡ ﻤﻌﺎﻤل ﺍﻟﺠﻤﻊ ﻜﺎﻟﺘﺎﻟﻲ:
;var Ss3 = Ss1 + Ss2
٤٥١
ـﻴﻜﻭﻴل
ـﺔ ﺴـــ
ـﺎﺭﺍﺕ ﻤﻘﺎﺭﻨـــ
ـﻥ ﺨﻴـــ
ـﺔ ﻤـــ
ـﺎﺭﺍﺕ ﺍﻟﻤﻘﺎﺭﻨـــ
ﺨﻴـــ
:CompareOptionsFromSqlCompareOptions
ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ،SqlCompareOptionsﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﺍﻟﻘﻴﻤـﺔ
ﺍﻟﻤﻨﺎﻅﺭﺓ ﻟﻬﺎ ﻓﻲ ﺍﻟﻤﺭﻗﻡ CompareOptionsﺍﻟﺫﻱ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻪ ﻓﻲ ﻜﺘﺎﺏ ﺒﺭﻤﺠﺔ ﺇﻁﺎﺭ
ﺍﻟﻌﻤل.
ﻨﺴﺦ :Clone
ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﺠﺩﻴﺩﺓ ﻤﻥ ﺍﻟﺴﺠل SqlStringﺒﻬﺎ ﻨﻔﺱ ﻗﻴﻤﺔ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ.
٤٥٢
ﺴﺠل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ SqlBinary Structure
ﻴﻤﺜل ﻫﺫﺍ ﺍﻟﺴﺠل ﻤﺼﻔﻭﻓﺔ ﻤﻥ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ..Byte Arrayﻭﻴﻤﻜﻨﻙ ﻤلﺀ ﻫـﺫﺍ ﺍﻟﺴـﺠل
ﺒﺎﻟﺒﻴﺎﻨﺎﺕ ﺒﺈﺭﺴﺎل ﻤﺼﻔﻭﻓﺔ ﺜﻨﺎﺌﻴﺔ ﺇﻟﻰ ﺤﺩﺙ ﺇﻨﺸﺎﺌﻪ ..ﻤﺜﺎل:
;)}SqlBinary Sb1 = new SqlBinary(new byte[] {100, 220, 3
ﻭﻨﻅﺭﺍ ﻷﻥ ﻫﺫﺍ ﺍﻟﺴﺠل ﻴﻌﺭﻑ ﻤﻌﺎﻤل ﺍﻟﺘﺤﻭﻴـل ﺍﻟﻀـﻤﻨﻲ ،Implicit Operatorﻓﻴﻤﻜﻨـﻙ
ﻭﻀﻊ ﻤﺼﻔﻭﻓﺔ ﺜﻨﺎﺌﻴﺔ ﻓﻲ ﻫﺫﺍ ﺍﻟﺴﺠل ﻤﺒﺎﺸﺭﺓ:
;}SqlBinary Sb1 = new byte[] {100, 220, 3
ﺍﻟﻁﻭﺍل :Length
ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺴﺠل ..ﻤﺜﺎل:
MessageBox.Show(Sb1.Length.ToString( )); // 3
ﺍﻟﻤﻔﻬﺭﺱ :Indexer
ﻴﻌﻴﺩ ﺍﻟﻭﺤﺩﺓ ﺍﻟﺜﻨﺎﺌﻴﺔ Byteﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻤﻭﻀﻊ ﻤﻌﻴﻥ ﻓﻲ ﺍﻟﺴﺠل ..ﻤﺜﺎل:
MessageBox.Show(Sb1[1].ToString( )); // 220
ﺍﻟﻤﺅﺴﻑ ﺃﻥ ﻫﺫﺍ ﺍﻟﻤﻔﻬﺭﺱ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ ،ﻟﺫﺍ ﻓﻼ ﻴﻤﻜﻨـﻙ ﺍﺴـﺘﺨﺩﺍﻤﻪ ﻟﺘﻐﻴﻴـﺭ ﺍﻟﻌﻨﺼـﺭ
ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻤﻭﻀﻊ ﻤﻌﻴﻥ ﻤﻥ ﺍﻟﻤﺼﻔﻭﻓﺔ ..ﻭﻟﺴﺕ ﺃﺩﺭﻯ ﻤﺎ ﺍﻟﺤﻜﻤﺔ ﻤﻥ ﻫﺫﺍ!
ﺍﻟﻘﻴﻤﺔ :Value
ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺜﻨﺎﺌﻴﺔ Byte Arrayﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻘﻴﻡ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ.
٤٥٣
ﻜﻤﺎ ﻴﻤﺘﻠﻙ ﻫﺫﺍ ﺍﻟﺴﺠل ﻋﺩﺩﺍ ﻤﻥ ﺍﻟﻭﺴﺎﺌل ﺍﻟﻤﺸﺘﺭﻜﺔ ،Sharedﻭﻫﻲ ﺘﻘـﻭﻡ ﺒـﻨﻔﺱ ﻭﻅـﺎﺌﻑ
ﺍﻟﻤﻌﺎﻤﻼﺕ Operatorsﺍﻟﻤﻌﺭﻓﺔ ﻟﻬﺫﺍ ﺍﻟﺴﺠل ..ﻭﻴﻬﻤﻨﺎ ﻫﻨﺎ ﺃﻥ ﻨﺸﻴﺭ ﺇﻟﻰ ﺒﻌﻀﻬﺎ ﻷﻥ ﻁﺭﻴﻘـﺔ
ﻋﻤﻠﻬﺎ ﻤﺨﺘﻠﻔﺔ ﻨﻭﻋﺎ:
ﺇﻀﺎﻓﺔ :Add
ﺘﺸﺒﻴﻙ :Concat
ﺍﻟﻤﻌﺎﻤل : +
ﺘﻘﻭﻡ ﺒﺘﺸﺒﻴﻙ ﺴﺠﻠﻴﻥ ،ﺒﺩﻤﺞ ﺍﻟﻤﺼﻔﻭﻓﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺒﻌﺩ ﻨﻬﺎﻴﺔ ﺍﻟﻤﺼـﻔﻭﻓﺔ ﺍﻷﻭﻟـﻰ ،ﻭﺘﻌﻴـﺩ
ﺍﻟﻤﺼﻔﻭﻓﺔ ﺍﻟﺠﺩﻴﺩﺓ ﻓﻲ ﺴﺠل ﺠﺩﻴﺩ ..ﻤﺜﺎل:
;)var Sb = SqlBinary.Add(Sb1, Sb2
ﺃﻭ:
;)var Sb = SqlBinary.Concat(Sb1, Sb2
ﺃﻭ ﺒﺎﺨﺘﺼﺎﺭ:
;var Sb = Sb1 + Sb2
ﺒﻌﺩ ﺘﻨﻔﻴﺫ ﻫﺫﺍ ﺍﻟﻤﺜﺎل ،ﺴﻴﺤﺘﻭﻱ ﺴﺠل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ Sbﻋﻠﻰ ﺍﻟﻘﻴﻡ ﺍﻟﺘﺎﻟﻴﺔ:
.٢ ،٠ ،١ ،٣ ،٢٢٠ ،١٠٠
ﻴﺴﺎﻭﻱ :Equals
ﺍﻟﻤﻌﺎﻤل == :
ﺘﻌﻴﺩ trueﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠﻼﻥ ﻟﻬﻤﺎ ﻨﻔﺱ ﺍﻟﻁﻭل ﻭﻴﺤﺘﻭﻴﺎﻥ ﻋﻠـﻰ ﻨﻔـﺱ ﺍﻟﻘـﻴﻡ ﺒـﻨﻔﺱ
ﺍﻟﺘﺭﺘﻴﺏ ..ﻤﺜﺎل:
;)) (MessageBox.Show(SqlBinary.Equals(Sb1,Sb2).ToString
// false
ﺃﻭ ﺒﺎﺨﺘﺼﺎﺭ:
MessageBox.Show((Sb1 == Sb2).ToString( )); // false
ﻻﺤﻅ ﺃﻥ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻌﺎﺌﺩﺓ ﻤﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻫﻲ ﻤﻥ ﺍﻟﻨﻭﻉ ،SqlBolleanﺤﻴﺙ ﺘﻜﻭﻥ ﻨﺘﻴﺠـﺔ
ﺍﻟﻤﻘﺎﺭﻨﺔ Nullﺇﺫﺍ ﻜﺎﻥ ﺃﻱ ﻤﻥ ﺍﻟﺴﺠﻠﻴﻥ ﻤﻨﻌﺩﻤﺎ.
٤٥٤
ﻓﺌﺔ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ SqlBytes Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻤﺸﺎﺒﻬﺔ ﺒﺩﺭﺠﺔ ﻜﺒﻴﺭﺓ ﻟﻠﺴﺠل ،SqlBinaryﺇﻻ ﺃﻨﻬﺎ ﺘﻤﺘﻠﻙ ﻤﻴـﺯﺓ ﺇﻀـﺎﻓﻴﺔ ،ﻭﻫـﻲ
ﻗﺩﺭﺘﻬﺎ ﻋﻠﻰ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ Bytesﻤﻥ ﺨﻼل ﻤﺠﺭﻯ ﺒﻴﺎﻨﺎﺕ ..Streamﻫـﺫﺍ
ﻴﺘﻴﺢ ﻟﻙ ﻗـﺭﺍﺀﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤـﻥ ﻤﻠـﻑ FileStreamﺃﻭ ﻤـﻥ ﻤﺠـﺭﻯ ﺒﻴﺎﻨـﺎﺕ ﺍﻟـﺫﺍﻜﺭﺓ
MemoryStreamﺃﻭ ﻤﻥ ﺨﻼل ﺍﻟﺸﺒﻜﺔ .NetworkStream
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ:
.١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ.
.٢ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﺒﻴﺎﻨﺎﺘﻬﺎ ﻤﻥ ﻤﺼﻔﻭﻓﺔ ﺜﻨﺎﺌﻴﺔ .Byte Array
.٣ﻭﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﺒﻴﺎﻨﺎﺘﻬﺎ ﻤﻥ ﺴﺠل ﺒﻴﺎﻨﺎﺕ ﺜﻨﺎﺌﻴﺔ .SqlBinary
.٤ﻭﺍﻟﺭﺍﺒﻌﺔ ﺘﺴﺘﻘﺒل ﺒﻴﺎﻨﺎﺘﻬﺎ ﻤﻥ ﻤﺠﺭﻯ ﺒﻴﺎﻨﺎﺕ .Stream
ﻭﺘﺸﺒﻪ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﻴﻀﺎ ﺍﻟﻔﺌﺔ SqlCharsﻓﻲ ﻜل ﺨﺼﺎﺌﺼﻬﺎ ﻭﻭﺴﺎﺌﻠﻬﺎ ،ﻤﺎ ﻋﺩﺍ ﺃﻥ ﺍﻟﺘﻌﺎﻤل ﻫﻨﺎ
ﻴﻜﻭﻥ ﻤﻊ ﻤﺼﻔﻭﻓﺔ ﺜﻨﺎﺌﻴﺔ Byte Arrayﺒﺩﻻ ﻤﻥ ﻤﺼﻔﻭﻓﺔ ﺤﺭﻭﻑ ..Char Arrayﻟﻬـﺫﺍ ﻻ
ﻨﺤﺘﺎﺝ ﺇﻟﻰ ﺇﻋﺎﺩﺓ ﺸﺭﺡ ﻫﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ ﻭﺍﻟﻭﺴﺎﺌل:
٤٥٥
ﻤﺠﺭﻯ ﺍﻟﺒﻴﺎﻨﺎﺕ :Stream
ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻤﺠﺭﻯ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﻴﺘﻌﺎﻤل ﻤﻌﻪ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ ..ﻭﻴﺅﺩﻱ ﺍﺴـﺘﺨﺩﺍﻡ ﻫـﺫﻩ
ﺍﻟﺨﺎﺼﻴﺔ ﺇﻟﻰ ﺘﺤﻤﻴل ﻜل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻤﺠﺭﻯ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﺍﻟﺫﺍﻜﺭﺓ ،ﻭﻟـﻭ ﻜﺎﻨـﺕ ﻫـﺫﻩ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﻀﺨﻤﺔ ﻟﻠﻐﺎﻴﺔ ،ﻓﻘﺩ ﺘﺅﺩﻱ ﺇﻟﻰ ﺍﺴﺘﻬﻼﻙ ﻤﺴﺎﺤﺔ ﺍﻟﺫﺍﻜﺭﺓ ﻭﺤﺩﻭﺙ ﺨﻁﺄ ﻤﻥ ﺍﻟﻨﻭﻉ
.OutOfMemoryException
٤٥٦
ﻓﺌﺔ XML
SqlXml Class
ﺘﺤﻔﻅ ﻫﺫﻩ ﺍﻟﻔﺌـﺔ ﻭﺜﻴﻘـﺔ ،XMLﻭﻫـﻲ ﺘﻌﺘﻤـﺩ ﺩﺍﺨﻠﻴـﺎ ﻋﻠـﻰ "ﻗـﺎﺭﺉ ﺒﻴﺎﻨـﺎﺕ "XML
،XmlReaderﻟﻬﺫﺍ ﻴﺠﺏ ﻤﺭﺍﻋﺎﺓ ﺃﻥ ﻴﻜﻭﻥ ﺘﻨﺴﻴﻕ ﺒﻴﺎﻨﺎﺕ XMLﺍﻟﺫﻱ ﺘﻀﻌﻬﺎ ﻓـﻲ ﻫـﺫﻩ
ﺍﻟﻔﺌﺔ ﻤﻭﺍﻓﻘﺎ ﻟﻠﻤﻌﺎﻴﻴﺭ ﺍﻟﺘﻲ ﺘﻘﺒﻠﻬﺎ ﺍﻟﻔﺌﺔ ..XmlReaderﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻜﻴﻔﻴﺔ ﺍﻟﺘﻌﺎﻤـل ﻤـﻊ
ﺒﻴﺎﻨﺎﺕ XMLﻭﻓﺌﺎﺘﻬﺎ ﻓﻲ ﻜﺘﺎﺏ ﻤﺴﺘﻘل ﺒﺈﺫﻥ ﺍﷲ.
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺜﻼﺙ ﺼﻴﻎ:
-١ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ.
-٢ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻤﺠﺭﻯ ﺒﻴﺎﻨﺎﺕ .Stream
-٣ﻭﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ "ﻗﺎﺭﺉ .XmlReader "XML
ﺍﻟﻌﺩﻡ :Null
ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﻔﺌﺔ SqlXmlﻻ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﻱ ﻗﻴﻤﺔ.
ﺍﻟﻘﻴﻤﺔ :Value
ﺘﻌﻴﺩ ﻨﺼﺎ Stringﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻭﺜﻴﻘﺔ XMLﺍﻟﻤﺤﻔﻭﻅﺔ ﻓﻲ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ.
٤٥٧
ﻤﻠﺤﻭﻅﺔ:
ﻜل ﺃﻨﻭﺍﻉ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺍﻟﺴﺎﺒﻘﺔ ﺘﺩﻋﻡ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ،XMLﻤﻥ ﺨﻼل:
-ﺘﻤﺜﻴل ﺍﻟﻭﺍﺠﻬﺔ IXmlSerializableﻟﺤﻔﻅ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻜـﺎﺌﻥ ﻓـﻲ ﻭﺜﻴﻘـﺔ XML
ﻭﻗﺭﺍﺀﺘﻬﺎ ﻤﻨﻬﺎ ﻓﻲ ﺃﻱ ﻭﻗﺕ.
-ﺍﻤﺘﻼﻙ ﻭﺴﻴﻠﺔ ﻤﺸﺘﺭﻜﺔ Shared Methodﺍﺴﻤﻬﺎ ،GetXsdTypeﺘﺴﺘﻘﺒل "ﻨـﻭﻉ
ﻤﺨﻁـــﻁ ،XmlSchemaSet "XMLﻭﺘﻌﻴـــﺩ ﻨﺴـــﺨﺔ ﻤـــﻥ ﺍﻟﻔﺌـــﺔ
XmlQualifiedNameﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻻﺴﻡ ﺍﻟﻜﺎﻤل ﻟﻨﻭﻉ XMLﺍﻟﻤﻨﺎﻅﺭ.
٤٥٨
ﺤﻔﻅ ﺍﻟﻤﻠﻔﺎﺕ ﺨﺎﺭﺝ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ:
ﻴﻘﺩﻡ ﻟﻙ ﺴﻴﻜﻴﻭل ﺴﻴﺭﻓﺭ ٢٠٠٨ﺇﻤﻜﺎﻨﻴﺔ ﺭﺍﺌﻌﺔ ،ﻭﻫﻲ ﻗﺩﺭﺘﻙ ﻋﻠﻰ ﺤﻔـﻅ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﺜﻨﺎﺌﻴـﺔ
ﺍﻟﻀﺨﻤﺔ ) BLOBﺘﻜﻭﻥ ﻓﻲ ﺍﻟﻐﺎﻟﺏ ﺃﻜﺒﺭ ﻤﻥ ١ﻤﻴﺠﺎ ﺒﺎﻴﺕ( ﺍﻟﺘﻲ ﺘﺭﺴﻠﻬﺎ ﺇﻟﻰ ﻋﻤـﻭﺩ ﻤـﻥ
ﺍﻟﻨﻭﻉ ) varbinary(MAXﻓﻲ ﻤﻠﻑ ﺨﺎﺹ ﻤﺴﺘﻘل ﻋﻥ ﻤﻠﻑ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻴﺘﻡ ﺤﻔﻅﻪ ﻓﻲ
ﻤﺠﻠﺩ ﺨﺎﺹ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ..ﻫﺫﺍ ﻴﺤﻘﻕ ﻟﻙ ﺍﻟﻔﻭﺍﺌﺩ ﺍﻟﺘﺎﻟﻴﺔ:
-١ﻴﻀﻤﻥ ﻋﺩﻡ ﺘﻀﺨﻡ ﺤﺠﻡ ﻤﻠﻑ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺼﻭﺭﺓ ﻜﺒﻴﺭﺓ.
-٢ﻴﻘﻠل ﻤﻥ ﺍﻟﺯﻤﻥ ﺍﻟﻼﺯﻡ ﻟﻘﺭﺍﺀﺓ ﻫﺫﻩ ﺍﻟﺒﻴﺎﻨﺎﺕ.
-٣ﻴﺴﺘﻁﻴﻊ ﺍﻟﻨﻭﻉ ) varbinary(MAXﺤﻔﻅ ﺒﻴﺎﻨﺎﺕ ﺤﺠﻤﻬﺎ ﺘﻘﺭﻴﺒﺎ ٢ﺠﻴﺠﺎ ﺒﺎﻴﺕ ﻓـﻲ
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺒﻴﻨﻤﺎ ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﻤﻠﻔﺎﺕ ﺨﺎﺭﺠﻴﺔ ﻻ ﻴﻜﻭﻥ ﻫﻨﺎﻙ ﺍﻱ ﺤﺩ ﻟﺠﻡ ﺍﻟﻤﻠﻑ،
ﺇﻻ ﻤﻘﺩﺍﺭ ﺍﻟﻤﺴﺎﺤﺔ ﺍﻟﻤﺘﻭﻓﺭﺓ ﻋﻠﻰ ﺍﻟﻘﺭﺹ ﺍﻟﺼﻠﺏ!
-٤ﻗﺩﺭﺘﻙ ﻋﻠﻰ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻫﺫﻩ ﺍﻟﻤﻠﻔﺎﺕ ﻤﻥ ﺨﻼل ﺍﺴـﺘﻌﻼﻤﺎﺕ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﺃﻭ
ﺍﻟﺘﻌﺎﻤل ﻤﻌﻬﺎ ﻤﺒﺎﺸﺭﺓ ﻤﻥ ﺨﻼل ﻨﻅﺎﻡ ﻤﻠﻔﺎﺕ ﺍﻟﻭﻴﻨﺩﻭﺯ .Windows File System
-٥ﺘﻘﺩﻡ ﻟﻙ ﺩﻭﺕ ﺕ ٢٠١٠ﻓﺌﺔ ﺨﺎﺼﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺤﻔﻭﻅﺔ ﺨـﺎﺭﺝ ﻗﺎﻋـﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﻭﻫﻲ ﺍﻟﻔﺌﺔ SqlFileStreamﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ.
ﻭﻫﻨﺎﻙ ﺃﺭﺒﻊ ﺨﻁﻭﺍﺕ ﻋﻠﻴﻙ ﺍﺘﺒﺎﻋﻬﺎ ،ﺤﺘﻰ ﺘﺴﺘﻁﻴﻊ ﺤﻔﻅ ﻗﻴﻡ ﺍﻷﻋﻤﺩﺓ ﺍﻟﻀـﺨﻤﺔ ﻓـﻲ ﻤﻠﻔـﺎﺕ
ﻤﺴﺘﻘﻠﺔ ..ﻫﺫﻩ ﺍﻟﺨﻁﻭﺍﺕ ﻫﻲ:
٤٥٩
-ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ،ﺍﻀﻐﻁ ﺍﻟﺸﺭﻴﻁ ﺍﻟﻌﻠـﻭﻱ Tabﺍﻟﻤﺴـﻤﻰ FILESTREAM
ﻟﻌﺭﺽ ﺼﻔﺤﺔ ﺨﺼﺎﺌﺼﻪ.
-ﻀﻊ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﺃﻤﺎﻡ ﺍﻻﺨﺘﻴﺎﺭ:
Enable FILESTREAM for Transact-SQL access
ﻫﺫﺍ ﺴﻴﺘﻴﺢ ﻟﻙ ﻗﺭﺍﺀﺓ ﻭﻜﺘﺎﺒﺔ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺨﺎﺭﺠﺤﻴﺔ ﻤﻥ ﺨﻼل ﺍﻻﺴﺘﻌﻼﻤﺎﺕ.
٤٦٠
ﺍﻟﻔﺌﺔ ..SqlFileStreamﻭﻴﺠﺏ ﻋﻠﻴﻙ ﺃﻥ ﺘﻜﺘﺏ ﻓﻲ ﻤﺭﺒـﻊ ﺍﻟـﻨﺹ ﺍﺴـﻡ ﻤﺠﻠـﺩ
ﺍﻟﻤﺸﺎﺭﻜﺔ ﺍﻟﺫﻱ ﺴﺘﻘﺭﺃ ﺍﻟﻤﻠﻑ ﻤﻥ ﺨﻼﻟﻪ ..ﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﻴﻜﻭﻥ ﻫﺫﺍ ﺍﻻﺴـﻡ
ﻫﻭ ،SQLEXPRESSﻟﻜﻥ ﻴﻤﻜﻨﻙ ﺘﻐﻴﻴﺭﻩ ﺇﻟﻰ ﻤﺎ ﺘﺸﺎﺀ.
-ﺇﺫﺍ ﻭﻀﻌﺕ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﺃﻤﺎﻡ ﺍﻻﺨﺘﻴﺎﺭ:
Allow remote clients to have streaming access to FILESTREAM
ﻓﺴﻴﺴﻤﺢ ﻫﺫﺍ ﻟﻠﻤﺴﺘﺨﺩﻤﻴﻥ ﻤﻥ ﺨﺎﺭﺝ ﺍﻟﺸـﺒﻜﺔ ﺍﻟﻤﺤﻠﻴـﺔ Remote Usersﺒﻘـﺭﺍﺀﺓ
ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻠﻑ ﻋﺒﺭ ﻨﻅﺎﻡ ﻤﺸﺎﺭﻜﺔ ﺍﻟﻤﻠﻔﺎﺕ.
ﻤﻠﺤﻭﻅﺔ ﻫﺎﻤﺔ:
ﻴﺸﻜﻭ ﻜﺜﻴﺭ ﻤﻥ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻔﺌﺔ SqlFileStreamﻤﻥ ﺃﻥ ﺭﺴـﺎﻟﺔ
ﺨﻁﺄ ﺘﻅﻬﺭ ﻟﻬﻡ ﺘﺨﺒﺭﻫﻡ ﺒﺄﻥ ﻤﺴﺎﺭ ﺍﻟﻤﻠﻑ ﻏﻴﺭ ﻤﻭﺠﻭﺩ ﻋﻠﻰ ﺍﻟﺸﺒﻜﺔ ..ﻴﻌـﻭﺩ ﻫـﺫﺍ
ﺍﻟﺴﺒﺏ ﻓﻲ ﺍﻟﻐﺎﻟﺏ ﺇﻟﻰ ﺘﻌﻁﻴﻠﻬﻡ ﻹﻤﻜﺎﻨﻴﺔ ﻤﺸـﺎﺭﻜﺔ ﺍﻟﻤﻠﻔـﺎﺕ Sharingﺍﻟﺨﺎﺼـﺔ
ﺒﺎﻟﻭﻴﻨﺩﻭﺯ ،ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺍﻟﺘﺄﻜﺩ ﻤﻥ ﺘﻔﻌﻴﻠﻬﺎ ﻗﺒل ﺘﻔﻌﻴل ﺍﻻﺨﺘﻴﺎﺭ:
Enable FILESTREAM for file I/O streaming access
ﻭﻟﻘﻌل ﻫﺫﺍ ،ﺍﺘﺒﻊ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ:
-ﺍﻓﺘﺢ ﻤﺘﺼﻔﺢ ﺍﻟﻭﻴﻨﺩﻭﺯ Windows Explorerﻭﻤﻥ ﺍﻟﻘﺎﺌﻤـﺔ ﺍﻟﺭﺌﻴﺴـﻴﺔ
Toolsﺍﻀﻐﻁ .Folder Options
-ﻓﻲ ﻨﺎﻓﺫﺓ ﺨﻴﺎﺭﺍﺕ ﺍﻟﻤﺠﻠﺩﺍﺕ ،ﺍﻀﻐﻁ ﺍﻟﺸﺭﻴﻁ ﺍﻟﻌﻠﻭﻱ ،Viewﻭﺘﺄﻜﺩ ﻤـﻥ
ﻭﻀﻊ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﺃﻤﺎﻡ ﺍﻟﺨﻴﺎﺭ ﺍﻷﺨﻴﺭ ﻓﻲ ﻗﺎﺌﻤﺔ ﺍﻟﺨﻴﺎﺭﺍﺕ:
Use simple file sharing.
-ﺍﻀﻐﻁ Okﻹﻏﻼﻕ ﺍﻟﻨﺎﻓﺫﺓ.
ﻭﺇﺫﺍ ﻜﻨﺕ ﻓﻌﻠﺕ ﺨﻴﺎﺭﺍﺕ ﻤﺠﺭﻯ ﺍﻟﺒﻴﺎﻨﺎﺕ FileStreamﺍﻟﺨﺎﺼﺔ ﺒﺨﺎﺩﻡ ﺴـﻴﻜﻭﻴل
ﻗﺒل ﺘﻔﻌﻴل ﺍﻟﻤﺸﺎﺭﻜﺔ ،ﻓﻘﻡ ﺒﺘﻌﻁﻴل ﺍﻟﺨﻴﺎﺭﺍﺕ ،FileStreamﻭﺃﻋﺩ ﺘﺸـﻐﻴل ﺨـﺎﺩﻡ
ﺴﻴﻜﻭﻴل ،Restartﺜﻡ ﺃﻋﺩ ﺘﻔﻴﻌل ﺨﻴﺎﺭﺍﺕ ﻤﺠـﺭﻯ ﺍﻟﺒﻴﺎﻨـﺎﺕ ..ﺒﻬـﺫﻩ ﺍﻟﻁﺭﻴﻘـﺔ
ﺴﺘﻀﻤﻥ ﻭﺼﻭل ﺒﺭﻨﺎﻤﺠﻙ ﺇﻟﻰ ﻤﻠﻔﺎﺕ ﺍﻟﻤﺸﺎﺭﻜﺔ ﺍﻟﺨﺎﺼﺔ ﺒﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ،ﻭﺍﻟﺘﻲ
ﻴﺤﻔﻅ ﻓﻴﻬﺎ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺨﺎﺭﺠﻴﺔ.
٤٦١
-ﺍﻀﻐﻁ Okﻹﻏﻼﻕ ﺍﻟﻨﺎﻓﺫﺓ ﻭﺤﻔﻅ ﻫﺫﻩ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ.
٤٦٢
ﺴﺘﺠﺩ ﺃﻭل ﺨﺎﺼﻴﺔ ﻓﻴﻬﺎ ﻫﻲ ،Filestream Access Levelﺍﻟﺘﻲ ﺘﻭﻀﺢ ﻜﻴﻔﻴﺔ ﺍﻟﺘﻌﺎﻤل
ﻤﻊ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺨﺎﺭﺠﻴﺔ ..ﺴﺘﺠﺩ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ Disabledﺃﻱ ﺃﻥ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻤﻠﻔﺎﺕ
ﺍﻟﺨﺎﺭﺠﻴﺔ ﻤﻤﻨﻭﻉ! ..ﺍﻀﻐﻁ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ،ﻭﺍﺨﺘﺭ ﺍﻟﺘﻌﺎﻤـل ﺍﻟﻜﺎﻤـل Full Access
..Enabledﻫﺫﺍ ﻴﺴﻤﺢ ﺒﺎﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻤﻠﻔﺎﺕ ﻤﺒﺎﺸﺭﺓ ،ﺃﻭ ﻤﻥ ﺨﻼل ﺍﻻﺴﺘﻌﻼﻤﺎﺕ ..ﺃﻤـﺎ
ﺇﺫﺍ ﺃﺭﺩﺕ ﻗﺼﺭ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻤﻠﻔﺎﺕ ﻤﻥ ﺨﻼل ﺍﺴﺘﻌﻼﻤﺎﺕ SQLﻓﻘﻁ ،ﻓﺎﺨﺘﺭ ﺍﻻﺨﺘﻴـﺎﺭ
ﺍﻟﺜﺎﻨﻲ ..Transact-SQL Access Enabled :ﺍﻀﻐﻁ ﺍﻟﺯﺭ Okﻹﻏـﻼﻕ ﺍﻟﻨﺎﻓـﺫﺓ..
ﺴﺘﻅﻬﺭ ﻟﻙ ﺭﺴﺎﻟﺔ ﺘﺨﺒﺭﻙ ﺒﺄﻥ ﺒﻌﺽ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻟﻥ ﺘﺤﺩﺙ ﺇﻻ ﺇﺫﺍ ﺃﻭﻗﻔﺕ ﺨﺎﺩﻡ ﺴـﻴﻜﻭﻴل
ﻋﻥ ﺍﻟﻌﻤل ﻭﺃﻋﺩﺕ ﺘﺸﻐﻴﻠﻪ ..Restartﻴﻤﻜﻨﻙ ﻓﻌل ﻫﺫﺍ ﻤﻥ ﻤﺩﻴﺭ ﺘﻬﻴﺌﺔ ﺨـﺎﺩﻡ ﺴـﻴﻜﻭﻴل
SQL Server Configuration Managerﻜﻤﺎ ﺘﻌﻠﻤﻨﺎ ﻤﻥ ﻗﺒل.
٤٦٣
-ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ،ﺍﻀﻐﻁ ﺍﻟﻌﻨﺼﺭ FileGroupsﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﺴـﺭﻯ ..ﺴـﺘﺠﺩ
ﺍﻟﺠﺯﺀ ﺍﻷﻴﻤﻥ ﻤﻘﺴﻭﻤﺎ ﺇﻟﻰ ﻨﺼﻔﻴﻥ:
ﺃ .ﺍﻟﻨﺼﻑ ﺍﻟﻌﻠﻭﻱ ﻴﻌﺭﺽ ﻤﺠﻤﻭﻋﺎﺕ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﻌﺎﺩﻴﺔ )ﻤﻠﻔﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺍﻷﺴﺎﺴﻴﺔ( ..ﻭﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﻤﺠﻤﻭﻋﺔ ﺃﺨﺭﻯ ﻟﻭ ﺃﺭﺩﺕ ﺘﻘﺴﻴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﻋﻠﻰ ﺃﻜﺜﺭ ﻤﻥ ﻤﻠﻑ.
ﺏ .ﺍﻟﻨﺼﻑ ﺍﻟﺴﻔﻠﻲ ﻴﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺨﺎﺭﺠﻴـﺔ ..Filestreamﻫـﺫﺍ ﻫـﻭ
ﺍﻟﻨﺼﻑ ﺍﻟﺫﻱ ﻴﻌﻨﻴﻨﺎ ..ﺍﻀﻐﻁ ﺍﻟﺯﺭ Addﻹﻀﺎﻓﺔ ﺼﻑ ﺠﺩﻴﺩ ،ﻭﻓـﻲ ﺨﺎﻨـﺔ
ﺍﻻﺴﻡ ﺍﻜﺘﺏ ،FILESTREAMﻭﻀـﻊ ﻋﻼﻤـﺔ ﺍﻻﺨﺘﻴـﺎﺭ ﻓـﻲ ﺍﻟﺨﺎﻨـﺔ
Defaultﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ.
-ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﺴﺭﻯ ﺍﻀﻐﻁ ﺍﻟﻌﻨﺼﺭ Filesﻟﻌﺭﺽ ﻤﻠﻔﺎﺕ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ،ﻭﻓـﻲ
ﺍﻟﺠﻬﺔ ﺍﻟﻴﻤﻨﻰ ﺍﻀﻐﻁ ﺍﻟﺯﺭ Addﻹﻀﺎﻓﺔ ﺼﻑ ﺠﺩﻴﺩ.
٤٦٤
-ﺍﻜﺘﺏ ﻓﻲ ﺨﺎﻨﺔ ﺍﻻﺴﻡ ..BooksFilesﺴﻴﻜﻭﻥ ﻫﺫﺍ ﻫﻭ ﺍﺴﻡ ﺍﻟﻤﺠﻠﺩ ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺤﻔـﻅ
ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺘﺎﺒﻌﺔ ﻟﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻴﻪ.
-ﻓﻲ ﺍﻟﺨﺎﻨﺔ File Typeﺃﺴﺩل ﻗﺎﺌﻤﺔ ﺍﻟﻌﻨﺎﺼﺭ ﻭﺍﺨﺘـﺭ ﺍﻟﻨـﻭﻉ ..FILESTREAM
ﻫﺫﺍ ﺴﻴﺠﻌل ﺍﻟﺨﺎﻨﺔ FileGroupﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻘﻴﻤﺔ FILESTREAMﺃﻴﻀﺎ.
-ﺍﺴﺘﺨﺩﻡ ﺍﻟﺯﺭ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﺨﺎﻨﺔ Pathﻻﺨﺘﻴﺎﺭ ﻤﻭﻀﻊ ﺤﻔﻅ ﺍﻟﻤﺠﻠﺩ ..ﻫﻨﺎﻙ ﺸـﺭﻁ
ﺇﺠﺒﺎﺭﻱ ﻋﻠﻴﻙ ﺍﻻﻟﺘﺯﺍﻡ ﺒﻪ ،ﻭﻫﻭ ﺤﺘﻤﻴﺔ ﺍﺨﺘﻴﺎﺭ ﻤﺴﺎﺭ ﻋﻠﻰ ﺠﺯﺀ ﻤﻥ ﺍﻟﻘﺭﺹ ﺍﻟﺼـﻠﺏ
ﻤﻬﻴﺄ ﺒﺘﻨﺴﻴﻕ NTFSﻭﻟﻴﺱ .FAT32
-ﺍﻀﻐﻁ Okﻟﺘﻨﻔﻴﺫ ﻜل ﻤﺎ ﻓﻌﻠﻨﺎﻩ ..ﺴﺘﺠﺩ ﺃﻥ ﻤﺠﻠﺩﺍ ﺍﺴﻤﻪ BooksFilesﻗﺩ ﺘﻡ ﺇﻨﺸﺎﺅﻩ
ﻋﻠﻰ ﺍﻟﻤﺴﺎﺭ ﺍﻟﺫﻱ ﺍﺨﺘﺭﺘﻪ ..ﻭﻴﺴﻤﻰ ﻫﺫﺍ ﺍﻟﻤﺠﻠﺩ ﺒﺤﺎﻭﻴﺔ ﺍﻟﻤﻠﻔﺎﺕ ،Data Container
ﺃﻭ ﺒﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻠﻔﺎﺕ ..Filegroupﻻﺤﻅ ﻤﺎ ﻴﻠﻲ:
ﺃ -ﻻ ﻴﻤﻜﻨﻙ ﺇﻨﺸﺎﺀ ﻤﺠﻤﻭﻋﺎﺕ ﻤﻠﻔﺎﺕ ﻤﺘﺩﺍﺨﻠﺔ.
ﺏ -ﻻ ﻴﻤﻜﻨﻙ ﺤﻔﻅ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻠﻔﺎﺕ ﻋﻠـﻰ ﻗﺴـﻡ ﻤﻀـﻐﻭﻁ Compressed
Volumeﻤﻥ ﺍﻟﻘﺭﺹ ﺍﻟﺼﻠﺏ.
ﺝ -ﻻ ﻴﺘﻡ ﺘﺸﻔﻴﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺤﻔﻭﻅﺔ ﻓﻲ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺨﺎﺭﺠﻴﺔ ،ﺤﺘﻰ ﻟﻭ ﻜﺎﻨﺕ ﻗﺎﻋﺩﺓ
ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﻘﻭﻡ ﺒﺘﺸﻔﻴﺭ ﺒﻴﺎﻨﺎﺘﻬﺎ ..ﻟﻬﺫﺍ ﻟﻭ ﻜﺎﻥ ﺍﻟﺘﺸـﻔﻴﺭ ﻤﻬﻤـﺎ ﺒﺎﻟﻨﺴـﺒﺔ ﻟـﻙ،
ﻓﺄﺭﺴل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺸﻔﺭﺓ ﻤﻨﺫ ﺍﻟﺒﺩﺍﻴﺔ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ.
ﺩ -ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺃﻭﺍﻤـﺭ ﺍﻟﺘﺤـﺩﻴﺙ Updateﻭﺍﻟﺤـﺫﻑ Deleteﻭﺍﻹﺩﺭﺍﺝ
Insertﺍﻟﺨﺎﺼﺔ ﺒﻠﻐﺔ SQLﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﻴﺤﻔﻅ ﺒﻴﺎﻨﺎﺘﻪ ﻓﻲ ﻤﻠﻑ
ﺨﺎﺭﺠﻲ ،ﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺘﺘﻌﺎﻤل ﺒﻬﺎ ﻤﻊ ﺃﻱ ﻋﻤﻭﺩ ﻋﺎﺩﻱ ،ﺤﻴﺙ ﺴـﺘﻘﻭﻡ
ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺈﺠﺭﺍﺀ ﻫﺫﻩ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻋﻠﻰ ﺍﻟﻤﻠﻑ ﺍﻟﺨﺎﺭﺠﻲ ﺩﻭﻥ ﺃﻥ ﺘﺸـﻐل
ﺫﻫﻨﻙ ﺒﻬﺫﺍ.
ﻫـ -ﻻ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻷﻤﺭ UPDATE .Writeﻟﻜﺘﺎﺒﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺠـﺯﺃﺓ ﻓـﻲ
ﻋﻤﻭﺩ ﻴﺤﻔﻅ ﺒﻴﺎﻨﺎﺘﻪ ﻓﻲ ﻤﻠﻑ ﺨﺎﺭﺠﻲ ..ﻭﺒﺩﻻ ﻤﻥ ﻫﺫﺍ ﻋﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻔﺌـﺔ
SqlFileStreamﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ ،ﻟﻜﺘﺎﺒﺔ ﺃﺠﺯﺍﺀ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻓـﻲ
ﺍﻟﻤﻠﻑ ﻤﺒﺎﺸﺭﺓ.
٤٦٥
-٤ﺘﻔﻌﻴل ﺍﺴﺘﺨﺩﺍﻡ ﻤﺠﺭﻯ ﺍﻟﺒﻴﺎﻨﺎﺕ FILESTREAMﻓﻲ ﺍﻟﻌﻤﻭﺩ:
ﺍﻵﻥ ﻴﻤﻜﻨﻙ ﺇﻨﺸﺎﺀ ﻋﻤﻭﺩ ﻓﻲ ﺠﺩﻭل ﺍﻟﻨﺎﺸﺭﻴﻥ )ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜﺎل( ﻴﺤﻔﻅ ﺒﻴﺎﻨﺎﺘﻪ ﻓﻲ ﻤﻠﻔﺎﺕ
ﺨﺎﺭﺠﻴﺔ ..ﻟﻜﻥ ﻟﻼﺴﻑ ،ﻻ ﻴﺤﺘﻭﻱ ﻤﺼﻤﻡ ﺍﻟﺠﺩﻭل ﻋﻠﻰ ﺨﺎﺼﻴﺔ ﺘﺘﻴﺢ ﻟﻨـﺎ ﺍﻟﻘﻴـﺎﻡ ﺒﻬـﺫﺍ
ﺒﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ،ﻟﻬﺫﺍ ﻟﻴﺱ ﺃﻤﺎﻤﻨﺎ ﺴﻭﻯ ﺍﺴﺘﺨﺩﺍﻡ ﺍﺴﺘﻌﻼﻡ ..T-SQLﻟﻔﻌل ﻫﺫﺍ ﺍﺘﺒـﻊ ﻤـﺎ
ﻴﻠﻲ:
-ﻓﻲ ﻤﺘﺼﻔﺢ ﺍﻟﻜﺎﺌﻨﺎﺕ ،ﺍﻀﻐﻁ ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ BOOKS.MDFﺒـﺯﺭ ﺍﻟﻔـﺄﺭﺓ
ﺍﻷﻴﻤﻥ ،ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤﺭ .New Query
-ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻻﺴﺘﻌﻼﻤﺎﺕ ،ﺍﻜﺘﺏ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺘﺎﻟﻲ:
ALTER TABLE Publishers
ADD Logo3 VARBINARY(MAX) FILESTREAM NULL,
RowGuid UNIQUEIDENTIFIER
NOT NULL ROWGUIDCOL
) (UNIQUE DEFAULT NEWID
GO
ﻫﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ ﻴﻴﻀﻑ ﺇﻟﻰ ﺍﻟﺠﺩﻭل Publishersﻋﻤﻭﺩﻴﻥ ﺠﺩﻴﺩﻴﻥ:
ﺃ .ﺍﻟﻌﻤﻭﺩ ،Logo3ﻭﻫﻭ ﻤﻥ ﺍﻟﻨـﻭﻉ ) VARBINARY(MAXﻭﺴـﻴﺤﻔﻅ
ﺒﻴﺎﻨﺎﺘﻪ ﻓﻲ ﻤﻠﻑ ﺨﺎﺭﺠﻲ ،FILESTREAMﻭﻴﻘﺒل ﺍﻟﻘﻴﻤﺔ .NULL
ﺏ .ﻭﺍﻟﻌﻤـــﻭﺩ ،RowGuidﻭﻫـــﻭ ﻤﻌـــﺭﻑ ﻤﺘﻔـــﺭﺩ ﻟﻠﺠـــﺩﻭل
،UNIQUEIDENTIFIERﻭﻻ ﻴﻘﺒل ﺍﻟﻌﺩﻡ ،NOT NULLﻭﻫﻭ ﻴﻌﻤل
ﻜﻤﻌﺭﻑ ﻟﺼﻔﻭﻑ ﺍﻟﺠﺩﻭل ..ROWGUIDCOLﻻﺤﻅ ﺃﻥ ﻭﺠـﻭﺩ ﻫـﺫﺍ
ﺍﻟﻌﻤﻭﺩ ﺇﺠﺒﺎﺭﻱ ﺇﺫﺍ ﺃﺭﺩﺕ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺨﺎﺭﺠﻴﺔ ،ﻷﻨﻪ ﻴﺴﺘﺨﺩﻡ ﻓﻲ ﺭﺒﻁ
ﺍﻟﻤﻠﻑ ﺒﺎﻟﺼﻑ ﺍﻟﺫﻱ ﻴﺤﻔﻅ ﺒﻴﺎﻨﺎﺘﻪ ،ﻟﻬﺫﺍ ﻻ ﻴﻤﻜﻥ ﺃﻥ ﻴﻘﺒل ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻘﻴﻤـﺔ
.Null
ﻭﻟﺘﻨﻔﻴﺫ ﻫﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ ،ﺍﻀﻐﻁ ﻓﻲ ﺃﻱ ﻤﻭﻀﻊ ﻤﻥ ﺍﻟﻨﺎﻓﺫﺓ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ ،ﻭﺍﻀﻐﻁ
ﺍﻷﻤﺭ ..Executeﺴﻴﻅﻬﺭ ﻓﻲ ﺍﻟﺠﺯﺀ ﺍﻟﺴﻔﻠﻲ ﻤﻥ ﺍﻟﻨﺎﻓﺫﺓ ﺭﺴﺎﻟﺔ ﺘﺨﺒـﺭﻙ ﺒﻨﺠـﺎﺡ ﺃﻭ
ﻓﺸل ﺍﻟﺘﻨﻔﻴﺫ.
٤٦٦
ﻭﻴﻤﻜﻨﻙ ﺍﻻﺴﺘﺭﺸﺎﺩ ﺒﻬﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ ﻓﻲ ﺍﻟﻤﻭﺍﻗﻑ ﺍﻟﻤﻤﺎﻟﺜﺔ ،ﻓﻜل ﻤﺎ ﻋﻠﻴﻙ ﻫﻭ ﺘﻐﻴﻴﺭ ﺍﺴﻡ
ﺍﻟﺠﺩﻭل Publishersﻭﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ Logo3ﻟﻴﺼﻴﺭ ﺍﻻﺴﺘﻌﻼﻡ ﻤﻨﺎﺴﺒﺎ ﻻﺤﺘﻴﺎﺠﺎﺘﻙ.
ﺍﻵﻥ ﻓﻘﻁ ﺃﻨﺠﺯﻨﺎ ﺍﻟﻤﻬﻤﺔ ﻜﺎﻤﻠﺔ ،ﻭﺒﺈﻤﻜﺎﻨﻙ ﺃﻥ ﺘﺤﻔﻅ ﺼﻭﺭﺓ ﻓـﻲ ﺍﻟﺤﻘـل Logo3ﺍﻟﺨـﺎﺹ
ﺒﺎﻟﻨﺎﺸﺭ ﺍﻷﻭل ،ﺒﺎﺴﺘﻌﻼﻡ ﻋﺎﺩﻱ ﻜﺎﻟﺘﺎﻟﻲ:
UPDATE Publishers
SET Logo3 = @Logo
WHERE ID = 1
ﻭﻴﻤﻜﻨﻙ ﺘﻨﻔﻴﺫ ﻫـﺫﺍ ﺍﻻﺴـﺘﻌﻼﻡ ﺒﻀـﻐﻁ ﺍﻟـﺯﺭ Write to FileStreamﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ
،WriteLargeDataﺤﻴﺙ ﺴﻴﻅﻬﺭ ﻟﻙ ﻤﺭﺒﻊ ﺤﻭﺍﺭ ﺍﺨﺘﻴﺎﺭ ﺼـﻭﺭﺓ ..ﺍﺨﺘـﺭ ﺃﻱ ﺼـﻭﺭﺓ
ﺘﺭﻴﺩﻫﺎ ﻭﺍﻀﻐﻁ ..OKﺍﻵﻥ ﻟﻭ ﻓﺘﺤﺕ ﺍﻟﻤﺠﻠﺩ BooksFilesﻓﺴﺘﺠﺩ ﻤﻠﻔﺎ ﺠﺩﻴﺩﺍ ﻗـﺩ ﺃﻀـﻴﻑ
ﺇﻟﻴﻪ ..ﻫﺫﺍ ﺍﻟﻤﻠﻑ ﺴﻴﺤﻤل ﺍﺴﻤﺎ ﻋﺠﻴﺒﺎ ﻟﻀﻤﺎﻥ ﻋﺩﻡ ﺘﺸﺎﺒﻪ ﺃﺴﻤﺎﺀ ﺍﻟﻤﻠﻔﺎﺕ ،ﻟﻜﻨﻙ ﻟـﻭ ﺃﺨـﺫﺕ
ﻨﺴﺨﺔ ﻤﻨﻪ ﻭﻏﻴﺭﺕ ﺍﻤﺘﺩﺍﺩﻫﺎ ﺇﻟﻰ ﺍﻤﺘﺩﺍﺩ ﺼﻭﺭﺓ ) .bmpﻤﺜﻼ( ﻓﺴﺘﺠﺩ ﺃﻨﻬﺎ ﻨﻔﺱ ﺍﻟﺼﻭﺭﺓ ﺍﻟﺘـﻲ
ﺍﺨﺘﺭﺘﻬﺎ.
ﻭﻟﻭ ﻋﺭﻀﺕ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻭل ﺍﻟﻨﺎﺸﺭﻴﻥ ،ﻓﺴﺘﺠﺩ ﻓﻲ ﺍﻟﺨﺎﻨﺔ ﺍﻟﺘﻲ ﺤﻔﻅﻨﺎ ﻓﻴﻬﺎ ﺍﻟﺼـﻭﺭﺓ ﺃﺭﻗﺎﻤـﺎ
ﺴﺩﺍﺴﻴﺔ ﻋﺸﺭﻴﺔ ،ﺘﺸﻴﺭ ﺇﻟﻰ ﻤﻭﻀﻊ ﻤﻠﻑ ﺍﻟﺼﻭﺭﺓ ﻓﻲ ﺍﻟﻤﺠﻠﺩ .BooksFiles
ﻭﻴﻤﻜﻨﻙ ﻗﺭﺍﺀﺓ ﺒﻴﺎﻨﺎﺕ ﻫﺫﻩ ﺍﻟﺼﻭﺭﺓ ﺒﺎﺴﺘﻌﻼﻡ ﻋﺎﺩﻱ ﻜﺎﻟﺘﺎﻟﻲ:
SELECT ID, Logo3
FROM Publishers
٤٦٧
ﺤﻴﺙ ﻴﻤﻜﻨﻙ ﻗﺭﺍﺀﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺼﻭﺭﺓ ﻜﺎﻤﻠﺔ ﺃﻭ ﺒﻁﺭﻴﻘﺔ ﺘﺘﺎﺒﻌﻴﺔ Sequentialﺒﺎﺴـﺘﺨﺩﺍﻡ ﻗـﺎﺭﺉ
ﺍﻟﺒﻴﺎﻨﺎﺕ DataReaderﻜﻤﺎ ﺴﻨﺭﻯ ﻻﺤﻘﺎ ..ﻭﻫﺫﺍ ﻫﻭ ﻨﻔﺱ ﻤﺎ ﻴﻤﻜﻨﻙ ﻓﻌﻠـﻪ ﻤـﻊ ﺍﻟﺒﻴﺎﻨـﺎﺕ
ﺍﻟﺜﻨﺎﺌﻴﺔ ﺍﻟﻤﺤﻔﻭﻅﺔ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤﺜـل imageﺃﻭ ) ..varbinary(MAXﻭﻴﻤﻜﻨـﻙ
ﻀﻐﻁ ﺍﻟﺯﺭ Read FileStreamﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ReadLargeDataﻟﺘﺠﺭﺒﺔ ﻗﺭﺍﺀﺓ ﺍﻟﺼﻭﺭﺓ
ﺍﻟﺘﻰ ﺤﻔﻅﺘﻬﺎ ﺨﺎﺭﺝ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺤﻴـﺙ ﺴـﻴﺘﻡ ﺤﻔﻅﻬـﺎ ﻋﻠـﻰ ﺍﻟﻤﺤـﺭﻙ \ C:ﺒﺎﻻﺴـﻡ
.Logo1.bmp
ﻭﻫﻜﺫﺍ ﻨﻜﻭﻥ ﻗﺩ ﺘﻌﺎﻤﻠﻨﺎ ﻤﻊ ﺍﻟﻤﻠﻑ ﺍﻟﺨﺎﺭﺠﻲ ﻜﺄﻨﻪ ﺠﺯﺀ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ..ﻴﺘﺒﻘـﻰ ﻏـﺫﻥ ﺃﻥ
ﻨﻌﺭﻑ ﻜﻴﻑ ﻨﺘﻌﺎﻤل ﻤﻊ ﻫﺫﺍ ﺍﻟﻤﻠﻑ ﻤﺒﺎﺸـﺭﺓ ..ﻫـﺫﺍ ﻫـﻭ ﺩﻭﺭ ﺍﻟﻔﺌـﺔ ..SqlFileStream
ﻓﻠﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ.
٤٦٨
ﻓﺌﺔ ﻤﺠﺭﻯ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل SqlFileStream Class
ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ،Streamﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻨﻬﺎ ﺘﻤﻠﻙ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﺍﻟﻘﺭﺍﺀﺓ ﻤﻥ ﺍﻟﻤﻠﻔـﺎﺕ
ﻭﺍﻟﻜﺘﺎﺒﺔ ﻓﻴﻬﺎ )ﺭﺍﺠﻊ ﻓﺼل ﺍﻟﻤﻠﻔﺎﺕ ﻓﻲ ﻜﺘﺎﺏ ﺇﻁﺎﺭ ﺍﻟﻌﻤل( ..ﻟﻜﻥ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻤﺨﺼﺼﺔ ﻟﻠﺘﻌﺎﻤل
ﻤﻊ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﻤﺤﻔﻭﻅﺔ ﺨﺎﺭﺝ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺨﻼل ﺍﻟﺴﻤﺔ .FILESTREAM
ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺼﻴﻐﺘﺎﻥ:
-١ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ ،ﻫﻲ ﺒﺎﻟﺘﺭﺘﻴﺏ:
-ﻨﺹ ﻴﻤﺜل ﻤﺴﺎﺭ ﺍﻟﻤﻠﻑ ..ﻭﻴﻤﻜﻨﻙ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤﺴـﺎﺭ ﺍﻟﻤﻠـﻑ ﻤـﻥ ﺨـﻼل
ﺍﺴﺘﻌﻼﻡ ،SQLﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ) ( .PathNameﺍﻟﺘﻲ ﺘﺴـﺘﺨﺩﻡ ﻤـﻊ ﺍﺴـﻡ
ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﻴﺤﻔﻅ ﺒﻴﺎﻨﺎﺘﻪ ﻓﻲ ﻤﻠﻔﺎﺕ ﺨﺎﺭﺠﻴﺔ.
-ﻤﺼﻔﻭﻓﺔ ﻭﺤـﺩﺍﺕ ﺜﻨﺎﺌﻴـﺔ Byte Arrayﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﻤﺤﺘـﻭﻯ ﺍﻟﺘﻌﺎﻤـل
..Transaction Contextﻭﻴﻤﻜﻨﻙ ﺍﻟﺤﺼﻭل ﻋﻠﻴﻬﺎ ﻤﻥ ﺨﻼل ﺍﺴﺘﻌﻼﻡ ،SQL
ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ:
GET_FILESTREAM_TRANSACTION_CONTEXT
ﻻﺤﻅ ﺃﻨﻙ ﻻ ﺘﺴﺘﻁﻴﻊ ﺇﺭﺴﺎل ﺍﻟﻘﻴﻤﺔ nullﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ..ﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﻋﻠﻴﻙ
ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻤﻠﻑ ﻤﻥ ﺨﻼل ﺘﻌﺎﻤل ..Transactionﻫﺫﺍ ﻴﻠﻔﺕ ﺍﻨﺘﺒﺎﻫﻙ ﺇﻟـﻰ ﺃﻥ
ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻤﺎ ﺯﺍل ﻴﻨﻅﻡ ﻋﻤﻠﻴﺔ ﺍﻟﻜﺘﺎﺒﺔ ﻓﻲ ﺍﻟﻤﻠـﻑ ،ﻭﺇﻥ ﺤـﺩﺙ ﺃﻱ ﺨﻁـﺄ
ﻓﺴﻴﻠﻐﻲ ﻜل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ،ﻭﻟﻥ ﻴﺘﻡ ﺤﻔﻅ ﻫـﺫﻩ ﺍﻟﺘﻐﻴﻴـﺭﺍﺕ ﺇﻻ ﺇﺫﺍ ﻗﻤـﺕ ﺒـﺈﺠﺭﺍﺀ
ﺍﻟﺘﻌﺎﻤﻼﺕ ،Commit Transactionsﻜﻤﺎ ﺴﻨﻌﺭﻑ ﻻﺤﻘﺎ
-ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ FileAccessﺍﻟﺘﻲ ﺘﻭﻀﺢ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﻟﺘﻌﺎﻤل ﺒﻬﺎ ﻤـﻊ
ﺍﻟﻤﻠﻑ :ﻟﻠﻘﺭﺍﺀﺓ ،Readﻟﻠﻜﺘﺎﺒﺔ ،Writeﺃﻡ ﻟﻠﻘﺭﺍﺀﺓ ﻭﺍﻟﻜﺘﺎﺒﺔ ﻤﻌﺎ .ReadWrite
-٢ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﻤﻌﺎﻤﻠﻴﻥ ﺇﻀﺎﻓﻴﻴﻥ:
-ﻤﻌﺎﻤل ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﺭﻗﻡ ،FileOptionsﻴﺤﺩﺩ ﺨﻴﺎﺭﺍﺕ ﺍﻟﺘﻤﻌﺎﻤل ﻤـﻊ ﺍﻟﻤﻠـﻑ..
ﻭﻗﺩ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﻤﺭﻗﻡ ﻓﻲ ﻓﺼل ﺍﻟﻤﻠﻔﺎﺕ ﻓﻲ ﻜﺘﺎﺏ ﺒﺭﻤﺠﺔ ﺇﻁـﺎﺭ ﺍﻟﻌﻤـل،
ﻟﻜﻥ ﻤﺎ ﻴﻬﻤﻨﺎ ﻤﻥ ﻗﻴﻤﻪ ﻫﻨﺎ ﻫﻭ ﺍﻟﻘﻴﻤﺔ ،SequentialScanﻓﻬﻲ ﺘﺘﻴﺢ ﻟﻨﺎ ﻗـﺭﺍﺀﺓ
٤٦٩
ﺃﺠﺯﺍﺀ ﻤﻥ ﺍﻟﻤﻠﻑ ﻋﻠﻰ ﺍﻟﺘﻭﺍﻟﻲ ،ﻭﺍﻟﻘﻴﻤﺔ RandomAccessﺍﻟﺘـﻲ ﺘﺘـﻴﺢ ﻟﻨـﺎ
ﺍﻟﻘﺭﺍﺀﺓ ﻤﻥ ﺃﻱ ﻤﻭﻀﻊ ﻓﻲ ﺍﻟﻤﻠﻑ ﺩﻭﻥ ﺘﺭﺘﻴﺏ.
-ﻋﺩﺩ ﺼﺤﻴﺢ ﻴﺴﺘﻘﺒل ﺍﻟﺤﺠﻡ ﺍﻟﻤﺒﺩﺌﻲ ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺤﺠﺯﻩ ﻟﻠﻤﻠﻑ ﻋﻨﺩ ﺇﻨﺸـﺎﺌﻪ ..ﻭﺇﺫﺍ
ﺃﺭﺩﺕ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﺍﻟﺨﺎﺼﺔ ﺒﺩﻭﺕ ﻨﺕ ،ﻓﺄﺭﺴﺎل ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤـل
ﺍﻟﻘﻴﻤﺔ ﺼﻔﺭ.
ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ﻤﻥ ﻭﺴﺎﺌل ﻭﺨﺼﺎﺌﺹ ،ﺘﻤﺘﻠﻙ ﻫـﺫﻩ ﺍﻟﻔﺌـﺔ ﺍﻟﺨﺎﺼـﻴﺘﻴﻥ
ﺍﻟﺘﺎﻟﻴﺘﻴﻥ:
ﺍﻻﺴﻡ :Name
ﺘﻌﻴﺩ ﻤﺴﺎﺭ ﺍﻟﻤﻠﻑ ﺍﻟﻤﺭﺴل ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ﻓﻲ ﺤﺩﺙ ﺍﻹﻨﺸﺎﺀ.
٤٧٠
ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﺴﺘﻌﻼﻤﺎ ﺁﺨﺭ ﻟﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ Nullﻓﻲ ﺍﻟﻤﻠﻑ ،ﻭﻓﺼـﻠﻨﺎ ﺍﻻﺴـﺘﻌﻼﻤﻴﻥ ﺒﺎﻟﻔﺎﺼـﻠﺔ
ﺍﻟﻤﻨﻘﻭﻁﺔ:
UPDATE Publishers
SET Logo3 = 0x0
;Where ID = 2
SELECT Logo3.PathName( ),
) ( GET_FILESTREAM_TRANSACTION_CONTEXT
From Publishers
Where ID = 2
ﺍﻟﺤﻜﻤﺔ ﻤﻥ ﻭﺭﺍﺀ ﻫﺫﺍ ،ﻫﻭ ﺃﻥ ﺨﺎﻨﺔ ﺍﻟﺼﻭﺭﺓ ﻟﻭ ﻜﺎﻨﺕ ﻓﺎﺭﻏﺔ ﻓﻠﻥ ﻴﻜﻭﻥ ﻫﻨﺎﻙ ﻤﻠـﻑ ﻤـﺭﺘﺒﻁ
ﺒﻬﺎ ،ﻭﺴﺘﻌﻴﺩ ﺍﻟﻭﺴﻴﻠﺔ PathNameﺍﻟﻘﻴﻤﺔ Nullﻭﺒﺎﻟﺘﺎﻟﻲ ﻟﻥ ﻨﺴﺘﻁﻴﻊ ﺍﻟﻜﺘﺎﺒﺔ ﻓﻲ ﺍﻟﻤﻠﻑ ﺒﺎﻟﻔﺌﺔ
..SqlFileStreamﻟﻬﺫﺍ ﺴﻨﻀﻊ ﻓﻲ ﺍﻟﺨﺎﻨﺔ ﺍﻟﻘﻴﻤﺔ ..0x0ﻫﺫﺍ ﻴﺠﻌﻠﻬﺎ ﺘﻨﺸﺊ ﻤﻠﻔـﺎ ﻭﺘﺘﺭﻜـﻪ
ﻓﺎﺭﻏﺎ ،ﻟﻜﻥ ﻤﺎ ﻴﻌﻨﻴﻨﺎ ﻫﻨﺎ ﻫﻭ ﺃﻨﻨﺎ ﻨﺴﺘﻁﻴﻊ ﻤﻌﺭﻓﺔ ﻤﺴﺎﺭﻩ ﻟﻠﻜﺘﺎﺒﺔ ﻓﻴﻪ ..ﺃﻤﺎ ﻟﻭ ﻜﺎﻥ ﻫﻨﺎﻙ ﻤﻠـﻑ
ﻓﻌﻼ ﻭﺒﻪ ﺒﻴﺎﻨﺎﺕ ،ﻓﺴﻴﺘﻡ ﻤﺤﻭﻫﺎ ،ﻭﻫﺫﺍ ﻴﻨﺎﺴﺒﻨﺎ ﻓﻌﻼ ،ﻷﻨﻨﺎ ﺴﻨﻜﺘﺏ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻴﺩﺓ.
ﺃﻤﺎ ﻟﻭ ﺃﺭﺩﺕ ﺇﻀﺎﻓﺔ ﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻨﻬﺎﻴﺔ ﻤﻠﻑ ﻤﻭﺠﻭﺩ ،ﻓﺴﺘﺤﺘﺎﺝ ﺃﻭﻻ ﺇﻟـﻰ ﺍﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ
SqlFileStream.Seekﻟﻠﻭﺼﻭل ﺇﻟﻰ ﻨﻬﺎﻴﺔ ﻫﺫﺍ ﺍﻟﻤﻠﻑ ﻗﺒل ﺒﺩﺀ ﺍﻟﻜﺘﺎﺒﺔ ﻓﻴﻪ ،ﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘـﺔ
ﺍﻟﺘﻘﻠﻴﺩﻴﺔ ﺍﻟﺘﻲ ﺘﺘﻌﺎﻤل ﺒﻬﺎ ﻤﻊ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﻌﺎﺩﻴﺔ:
;)SqlFileStream.Seek(0, SeekOrigin.End
ﻁﺒﻌﺎ ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻟﻥ ﺘﺴﺘﺨﺩﻡ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺫﻱ ﻴﻀﻊ ﺍﻟﻘﻴﻤﺔ 0x0ﻓﻲ ﺍﻟﺨﺎﻨﺔ ،ﻷﻨـﻙ ﺘﺭﻴـﺩ
ﺍﻟﻤﺤﺎﻓﻅﺔ ﻋﻠﻰ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻠﻑ ﺍﻟﻤﻭﺠﻭﺩ.
٤٧١
ﻤﻠﺤﻕ٣ :
ﺇﻋﺩﺍﺩ ﺘﻁﺒﻴﻕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل
ﺒﻌﺩ ﺃﻥ ﺘﻨﺘﻬﻲ ﻤﻥ ﻜﺘﺎﺒﺔ ﻤﺸﺭﻭﻉ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺴﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺘﺸﻐﻴﻠﻪ ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل..
ﻟﻔﻌل ﻫﺫﺍ ﺍﺘﺒﻊ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ:
ﻤﻠﺤﻭﻅﺔ:
ﺍﻟﺸﺭﺡ ﺍﻟﺴﺎﺒﻕ ﻴﺨﺹ ﺘﻁﺒﻴﻘﺎﺕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺨﺎﺼﺔ ﺒﺸﺭﻜﺔ ﺃﻭ ﻋﻤﻴل ﻤﺤﺩﺩ ،ﻷﻨﻙ ﻫﻨﺎ ﺘﻌﺩ
ﺍﻟﺒﺭﻨﺎﻤﺞ ﻤﺭﺓ ﻭﺍﺤﺩﺓ ﻓﻘﻁ ﻟﻌﻤﻴل ﻭﺍﺤﺩ ،ﻭﺒﺎﻟﺘﺎﻟﻲ ﺘﺴﺘﻁﻴﻊ ﺃﻥ ﺘﺭﻴﻪ ﻫﺫﻩ ﺍﻟﺨﻁﻭﺍﺕ ﻋﻤﻠﻴﺎ ﻭﺘﺩﺭﺒﻪ
٤٧٣
ﻋﻠﻴﻬﺎ ﻟﻴﻘﻭﻡ ﺒﻬﺎ ﺒﻨﻔﺴﻪ ﺒﻌﺩ ﺫﻟﻙ ،ﻤﻊ ﻜﺘﺎﺒﺔ ﻤﻠﻑ ﺘﻌﻠﻴﻤﺎﺕ ﻴﺸﺭﺡ ﻟﻪ ﻫﺫﻩ ﺍﻟﺨﻁﻭﺍﺕ ،ﺒﺤﻴـﺙ ﻻ
ﻴﺤﺘﺎﺝ ﺇﻟﻴﻙ ﺒﻌﺩ ﻫﺫﺍ.
ﻟﻜﻥ ﺍﻷﻤﺭ ﺴﻴﺨﺘﻠﻑ ﻤﻊ ﺍﻟﺒﺭﺍﻤﺞ ﺍﻟﺘﻲ ﺘﺒﺎﻉ ﻓﻲ ﺍﻟﺴﻭﻕ ﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻜﺜﺭ ،ﻓﻔﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻋﻠﻴﻙ
ﺃﻥ ﺘﻜﺘﺏ ﺒﺭﻨﺎﻤﺞ ﺇﻋﺩﺍﺩ ﻴﻘﻭﻡ ﺒﻜل ﺃﻭ ﻤﻌﻅﻡ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺴﺎﺒﻘﺔ ﺁﻟﻴـﺎ )ﺃﻱ ﺃﻨـﻙ ﺴـﺘﺠﻤﻊ ﻜـل
ﺍﻟﺨﻁﻭﺍﺕ ﻓﻲ ﺍﻟﺨﻁﻭﺓ ﺭﻗﻡ ٤ﻓﻲ ﺍﻟﺸﺭﺡ ﺍﻟﺴﺎﺒﻕ( ..ﻭﻓﻲ ﺍﻟﻐﺎﻟﺏ ﺴﻴﺴﺘﺜﻨﻰ ﻤﻥ ﺍﻷﻤـﺭ ﺇﻋـﺩﺍﺩ
ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻷﻨﻪ ﺘﻁﺒﻴﻕ ﻤﺴﺘﻘل ﺒﺘﺭﺨﻴﺹ ،ﻜﻤﺎ ﺃﻥ ﺇﻋﺩﺍﺩﻩ ﻋﻠﻰ ﺨﺎﺩﻡ ﻴﻌﻤل ﻋﻠـﻰ ﺸـﺒﻜﺔ
ﺃﻤﺭ ﻴﺨﺹ ﺍﻟﻤﺴﺌﻭﻟﻴﻥ ﻋﻥ ﺇﺩﺍﺭﺓ ﻫﺫﻩ ﺍﻟﺸﺒﻜﺔ ﻭﺘﺄﻤﻴﻨﻬﺎ ﻭﻤﻨﺢ ﺍﻟﺼﻼﺤﻴﺎﺕ ﻟﻤﺴﺘﺨﺩﻤﻴﻬﺎ.
ﻫل ﻴﻤﻜﻥ ﺍﻻﻋﺘﻤﺎﺩ ﻋﻠﻰ ﻨﺴﺨﺔ SQL Server Expressﻋﻨﺩ ﺘﻭﺯﻴﻊ ﺍﻟﺒﺭﻨﺎﻤﺞ؟
SQL Server Expressﻫﻲ ﺍﻟﻨﺴﺨﺔ ﺍﻟﻤﺠﺎﻨﻴﺔ ﻤﻥ ﺴﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ ،ﻭﻫـﻲ ﺘﺴـﻤﺢ ﻟـﻙ
ﺒﺎﻟﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻴﺼل ﺤﺠﻤﻬﺎ ﺍﻷﻗﺼﻰ ﺇﻟﻰ ١٠ﺠﻴﺠﺎ ﺒﺎﻴﺕ ،ﻭﻋﻤﻠﻴـﺎﺕ ﻤﺘﺯﺍﻤﻨـﺔ
ﺘﺴﺘﻬﻠﻙ ١ﺠﻴﺠﺎ ﻤﻥ ﺍﻟﺫﺍﻜﺭﺓ ﺒﺤﺩ ﺃﻗﺼﻰ ..ﻭﺭﻏﻡ ﺃﻥ ﻫﺫﻩ ﺍﻟﻘﻴﻭﺩ ﺘﺒﺩﻭ ﻓﻘﻴﺭﺓ ﻟﻠﻐﺎﻴﺔ ﺒﺠﻭﺍﺭ ﻤـﺎ
ﻴﻤﻜﻥ ﺃﻥ ﺘﻔﻌﻠﻪ ﺍﻟﻨﺴﺨﺔ ﺍﻟﻜﺎﻤﻠﺔ ﻤﻥ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ،ﺘﻅلّ ﺍﻟﻨﺴـﺨﺔ ﺍﻟﻤﺠﺎﻨﻴـﺔ ﻤﻨﺎﺴـﺒﺔ ﺠـﺩﺍ
ﻟﻠﺘﻁﺒﻴﻘﺎﺕ ﺍﻟﺘﻲ ﺘﻌﻤل ﻋﻠﻰ ﺠﻬﺎﺯ ﺸﺨﺼﻲ ﺃﻭ ﺸﺒﻜﺔ ﺼﻐﻴﺭﺓ ﺃﻭ ﻤﻭﻗﻊ ﺇﻨﺘﺭﻨﺕ ﺼﻐﻴﺭ ،ﻓﻤـلﺀ
ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺒـ ١٠ﺠﻴﺠﺎ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻤﺭ ﺼﻌﺏ ،ﻤﺎ ﻟﻡ ﺘﻜﻥ ﺘﺘﻌﺎﻤل ﻤﻊ ﺸـﺭﻜﺔ ﻋﻤﻼﻗـﺔ
ﺘﺨﺩﻡ ﺁﻻﻑ ﺍﻟﻌﻤﻼﺀ ﻴﻭﻤﻴﺎ ،ﻭﺘﺤﻔﻅ ﺒﻌﺽ ﺍﻟﺼﻭﺭ ﻭﺍﻟﻤﻠﻔﺎﺕ ﻜﺒﻴﺭﺓ ﺍﻟﺤﺠﻡ.
ﻟﻜﻥ ..ﻤﺎﺫﺍ ﻟﻭ ﺯﺍﺩ ﺍﻟﻀﻐﻁ ﻋﻠﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﻤﺘﻸﺕ ﻓﻌﻼ ﻭﺘﻭﻗﻑ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻬـﺎ )ﻓﻠﻨﻘـل
ﺒﻌﺩ ﻋﺎﻤﻴﻥ ﺃﻭ ﺜﻼﺜﺔ ﻤﺜﻼ(؟!
ﻓﻲ ﺍﻟﺤﻘﻴﻘﺔ ،ﺃﻨﺎ ﻟﺴﺕ ﻤﻥ ﺃﻨﺼﺎﺭ ﺘﺭﻙ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺘﻀﺨﻡ ﺒﻼ ﺤﺩ ﺤﺘﻰ ﻟﻭ ﻜﻨﺎ ﻨﺘﻌﺎﻤل ﻤـﻊ
ﺍﻟﻨﺴﺨﺔ ﺍﻟﻜﺎﻤﻠﺔ ﻤﻥ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ..ﻓﺯﻴﺎﺩﺓ ﺤﺠﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺴﺒﺏ ﺴﻠﺒﻴﺎﺕ ﻜﺜﻴﺭﺓ ﻤﻨﻬﺎ:
-ﻭﻗﺕ ﺃﻁﻭل ﻓﻲ ﺍﻟﺒﺤﺙ ﻭﺍﻟﻔﻬﺭﺴﺔ ﻭﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻟﻀﻐﻁ ﻭﺍﻹﺼﻼﺡ.
-ﻤﺸﺎﻜل ﺃﻜﺜﺭ ﻋﻨﺩ ﺤﻔﻅ ﻨﺴﺦ ﺍﺤﺘﻴﺎﻁﻴﺔ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ،ﺘﺘﻌﻠﻕ ﺒﻤﺴـﺎﺤﺔ ﺍﻟﺘﺨـﺯﻴﻥ
ﻭﺒﻁﺀ ﺍﻟﻨﻘل.
-ﻭﺠﻭﺩ ﺒﻴﺎﻨﺎﺕ ﻜﺜﻴﺭﺓ ﻗﺩﻴﻤﺔ ﻗﺩ ﺘﻜﻭﻥ ﺍﻟﺤﺎﺠﺔ ﺇﻟﻴﻬﺎ ﻗﺩ ﺍﻨﺘﻬﺕ ﺃﻭ ﻗﻠﺕ ،ﺘﻅل ﺘﺩﺨل ﻓـﻲ
ﻋﻤﻠﻴﺎﺕ ﺤﺴﺎﺒﻴﺔ ﺃﻭ ﻨﻘﺩﻴﺔ ﺃﻭ ﺇﺤﺼﺎﺌﻴﺎﺕ ﺃﻭ ﻨﺘﺎﺌﺞ ﺒﺤﺙ ﺃﻭ ﻏﻴﺭ ﺫﻟﻙ.
٤٧٤
ﻟﻬﺫﺍ ﻴﻭﺠﺩ ﺤلّ ﻋﻤﻠﻲ ﺒﺴﻴﻁ ،ﻴﺴﺘﺨﺩﻡ ﺤﺘﻰ ﺨﺎﺭﺝ ﺍﻟﻨﻅﻡ ﺍﻟﺭﻗﻤﻴﺔ ﻓـﻲ ﺃﻱ ﺘﻌـﺎﻤﻼﺕ ﻭﺭﻗﻴـﺔ
ﺤﻜﻭﻤﻴﺔ ﺃﻭ ﺘﺠﺎﺭﻴﺔ ،ﻭﻫﻭ ﺇﻏﻼﻕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻨﻬﺎﻴﺔ ﻜل ﻋﺎﻡ ﻭﺤﻔﻅﻬﺎ ﻜﻨﺴﺨﺔ ﺃﺭﺸـﻴﻔﻴﺔ،
ﻭﺇﻨﺸﺎﺀ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻴﺩﺓ ﺒﺘﺎﺭﻴﺦ ﺍﻟﺴﻨﺔ ﺍﻟﺘﺎﻟﻴﺔ ..ﻭﻫﻜﺫﺍ ﻴﺤﺘﻔﻅ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺒﻘﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻤﻐﻠﻘﺔ
ﻟﻜل ﺴﻨﺔ ﻤﻀﺕ ،ﻭﻴﺘﻌﺎﻤل ﻓﻘﻁ ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺴﻨﺔ ﺍﻟﺤﺎﻟﻴﺔ ،ﻤﻊ ﺘﻘﺩﻴﻡ ﺇﻤﻜﺎﻨﻴـﺔ ﻟﻠﻤﺴـﺘﺨﺩﻡ
ﻟﻔﺘﺢ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺴﻨﻭﺍﺕ ﺍﻟﻤﺎﻀﻴﺔ ﻟﻠﺒﺤﺙ ﻓﻴﻬﺎ )ﺒﺼﻼﺤﻴﺔ ﻤﺴﺘﺨﺩﻡ( ﺃﻭ ﺘﻌﺩﻴﻠﻬﺎ )ﺒﺼـﻼﺤﻴﺔ
ﺍﻟﻤﺩﻴﺭ(.
ﻨﺼﻴﺤﺔ :ﻓﻲ ﺍﻟﻨﻅﻡ ﺍﻟﺘﻲ ﻴﻤﻠﻙ ﻓﻴﻬﺎ ﺃﻜﺜﺭ ﻤﻥ ﻤﺴﺘﺨﺩﻡ ﺼﻼﺤﻴﺔ ﺍﻟﺘﻌﺩﻴل ،ﺍﺤﻔﻅ ﻓﻲ ﻜل ﺴـﺠل،
ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻟﺫﻱ ﺃﺩﺨﻠﻪ ﻭﺁﺨﺭ ﻤﺴﺘﺨﺩﻡ ﻋﺩﻟﻪ ،ﻟﻤﻨﻊ ﺃﻱ ﺘﻼﻋﺒﺎﺕ.
٤٧٥
ﻤﻠﺤﻭﻅﺔ:
ﺘﻡ ﻨﺸﺭ ﺍﻟﻔﺼﻭل ١٥ﻭ ١٦ﻭ ١٧ﻭﺍﻟﻤﻠﺤﻕ ١ﻓﻲ ﻜﺘﺎﺏ ﻤﺴﺘﻘل ﺒﻌﻨﺎﻭﺍﻥ:
ﺠﺩﻭل ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ DataGridView
ﻴﻤﻜﻨﻡ ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻫﻨﺎ:
https://drive.google.com/file/d/1tm27L0yGX1RA__vxXng0t5ny3XuJUFv_/view?fbclid=IwAR0IM7zdX9dqkWPXttQyQU6s-
FPna2m0hshLq9itiog9SS6PISFdes7Gm_0
ﺃﺨﻲ ﺍﻟﻔﺎﻀل:
ﺇﺫﺍ ﻜﻨﺕ ﻗﺩ ﺍﺴﺘﻔﺩﺕ ﺒﺒﻌﺽ ﺃﻭ ﻜل ﻤﺎ ﻗﺭﺃﺘﻪ ﻓﻲ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ،ﻓﻼ ﺘﻨﺴﻨﻲ ﻤﻥ ﺩﻋﺎﺌﻙ ﺒﺎﻟﻬﺩﺍﻴﺔ
ﻭﺍﻟﺘﻭﻓﻴﻕ ﻭﺍﻟﺴﺩﺍﺩ ﻭﺍﻟﺭﺯﻕ ﻭﺤﺴﻥ ﺍﻟﺨﺎﺘﻤﺔ.
ﻭﺍﺩﻉ ﻷﺒﻲ ﺭﺤﻤﻪ ﺍﷲ ﺒﺎﻟﺭﺤﻤﺔ ،ﻓﻘﺩ ﻨﺸﺭﺕ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ﻤﺠﺎﻨﺎ ﻜﺼﺩﻗﺔ ﺠﺎﺭﻴﺔ ﻟﻪ ..ﻭﺇﺫﺍ ﻜﻨﺕ
ﻓﻲ ﺍﻟﺤﺭﻡ ﺃﻭ ﻋﻠﻰ ﻤﻘﺭﺒﺔ ﻤﻨﻪ ،ﻓﻼ ﺘﺒﺨل ﺒﻌﻤل ﻋﻤﺭﺓ ﺴﺭﻴﻌﺔ ﻟﻪ ﻭﺍﻟﺩﻋﺎﺀ ﻟﻪ ﻓﻲ ﺍﻟﺤﺭﻡ
ﺍﻟﻤﻜﻲ ﻭﺍﻟﺤﺭﻡ ﺍﻟﻨﺒﻭﻱ ﺍﻟﺸﺭﻴﻔﻴﻥ.
٤٧٦