0% found this document useful (0 votes)
32 views

C# Ado - Net 2010

Uploaded by

aaltnazfti
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
32 views

C# Ado - Net 2010

Uploaded by

aaltnazfti
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 476

‫ﻤﻥ ﺍﻟﺼﻔﺭ ﺇﻟﻰ ﺍﻻﺤﺘﺭﺍﻑ‪:‬‬

‫‪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‬‬

‫ﻜﺘﺏ ﻤﺠﺎﻨﻴﺔ ﻟﻠﻜﺎﺘﺏ ﻟﻠﺘﻨﺯﻴل‪:‬‬


‫‪ - ١‬ﻜﺘﺎﺏ‪" :‬ﺨﺭﺍﻓﺔ ﺩﺍﺭﻭﻴﻥ‪ ،‬ﺤﻴﻨﻤﺎ ﺘﺘﺤﻭل ﺍﻟﺼﺩﻓﺔ ﺇﻟﻰ ﻋﻠﻡ"‪:‬‬
‫‪http://mhmdhmdy.blogspot.com/2013/11/blog-post_29.html‬‬
‫‪ - ٢‬ﻜﺘﺏ ﺃﺩﺒﻴﺔ )ﺃﺸﻌﺎﺭ ﻭﻗﺼﺹ ﻭﺭﻭﺍﻴﺎﺕ(‪:‬‬
‫‪https://mhmdhmdy.blogspot.com/2018/10/blog-post_23.html‬‬
‫‪ -٣‬ﺍﻟﻤﺒﺭﻤﺞ ﺍﻟﺼﻐﻴﺭ‪:‬‬
‫‪http://mhmdhmdy.blogspot.com.eg/2016/10/blog-post_9.html‬‬
‫‪ -٤‬ﺍﻟﺭﺴﻡ ﻭﺍﻟﺘﻠﻭﻴﻥ ﻭﺍﻟﺼﻭﺭ ﻭﺍﻟﻤﺠﺴﻤﺎﺕ ﻟﻤﺒﺭﻤﺠﻲ ﻓﻴﺠﻭﺍل ﺒﻴﺯﻴﻙ ﺩﻭﺕ ﻨﺕ‪:‬‬
‫‪http://mhmdhmdy.blogspot.com.eg/2014/05/blog-post_26.html‬‬
‫‪ -٥‬ﺍﻟﺭﺴﻡ ﻭﺍﻟﺘﻠﻭﻴﻥ ﻭﺍﻟﺼﻭﺭ ﻭﺍﻟﻤﺠﺴﻤﺎﺕ ﻟﻤﺒﺭﻤﺠﻲ ﺴﻲ ﺸﺎﺭﺏ‪:‬‬
‫‪http://mhmdhmdy.blogspot.com.eg/2014/08/c.html‬‬
‫‪ -٦‬ﻤﻥ ﺍﻟﺼﻔﺭ ﺇﻟﻰ ﺍﻻﺤﺘﺭﺍﻑ‪ :‬ﺒﺭﻤﺠﺔ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻓﻴﺠﻭﺍل ﺒﻴﺯﻴﻙ ﺩﻭﺕ ﻨﺕ‪:‬‬
‫‪http://mhmdhmdy.blogspot.com.eg/2016/02/blog-post_28.html‬‬

‫‪٤‬‬
‫ﻜﺘﺏ ﻤﻁﺒﻭﻋﺔ ﻟﻠﻜﺎﺘﺏ‪:‬‬

‫ـﻴﻥ‬
‫ـﺩﻯ ﺍﻟﻠﻐﺘـ‬
‫ـﻥ ﺇﺤـ‬
‫ـﺎل ﻤـ‬
‫ـﺭ ﻟﻼﻨﺘﻘـ‬
‫ـﻙ ﺍﻟﻤﺨﺘﺼـ‬
‫ـﺎﺭﺏ‪ :‬ﻁﺭﻴﻘـ‬
‫ـﻲ ﺸـ‬
‫ـﻙ ﻭﺴـ‬
‫ـﻭﺍل ﺒﻴﺯﻴـ‬
‫‪ .١‬ﻓﻴﺠﻴـ‬
‫ﺇﻟﻰ ﺍﻷﺨﺭﻯ‪.‬‬
‫‪ .٢‬ﻤﻥ ﺍﻟﺼﻔﺭ ﺇﻟﻰ ﺍﻻﺤﺘﺭﺍﻑ‪ :‬ﻓﻴﺠﻴﻭﺍل ﺒﻴﺯﻴﻙ ﺩﻭﺕ ﻨﺕ ‪.٢٠١٧‬‬
‫‪ .٣‬ﻤﻥ ﺍﻟﺼﻔﺭ ﺇﻟﻰ ﺍﻻﺤﺘﺭﺍﻑ‪ :‬ﺴﻲ ﺸﺎﺭﺏ ‪.٢٠١٧‬‬
‫‪ .٤‬ﻤﻥ ﺍﻟﺼﻔﺭ ﺇﻟﻰ ﺍﻻﺤﺘﺭﺍﻑ‪ :‬ﺒﺭﻤﺠﺔ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﻟﻤﺒﺭﻤﺠﻲ ﻓﻴﺠﻴـﻭﺍل ﺒﻴﺯﻴـﻙ ﺩﻭﺕ ﻨـﺕ ﻭﺴـﻲ‬
‫ﺸﺎﺭﺏ‪.‬‬
‫‪ .٥‬ﻤﻥ ﺍﻟﺼﻔﺭ ﺇﻟﻰ ﺍﻻﺤﺘﺭﺍﻑ‪ :‬ﺒﺭﻤﺠﺔ ﻨﻤﺎﺫﺝ ﺍﻟﻭﻴﻨﺩﻭﺯ ﻟﻤﺒﺭﻤﺠﻲ ﻓﻴﺠﻴﻭﺍل ﺒﻴﺯﻴﻙ ﺩﻭﺕ ﻨـﺕ ﻭﺴـﻲ‬
‫ﺸﺎﺭﺏ‪.‬‬
‫‪ .٦‬ﺍﻟﻤﺩﺨل ﺍﻟﻌﻤﻠﻲ ﺍﻟﺴﺭﻴﻊ ﺇﻟﻰ ﻓﻴﺠﻴﻭﺍل ﺒﻴﺯﻴﻙ ﺩﻭﺕ ﻨﺕ ‪.٢٠١٧‬‬
‫‪ .٧‬ﺍﻟﻤﺩﺨل ﺍﻟﻌﻤﻠﻲ ﺍﻟﺴﺭﻴﻊ ﺇﻟﻰ ﺴﻲ ﺸﺎﺭﺏ ‪.٢٠١٧‬‬
‫‪ .٨‬ﺃﺴﺎﺴﻴﺎﺕ ‪ WPF‬ﻟﻤﺒﺭﻤﺠﻲ ﻓﻴﺠﻴﻭﺍل ﺒﻴﺯﻴﻙ ﺩﻭﺕ ﻨﺕ‪.‬‬
‫‪ .٩‬ﺃﺴﺎﺴﻴﺎﺕ ‪ WPF‬ﻟﻤﺒﺭﻤﺠﻲ ﺴﻲ ﺸﺎﺭﺏ‪.‬‬
‫‪٥‬‬
‫ﻟﻘﺭﺍﺀﺓ ﻤﻘﺩﻤﺔ ﻭﻓﻬﺭﺱ ﻜل ﻜﺘﺎﺏ‪:‬‬
‫‪https://drive.google.com/drive/folders/1J21xi8Aw15BFSv-‬‬
‫‪GUgVOElLuYM6zoNct‬‬
‫ﻟﺸﺭﺍﺀ ﻫﺫﻩ ﺍﻟﻜﺘﺏ‪ ،‬ﻴﺘﻡ ﺘﺤﻭﻴل ﺍﻟﺜﻤﻥ ﺒﺤﻭﺍﻟﺔ ﺒﺭﻴﺩﻴﺔ ﺩﺍﺨل ﻤﺼﺭ‪ ،‬ﺃﻭ ﺒﻭﻴﺴﺘﺭﻥ ﻴﻭﻨﻴﻭﻥ ﻤﻥ‬
‫ﺨﺎﺭﺝ ﻤﺼﺭ‪ ،‬ﻭﻴﺘﻡ ﺇﺭﺴﺎل ﺍﻟﻜﺘﺏ ﺒﻁﺭﺩ ﺒﺎﻟﺒﺭﻴﺩ ﺍﻟﺴﺭﻴﻊ‪ ..‬ﻟﻤﺯﻴﺩ ﻤﻥ ﺍﻟﺘﻔﺎﺼﻴل ﺃﺭﺴل ﺭﺴﺎﻟﺔ‬
‫ﺒﺎﻟﻜﺘﺏ ﺍﻟﻤﻁﻠﻭﺒﺔ ﺇﻟﻰ‪:‬‬
‫‪[email protected]‬‬

‫ﻜﺘﺏ ﺃﺠﻬﺯ ﻟﻜﺘﺎﺒﺘﻬﺎ ﻓﻲ ﺍﻟﻤﺭﺤﻠﺔ ﺍﻟﻘﺎﺩﻤﺔ ﺒﺈﺫﻥ ﺍﷲ‪:‬‬


‫‪ -‬ﺒﺭﻤﺠﺔ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒـ ‪.Entity Framework‬‬
‫‪ -‬ﺇﻨﺸﺎﺀ ﺘﻘﺎﺭﻴﺭ ‪ Report Viewer‬ﻭ ‪ Crystal Reports‬ﻭﻋﺭﻀﻬﺎ ﻭﻁﺒﺎﻋﺘﻬﺎ‪.‬‬
‫‪ -‬ﺒﺭﻤﺠﺔ ﻤﻭﺍﻗﻊ ﺍﻟﻭﻴﺏ ﺒـ ‪.ASP.NET MVC Core‬‬
‫‪ -‬ﺍﻟﻤﻭﺍﻀﻴﻊ ﺍﻟﻤﺘﻘﺩﻤﺔ ﻓﻲ ﺒﺭﻤﺠﺔ ﺇﻁﺎﺭ ﺍﻟﻌﻤل‪.‬‬
‫‪ -‬ﺍﻟﻭﺴﺎﺌﻁ ﺍﻟﻤﺘﻌﺩﺩﺓ ﻓﻲ ‪.WPF‬‬
‫‪ -‬ﺒﺭﻤﺠﺔ ﻤﺸﺎﺭﻴﻊ ‪.Windows Universal Applications‬‬
‫‪ -‬ﺒﺭﻤﺠﺔ ﺍﻻﻨﺩﺭﻭﻴﺩ ﺒـ ‪.Xamarin‬‬
‫‪ -‬ﻴﺭﻤﺠﺔ ﺍﻟﺸﺒﻜﺎﺕ ﺒﺩﻭﺕ ﻨﺕ‪.‬‬

‫ﺴﺠﻠﻭﺍ ﺇﻋﺠﺎﺒﻜﻡ ﺒﺼﻔﺤﺘﻲ ﺍﻟﺒﺭﻤﺠﻴﺔ ﻟﻤﺘﺎﺒﻌﺔ ﺼـﺩﻭﺭ ﻫـﺫﻩ ﺍﻟﻜﺘـﺏ ﺒـﺈﺫﻥ ﺍﷲ‪ ،‬ﻭﺍﻻﺴـﺘﻔﺎﺩﺓ‬
‫ﺒﺎﻟﻤﻼﺤﻅﺎﺕ ﺍﻟﺒﺭﻤﺠﻴﺔ ﺍﻟﻌﻤﻠﻴﺔ ﺍﻟﺘﻲ ﺃﻨﺸﺭﻫﺎ ﻋﻠﻰ ﺍﻟﺼﻔﺤﺔ‪:‬‬
‫‪https://www.facebook.com/vbandcsharp‬‬

‫ﻟﻠﺘﻭﺍﺼل ﻤﻊ ﺒﺎﻗﻲ ﺍﻟﻤﺒﺭﻤﺠﻴﻥ ﻭﺘﺒﺎﺩل ﺍﻷﺴﺌﻠﺔ ﻭﺍﻟﺨﺒﺭﺍﺕ‪ ،‬ﻴﻤﻜﻨﻜﻡ ﺍﻻﻨﻀﻤﺎﻡ ﺇﻟﻰ‬


‫ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ‪:‬‬
‫‪https://www.facebook.com/groups/123809374886424/‬‬

‫‪٦‬‬
‫ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻜﺘﺎﺏ‬

١٤ ‫· ﻤﻘﺩﻤﺔ‬
١٦ ‫· ﻟﻤﻥ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ‬

:‫ﻤﻠﺤﻭﻅﺔ‬
:‫ﺘﻡ ﻨﺸﺭ ﺍﻟﻔﺼﻭل ﺍﻷﺍﺭﺒﻌﺔ ﺍﻷﻭﻟﻰ ﻓﻲ ﻜﺘﺎﺏ ﻤﺴﺘﻘل ﺒﻌﻨﻭﺍﻥ‬
SQL ‫ﺇﻨﺸﺎﺀ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻜﺘﺎﺒﺔ ﺍﺴﺘﻌﻼﻤﺎﺕ‬
:‫ﻴﻤﻜﻨﻡ ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻫﻨﺎ‬
https://drive.google.com/file/d/1H3FqC-jEXihVI5fx-
7ZBFmVGJm2D7Oi3/edit?fbclid=IwAR31PkLyHT1QTN1xNoozTWsvkwQ 5-
uowwQzNarL4EhPULQsy4-CGBOl1Cv0

-٥-
ADO.NET ‫ﺘﻘﻨﻴﺔ‬

١٩ Client ‫ ﻭﺍﻟﻌﻤﻴل‬Server ‫ﺍﻟﺨﺎﺩﻡ‬


ADO.NET ‫ﺘﻘﻨﻴﺔ‬
XML ‫ﻟﻐﺔ‬
Database Providers ‫ﻤﺯﻭﺩﺍﺕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‬

-٦-
Connection Object ‫ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل‬

٢٩ Connection String ‫ﻨﺹ ﺍﻻﺘﺼﺎل‬


DbConnectionStringBuilder Class ‫ﻓﺌﺔ ﺒﺎﻨﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‬
SqlConnectionStringBuilder Class ‫ﻓﺌﺔ ﺒﺎﻨﻲ ﻨﺹ ﺍﺘﺼﺎل ﺴﻴﻜﻴﻭل‬
Settings ‫ﺤﻔﻅ ﻨﺹ ﺍﻻﺘﺼﺎل ﻓﻲ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﺒﺭﻨﺎﻤﺞ‬
ConnectionStringsSection Class ‫ﻓﺌﺔ ﻤﻘﻁﻊ ﻨﺼﻭﺹ ﺍﻻﺘﺼﺎل‬
٧
ConnectionStringSettings Class ‫ﻓﺌﺔ ﺇﻋﺩﺍﺩﺍﺕ ﻨﺹ ﺍﻻﺘﺼﺎل‬
IDbConnection Interface ‫ﻭﺍﺠﻬﺔ ﺍﻻﺘﺼﺎل ﺒﻘﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
DbConnection Class ‫ﻓﺌﺔ ﺍﻻﺘﺼﺎل‬
SqlConnection Class ‫ﻓﺌﺔ ﺍﺘﺼﺎل ﺴﻴﻜﻴﻭل‬
SqlError Class ‫ﻓﺌﺔ ﺨﻁﺄ ﺴﻴﻜﻴﻭل‬

-٧-
Command Object ‫ﻜﺎﺌﻥ ﺍﻷﻤﺭ‬

٦٨ IDbCommand Interface ‫ﻭﺍﺠﻬﺔ ﺃﻤﺭ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‬


DbCommand Class ‫ﻓﺌﺔ ﺃﻤﺭ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
SqlCommand Class ‫ﻓﺌﺔ ﺃﻤﺭ ﺴﻴﻜﻭﻴل‬
‫ﺘﻤﺭﻴﺭ ﺍﻟﻘﻴﻡ ﺇﻟﻰ ﺠﻤل ﺍﻻﺴﺘﻌﻼﻡ‬
SQL Injection ‫ﺩﺱ ﺍﻻﺴﺘﻌﻼﻤﺎﺕ‬
Parameters ‫ﺍﻟﻤﻌﺎﻤﻼﺕ‬
DbParameterCollection ‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﻤﻌﺎﻤﻼﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
SqlParameterCollection Class ‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﻤﻌﺎﻤﻼﺕ ﺴﻴﻜﻭﻴل‬
IDataParameter Interface ‫ﻭﺍﺠﻬﺔ ﻤﻌﺎﻤل ﺍﻟﺒﻴﺎﻨﺎﺕ‬
IDbDataParameter Interface ‫ﻭﺍﺠﻬﺔ ﻤﻌﺎﻤل ﺒﻴﺎﻨﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
DbParameter Class ‫ﻓﺌﺔ ﻤﻌﺎﻤل ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
SqlParameter Class ‫ﻓﺌﺔ ﻤﻌﺎﻤل ﺴﻴﻜﻭﻴل‬

-٨-
DataReader ‫ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‬

١٠٥ IDataRecord Interface ‫ﻭﺍﺠﻬﺔ ﺴﺠل ﺍﻟﺒﻴﺎﻨﺎﺕ‬


DbDataRecord Class ‫ﻓﺌﺔ ﺴﺠل ﺍﻟﺒﻴﺎﻨﺎﺕ‬
IDataReader Interface ‫ﻭﺍﺠﻬﺔ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
٨
DbDataReader Class ‫ﻓﺌﺔ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
SqlDataReader Class ‫ﻓﺌﺔ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل‬

-٩-
DataAdapter ‫ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‬

١١٩ IDataAdapter Interface ‫ﻭﺍﺠﻬﺔ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‬


IDbDataAdapter Interface ‫ﻭﺍﺠﻬﺔ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
DataAdapter Class ‫ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
DbDataAdapter Class ‫ﻓﺌﺔ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
SqlDataAdapter Class ‫ﻓﺌﺔ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل‬
‫ﺍﻟﺘﺼﺎﺭﻉ ﻋﻠﻰ ﺘﺤﺩﻴﺙ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
Data Adapter Configuration Wizard ‫ﻤﻌﺎﻟﺞ ﺇﻋﺩﺍﺩ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
DbCommandBuilder Class ‫ﻓﺌﺔ ﺒﺎﻨﻲ ﺃﻭﺍﻤﺭ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
SqlCommandBuilder Class ‫ﻓﺌﺔ ﺒﺎﻨﻲ ﺃﻭﺍﻤﺭ ﺴﻴﻜﻭﻴل‬
ITableMappingCollection ‫ﻭﺍﺠﻬﺔ ﻤﺠﻤﻭﻋﺔ ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل‬
DataTableMappingCollection Class ‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل‬
ITableMapping Interface ‫ﻭﺍﺠﻬﺔ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل‬
DataTableMapping Class ‫ﻓﺌﺔ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل‬
IColumnMappingCollection ‫ﻭﺍﺠﻬﺔ ﻤﺠﻤﻭﻋﺔ ﺨﺭﺍﺌﻁ ﺍﻟﻌﻤﻭﺩ‬
DataColumnMappingCollection ‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺨﺭﺍﺌﻁ ﺍﻟﻌﻤﻭﺩ‬
IColumnMapping Interface ‫ﻭﺍﺠﻬﺔ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ‬
DataColumnMapping Class ‫ﻓﺌﺔ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ‬

٩
‫‪-١٠-‬‬
‫ﻤﺼﺎﻨﻊ ﺍﻟﻤﺯﻭﺩﺍﺕ ‪Provider Factories‬‬

‫‪١٨٧‬‬ ‫ﻓﺌﺔ ﻤﺼﺎﻨﻊ ﺍﻟﻤﺯﻭﺩﺍﺕ ‪DbProviderFactories Class‬‬


‫ﻓﺌﺔ ﻤﺼﻨﻊ ﺍﻟﻤﺯﻭﺩ ‪DbProviderFactory Class‬‬
‫ﺍﻟﻁﺒﻘﺎﺕ ﺍﻟﻤﺘﻌﺩﺩﺓ ‪N-Tiers‬‬
‫ﻓﺌﺔ ﻋﺩﺍﺩ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DbDataSourceEnumerator Class‬‬
‫ﻓﺌﺔ ﻋﺩﺍﺩ ﻤﺼﺎﺩﺭ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ‪SqlDataSourceEnumerator‬‬

‫‪-١١-‬‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DataSet‬‬

‫‪٢٠٢‬‬ ‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DataSet Class‬‬


‫ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﻹﻨﺸﺎﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪Generate DataSet Wezard‬‬
‫ﺇﻨﺸﺎﺀ ﻤﺠﻤﻭﻋﺎﺕ ﺒﻴﺎﻨﺎﺕ ﺨﺎﺼ‪‬ﺔ ‪Custom DataSet‬‬
‫ﺤﻔﻅ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺸﺠﺭﺓ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ‪TableAdapter Class‬‬
‫ﻓﺌﺔ ﻤﺩﻴﺭ ﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ‪TableAdapterManager‬‬

‫‪-١٢-‬‬
‫ﺍﻟﺠﺩﺍﻭل ﻭﺍﻟﻌﻼﻗﺎﺕ ﻭﺍﻟﻘﻴﻭﺩ‬

‫‪٢٦٤‬‬ ‫ﻓﺌﺔ ﺃﺴﺎﺱ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺩﺍﺨﻠﻴﺔ ‪InternalDataCollectionBase Class‬‬


‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل ‪DataTableCollection Class‬‬
‫ﻓﺌﺔ ﺠﺩﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DataTable Class‬‬
‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ ‪DataRowCollection Class‬‬
‫ﻓﺌﺔ ﺼﻑﹼ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DataRow Class‬‬
‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ ‪DataColumnCollection Class‬‬

‫‪١٠‬‬
DataColumn Class ‫ﻓﺌﺔ ﻋﻤﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
DataTableReader Class ‫ﻓﺌﺔ ﻗﺎﺭﺉ ﺠﺩﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ‬
DataRelationCollection Class ‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻌﻼﻗﺎﺕ‬
DataRelation Class ‫ﻓﺌﺔ ﺍﻟﻌﻼﻗﺔ‬
ConstraintCollection Class ‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻘﻴﻭﺩ‬
Constraint Class ‫ﻓﺌﺔ ﺍﻟﻘﻴﺩ‬
UniqueConstraint Class ‫ﺩ‬‫ﻓﺌﺔ ﻗﻴﺩ ﺍﻟﺘﻔﺭ‬
ForeignKeyConstraint Class ‫ﻓﺌﺔ ﻗﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺜﺎﻨﻭﻱ‬

-١٣-
Data Views ‫ﻋﺭﻭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ‬

٣٣٦ IBindingList Interface ‫ﻭﺍﺠﻬﺔ ﻗﺎﺌﻤﺔ ﺍﻟﺭﺒﻁ‬


ITypedList Interface ‫ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ‬
DataViewManager Class ‫ﻓﺌﺔ ﻤﺩﻴﺭ ﺍﻟﻌﺭﺽ‬
DataViewSetting Class ‫ﻓﺌﺔ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻌﺭﺽ‬
IBindingListView Interface ‫ﻭﺍﺠﻬﺔ ﺭﺒﻁ ﻗﺎﺌﻤﺔ ﺍﻟﻌﺭﺽ‬
ListSortDescription Class ‫ﻓﺌﺔ ﻭﺍﺼﻑ ﺘﺭﺘﻴﺏ ﺍﻟﻘﺎﺌﻤﺔ‬
DataView Class ‫ﻓﺌﺔ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
IEditableObject Interface ‫ﻭﺍﺠﻬﺔ ﺍﻟﻜﺎﺌﻥ ﺍﻟﻘﺎﺒل ﻟﻠﺘﺤﺭﻴﺭ‬
INotifyPropertyChanged Interface ‫ﻭﺍﺠﻬﺔ ﺍﻟﺘﻨﺒﻴﻪ ﺒﺘﻐﻴﺭ ﺨﺎﺼﻴﺔ‬
DataRowView Class ‫ﻓﺌﺔ ﻋﺭﺽ ﺼﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ‬

-١٤-
Data Binding ‫ﺭﺒﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ‬

٣٦٤ IBindableComponent Interfac ‫ﻭﺍﺠﻬﺔ ﺍﻟﻤﻜﻭﻥ ﺍﻟﻘﺎﺒل ﻟﻼﺭﺘﺒﺎﻁ‬


BindingsCollection Class ‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻻﺭﺘﺒﺎﻁﺎﺕ‬
١١
ControlBindingsCollection Class ‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﺭﺘﺒﺎﻁﺎﺕ ﺍﻷﺩﺍﺓ‬
Binding Class ‫ﻓﺌﺔ ﺍﻻﺭﺘﺒﺎﻁ‬
BindingMemberInfo Structure ‫ﺴﺠل ﻤﻌﻠﻭﻤﺎﺕ ﻋﻨﺼﺭ ﺍﻟﺭﺒﻁ‬
BindingContext Class ‫ﻓﺌﺔ ﻤﺤﺘﻭﻯ ﺍﻟﺭﺒﻁ‬
BindingManagerBase Class ‫ﻓﺌﺔ ﺃﺴﺎﺱ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ‬
PropertyManager Class ‫ﻓﺌﺔ ﻤﺩﻴﺭ ﺍﻟﺨﺎﺼﻴﺔ‬
CurrencyManager Class ‫ﻓﺌﺔ ﻤﺩﻴﺭ ﺍﻟﺘﺴﻠﺴل‬
‫ﺭﺒﻁ ﺍﻷﺩﻭﺍﺕ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ‬
Binding List Boxs ‫ﺭﺒﻁ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻘﻭﺍﺌﻡ‬
Data Source Configuration Wizard ‫ﻤﻌﺎﻟﺞ ﺘﻬﻴﺌﺔ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫ﻤﺘﺼﻔﺢ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
ICurrencyManagerProvider Interface ‫ﻭﺍﺠﻬﺔ ﻤﺯﻭﺩ ﻤﺩﻴﺭ ﺍﻟﺘﺴﻠﺴل‬
ICancelAddNew Interface ‫ﻭﺍﺠﻬﺔ ﺇﻟﻐﺎﺀ ﺇﻀﺎﻓﺔ ﺍﻟﺠﺩﻴﺩ‬
IRaiseItemChangedEvents Interface ‫ﻭﺍﺠﻬﺔ ﺇﻁﻼﻕ ﺃﺤﺩﺍﺙ ﺍﻟﺘﻐﻴﺭ‬
BindingList<T> Class ‫ﻓﺌﺔ ﻗﺎﺌﻤﺔ ﺍﻟﺭﺒﻁ ﻋﺎﻤﺔ ﺍﻟﻨﻭﻉ‬
IListSource Interface ‫ﻭﺍﺠﻬﺔ ﻤﺼﺩﺭ ﺍﻟﻘﺎﺌﻤﺔ‬
BindingSource Class ‫ﻓﺌﺔ ﻤﺼﺩﺭ ﺍﻟﺭﺒﻁ‬
ListBindingHelper Class ‫ﻓﺌﺔ ﻤﺴﺎﻋﺩ ﺭﺒﻁ ﺍﻟﻘﻭﺍﺌﻡ‬
BindingNavigator Class ‫ﻓﺌﺔ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ‬

:‫ﻤﻠﺤﻭﻅﺔ‬
:‫ ﻓﻲ ﻜﺘﺎﺏ ﻤﺴﺘﻘل ﺒﻌﻨﺎﻭﺍﻥ‬١ ‫ ﻭﺍﻟﻤﻠﺤﻕ‬١٧ ‫ ﻭ‬١٦ ‫ ﻭ‬١٥ ‫ﺘﻡ ﻨﺸﺭ ﺍﻟﻔﺼﻭل‬
DataGridView ‫ﺠﺩﻭل ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
:‫ﻴﻤﻜﻨﻡ ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻫﻨﺎ‬
https://drive.google.com/file/d/1tm27L0yGX1RA__vxXng0t5ny3XuJUFv_/view?fbclid=IwAR0IM7zdX9dqkWPXttQyQU6s-
FPna2m0hshLq9itiog9SS6PISFdes7Gm_0

١٢
‫ﻤﻠﺤﻕ ‪٢‬‬
‫ﺃﻨﻭﺍﻉ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺍﻟﻤﺩﺍﺭﺓ‬
‫‪Managed SQL Data Types‬‬

‫‪٤٣٧‬‬ ‫ﺴﺠل ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻨﻁﻘﻴﺔ ‪SqlBoolean Structure‬‬


‫ﺴﺠل ﺍﻟﻭﺤﺩﺓ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪SqlByte Structure‬‬
‫ﺴﺠل ﺍﻷﻋﺩﺍﺩ ﺍﻟﻌﺸﺭﻴﺔ ‪SqlDecimal Structure‬‬
‫ﻓﺌﺔ ﺍﻟﺤﺭﻭﻑ ‪SqlChars Class‬‬
‫ﺴﺠل ﺍﻟﻨﺹ ‪SqlString Structure‬‬
‫ﺴﺠل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪SqlBinary Structure‬‬
‫ﻓﺌﺔ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪SqlBytes Class‬‬
‫ﻓﺌﺔ "‪SqlXml Class "XML‬‬
‫ﺤﻔﻅ ﺍﻟﻤﻠﻔﺎﺕ ﺨﺎﺭﺝ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫ﻓﺌﺔ ﻤﺠﺭﻯ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ‪SqlFileStream Class‬‬

‫ﻤﻠﺤﻕ‪٣ :‬‬
‫ﺇﻋﺩﺍﺩ ﺘﻁﺒﻴﻕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل‬

‫‪٤٧٢‬‬ ‫ﺇﻋﺩﺍﺩ ﺘﻁﺒﻴﻕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل‬


‫‪٤٧٤‬‬ ‫ﻤﻼﺤﻅﺎﺕ ﺤﻭل ﺍﺴﺘﺨﺩﺍﻡ ‪SQL Server Express‬‬

‫‪١٣‬‬
‫ﻤﻘﺩﻤﺔ‬

‫ﺒﺴﻡ ﺍﷲ‪ ،‬ﻭﺍﻟﺤﻤﺩ ﷲ‪ ،‬ﻭﺍﻟﺼﻼﺓ ﻭﺍﻟﺴﻼﻡ ﻋﻠﻰ ﺭﺴﻭل ﺍﷲ‪ ،‬ﻭﺒﻌﺩ‪:‬‬


‫ﻴﻌﻠﻤﻙ ﺍﻟﻜﺘﺎﺏ ﻜﻴﻑ ﺘﺘﻌﺎﻤل ﻤﻊ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺩﺍﺨل ﻤﺸﺎﺭﻴﻊ ﺴﻲ ﺸﺎﺭﺏ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺘﻘﻨﻴـﺔ‬
‫‪ ،ADO.NET‬ﻟﺘﺴﺘﻁﻴﻊ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻁﻠﺏ ﺍﻟﺴﺠﻼﺕ ﻤﻨﻬـﺎ‪ ،‬ﻭﻜﻴـﻑ ﺘﻘـﻭﻡ‬
‫ﺒﺤﻔﻅﻬﺎ ﻤﺭﺓ ﺃﺨﺭﻯ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﺫﺍ ﺩﺨﻠﺕ ﻋﻠﻴﻬﺎ ﺃﻴﺔ ﺘﻌﺩﻴﻼﺕ‪.‬‬
‫ﻭﻴﻌﻠﻤﻙ ﺍﻟﻜﺘﺎﺏ ﺃﻴﻀﺎ ﻜﻴﻑ ﺘﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻤﻥ ﺨﻼل ﺘﻘﻨﻴـﺔ ﺍﻟـﺭﺒﻁ ‪،Binding‬‬
‫ـﺭﺒﻁ‬
‫ـل ﻤﻭﺠـﻪ ﺍﻟـ‬
‫ـﺫﺍ ﺍﻟﻐـﺭﺽ‪ ،‬ﻤﺜـ‬
‫ـﻡ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﺨﺼﺼـﺔ ﻟﻬـ‬
‫ﻭﻴﺸـﺭﺡ ﺒﺎﻟﺘﻔﺼـﻴل ﺃﻫـ‬
‫‪ BindingNavigator‬ﻭﻤﺼﺩﺭ ﺍﻟﺭﺒﻁ ‪.BindingSource‬‬
‫***‬
‫ﻭﻴﺸﺭﺡ ﺍﻟﻜﺘﺎﺏ ﺒﺎﻟﺘﻔﺼﻴل ﺃﻜﺜﺭ ﻤﻥ ‪ ٥٠‬ﻤﺸﺭﻭﻋﺎ ﻤﺘﻨﻭﻋﺎ ﺘﻐﻁﻲ ﻤﺤﺘﻭﻴﺎﺘﻪ‪ ،‬ﻟﺘﺘﻌﻠﻡ ﻤﻥ ﺨﻼﻟﻬﺎ‪:‬‬
‫‪ -‬ﻜﻴﻑ ﺘﺤﺼل ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻤﺨﺘﻠﻑ ﺍﻟﻁـﺭﻕ‪ ،‬ﺴـﻭﺍﺀ ﺒﺎﺴـﺘﺨﺩﺍﻡ‬
‫ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataReader‬ﺃﻭ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataAdapter‬ﺃﻭ ﻤﻬﻴﺊ ﺍﻟﺠـﺩﻭل‬
‫‪.TableAdapter‬‬
‫‪ -‬ﻜﻴﻑ ﺘﺤﺘﻔﻅ ﺒﺎﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﺫﺍﻜﺭﺓ‪ ،‬ﺒﺎﺴﺘﺨﺩﺍﻡ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ‪ DataSet‬ﺴﻭﺍﺀ ﻜﺎﻨﺕ‬
‫ﻋﺎﺩﻴﺔ ﺃﻭ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ‪.Typed‬‬
‫‪ -‬ﻜﻴﻑ ﺘﻨﻘل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻴﻥ ﻨﻭﻋﻴﻥ ﻤﺨﺘﻠﻔﻴﻥ ﻤﻥ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -‬ﻜﻴﻑ ﺘﺤﻔﻅ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪ Binary Data‬ﻓﻲ ﻤﻠﻔﺎﺕ ﻤﺴﺘﻘﻠﺔ ﻋﻠﻰ ﺍﻟﺨـﺎﺩﻡ ﺨـﺎﺭﺝ‬
‫ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ‪.SQL Server 2008‬‬

‫‪١٤‬‬
‫‪ -‬ﻜﻴـــﻑ ﺘﻌـــﺭ‪‬ﻑ ﺍﻟﻤﻌـــﺎﻤﻼﺕ ‪ 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‬‬
‫‪ -‬ﻜﻴﻑ ﺘﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﻼﻓﺘﺎﺕ ﻭﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺹ ﻭﺍﻟﻘﻭﺍﺌﻡ ﻭﺍﻟﺠﺩﺍﻭل‪ ،‬ﻭﻜﻴﻑ ﺘﺭﺒﻁ‬
‫ﻜل ﻫﺫﻩ ﺍﻟﻌﻨﺎﺼﺭ ﻤﻌﺎ‪.‬‬
‫ﻭﻏﻴﺭ ﻫﺫﺍ ﺍﻟﻜﺜﻴﺭ‪.‬‬
‫***‬
‫ﻭﻴﻐﻁﻲ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ﺒﺎﻟﺘﻔﺼﻴل ﺤﻭﺍﻟﻲ ‪ ١٣٥‬ﻭﺍﺠﻬﺔ ﻭﻓﺌﺔ ﻭﺴﺠﻼ ﻤﻥ ﻤﻜﺘﺒـﺔ ﺇﻁـﺎﺭ ﺍﻟﻌﻤـل‪،‬‬
‫ﻤﺨﺼﺼﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺘﻁﺒﻴﻘﺎﺕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺸﺎﺭﺤﺎ ﺨﺼﺎﺌﺹ ﻭﻭﺴـﺎﺌل ﻭﺃﺤـﺩﺍﺙ ﻫـﺫﻩ‬
‫ﺍﻟﻤﻜﻭﻨﺎﺕ ﺒﺎﻟﺘﻔﺼﻴل‪ ..‬ﻟﻬﺫﺍ ﻴﻌﺘﺒﺭ ﺍﻟﻜﺘﺎﺏ ﻤﺭﺠﻌﺎ ﻤﻔﺼﻼ ﻤﺒﻭﺒﺎ‪ ،‬ﻴﻤﻜﻥ ﻟﻘﺎﺭﺌﻪ ﺍﻟﺭﺠﻭﻉ ﺇﻟﻴﻪ ﻋﻨﺩ‬
‫‪١٥‬‬
‫ﺍﻟﺒﺤﺙ ﻋﻥ ﺘﻔﺎﺼﻴل ﺃﻱ ﻓﺌﺔ ﺃﻭ ﺨﺎﺼﻴﺔ ﺃﻭ ﻭﺴﻴﻠﺔ ﺃﻭ ﺤﺩﺙ‪ ،‬ﻓﻲ ﻨﻔﺱ ﺍﻟﻭﻗـﺕ ﺍﻟـﺫﻱ ﻴﺠﻌﻠـﻪ‬
‫ﺼﺎﻟﺤﺎ ﻟﻠﻘﺭﺍﺀﺓ ﻜﻜﺘﺎﺏ ﺘﻌﻠﻴﻤﻲ ﻋﻤﻠﻲ ﻤﺭﺘﺏ ﻤﻥ ﺍﻷﺴﻬل ﺇﻟﻰ ﺍﻷﺼﻌﺏ‪ ،‬ﻴﻨﻘل ﺇﻟﻰ ﺍﻟﻤﺒﺭﻤﺞ ﻓﻲ‬
‫ﺼﻔﺤﺎﺕ ﻤﻌﺩﻭﺩﺍﺕ ﺨﺒﺭﺓ ﺴﻨﻭﺍﺕ ﻓﻲ ﺒﺭﻤﺠﺔ ﺘﻁﺒﻴﻘﺎﺕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻴﺭﺸﺩﻩ ﺇﻟﻰ ﻜﻴﻔﻴﺔ ﺤل‬
‫ﺍﻟﻤﺸﻜﻼﺕ ﻏﻴﺭ ﺍﻟﻤﺘﻭﻗﻌﺔ ﺍﻟﺘﻲ ﺘﻭﺍﺠﻬﻪ ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﺠﺎل‪ ،‬ﻭﻜﻴﻑ ﻴﺤﺴﻥ ﺃﺩﺍﺀ ﺒﺭﻨﺎﻤﺠـﻪ ﺒﺘـﻭﻓﻴﺭ‬
‫ﺃﻜﺒﺭ ﻗﺩﺭ ﻤﻥ ﺍﻟﺫﺍﻜﺭﺓ‪ ،‬ﻭﻜﻴﻑ ﻴﺤﺎﻓﻅ ﻋﻠﻰ ﻜﻔﺎﺀﺓ ﺨﺎﺩﻡ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺒﺘﻘﻠﻴل ﻋﺩﺩ ﺍﻻﺘﺼﺎﻻﺕ ﻭﻭﻗﺕ‬
‫ﻜل ﺍﺘﺼﺎل ﺒﻘﺩﺭ ﺍﻹﻤﻜﺎﻥ‪.‬‬
‫ﺒﺎﺨﺘﺼﺎﺭ‪ :‬ﻫﺫﺍ ﻫﻭ ﺍﻟﻜﺘﺎﺏ ﺍﻟﺫﻱ ﺘﺒﺤﺙ ﻋﻨﻪ‪.‬‬
‫ﻭﺍﷲ ﻭﻟﻲ ﺍﻟﺘﻭﻓﻴﻕ‬

‫ﻟﻤﻥ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ‪:‬‬


‫ﺭﻏﻡ ﺃﻥ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ﻴﻔﺘﺭﺽ ﺃﻥ ﻗﺎﺭﺌﻪ ﻻ ﻴﻤﺘﻠﻙ ﺃﻴﺔ ﻤﻌﺭﻓﺔ ﻤﺴﺒﻘﺔ ﺒﻘﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﻟﺒـﺭﺍﻤﺞ‬
‫ﺍﻟﺘﻲ ﻴﻨﺸﺌﻬﺎ ﺒﻬﺎ‪ ،‬ﻓﺈﻨﻪ ﻋﻠﻰ ﺍﻟﺠﺎﻨﺏ ﺍﻵﺨﺭ‪ ،‬ﻴﺸﺘﺭﻁ ﻓﻲ ﻗﺎﺭﺌﻪ ﺃﻥ ﻴﻜﻭﻥ ﻋﻠﻰ ﺩﺭﺍﻴﺔ ﺒﻠﻐﺔ ﺴـﻲ‬
‫ﺸﺎﺭﺏ ‪ ،C#‬ﻭﺃﻥ ﻴﺠﻴﺩ ﺍﻟﻤﺘﻁﻠﺒﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -‬ﺃﺴﺎﺴﻴﺎﺕ ﻜﺘﺎﺒﺔ ﺍﻟﻜﻭﺩ ﺒﻠﻐﺔ ﺴﻲ ﺸﺎﺭﺏ‪ ،‬ﻜﺘﻌﺭﻴﻑ ﺍﻟﻤﺘﻐﻴﺭﺍﺕ ﻭﻜﺘﺎﺒـﺔ ﺠﻤـل ﺍﻟﺸـﺭﻁ‬
‫ﻭﺤﻠﻘﺎﺕ ﺍﻟﺘﻜﺭﺍﺭ ‪ ،Loops‬ﻭﻜﺘﺎﺒﺔ ﻭﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﺩﻭﺍل ‪.Functions‬‬
‫‪ -‬ﺃﺴﺎﺴﻴﺎﺕ ﻭﻤﻔﺎﻫﻴﻡ ﺍﻟﺒﺭﻤﺠﺔ ﺍﻟﻤﻭﺠﻬﺔ ﺒﺎﻟﻜﺎﺌﻨﺎﺕ ‪ ،OOP‬ﻜﺎﻟﻔﺌﺎﺕ ‪ Classes‬ﻭﺍﻟﻭﺍﺠﻬـﺎﺕ‬
‫‪ Interfaces‬ﻭﺍﻟﻭﺭﺍﺜﺔ ‪.Inheritance‬‬
‫‪ -‬ﺃﺴﺎﺴﻴﺎﺕ ﺍﻟﺘﻌﺎﻤل ﻤـﻊ ﺇﻁـﺎﺭ ﺍﻟﻌﻤـل‪ ،‬ﻭﻓﺌﺎﺘـﻪ ﺍﻟﺭﺌﻴﺴـﻴﺔ‪ ،‬ﺨﺎﺼـﺔ ﺍﻟﻤﺠﻤﻭﻋـﺎﺕ‬
‫‪ Collections‬ﻭﺍﻟﻤﻠﻔﺎﺕ ‪ Files‬ﻭﻓﺌﺎﺕ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺜﻘﺎﻓﺔ ‪.CultureInfo‬‬
‫‪ -‬ﺃﺴﺎﺴﻴﺎﺕ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻤﺸﺎﺭﻴﻊ ﺍﻟﻭﻴﻨﺩﻭﺯ‪ ،‬ﻭﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﺨﺘﻠﻔﺔ ﻜﻤﺭﺒﻊ ﺍﻟـﻨﺹ ‪TextBox‬‬
‫ﻭﻤﺭﺒﻊ ﺍﻻﺨﺘﻴﺎﺭ ‪ CheckBox‬ﻭﺍﻟﻘﻭﺍﺌﻡ ‪.Lists‬‬
‫ﻓﺈﺫﺍ ﻟﻡ ﺘﻜﻥ ﺘﺠﻴﺩ ﻫﺫﻩ ﺍﻷﺴﺎﺴﻴﺎﺕ‪ ،‬ﻓﻨﻨﺼﺢ ﺒﻘﺭﺍﺀﺓ ﺍﻟﻘﺴﻡ ﺍﻷﻭل ﻤﻥ ﻜﺘﺎﺒﻨـﺎ "ﺍﻟﻤـﺩﺨل ﺍﻟﻌﻤﻠـﻲ‬
‫ﺍﻟﺴﺭﻴﻊ ﺇﻟﻰ ﺴﻲ ﺸﺎﺭﺏ"‪ ،‬ﻓﻬﻭ ﻴﻐﻁﻲ ﻫﺫﻩ ﺍﻟﻤﻭﺍﻀﻴﻊ ﺒﺎﺨﺘﺼﺎﺭ ﻤﻥ ﺨـﻼل ﺇﻨﺸـﺎﺀ ﻤﺸـﺭﻭﻉ‬
‫ﻋﻤﻠﻲ ﻜﺎﻤل ﻤﺸﺭﻭﺡ ﺒﺎﻟﺘﻔﺼﻴل‪ ..‬ﺃﻤﺎ ﺍﻟﻨﺼﻑ ﺍﻟﺜﺎﻨﻲ ﻤﻥ ﺍﻟﻜﺘﺎﺏ‪ ،‬ﻓﻴﺸـﺭﺡ ﻤﺸـﺭﻭﻉ ﻗﻭﺍﻋـﺩ‬
‫‪١٦‬‬
‫ﺒﻴﺎﻨﺎﺕ ﻜﺎﻤﻼ ﻤﻜﺘﻭﺒﺎ ﺒﺘﻘﻨﻴﺔ ‪ LinQ To SQL‬ﻭﻫﻲ ﻏﻴﺭ ﻤﺸﺭﻭﺤﺔ ﻓﻲ ﺍﻟﻜﺘﺎﺏ ﺍﻟـﺫﻱ ﺘﻘـﺭﺅﻩ‬
‫ﺍﻵﻥ‪ ..‬ﻭﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﻜﺘﺎﺏ ﺍﻟﻤﺩﺨل ﺍﻟﻌﻤﻠﻲ ﻤﻜﻤل ﻟﻬﺫﺍ ﺍﻟﻤﺭﺠﻊ‪ ،‬ﻓﻬـﻭ ﻤـﻥ ﺠﻬـﺔ ﻴﺸـﺭﺡ‬
‫ﻤﺸﺭﻭﻉ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ﻭﺍﺤﺩﺍ ﻜﺒﻴﺭﺍ ﺒﻴﻨﻤﺎ ﻴﺴﺘﻌﻴﻥ ﺍﻟﻤﺭﺠﻊ ﺍﻟﺫﻱ ﺒﻴﻥ ﻴﺩﻴﻙ ﺒﻌﺸﺭﺍﺕ ﺍﻟﻤﺸـﺎﺭﻴﻊ‬
‫ﺍﻟﺼﻐﻴﺭﺓ ﻟﺸﺭﺡ ﻤﺤﺘﻭﺍﻩ‪ ،‬ﻜﻤﺎ ﺃﻥ ﻫﺫﺍ ﺍﻟﻤﺭﺠﻊ ﻴﺸﺭﺡ ﺘﻘﻨﻴﺔ ‪ ADO.NET‬ﺒﻴﻨﻤﺎ ﻴﻌﻁﻴﻙ ﻜﺘـﺎﺏ‬
‫ﺍﻟﻤﺩﺨل ﺍﻟﻌﻤﻠﻲ ﻓﻜﺭﺓ ﺠﻴﺩﺓ ﻋـﻥ ﺍﺴـﺘﺨﺩﺍﻡ ﺍﻟﻨﻤـﻭﺫﺝ ﺍﻟﺘﺼـﻭﺭﻱ ‪Conceptual Model‬‬
‫ﺒﺎﺴﺘﺨﺩﺍﻡ ﺘﻘﻨﻴﺔ ‪.LinQ To SQL‬‬

‫ﺍﻟﺭﻤﻭﺯ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻓﻲ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ‪:‬‬

‫ﺴﺠل ‪.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‬‬

‫ﺍﻟﺨﺎﺩﻡ ‪ Server‬ﻭﺍﻟﻌﻤﻴل ‪:Client‬‬


‫ﺍﻟﺨــﺎﺩﻡ ‪ Server‬ﻫــﻭ ﺤﺎﺴــﻭﺏ ﺘﻭﺠــﺩ ﻋﻠﻴــﻪ ﻗﺎﻋــﺩﺓ ﺍﻟﺒﻴﺎﻨــﺎﺕ‪ ،‬ﻭﻴﻌﻤــل ﻋﻠﻴــﻪ‬
‫‪ ،SQL Server‬ﻟﻬﺫﺍ ﻓﻬﻭ ﻴﻘﻭﻡ ﺒﻘﺭﺍﺀﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻁﻠﻭﺒﺔ ﻭﺇﺭﺴﺎﻟﻬﺎ ﺇﻟﻰ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺃﻭ ﺍﺴـﺘﻘﺒﺎل‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻭﺍﺭﺩﺓ ﻤﻥ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻭﺤﻔﻅﻬﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﺒﻴﻨﻤﺎ ﺍﻟﻌﻤﻴل ‪ Client‬ﻫﻭ ﺃﻱ‪ ‬ﺠﻬﺎﺯ ﺤﺎﺴﻭﺏ ﺁﺨﺭ ﻴﻭﺠﺩ ﻋﻠﻴﻪ ﺒﺭﻨﺎﻤﺞ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟـﺫﻱ‬
‫ﻜﺘﺒﺘﻪ ﺃﻨﺕ‪ ،‬ﻭﻴﻘﻭﻡ ﺒﺎﻻﺘﺼﺎل ﺒﺨﺎﺩﻡ ﺴﻴﻜﻴﻭل ﻟﻁﻠﺏ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻭ ﺤﻔﻅﻬﺎ ﻋﻠﻴﻪ‪.‬‬
‫ﻭﻴﻤﻜﻥ ﺃﻥ ﻴﻜﻭﻥ ﻫﻨﺎﻙ ﺁﻻﻑ ﺍﻟﻌﻤﻼﺀ ‪ ،Clients‬ﻜلّ ﻤﻨﻬﻡ ﻴﺤﺎﻭل ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ﻓﻲ ﻨﻔﺱ ﺍﻟﻭﻗﺕ‪ ،‬ﻭﻁﻠﺏ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻨﻬﺎ ﻟﻤﻌﺎﻟﺠﺘﻬﺎ ﻋﻠﻰ ﺃﺠﻬﺯﺘﻬﻡ‪ ،‬ﺜـﻡ‪ ‬ﺇﺭﺴـﺎل ﺃﻱ‪‬‬
‫ﺘﻌﺩﻴﻼﺕ ﺘﻡ‪ ‬ﺇﺠﺭﺍﺅﻫﺎ ﻋﻠﻴﻬﺎ ﺇﻟﻰ ﺍﻟﺨﺎﺩﻡ ﻤﺭ‪‬ﺓ ﺃﺨﺭﻯ‪ ،‬ﻟﻴﺘﻡ‪ ‬ﺤﻔﻅﻬﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﻴﻘﺩﻡ ﻟﻨﺎ ﻨﻤﻭﺫﺝ ﺍﻟﺨﺎﺩﻡ ﻭﺍﻟﻌﻤﻴل ﺍﻟﻤﻴﺯﺍﺕٍ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -‬ﻭﺠﻭﺩ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻋﻠـﻰ ﺍﻟﺨـﺎﺩﻡ ﻴـﻭﻓﺭ ﻟﻤﺴـﺘﺨﺩﻤﻴﻬﺎ ﻤﺴـﺎﺤﺔ ﺍﻟﺘﺨـﺯﻴﻥ‬
‫)ﺒﺩﻻ ﻤﻥ ﻭﻀﻌﻬﺎ ﻋﻠﻰ ﺃﺠﻬﺯﺓ ﻜل ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ(‪ ،‬ﺨﺎﺼ‪‬ﺔ ﺤﻴﻨﻤﺎ ﺘﻜﻭﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﻋﻤﻼﻗﺔ‪.‬‬
‫‪ -‬ﻭﺠﻭﺩ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ﻴﺘﺭﻙ ﻟﻠﺠﻬـﺔ ﺍﻟﻤﺴـﺌﻭﻟﺔ ﻋﻨﻬـﺎ ﻤﻬﻤ‪‬ـﺔ ﺘﺤـﺩﻴﺜﻬﺎ‬
‫ﺒﺎﺴﺘﻤﺭﺍﺭ‪ ،‬ﻭﻫﻭ ﺃﻓﻀل ﻤﻥ ﺍﻀﻁﺭﺍﺭ ﻜلّ ﻤﺴﺘﺨﺩﻡ ﺇﻟﻰ ﺸﺭﺍﺀ ﻨﺴﺨﺔ ﺤﺩﻴﺜﺔ ﻤﻥ ﻗﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜلّ ﻓﺘﺭﺓ‪.‬‬
‫‪ -‬ﻭﺠﻭﺩ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ﻴﻀﻤﻥ ﻤﺸﺎﺭﻜﺘﻬﺎ ﺒﻴﻥ ﻤﺌﺎﺕ ﺍﻟﻤﺴـﺘﺨﺩﻤﻴﻥ‪ ،‬ﻤﻤـﺎ‬
‫ﻴﻀﻤﻥ ﻤﺴﺎﻫﻤﺘﻬﻡ ﻓﻲ ﺇﻀﺎﻓﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻗﺩﺭﺓ ﻜل ﻤﻨﻬﻡ ﻋﻠﻰ ﺭﺅﻴﺔ ﺍﻟﺘﻌـﺩﻴﻼﺕ ﺍﻟﺘـﻲ‬
‫ﺃﺠﺭﺍﻫﺎ ﺍﻵﺨﺭ‪ ،‬ﺒﻴﻨﻤﺎ ﻟﻭ ﻜﺎﻨﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺠﻬﺎﺯ ﻜل ﻤـﻨﻬﻡ ﺒﻤﻔـﺭﺩﻩ‪ ،‬ﻓﻠـﻥ‬
‫‪١٩‬‬
‫ﻴﻤﻜﻨﻬﻡ ﺍﻟﻌﻤل ﺍﻟﺠﻤﺎﻋﻲ ﻋﻠﻴﻬﺎ‪ ،‬ﻭﻫﺫﺍ ﻻ ﻴﻨﺎﺴﺏ ﻨﺸﺎﻁ ﺍﻟﺸﺭﻜﺎﺕ ﺍﻟﺘﺠﺎﺭﻴﺔ ﻭﺍﻟﻤﺅﺴﺴﺎﺕ‬
‫ﺍﻟﻤﺎﻟﻴﺔ‪.‬‬
‫‪ -‬ﺇﺠﺭﺍﺀ ﺍﻟﻌﻤﻠ‪‬ﻴ‪‬ﺎﺕ ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل ﺒﻌﺩ ﺍﻟﺤﺼﻭل ﻋﻠﻴﻬﺎ ﻤـﻥ ﺍﻟﺨـﺎﺩﻡ‪،‬‬
‫ﻴﻜﻭﻥ ﺃﺴﺭﻉ ﺒﻜﺜﻴﺭ ﻤﻥ ﺘﻨﻔﻴﺫ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ﺜﻡ‪ ‬ﺇﺭﺴﺎل ﺍﻟﻨﺎﺘﺞ ﺇﻟﻰ ﺍﻟﻌﻤﻴل‪ ،‬ﻭﺫﻟﻙ‬
‫ﻷﻥ‪ ‬ﻫﻨﺎﻙ ﻋﺩﺩﺍ ﻀﺨﻤﺎ ﻤﻥ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﺍﻟﺫﻴﻥ ﻴﺠﺭﻭﻥ ﺁﻻﻑ ﺍﻟﻌﻤﻠ‪‬ﻴ‪‬ـﺎﺕ ﻓـﻲ ﻨﻔـﺱ‬
‫ﺍﻟﻠﺤﻅﺔ‪.‬‬
‫ﻭﻴﻨﻅﻡ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﻋﻤﻠﻴﺎﺕ ﺍﻻﺘﺼﺎل ﻤﻊ ﺍﻟﻌﻤﻼﺀ‪ ،‬ﺤﻴﺙ ﻴﺨﺼﺹ ﻟﻜل ﺍﺘﺼﺎل ﻋﻤﻠﻴﺔ ﻓﺭﻋﻴﺔ‬
‫‪ Thread‬ﻟﻠﻘﺭﺍﺀﺓ ﺃﻭ ﺍﻟﻜﺘﺎﺒﺔ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻴﺘﻭﻗﻑ ﻋﺩﺩ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺘﺎﺤﺔ ﻓﻲ ﻨﻔـﺱ‬
‫ﺍﻟﻠﺤﻅﺔ ﻋﻠﻰ ﺤﺠﻡ ﺍﻟﺫﺍﻜﺭﺓ ﺍﻟﻤﺅﻗﺘﺔ ‪ RAM‬ﺍﻟﻤﻭﺠﻭﺩﺓ ﻋﻠﻰ ﺍﻟﺠﻬﺎﺯ ﺍﻟﺨﺎﺩﻡ ﻭﻗﻭﺓ ﺍﻟﻤﺸﻐل ﺍﻟﺩﻗﻴﻕ‬
‫ﺍﻟﺨﺎﺹ ﺒﻪ‪ ..‬ﻭﻓﻲ ﺤﺎﻟﺔ ﺍﺯﺩﻴﺎﺩ ﺍﻟﻀﻐﻁ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ﻴﻘﻭﻡ ﺒﺘﺄﺠﻴل ﺍﻻﺴﺘﺠﺎﺒﺔ ﻟـﺒﻌﺽ ﺍﻟﻌﻤـﻼﺀ‬
‫ﺇﻟﻰ ﺤﻴﻥ ﺍﻻﻨﺘﻬﺎﺀ ﻤﻥ ﺨﺩﻤﺔ ﺍﻟﻌﻤﻼﺀ ﺍﻟﺴﺎﺒﻘﻴﻥ‪ ،‬ﻤﻤﺎ ﻗﺩ ﻴﺅﺩﻱ ﺇﻟﻰ ﺒـﻁﺀ ﺒﺭﻨﺎﻤﺠـﻙ ﻭﺘﻀـﺎﻴﻕ‬
‫ﻤﺴﺘﺨﺩﻤﻴﻪ ﺒﺴﺒﺏ ﺘﻌﻁﻠﻪ ﻋﻥ ﺍﻻﺴﺘﺠﺎﺒﺔ ﻟﻔﺘﺭﺍﺕ ﻁﻭﻴﻠﺔ‪ ..‬ﻟﻬﺫﺍ ﺘﻘﻊ ﻋﻠﻰ ﺒﺭﻨﺎﻤﺠـﻙ ﻤﺴـﺌﻭﻟﻴ‪‬ﺔ‬
‫ﻀﻤﺎﻥ ﻜﻔﺎﺀﺓ ﻋﻤﻠﻴﺎﺕ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺒﻤﺭﺍﻋﺎﺓ ﻤﺎ ﻴﻠﻲ‪:‬‬
‫‪ -‬ﺃﻻ ﻴﻁﻠﺏ ﺒﺭﻨﺎﻤﺠﻙ ﺒﻴﺎﻨﺎﺕ ﻻ ﻀﺭﻭﺭﺓ ﻟﻬﺎ‪ ..‬ﻓﺈﻥ ﻜﻨﺕ ﺘﺤﺘﺎﺝ ﻤـﺜﻼ ﺇﻟـﻰ ﺤﻘـل ﺃﻭ‬
‫ﺤﻘﻠﻴﻥ ﻤﻥ ﺍﻟﺠﺩﻭل‪ ،‬ﻓﻤﺎ ﺍﻟﺩﺍﻋﻲ ﻷﻥ ﺘﻘﺭﺃ ﻜل ﺍﻟﺤﻘﻭل؟‬
‫‪ -‬ﺍﻟﺘﺄﻜﺩ ﻤﻥ ﻜﺘﺎﺒﺔ ﺃﻗﺼﺭ ﻭﺃﻜﻔﺄ ﺍﺴﺘﻌﻼﻤﺎﺕ ‪ SQL‬ﻟﻴﻜﻭﻥ ﺘﻨﻔﻴﺫﻫﺎ ﺃﺴـﺭﻉ ﻓـﻼ ﺘﺭﻫـﻕ‬
‫ﺍﻟﺨﺎﺩﻡ‪.‬‬
‫‪ -‬ﺍﻻﺤﺘﻔﺎﻅ ﺒﺒﻌﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺠﻬﺯﺓ ‪ Cashed‬ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل ﺒﺩﻻ ﻤﻥ ﺇﻋﺎﺩﺓ ﻁﻠﺒﻬﺎ‬
‫ﺃﻜﺜﺭ ﻤﻥ ﻤﺭﺓ ﻓﻲ ﻓﺘﺭﺍﺕ ﺯﻤﻨﻴﺔ ﺼﻐﻴﺭﺓ‪ ،‬ﻭﺫﻟﻙ ﺇﺫﺍ ﻜﻨﺕ ﺘﻀﻤﻥ ﻋـﺩﻡ ﺘﻐﻴـﺭ ﻫـﺫﻩ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺴﺭﻋﺔ ﻜﺒﻴﺭﺓ‪ ..‬ﻭﺇﺫﺍ ﻜﺎﻨﺕ ﻫﺫﻩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺴﺘﻅل ﺜﺎﺒﺘﺔ ﻟﺠﻤﻴﻊ ﺍﻟﻌﻤـﻼﺀ ﻟﻔﺘـﺭﺓ‬
‫ﻁﻭﻴﻠﺔ‪ ،‬ﻓﻴﻤﻜﻥ ﺘﺠﻬﻴﺯﻫﺎ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ﻭﺇﺭﺴﺎﻟﻬﺎ ﺇﻟﻴﻬﻡ ﻤﺒﺎﺸﺭﺓ ﻜﻠﻤﺎ ﻁﻠﺒﻭﻫﺎ ﺒﺩﻭﻥ ﺇﻋﺎﺩﺓ‬
‫ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ‪ ،‬ﻭﻻ ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺠﻬﺯﺓ ﺇﻻ ﺇﺫﺍ ﺤﺩﺙ ﺘﻐﻴﻴﺭ ﻓﻴﻬﺎ ﻓﻲ ﻗﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫‪٢٠‬‬
‫ﻤﺜل ﻫﺫﺍ ﺍﻟﺘﻨﻅﻴﻡ ﻴﻀﻤﻥ ﺘﺨﻔﻴﻑ ﻋﺏﺀ ﻫﺎﺌل ﻤﻥ ﻋﻠﻰ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﻭﺘﻘﻠﻴل ﺠﻤل ‪ 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‬‬

‫ﺃﺩﻭﺍﺕ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ‬


‫‪Data-Bound Controls‬‬

‫‪٢٣‬‬
‫ﻭﺘﻭﺠﺩ ﻓﺌﺎﺕ ‪ 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 -٣‬‬
‫ﺘﻭﻓﺭ ﺩﻭﺕ ﻨﺕ ﺩﻋﻤﺎ ﺨﺎﺼﺎ ﻟﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﺒﺎﻋﺘﺒﺎﺭﻩ ﺃﻫﻡ ﺘﻁﺒﻴﻘﺎﺕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺍﻟﺘﻲ ﺃﻨﺘﺠﺘﻬﺎ ﻤﻴﻜﺭﻭﺴﻭﻓﺕ ﺒﻌﺩ ﺍﻨﺘﺸﺎﺭ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺸﺒﻜﺎﺕ ﻭﺍﻹﻨﺘﺭﻨﺕ ﻓﻲ ﻋﺎﻟﻡ ﺍﻟﺘﺠﺎﺭﺓ‬
‫ﻭﺍﻷﻋﻤﺎل‪.‬‬
‫ﻭﺘﻭﺠﺩ ﻓﺌﺎﺕ ﻫﺫﺍ ﺍﻟﻤﺯﻭﺩ ﻓﻲ ﺍﻟﻨﻁﺎﻗﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻓﺌﺎﺕ ﻤﺯﻭﺩ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪.‬‬ ‫‪System.Data.SqlClient‬‬


‫ﻴﻘﺩﻡ ﺒﻌﺽ ﺍﻟﻭﻅﺎﺌﻑ ﺍﻟﺨﺎﺼﺔ ﺒﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪.‬‬ ‫‪System.Data.SQL‬‬
‫‪Data‬‬ ‫ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻓﺌﺎﺕ ﺘﻤﺜل ﺃﻨﻭﺍﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ‬ ‫‪System.Data.SqlTypes‬‬
‫‪ Types‬ﺍﻟﺨﺎﺼﺔ ﺒﺴﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ‪ ،‬ﻟﻴﻤﻜﻨـﻙ‬
‫ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﺒﺩﻻ ﻤﻥ ﺃﻨﻭﺍﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ‬
‫ﺇﻁﺎﺭ ﺍﻟﻌﻤل‬
‫‪ Microsoft.SqlServer.Server‬ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻔﺌﺎﺕ ﺍﻟﻼﺯﻤﺔ ﻟﺘﺸﻐﻴل ﺴـﻴﻜﻭﻴل‬
‫ﺴﻴﺭﻓﺭ ﻓﻲ ﺩﻭﺕ ﻨﺕ‪.‬‬

‫‪:SQL Server Compact 3.5 -٤‬‬


‫ﻴﺘﻴﺢ ﻟﻙ ﻫﺫﺍ ﺍﻟﻤﺯﻭﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻨﺸﺄﺓ ﺒﺎﻟﻨﺴﺨﺔ ﺍﻟﺨﻔﻴﻔﺔ ﻤﻥ ﺴﻜﻴﻭﻴل‬
‫ﺴﻴﺭﻓﺭ ‪ ،SQL Server Compact Edition‬ﺍﻟﻤﺨﺼﺼﺔ ﻹﻨﺸﺎﺀ ﻗﻭﺍﻋـﺩ ﺒﻴﺎﻨـﺎﺕ‬
‫ﻟﻸﺠﻬﺯﺓ ﺍﻟﻜﻔﻴﺔ ﺍﻟﻤﺤﻤﻭﻟﺔ‪ ،‬ﺍﻟﺘﻲ ﺘﺘﻌﺎﻤـل ﻤـﻊ ﺍﻟﻨﺴـﺨﺔ ﺍﻟﺨﻔﻴﻔـﺔ ﻤـﻥ ﺍﻟﻭﻴﻨـﺩﻭﺯ‬
‫‪ Windows CE‬ﻭﺍﻟﻨﺴـﺨﺔ ﺍﻟﺨﻔﻴﻔـﺔ ﻤـﻥ ﺇﻁـﺎﺭ ﺍﻟﻌﻤـل ‪.NET Compact‬‬
‫‪.Framework‬‬
‫ﻭﺘﻭﺠﺩ ﻓﺌﺎﺕ ﻫﺫﺍ ﺍﻟﻤﺯﻭﺩ ﻓﻲ ﺍﻟﻨﻁﺎﻕ‪:‬‬
‫‪System.Data.SqlServerCe‬‬
‫ﻟﻜﻥ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﺍ ﺍﻟﻨﻁﺎﻕ ﻴﺘﻁﻠﺏ ﻤﻨﻙ ﺃﻭﻻ ﺇﻀﺎﻓﺔ ﻤﺭﺠﻊ ﺇﻟﻴﻪ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻙ‪ ،‬ﻋﻠﻤـﺎ‬
‫ﺒﺄﻨﻪ ﻴﻭﺠﺩ ﻓﻲ ﺍﻟﻤﻠﻑ‪:‬‬
‫‪system.data.sqlserverce.dll‬‬
‫‪٢٦‬‬
‫‪:Oracle -٥‬‬
‫ﻗﺩﻤﺕ ﻤﻴﻜﺭﻭﺴﻭﻓﺕ ﻤﻨﺫ ﺇﺼﺩﺍﺭ ﺩﻭﺕ ﻨﺕ ‪ ٢٠٠٣‬ﺩﻋﻤﺎ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨـﺎﺕ‬
‫ﺃﻭﺭﺍﻜل‪ ،‬ﻓﻬﻲ ﺘﻤﺘﺎﺯ ﺒﺎﻟﻘﻭﺓ ﻭﺍﻟﺸﻬﺭﺓ ﻭﺍﻻﻨﺘﺸﺎﺭ‪.‬‬
‫ﻭﺘﻭﺠﺩ ﻓﺌﺎﺕ ﻫﺫﺍ ﺍﻟﻤﺯﻭﺩ ﻓﻲ ﺍﻟﻨﻁﺎﻕ‪System.Data.OracleClient :‬‬
‫ﻟﻜﻥ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﺍ ﺍﻟﻨﻁﺎﻕ ﻴﺘﻁﻠﺏ ﻤﻨﻙ ﺃﻭﻻ ﺇﻀﺎﻓﺔ ﻤﺭﺠﻊ ﺇﻟﻴﻪ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻙ‪ ،‬ﻋﻠﻤـﺎ‬
‫ﺒﺄﻨﻪ ﻴﻭﺠﺩ ﻓﻲ ﺍﻟﻤﻠﻑ‪:‬‬
‫‪System.Data.OracleClient.dll‬‬

‫ﻭﻋﻠﻴﻙ ﺃﻥ ﺘﻼﺤﻅ ﺃﻥ ﺠﻤﻴﻊ ﻫﺫﻩ ﺍﻟﻤﺯﻭﺩﺍﺕ ﺘﻭﻓﺭ ﻨﻔﺱ ﺃﺩﻭﺍﺕ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ )ﻜـﺎﺌﻥ‬
‫ﺍﻻﺘﺼﺎل ‪ ،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‬‬

‫ﻨﺹ ﺍﻻﺘﺼﺎل ‪:Connection String‬‬


‫ﻟﻼﺘﺼﺎل ﺒﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪ ،‬ﻴﺠﺏ ﺃﻥ ﺘﺭﺴل ﺇﻟﻴﻪ ﻨﺼﺎ ﻴﺤﺘﻭﻯ ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻼﺯﻤﺔ‪ ،‬ﻤﺜل ﺍﺴﻡ‬
‫ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺍﺴﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻭﻜﻠﻤﺔ ﺍﻟﺴﺭ‪ ..‬ﻭﻴﺘﻜﻭﻥ ﻨﺹ ﺍﻻﺘﺼﺎل ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﻤﻥ ﺍﻟﻘﻴﻡ‪،‬‬
‫ﻴﻔﺼل ﺒﻴﻥ ﻜل ﻤﻨﻬﺎ ﺍﻟﻌﻼﻤﺔ ; ﻭﺫﻟﻙ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ‪:‬‬
‫‪Property1 = Value1; Property2 = Value2; ……………….‬‬
‫‪PropertyN = ValueN‬‬
‫ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜﺎل‪ ،‬ﺍﻟﻨﺹ ﺍﻟﺘﺎﻟﻲ ﻫﻭ ﻨﺹ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺒﻴﺎﻨـﺎﺕ ﺍﻟﻜﺘـﺏ ﻋﻠـﻰ ﺴـﻴﻜﻭﻴل‬
‫ﺴﻴﺭﻓﺭ‪:‬‬
‫;‪Data Source = .\SQLEXPRESS‬‬
‫;‪AttachDbFilename = C:\Books.mdf‬‬
‫;‪Integrated Security = true‬‬
‫;‪Connect Timeout = 30‬‬
‫‪٢٩‬‬
‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫{‪ ،‬ﻋﻠﻰ‬ ‫}‬ ‫ﻋﻨﺩ ﺒﻨﺎﺀ ﻨﺹ ﺍﻻﺘﺼﺎل ﺒﻤﺯﻭﺩ ‪ ،ODBC‬ﻋﻠﻴﻙ ﻭﻀﻊ ﺍﻟﻘﻴﻡ ﺒﻴﻥ ﻗﻭﺴﻴﻥ ﻤﺘﻌﺭﺠﻴﻥ‬
‫ﺍﻟﺼﻴﻐﺔ‪:‬‬
‫;}‪Property1 = {Value1‬‬ ‫= ‪Property2 = {Value2}; ……. PropertyN‬‬
‫}‪{ValueN‬‬
‫ﺒﻴﻨﻤﺎ ﻓﻲ ﺒﺎﻗﻲ ﺍﻟﻤﺯﻭﺩﺍﺕ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻋﻼﻤﺎﺕ ﺍﻟﺘﻨﺼﻴﺹ ﺒﺩﻻ ﻤﻥ ﺍﻷﻗﻭﺍﺱ ﺍﻟﻤﺘﻌﺭﺠﺔ‪.‬‬

‫ﻭﺘﺨﺘﻠﻑ ﺒﻌﺽ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﻤﺭﺴﻠﺔ ﻋﺒﺭ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ،‬ﺘﺒﻌﺎ ﻟﻨﻭﻉ ﻤـﺯﻭﺩ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ‪ ..‬ﻭﻗﺩ ﻜﺎﻨﺕ ﻜﺘﺎﺒﺔ ﻨﺹ ﺍﻻﺘﺼﺎل ﺘﻤﺜل ﻤﺸﻜﻠﺔ ﻗﺒل ﻅﻬﻭﺭ ﺍﻹﺼﺩﺍﺭ ﺍﻟﺜﺎﻨﻲ ﻤﻥ ﺇﻁﺎﺭ‬
‫ﺍﻟﻌﻤل ﻤﻊ ﺩﻭﺕ ﻨﺕ ‪ ،٢٠٠٥‬ﺤﻴﺙ ﻭﻓﺭ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﻓﺌﺔ ﺨﺎﺼﺔ ﺘﺴﻤﻰ "ﺒﺎﻨﻲ ﻨﺹ ﺍﻻﺘﺼـﺎل"‬
‫‪ ،DbConnectionStringBuilder‬ﻭﻤﻨﻬﺎ ﺘﻡ ﺍﺸﺘﻘﺎﻕ ﻓﺌﺔ ﻟﺒﻨﺎﺀ ﻨﺹ ﺍﺘﺼﺎل ﻜل ﻤﺯﻭﺩ ﻤـﻥ‬
‫ﻤﺯﻭﺩﺍﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻫﻲ‪:‬‬

‫ﻭﻅﻴﻔﺘﻬﺎ‬ ‫ﺍﻟﻔﺌﺔ‬
‫ﺒﺎﻨﻲ ﻨﺹ ﺍﺘﺼﺎل ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪.‬‬ ‫‪SqlConnectionStringBuilder‬‬
‫ﺒﺎﻨﻲ ﻨﺹ ﺍﺘﺼﺎل ‪.OLEDB‬‬ ‫‪OleDbConnectionStringBuilder‬‬
‫ﺒﺎﻨﻲ ﻨﺹ ﺍﺘﺼﺎل ‪.ODBC‬‬ ‫‪OdbcConnectionStringBuilder‬‬
‫ﺒﺎﻨﻲ ﻨﺹ ﺍﺘﺼﺎل ﺃﻭﺭﺍﻜل‪.‬‬ ‫‪OracleConnectionStringBuilder‬‬

‫‪٣٠‬‬
‫ﻓﺌﺔ ﺒﺎﻨﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‬
‫‪DbConnectionStringBuilder Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﻨﻁﺎﻕ ‪ ،System.Data.Common‬ﻭﻫﻲ ﺘﻤﺜل ﻭﺍﺠﻬـﺔ ﺍﻟﻘـﺎﻤﻭﺱ‬


‫‪ ،IDictionary‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻨﻬﺎ ﻤﺠﻤﻭﻋﺔ ‪ Collection‬ﻜل ﻋﻨﺼﺭ ﻤﻥ ﻋﻨﺎﺼﺭﻫﺎ ﻴﻜﻭﻥ ﻓـﻲ‬
‫ﺼﻭﺭﺓ ﻤﻔﺘﺎﺡ ‪ Key‬ﻭﻗﻴﻤﺔ ‪ ..Value‬ﻭﺒﻬﺫﺍ ﺍﻟﺘﺼﻤﻴﻡ ﺘﺴﺘﻁﻴﻊ ﺘﻜﻭﻴﻥ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ،‬ﺒﺈﻀـﺎﻓﺔ‬
‫ﻋﻨﺎﺼﺭ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻘﺎﻤﻭﺱ‪ ،‬ﻜل ﻋﻨﺼﺭ ﻤﻨﻬﺎ ﻴﺘﻜﻭﻥ ﻤﻥ ﺍﺴﻡ ﺨﺎﺼﻴﺔ ﺍﻻﺘﺼﺎل ﻭﻗﻴﻤﺘﻬﺎ‪ ،‬ﺤﻴـﺙ‬
‫ﺴﺘﻘﻭﻡ ﺍﻟﻔﺌﺔ ‪ DbConnectionStringBuilder‬ﺒﺘﻜﻭﻴﻥ ﺼﻴﻐﺔ ﻨﺹ ﺍﻻﺘﺼﺎل ﺒﻨﺎﺀ ﻋﻠﻰ ﻫﺫﻩ‬
‫ﺍﻟﻌﻨﺎﺼﺭ‪.‬‬
‫ﻭﺘﻭﺠﺩ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻓﻲ ﻨﻁﺎﻕ ﺍﻻﺴﻡ ‪ ،System.Data.Common‬ﻟﻬﺫﺍ ﻻ ﺘﻨﺱ‪ ‬ﺇﻀﺎﻓﺔ ﺍﻟﺠﻤﻠـﺔ‬
‫ﺍﻟﺘﺎﻟﻴﺔ ﺃﻋﻠﻰ ﺼﻔﺤﺔ ﺍﻟﻜﻭﺩ ﻗﺒل ﺍﺴﺘﺨﺩﺍﻤﻬﺎ‪:‬‬
‫;‪using System.Data.Common‬‬
‫ﻭﻟﺤﺩﺙ ﺍﻹﻨﺸﺎﺀ ‪ Constructor‬ﺍﻟﺨﺎﺹ ﺒﻬﺫﻩ ﺍﻟﻔﺌﺔ ﺼﻴﻐﺘﺎﻥ‪:‬‬
‫‪ -١‬ﺍﻷﻭﻟﻰ ﻻ ﺘﺴﺘﻘﺒل ﺃﻱ ﻤﻌﺎﻤﻼﺕ‪ ..‬ﻤﺜﺎل‪:‬‬
‫;) (‪var CsB = new DbConnectionStringBuilder‬‬
‫‪ -٢‬ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻤﻨﻁﻘﻴﺎ ‪ ،Boolean‬ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻪ ‪ true‬ﻓﺴﻴﺘﻡ ﻭﻀﻊ ﺍﻟﻘـﻴﻡ‬
‫ﺍﻟﻤﻜﺘﻭﺒﺔ ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل ﺒﻴﻥ ﻗﻭﺴـﻴﻥ ﻤﻀـﻠﻌﻴﻥ } { ﻻﺴـﺘﺨﺩﺍﻤﻪ ﻤـﻊ ﻤـﺯﻭﺩ‬
‫‪.ODBC‬‬
‫ﻭﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﺨﺼﺎﺌﺹ ﺍﻟﻘﺎﻤﻭﺱ ﺍﻟﺸﻬﻴﺭﺓ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﻨﺹ ﺍﻻﺘﺼﺎل ‪:ConnectionString‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻨﺹ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﺘﺘﻌﺎﻤل ﻤﻌﻪ ﻫﺫﻩ ﺍﻟﻔﺌﺔ‪ ..‬ﻭﺘﺴﺘﻁﻴﻊ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻨﺹ‬
‫ﺍﻻﺘﺼﺎل ﺃﻴﻀﺎ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ ..ToString‬ﻻﺤﻅ ﺃﻥ ﺒﺎﻨﻲ ﻨﺹ ﺍﻻﺘﺼـﺎل ﻴﺭﺘـﺏ‬
‫ﺍﻟﻤﻔﺎﺘﻴﺢ ﻓﻲ ﺍﻟﻨﺹ ﺍﻟﻌﺎﺌﺩ ﺤﺴﺏ ﺃﻭﻟﻭﻴﺘﻬﺎ‪ ،‬ﻭﻟﻴﺱ ﻋﻠﻰ ﺤﺴﺏ ﺘﺭﺘﻴﺏ ﺇﻀﺎﻓﺘﻙ ﻟﻬﺎ‪.‬‬

‫‪٣١‬‬
‫ﻨﺹ ﺍﺘﺼﺎل ﻗﺎﺒل ﻟﻠﺘﺼﻔﺢ ‪:BrowsableConnectionString‬‬
‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،true‬ﻓﺴﻴﺘﻡ ﻋﺭﺽ ﻨﺹ ﺍﻻﺘﺼﺎل ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼـﺎﺌﺹ‬
‫ﻋﻨﺩﻤﺎ ﺘﺴﺘﺨﺩﻡ ﺍﻷﺩﺍﺓ ‪ PropertyGrid‬ﻟﻌﺭﺽ ﺨﺼﺎﺌﺹ ﺒﺎﻨﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪.‬‬

‫ﻭﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﻭﺴﺎﺌل ﺍﻟﻘﺎﻤﻭﺱ ﺍﻟﺸﻬﻴﺭﺓ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺇﻀﺎﻓﺔ ﻤﻔﺘﺎﺡ ﻭﻗﻴﻤﺔ ‪:AppendKeyValuePair‬‬


‫ﺘﺘﻴﺢ ﻟﻙ ﺇﻀﺎﻓﺔ ﺨﺎﺼﻴﺔ ﻭﻗﻴﻤﺘﻬـﺎ ﺇﻟـﻰ ﻨـﺹ ﺍﺘﺼـﺎل ﻤﻭﺠـﻭﺩ ﻓـﻲ ﺒـﺎﻨﻲ ﻨـﺹ‬
‫‪ ،StringBuilder‬ﺤﻴﺙ ﺴﺘﻘﻭﻡ ﺒﺘﻜﻭﻴﻥ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺼـﺤﻴﺤﺔ ﻟﻠﺨﺎﺼـﻴﺔ ﻭﺍﻟﻘﻴﻤـﺔ‪ ،‬ﺜـﻡ‬
‫ﺇﻀﺎﻓﺘﻬﺎ ﻓﻲ ﻨﻬﺎﻴﺔ ﺒﺎﻨﻲ ﺍﻟﻨﺹ‪.‬‬
‫ﻭﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ‪ :‬ﺒﺎﻨﻲ ﺍﻟﻨﺹ ‪ ،StringBuilder‬ﻭﻨﺼﺎ ﻴﻤﺜل ﺍﺴـﻡ‬
‫ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻭﻨﺼﺎ ﻴﻤﺜل ﻗﻴﻤﺘﻬﺎ‪ ..‬ﻤﺜﺎل‪:‬‬
‫(‪var SB = new System.Text.StringBuilder‬‬
‫;)";‪"Data Source = .\\SQLEXPRESS‬‬
‫‪DbConnectionStringBuilder.AppendKeyValuePair(SB,‬‬
‫;)"‪"AttachDbFilename", "C:\\Books.mdf‬‬
‫‪DbConnectionStringBuilder.AppendKeyValuePair(SB,‬‬
‫;)"‪"Integrated Security", "true‬‬
‫;)) (‪MessageBox.Show(SB.ToString‬‬
‫ﺴﺘﻌﺭﺽ ﺍﻟﺭﺴﺎﻟﺔ ﺍﻟﻨﺹ‪:‬‬
‫‪Data Source = .\SQLEXPRESS; AttachDbFilename‬‬
‫‪= C:\Books.mdf; Integrated Security=true‬‬
‫ﻭﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺃﺨﺭﻯ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﻤﻌﺎﻤل ﺭﺍﺒﻊ‪ ،‬ﺇﺫﺍ ﺠﻌﻠـﺕ‬
‫ﻗﻴﻤﺘﻪ ‪ ،true‬ﻓﺴﻴﺘﻡ ﻭﻀﻊ ﺍﻟﻘﻴﻡ ﺒﻴﻥ ﻗﻭﺴﻴﻥ ﻤﺘﻌﺭﺠﻴﻥ }{ ﻻﺴﺘﺨﺩﺍﻡ ﻨﺹ ﺍﻻﺘﺼﺎل ﻤـﻊ‬
‫ﻤﺯﻭﺩ ‪.ODBC‬‬

‫‪٣٢‬‬
‫ﻤﺴﺎﻭٍ ﻟـ ‪:EquivalentTo‬‬
‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﻔﺌﺔ ‪ DbConnectionStringBuilder‬ﻟﻤﻘﺎﺭﻨﺘﻬـﺎ‬
‫ﺒﺎﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ﻤﻥ ﺍﻟﻔﺌﺔ ‪ ..DbConnectionStringBuilder‬ﻭﺘﺘﻡ ﺍﻟﻤﻘﺎﺭﻨـﺔ ﺒﺎﻟﺘﺄﻜـﺩ‬
‫ﻤﻥ ﺃﻥ ﻜل ﻤﻔﺘﺎﺡ ﻓﻲ ﺍﻟﻘﺎﻤﻭﺱ ﺍﻷﻭل ﻟﻪ ﻤﺎ ﻴﻨﺎﻅﺭﻩ ﻓﻲ ﺍﻟﻘﺎﻤﻭﺱ ﺍﻟﺜﺎﻨﻲ )ﺒﻐـﺽ ﺍﻟﻨﻅـﺭ‬
‫ﻋﻥ ﺍﻟﺘﺭﺘﻴﺏ(‪ ،‬ﻭﺃﻥ ﺍﻟﻘﻴﻤﺘﻴﻥ ﺍﻟﻤﺤﻔﻭﻅﺘﻴﻥ ﻓﻲ ﻜﻠﻴﻬﻤﺎ ﻤﺘﺴـﺎﻭﻴﺘﺎﻥ‪ ..‬ﻻﺤـﻅ ﺃﻥ ﻤﻘﺎﺭﻨـﺔ‬
‫ﺍﻟﻤﻔﺎﺘﻴﺢ ﻻ ﺘﺭﺍﻋﻲ ﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ‪ ،‬ﺒﻴﻨﻤﺎ ﻤﻘﺎﺭﻨﺔ ﺍﻟﻘﻴﻡ ﺘﺭﺍﻋﻲ ﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ‪ ..‬ﻭﻓﻲ ﺤﺎﻟﺔ‬
‫ﻨﺠﺎﺡ ﺍﻟﻤﻘﺎﺭﻨﺔ ﻴﻌﺘﺒﺭ ﻨﺼﺎ ﺍﻻﺘﺼﺎل ﺍﻟﻤﻭﺠﻭﺩﻴﻥ ﻓﻲ ﺍﻟﻘﺎﻤﻭﺴﻴﻥ ﻤﺘﺴﺎﻭﻴﻴﻥ‪ ،‬ﻭﺘﻌﻴـﺩ ﻫـﺫﻩ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪ ..true‬ﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﻓـﻲ ﺍﻟـﺯﺭ ‪ EquivalentTo‬ﻓـﻲ‬
‫ﺍﻟﻤﺸﺭﻭﻉ ‪.ConStrBuilder‬‬

‫ﻫل ﻴﺤﺘﻭﻱ ﻋﻠﻰ ‪:ShouldSerialize‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺫﻱ ﺃﺭﺴﻠﺘﻪ ﺇﻟﻴﻬﺎ ﻜﻤﻌﺎﻤل ﻤﻭﺠﻭﺩﺍ ﻀﻤﻥ ﻨﺹ ﺍﻻﺘﺼـﺎل‪..‬‬
‫ﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻜﺎﻓﺌﺔ ﺘﻤﺎﻤﺎ ﻟﻠﻭﺴﻴﻠﺔ ‪ ..ContainsKey‬ﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻋﻠـﻰ‬
‫ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﺯﺭ ‪ ShouldSerialize‬ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪.ConStrBuilder‬‬

‫ﻤﺤﺎﻭﻟﺔ ﻤﻌﺭﻓﺔ ﺍﻟﻘﻴﻤﺔ ‪:TryGetValue‬‬


‫ﺘﺤﺎﻭل ﻗﺭﺍﺀﺓ ﻗﻴﻤﺔ ﺃﺤﺩ ﺍﻟﻤﻔﺎﺘﻴﺢ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ،‬ﻓﺈﻥ ﻜﺎﻥ ﺍﻟﻤﻔﺘﺎﺡ ﻤﻭﺠـﻭﺩﺍ‬
‫ﺃﻋﺎﺩﺕ ‪ ،true‬ﻭﺇﻥ ﻟﻡ ﻴﻜﻥ ﻤﻭﺠﻭﺩﺍ ﺃﻋﺎﺩﺕ ‪ false‬ﺩﻭﻥ ﺃﻥ ﺘﺴﺒﺏ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨـﺎﻤﺞ‪..‬‬
‫ﻟﻬﺫﺍ ﻴﻌﺘﺒﺭ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﺃﻓﻀل ﻤﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﻔﻬﺭﺱ ‪ Indexer‬ﻟﻘﺭﺍﺀﺓ ﻗﻴﻤﺔ ﺍﻟﻤﻔﺘﺎﺡ‪ ،‬ﻓﻬﻭ‬
‫ﻴﺴﺒﺏ ﺨﻁﺄ ﺇﻥ ﻟﻡ ﻴﻜﻥ ﺍﻟﻤﻔﺘﺎﺡ ﻤﻭﺠﻭﺩﺍ‪ ،‬ﻤﻤﺎ ﻴﺴﺘﻠﺯﻡ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ ‪ContainsKey‬‬
‫ﺃﻭﻻ ﻋﻠﻰ ﺴﺒﻴل ﺍﻻﺤﺘﻴﺎﻁ‪ ..‬ﻤﺜﻼ‪:‬‬
‫))"‪if (CsB.ContainsKey("AttachDbFilename‬‬
‫‪MessageBox.Show(CsB["AttachDbFilename"].‬‬
‫‪ToString( )); // C:\Books.mdf‬‬

‫‪٣٣‬‬
‫ﻭﻟﻠﻭﺴﻴﻠﺔ ‪ TryGetValue‬ﻤﻌﺎﻤﻼﻥ‪ :‬ﺍﻷﻭل ﻤﻌﺎﻤل ﻨﺼﻲ ﻴﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻤﻔﺘﺎﺡ‪ ،‬ﻭﺍﻟﺜـﺎﻨﻲ‬
‫ﻤﻌﺎﻤل ﺇﺨﺭﺍﺝ ‪ out‬ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،Object‬ﻴﻌﻴﺩ ﺇﻟﻴﻙ ﻗﻴﻤﺔ ﺍﻟﻤﻔﺘﺎﺡ ﺇﻥ ﻭﺠﺩ‪ ..‬ﻭﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ‬
‫ﻫﻭ ﺇﻋﺎﺩﺓ ﻜﺘﺎﺒﺔ ﻟﻠﻤﺜﺎل ﺍﻟﺴﺎﺒﻕ ﺒﺎﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪:‬‬
‫;‪object Value = null‬‬
‫))‪if (CsB.TryGetValue("AttachDbFilename", out Value‬‬
‫‪MessageBox.Show(CsB["AttachDbFilename"].‬‬
‫‪ToString ( )); // C:\Books.mdf‬‬

‫‪٣٤‬‬
‫ﻓﺌﺔ ﺒﺎﻨﻲ ﻨﺹ ﺍﺘﺼﺎل ﺴﻴﻜﻴﻭل‬
‫‪SqlConnectionStringBuilder Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ ،DbConnectionStringBuilder‬ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﺒﻨـﺎﺀ ﻨـﺹ‬


‫ﺍﻻﺘﺼﺎل ﺒﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪ ،‬ﻓﻬﻲ ﺘﻤﺘﻠﻙ ﺍﻟﻤﺯﻴﺩ ﻤﻥ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﻲ ﺘﺤﻤل ﺃﺴﻤﺎﺀ ﺍﻟﻤﻌﻠﻭﻤـﺎﺕ‬
‫ﺍﻟﻼﺯﻤﺔ ﻟﻼﺘﺼﺎل ﺒﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪ ،‬ﻤﻤﺎ ﻴﺠﻌل ﺘﻜﻭﻴﻥ ﻨﺹ ﺍﻻﺘﺼﺎل ﻓـﻲ ﻤﻨﺘﻬـﻰ ﺍﻟﺴـﻬﻭﻟﺔ‬
‫ﻭﺍﻟﻭﻀﻭﺡ‪.‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺼﻴﻐﺘﺎﻥ‪:‬‬
‫‪ -١‬ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻨﺹ ﺍﺘﺼﺎل ﻹﻀﺎﻓﺘﻪ ﺇﻟﻴﻬﺎ ﻤﺒﺩﺌﻴﺎ‪ ،‬ﺤﻴﺙ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﺃﻱ ﺘﻔﺎﺼـﻴل‬
‫ﺃﺨﺭﻯ ﺇﻟﻴﻪ ﺒﻌﺩ ﺫﻟﻙ‪.‬‬

‫ﻭﺒﺠﻭﺍﺭ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﻭﺼﻴل ﻤﻠﻑ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:AttachDBFilename‬‬


‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ AttachDBFilename‬ﺃﻭ ‪ initial file name‬ﻓﻲ ﻨـﺹ ﺍﻻﺘﺼـﺎل‪..‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻀﻊ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﺴﺎﺭ ﻭﺍﺴﻡ ﺍﻟﻤﻠﻑ ﺍﻷﺴﺎﺴﻲ ﻟﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﺘـﻲ‬
‫ﺘﺭﻴﺩ ﺍﻻﺘﺼﺎل ﺒﻬﺎ‪ ،‬ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﻴﺘﻡ ﺘﻭﺼﻴل ﻫﺫﻩ ﺍﻟﻘﺎﻋﺩﺓ ﺒﺎﻟﺨﺎﺩﻡ‪ ،‬ﻭﺍﻻﺘﺼﺎل ﺒﻬﺎ‪.‬‬
‫ﻭﻋﻠﻴﻙ ﺃﻥ ﺘﺘﺄﻜﺩ ﺃﻥ ﻤﻠﻑ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻴﺱ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ ‪ ،Read Only‬ﻷﻥ ﺘﻭﺼـﻴل‬
‫ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺤﺘﺎﺝ ﺇﻟﻰ ﺇﻨﺸﺎﺀ ﻤﻠﻑ ﺴﺠل ﺍﻷﺩﺍﺓ ‪ ،Log‬ﻭﺍﺴﻤﻪ ﻴﻭﻀﻊ ﻓﻲ ﻤﻠﻑ ﻗﺎﻋﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻟﻭ ﻜﺎﻨﺕ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ ﻓﺴﻴﺤﺩﺙ ﺨﻁﺄ ﻭﻟﻥ ﻴﻨﺠﺢ ﺍﻻﺘﺼﺎل‪.‬‬
‫ﺃﻴﻀﺎ‪ ،‬ﻗﺩ ﻴﺤﺩﺙ ﺨﻁﺄ ﺇﺫﺍ ﻜﺎﻥ ﻤﻠﻑ ﺴﺠل ﺍﻷﺩﺍﺀ ‪ Log‬ﻤﻭﺠﻭﺩﺍ ﻓﻲ ﻤﺠﻠﺩ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫ﻭﺃﻨﺕ ﺘﺤﺎﻭل ﺘﻭﺼﻴﻠﻬﺎ‪ ،‬ﻤﻊ ﻭﺠﻭﺩ ﺍﻟﻤﻔﺘﺎﺡ ‪ Database‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻓـﻲ ﻫـﺫﻩ‬
‫ﺍﻟﺤﺎﻟﺔ ﻋﻠﻴﻙ ﺤﺫﻑ ﻤﻠﻑ ﺴﺠل ﺍﻷﺩﺍﺀ ﻭﺇﻋﺎﺩﺓ ﺍﻻﺘﺼﺎل‪ ،‬ﺤﻴﺙ ﺴﻴﺘﻡ ﺇﻨﺸـﺎﺀ ﺴـﺠل ﺃﺩﺍﺀ‬
‫ﺠﺩﻴﺩ ﻟﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫‪٣٥‬‬
‫ﺍﻟﻔﻬﺭﺱ ﺍﻷﺴﺎﺴﻲ ‪:InitialCatalog‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﻻﺘﺼﺎل ﺒﻬﺎ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ‪ ..‬ﻭﺘﺨﺘﻠـﻑ ﻫـﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﻋﻥ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺴﺎﺒﻘﺔ ﻓﻲ ﺃﻨﻬﺎ ﺘﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻤﺘﺼﻠﺔ ﺒﺎﻟﺨـﺎﺩﻡ ﻓﻌـﻼ‬
‫ﻓﻲ ﻫﺫﻩ ﺍﻟﻠﺤﻅﺔ‪ ،‬ﻟﻬﺫﺍ ﻴﺘﻡ ﺫﻜﺭ ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻘﻁ ﺒﺩﻭﻥ ﺍﻟﻤﺴﺎﺭ ﻭﺍﻻﻤﺘـﺩﺍﺩ )ﻤﺜـل‬
‫‪ ..(Books‬ﺒﻴﻨﻤﺎ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ AttachDBFilename‬ﻴﺘﻡ ﺫﻜﺭ ﻤﺴﺎﺭ ﻤﻠـﻑ ﻗﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﺘﻭﺼﻴﻠﻬﺎ ﺒﺎﻟﺨﺎﺩﻡ ﺜﻡ ﺍﻻﺘﺼﺎل ﺒﻬﺎ‪.‬‬
‫ﻭﺘﻨﺎﻅﺭ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻤﻔﺘﺎﺡ ‪ database‬ﺃﻭ ‪ Initial Catalog‬ﻓﻲ ﻨـﺹ ﺍﻻﺘﺼـﺎل‪..‬‬
‫ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ﻨﺼﺎ ﻓﺎﺭﻏﺎ‪.‬‬

‫ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataSource‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﻭﺍﻥ ﺨﺎﺩﻡ ﺴـﻴﻜﻭﻴل‪ ..‬ﻗـﺩ ﻴﻜـﻭﻥ ﻫـﺫﺍ ﺍﻟﻌﻨـﻭﺍﻥ ﻟﻠﺨـﺎﺩﻡ ﺍﻟﻤﺤﻠـﻲ‬
‫‪ ،.\SQLEXPRESS‬ﺃﻭ ﻟﺨﺎﺩﻡ ﺒﻌﻴـﺩ ‪ Remote Server‬ﻟـﻪ ﻋﻨـﻭﺍﻥ ﺒﺭﻭﺘﻭﻜـﻭل‬
‫ﺍﻹﻨﺘﺭﻨﺕ ‪ IP Address‬ﺍﻟﺨﺎﺹ ﺒﻪ ﻤﺜل )‪.(10.0.0.127‬‬
‫ﻭﺘﻨﺎﻅﺭ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻤﻔﺘـﺎﺡ ‪ Data Source‬ﺃﻭ ‪ server‬ﺃﻭ ‪ address‬ﺃﻭ ‪ addr‬ﺃﻭ‬
‫‪ network address‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ﻨﺼـﺎ‬
‫ﻓﺎﺭﻏﺎ‪.‬‬

‫ﺒﺩﻴل ﻓﺸل ﺍﻻﺘﺼﺎل ‪:FailoverPartner‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﻭﺍﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﺍﻟﺒﺩﻴل‪ ،‬ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺍﺴـﺘﺨﺩﺍﻤﻪ ﺇﺫﺍ ﻓﺸـل ﺍﻻﺘﺼـﺎل‬
‫ﺒﺎﻟﺨﺎﺩﻡ ﺍﻟﺭﺌﻴﺴﻲ ﺍﻟﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺴﺎﺒﻘﺔ‪.‬‬
‫ﻭﺘﻨﺎﻅﺭ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻤﻔﺘﺎﺡ ‪ Failover Partner‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓـﻲ ﺍﻟﻭﻀـﻊ‬
‫ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ﻨﺼﺎ ﻓﺎﺭﻏﺎ‪.‬‬

‫‪٣٦‬‬
‫ﺤﻤﺎﻴﺔ ﻤﺘﻜﺎﻤﻠﺔ ‪:IntegratedSecurity‬‬
‫ﺘﻨــﺎﻅﺭ ﺍﻟﻤﻔﺘــﺎﺡ ‪ trusted_connection‬ﺃﻭ ‪ Integrated Security‬ﻓــﻲ ﻨــﺹ‬
‫ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ‪ ،false‬ﻤﻤﺎ ﻴﻌﻨـﻲ ﺃﻥ ﻋﻠﻴـﻙ ﺇﻤـﺩﺍﺩ‬
‫ﺍﻻﺘﺼﺎل ﺒﺎﺴﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻭﻜﻠﻤﺔ ﺍﻟﻤﺭﻭﺭ‪ ..‬ﺃﻤﺎ ﻟﻭ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ ‪ ،true‬ﻓﺴـﻴﺘﻡ ﺍﺴـﺘﺨﺩﺍﻡ‬
‫ﺤﺴﺎﺏ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻋﻠﻰ ﺍﻟﻭﻴﻨﺩﻭﺯ ﻟﻼﺘﺼﺎل‪ ..‬ﻫﺫﺍ ﻤﻔﻴﺩ ﻋﻨﺩ ﺍﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ ﺍﻟﻤﺤﻠـﻲ‪ ،‬ﺃﻭ‬
‫ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺩﺍﺨل ﺸﺭﻜﺔ ﺘﺴﺘﺨﺩﻡ ﺸﺒﻜﺔ ﺩﺍﺨﻠﻴﺔ ‪ ،LAN‬ﻓﻔﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻴﻘـﻭﻡ‬
‫ﻤﺩﻴﺭ ﻨﻅﺎﻡ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ‪ System Administrator‬ﺒﺘﻌﺭﻴﻑ ﺤﺴـﺎﺒﺎﺕ ﺍﻟﻭﻴﻨـﺩﻭﺯ‬
‫ﺍﻟﺨﺎﺼﺔ ﺒﺄﺠﻬﺯﺓ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﺍﻟﻤﺘﺼﻠﺔ ﺒﺎﻟﺸﺒﻜﺔ ﻭﺍﻟﻤﺴﻤﻭﺡ ﻟﻬﺎ ﺒﺎﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ‪ ،‬ﻭﺒﻬـﺫﺍ‬
‫ﻴﻜﻔﻲ ﻤﺠﺭﺩ ﺘﺴﺠﻴل ﺍﻟﺩﺨﻭل ﻋﻠﻰ ﺍﻟﻭﻴﻨﺩﻭﺯ ﻟﻀﻤﺎﻥ ﺴﺭﻴﺔ ﺍﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ‪.‬‬

‫ﻤﻌﺭﻑ ﺍﻟﻤﺴﺘﺨﺩﻡ ‪:UserID‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻟﺫﻱ ﻴﺘﺼل ﺒﺎﻟﺨﺎﺩﻡ‪ ،‬ﻭﺫﻟﻙ ﻓﻲ ﺤﺎﻟﺔ ﻋﺩﻡ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺤﻤﺎﻴـﺔ‬
‫ﺍﻟﻤﺘﻜﺎﻤﻠﺔ ‪.Integrated Security‬‬
‫ﻭﺘﻨﺎﻅﺭ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻤﻔﺘﺎﺡ ‪ User ID‬ﺃﻭ ‪ user‬ﺃﻭ ‪ uid‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼـﺎل‪ ..‬ﻭﻓـﻲ‬
‫ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ﻨﺼﺎ ﻓﺎﺭﻏﺎ‪.‬‬

‫ﻜﻠﻤﺔ ﺍﻟﺴﺭ ‪:Password‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﻠﻤﺔ ﺍﻟﻤﺭﻭﺭ ﺍﻟﻼﺯﻤﺔ ﻟﻼﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ‪ ،‬ﻭﺫﻟﻙ ﻓﻲ ﺤﺎﻟـﺔ ﻋـﺩﻡ ﺍﺴـﺘﺨﺩﺍﻡ‬
‫ﺍﻟﺤﻤﺎﻴﺔ ﺍﻟﻤﺘﻜﺎﻤﻠﺔ ‪.Integrated Security‬‬
‫ﻭﺘﻨﺎﻅﺭ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻤﻔﺘﺎﺡ ‪ Password‬ﺃﻭ ‪ pwd‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀـﻊ‬
‫ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ﻨﺼﺎ ﻓﺎﺭﻏﺎ‪.‬‬

‫ﻤﻌﺭﻑ ﺍﻟﺠﻬﺎﺯ ‪:WorkstationID‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﺍﻟﺠﻬﺎﺯ ﺍﻟﺫﻱ ﻴﺘﺼل ﺒﺎﻟﺨﺎﺩﻡ‪ ،‬ﻭﻫﻲ ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪Workstation ID‬‬
‫ﺃﻭ ‪ wsid‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ﻨﺼﺎ ﻓﺎﺭﻏﺎ‪.‬‬

‫‪٣٧‬‬
‫ﺇﺒﻘﺎﺀ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺴﺭﻴﺔ ‪:PersistSecurityInfo‬‬
‫ـﺹ‬
‫ـﻲ ﻨـ‬
‫ـﺎﺡ ‪ Persist Security Info‬ﺃﻭ ‪ persistsecurityinfo‬ﻓـ‬
‫ـﺎﻅﺭ ﺍﻟﻤﻔﺘـ‬
‫ﺘﻨـ‬
‫ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ‪ ،false‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﻋﻠﻴـﻙ ﺇﺭﺴـﺎل‬
‫ﺍﺴﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻭﻜﻠﻤﺔ ﺍﻟﺴﺭ ﻜﻠﻤﺎ ﺃﺭﺩﺕ ﻓﺘﺢ ﺍﻻﺘﺼﺎل‪ ..‬ﺃﻤﺎ ﻟـﻭ ﺠﻌﻠـﺕ ﻗﻴﻤﺘﻬـﺎ ‪،true‬‬
‫ﻓﻴﻤﻜﻨﻙ ﺇﺭﺴﺎل ﻫﺫﻩ ﺍﻟﻤﻌﻠﻭﻤﺎﺕ ﻋﻨﺩ ﻓﺘﺢ ﺍﻻﺘﺼﺎل ﻷﻭل ﻤﺭﺓ ﻓﻘﻁ‪ ،‬ﻭﺴﻴﺘﻡ ﺍﻻﺤﺘﻔﺎﻅ ﺒﻬـﺎ‬
‫ﻻﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﻓﺘﺢ ﺍﻻﺘﺼﺎل ﺒﻌﺩ ﻫﺫﺍ‪.‬‬

‫ﻨﻔﺎﺩ ﻭﻗﺕ ﺍﻻﺘﺼﺎل ‪:ConnectTimeout‬‬


‫ﺘﻤﺜل ﻭﻗﺕ ﺍﻻﻨﺘﻅﺎﺭ ﺍﻟﺫﻱ ﺴﺘﻌﺘﺒﺭ ﻤﺤﺎﻭﻟﺔ ﺍﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ ﻓﺎﺸـﻠﺔ ﺒﻌـﺩ ﻤـﺭﻭﺭﻩ ﺩﻭﻥ‬
‫ﺍﺴﺘﺠﺎﺒﺔ ﻤﻥ ﺍﻟﺨﺎﺩﻡ‪ ،‬ﻭﻫﻲ ﺘﻨـﺎﻅﺭ ﺍﻟﻤﻔﺘـﺎﺡ ‪ Connect Timeout‬ﺃﻭ ‪connection‬‬
‫‪ timeout‬ﺃﻭ ‪ timeout‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ‪١٥‬‬
‫ﺜﺎﻨﻴﺔ‪.‬‬

‫ﺍﺴﻡ ﺍﻟﺘﻁﺒﻴﻕ ‪:ApplicationName‬‬


‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ app‬ﺃﻭ ‪ Application Name‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼـﺎل‪ ..‬ﻭﻓـﻲ ﺍﻟﻭﻀـﻊ‬
‫ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ‪ ،.NET SqlClient Data Provider‬ﻟﻜﻥ ﺒﺈﻤﻜﺎﻨﻙ ﺃﻥ ﺘﻀﻊ‬
‫ﻓﻴﻬﺎ ﺍﺴﻡ ﺒﺭﻨﺎﻤﺠﻙ‪.‬‬

‫ﺍﻟﻠﻐﺔ ﺍﻟﺤﺎﻟﻴﺔ ‪:CurrentLanguage‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﺴﺠل ﺍﻟﻠﻐﺔ ﻓﻲ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪ ،‬ﻭﻫﻲ ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘـﺎﺡ ‪ language‬ﺃﻭ‬
‫‪ Current Language‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ﻨﺼﺎ‬
‫ﻓﺎﺭﻏﺎ ""‪.‬‬

‫‪٣٨‬‬
‫ﺍﻟﺘﺸﻔﻴﺭ ‪:Encrypt‬‬
‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ Encrypt‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜـﻭﻥ ﻗﻴﻤﺘﻬـﺎ‬
‫‪ false‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﻟﻥ ﻴﺸﻔﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺭﺴﻠﺔ ﺒﻴﻨﻪ ﻭﺒﻴﻥ ﺍﻟﻌﻤﻴل‪ ..‬ﻭﻟـﻭ‬
‫ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ ‪ ،true‬ﻓﺴﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻡ ﻨﻭﻉ ﻤﻥ ﺍﻟﺘﺸﻔﻴﺭ ﻴﺴﻤﻰ ‪ SSL Encryption‬ﺍﻟـﺫﻱ‬
‫ﻴﻌﻨﻲ "ﺘﺸﻔﻴﺭ ﻁﺒﻘﺔ ﻤﻘﺎﺒﺱ ﺍﻻﺘﺼﺎل ﺍﻵﻤﻨﺔ" ‪،Secure Sockets Layer Encryption‬‬
‫ﻭﻫﻭ ﻴﺴﺘﺨﺩﻡ ﻤﻔﺘﺎﺤﻴﻥ‪ :‬ﻤﻔﺘﺎﺤﺎ ﻋﺎﻤﺎ ‪ Public Key‬ﻴﺴﺘﺨﺩﻡ ﻟﺘﺸﻔﻴﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ،‬ﻭﻤﻔﺘﺎﺤـﺎ‬
‫ﺨﺎﺼﺎ ‪ Private Key‬ﻴﺴﺘﺨﺩﻡ ﻟﻔﻙ ﺘﺸﻔﻴﺭﻫﺎ‪.‬‬

‫ﺇﺠﺎﺯﺓ ﺨﺎﺩﻡ ﻤﻭﺜﻭﻕ ﺒﻪ ‪:TrustServerCertificate‬‬


‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ TrustServerCertificate‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼـﺎل‪ ،‬ﻭﺇﺫﺍ ﺠﻌﻠـﺕ ﻗﻴﻤﺘﻬـﺎ‬
‫‪ ،true‬ﻓﺴﻴﺘﻡ ﺘﺠﺎﻫل ﻋﻤﻠﻴﺔ ﺇﺠﺎﺯﺓ ﺍﻟﺨﺎﺩﻡ ‪ ،Certification‬ﺍﻜﺘﻔـﺎﺀ‪ ‬ﺒﺤﻤﺎﻴـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺒﺎﺴﺘﺨﺩﺍﻡ ﺘﺸﻔﻴﺭ ‪.SSL‬‬

‫ﺍﺘﺼﺎل ﺒﺎﻟﻤﺤﺘﻭﻯ ‪:ContextConnection‬‬


‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ Context Connection‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀـﻲ‬
‫ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ‪.false‬‬
‫ـﻲ‬
‫ـﺎﺩﻡ ﻤﺤﻠـ‬
‫ـﺎل ﺒﺨـ‬
‫ـﺩ ﺍﻻﺘﺼـ‬
‫ـﻴﺔ ‪ true‬ﻋﻨـ‬
‫ـﺫﻩ ﺍﻟﺨﺎﺼـ‬
‫ـﺔ ﻫـ‬
‫ـل ﻗﻴﻤـ‬
‫ـﺢ ﺒﺠﻌـ‬
‫ﻭﻴﻨﺼـ‬
‫‪ Local Server‬ﺘﻭﺠﺩ ﻋﻠﻴﻪ ﺍﻹﺠﺭﺍﺀﺍﺕ ﺍﻟﻤﺨﺯﻨﺔ ﻭﺩﻭﺍل ‪ T-SQL‬ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺘﻨﻔﻴـﺫﻫﺎ‪،‬‬
‫ﻷﻥ ﻫﺫﺍ ﻴﺠﻌﻠﻙ ﺘﺴﺘﺨﺩﻡ ﻨﻔﺱ ﻤﻭﺍﺭﺩ ﺍﻻﺘﺼﺎل ﺍﻟﺴﺎﺒﻕ ﺒﺎﻟﺨﺎﺩﻡ ﺍﻟﻤﺤﻠﻲ‪ ،‬ﻤﻤﺎ ﻴﻭﻓﺭ ﻋﻠﻴـﻙ‬
‫ﺇﻋﺎﺩﺓ ﺇﺩﺨﺎل ﺍﺴﻡ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻭﻜﻠﻤـﺔ ﺍﻟﺴـﺭ‪ ،‬ﻭﻴﺘـﻴﺢ ﻟـﻙ ﺍﻟﺘﻔﺎﻋـل ﻤـﻊ ﺍﻟﺘﻌـﺎﻤﻼﺕ‬
‫‪ Transactions‬ﺍﻟﺘﻲ ﻟﻡ ﻴﺘﻡ ﺤﻔﻅﻬﺎ ﺒﻌﺩ‪ ،‬ﻜﻤﺎ ﻴﺘﻴﺢ ﻟﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺠﺩﺍﻭل ﺍﻟﻤﺅﻗﺘﺔ ﺍﻟﺘﻲ ﺘﻡ‬
‫ﺇﻨﺸﺎﺅﻫﺎ ﻋﻠﻰ ﺍﻻﺘﺼﺎل ﺍﻟﻤﺤﻠﻲ‪ ..‬ﻭﺘﺅﺩﻱ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﺇﻟﻰ ﺃﺩﺍﺀ ﺃﻓﻀل ﻟﻠﺒﺭﻨﺎﻤﺞ‪ ،‬ﻷﻨﻬـﺎ‬
‫ﺘﺘﺠﺎﻫل ﺒﺭﻭﺘﻭﻜﻭﻻﺕ ﺍﻟﺸﺒﻜﺔ ﻭﻤﺭﺍﺤل ﻨﻘل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﺒﺭﻫﺎ‪ ،‬ﻭﺘﺘﻌﺎﻤل ﻤﺒﺎﺸﺭﺓ ﻤﻊ ﺍﻟﺨـﺎﺩﻡ‬
‫ﺍﻟﻤﺤﻠﻲ )ﻷﻨﻪ ﻴﻭﺠﺩ ﻋﻠﻰ ﻨﻔﺱ ﺍﻟﺠﻬﺎﺯ(‪ ،‬ﻤﻤﺎ ﻴﺠﻌل ﺍﻻﺘﺼﺎل ﺃﺴﺭﻉ ﻭﺃﻜﻔﺄ‪.‬‬

‫‪٣٩‬‬
‫ﻭﻴﻨﺼــﺢ ﺒﺠﻌــل ﻗﻴﻤــﺔ ﻫــﺫﻩ ﺍﻟﺨﺎﺼــﻴﺔ ‪ false‬ﻻﺴــﺘﺨﺩﺍﻡ ﺍﻻﺘﺼــﺎل ﺍﻟﻌــﺎﺩﻱ‬
‫‪ ،Regular Connection‬ﻭﺫﻟﻙ ﻋﻨﺩ ﺍﻻﺘﺼﺎل ﺒﺨﺎﺩﻡ ﺒﻌﻴﺩ )ﻏﻴـﺭ ﻤﺤﻠـﻲ( ‪Remote‬‬
‫‪.Server‬‬
‫ﻭﺍﻟﺸﻜل ﺍﻟﺘﺎﻟﻲ ﻴﻠﺨﺹ ﺍﻟﻔﺎﺭﻕ ﺒﻴﻥ ﻫﺫﻴﻥ ﺍﻟﻨﻭﻋﻴﻥ ﻤﻥ ﺍﻻﺘﺼﺎل‪ ..‬ﻻﺤـﻅ ﺃﻥ ﺍﻻﺘﺼـﺎل‬
‫ﺒﺎﻟﻤﺤﺘﻭﻯ ﻴﺘﺠﺎﻫل ﺍﻟﻌﺩﻴﺩ ﻤﻥ ﻁﺒﻘﺎﺕ ﺍﻻﺘﺼـﺎل ﻋﺒـﺭ ﺍﻟﺸـﺒﻜﺔ ‪،Network Layers‬‬
‫ﻭﻴﺴﺘﺨﺩﻡ ﻭﺍﺠﻬﺔ ﺍﻻﺘﺼﺎل ﺍﻟﻤﺒﺎﺸﺭ ‪.In-Process Interface‬‬

‫‪٤٠‬‬
‫ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ‪:Enlist‬‬
‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ Enlist‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀـﻲ ﺘﻜـﻭﻥ ﻗﻴﻤﺘﻬـﺎ‬
‫‪ ،true‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﺍﻻﺘﺼﺎل ﺍﻟﺤﺎﻟﻲ ﺴﻴﻭﻀﻊ ﻓﻲ ﻗﺎﺌﻤﺔ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻟﻤﺤﺘﻭﻯ‬
‫ﺍﻟﺘﻌﺎﻤﻼﺕ ﺍﻟﺤﺎﻟﻲ ‪ ..Current Transaction Context‬ﻫﺫﺍ ﻴﺴﻤﺢ ﺒﺠﻌل ﺃﻜﺜـﺭ ﻤـﻥ‬
‫ﺍﺘﺼﺎل ﺘﺘﺸﺎﺭﻙ ﻓﻲ ﺘﻌﺎﻤل ‪ Transaction‬ﻭﺍﺤﺩ‪ ،‬ﺒﺤﻴﺙ ﻟﻭ ﻓﺸﻠﺕ ﺃﻱ ﻋﻤﻠﻴﺔ ﻋﻠـﻰ ﺃﻱ‬
‫ﺍﺘﺼﺎل ﻤﻨﻬﺎ ﻴﺘﻡ ﺇﻟﻐﺎﺀ ﺍﻟﻌﻤﻠﻴﺎﺕ ﺍﻟﺘﻲ ﺘﻤﺕ ﻋﻠﻰ ﺒﺎﻗﻲ ﺍﻻﺘﺼـﺎﻻﺕ‪ ..‬ﻟـﻥ ﻨﺘﻌﻤـﻕ ﻓـﻲ‬
‫ﻤﻭﻀﻭﻉ ﺍﻟﺘﻌﺎﻤﻼﺕ ‪ Transactions‬ﻓﻲ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ‪ ،‬ﻭﺴـﻨﺘﻌﺭﻑ ﻋﻠﻴـﻪ ﻤـﻊ ﺒـﺎﻗﻲ‬
‫ﺍﻟﻤﻭﺍﻀﻴﻊ ﺍﻟﻤﺘﻘﺩﻤﺔ ﻓﻲ ﺒﺭﻤﺠﺔ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﻜﺘﺎﺏ ﺍﻟﻘﺎﺩﻡ ﺒﺈﺫﻥ ﺍﷲ‪.‬‬

‫ﻤﻌﺎﻟﺠﺔ ﻏﻴﺭ ﻤﺘﺯﺍﻤﻨﺔ ‪:AsynchronousProcessing‬‬


‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ async‬ﺃﻭ ‪ Asynchronous Processing‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓـﻲ‬
‫ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ‪ ،false‬ﻭﻟﻭ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ ‪ true‬ﻓﺴﻴﻌﻨﻲ ﻫﺫﺍ ﺍﻟﺴـﻤﺎﺡ‬
‫ﻟﻠﺨﺎﺩﻡ ﺒﺈﺠﺭﺍﺀ ﻋﻤﻠﻴﺎﺕ ﻤﻌﺎﻟﺠﺔ ﻏﻴﺭ ﻤﺘﺯﺍﻤﻨﺔ‪ ..‬ﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﺒﺭﻨﺎﻤﺠﻙ ﺴﻴﻭﺍﺼل ﺍﻟﻌﻤـل‬
‫ﻤﺒﺎﺸﺭﺓ ﺒﻌﺩ ﺇﺭﺴﺎل ﺍﻻﺴﺘﻌﻼﻡ ﺇﻟﻰ ﺍﻟﺨﺎﺩﻡ‪ ،‬ﺘﺎﺭﻜﺎ ﺍﻟﺨﺎﺩﻡ ﻴﻭﺍﺼل ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ‪ ..‬ﻫـﺫﺍ‬
‫ﻴﻭﻓﺭ ﻋﻠﻴﻙ ﻜﺘﺎﺒﺔ ﺍﻟﻜﺜﻴﺭ ﻤﻥ ﺍﻟﻜﻭﺩ ﻹﻨﺸﺎﺀ ﻋﻤﻠﻴﺎﺕ ﻓﺭﻋﻴﺔ ‪ Threads‬ﺃﻭ ﻋﻤﻠﻴﺎﺕ ﻏﻴـﺭ‬
‫ﻤﺘﺯﺍﻤﻨﺔ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻙ ﻟﻀﻤﺎﻥ ﻤﻭﺍﺼﻠﺔ ﺍﻻﺴﺘﺠﺎﺒﺔ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺃﺜﻨـﺎﺀ ﻤﻌﺎﻟﺠـﺔ ﺴـﻴﻜﻭﻴل‬
‫ﺴﻴﺭﻓﺭ ﻟﻼﺴﺘﻌﻼﻤﺎﺕ ﺍﻟﺴﺎﺒﻘﺔ‪.‬‬

‫ﻤﺴﺎﻫﻤﺔ ‪:Pooling‬‬
‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ Pooling‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜـﻭﻥ ﻗﻴﻤﺘﻬـﺎ‬
‫‪ ،true‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﻫﺫﺍ ﺍﻻﺘﺼـﺎل ﺴﻴﻨﻀـﻡ ﺇﻟـﻰ ﺭﺼـﻴﺩ ﺍﻻﺘﺼـﺎﻻﺕ ﺍﻟﻤﺴـﺎﻫﻤﺔ‬
‫‪ Connection Pool‬ﺍﻟﺘﻲ ﺘﻅل ﻤﻔﺘﻭﺤﺔ ﺩﺍﺌﻤﺎ ﻻﺴﺘﺨﺩﺍﻤﻪ ﻓﻭﺭ ﺍﻟﺤﺎﺠﺔ ﺇﻟﻴﻬﺎ‪ ..‬ﺃﻤـﺎ ﻟـﻭ‬
‫ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ ‪ false‬ﻓﺴﻴﻌﻨﻲ ﻫﺫﺍ ﺃﻥ ﻫﺫﺍ ﺍﻻﺘﺼﺎل ﺴﻴﺘﻡ ﻓﺘﺤﻪ ﻭﺇﻏﻼﻗـﻪ ﻤﺒﺎﺸـﺭﺓ ﺒﻌـﺩ‬
‫ﺍﻨﺘﻬﺎﺀ ﺍﺴﺘﺨﺩﺍﻤﻪ‪ ،‬ﻭﻋﻨﺩ ﺍﻻﺤﺘﻴﺎﺝ ﺇﻟﻴﻪ ﻤﺠﺩﺩﺍ ﻴﺘﻡ ﻓﺘﺤﻪ ﻤﻥ ﺠﺩﻴﺩ‪ ..‬ﻭﻫﻜﺫﺍ‪.‬‬

‫‪٤١‬‬
‫ﺃﻗﺼﻰ ﺤﺠﻡ ﻟﻠﻤﺴﺎﻫﻤﺔ ‪:MaxPoolSize‬‬
‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ Max Pool Size‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜـﻭﻥ‬
‫ﻗﻴﻤﺘﻬﺎ ‪ ،١٠٠‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺍﻻﺤﺘﻔﺎﻅ ﻓﻲ ﺭﺼﻴﺩ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺴﺎﻫﻤﺔ ﺍﻟﻤﺘﺎﺤﺔ ﻟﻼﺴـﺘﺨﺩﺍﻡ‪،‬‬
‫ﺒﻤﺌﺔ ﺍﺘﺼﺎل ـ ﻜﺤﺩ ﺃﻗﺼﻰ ـ ﻤﻔﺘﻭﺤﺔ ﺒﻴﻥ ﺍﻟﺨﺎﺩﻡ ﻭﺍﻟﻌﻤﻴل‪.‬‬

‫ﺃﻗل ﺤﺠﻡ ﻟﻠﻤﺴﺎﻫﻤﺔ ‪:MinPoolSize‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺃﺼﻐﺭ ﻋﺩﺩ ﻤﻥ ﺍﻻﺘﺼﺎﻻﺕ ﻴﺠﺏ ﺃﻥ ﻴﻅل ﻤﻔﺘﻭﺤﺎ ﺒﻴﻥ ﺍﻟﺨﺎﺩﻡ ﻭﺍﻟﻌﻤﻴل ﻓﻲ‬
‫ﺭﺼﻴﺩ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺴﺎﻫﻤﺔ‪ ،‬ﻭﻫـﻲ ﺘﻨـﺎﻅﺭ ﺍﻟﻤﻔﺘـﺎﺡ ‪ Min Pool Size‬ﻓـﻲ ﻨـﺹ‬
‫ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ﺼﻔﺭﺍ‪.‬‬

‫ﻭﻗﺕ ﺍﻨﺘﻅﺎﺭ ﺘﻭﺍﺯﻥ ﺍﻟﺤﻤل ‪:LoadBalanceTimeout‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﻭﻗﺕ ﺒﺎﻟﺜﺎﻨﻴﺔ‪ ،‬ﺍﻟﺫﻱ ﻴﺘﻡ ﺍﻨﺘﻅﺎﺭﻩ ﺃﺜﻨـﺎﺀ ﻭﺠـﻭﺩ ﺍﻻﺘﺼـﺎل ﻓـﻲ ﺭﺼـﻴﺩ‬
‫ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺴﺎﻫﻤﺔ ‪ ،Connection Pool‬ﻗﺒل ﺃﻥ ﻴـﺘﻡ ﺇﻏـﻼﻕ ﺍﻻﺘﺼـﺎل‪ ،‬ﻭﺫﻟـﻙ‬
‫ﻟﻀﻤﺎﻥ ﻋﺩﻡ ﺘﺭﻙ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﻔﺘﻭﺤﺔ ﺨﺎﻤﻠﺔ ﺒﺩﻭﻥ ﺍﺴﺘﺨﺩﺍﻡ ﻟﻔﺘﺭﺍﺕ ﻁﻭﻴﻠـﺔ‪ ..‬ﻭﻓـﻲ‬
‫ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ﺼﻔﺭﺍ‪ ،‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺘﺭﻙ ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺤﺎ ﺩﺍﺌﻤـﺎ ﺒـﺩﻭﻥ‬
‫ﻗﻴـــﻭﺩ‪ ..‬ﻭﺘﻨـــﺎﻅﺭ ﻫـــﺫﻩ ﺍﻟﺨﺎﺼـــﻴﺔ ﺍﻟﻤﻔﺘـــﺎﺡ ‪connection lifetime‬‬
‫ﺃﻭ ‪ Load Balance Timeout‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪.‬‬

‫ﺇﻋﺎﺩﺓ ﺍﻻﺘﺼﺎل ﺇﻟﻰ ﻭﻀﻌﻪ ﺍﻷﺼﻠﻲ ‪:ConnectionReset‬‬


‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ Connection Reset‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀـﻊ ﺍﻻﻓﺘﺭﺍﻀـﻲ‬
‫ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ‪ ،true‬ﻭﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﺍﻻﺘﺼﺎل ﺴﻴﻌﻭﺩ ﺇﻟﻰ ﻭﻀﻌﻪ ﺍﻷﺼـﻠﻲ ﻋﻨـﺩ ﻁﻠـﺏ‬
‫ﺍﺴﺘﺨﺩﺍﻤﻪ ﻤﻥ ﺭﺼﻴﺩ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺴﺎﻫﻤﺔ ‪.Connection Pool‬‬

‫‪٤٢‬‬
‫ﻤﺠﻤﻭﻋﺎﺕ ﺍﻟﻨﺘﺎﺌﺞ ﺍﻟﻔﻌﺎﻟﺔ ﺍﻟﻤﺘﻌﺩﺩﺓ‪:MultipleActiveResultSets‬‬
‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ MultipleActiveResultSets‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼـﺎل‪ ..‬ﻭﻓـﻲ ﺍﻟﻭﻀـﻊ‬
‫ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ‪ ،false‬ﻭﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺍﺴـﺘﺨﺩﺍﻡ "ﻤﺠﻤﻭﻋـﺔ ﺍﻟﻨﺘـﺎﺌﺞ ﺍﻟﻌﺎﺩﻴـﺔ"‬
‫‪ ،Default Result Set‬ﻭﻓﻴﻬﺎ ﻴﺘﻡ ﺇﺭﺴﺎل ﻨﺘﺎﺌﺞ ﺍﻻﺴﺘﻌﻼﻡ ﻤﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﺇﻟﻰ ﺠﻬﺎﺯ‬
‫ﺍﻟﻌﻤﻴل‪ ،‬ﺤﻴﺙ ﻴﺘﻡ ﺤﻔﻅﻬﺎ ﻓﻲ ﻤﺨﺯﻥ ﻭﺴﻴﻁ ‪ Buffer‬ﻓـﻲ ﺍﻟـﺫﺍﻜﺭﺓ‪ ،‬ﻭﻋﻨـﺩﻤﺎ ﻴﺤﺘـﺎﺝ‬
‫ﺒﺭﻨﺎﻤﺠﻙ ﺇﻟﻰ ﻋﺭﻀﻬﺎ ﻟﻠﻤﺴﺘﺨﺩﻡ‪ ،‬ﻴﺘﻡ ﺍﻟﻤﺭﻭﺭ ﻋﺒﺭﻫﺎ ﺴﺠﻼ ﺒﺴﺠل‪ ..‬ﻭﻻ ﻴﺴﺘﻁﻴﻊ ﺍﻟﻌﻤﻴل‬
‫ﺍﺴﺘﺨﺩﺍﻡ ﺍﻻﺘﺼﺎل ﺍﻟﻤﻔﺘﻭﺡ ﻤﻊ ﺍﻟﺨﺎﺩﻡ ﻓﻲ ﺘﺤﺩﻴﺙ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻗﺒل ﺃﻥ ﻴﻨﺘﻬﻲ ﻤﻥ ﺍﻟﺘﻌﺎﻤل ﻤﻊ‬
‫ﻜل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺃﺭﺴﻠﻬﺎ ﺍﻟﺨﺎﺩﻡ ﺃﻭﻻ‪ ،‬ﺃﻭ ﻗﺒل ﺃﻥ ﻴﺭﺴل ﺇﻟﻰ ﺍﻟﺨﺎﺩﻡ ﻁﻠﺒﺎ ﻹﻟﻐﺎﺀ ﺇﺭﺴـﺎل‬
‫ﺒﺎﻗﻲ ﺍﻟﻨﺘﺎﺌﺞ‪ ..‬ﻭﺘﻌﺘﺒﺭ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﺃﻜﺜﺭ ﻜﻔﺎﺀﺓ ﻓﻲ ﺍﺴﺘﻐﻼل ﺍﻻﺘﺼﺎل‪ ،‬ﻷﻥ ﺍﻟﺨﺎﺩﻡ ﻴﺭﺴل‬
‫ﺃﻜﺒﺭ ﻜﻡ ﻤﻤﻜﻥ ﻤﻥ ﺍﻟﻨﺘﺎﺌﺞ ﻋﺒﺭ ﺤـﺯﻡ ﺍﻟﺒﻴﺎﻨـﺎﺕ ‪ Packets‬ﺍﻟﻤﺭﺴـﻠﺔ ﻋﺒـﺭ ﺍﻟﺸـﺒﻜﺔ‬
‫‪.Network‬‬
‫ﻭﻟﻭ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،true‬ﻓﺴﻴﺘﻡ ﺍﺴـﺘﺨﺩﺍﻡ "ﻤﺠﻤﻭﻋـﺎﺕ ﺍﻟﻨﺘـﺎﺌﺞ ﺍﻟﻔﻌﺎﻟـﺔ‬
‫ﺍﻟﻤﺘﻌﺩﺩﺓ" ‪ Multiple Active Result Sets‬ﺃﻭ ﺍﺨﺘﺼﺎﺭﺍ ‪ ،MARS‬ﻭﻫﻲ ﻤﺘﺎﺤﺔ ﻓﻘﻁ‬
‫ﻤﻊ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ‪ ٢٠٠٥‬ﻭﻤﺎ ﻴﻠﻴﻪ ﻤﻥ ﺇﺼﺩﺍﺭﺍﺕ‪ ،‬ﻭﻓﻴﻬﺎ ﻴﺴﻤﺢ ﻟﻠﻌﻤﻴل ﺒﺎﺴﺘﺨﺩﺍﻡ ﺃﻜﺜـﺭ‬
‫ﻤﻥ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ‪ SqlDataReader‬ﻓﻲ ﻨﻔﺱ ﺍﻟﻭﻗﺕ‪.‬‬

‫ﻤﻜﺘﺒﺔ ﺍﻟﺸﺒﻜﺔ ‪:NetworkLibrary‬‬


‫ﻀﻊ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﺴﻡ ﻤﻜﺘﺒﺔ ﺍﻟﺭﺒﻁ ‪ DLL‬ﺍﻟﺘﻲ ﺘﺭﻴـﺩ ﻤـﻥ ﺍﻟﺨـﺎﺩﻡ ﺍﺴـﺘﺨﺩﺍﻤﻬﺎ‬
‫ﻟﻼﺘﺼﺎل ﻋﺒﺭ ﺍﻟﺸﺒﻜﺔ‪ ،‬ﻭﺫﻟﻙ ﺒﺸﺭﻁ ﺘﻭﻓﺭ ﻫﺫﻩ ﺍﻟﻤﻜﺘﺒﺔ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ‪.‬‬
‫ﻭﺘﻨﺎﻅﺭ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻤﻔﺘﺎﺡ ‪ Network Library‬ﺃﻭ ‪ network‬ﺃﻭ ‪ net‬ﻓـﻲ ﻨـﺹ‬
‫ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ﻨﺼﺎ ﻓﺎﺭﻏﺎ‪ ..‬ﻭﺍﻟﺠﺩﻭل ﺍﻟﺘﺎﻟﻲ ﻴﻭﻀـﺢ‬
‫ﺍﻟﻘﻴﻡ ﺍﻟﻤﺤﺘﻤﻠﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪:‬‬

‫ﻨﻭﻉ ﺍﻻﺘﺼﺎل‬ ‫ﺍﺴﻡ ﻤﻜﺘﺒﺔ ﺍﻟﺭﺒﻁ‬


‫‪Named Pipes‬‬ ‫‪dbnmpntw‬‬
‫‪Multiprotocol‬‬ ‫‪dbmsrpcn‬‬

‫‪٤٣‬‬
‫‪AppleTalk‬‬ ‫‪dbmsadsn‬‬
‫‪VIA‬‬ ‫‪dbmsgnet‬‬
‫‪Shared Memory‬‬ ‫‪dbmslpcn‬‬
‫‪IPX/SPX‬‬ ‫‪dbmsspxn‬‬
‫‪TCP/IP‬‬ ‫‪dbmssocn‬‬

‫ﻭﻓﻲ ﺤﺎﻟﺔ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺨﺎﺩﻡ ﻤﺤﻠﻲ ﻭﺘﺭﻙ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻓﺎﺭﻏـﺔ‪ ،‬ﻴـﺘﻡ ﺍﺴـﺘﺨﺩﺍﻡ‬
‫ﺍﻟﻤﻜﺘﺒﺔ ‪.(Shared Memory) dbmslpcn‬‬

‫ﺤﺠﻡ ﺤﺯﻤﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:PacketSize‬‬


‫ﻋﻨﺩ ﺇﺭﺴﺎل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﺒﺭ ﺍﻟﺸﺒﻜﺔ ﺃﻭ ﺍﻹﻨﺘﺭﻨﺕ‪ ،‬ﻴﺘﻡ ﺘﻘﺴﻴﻤﻬﺎ ﺇﻟﻰ ﺤﺯﻡ ‪ ..Packets‬ﻭﺘﺤﺩﺩ‬
‫ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺤﺠﻡ ﻜل ﺤﺯﻤﺔ ﻤﻥ ﻫﺫﻩ ﺍﻟﺤﺯﻡ ﺒﺎﻟﻭﺤﺩﺓ ﺍﻟﺜﻨﺎﺌﻴـﺔ ‪ ،Byte‬ﻭﻓـﻲ ﺍﻟﻭﻀـﻊ‬
‫ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤﺘﻬﺎ ‪ ٨٠٠٠‬ﻭﺤﺩﺓ ‪.Byte‬‬
‫ﻭﺘﻨﺎﻅﺭ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻤﻔﺘﺎﺡ ‪ Packet Size‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪.‬‬

‫ﺍﻟﻨﺴﺦ ﺍﻟﻤﻁﺎﺒﻕ ‪:Replication‬‬


‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ Replication‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀـﻊ ﺍﻻﻓﺘﺭﺍﻀـﻲ ﺘﻜـﻭﻥ‬
‫ﻗﻴﻤﺘﻬﺎ ‪ ،false‬ﻭﺇﺫﺍ ﺠﻌﻠﺘﻬﺎ ‪ true‬ﻓﺴﻴﺘﻡ ﺘﻤﻜﻴﻥ ﻋﻤﻠﻴﺔ ﺍﻟﻨﺴـﺦ ﺍﻟﻤﻁـﺎﺒﻕ ‪Replication‬‬
‫ﻋﺒﺭ ﻫﺫﺍ ﺍﻻﺘﺼﺎل‪ ،‬ﻭﻫﻲ ﺘﻘﻨﻴﺔ ﺁﻟﻴﺔ ﺘﺘﻴﺢ ﻟﻙ ﻨﺴﺦ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺒﻴﻥ ﺃﻜﺜـﺭ ﻤـﻥ ﺨـﺎﺩﻡ‪،‬‬
‫ﻭﺇﺒﻘﺎﺀ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺘﺯﺍﻤﻨﺔ ﺒﻴﻥ ﺍﻟﺨﺎﺩﻤﻴﻥ‪ ،‬ﺒﺤﻴﺙ ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﺇﺤﺩﻯ ﻗﺎﻋـﺩﺘﻲ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺇﺫﺍ‬
‫ﺤﺩﺙ ﺘﻐﻴﻴﺭ ﻓﻲ ﺍﻷﺨﺭﻯ‪ ..‬ﻫﺫﺍ ﻤﻔﻴﺩ ﻋﻨﺩﻤﺎ ﻴﻜﻭﻥ ﻫﻨﺎﻙ ﺨﺎﺩﻡ ﺃﺼﻠﻲ ﻭﺨـﺎﺩﻡ ﺍﺤﺘﻴـﺎﻁﻲ‬
‫ﻟﻠﺘﻌﺎﻤل ﻤﻌﻪ ﺇﺫﺍ ﺤﺩﺜﺕ ﻤﺸﻜﻠﺔ ﻓﻲ ﺍﻟﺨﺎﺩﻡ ﺍﻷﺼﻠﻲ ﺃﻭ ﺘﻡ ﺇﻴﻘﺎﻓﻪ ﻟﻠﺼﻴﺎﻨﺔ ﻤﺜﻼ‪.‬‬

‫ﺭﺒﻁ ﺍﻟﺘﻌﺎﻤﻼﺕ ‪:TransactionBinding‬‬


‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ Transaction Binding‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻀـﻊ ﻓﻴﻬـﺎ‬
‫ﺇﺤﺩﻯ ﺍﻟﻘﻴﻤﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫‪٤٤‬‬
‫ﻓﻙ ﺍﺭﺘﺒﺎﻁ ﻀﻤﻨﻲ‪ :‬ﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀـﻴﺔ‪ ،‬ﻭﻓﻴﻬـﺎ ﻴـﺅﺩﻱ‬ ‫‪Implicit‬‬
‫‪Unbind‬‬
‫‪Current‬‬ ‫ﺇﻏﻼﻕ ﺍﻻﺘﺼﺎل ﺇﻟﻰ ﻓﺼﻠﻪ ﻋﻥ ﺍﻟﺘﻌﺎﻤﻼﺕ ﺍﻟﺠﺎﺭﻴﺔ‬
‫‪.Transactions‬‬
‫ﻓﻙ ﺍﺭﺘﺒﺎﻁ ﺼﺭﻴﺢ‪ :‬ﻴﺠﺏ ﻋﻠﻴﻙ ﻓﻙ ﺍﻻﺭﺘﺒﺎﻁ ﺒـﻴﻥ ﺍﻻﺘﺼـﺎل‬ ‫‪Explicit‬‬
‫‪Unbind‬‬
‫ﻭﺍﻟﺘﻌﺎﻤﻼﺕ ﺍﻟﺠﺎﺭﻴﺔ ﺒﻁﺭﻴﻘﺔ ﺼﺭﻴﺤﺔ ﻗﺒل ﺇﻏـﻼﻕ ﺍﻻﺘﺼـﺎل‪،‬‬
‫ﻭﺇﻻ ﺤﺩﺙ ﺨﻁﺄ‪.‬‬

‫ﺇﺼﺩﺍﺭ ﻨﻅﺎﻡ ﺍﻷﻨﻭﺍﻉ ‪:TypeSystemVersion‬‬


‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ Type System Version‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ،‬ﻭﻫﻲ ﺘﺘﻴﺢ ﻟـﻙ ﺘﺤﺩﻴـﺩ‬
‫ﺇﺼﺩﺍﺭ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺃﻥ ﺘﺴﺘﺨﺩﻡ ﺃﻨﻭﺍﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺨﺎﺼﺔ ﺒﻪ‪ ..‬ﻋﻠﻰ ﺴـﺒﻴل‬
‫ﺍﻟﻤﺜﺎل‪ ،‬ﻟﻭ ﻜﺎﻥ ﺍﻟﺨـﺎﺩﻡ ﻴﺴـﺘﺨﺩﻡ ﺴـﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ ‪ ،٢٠٠٨‬ﻭﺠﻌﻠـﺕ ﻗﻴﻤـﺔ ﻫـﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ‪ ،SQL Server 2000‬ﻓﻴﻤﻜﻨـﻙ ﺍﺴـﺘﺨﺩﺍﻡ ﺃﻨـﻭﺍﻉ ﺍﻟﺒﻴﺎﻨـﺎﺕ ‪Data Types‬‬
‫ﺍﻟﺨﺎﺼﺔ ﺒﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ‪ ،٢٠٠٠‬ﺤﻴﺙ ﺴـﻴﻘﻭﻡ ﺴـﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ ‪ ٢٠٠٨‬ﺒـﺈﺠﺭﺍﺀ‬
‫ﺍﻟﺘﺤﻭﻴﻼﺕ ﺍﻟﻤﻨﺎﺴﺒﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻌﻬﺎ‪ ..‬ﻭﺍﻟﺠﺩﻭل ﺍﻟﺘﺎﻟﻲ ﻴﻠﺨﺹ ﻟﻙ ﺍﻟﻘـﻴﻡ ﺍﻟﻤﻤﻜﻨـﺔ ﻟﻬـﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ‪:‬‬

‫ﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺨﺎﺼﻴﺔ ﺒﺴﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ ‪ ..٢٠٠٠‬ﻫـﺫﺍ‬ ‫‪SQL Server‬‬


‫‪2000‬‬
‫ﻴﻌﻨﻲ ﺇﺠﺭﺍﺀ ﺒﻌﺽ ﺍﻟﺘﺤﻭﻴﻼﺕ ﻋﻨﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺇﺼﺩﺍﺭﺍﺕ ﺃﺤﺩﺙ‪،‬‬
‫ﻤﺜل‪:‬‬
‫‪ -‬ﺘﺤﻭﻴل ‪ XML‬ﺇﻟﻰ ‪.NTEXT‬‬
‫‪ -‬ﺘﺤﻭﻴل ‪ UDT‬ﺇﻟﻰ ‪.VARBINARY‬‬
‫‪ -‬ﺘﺤﻭﻴل )‪ VARCHAR(MAX‬ﺇﻟﻰ ‪.TEXT‬‬
‫‪ -‬ﺘﺤﻭﻴل )‪ NVARCHAR(MAX‬ﺇﻟﻰ ‪.NEXT‬‬
‫‪ -‬ﺘﺤﻭﻴل )‪ ARBINARY(MAX‬ﺇﻟﻰ ‪.IMAGE‬‬
‫ﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺨﺎﺼﻴﺔ ﺒﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ‪.٢٠٠٥‬‬ ‫‪SQL Server‬‬
‫‪2005‬‬

‫‪٤٥‬‬
‫ﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺨﺎﺼﻴﺔ ﺒﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ‪.٢٠٠٨‬‬ ‫‪SQL Server‬‬
‫‪2008‬‬
‫ﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻡ ﺃﺤﺩﺙ ﺇﺼﺩﺍﺭ ﻤﻥ ﺴﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ ﻴﻤﻜـﻥ ﻟﻠﺨـﺎﺩﻡ‬ ‫‪Latest‬‬
‫ﻭﺍﻟﻌﻤﻴل ﺍﻟﺘﻌﺎﻤل ﻤﻌﻪ‪.‬‬

‫ﻨﺴﺨﺔ ﺍﻟﻤﺴﺘﺨﺩﻡ ‪:UserInstance‬‬


‫ﺘﻨﺎﻅﺭ ﺍﻟﻤﻔﺘﺎﺡ ‪ User Instance‬ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀـﻲ ﺘﻜـﻭﻥ‬
‫ﻗﻴﻤﺘﻬﺎ ‪ ،false‬ﻭﻟﻭ ﺠﻌﻠﺘﻬﺎ ‪ true‬ﻓﺴﻴﺘﻡ ﺘﻭﺠﻴﻪ ﺍﻻﺘﺼﺎل ﻤـﻥ ﻨﺴـﺨﺔ ﺨـﺎﺩﻡ ﺴـﻴﻜﻭﻴل‬
‫ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪ ،‬ﺇﻟﻰ ﻨﺴﺨﺔ ﺃﺨﺭﻯ ﻤﺨﺼﺼﺔ ﻟﻠﻌﻤﻴل‪ ،‬ﻟﻜﻥ ﻫﺫﺍ ﻗـﺩ ﻴﺴـﺒﺏ ﺃﺨﻁـﺎﺀ ﻓـﻲ‬
‫ﺍﻻﺘﺼﺎل ﺇﺫﺍ ﻜﻨﺕ ﺘﺴﺘﺨﺩﻡ ﺍﻟﺴﻤﺔ ‪ FILESTREAM‬ﻟﺤﻔﻅ ﺒﻴﺎﻨﺎﺕ ﺒﻌﺽ ﺍﻷﻋﻤﺩﺓ ﻓـﻲ‬
‫ﻤﻠﻔﺎﺕ ﺨﺎﺭﺠﻴﺔ‪.‬‬

‫ﻭﻻ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﻴﺔ ﻭﺴﺎﺌل ‪ 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‬ﺍﻟﺘﻲ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻓﻲ ﻜﺘﺎﺏ ﺒﺭﻤﺠﺔ ﺍﻟﻭﻴﻨﺩﻭﺯ‪.‬‬
‫ﻭﻻ ﺠﺩﻴﺩ ﻓﻲ ﻫﺫﻩ ﺍﻟﻔﺌﺔ‪ ،‬ﺴﻭﻯ ﺍﻤﺘﻼﻜﻬﺎ ﻟﻠﺨﺎﺼﻴﺔ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻨﺼﻭﺹ ﺍﻻﺘﺼﺎل ‪:ConnectionStrings‬‬


‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،ConnectionStringSettingsCollection‬ﺍﻟﺘـﻲ ﺘـﺭﺙ‬
‫ﺍﻟﻔﺌﺔ ‪ ،ConfigurationElementCollection‬ﻭﻜـل ﻋﻨﺼـﺭ ﻤـﻥ ﻋﻨﺎﺼـﺭ ﻫـﺫﻩ‬
‫ﺍﻟﻤﺠﻤﻭﻋﺔ ﻫﻭ ﻤﻥ ﻨﻭﻉ ﻓﺌﺔ ﺇﻋﺩﺍﺩﺍﺕ ﻨﺹ ﺍﻻﺘﺼـﺎل ‪ConnectionStringSettings‬‬
‫‪ ،Class‬ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﺒﻌﺩ ﻗﻠﻴل‪.‬‬
‫ﻭﺘﺘﻴﺢ ﻟﻙ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﻗﺭﺍﺀﺓ ﻜل ﻨﺼﻭﺹ ﺍﻻﺘﺼﺎل ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻤﻠﻑ ﺍﻹﻋﺩﺍﺩﺍﺕ‪.‬‬

‫ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺇﺤﺩﻯ ﻭﺴﺎﺌل ﻓﺘﺢ ﺍﻟﺘﻬﻴﺌـﺔ ‪ OpenxxConfiguration‬ﺍﻟﺨﺎﺼـﺔ ﺒﻤـﺩﻴﺭ‬


‫ﺍﻟﺘﻬﻴﺌﺔ ‪ ConfigurationManager‬ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻜﺎﺌﻥ ﺘﻬﻴﺌـﺔ ‪Configuration Object‬‬
‫ﻴﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻨﻭﻉ ﺍﻟﻤﺭﺍﺩ ﻤﻥ ﺍﻹﻋﺩﺍﺩﺍﺕ‪ ،‬ﺜﻡ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼﻴﺔ ‪ ConnectionStrings‬ﻟﻜﺎﺌﻥ‬
‫ﺍﻟﺘﻬﻴﺌﺔ ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﻔﺌﺔ ‪ ConnectionStringsSection‬ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫‪var Cnfg = ConfigurationManager.‬‬
‫;) (‪OpenMachineConfiguration‬‬
‫;‪var CnStrSett = Cnfg.ConnectionStrings‬‬

‫‪٥٠‬‬
‫ﻓﺌﺔ ﺇﻋﺩﺍﺩﺍﺕ ﻨﺹ ﺍﻻﺘﺼﺎل‬
‫‪ConnectionStringSettings Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﻓﺌﺔ ﻋﻨﺼﺭ ﺍﻟﺘﻬﻴﺌﺔ ‪ ،ConfigurationElement Class‬ﺍﻟﺘﻲ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬـﺎ‬
‫ﻓﻲ ﻜﺘﺎﺏ ﺒﺭﻤﺠﺔ ﻨﻤﺎﺫﺝ ﺍﻟﻭﻴﻨﺩﻭﺯ‪.‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺜﻼﺙ ﺼﻴﻎ‪:‬‬
‫‪ -١‬ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ‪ :‬ﺍﺴﻡ ﺨﺎﺼﻴﺔ ﺍﻹﻋﺩﺍﺩ ﺍﻟﺘﻲ ﺴﺘﺤﻔﻅ ﻨﺹ ﺍﻻﺘﺼـﺎل‪ ،‬ﻭﻨـﺹ‬
‫ﺍﻻﺘﺼﺎل ﻨﻔﺴﻪ‪.‬‬
‫‪ -٣‬ﻭﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻟـﺙ‪ ،‬ﻴﺴـﺘﻘﺒل ﺍﺴـﻡ ﻤـﺯﻭﺩ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫‪ Provider‬ﺍﻟﺫﻱ ﺴﻴﺴﺘﺨﺩﻡ ﻨﺹ ﺍﻻﺘﺼﺎل‪.‬‬
‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻻﺴﻡ ‪:Name‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﺨﺎﺼﻴﺔ ﺍﻹﻋﺩﺍﺩ ﺍﻟﺘﻲ ﺴﺘﺤﻔﻅ ﻨﺹ ﺍﻻﺘﺼﺎل‪.‬‬

‫ﻨﺹ ﺍﻻﺘﺼﺎل ‪:ConnectionString‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻨﺹ ﺍﻻﺘﺼﺎل ﺍﻟﻤﺤﻔﻭﻅ ﻓﻲ ﺨﺎﺼﻴﺔ ﺍﻹﻋﺩﺍﺩ‪.‬‬

‫ﺍﺴﻡ ﺍﻟﻤﺯﻭﺩ ‪:ProviderName‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺴﻴﺴﺘﺨﺩﻡ ﻨﺹ ﺍﻻﺘﺼﺎل‪.‬‬

‫ﻭﻴﻤﻜﻨﻙ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﺇﻋﺩﺍﺩﺍﺕ ﻨﺼﻭﺹ ﺍﻻﺘﺼﺎل ﺍﻟﺨﺎﺼﺔ ﺒـﺎﻟﺘﻁﺒﻴﻕ‪ ،‬ﺒﺎﺴـﺘﺨﺩﺍﻡ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻤﺸﺘﺭﻜﺔ ‪ Static Property‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪٥١‬‬
‫;‪var CnStrSett = ConfigurationManager.ConnectionStrings‬‬

‫ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﺽ ﻟﻙ ﻜل ﻨﺼﻭﺹ ﺍﻻﺘﺼﺎل ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻤﻠﻑ ﺇﻋﺩﺍﺩ ﺍﻟﺘﻁﺒﻴﻕ‪:‬‬


‫;‪var CnStrSett = ConfigurationManager.ConnectionStrings‬‬
‫)‪foreach (ConnectionStringSettings CnStr in CnStrSett‬‬
‫{‬
‫;)‪MessageBox.Show(CnStr.Name‬‬
‫;)‪MessageBox.Show(CnStr.ProviderName‬‬
‫;)‪MessageBox.Show(CnStr.ConnectionString‬‬
‫}‬

‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ﻴﺠﺏ ﻋﻠﻴﻙ ﺤﻤﺎﻴﺔ ﻨﺹ ﺍﻻﺘﺼﺎل ﺒﺘﺸﻔﻴﺭﻩ‪ ،‬ﻭﺫﻟﻙ ﻷﻥ ﻤﻠﻑ ﺍﻹﻋـﺩﺍﺩﺍﺕ ﻴـﺘﻡ ﺘﻭﺯﻴﻌـﻪ ﻤـﻊ‬
‫ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ،‬ﻤﻤﺎ ﻴﺠﻌل ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻗﺎﺩﺭﻴﻥ ﻋﻠﻰ ﻗﺭﺍﺀﺘﻪ ﻭﺃﺨﺫ ﻜﻠﻤﺎﺕ ﺍﻟﻤﺭﻭﺭ ﻤﻨﻪ‪.‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺘﺸﻔﻴﺭ ﻤﻘﻁﻊ ﻨﺼﻭﺹ ﺍﻻﺘﺼﺎل >‪ <ConnectionStrings‬ﻓﻲ ﻤﻠـﻑ ﺍﻹﻋـﺩﺍﺩﺍﺕ‪،‬‬
‫ﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺸﺭﺤﻨﺎﻫﺎ ﻓﻲ ﻜﺘﺎﺏ ﺒﺭﻤﺠـﺔ ﺍﻟﻭﻴﻨـﺩﻭﺯ‪ ،‬ﻭﺍﺴـﺘﺨﺩﻤﻨﺎﻫﺎ ﻓـﻲ ﺍﻟﺒﺭﻨـﺎﻤﺞ‬
‫‪ AddAppSettings‬ﺍﻟﻤﺭﻓﻕ ﺒﺫﻟﻙ ﺍﻟﻜﺘﺎﺏ‪.‬‬

‫‪٥٢‬‬
‫ﻭﺍﺠﻬﺔ ﺍﻻﺘﺼﺎل ﺒﻘﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫‪IDbConnection Interface‬‬

‫ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDisposable‬ﻭﻫﻲ ﺘﺘﻴﺢ ﻟﻙ ﺇﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺍﺘﺼﺎل ﺨـﺎﺹ ﺒـﻙ‪،‬‬
‫ﻭﺫﻟﻙ ﺒﻜﺘﺎﺒﺔ ﻓﺌﺔ ﺘﻤﺜﻠﻬﺎ ‪ ..Implements the interface‬ﻫﺫﺍ ﻴﺴﻬل ﻋﻠﻴﻙ ﻜﺘﺎﺒﺔ ﻤﺯﻭﺩ ﺠﺩﻴـﺩ‬
‫ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻨﻭﻉ ﻤﻌﻴﻥ ﻤﻥ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻏﻴﺭ ﻤﺘﺎﺡ ﻓﻲ ﺇﻁﺎﺭ ﺍﻟﻌﻤل‪.‬‬
‫ﻭﺘﻤﺘﻠﻙ ﺍﻟﻭﺍﺠﻬﺔ ‪ IDbConnection‬ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻨﺹ ﺍﻻﺘﺼﺎل ‪:ConnectionString‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻨﺹ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﻼﺯﻤـﺔ ﻟﻼﺘﺼـﺎل ﺒﻘﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻻ ﻴﻤﻜﻨﻙ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺇﻻ ﻋﻨﺩﻤﺎ ﻴﻜﻭﻥ ﺍﻻﺘﺼـﺎل ﻤـﻊ ﻗﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻐﻠﻘﺎ‪.‬‬

‫ﻭﻗﺕ ﺍﻻﻨﺘﻅﺎﺭ ‪:ConnectionTimeout‬‬


‫ﺘﻘﺒل ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ‪ ،‬ﻴﻤﺜل ﺍﻟﻭﻗﺕ ﺒﺎﻟﺜﺎﻨﻴﺔ‪ ،‬ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺍﻨﺘﻅﺎﺭﻩ ﺃﺜﻨـﺎﺀ ﻤﺤﺎﻭﻟـﺔ ﺍﻻﺘﺼـﺎل‬
‫ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻓﺈﺫﺍ ﻤﺭ ﻫﺫﺍ ﺍﻟﻭﻗﺕ ﺩﻭﻥ ﺃﻥ ﻴﺴﺘﺠﻴﺏ ﺍﻟﺨﺎﺩﻡ‪ ،‬ﻴﺘﻡ ﺇﻟﻐﺎﺀ ﺍﻟﻌﻤﻠﻴﺔ ﻭﻴﻨﻁﻠـﻕ‬
‫ﺨﻁﺄ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻙ‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫﻲ ‪ ١٥‬ﺜﺎﻨﻴﺔ‪ ،‬ﻟﻜـﻥ ﺇﺫﺍ ﺃﺭﺩﺕ‬
‫ﺃﻥ ﺘﻅل ﻤﻨﺘﻅﺭﺍ ﺇﺘﻤﺎﻡ ﺍﻻﺘﺼﺎل ﺇﻟﻰ ﻤﺎ ﻻﻨﻬﺎﻴﺔ‪ ،‬ﻓﻀﻊ ﺼﻔﺭﺍ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ!‪ ..‬ﻟﻜـﻥ‬
‫ﻫﺫﺍ ﻗﺩ ﻴﺅﺩﻱ ﺇﻟﻰ ﺘﻭﻗﻑ ﺒﺭﻨﺎﻤﺠﻙ ﻋﻥ ﺍﻟﻌﻤل ﺇﺫﺍ ﻓﺸﻠﺕ ﻋﻤﻠﻴﺔ ﺍﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ‪ ،‬ﻟﻬﺫﺍ ﻟﻭ‬
‫ﺍﺴﺘﺨﺩﻤﺕ ﻫﺫﻩ ﺍﻟﻘﻴﻤﺔ ﻓﻴﺠﺏ ﺃﻥ ﺘﻌﻁﻲ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻁﺭﻴﻘﺔ ﻹﻟﻐﺎﺀ ﻤﺤﺎﻭﻟﺔ ﺍﻻﺘﺼﺎل ﺒﻨﻔﺴﻪ‪،‬‬
‫ﻜﺄﻥ ﺘﻀﻊ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ﺯﺭ ﺇﻟﻐﺎﺀ‪ ،‬ﻤﻊ ﺠﻌل ﻋﻤﻠﻴﺔ ﺍﻻﺘﺼﺎل ﻓﻲ ﻋﻤﻠﻴﺔ ﻓﺭﻋﻴﺔ ﻤﺴـﺘﻘﻠﺔ‬
‫‪ Thread‬ﻟﻜﻲ ﻻ ﻴﺘﻭﻗﻑ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻋﻥ ﺍﻻﺴﺘﺠﺎﺒﺔ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﻭﻀﻊ ﻗﻴﻤﺔ ﻜﺒﻴﺭﺓ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺴﻴﺅﺩﻱ ﺇﻟﻰ ﺘﻌﻁﻴل ﺍﻟﺒﺭﻨﺎﻤﺞ ﻟﻔﺘﺭﺓ ﺃﻁﻭل‪،‬‬
‫ﻭﻭﻀﻊ ﻗﻴﻤﺔ ﺼﻐﻴﺭﺓ ﻓﻴﻬﺎ ﺴﻴﺅﺩﻱ ﺇﻟﻰ ﻓﺸل ﻤﺤﺎﻭﻻﺕ ﺍﻻﺘﺼﺎل ﺒﺴﺭﻋﺔ‪ ..‬ﻭﺃﻓﻀل ﻗﻴﻤـﺔ‬
‫ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫﻲ ﻤﺎ ﺘﺭﺍﻩ ﻤﻨﺎﺴﺒﺎ ﻟﻅﺭﻭﻑ ﺒﺭﻨﺎﻤﺠﻙ‪ ..‬ﻓﻠﻭ ﻜﻨﺕ ﺘﺘﻭﻗﻊ ﻀـﻐﻁﺎ ﻜﺒﻴـﺭﺍ‬

‫‪٥٣‬‬
‫ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ﻴﺠﻌل ﺍﺴﺘﺠﺎﺒﺘﻪ ﻟﻤﺤﺎﻭﻻﺕ ﺍﻻﺘﺼﺎل ﺒﻁﻴﺌﺔ ﺃﻭ ﻤﺘﺄﺨﺭﺓ‪ ،‬ﻓﻀﻊ ﻗﻴﻤﺔ ﺃﻜﺒﺭ ﻓـﻲ‬
‫ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ )ﻤﺜل ‪ ٦٠‬ﺃﻭ ‪ ٩٠‬ﻤﺜﻼ(‪.‬‬

‫ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:Database‬‬


‫ﺘﻌﻴﺩ ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﻴﺘﻡ ﺍﻻﺘﺼﺎل ﺒﻬﺎ‪.‬‬

‫ﺍﻟﺤﺎﻟﺔ ‪:State‬‬
‫ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ ConnectionState‬ﺍﻟﺘﻲ ﺘﻌﺒﺭ ﻋﻥ ﺤﺎﻟﺔ ﺍﻻﺘﺼـﺎل ﻓـﻲ ﻫـﺫﻩ‬
‫ﺍﻟﻠﺤﻅﺔ‪ ،‬ﻭﻫﻲ‪:‬‬

‫ﺘﻡ ﺇﻏﻼﻕ ﺍﻻﺘﺼﺎل‪.‬‬ ‫‪Closed‬‬


‫ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺡ‪.‬‬ ‫‪Open‬‬
‫‪ Connecting‬ﻴﺘﻡ ﺇﺠﺭﺍﺀ ﺍﻻﺘﺼﺎل‪.‬‬
‫‪ Executing‬ﻴﺘﻡ ﺘﻨﻔﻴﺫ ﺃﺤﺩ ﺍﻷﻭﺍﻤﺭ ﻋﻠﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﺒﺭ ﺍﻻﺘﺼﺎل ﺍﻟﺤﺎﻟﻲ‪.‬‬
‫‪ Fetching‬ﻴﺘﻡ ﺇﺤﻀﺎﺭ ﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﺒﺭ ﺍﻻﺘﺼﺎل ﺍﻟﺤﺎﻟﻲ‪.‬‬
‫ﺘﻡ ﻓﺘﺢ ﺍﻻﺘﺼﺎل‪ ،‬ﻟﻜﻥ ﺤﺩﺙ ﻋﻁل ﺃﺩﻯ ﺇﻟﻰ ﺇﻏﻼﻗﻪ‪ ..‬ﻭﻴﻤﻜﻨﻙ ﺇﻋـﺎﺩﺓ‬ ‫‪Broken‬‬
‫ﻤﺤﺎﻭﻟﺔ ﻓﺘﺢ ﻫﺫﺍ ﺍﻻﺘﺼﺎل‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻓﺘﺢ ‪:Open‬‬
‫ـﻴﺔ‬
‫ـﻲ ﺍﻟﺨﺎﺼـ‬
‫ـﻭﺩﺓ ﻓـ‬
‫ـﺎﺕ ﺍﻟﻤﻭﺠـ‬
‫ـﺎ ﻟﻠﺒﻴﺎﻨـ‬
‫ـﺎﺕ‪ ،‬ﺘﺒﻌـ‬
‫ـﺩﺓ ﺍﻟﺒﻴﺎﻨـ‬
‫ـﺎل ﺒﻘﺎﻋـ‬
‫ـﺘﺢ ﺍﻻﺘﺼـ‬
‫ﺘﻔـ‬
‫‪.ConnectionString‬‬

‫ﺘﻐﻴﻴﺭ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:ChangeDatabase‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻼ ﻨﺼﻴﺎ‪ ،‬ﻴﻤﺜل ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻴﺩﺓ ﻤﻭﺠﻭﺩﺓ ﻋﻠﻰ ﻨﻔﺱ‬
‫ﺍﻟﺨﺎﺩﻡ‪ ،‬ﻟﻠﺘﻌﺎﻤل ﻤﻌﻬﺎ ﺒﺩﻻ ﻤﻥ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﺤﺎﻟﻴـﺔ ﺍﻟﻤﻭﻀـﺤﺔ ﻓـﻲ ﺍﻟﺨﺎﺼـﻴﺔ‬

‫‪٥٤‬‬
‫‪ ..Database‬ﻻﺤﻅ ﺃﻥ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﺘﺎﺡ ﻓﻘﻁ ﺃﺜﻨﺎﺀ ﻓﺘﺢ ﺍﻻﺘﺼـﺎل ﺒﺎﻟﺨـﺎﺩﻡ‪،‬‬
‫ﻭﺇﻻ ﺤﺩﺙ ﺨﻁﺄ ﻴﺨﺒﺭﻙ ﺃﻥ ﺍﻻﺘﺼﺎل ﻤﻐﻠﻕ!‪ ..‬ﺍﻟﺤﻜﻤﺔ ﻤﻥ ﻫﺫﺍ‪ ،‬ﻫـﻭ ﺍﺴـﺘﻐﻼل ﻨﻔـﺱ‬
‫ﺍﻻﺘﺼﺎل ﺍﻟﻤﻔﺘﻭﺡ ﻤﻊ ﺍﻟﺨﺎﺩﻡ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺃﻜﺜﺭ ﻤﻥ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﺘﻭﻓﻴﺭ ﻭﻗـﺕ ﺇﻏـﻼﻕ‬
‫ﺍﻻﺘﺼﺎل ﻭﺇﻋﺎﺩﺓ ﻓﺘﺢ ﺍﺘﺼﺎل ﺠﺩﻴﺩ‪.‬‬

‫ﺇﻨﺸﺎﺀ ﺃﻤﺭ ‪:CreateCommand‬‬


‫ﺘﻨﺸﺊ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺃﻤﺭ ‪ Command Object‬ﺠﺩﻴﺩ ﻟﺘﻨﻔﻴﺫﻩ ﻋﺒﺭ ﻜـﺎﺌﻥ ﺍﻻﺘﺼـﺎل‬
‫ﺍﻟﺤﺎﻟﻲ‪ ..‬ﻭﻻ ﻴﺸﺘﺭﻁ ﺃﻥ ﻴﻜﻭﻥ ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺤﺎ ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ‪ ،‬ﻓﻜـل ﻤـﺎ‬
‫ﺘﻔﻌﻠﻪ ﻫﻭ ﺇﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺃﻤﺭ ﻤﻨﺎﺴﺏ‪ ،‬ﻭﻭﻀﻊ ﻤﺭﺠﻊ ﻟﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪ Connection‬ﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻷﻤﺭ‪ ..‬ﻟﻜﻥ ﻋﻨﺩ ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ﻴﺠﺏ ﺃﻥ ﻴﻜـﻭﻥ ﺍﻻﺘﺼـﺎل‬
‫ﻤﻔﺘﻭﺤﺎ ﻓﻌﻼ‪ ،‬ﻭﺇﻻ ﺤﺩﺙ ﺨﻁﺄ‪.‬‬
‫ﻭﺍﻟﻘﻴﻤﺔ ﺍﻟﻌﺎﺌﺩﺓ ﻤﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻥ ﻨﻭﻉ ﻭﺍﺠﻬﺔ "ﺃﻤﺭ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ" ‪IDbCommand‬‬
‫ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬

‫ﺒﺩﺀ ﺍﻟﺘﻌﺎﻤﻼﺕ ‪:BeginTransaction‬‬


‫ﺘﻨﺸﺊ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺘﻌﺎﻤﻼﺕ ‪ Transaction Object‬ﻟﺘﺴﺘﻁﻴﻊ ﻤﻥ ﺨﻼﻟﻪ ﺇﺠـﺭﺍﺀ‬
‫ﻋﺩﺩ ﻤﻥ ﺍﻟﻌﻤﻠﻴﺎﺕ ﻋﻠﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻤﻊ ﻗﺩﺭﺘﻙ ﻋﻠﻰ ﺍﻟﺘﺭﺍﺠـﻊ ﻋﻨﻬـﺎ ﺒﻌـﺩ ﺫﻟـﻙ‪..‬‬
‫ﻭﺍﻟﻘﻴﻤﺔ ﺍﻟﻌﺎﺌﺩﺓ ﻤﻥ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﻤـﻥ ﻨـﻭﻉ ﻭﺍﺠﻬـﺔ "ﺘﻌـﺎﻤﻼﺕ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ"‬
‫‪ IDbTransaction‬ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬
‫ﻭﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﻟﻬﺎ ﻤﻌﺎﻤل ﻭﺍﺤﺩ ﻴﺴﺘﻘﺒل ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤـﺭﻗﻡ "ﻤﺴـﺘﻭﻯ‬
‫ﺍﻟﻌﺯل" ‪ ..IsolationLevel‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﻤﺭﻗﻡ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬

‫ﺇﻏﻼﻕ ‪:Close‬‬
‫ﺘﻘﻭﻡ ﺒﺎﻟﺘﺭﺍﺠﻊ ‪ Rollback‬ﻋﻥ ﺃﻱ ﺘﻌﺎﻤﻼﺕ ‪ Transactions‬ﻟﻡ ﻴﺘﻡ ﺇﺤﺎﻟﺘﻬﺎ ﺇﻟﻰ ﻗﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ..Committed‬ﺜﻡ ﺘﻐﻠﻕ ﺍﻻﺘﺼﺎل‪.‬‬
‫‪٥٥‬‬
‫ﻻﺤﻅ ﺃﻨﻪ ﻓﻲ ﺤﺎﻟﺔ ﺘﻔﻌﻴل ﺨﺎﺼﻴﺔ ﺍﻟﻤﺴﺎﻫﻤﺔ ‪ ،Pooling‬ﻓﺈﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﻴﺤﺎﻓﻅ ﻋﻠـﻰ‬
‫ﻋﺩﺩ ﻤﺤﺩﺩ ﻤﻥ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﻔﺘﻭﺤﺔ ﺒﻴﻨﻪ ﻭﺒﻴﻥ ﺒﺭﻨﺎﻤﺠﻙ‪ ،‬ﻭﺫﻟﻙ ﻟﺘﻭﻓﻴﺭ ﻭﻗـﺕ ﺇﻏـﻼﻕ‬
‫ﻭﺇﻋﺎﺩﺓ ﻓﺘﺢ ﺍﻻﺘﺼﺎﻻﺕ ﺒﻴﻨﻬﻤﺎ‪ ..‬ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻻ ﺘﻘـﻭﻡ ﺍﻟﻭﺴـﻴﻠﺔ ‪ Close‬ﺒـﺈﻏﻼﻕ‬
‫ﺍﻻﺘﺼﺎل‪ ،‬ﺒل ﺘﺘﺭﻙ ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺤﺎ‪ ،‬ﻭﺘﻀﻴﻔﻪ ﺇﻟـﻰ ﺭﺼـﻴﺩ ﺍﻻﺘﺼـﺎﻻﺕ ﺍﻟﻤﺴـﺎﻫﻤﺔ‬
‫‪ ،Connection Pool‬ﻟﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻤﺒﺎﺸﺭﺓ ﻋﻨﺩ ﺍﻻﺤﺘﻴﺎﺝ ﺇﻟﻴﻪ‪.‬‬

‫‪٥٦‬‬
‫ﻓﺌﺔ ﺍﻻﺘﺼﺎل ‪DbConnection Class‬‬

‫ﻫــﺫﻩ ﺍﻟﻔﺌــﺔ ﺃﺴﺎﺴــﻴﺔ ﻤﺠــﺭﺩﺓ ‪ ،Abstract Base Class‬ﻭﻫــﻲ ﺘﻤﺜــل ﺍﻟﻭﺍﺠﻬــﺔ‬


‫‪ ،IDbConnection‬ﻜﻤﺎ ﺃﻨﻬﺎ ﺘﺭﺙ ﻓﺌﺔ ﺍﻟﻤﻜﻭﻥ ‪ ،Component Class‬ﻟﻜﻨﻙ ﻟـﻥ ﺘﺴـﺘﻁﻴﻊ‬
‫ﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﺼﻴﻨﻴﺔ ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤﻭﺫﺝ ‪ Component Tray‬ﻷﻨﻙ ﻻ ﺘﺴﺘﻁﻴﻊ ﺇﻨﺸـﺎﺀ ﻨﺴـﺨﺔ‬
‫ﺠﺩﻴﺩﺓ ﻤﻨﻬﺎ‪ ،‬ﻟﻜﻥ ﺍﻟﻔﺌﺎﺕ ﺍﻟﻤﺸﺘﻘﺔ ﻤﻨﻬﺎ ﻤﺜل ‪ SQLConnection‬ﻴﻤﻜﻥ ﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﺼـﻴﻨﻴﺔ‬
‫ﺍﻟﻤﻜﻭﻨﺎﺕ‪ ..‬ﻟﻔﻌل ﻫﺫﺍ ﺍﻓﺘﺢ ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ ‪ ،Toolbox‬ﻭﺃﺴﺩل ﺍﻟﺸﺭﻴﻁ ‪ ،Data‬ﻭﺍﻀـﻐﻁﻪ‬
‫ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ‪ ،‬ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ‪ ،Choose Items‬ﻭﻓـﻲ ﺍﻟﻨﺎﻓـﺫﺓ ﺍﻟﺘـﻲ‬
‫ﺴﺘﻅﻬﺭ‪ ،‬ﻀﻊ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﺒﺠﻭﺍﺭ ﻤﺠﻤﻭﻋﺔ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺘﻲ ﺘﺒﺩﺃ ﺒـﺎﻟﺤﺭﻭﻑ ‪ SQL‬ﻭﻤـﻥ‬
‫ﻀﻤﻨﻬﺎ ‪ ،SQLConnection‬ﺜﻡ ﺍﻀﻐﻁ ﺍﻟﺯﺭ ‪ ..OK‬ﺍﻵﻥ ﺴﺘﺠﺩ ﻫﺫﻩ ﺍﻷﺩﻭﺍﺕ ﺘﺤﺕ ﺍﻟﺸﺭﻴﻁ‬
‫‪ Data‬ﻓﻲ ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ‪ ..‬ﺍﻨﻘﺭ ﺍﻷﺩﺍﺓ ‪ SQLConnection‬ﻤﺭﺘﻴﻥ ﺒﺎﻟﻔﺄﺭﺓ ﻹﻀﺎﻓﺔ ﻨﺴﺨﺔ‬
‫ﻤﻨﻬﺎ ﺇﻟﻰ ﺼﻴﻨﻴﺔ ﺍﻟﻤﻜﻭﻨﺎﺕ‪.‬‬
‫ﻭﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﺨﺼﺎﺌﺹ ﺍﻟﻭﺍﺠﻬـﺔ ‪ ،IDbConnection‬ﺘﻤﻠـﻙ ﻫـﺫﻩ ﺍﻟﻔﺌـﺔ‬
‫ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataSource‬‬


‫ﺘﻌﻴﺩ ﺍﺴﻡ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺍﻻﺘﺼﺎل ﺒﻪ‪.‬‬

‫ﺇﺼﺩﺍﺭ ﺍﻟﺨﺎﺩﻡ ‪:ServerVersion‬‬


‫ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﻤﺜل ﺇﺼﺩﺍﺭ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﺍﻟﺫﻱ ﻴﺘﺼل ﺒﻪ ﺍﻟﻌﻤﻴـل‪ ..‬ﻭﻴﺠـﺏ ﺃﻥ ﻴﻜـﻭﻥ‬
‫ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺤﺎ ﻓﻲ ﺘﻠﻙ ﺍﻟﻠﺤﻅﺔ ﻭﺇﻻ ﺤﺩﺙ ﺨﻁﺄ‪.‬‬

‫ﻭﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﻭﺴﺎﺌل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDbConnection‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴـﻴﻠﺘﻴﻥ‬


‫ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫‪٥٧‬‬
‫ﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻗﺎﺌﻤﺔ ﺍﻟﺘﻌﺎﻤﻼﺕ ‪:EnlistTransaction‬‬
‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺍﻟﺘﻌـﺎﻤﻼﺕ ‪ Transaction Object‬ﺍﻟـﺫﻱ ﺘﺭﻴـﺩ ﻀـﻡ‬
‫ﺘﻌﺎﻤﻼﺕ ﺍﻻﺘﺼﺎل ﺍﻟﺤﺎﻟﻲ ﺇﻟﻴﻪ‪ ،‬ﻟﺘﻜﻭﻴﻥ ﺘﻌﺎﻤﻼﺕ ﻤﻨﺘﺸـﺭﺓ ‪،Distributed Transaction‬‬
‫ﻭﻫﻲ ﺘﻌﺎﻤﻼﺕ ﺘﻨﻔﺫ ﻋﻤﻠﻴﺎﺕ ﻋﻠﻰ ﺃﻜﺜﺭ ﻤﻥ ﻤﺼﺩﺭ ﻭﺃﻜﺜﺭ ﻤﻥ ﺍﺘﺼﺎل‪ ،‬ﻭﻻ ﻴﻨﺠﺢ ﺘﻨﻔﻴﺫﻫﺎ‬
‫ﺇﻻ ﺇﺫﺍ ﻨﺠﺤﺕ ﻜل ﺃﺠﺯﺍﺌﻬﺎ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻜﺎﺌﻥ ﺍﻟﺘﻌﺎﻤﻼﺕ ﻻﺤﻘﺎ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﻤﺨﻁﻁ ‪:GetSchema‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺠﺩﻭل ‪ ،DataTable Object‬ﻴﺤﺘﻭﻱ ﻋﻠـﻰ ﺒﻴﺎﻨـﺎﺕ ﺍﻟﻤﺨﻁـﻁ ﺍﻟﺨـﺎﺹ‬
‫ﺒﺎﻟﺨﺎﺩﻡ‪.‬‬
‫ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺼﻴﻐﺔ ﺜﺎﻨﻴﺔ‪ ،‬ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻨﺼﻴﺎ ﻴﻤﺜـل ﺍﺴـﻡ ﺍﻟﻤﺨﻁـﻁ ﺍﻟـﺫﻱ ﺘﺭﻴـﺩ‬
‫ﺍﺴﺘﻌﺎﺩﺘﻪ‪.‬‬
‫ﻜﻤﺎ ﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺜﺎﻟﺜﺔ‪ ،‬ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ‪ ،‬ﻴﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺔ ﻨﺼـﻴﺔ‬
‫‪ ،String Array‬ﺘﻤﺜل ﺍﻟﻘﻴﻭﺩ ‪ Restrictions‬ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤﺨﻁﻁﻬﺎ‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺤﺩﺙ ﺍﻟﺘﺎﻟﻲ‪:‬‬

‫ﺘﻐﻴﺭ ﺍﻟﺤﺎﻟﺔ ‪:StateChange‬‬


‫ﻴﻨﻁﻠﻕ ﻋﻨﺩ ﺘﻐﻴﺭ ﺤﺎﻟﺔ ﺍﻻﺘﺼﺎل )ﻋﻨﺩ ﺇﻏﻼﻗﻪ ﺃﻭ ﻓﺘﺤﻪ(‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ‬
‫ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،StateChangeEventArgs‬ﻭﻫﻭ ﻴﻤﺘﻠﻙ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫‪ OriginalState‬ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ ConnectionState‬ﺍﻟﺘﻲ ﺘﻤﺜـل‬


‫ﺤﺎﻟﺔ ﺍﻻﺘﺼﺎل ﻗﺒل ﺤﺩﻭﺙ ﺍﻟﺘﻐﻴﻴﺭ‪.‬‬
‫ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ ConnectionState‬ﺍﻟﺘﻲ ﺘﻤﺜـل‬ ‫‪CurrentState‬‬
‫ﺤﺎﻟــــــﺔ ﺍﻻﺘﺼــــــﺎل ﺍﻟﺤﺎﻟﻴــــــﺔ‬
‫)ﺒﻌﺩ ﺤﺩﻭﺙ ﺍﻟﺘﻐﻴﻴﺭ(‪.‬‬

‫‪٥٨‬‬
‫ﻭﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪:DbConnection‬‬
‫‪.SqlConnection .١‬‬
‫‪.OdbcConnection .٢‬‬
‫‪.OleDbConnection .٣‬‬
‫‪.OracleConnection .٤‬‬
‫ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻨﻬﺎ ﺠﻤﻴﻌﺎ ﺘﻤﺘﻠﻙ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﻫﺫﻩ ﺍﻟﻔﺌﺔ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﺍﻵﻥ ﻋﻠﻰ ﻭﺍﺤﺩﺓ ﻤـﻥ‬
‫ﻫﺫﻩ ﺍﻟﻔﺌﺎﺕ‪ ،‬ﻭﻫﻲ ﺍﻟﻔﺌﺔ ‪.SqlConnection‬‬

‫‪٥٩‬‬
‫ﻓﺌﺔ ﺍﺘﺼﺎل ﺴﻴﻜﻴﻭل ‪SqlConnection Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ ،DbConnection‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻨﻬﺎ ﻀﻤﻨﻴﺎ ﺘـﺭﺙ ﺍﻟﻔﺌـﺔ ‪Component‬‬
‫ﻭﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪.IDbConnection‬‬
‫ﻭﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﻲ ﺘﺭﺜﻬﺎ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺇﻁﻼﻕ ﺤﺩﺙ ﺍﻟﺨﻁﺄ ‪:FireInfoMessageEventOnUserErrors‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،true‬ﻓﺴﻴﺘﻡ ﺇﻁﻼﻕ ﺍﻟﺤﺩﺙ ‪ InfoMessage‬ﻓﻭﺭ ﺤﺩﻭﺙ‬
‫ﺨﻁﺄ ﻓﻲ ﺍﻻﺘﺼﺎل‪ ،‬ﻭﺩﻭﻥ ﺍﻨﺘﻅﺎﺭ ﺍﻨﺘﻬﺎﺀ ﺘﻨﻔﻴﺫ ﺍﻹﺠﺭﺍﺀ ﺍﻟﺫﻱ ﺃﻨﺸـﺄ ﺍﻻﺘﺼـﺎل‪ ..‬ﺃﻤـﺎ ﺇﺫﺍ‬
‫ﺘﺭﻜﺕ ﻗﻴﻤﺘﻬﺎ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ‪ ،false‬ﻓﺴﻴﻨﻁﻠﻕ ﺍﺴﺘﺜﻨﺎﺀ ‪ Exception‬ﻓـﻲ ﺍﻟﺒﺭﻨـﺎﻤﺞ ﻋﻨـﺩ‬
‫ﺤﺩﻭﺙ ﺨﻁﺄ ﻓﻲ ﺍﻻﺘﺼﺎل‪ ،‬ﻭﻟﻡ ﻴﻨﻁﻠﻕ ﺍﻟﺤﺩﺙ ‪ InfoMessage‬ﺇﻻ ﺒﻌـﺩ ﺍﻨﺘﻬـﺎﺀ ﺘﻨﻔﻴـﺫ‬
‫ﺍﻹﺠﺭﺍﺀ ﺍﻟﺫﻱ ﺃﻨﺸﺄ ﺍﻻﺘﺼﺎل‪.‬‬

‫ﺤﺠﻡ ﺤﺯﻡ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:PacketSize‬‬


‫ﺘﻌﻴﺩ ﺤﺠﻡ ﺤِِﺯﻡ ﺍﻟﺒﻴﺎﻨﺎﺕ )ﺒﺎﻟﻭﺤﺩﺓ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪ (Byte‬ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻓﻲ ﻨﻘل ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺘﻔﻌﻴل ﺍﻹﺤﺼﺎﺌﻴﺎﺕ ‪:StatisticsEnabled‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،true‬ﻓﺴﻴﺘﻡ ﺠﻤﻊ ﺇﺤﺼﺎﺌﻴﺎﺕ ﻋـﻥ ﻋﻤﻠﻴـﺔ ﺍﻻﺘﺼـﺎل‪..‬‬
‫ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﻤﻔﻴﺩ ﻓﻲ ﺒﻌﺽ ﺍﻟﺤﺎﻻﺕ‪ ،‬ﻟﻜﻨﻪ ﻗﺩ ﻴﺅﺩﻱ ﺇﻟﻰ ﺇﺒﻁـﺎﺀ ﺍﻻﺘﺼـﺎل‪ ،‬ﻟﻬـﺫﺍ ﻻ‬
‫ﺘﺴﺘﺨﺩﻤﻪ ﺇﻻ ﻟﻠﻀﺭﻭﺭﺓ‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫﻲ ‪.false‬‬

‫ﻤﻌﺭﻑ ﺍﻟﺠﻬﺎﺯ ‪:WorkstationId‬‬


‫ﺘﻌﻴﺩ ﺍﺴﻡ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل ﺍﻟﻤﺘﺼل ﺒﺎﻟﺨﺎﺩﻡ‪.‬‬

‫ﻭﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﻭﺴﺎﺌل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDbConnection‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴـﺎﺌل‬


‫ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪٦٠‬‬
‫ﺘﻐﻴﻴﺭ ﻜﻠﻤﺔ ﺍﻟﺴﺭ ‪:ChangePassword‬‬
‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻨﺹ ﺍﻻﺘﺼﺎل‪ ،‬ﻭﻜﻠﻤﺔ ﺍﻟﺴﺭ ﺍﻟﺠﺩﻴﺩﺓ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﺴـﺘﺨﺩﺍﻤﻬﺎ ﻤـﻊ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻟﻤﺤﺩﺩ ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل ﺒﺩﻻ ﻤﻥ ﻜﻠﻤﺔ ﺍﻟﺴﺭ ﺍﻟﻘﺩﻴﻤﺔ‪ ..‬ﻫﺫﺍ ﻤﻌﻨـﺎﻩ ﺃﻥ ﻨـﺹ‬
‫ﺍﻻﺘﺼﺎل ﻴﺠﺏ ﺃﻥ ﻴﺤﺘﻭﻱ ﻋﻠـﻰ ﺍﺴـﻡ ﺍﻟﻤﺴـﺘﺨﺩﻡ ‪ UserID‬ﻭﻜﻠﻤـﺔ ﺍﻟﺴـﺭ ﺍﻟﻘﺩﻴﻤـﺔ‬
‫ـﺔ‬
‫ـﺔ ﺍﻟﻤﺘﻜﺎﻤﻠـ‬
‫ـﻪ ﺨﻴـﺎﺭ ﺍﻟﺤﻤﺎﻴـ‬
‫ـﺎل ﻓﻴـ‬
‫ـﻠﺕ ﻨـﺹ ﺍﺘﺼـ‬
‫ـﻭ ﺃﺭﺴـ‬
‫‪ ،Password‬ﻟﻬـﺫﺍ ﻟـ‬
‫‪ IntegratedSecurity‬ﻓﺴﻴﺤﺩﺙ ﺨﻁﺄ‪.‬‬
‫ﻭﺘﻘﻭﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺒﻔﺘﺢ ﺍﺘﺼﺎل ﺨﺎﺹ ﺒﻬﺎ ﻟﺘﻐﻴﻴﺭ ﻜﻠﻤﺔ ﺍﻟﺴﺭ‪ ،‬ﻭﺇﻏﻼﻗﻪ ﻓﻭﺭ ﺍﻻﻨﺘﻬﺎﺀ ﻤﻥ‬
‫ﻫﺫﺍ‪ ،‬ﺩﻭﻥ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺭﺼﻴﺩ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺴﺎﻫﻤﺔ ‪.Connection Pool‬‬
‫ﻭﺘﻔﻴﺩﻙ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺇﺫﺍ ﻜﺎﻨﺕ ﻜﻠﻤﺔ ﺍﻟﺴﺭ ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﻤﺴﺘﺨﺩﻡ ﻗـﺩ ﺍﻨﺘﻬـﺕ ﺼـﻼﺤﻴﺘﻬﺎ‬
‫‪ Expired‬ﻭﻴﺠﺏ ﺘﻐﻴﻴﺭﻫﺎ‪ ..‬ﻭﻴﻤﻜﻨﻙ ﻤﻌﺭﻓﺔ ﻫﺫﺍ ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ ‪ Open‬ﻟﻔـﺘﺢ‬
‫ﺍﻻﺘﺼﺎل‪ ،‬ﺤﻴﺙ ﺴﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻤﻥ ﺍﻟﻨـﻭﻉ ‪ ،SqlException‬ﻭﻋﻠﻴـﻙ ﺃﻥ‬
‫ﺘﻔﺤﺹ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪ Number‬ﺍﻟﺨﺎﺼﺔ ﺒﻬﺫﺍ ﺍﻻﺴﺘﺜﻨﺎﺀ‪ ،‬ﻓﺈﻥ ﻭﺠﺩﺕ ﻗﻴﻤﺘﻬـﺎ ‪١٨٤٨٧‬‬
‫ﺃﻭ ‪ ١٨٤٨٨‬ﻓﻬﺫﺍ ﻤﻌﻨﺎﻩ ﺍﻨﺘﻬﺎﺀ ﺼﻼﺤﻴﺔ ﻜﻠﻤﺔ ﺍﻟﺴﺭ ﻭﻭﺠﻭﺏ ﺘﻐﻴﻴﺭﻫﺎ‪.‬‬
‫ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﺤﺎﻭل ﺍﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ‪ ،‬ﻓﺈﻥ ﻓﺸل ﺍﻻﺘﺼﺎل ﺒﺴﺒﺏ ﺍﻨﺘﻬﺎﺀ ﺼﻼﺤﻴﺔ ﻜﻠﻤـﺔ‬
‫ﺍﻟﺴﺭ‪ ،‬ﻓﺈﻨﻪ ﻴﻐﻴﺭ ﻜﻠﻤﺔ ﺍﻟﺴﺭ ﺍﻟﻘﺩﻴﻤﺔ‪:‬‬
‫;) (‪var Csb = new SqlConnectionStringBuilder‬‬
‫;"‪Csb.DataSource = ".\\SQLEXPRESS‬‬
‫;"‪Csb.InitialCatalog = "Books‬‬
‫;"‪Csb.UserID = "User1‬‬
‫;"‪Csb.Password = "2009‬‬
‫;)) (‪SqlConnection Cn = new SqlConnection(Csb.ToString‬‬
‫{ ‪try‬‬
‫;) (‪Cn.Open‬‬
‫}‬
‫{ )‪catch (SqlException ex‬‬
‫)‪if (ex.Number == 18487 || ex.Number == 18488‬‬
‫‪SqlConnection.ChangePassword(Csb.ToString( ),‬‬
‫;)"‪"2010‬‬
‫}‬

‫‪٦١‬‬
‫ﺇﻟﻐﺎﺀ ﺍﻟﻤﺴﺎﻫﻤﺔ ‪:ClearPool‬‬
‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ‪ SqlConnection Object‬ﻹﻟﻐـﺎﺀ ﺍﻻﺘﺼـﺎل‬
‫ﺍﻟﺫﻱ ﻴﻤﺜﻠﻪ ﻤﻥ ﺭﺼﻴﺩ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺴـﺎﻫﻤﺔ ‪ ..Connection Pool‬ﻻﺤـﻅ ﺃﻥ ﻫـﺫﺍ‬
‫ﺍﻻﺘﺼﺎل ﻗﺩ ﻴﻜﻭﻥ ﻤﺴﺘﺨﺩﻤﺎ ﻓﻲ ﺘﻠﻙ ﺍﻟﻠﺤﻅﺔ ﻷﺩﺍﺀ ﺒﻌﺽ ﺍﻻﺴﺘﻌﻼﻤﺎﺕ‪ ،‬ﻟﻬﺫﺍ ﻴﺘﻡ ﺇﻨﻬـﺎﺅﻩ‬
‫ﻓﻲ ﺍﻟﺤﺎل‪ ،‬ﻭﺴﻴﻅل ﻤﺴﺘﺨﺩﻤﺎ ﺇﻟﻰ ﺤﻴﻥ ﺇﻏﻼﻗﻪ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ ،Close‬ﻭﻋﻨـﺩﻫﺎ ﻟـﻥ‬
‫ﻴﻌﻭﺩ ﺇﻟﻰ ﺭﺼﻴﺩ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﺴﺎﻫﻤﺔ‪ ،‬ﺒل ﺴﻴﻐﻠﻕ ﻓﻲ ﺍﻟﺤﺎل‪ ..‬ﻤﺜﺎل‪:‬‬
‫;)‪SqlConnection.ClearPool(Cn‬‬

‫ﺇﻟﻐﺎﺀ ﻜل ﺃﺭﺼﺩﺓ ﺍﻟﻤﺴﺎﻫﻤﺔ ‪:ClearAllPools‬‬


‫‪Connection‬‬ ‫ﺘﻐﻠﻕ ﺠﻤﻴﻊ ﺍﻻﺘﺼﺎﻻﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺭﺼﻴﺩ ﺍﻻﺘﺼـﺎﻻﺕ ﺍﻟﻤﺴـﺎﻫﻤﺔ‬
‫‪ ،Pool‬ﻭﺇﺫﺍ ﻜﺎﻥ ﺒﻌﻀﻬﺎ ﻤﺴﺘﺨﺩﻤﺎ‪ ،‬ﻻ ﻴﺘﻡ ﺇﻨﻬﺎﺅﻩ ﺇﻟﻰ ﺃﻥ ﻴﺘﻡ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ ‪Close‬‬
‫ﺍﻟﺨﺎﺼﺔ ﺒﻪ‪ ..‬ﻤﺜﺎل‪:‬‬
‫;) (‪SqlConnection.ClearAllPools‬‬

‫ﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻗﺎﺌﻤﺔ ﺍﻟﺘﻌﺎﻤﻼﺕ ﺍﻟﻤﻨﺘﺸﺭﺓ ‪:EnlistDistributedTransaction‬‬


‫ﻤﻤﺎﺜﻠﺔ ﻟﻠﻭﺴﻴﻠﺔ ‪.EnlistTransaction‬‬

‫ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﺍﻹﺤﺼﺎﺌﻴﺎﺕ ‪:RetrieveStatistics‬‬


‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺘﻤﺜل ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﻤﻭﺱ ‪ ،IDictionary‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺯﻭﺍﺝ ﻤﻥ ﺍﻟﻤﻔـﺎﺘﻴﺢ‬
‫‪ Keys‬ﻭﺍﻟﻘﻴﻡ ‪ ،Values‬ﺘﻤﺜل ﺇﺤﺼﺎﺌﻴﺎﺕ ﺍﻻﺘﺼﺎل ﺤﺘـﻰ ﻫـﺫﻩ ﺍﻟﻠﺤﻅـﺔ‪ ..‬ﻭﻴﻤﻜﻨـﻙ‬
‫ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺃﻜﺜﺭ ﻤﻥ ﻤﺭﺓ ﻋﻠـﻰ ﻓﺘـﺭﺍﺕ‪ ،‬ﻟﻠﺤﺼـﻭل ﻋﻠـﻰ ﺃﺤـﺩﺙ ﻗـﻴﻡ‬
‫ﻟﻺﺤﺼﺎﺌﻴﺎﺕ‪ ..‬ﻻﺤﻅ ﺃﻨﻙ ﻟﻥ ﺘﺤﺼل ﻋﻠﻰ ﺃﻱ ﺇﺤﺼـﺎﺌﻴﺎﺕ ﺇﻻ ﺇﺫﺍ ﺠﻌﻠـﺕ ﻟﻠﺨﺎﺼـﻴﺔ‬
‫‪ StatisticsEnabled‬ﺍﻟﻘﻴﻤﺔ ‪ true‬ﺃﻭﻻ‪.‬‬

‫ﺘﺼﻔﻴﺭ ﺍﻹﺤﺼﺎﺌﻴﺎﺕ ‪:ResetStatistics‬‬


‫ﺘﻌﻴﺩ ﺠﻤﻴﻊ ﻗﻴﻡ ﺍﻹﺤﺼﺎﺌﻴﺎﺕ ﺇﻟﻰ ﺍﻟﺼﻔﺭ‪.‬‬
‫‪٦٢‬‬
‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺤﺩﺙ ﺍﻟﺘﺎﻟﻲ‪:‬‬

‫ﺭﺴﺎﻟﺔ ﺍﻟﻤﻌﻠﻭﻤﺎﺕ ‪:InfoMessage‬‬


‫ﻴﻨﻁﻠﻕ ﻋﻨﺩﻤﺎ ﻴﺭﺴل ﺍﻟﺨﺎﺩﻡ ﺭﺴﺎﻟﺔ ﺘﺤﺫﻴﺭ ﺃﻭ ﺨﻁﺄ‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤـﻥ‬
‫ﺍﻟﻨﻭﻉ ‪ ،SqlInfoMessageEventArgs‬ﻭﻫﻭ ﻴﻤﺘﻠﻙ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﻤﻥ ﺍﻟﻨـﻭﻉ ‪ ،SqlErrorCollection‬ﺍﻟﺘـﻲ‬ ‫‪Errors‬‬


‫ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،ICollection‬ﻭﻜل ﻋﻨﺼﺭ ﻤﻥ ﻋﻨﺎﺼـﺭﻫﺎ‬
‫ﻤﻥ ﻨﻭﻉ ﺍﻟﻔﺌﺔ ‪ ،SqlError‬ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺤﺩ ﺍﻷﺨﻁـﺎﺀ‬
‫ﺃﻭ ﺍﻟﺘﺤﺫﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺃﺭﺴﻠﻬﺎ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ‬
‫ﺍﻟﻔﺌﺔ ‪ SqlError‬ﺒﻌﺩ ﻗﻠﻴل‪.‬‬
‫ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﺸﺭﺡ ﺃﻭل ﺨﻁﺄ ﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻷﺨﻁـﺎﺀ‬ ‫‪Message‬‬
‫‪ ..Errors‬ﻫﺫﺍ ﻤﻔﻴﺩ ﺇﻥ ﻜﺎﻥ ﻫﻨﺎﻙ ﺨﻁﺎ ﻭﺍﺤﺩ ﻓﻘﻁ‪.‬‬
‫ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﺤﺩﺩ ﺍﺴﻡ ﺍﻟﻜﺎﺌﻥ ﺍﻟـﺫﻱ ﺘﺴـﺒﺏ ﻓـﻲ ﺃﻭل ﺨﻁـﺄ‬ ‫‪Source‬‬
‫ﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻷﺨﻁﺎﺀ ‪ ..Errors‬ﻫﺫﺍ ﻤﻔﻴﺩ ﺇﻥ ﻜـﺎﻥ‬
‫ﻫﻨﺎﻙ ﺨﻁﺎ ﻭﺍﺤﺩ ﻓﻘﻁ‪.‬‬

‫ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻓﺌﺔ ﺍﺘﺼﺎل ﺴﻴﻜﻭﻴل ﻓﻲ ﺍﻟﺘﻁﺒﻴﻕ ‪ AuthorBooks_Reader‬ﻟﻼﺘﺼﺎل ﺒﻘﺎﻋـﺩﺓ‬


‫ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ﺍﻟﻤﺤﻠﻲ‪ ..‬ﻻﺤﻅ ﺃﻨﻨﺎ ﻓﺘﺤﻨﺎ ﺍﻻﺘﺼﺎل ﻓﻲ ﺤـﺩﺙ ﺘﺤﻤﻴـل ﺍﻟﻨﻤـﻭﺫﺝ‬
‫‪ Load‬ﻭﻟﻡ ﻨﻐﻠﻘﻪ ﺇﻻ ﻓﻲ ﺤﺩﺙ ﺇﻏﻼﻕ ﺍﻟﻨﻤﻭﺫﺝ ‪ ،FormClosing‬ﻤﻤﺎ ﺃﺘﺎﺡ ﻟﻨﺎ ﺍﺴﺘﺨﺩﺍﻡ ﻨﻔـﺱ‬
‫ﺍﻻﺘﺼﺎل ﻟﺘﻨﻔﻴﺫ ﺠﻤﻴﻊ ﺍﻻﺴﺘﻌﻼﻤﺎﺕ ﺍﻟﺘﻲ ﻴﻘﻭﻡ ﺒﻬﺎ ﺍﻟﻤﺴﺘﺨﺩﻡ‪ ..‬ﻭﺭﻏﻡ ﺃﻥ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﻤﻌﺭﻑ‬
‫ﻜﻤﺘﻐﻴﺭ ﻤﻭﻀﻌﻲ ‪ Local Variable‬ﻓﻲ ﺤﺩﺙ ﺘﺤﻤﻴل ﺍﻟﻨﻤﻭﺫﺝ‪ ،‬ﺇﻻ ﺃﻨﻨﺎ ﻭﻀﻌﻨﺎ ﻤﺭﺠﻌـﺎ ﻟـﻪ‬
‫ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ Connection‬ﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻷﻤﺭ ‪ Command Object‬ﺍﻟﻤﻌـﺭﻑ ﻋﻠـﻰ‬
‫ﻤﺴﺘﻭﻯ ﺍﻟﻨﻤﻭﺫﺝ‪ ،‬ﻤﻤﺎ ﺠﻌل ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺤﻴﺎ ﻁﺎﻟﻤﺎ ﻜﺎﻥ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﺤﻴﺎ‪ ..‬ﻫﺫﺍ ﻫﻭ ﺍﻟﺴـﺒﺏ‬

‫‪٦٣‬‬
‫ﻓﻲ ﺃﻨﻨﺎ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﺨﺎﺼﻴﺔ ‪ Connection‬ﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻷﻤﺭ ﻹﻏﻼﻕ ﺍﻻﺘﺼﺎل ﻓﻲ ﺤـﺩﺙ‬
‫ﺇﻏﻼﻕ ﺍﻟﻨﻤﻭﺫﺝ ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫;) (‪Cmd.Connection.Close‬‬
‫ﻻﺤﻅ ﺃﻥ ﺘﺭﻙ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺤﺎ ﻁﻭﺍل ﺍﻟﻭﻗﺕ ﻋﻤﻠﻲ‪ ‬ﻓﻘﻁ ﻓﻲ ﺤﺎﻟﺘﻨﺎ ﻫﺫﻩ‪ ،‬ﻷﻨﻨﺎ ﻨﺘﻌﺎﻤـل‬
‫ﻫﻨﺎ ﻤﻊ ﺨﺎﺩﻡ ﻤﺤﻠﻲ‪ ،‬ﻭﻫﻨﺎﻙ ﻨﺴﺨﺔ ﻭﺍﺤﺩﺓ ﻓﻘﻁ ﻤﻥ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺘﺘﻌﺎﻤل ﻤﻌﻪ‪ ..‬ﻟﻜﻥ ﻓﻲ ﺍﻟﺒـﺭﺍﻤﺞ‬
‫ﺍﻟﻌﻤﻠﻴﺔ ﺍﻟﺘﻲ ﺘﺘﻌﺎﻤل ﻤﻊ ﺨﺎﺩﻡ ﺤﻘﻴﻘﻲ‪ ،‬ﻗﺩ ﻴﺅﺩﻱ ﺘﺭﻙ ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺤـﺎ ﺇﻟـﻰ ﺘﻘﻠﻴـل ﻜﻔـﺎﺀﺓ‬
‫ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ،‬ﺨﺎﺼﺔ ﺇﺫﺍ ﻜﺎﻥ ﻫﻨﺎﻙ ﻋﺩﺩ ﻜﺒﻴﺭ ﻤﻥ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻴﺘﻌﺎﻤﻠﻭﻥ ﻤﻊ ﺒﺭﻨﺎﻤﺠﻙ ﻓﻲ ﻨﻔـﺱ‬
‫ﺍﻟﻠﺤﻅﺔ‪.‬‬
‫ﺍﻓﺘﺭﺽ ﻤﺜﻼ ﺃﻨﻙ ﺘﺼﻤﻡ ﺒﺭﻨﺎﻤﺠﺎ ﻟﺸﺭﻜﺔ ﻴﻌﻤل ﺒﻬﺎ ‪ ٢٥٠‬ﻤﻭﻅﻔﺎ‪ ،‬ﻭﺃﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﻤﺠﻬـﺯ‬
‫ﻻﺴﺘﻘﺒﺎل ‪ ١٠٠‬ﺍﺘﺼﺎل ﻓﻘﻁ ﻓﻲ ﻨﻔﺱ ﺍﻟﻠﺤﻅﺔ‪ ..‬ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻟﻭ ﺠﻌﻠﺕ ﺒﺭﻨﺎﻤﺠﻙ ﻴﻔﺘﺢ ﻨﻔـﺱ‬
‫ﺍﻻﺘﺼﺎل ﺒﺼﻭﺭﺓ ﺩﺍﺌﻤﺔ ﻁﻭﺍل ﺘﺸﻐﻴل ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﻪ‪ ،‬ﻓﺈﻥ ﺃﻭل ‪ ١٠٠‬ﻤﻭﻅﻑ ﻴﻔﺘﺤﻭﻥ ﺍﻟﺒﺭﻨـﺎﻤﺞ‬
‫ﻋﻠﻰ ﺃﺠﻬﺯﺘﻬﻡ ﺴﻴﻤﻨﻌﻭﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﻤﻥ ﺍﻻﺴﺘﺠﺎﺒﺔ ﻟﻤﺌﺔ ﻭﺨﻤﺴﻴﻥ ﻤﻭﻅﻔﺎ ﺁﺨـﺭﻴﻥ ﺇﻟـﻰ ﺃﻥ‬
‫ﻴﻐﻠﻕ ﺒﻌﺽ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﺍﻟﺒﺭﻨﺎﻤﺞ!‬
‫ﻟﻘﺩ ﺃﺤﺒﺒﺕ ﺃﻥ ﺃﺭﻴﻙ ﻜﻴﻑ ﻴﻤﻜﻥ ﺃﻥ ﻴﺅﺩﻱ ﺍﻟﺘﺼﻤﻴﻡ ﺍﻟﺨﺎﻁﺊ ﻟﺒﺭﻨﺎﻤﺠﻙ ﺇﻟـﻰ ﻨﺘـﺎﺌﺞ ﻜﺎﺭﺜﻴـﺔ‪،‬‬
‫ﻭﻴﻌﻁل ﺍﻟﻌﻤل ﻭﻻ ﺘﺠﻨﻲ ﻤﻥ ﻭﺭﺍﺌﻪ ﺴﻭﻯ ﺍﻟﺴﺨﻁ ‪! J‬‬
‫ﻭﺭﻏﻡ ﺃﻥ ﻫﺫﺍ ﻤﺜﺎل ﺍﻓﺘﺭﺍﻀﻲ ﻟﺘﻘﺭﻴﺏ ﺍﻟﻔﻜﺭﺓ‪ ،‬ﺤﻴﺙ ﺇﻥ ﺴﻴﻜﻴﻭل ﺴﻴﺭﻓﺭ ﻴﺴﺘﻁﻴﻊ ﻓﻌﻠﻴﺎ ﺨﺩﻤـﺔ‬
‫ﺒﻀﻊ ﻤﺌﺎﺕ ﻤﻥ ﺍﻟﻌﻤﻼﺀ ﻭﺭﺒﻤﺎ ﺃﻜﺜﺭ ﻓﻲ ﻨﻔﺱ ﺍﻟﻭﻗﺕ‪ ،‬ﺇﻻ ﺃﻥ ﻫﺫﺍ ﺍﻟﻌﺩﺩ ﻤﻬﻤﺎ ﺒﺩﺍ ﻜﺒﻴﺭﺍ ﻟﻙ ﻓﻬﻭ‬
‫ﻤﺤﺩﻭﺩ‪ ،‬ﻭﻴﻤﻜﻥ ﺘﺠﺎﻭﺯﻩ ﻋﻤﻠﻴﺎ ﻓﻲ ﺍﻟﻤﺅﺴﺴﺎﺕ ﺍﻟﻀﺨﻤﺔ ﻜﺎﻟﺤﻜﻭﻤﺔ ﺍﻻﻟﻜﺘﺭﻭﻨﻴـﺔ ﻤـﺜﻼ )ﻫـل‬
‫ﺘﺘﺨﻴل ﻜﻡ ﻋﺩﺩ ﺍﻟﻤﻭﻅﻔﻴﻥ ﻓﻲ ﺍﻟﻭﺯﺍﺭﺍﺕ ﺍﻟﻤﺨﺘﻠﻔﺔ ﺍﻟﺫﻴﻥ ﻴﺘﻌﺎﻤﻠﻭﻥ ﻤﻊ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻨﻔـﺱ‬
‫ﺍﻟﻠﺤﻅﺔ؟(‪ ،‬ﺃﻭ ﻓﻲ ﻤﻭﺍﻗﻊ ﺍﻹﻨﺘﺭﻨﺕ ﺍﻟﺘﻲ ﺘﺤﻔﻅ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺘﻬﺎ ﻋﻠﻰ ﺴﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ‪ ،‬ﻭﺃﻨـﺕ‬
‫ﺘﻌﺭﻑ ﺃﻥ ﺒﻌﺽ ﻫﺫﻩ ﺍﻟﻤﻭﺍﻗﻊ ﻴﺼل ﺯﻭﺍﺭﻫﺎ ﺇﻟﻰ ﻋﺩﺓ ﻤﻼﻴﻴﻥ ﻴﻭﻤﻴﺎ‪ ،‬ﻤﻤﺎ ﻴـﺅﺩﻱ ﺇﻟـﻰ ﺒـﻁﺀ‬
‫ﺍﺴﺘﺠﺎﺒﺔ ﺍﻟﺨﺎﺩﻡ‪ ،‬ﻭﺍﻋﺘﺫﺍﺭﻩ ﻟﻠﻜﺜﻴﺭ ﻤﻥ ﺍﻟﻌﻤﻼﺀ ﻋﻥ ﻭﺠﻭﺩ ﻀﻐﻁ ﻜﺒﻴﺭ ﺒﺠﺒﺭﻫﻡ ﻋﻠﻰ ﺍﻻﻨﺘﻅـﺎﺭ‬
‫)ﻻ ﺭﻴﺏ ﺃﻨﻙ ﻭﺍﺠﻬﺕ ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ﻤﻊ ﺨﺎﺩﻡ ﺒﺭﻴﺩ ﻫﻭﺘﻤﻴل ﻓﻲ ﺒﻌﺽ ﺍﻷﻭﻗﺎﺕ(!‬
‫ﻭﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻨﺎ‪ ،‬ﻋﻠﻴﻙ ﺃﻥ ﺘﻨﻘل ﺍﻟﻜﻭﺩ ﻤﻥ ﺤﺩﺙ ﺘﺤﻤﻴـل ﺍﻟﻨﻤـﻭﺫﺝ ﻭﺤـﺩﺙ‬
‫ﺇﻏﻼﻗﻪ ﺇﻟﻰ ﺤﺩﺙ ﻀﻐﻁ ﺍﻟﺯﺭ‪ ،‬ﻟﻴﺘﻡ ﻓﺘﺢ ﺍﻻﺘﺼﺎل ﻭﺇﻏﻼﻗﻪ ﻓﻘﻁ ﻋﻨﺩ ﺍﻟﺤﺎﺠﺔ‪ ..‬ﻭﻻ ﺘﻘﻠﻕ ﻤـﻥ‬

‫‪٦٤‬‬
‫ﻜﺜﺭﺓ ﻓﺘﺢ ﺒﺭﻨﺎﻤﺠﻙ ﻟﻼﺘﺼﺎل ﻭﺇﻏﻼﻗـﻪ‪ ،‬ﻓﺨﺎﺼـﻴﺔ ﻤﺴـﺎﻫﻤﺔ ﺍﻻﺘﺼـﺎﻻﺕ ‪Connection‬‬
‫‪ Pooling‬ﺍﻟﺘﻲ ﻴﺩﻋﻤﻬﺎ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﺘﻐﻨﻴﻙ ﻋﻥ ﺃﻱ ﻋﻨﺎﺀ ﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ‪ ،‬ﺤﻴﺙ ﻴﺘﻡ ﺘـﺭﻙ‬
‫ﺒﻌﺽ ﺍﻻﺘﺼﺎﻻﺕ ﻤﻔﺘﻭﺤﺔ ﻟﻀﻤﺎﻥ ﺴﺭﻋﺔ ﺍﻻﺴﺘﺠﺎﺒﺔ ﻟﻼﺴـﺘﻌﻼﻤﺎﺕ ﺍﻟﻤﺘﻜـﺭﺭﺓ‪ ،‬ﺩﻭﻥ ﺇﻋﺎﻗـﺔ‬
‫ﺒﻌﺽ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻋﻥ ﺍﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ‪ ..‬ﻭﻟﺤﺴﻥ ﺍﻟﺤﻅ ﻓـﺈﻥ ﺘﻘﻨﻴـﺔ ﺍﻟﻤﺴـﺎﻫﻤﺔ ‪Pooling‬‬
‫ﺘﻜﻭﻥ ﻓﻌﺎﻟﺔ ﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ‪ ،‬ﻤﺎ ﻟﻡ ﺘﻁﻠﺏ ﺃﻨﺕ ﺇﻴﻘﺎﻓﻬﺎ ﺼﺭﺍﺤﺔ ﻋﺒﺭ ﻨـﺹ ﺍﻻﺘﺼـﺎل‬
‫ﻜﻤﺎ ﻋﺭﻓﻨﺎ ﺴﺎﺒﻘﺎ‪.‬‬
‫ﻭﺴﺘﺠﺩ ﻜﻭﺩ ﺍﻻﺘﺼﺎل ﺍﻟﻤﺤﺴ‪‬ﻥ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ﺍﻟﻤﺴﻤﻰ ‪.AuthorBooks_Reader2‬‬

‫‪٦٥‬‬
‫ﻓﺌﺔ ﺨﻁﺄ ﺴﻴﻜﻴﻭل ‪SqlError Class‬‬

‫ﺘﺤﺘﻭﻱ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻋﻠﻰ ﻤﻌﻠﻭﻤﺎﺕ ﻋﻥ ﺭﺴﺎﻟﺔ ﺍﻟﺨﻁﺄ )ﺃﻭ ﺍﻟﺘﺤﺫﻴﺭ( ﺍﻟﺘﻲ ﺃﺭﺴﻠﻬﺎ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل‪..‬‬
‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻟﺭﺘﺒﺔ ‪:Class‬‬
‫ﺘﻌﻴﺩ ﺭﻗﻤﺎ ﻤﻥ ‪ ٠‬ﺇﻟﻰ ‪ ٢٥٥‬ﻴﻤﺜل ﺩﺭﺠﺔ ﺨﻁﻭﺭﺓ ﺍﻟﺨﻁﺄ‪ ..‬ﻭﻗﻴﻤﺘﻬﺎ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ‪.٠‬‬

‫ﺍﻟﺨﺎﺩﻡ ‪:Server‬‬
‫ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﻨﺴﺨﺔ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﺍﻟﺘﻲ ﺃﺭﺴﻠﺕ ﺍﻟﺨﻁﺄ‪.‬‬

‫ﺍﻟﻤﺼﺩﺭ ‪:Source‬‬
‫ﺘﻌﻴﺩ ﺍﺴﻡ ﺍﻟﻤﺯﻭﺩ ‪ Provider‬ﺍﻟﺫﻱ ﺘﺴﺒﺏ ﻓﻲ ﺍﻟﺨﻁﺄ‪.‬‬

‫ﺍﻹﺠﺭﺍﺀ ‪:Procedure‬‬
‫ﺘﻌﻴﺩ ﺍﺴﻡ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ﺍﻟﺫﻱ ﺘﺴﺒﺏ ﻓﻲ ﺍﻟﺨﻁﺄ‪.‬‬

‫ﺭﻗﻡ ﺍﻟﺴﻁﺭ ‪:LineNumber‬‬


‫ﺘﻌﻴﺩ ﺭﻗﻡ ﺍﻟﺴﻁﺭ ﺍﻟﺫﻱ ﺘﺴﺒﺏ ﻓﻲ ﺍﻟﺨﻁﺄ ﻓﻲ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ‪.‬‬

‫ﺍﻟﺭﺴﺎﻟﺔ ‪:Message‬‬
‫ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﺼﻑ ﺭﺴﺎﻟﺔ ﺍﻟﺨﻁﺄ‪ .. ..‬ﻭﻴﻤﻜﻨـﻙ ﺃﻴﻀـﺎ ﺍﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ ‪ToString‬‬
‫ﺍﻟﺨﺎﺼﺔ ﺒﻬﺫﺍ ﺍﻟﻜﺎﺌﻥ ﻟﻌﺭﺽ ﻨﺹ ﻫﺫﻩ ﺍﻟﺭﺴﺎﻟﺔ‪.‬‬

‫ﺍﻟﺭﻗﻡ ‪:Number‬‬
‫ﺘﻌﻴﺩ ﺭﻗﻡ ﺍﻟﺨﻁﺄ‪.‬‬

‫ﺍﻟﺤﺎﻟﺔ ‪:State‬‬
‫ﺘﻌﻴﺩ ﺭﻗﻤﺎ ﻤﻥ ‪ ٠‬ﺇﻟﻰ ‪ ٢٥٥‬ﻴﻤﺜل ﺍﻟﻜﻭﺩ ﺍﻟﺭﻗﻤﻲ ﻟﻠﺨﻁﺄ‪.‬‬

‫‪٦٦‬‬
‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻓﺌﺔ ﺍﺴﺘﺜﻨﺎﺀ ﺴﻴﻜﻭﻴل ‪ SqlException‬ﻤﻥ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﻤﻥ ﺍﻟﻔﺌﺔ‬
‫ﺍﻷﻡ ‪ SystemException‬ﻭﺍﻟﻔﺌﺔ ﺍﻷﻡ ‪ ،Exception‬ﻓﺈﻨﻬﺎ ﺘﻤﺘﻠﻙ ﻨﻔـﺱ ﺨﺼـﺎﺌﺹ ﺍﻟﻔﺌـﺔ‬
‫‪ ،SqlError‬ﻤﻤﺎ ﻴﻤﻨﺤﻙ ﺍﻟﻘﺩﺭﺓ ﻋﻠﻰ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻨﻔـﺱ ﺍﻟﻤﻌﻠﻭﻤـﺎﺕ‪ ،‬ﺴـﻭﺍﺀ ﺍﺴـﺘﺨﺩﻤﺕ‬
‫ﺍﻟﺤﺩﺙ ‪ InfoMessage‬ﺃﻡ ﺍﺴﺘﺨﺩﻤﺕ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻘﻠﻴﺩﻴـﺔ ﻟﻤﻌﺎﻟﺠـﺔ ﺍﻷﺨﻁـﺎﺀ ‪Exception‬‬
‫‪.Handling‬‬

‫‪٦٧‬‬
‫‪-٧-‬‬
‫ﻜــﺎﺌـــــﻥ ﺍﻷﻤــــــﺭ‬
‫‪Command Object‬‬

‫ﻴﺘﻌﺎﻤل ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﻤﻊ ﺍﺴﺘﻌﻼﻡ ‪ SQL‬ﺃﻭ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻥ ‪ ،Stored Procedure‬ﻤﻊ ﺍﻤﺘﻼﻜـﻪ‬
‫ﺍﻟﻭﺴﺎﺌل ﺍﻟﻼﺯﻤﺔ ﻟﺘﻨﻔﻴﺫﻫﻤﺎ ﻋﺒﺭ ﺍﺘﺼﺎل ﻤﻔﺘﻭﺡ‪ ،‬ﻭﺍﺴﺘﻼﻡ ﺍﻟﻨﺘﻴﺠﺔ ﻤﻥ ﺍﻟﺨﺎﺩﻡ‪.‬‬
‫ﻭﺴﻨﺘﻌﺭﻑ ﻓﻲ ﻫﺫﺍ ﺍﻟﻔﺼل ﻋﻠﻰ ﻜﻴﻔﻴﺔ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻜﺎﺌﻥ ﺍﻷﻤﺭ‪.‬‬

‫ﻭﺍﺠﻬﺔ ﺃﻤﺭ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪IDbCommand Interface‬‬

‫ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDisposable‬ﻭﻫﻲ ﺘﻭﺠـﺩ ﻓـﻲ ﺍﻟﻨﻁـﺎﻕ ‪..System.Data‬‬


‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻻﺘﺼﺎل ‪:Connection‬‬
‫ﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺃﻱ ﻜـﺎﺌﻥ ﺍﺘﺼـﺎل ﻴﻤﺜـل ﺍﻟﻭﺍﺠﻬـﺔ ‪ ،IDbConnection‬ﻟﻴـﺘﻡ‬
‫ﺍﺴﺘﺨﺩﺍﻤﻪ ﻓﻲ ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ‪ ..‬ﻭﻴﺸﺘﺭﻁ ﻓﺘﺢ ﺍﻻﺘﺼﺎل ﺃﻭﻻ ﻗﺒل ﻤﺤﺎﻭﻟﺔ ﺘﻨﻔﻴـﺫ ﺍﻷﻤـﺭ‪ ،‬ﻭﺇﻻ‬
‫ﺤﺩﺙ ﺨﻁﺄ‪.‬‬

‫ﻨﻭﻉ ﺍﻷﻤﺭ ‪:CommandType‬‬


‫ﺘﺤﺩﺩ ﻨﻭﻉ ﺍﻷﻤﺭ ﺍﻟﻤﺭﺍﺩ ﺘﻨﻔﻴﺫﻩ‪ ،‬ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ CommandType‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪٦٨‬‬
‫ﺍﻷﻤﺭ ﻴﺘﻜﻭﻥ ﻤﻥ ﺠﻤﻠﺔ ‪ ..SQL‬ﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪.‬‬ ‫‪Text‬‬
‫‪ StoredProcedure‬ﺍﻷﻤﺭ ﻴﺘﻜﻭﻥ ﻤﻥ ﺍﺴﻡ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻥ ﻴﺭﺍﺩ ﺘﻨﻔﻴﺫﻩ‪.‬‬
‫ﺍﻷﻤﺭ ﻴﺘﻜﻭﻥ ﻤﻥ ﺍﺴﻡ ﺠﺩﻭل ﻴﺭﺍﺩ ﺘﺤﻤﻴل ﻜل ﺒﻴﺎﻨﺎﺘﻪ ﻜﺎﻤﻠﺔ ﻤﻥ‬ ‫‪TableDirect‬‬
‫ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺒﺎﺸﺭﺓ‪ ..‬ﻫﺫﻩ ﺍﻟﻘﻴﻤﺔ ﻏﻴﺭ ﻤﻘﺒﻭﻟﺔ ﻤﻊ ﻗﻭﺍﻋـﺩ‬
‫ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪ ،‬ﻟﻜﻥ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻤـﻊ ﻗﻭﺍﻋـﺩ‬
‫ﺒﻴﺎﻨﺎﺕ ﺁﻜﺴﻴﺱ‪.‬‬

‫ﻨﺹ ﺍﻷﻤﺭ ‪:CommandText‬‬


‫ﺠﻤﻠﺔ ‪ SQL‬ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺘﻨﻔﻴﺫﻫﺎ‪ ،‬ﺃﻭ ﺍﺴﻡ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ﺍﻟﺫﻱ ﺘﺭﻴـﺩ ﺘﻨﻔﻴـﺫﻩ‪ ،‬ﺃﻭ ﺍﺴـﻡ‬
‫ﺍﻟﺠﺩﻭل ﺍﻟﻤﺭﺍﺩ ﺘﺤﻤﻴﻠﻪ‪ ،‬ﻭﺫﻟﻙ ﺘﺒﻌﺎ ﻟﻘﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪.CommandType‬‬
‫ﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﻜﺘﺎﺒﺔ ﺃﻜﺜﺭ ﻤﻥ ﺠﻤﻠﺔ ‪ SQL‬ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤـﻊ ﺍﻟﻔﺼـل ﺒﻴﻨﻬـﺎ‬
‫ﺒﺎﻟﻔﺎﺼﻠﺔ ﺍﻟﻤﻨﻘﻭﻁﺔ ; ‪ ،‬ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﻴﺘﻡ ﺘﻨﻔﻴﺫﻫﺎ ﺠﻤﻴﻌﺎ‪ ،‬ﻭﺍﻟﺤﺼﻭل ﻋﻠﻰ ﺃﻜﺜﺭ ﻤـﻥ‬
‫ﻤﺠﻤﻭﻋﺔ ﻤﻥ ﺍﻟﻨﺘﺎﺌﺞ ‪ ،Resultsets‬ﻭﻫﻭ ﻨﻔﺱ ﻤﺎ ﻴﻤﻜﻥ ﺃﻥ ﻴﺤﺩﺙ ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺇﺠـﺭﺍﺀ‬
‫ﻤﺨﺯﻥ ﻴﻘﻭﻡ ﺒﺘﻨﻔﻴﺫ ﺃﻜﺜﺭ ﻤﻥ ﺠﻤﻠﺔ ‪ ..SELECT‬ﻭﺴﻨﻌﺭﻑ ﻜﻴﻑ ﻴﻤﻜﻥ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻫـﺫﻩ‬
‫ﺍﻟﻨﺘﺎﺌﺞ ﻻﺤﻘﺎ‪.‬‬

‫ﻭﻗﺕ ﺍﻨﺘﻅﺎﺭ ﺍﻷﻤﺭ ‪:CommandTimeout‬‬


‫ﺘﺤﺩﺩ ﻭﻗﺕ ﺍﻻﻨﺘﻅﺎﺭ ﺒﺎﻟﺜﺎﻨﻴﺔ‪ ،‬ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺒﻌﺩﻩ ﺍﻋﺘﺒﺎﺭ ﻤﺤﺎﻭﻟﺔ ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ﻓﺎﺸﻠﺔ‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ‬
‫ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫﻲ ‪ ٣٠‬ﺜﺎﻨﻴﺔ‪ ،‬ﻭﻋﻠﻴﻙ ﺃﻥ ﺘﺯﻴﺩﻫﺎ ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻻﺴﺘﻌﻼﻡ ﻤﻌﻘـﺩﺍ‬
‫ﻴﺘﻡ ﻋﻠﻰ ﺠﺩﺍﻭل ﻜﺜﻴﺭﺓ ﻭﻴﺠﺭﻱ ﻋﻠﻴﻬﺎ ﺍﻟﻜﺜﻴﺭ ﻤﻥ ﺍﻟﻌﻤﻠﻴﺎﺕ‪ ،‬ﺃﻭ ﻜﺎﻥ ﻫﻨﺎﻙ ﻀـﻐﻁ ﻜﺒﻴـﺭ‬
‫ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ﻴﺠﻌل ﺍﺴﺘﺠﺎﺒﺘﻪ ﺒﻁﻴﺌﺔ‪.‬‬

‫ﺍﻟﻤﻌﺎﻤﻼﺕ ‪:Parameters‬‬
‫ﺘﻌﻴـــﺩ ﺃﻱ ﻜـــﺎﺌﻥ ﻴﻤﺜـــل ﻭﺍﺠﻬـــﺔ "ﻤﺠﻤﻭﻋـــﺔ ﻤﻌـــﺎﻤﻼﺕ ﺍﻟﺒﻴﺎﻨـــﺎﺕ"‬
‫‪ ،IDataParameterCollection‬ﺍﻟﺘﻲ ﺘﺭﺙ ﻭﺍﺠﻬـﺔ ﺍﻟﻘﺎﺌﻤـﺔ ‪ ..IList‬ﻭﺘﺘـﻴﺢ ﻟـﻙ‬

‫‪٦٩‬‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺇﻀﺎﻓﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﻁﻠﻭﺏ ﺍﻟﺘﻌﻭﻴﺽ ﺒﻬﺎ ﻓﻲ ﺠﻤﻠﺔ ﺍﻻﺴـﺘﻌﻼﻡ ﺃﻭ‬
‫ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬

‫ﺍﻟﺘﻌﺎﻤل ‪:Transaction‬‬
‫ﺘﺴﺘﻘﺒل ﺃﻱ ﻜﺎﺌﻥ ﻤﻥ ﻨﻭﻉ ﻭﺍﺠﻬﺔ ﺘﻌﺎﻤﻼﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ‪ ،IDbTransaction‬ﻟﻴـﺘﻡ‬
‫ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﻨﻁﺎﻗﻪ‪.‬‬

‫ﻤﺼﺩﺭ ﺍﻟﺼﻔﻭﻑ ﺍﻟﻤﺤﺩ‪‬ﺜﺔ ‪:UpdatedRowSource‬‬


‫ﺘﺤﺩﺩ ﻜﻴﻑ ﺴﻴﺘﻡ ﺘﺤﺩﻴﺙ ﺼﻔﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataRow‬ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺃﺤﺩ ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ..DataSet‬ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻔﻴﺩﺓ ﻓﻘﻁ ﻋﻨﺩﻤﺎ ﻴﻜﻭﻥ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﺍﻟﺤﺎﻟﻲ‬
‫ﻫﻭ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ‪ Update Command‬ﺍﻟﺨﺎﺹ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ ‪DbDataAdapter‬‬
‫ﺍﻟﺫﻱ ﻴﻤﻸ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻴﺤﺩﺜﻬﺎ‪ ..‬ﻭﻴﺘﻡ ﺘﻨﻔﻴﺫ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﻋﻠﻰ ﺍﻟﺼـﻔﻭﻑ ﺍﻟﺘـﻲ‬
‫ﺘﻐﻴﺭﺕ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﺤﺩﺍ ﺘﻠﻭ ﺍﻵﺨﺭ‪ ،‬ﻭﻗﺩ ﻴﺤﺘﻭﻱ ﻫﺫﺍ ﺍﻷﻤﺭ ﻋﻠﻰ ﻤﻌـﺎﻤﻼﺕ‬
‫ﺇﺨﺭﺍﺝ ‪ ،Output Parameters‬ﺃﻭ ﺘﺭﺍﻓﻘﻪ ﺠﻤﻠﺔ ‪ SELECT‬ﺘﻌﻴﺩ ﺍﻟﺴﺠل ﺒﻌﺩ ﺘﺤﺩﻴﺜـﻪ‬
‫ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﺍﻟﺤﻜﻤﺔ ﻓﻲ ﻫﺫﺍ ﺃﻥ ﻫﻨﺎﻙ ﺒﻌﺽ ﺍﻟﻘﻴﻡ ﺍﻟﺘﻲ ﺘﻭﻟﺩﻫﺎ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺒﻨﻔﺴﻬﺎ )ﻤﺜل ﻋﻤﻭﺩ ﺍﻟﺘﺭﻗﻴﻡ ﺍﻟﺘﻠﻘﺎﺌﻲ( ﻭﻻ ﻴﻤﻜﻨﻙ ﻤﻌﺭﻓﺔ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻷﻋﻤﺩﺓ ﺇﻻ ﺒﺎﻟﺤﺼـﻭل‬
‫ﻋﻠﻰ ﺍﻟﺴﺠل ﻤﺭﺓ ﺃﺨﺭﻯ ﺒﻌﺩ ﺘﺤﺩﻴﺜﻪ )ﺃﻭ ﺇﻀﺎﻓﺘﻪ(‪ ..‬ﻭﺘﺘﺤﻜﻡ ﻫﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﻓـﻲ ﻜﻴﻔﻴـﺔ‬
‫ﺍﻻﺴﺘﻔﺎﺩﺓ ﻤﻥ ﺍﻟﻘﻴﻡ ﺍﻟﻌﺎﺌﺩﺓ ﻤﻥ ﺃﻤـﺭ ﺍﻟﺘﺤـﺩﻴﺙ‪ ،‬ﻭﻫـﻲ ﺘﺄﺨـﺫ ﺇﺤـﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ‬
‫‪ UpdateRowSource‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻴﺘﻡ ﺘﺠﺎﻫل ﺃﻱ ﻤﻌﺎﻤﻼﺕ ﺃﻭ ﺼﻔﻭﻑ ﻋﺎﺌـﺩﺓ ﻤـﻥ ﺃﻤـﺭ‬ ‫‪None‬‬


‫ﺍﻟﺘﺤﺩﻴﺙ‪.‬‬
‫ﺘﻭﻀــــﻊ ﻗــــﻴﻡ ﻤﻌــــﺎﻤﻼﺕ ﺍﻹﺨــــﺭﺍﺝ‬ ‫‪OutputParameters‬‬
‫‪ Output Parameters‬ﻓﻲ ﺨﺎﻨـﺎﺕ ﺴـﺠل ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ‪.DataSet‬‬
‫‪ FirstReturnedRecord‬ﺘﻭﻀﻊ ﻗﻴﻡ ﺃﻭل ﺴﺠل ﻋﺎﺌﺩ ﻤﻥ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﻓﻲ ﺴـﺠل‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪٧٠‬‬
‫ﺘﻭﻀﻊ ﻗﻴﻡ ﻤﻌﺎﻤﻼﺕ ﺍﻹﺨﺭﺍﺝ ﻭﺃﻭل ﺴﺠل ﻋﺎﺌﺩ ﻤﻥ ﺃﻤﺭ‬ ‫‪Both‬‬
‫ﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﻓﻲ ﺴﺠل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﺠﻬﻴﺯ ‪:Prepare‬‬
‫ﺘﻨﺸﺊ ﻨﺴﺨﺔ ﻤﺤﺴﻨﺔ ﻤﺠﻬﺯﺓ ﻤﻥ ﺍﻷﻤﺭ ﻭﺘﺤﻔﻅﻬﺎ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ‪ ،‬ﻟﻴﻜﻭﻥ ﺘﻨﻔﻴـﺫﻫﺎ ﺃﺴـﺭﻉ‪..‬‬
‫ﻭﻻ ﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻼﺕ‪ ،‬ﻭﻟﻴﺱ ﻟﻬﺎ ﻗﻴﻤﺔ ﻋﺎﺌﺩﺓ‪ ،‬ﻭﻻ ﻴﻜﻭﻥ ﻟﻬـﺎ ﺃﻱ ﺘـﺄﺜﻴﺭ ﺇﻥ‬
‫ﻜﺎﻨﺕ ﻟﻠﺨﺎﺼﻴﺔ ‪ CommandType‬ﺍﻟﻘﻴﻤﺔ ‪.TableDirect‬‬
‫ﻻﺤﻅ ﺃﻥ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺼﺎﺭ ﻋﺩﻴﻡ ﺍﻟﻘﻴﻤـﺔ ﺘﻘﺭﻴﺒـﺎ‪ ،‬ﻷﻥ ﺇﺼـﺩﺍﺭﺍﺕ ﺴـﻴﻜﻭﻴل‬
‫ﺴﻴﺭﻓﺭ ‪ ٢٠٠٠‬ﻭ ‪ ٢٠٠٥‬ﻭ ‪ ٢٠٠٨‬ﺘﻘﻭﻡ ﺒﺘﺠﻬﻴﺯ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﻠﻘﺎﺌﻴﺎ ﻋﻨﺩ ﺍﻟﻠـﺯﻭﻡ‪ ،‬ﻟﺘﺤﺴـﻴﻥ‬
‫ﺍﻷﺩﺍﺀ‪.‬‬

‫ﺇﻨﺸﺎﺀ ﻤﻌﺎﻤل ‪:CreateParameter‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،IDbDataParameter‬ﻟﺘﺨﺼﺼﻪ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺃﺤـﺩ ﺍﻟﻤﻌـﺎﻤﻼﺕ‬
‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻻﺴﺘﻌﻼﻡ‪.‬‬

‫ﺘﻨﻔﻴﺫ ﺒﺩﻭﻥ ﺍﺴﺘﻌﻼﻡ ‪:ExecuteNonQuery‬‬


‫ﺘﻨﻔﺫ ﺍﻷﻤﺭ ﺩﻭﻥ ﺃﻥ ﺘﻌﻴﺩ ﺃﻴﺔ ﺴﺠﻼﺕ‪ ،‬ﻭﻟﻜﻥ ﺘﻌﻴﺩ ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ ‪ Integer‬ﻴﻤﺜـل ﻋـﺩﺩ‬
‫ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﺄﺜﺭﺕ ﺒﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ‪ ..‬ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻟﺘﻨﻔﻴﺫ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ‬
‫‪ Update‬ﻭﺍﻟﺤﺫﻑ ‪ Delete‬ﻭﺍﻹﺩﺭﺍﺝ ‪.Insert‬‬
‫ﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﻓـﻲ ﺍﻟـﺯﺭ ‪ CREATE PROC‬ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ‬
‫‪ AccessStoredProcedure‬ﺍﻟﺫﻱ ﻋﺭﻓﻨﺎ ﻤﻥ ﻗﺒل ﺃﻨﻪ ﻴﻨﺸﺊ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻨﺎ ﻓﻲ ﻗﺎﻋـﺩﺓ‬
‫ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ ﺍﻟﻤﻨﺸﺄﺓ ﺒﺘﻁﺒﻴﻕ ‪ ..Access‬ﻓﻲ ﻫﺫﺍ ﺍﻟﺯﺭ ﻨﺴﺘﺨﺩﻡ ﻜﺎﺌﻥ ﺃﻤﺭ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪ OleDbCommand‬ﻟﻠﺘﻌﺎﻤل ﻤـﻊ ﻗﺎﻋـﺩﺓ ﺒﻴﺎﻨـﺎﺕ ‪ ،Access‬ﻭﻨﺴـﺘﺨﺩﻡ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪ ExecuteNonQuery‬ﻟﺘﻨﻔﻴﺫ ﺍﺴﺘﻌﻼﻡ ‪ SQL‬ﺍﻟﺫﻱ ﻴﻨﺸﺊ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ﻓﻲ ﻗﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻷﻨﻪ ﻻ ﻴﻌﻴﺩ ﺇﻟﻴﻨﺎ ﺃﻱ ﻨﺎﺘﺞ‪.‬‬

‫‪٧١‬‬
‫ﻻﺤﻅ ﺃﻥ ﻜﻭﺩ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺘﻨﻔﻴﺫ ﺍﻷﻭﺍﻤﺭ ﻴﺘﻜﺭﺭ ﻜﺜﻴﺭﺍ‪ ،‬ﻭﻫﻭ ﻴﺒﺩﻭ ﻁـﻭﻴﻼ‬
‫ﻤﻊ ﻭﺠﻭﺩ ﺘﻌﺩﻴﻼﺕ ﻁﻔﻴﻔﺔ‪ ..‬ﻟﻬﺫﺍ ﺴﻴﻜﻭﻥ ﻤﻥ ﺍﻷﺫﻜﻰ ﻟﻭ ﺠﻤﻌﻨﺎ ﺍﻟﻜﻭﺩ ﺍﻟﻤﺘﺸﺎﺒﻪ ﻓﻲ ﺇﺠﺭﺍﺀ‬
‫ﻭﺃﺭﺴﻠﻨﺎ ﺇﻟﻴﻪ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﻲ ﺘﻨﺎﺴﺏ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺫﻱ ﻨﻨﻔﺫﻩ‪ ..‬ﻭﻟﻘﺩ ﻓﻌﻠﻨﺎ ﻫﺫﺍ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ‬
‫‪ ،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‬ﺃﻟﻴﺱ ﻫﺫﺍ ﺸﻴﺌﺎ ﻤﺭﻴﺤﺎ؟‬

‫ﺘﻨﻔﻴﺫ ﻗﺎﺭﺉ ‪:ExecuteReader‬‬


‫ﺘﻨﻔﺫ ﺍﻷﻤﺭ‪ ،‬ﻭﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،IDataReader‬ﺤﻴﺙ ﻴﻤﻜﻨـﻙ ﺍﺴـﺘﺨﺩﺍﻤﻪ ﻟﻘـﺭﺍﺀﺓ‬
‫ﺍﻟﻨﺘﻴﺠﺔ ﺴﺠﻼ ﺘﻠﻭ ﺴﺠلّ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻻﺤﻘﺎ‪.‬‬
‫ﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪.AuthorBooks_Reader‬‬
‫ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺼﻴﻐﺔ ﺜﺎﻨﻴﺔ‪ ،‬ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤـﺭﻗﻡ ‪،CommandBehavior‬‬
‫ﺍﻟﺫﻱ ﻴﺤﺩﺩ ﺴﻠﻭﻙ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬

‫ﺍﻟﺴﻠﻭﻙ ﺍﻟﻌﺎﺩﻱ‪ ،‬ﺤﻴﺙ ﻴﻤﻜﻥ ﺃﻥ ﻴﺅﺩﻱ ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ﺇﻟـﻰ ﺍﻟﺤﺼـﻭل‬ ‫‪Default‬‬
‫ﻋﻠﻰ ﺃﻜﺜﺭ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﻤﻥ ﻤﺠﻤﻭﻋﺎﺕ ﺍﻟﻨﺘﺎﺌﺞ ‪) Result Sets‬ﻜﻤﺎ‬
‫ﻴﺤﺩﺙ ﻓﻲ ﺤﺎﻟﺔ ﺘﻨﻔﻴﺫ ﺃﻜﺜﺭ ﻤﻥ ﺠﻤﻠﺔ ‪ SQL‬ﻤـﻥ ﺩﺍﺨـل ﺇﺠـﺭﺍﺀ‬
‫ﻤﺨﺯﻥ(‪ ..‬ﻫﺫﺍ ﻤﻜﺎﻓﺊ ﻻﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ ExecuteReader‬ﺒـﺩﻭﻥ‬
‫ﻤﻌﺎﻤﻼﺕ‪.‬‬

‫‪٧٣‬‬
‫ﻴﺅﺩﻱ ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ ﺇﻟﻰ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﻨﺘـﺎﺌﺞ ﻭﺍﺤـﺩﺓ‬ ‫‪SingleResult‬‬

‫ﻓﻘﻁ‪.‬‬
‫‪ SchemaOnly‬ﻴﺅﺩﻱ ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ ﺇﻟﻰ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻷﻋﻤﺩﺓ ﻓﻘـﻁ‬
‫ﺒﺩﻭﻥ ﺃﻱ ﺴﺠﻼﺕ‪ ..‬ﻫﺫﺍ ﻴﻌﻨﻲ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﺠـﺩﻭل ﻓـﺎﺭﻍ ﺒـﻪ‬
‫ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﻓﻘﻁ‪ ..‬ﻫﺫﺍ ﻤﻤﺎﺜل ﻻﺴﺘﺨﺩﺍﻡ ﻤﺯﻭﺩ ﺴﻴﻜﻭﻴل ﻟﻠﺨﻴﺎﺭ‪:‬‬
‫‪SET FMTONLY ON‬‬
‫ﻴﺅﺩﻱ ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ ﺇﻟﻰ ﺍﻟﺤﺼـﻭل ﻋﻠـﻰ ﻤﻌﻠﻭﻤـﺎﺕ ﺍﻷﻋﻤـﺩﺓ‬ ‫‪KeyInfo‬‬
‫ﻭﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ‪.Primary Key‬‬
‫ﻴﺅﺩﻱ ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ ﺇﻟﻰ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﺴﺠل ﻭﺍﺤﺩ ﻓﻘـﻁ‪ ،‬ﻭﻟـﻭ‬ ‫‪SingleRow‬‬

‫ﻜﺎﻥ ﺍﻻﺴﺘﻌﻼﻡ ﻴﺤﺼل ﻋﻠﻰ ﺃﻜﺜﺭ ﻤـﻥ ﻤﺠﻤﻭﻋـﺔ ﻤـﻥ ﺍﻟﻨﺘـﺎﺌﺞ‪،‬‬


‫ﻓﺴﻴﻜﻭﻥ ﺒﻜل ﻤﺠﻤﻭﻋﺔ ﻤﻨﻬﺎ ﺴﺠﻼ ﻭﺍﺤﺩﺍ ﻓﻘـﻁ‪ ..‬ﺍﺴـﺘﺨﺩﻡ ﻫـﺫﺍ‬
‫ﺍﻻﺨﺘﻴﺎﺭ ﻋﻨﺩﻤﺎ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺃﻭ ﺴﺠل ﻓﻘﻁ‪ ،‬ﻓﻬﺫﺍ ﻴﺅﺩﻱ ﺇﻟﻰ ﺘﺤﺴـﻴﻥ‬
‫ﺃﺩﺍﺀ ﻭﺴﺭﻋﺔ ﺍﻟﺒﺭﻨﺎﻤﺞ‪.‬‬
‫ﻗﺭﺍﺀﺓ ﺘﺘﺎﺒﻌﻴﺔ‪ ..‬ﻫﺫﺍ ﻤﻔﻴﺩ ﻟﻠﻘﺭﺍﺀﺓ ﻤﻥ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺘﺤـﻭﻱ ﻗـﺩﺭﺍ‬ ‫‪Sequential‬‬
‫‪Access‬‬
‫ﻀﺨﻤﺎ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻓﺒﺩﻻ ﻤﻥ ﻁﻠﺒﻬﺎ ﻜﻠﻬﺎ ﻤﻥ ﺍﻟﺨﺎﺩﻡ‪ ،‬ﻴـﺘﻡ ﻁﻠـﺏ‬
‫ﺃﺠﺯﺍﺀ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻘﻁ ﺘﺒﻌﺎ ﻻﺤﺘﻴﺎﺠﻙ‪ ..‬ﻻﺤﻅ ﺍﻵﺘﻲ‪:‬‬
‫‪ -‬ﻴﺠﺏ ﻋﻠﻴﻙ ﻗﺭﺍﺀﺓ ﻗﻴﻡ ﺍﻟﺤﻘﻭل ﺒﻨﻔﺱ ﺘﺭﺘﻴﺒﻬﺎ ﻓﻲ ﺍﻻﺴﺘﻌﻼﻡ‪ ،‬ﻷﻨﻙ‬
‫ﻟﻭ ﻗﺭﺃﺕ ﺃﻱ ﺤﻘل‪ ،‬ﻓﻠﻥ ﺘﺴﺘﻁﻴﻊ ﻗﺭﺍﺀﺓ ﺍﻟﺤﻘل ﺍﻟﺴﺎﺒﻕ ﻟـﻪ ﻤـﺭﺓ‬
‫ﺃﺨﺭﻯ‪ ،‬ﻓﻨﺤﻥ ﻫﻨﺎ ﻨﻘﺭﺃ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺘﺎﺒﻌﻴﺎ‪ ،‬ﺃﻱ ﺒﺎﻟﺘﺭﺘﻴﺏ‪.‬‬
‫‪ -‬ﺍﺴﺘﺨﺩﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ GetValue‬ﻟﻘﺭﺍﺀﺓ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓـﻲ ﺃﻱ‬
‫ﺤﻘل ﻜﺎﻤﻠﺔ‪.‬‬
‫‪ -‬ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ GetBytes‬ﺍﻟﺨﺎﺼﺔ ﺒﻘﺎﺭﺉ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻟﻘـﺭﺍﺀﺓ‬
‫ﺃﺠﺯﺍﺀ ﻤﻥ ﺍﻟﺤﻘل ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺒﻴﺎﻨﺎﺕ ﺜﻨﺎﺌﻴﺔ ﻀﺨﻤﺔ‪ ،‬ﻤﺜـل‬
‫‪ image‬ﻭ )‪.varbinary(MAX‬‬
‫‪ -‬ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ GetChars‬ﺍﻟﺨﺎﺼﺔ ﺒﻘﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻘـﺭﺍﺀﺓ‬
‫‪٧٤‬‬
‫ﺃﺠﺯﺍﺀ ﻤﻥ ﺍﻟﺤﻘل ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻨﺼﻭﺹ ﻀـﺨﻤﺔ‪ ،‬ﻤﺜـل‬
‫)‪varchar(MAX‬‬ ‫ﻭ‬ ‫‪ntext‬‬ ‫ﻭ‬ ‫‪text‬‬
‫ﻭ )‪.nvarchar(MAX‬‬
‫ﻭﺍﻟﻤﺸﺭﻭﻉ ‪ ReadLargeData‬ﻴﻘﺭﺃ ﺍﻟﺼﻭﺭ ﺍﻟﺨﺎﺼﺔ ﺒﺸﻌﺎﺭ ﻜـل‬
‫ﻨﺎﺸﺭ ﻤﻥ ﺍﻟﺠﺩﻭل ‪ ،Publishers‬ﻭﻴﺤﻔﻅﻬﺎ ﻓﻲ ﻤﻠﻑ ﻋﻠﻰ ﺍﻟﺠﻬﺎﺯ‪..‬‬
‫ﻭﻨﻅﺭﺍ ﻷﻥ ﺍﻟﺼﻭﺭﺓ ﻗﺩ ﺘﻜﻭﻥ ﻀﺨﻤﺔ‪ ،‬ﻓﻘﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﺍ ﺍﻟﺨﻴـﺎﺭ‬
‫ﻟﻨﻘﺭﺃ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺘﺎﺒﻌﻴﺎ‪ ..‬ﻭﻴﺭﻴﻙ ﻫﺫﺍ ﺍﻟﻤﺸـﺭﻭﻉ ﺃﻥ ﻫـﺫﻩ ﺍﻟﻁﺭﻴﻘـﺔ‬
‫ﺘﺼﻠﺢ ﻟﻠﻘﺭﺍﺀﺓ ﻤﻥ ﺍﻟﺤﻘل ‪ Logo‬ﺍﻟﺫﻱ ﻨﻭﻋـﻪ ‪ ،image‬ﻭﺘﺼـﻠﺢ‬
‫ﺃﻴﻀــﺎ ﻟﻠﻘــﺭﺍﺀﺓ ﻤــﻥ ﺍﻟﺤﻘــل ‪ Logo2‬ﺍﻟــﺫﻱ ﻨﻭﻋــﻪ‬
‫)‪.varbinary(MAX‬‬
‫ﻴﺘﻡ ﺇﻏﻼﻕ ﺍﻻﺘﺼﺎل ‪ Connection‬ﻤﻊ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻟﻴﺎ‪ ،‬ﺒﻤﺠﺭﺩ‬ ‫‪Close‬‬
‫‪Connection‬‬
‫ﺇﻏﻼﻕ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺃﻜﺜﺭ ﻤﻥ ﻗﻴﻤﺔ ﻤﻥ ﻫﺫﻩ ﺍﻟﻘﻴﻡ‪ ،‬ﺒﺭﺒﻁﻬﺎ ﻤﻌﺎ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﻌﺎﻤل |‪.‬‬
‫ﻭﻗﺩ ﺃﻀﻔﻨﺎ ﻭﺴﻴﻠﺔ ﺍﺴﻤﻬﺎ ‪ 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‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺎﺼﻴﺔ‬
‫ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﺭﺌﻴﺔ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ ‪:DesignTimeVisible‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪) true‬ﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ(‪ ،‬ﻓﺴﻴﻅﻬﺭ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﻓﻲ‬
‫ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ ﻓﻲ ﻭﺍﺠﻬﺔ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺘﻲ ﺘﺴﺘﺨﺩﻤﻪ‪.‬‬

‫ﻭﻻ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﻱ ﻭﺴﺎﺌل ﺠﺩﻴﺩﺓ ﻏﻴﺭ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﻭﺴﺎﺌل ﺍﻟﻭﺍﺠﻬﺔ ‪.IDbCommand‬‬
‫ﻻﺤﻅ ﺃﻥ ﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪:DbCommand‬‬
‫‪.OdbcCommand Class .١‬‬
‫‪.OleDbCommand Class .٢‬‬
‫‪.SqlCommand Class .٣‬‬
‫‪.OracleCommand Class .٤‬‬
‫ﻭﺴﻨﻜﺘﻔﻲ ﻫﻨﺎ ﺒﺎﻟﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ ‪.SqlCommand‬‬

‫‪٧٧‬‬
‫ﻓﺌﺔ ﺃﻤﺭ ﺴﻴﻜﻭﻴل ‪SqlCommand Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ ،DbCommand‬ﻭﻫﻲ ﻤﺨﺼﺼﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺍﻷﻭﺍﻤﺭ ﺍﻟﺘﻲ ﻴﺘﻡ ﺘﻨﻔﻴـﺫﻫﺎ‬
‫ﻋﻠﻰ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪.‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺭﺒﻊ ﺼﻴﻎ ﻤﺨﺘﻠﻔﺔ‪:‬‬
‫‪ .١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ .٢‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺎ ﻤﻌﺎﻤل ﻭﺍﺤﺩ‪ ،‬ﻴﺴﺘﻘﺒل ﻨﺹ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺫﻱ ﺴﻴﻭﻀﻊ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ‬
‫‪.CommandText‬‬
‫‪ .٣‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ ﻤﻥ ﺍﻟﻨﻭﻉ ‪،SqlConnection‬‬
‫ﻴﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ﻤﻥ ﺨﻼﻟﻪ‪.‬‬
‫‪ .٤‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻷﺨﻴﺭﺓ ﺘﺯﻴـﺩ ﻋﻠـﻰ ﺍﻟﺼـﻴﻐﺔ ﺍﻟﺴـﺎﺒﻘﺔ ﺒﻤﻌﺎﻤـل ﺜﺎﻟـﺙ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪ ،SqlTransaction‬ﻴﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﺘﻌﺎﻤل ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ﻓﻲ ﻨﻁﺎﻗﻪ‪.‬‬
‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﺍﻟﺘﻨﺒﻴﻪ ‪:Notification‬‬
‫ﺘﺤﺩﺩ ﻜﺎﺌﻥ ﻁﻠﺏ ﺍﻟﺘﻨﺒﻴﻪ ‪ SqlNotificationRequest‬ﺍﻟﺫﻱ ﺴﻴﺴﺘﺨﺩﻤﻪ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﻓﻲ‬
‫ﺘﻠﻘﻲ ﺍﻟﺘﻨﺒﻴﻬـﺎﺕ ﻤـﻥ ﺍﻟﺨـﺎﺩﻡ ﻋﻨـﺩ ﺘﻨﻔﻴـﺫ ﺍﻻﺴـﺘﻌﻼﻡ‪ ..‬ﻭﺴـﻨﺘﻌﺭﻑ ﻋﻠـﻰ ﺍﻟﻔﺌـﺔ‬
‫‪ SqlNotificationRequest‬ﻻﺤﻘﺎ‪.‬‬

‫ﻀﻡ ﺘﻠﻘﺎﺌﻲ ﺇﻟﻰ ﻗﺎﺌﻤﺔ ﺍﻟﺘﻨﺒﻴﻬﺎﺕ ‪:NotificationAutoEnlist‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،true‬ﻓﺴﻴﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﺘﻨﺒﻴﻬﺎﺕ ﺘﻠﻘﺎﺌﻴﺔ ﻤـﻥ ﻜـﺎﺌﻥ‬
‫ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺘﺒﻌﻴﺔ ‪ ..SqlDependency‬ﻭﺘﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻊ ﺼـﻔﺤﺎﺕ ﺍﻟﻤﻭﺍﻗـﻊ‬
‫ﻓﻲ ‪ ASP.NET‬ﻟﺘﺘﻴﺢ ﻋﺭﺽ ﺍﻟﺼﻔﺤﺔ ﺍﻟﻤﺠﻬﺯﺓ ‪ Cashed Page‬ﺇﻟﻰ ﺃﻥ ﻴـﺄﺘﻲ ﺘﻨﺒﻴـﻪ‬
‫ـﺔ‬
‫ـﻰ ﺍﻟﻔﺌـ‬
‫ـﻨﺘﻌﺭﻑ ﻋﻠـ‬
‫ـﻬﺎ‪ ..‬ﻭﺴـ‬
‫ـﺘﻡ ﺇﻨﻌﺎﺸـ‬
‫ـﺎ ﻓﻴـ‬
‫ـﺽ ﺒﻴﺎﻨﺎﺘﻬـ‬
‫ـﻲ ﺒﻌـ‬
‫ـﺭ ﻓـ‬
‫ـﺩﻭﺙ ﺘﻐﻴـ‬
‫ﺒﺤـ‬
‫‪ SqlDependency‬ﻻﺤﻘﺎ‪.‬‬

‫‪٧٨‬‬
‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻨﺴﺦ ‪:Clone‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ‪ SqlCommand‬ﺠﺩﻴﺩﺍ ﻤﻤﺎﺜﻼ ﻓﻲ ﻜل ﺸﻲﺀ ﻟﻠﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﺘﺼﻔﻴﺭ ﺯﻤﻥ ﺍﻨﺘﻅﺎﺭ ﺍﻷﻤﺭ ‪:ResetCommandTimeout‬‬


‫ﺘﻌﻴﺩ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪ CommandTimeout‬ﺇﻟﻰ ﻗﻴﻤﺘﻬﺎ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ‪ ٣٠‬ﺜﺎﻨﻴﺔ‪.‬‬

‫"ﺘﻨﻔﻴﺫ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ‪:ExecuteXmlReader "XML‬‬


‫ﺘﻨﻔﺫ ﺍﻷﻤﺭ‪ ،‬ﻭﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،XMLReader‬ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺘﻨﺴـﻴﻕ‬
‫‪ ..XML‬ﻻﺤﻅ ﺃﻥ ﻋﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﺤﺎﻻﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -‬ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻔﻘﺭﺓ ‪ FOR XML‬ﻓﻲ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ‪ ..‬ﻫﺫﻩ ﺍﻟﻔﻘﺭﺓ ﺘﻌﻴﺩ ﻨـﺎﺘﺞ‬
‫ﺍﻻﺴﺘﻌﻼﻡ ﻓﻲ ﺼﻭﺭﺓ ﻭﺜﻴﻘﺔ ‪.XML‬‬
‫‪ -‬ﻋﻨﺩ ﻗﺭﺍﺀﺓ ﻋﻤﻭﺩ ﻨﻭﻉ ﺒﻴﺎﻨﺎﺘﻪ ‪.XML‬‬
‫‪ -‬ﻋﻨﺩ ﻗﺭﺍﺀﺓ ﻋﻤﻭﺩ ﻨﻭﻉ ﺒﻴﺎﻨﺎﺘﻪ ‪ ntext‬ﺃﻭ ‪ nvarchar‬ﻟﻜﻨﻪ ﻴﺤﺘﻭﻱ ﻋﻠـﻰ ﺒﻴﺎﻨـﺎﺕ‬
‫ﺒﺘﻨﺴﻴﻕ ‪.XML‬‬
‫ﻭﻟﻥ ﻨﺘﻁﺭﻕ ﺇﻟﻰ ﻓﺌﺎﺕ ‪ XML‬ﻓﻲ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ‪ ،‬ﻭﺴﻨﻔﺭﺩ ﻟﻬﺎ ﻜﺘﺎﺒﺎ ﻤﺴﺘﻘﻼ ﺇﻥ ﻗﺩﺭ ﺍﷲ‪.‬‬

‫ﻭﺘـــﺩﻋﻡ ﺍﻟﻔﺌـــﺔ ‪ SqlCommand‬ﺍﺴـــﺘﺨﺩﺍﻡ ﺍﻟﻌﻤﻠﻴـــﺎﺕ ﻏﻴـــﺭ ﺍﻟﻤﺘﺯﺍﻤﻨـــﺔ‬


‫‪ Asynchronous Operations‬ﻟﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ‪ ،‬ﻭﺫﻟﻙ ﻤﻥ ﺨﻼل ﺃﺯﻭﺍﺝ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪EndExecuteXmlReader‬‬ ‫‪BeginExecuteXmlReader‬‬
‫‪EndExecuteNonQuery‬‬ ‫‪BeginExecuteNonQuery‬‬
‫‪EndExecuteReader‬‬ ‫‪BeginExecuteReader‬‬
‫ﺤﻴﺙ ﺘﻘﻭﻡ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﻲ ﺘﺒﺩﺃ ﺒﺎﻟﻜﻠﻤﺔ ‪ Begin‬ﺒﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ ﻓﻲ ﻋﻤﻠﻴﺔ ﻏﻴﺭ ﻤﺘﺯﺍﻤﻨﺔ‪ ،‬ﻭﻴﻤﻜﻨـﻙ‬
‫ﺃﻥ ﺘﺭﺴل ﺇﻟﻴﻬﺎ ﻤﻨﺩﻭﺒﺎ ﻋﻥ ﺇﺠﺭﺍﺀ ﻴﺘﻡ ﺍﺴﺘﺩﻋﺎﺅﻩ ﺒﻌﺩ ﺍﻨﺘﻬﺎﺀ ﺍﻟﻌﻤﻠﻴﺔ ﻟﺘﻘﺭﺃ ﻓﻴﻪ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻨﺎﺘﺠﺔ‪..‬‬
‫‪٧٩‬‬
‫ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﻜﺎﺌﻨﺎ ﻴﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ IAsyncResult‬ﻟﻴﻤﻜﻨـﻙ ﺍﺴـﺘﺨﺩﺍﻤﻪ ﻓـﻲ ﻤﺘﺎﺒﻌـﺔ‬
‫ﺍﻟﻌﻤﻠﻴﺔ‪ ،‬ﻜﻤﺎ ﻴﻤﻜﻥ ﺇﺭﺴﺎﻟﻪ ﺇﻟﻰ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﻲ ﺘﺒﺩﺃ ﺒﺎﻟﻜﻠﻤﺔ ‪ End‬ﻹﻨﻬﺎﺀ ﺍﻟﻌﻤﻠﻴﺔ ﻏﻴﺭ ﺍﻟﻤﺘﺯﺍﻤﻨـﺔ‬
‫ﻭﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻨﺘﺎﺌﺠﻬﺎ‪.‬‬
‫ﻭﺘﻤﺘﺎﺯ ﺍﻟﻌﻤﻠﻴﺎﺕ ﻏﻴﺭ ﺍﻟﻤﺘﺯﺍﻤﻨﺔ ﺒﺄﻨﻬﺎ ﻻ ﺘﻭﻗﻑ ﺘﻨﻔﻴﺫ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﻟﻰ ﺤﻴﻥ ﺍﻨﺘﻬﺎﺀ ﺇﺘﻤﺎﻡ ﺍﻟﻌﻤﻠﻴـﺔ‪،‬‬
‫ﺒل ﻴﻨﺘﻘل ﺍﻟﺘﻨﻔﻴﺫ ﺇﻟﻰ ﺍﻟﺴﻁﺭ ﺍﻟﺘﺎﻟﻲ ﻤﺒﺎﺸﺭﺓ‪ ،‬ﺒﻴﻨﻤﺎ ﺘﺭﺴل ﺍﻟﻨﺘﺎﺌﺞ ﻓـﻭﺭ ﺘﻭﻓﺭﻫـﺎ ﺇﻟـﻰ ﺍﻟﺩﺍﻟـﺔ‬
‫ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﺤﺼﻭل ﻋﻠﻰ ﺍﻟﻨﺘﺎﺌﺞ ‪ ..Callback Function‬ﻭﺘﻘﻊ ﺍﻟﻌﻤﻠﻴـﺎﺕ ﻏﻴـﺭ ﺍﻟﻤﺘﺯﺍﻤﻨـﺔ‬
‫ﺨﺎﺭﺝ ﻨﻁﺎﻕ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ‪ ،‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﺒﺎﻟﺘﻔﺼﻴل ﺒﺈﺫﻥ ﺍﷲ ﻓﻲ ﻜﺘﺎﺏ ﺍﻟﻤﻭﺍﻀﻴﻊ ﺍﻟﻤﺘﻘﺩﻤـﺔ‬
‫ﻓﻲ ﺒﺭﻤﺠﺔ ﺇﻁﺎﺭ ﺍﻟﻌﻤل‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﺍﻟﻔﺌﺔ ‪ SqlCommand‬ﺍﻟﺤﺩﺙ ﺍﻟﺘﺎﻟﻲ‪:‬‬

‫ﺍﻜﺘﻤﻠﺕ ﺍﻟﺠﻤﻠﺔ ‪:StatementCompleted‬‬


‫ﻴﻨﻁﻠﻕ ﻋﻨﺩ ﺍﻜﺘﻤﺎل ﺘﻨﻔﻴﺫ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻷﻤﺭ‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ‬
‫ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،StatementCompletedEventArgs‬ﻭﻫﻭ ﻴﻤﺘﻠﻙ ﺨﺎﺼﻴﺔ ﻭﺍﺤـﺩﺓ‬
‫ﻫﻲ "ﻋﺩﺩ ﺍﻟﺴﺠﻼﺕ" ‪ ،RecordCount‬ﺍﻟﺘﻲ ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘـﺄﺜﺭﺕ ﺒﺘﻨﻔﻴـﺫ‬
‫ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ‪.‬‬

‫‪٨٠‬‬
‫ﺘﻤﺭﻴﺭ ﺍﻟﻘﻴﻡ ﺇﻟﻰ ﺠﻤل ﺍﻻﺴﺘﻌﻼﻡ‪:‬‬
‫ﺍﻓﺘﺭﺽ ﺃﻨﻙ ﺘﺭﻴﺩ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻜﺘﺏ "ﺘﻭﻓﻴﻕ ﺍﻟﺤﻜﻴﻡ" ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻓﻲ ﻫـﺫﻩ ﺍﻟﺤﺎﻟـﺔ‬
‫ﻴﻤﻜﻨﻙ ﻭﻀﻊ ﺠﻤﻠﺔ ‪ 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‬‬

‫ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDataParameterCollection‬ﺍﻟﺘﻲ ﺘﺭﺙ ﻭﺍﺠﻬـﺔ ﺍﻟﻘﺎﺌﻤـﺔ‬


‫‪ ..IList‬ﻭﻜل ﻋﻨﺼﺭ ﻴﻀﺎﻑ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ ‪ DbParameterCollection‬ﻫﻭ ﻤـﻥ ﻨـﻭﻉ‬
‫ﺍﻟﻔﺌﺔ ‪ DbParameter‬ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ‪.‬‬
‫ﻭﻻ ﺘﺤﺘﻭﻱ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻋﻠﻰ ﺃﻴﺔ ﺨﺼﺎﺌﺹ ﺃﻭ ﻭﺴﺎﺌل ﺠﺩﻴﺩﺓ ﻏﻴﺭ ﻤـﺎ ﺘﻤﺜﻠـﻪ ﻤـﻥ ﻋﻨﺎﺼـﺭ‬
‫ﺍﻟﻭﺍﺠﻬﺔ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﺍﻟﻔﺌﺔ ‪ DbParameterCollection‬ﺃﺴﺎﺴﻴﺔ ﻤﺠﺭﺩﺓ ﺘﺠﺏ ﻭﺭﺍﺜﺘﻬﺎ‪ ،‬ﻟﻬﺫﺍ ﺘﺭﺜﻬﺎ ﻜـل‬
‫ﻤﻥ ﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪OdbcParameterCollection Class .١‬‬
‫‪OleDbParameterCollection Class .٢‬‬
‫‪SqlParameterCollection Class .٣‬‬
‫‪OracleParameterCollection Class .٤‬‬
‫ـﻴﻜﻭﻴل‬
‫ـﺎﻤﻼﺕ ﺴــ‬
‫ـﺔ ﻤﻌــ‬
‫ـﺔ ﻤﺠﻤﻭﻋــ‬
‫ـﻰ ﻓﺌــ‬
‫ـﺭﻑ ﻋﻠــ‬‫ـﺎ ﺃﻥ ﻨﺘﻌــ‬
‫ـﺎ ﻫﻨــ‬ ‫ﻭﻴﻌﻨﻴﻨــ‬
‫‪.SqlParameterCollection Class‬‬

‫‪٨٩‬‬
‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﻤﻌﺎﻤﻼﺕ ﺴﻴﻜﻭﻴل‬
‫‪SqlParameterCollection Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ ،DbParameterCollection‬ﻭﻫﻲ ﻻ ﺘﺨﺘﻠﻑ ﻋﻨﻬﺎ ﻜﺜﻴﺭﺍ ﺇﻻ ﻓـﻲ ﺃﻥ‬
‫ﻋﻨﺎﺼﺭﻫﺎ ﻤﻥ ﻨﻭﻉ ﺍﻟﻔﺌﺔ ‪ ، SqlParameter‬ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﺒﻌﺩ ﻗﻠﻴل‪.‬‬
‫ﻜﻤﺎ ﺃﻥ ﻟﻠﻭﺴﻴﻠﺔ ‪ Add‬ﺍﻟﺨﺎﺼﺔ ﺒﻬﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻌﺩﻴﺩ ﻤﻥ ﺍﻟﺼﻴﻎ ﺍﻟﺘﻲ ﻤﻥ ﺍﻟﻤﻔﻴﺩ ﺃﻥ ﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ‪:‬‬

‫ﺇﻀﺎﻓﺔ ‪:Add‬‬
‫ﺘﻀﻴﻑ ﻤﻌﺎﻤﻼ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ‪ ،‬ﻭﻟﻬﺎ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻭﺍﺤﺩﺍ ﻤﻥ ﺍﻟﻨﻭﻉ ‪.SqlParameter‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻨﺼـﺎ ﻴﻤﺜـل ﺍﺴـﻡ ﺍﻟﻤﻌﺎﻤـل ﻭﺇﺤـﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ‬
‫‪ SqlDbType‬ﺍﻟﺘﻲ ﺘﻭﻀﺢ ﻨﻭﻉ ﺍﻟﻤﻌﺎﻤل‪.‬‬
‫‪ -٣‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻟﺙ ﻤـﻥ ﺍﻟﻨـﻭﻉ ‪،Integer‬‬
‫ﻴﺴﺘﻘﺒل ﺤﺠﻡ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺴﺘﻭﻀﻊ ﻓﻲ ﺍﻟﻤﻌﺎﻤل‪.‬‬
‫‪ -٤‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل‪ ،‬ﻴﺴﺘﻘﺒل ﺍﺴـﻡ ﻋﻤـﻭﺩ ﻓـﻲ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataSet‬ﻟﻴﺭﺒﻁ ﺍﻟﻤﻌﺎﻤل ﺒﻪ‪.‬‬
‫‪ -٥‬ﻭﻫﻨﺎﻙ ﺼﻴﻐﺔ ﺨﺎﻤﺴﺔ ﻟﻜﻨﻬﺎ ﻟﻡ ﻴﻌﺩ ﻤﻥ ﺍﻟﻤﻨﺼـﻭﺡ ﺍﺴـﺘﺨﺩﺍﻤﻬﺎ‪ ،‬ﺘﺴـﺘﻘﺒل ﺍﺴـﻡ‬
‫ﺍﻟﻤﻌﺎﻤل‪ ،‬ﻭﻜﺎﺌﻨﺎ ‪ Object‬ﻴﺤﻤل ﻗﻴﻤﺘﻪ‪ ،‬ﻭﺫﻟﻙ ﺒﺴـﺒﺏ ﺍﻟﺘﻌـﺎﺭﺽ ﺒﻴﻨﻬـﺎ ﻭﺒـﻴﻥ‬
‫ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ‪ ،‬ﻟﻬﺫﺍ ﺘﻡ ﺇﻀﺎﻓﺔ ﻭﺴﻴﻠﺔ ﺠﺩﻴﺩﺓ ﺍﺴﻤﻬﺎ ‪ AddWithValue‬ﻜﺒـﺩﻴل‬
‫ﻟﻬﺫﻩ ﺍﻟﺼﻴﻐﺔ‪.‬‬

‫ﺇﻀﺎﻓﺔ ﺒﺎﻟﻘﻴﻤﺔ ‪:AddWithValue‬‬


‫ﺘﻀﻴﻑ ﻤﻌﺎﻤﻼ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻤﻌﺎﻤـل‪ ،‬ﻭﻜﺎﺌﻨـﺎ ‪ Object‬ﻴﺤﻤـل‬
‫ﻗﻴﻤﺘﻪ‪ ..‬ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﺭﺠﻌﺎ ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺫﻱ ﺘﻡ ﺇﻨﺸﺎﺅﻩ‪.‬‬

‫‪٩٠‬‬
‫ﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺃﻥ ﺘﺭﺴل ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤـل ﺍﻟﺜـﺎﻨﻲ ﻟﻬـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﻗـﺎﺭﺉ ﺒﻴﺎﻨـﺎﺕ‬
‫‪ DataReader‬ﺃﻭ ﺠﺩﻭل ﺒﻴﺎﻨﺎﺕ ‪ ،DataTable‬ﻟﺘﺘﻡ ﻗﺭﺍﺀﺓ ﻜل ﺍﻟﺼـﻔﻭﻑ ﺍﻟﻤﻭﺠـﻭﺩﺓ‬
‫ﻓﻴﻬﻤﺎ ﻭﻭﻀﻌﻬﺎ ﻓﻲ ﺍﻟﻤﻌﺎﻤل‪ ..‬ﻫﺫﺍ ﻤﻔﻴﺩ ﺇﺫﺍ ﻜﻨﺕ ﺘﺘﻌﺎﻤل ﻤﻊ ﺇﺠـﺭﺍﺀ ﻤﺨـﺯﻥ ﻴﺴـﺘﻘﺒل‬
‫ﻤﻌﺎﻤﻼ ﺠﺩﻭﻻ ‪ ،Table-Valued Parameter‬ﻭﺘﺭﻴﺩ ﺃﻥ ﺘﺭﺴل ﺇﻟﻴﻪ ﺠـﺩﻭﻻ ﻜـﺎﻤﻼ‪..‬‬
‫ﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫـﺫﺍ ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ ‪ ..TableValuedParameters‬ﻓـﻲ ﻫـﺫﺍ‬
‫ﺍﻟﻤﺸﺭﻭﻉ ﻨﻘﺭﺃ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻓﻲ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،OledbDataReader‬ﺜـﻡ‬
‫ﻨﺭﺴﻠﻪ ﻜﻤﻌﺎﻤل ﺜﺎﻥ ﺇﻟﻰ ﺍﻟﻭﺴﻴﻠﺔ ‪ AddWithValue‬ﻻﺴـﺘﺨﺩﺍﻤﻪ ﻜﻤﻌﺎﻤـل ﻟﻺﺠـﺭﺍﺀ‬
‫ﺍﻟﻤﺨﺯﻥ ‪ ،InsertAuthors‬ﻭﺒﻬﺫﺍ ﻨﺴﺘﻁﻴﻊ ﺇﻀﺎﻓﺔ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻤﻥ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨـﺎﺕ ﺁﻜﺴـﻴﺱ‬
‫ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺠﺏ ﺃﻥ ﻴﻅل ﻤﻔﺘﻭﺤﺎ ﻫﻭ ﻭﺍﻻﺘﺼـﺎل ﺍﻟـﺫﻱ ﻴﺴـﺘﻨﺨﺩﻤﻪ‪ ،‬ﻷﻥ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪ AddWithValue‬ﻻ ﺘﻨﺴﺦ ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻌﻠﻴﺎ‪ ،‬ﻭﻻ ﻴﺘﻡ ﻨﺴـﺦ‬
‫ﻫﺫﻩ ﺍﻟﺴﺠﻼﺕ ﺇﻻ ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ ExecuteNonQuery‬ﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻷﻤـﺭ‬
‫ﺍﻟﺫﻱ ﻴﻨﻔﺫ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ﻭﻴﺭﺴل ﺇﻟﻪ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺠﺩﻭل‪.‬‬

‫‪٩١‬‬
‫ﻭﺍﺠﻬﺔ ﻤﻌﺎﻤل ﺍﻟﺒﻴﺎﻨﺎﺕ ‪IDataParameter Interface‬‬

‫ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﻤﻌﺭﻓﺔ ﻓﻲ ﺍﻟﻨﻁﺎﻕ ‪ ،System.Data‬ﻭﻫﻲ ﺘﻤﺘﻠﻙ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﺴﻡ ﺍﻟﻤﻌﺎﻤل ‪:ParameterName‬‬


‫ﺘﺤﺩﺩ ﺍﺴﻡ ﺍﻟﻤﻌﺎﻤل‪ ،‬ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻨﻪ ﻴﺒﺩﺃ ﺒﺎﻟﺭﻤﺯ @‪ ،‬ﻤﺜل ‪.@UserName‬‬

‫ﻫل ﻫﻭ ﻤﻨﻌﺩﻡ ‪:IsNullable‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،true‬ﻓﺴـﻴﻘﺒل ﺍﻟﻤﻌﺎﻤـل ﺍﻟﻘﻴﻤـﺔ ‪ ..DBNull‬ﻭﺍﻟﻘﻴﻤـﺔ‬
‫ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻫﻲ ‪.false‬‬

‫ﺍﻻﺘﺠﺎﻩ ‪:Direction‬‬
‫ﺘﺤﺩﺩ ﺍﺘﺠﺎﻩ ﺍﻟﻤﻌﺎﻤل‪ ،‬ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ ParameterDirection‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﻌﺎﻤل ﺇﺩﺨﺎل‪ ،‬ﻹﺭﺴﺎل ﻗﻴﻤﺔ ﺇﻟﻰ ﺍﻻﺴﺘﻌﻼﻡ ﺃﻭ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ‪.‬‬ ‫‪Input‬‬


‫ﻤﻌﺎﻤل ﺇﺨﺭﺍﺝ‪ ،‬ﻟﻘﺭﺍﺀﺓ ﻗﻴﻤﺔ ﻤﺘﻐﻴﺭ ﺇﺨﺭﺍﺝ ﻤﻥ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨـﺯﻥ‪ ..‬ﻫـﺫﺍ‬ ‫‪Output‬‬
‫ﺍﻟﻨﻭﻉ ﻏﺭ ﻤﺘﺎﺡ ﻤﻊ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ﺁﻜﺴﻴﺱ!‬
‫‪ InputOutput‬ﻤﻌﺎﻤل ﺇﺩﺨﺎل ﻭﺇﺨﺭﺍﺝ ﻤﻌﺎ‪.‬‬
‫‪ ReturnValue‬ﻗﻴﻤﺔ ﻋﺎﺌﺩﺓ ﻤﻥ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻥ‪.‬‬

‫ﺍﻟﻘﻴﻤﺔ ‪:Value‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺍﻟﻤﻌﺎﻤل‪ ،‬ﻭﻫﻲ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ Object‬ﻟﺘﻘﺒل ﺃﻱ ﻨﻭﻉ ﻤﻥ ﺍﻟﻘﻴﻡ‪ ،‬ﻟﻜﻥ ﻫـﺫﺍ‬
‫ﻻ ﻴﻌﻨﻲ ﺃﻥ ﻜل ﺍﻟﻘﻴﻡ ﻤﺴﻤﻭﺡ ﺒﻬﺎ‪ ،‬ﻷﻥ ﺍﻟﺨﺎﺼﻴﺔ ‪ DbType‬ﺘﺤﺩﺩ ﻨﻭﻉ ﺍﻟﻘﻴﻡ ﺍﻟﻤﺴـﻤﻭﺡ‬
‫ﺒﻬﺎ‪.‬‬

‫‪٩٢‬‬
‫ﻭﻴﺠﺏ ﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻗﺒل ﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ ﻤﻥ ﺨﻼل ﻜﺎﺌﻥ ﺍﻷﻤـﺭ‪ ،‬ﻭﺇﺫﺍ‬
‫ﻜﺎﻥ ﺍﻟﻤﻌﺎﻤل ﻟﻺﺨﺭﺍﺝ‪ ،‬ﻓﺈﻥ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻌﺎﺌﺩﺓ ﻤﻥ ﺍﻟﺨﺎﺩﻡ ﺘﻭﻀﻊ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺒﻌﺩ ﺘﻨﻔﻴﺫ‬
‫ﺍﻷﻤﺭ ﺃﻭ ﺒﻌﺩ ﺇﻏﻼﻕ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataReader‬ﺇﻥ ﻜﻨﺕ ﺘﺴﺘﺨﺩﻤﻪ‪.‬‬
‫ﻭﻟﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ ‪ DBNull‬ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﺍﺴﺘﺨﺩﻡ ﺍﻟﻔﺌﺔ ‪ DBNull‬ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫;‪P.Value = DBNull.Value‬‬
‫ﺤﻴﺙ ﺇﻥ ‪ Value‬ﻫﻲ ﺨﺎﺼﻴﺔ ﺜﺎﺒﺘـﺔ ﻟﻠﻘـﺭﺍﺀﺓ ﻓﻘـﻁ ‪Static ReadOnly Property‬‬
‫ﻤﻌﺭﻓﺔ ﻓﻲ ﺍﻟﻔﺌﺔ ‪ DBNull‬ﻟﺘﻌﻴﺩ ﻨﺴﺨﺔ ﺠﺩﻴﺩﺓ ﻤﻥ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﺍﻟﻘﻴﻤﺔ ‪.DBNull‬‬

‫ﺍﻟﻨﻭﻉ ‪:DbType‬‬
‫ﺘﺤﺩﺩ ﻨﻭﻉ ﺍﻟﻤﻌﺎﻤل‪ ،‬ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ DbType‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﺠﺎل ﺍﻟﻘﻴﻡ ﺃﻭ ﻁﻭل ﺍﻟﻤﺘﻐﻴﺭ‬ ‫ﻤﻌﻨﺎﻩ‬ ‫ﺍﻟﺜﺎﺒﺕ‬


‫ﻤﻥ ‪ ١٢٨-‬ﺇﻟﻰ ‪١٢٧‬‬ ‫ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ ﺒﺈﺸﺎﺭﺓ‪.‬‬ ‫‪SByte‬‬
‫ﻤﻥ ‪٠‬‬ ‫ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ ﻤﻭﺠﺒﺔ‪.‬‬ ‫‪Byte‬‬
‫ﺇﻟﻰ ‪٢٥٥‬‬
‫ﻤﻥ ‪ ١‬ﺒﺎﻴﺕ‬ ‫ﺒﻴﺎﻨﺎﺕ ﺜﻨﺎﺌﻴﺔ‬ ‫‪Binary‬‬
‫ﺇﻟﻰ ‪ ٨٠٠٠‬ﺒﺎﻴﺕ‬
‫‪ false‬ﺃﻭ ‪true‬‬ ‫ﻗﻴﻤﺔ ﻤﻨﻁﻘﻴﺔ‬ ‫‪Boolean‬‬
‫ﻤﻥ ‪٣٢٧٦٨-‬‬ ‫ﻋﺩﺩ ﻗﺼﻴﺭ‬ ‫‪Int16‬‬
‫ﺇﻟﻰ ‪٣٢٧٦٧‬‬
‫ﻤﻥ ‪ ٠‬ﺇﻟﻰ ‪٦٥٥٣٥‬‬ ‫ﻋﺩﺩ ﻗﺼﻴﺭ ﻤﻭﺠﺏ‬ ‫‪UInt16‬‬
‫ﻤﻥ ‪٢١٤٧٤٨٣٦٤٨-‬‬ ‫ﻋﺩﺩ ﺼﺤﻴﺢ‬ ‫‪Int32‬‬
‫ﺇﻟﻰ ‪٢١٤٧٤٨٣٦٤٧‬‬
‫ﻤﻥ ‪٠‬‬ ‫ﻋﺩﺩ ﺼﺤﻴﺢ ﻤﻭﺠﺏ‬ ‫‪UInt32‬‬
‫ﺇﻟﻰ ‪٤٢٩٤٩٦٧٢٩٥‬‬

‫‪٩٣‬‬
‫ﻤﺠﺎل ﺍﻟﻘﻴﻡ ﺃﻭ ﻁﻭل ﺍﻟﻤﺘﻐﻴﺭ‬ ‫ﻤﻌﻨﺎﻩ‬ ‫ﺍﻟﺜﺎﺒﺕ‬
‫ﻤﻥ ‪٩٢٢٣٣٧٢٠٣٦٨٥٤٧٧٥٨٠٨-‬‬ ‫ﻋﺩﺩ ﻁﻭﻴل‬ ‫‪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‬‬

‫ﺼﻔﺤﺔ ‪ XML‬ﺃﻭ ﺠﺯﺀ ﻤﻨﻬﺎ‪.‬‬ ‫‪XML‬‬ ‫‪Xml‬‬


‫ﺃﻱ ﻨﻭﻉ ﻏﻴﺭ ﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺴﺎﺒﻘﺔ‪.‬‬ ‫ﻜﺎﺌﻥ‬ ‫‪Object‬‬

‫ﻋﻤﻭﺩ ﺍﻟﻤﺼﺩﺭ ‪:SourceColumn‬‬


‫ﺘﺤﺩﺩ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataSet‬ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺭﺒﻁﻪ ﺒﺎﻟﻤﻌﺎﻤـل‪ ..‬ﻫـﺫﺍ‬
‫ﻤﻔﻴﺩ ﻋﻨﺩ ﺤﻔﻅ ﺍﻟﺘﻐﻴﺭﺍﺕ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺃﻤـﺭ‬
‫ﺍﻟﺘﺤﺩﻴﺙ ‪ ..Update Command‬ﻫﺫﻩ ﻫﻲ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺘﻲ ﺘﺤﺩﺙ‪:‬‬
‫‪ -‬ﻴﻌﺭ‪‬ﻑ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ‪ Update Comman‬ﻤﻌﺎﻤﻼ ﻟﻜل ﻋﻤـﻭﺩ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻴﻀﻊ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ SourceColumn‬ﻟﻜل ﻤﻌﺎﻤل‪.‬‬
‫‪ -‬ﻋﻨﺩ ﺘﻨﻔﻴﺫ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﻴﻘﻭﻡ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataAdapter‬ﺒﺎﻟﻤﺭﻭﺭ ﻋﻠـﻰ‬
‫ﺼﻔﻭﻑ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﺤﺩﺍ ﺘﻠﻭ ﺍﻵﺨﺭ ﻭﺘﻨﻔﻴﺫ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﻋﻠﻰ ﻜل ﻤﻨﻬـﺎ‬
‫ﻋﻠﻰ ﺤﺩﺓ‪.‬‬
‫‪ -‬ﻟﺘﺤﺩﻴﺙ ﺃﻱ ﺼﻑ‪ ،‬ﻴﻀﻊ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻜل ﻤﻌﺎﻤـل ﻤـﻥ ﻤﻌـﺎﻤﻼﺕ ﺃﻤـﺭ‬
‫ﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﻗﻴﻤـﺔ ﺍﻟﺨﺎﻨـﺔ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓـﻲ ﺍﻟﻌﻤـﻭﺩ ﺍﻟﻤﺤـﺩﺩ ﻓـﻲ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪٩٥‬‬
‫‪ ،DbParameter.SourceColumn‬ﻭﺒﻬﺫﺍ ﻴﻤﻜﻥ ﺘﻨﻔﻴﺫ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴﺙ ﻟﻜـل‬
‫ﺼﻑ ﺒﺼﻭﺭﺓ ﺼﺤﻴﺤﺔ‪.‬‬
‫ﻭﺴﺘﻔﻬﻡ ﻫﺫﻩ ﺍﻷﻤﻭﺭ ﺒﺼﻭﺭﺓ ﺃﻭﻀﺢ ﻋﻨﺩﻤﺎ ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DataAdapter‬‬
‫ﻭﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪.DataSet‬‬

‫ﺇﺼﺩﺍﺭ ﺍﻟﻤﺼﺩﺭ ‪:SourceVersion‬‬


‫ﺘﺤﺩﺩ ﻨﻭﻉ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﺴﺘﻭﻀﻊ ﻓﻲ ﺍﻟﻤﻌﺎﻤل‪ ..‬ﻫﺫﺍ ﻤﻔﻴﺩ ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﻌﺎﻤل ﻟﺘﺤـﺩﻴﺙ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻷﻥ ﻜل ﺨﺎﻨﺔ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘﻔﻅ ﺒﻌﺩﺓ ﺃﻨﻭﺍﻉ ﻤﻥ ﺍﻟﻘﻴﻡ‪ ،‬ﻤﺜل‬
‫ﺍﻟﻘﻴﻤﺔ ﺍﻷﺼﻠﻴﺔ )ﺍﻟﻘﺎﺩﻤﺔ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ(‪ ،‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻟﺤﺎﻟﻴﺔ )ﺍﻟﻘﻴﻤﺔ ﺍﻟﺠﺩﻴﺩﺓ ﺍﻟﺘﻲ ﺃﺩﺨﻠﻬﺎ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ(‪ ..‬ﻭﺘﺄﺨﺫ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ ..DataRowVersion‬ﻭﺴﻨﺘﻌﺭﻑ‬
‫ﻋﻠﻰ ﻫﺫﺍ ﺍﻷﻤﺭ ﺒﺘﻔﺼﻴل ﺃﻜﺒﺭ ﻻﺤﻘﺎ ﻋﻨﺩ ﺍﻟﺘﻌﺭﻑ ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫‪٩٦‬‬
‫ﻭﺍﺠﻬﺔ ﻤﻌﺎﻤل ﺒﻴﺎﻨﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫‪IDbDataParameter Interface‬‬

‫ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺘﺭﺙ ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDataParameter‬ﻭﻫﻲ ﺘﻤﺘﻠﻙ ﺒﻌﺽ ﺍﻟﺨﺼـﺎﺌﺹ ﺍﻹﻀـﺎﻓﻴﺔ‬


‫ﺍﻟﺘﻲ ﺘﻀﻴﻑ ﻤﺯﻴﺩﺍ ﻤﻥ ﺍﻟﺘﺤﻜﻡ ﻓﻲ ﺍﻟﻘﻴﻡ ﺍﻟﺘﻲ ﻴﻘﺒﻠﻬﺎ ﺍﻟﻤﻌﺎﻤل‪ ..‬ﻭﻫﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ ﻫﻲ‪:‬‬

‫ﺍﻟﺤﺠﻡ ‪:Size‬‬
‫ﺘﺤﺩﺩ ﺃﻗﺼﻰ ﺤﺠﻡ ﻤﺴﻤﻭﺡ ﺒﻪ ﻟﻠﻤﻌﺎﻤل ﺒﺎﻟﻭﺤﺩﺓ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪ ..Byte‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﺘﺴﺘﻨﺘﺞ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﻌﺎﻤل‪ ،‬ﻓﺈﻥ ﻜﺎﻥ ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜـﺎل‪ ،‬ﺘﻜـﻭﻥ‬
‫ﻗﻴﻤﺘﻬﺎ ‪ ،٤‬ﻭﺇﻥ ﻜﺎﻥ ﺍﻟﻤﻌﺎﻤل ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻤﺼﻔﻭﻓﺔ ﻓﺈﻥ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﺘﺄﺨـﺫ ﻁـﻭل‬
‫ﺍﻟﻤﺼﻔﻭﻓﺔ‪ ..‬ﻻﺤﻅ ﺃﻨﻙ ﻟﻭ ﺼﻐﺭﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻋﻥ ﺤﺠﻡ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻓﺴـﻴﺘﻡ ﺃﺨـﺫ‬
‫ﺠﺯﺀ ﻤﻥ ﻫﺫﻩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻘﻁ ﻭﺇﺴﻘﺎﻁ ﺍﻟﺠﺯﺀ ﺍﻟﺯﺍﺌﺩ‪.‬‬

‫ﺍﻟﺩﻗﺔ ‪:Precision‬‬
‫ﺘﺤﺩﺩ ﺃﻜﺒﺭ ﻋﺩﺩ ﻤﺴﻤﻭﺡ ﺒﻪ ﻤﻥ ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﺭﻗﻤﻴﺔ ﻓﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﻴﻘﺒﻠﻬﺎ ﺍﻟﻤﻌﺎﻤل‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ‬
‫ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻫﻲ ‪ ،٠‬ﻭﻫﻲ ﺘﻌﻨﻲ ﻋﺩﻡ ﻓﺭﺽ ﻗﻴﻭﺩ ﻋﻠﻰ ﻋﺩﺩ ﺍﻟﺨﺎﻨﺎﺕ‪ ،‬ﻭﺘﺭﻙ ﺫﻟﻙ ﻟﻤـﺯﻭﺩ‬
‫ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺍﻟﻤﻘﻴﺎﺱ ‪:Scale‬‬
‫ﺘﺤﺩﺩ ﺃﻜﺒﺭ ﻋﺩﺩ ﻤﺴﻤﻭﺡ ﺒﻪ ﻤﻥ ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﻌﺸﺭﻴﺔ ﻓﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﻴﻘﺒﻠﻬﺎ ﺍﻟﻤﻌﺎﻤل‪ ..‬ﻭﻟـﻭ‬
‫ﺯﺍﺩ ﻋﺩﺩ ﺍﻟﺨﺎﻨﺎﺕ ﻋﻥ ﻫﺫﺍ ﺍﻟﺭﻗﻡ ﻴﺘﻡ ﺘﻘﺭﻴﺒﻪ‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻫﻲ ‪. ٠‬‬

‫‪٩٧‬‬
‫ﻓﺌﺔ ﻤﻌﺎﻤل ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DbParameter Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻤﻌﺭﻓﺔ ﻓﻲ ﺍﻟﻨﻁﺎﻕ ‪ ،System.Data.Common‬ﻭﻫﻲ ﻓﺌﺔ ﺃﺴﺎﺴﻴﺔ ﻤﺠـﺭﺩﺓ ﺘﺠـﺏ‬


‫ﻭﺭﺍﺜﺘﻬﺎ‪ ،‬ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ IDbDataParameter‬ﻤﻤﺎ ﻴﻌﻨـﻲ ﺃﻨﻬـﺎ ﺘﻤﺜـل ﺃﻴﻀـﺎ ﺍﻟﻭﺍﺠﻬـﺔ‬
‫‪ ،IDataParameter‬ﻭﺒﺎﻟﺘﺎﻟﻲ ﻓﻬﻲ ﺘﻤﻠﻙ ﻜل ﺨﺼﺎﺌﺼﻬﻤﺎ‪ ،‬ﻭﻻ ﺘﺯﻴﺩ ﻋﻠﻴﻬﺎ ﺇﻻ ﺨﺎﺼﻴﺔ ﻭﺍﺤﺩﺓ‬
‫ﺠﺩﻴﺩﺓ ﻭﻫﻲ‪:‬‬

‫ﺘﻤﺜﻴل ﻗﻴﻤﺔ ﻤﻨﻌﺩﻤﺔ ﻓﻲ ﻋﻤﻭﺩ ﺍﻟﻤﺼﺩﺭ ‪:SourceColumnNullMapping‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،true‬ﻓﺴﻴﻌﻨﻲ ﻫﺫﺍ ﺃﻥ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻴﺴﺘﺨﺩﻡ ﻹﺨﺒـﺎﺭﻙ ﺇﻥ‬
‫ﻜﺎﻥ ﻋﻤﻭﺩ ﺍﻟﻤﺼﺩﺭ ‪ SourceColumn‬ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﺎﺭﻏﺎ ﺃﻡ ﻻ‪ ،‬ﺤﻴﺙ ﺘﻜﻭﻥ‬
‫ﻗﻴﻤﺔ ﺍﻟﻤﻌﺎﻤل ‪ ٠‬ﺇﻥ ﻜﺎﻥ ﺍﻟﻌﻤﻭﺩ ﻓﺎﺭﻏﺎ‪ ،‬ﻭﺘﻜﻭﻥ ﻗﻴﻤﺘﻪ ‪ ١‬ﺇﻥ ﻜﺎﻥ ﺍﻟﻌﻤﻭﺩ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺃﻱ‬
‫ﻗﻴﻤﺔ‪ ..‬ﻫﺫﺍ ﻤﻔﻴﺩ ﻋﻨﺩ ﺘﻌﺭﻴﻑ ﺍﺴﺘﻌﻼﻤﺎﺕ ﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﻷﻥ ﻤﻘﺎﺭﻨـﺔ ﺃﻱ ﺨـﺎﻨﺘﻴﻥ ﻗﻴﻤﺘﻬﻤـﺎ‬
‫‪ Null‬ﺘﻜﻭﻥ ﻨﺘﻴﺠﺘﻪ ‪ false‬ﺭﻏﻡ ﺃﻥ ﺍﻟﺨﺎﻨﺘﻴﻥ ﻤﺘﺴﺎﻭﻴﺘﻴﻥ ﻓﻌﻼ!‪ ..‬ﻟﻬﺫﺍ ﻴﺠﺏ ﺃﻥ ﻨﺘﺄﻜﺩ ﻗﺒل‬
‫ﺇﺠﺭﺍﺀ ﺍﻟﻤﻘﺎﺭﻨﺔ ﺇﻥ ﻜﺎﻨﺕ ﺍﻟﺨﺎﻨﺘﻴﻥ ﻓﺎﺭﻏﺘﻴﻥ ﺃﻡ ﻻ‪ ..‬ﻭﺴﻨﺭﻯ ﻜﻴﻑ ﻨﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘـﺔ‬
‫ﻋﻨﺩ ﺍﻟﺘﻌﺭﻑ ﻋﻠﻰ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻓﺼل ﻻﺤﻕ‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻭﺴﻴﻠﺔ ﻭﺍﺤﺩﺓ‪ ،‬ﻭﻫﻲ‪:‬‬

‫ﺘﺼﻔﻴﺭ ﺍﻟﻨﻭﻉ ‪:ResetDbType‬‬


‫ﺘﻌﻴﺩ ﺍﻟﺨﺎﺼﻴﺔ ‪ DbType‬ﺇﻟﻰ ﻗﻴﻤﺘﻬﺎ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪.‬‬

‫ﻭﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺭﺙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ‪:‬‬


‫‪OdbcParameter Class .١‬‬
‫‪OleDbParameter Class .٢‬‬
‫‪SqlParameter Class .٣‬‬
‫‪OracleParameter Class .٤‬‬
‫ﻭﺴﻨﻘﺘﺼﺭ ﻫﻨﺎ ﻋﻠﻰ ﺍﻟﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ ‪.SqlParameter‬‬

‫‪٩٨‬‬
‫ﻓﺌﺔ ﻤﻌﺎﻤل ﺴﻴﻜﻭﻴل ‪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‬ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻟﻤﻌﺭﻑ ﺍﻟﻤﺤﻠﻲ ‪:LocaleId‬‬


‫ﺘﺤﺩﺩ ﻤﻌﺭﻑ ﺍﻟﺜﻘﺎﻓﺔ ﺍﻟﻤﺤﻠﻴﺔ ﻟﻠﻠﻐﺔ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻗﻴﻤﺔ ﺍﻟﻤﻌﺎﻤـل‪..‬‬
‫ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜﺎل‪ :‬ﻤﻌﺭﻑ ﺍﻟﻠﻐﺔ ﺍﻟﻌﺭﺒﻴﺔ ﺒﺎﻟﻨﻅﺎﻡ ﺍﻟﺴﺩﺍﺴـﻲ ﻋﺸـﺭﻱ ﻫـﻭ‪. 0x0001 :‬‬
‫)ﺭﺍﺠﻊ ﻜﺘﺎﺏ‪" :‬ﺒﺭﻤﺠﺔ ﺇﻁﺎﺭ ﺍﻟﻌﻤل"‪ ،‬ﻟﻠﻤﺯﻴﺩ ﻤﻥ ﺍﻟﺘﻔﺎﺼـﻴل ﻋـﻥ ﺍﻟﺜﻘﺎﻓـﺎﺕ ﺍﻟﻌﺎﻟﻤﻴـﺔ‬
‫ﻭﻤﻌﺭﻓﺎﺘﻬﺎ(‪.‬‬

‫ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﻤﻘﺎﺭﻨﺔ ‪:CompareInfo‬‬


‫ﺘﺤﺩﺩ ﻜﻴﻑ ﺴﻴﻘﻭﻡ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﺒﻤﻘﺎﺭﻨﺔ ﺍﻟﻨﺼﻭﺹ‪ ،‬ﻭﻫﻲ ﺘﺄﺨـﺫ ﺇﺤـﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ‬
‫‪ SqlCompareOptions‬ﺍﻟﺘﻲ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺴﺎﺩﺱ‪.‬‬

‫ﺍﻹﺯﺍﺤﺔ ‪: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‬ﻓﻲ ﻨﻔﺱ ﺍﻟﺒﺭﻨﺎﻤﺞ‪.‬‬

‫ﻨﻭﻉ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ‪:SqlDbType‬‬


‫ﺘﺤﺩﺩ ﻨﻭﻉ ﺍﻟﻤﻌﺎﻤل ﻤﻥ ﺒﻴﻥ ﺃﻨﻭﺍﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪ ،‬ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤـﺩﻯ ﻗـﻴﻡ‬
‫ﺍﻟﻤﺭﻗﻡ ‪ ،SqlDbType‬ﻭﻫﻲ ﺘﺤﻤل ﻨﻔﺱ ﺃﺴﻤﺎﺀ ﺃﻨﻭﺍﻉ ﺒﻴﺎﻨﺎﺕ ﺴـﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ ﺍﻟﺘـﻲ‬
‫ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﺒﺎﻟﺘﻔﺼﻴل ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺜﺎﻟﺙ‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﻫـﻲ‬
‫‪.NVarChar‬‬
‫ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﺭﺘﺒﻁﺔ ﺒﺎﻟﺨﺎﺼﻴﺔ ‪ ،DbType‬ﻟﻬﺫﺍ ﻟﻭ ﻏﻴﺭﺕ ﻗﻴﻤـﺔ ﺇﺤـﺩﺍﻫﻤﺎ‬
‫ﻓﺴﺘﺘﻐﻴﺭ ﺍﻷﺨﺭﻯ ﺘﻠﻘﺎﺌﻴﺎ‪ ،‬ﺒﺤﻴﺙ ﺘﺤﺘﻭﻱ ﺍﻟﺨﺎﺼﻴﺔ ‪ SqlDbType‬ﺩﺍﺌﻤﺎ ﻋﻠﻰ ﺃﻨﺴﺏ ﻨـﻭﻉ‬
‫ﻤﻥ ﺃﻨﻭﺍﻉ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻴﻭﺍﻓﻕ ﻨﻭﻉ ﺍﻟﻤﺘﻐﻴﺭ ﺍﻟﻤﻭﺠـﻭﺩ ﻓـﻲ ﺍﻟﺨﺎﺼـﻴﺔ ‪..DbType‬‬
‫ﺠﺭﺏ ﻤﺜﻼ‪:‬‬
‫;) (‪SqlParameter P = new SqlParameter‬‬
‫;‪P.SqlDbType = SqlDbType.Money‬‬
‫‪MessageBox.Show(P.DbType.ToString( )); // Currency‬‬
‫;‪P.DbType = DbType.Int64‬‬
‫‪MessageBox.Show(P.SqlDbType.ToString( )); // BigInt‬‬
‫‪١٠٢‬‬
‫ﺍﺴﻡ ﺍﻟﻨﻭﻉ ‪:TypeName‬‬
‫ﺇﺫﺍ ﻜﻨﺕ ﺘﺴﺘﺨﺩﻡ ﺍﻟﻤﻌﺎﻤل ﻹﺭﺴﺎل ﻗﻴﻤﺔ ﺇﻟﻰ ﻤﻌﺎﻤل ﺠﺩﻭل ‪ ،Table-Valued‬ﻓﻀﻊ ﺍﺴﻡ‬
‫ﻫﺫﺍ ﺍﻟﻨﻭﻉ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻤﺘﻀﻤﻨﺎ ﺍﺴﻡ ﺍﻟﻤﺎﻟﻙ ‪ ..Owner‬ﻓﻤﺜﻼ‪ ،‬ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻨـﻭﻉ‬
‫‪ AuthorType‬ﺍﻟﺫﻱ ﻴﺴﺘﺨﺩﻤﻪ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ ‪ ،InsertAuthors‬ﻭﻀﻌﻨﺎ ﻓـﻲ ﻫـﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻨﺹ "‪ "dbo.AuthorType‬ﺃﻭ ﺒﺎﺨﺘﺼﺎﺭ "‪ "AuthorType‬ﻷﻥ ﺍﻟﻤﺎﻟﻙ ﻫﻨﺎ‬
‫ﺍﻓﺘﺭﺍﻀﻲ‪ ..‬ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻻ ﺒﺩ ﺍﻥ ﻨﻀـﻊ ﻓـﻲ ﺍﻟﺨﺎﺼـﻴﺔ ‪ SqlDbType‬ﺍﻟﻘﻴﻤـﺔ‬
‫ـﺭﻭﻉ‬
‫ـﻲ ﺍﻟﻤﺸــ‬
‫ـﺎﻩ ﻓــ‬
‫ـﺎ ﻓﻌﻠﻨــ‬
‫ـﻭ ﻤــ‬
‫ـﺫﺍ ﻫــ‬
‫‪ ..SqlDbType.Structured‬ﻭﻫــ‬
‫‪.TableValuedParameters‬‬

‫ﺍﺴﻡ ﺍﻟﻤﺘﻐﻴﺭ ﺍﻟﺨﺎﺹ ﺒﺎﻟﻤﺴﺘﺨﺩﻡ ‪:UdtTypeName‬‬


‫ﺇﺫﺍ ﻜﻨﺕ ﺘﺴﺘﺨﺩﻡ ﺍﻟﻤﻌﺎﻤل ﻹﺭﺴﺎل ﻗﻴﻤﺔ ﺇﻟـﻰ ﻨـﻭﻉ ﻤـﻥ ﺘﻌﺭﻴـﻑ ﺍﻟﻤﺴـﺘﺨﺩﻡ ‪User-‬‬
‫‪ ،Defined Type‬ﻓﻀﻊ ﺍﺴﻡ ﻫﺫﺍ ﺍﻟﻨﻭﻉ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻭﻻ ﺘـﻨﺱ‪ ‬ﺃﻥ ﺘﻀـﻊ ﻓـﻲ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ‪ SqlDbType‬ﺍﻟﻘﻴﻤﺔ ‪ ..SqlDbType.Udt‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻤﺘﻐﻴﺭﺍﺕ ﺍﻟﺘﻲ‬
‫ﻴﻌﺭﻓﻬﺎ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻻﺤﻘﺎ‪.‬‬

‫ﻗﻴﻤﺔ ﺴﻴﻜﻭﻴل ‪:SqlValue‬‬


‫ﻤﻤﺎﺜﻠﺔ ﻟﻠﺨﺎﺼﻴﺔ ‪ ،Value‬ﻭﻜﻠﺘﺎﻫﻤﺎ ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺍﻟﻤﻌﺎﻤل‪.‬‬

‫ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺨﻁﻁ ‪:XmlSchemaCollectionDatabase‬‬


‫ﺘﻌﻴﺩ ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﻭﺠﺩ ﺒﻬﺎ ﻤﺠﻤﻭﻋﺔ ﻤﺨﻁﻁﺎﺕ ‪ ..XML‬ﻭﺇﺫﺍ ﻜﺎﻨﺕ ﻗﻴﻤـﺔ‬
‫ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻨﺼﺎ ﻓﺎﺭﻏﺎ‪ ،‬ﻓﻬﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﺍﻟﻤﺨﻁﻁﺎﺕ ﺘﻭﺠﺩ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺤﺎﻟﻴـﺔ‪،‬‬
‫ﺃﻭ ﺃﻨﻪ ﻻ ﺘﻭﺠﺩ ﻤﺨﻁﻁﺎﺕ ﺃﺼﻼ‪ ،‬ﻭﻓﻲ ﺍﻟﺤﺎﻟـﺔ ﺍﻷﺨﻴـﺭﺓ ﺴـﺘﻜﻭﻥ ﻗﻴﻤـﺔ ﺍﻟﺨﺎﺼـﻴﺘﻴﻥ‬
‫‪XmlSchemaCollectionName‬‬
‫ﻭ ‪ XmlSchemaCollectionOwningSchema‬ﻨﺼﺎ ﻓﺎﺭﻏﺎ‪.‬‬

‫‪١٠٣‬‬
‫ﺍﺴﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﺨﻁﻁﺎﺕ ‪:XmlSchemaCollectionName‬‬
‫ﺘﻌﻴﺩ ﺍﺴﻡ ﻤﺠﻤﻭﻋﺔ ﻤﺨﻁﻁﺎﺕ ‪.XML‬‬

‫‪:XmlSchemaCollectionOwningSchema‬‬
‫ﺘﻌﻴﺩ ﻤﺨﻁﻁ ﺍﻟﻌﻼﻗﺎﺕ ﺍﻟﺭﺌﻴﺴﻲ‪ ،‬ﺍﻟﺫﻱ ﻴﺤﺩﺩ ﻤﻭﻀﻊ ﻤﺠﻤﻭﻋﺔ ﻤﺨﻁﻁﺎﺕ ‪.XML‬‬

‫‪١٠٤‬‬
‫‪-٨-‬‬
‫ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DataReader‬‬

‫ﻴﺘﻡ ﺇﻨﺸﺎﺀ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataReader‬ﺒﺎﺴـﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ ‪ ExecuteReader‬ﺍﻟﺨﺎﺼـﺔ‬


‫ﺒﻜﺎﺌﻥ ﺍﻷﻤﺭ ‪ ..Command Object‬ﻭﻴﺴﺘﻘﺒل ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻨﺘﻴﺠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟـﺫﻱ ﻴﻨﻔـﺫﻩ‬
‫ﻜﺎﺌﻥ ﺍﻷﻤﺭ‪ ،‬ﻭﻴﻘﻭﻡ ﺒﺘﺨﺯﻴﻥ ﻤﺎ ﻴﺼل ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺍﻟﺨﺎﺩﻡ ﻓﻲ ﺍﻟﻤﺨـﺯﻥ ﺍﻟﻭﺴـﻴﻁ ﻟﻠﺸـﺒﻜﺔ‬
‫‪ Network Buffer‬ﺍﻟﻤﻭﺠﻭﺩ ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل‪ ،‬ﺤﻴﺙ ﻴﻤﻜﻨﻙ ﺍﻟﻤـﺭﻭﺭ ﻋﺒـﺭ ﺍﻟﺴـﺠﻼﺕ‬
‫ﺍﻟﻤﺴﺘﻠﻤﺔ ﻭﺍﺤﺩ‪‬ﺍ ﺘﻠﻭ ﺍﻵﺨﺭ ﻋﻠﻰ ﺍﻟﺘﻭﺍﻟﻲ‪ ،‬ﻤﻤﺎ ﻴﻭﻓﺭ ﻤﻴﺯﺘﻴﻥ ﻫﺎﻤﺘﻴﻥ‪:‬‬
‫‪ -١‬ﺍﻟﺴﺭﻋﺔ‪ :‬ﺤﻴﺙ ﻴﻤﻜﻨﻙ ﻗﺭﺍﺀﺓ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﺘﻭﻓﺭﺓ ﻓﻭﺭ ﻭﺼﻭﻟﻬﺎ‪ ،‬ﺩﻭﻥ ﺍﻨﺘﻅﺎﺭ ﺍﻜﺘﻤـﺎل‬
‫ﻭﺼﻭل ﻜل ﺍﻟﺴﺠﻼﺕ ﺃﻭﻻ‪.‬‬
‫‪ -٢‬ﻋﺩﻡ ﺍﺴﺘﻬﻼﻙ ﺍﻟﺫﺍﻜﺭﺓ‪ :‬ﻷﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺤﺘﻔﻅ ﺒﺴﺠل ﻭﺍﺤﺩ ﻓﻘﻁ ﻓﻲ ﺍﻟﺫﺍﻜﺭﺓ ﻓـﻲ‬
‫ﻜل ﻤﺭﺓ‪.‬‬
‫ﻟﻜﻥ‪ ‬ﻟﻬﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﻋﻴﺒﻴﻥ ﺃﺴﺎﺴﻴﻴﻥ‪:‬‬
‫‪ -١‬ﻋﺩﻡ ﺍﻟﻘﺩﺭﺓ ﻋﻠﻰ ﺘﺤﺩﻴﺙ ﺴﺠﻼﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﺒﻌﺒﺎﺭﺓ ﺃﺨـﺭﻯ‪ :‬ﻗـﺎﺭﺉ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ ﻜﻤﺎ ﻴﻘﻭل ﺍﺴﻤﻪ‪ ،‬ﻭﻟﻴﺱ ﻟﻠﻜﺘﺎﺒﺔ!‬
‫‪ -٢‬ﻋﺩﻡ ﺍﻟﻘﺩﺭﺓ ﻋﻠﻰ ﺍﻟﺘﺭﺍﺠﻊ ﺇﻟﻰ ﺍﻟﺨﻠﻑ‪ ،‬ﺃﻭ ﺍﻟﻘﻔﺯ ﻤﺒﺎﺸﺭﺓ ﺇﻟﻰ ﺴﺠل ﻓﻲ ﻤﻭﻀﻊ ﻤﻌـﻴﻥ‬
‫ﻓﻲ ﺍﻟﻨﺘﻴﺠﺔ ﺩﻭﻥ ﺍﻟﻤﺭﻭﺭ ﻋﻠﻰ ﻤﺎ ﻗﺒﻠﻪ ﻤﻥ ﺍﻟﺴﺠﻼﺕ‪.‬‬
‫ﻟﻬﺫﺍ ﻴﻭﺼﻑ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺄﻨﻪ "ﻤﺠﺭﻯ ﺒﻴﺎﻨﺎﺕ ﻟﻸﻤﺎﻡ ﻓﻘﻁ ﻭﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ"‪:‬‬
‫‪Forward-only, Read-only Stream.‬‬
‫ﻟﻜل ﻫﺫﺍ‪ ،‬ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﺤﺎﻻﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﻟﻭ ﻜﻨﺕ ﺴﺘﺘﻌﺎﻤل ﻤﻊ ﺴﺠلّ ﻭﺍﺤﺩ ﻓﻘﻁ‪.‬‬

‫‪١٠٥‬‬
‫‪ -٢‬ﻟﻭ ﻜﻨﺕ ﺴﺘﻘﺭﺃ ﻜل ﺴﺠل ﻤﺭﺓ ﻭﺍﺤﺩﺓ ﻓﻘﻁ‪ ،‬ﻭﻻ ﻴﻌﻨﻴﻙ ﺍﻟﺭﺠﻭﻉ ﺇﻟﻴﻪ ﻤﺭﺓ ﺃﺨﺭﻯ‪.‬‬
‫‪ -٣‬ﻟﻭ ﻜﺎﻨﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻭﺠﻭﺩﺓ ﻋﻠﻰ ﻨﻔﺱ ﺍﻟﺠﻬﺎﺯ‪ ،‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺴﺭﻋﺔ ﺍﻟﺤﺼﻭل ﻋﻠﻰ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻨﻬﺎ ﻤﺒﺎﺸﺭﺓ‪ ،‬ﺩﻭﻥ ﺍﻟﺤﺎﺠﺔ ﺇﻟﻰ ﺘﺤﻤﻴﻠﻬﺎ ﻓﻲ ﺍﻟﺫﺍﻜﺭﺓ‪.‬‬
‫‪ -٤‬ﻋﻨﺩﻤﺎ ﺘﺭﻴﺩ ﻗﺭﺍﺀﺓ ﺍﻟﻨﺘﺎﺌﺞ ﺩﻭﻥ ﺍﻟﺤﺎﺠﺔ ﺇﻟﻰ ﺘﻐﻴﻴﺭ ﺃﻱ ﺠﺯﺀ ﻤﻨﻬﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫‪١٠٦‬‬
‫ﻭﺍﺠﻬﺔ ﺴﺠل ﺍﻟﺒﻴﺎﻨﺎﺕ ‪IDataRecord Interface‬‬

‫ﺘﻘﺩﻡ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺨﺼﺎﺌﺹ ﻭﺍﻟﻭﺴﺎﺌل ﺍﻟﻼﺯﻤﺔ ﻟﻘﺭﺍﺀﺓ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻓـﻲ ﻗـﺎﺭﺉ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﻋﺩﺩ ﺍﻟﺤﻘﻭل ‪:FieldCount‬‬


‫ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻷﻋﻤﺩﺓ ﻓﻲ ﺍﻟﺴﺠل‪ ..‬ﻫﺫﺍ ﻴﺘﻴﺢ ﻟﻙ ﻜﺘﺎﺒﺔ ﺤﻠﻘﺔ ﺘﻜﺭﺍﺭ ‪ Loop‬ﻟﻠﻤﺭﻭﺭ ﻋﺒﺭ ﻜل‬
‫ﺍﻷﻋﻤﺩﺓ ﺒﺩﺀﺍ ﻤﻥ ﺍﻟﻌﻤﻭﺩ ﺭﻗﻡ ﺼﻔﺭ ﺇﻟﻰ ﺍﻟﻌﻤﻭﺩ ﺭﻗـﻡ ‪ ..FieldCount - 1‬ﻫـﺫﺍ ﻤﻔﻴـﺩ‬
‫ﻻﺨﺘﺼﺎﺭ ﺍﻟﻜﻭﺩ ﻋﻨﺩﻤﺎ ﺘﺴﺘﺨﺩﻡ ﺍﺴﺘﻌﻼﻡ ﻴﻌﻴﺩ ﻋﺩﺩﺍ ﻜﺒﻴﺭﺍ ﻤﻥ ﺍﻟﺤﻘﻭل‪.‬‬

‫ﺍﻟﻤﻔﻬﺭﺱ ‪:Indexer‬‬
‫ﻴﺴﺘﻘﺒل ﺭﻗﻡ ﺍﻟﻌﻤﻭﺩ ﺃﻭ ﺍﺴﻤﻪ ﻜﻤﻌﺎﻤل‪ ،‬ﻭﻴﻌﻴـﺩ ﻜﺎﺌﻨـﺎ ‪ Object‬ﻴﺤﺘـﻭﻱ ﻋﻠـﻰ ﺍﻟﻘﻴﻤـﺔ‬
‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ‪ ..‬ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﺽ ﻗﻴﻤﺔ ﺍﻟﺨﺎﻨﺔ ﺍﻻﻭﻟﻰ‬
‫ﻓﻲ ﺍﻟﺼﻑ‪:‬‬
‫;)) (‪MessageBox.Show(Reader[0].ToString‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻻﺴﻡ ‪:GetName‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺭﻗﻡ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﺍﺴﻤﻪ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﺘﺭﺘﻴﺏ ‪:GetOrdinal‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﺭﻗﻤﻪ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﻨﻭﻉ ﺍﻟﺤﻘل ‪:GetFieldType‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺭﻗﻡ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻜﺎﺌﻥ ﺍﻟﻨﻭﻉ ‪ Type‬ﺍﻟـﺫﻱ ﻴﻤﺜـل ﻨـﻭﻉ‬
‫ﺒﻴﺎﻨﺎﺘﻪ‪.‬‬
‫‪١٠٧‬‬
‫ﻤﻌﺭﻓﺔ ﺍﺴﻡ ﻨﻭﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:GetDataTypeName‬‬
‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺭﻗﻡ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﻨﻭﻉ ﺒﻴﺎﻨﺎﺘﻪ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﻘﻴﻤﺔ ‪:GetValue‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺭﻗﻡ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻜﺎﺌﻨﺎ ‪ Object‬ﻴﺤﺘـﻭﻱ ﻋﻠـﻰ ﺍﻟﻘﻴﻤـﺔ‬
‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﻘﻴﻡ ‪:GetValues‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﺼﻔﻭﻓﺔ ﻜﺎﺌﻨـﺎﺕ ‪ ،Object Array‬ﻟﻴـﺘﻡ ﻤﻠﺅﻫـﺎ ﺒﺎﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺨﺎﻨﺎﺕ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻻﺤﻅ ﺃﻨﻙ ﻟﻭ ﺃﺭﺴﻠﺕ ﻤﺼﻔﻭﻓﺔ‬
‫ﺃﻗﺼﺭ ﻤﻥ ﻋﺩﺩ ﺨﺎﻨﺎﺕ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ ﻓﻠﻥ ﻴﺤﺩﺙ ﺨﻁﺄ‪ ،‬ﺒل ﺴﻴﺘﻡ ﻨﺴﺦ ﺠﺯﺀ ﻤﻥ ﺍﻟﺨﺎﻨﺎﺕ‬
‫ﻓﻘﻁ ﺇﻟﻰ ﺍﻟﻤﺼﻔﻭﻓﺔ ﻭﺇﻫﻤﺎل ﺍﻟﺒﺎﻗﻲ‪ ..‬ﺃﻤﺎ ﻟﻭ ﺃﺭﺴﻠﺕ ﻤﺼﻔﻭﻓﺔ ﺃﻁﻭل ﻤﻥ ﻋـﺩﺩ ﺨﺎﻨـﺎﺕ‬
‫ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻓﺴﻴﺘﻡ ﻨﺴﺦ ﻜل ﺍﻟﺨﺎﻨﺎﺕ ﺇﻟﻰ ﺠﺯﺀ ﻤﻥ ﺍﻟﻤﺼﻔﻭﻓﺔ ﻭﺘﺭﻙ ﺒﺎﻗﻲ ﺍﻟﻤﺼﻔﻭﻓﺔ‬
‫ﻓﺎﺭﻏﺎ‪ ..‬ﻭﻓﻲ ﻜل ﺍﻷﺤﻭﺍل‪ ،‬ﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩ ﺍﻟﺨﺎﻨـﺎﺕ ﺍﻟﺘـﻲ ﺘـﻡ ﻨﺴـﺨﻬﺎ ﺇﻟـﻰ‬
‫ﺍﻟﻤﺼﻔﻭﻓﺔ‪.‬‬

‫ﻗﺭﺍﺀﺓ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪:GetBytes‬‬


‫ﺘﺴﺘﺨﺩﻡ ﻋﻨﺩﻤﺎ ﻴﻜﻭﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺘﺎﺒﻌﻴﺎ ‪ ،Sequential‬ﻭﻫﻲ ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﻭﺤـﺩﺍﺕ‬
‫ﺜﻨﺎﺌﻴﺔ ‪ ،Bytes‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺃﺤـﺩ ﺍﻷﻋﻤـﺩﺓ‪ ..‬ﻭﻟﻬـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -‬ﺭﻗﻡ ﺍﻟﻌﻤﻭﺩ‪.‬‬
‫‪ -‬ﻤﻭﻀﻊ ﺒﺩﺍﻴﺔ ﺍﻟﻘﺭﺍﺀﺓ ﻤﻥ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻌﻤﻭﺩ‪.‬‬
‫‪ -‬ﻤﺼﻔﻭﻓﺔ ﻭﺤﺩﺍﺕ ﺜﻨﺎﺌﻴﺔ ‪ Bytes‬ﻻﺴﺘﻘﺒﺎل ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺨﻁﺄ ﺴﻴﺤﺩﺙ ﻟـﻭ‬
‫ﻜﺎﻨﺕ ﺍﻟﻤﺼﻔﻭﻓﺔ ﺃﻗﺼﺭ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻁﻠﻭﺒﺔ‪.‬‬
‫‪ -‬ﻤﻭﻀﻊ ﺒﺩﺍﻴﺔ ﺍﻟﻜﺘﺎﺒﺔ ﻓﻲ ﺍﻟﻤﺼﻔﻭﻓﺔ‪.‬‬

‫‪١٠٨‬‬
‫‪ -‬ﻋﺩﺩ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪ Bytes‬ﺍﻟﻤﻁﻠﻭﺏ ﻗﺭﺍﺀﺘﻬﺎ ﻤﻥ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﺒﺩﺀﺍ ﻤـﻥ‬
‫ﺍﻟﻤﻭﻀﻊ ﺍﻟﻤﺤﺩﺩ ﻓﻲ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺨﻁﺄ ﺴﻴﺤﺩﺙ ﻟﻭ ﻜـﺎﻥ ﺍﻟﻁـﻭل‬
‫ﺍﻟﻤﻁﻠﻭﺏ ﺃﻜﺒﺭ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺘﺒﻘﻴﺔ ﻓﻲ ﺍﻟﻌﻤﻭﺩ‪.‬‬
‫ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺘﻲ ﺘﻡ ﻨﺴﺨﻬﺎ ﺇﻟﻰ ﺍﻟﻤﺼﻔﻭﻓﺔ‪ ،‬ﻭﻟﻭ ﺃﺭﺴﻠﺕ ﺇﻟﻰ ﻫـﺫﻩ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ﻤﺼﻔﻭﻓﺔ ﻓﺎﺭﻏﺔ ‪ ،null‬ﻓﺴﺘﻌﻴﺩ ﺇﻟﻴﻙ ﺍﻟﻌﺩﺩ ﺍﻹﺠﻤﺎﻟﻲ ﻟﻠﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴـﺔ ﺍﻟﻤﺘﺎﺤـﺔ‬
‫ﻓﻲ ﺍﻟﺨﺎﻨﺔ‪.‬‬
‫ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪ ReadLargeData‬ﻟﻘﺭﺍﺀﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺼـﻭﺭﺓ‪،‬‬
‫ﺤﻴﺙ ﻨﻘﺭﺃ ‪ ١٠٠‬ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ ‪ Byte‬ﻤﻥ ﺒﺩﺍﻴﺔ ﺍﻟﺼﻭﺭﺓ ﻭﻨﺤﻔﻅﻬﺎ ﻓﻲ ﺍﻟﻤﻠﻑ‪ ،‬ﺜـﻡ ﻨﻘـﺭﺃ‬
‫‪ ١٠٠‬ﻭﺤﺩﺓ ﺘﺎﻟﻴﺔ ﻭﻨﺤﻔﻅﻬﺎ ﻓﻲ ﺍﻟﻤﻠﻑ‪ ،‬ﻭﻨﺴﺘﻤﺭ ﻓﻲ ﻓﻌل ﻫـﺫﺍ ﺇﻟـﻰ ﺃﻥ ﻨﻜﻤـل ﻗـﺭﺍﺀﺓ‬
‫ﺍﻟﺼﻭﺭﺓ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺸﺭﻁ ﺍﻟﺘﻭﻗﻑ ﻋﻥ ﺍﻟﻘﺭﺍﺀﺓ‪ ،‬ﻫﻭ ﺃﻥ ﺘﻜﻭﻥ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻌﺎﺌﺩﺓ ﻤﻥ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪ GetBytes‬ﺃﺼﻐﺭ ﻤﻥ ﻋﺩﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﻁﻠﺒﻨﺎ ﻗﺭﺍﺀﺘﻪ‪ ،‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﻫﺫﻩ ﻫـﻲ ﺁﺨـﺭ‬
‫ﺒﻴﺎﻨﺎﺕ ﻤﺘﺎﺤﺔ ﻓﻲ ﺍﻟﺨﺎﻨﺔ‪.‬‬

‫ﻗﺭﺍﺀﺓ ﺍﻟﺤﺭﻭﻑ ‪:GetChars‬‬


‫ﺘﺴﺘﺨﺩﻡ ﻋﻨﺩﻤﺎ ﻴﻜﻭﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺘﺎﺒﻌﻴﺎ ‪ ،Sequential‬ﻭﻫﻲ ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺤـﺭﻭﻑ‬
‫‪ Char Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺤﺭﻭﻑ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻋﻤﻭﺩ ﻨﺼﻲ‪ ،‬ﻭﻫﻲ ﻤﻤﺎﺜﻠﺔ ﻟﻠﻭﺴـﻴﻠﺔ‬
‫ﺍﻟﺴﺎﺒﻘﺔ ﻓﻲ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻭﺍﻟﻘﻴﻤﺔ ﺍﻟﻌﺎﺌﺩﺓ‪ ،‬ﻓﻴﻤﺎ ﻋﺩﺍ ﺃﻥ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻟﺙ ﻴﺴـﺘﻘﺒل ﻤﺼـﻔﻭﻓﺔ‬
‫ﺤﺭﻭﻑ ﺒﺩﻻ ﻤﻥ ﻤﺼﻔﻭﻓﺔ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ‪.‬‬

‫ﻫل ﺍﻟﻘﻴﻤﺔ ﻤﻨﻌﺩﻤﺔ ‪:IsDBNull‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﺨﺎﻨﺔ ﺍﻟﺘﻲ ﺃﺭﺴﻠﺕ ﺭﻗﻤﻬﺎ ﻜﻤﻌﺎﻤل ﻓﺎﺭﻏـﺔ ‪ ..DbNull‬ﻻﺤـﻅ ﺃﻥ‬
‫ﻋﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻗﺒل ﻤﺤﺎﻭﻟﺔ ﻗﺭﺍﺀﺓ ﻗﻴﻤﺔ ﺃﻱ ﺨﺎﻨﺔ‪ ،‬ﻓﻘﺎﺭﺉ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻴﺴـﺒﺏ‬
‫ﺨﻁﺄ ﺇﺫﺍ ﻜﺎﻨﺕ ﻗﻴﻤﺔ ﺍﻟﺨﺎﻨﺔ ‪ ..NULL‬ﻫﻜﺫﺍ ﻤﺜﻼ ﻴﻤﻜﻨﻙ ﻤﺤﺎﻭﻟﺔ ﻗﺭﺍﺀﺓ ﺍﻟﺨﺎﻨﺔ ﺍﻟﻤﻭﺠﻭﺩﺓ‬
‫ﻓﻲ ﺍﻟﻌﻤﻭﺩ ﺍﻷﻭل ﻓﻲ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ‪:‬‬
‫))‪if (! Reader.IsDBNull(0‬‬
‫;)) (‪MessageBox.Show(Reader[0].ToString‬‬
‫‪١٠٩‬‬
‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﻋﺩﺩﺍ ﻤﻥ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﻲ ﺘﺴﺘﻘﺒل ﺭﻗﻡ ﺍﻟﻌﻤـﻭﺩ‪ ،‬ﻭﺘﻌﻴـﺩ ﻗﻴﻤـﺔ ﺍﻟﺨﺎﻨـﺔ‬
‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ‪ ..‬ﻭﺘﺨﺘﻠﻑ ﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﻓﻲ ﻨﻭﻉ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻌﺎﺌـﺩﺓ‬
‫ﻤﻨﻬﺎ‪ ،‬ﺤﻴﺙ ﺘﻘﻭﻡ ﻜل ﻤﻨﻬﺎ ﺒﺘﺤﻭﻴل ﺒﻴﺎﻨﺎﺕ ﺍﻟﺨﺎﻨﺔ ﺇﻟﻰ ﺃﺤﺩ ﺃﻨﻭﺍﻉ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﺍﻷﺴﺎﺴـﻴﺔ‪ ،‬ﻜﻤـﺎ‬
‫ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﺘﺎﻟﻲ‪:‬‬

‫ﺍﻟﻨﻭﻉ ﺍﻟﺫﻱ ﺘﻌﻴﺩﻩ‬ ‫ﺍﻟﻭﺴﻴﻠﺔ‬


‫ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ ‪.Byte‬‬ ‫‪GetByte‬‬
‫ﺤﺭﻑ ‪.Char‬‬ ‫‪GetChar‬‬
‫ﻗﻴﻤﺔ ﻤﻨﻁﻘﻴﺔ ‪.Boolean‬‬ ‫‪GetBoolean‬‬
‫ﻋﺩﺩ ﻗﺼﻴﺭ ‪.Short‬‬ ‫‪GetInt16‬‬
‫ﻋﺩﺩ ﺼﺤﻴﺢ ‪.Integer‬‬ ‫‪GetInt32‬‬
‫ﻋﺩﺩ ﻁﻭﻴل ‪.Long‬‬ ‫‪GetInt64‬‬
‫ﻋﺩﺩ ﻤﻔﺭﺩ ‪.float‬‬ ‫‪GetFloat‬‬
‫ﻋﺩﺩ ﻤﺯﺩﻭﺝ ‪.Double‬‬ ‫‪GetDouble‬‬
‫ﻋﺩﺩ ﻋﺸﺭﻱ ‪.Decimal‬‬ ‫‪GetDecimal‬‬
‫ﺘﺎﺭﻴﺦ ﻭﻭﻗﺕ ‪.DateTime‬‬ ‫‪GetDateTime‬‬
‫ﻨﺹ ‪.String‬‬ ‫‪GetString‬‬
‫ﺴﺠل ﺍﻟﻤﻌﺭﻑ ﺍﻟﻤﺘﻔﺭﺩ ‪.Guid Structure‬‬ ‫‪GetGuid‬‬

‫ﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﻓﺸﻠﺕ ﻓﻲ ﺘﺤﻭﻴل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﺍﻟﻨﻭﻉ ﺍﻟﻤﻁﻠﻭﺏ‪.‬‬

‫‪١١٠‬‬
‫ﻓﺌﺔ ﺴﺠل ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DbDataRecord Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDataRecord‬ﻭﻫﻲ ﺘﻤﺘﻠﻙ ﻜل ﻭﺴﺎﺌﻠﻬﺎ ﻭﺨﺼﺎﺌﺼﻬﺎ ﺩﻭﻥ ﺃﻥ ﺘﺯﻴﺩ‬
‫ﻋﻠﻴﻬﺎ ﺸﻴﺌﺎ‪.‬‬
‫ﻭﺘﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻤﻊ ﻭﺍﺠﻬﺔ ﺍﻟﻌﺩﺍﺩ ﻟﻠﻤﺭﻭﺭ ﻋﺒﺭ ﺴﺠﻼﺕ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻜﻤﺎ ﺴﻨﺭﻯ ﻻﺤﻘﺎ‪.‬‬

‫‪١١١‬‬
‫ﻭﺍﺠﻬﺔ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪IDataReader Interface‬‬

‫ﺘﺭﺙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﻜﻼ ﻤﻥ ﺍﻟﻭﺍﺠﻬﺘﻴﻥ ‪ IDisposable‬ﻭ ‪.IDataRecord‬‬


‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺨﺼﺎﺌﺹ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺠﺩﻴﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻟﻌﻤﻕ ‪:Depth‬‬
‫ﺘﻌﻴﺩ ﺭﻗﻤﺎ ﻴﻤﺜل ﻋﻤﻕ ﺍﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ‪ ،‬ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﻨﺘﻴﺠﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺠـﺩﺍﻭل ﻤﺘﺩﺍﺨﻠـﺔ‬
‫)ﺨﺎﻨﺎﺕ ﺒﻬﺎ ﺠﺩﺍﻭل‪ ،‬ﺒﻬﺎ ﺨﺎﻨﺎﺕ ﺒﻬﺎ ﺠﺩﺍﻭل‪ ...‬ﺇﻟﺦ(‪ ،‬ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﺍﻟﺠﺩﻭل ﺍﻟﺨـﺎﺭﺠﻲ‬
‫ﻴﻜﻭﻥ ﻋﻤﻘﻪ ﺼﻔﺭﺍ‪ ،‬ﻭﺃﻭل ﺠﺩﻭﻻ ﺩﺍﺨﻠﻲ ﻋﻤﻘﻪ ‪ ... ١‬ﻭﻫﻜﺫﺍ‪.‬‬

‫ﻫل ﻫﻭ ﻤﻐﻠﻕ ‪:IsClosed‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﺘﻡ ﺇﻏﻼﻕ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﺘﺄﺜﺭﺓ ‪:RecordsAffected‬‬


‫ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﻲ ﺘﺄﺜﺭﺕ ﺒﺄﻭﺍﻤﺭ ﺍﻹﻀﺎﻓﺔ ﺃﻭ ﺍﻟﺘﺤﺩﻴﺙ ﺃﻭ ﺍﻟﺤﺫﻑ‪ ..‬ﻭﺘﻌﻴـﺩ ﻫـﺫﻩ‬
‫ﺍﻟﻭﺴــﻴﻠﺔ ‪ ٠‬ﺇﺫﺍ ﻟــﻡ ﺘﺘــﺄﺜﺭ ﺃﻴــﺔ ﺴــﺠﻼﺕ ﺃﻭ ﻓﺸــل ﺘﻨﻔﻴــﺫ ﺍﻻﺴــﺘﻌﻼﻡ‪،‬‬
‫ﻭﺘﻌﻴﺩ ‪ ١-‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻻﺴﺘﻌﻼﻡ ﻴﺴﺘﺨﺩﻡ ﺍﻷﻤﺭ ‪.SELECT‬‬
‫ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻻ ﺘﻌﻁﻴﻙ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺼﺤﻴﺤﺔ ﺇﻻ ﺒﻌﺩ ﺇﻏﻼﻕ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﻬـﺫﺍ‬
‫ﻋﻠﻴﻙ ﺃﻥ ﺘﺘﺄﻜـﺩ ﺃﻭﻻ ﺃﻥ ﻟﻠﺨﺎﺼـﻴﺔ ‪ IsClosed‬ﺍﻟﻘﻴﻤـﺔ ‪ ،true‬ﺃﻭ ﺘﺴـﺘﺨﺩﻡ ﺍﻟﺨﺎﺼـﺔ‬
‫‪ RecordsAffected‬ﺒﻌﺩ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪.Close‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺠﺩﻴﺩﺓ‪:‬‬

‫‪١١٢‬‬
‫ﻗﺭﺍﺀﺓ ‪:Read‬‬
‫ﺘﺠﻌل ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﻨﺘﻘل ﺇﻟﻰ ﺍﻟﺴﺠل ﺍﻟﺘﺎﻟﻲ‪ ،‬ﻭﺘﻌﻴﺩ ‪ ..true‬ﺃﻤﺎ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠل ﺍﻟﺤـﺎﻟﻲ‬
‫ﻫﻭ ﺁﺨﺭ ﺴﺠل ﻭﻻ ﻴﻭﺠﺩ ﺴﺠل ﺘﺎل‪ ،‬ﻓﺈﻨﻬﺎ ﺘﻌﻴﺩ ‪ ،false‬ﻭﻋﻠﻴﻙ ﺍﻟﺘﻭﻗﻑ ﻋﻥ ﺍﻟﻘﺭﺍﺀ ﻓـﻲ‬
‫ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ‪ ،‬ﻭﺇﻻ ﺤﺩﺙ ﺨﻁﺄ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺸﻴﺭ ﻤﺒﺩﺌﻴﺎ ﺇﻟﻰ ﺍﻟﺴﺠل ﺭﻗﻡ ‪ ،١-‬ﺃﻱ ﺃﻨﻪ ﻴﺸﻴﺭ ﺇﻟـﻰ ﺍﻟﺴـﺠل‬
‫ﺍﻟﺴﺎﺒﻕ ﻷﻭل ﺴﺠل‪ ،‬ﻭﻫﺫﺍ ﺴﻴﺠﻌل ﻤﺤﺎﻭﻟﺔ ﺍﻟﻘﺭﺍﺀﺓ ﺘﺴﺒﺏ ﺨﻁﺄ ﻓـﻲ ﺍﻟﺒﺭﻨـﺎﻤﺞ‪ ،‬ﺤﻴـﺙ‬
‫ﺴﺘﺨﺒﺭﻙ ﺭﺴﺎﻟﺔ ﺍﻟﺨﻁﺄ ﺃﻥ ﻫﺫﻩ ﻤﺤﺎﻭﻟﺔ ﻏﻴﺭ ﻤﺴﻤﻭﺡ ﺒﻬﺎ ﻟﻠﻘﺭﺍﺀﺓ ﺒﻴﻨﻤﺎ ﻻ ﺘﻭﺠﺩ ﺒﻴﺎﻨـﺎﺕ‬
‫ﺤﺎﻟﻴﺎ‪:‬‬
‫‪Invalid attempt to read when no data is present.‬‬
‫ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ Read‬ﺃﻭﻻ ﻟﻼﻨﺘﻘﺎل ﺇﻟﻰ ﺃﻭل ﺴﺠل ﻭﻗﺭﺍﺀﺘﻪ‪ ،‬ﺜﻡ ﺍﻻﺴﺘﻤﺭﺍﺭ‬
‫ﻓﻲ ﺍﺴﺘﺩﻋﺎﺌﻬﺎ ﺇﻟﻰ ﺃﻥ ﺘﻌﻴﺩ ‪ ،false‬ﻭﺫﻟﻙ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫{ )) (‪while (Reader.Read‬‬
‫ﺍﻟﻜﻭﺩ ﺍﻟﻼﺯﻡ ﻟﻘﺭﺍﺀﺓ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ‪//‬‬
‫}‬

‫ﺍﻟﻨﺘﻴﺠﺔ ﺍﻟﺘﺎﻟﻴﺔ ‪:NextResult‬‬


‫ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﻟﺘﻨﻔﻴﺫ ﺃﻜﺜﺭ ﻤﻥ ﺠﻤﻠﺔ ‪ ،SQL‬ﺃﻭ ﺘﻨﻔﻴﺫ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻥ ﻴﻌﻴﺩ ﺃﻜﺜﺭ‬
‫ﻤﻥ ﻨﺘﻴﺠﺔ‪ ،‬ﻓﺈﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺸﻴﺭ ﻤﺒﺩﺌﻴﺎ ﺇﻟﻰ ﺃﻭل ﻨﺘﻴﺠﺔ‪ ،‬ﻭﻋﻠﻴـﻙ ﺒﻌـﺩ ﻗـﺭﺍﺀﺓ ﻜـل‬
‫ﺴﺠﻼﺘﻬﺎ ﺃﻥ ﺘﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻟﺠﻌل ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺸﻴﺭ ﺇﻟﻰ ﺍﻟﻨﺘﻴﺠﺔ ﺍﻟﺘﺎﻟﻴﺔ‪ ..‬ﻭﺘﻌﻴـﺩ‬
‫ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ‪ true‬ﺇﺫﺍ ﻭﺠﺩﺕ ﻨﺘﻴﺠﺔ ﺘﺎﻟﻴﺔ‪ ،‬ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺃﻥ ﺘﺴﺘﻤﺭ ﻓﻲ ﺍﺴﺘﺩﻋﺎﺌﻬﺎ ﺇﻟـﻰ ﺃﻥ‬
‫ﺘﻌﻴﺩ ‪ ،false‬ﻭﺫﻟﻙ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪do‬‬
‫{‬
‫)) (‪while (Reader.Read‬‬
‫{‬
‫ﺍﻟﻜﻭﺩ ﺍﻟﻼﺯﻡ ﻟﻘﺭﺍﺀﺓ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ‪//‬‬
‫}‬
‫;)) (‪} while (Reader.NextResult‬‬
‫‪١١٣‬‬
‫ﻗﺭﺍﺀﺓ ﺠﺩﻭل ﺍﻟﻤﺨﻁﻁ ‪:GetSchemaTable‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺠﺩﻭل ‪ DataTable‬ﻓﺎﺭﻏﺎ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻤﺨﻁﻁ ﺍﻟﻨﺘﻴﺠﺔ ﺍﻟﺘﻲ ﻴﺘﻌﺎﻤـل ﻤﻌﻬـﺎ‬
‫ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ﻻﺤﻘﺎ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﺍﻟﻤﺨﻁﻁ ﻴﺒﺤﺘﻭﻱ ﻋﻠﻰ ﺍﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﻭﺃﻨﻭﺍﻉ ﺒﻴﺎﻨﺎﺘﻬﺎ ﻭﺃﺤﺠﺎﻤﻬﺎ‪ ..‬ﻭﻴﻤﻜﻨﻙ ﺃﻥ‬
‫ﺘﺭﻯ ﻫﺫﺍ ﺍﻟﻤﺨﻁﻁ ﺒﺸﻜل ﻋﻤﻠﻲ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪ ..SchemaTable‬ﻓﻲ ﻫـﺫﺍ ﺍﻟﻤﺸـﺭﻭﻉ‬
‫ﺍﺴــﺘﺨﺩﻤﻨﺎ ﻗــﺎﺭﺉ ﺒﻴﺎﻨــﺎﺕ ﻟﻴﺤﻤــل ﺠــﺩﻭل ﺍﻟﻤــﺅﻟﻔﻴﻥ‪ ،‬ﻭﺍﺴــﺘﺨﺩﻤﻨﺎ ﺍﻟﻭﺴــﻴﻠﺔ‬
‫‪ GetSchemaTable‬ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻤﺨﻁﻁ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ‪ ،‬ﻭﻋﺭﻀﻨﺎﻩ ﻓـﻲ ﺠـﺩﻭل‬
‫ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataGridView‬ﺍﻟﺫﻱ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻪ ﺒﺎﻟﺘﻔﺼﻴل ﻓﻲ ﻓﺼل ﻻﺤﻕ‪.‬‬

‫ﺇﻏﻼﻕ ‪:Close‬‬
‫ﺘﻐﻠﻕ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻫﺫﺍ ﻀﺭﻭﺭﻱ ﻟﺘﺤﺭﻴﺭ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﻤﺭﺘﺒﻁ ﺒﻘـﺎﺭﺉ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪،‬‬
‫ﻷﻨﻙ ﻟﻥ ﺘﺴﺘﻁﻴﻊ ﺍﺴﺘﺨﺩﺍﻡ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﻓﻲ ﺃﻱ ﻋﻤﻠﻴﺔ ﺃﺨﺭﻯ ﻁﺎﻟﻤﺎ ﻜﺎﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫ﻴﺴﺘﺨﺩﻤﻪ‪.‬‬
‫ﻭﻜﻤﺎ ﺫﻜﺭﻨﺎ ﻤﻥ ﻗﺒل‪ ،‬ﻟﻭ ﺃﻨﺸﺄﺕ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ ‪ExecuteReader‬‬
‫ﺒﺎﻟﺼﻴﻐﺔ ﺍﻟﺘﺎﻟﻴﺔ )ﺤﻴﺙ ‪ Cmd‬ﻫﻭ ﺍﺴﻡ ﻜﺎﺌﻥ ﺍﻷﻤﺭ(‪:‬‬
‫(‪var Dr = Cmd.ExecuteReader‬‬
‫;)‪CommandBehavior.CloseConnection‬‬
‫ﻓﺈﻥ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ‪ Dr‬ﺴﻴﻘﻭﻡ ﺒﺈﻏﻼﻕ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﻠﻘﺎﺌﻴﺎ ﺒﻤﺠـﺭﺩ ﺍﺴـﺘﺩﻋﺎﺀ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪.Close‬‬

‫‪١١٤‬‬
‫ﻓﺌﺔ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DbDataReader Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺴﺎﺴﻴﺔ ﻤﺠﺭﺩﺓ ﺘﺠﺏ ﻭﺭﺍﺜﺘﻬﺎ‪ ،‬ﻭﻫﻲ ﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﻨﻁﺎﻕ‪:‬‬


‫‪System.Data.Common‬‬
‫ﻭﺘﻤﺜل ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺍﺠﻬـﺔ ‪ ،IDataReader‬ﻤﻤـﺎ ﻴﻌﻨـﻲ ﺃﻨﻬـﺎ ﺘﻤﺜـل ﺃﻴﻀـﺎ ﺍﻟـﻭﺍﺠﻬﺘﻴﻥ‬
‫ـﺩ‬
‫ـﺔ ﻟﻠﻌـ‬
‫ـﺔ ﺍﻟﻘﺎﺒﻠﻴـ‬
‫ـﺎ ﻭﺍﺠﻬـ‬
‫ـل ﺃﻴﻀـ‬
‫ـﺎ ﺘﻤﺜـ‬
‫ـﺎ ﺃﻨﻬـ‬
‫‪ IDataRecord‬ﻭ ‪ ..IDisposable‬ﻜﻤـ‬
‫‪ ،IEnumerable‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻨﻬﺎ ﺘﻤﺘﻠﻙ ﺍﻟﻭﺴﻴﻠﺔ ‪ GetEnumerator‬ﺍﻟﺘﻲ ﺘﻌﻴﺩ ﻋـﺩﺍﺩﺍ ﻴﻤـﺭ‬
‫ﻋﺒﺭ ﺴﺠﻼﺕ ﺍﻟﻨﺘﻴﺠﺔ ﻭﺍﺤﺩﺍ ﺒﻌﺩ ﺍﻵﺨﺭ‪ ،‬ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﻜل ﻋﻨﺼﺭ ﻓﻲ ﻫﺫﺍ ﺍﻟﻌﺩﺍﺩ ﻫـﻭ ﻤـﻥ‬
‫ﻨﻭﻉ ﺍﻟﻔﺌﺔ ‪ ..DbDataRecord‬ﻟﻬﺫﺍ ﺘﺴﺘﻁﻴﻊ ﺍﻟﻤﺭﻭﺭ ﻋﺒﺭ ﺴﺠﻼﺕ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﺴـﺘﺨﺩﺍﻡ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪ Read‬ﻜﻤﺎ ﺸﺭﺤﻨﺎ ﺴﺎﺒﻘﺎ‪ ،‬ﺃﻭ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺤﻠﻘﺔ ﺍﻟﺘﻜﺭﺍﺭ ‪ foreach‬ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫)‪foreach (DbDataRecord R in Dr‬‬
‫{‬
‫;)) (‪MessageBox.Show(R[0].ToString‬‬
‫;)) (‪MessageBox.Show(R[1].ToString‬‬
‫}‬
‫ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﺴﻴﻌﺭﺽ ﻤﺤﺘﻭﻴﺎﺕ ﺃﻭل ﻭﺜﺎﻨﻲ ﺤﻘل ﻓﻲ ﻜل ﺴﺠل ﻤﻥ ﺴﺠﻼﺕ ﻗـﺎﺭﺉ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪،‬‬
‫ﺒﺎﻓﺘﺭﺍﺽ ﺃﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﺴﻤﻪ ‪ Dr‬ﻭﺃﻥ ﺍﻟﻨﺘﻴﺠﺔ ﺒﻬﺎ ﺤﻘﻼﻥ ﺃﻭ ﺃﻜﺜﺭ‪.‬‬
‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺨﺼﺎﺌﺹ‪ ،‬ﺘﻤﺘﻠﻙ ﺍﻟﻔﺌﺔ ‪ DbDataReader‬ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺠﺩﻴـﺩﺘﻴﻥ‬
‫ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﺒﻪ ﺼﻔﻭﻑ ‪:HasRows‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺘﻌﺎﻤل ﻤﻊ ﻨﺘﻴﺠﺔ ﺒﻬﺎ ﺼﻔﻭﻑ‪.‬‬

‫ﻋﺩﺩ ﺍﻟﺤﻘﻭل ﺍﻟﻤﺭﺌﻴﺔ ‪:VisibleFieldCount‬‬


‫ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻷﻋﻤﺩﺓ ﻏﻴﺭ ﺍﻟﺨﻔﻴﺔ ﻓﻲ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺠﺩﻴﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬


‫‪١١٥‬‬
‫ﻤﻌﺭﻓﺔ ﻨﻭﻉ ﺍﻟﺤﻘل ﻁﺒﻘﺎ ﻟﻠﻤﺯﻭﺩ ‪:GetProviderSpecificFieldType‬‬
‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺭﻗﻡ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻜﺎﺌﻥ ﺍﻟﻨﻭﻉ ‪ Type‬ﺍﻟـﺫﻱ ﻴﻤﺜـل ﻨـﻭﻉ‬
‫ﺒﻴﺎﻨﺎﺘﻪ‪ ..‬ﻫﺫﺍ ﺍﻟﻨﻭﻉ ﻟﻥ ﻴﻜﻭﻥ ﻤﻥ ﺃﻨﻭﺍﻉ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﺍﻷﺴﺎﺴﻴﺔ‪ ،‬ﺒل ﺴﻴﻜﻭﻥ ﻤﻥ ﺍﻷﻨـﻭﺍﻉ‬
‫ﺍﻟﺨﺎﺼﺔ ﺒﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜﺎل‪ ،‬ﻟﻭ ﻜﻨﺕ ﺘﺘﻌﺎﻤـل ﻤـﻊ ﻤـﺯﻭﺩ ﺴـﻴﻜﻭﻴل‬
‫ﺴﻴﺭﻓﺭ‪ ،‬ﻭﻜﺎﻥ ﺍﻟﻌﻤﻭﺩ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻨﺼﻭﺹ‪ ،‬ﻓﺈﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺴﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ﻴﻤﺜـل ﻨـﻭﻉ‬
‫ﺍﻟﻔﺌﺔ ‪ SqlString‬ﻭﻟﻴﺱ ﺍﻟﻔﺌﺔ‪.String‬‬

‫ﻗﺭﺍﺀﺓ ﺍﻟﻘﻴﻤﺔ ﻁﺒﻘﺎ ﻟﻠﻤﺯﻭﺩ ‪:GetProviderSpecificValue‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺭﻗﻡ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻜﺎﺌﻨﺎ ‪ Object‬ﻴﺤﻤل ﻗﻴﻤﺘﻪ‪ ..‬ﻻﺤـﻅ ﺃﻥ‬
‫ﻫﺫﻩ ﺍﻟﻘﻴﻤﺔ ﺴﺘﻜﻭﻥ ﻤﻥ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺨﺎﺼﺔ ﺒﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﻬﺫﺍ ﺴﻴﺴﺒﺏ ﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﺨﻁﺄ‬
‫)ﺒﺎﻓﺘﺭﺍﺽ ﺃﻥ ﺍﻟﻌﻤﻭﺩ ﺭﻗﻡ ﺼﻔﺭ ﻋﻤﻭﺩ ﻨﺼﻲ(‪:‬‬
‫;)‪var Name = (string) Dr.GetProviderSpecificValue(0‬‬
‫;)‪MessageBox.Show(Name‬‬
‫ﻭﺍﻟﺼﻭﺍﺏ ﺃﻥ ﺘﺴﺘﺨﺩﻡ ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ‪:‬‬
‫;)‪var Name = (SqlString) Dr.GetProviderSpecificValue(0‬‬
‫;)‪MessageBox.Show(Name.Value‬‬

‫ﻗﺭﺍﺀﺓ ﺍﻟﻘﻴﻡ ﻁﺒﻘﺎ ﻟﻠﻤﺯﻭﺩ ‪:GetProviderSpecificValues‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﺼﻔﻭﻓﺔ ﻜﺎﺌﻨﺎﺕ ‪ ،Object Array‬ﻟﺘﻤﻸﻫﺎ ﻟﻙ ﺒﺒﻴﺎﻨﺎﺕ ﺍﻟﺼـﻑ‬
‫ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩ ﺍﻟﺨﺎﻨـﺎﺕ ﺍﻟﺘـﻲ ﺘـﻡ ﻤﻠﺅﻫـﺎ ﻓـﻲ‬
‫ﺍﻟﻤﺼﻔﻭﻓﺔ‪.‬‬

‫ﻭﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪:DbDataReader‬‬


‫‪DataTableReader Class -١‬‬
‫‪OdbcDataReader Class -٢‬‬
‫‪OleDbDataReader Class -٣‬‬
‫‪SqlDataReader Class -٤‬‬
‫‪OracleDataReader Class -٥‬‬

‫‪١١٦‬‬
‫ﻭﺴﻨﻜﺘﻔﻲ ﻫﻨﺎ ﺒﺎﻟﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ ‪ ،SqlDataReader‬ﻭﺴﻨﺘﻌﺭﻑ ﻓﻲ ﻓﺼل ﻻﺤﻕ ﻋﻠﻰ ﺍﻟﻔﺌﺔ‬
‫‪.DataTableReader‬‬

‫ﻓﺌﺔ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ‪SqlDataReader Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ DbDataReader‬ﺒﻜل ﻭﺴﺎﺌﻠﻬﺎ ﻭﺨﺼﺎﺌﺼﻬﺎ‪ ،‬ﻭﻫﻲ ﺘﻤﻜﻨﻙ ﻤﻥ ﻗـﺭﺍﺀﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻘﺎﺩﻤﺔ ﻤﻥ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل‪.‬‬
‫ﻭﻟﻴﺱ ﻟﻬﺫﻩ ﺍﻟﻔﺌﺔ ﺤﺩﺙ ﺇﻨﺸﺎﺀ‪ ،‬ﻭﻟﻜﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻨﺴﺨﺔ ﻤﻨﻬﺎ ﺒﺎﺴـﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪ ExecuteReader‬ﺍﻟﺨﺎﺼﺔ ﺒﺄﻤﺭ ﺴﻴﻜﻭﻴل ‪.SqlCommand‬‬
‫ﻭﻻ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﻴﺔ ﺨﺼﺎﺌﺹ ﺠﺩﻴﺩﺓ ﻏﻴﺭ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪ ،‬ﻭﻟﻜﻨﻬﺎ ﺘﻤﺘﻠﻙ ﺍﻟﻌﺩﻴﺩ ﻤﻥ‬
‫ﺍﻟﻭﺴﺎﺌل ﺍﻟﺠﺩﻴﺩﺓ‪ ،‬ﻭﻫﻲ ﺘﻘﻭﻡ ﺒﻘﺭﺍﺀﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﺃﺭﺴﻠﺕ ﺭﻗﻤﻪ ﺇﻟﻴﻬـﺎ ﻜﻤﻌﺎﻤـل‪،‬‬
‫ﻭﺘﺤﻭﻟﻬﺎ ﺇﻟﻰ ﻨﻭﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻁﻠﻭﺏ‪ ..‬ﻭﻷﺴﻤﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺼﻴﻐﺔ ﺍﻟﻌﺎﻤﺔ ‪ ،GetX‬ﺤﻴـﺙ‬
‫‪ X‬ﻫﻭ ﺍﺴﻡ ﻨﻭﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺍﻟﺘﺤﻭﻴل ﺇﻟﻴﻪ‪ ..‬ﻭﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﻫﻲ‪:‬‬
‫‪GetSqlBoolean‬‬ ‫‪GetSqlBinary‬‬
‫‪GetSqlBytes‬‬ ‫‪GetSqlByte‬‬
‫‪GetSqlDateTime‬‬ ‫‪GetSqlChars‬‬
‫‪GetSqlDouble‬‬ ‫‪GetSqlDecimal‬‬
‫‪GetSqlInt16‬‬ ‫‪GetSqlGuid‬‬
‫‪GetSqlInt64‬‬ ‫‪GetSqlInt32‬‬
‫‪GetSqlSingle‬‬ ‫‪GetSqlMoney‬‬
‫‪GetSqlValue‬‬ ‫‪GetSqlString‬‬
‫‪GetSqlXml‬‬ ‫‪GetSqlValues‬‬
‫‪GetTimeSpan‬‬

‫ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜﺎل‪ ،‬ﺍﻟﻭﺴﻴﻠﺔ ‪ GetSqlBinary‬ﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ﻤـﻥ ﺍﻟﻨـﻭﻉ ‪ ،SqlBinary‬ﻭﻫـﻲ‬


‫ﻤﻨﺎﺴﺒﺔ ﻟﻘﺭﺍﺀﺓ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﺒﻴﺎﻨﺎﺕ ﻤـﻥ ﺍﻟﻨـﻭﻉ ‪ image‬ﺃﻭ )‪،varbinary(MAX‬‬
‫ﻟﻬﺫﺍ ﺍﺴﺘﺨﺩﻤﺎﻫﺎ ﻓﻲ ﺍﻟﺯﺭ ‪ GetSqlBinary‬ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ ‪ ،ReadLargeData‬ﻟﻘـﺭﺍﺀﺓ‬
‫ﺼﻭﺭﺓ ﺃﻭل ﻨﺎﺸﺭ ﻤﻥ ﺍﻟﻌﻤﻭﺩ ‪) Logo2‬ﻭﻴﻤﻜﻨﻙ ﺍﺴـﺘﺨﺩﺍﻤﻬﺎ ﺃﻴﻀـﺎ ﻟﻠﻘـﺭﺍﺀﺓ ﻤـﻥ ﺍﻟﻌﻤـﻭﺩ‬

‫‪١١٧‬‬
‫‪ ،(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‬‬

‫ﺘﻘﺩﻡ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻷﺴﺎﺴﻴﺔ ﻟﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻫﻲ ﺘﻤﺘﻠﻙ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل ‪:TableMappings‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ﻴﻤﺜل ﺍﻟﻤﺠﻤﻭﻋﺔ ‪ ،ITableMappingCollection‬ﻴﺤﺘـﻭﻱ ﻋﻠـﻰ ﺨـﺭﺍﺌﻁ‬
‫ﺍﻟﺠﺩﺍﻭل ﺍﻟﺘﻲ ﻴﺴﺘﺨﺩﻤﻬﺎ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﺭﺒﻁ ﺍﻟﺠﺩﺍﻭل ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﻟﺠـﺩﺍﻭل‬
‫ﺍﻷﺼﻠﻴﺔ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﻤﻭﻀﻭﻉ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬

‫ﺍﻟﺘﺼﺭﻑ ﻋﻨﺩ ﻏﻴﺎﺏ ﺍﻟﺨﺭﻴﻁﺔ ‪:MissingMappingAction‬‬


‫ﺘﺤﺩﺩ ﻤﺎﺫﺍ ﺴﻴﺤﺩﺙ ﻓﻲ ﺤﺎﻟﺔ ﻋﺩﻡ ﻭﺠﻭﺩ ﺨﺭﻴﻁﺔ ﻟﺒﻌﺽ ﺍﻟﺠﺩﺍﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪،‬‬
‫ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ MissingMappingAction‬ﺍﻟﺘـﻲ ﺴـﻨﺘﺘﻌﺭﻑ ﻋﻠﻴﻬـﺎ‬
‫ﻻﺤﻘﺎ‪.‬‬
‫ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫﻲ ‪ ،Passthrough‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫ﺘﺴﺘﺨﺩﻡ ﻨﻔﺱ ﺃﺴﻤﺎﺀ ﺠﺩﺍﻭل ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻻ ﺤﺎﺠﺔ ﺇﻟﻰ ﻭﺠﻭﺩ ﺨﺭﻴﻁﺔ ﻟﻠﺠﺩﺍﻭل ﻓـﻲ‬
‫ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ‪.‬‬

‫ﺍﻟﺘﺼﺭﻑ ﻋﻨﺩ ﻏﻴﺎﺏ ﺍﻟﻤﺨﻁﻁ ‪:MissingSchemaAction‬‬


‫ﺘﺤﺩﺩ ﻤﺎﺫﺍ ﺴﻴﺤﺩﺙ ﻓﻲ ﺤﺎﻟﺔ ﻋﺩﻡ ﻭﺠﻭﺩ ﺒﻌﺽ ﺍﻟﺠـﺩﺍﻭل ﺃﻭ ﺍﻷﻋﻤـﺩﺓ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤـﺭﻗﻡ ‪ MissingSchemaAction‬ﺍﻟﺘـﻲ ﺴـﻨﺘﻌﺭﻑ‬
‫ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ‪.‬‬
‫ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫﻲ ‪ ،Add‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﺍﻟﺠﺩﻭل ﺍﻟﻨﺎﻗﺹ ﺃﻭ ﺍﻟﻌﻤـﻭﺩ‬
‫ﺍﻟﻨﺎﻗﺹ ﺴﻴﻀﺎﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﻠﻘﺎﺌﻴﺎ ﻋﻨﺩ ﻤﻠﺌﻬﺎ ﺒﺎﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪١٢٠‬‬
‫ﻤلﺀ ‪: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‬‬

‫ﻤلﺀ ﺍﻟﻤﺨﻁﻁ ‪:FillSchema‬‬


‫ﺘﻤﻸ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻤﺨﻁﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ..Schema‬ﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﺍﻟﺠﺩﺍﻭل ﺍﻟﻨﺎﺘﺠﺔ ﻋﻥ‬
‫ﺍﻻﺴﺘﻌﻼﻡ ﻭﺘﻔﺎﺼﻴل ﺃﻋﻤﺩﺘﻬﺎ ﺴﺘﻀﺎﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ،‬ﻟﻜـﻥ ﺩﻭﻥ ﺇﻀـﺎﻓﺔ ﺃﻱ‬
‫ﺴﺠﻼﺕ ﺇﻟﻴﻬﺎ‪ ..‬ﺒﺘﻌﺒﻴﺭ ﺁﺨﺭ‪ :‬ﺴﻴﺘﻡ ﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺠﺩﺍﻭل ﻓﺎﺭﻏﺔ‪.‬‬
‫ﻭﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻠﻴﻥ‪:‬‬
‫‪ -‬ﻜﺎﺌﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataSet‬ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﻤﻠﺅﻩ ﺒﺎﻟﻤﺨﻁﻁ‪.‬‬
‫‪١٢٢‬‬
‫‪ -‬ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ ،SchemaType‬ﺘﺤﺩﺩ ﻤﺎﺫﺍ ﺴﻴﺤﺩﺙ ﻟﻭ ﻜﺎﻥ ﻤﻬﻴـﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﻴﺤﺘﻭﻱ ﻤﺴﺒﻘﺎ ﻋﻠﻰ ﺨﺭﺍﺌﻁ ﻟﻠﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ‪ ،Mappings‬ﻭﻫﺫﻩ ﺍﻟﻘﻴﻡ ﻫﻲ‪:‬‬

‫ﺘﺠﺎﻫل ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل ﻭﺨﺭﺍﺌﻁ ﺍﻷﻋﻤـﺩﺓ‪ ،‬ﻭﻤـلﺀ ﻤﺠﻤﻭﻋـﺔ‬ ‫‪Source‬‬


‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻨﻔﺱ ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺍﻷﺼﻠﻴﺔ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓـﻲ‬
‫ﺍﻟﻤﺨﻁﻁ ‪.Schema‬‬
‫‪ Mapped‬ﺍﺴﺘﺨﺩﺍﻡ ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل ﻭﺨﺭﺍﺌﻁ ﺍﻷﻋﻤﺩﺓ‪ ،‬ﻭﻤـلﺀ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﻷﺴﻤﺎﺀ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺭﺍﺌﻁ ﺒﺩﻻ ﻤـﻥ ﺃﺴـﻤﺎﺀ‬
‫ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺍﻷﺼﻠﻴﺔ‪.‬‬

‫ﻭﻴﺘﻀﻤﻥ ﺍﻟﻤﺨﻁﻁ ﺍﻟﺫﻱ ﻴﺘﻡ ﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻪ ﺍﻟﺘﻔﺎﺼﻴل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬


‫‪ -‬ﺍﺴﻡ ﺍﻟﺠﺩﻭل‪ ،‬ﻭﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ‪.‬‬
‫‪ -‬ﺨﺼﺎﺌﺹ ﻜل ﻋﻤﻭﺩ‪ ،‬ﻤﺜل‪:‬‬
‫ﺃ‪ .‬ﺍﻟﺴﻤﺎﺡ ﺒﺘﺭﻜﻪ ﻓﺎﺭﻏﺎ ‪.AllowDBNull‬‬
‫ﺏ‪ .‬ﻫل ﻫﻭ ﻤﺘﻔﺭﺩ ‪.Unique‬‬
‫ﺝ‪ .‬ﻫل ﻫﻭ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ ‪.ReadOnly‬‬
‫ﺩ‪ .‬ﻫل ﻴﺯﻴﺩ ﺘﻠﻘﺎﺌﻴﺎ ‪ ..AutoIncrement‬ﻟﻜﻥ ﻋﻠﻴﻙ ﺃﻨﺕ ﺘﺤﺩﻴﺩ ﻤﻌـﺩل ﺍﻟﺯﻴـﺎﺩﺓ‬
‫ﻭﺒﺩﺍﻴﺔ ﺍﻟﻌﺩﺍﺩ‪ ،‬ﻓﻬﻤﺎ ﻻ ﻴﻀﺎﻓﺎﻥ ﺘﻠﻘﺎﺌﻴﺎ‪.‬‬
‫ﻫـ‪ .‬ﺃﻗﺼﻰ ﻁﻭل ﻟﻠﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﻌﻤﻭﺩ ‪.MaxLength‬‬

‫‪ -‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ‪ Primary Key‬ﻤﻭﺠﻭﺩﺍ ﻀﻤﻥ ﺃﻋﻤﺩﺓ ﺍﻟﻨﺘﻴﺠـﺔ‪ ،‬ﻴـﺘﻡ‬
‫ﺍﺴﺘﺨﺩﺍﻤﻪ ﻜﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ ﻟﻠﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺇﺫﺍ ﻟﻡ ﻴﻭﺠـﺩ ﻤﻔﺘـﺎﺡ‬
‫ﺃﺴﺎﺴﻲ ﻭﻜﺎﻥ ﻫﻨﺎﻙ ﺤﻘل ﻤﺘﻔﺭﺩ ﺍﻟﻘﻴﻤﺔ ‪ ،Unique‬ﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻜﻤﻔﺘﺎﺡ ﺃﺴﺎﺴـﻲ‬
‫ﻟﻠﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺒﺸـﺭﻁ ﺃﻻ ﻴﻜـﻭﻥ ﻤﺴـﻤﻭﺤﺎ ﺒﺘﺭﻜـﻪ ﻓﺎﺭﻏـﺎ‬
‫)‪ ..(AllowDbNull = False‬ﺃﻤﺎ ﺇﺫﺍ ﻜـﺎﻥ ﺍﻟﺤﻘـل ﺍﻟﻤﺘﻔـﺭﺩ ﻴﻘﺒـل ﺍﻟﻘﻴﻤـﺔ‬
‫‪ ،NULL‬ﻓﻠﻥ ﻴﺴﺘﺨﺩﻡ ﻜﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ‪ ،‬ﻭﺴﺘﻜﺘﻔﻲ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺒﺈﻀﺎﻓﺔ ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ‬
‫‪١٢٣‬‬
‫‪ UniqueConstraint‬ﺍﻟﺨــﺎﺹ ﺒﻬــﺫﺍ ﺍﻟﻌﻤــﻭﺩ ﺇﻟــﻰ ﻤﺠﻤﻭﻋــﺔ ﺍﻟﻘﻴــﻭﺩ‬
‫‪ ConstrainsCollection‬ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﺠﺩﻭل‪.‬‬
‫‪ -‬ﺃﻱ ﻗﻴﻭﺩ ﺃﺨﺭﻯ ﻏﻴﺭ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻭﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ ﻻ ﺘﻀـﺎﻑ ﺇﻟـﻰ ﺍﻟﺠـﺩﻭل‪،‬‬
‫ﻭﻋﻠﻴﻙ ﺇﻀﺎﻓﺘﻬﺎ ﺒﻨﻔﺴﻙ!‬

‫ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﺼﻔﻭﻓﺔ ﺠـﺩﺍﻭل ‪ ،DataTable Array‬ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﻜﺎﺌﻨـﺎﺕ‬


‫ﺍﻟﺠﺩﺍﻭل ﺍﻟﺘﻲ ﺘﻤﺕ ﺇﻀﺎﻓﺔ ﻤﺨﻁﻁﺎﺘﻬﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺒﻌﺩ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ Fill‬ﻟﻤلﺀ ﺍﻟﺠﺩﺍﻭل ﺒﺎﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻟﻜﻥ‬
‫ﻜﻤﺎ ﺫﻜﺭﻨﺎ ﺴﺎﺒﻘﺎ‪ ،‬ﻓﺈﻥ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ Fill‬ﺒﻤﻔﺭﺩﻫـﺎ ﻴﻀـﻴﻑ ﻤﺨﻁﻁـﺎﺕ ﺍﻟﺠـﺩﺍﻭل‬
‫ﻭﺍﻟﺴﺠﻼﺕ ﻤﻌﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻤﻤﺎ ﻴﻐﻨﻲ ﻓﻲ ﻤﻌﻅﻡ ﺍﻟﺤـﺎﻻﺕ ﻋـﻥ ﺍﺴـﺘﺩﻋﺎﺀ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪ FillSchema‬ﺃﻭﻻ‪.‬‬
‫ﺇﺫﻥ‪ ..‬ﻓﻤﺎ ﻓﺎﺌﺩﺓ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ؟‬
‫ﺘﻔﻴﺩﻙ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺇﺫﺍ ﺃﺭﺩﺕ ﻋﺭﺽ ﺍﻟﺠﺩﺍﻭل ﻓﺎﺭﻏﺔ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻟﻴﺩﺨل ﺒﻴﺎﻨـﺎﺕ ﺠﺩﻴـﺩﺓ‬
‫ﺩﻭﻥ ﺃﻥ ﻴﻌﺒﺙ ﺒﺎﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻘﺩﻴﻤﺔ‪ ..‬ﻫﺫﺍ ﻫﻭ ﻤﺎ ﻓﻌﻠﻨﺎﻩ ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ ‪،UpdateErrors‬‬
‫ﺤﻴﺙ ﺴﻴﻌﺭﺽ ﺠﺩﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻋﻤﺩﺓ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ‪ ،‬ﻟﻜﻨﻪ ﻟـﻥ ﻴﻌـﺭﺽ ﺒﻴﺎﻨـﺎﺕ ﺃﻱ‬
‫ﻤﺅﻟﻔﻴﻥ‪ ،‬ﻭﺒﻬﺫﺍ ﻴﺴﺘﻁﻴﻊ ﺍﻟﻤﺴﺘﺨﺩﻤﻥ ﺇﺩﺨﺎل ﻤﺅﻟﻔﻴﻥ ﺠﺩﺩﺍ ﻭﺤﻔﻅﻬﻡ ﻓﻲ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪،‬‬
‫ﺩﻭﻥ ﺃﻥ ﻴﻐﻴﺭ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺅﻟﻔﻴﻥ ﺍﻟﺴﺎﺒﻘﻴﻥ‪.‬‬
‫ﺘﻔﻴﺩﻙ ﺃﻴﻀﺎ ﺇﺫﺍ ﺃﺭﺩﺕ ﺇﻨﺸﺎﺀ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻓﻲ ﺠﺩﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻓﺎﺴـﺘﺨﺩﺍﻡ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪ Fill‬ﺒﻤﻔﺭﺩﻫﺎ ﻻ ﻴﻨﺸﺊ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﺘﻀﻴﻔﻪ ﺇﻟﻰ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻫﺫﺍ ﻗﺩ ﻴﺴﺒﺏ ﻟﻙ ﻤﺸﺎﻜل ﻓﻲ ﺒﻌﺽ ﺍﻟﺤﺎﻻﺕ ﺍﻟﺘﻲ ﺘﺤﺘﺎﺝ ﻓﻴﻬﺎ ﺇﻟـﻰ ﺍﻟﻤﻔﺘـﺎﺡ‬
‫ﺍﻷﺴﺎﺴﻲ‪ ،‬ﻜﻤﺎ ﻴﺤﺩﺙ ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ DataSet.Merge‬ﻟﺩﻤﺞ ﺍﻟﺴﺠﻼﺕ‪ ،‬ﺃﻭ ﻋﻨﺩ‬
‫ﺍﻟﺒﺤﺙ ﻋﻥ ﺴﺠل ﺒﺩﻻﻟﺔ ﻤﻔﺘﺎﺤﻪ ﺍﻷﺴﺎﺴﻲ‪ ...‬ﺇﻟﺦ‪.‬‬

‫‪١٢٤‬‬
‫ﻤﻌﺭﻓﺔ ﻤﻌﺎﻤﻼﺕ ﺍﻟﻤلﺀ ‪: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‬ﻭﻫﻲ ﺘﻤﺩ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒـﺎﻷﻭﺍﻤﺭ ﺍﻟﻼﺯﻤـﺔ‬
‫ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻷﻡ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ‪:SelectCommand‬‬


‫ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻥ ﻨﻭﻉ ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDbCommand‬ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﻜـﺎﺌﻥ ﺍﻷﻤـﺭ ﺍﻟـﺫﻱ‬
‫ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺠﻤﻠﺔ ﺍﻟﺘﺤﺩﻴﺩ ‪ SELECT‬ﺍﻟﺘﻲ ﺴﻴﺴﺘﺨﺩﻤﻬﺎ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻠﺤﺼﻭل ﻋﻠـﻰ‬
‫ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺌﻙ ﻟﻠﻭﺴﻴﻠﺔ ‪ Fill‬ﺃﻭ ‪ ..FillSchema‬ﻭﻴﻤﻜـﻥ‬
‫ﺃﻥ ﻴﺤﺘﻭﻱ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﻤﻥ ﺍﻷﻭﺍﻤﺭ ‪ ،Batch SQL‬ﻭﻓﻲ ﻫـﺫﻩ ﺍﻟﺤﺎﻟـﺔ‬
‫ﻴﻀﻴﻑ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻜﺜﺭ ﻤﻥ ﺠﺩﻭل ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺃﻤﺭ ﺍﻹﺩﺭﺍﺝ ‪:InsertCommand‬‬


‫ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻥ ﻨﻭﻉ ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDbCommand‬ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﻜـﺎﺌﻥ ﺍﻷﻤـﺭ ﺍﻟـﺫﻱ‬
‫ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺠﻤﻠﺔ ﺍﻹﺩﺭﺍﺝ ‪ INSERT‬ﺍﻟﺘﻲ ﻴﺴﺘﺨﺩﻤﻬﺎ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻨـﺩ ﺍﺴـﺘﺩﻋﺎﺀ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪ ،Update‬ﻹﻀﺎﻓﺔ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺠﺩﻴﺩﺓ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻫﺫﺍ ﺍﻟﻤﺸﺭﻭﻉ ﻴﺴﺘﺨﺩﻡ ﺍﺴﺘﻌﻼﻡ ﺍﻹﺩﺭﺍﺝ ﺍﻟﺘﺎﻟﻲ‪:‬‬
‫‪INSERT INTO Authors‬‬
‫)‪(Author, CountryID, Phone, About‬‬
‫)‪VALUES (@Author, @CountryID, @Phone, @About‬‬
‫ﻭﻴﺭﻴﻙ ﺍﻟﻤﺸﺭﻭﻉ ‪ CopyAuthors‬ﻜﻴﻑ ﻴﻤﻜـﻥ ﺘﻌﺭﻴـﻑ ﺃﻤـﺭ ﺍﻹﺩﺭﺍﺝ ﻭﻤﻌﺎﻤﻼﺘـﻪ‪،‬‬
‫ﻭﻭﻀﻌﻪ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،InsertCommand‬ﻭﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ Update‬ﻻﺴـﺘﺨﺩﺍﻤﻪ‬
‫ﻓﻲ ﺤﻔﻅ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺠﺩﻴﺩﺓ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟـﻰ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ..‬ﻻﺤـﻅ ﺃﻥ‬
‫ﻤﻌﺎﻤﻼﺕ ﺃﻤﺭ ﺍﻹﺩﺭﺍﺝ ﻤﺭﺒﻭﻁﺔ ﺒﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻷﺨﺫ ﻗﻴﻤﻬﺎ ﻤﻥ ﺃﻋﻤﺩﺘﻬﺎ ﻤﺒﺎﺸـﺭﺓ‪..‬‬
‫ﻟﻘﺩ ﻋﺭﻓﻨـﺎ ﻤـﻥ ﻗﺒـل ﺃﻥ ﻜـﺎﺌﻥ ﺍﻟﻤﻌﺎﻤـل ‪ Parameter Object‬ﻴﻤﻠـﻙ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪١٢٧‬‬
‫‪ SourceColumn‬ﺍﻟﺘﻲ ﻨﻀﻊ ﻓﻴﻬﺎ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﺴﻨﻘﺭﺃ ﺍﻟﻘﻴﻤﺔ ﻤﻨﻪ ﻤـﻥ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺴﺘﺠﺩ ﺃﻨﻨﺎ ﺃﺭﺴﻠﻨﺎ ﺍﻟﻘﻴﻤﺔ ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻥ ﺨﻼل ﺍﻟﻤﻌﺎﻤل ﺍﻟﺭﺍﺒﻊ ﻟﺤـﺩﺙ‬
‫ﺍﻹﻨﺸﺎﺀ ‪ New‬ﻋﻨﺩ ﺘﻌﺭﻴﻑ ﻜل ﻤﻌﺎﻤل‪ ،‬ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ‪:‬‬
‫{ ][‪InsertCmd.Parameters.AddRange(new SqlParameter‬‬
‫‪new SqlParameter("@Author",‬‬
‫‪SqlDbType.NVarChar, 0, "Author"),‬‬
‫‪new SqlParameter("@CountryID",‬‬
‫‪SqlDbType.SmallInt, 0, "CountryID"),‬‬
‫‪new SqlParameter("@Phone",‬‬
‫‪SqlDbType.VarChar, 0, "Phone"),‬‬
‫‪new SqlParameter("@About",‬‬
‫;)})"‪SqlDbType.NVarChar, 0, "About‬‬
‫ﻟﻬﺫﺍ ﻻ ﻨﺤﺘﺎﺝ ﺇﻟﻰ ﻜﺘﺎﺒﺔ ﺃﻱ ﻜﻭﺩ ﻟﻘﺭﺍﺀﺓ ﺍﻟﻘﻴﻡ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ،‬ﻓﻌﻨـﺩ ﺍﺴـﺘﺩﻋﺎﺀ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪ ،Update‬ﻓﺈﻨﻬﺎ ﺘﻤﺭ ﻋﺒﺭ ﻜل ﺼﻑ ﻤﻥ ﺼﻔﻭﻑ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺘﺄﺨﺫ ﻗـﻴﻡ‬
‫ﺃﻋﻤﺩﺘﻪ ﻭﺘﻤﺭﺭﻫﺎ ﺇﻟﻰ ﻤﻌﺎﻤﻼﺕ ﺃﻤﺭ ﺍﻹﺩﺭﺍﺝ‪ ،‬ﻭﺘﻨﻔﺫ ﺃﻤﺭ ﺍﻹﺩﺭﺍﺝ‪.‬‬

‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ﻹﻨﻌﺎﺵ ﺍﻟﺴﺠل ﺍﻟﻤﻌﺭﻭﺽ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻴﻤﻜﻨﻙ ﺍﺴـﺘﺨﺩﺍﻡ ﺠﻤﻠـﺔ ‪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‬ﻤﻤﺎ ﻴﺠﻌﻠﻬﺎ ﺘﻌﻴﺩ ﻤﻌﺭﻓﺎ ﻏﻴﺭ ﺍﻟﻤﻌﺭﻑ ﺍﻟﺨﺎﺹ ﺒﺎﻟﺴﺠل ﺍﻟﺫﻱ ﺃﻀﻔﺘﻪ‪.‬‬

‫ﺃﻤﺭ ﺍﻟﺤﺫﻑ ‪:DeleteCommand‬‬


‫ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻥ ﻨﻭﻉ ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDbCommand‬ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﻜـﺎﺌﻥ ﺍﻷﻤـﺭ ﺍﻟـﺫﻱ‬
‫ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺠﻤﻠﺔ ﺍﻟﺤﺫﻑ ‪ DELETE‬ﺍﻟﺘﻲ ﻴﺴﺘﺨﺩﻤﻬﺎ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻨـﺩ ﺍﺴـﺘﺩﻋﺎﺀ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪ ،Update‬ﻟﺤﺫﻑ ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﺫﺍ ﻜﺎﻨﺕ ﻗﺩ ﺤﺫﻓﺕ ﻤﻥ ﻤﺠﻤﻭﻋﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ‪:UpdateCommand‬‬


‫ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻥ ﻨﻭﻉ ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDbCommand‬ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﻜـﺎﺌﻥ ﺍﻷﻤـﺭ ﺍﻟـﺫﻱ‬
‫ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺠﻤﻠﺔ ﺍﻟﺘﺤﺩﻴﺙ ‪ UPDATE‬ﺍﻟﺘﻲ ﻴﺴﺘﺨﺩﻤﻬﺎ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻨﺩ ﺍﺴـﺘﺩﻋﺎﺀ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪ ،Update‬ﻟﻨﻘل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ﺇﺫﺍ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ‪ ،‬ﻭﻜﻨﺕ ﻗﺩ ﻭﻀﻌﺕ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ‬
‫ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،SelectCommand‬ﻓﻠﺴﺕ ﻤﺠﺒﺭﺍ ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻋﻠﻰ ﺇﻨﺸـﺎﺀ ﺃﻭﺍﻤـﺭ‬
‫ﺍﻟﺤﺫﻑ ﻭﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺘﺤﺩﻴﺙ ﺒﻨﻔﺴﻙ‪ ،‬ﻓﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺴﺘﻁﻴﻊ ﺇﻨﺘﺎﺝ ﻫﺫﻩ ﺍﻷﻭﺍﻤـﺭ ﺁﻟﻴـﺎ‬
‫ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ Update‬ﺍﻟﺨﺎﺼﺔ ﺒﻪ‪ ،‬ﻭﻫﻭ ﻴﺴﺘﺨﺩﻡ ﻟﻔﻌل ﻫﺫﺍ ﻓﺌﺎﺕ ﺒﻨﺎﺀ ﺍﻷﻭﺍﻤﺭ‬
‫‪ CommandBuilders‬ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ ﻓﻲ ﻫﺫﺍ ﺍﻟﻔﺼل‪.‬‬

‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ‬

‫‪١٢٩‬‬
‫ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DataAdapter Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDataAdapter‬ﻜﻤﺎ ﺃﻨﻬﺎ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪.Component‬‬


‫ﻭﻻ ﻴﻤﻜﻨﻙ ﺘﻌﺭﻴﻑ ﻨﺴﺨﺔ ﺠﺩﻴﺩﺓ ﻤﻥ ﻫﺫﻩ ﺍﻟﻔﺌﺔ‪ ،‬ﻷﻥ ﺤﺩﺙ ﺇﻨﺸـﺎﺌﻬﺎ ‪ Constructor‬ﻤﺤﻤـﻲ‬
‫‪ ،Protected‬ﻟﻜﻥ ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻔﺌﺎﺕ ﺍﻟﻔﺭﻋﻴﺔ ﺍﻟﻤﺸﺘﻘﺔ ﻤﻨﻬﺎ‪.‬‬

‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﺨﺼﺎﺌﺹ ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IDataAdapter‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼـﺎﺌﺹ‬


‫ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻗﺒﻭل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺃﺜﻨﺎﺀ ﺍﻟﻤلﺀ ‪:AcceptChangesDuringFill‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪) True‬ﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ(‪ ،‬ﻓﺴﻴﺘﻡ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ‬
‫‪ DataRow.AcceptChanges‬ﺒﻌﺩ ﺇﻀﺎﻓﺔ ﻜل ﺼﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺒﻬـﺫﺍ‬
‫ﺴﺘﻌﺘﺒﺭ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻥ ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﺘﻤﺕ ﺇﻀﺎﻓﺘﻪ ﻟﻡ ﻴﺤﺩﺙ ﺒـﻪ ﺃﻱ ﺘﻐﻴﻴـﺭ ﻋـﻥ‬
‫ﺍﻟﺴﺠل ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﺃﻤﺎ ﻟﻭ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،False‬ﻓﺴﺘﻌﺘﺒﺭ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻥ ﺍﻟﺴﺠل ﺍﻟﻤﻀﺎﻑ ﺇﻟﻴﻬﺎ ﻫﻭ ﺴﺠل ﺠﺩﻴﺩ ﻟﻡ ﻴﺩﺭﺝ ﺒﻌـﺩ ﻓـﻲ ﻗﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻋﻨﺩ ﺇﺠﺭﺍﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ ﺴﻴﺤﺎﻭل ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺇﻀـﺎﻓﺘﻪ ﺇﻟـﻰ ﻗﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﺴﺠل ﺠﺩﻴﺩ‪ ،‬ﻭﻫﻭ ﺃﻤﺭ ﻏﻴﺭ ﻤﺭﻏﻭﺏ ﻓﻴﻪ ﺒﺎﻟﻁﺒﻊ‪ ،‬ﺇﻻ ﻓـﻲ ﺒﻌـﺽ ﺍﻟﺤـﺎﻻﺕ‬
‫ﺍﻟﺨﺎﺼﺔ‪ ،‬ﻜﺄﻥ ﺘﻘﻭﻡ ﺒﺘﺤﻤﻴل ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﺃﺤﺩ ﺍﻟﺠﺩﺍﻭل‪ ،‬ﻭﺘﺭﻴﺩ ﺤﻔﻅﻬﺎ ﻓﻲ ﺠﺩﻭل ﻤﺅﻗﺕ‬
‫ﻓﻲ ﻨﻔﺱ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎ‪ ،‬ﺃﻭ ﺠﺩﻭل ﺁﺨﺭ ﻓﻲ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺃﺨﺭﻯ‪ ..‬ﻻﺤﻅ ﺃﻨـﻙ ﻓـﻲ ﻫـﺫﻩ‬
‫ﺍﻟﺤﺎﻟﺔ ﺴﺘﻔﻌل ﻤﺎ ﻴﻠﻲ‪:‬‬
‫‪ -١‬ﺘﺠﻌل ﻟﻠﺨﺎﺼﻴﺔ ‪ AcceptChangesDuringFill‬ﻟﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﻭل ﺍﻟﻘﻴﻤـﺔ‬
‫‪.False‬‬
‫‪ -٢‬ﻨﺴﺘﺨﺩﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ Fill‬ﺍﻟﺨﺎﺼﺔ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﻭل ﻟﻤلﺀ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺒﺎﻟﺴﺠﻼﺕ‪.‬‬

‫‪١٣٠‬‬
‫‪ -٣‬ﺘﺴﺨﺩﻡ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺁﺨﺭ ﻟﺘﺤﺩﻴﺙ ﺍﻟﺠﺩﻭل ﺍﻟﺠﺩﻴﺩ ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ ‪،Update‬‬
‫ﻤﻊ ﺍﺴﺘﺨﺩﺍﻡ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺤﺫﻑ ﺍﻟﻤﻨﺎﺴﺒﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻫﺫﺍ ﺍﻟﺠﺩﻭل‬
‫ﺍﻟﺠﺩﻴﺩ‪.‬‬
‫ﻭﺍﻟﻤﺸﺭﻭﻉ ‪ CopyAuthors‬ﻴﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﻟﻨﺴﺦ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻤـﻥ ﻗﺎﻋـﺩﺓ ﺒﻴﻨـﺎﺕ‬
‫ﺁﻜﺴﻴﺱ ﻭﺇﻀﺎﻓﺘﻬﻡ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺍﻟﻭﺴﻴﻠﺔ ‪ Update‬ﻟـﻥ‬
‫ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ﻭﺍﻟﺤﺫﻑ ﻭﺍﻟﺘﺤﺩﻴﺙ ﻓﻲ ﺤﺎﻟﺘﻨﺎ ﻫﺫﻩ‪ ،‬ﻟﻬﺫﺍ ﻟـﻥ ﻨﻌﺭﻓﻬـﺎ‪ ،‬ﻭﻟـﻥ‬
‫ﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ‪.‬‬

‫ﻗﺒﻭل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺃﺜﻨﺎﺀ ﺍﻟﺘﺤﺩﻴﺙ ‪:AcceptChangesDuringUpdate‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪) True‬ﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ(‪ ،‬ﻓﺴﻴﺘﻡ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ‬
‫‪ DataRow.AcceptChanges‬ﺒﻌﺩ ﻨﻘل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻤﻥ ﻜل ﺼﻑ ﻓﻲ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺒﻬﺫﺍ ﺘﻌﺘﺒﺭ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻫﺫﺍ ﺍﻟﺼﻑ ﺼﻔﺎ ﻋﺎﺩﻴـﺎ ﻟـﻡ‬
‫ﻴﺤﺩﺙ ﺒﻪ ﺃﻱ ﺘﻐﻴﻴﺭ ﻋﻥ ﺍﻟﺴﺠل ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﺃﻤﺎ ﻟﻭ ﺠﻌﻠﺕ ﻗﻴﻤـﺔ ﻫـﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ‪ ،False‬ﻓﺴﺘﻅل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﻌﺘﺒﺭ ﻫﺫﺍ ﺍﻟﺼﻑ ﻤﺨﺘﻠﻔـﺎ ﻋـﻥ ﺍﻟﺼـﻑ‬
‫ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻋﻨﺩ ﺇﺠﺭﺍﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ ﻤﺭﺓ ﺃﺨﺭﻯ ﺴﻴﻌﺎﺩ ﺘﺤﺩﻴﺜﻪ ﻓـﻲ‬
‫ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻫﻭ ﺃﻤﺭ ﻏﻴﺭ ﻤﺭﻏﻭﺏ ﻓﻴﻪ ﻓﻲ ﻤﻌﻅﻡ ﺍﻟﺤـﺎﻻﺕ‪ ،‬ﺇﻻ ﺇﺫﺍ ﻜﻨـﺕ ﺘﺭﻴـﺩ‬
‫ﺍﺴﺘﺨﺩﺍﻡ ﻨﻔﺱ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﺘﺤﺩﻴﺙ ﺃﻜﺜﺭ ﻤﻥ ﺠـﺩﻭل‪ ..‬ﻤـﺜﻼ‪ :‬ﺇﺫﺍ ﺃﺭﺩﺕ ﺘﺤـﺩﻴﺙ‬
‫ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻓﻲ ﻜل ﻤﻥ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺁﻜﺴﻴﺱ ﻭﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪ ،‬ﻓﻴﺠﺏ‬
‫ﻋﻠﻴﻙ ﻓﻌل ﻤﺎ ﻴﻠﻲ‪:‬‬
‫‪ -‬ﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ ‪ False‬ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ AcceptChangesDuringUpdate‬ﻟﻤﻬﻴﺊ‬
‫ﺒﻴﺎﻨﺎﺕ ﺁﻜﺴﻴﺱ ﻭﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪.Update‬‬
‫‪ -‬ﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ ‪ True‬ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ AcceptChangesDuringUpdate‬ﻟﻤﻬﻴـﺊ‬
‫ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻭﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ ..Update‬ﻫﺫﺍ ﺴـﻴﺠﻌل ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﻘﺒل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ‪ ،‬ﻭﺘﻌﺘﺒﺭ ﺃﻥ ﻜل ﺴﺠﻼﺘﻬﺎ ﻤﻁﺎﺒﻘﺔ ﻟﻠﺴﺠﻼﺕ ﺍﻻﺼﻠﻴﺔ‪ ،‬ﻓﻘﺩ‬
‫ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻓﻌﻼ ﻭﻟﻡ ﻨﻌﺩ ﻨﺤﺘﺎﺠﻬﺎ‪.‬‬
‫‪١٣١‬‬
‫ﻭﺍﻟﻤﺸﺭﻭﻉ ‪ UpdateAll‬ﻴﺭﻴﻙ ﻫﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﻋﻤﻠﻴﺎ‪ ،‬ﻓﻬﻭ ﻴﺤﻤل ﺴﺠﻼﺕ ﺍﻟﻤـﺅﻟﻔﻴﻥ ﻤـﻥ‬
‫ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪ ،‬ﻭﻴﻤﻸ ﺒﻬﺎ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻴﻌﺭﻀﻬﺎ ﻟﻠﻤﺴـﺘﺨﺩﻡ ﻓـﻲ‬
‫ﺠﺩﻭل‪ ،‬ﺤﻴﺙ ﻴﺴﺘﻁﻴﻊ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺇﺠﺭﺍﺀ ﺍﻟﺘﻐﻴﺭﺍﺕ ﺍﻟﺘﻲ ﻴﺭﻴﺩﻫﺎ ﻋﻠﻰ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤـﺅﻟﻔﻴﻥ‪ ،‬ﺃﻭ‬
‫ﻴﻀﻴﻑ ﺃﻭ ﻴﺤﺫﻑ ﺒﻌﺽ ﺍﻟﻤﺅﻟﻔﻴﻥ‪ ،‬ﻭﻋﻨﺩﻤﺎ ﻴﻀـﻐﻁ ﺯﺭ ﺍﻟﺘﺤـﺩﻴﺙ‪ ،‬ﻴـﺘﻡ ﺤﻔـﻅ ﻫـﺫﻩ‬
‫ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻓﻲ ﻗﺎﻋﺩﺘﻲ ﺁﻜﺴﻴﺱ ﻭﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻤﻌﺎ‪ ،‬ﻭﺒﻬﺫﺍ ﻨﻀﻤﻥ ﺃﻥ ﻴﻅﻼ ﻤﺘـﺯﺍﻤﻨﺘﻴﻥ‬
‫ﺩﺍﺌﻤﺎ‪.‬‬
‫ﻻﺤﻅ ﺃﻨﻨﺎ ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﺸﺭﻭﻉ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ‪ Wizard‬ﻹﻨﺘﺎﺝ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫ﻭﺃﻭﺍﻤﺭﻩ‪ ،‬ﻟﻬﺫﺍ ﻟﻥ ﺘﺠﺩ ﺍﻟﻜﺜﻴﺭ ﻤﻥ ﺍﻟﻜﻭﺩ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻟﺞ ﺒﻌﺩ‬
‫ﻗﻠﻴل‪ ..‬ﻭﻤﻥ ﺍﻟﻤﻬﻡ ﺃﻥ ﺘﻼﺤﻅ ﺃﻥ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ‪ UPDATE‬ﻴﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﺴﺠل ﺍﻟﻤﺭﺍﺩ‬
‫ﺘﺤﺩﻴﺜﻪ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺨﻼل ﻤﻔﺘﺎﺤﻪ ﺍﻷﺴﺎﺴﻲ )ﺍﻟﺤﻘل ‪ ID‬ﻓﻲ ﻤﺜﺎﻟﻨﺎ ﻫﺫﺍ( ﻭﻗﻴﻤـﻪ‬
‫ﺍﻷﺼﻠﻴﺔ ﻜﻤﺎ ﺸﺭﺤﻨﺎ ﺴﺎﺒﻘﺎ‪ ..‬ﻟﻬﺫﺍ ﻟﻭ ﻴﻜﻥ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻓﻲ ﻗﺎﻋﺩﺓ ﺁﻜﺴﻴﺱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ‬
‫ﺒﻌﺽ ﺍﻟﻤﺅﻟﻔﻴﻥ ﺍﻟﻤﻭﺠﻭﺩﻴﻥ ﻓﻲ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻓﻠﻥ ﻴﺤﺩﺙ ﺨﻁﺄ‪ ،‬ﻟﻜﻥ ﻟـﻥ‬
‫ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﻫﺅﻻﺀ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻷﻨﻬﻡ ﻏﻴﺭ ﻤﻭﺠﻭﺩﻴﻥ ﺃﺼﻼ‪ ،‬ﻭﻟﻥ ﺘﺘﻡ ﺇﻀﺎﻓﺘﻬﻡ ﺃﻴﻀﺎ‪ ..‬ﻫـﺫﻩ‬
‫ﺍﻟﻤﺸﻜﻠﺔ ﻟﻥ ﺘﺤﺩﺙ ﻟﻭ ﺃﻀﻔﺕ ﻤﺅﻟﻔﻴﻥ ﺠﺩﺩﺍ ﺇﻟﻰ ﺠﺩﻭل ﺍﻟﻌﺭﺽ‪ ،‬ﻓﻔﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟـﺔ ﺴـﻴﺘﻡ‬
‫ﺤﻔﻅﻬﻡ ﻓﻲ ﻗﺎﻋﺩﺘﻲ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺸﻜل ﺼﺤﻴﺢ‪ ..‬ﻟﻬﺫﺍ ﻟﻭ ﺃﺭﺩﺕ ﺃﻥ ﺘﻀﻤﻥ ﺃﻥ ﻴﻌﻤـل ﻫـﺫﺍ‬
‫ﺍﻟﺒﺭﻨﺎﻤﺞ ﺒﺼﻭﺭﺓ ﺩﻗﻴﻘﺔ‪ ،‬ﻓﻴﺠﺏ ﺃﻥ ﺘﺠﻌل ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻓﻲ ﻜﻠﺘﺎ ﺍﻟﻘﺎﻋﺩﺘﻴﻥ ﻤﺘﻤﺎﺜﻠﻴﻥ ﻤﻨﺫ‬
‫ﺍﻟﺒﺩﺍﻴﺔ‪ ،‬ﻟﺒﺤﺎﻓﻅ ﻋﻠﻴﻬﻤﺎ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻫﻜﺫﺍ ﺒﺎﺴﺘﻤﺭﺍﺭ‪.‬‬

‫ﺍﺴﺘﻤﺭﺍﺭ ﺍﻟﺘﺤﺩﻴﺙ ﻋﻨﺩ ﺍﻟﺨﻁﺄ ‪:ContinueUpdateOnError‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،True‬ﻓﻠﻥ ﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﺤﺩﺜﺕ ﻤﺸﻜﻠﺔ ﻓﻲ‬
‫ﺘﺤﺩﻴﺙ ﺃﺤﺩ ﺴﺠﻼﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺴﺘﺴﺘﻤﺭ ﻤﺤﺎﻭﻟﺔ ﺘﺤﺩﻴﺙ ﺒـﺎﻗﻲ ﺍﻟﺴـﺠﻼﺕ‪ ،‬ﻤـﻊ‬
‫ﺇﺭﺴﺎل ﺘﻔﺎﺼﻴل ﺍﻟﻤﺸﻜﻠﺔ ﺇﻟﻰ ﺍﻟﺨﺎﺼﻴﺔ ‪ RowError‬ﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻟﺼﻑ ‪DataRow‬‬
‫ﺍﻟﺫﻱ ﻓﺸﻠﺕ ﻋﻤﻠﻴﺔ ﺘﺤﺩﻴﺜﻪ‪.‬‬
‫ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫﻲ ‪ ،False‬ﻟﻬﺫﺍ ﺴﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻟﻭ ﻓﺸل‬
‫ﺘﺤﺩﻴﺙ ﺃﺤﺩ ﺍﻟﺴﺠﻼﺕ‪ ،‬ﻤﻤﺎ ﺴﻴﻨﻬﻲ ﻋﻤﻠﻴﺔ ﺘﺤﺩﻴﺙ ﺍﻟﺴﺠﻼﺕ ﻓﻲ ﺍﻟﺤﺎل‪ ..‬ﻭﺃﻨﺕ ﺤﺭ ﻓـﻲ‬

‫‪١٣٢‬‬
‫ﺍﺨﺘﻴﺎﺭ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺘﻨﺎﺴـﺒﻙ ﺃﻜﺜـﺭ ﻟﻤﻌﺎﻟﺠـﺔ ﻤﺜـل ﻫـﺫﻩ ﺍﻻﺨﻁـﺎﺀ‪ ..‬ﻭﺍﻟﻤﺸـﺭﻭﻉ‬
‫‪ 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‬ﻤﻤﺎ ﻴﻤﻨﻊ ﺍﻟﻤﺴـﺘﺨﺩﻡ ﻤـﻥ ﻤﻌﺭﻓـﺔ‬
‫ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺨﻴﺎﺭ ﺍﻟﺘﺤﻤﻴل ‪:FillLoadOption‬‬


‫ﺘﺤﺩﺩ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﺎﺫﺍ ﺴﻴﺤﺩﺙ ﻟﻠﻨﺴـﺨﺔ ﺍﻻﺼـﻠﻴﺔ ‪ Original Version‬ﻭﺍﻟﻨﺴـﺨﺔ‬
‫ﺍﻟﺤﺎﻟﻴﺔ ‪ Current Version‬ﻤﻥ ﺍﻟﺴﺠل ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ ‪ Fill‬ﻟﻤـلﺀ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺘﺄﺨﺫ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ LoadOption‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪ OverwriteChanges‬ﺘﺠﺎﻫل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺤﺩﺜﺕ ﺴﺎﺒﻘﺎ ﻟﻠﺴﺠل‪ ،‬ﻭﻭﻀﻊ ﺍﻟﻘـﻴﻡ‬


‫ﺍﻟﻘﺎﺩﻤﺔ ﻤﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻜل ﻤـﻥ ﺍﻟﻨﺴـﺨﺔ ﺍﻷﺼـﻠﻴﺔ‬
‫ﻭﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ﻟﻠﺴﺠل‪.‬‬
‫ﻫﺫﻩ ﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪ ،‬ﻭﻫﻲ ﺘﺤـﺎﻓﻅ ﻋﻠـﻰ ﺍﻟﻨﺴـﺨﺔ‬ ‫‪PreserveChanges‬‬
‫ﺍﻟﺤﺎﻟﻴﺔ ﻟﻠﺴﺠل ﺒﺩﻭﻥ ﺘﻐﻴﻴﺭ‪ ،‬ﻭﻭﻀﻊ ﺍﻟﻘﻴﻡ ﺍﻟﻘﺎﺩﻤﺔ ﻤﻥ ﻗـﺎﺭﺉ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻨﺴﺨﺔ ﺍﻟﺴﺠل ﺍﻷﺼﻠﻴﺔ ﻓﻘﻁ‪.‬‬
‫ﺍﻻﺤﺘﻔﺎﻅ ﺒﺎﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ ﻟﻠﺴﺠل ﺒﺩﻭﻥ ﺘﻐﻴﻴﺭ‪ ،‬ﻭﻭﻀﻊ ﺍﻟﻘﻴﻡ‬ ‫‪Upsert‬‬
‫ﺍﻟﻘﺎﺩﻤﺔ ﻤﻥ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻨﺴﺨﺔ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻴﺔ ﻓﻘﻁ‪.‬‬

‫ﺇﻋﺎﺩﺓ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﻤﺯﻭﺩ ‪:ReturnProviderSpecificTypes‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،True‬ﻓﺴﺘﻘﻭﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ ،Fill‬ﺒﺎﺴﺘﺨﺩﺍﻡ ﺃﻨـﻭﺍﻉ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺍﻟﺨﺎﺼﺔ ﺒﻜل ﻤﺯﻭﺩ )ﻤﺜل ﺃﻨﻭﺍﻉ ﺴﻴﻜﻭﻴل(‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﻫـﻲ‬
‫‪١٣٦‬‬
‫‪ ،False‬ﻤﻤﺎ ﻴﺠﻌل ﺍﻟﻭﺴﻴﻠﺔ ‪ Fill‬ﺘﺤﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﺃﻨﻭﺍﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻌﺎﺩﻴﺔ ﺍﻟﻤﺴـﺘﺨﺩﻤﺔ‬
‫ﻓﻲ ﺇﻁﺎﺭ ﺍﻟﻌﻤل‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﺼﻔﻴﺭ ﺨﻴﺎﺭ ﺍﻟﺘﺤﻤﻴل ‪:ResetFillLoadOption‬‬


‫ﺘﻌﻴﺩ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪ FillLoadOption‬ﺇﻟﻰ ﻗﻴﻤﺘﻬﺎ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪ ،‬ﻭﺘﺠﺒـﺭ ﺍﻟﻭﺴـﻴﻠﺔ ‪Fill‬‬
‫ﻋﻠﻰ ﻤﺭﺍﻋﺎﺓ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪.AcceptChangesDuringFill‬‬

‫ﺤﻔــــﻅ ﺨﺎﺼــــﻴﺔ ﻗﺒــــﻭل ﺍﻟﺘﻐﻴﻴــــﺭﺍﺕ ﺃﺜﻨــــﺎﺀ ﺍﻟﻤــــلﺀ‬


‫‪:ShouldSerializeAcceptChangesDuringFill‬‬
‫ﺇﺫﺍ ﺠﻌﻠــﺕ ﻗﻴﻤــﺔ ﻫــﺫﻩ ﺍﻟﺨﺎﺼــﻴﺔ ‪ ،True‬ﻓﺴــﻴﺘﻡ ﺍﻻﺤﺘﻔــﺎﻅ ﺒﻘﻴﻤــﺔ ﺍﻟﺨﺎﺼــﻴﺔ‬
‫‪.AcceptChangesDuringFill‬‬

‫ﺤﻔﻅ ﺨﺎﺼﻴﺔ ﺨﻴﺎﺭ ﺍﻟﺘﺤﻤﻴل ‪:ShouldSerializeFillLoadOption‬‬


‫ﺇﺫﺍ ﺠﻌﻠــﺕ ﻗﻴﻤــﺔ ﻫــﺫﻩ ﺍﻟﺨﺎﺼــﻴﺔ ‪ ،True‬ﻓﺴــﻴﺘﻡ ﺍﻻﺤﺘﻔــﺎﻅ ﺒﻘﻴﻤــﺔ ﺍﻟﺨﺎﺼــﻴﺔ‬
‫‪.FillLoadOption‬‬

‫ﻭﺘﻤﺘﻠﻙ ﺍﻟﻔﺌﺔ ‪ DataAdapter‬ﺍﻟﺤﺩﺙ ﺍﻟﻭﺤﻴﺩ ﺍﻟﺘﺎﻟﻲ‪:‬‬

‫ﺨﻁﺄ ﺍﻟﻤلﺀ ‪:FillError‬‬


‫ﻴﻨﻁﻠﻕ ﺇﺫﺍ ﺤﺩﺙ ﺨﻁﺄ ﺃﺜﻨﺎﺀ ﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ‬
‫ﺍﻟﻨﻭﻉ ‪ ،FillErrorEventArgs‬ﻭﻟﻪ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪ DataTable‬ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺠﺩﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataTable‬ﺍﻟﺫﻱ ﺤـﺩﺙ ﺍﻟﺨﻁـﺄ‬


‫ﺃﺜﻨﺎﺀ ﻤﻠﺌﻪ‪.‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻻﺴـﺘﺜﻨﺎﺀ ‪ Exception‬ﺍﻟـﺫﻱ ﻴﺤﺘـﻭﻱ ﻋﻠـﻰ‬ ‫‪Errors‬‬
‫ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ‪.‬‬

‫‪١٣٧‬‬
‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﻜﺎﺌﻨﺎﺕ ‪ ،Object Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻘـﻴﻡ‬ ‫‪Values‬‬
‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﺒﺎﻟﺼﻑ ﺍﻟﺫﻱ ﺤﺩﺙ ﺒﻪ ﺍﻟﺨﻁﺄ‪.‬‬
‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ ‪ ،True‬ﻓﺴﻴﺴﺘﻤﺭ ﻤلﺀ ﺍﻟﺠﺩﻭل ﺒﺎﻟﺴـﺠﻼﺕ‬ ‫‪Continue‬‬
‫ﻭﺘﺠﺎﻫل ﺍﻟﺨﻁﺄ‪ ..‬ﺃﻤﺎ ﺇﻥ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ ‪ False‬ﻓﺴﻴﺘﻭﻗﻑ ﻤلﺀ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﺤﺎل‪.‬‬

‫‪١٣٨‬‬
‫ﻓﺌﺔ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DbDataAdapter Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺴﺎﺴﻴﺔ ﻤﺠﺭﺩﺓ ﺘﺠﺏ ﻭﺭﺍﺜﺘﻬﺎ‪ ،‬ﻭﻫﻲ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪.DataAdapter‬‬
‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ ﻟﺠﺩﻭل ﺍﻟﻤﺼﺩﺭ ‪:DefaultSourceTableName‬‬


‫ﻗﻴﻤﺔ ﻫﺫﺍ ﺍﻟﺜﺎﺒﺕ ﻫﻲ ‪ ،Table‬ﻭﻫﻭ ﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺍﻟﺫﻱ ﻴﺴﺘﺨﺩﻡ ﻋﻨﺩ ﺇﻀﺎﻓﺔ ﺠـﺩﻭل‬
‫ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺤﺠﻡ ﻤﺠﻤﻭﻋﺔ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ‪:UpdateBatchSize‬‬


‫ﻀﻊ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻋﺩﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺴﻴﻘﻭﻡ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺤﻔﻅ ﺘﻐﻴﻴﺭﺍﺘﻬـﺎ ﻓـﻲ‬
‫ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﻤﺭﺓ ﺍﻟﻭﺍﺤﺩﺓ‪ ..‬ﻫﺫﺍ ﻤﻔﻴﺩ ﻟﺘﻘﻴل ﻋﺩﺩ ﺩﻭﺭﺍﺕ ﺍﻻﺘﺼـﺎل ﻤـﻊ ﺍﻟﺨـﺎﺩﻡ‬
‫‪ Round Trips‬ﺃﺜﻨﺎﺀ ﺘﺤﺩﻴﺙ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻤﻤﺎ ﻴﺠﻌل ﺃﺩﺍﺀ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺃﻓﻀـل‪ ..‬ﻟﻜـﻥ‬
‫ﻋﻠﻴﻙ ﺃﻥ ﺘﺭﺍﻋﻲ ﺃﻥ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻋﺩﺩ ﻜﺒﻴﺭ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﻓﻲ ﻨﻔﺱ ﺍﻟﻠﺤﻅﺔ ﻗﺩ ﻴﺅﺩﻱ ﺇﻟﻰ‬
‫ﺘﻘﻠﻴل ﺍﻟﻜﻔﺎﺀﺓ )ﺍﺴﺘﻬﻼﻙ ﺫﺍﻜﺭﺓ ﺃﻜﺒﺭ‪ ،‬ﺇﺭﺴﺎل ﺒﻴﺎﻨﺎﺕ ﺃﻜﺜﺭ ﻭﻭﻗﺕ ﺍﻨﺘﻅﺎﺭ ﺃﻁـﻭل(‪ ،‬ﻟﻬـﺫﺍ‬
‫ﻋﻠﻴﻙ ﺍﺨﺘﻴﺎﺭ ﻋﺩﺩ ﻤﻌﻘﻭل ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﻴﺤﻘﻕ ﺃﻓﻀل ﺃﺩﺍﺀ‪.‬‬
‫ﻭﻻ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻊ ﺁﻜﺴﻴﺱ‪ ،‬ﻷﻨﻪ ﻻ ﻴﺴﻤﺢ ﺒﺘﻨﻔﻴﺫ ﺃﻜﺜﺭ ﻤﻥ ﺃﻤـﺭ ﻓـﻲ‬
‫ﺍﻟﻤﺭﺓ ﺍﻟﻭﺍﺤﺩﺓ‪ ..‬ﻟﻜﻥ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻤﻊ ﺴﻜﻴﻭﻴل ﺴﻴﺭﻓﺭ ﻭﺃﻭﺭﺍﻜل‪.‬‬
‫ﻭﺍﻟﺠﺩﻭل ﺍﻟﺘﺎﻟﻲ ﻴﻠﺨﺹ ﺘﺄﺜﻴﺭ ﺍﻟﻘﻴﻡ ﺍﻟﻤﺨﺘﻠﻔﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪:‬‬

‫ﺘﺤﺩﻴﺙ ﺴﺠل ﻭﺍﺤﺩ ﻓﻲ ﻜل ﻤﺭﺓ‪ ..‬ﻫﺫﻩ ﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪.‬‬ ‫‪١‬‬


‫< ‪ ١‬ﺘﺤﺩﻴﺙ ﺍﻟﻌﺩﺩ ﺍﻟﻤﺤﺩﺩ ﻤﻥ ﺍﻟﺴﺠﻼﺕ‪ ،‬ﺤﻴﺙ ﻴﺘﻡ ﺘﻜﻭﻴﻥ ﺍﺴﺘﻌﻼﻡ ﻟﺘﺤﺩﻴﺙ ﻜـل‬
‫ﺴﺠل‪ ،‬ﻭﺩﻤﺞ ﻫﺫﻩ ﺍﻻﺴﺘﻌﻼﻤﺎﺕ ﻤﻌﺎ ﺒﻭﻀﻊ ; ﺒﻴﻨﻬﺎ‪.‬‬
‫ﻻ ﺘﻭﺠﺩ ﻗﻴﻭﺩ ﻋﻠﻰ ﻋﺩﺩ ﺍﻷﻭﺍﻤﺭ‪ ،‬ﻭﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺍﺴﺘﺨﺩﺍﻡ ﺃﻜﺒﺭ ﻋﺩﺩ ﻤﻥ ﺍﻷﻭﺍﻤﺭ‬ ‫‪٠‬‬
‫ﻴﻤﻜﻥ ﻟﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻪ‪.‬‬
‫ﺴﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ!‬ ‫>‪١‬‬
‫‪١٣٩‬‬
‫ﻭﻓﻲ ﺤﺎﻟﺔ ﺍﺴﺘﺨﺩﺍﻡ ﺃﻱ ﻗﻴﻤﺔ ﻏﻴﺭ ﺍﻟﻭﺍﺤﺩ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻴﺠﺏ ﻋﻠﻴـﻙ ﺃﻥ ﺘﻀـﻊ ﻓـﻲ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ‪ UpdatedRowSource‬ﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻷﻤﺭ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﺘﻨﻔﻴـﺫ ﻋﻤﻠﻴـﺔ‬
‫ﺍﻟﺘﺤﺩﻴﺙ ﺍﻟﻘﻴﻤﺔ ‪ None‬ﺃﻭ ‪ ،OutputParameters‬ﻭﺇﻻ ﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ‪.‬‬

‫ﻜﻤﺎ ﺃﻥ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻀﻴﻑ ﻋﺩﺓ ﺼﻴﻎ ﺠﺩﻴﺩﺓ ﻟﻜل ﻤﻥ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ‪ Fill‬ﻭ ‪ ..FillSchema‬ﺩﻋﻨـﺎ‬
‫ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﺼﻴﻎ‪:‬‬

‫ﻤلﺀ ‪:Fill‬‬
‫ﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺜﻼﺙ ﺼﻴﻎ ﺠﺩﻴﺩﺓ ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﻭﻫﻲ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﺠﺩﻭل ﺒﻴﺎﻨـﺎﺕ ‪ DataTable‬ﻟـﺘﻤﻸﻩ ﺒﺎﻟﺴـﺠﻼﺕ‪ ..‬ﻭﻗـﺩ‬
‫ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫـﺫﻩ ﺍﻟﺼـﻴﻐﺔ ﻓـﻲ ﺍﻟﻭﺴـﻴﻠﺔ ‪ MyDbConnector.GetTable‬ﻓـﻲ‬
‫ﺍﻟﻤﺸﺭﻭﻉ ‪ DbTasks‬ﻟﻤـلﺀ ﻜـﺎﺌﻥ ﺠـﺩﻭل ‪ DataTable‬ﺒﺎﻟﺒﻴﺎﻨـﺎﺕ ﻭﺇﻋﺎﺩﺘـﻪ‬
‫ﻟﻠﻤﺴﺘﺨﺩﻡ‪ ..‬ﺒﻌﺩ ﻫﺫﺍ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨـﺎﺕ‪ ،‬ﺃﻭ ﻋـﺭﺽ‬
‫ﺒﻴﺎﻨﺎﺘﻪ ﻤﺒﺎﺸﺭﺓ ﻓﻲ ﺠﺩﻭل ﻋﺭﺽ‪ ،‬ﺃﻭ ﺘﻨﻔﻴﺫ ﺃﻱ ﻋﻤﻠﻴﺔ ﺘﺭﻴﺩﻫﺎ ﻋﻠﻴﻪ‪ ..‬ﻭﺴـﺘﺠﺩ ﻓـﻲ‬
‫ﻨﻔﺱ ﺍﻟﻤﺸﺭﻭﻉ ﻤﺜﺎﻻ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ ،GetTable‬ﻭﺫﻟـﻙ ﺒﻀـﻐﻁ ﺍﻟـﺯﺭ‬
‫"ﻋﺭﺽ ﺍﻟﻤﺅﻟﻔﻴﻥ"‪ ،‬ﺍﻟﺫﻱ ﻴﻌﺭﺽ ﻨﻤﻭﺫﺠﺎ ﺠﺩﻴﺩﺍ ﻋﻠﻴﻪ ﺠﺩﻭل ﻓﻴﻪ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺅﻟﻔﻴﻥ‪.‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺭﺍﺩ ﻤﻠﺌﻬﺎ‪ ،‬ﻭﻨﺼﺎ ﻴﻤﺜل ﺍﺴـﻡ ﺍﻟﺠـﺩﻭل‬
‫ﺍﻟﻤﻀﺎﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻤﺜﻼ ﺴﻴﻀﻴﻑ ﺠﺩﻭل ﺍﻟﻜﺘـﺏ ﺇﻟـﻰ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﻻﺴﻡ ‪:TblBooks‬‬
‫;)"‪DaBooks.Fill(Ds, "TblBooks‬‬
‫‪MessageBox.Show(Ds.Tables[0].TableName); //TblBooks‬‬
‫‪ -٣‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﻟﻬﺎ ﺃﺭﺒﻌﺔ ﻤﻌﺎﻤﻼﺕ‪ ،‬ﻫﻲ ﺒﺎﻟﺘﺭﺘﻴﺏ‪:‬‬
‫‪ -‬ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -‬ﺭﻗﻡ ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺍﻟﻘﺭﺍﺀﺓ ﺒﺩﺀﺍ ﻤﻨﻬﺎ‪ ،‬ﻋﻠﻤﺎ ﺒﺄﻥ ﺃﻭل ﺴـﺠل ﻓـﻲ ﺍﻟﺠـﺩﻭل‬
‫ﺭﻗﻤﻪ ﺼﻔﺭ‪.‬‬

‫‪١٤٠‬‬
‫‪ -‬ﺃﻗﺼﻰ ﻋﺩﺩ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﺘﺭﻴﺩ ﻗﺭﺍﺀﺘﻪ ﻤﻥ ﺍﻟﺠﺩﻭل‪ ..‬ﻭﻟﻥ ﻴﺤﺩﺙ ﺨﻁﺄ ﺇﺫﺍ ﻜﺎﻥ‬
‫ﺍﻟﺠﺩﻭل ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻋﺩﺩ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﺃﻗل ﻤﻥ ﻫﺫﺍ ﺍﻟﻌﺩﺩ‪.‬‬
‫‪ -‬ﻨﺹ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -٤‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﻔﻴﺩﻙ ﻋﻨﺩﻤﺎ ﺘﺭﻴﺩ ﺃﺨﺫ ﺠﺯﺀ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﺃﻤﺭ ﺘﺤﺩﻴـﺩ ﻴﻌﻴـﺩ‬
‫ﺃﻜﺜﺭ ﻤﻥ ﺠﺩﻭل‪ ،‬ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ‪:‬‬
‫‪ -‬ﺭﻗﻡ ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺍﻟﻘﺭﺍﺀﺓ ﺒﺩﺀﺍ ﻤﻨﻬﺎ‪.‬‬
‫‪ -‬ﺃﻗﺼﻰ ﻋﺩﺩ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﺘﺭﻴﺩ ﻗﺭﺍﺀﺘﻪ ﻤﻥ ﻜل ﺠﺩﻭل‪.‬‬
‫‪DataTable‬‬ ‫‪ -‬ﻤﺼﻔﻭﻓﺔ ﻤﻌﺎﻤﻼﺕ ‪ ParamAray‬ﺘﺴﺘﻘﺒل ﻤﺼـﻔﻭﻓﺔ ﺠـﺩﺍﻭل‬
‫‪ ،Array‬ﻟﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺭﺴل ﺇﻟﻴﻬﺎ ﺍﻟﺠﺩﺍﻭل ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻤﻸﻫﺎ ﺒﺎﻟﺴﺠﻼﺕ‪.‬‬
‫ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﻤﺕ ﺇﻀﺎﻓﺘﻬﺎ ﺃﻭ ﺘﺤﺩﻴﺜﻬﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻤلﺀ ﺍﻟﻤﺨﻁﻁ ‪:FillSchema‬‬


‫ﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺼﻴﻐﺘﻴﻥ ﺠﺩﻴﺩﺘﻴﻥ ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﻭﻫﻤﺎ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ‪ DataTable‬ﺍﻟﻤﺭﺍﺩ ﻤﻠﺅﻩ‪ ،‬ﻭﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ‬
‫‪ SchemaType‬ﺍﻟﺘﻲ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﺴﺎﺒﻘﺎ‪.‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺇﺤﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ ‪،SchemaType‬‬
‫ﻭﺍﺴﻡ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﺘﻌﻴﺩ ﻜﻠﺘﺎ ﺍﻟﺼﻴﻐﺘﻴﻥ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ‪ DataTable‬ﺍﻟﺫﻱ ﺘﻡ ﻤﻠﺅﻩ ﺒﺎﻟﺴﺠﻼﺕ‪.‬‬

‫ﻭﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪:DbDataAdapter‬‬


‫‪OdbcDataAdapter Class .١‬‬
‫‪OleDbDataAdapter Class .٢‬‬
‫‪SqlDataAdapter Class .٣‬‬
‫‪OracleDataAdapter Class .٤‬‬

‫ﻭﺴﻨﻜﺘﻔﻲ ﻫﻨﺎ ﺒﺎﻟﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ ‪.SqlDataAdapter‬‬

‫‪١٤١‬‬
‫ﻓﺌﺔ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ‪SqlDataAdapter Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ ،DbDataAdapter‬ﻭﻫﻲ ﺘﻌﻤل ﻜﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﻤﺨﺼﺹ ﻟﻠﺘﻌﺎﻤل ﻤـﻊ‬
‫ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪.‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺭﺒﻊ ﺼﻴﻎ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ‪ SelectCommand‬ﺍﻟﺫﻱ ﺴﻴﺴـﺘﺨﺩﻤﻪ ﻤﻬﻴـﺊ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -٣‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ‪:‬‬
‫‪ -‬ﻨﺹ ﺠﻤﻠﺔ ﺍﻟﺘﺤﺩﻴﺩ ‪.SELECT‬‬
‫‪ -‬ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ‪ SqlConnection‬ﺍﻟﺫﻱ ﺴﻴﺴﺘﺨﺩﻡ ﻓـﻲ ﺍﻻﺘﺼـﺎل ﺒﻘﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺴﻴﻘﻭﻡ ﺒﺈﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺃﻤﺭ ‪ SqlCommand‬ﻭﺴﻴﻀـﻊ ﺠﻤﻠـﺔ‬
‫ﺍﻟﺘﺤﺩﻴﺩ ‪ SELECT‬ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ CommandText‬ﺍﻟﺨﺎﺼﺔ ﺒﻪ‪ ،‬ﻜﻤﺎ ﺴﻴﻀﻊ ﻜﺎﺌﻥ‬
‫ﺍﻻﺘﺼﺎل ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ Connection‬ﺍﻟﺨﺎﺼﺔ ﺒﻪ‪ ..‬ﺒﻌﺩ ﻫﺫﺍ ﺴﻴﻭﻀﻊ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﻓﻲ‬
‫ﺍﻟﺨﺎﺼﺔ ‪ SelectCommand‬ﺍﻟﺨﺎﺼﺔ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻤﻌﻨﻰ ﻫﺫﺍ ﺃﻥ ﻫﺫﻩ ﺍﻟﺼـﻴﻐﺔ‬
‫ﺘﺨﺘﺼﺭ ﻋﻠﻴﻙ ﺍﻟﻜﺜﻴﺭ ﻤﻥ ﺍﻟﺨﻁﻭﺍﺕ‪.‬‬
‫‪ -٤‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ‪ ،‬ﻭﻟﻜﻥ ﻤﻌﺎﻤﻠﻬﺎ ﺍﻟﺜﺎﻨﻲ ﻴﺴﺘﻘﺒل ﻨﺹ ﺍﻻﺘﺼـﺎل‬
‫‪ Connection String‬ﺍﻟﻼﺯﻡ ﻟﻼﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﻴﺘﻡ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻓـﻲ ﺇﻨﺸـﺎﺀ‬
‫ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ‪.SqlCommand‬‬

‫ﻭﺒﺎﻹﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ﻤﻥ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﻭﺃﺤﺩﺍﺙ‪ ،‬ﺘﻤﺘﻠـﻙ ﻫـﺫﻩ ﺍﻟﻔﺌـﺔ‬
‫ﺍﻟﺤﺩﺜﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ‪:‬‬

‫‪١٤٢‬‬
‫ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﺍﻟﺴﺠل ‪:RowUpdating‬‬
‫ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ Update‬ﺍﻟﺨﺎﺼﺔ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻓﺈﻨﻬﺎ ﺘﻘﻭﻡ ﺒﺎﻟﻤﺭﻭﺭ ﻋﺒﺭ ﻜـل‬
‫ﺴﺠل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻻﺴﺘﺨﺩﺍﻤﻪ ﻓﻲ ﺘﺤﺩﻴﺙ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻴﻨﻁﻠﻕ ﻫﺫﺍ ﺍﻟﺤﺩﺙ‬
‫ﻗﺒل ﺍﺴﺘﺨﺩﺍﻡ ﻜل ﺴﺠل ﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ‪.‬‬
‫ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨـﻭﻉ ‪ ،SqlRowUpdatingEventArgs‬ﻭﻫـﻭ‬
‫ﻴﻤﺘﻠﻙ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪ Command‬ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ‪ SqlCommand‬ﺍﻟﺫﻱ ﺴﻴﺴـﺘﺨﺩﻡ‬


‫ﻓﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ‪.‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻻﺴـﺘﺜﻨﺎﺀ ‪ Exception‬ﺍﻟـﺫﻱ ﻴﺤﺘـﻭﻱ ﻋﻠـﻰ‬ ‫‪Errors‬‬
‫ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ‪) ..‬ﻤﻔﻴﺩﺓ ﻓﻘـﻁ‬
‫ﻓﻲ ﺍﻟﺤﺩﺙ ‪.(RowUpdated‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺼﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ،DataRow‬ﺍﻟﺫﻱ ﻴﺘﻡ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻪ‬ ‫‪Row‬‬
‫ﺤﺎﻟﻴﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ Statement‬ﺘﻌﻴﺩ ﻨﻭﻉ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺘﻲ ﺴﻴﺘﻡ ﺘﻨﻔﻴـﺫﻫﺎ‪ ،‬ﻭﻫـﻲ ﺘﻌﻴـﺩ‬
‫‪Type‬‬
‫ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ StatementType‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ :Select -‬ﺠﻤﻠﺔ ﺘﺤﺩﻴﺩ‪.‬‬
‫‪ :Insert -‬ﺠﻤﻠﺔ ﺇﺩﺭﺍﺝ‪.‬‬
‫‪ :Update -‬ﺠﻤﻠﺔ ﺘﺤﺩﻴﺙ‪.‬‬
‫‪ :Delete -‬ﺠﻤﻠﺔ ﺤﺫﻑ‪.‬‬
‫‪ :Batch -‬ﺍﺴﺘﻌﻼﻡ ﻴﺘﻜﻭﻥ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺃﻭﺍﻤﺭ‪.‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺤﺎﻟﺔ ﻜﺎﺌﻥ ﺍﻷﻤﺭ‪ ،‬ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤـﺭﻗﻡ‬ ‫‪Status‬‬
‫‪ UpdateStatus‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ :Continue -‬ﺍﻻﺴﺘﻤﺭﺍﺭ ﻓـﻲ ﺘﺤـﺩﻴﺙ ﺍﻟﺴـﺠل ﺍﻟﺤـﺎﻟﻲ‬
‫ﻭﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ ﻟﻪ‪.‬‬

‫‪١٤٣‬‬
‫‪ :ErrorsOccurred -‬ﺘﻁﻠﺏ ﻤﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻌﺎﻤل ﻤﻊ‬
‫ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ ﻜﺄﻨﻬﺎ ﺘﺴﺒﺒﺕ ﻓﻲ ﺤﺩﻭﺙ ﺨﻁﺄ‪ ..‬ﻓـﻲ ﻫـﺫﻩ‬
‫ﺍﻟﺤﺎﻟﺔ ﺴﻴﻨﻁﻠﻕ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻓﻌﻼ ﻓﻲ ﺍﻟﺴـﻁﺭ ﺍﻟـﺫﻱ‬
‫ﺍﺴﺘﺩﻋﻴﺕ ﻓﻴﻪ ﺍﻟﻭﺴﻴﻠﺔ ‪ Update‬ﺍﻟﺨﺎﺼﺔ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪،‬‬
‫ﻭﻋﻠﻴﻙ ﻤﻌﺎﻟﺠﺔ ﻫﺫﺍ ﺍﻟﺨﻁﺄ ﺒﻤﻘﻁﻊ ‪.Try Catch‬‬
‫‪ :SkipCurrentRow -‬ﺘﺠــﺎﻭﺯ ﺍﻟﺴــﺠل ﺍﻟﺤــﺎﻟﻲ ﺩﻭﻥ‬
‫ﺍﺴﺘﺨﺩﺍﻤﻪ ﻓﻲ ﺘﺤﺩﻴﺙ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻤﻊ ﺍﺴﺘﻤﺭﺍﺭ ﺘﺤﺩﻴﺙ‬
‫ﺒﺎﻗﻲ ﺍﻟﺴﺠﻼﺕ‪.‬‬
‫‪ :SkipAllRemainingRows -‬ﺘﺠﺎﻭﺯ ﺍﻟﺴـﺠل ﺍﻟﺤـﺎﻟﻲ‬
‫ﻭﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ ﻟﻪ ﻭﺇﻴﻘﺎﻑ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ ﻓﻲ ﺍﻟﺤﺎل‪.‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺨﺭﻴﻁﺔ ﺍﻟﺠـﺩﻭل ‪ ،DataTableMapping‬ﺍﻟـﺫﻱ‬ ‫‪Table‬‬
‫‪Mapping‬‬
‫ﻴﺴﺘﺨﺩﻡ ﻟﻠﺭﺒﻁ ﺒﻴﻥ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﻟﺠـﺩﻭل‬
‫ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺘﻡ ﺘﺤﺩﻴﺙ ﺍﻟﺴﺠل ‪:RowUpdated‬‬


‫ﻴﻨﻁﻠﻕ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻓﻲ ﻜل ﻤﺭﺓ ﻴﻨﺘﻬﻲ ﻓﻴﻬﺎ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺍﺴـﺘﺨﺩﺍﻡ ﺃﺤـﺩ ﺴـﺠﻼﺕ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ‪.‬‬
‫ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨـﻭﻉ ‪ ،SqlRowUpdatedEventArgs‬ﻭﻫـﻭ‬
‫ﻴﻤﺘﻠﻙ ﻨﻔﺱ ﺨﺼﺎﺌﺹ ﺍﻟﻔﺌﺔ ‪ SqlRowUpdatingEventArgs‬ﺍﻟﺘﻲ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻓـﻲ‬
‫ﺍﻟﺤﺩﺙ ﺍﻟﺴﺎﺒﻕ‪ ،‬ﻭﻴﺯﻴﺩ ﻋﻠﻴﻬﺎ ﺒﺎﻟﻌﻨﺎﺼﺭ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪ RecordsAffected‬ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﺄﺜﺭﺕ ﺒﻌﻤﻠﻴﺔ ﺍﻟﺘﺤـﺩﻴﺙ‬


‫ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻫﺫﺍ ﺍﻟﻌﺩﺩ ﻴﻜﻭﻥ ﺼﻔﺭﺍ ﺇﺫﺍ ﻟـﻡ‬
‫ﻴﺘﻡ ﺍﻟﻌﺜﻭﺭ ﻋﻠﻰ ﺍﻟﺴﺠل‪ ،‬ﺃﻭ ﺤﺩﺙ ﺨﻁﺄ‪ ،‬ﻭﻴﻜﻭﻥ ‪-‬‬
‫‪ ١‬ﺇﺫﺍ ﻜﻨﺕ ﺘﺴﺘﺨﺩﻡ ﺠﻤﻠـﺔ ‪ SELECT‬ﺍﻟﺨﺎﺼـﺔ‬
‫ـﺔ ‪SELECT‬‬ ‫ـﻅ ﺃﻥ ﺠﻤﻠـ‬ ‫ـﺩ‪ ..‬ﻻﺤـ‬‫ـﺄﻤﺭ ﺍﻟﺘﺤﺩﻴـ‬ ‫ﺒـ‬
‫‪١٤٤‬‬
‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻨﻬﺎﻴﺔ ﺃﻤﺭ ﺍﻟﺘﺤـﺩﻴﺙ ﻭﺃﻤـﺭ ﺍﻹﺩﺭﺍﺝ‬
‫ﺘﺅﺜﺭ ﻋﻠﻰ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ‪ ،‬ﻭﺘﺠﻌـل ﻗﻴﻤﺘﻬـﺎ‬
‫ﺼﻔﺭﺍ!‬
‫ﻫــﺫﻩ ﺍﻟﺨﺎﺼــﻴﺔ ﻤﻔﻴــﺩﺓ ﺇﺫﺍ ﻜﺎﻨــﺕ ﻟﻠﺨﺎﺼــﻴﺔ‬ ‫‪RowCount‬‬
‫‪ UpdateBatchSize‬ﺍﻟﺨﺎﺼﺔ ﺒﻤﻬﻴـﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﻗﻴﻤﺔ ﺃﻜﺒﺭ ﻤﻥ ‪ ،١‬ﻓﻔﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟـﺔ ﻴﻘـﻭﻡ ﻤﻬﻴـﺊ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺘﺤﺩﻴﺙ ﺃﻜﺜﺭ ﻤﻥ ﺴﺠل ﻓﻲ ﺍﻟﻤﺭﺓ ﺍﻟﻭﺍﺤﺩﺓ‪،‬‬
‫ﺤﻴﺙ ﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻋﺩﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘـﻲ‬
‫ﺘﻡ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻬﺎ‪ ..‬ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﺍﻟﻌﺩﺩ ﻗـﺩ ﻴﻜـﻭﻥ‬
‫ﻤﺴﺎﻭﻴﺎ ﻟﻘﻴﻤﺔ ﺍﻟﺨﺎﺼـﻴﺔ ‪ UpdateBatchSize‬ﺃﻭ‬
‫ﺃﺼﻐﺭ ﻤﻨﻬﺎ )ﻓﻲ ﺤﺎﻟﺔ ﻋﺩﻡ ﻭﺠﻭﺩ ﺴﺠﻼﺕ ﻜﺎﻓﻴـﺔ‬
‫ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ(‪.‬‬
‫ﺇﺫﺍ ﻜﺎﻨﺕ ﻟﻠﺨﺎﺼﻴﺔ ‪ UpdateBatchSize‬ﻗﻴﻤﺔ ﺃﻜﺒﺭ‬ ‫‪CopyToRows‬‬
‫ﻤﻥ ‪ ،١‬ﻓﺈﻥ ﺍﻟﺨﺎﺼﻴﺔ ‪ e.Row‬ﻻ ﺘﻔﻴﺩﻙ ﻟﻤﻌﺭﻓـﺔ‬
‫ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﻡ ﺤﻔﻅ ﺒﻴﺎﻨﺎﺘﻬﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪..‬‬
‫ﻭﺒﺩﻻ ﻤﻨﻬﺎ‪ ،‬ﻴﻤﻜﻨﻙ ﺇﺭﺴـﺎل ﻤﺼـﻔﻭﻓﺔ ﺼـﻔﻭﻑ‬
‫‪ DataRow Array‬ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ‪ ،‬ﻟﺘﻭﻀـﻊ‬
‫ﻓﻴﻬﺎ ﺼﻔﻭﻑ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﻡ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ‬
‫ﻓﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﻫﺫﻩ ﺍﻟﻌﻤﻠﻴـﺔ‬
‫ﻤﺭﺠﻌﻴﺔ ‪ ،By Reference‬ﺃﻱ ﺃﻥ ﺃﻱ ﺘﻐﻴﻴﺭ ﻓـﻲ‬
‫ﺍﻟﺼﻔﻭﻑ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﻤﺼﻔﻭﻓﺔ‪ ،‬ﺴﻴﻅﻬﺭ ﺘﺄﺜﻴﺭﻩ‬
‫ﻓﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ..‬ﻭﻴﺠـﺏ ﺃﻥ ﺘﺤﺘـﻭﻱ‬
‫ﺍﻟﻤﺼﻔﻭﻓﺔ ﺍﻟﻤﺭﺴﻠﺔ ﻋﻠﻰ ﺍﻷﻗل ﻋﻠـﻰ ﻋـﺩﺩ ﻤـﻥ‬
‫ﺍﻟﺨﺎﻨﺎﺕ ﻴﺴﺎﻭﻱ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪.e.RowCount‬‬
‫ﻭﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺃﺨﺭﻯ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﻟﻬﺎ ﻤﻌﺎﻤل ﺜﺎﻥ‪،‬‬
‫ﻴﺴﺘﻘﺒل ﺭﻗﻡ ﺍﻟﺨﺎﻨﺔ‪ ،‬ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﻭﻀﻊ ﺍﻟﺼﻔﻭﻑ ﻓﻲ‬
‫ﺍﻟﻤﺼﻔﻭﻓﺔ ﺒﺩﺀﺍ ﻤﻨﻪ‪.‬‬

‫‪١٤٥‬‬
‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ـﻴﻠﺔ‬
‫ـﺩﺙ ‪ RowUpdating‬ﺍﻟﻭﺴـ‬
‫ـل ﺍﻟﺤـ‬
‫ـﻙ ﻤﻌﺎﻤـ‬
‫ـﺎﺫﺍ ﻻ ﻴﻤﺘﻠـ‬
‫ـﺎﺀل ﻟﻤـ‬
‫ـﻙ ﺘﺘﺴـ‬
‫ﻟﻌﻠـ‬
‫‪ ..CopyToRows‬ﺍﻟﺴﺒﺏ ﻓﻲ ﻫﺫﺍ ﺃﻥ ﺍﻟﺤﺩﺙ ‪ RowUpdating‬ﻴﻨﻁﻠﻕ ﺩﺍﺌﻤﺎ ﻗﺒـل‬
‫ﺘﺤﺩﻴﺙ ﻜل ﺼﻑ ﻋﻠﻰ ﺤﺩﺓ‪ ،‬ﺤﺘﻰ ﻟﻭ ﻜﺎﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺴﻴﺴﺘﺨﺩﻡ ﻤﺠﻤﻭﻋﺔ ﺃﻭﺍﻤـﺭ‬
‫‪ Batch SQL‬ﻟﺘﺤﺩﻴﺙ ﻤﺠﻤﻭﻋﺔ ﺼﻔﻭﻑ ﺩﻓﻌﺔ ﻭﺍﺤﺩﺓ‪ ..‬ﻫﺫﺍ ﻤﻨﻁﻘـﻲ‪ ،‬ﻷﻥ ﻤﻬﻴـﺊ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﻘﺭﺃ ﺴـﺠﻼ ﺘﻠـﻭ ﺴـﺠل ﻤـﻥ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ )ﻭﻴﻁﻠـﻕ ﺍﻟﺤـﺩﺙ‬
‫‪ RowUpdating‬ﻟﻜل ﺴﺠل(‪ ،‬ﻭﺒﻌﺩ ﻫﺫﺍ ﻴﻜ ‪‬ﻭ‪‬ﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤﺠﻤﻭﻋـﺔ ﺃﻭﺍﻤـﺭ‬
‫ﻟﺘﺤﺩﻴﺙ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﻗﺭﺃﻫﺎ‪ ،‬ﻭﻴﺭﺴل ﻫﺫﻩ ﺍﻷﻭﺍﻤﺭ ﺍﻟﻤﺠﻤﻌﺔ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺜﻡ‬
‫ﻴﻁﻠﻕ ﺍﻟﺤﺩﺙ ‪ RowUpdated‬ﺒﻌﺩ ﺘﻨﻔﻴﺫﻫﺎ‪.‬‬

‫ﻻﺤﻅ ﺃﻨﻙ ﻟﻭ ﻟﻡ ﺘﻜﺘﺏ ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺴﺘﺠﻴﺏ ﻟﻠﺤـﺩﺙ ‪ ،RowUpdated‬ﻓـﺈﻥ ﺍﻟﻭﺴـﻴﻠﺔ‬


‫ـﺎﺒﻕ‬
‫ـﻜﻠﺔ ﺘﻁــ‬
‫ـﺩﺜﺕ ﻤﺸــ‬
‫ـﺎﻤﺞ ﺇﺫﺍ ﺤــ‬
‫ـﻲ ﺍﻟﺒﺭﻨــ‬
‫ـﺄ ﻓــ‬
‫ـﻕ ﺨﻁــ‬
‫‪ Update‬ﺘﻁﻠــ‬
‫‪ Concurrency Violation‬ﻋﻨﺩ ﺤﻔﻅ ﺃﺤﺩ ﺍﻟﺴـﺠﻼﺕ‪ ..‬ﺃﻤـﺎ ﺇﺫﺍ ﻜﺘﺒـﺕ ﺍﻹﺠـﺭﺍﺀ‬
‫ﺍﻟﻤﺴﺘﺠﻴﺏ ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ‪ ،‬ﻓﺈﻥ ﺍﻟﺨﻁﺄ ﻻ ﻴﺤﺩﺙ‪ ،‬ﻭﺘﺘﺎﺡ ﻟﻙ ﺍﻟﻔﺭﺼﺔ ﻟﻜﺘﺎﺒﺔ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﻴﺤل‬
‫ﻤﺸﻜﻠﺔ ﺍﻟﺘﻁﺎﺒﻕ‪.‬‬
‫ﻟﻜﻥ‪ ..‬ﻤﺎ ﻫﻭ ﻤﻭﻀﻭﻉ ﺍﻟﺘﻁﺎﺒﻕ ‪ Concurrency‬ﻫﺫﺍ؟‬
‫ﻫﺫﺍ ﻫﻭ ﻤﻭﻀﻭﻉ ﺍﻟﻔﻘﺭﺓ ﺍﻟﺘﺎﻟﻴﺔ‪.‬‬

‫‪١٤٦‬‬
‫ﺍﻟﺘﺼﺎﺭﻉ ﻋﻠﻰ ﺘﺤﺩﻴﺙ ﺍﻟﺒﻴﺎﻨﺎﺕ‪:‬‬
‫ﻫﻨﺎﻙ ﻤﺸﻜﻠﺔ ﺭﺌﻴﺴﻴﺔ ﺴﺘﻭﺍﺠﻬﻙ ﻋﻨﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻴﺴﺘﺨﺩﻤﻬﺎ ﺃﻜﺜﺭ ﻤﻥ ﻤﻭﻅﻑ ﻓﻲ‬
‫ﻨﻔﺱ ﺍﻟﻭﻗﺕ‪ ،‬ﻭﻫﻲ ﺍﻟﺘﻀﺎﺭﺏ ﺒﻴﻥ ﺍﻟﺘﻌﺩﻴﺭﺕ ﺍﻟﺘﻲ ﻴﺠﺭﻴﻬﺎ ﺃﻜﺜﺭ ﻤـﻥ ﻤﻭﻅـﻑ ﻋﻠـﻰ ﻨﻔـﺱ‬
‫ﺍﻟﺴﺠل‪ ..‬ﺘﺨﻴل ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ‪:‬‬
‫‪ -‬ﻗﺎﻡ ﻤﺴﺘﺨﺩﻡ ﺒﺭﻨﺎﻤﺠﻙ ﺒﺘﺤﻤﻴل ﺴﺠﻼﺕ ﺍﻟﻜﺘﺏ‪ ،‬ﻭﻗﺎﻡ ﺒﺘﻌﺩﻴل ﺴﻌﺭ ﻜﺘﺎﺏ "ﻋﻀﺎ ﺍﻟﺤﻜﻴﻡ"‬
‫ﻤﻥ ‪ ٥‬ﺠﻨﻴﻬﺎﺕ ﺇﻟﻰ ‪ ٧‬ﺠﻨﻴﻬﺎﺕ‪.‬‬
‫‪ -‬ﻋﻨﺩ ﻤﺤﺎﻭﻟﺔ ﺒﺭﻨﺎﻤﺠﻙ ﺤﻔﻅ ﻫﺫﻩ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻜﺎﻥ ﻤﺴﺘﺨﺩﻡ ﺁﺨﺭ ﻗـﺩ‬
‫ﻏﻴﺭ ﻋﺩﺩ ﺍﻟﻨﺴﺦ ﺍﻟﻤﺘﺎﺤﺔ ﺍﻟﻤﺘﺎﺤﺔ ﻤﻥ ﻜﺘﺎﺏ "ﻋﺼﺎ ﺍﻟﺤﻜﻴﻡ" ﻤﻥ ‪ ٢٠٠٠‬ﺇﻟﻰ ‪.٣٠٠٠‬‬
‫ﺍﻟﺴﺅﺍل ﺍﻵﻥ ﻫﻭ‪ :‬ﻤﺎﺫﺍ ﻨﻔﻌل ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ؟‬
‫ﻟﻭ ﺤﻔﻅ ﺒﺭﻨﺎﻤﺠﻙ ﺴﺠل ﺍﻟﻜﺘﺎﺏ "ﻋﻀﺎ ﺍﻟﺤﻜﻴﻡ" ﻓﺴﻴﻌﺩل ﺴﻌﺭﻩ ﺇﻟﻰ ‪ ٧‬ﺠﻨﻴﻬﺎﺕ‪ ،‬ﻟﻜﻨـﻪ ﺴـﻴﻌﻴﺩ‬
‫ﻋﺩﺩ ﺍﻟﻨﺴﺦ ﺍﻟﻤﺘﺎﺤﺔ ﻤﻨﻪ ﺇﻟﻰ ‪!٢٠٠٠‬‬
‫ﺃﻤﺎ ﻟﻭ ﺃﺒﻘﻴﻨﺎ ﻋﻠﻰ ﺍﻟﺘﻌﺩﻴﻼﺕ ﺍﻟﺘﻲ ﺃﺠﺭﺍﻫﺎ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻵﺨﺭ‪ ،‬ﻓﻬﺫﺍ ﻤﻌﻨﺎﻩ ﺍﻹﺒﻘﺎﺀ ﻋﻠـﻰ ﺍﻟﺘﻌـﺩﻴل‬
‫ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﻋﺩﺩ ﺍﻟﻨﺴﺦ‪ ،‬ﻟﻜﻥ ﺍﻟﺴﻌﺭ ﺴﻴﻅل ‪ ٥‬ﺠﻨﻴﻬﺎﺕ!‬
‫ﻁﺒﻌﺎ ﻓﻲ ﻜﻠﺘﺎ ﺍﻟﺤﺎﻟﺘﻴﻥ ﺴﺘﺤﺩﺙ ﻤﺸﻜﻠﺔ ﻓﻲ ﺍﻟﻌﻤل‪ ..‬ﻭﻋﻨﺩﻤﺎ ﺴﻴﺤﺎﻭل ﺍﻟﻤﺩﻴﺭ ﻤﻌﺎﻗﺒـﺔ ﻤﺴـﺌﻭل‬
‫ﺍﻟﻤﺨﺎﺯﻥ ﻓﻲ ﺍﻟﺤﺎﻟﺔ ﺍﻷﻭﻟﻰ ﻓﺴﻴﻘﺴﻡ ﻟﻪ ﺒﺄﻏﻠﻅ ﺍﻷﻴﻤﺎﻥ ﺇﻨﻪ ﻏﻴﺭ ﻋﺩﺩ ﺍﻟﻨﺴﺦ ﺍﻟﻤﺘﺎﺤـﺔ‪ ،‬ﻭﻋﻨـﺩﻤﺎ‬
‫ﺴﻴﺤﺎﻭل ﻤﻌﺎﻗﺒﺔ ﻤﺴﺌﻭل ﺍﻟﻤﺒﻴﻌﺎﺕ ﻓﻲ ﺍﻟﺤﺎﻟﺔ ﺍﻟﺜﺎﻨﻴﺔ‪ ،‬ﻓﺴﻴﻘﺴﻡ ﻟﻪ ﺇﻨـﻪ ﻏﻴـﺭ ﺜﻤـﻥ ﺍﻟﻨﺴـﺨﺔ‪،‬‬
‫ﻭﻜﻼﻫﻤﺎ ﺼﺎﺩﻕ ﻓﻲ ﻗﺴﻤﻪ‪ ،‬ﻭﺃﻨﺕ ﺍﻟﺫﻱ ﺨﺭﺒﺕ ﺒﻴﺘﻪ!‬
‫ﺘﺴﻤ‪‬ﻰ ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ ﺒﺎﺴﻡ ﻤﺸﻜﻠﺔ ﺍﻟﺘﻁﺎﺒﻕ ‪ ..Concurrency Problem‬ﻭﻴﻤﻜﻥ ﻋﻼﺠﻬﺎ ﺒﺄﺤﺩ‬
‫ﺍﻟﺤﻠﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ‪:‬‬

‫‪ -١‬ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﺸﺎﺌﻡ ‪:Pessimistic Concurrency‬‬


‫ﺍﺴﺘﺨﺩﻡ ﻫﺫﺍ ﺍﻟﺤل ﻟﻭ ﻜﻨﺕ "ﻤﺘﺸﺎﺌﻤﺎ" ﺒﺨﺼـﻭﺹ ﺘﻌـﺎﺭﺽ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﺘـﻲ ﻴﺤﻔﻅﻬـﺎ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ‪ ،‬ﺃﻭ ﻜﺎﻥ ﺃﻱ ﺘﻌﺎﺭﺽ ﻴﻤﻜﻥ ﺃﻥ ﻴﺅﺩﻱ ﺇﻟﻰ ﺨﺴﺎﺌﺭ ﻜﺒﻴﺭﺓ ﻟﻠﻌﻤل ﺍﻟﺫﻱ ﻴﻨﻅﻤـﻪ‬
‫ﺒﺭﻨﺎﻤﺠﻙ‪ ،‬ﺤﻴﺙ ﺇﻥ ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﺸﺎﺌﻡ ﻴﻤﻨﻊ ﺤﺩﻭﺙ ﺃﻱ ﺘﻀﺎﺭﺏ ﻓـﻲ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ،‬ﻭﺫﻟـﻙ‬
‫ﺒﺈﻏﻼﻕ ‪ Lock‬ﺴﺠﻼﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻗﺎﻡ ﺃﻱ ﻤﺴﺘﺨﺩﻡ ﺒﺘﺤﻤﻴﻠﻬﺎ‪ ،‬ﻤﻤﺎ ﻴﻤﻨﻊ ﺃﻱ ﻤﺴـﺘﺨﺩﻡ‬

‫‪١٤٧‬‬
‫ﺁﺨﺭ ﻤﻥ ﺘﻐﻴﻴﺭﻫﺎ ﺇﻟﻰ ﺃﻥ ﻴﺘﻡ ﻴﻐﻠﻕ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻷﻭل ﺍﻻﺘﺼﺎل ﻭﺘـﺘﻡ ﺇﺯﺍﻟـﺔ ﺍﻹﻏـﻼﻕ‪..‬‬
‫ﻭﻴﻤﻜﻥ ﺘﻨﻔﻴﺫ ﻫﺫﺍ ﺍﻟﺤل ﻓﻲ ﺩﻭﺕ ﻨﺕ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺘﻌﺎﻤﻼﺕ ‪ ،Transactions‬ﻟﻬﺫﺍ ﺴﻨﺅﺠل‬
‫ﺘﻁﺒﻴﻘﻪ ﺇﻟﻰ ﺍﻟﻜﺘﺎﺏ ﺍﻟﻘﺎﺩﻡ ﺇﻥ ﺸﺎﺀ ﺍﷲ‪.‬‬
‫ﻭﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﺍ ﺍﻟﺤل‪ ،‬ﺘﻜﻭﻥ ﺠﻤﻠﺔ ﺍﻟﺘﺤﺩﻴﺙ ﺒﺴﻴﻁﺔ ﻟﻠﻐﺎﻴﺔ‪ ،‬ﻷﻨـﻙ ﺘﺴـﺘﺨﺩﻡ ﺍﻟﻤﻔﺘـﺎﺡ‬
‫ﺍﻷﺴﺎﺴﻲ ﻟﻠﺤﻘل ﻟﻠﻌﺜﻭﺭ ﻋﻠﻴﻪ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻤﻥ ﺜﻡ ﺘﻐﻴﺭ ﻗﻴﻤﻪ ﻤﺒﺎﺸﺭﺓ‪ ،‬ﻷﻨﻙ ﻭﺍﺜـﻕ‬
‫ﺃﻨﻪ ﻟﻡ ﻴﺘﻐﻴﺭ ﻤﻨﺫ ﺃﻥ ﻗﻤﺕ ﺒﺘﺤﻤﻴﻠﻪ‪ ..‬ﻫﻜﺫﺍ ﻤﺜﻼ ﺴﺘﻜﻭﻥ ﺠﻤﻠﺔ ﺘﺤﺩﻴﺙ ﺴﺠﻼﺕ ﺍﻟﻤﺅﻟﻔﻴﻥ‪:‬‬
‫‪UPDATE Authors‬‬
‫‪SET Author = @Author,‬‬
‫‪CountryID = @CountryID,‬‬
‫‪Phone = @Phone,‬‬
‫‪About = @About‬‬
‫;‪WHERE ID = @ID‬‬
‫ﻻﺤﻅ ﺃﻥ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻫﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ ﺘﺄﺨﺫ ﻗﻴﻤﻬﺎ ﻤﻥ ﺨﺎﻨﺎﺕ ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﻴﺘﻡ‬
‫ﺘﺤﺩﻴﺜﻪ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﻴﻌﺘﺒﺭ ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﺸﺎﺌﻡ ﺤﻼ ﺤﺎﺴﻤﺎ ﻟﻠﻤﺸﻜﻠﺔ‪ ،‬ﻷﻥ ﺃﻱ ﻤﺴﺘﺨﺩﻡ ﺁﺨـﺭ ﺴـﻴﺤﺎﻭل ﺘﻌـﺩﻴل‬
‫ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﺘﻨﺎﺯﻉ ﻋﻠﻴﻬﺎ ﺴﻴﺤﺼل ﻋﻠﻰ ﺭﺴﺎﻟﺔ ﺨﻁﺄ ﺘﺨﺒﺭﻩ ﺒﺄﻨﻬﺎ ﻤﻐﻠﻘﺔ ﻤﻥ ﻗﺒل ﻤﺴﺘﺨﺩﻡ‬
‫ﺁﺨﺭ‪ ..‬ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻋﻠﻴﻙ ﺃﻥ ﺘﺠﻌل ﺒﺭﻨﺎﻤﺠﻙ ﻴﻨﺘﻅﺭ ﺍﻨﺘﻬﺎﺀ ﺍﻹﻏﻼﻕ‪ ،‬ﻭﻤﻥ ﺜﻡ ﻴﻌﺭﺽ‬
‫ﻟﻠﻤﺴﺘﺨﺩﻡ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﻴﺤﺎﻭل ﺘﺤﺩﻴﺜﻬﺎ‪ ،‬ﻟﻴﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺘﻤﺕ ﻋﻠﻴﻬﺎ‪ ،‬ﻭﻤﻥ‬
‫ﺜﻡ ﻴﻘﺭﺭ ﻜﻴﻑ ﻴﻭﺍﺌﻡ ﺒﻴﻨﻬﺎ ﻭﺒﻴﻥ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺃﺠﺭﺍﻫﺎ‪ ،‬ﺜﻡ ﻴﻌﻴـﺩ ﺤﻔﻅﻬـﺎ ﻓـﻲ ﻗﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻟﻜﻥ ﺍﻟﻤﺸﻜﻠﺔ ﻫﻲ ﺃﻥ ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﺸﺎﺌﻡ ﺴﻴﻬﺒﻁ ﺒﻜﻔﺎﺀﺓ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﺍﺴـﺘﻤﺭ ﺇﻏـﻼﻕ ﻜـل‬
‫ﺴﺠل ﻟﻔﺘﺭﺍﺕ ﺯﻤﻨﻴﺔ ﻁﻭﻴﻠﺔ‪ ،‬ﺃﻭ ﺇﺫﺍ ﺘﻡ ﺘﺤﺩﻴﺙ ﺃﻋﺩﺍﺩ ﻀﺨﻤﺔ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﻋﻠﻰ ﺍﻟﺘﺘـﺎﺒﻊ‪،‬‬
‫ﻭﺫﻟﻙ ﻷﻥ ﺇﻏﻼﻕ ﺍﻟﺴﺠﻼﺕ ﻴﺴﺘﻬﻠﻙ ﺠﺯﺀﺍ ﻤﻥ ﻭﻗﺕ ﺘﺸﻐﻴل ﻭﺫﺍﻜﺭﺓ ﺍﻟﺨﺎﺩﻡ‪ ،‬ﻜﻤـﺎ ﺃﻨـﻪ‬
‫ﻴﺤﺘﺎﺝ ﺇﻟﻰ ﺇﺒﻘﺎﺀ ﻗﻨﻭﺍﺕ ﺍﻻﺘﺼﺎل ﻤﻔﺘﻭﺤﺔ ﻤﻊ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﺍﻟﺫﻴﻥ ﻗﺎﻤﻭﺍ ﺒﻌﻤﻠﻴﺔ ﺍﻹﻏـﻼﻕ‪،‬‬
‫ﻤﻤﺎ ﻴﺤﺭﻡ ﻤﺴﺘﺨﺩﻤﻴﻥ ﺁﺨﺭﻴﻥ ﻤﻥ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺫﻟﻙ ﺍﻟﻭﻗﺕ‪ ..‬ﻟﻜـﻥ ﻴﻅـل‬
‫ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﺸﺎﺌﻡ ﺍﻟﺤل ﺍﻷﻓﻀل ﻋﻨﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻴﺘﺼل ﺒﻬﺎ ﻋﺩﺩ ﻜﺒﻴﺭ ﻤـﻥ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻓﻲ ﻨﻔﺱ ﺍﻟﻠﺤﻅﺔ‪ ،‬ﻭﻴﺘﺼﺎﺭﻋﻭﻥ ﻋﻠﻰ ﺘﺤﺩﻴﺙ ﻨﻔﺱ ﺍﻟﺴﺠﻼﺕ‪ ،‬ﻷﻥ ﺍﺴـﺘﺨﺩﺍﻡ‬
‫‪١٤٨‬‬
‫ﻜﺜﺭﺓ ﻋﻤﻠﻴﺎﺕ ﺍﻟﺘﺭﺍﺠﻊ ﻋﻥ ﺍﻟﺘﻌـﺎﻤﻼﺕ ‪ Transactions Rollback‬ﻻﺴـﺘﻌﺎﺩﺓ ﺍﻟﻘـﻴﻡ‬
‫ﺍﻷﺼﻠﻴﺔ ﻗﺒل ﺍﻟﺘﻀﺎﺭﺏ‪ ،‬ﺘﺴﺘﻬﻠﻙ ﺍﻟﺨﺎﺩﻡ ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺒﺄﻜﺜﺭ ﻤﻤﺎ ﺘﻔﻌل ﻋﻤﻠﻴﺎﺕ ﺍﻹﻏﻼﻕ‪.‬‬

‫‪ -٢‬ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﻔﺎﺌل ‪:Optimistic Concurrency‬‬


‫ﻫﺫﺍ ﻫﻭ ﺍﻟﺤل ﺍﻟﻤﻔﻀل ﻭﺍﻷﺴﻬل ﻓﻲ ﺘﻘﻨﻴﺔ ‪ ،ADO.NET‬ﻭﻗﺩ ﺴﻤﻲ ﺒﻬـﺫﺍ ﺍﻻﺴـﻡ ﻷﻨـﻪ‬
‫ﻴﻔﺘﺭﺽ ﺃﻥ ﺍﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻟﻥ ﻴﺤﺎﻭﻟﻭﺍ ﺘﻌﺩﻴل ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺃﺜﻨﺎﺀ ﺘﻌﺎﻤﻠﻙ ﻤﻊ ﺒﻴﺎﻨﺎﺘﻬﺎ ﻓـﻲ‬
‫ﺒﺭﻨﺎﻤﺠﻙ‪ ،‬ﺇﻻ ﻓﻲ ﺤﺎﻻﺕ ﻨﺎﺩﺭﺓ‪.‬‬
‫ﻟﻜﻥ ﻤﺎﺫﺍ ﻴﺤﺩﺙ ﻟﻭ ﺤﺩﺙ ﺍﻟﺘﻌﺎﺭﺽ ﻓﻌﻼ؟‪ ..‬ﻜﻴﻑ ﻨﺤل ﺍﻟﻤﺸﻜﻠﺔ ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ؟‬
‫ﻓﻲ ﺍﻟﺤﻘﻴﻘﺔ ﻫﻨﺎﻙ ﻋﺩﺓ ﺤﻠﻭل ﻤﺘﺒﻌﺔ‪ ،‬ﻭﻋﻠﻴﻙ ﺍﺨﺘﻴﺎﺭ ﻟﺤل ﺍﻟﺫﻱ ﻴﻨﺎﺴﺒﻙ ﻤﻨﻬـﺎ ﺘﺒﻌـﺎ ﻟﻤـﺎ‬
‫ﻴﻨﺎﺴﺒﻙ‪ ..‬ﻭﻫﺫﻩ ﺍﻟﺤﻠﻭل ﻫﻲ‪:‬‬
‫ﺃ‪ .‬ﻋﻨﺩ ﺘﺤﺩﻴﺙ ﺍﻟﺴﺠل‪ ،‬ﻴﺘﻡ ﺍﻟﺒﺤﺙ ﻋﻨﻪ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻭﺍﺴﻁﺔ ﻗﻴﻡ ﻜل ﺤﻘﻭﻟـﻪ‪،‬‬
‫ﻭﻟﻴﺱ ﻓﻘﻁ ﺒﻭﺍﺴﻁﺔ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ‪ ..‬ﻫﺫﻩ ﻤﺜﻼ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺨﺎﺼﺔ ﺒﺘﺤﺩﻴﺙ‬
‫ﺴﺠﻼﺕ ﺍﻟﻤﺅﻟﻔﻴﻥ‪:‬‬
‫‪UPDATE Authors‬‬
‫‪SET Author = @Author,‬‬
‫‪CountryID = @CountryID,‬‬
‫‪Phone = @Phone,‬‬
‫‪About = @About‬‬
‫‪WHERE (ID = @Original_ID) AND‬‬
‫‪(Author = @Original_Author) AND‬‬
‫‪(CountryID = @Original_CountryID) AND‬‬
‫‪(@IsNull_Phone = 1) AND (Phone IS NULL) OR‬‬
‫‪(ID = @Original_ID) AND‬‬
‫‪(Author = @Original_Author) AND‬‬
‫‪(CountryID = @Original_CountryID) AND‬‬
‫)‪(Phone = @Original_Phone‬‬
‫ﻫﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ ﻴﻀﻤﻥ ﻟﻙ ﺃﻨﻪ ﻟﻭ ﺤﺩﺙ ﺃﻱ ﺘﻐﻴﻴﺭ ﻓﻲ ﺍﻟﺴﺠل ﻤﻥ ﻗﺒل ﻤﺴـﺘﺨﺩﻤﻴﻥ‬
‫ﺁﺨﺭﻴﻥ‪ ،‬ﻓﻠﻥ ﻴﻌﺜﺭ ﻋﻠﻴﻪ ﺒﺭﻨﺎﻤﺠﻙ‪ ،‬ﻭﺒﺎﻟﺘﺎﻟﻲ ﻟﻥ ﻴﺘﻡ ﺤﻔﻅ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﻗﺎﻡ ﺒﻬـﺎ‬
‫ﻤﺴﺘﺨﺩﻡ ﺒﺭﻨﺎﻤﺠﻙ‪.‬‬

‫‪١٤٩‬‬
‫ﻟﻌﻠﻙ ﺘﻼﺤﻅ ﻓﻲ ﻫﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ ﻭﺠﻭﺩ ﻤﻌﺎﻤﻠﻴﻥ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻜل ﺤﻘل‪ ..‬ﻤﺜﻼ‪ ،‬ﻴـﺘﻡ‬
‫ﺍﻟﺘﻌﺎﻤــل ﻤــﻊ ﺤﻘــل ﺍﻟﻤــﺅﻟﻔﻴﻥ ﻤــﻥ ﺨــﻼل ﺍﻟﻤﻌــﺎﻤﻠﻴﻥ ‪@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‬ﻭﺍﻜﺘﺏ ﻓﻲ ﻗﻴﻤﺘﻬـﺎ ﺠﻤﻠـﺔ ﺍﻻﺴـﺘﻌﻼﻡ‬
‫ﺍﻟﻤﻜﻭﻨﺔ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﺃﻤﺭ‪.‬‬
‫‪ -‬ﺍﻀﻐﻁ ﺯﺭ ﺍﻟﺤﻔﻅ ﺃﻭ ﺍﻨﺘﻘل ﺇﻟﻰ ﺃﻱ ﺨﺎﺼﻴﺔ ﺃﺨﺭﻯ ﺃﻭ ﺇﻟﻰ ﺍﻟﻨﻤﻭﺫﺝ‪ ..‬ﺴﺘﻅﻬﺭ‬
‫ﺭﺴﺎﻟﺔ ﺘﺴﺄﻟﻙ ﺇﻥ ﻜﻨﺕ ﺘﺭﻴﺩ ﺘﺤﺩﻴﺙ ﻤﻌـﺎﻤﻼﺕ ﻫـﺫﺍ ﺍﻻﻤـﺭ‪ ..‬ﺍﻀـﻐﻁ ﺯﺭ‬
‫ﺍﻟﻤﻭﺍﻓﻘﺔ‪.‬‬

‫‪١٦٢‬‬
‫ﺍﻵﻥ ﺴﻴﻜﻭﻥ ﻜل ﺸﻲﺀ ﻋﻠﻰ ﻤﺎ ﻴﺭﺍﻡ‪ ،‬ﻭﺴﻴﻤﻸ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺒﺠﺩﻭﻟﻲ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺍﻟﻜﺘﺏ!‬
‫ﺍﻟﻁﺭﻴﻑ ﺃﻨﻙ ﻟﻭ ﺃﻋﺩﺕ ﻓﺘﺢ ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﻓﺴﺘﺠﺩ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﻤﺭﻜﺒﺔ ﻤـﻥ‬
‫ﺃﻜﺜﺭ ﻤﻥ ﺃﻤﺭ ﻤﻌﺭﻭﻀﺔ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ‪ ،‬ﻭﻟﻜﻨﻙ ﺴﺘﻅل ﺘﺤﺼل ﻋﻠﻰ ﺨﻁـﺄ ﻟـﻭ‬
‫ﺤﺎﻭﻟﺕ ﺍﻻﻨﺘﻘﺎل ﺇﻟﻰ ﺍﻟﺨﻁﻭﺓ ﺍﻟﺘﺎﻟﻴﺔ!‬

‫ﻭﺘﺤﺘــﻭﻱ ﻫــﺫﻩ ﺍﻟﻨﺎﻓــﺫﺓ ﺃﻴﻀــﺎ ﻋﻠــﻰ ﺍﻟــﺯﺭ‪" ‬ﺨﻴــﺎﺭﺍﺕ ﻤﺘﻘﺩ‪‬ﻤــﺔ"‬


‫‪ ،Advanced Options‬ﻭﻟﻭ ﻀﻐﻁﺘﻪ ﻓﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﺒﻬﺎ ﺍﻟﺨﻴﺎﺭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪:Generate Insert, Update And Delete Statements .١‬‬


‫ﺍﺴﺘﺨﺩﻡ ﻫﺫﺍ ﺍﻟﺨﻴﺎﺭ ﺇﺫﺍ ﻜﺎﻥ ﺒﺭﻨﺎﻤﺠﻙ ﺴﻴﺠﺭﻱ ﺘﻌﺩﻴﻼﺕ ﻓـﻲ ﺍﻟﺴـﺠﻼﺕ ﺍﻟﺘـﻲ‬
‫ﻴﺤﻤﻠﻬﺎ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﻴﺘﻡ‪ ‬ﺇﻨﺘﺎﺝ ﺠﻤل ﺍﻟﺘﺤـﺩﻴﺙ ﻭﺍﻹﺩﺭﺍﺝ‬
‫ﻭﺍﻟﺤﺫﻑ ﺁﻟﻴﺎ‪ ،‬ﺒﺎﻻﻋﺘﻤﺎﺩ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓـﻲ ﺠﻤﻠـﺔ‬
‫‪ Select‬ﺍﻟﺘﻲ ﺃﻨﺸﺄﺘﻬﺎ‪.‬‬

‫‪ .٢‬ﺍﺴﺘﺨﺩﻡ ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﻔﺎﺌل ‪:Use Optimistic Concurrency‬‬


‫ﻫﺫﺍ ﺍﻟﺨﻴﺎﺭ ﻴﺘﻴﺢ ﻟﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﻔﺎﺌل ﻋﻨﺩ ﺘﺤﺩﻴﺙ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻻﺤـﻅ ﺃﻥ‬
‫ﺇﺯﺍﻟﺔ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﻴﻌﻨﻲ ﺃﻨﻙ ﺘﺭﻴﺩ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺘﻁﺎﺒﻕ ﺍﻟﻤﺘﺸﺎﺌﻡ‪ ..‬ﻫﺫﺍ ﻴﺅﺜﺭ ﻓﻘﻁ‬
‫ﻋﻠﻰ ﺼﻴﻐﺔ ﺠﻤﻠﺔ ﺍﻟﺘﺤﺩﻴﺙ ‪ ،UpdateCommand‬ﻟﻜﻨﻪ ﻟﻥ ﻴﻜﺘﺏ ﻟـﻙ ﺍﻟﻜـﻭﺩ‬

‫‪١٦٣‬‬
‫ﺍﻟﻤﻨﺎﺴﺏ ﻹﻏﻼﻕ ‪ Lock‬ﺴﺠﻼﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻋﻠﻴﻙ ﺃﻥ ﺘﻜﺘﺏ ﻫـﺫﺍ ﺍﻟﻜـﻭﺩ‬
‫ﺒﻨﻔﺴﻙ ﻤﻥ ﺨﻼل ﻜﺎﺌﻥ ﺍﻟﺘﻌﺎﻤﻼﺕ ‪.Transaction Object‬‬

‫‪ .٣‬ﺇﻨﻌﺎﺵ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:Refresh The DataSet‬‬


‫ﻫﺫﺍ ﺍﻟﺨﻴﺎﺭ ﻴﻀﻴﻑ ﺠﻤﻠﺔ ﺘﺤﺩﻴﺩ ‪ SELECT‬ﺒﻌﺩ ﺠﻤﻠﺘـﻲ ﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺘﺤـﺩﻴﺙ‪،‬‬
‫ﻭﺫﻟﻙ ﻹﻨﻌﺎﺵ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻌﺩ ﺘﻨﻔﻴﺫ ﺃﻭﺍﻤﺭ ﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﻜﻤﺎ ﺸﺭﺤﻨﺎ‬
‫ﺴﺎﺒﻘﺎ‪ ..‬ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﺍﻟﺨﻴﺎﺭ ﻏﻴﺭ ﻤﺘﺎﺡ ﻓﻲ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ‪ ،Access‬ﻭﺫﻟﻙ ﻷﻨـﻪ‬
‫ﻻ ﻴﺴﻤﺢ ﺃﺼﻼ ﺒﺘﻨﻔﻴﺫ ﺃﻜﺜﺭ ﻤﻥ ﺍﺴﺘﻌﻼﻡ ﻓﻲ ﺍﻟﻤﺭﺓ ﺍﻟﻭﺍﺤﺩﺓ‪.‬‬

‫‪ -‬ﺃﻤﺎ ﻟﻭ ﺍﺨﺘﺭﺕ ﺍﺴﺘﺨﺩﺍﻡ ﺇﺠﺭﺍﺀﺍﺕ ﻤﺨﺯﻨﺔ ﻤﻭﺠﻭﺩﺓ ﺴﺎﺒﻘﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻓﺴﺘﻅﻬﺭ‬
‫ﻟﻙ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪١٦٤‬‬
‫ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﺒﻬﺎ ﺃﺭﺒﻊ ﻗﻭﺍﺌﻡ ﻤﻨﺴﺩﻟﺔ‪ ،‬ﺘﻌﺭﺽ ﻜل ﻤﻨﻬﺎ ﺃﺴـﻤﺎﺀ ﺍﻹﺠـﺭﺍﺀﺍﺕ ﺍﻟﻤﺨﺯﻨـﺔ‬
‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﺘﺨﺘﺎﺭ ﻤﻨﻬﺎ ﺇﺠﺭﺍﺀ ﻟﻠﺘﺤﺩﻴـﺩ ‪ 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‬‬
‫)ﻜﺎﺴﻡ ﺍﻟﺠﺩﻭل ﻭﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ(‪ ،‬ﻭﺘﺒﻨﻲ ﺍﻷﻤﺭ ﺍﻟﻤﻨﺎﺴﺏ ﻟﺘﺤﺩﻴﺙ ﺃﻭ ﺤﺫﻑ ﺃﻭ ﺇﺩﺭﺍﺝ ﺍﻟﺴـﺠل‬
‫ﺍﻟﺫﻱ ﺃﻁﻠﻕ ﺍﻟﺤﺩﺙ‪.‬‬
‫ﻭﻴﺘﻡ ﺭﺒﻁ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﻭﺍﺤﺩ ﻓﻘﻁ ﺒﺒﺎﻨﻲ ﺃﻭﺍﻤﺭ ﻭﺍﺤﺩ ﻓﻘﻁ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataAdapter‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺇﻨﺸﺎﺀ ﺃﻭﺍﻤﺭﻩ‪ ..‬ﻻﺤﻅ ﺃﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻴﺠـﺏ‬
‫ﺃﻥ ﻴﺤﻘﻕ ﺍﻟﺸﺭﻭﻁ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﺃﻥ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺃﻤﺭ ﺘﺤﺩﻴﺙ ‪.SELECT Command‬‬
‫‪ -٢‬ﺃﻥ ﻴﻜﻭﻥ ﻀﻤﻥ ﺃﻋﻤﺩﺓ ﺍﻟﻨﺘﻴﺠﺔ ﺍﻟﺘﻲ ﻴﻌﻴﺩﺍ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﺃﻭ ﻋﻤـﻭﺩ‬
‫ﻤﺘﻔﺭﺩ ‪ Unique‬ﻴﻤﻴﺯ ﻜل ﺼﻑ‪.‬‬
‫‪ -٣‬ﺃﻥ ﻴﻌﻴﺩ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ﺍﻟﻨﺘﺎﺌﺞ ﻤﻥ ﺠﺩﻭل ﻭﺍﺤﺩ ﻓﻘﻁ‪ ..‬ﺍﺴﺘﻌﻼﻤﺎﺕ ﺍﻟﺭﺒﻁ ﺒﻴﻥ ﺃﻜﺜﺭ ﻤﻥ‬
‫ﺠﺩﻭل ﻤﺭﻓﻭﻀﺔ‪.‬‬
‫ﻭﻋﻨﺩ ﺍﻹﺨﻼل ﺒﺄﻱ ﻤﻥ ﻫﺫﻩ ﺍﻟﺸﺭﻭﻁ‪ ،‬ﺴﻴﺤﺩﺙ ﺨﻁﺄ ﻭﻴﺭﻓﺽ ﺒﺎﻨﻲ ﺍﻷﻭﺍﻤﺭ ﺇﻨﺘﺎﺝ ﺃﻭﺍﻤـﺭ‬
‫ﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻟﺤﺫﻑ ﻭﺍﻹﺩﺭﺍﺝ‪ ..‬ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ ‪ Dispose‬ﺍﻟﺨﺎﺼـﺔ ﺒﺒـﺎﻨﻲ‬
‫ﺍﻷﻭﺍﻤﺭ‪ ،‬ﻹﻨﻬﺎﺀ ﺍﺭﺘﺒﺎﻁﻪ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺍﻟﻭﻗﻭﻑ ﻋﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻷﻭﺍﻤﺭ ﺍﻟﺘﻲ ﺃﻨﺸﺄﻫﺎ‪.‬‬
‫‪١٦٦‬‬
‫ﻭﻀﻊ ﺠﻤﻴﻊ ﺍﻟﻘﻴﻡ ‪:SetAllValues‬‬
‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻬﺎ ‪ ،True‬ﻴﻨﺘﺞ ﺒﺎﻨﻲ ﺍﻷﻭﺍﻤﺭ ﺃﻤﺭ ﺘﺤﺩﻴﺙ ‪ Update‬ﻴﺤﺩﺙ ﺠﻤﻴـﻊ ﻗـﻴﻡ‬
‫ﺍﻟﺴﺠل‪ ..‬ﺃﻤﺎ ﺇﺫﺍ ﺠﻌﻠﺘﻬﺎ ‪ ،False‬ﻓﺴﻴﻨﺘﺞ ﺃﻤﺭ ﺘﺤﺩﻴﺙ ﻴﺤﺩﺙ ﻓﻘـﻁ ﻗـﻴﻡ ﺍﻟﺤﻘـﻭل ﺍﻟﺘـﻲ‬
‫ﺘﻐﻴﺭﺕ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺒﺎﻨﻲ ﺍﻷﻭﺍﻤﺭ ﻴﺘﺎﺒﻊ ﺍﻟﺤـﺩﺙ ‪RowUpdating‬‬
‫ﺍﻟﺨﺎﺹ ﺒﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻴﻔﺤﺹ ﻜل ﺼﻑ ﻗﺒل ﺘﺤﺩﻴﺜﻪ‪ ،‬ﻭﻤﻥ ﺜﻡ ﻴﻨﺘﺞ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺘﺤـﺩﻴﺙ‬
‫ﺍﻟﻤﻨﺎﺴﺏ ﻟﻬﺫﺍ ﺍﻟﺼﻑ ﺘﺒﻌﺎ ﻟﻠﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺤﺩﺜﺕ ﻓﻴﻪ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻁﺒﻌﺎ ﻫﺫﺍ ﺃﺫﻜﻰ ﻭﺃﺴﺭﻉ ﻭﺃﻗل ﻋﺒﺌﺎ ﻋﻠﻰ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل‪ ،‬ﻟﻜﻨﻪ ﻗﺩ ﻴﺅﺩﻱ ﺇﻟﻰ ﻨﺘـﺎﺌﺞ ﻏﻴـﺭ‬
‫ﻤﺘﻭﻗﻌﺔ ﺇﺫﺍ ﻜﻨﺕ ﺘﺤل ﻤﺸﺎﻜل ﺍﻟﺘﻭﺍﻓﻕ ‪ Concurrency Conflicts‬ﺒﺎﺴـﺘﺨﺩﺍﻡ ﻁﺭﻴﻘـﺔ‬
‫"ﺍﻷﺨﻴﺭ ﻴﻜﺴﺏ"‪ ،‬ﺒﺤﻴﺙ ﺘﺤﻔﻅ ﺘﻐﻴﻴﺭﺍﺘﻙ ﻤﺒﺎﺸﺭﺓ‪ ،‬ﻓﻬﺫﺍ ﻗﺩ ﻴﺠﻌل ﻗﻴﻡ ﺍﻟﺴﺠل ﺘﺘﻜـﻭﻥ ﻤـﻥ‬
‫ﻤﺯﻴﺞ ﻤﻥ ﺘﻐﻴﻴﺭﺍﺘﻙ ﻭﺘﻐﻴﻴﺭﺍﺕ ﻤﺴﺘﺨﺩﻡ ﺁﺨﺭ‪ ..‬ﺃﻤﺎ ﺇﺫﺍ ﻜﻨﺕ ﺘﺴـﺘﺨﺩﻡ ﻁـﺎﺒﻊ ﺍﻟﻭﻗـﺕ ﺃﻭ‬
‫ﺘﻘﺎﺭﻥ ﻜل ﺍﻟﺤﻘﻭل‪ ،‬ﻓﻼ ﺨﻭﻑ ﻤﻥ ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ‪.‬‬

‫ﺨﻴﺎﺭ ﺍﻟﺘﻌﺎﺭﺽ ‪:ConflictOption‬‬


‫ﺘﺤﺩﺩ ﻜﻴﻑ ﻴﺘﻡ ﺇﻨﺸﺎﺀ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ‪ UPDATE‬ﻭﺍﻟﺤﺫﻑ ‪ DELETE‬ﻟﺘﻼﻓﻲ ﻤﺸـﺎﻜل‬
‫ﺍﻟﺘﻁﺎﺒﻕ ‪ ..Concurrency Conflicts‬ﻭﺘﺄﺨﺫ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﺇﺤـﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ‬
‫‪ ConflictOption‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻴﺘﻡ ﺍﻟﺒﺤﺙ ﻋﻥ ﺍﻟﺴﺠل ﺍﻟﻤﺭﺍﺩ ﺘﺤﺩﻴﺜﻪ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪،‬‬ ‫‪CompareAll‬‬


‫ﺒﻤﻘﺎﺭﻨﺔ ﺠﻤﻴﻊ ﻗﻴﻡ ﺍﻟﺤﻘﻭل ﺍﻟﻌﺩﺩﻴـﺔ ﻭﺍﻟﻨﺼـﻴﺔ ﺍﻟﺼـﻐﻴﺭﺓ‪،‬‬ ‫‪SearchableValues‬‬
‫ﻟﻠﺘﺄﻜﺩ ﻤﻥ ﺃﻥ ﺍﻟﺴﺠل ﻟﻡ ﺘﺩﺨل ﻋﻠﻴﻪ ﺃﻴﺔ ﺘﻌﺩﻴﻼﺕ‪.‬‬
‫ﻴﺘﻡ ﺍﻟﺒﺤﺙ ﻋﻥ ﺍﻟﺴﺠل ﺍﻟﻤﺭﺍﺩ ﺘﺤﺩﻴﺜﻪ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪،‬‬ ‫‪Compare‬‬
‫‪RowVersion‬‬
‫ﺒﻤﻘﺎﺭﻨﺔ ﺤﻘل ﺍﻹﺼﺩﺍﺭ‪ ..‬ﻫﺫﺍ ﻴﺘﻁﻠﺏ ﻭﺠﻭﺩ ﺤﻘل ﻤﻥ ﺍﻟﻨﻭﻉ‬
‫‪ TimeStamp‬ﻓﻲ ﺍﻟﺠﺩﻭل‪.‬‬
‫ﻴﺘﻡ ﺍﻟﺒﺤﺙ ﻋﻥ ﺍﻟﺴﺠل ﺍﻟﻤﺭﺍﺩ ﺘﺤﺩﻴﺜﻪ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪،‬‬ ‫‪Overwrite‬‬
‫ﺒﺎﺴﺘﺨﺩﺍﻡ ﻤﻔﺘﺎﺤﻪ ﺍﻷﺴﺎﺴﻲ ﻓﻘﻁ‪ ،‬ﻭﻫﺫﺍ ﻴﻌﻨﻲ ﺃﻥ ﺍﻟﺘﻐﻴﻴـﺭﺍﺕ‬ ‫‪Changes‬‬
‫ﺍﻟﺨﺎﺼﺔ ﺒﺒﺭﻨﺎﻤﺠﻙ ﺴﻴﺘﻡ ﺤﻔﻅﻬﺎ ﻓﻲ ﺍﻟﺴـﺠل ﻟﺘﻠﻐـﻲ ﺃﻴـﺔ‬
‫ﺘﻐﻴﻴﺭﺍﺕ ﺃﺩﺨﻠﻬﺎ ﺍﻟﻤﺴﺘﺨﺩﻤﻭﻥ ﺍﻵﺨﺭﻭﻥ‪.‬‬
‫‪١٦٧‬‬
‫ﻗﻭﺱ ﺍﻟﻔﺘﺢ ‪:QuotePrefix‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﻨﺹ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻜﻘﻭﺱ ﻓﺘﺢ‪ ،‬ﻻﺴﺘﺨﺩﺍﻤﻪ ﻤﻊ ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ‬
‫ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﺴﺎﻓﺎﺕ ﺃﻭ ﺤﺭﻭﻑ ﻏﻴﺭ ﻤﻘﺒﻭﻟﺔ‪.‬‬

‫ﻗﻭﺱ ﺍﻹﻏﻼﻕ ‪:QuoteSuffix‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﻨﺹ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻜﻘﻭﺱ ﺇﻏﻼﻕ‪.‬‬
‫ﻤﻊ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺄﻟﻭﻓﺔ‪ ،‬ﻴﻜﻭﻥ ﻗﻭﺴﺎ ﺍﻟﻔﺘﺢ ﻭﺍﻹﻏﻼﻕ ] [‪.‬‬

‫ﻤﻭﻀﻊ ﺍﻟﻔﻬﺭﺱ ‪:CatalogLocation‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﻤﻭﻀﻊ ﺍﻟﺫﻱ ﺴﻴﻭﻀﻊ ﻓﻴﻪ ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻋﻨﺩ ﺘﻜـﻭﻴﻥ ﺍﻟﻤﺴـﺎﺭﺍﺕ‬
‫ﺍﻟﻜﺎﻤﻠﺔ ﻷﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﻓﻲ ﺃﻭﺍﻤـﺭ ‪ ..SQL‬ﻭﻫـﻲ ﺘﺄﺨـﺫ ﺇﺤـﺩﻯ ﻗﻴﻤﺘـﻲ ﺍﻟﻤـﺭﻗﻡ‬
‫‪ CatalogLocation‬ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫‪ Start‬ﻴﻭﻀﻊ ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺒﺩﺍﻴﺔ ﺍﻟﻤﺴﺎﺭ‪.‬‬


‫‪ End‬ﻴﻭﻀﻊ ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻨﻬﺎﻴﺔ ﺍﻟﻤﺴﺎﺭ‪.‬‬

‫ﻓﺎﺼل ﺍﻟﻔﻬﺭﺱ ‪:CatalogSeparator‬‬


‫ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﻨﺹ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻜﻔﺎﺼل ﺒﻴﻥ ﺍﺴﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﺴﻡ ﺍﻟﺠـﺩﻭل ﻋﻨـﺩ ﻜﺘﺎﺒـﺔ‬
‫ﺍﻟﻤﺴﺎﺭ ﺍﻟﻜﺎﻤل‪ ..‬ﺍﻟﻤﺄﻟﻭﻑ ﺃﻥ ﺘﺴﺘﺨﺩﻡ ﺍﻟﻨﻘﻁﺔ ‪ .‬ﻜﻔﺎﺼل‪ ،‬ﻤﺜل ‪. Books.Author‬‬

‫ﻓﺎﺼل ﺍﻟﻤﺨﻁﻁ ‪:SchemaSeparator‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﻨﺹ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻜﻔﺎﺼل ﺒﻴﻥ ﺍﺴـﻡ ﺍﻟﻤﺨﻁـﻁ ‪ Schema‬ﻭﺍﺴـﻡ ﻤﻌـﺭﻑ‬
‫‪ Identifier‬ﻤﻭﺠﻭﺩ ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﺨﻁﻁ‪ ..‬ﻭﺍﻟﻤﺄﻟﻭﻑ ﺃﻥ ﺘﺴﺘﺨﺩﻡ ﺍﻟﻨﻘﻁﺘـﺎﻥ ﺍﻟﻤﺘﻌﺎﻤـﺩﺘﺎﻥ ‪:‬‬
‫ﻜﻔﺎﺼل‪ ،‬ﻤﺜل‪.Person:CustomerName :‬‬

‫ﻜﻤﺎ ﺘﻤﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬


‫‪١٦٨‬‬
‫ﺇﻨﻌﺎﺵ ﺍﻟﻤﺨﻁﻁ ‪:RefreshSchema‬‬
‫ﺘﺤﺫﻑ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻟﺤﺫﻑ ﻭﺍﻹﺩﺭﺍﺝ ﺍﻟﺘﻲ ﻗﺎﻡ ﺒﺎﻨﻲ ﺍﻷﻭﺍﻤﺭ ﺒﺒﻨﺎﺌﻬﺎ‪ ..‬ﻫﺫﺍ ﻀﺭﻭﺭﻱ‬
‫ﺇﺫﺍ ﺃﺭﺩﺕ ﺃﻥ ﺘﺠﻌل ﺒﺎﻨﻲ ﺍﻷﻭﺍﻤﺭ ﻴﻨﻌﺵ ﻤﻌﻠﻭﻤﺎﺘﻪ ﻋﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺤﻴـﺙ ﺇﻥ ﺒـﺎﻨﻲ‬
‫ﺍﻷﻭﺍﻤﺭ ﻴﺒﻨﻲ ﺃﻭﺍﻤﺭﻩ ﺒﻌﺩ ﺃﻭل ﻋﻤﻠﻴﺔ ﺘﺤﺩﻴﺙ ﻟﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻴﺴﺘﺨﺩﻤﻬﺎ ﻜﻤﺎ ﻫﻲ ﺒﻌـﺩ‬
‫ﻫﺫﺍ‪ ..‬ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺇﺫﺍ ﻗﻤﺕ ﺒﺘﻐﻴﻴـﺭ ﺍﺴـﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴـﺩ ﺃﻭ ﻭﻗـﺕ‬
‫ﺍﻻﻨﺘﻅﺎﺭ ‪ CommandTimeout‬ﺃﻭ ﻜﺎﺌﻥ ﺍﻟﺘﻌﺎﻤﻼﺕ ‪ Transaction‬ﺍﻟـﺫﻱ ﻴﺴـﺘﺨﺩﻤﻪ‬
‫ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ‪ ،‬ﻟﻴﻌﻴﺩ ﺒﺎﻨﻲ ﺍﻷﻭﺍﻤﺭ ﺇﻨﺸﺎﺀ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻟﺤﺫﻑ ﻭﺍﻹﺩﺭﺍﺝ ﻟـﺘﻼﺌﻡ ﻫـﺫﻩ‬
‫ﺍﻟﺘﻐﻴﻴﺭﺍﺕ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ‪:GetUpdateCommand‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ‪ DbCommand‬ﺍﻟﺫﻱ ﺘﻡ ﺇﻨﺘﺎﺠﻪ ﻟﺘﺤﺩﻴﺙ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﻫﻨﺎﻙ ﺼﻴﻐﺔ ﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻤﻨﻁﻘﻴﺎ‪ ،‬ﺇﺫﺍ ﺠﻌﻠﺘﻪ ‪ ،False‬ﻓﺴﻴﺴـﺘﺨﺩﻡ‬
‫ﺒــﺎﻨﻲ ﺍﻷﻭﺍﻤــﺭ ﻓــﻲ ﺃﻤــﺭ ﺍﻟﺘﺤــﺩﻴﺙ‪ ،‬ﻤﻌــﺎﻤﻼﺕ ﻟﻬــﺎ ﺍﻷﺴــﻤﺎﺀ ‪ P1‬ﻭ ‪P2‬‬
‫ﻭ ‪ P3‬ﻭﻫﻜﺫﺍ‪) ...‬ﻤﺜل ‪ ..(SET Author = @P1‬ﻭﻫﺫﻩ ﻫﻲ ﺍﻟﺤﺎﻟﺔ ﺍﻻﻓﺘﺭﺍﻀـﻴﺔ ﻓـﻲ‬
‫ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪.‬‬
‫ﺃﻤﺎ ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ‪ ،True‬ﻓﺴﻴﺘﻡ ﺇﻨﺘﺎﺝ ﻤﻌﺎﻤﻼﺕ ﻟﻬﺎ ﻨﻔﺱ ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ‪،‬‬
‫ﻜﻠﻤﺎ ﻜﺎﻥ ﻫﺫﺍ ﻤﻤﻜﻨﺎ )ﻤﺜل ‪ ..(SET Author = @Author‬ﻻﺤﻅ ﺃﻥ ﻤﺤﺎﻭﻟـﺔ ﺇﻨﺘـﺎﺝ‬
‫ﻫﺫﻩ ﺍﻷﺴﻤﺎﺀ ﺴﺘﺴﺒﺏ ﺨﻁـﺄ ﺇﻻ ﺇﺫﺍ ﺠﻌﻠـﺕ ﻜـﺎﺌﻥ ‪DbMetaDataColumnNames‬‬
‫ﻴﻠﺘﺯﻡ ﺒﺎﻟﺸﺭﻭﻁ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﺘﺤﺩﻴﺩ ﺃﻗﺼﻰ ﻁـﻭل ﻤﻤﻜـﻥ ﻷﺴـﻤﺎﺀ ﺍﻟﻤﻌـﺎﻤﻼﺕ‪ ،‬ﻤـﻥ ﺨـﻼل ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪.ParameterNameMaxLength‬‬
‫‪ -٢‬ﺘﻭﻀــﻴﺢ ﺼــﻴﻐﺔ ﺃﺴــﻤﺎﺀ ﺍﻟﻤﻌــﺎﻤﻼﺕ‪ ،‬ﻤــﻥ ﺨــﻼل ﺍﻟﺨﺎﺼــﻴﺔ‬
‫‪.ParameterNamePattern‬‬
‫‪ -٣‬ﺘﺤﺩﻴــﺩ ﺘﻨﺴــﻴﻕ ﺍﻟﻌﻼﻤــﺔ ﺍﻟﻤﻤﻴــﺯﺓ ﻟﻠﻤﻌﺎﻤــل‪ ،‬ﻤــﻥ ﺨــﻼل ﺍﻟﺨﺎﺼــﻴﺔ‬
‫‪.ParameterMarkerFormat‬‬

‫‪١٦٩‬‬
‫ﻤﻌﺭﻓﺔ ﺃﻤﺭ ﺍﻹﺩﺭﺍﺝ ‪:GetInsertCommand‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ‪ DbCommand‬ﺍﻟﺫﻱ ﺘﻡ ﺇﻨﺘﺎﺠﻪ ﻹﺩﺭﺍﺝ ﺼـﻑ ﺠﺩﻴـﺩ ﻓـﻲ ﻗﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻫﻲ ﻤﻤﺎﺜﻠﺔ ﻓﻲ ﺼﻴﻐﺘﻴﻬﺎ ﻟﻠﻭﺴﻴﻠﺔ ‪.GetUpdateCommand‬‬

‫ﻤﻌﺭﻓﺔ ﺃﻤﺭ ﺍﻟﺤﺫﻑ ‪:GetDeleteCommand‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ‪ DbCommand‬ﺍﻟﺫﻱ ﺘﻡ ﺇﻨﺘﺎﺠﻪ ﻟﺤﺫﻑ ﺼﻑ ﻤﻥ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪..‬‬
‫ﻭﻫﻲ ﻤﻤﺎﺜﻠﺔ ﻓﻲ ﺼﻴﻐﺘﻴﻬﺎ ﻟﻠﻭﺴﻴﻠﺔ ‪.GetUpdateCommand‬‬

‫ﺘﻘﻭﻴﺱ ﺍﻟﻤﻌﺭﻑ ‪:QuoteIdentifier‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻨﺼﺎ ﻴﻤﺜل ﻤﺴﺎﺭﺍ ﻜﺎﻤﻼ ﻷﺤﺩ ﻋﻨﺎﺼﺭ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ )ﻤﺜـل‬
‫‪ ،(Books.Author.ID‬ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻨﻔﺱ ﺍﻟﻤﺴﺎﺭ ﺒﻌﺩ ﻭﻀـﻊ ﻜـل ﺃﺴـﻤﺎﺀ ﺍﻟﻌﻨﺎﺼـﺭ‬
‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻴﻪ ﺒﻴﻥ ﻗﻭﺴﻴﻥ )ﻤﺜـل ]‪ ..([Books].[Author].[ID‬ﻻﺤـﻅ ﺃﻥ ﺍﻟﻤﻌـﺭﻑ‬
‫ﺍﻟﻤﺤﺎﻁ ﺒﻘﻭﺴﻴﻥ ﻓﻌﻼ ﺴﻴﺘﻡ ﺘﺠﺎﻫﻠﻪ‪.‬‬

‫ﺇﺯﺍﻟﺔ ﺘﻘﻭﻴﺱ ﺍﻟﻤﻌﺭﻑ ‪:UnquoteIdentifier‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻨﺼﺎ ﻴﻤﺜل ﻤﺴﺎﺭﺍ ﻤﻘﻭﺴﺎ ﻷﺤﺩ ﻋﻨﺎﺼﺭ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ )ﻤﺜـل‬
‫]‪ ،([Books].[Author].[ID‬ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻨﻔﺱ ﺍﻟﻤﺴﺎﺭ ﺒﻌﺩ ﺇﺯﺍﻟﺔ ﺠﻤﻴﻊ ﺍﻷﻗـﻭﺍﺱ ﻤﻨـﻪ‬
‫)ﻤﺜل ‪.(Books.Author.ID‬‬

‫ﻭﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪:DbCommandBuilder‬‬


‫‪.OdbcCommandBuilder Class .١‬‬
‫‪.OleDbCommandBuilder Class .٢‬‬
‫‪.OracleCommandBuilder Class .٣‬‬
‫‪.SqlCommandBuilder Class .٤‬‬

‫ﻭﺴﻨﺘﻌﺭﻑ ﻫﻨﺎ ﻓﻘﻁ ﻋﻠﻰ ﺍﻟﻔﺌﺔ ‪.SqlCommandBuilder‬‬

‫‪١٧٠‬‬
‫ﻓﺌﺔ ﺒﺎﻨﻲ ﺃﻭﺍﻤﺭ ﺴﻴﻜﻭﻴل‬
‫‪SqlCommandBuilder Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ ،DbCommandBuilder‬ﻭﻫﻲ ﺘﻤﺘﻠﻙ ﻨﻔﺱ ﺨﺼﺎﺌﺼﻬﺎ ﻭﻭﺴﺎﺌﻠﻬﺎ‪ ،‬ﻤﻊ‬
‫ﻓﺎﺭﻕ ﺒﺴﻴﻁ ﺃﻨﻬﺎ ﻤﺨﺼﺼﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻭﺃﻭﺍﻤﺭﻩ ‪.SqlCommand‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺼﻴﻐﺘﺎﻥ‪:‬‬
‫‪ -١‬ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ SqlDataAdapter‬ﺍﻟﺫﻱ ﺴﻴﺭﺘﺒﻁ ﺒﻪ ﺒﺎﻨﻲ ﺍﻷﻭﺍﻤﺭ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺠﺩﻴﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﺸﺘﻘﺎﻕ ﺍﻟﻤﻌﺎﻤﻼﺕ ‪:DeriveParameters‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺃﻤﺭ ‪ SqlCommand‬ﻤﺠﻬﺯ ﻟﺘﻨﻔﻴﺫ ﺇﺠﺭﺍﺀ ﻤﺨﺯﻥ‪ ،‬ﻟﺘﻘـﻭﻡ‬
‫ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺒﺎﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺍﻟﺤﺼﻭل ﻋﻠـﻰ ﻤﻌﻠﻭﻤـﺎﺕ ﻋـﻥ ﻤﻌـﺎﻤﻼﺕ‬
‫ﺍﻹﺠﺭﺍﺀ ﺍﻟﻤﺨﺯﻥ‪ ،‬ﻭﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻹﻀﺎﻓﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﻨﺎﺴﺒﺔ ﻏﻠﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﻌـﺎﻤﻼﺕ‬
‫‪ Parameters Collection‬ﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻷﻤﺭ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺨﻁﺄ ﺴﻴﺤﺩﺙ ﻟﻭ ﺃﺭﺴﻠﺕ‬
‫ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺃﻤﺭ ﻴﺘﻌﺎﻤل ﻤﻊ ﺍﺴﺘﻌﻼﻡ ‪ SQL‬ﺃﻭ ﻴﺤﺘﻭﻱ ﻋﻠـﻰ ﺍﺴـﻡ ﺇﺠـﺭﺍﺀ‬
‫ﻤﺨﺯﻥ ﻏﻴﺭ ﺼﺤﻴﺢ‪.‬‬
‫ﻭﻴﻌﻴﺏ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺃﻨﻬﺎ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺍﻻﺘﺼﺎل ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤـﺭﺓ ﺇﻀـﺎﻓﻴﺔ ﻹﺤﻀـﺎﺭ‬
‫ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻌﺎﻤﻼﺕ‪ ،‬ﻷﻨﻙ ﺒﺎﻟﺘﺄﻜﻴﺩ ﺴﺘﺘﺼل ﻤﺭﺓ ﺜﺎﻨﻴﺔ ﻟﺘﻨﻔﻴﺫ ﺍﻷﻤﺭ‪.‬‬

‫ﻭﺍﻟﻤﺸﺭﻭﻉ ‪ CommandBuilder‬ﻴﺭﻴﻙ ﻤﺜﺎﻻ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻹﻨﺘﺎﺝ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ‬
‫ﻭﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺤﺫﻑ‪.‬‬

‫‪١٧١‬‬
‫ﺨﺭﺍﺌﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:Data Mapping‬‬
‫ﻴﺘﻴﺢ ﻟﻙ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻤل ﺨﺭﺍﻁ ﻟﻠﺠـﺩﺍﻭل ‪ ،Table Mapping‬ﻭﺫﻟـﻙ ﺒﺈﻋـﺎﺩﺓ ﺘﺴـﻤﻴﺔ‬
‫ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺒﺄﺴﻤﺎﺀ ﺨﺎﺼﺔ ﺒﻙ‪ ،‬ﻭﺭﺒﻁﻬﺎ ﺒﺎﻷﺴﻤﺎﺀ ﺍﻟﺤﻘﻴﻘﻴﺔ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ..‬ﻫـﺫﺍ‬
‫ﻴﺤﻘﻕ ﻟﻙ ﺍﻟﻔﻭﺍﺌﺩ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﺘﻐﻴﻴﺭ ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﺃﻭ ﺍﻷﻋﻤﺩﺓ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﻤﺎ ﻴﻨﺎﺴﺏ ﺒﺭﻨﺎﻤﺠـﻙ‪ ،‬ﺩﻭﻥ‬
‫ﺍﻟﻌﺒﺙ ﺒﻘﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﺼﻠﻴﺔ‪.‬‬
‫‪ -٢‬ﻋﺭﺽ ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺒﺎﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺘﻨﺎﺴﺒﻙ‪.‬‬
‫‪ -٣‬ﺇﺫﺍ ﻜﺎﻥ ﻟﺩﻴﻙ ﺠﺩﻭﻻﻥ ﻟﻬﻤﺎ ﻨﻔﺱ ﺍﻻﺴﻡ ﻓﻲ ﻗﺎﻋﺩﺘﻲ ﺒﻴﺎﻨﺎﺕ ﻤﺨﺘﻠﻔﺘﻴﻥ‪ ،‬ﻓـﺈﻥ ﺒﺈﻤﻜﺎﻨـﻙ‬
‫ﺇﻀﺎﻓﺘﻬﻤﺎ ﺇﻟﻰ ﻨﻔﺱ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺫﻟﻙ ﺒﺘﻐﻴﻴﺭ ﺍﺴﻤﻴﻬﻤﺎ ﻤـﻥ ﺨـﻼل ﺨﺭﻴﻁـﺔ‬
‫ﺍﻟﺠﺩﻭل ﺍﻟﺨﺎﺼﺔ ﺒﻜل ﻤﻨﻬﻤﺎ‪.‬‬

‫ﻭﺘﻨﻘﺴﻡ ﺨﺭﺍﺌﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻨﻭﻋﻴﻥ‪:‬‬

‫‪ -١‬ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل ‪:Table Mapping‬‬


‫ﺤﻴﺙ ﺘﻜﻭﻥ ﻟﻜل ﺠﺩﻭل ﺨﺭﻴﻁﺔ‪ ،‬ﻴﺫﻜﺭ ﻓﻴﻬﺎ ﺍﺴﻤﻪ ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﺴـﻤﻪ‬
‫ﺍﻟﺠﺩﻴﺩ ﻓﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ..‬ﻭﺘﻭﻀـﻊ ﺨـﺭﺍﺌﻁ ﺍﻟﺠـﺩﺍﻭل ﻓـﻲ ﺍﻟﻤﺠﻤﻭﻋـﺔ‬
‫‪ TableMappings‬ﻓﻲ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﻫﻨﺎﻙ ﻨﻘﻁﺔ ﻫﺎﻤﺔ ﻴﺠﺏ ﺃﻥ ﺘﻨﺘﺒﻪ ﺇﻟﻴﻬﺎ ﻋﻨﺩ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﺨـﺭﺍﺌﻁ‪ ،‬ﻫـﻲ ﺍﻥ ﻤﻬﻴـﺊ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﻌﻁﻲ ﺃﺴﻤﺎﺀ ﺍﻓﺘﺭﺍﻀﻴﺔ ﻟﻠﺠﺩﺍﻭل‪ ،‬ﺘﺨﺘﻠﻑ ﻋـﻥ ﺃﺴـﻤﺎﺌﻬﺎ ﺍﻷﺼـﻠﻴﺔ )ﻤﺜـل‬
‫‪ Table‬ﻭ ‪ ...Table1‬ﺇﻟﺦ(‪ ..‬ﻫﺫﺍ ﻗﺩ ﻴﺴﺒﺏ ﻟﻙ ﺍﺭﺘﺒﺎﻜـﺎ ﻭﺃﻨـﺕ ﺘﻨﺸـﺊ ﺨـﺭﺍﺌﻁ‬
‫ﺍﻟﺠﺩﺍﻭل‪ ،‬ﻓﺴﺘﺒﺩﻭ ﻟﻙ ﻭﻅﻴﻔﺘﻬﺎ ﻋﻜﺴﻴﺔ‪ ،‬ﻓﺒﺩﻻ ﻤﻥ ﺃﻥ ﺘﻌﻁﻲ ﺍﻟﺠﺩﻭل ﺍﻷﺼـﻠﻲ ﺍﺴـﻤﺎ‬
‫ﺠﺩﻴﺩﺍ‪ ،‬ﺴﺘﺤﺎﻭل ﺃﻥ ﺘﻌﻴﺩ ﺘﺴﻤﻴﺔ ﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺍﻟﺨﺎﺹ ﺒﻤﻬﻴـﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺒﺎﺴـﻡ‬
‫ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ!‬
‫ﻟﻜﻥ ﺒﻘﻠﻴل ﻤﻥ ﺍﻟﺘﺄﻤل‪ ،‬ﺴﺘﻔﻬﻡ ﻟﻤﺎﺫﺍ ﻴﻔﻌل ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻫﺫﺍ‪ ..‬ﻓﺎﺴﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴﺩ ﻓـﻲ‬
‫ﻤﻌﻅﻡ ﺍﻟﺤﺎﻻﺕ ﻻ ﻴﻌﻴﺩ ﺠﺩﻭﻻ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺒل ﻗﺩ ﻴﻌﻴﺩ ﺃﺠﺯﺍﺀ ﻤﻥ ﻋﺩﺓ ﺠﺩﺍﻭل‬

‫‪١٧٢‬‬
‫)ﻜﻤﺎ ﻓﻲ ﺤﺎﻟﺔ ﺍﻟﺭﺒﻁ ‪ (Joining‬ﺃﻭ ﻗﺩ ﻴﻌﻴﺩ ﻨﺘﺎﺌﺞ ﻤﺤﺴﻭﺒﺔ ﻤﻥ ﺠﺩﻭل ﺃﻭ ﺃﻜﺜﺭ )ﻜﻤـﺎ‬
‫ﻓﻲ ﺤﺎﻟﺔ ﺍﻟﺘﺠﻤﻴﻊ ‪ ..(Aggregation‬ﻟﻬﺫﺍ ﻴﺭﻴﺢ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻨﻔﺴﻪ ﻤﻥ ﻜـل ﻫـﺫﻩ‬
‫ﺍﻻﺤﺘﻤﺎﻻﺕ ﺍﻟﻤﻌﻘﺩﺓ‪ ،‬ﻭﻴﺴﻤﻲ ﺍﻟﺠﺩﺍﻭل ﺍﻟﻨﺎﺘﺠﺔ ﻤﻥ ﺍﻻﺴـﺘﻌﻼﻡ ﺒﺄﺴـﻤﺎﺀ ﺍﻓﺘﺭﺍﻀـﻴﺔ‪،‬‬
‫ﻭﻴﺘﺭﻙ ﻟﻙ ﺤﺭﻴﺔ ﺇﻨﺸﺎﺀ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﺍﻭل ﺍﻟﺘﻲ ﺘﺼﺤﺢ ﻓﻴﻬﺎ ﺍﻷﺴﻤﺎﺀ ﺒﻁﺭﻴﻘﺘﻙ‪.‬‬
‫ﻭﺍﻟﻤﺸﺭﻭﻉ ‪ Mapping‬ﻴﺭﻴﻙ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫﺫﺍ‪ ..‬ﻓﻨﺤﻥ ﻨﺴﺘﺨﺩﻡ ﺍﺴﺘﻌﻼﻡ ﺭﺒـﻁ ﻴﻌﻴـﺩ‬
‫ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﻜﺘﺒﻬﻡ‪ ..‬ﻨﺘﻴﺠﺔ ﻫﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ ﺴﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺠﺩﻭل ﻤﺨﻠﻕ‪ ،‬ﺴﻴﻌﻁﻴﻪ ﻤﻬﻴـﺊ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ ‪ ،Table‬ﻟﻬﺫﺍ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل ﻹﻋﺎﺩﺓ ﺘﺴـﻤﻴﺘﻪ‬
‫‪.Authors-Books‬‬

‫‪ -٢‬ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ ‪:Column Mapping‬‬


‫ﺤﻴﺙ ﺘﻜﻭﻥ ﻟﻜل ﻋﻤﻭﺩ ﺨﺭﻴﻁﺔ‪ ،‬ﻴﺫﻜﺭ ﻓﻴﻬﺎ ﺍﺴﻤﻪ ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﺴـﻤﻪ‬
‫ﺍﻟﺠﺩﻴﺩ ﻓﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ..‬ﻭﺘﻭﻀـﻊ ﺨـﺭﺍﺌﻁ ﺍﻷﻋﻤـﺩﺓ ﻓـﻲ ﺍﻟﻤﺠﻤﻭﻋـﺔ‬
‫‪ ColumnMappings‬ﻓﻲ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﺘﻨﺘﻤﻲ ﺇﻟﻴﻪ‪.‬‬
‫ﻭﻻ ﻴﺤﺘﺎﺝ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﺘﺴﻤﻴﺔ ﺍﻷﻋﻤﺩﺓ ﺒﺄﺴﻤﺎﺀ ﺍﻓﺘﺭﺍﻀﻴﺔ‪ ،‬ﻟﺴﺒﺏ ﺒﺴﻴﻁ‪ :‬ﻫﻭ ﺃﻥ‬
‫ﻜل ﻋﻤﻭﺩ ﻴﺘﻡ ﺫﻜﺭﻩ ﺼﺭﺍﺤﺔ ﻓﻲ ﺍﺴﺘﻌﻼﻡ ﺍﻟﺘﺤﺩﻴﺩ‪ ،‬ﻭﺤﺘﻰ ﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﻭﻟﺩﺓ )ﺍﻷﻋﻤـﺩﺓ‬
‫ﺍﻟﻤﺤﺴﻭﺒﺔ( ﻴﺘﻡ ﺘﺴﻤﻴﺘﻬﺎ ﺇﺠﺒﺎﺭﻴﺎ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻔﻘﺭﺓ ‪ ،As‬ﻟﻬﺫﺍ ﻓﺈﻥ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﻌﺭﻑ‬
‫ﻴﻘﻴﻨﺎ ﺍﺴﻡ ﻜل ﻋﻤﻭﺩ ﻓﻲ ﺍﻟﻨﺘﻴﺠﺔ‪ ..‬ﻭﻻ ﻴﺘﺩﺨل ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻹﻋﺎﺩﺓ ﺘﺴﻤﻴﺔ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﺇﻻ‬
‫ﻓﻲ ﺤﺎﻟﺔ ﻭﺠﻭﺩ ﻋﻤﻭﺩﻴﻥ ﺒﻨﻔﺱ ﺍﻻﺴﻡ )ﻴﻤﻜﻥ ﺃﻥ ﻴﺤﺩﺙ ﻫﺫﺍ ﻟﻭ ﻜﻨﺕ ﺘﺴﺘﺨﺩﻡ ﺃﻜﺜﺭ ﻤﻥ‬
‫ﺠﻤﻠﺔ ‪ SELECT‬ﻓﻲ ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ﻤﺜﻼ(‪.‬‬
‫ﻭﺃﻫﻡ ﺍﺴﺘﺨﺩﺍﻡ ﻟﺨﺭﺍﺌﻁ ﺍﻷﻋﻤﺩﺓ‪ ،‬ﻫﻭ ﺇﻋﺎﺩﺓ ﺘﺴﻤﻴﺔ ﺍﻷﻋﻤﺩﺓ ﺒﻁﺭﻴﻘﺔ ﺘﺼـﻠﺢ ﻟﻌﺭﻀـﻬﺎ‬
‫ﻟﻠﻤﺴﺘﺨﺩﻡ‪ ..‬ﻭﺍﻟﻤﺸﺭﻭﻉ ‪ Mapping‬ﻴﺭﻴﻙ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫﺫﺍ‪ ،‬ﺤﻴﺙ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺨﺭﻴﻁـﺔ‬
‫ﺍﻷﻋﻤﺩﺓ ﻹﻋﺎﺩﺓ ﺘﺴﻤﻴﺔ ﺍﻟﻌﻤﻭﺩ ‪ Author‬ﺒﺎﻻﺴﻡ "ﺍﻟﻤﺅﻟﻑ"‪ ،‬ﻭﺍﻟﻌﻤﻭﺩ ‪ Books‬ﺒﺎﻻﺴـﻡ‬
‫"ﺍﻟﻜﺘﺎﺏ"‪ ..‬ﻫﺫﺍﻥ ﺍﻻﺴﻤﺎﻥ ﺴﻴﻅﻬﺭﺍﻥ ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻭﻫـﺫﺍ ﻤﻨﺎﺴـﺏ ﻟﻠﻤﺴـﺘﺨﺩﻡ‬
‫ﺍﻟﻌﺭﺒﻲ ﻟﻠﺒﺭﻨﺎﻤﺞ‪.‬‬

‫‪١٧٣‬‬
‫ﻻﺤﻅ ﺃﻨﻙ ﺒﻌﺩ ﻋﻤل ﺨﺭﺍﺌﻁ ﺍﻟﺭﺒﻁ‪ ،‬ﺴﺘﺴﺘﺨﺩﻡ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻟﺠﺩﻴﺩ ﻭﺍﺴﻤﻲ ﺍﻟﻌﻤﻭﺩﻴﻥ ﺍﻟﻌﺭﺒﻴﻴﻥ‬
‫ﻓﻲ ﺍﻟﻜﻭﺩ ﻋﻨﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻬﻤﺎ ﻤﻥ ﺨﻼل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻤﺜﻼ‪:‬‬
‫;]"‪var T = Ds.Tables["Authors-Books‬‬
‫;)) (‪"].MaxLength.ToString‬ﺍﻟﻤﺅﻟﻑ"[‪MeesageBox.Show(T.Columns‬‬
‫ﻓﻲ ﺍﻟﺤﻘﻴﻘﺔ ﻫﻨﺎﻙ ﺤل ﺁﺨﺭ ﻟﻌﺭﺽ ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﺒﺄﺴﻤﺎﺀ ﻋﺭﺒﻴـﺔ ﺩﻭﻥ ﺍﺴـﺘﺨﺩﺍﻡ ﺨﺭﻴﻁـﺔ‬
‫ﺍﻷﻋﻤﺩﺓ‪ ،‬ﻭﺫﻟﻙ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺨﺼﺎﺌﺹ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻨﻔﺴﻪ ﻹﻋﺎﺩﺓ ﺘﺴﻤﻴﺔ ﻋﻨﻭﺍﻥ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﻜﻤـﺎ‬
‫ﺴﻨﺭﻯ ﻓﻴﻬﺎ ﺒﻌﺩ‪.‬‬

‫ﻭﺍﻵﻥ‪ ،‬ﻓﻠﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ‪ ،‬ﻭﻜﻴﻔﻴﺔ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ‪ ..‬ﻭﺴـﻨﺘﻔﻕ ﻫﻨـﺎ ﻋﻠـﻰ‬
‫ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺘﻌﺒﻴﺭ "ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ" ﻟﻺﺸﺎﺭﺓ ﺇﻟﻰ ﺍﻻﺴﻡ ﺍﻟـﺫﻱ ﻴﻤﻨﺤـﻪ ﻤﻬﻴـﺊ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﻟﻠﺠﺩﻭل‪ ،‬ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﻫﺫﺍ ﺍﻻﺴﻡ ﺤﺴﺎﺱ ﻟﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ‪ ..‬ﻜﻤﺎ ﺴﻨﺴـﺘﺨﺩﻡ ﺍﻟﺘﻌﺒﻴـﺭ "ﺍﺴـﻡ‬
‫ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ" ﻟﻺﺸﺎﺭﺓ ﺇﻟﻰ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺃﻭ ﺍﻻﺴﻡ ﺍﻟﺒﺩﻴل ﺍﻟﺫﻱ ﺴﻤﺎﻩ ﺒـﻪ‬
‫ﻤﻬﻴﺌﺎ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻥ ﺤﺩﺙ ﺘﻌﺎﺭﺽ ﺒﻴﻥ ﻋﻤﻭﺩﻴﻥ‪ ،‬ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﻫﺫﺍ ﺍﻻﺴﻡ ﻏﻴﺭ ﺤﺴﺎﺱ ﻟﺤﺎﻟﺔ‬
‫ﺍﻷﺤﺭﻑ‪.‬‬

‫‪١٧٤‬‬
‫ﻭﺍﺠﻬﺔ ﻤﺠﻤﻭﻋﺔ ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل‬
‫‪ITableMappingCollection Interface‬‬

‫ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺘﺭﺙ ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ‪ ،IList‬ﻭﻫﻲ ﻗﺎﺌﻤﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل‪.‬‬
‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ﻤﻥ ﻋﻨﺎﺼﺭ‪ ،‬ﺘﻤﺘﻠﻙ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻭﺤﻴﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﺨﺭﻴﻁﺔ ﻤﻥ ﺠﺩﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:GetByDataSetTable‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺠﺩﻭل ﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ،DataSet‬ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ‬
‫ﻜﺎﺌﻨﺎ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،ITableMapping‬ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺨﺭﻴﻁﺔ ﻫـﺫﺍ ﺍﻟﺠـﺩﻭل‪ ..‬ﻭﻤـﻥ‬
‫ﺍﻟﻤﺘﻭﻗﻊ ﺃﻥ ﻴﻜﻭﻥ ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ ﻤﻥ ﻨﻭﻉ ﺍﻟﻔﺌﺔ ‪ DataTableMapping‬ﺍﻟﺘـﻲ ﺴـﻨﺘﻌﺭﻑ‬
‫ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ‪.‬‬

‫ﻜﻤﺎ ﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺼﻴﻐﺔ ﺃﺨﺭﻯ ﻟﺒﻌﺽ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺘﻘﻠﻴﺩﻴﺔ‪ ،‬ﻤﺜل‪:‬‬

‫ﺍﻟﻌﻨﺼﺭ ‪:Item‬‬
‫ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﺴﻡ ﺍﻟﺠـﺩﻭل ﺍﻷﺼـﻠﻲ )ﻭﻫـﻭ ﺤﺴـﺎﺱ ﻟﺤﺎﻟـﺔ‬
‫ﺍﻷﺤﺭﻑ(‪ ،‬ﻭﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ‪ Object‬ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺨﺭﻴﻁﺔ ﻫـﺫﺍ ﺍﻟﺠـﺩﻭل ﺇﻥ ﻭﺠـﺩﺕ ﻓـﻲ‬
‫ﺍﻟﻘﺎﺌﻤﺔ‪ ،‬ﻭﺇﻥ ﻟﻡ ﺘﻭﺠﺩ ﻓﺴﻴﺤﺩﺙ ﺨﻁﺄ‪.‬‬
‫ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻟﺘﻐﻴﻴﺭ ﻜﺎﺌﻥ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل‪ ،‬ﻓﻬـﻲ ﻗﺎﺒﻠـﺔ ﻟﻠﻘـﺭﺍﺀﺓ‬
‫ﻭﻟﻠﻜﺘﺎﺒﺔ ﺃﻴﻀﺎ‪.‬‬

‫ﺇﻀﺎﻓﺔ ‪:Add‬‬
‫ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻠﻴﻥ ﻨﺼﻴﻴﻥ ‪ ،Strings‬ﺃﻭﻟﻬﺎ ﻫﻭ ﺍﺴﻡ ﺍﻟﺠـﺩﻭل‬
‫ﺍﻷﺼﻠﻲ )ﻭﻫﻭ ﺤﺴﺎﺱ ﻟﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ ‪ ،(Case-Sensitive‬ﻭﺜﺎﻨﻴﻬﻤﺎ ﻫﻭ ﺍﺴﻡ ﺍﻟﺠـﺩﻭل‬
‫ﻓﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ..‬ﻭﺘﻘـﻭﻡ ﻫـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺒﺈﻨﺸـﺎﺀ ﻜـﺎﺌﻥ ﺨﺭﻴﻁـﺔ ﺠـﺩﻭل‬
‫‪ DataTableMapping‬ﻴﻤﺜل ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺠﺩﻭﻟﻴﻥ ﻭﺘﻀﻴﻔﻪ ﺇﻟﻰ ﺍﻟﻘﺎﺌﻤﺔ‪ ،‬ﻭﺘﻌﻴﺩ ﻨﺴـﺨﺔ‬
‫ﻤﻥ ﺍﻟﻭﺍﺠﻬﺔ ‪ ITableMapping‬ﺘﺸﻴﺭ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ‪.‬‬

‫‪١٧٥‬‬
‫ﺘﺤﺘﻭﻱ ﻋﻠﻰ ‪:Contains‬‬
‫ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ‪ ،‬ﻭﺘﻌﻴﺩ ‪ True‬ﺇﺫﺍ ﻜﺎﻨﺕ ﻫﻨـﺎﻙ‬
‫ﺨﺭﻴﻁﺔ ﻟﻬﺫﺍ ﺍﻟﺠﺩﻭل ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ‪.‬‬

‫ﺭﻗﻡ ﺍﻟﻌﻨﺼﺭ ‪:IndexOf‬‬


‫ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ‪ ،‬ﻭﺘﻌﻴﺩ ﺭﻗﻡ ﺍﻟﺨﺎﻨﺔ ﺍﻟﺘﻲ ﻴﻭﺠـﺩ‬
‫ﺒﻬﺎ ﻜﺎﺌﻥ ﺨﺭﻴﻁﺔ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺇﻥ ﻭﺠﺩ‪ ،‬ﺃﻭ ﺘﻌﻴﺩ ‪ ١-‬ﺇﻥ ﻟﻡ ﺘﻭﺠﺩ ﺨﺭﻴﻁﺔ ﻟﻬﺫﺍ‬
‫ﺍﻟﺠﺩﻭل‪.‬‬

‫ﺤﺫﻑ ﻤﻥ ﻤﻭﻀﻊ ‪:RemoveAt‬‬


‫ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ‪ ،‬ﻭﺘﺒﺤﺙ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﻋﻥ ﻜـﺎﺌﻥ‬
‫ﺨﺭﻴﻁﺔ ﻫﺫﺍ ﺍﻟﺠﺩﻭل‪ ،‬ﻭﺘﺤﺫﻓﻪ ﺇﻥ ﻭﺠﺩﺘﻪ‪.‬‬

‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ﻫﺫﻩ ﺍﻟﺼﻴﻐﺔ ﺘﺒﺩﻭ ﻤﺨﺘﻠﻔﺔ ﻓﻲ ﻭﻅﻴﻔﺘﻬﺎ ﻋﻥ ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺍﻟﻤﺄﻟﻭﻓﺔ‪ ،‬ﺍﻟﺘﻲ ﺘﺴﺘﻘﺒل ﺭﻗـﻡ‬
‫ﺨﺎﻨﺔ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﻭﺘﺤﺫﻓﻬﺎ ﻹﺯﺍﻟﺔ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل ﺍﻟﻤﻭﺠﻭﺩﺓ ﺒﻬﺎ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ‪ ..‬ﻭﺇﻥ ﺸﺌﺕ‬
‫ﺭﺃﻴﻲ‪ ،‬ﻜﺎﻥ ﺍﻟﻤﻨﻁﻘﻲ ﺃﻥ ﺘﻜﻭﻥ ﻫﺫﻩ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺠﺩﻴﺩﺓ ﻫـﻲ ﺍﻟﺼـﻴﻐﺔ ﺍﻟﺜﺎﻨﻴـﺔ ﻟﻠﻭﺴـﻴﻠﺔ‬
‫‪ Remove‬ﻭﻟﻴﺱ ‪ RemoveAt‬ﻤﻨﻌﺎ ﻟﻼﻟﺘﺒﺎﺱ!!‬

‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ‬

‫‪١٧٦‬‬
‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل‬
‫‪DataTableMappingCollection Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،ITableMappingCollection‬ﻭﻫﻲ ﺘﻌﻤل ﻜﻘﺎﺌﻤـﺔ‪ ،‬ﻋﻨﺎﺼـﺭﻫﺎ‬


‫ﻤﻥ ﻨﻭﻉ ﺍﻟﻔﺌﺔ ‪ DataTableMapping‬ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﺒﻌﺩ ﻗﻠﻴل‪.‬‬
‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻭﺍﺠﻬﺔ ‪ ITableMappingCollection‬ﻭﺍﻟﻭﺍﺠﻬـﺔ ‪ IList‬ﻤـﻥ‬
‫ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺠﺩﻴﺩﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﻤﻌﺭﻓﺔ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل ‪:GetTableMappingBySchemaAction‬‬


‫ﺘﺒﺤﺙ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺨﺭﺍﺌﻁ ﻋﻥ ﺍﻟﺨﺭﻴﻁﺔ ﺍﻟﺘﻲ ﺘﺭﺒﻁ ﺒﻴﻥ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ﻭﺍﺴـﻡ‬
‫ﺍﻟﺠــﺩﻭل ﻓــﻲ ﻤﺠﻤﻭﻋــﺔ ﺍﻟﺒﻴﺎﻨــﺎﺕ‪ ،‬ﻓــﺈﻥ ﻭﺠﺩﺘــﻪ ﺘﻌﻴــﺩ ﻜﺎﺌﻨــﺎ ﻤــﻥ ﺍﻟﻨــﻭﻉ‬
‫‪ DataTableMapping‬ﻴﻤﺜل ﻫﺫﻩ ﺍﻟﺨﺭﻴﻁﺔ‪ ..‬ﻭﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﺎﻟﻴـﺔ‬
‫ﺒﺎﻟﺘﺭﺘﻴﺏ‪:‬‬
‫‪ -‬ﻤﺠﻤﻭﻋﺔ ﺨﺭﺍﺌﻁ ﺍﻟﺠﺩﺍﻭل ‪ DataTableMappingCollection‬ﺍﻟﺘﻲ ﺴﻴﺘﻡ ﺍﻟﺒﺤـﺙ‬
‫ﻓﻴﻬﺎ‪.‬‬
‫‪ -‬ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ‪.‬‬
‫‪ -‬ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -‬ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ MissingMappingAction‬ﺍﻟﺘﻲ ﺘﺤﺩﺩ ﻤﺎﺫﺍ ﺴﻴﺤﺩﺙ ﺇﺫﺍ ﻟﻡ ﺘﻜﻥ‬
‫ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل ﻤﻭﺠﻭﺩﺓ‪ ،‬ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﺘﺎﻟﻲ‪:‬‬

‫‪ Passthrough‬ﻴﺘﻡ ﺇﻨﺸﺎﺀ ﺨﺭﻴﻁﺔ ﺠﺩﻭل ﺠﺩﻴﺩﺓ‪ ،‬ﺘﺤﻤل ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻷﺼـﻠﻲ‬


‫ﺍﻟﻤﺭﺴل ﻓﻲ ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل‪ ،‬ﻭﺍﺴﻡ ﺠﺩﻭل ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺍﻟﻤﺭﺴل ﻓﻲ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ‪.‬‬
‫ﻴﺘﻡ ﺘﺠﺎﻫل ﺍﻟﺨﻁﺄ‪ ،‬ﻭﺘﻌﻴﺩ ﺍﻟﻭﺴﻴﻠﺔ ‪.Nothing‬‬ ‫‪Ignore‬‬
‫ﻴﺘﻡ ﺇﻁﻼﻕ ﺨﻁﺄ ﻤﻥ ﺍﻟﻨﻭﻉ ‪.InvalidOperationException‬‬ ‫‪Error‬‬

‫‪١٧٧‬‬
‫ﺭﻗﻡ ﺠﺩﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:IndexOfDataSetTable‬‬
‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﺘﻌﻴﺩ ﺇﻟﻴـﻙ ﺭﻗـﻡ ﺨﺭﻴﻁـﺔ‬
‫ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺨﺭﺍﺌﻁ ﺍﻟﺤﺎﻟﻴﺔ‪ ..‬ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ‪ ١-‬ﺇﺫﺍ ﻟـﻡ ﺘﻌﺜـﺭ ﻋﻠـﻰ‬
‫ﺨﺭﻴﻁﺔ ﻫﺫﺍ ﺍﻟﺠﺩﻭل‪.‬‬

‫‪١٧٨‬‬
‫ﻭﺍﺠﻬﺔ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل‬
‫‪ITableMapping Interface‬‬

‫ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﻼﺯﻤﺔ ﻟﺭﺴﻡ ﺨﺭﻴﻁﺔ ﺍﻟﺭﺒﻁ ﺒﻴﻥ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ﻭﺍﻟﺠـﺩﻭل‬
‫ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻫﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ ﻫﻲ‪:‬‬

‫ﺠﺩﻭل ﺍﻟﻤﺼﺩﺭ ‪:SourceTable‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ‪.‬‬

‫ﺠﺩﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataSetTable‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺨﺭﺍﺌﻁ ﺍﻷﻋﻤﺩﺓ ‪:ColumnMappings‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ﻴﻤﺜل ﻭﺍﺠﻬﺔ ﺨـﺭﺍﺌﻁ ﺍﻷﻋﻤـﺩﺓ ‪ ،IColumnMappingCollection‬ﻭﻫـﻭ‬
‫ﺘﺤﺩﻴﺩﺍ ﻤﻥ ﻨﻭﻉ ﺍﻟﻔﺌﺔ ‪ ،ColumnMappingCollection‬ﺍﻟﺘﻲ ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻀﻴﻑ ﺇﻟﻴﻬـﺎ‬
‫ﺨﺭﺍﺌﻁ ﺍﻟﺭﺒﻁ ﺒﻴﻥ ﺍﻷﻋﻤﺩﺓ‪.‬‬

‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ‬

‫‪١٧٩‬‬
‫ﻓﺌﺔ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل ‪DataTableMapping Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،DataTableMapping‬ﻭﻫﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻤﻌﻠﻭﻤـﺎﺕ ﺍﻟﻼﺯﻤـﺔ‬


‫ﻟﺭﺒﻁ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ‪.‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺜﻼﺙ ﺼﻴﻎ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ﻭﺍﺴﻡ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -٣‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻟﺙ‪ ،‬ﻴﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺔ ﻤﻥ ﺍﻟﻨﻭﻉ‬
‫‪ ،DataColumnMapping‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺭﺒﻁ ﺒﻴﻥ ﺃﻋﻤﺩﺓ ﺍﻟﺠﺩﻭﻟﻴﻥ‪.‬‬
‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺠﺩﻴﺩﺓ‪:‬‬

‫ﻤﻌﺭﻓﺔ ﻋﻤﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:GetDataColumn‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻋﻤﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataColumn‬ﺍﻟﺫﻱ ﻴﻤﺜـل ﺍﻟﻌﻤـﻭﺩ ﺍﻟﻤﺤـﺩﺩ ﺒﺎﻟﻤﻌـﺎﻤﻼﺕ‬
‫ﺍﻟﻤﺭﺴﻠﺔ‪ ،‬ﻭﻫﻲ ﺒﺎﻟﺘﺭﺘﻴﺏ‪:‬‬
‫‪ -‬ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ‪.‬‬
‫‪ -‬ﻜﺎﺌﻥ ﺍﻟﻨﻭﻉ ‪ Type‬ﺍﻟﺫﻱ ﻴﻤﺜل ﻨﻭﻉ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ‪.‬‬
‫‪ -‬ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ‪ DataTable‬ﺍﻟﺫﻱ ﻴﻤﺜل ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -‬ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ MissingMappingAction‬ﺍﻟﺘﻲ ﺘﺤﺩﺩ ﻤﺎﺫﺍ ﺴﻴﺤﺩﺙ ﻟﻭ ﻟﻡ ﻴـﺘﻡ‬
‫ﺍﻟﻌﺜﻭﺭ ﻋﻠﻰ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺨﺭﻴﻁﺔ ﺍﻟﺠﺩﻭل‪ ..‬ﻭﻗﺩ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻰ ﻗﻴﻡ ﻫﺫﺍ ﺍﻟﻤﺭﻗﻡ ﺴﺎﺒﻘﺎ‪.‬‬
‫‪ -‬ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ MissingSchemaAction‬ﺍﻟﺘﻲ ﺘﺤﺩﺩ ﻤﺎﺫﺍ ﺴﻴﺤﺩﺙ ﻟﻭ ﻟـﻡ ﻴـﺘﻡ‬
‫ﺍﻟﻌﺜﻭﺭ ﻋﻠﻰ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻤﺨﻁﻁ ﺍﻟﺠﺩﻭل ‪ ،Schema‬ﻭﻫﺫﻩ ﺍﻟﻘﻴﻡ ﻫﻲ‪:‬‬

‫ﻴﻀﺎﻑ ﺍﻟﻌﻤﻭﺩ ﺇﻟﻰ ﻤﺨﻁﻁ ﺍﻟﺠﺩﻭل‪.‬‬ ‫‪Add‬‬


‫‪ AddWithKey‬ﻴﻀﺎﻑ ﺍﻟﻌﻤﻭﺩ ﻭﺍﻟﻤﻔﺘـﺎﺡ ﺍﻷﺴﺎﺴـﻲ ‪ Primary Key‬ﺇﻟـﻰ‬
‫ﻤﺨﻁﻁ ﺍﻟﺠﺩﻭل‪.‬‬

‫‪١٨٠‬‬
‫ﻴﺘﻡ ﺘﺠﺎﻫل ﺍﻟﻌﻤﻭﺩ‪.‬‬ ‫‪Ignore‬‬
‫ﻴــــﺘﻡ ﺇﻁــــﻼﻕ ﺨﻁــــﺄ ﻤــــﻥ ﺍﻟﻨــــﻭﻉ‬ ‫‪Error‬‬
‫‪.InvalidOperationException‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﺠﺩﻭل ‪:GetDataTableBySchemaAction‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺠﺩﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataTable‬ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ،‬ﻭﺍﻟﻤـﺫﻜﻭﺭ‬
‫ﺍﺴﻤﻪ ﻓﻲ ﺨﺭﻴﻁﺔ ﺍﻟﺭﺒﻁ‪ ..‬ﻭﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻠﻴﻥ‪:‬‬
‫‪ -‬ﻜﺎﺌﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪.DataSet‬‬
‫‪ -‬ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ ،MissingSchemaAction‬ﺍﻟﺘﻲ ﺘﻭﻀﺢ ﺍﻟﺘﺼﺭﻑ ﺍﻟﻤﻨﺎﺴـﺏ ﺇﺫﺍ‬
‫ﻟﻡ ﻴﺘﻡ ﺍﻟﻌﺜﻭﺭ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ ‪:GetColumnMappingBySchemaAction‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ ‪ DataColumnMapping‬ﻟﻠﻌﻤﻭﺩ ﺍﻟـﺫﻱ ﺘﺭﻴﺩﺠـﻪ‪ ،‬ﻭﻫـﻲ‬
‫ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ‪:‬‬
‫‪ -‬ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ‪.‬‬
‫‪ -‬ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ MissingMappingAction‬ﺍﻟﺫﻱ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻪ ﻤﻥ ﻗﺒل‪ ،‬ﻭﺍﻟﺘـﻲ‬
‫ﺘﻭﻀﺢ ﺭﺩ ﺍﻟﻔﻌل ﺇﺫﺍ ﻟﻡ ﻴﺘﻡ ﺍﻟﻌﺜﻭﺭ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ‪.‬‬

‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ‬

‫‪١٨١‬‬
‫ﻭﺍﺠﻬﺔ ﻤﺠﻤﻭﻋﺔ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ‬
‫‪IColumnMappingCollection Interface‬‬

‫ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺘﺭﺙ ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ‪ ،IList‬ﻭﻫﻲ ﺘﻤﻠﻙ ﻭﺴﻴﻠﺔ ﻭﺤﻴﺩﺓ ﺠﺩﻴﺩﺓ‪ ،‬ﻭﻫﻲ‪:‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﺨﺭﻴﻁﺔ ﺒﻭﺍﺴﻁﺔ ﻋﻤﻭﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:GetByDataSetColumn‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻜﺎﺌﻨﺎ ﻴﻤﺜل ﻭﺍﺠﻬـﺔ‬
‫ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ ‪ IColumnMapping‬ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻤﻌﻠﻭﻤﺎﺕ ﺭﺒﻁ ﻫـﺫﺍ ﺍﻟﻌﻤـﻭﺩ‬
‫ﺒﺎﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ‪ ..‬ﻭﺴﻴﻜﻭﻥ ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ ﻤـﻥ ﻨـﻭﻉ ﺍﻟﻔﺌـﺔ ‪DataColumnMapping‬‬
‫ﺘﺤﺩﻴﺩﺍ‪.‬‬

‫ﻭﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺼﻴﻐﺔ ﺃﺨﺭﻯ ﻟﺒﻌﺽ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺘﻘﻠﻴﺩﻴﺔ‪ ،‬ﻤﺜل‪:‬‬

‫ﺍﻟﻌﻨﺼﺭ ‪:Item‬‬
‫ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ )ﻭﻫﻭ ﺤﺴﺎﺱ ﻟﺤﺎﻟﺔ ﺍﻷﺤـﺭﻑ‬
‫‪ ،(Case-Sensitive‬ﻭﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ‪ Object‬ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺨﺭﻴﻁﺔ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﺇﻥ ﻭﺠـﺩﺕ‬
‫ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ‪ ،‬ﻭﺇﻥ ﻟﻡ ﺘﻭﺠﺩ ﻓﺴﻴﺤﺩﺙ ﺨﻁﺄ‪.‬‬

‫ﺇﻀﺎﻓﺔ ‪:Add‬‬
‫ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻠﻴﻥ ﻨﺼﻴﻴﻥ ‪ ،Strings‬ﺃﻭﻟﻬﺎ ﻫﻭ ﺍﺴـﻡ ﺍﻟﻌﻤـﻭﺩ‬
‫ﺍﻷﺼﻠﻲ‪ ،‬ﻭﺜﺎﻨﻴﻬﻤﺎ ﻫﻭ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺘﻘﻭﻡ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺒﺈﻨﺸـﺎﺀ‬
‫ﻜﺎﺌﻥ ﺨﺭﻴﻁﺔ ﺃﻋﻤﺩﺓ ‪ DataColumnMapping‬ﻴﻤﺜل ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﻌﻤﻭﺩﻴﻥ ﻭﺘﻀـﻴﻔﻪ‬
‫ﺇﻟﻰ ﺍﻟﻘﺎﺌﻤﺔ‪ ،‬ﻭﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﻭﺍﺠﻬﺔ ‪ IColumnMapping‬ﺘﺸﻴﺭ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ‪.‬‬

‫‪١٨٢‬‬
‫ﺘﺤﺘﻭﻱ ﻋﻠﻰ ‪:Contains‬‬
‫ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ )ﻭﻫﻭ ﺤﺴﺎﺱ ﻟﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ(‪،‬‬
‫ﻭﺘﻌﻴﺩ ‪ True‬ﺇﺫﺍ ﻜﺎﻨﺕ ﻫﻨﺎﻙ ﺨﺭﻴﻁﺔ ﻟﻬﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ‪.‬‬

‫ﺭﻗﻡ ﺍﻟﻌﻨﺼﺭ ‪:IndexOf‬‬


‫ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ‪ ،‬ﻭﺘﻌﻴﺩ ﺭﻗﻡ ﺍﻟﺨﺎﻨﺔ ﺍﻟﺘﻲ ﻴﻭﺠـﺩ‬
‫ﺒﻬﺎ ﻜﺎﺌﻥ ﺨﺭﻴﻁﺔ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺇﻥ ﻭﺠﺩ‪ ،‬ﺃﻭ ﺘﻌﻴﺩ ‪ ١-‬ﺇﻥ ﻟﻡ ﺘﻭﺠﺩ ﺨﺭﻴﻁﺔ ﻟﻬﺫﺍ‬
‫ﺍﻟﻌﻤﻭﺩ‪.‬‬

‫ﺤﺫﻑ ﻤﻥ ﻤﻭﻀﻊ ‪:RemoveAt‬‬


‫ﺘﺴﺘﻘﺒل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ‪ ،‬ﻭﺘﺒﺤﺙ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﻋﻥ ﻜـﺎﺌﻥ‬
‫ﺨﺭﻴﻁﺔ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﻭﺘﺤﺫﻓﻪ ﺇﻥ ﻭﺠﺩﺘﻪ‪.‬‬

‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ‬

‫‪١٨٣‬‬
‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ‬
‫‪DataColumnMappingCollection Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IColumnMappingCollection‬ﻭﻫﻲ ﺘﻌﻤـل ﻜﻘﺎﺌﻤـﺔ ﺘﺤﺘـﻭﻱ‬


‫ﻋﻠﻰ ﻜﺎﺌﻨﺎﺕ ﻤﻥ ﻨﻭﻉ ﺍﻟﻔﺌﺔ ‪ ،DataColumnMapping‬ﺍﻟﺘﻲ ﺘﺭﺴﻡ ﺨﺭﺍﺌﻁ ﺍﻟﺭﺒﻁ ﺒﻴﻥ ﺃﻋﻤﺩﺓ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﻷﻋﻤﺩﺓ ﺍﻷﺼﻠﻴﺔ‪.‬‬
‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﺍﻟﻭﺍﺠﻬـﺔ ‪،IColumnMappingCollection‬‬
‫ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﻌﺭﻓﺔ ﻋﻤﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:GetDataColumn‬‬


‫ﻤﻤﺎﺜﻠﺔ ﻟﻠﻭﺴﻴﻠﺔ ‪ GetDataColumn‬ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﻔﺌﺔ ‪ ،DataTableMapping‬ﻤﻊ ﻓـﺎﺭﻕ‬
‫ﻭﺤﻴﺩ‪ ،‬ﻫﻭ ﺃﻨﻬﺎ ﻫﻨﺎ ﻭﺴﻴﻠﺔ ﻤﺸﺘﺭﻜﺔ ‪ ،Shared‬ﻟﻬﺫﺍ ﺘﻤﺘﻠﻙ ﻤﻌﺎﻤﻼ ﺯﺍﺌﺩﺍ‪ ،‬ﻫـﻭ ﺍﻟﻤﻌﺎﻤـل‬
‫ﺍﻷﻭل ﺍﻟﺫﻱ ﻴﺴﺘﻘﺒل ﻤﺠﻤﻭﻋﺔ ﺨﺭﺍﺌﻁ ﺍﻷﻋﻤﺩﺓ ‪DataColumnMappingCollection‬‬
‫ﺍﻟﺘﻲ ﺴﻴﺘﻡ ﺍﻟﺒﺤﺙ ﻓﻴﻬﺎ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ ‪:GetColumnMappingBySchemaAction‬‬


‫ـﺔ‬
‫ـﺔ ﺒﺎﻟﻔﺌـ‬
‫ـﻴﻠﺔ ‪ GetColumnMappingBySchemaAction‬ﺍﻟﺨﺎﺼـ‬
‫ـﺔ ﻟﻠﻭﺴـ‬
‫ﻤﻤﺎﺜﻠـ‬
‫‪ ،DataTableMapping‬ﻤﻊ ﻓﺎﺭﻕ ﻭﺤﻴﺩ‪ ،‬ﻫﻭ ﺃﻨﻬﺎ ﻫﻨﺎ ﻭﺴﻴﻠﺔ ﻤﺸﺘﺭﻜﺔ ‪ ،Shared‬ﻟﻬﺫﺍ‬
‫ﺘﻤﺘﻠﻙ ﻤﻌﺎﻤﻼ ﺯﺍﺌﺩﺍ‪ ،‬ﻫﻭ ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ﺍﻟـﺫﻱ ﻴﺴـﺘﻘﺒل ﻤﺠﻤﻭﻋـﺔ ﺨـﺭﺍﺌﻁ ﺍﻷﻋﻤـﺩﺓ‬
‫‪ DataColumnMappingCollection‬ﺍﻟﺘﻲ ﺴﻴﺘﻡ ﺍﻟﺒﺤﺙ ﻓﻴﻬﺎ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺭﻗﻡ ﺍﻟﻌﻤﻭﺩ ‪:IndexOfDataSetColumn‬‬


‫ﺘﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺘﻌﻴﺩ ﺭﻗﻡ ﺍﻟﺨﺎﻨﺔ ﺍﻟﺘـﻲ ﻴﻭﺠـﺩ ﺒﻬـﺎ ﻜـﺎﺌﻥ‬
‫ﺨﺭﻴﻁﺔ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺇﻥ ﻭﺠﺩ‪ ،‬ﺃﻭ ﺘﻌﻴﺩ ‪ ١-‬ﺇﻥ ﻟﻡ ﺘﻭﺠﺩ ﺨﺭﻴﻁﺔ ﻟﻬﺫﺍ ﺍﻟﻌﻤﻭﺩ‪.‬‬

‫‪١٨٤‬‬
‫ﻭﺍﺠﻬﺔ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ‬
‫‪IColumnMapping Interface‬‬

‫ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺨﺎﺼﻴﺘﻴﻥ ﻓﻘﻁ‪ ،‬ﺘﺴﺘﺨﺩﻤﺎﻥ ﻟﺭﺒﻁ ﻋﻤﻭﺩ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ،‬ﺒـﺎﻟﻌﻤﻭﺩ‬
‫ﺍﻷﺼﻠﻲ‪ ،‬ﻭﻫﻤﺎ‪:‬‬

‫ﻋﻤﻭﺩ ﺍﻟﻤﺼﺩﺭ ‪:SourceColumn‬‬


‫ﺘﺤﺩﺩ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ‪.‬‬

‫ﻋﻤﻭﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataSetColumn‬‬


‫ﺘﺤﺩﺩ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ‬

‫‪١٨٥‬‬
‫ﻓﺌﺔ ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ‬
‫‪DataColumnMapping Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬـﺔ ‪ IColumnMapping‬ﻭﺍﻟﻭﺍﺠﻬـﺔ ‪ ،ICloneable‬ﻭﻫـﻲ ﺘﺭﺴـﻡ‬


‫ﺨﺭﻴﻁﺔ ﺍﻟﺭﺒﻁ ﺒﻴﻥ ﻋﻤﻭﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ‪.‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺼﻴﻐﺘﺎﻥ‪:‬‬
‫‪ -١‬ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﻭﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺎ ﻤﻌﺎﻤﻼﻥ ﻨﺼﻴﺎﻥ‪ ،‬ﻴﺴﺘﻘﺒﻼﻥ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ ﻭﺍﺴﻡ ﻋﻤـﻭﺩ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺍﻟﺘﺭﺘﻴﺏ‪.‬‬
‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﺨﺼﺎﺌﺹ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻭﺤﻴﺩﺓ ﺍﻟﺠﺩﻴﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﻌﺭﻓﺔ ﻋﻤﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:GetDataColumnBySchemaAction‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻋﻤﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataColumn‬ﺍﻟﻤﻁﻠﻭﺏ ﺘﺒﻌﺎ ﻟﻠﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -‬ﻜﺎﺌﻥ ﺠﺩﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataTable‬ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﺍﻟﻌﻤﻭﺩ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻓـﻲ‬
‫ﺍﻟﺠﺩﻭل ﺘﺤﺩﺩﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ DataSetColumn‬ﺍﻟﺨﺎﺼﺔ ﺒﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺤﺎﻟﻴﺔ‪.‬‬
‫‪ -‬ﻜﺎﺌﻥ ﺍﻟﻨﻭﻉ ‪ Type‬ﺍﻟﺫﻱ ﻴﻤﺜل ﻨﻭﻉ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻌﻤﻭﺩ‪.‬‬
‫‪ -‬ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ ،MissingSchemaAction‬ﺘﺤﺩﺩ ﻤﺎﺫﺍ ﺴـﻴﺤﺩﺙ ﺇﻥ ﻟـﻡ ﻴﻭﺠـﺩ‬
‫ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻤﺨﻁﻁ ﺍﻟﺠﺩﻭل‪.‬‬
‫ﻭﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺃﺨﺭﻯ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﻭﻫﻲ ﺼﻴﻐﺔ ﻤﺸﺘﺭﻜﺔ ‪ ،Shared‬ﻟﻬﺫﺍ ﺘﺯﻴﺩ ﺒﻤﻌﺎﻤﻠﻴﻥ‬
‫ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ‪ ،‬ﻫﻤﺎ ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ﻭﺍﻟﺜﺎﻨﻲ‪ ،‬ﺍﻟﻠﺫﺍﻥ ﻴﺴﺘﻘﺒﻼﻥ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺍﻷﺼﻠﻲ‬
‫ﻭﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺍﻟﺘﺭﺘﻴﺏ‪.‬‬

‫ﻭﻟﻘﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪ Mapping‬ﻹﻋﺎﺩﺓ ﺘﺴﻤﻴﺔ ﺍﻟﺠﺩﻭل ﻭﻋﻤﻭﺩﻴﻪ‪:‬‬


‫(‪var TM = DaAuthors.TableMappings.Add‬‬
‫;)"‪"Table", "Authors-Books‬‬
‫;)"ﺍﻟﻤﺅﻟﻑ" ‪TM.ColumnMappings.Add("Author",‬‬
‫;)"ﺍﻟﻜﺘﺎﺏ" ‪TM.ColumnMappings.Add("Book",‬‬

‫‪١٨٦‬‬
‫‪-١٠-‬‬
‫ﻤﺼﺎﻨﻊ ﺍﻟﻤﺯﻭﺩﺍﺕ ‪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‬ﻭﻫـﻲ ﻻ ﺘﻤﺘﻠـﻙ ﺇﻻ‬
‫ﻭﺴﻴﻠﺘﻴﻥ ﻤﺸﺘﺭﻜﺘﻴﻥ‪ ،‬ﻫﻤﺎ‪:‬‬

‫ﻤﻌﺭﻓﺔ ﻓﺌﺎﺕ ﺍﻟﻤﺼﺎﻨﻊ ‪:GetFactoryClasses‬‬


‫ﺘﻌﻴﺩ ﺠﺩﻭل ﺒﻴﺎﻨﺎﺕ ‪ ،DataTable‬ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺒﻴﺎﻨﺎﺕ ﻋﻥ ﻤﺼﺎﻨﻊ ﺍﻟﻤﺯﻭﺩﺍﺕ ﺍﻟﻤﺘﺎﺤـﺔ‬
‫ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻤﺴﺘﺨﺩﻡ‪ ..‬ﻭﻴﻤﺜل ﻜل ﺼﻑ ﻓﻲ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ﺃﺤﺩ ﺍﻟﻤﺯﻭﺩﺍﺕ‪ ،‬ﺒﻴﻨﻤﺎ ﺘﻌـﺭﺽ‬
‫ﺍﻷﻋﻤﺩﺓ ﺘﻔﺎﺼﻴل ﻫﺫﺍ ﺍﻟﻤﺯﻭﺩ‪ ..‬ﻭﻫﺫﻩ ﺍﻷﻋﻤﺩﺓ ﻫﻲ‪:‬‬

‫ﺍﺴﻡ ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬ ‫‪Name‬‬


‫ﻭﺼﻑ ﻤﺨﺘﺼﺭ ﻟﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬ ‫‪Description‬‬
‫‪ InvariantName‬ﺍﻻﺴﻡ ﺍﻟﺜﺎﺒﺕ ﻟﻠﻤﺯﻭﺩ‪ ،‬ﻭﺍﻟﺫﻱ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻟﻠﺤﺼﻭل ﻋﻠـﻰ‬
‫ﺍﻟﻤﺼﻨﻊ ﺍﻟﺨﺎﺹ ﺒﻪ‪.‬‬
‫ﺍﻻﺴﻡ ﺍﻟﻜﺎﻤل ﻟﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻫﻭ ﻴﺤﺘـﻭﻱ ﻋﻠـﻰ ﺍﻟﺘﻔﺎﺼـﻴل‬ ‫‪Assembly‬‬
‫‪QualifiedName‬‬
‫ﺍﻟﻜﺎﻓﻴﺔ ﻋﻨﻪ‪ ،‬ﻤﺜل ﺍﻹﺼﺩﺍﺭ ﻭﺍﻟﺜﻘﺎﻓﺔ ﺍﻟﺘﻲ ﻴﺴﺘﺨﺩﻤﻬﺎ‪.‬‬

‫ﻭﻴﻤﻜﻨﻙ ﺭﺅﻴﺔ ﻫﺫﻩ ﺍﻟﺘﻔﺎﺼﻴل ﺒﻨﻔﺴﻙ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪ ،DataProviders‬ﻓﻬﻭ ﻴﻌﺭﺽ ﻨﺎﺘﺞ‬


‫ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺠﺩﻭل ﻋﺭﺽ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﻤﺼﻨﻊ ‪:GetFactory‬‬


‫ﺘﻌﻴﺩ ﻤﺼﻨﻊ ﺍﻟﻤﺯﻭﺩ ‪ DbProviderFactory‬ﺍﻟﺫﻱ ﻴﺘﻴﺢ ﻟﻙ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻤﺯﻭﺩ ﻤﻌـﻴﻥ‪..‬‬
‫ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺼﻴﻐﺘﺎﻥ ﺍﻟﺘﺎﻟﻴﺘﺎﻥ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﺍﻻﺴﻡ ﺍﻟﺜﺎﺒﺕ ﻟﻠﻤﺯﻭﺩ ‪.InvariantName‬‬

‫‪١٨٩‬‬
‫‪ -٢‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﺼﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataRow‬ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺘﻔﺎﺼـﻴل‬
‫ﺍﻟﻤﺯﻭﺩ‪ ..‬ﻭﻴﻤﻜﻨﻙ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﺼﻑ ﻤﻥ ﺍﻟﺠﺩﻭل ﺍﻟﻌﺎﺌﺩ ﻤـﻥ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪.GetFactoryClasses‬‬
‫ﺩﻋﻨﺎ ﺇﺫﻥ ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ ‪.DbProviderFactory‬‬

‫‪١٩٠‬‬
‫ﻓﺌﺔ ﻤﺼﻨﻊ ﺍﻟﻤﺯﻭﺩ ‪DbProviderFactory Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺴﺎﺴﻴﺔ ﻤﺠﺭﺩﺓ ﺘﺠﺏ ﻭﺭﺍﺜﺘﻬﺎ‪ ،‬ﻭﻫﻲ ﺘﻤﺘﻠﻙ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻼﺯﻤﺔ ﻟﻠﺘﻌﺎﻤـل ﻤـﻊ ﻤـﺯﻭﺩ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺨﺼﺼﺕ ﻟﻠﺘﻌﺎﻤل ﻤﻌﻪ‪.‬‬
‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻴﻤﻜﻨﻪ ﺇﻨﺸﺎﺀ ﻋﺩﺍﺩ ﻟﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:CanCreateDataSourceEnumerator‬‬


‫ﺘﻌﻴـــﺩ ‪ true‬ﺇﺫﺍ ﻜـــﺎﻥ ﻤﺼـــﻨﻊ ﺍﻟﻤـــﺯﻭﺩ ﻴﺴـــﻤﺢ ﺒﺎﺴـــﺘﺨﺩﺍﻡ ﺍﻟﻔﺌـــﺔ‬
‫‪ DbDataSourceEnumerator‬ﻟﻠﻤﺭﻭﺭ ﻋﺒـﺭ ﻜـل ﺨـﻭﺍﺩﻡ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﻤﺘﺎﺤـﺔ‪..‬‬
‫ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺒﻌﺩ ﻗﻠﻴل‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺇﻨﺸﺎﺀ ﺒﺎﻨﻲ ﻨﺹ ﺍﻻﺘﺼﺎل ‪:CreateConnectionStringBuilder‬‬


‫ﺘﻌﻴﺩ ﺒﺎﻨﻲ ﻨﺹ ﺍﻻﺘﺼﺎل ﻤﻥ ﺍﻟﻨﻭﻉ ﺍﻟﻌـﺎﻡ ‪ ،DbConnectionStringBuilder‬ﻟﻜﻨـﻪ‬
‫ﻴﻜﻭﻥ ﻤﺨﺼﺼﺎ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺃﻨﺸﺄﺕ ﺍﻟﻤﺼﻨﻊ ﺍﻟﺤﺎﻟﻲ ﻟﻠﺘﻌﺎﻤل ﻤﻌﻪ‪.‬‬

‫ﺇﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺍﺘﺼﺎل ‪:CreateConnection‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﺘﺼﺎل ﻤﻥ ﺍﻟﻨﻭﻉ ﺍﻟﻌﺎﻡ ‪ ،DbConnection‬ﻟﻜﻨﻪ ﻴﻜﻭﻥ ﻤﺨﺼﺼﺎ ﻟﻠﺘﻌﺎﻤل ﻤﻊ‬
‫ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺃﻨﺸﺄﺕ ﺍﻟﻤﺼﻨﻊ ﺍﻟﺤﺎﻟﻲ ﻟﻠﺘﻌﺎﻤل ﻤﻌﻪ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﺴﺘﺤﺼل ﻋﻠﻴﻪ ﻏﻴﺭ ﻤﺭﺘﺒﻁ ﺒﺄﻱ ﻨﺹ ﺍﺘﺼﺎل‪ ،‬ﻟﻬﺫﺍ ﻋﻠﻴـﻙ‬
‫ﻭﻀﻊ ﻨﺹ ﺍﻻﺘﺼﺎل ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ ConnectionString‬ﺍﻟﺨﺎﺼﺔ ﺒﻪ ﻗﺒل ﻤﺤﺎﻭﻟﺔ ﻓـﺘﺢ‬
‫ﺍﻻﺘﺼﺎل‪.‬‬

‫‪١٩١‬‬
‫ﺇﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺃﻤﺭ ‪:CreateCommand‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺃﻤﺭ ﻤﻥ ﺍﻟﻨﻭﻉ ﺍﻟﻌﺎﻡ ‪ ،DbCommand‬ﻟﻜﻨﻪ ﻴﻜﻭﻥ ﻤﺨﺼﺼـﺎ ﻟﻠﺘﻌﺎﻤـل ﻤـﻊ‬
‫ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺃﻨﺸﺄﺕ ﺍﻟﻤﺼﻨﻊ ﺍﻟﺤﺎﻟﻲ ﻟﻠﺘﻌﺎﻤل ﻤﻌﻪ‪ ..‬ﻭﺃﻨﺕ ﺘﻌﺭﻑ ﺃﻨـﻙ ﺘﺴـﺘﻁﻴﻊ‬
‫ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪.ExecuteReader‬‬
‫ﻻﺤﻅ ﺃﻥ ﻜﺎﺌﻥ ﺍﻷﻤﺭ ﺍﻟﺫﻱ ﺴﺘﺤﺼل ﻋﻠﻴﻪ ﻟﻴﺱ ﻤﺭﺘﺒﻁﺎ ﺒﺄﻱ ﺍﺘﺼﺎل‪ ،‬ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺭﺒﻁـﻪ‬
‫ﺒﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﺤﺼﻠﺕ ﻋﻠﻴﻪ ﻤﻥ ﺍﻟﻭﺴﻴﻠﺔ ‪ ،CreateConnection‬ﻭﻫﻭ ﻤﺎ ﻓﻌﻠﻨـﺎﻩ‬
‫ﻓﻲ ﺍﻟﺩﺍﻟﺔ ‪ CreateCommand‬ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪ Factories‬ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫;) (‪var Command = Fac.CreateCommand‬‬
‫;‪Command.Connection = Cn‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺃﺩﺍﺀ ﻨﻔﺱ ﻭﻅﻴﻔﺔ ﻫـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ‪ ،‬ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ ‪CreateCommand‬‬
‫ﺍﻟﺨﺎﺼﺔ ﺒﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل‪ ،‬ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﺘﺨﺘﺼﺭ ﺍﻟﺴﻁﺭ ﺍﻟﺜﺎﻨﻲ ﻤﻥ ﺍﻟﻜﻭﺩ ﺍﻟﺴﺎﺒﻕ‪:‬‬
‫;) (‪var Command = Cn.CreateCommand‬‬

‫ﺇﻨﺸﺎﺀ ﻤﻌﺎﻤل ‪:CreateParameter‬‬


‫ﺘﻌﻴﺩ ﻤﻌﺎﻤﻼ ﻤﻥ ﺍﻟﻨﻭﻉ ﺍﻟﻌﺎﻡ ‪ ،DbParameter‬ﻟﻜﻨﻪ ﻴﻜﻭﻥ ﻤﺨﺼﺼﺎ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻤـﺯﻭﺩ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺃﻨﺸﺄﺕ ﺍﻟﻤﺼﻨﻊ ﺍﻟﺤﺎﻟﻲ ﻟﻠﺘﻌﺎﻤل ﻤﻌﻪ‪.‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺃﺩﺍﺀ ﻨﻔﺱ ﺍﻟﻭﻅﻴﻔﺔ‪ ،‬ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ CreateParameter‬ﺍﻟﺨﺎﺼـﺔ ﺒﻜـﺎﺌﻥ‬
‫ﺍﻷﻤﺭ‪.‬‬

‫ﺇﻨﺸﺎﺀ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ‪:CreateDataAdapter‬‬


‫ﺘﻌﻴﺩ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺍﻟﻨﻭﻉ ﺍﻟﻌﺎﻡ ‪ ،DbDataAdapter‬ﻟﻜﻨﻪ ﻴﻜﻭﻥ ﻤﺨﺼﺼﺎ ﻟﻠﺘﻌﺎﻤـل‬
‫ﻤﻊ ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺃﻨﺸﺄﺕ ﺍﻟﻤﺼﻨﻊ ﺍﻟﺤﺎﻟﻲ ﻟﻠﺘﻌﺎﻤل ﻤﻌـﻪ‪ ..‬ﻭﻗـﺩ ﺍﺴـﺘﺨﺩﻤﻨﺎ ﻫـﺫﻩ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﺩﺍﻟﺔ ‪ GetTable‬ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪ Factories‬ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫;) (‪DataTable Table = new DataTable‬‬
‫;) (‪var Da = Fac.CreateDataAdapter‬‬
‫;‪Da.SelectCommand = Cmd‬‬
‫;)‪Da.Fill(Table‬‬
‫‪١٩٢‬‬
‫ﺇﻨﺸﺎﺀ ﺒﺎﻨﻲ ﺃﻭﺍﻤﺭ ‪:CreateCommandBuilder‬‬
‫ﺘﻌﻴﺩ ﺒﺎﻨﻲ ﺃﻭﺍﻤﺭ ﻤﻥ ﺍﻟﻨﻭﻉ ﺍﻟﻌﺎﻡ ‪ ،DbCommandBuilder‬ﻟﻜﻨـﻪ ﻴﻜـﻭﻥ ﻤﺨﺼﺼـﺎ‬
‫ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺃﻨﺸﺄﺕ ﺍﻟﻤﺼﻨﻊ ﺍﻟﺤﺎﻟﻲ ﻟﻠﺘﻌﺎﻤل ﻤﻌﻪ‪.‬‬

‫ﺇﻨﺸﺎﺀ ﻋﺩﺍﺩ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:CreateDataSourceEnumerator‬‬


‫ﺘﻌﻴﺩ ﻋﺩﺍﺩﺍ ﻟﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺍﻟﻨﻭﻉ ﺍﻟﻌﺎﻡ ‪ ،DbDataSourceEnumerator‬ﻟﻜﻨـﻪ‬
‫ﻴﻜﻭﻥ ﻤﺨﺼﺼﺎ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺃﻨﺸﺄﺕ ﺍﻟﻤﺼﻨﻊ ﺍﻟﺤﺎﻟﻲ ﻟﻠﺘﻌﺎﻤل ﻤﻌـﻪ‪..‬‬
‫ﻻﺤﻅ ﺃﻥ ﻤﺯﻭﺩ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻫﻭ ﺍﻟﻭﺤﻴﺩ ﺍﻟﺫﻱ ﻴﺩﻋﻡ ﻫـﺫﻩ ﺍﻹﻤﻜﺎﻨﻴـﺔ‪ ،‬ﻷﻥ ﻗﻭﺍﻋـﺩ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺨﺎﺼﺔ ﺒﻪ ﺘﻌﻤل ﻋﻠﻰ ﺨﺎﺩﻡ‪ ،‬ﻟﻬﺫﺍ ﺴﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ‪ null‬ﺇﺫﺍ ﺍﺴﺘﺨﺩﻤﺘﻬﺎ ﻤـﻊ‬
‫ﺃﻱ ﻤﺯﻭﺩ ﺒﻴﺎﻨﺎﺕ ﺁﺨﺭ ﻏﻴﺭ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ!‬
‫ﻭﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺴﺘﺨﺩﻡ ﺍﻟﺨﺎﺼـﻴﺔ ‪ CanCreateDataSourceEnumerator‬ﺃﻭﻻ ﻗﺒـل‬
‫ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﻟﺘﻌﺭﻑ ﺇﻥ ﻜﺎﻥ ﺍﻟﻤﺯﻭﺩ ﻴﺩﻋﻡ ﻋﺩﺍﺩ ﺍﻟﻤﺼﺎﺩﺭ ﺃﻡ ﻻ‪.‬‬

‫ﺇﻨﺸﺎﺀ ﺘﺼﺭﻴﺢ ‪:CreatePermission‬‬


‫ﺘﻌﻴﺩ ﺘﺼﺭﻴﺤﺎ ﻤﻥ ﺍﻟﻨﻭﻉ ﺍﻟﻌﺎﻡ ‪ ،CodeAccessPermission‬ﻟﻜﻨﻪ ﻴﻜـﻭﻥ ﻤﺨﺼﺼـﺎ‬
‫ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﻤﺯﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺃﻨﺸﺄﺕ ﺍﻟﻤﺼﻨﻊ ﺍﻟﺤﺎﻟﻲ ﻟﻠﺘﻌﺎﻤل ﻤﻌﻪ‪ ...‬ﻻﺤﻅ ﺃﻥ ﺍﻟﻔﺌـﺔ‬
‫‪ DBDataPermission‬ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ ،CodeAccessPermission‬ﻭﻤﻨﻪ ﺘﺸﺘﻕ ﻓﺌـﺎﺕ‬
‫ﺍﻟﺘﺼﺭﻴﺢ ﺍﻟﺨﺎﺼﺔ ﺒﻜل ﻤﺯﻭﺩ ﺒﻴﺎﻨﺎﺕ ﻤﺜـل ‪ ..SqlClientPermission‬ﺸـﺭﺡ ﻫـﺫﻩ‬
‫ﺍﻟﻤﻭﺍﻀﻴﻊ ﺨﺎﺭﺝ ﻨﻁﺎﻕ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ‪.‬‬

‫ﻭﺘﺭﺙ ﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ ﺍﻟﻔﺌﺔ ‪:DbProviderFactory‬‬


‫‪.OdbcFactory Class -١‬‬
‫‪.OleDbFactory Class -٢‬‬
‫‪.OracleClientFactory Class -٣‬‬
‫‪.SqlClientFactory Class -٤‬‬
‫‪١٩٣‬‬
‫ﻭﻻ ﻴﻭﺠﺩ ﺠﺩﻴﺩ ﻓﻲ ﻫﺫﻩ ﺍﻟﻔﺌﺎﺕ ﻴﺴﺘﺤﻕ ﺸﺭﺤﻪ‪ ،‬ﻓﻬﻲ ﺘﻤﻠﻙ ﻨﻔﺱ ﻭﺴﺎﺌل ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪ ،‬ﻟﻜـﻥ ﻤـﻊ‬
‫ﻓﺎﺭﻕ ﻭﺍﺤﺩ‪ :‬ﺃﻨﻬﺎ ﺘﻌﻴﺩ ﺃﻨﻭﺍﻋﺎ ﺨﺎﺼﺔ ﺒﻜل ﻤﺯﻭﺩ‪ ،‬ﺒﺩﻻ ﻤﻥ ﺍﻷﻨﻭﺍﻉ ﺍﻟﻌﺎﻤﺔ ﺍﻟﺘﻲ ﺘﻌﻴـﺩ ﻭﺴـﺎﺌل‬
‫ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪.‬‬

‫ﻭﺨﻴﺭ ﻁﺭﻴﻘﺔ ﻹﺩﺭﺍﻙ ﻋﺒﻘﺭﻴﺔ ﻤﺼﺎﻨﻊ ﺍﻟﻤﺯﻭﺩﺍﺕ‪ ،‬ﻫﻲ ﺃﻥ ﻨﻌﻴﺩ ﻜﺘﺎﺒﺔ ﺍﻟﻤﺸـﺭﻭﻉ ‪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‬‬
‫ﻭﺘﺘﻴﺢ ﻟﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤﻌﻠﻭﻤﺎﺕ ﻋﻥ ﺍﻟﺨﻭﺍﺩﻡ ﺍﻟﻤﺘﻭﻓﺭﺓ ﺤﺎﻟﻴﺎ ﻋﻠﻰ ﺍﻟﺸـﺒﻜﺔ ﺍﻟﺘـﻲ‬
‫ﻴﺘﺼل ﺒﻬﺎ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل‪.‬‬
‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻭﺤﻴﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﻌﺭﻓﺔ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:GetDataSources‬‬


‫ﺘﻌﻴﺩ ﺠﺩﻭل ﺒﻴﺎﻨﺎﺕ ‪ ،DataTable‬ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺼﻔﻭﻑ ﻓﻴﻬﺎ ﺘﻔﺎﺼﻴل ﺍﻟﺨﻭﺍﺩﻡ ﺍﻟﻤﺘﺎﺤﺔ‪..‬‬
‫ﻭﻴﻌﺭﺽ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﺴﻡ ﺨﺎﺩﻡ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬ ‫‪ServerName‬‬


‫‪ InstanceName‬ﺍﺴﻡ ﺍﻟﻨﺴﺨﺔ ﺍﻟﺘﻲ ﺘﻌﻤل ﻤﻥ ﺍﻟﺨﺎﺩﻡ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺴـﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ‬
‫ﻴﺘﻴﺢ ﺘﺸﻐﻴل ﺃﻜﺜﺭ ﻤﻥ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﺨﺎﺩﻡ‪.‬‬
‫‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺨﺎﺩﻡ ﺠﺯﺀﺍ ﻤﻥ ﺘﺠﻤﻊ ‪ Cluster‬ﻤﻥ ﺍﻟﺨﻭﺍﺩﻡ‪.‬‬ ‫‪IsClustered‬‬
‫ﺇﺼﺩﺍﺭ ﺍﻟﺨﺎﺩﻡ‪.‬‬ ‫‪Version‬‬

‫ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻟﺘﻌﺭﺽ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻗﺎﺌﻤﺔ ﺒﺄﺴﻤﺎﺀ ﺍﻟﺨﻭﺍﺩﻡ ﺍﻟﻤﺘﺎﺤﺔ‪ ،‬ﻟﻴﺨﺘـﺎﺭ‬
‫ﺍﻟﺨﺎﺩﻡ ﺍﻟﺫﻱ ﻴﺭﻴﺩ ﺃﻥ ﻴﺘﺼل ﺒﻪ‪ ..‬ﻟﻜﻥ ﻋﻠﻴﻙ ﺃﻥ ﺘﻼﺤﻅ ﻤﺎ ﻴﻠﻲ‪:‬‬
‫‪ -١‬ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺘﺴﺘﻬﻠﻙ ﻭﻗﺘﺎ ﻋﻨﺩ ﺘﻨﻔﻴﺫﻫﺎ‪ ،‬ﺒﺴﺒﺏ ﺒﺤﺜﻬﺎ ﻋﻥ ﺍﻟﺨﻭﺍﺩﻡ ﺍﻟﻤﺘﺎﺤﺔ ﻋﻠـﻰ‬
‫ﺍﻟﺸﺒﻜﺔ‪.‬‬

‫‪١٩٨‬‬
‫‪ -٢‬ﻨﺎﺘﺞ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻗﺩ ﻴﺨﺘﻠﻑ ﻤﻥ ﻤﺭﺓ ﺇﻟﻰ ﺃﺨﺭﻯ‪ ،‬ﺒﺴﺒﺏ ﻅﻬﻭﺭ ﺒﻌﺽ ﺍﻟﺨﻭﺍﺩﻡ ﺃﻭ‬
‫ﺍﺨﺘﻔﺎﺌﻬﺎ!‬
‫‪ -٣‬ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻗﺩ ﻻ ﺘﻌﻴﺩ ﻜل ﺍﻟﺨﻭﺍﺩﻡ ﺍﻟﻤﺘﺎﺤﺔ ﻓﻌـﻼ‪ ،‬ﻟﻬـﺫﺍ ﻋﻠﻴـﻙ ﺃﻥ ﺘﻌـﺭﺽ‬
‫ﻟﻠﻤﺴﺘﺨﺩﻡ ﻤﺭﺒﻊ ﻨﺹ ﺃﻴﻀﺎ‪ ،‬ﻟﻴﻜﺘﺏ ﺍﺴﻡ ﺍﻟﺨﺎﺩﻡ ﺒﻨﻔﺴﻪ ﺇﺫﺍ ﻟﻡ ﻴﺠﺩﻩ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ‪.‬‬
‫ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﻤﺸـﺭﻭﻉ ‪ 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‬‬

‫ﻫــﺫﻩ ﺍﻟﻔﺌــﺔ ﻤﻭﺠــﻭﺩﺓ ﻓــﻲ ﺍﻟﻨﻁــﺎﻕ ‪ ،System.Data.Sql‬ﻭﻫــﻲ ﺘــﺭﺙ ﺍﻟﻔﺌــﺔ‬


‫‪.DbDataSourceEnumerator‬‬
‫ﻭﺘﺘﻌﺎﻤل ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻤﻊ ﻋﺩﺍﺩ ﻤﺨﺼﺹ ﻟﻠﻤﺭﻭﺭ ﻋﺒﺭ ﺨﻭﺍﺩﻡ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﺍﻟﻤﺘـﻭﻓﺭﺓ ﻋﻠـﻰ‬
‫ﺍﻟﺸﺒﻜﺔ ﺍﻟﺤﺎﻟﻴﺔ‪.‬‬
‫ﻭﺘﻤﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺨﺎﺼﻴﺔ ﻭﺍﺤﺩﺓ ﺠﺩﻴﺩﺓ‪ ،‬ﻭﻫﻲ‪:‬‬

‫ﺍﻟﻨﺴﺨﺔ ‪: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‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﺴﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataSetName‬‬


‫ﺘﺤــﺩﺩ ﺍﺴــﻡ ﻤﺠﻤﻭﻋــﺔ ﺍﻟﺒﻴﺎﻨــﺎﺕ‪ ،‬ﻟﻴــﺘﻡ ﺍﺴــﺘﺨﺩﺍﻤﻪ ﻜﺎﺴــﻡ ﻟﻌﻨﺼــﺭ ﺍﻟﻭﺜﻴﻘــﺔ‬
‫‪ Document Element‬ﻓﻲ ﻜﻭﺩ ‪ XML‬ﻋﻨﺩ ﺤﻔﻅ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻨﻁﺎﻕ ﺍﻻﺴﻡ ‪:Namespace‬‬


‫ﺘﺤﺩﺩ ﺍﺴﻡ ﺍﻟﻨﻁﺎﻕ ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺘﺤﺘﻪ ﺤﻔﻅ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻜﻭﺩ ‪.XML‬‬

‫ﺍﻟﺒﺎﺩﺌﺔ ‪:Prefix‬‬
‫ﺘﺤﺩﺩ ﺍﻟﺒﺎﺩﺌﺔ ﺍﻟﺘﻲ ﺴﺘﺴﺘﺨﺩﻡ ﻟﺘﻤﻴﻴﺯ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﺘﻲ ﺘﻨﺘﻤﻲ ﺇﻟﻰ ﻨﻁﺎﻕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪..‬‬
‫ﻫﺫﺍ ﻤﻔﻴﺩ ﺇﺫﺍ ﻜﺎﻥ ﻤﻠﻑ ‪ XML‬ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻨﻁﺎﻕ ﺍﻻﺴﻡ ﻓﻴﻪ ﺃﻜﺜﺭ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ‪،‬‬
‫ﻭﺘﺭﻴﺩ ﺘﻤﻴﻴﺯ ﻜل ﻤﻨﻬﺎ ﺘﺤﺕ ﻨﻁﺎﻕ ﻓﺭﻋﻲ ﺨﺎﺹ ﺒﻬﺎ‪.‬‬

‫‪٢٠٤‬‬
‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ‪ ReadXml‬ﻭ ‪ ReadXmlSchema‬ﻟﺘﺤﻤﻴـل ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺃﻭ‬
‫ﺍﻟﻤﺨﻁﻁ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻓﺈﻨﻬﻤﺎ ﺘﺒﺤﺜﺎﻥ ﻓﻲ ﻤﻠﻑ ‪ XML‬ﻋـﻥ ﻨﻁـﺎﻕ ﺍﻻﺴـﻡ‬
‫ﺍﻟﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺨﺎﺼـﻴﺔ ‪ ،DataSetName‬ﻭﻤﺠﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﻤﻤﻴـﺯﺓ ﺒﺎﻟﺒﺎﺩﺌـﺔ‬
‫ﺍﻟﻤﻭﻀﺤﺔ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،Prefix‬ﻓﺈﺫﺍ ﻟﻡ ﺘﻌﺜﺭ ﻓﻲ ﺍﻟﻤﻠﻑ ﻋﻥ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺘﺤﻘـﻕ‬
‫ﻫﺫﻴﻥ ﺍﻟﺸﺭﻁﻴﻥ‪ ،‬ﻻ ﻴﺘﻡ ﺘﺤﻤﻴل ﺃﻱ ﺸﻲﺀ ﻤﻥ ﺍﻟﻤﻠﻑ‪.‬‬

‫ﻭﻴﺭﻴﻙ ﺍﻟﻤﺸﺭﻭﻉ ‪ DataSetSample‬ﻤﺜﺎﻻ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ‪ ..‬ﺴﺘﺠﺩ ﻫـﺫﺍ‬


‫ﺍﻟﻜﻭﺩ ﻤﺜﻼ ﺤﺩﺙ ﺘﺤﻤﻴل ﺍﻟﻨﻤﻭﺫﺝ‪:‬‬
‫;"‪Ds.Namespace = "My Project‬‬
‫;"‪Ds.Prefix = "Authors-Books‬‬
‫;"‪Ds.DataSetName = "DsBooks‬‬
‫ﻭﻴﻅﻬﺭ ﺘﺄﺜﻴﺭ ﻫﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ ﻋﻨﺩ ﻀﻐﻁ ﺍﻟﺯﺭ "ﺤﻔﻅ ﺍﻟﻤﺨﻁﻁ ﻓﻲ ﻤﻠﻑ"‪ ،‬ﺤﻴﺙ ﺴـﺘﺠﺩ‬
‫ﺃﺴﻤﺎﺀ ﻫﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ ﻤﺴـﺘﺨﺩﻤﺔ ﻓـﻲ ﺘﻌﺭﻴـﻑ ﻤﺨﻁـﻁ ﻤﺠﻤﻭﻋـﺔ ﻓـﻲ ﺍﻟﻤﻠـﻑ‬
‫‪.C:\DsBooksSchema.xml‬‬

‫ﺤﺴﺎﺴﺔ ﻟﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ ‪:CaseSensitive‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،True‬ﻓﺴﺘﺼﻴﺭ ﻋﻤﻠﻴﺎﺕ ﺍﻟﻤﻘﺎﺭﻨﺔ ﻭﺍﻟﺘﺭﺸـﻴﺢ ‪Filtering‬‬
‫ﺤﺴﺎﺴﺔ ﻟﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ‪ ..‬ﻫﺫﺍ ﻴﺅﺜﺭ ﻓﻲ ﻨﺘﺎﺌﺞ ﺍﻟﻭﺴﻴﻠﺔ ‪ DataTable.Select‬ﻭﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪ ..DataColumn.Expression‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫﻲ ‪.False‬‬
‫ﻻﺤﻅ ﺃﻥ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﺴﻴﻐﻴﺭ ﺘﻠﻘﺎﺌﻴﺎ ﻗﻴﻤـﺔ ﺍﻟﺨﺎﺼـﻴﺔ ‪CaseSensitive‬‬
‫ﺍﻟﺨﺎﺼﺔ ﺒﻜل ﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫‪٢٠٥‬‬
‫ﺍﻟﻤﺤل ‪:Locale‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺜﻘﺎﻓﺔ ‪ ،CultureInfo‬ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺘﻔﺎﺼـﻴل ﺍﻟﻠﻐـﺔ‬
‫ﺍﻟﺘﻲ ﺘﺴﺘﺨﺩﻡ ﻟﻤﻘﺎﺭﻨﺔ ﻭﺘﺭﺘﻴﺏ ﺍﻟﻨﺼﻭﺹ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﺴﻴﻐﻴﺭ ﺘﻠﻘﺎﺌﻴﺎ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪ Local‬ﺍﻟﺨﺎﺼﺔ ﺒﻜـل‬
‫ﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻓﺭﺽ ﺍﻟﻘﻴﻭﺩ ‪:EnforceConstraints‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪) True‬ﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ(‪ ،‬ﻓﺴﻴﺘﻡ ﺍﻟﺘﺄﻜﺩ ﻤﻥ ﺼـﺤﺔ‬
‫ﺍﻟﻘﻴﻭﺩ ﺍﻟﻤﻔﺭﻭﻀﺔ ﻋﻠﻰ ﺍﻟﺠﺩﺍﻭل ﻋﻨﺩ ﺇﺠﺭﺍﺀ ﻋﻤﻠﻴﺎﺕ ﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺤﺫﻑ‪.‬‬

‫ﺘﻭﺠﺩ ﺒﻬﺎ ﺃﺨﻁﺎﺀ ‪:HasErrors‬‬


‫ﺘﻌﻴﺩ ‪ True‬ﺇﺫﺍ ﻜﺎﻥ ﺃﻱ ﻤﻥ ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻗﺩ ﺤﺩﺜﺕ ﺒﻪ ﺃﺨﻁﺎﺀ ﺃﺜﻨﺎﺀ ﻋﻤﻠﻴـﺔ‬
‫ﺍﻟﺘﺤﺩﻴﺙ‪ ..‬ﻭﻴﻤﻜﻨﻙ ﻓﺤﺹ ﺍﻟﺨﺎﺼﻴﺔ ‪ HasErrors‬ﺍﻟﺨﺎﺼﺔ ﺒﻜل ﺠﺩﻭل ﻟﻤﻌﺭﻓﺔ ﺍﻟﺠـﺩﻭل‬
‫ﺍﻟﺫﻱ ﺘﺴﺒﺏ ﻓﻲ ﺍﻟﺨﻁﺄ‪.‬‬

‫ﺍﻟﺠﺩﺍﻭل ‪:Tables‬‬
‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺠﺩﺍﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ،DataTableCollection‬ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠـﻰ ﻜﺎﺌﻨـﺎﺕ‬
‫ﺍﻟﺠﺩﺍﻭل ‪ DataTable Objects‬ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺴـﻨﺘﻌﺭﻑ ﻋﻠـﻰ‬
‫ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﺎﻟﺘﻔﺼﻴل ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺘﺎﻟﻲ‪.‬‬
‫ﻤﺎ ﻴﻌﻨﻴﺎ ﻫﻨﺎ ﻫﻭ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺘﺤﺭﻴﺭ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﻤﻥ ﺨﻼل ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ‪ ..‬ﻓﻠـﻭ‬
‫ﻀﻐﻁﺕ ﺯﺭ ﺍﻻﻨﺘﻘﺎل ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺨﺎﻨﺔ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻓﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﻤﺤـﺭﺭ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل ‪ Table Collection Editor‬ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪:‬‬

‫‪٢٠٦‬‬
‫ﻓﻲ ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﺠﺩﺍﻭل ﺠﺩﻴﺩ ﺒﻀﻐﻁ ﺍﻟﺯﺭ ‪ ،Add‬ﺜﻡ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺼـﺎﺌﺹ‬
‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﻘﺴﻡ ﺍﻷﻴﻤﻥ ﻤﻥ ﺍﻟﻨﺎﻓﺫﺓ ﻟﺘﻐﻴﻴﺭ ﺍﺴﻡ ﻫﺫﺍ ﺍﻟﺠﺩﻭل ﻭﻁﺭﻴﻘﺔ ﻋﺭﻀﻪ ﻭﺍﻷﻋﻤﺩﺓ‬
‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﺒﻪ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ ﺒﺎﻟﺘﻔﺼﻴل ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﻼﺤﻕ‪.‬‬

‫ﺍﻟﻌﻼﻗﺎﺕ ‪:Relations‬‬
‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﻋﻼﻗﺎﺕ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ،DataRelationCollection‬ﺍﻟﺘـﻲ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ‬
‫ﻜﺎﺌﻨﺎﺕ ﺍﻟﻌﻼﻗﺎﺕ ‪ DataRelation Objects‬ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪..‬‬
‫ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﺎﻟﺘﻔﺼﻴل ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺘﺎﻟﻲ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﻟﺠﺩﺍﻭل ﻭﺍﻟﺴﺠﻼﺕ ﻻ ﻴﻀﻴﻑ ﺍﻟﻌﻼﻗﺎﺕ ﺒﻴﻥ ﺍﻟﺠﺩﺍﻭل‬
‫ﺘﻠﻘﺎﺌﻴﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻌﻼﻗﺎﺕ ‪ ..Relations‬ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺃﻥ ﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺎﺕ ﺒﻨﻔﺴﻙ‬
‫ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻌﻼﻗﺎﺕ‪ ،‬ﺴﻭﺍﺀ ﻤﻥ ﺍﻟﻜﻭﺩ ﺃﻭ ﺒﺎﺴﺘﺨﺩﺍﻡ ﻨﺎﻓﺫﺓ ﺍﻟﻤﺨﻁﻁ ﻜﻤﺎ ﺴﻨﺭﻯ ﻻﺤﻘﺎ‪..‬‬
‫ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﺍﻟﻌﻼﻗﺎﺕ ﺒﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ﻤﻥ ﺨﻼل ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ‪ ..‬ﻓﻠﻭ ﻀـﻐﻁﺕ‬

‫‪٢٠٧‬‬
‫ﺯﺭ ﺍﻻﻨﺘﻘﺎل ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺨﺎﻨﺔ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻓﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﻤﺤﺭﺭ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﻌﻼﻗﺎﺕ ‪ Relations Collection Editor‬ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪:‬‬

‫ﺍﻀﻐﻁ ﺍﻟﺯﺭ ‪ Add‬ﻹﻀﺎﻓﺔ ﻋﻼﻗﺔ ﺠﺩﻴﺩﺓ‪ ..‬ﺴﺘﻅﻬﺭ ﻨﺎﻓﺫﺓ ﺇﻨﺸﺎﺀ ﺍﻟﻌﻼﻗـﺔ ﻟﺘﺴـﻤﺢ ﻟـﻙ‬
‫ﺒﺘﺤﺩﻴﺩ ﺍﻟﺠﺩﻭﻟﻴﻥ ﻭﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﺸﺘﺭﻜﺔ ﻓﻲ ﺍﻟﻌﻼﻗﺔ ﻜﻤﺎ ﺘﻌﻠﻤﻨﺎ ﻤﻥ ﻗﺒل‪ ..‬ﻭﺒﻌﺩ ﺃﻥ ﺘﻀـﻐﻁ‬
‫‪ OK‬ﻹﻏﻼﻕ ﻨﺎﻓﺫﺓ ﺍﻟﻌﻼﻗﺔ‪ ،‬ﺴﺘﻅﻬﺭ ﺍﻟﻌﻼﻗﺔ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﺴﺭﻯ‪ ،‬ﻭﺘﻅﻬﺭ ﺨﺼﺎﺌﺼﻬﺎ ﻓﻲ‬
‫ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﻤﻨﻰ‪ ..‬ﻭﻟﻭ ﺃﺭﺩﺕ ﺘﻐﻴﻴﺭ ﻋﻨﺎﺼﺭ ﺍﻟﻌﻼﻗﺔ ‪ ،‬ﻓﺎﻀﻐﻁ ﺍﻟﺯﺭ ‪ Edit‬ﻟﻌﺭﺽ ﻨﺎﻓـﺫﺓ‬
‫ﺍﻟﻌﻼﻗﺔ ﻤﺭﺓ ﺃﺨﺭﻯ‪.‬‬

‫ﻤﺩﻴﺭ ﺍﻟﻌﺭﺽ ﺍﻻﻓﺘﺭﺍﻀﻲ ‪:DefaultViewManager‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻤﺩﻴﺭ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataViewManager Object‬ﺍﻟﺫﻱ ﻴـﺘﺤﻜﻡ ﻓـﻲ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﻌﺭﻀﻬﺎ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬

‫‪٢٠٨‬‬
‫ﺍﻟﺨﺼﺎﺌﺹ ﺍﻹﻀﺎﻓﻴﺔ ‪:ExtendedProperties‬‬
‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺨﺼﺎﺌﺹ ‪ PropertyCollection‬ﺍﻟﺘﻲ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﺍﻟﺨﺼـﺎﺌﺹ‬
‫ﺍﻹﻀﺎﻓﻴﺔ ﺍﻟﺘﻲ ﺘﻀﻴﻔﻬﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﺍﻟﻤﺠﻤﻭﻋﺔ ‪ PropertyCollection‬ﺘﺭﺙ ﺍﻟﺠﺩﻭل ﺍﻟﻤﺨﺘﻠﻁ ‪ ،Hashtable‬ﻤﻤﺎ ﻴﺘﻴﺢ ﻟﻙ‬
‫ﺇﻀﺎﻓﺔ ﺍﺴﻡ ﺍﻟﺨﺎﺼﻴﺔ ﻜﻤﻔﺘﺎﺡ ‪ ،Key‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺤﻔﻅﻬﺎ ﻓﻴﻬﺎ ﻜﻘﻴﻤﺔ ‪.Value‬‬
‫ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻀﻴﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺨﺎﺼﻴﺔ ﺇﻀﺎﻓﻴﺔ ﺘﺤـﺘﻔﻅ ﺒﺎﺴـﻡ ﺍﻟﺒﺭﻨـﺎﻤﺞ‬
‫ﺍﻟﺨﺎﺹ ﺒﻙ‪ ،‬ﺜﻡ ﻴﻌﺭﺽ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻓﻲ ﺭﺴﺎﻟﺔ‪:‬‬
‫;)"‪Ds.ExtendedProperties.Add("ProgName", "MyProg‬‬
‫;))"‪Console.WriteLine(Ds.ExtendedProperties("ProgName‬‬

‫ﺘﻨﺴﻴﻕ ﺍﻟﺘﺭﺍﺴل ‪:RemotingFormat‬‬


‫ﺘﺤﺩﺩ ﺍﻟﺘﻨﺴﻴﻕ ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺒﻪ ﺇﺭﺴﺎل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺠﻬﺎﺯ ﺇﻟﻰ ﺁﺨﺭ‪ ،‬ﻋﻨﺩﻤﺎ ﺘﺘﻌﺎﻤل ﻤﺠﻤﻭﻋﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻊ ﺒﺭﻨﺎﻤﺞ ﻴﺴﺘﺨﺩﻡ ﺍﻟﺘﺤﻜﻡ ﻋﻥ ﺒﻌﺩ ‪ ،Remoting‬ﻭﻫﻲ ﺘﺄﺨـﺫ ﺇﺤـﺩﻯ ﻗﻴﻤﺘـﻲ‬
‫ﺍﻟﻤﺭﻗﻡ ‪ SerializationFormat‬ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﻴﺘﻡ ﺇﺭﺴﺎل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺼﻭﺭﺓ ﻨﺼـﻭﺹ ‪ ..XML‬ﻫـﺫﻩ ﻫـﻲ ﺍﻟﻘﻴﻤـﺔ‬ ‫‪Xml‬‬


‫ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪.‬‬
‫‪ Binary‬ﻴﺘﻡ ﺇﺭﺴﺎل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺼﻭﺭﺓ ﺃﺭﻗﺎﻡ ﺜﻨﺎﺌﻴﺔ ‪ ..Binary‬ﻫﺫﺍ ﻤﺘـﺎﺡ ﻓﻘـﻁ‬
‫ﺒﺩﺀﺍ ﻤﻥ ﺇﺼﺩﺍﺭ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﺍﻟﺜﺎﻨﻲ‪.‬‬

‫ﻻﺤﻅ ﺃﻥ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻴﻐﻴﺭ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪ RemotingFormat‬ﺍﻟﺨﺎﺼﺔ‬


‫ﺒﻜل ﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻁﺭﻴﻘﺔ ﺴﻠﺴﻠﺔ ﺍﻟﻤﺨﻁﻁ ‪:SchemaSerializationMode‬‬


‫ﺘﺤﺩﺩ ﻜﻴﻑ ﺴﻴﺘﻡ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻤﺨﻁﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻨﺩ ﺴﻠﺴﻠﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ‬
‫‪ ..Typed DataSet‬ﻭﺍﻟﺴ‪‬ﻠﺴﻠﺔ ‪ :Serialization‬ﻫﻲ ﺘﺤﻭﻴل ﻤﺤﺘﻭﻴﺎﺕ ﻜﺎﺌﻥ ﻤﻭﺠـﻭﺩ‬
‫ﻓﻲ ﺍﻟﺫﺍﻜﺭﺓ ﺇﻟﻰ ﺒﻴﺎﻨﺎﺕ ﻴﻤﻜﻥ ﺤﻔﻅﻬﺎ ﻓﻲ ﻤﻠﻑ ﺃﻭ ﺇﺭﺴﺎﻟﻬﺎ ﻋﺒﺭ ﺍﻟﺸﺒﻜﺔ‪ ..‬ﻫﺫﺍ ﻴﺘـﻴﺢ ﻟـﻙ‬
‫‪٢٠٩‬‬
‫ﺍﻻﺤﺘﻔﺎﻅ ﺒﺤﺎﻟﺔ ﺍﻟﻜﺎﺌﻥ ﺒﻌﺩ ﺇﻏﻼﻕ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻻﺴﺘﻌﺎﺩﺘﻬﺎ ﻤﺭﺓ ﺃﺨﺭﻯ ﺒﻌﺩ ﺇﻋﺎﺩﺓ ﺘﺸﻐﻴﻠﻪ‪ ،‬ﺃﻭ‬
‫ﺇﺭﺴﺎﻟﻬﺎ ﺇﻟﻰ ﺠﻬﺎﺯ ﺁﺨﺭ ﻟﻠﺘﺤﻜﻡ ﻓﻲ ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ ﻋﻥ ﺒﻌﺩ ‪ ..Remoting‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠـﻰ‬
‫ﺍﻟﺴﻠﺴﻠﺔ ‪ Serialization‬ﻭﺍﻟﺘﺤﻜﻡ ﺍﻟﺒﻌﻴﺩ ‪ Remoting‬ﺒﺎﻟﺘﻔﺼـﻴل ﻓـﻲ ﻜﺘـﺎﺏ ﺨـﺎﺹ‬
‫ﺒﺎﻟﻤﻭﺍﻀﻴﻊ ﺍﻟﻤﺘﻘﺩﻤﺔ ﻓﻲ ﺒﺭﻤﺠﺔ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﺒﺈﺫﻥ ﺍﷲ‪.‬‬
‫ﻭﺘﺄﺨﺫ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺇﺤﺩﻯ ﻗﻴﻤﺘﻲ ﺍﻟﻤﺭﻗﻡ ‪:SchemaSerializationMode‬‬

‫ﺇﻀﺎﻓﺔ ﻤﺨﻁﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻀﻤﻥ ﻋﻤﻠﻴﺔ ﺍﻟﺴﻠﺴﻠﺔ‪ ..‬ﻫﺫﻩ ﻫﻲ ﺍﻟﻘﻴﻤﺔ‬ ‫‪IncludeSchema‬‬


‫ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪.‬‬
‫‪ ExcludeSchema‬ﻋﺩﻡ ﺇﻀﺎﻓﺔ ﻤﺨﻁﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻀﻤﻥ ﻋﻤﻠﻴﺔ ﺍﻟﺴﻠﺴﻠﺔ‪ ،‬ﻤﻊ ﺍﻻﻜﺘﻔﺎﺀ‬
‫ﺒﺴﻠﺴﻠﺔ ﺨﺼﺎﺌﺹ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﻐﻴﺭﺕ ﻗﻴﻤﻬـﺎ ﻋـﻥ‬
‫ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪ ،‬ﻭﺒﻬﺫﺍ ﻴﺘﻡ ﺘﻘﻠﻴل ﺤﺠﻡ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﻤﺴﻠﺴـﻠﺔ‬
‫ﺒﺸﻜل ﻜﺒﻴﺭ‪ ..‬ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻘﻴﻤﺔ ﻻ ﺘﺼﻠﺢ ﺇﺫﺍ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﺎﺩﻴﺔ )ﻏﻴﺭ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ‪.(Un-typed DataSet‬‬

‫ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﻻﺤﻘﺎ ﻓﻲ ﻫﺫﺍ ﺍﻟﻔﺼل‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﺤﻭ ‪:Clear‬‬
‫ﺘﻤﺤﻭ ﻜل ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﻜل ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﻜﻨﻬﺎ ﻻ ﺘﻤﺤﻭ ﺍﻟﺠﺩﺍﻭل ﻨﻔﺴـﻬﺎ‪،‬‬
‫ﻭﻻ ﺍﻟﻌﻼﻗﺎﺕ ﺒﻴﻨﻬﺎ‪ ..‬ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺴﺘﺴﺒﺏ ﺨﻁﺄ ﻓـﻲ ﺍﻟﺒﺭﻨـﺎﻤﺞ ﻟـﻭ ﻜﺎﻨـﺕ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺘﻌﺎﻤل ﻤﻊ ﻭﺜﻴﻘﺔ ‪ XML‬ﻤﻥ ﺍﻟﻨﻭﻉ ‪.XmlDataDocument‬‬

‫ﺘﺼﻔﻴﺭ ‪:Reset‬‬
‫ﺘﻔﺭﻍ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺠﻤﻴﻊ ﻤﺤﺘﻭﻴﺎﺘﻬﺎ‪ ،‬ﺒﻤﺎ ﻓﻲ ﺫﻟﻙ ﺍﻟﺠﺩﺍﻭل ﻭﺍﻟﻌﻼﻗﺎﺕ ﻭﺍﻟﻘﻴﻭﺩ‪.‬‬

‫‪٢١٠‬‬
‫ﻨﺴﺦ ‪:Clone‬‬
‫ﺘﻨﺴﺦ ﺘﺭﻜﻴﺏ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ )ﻤﺨﻁﻁﺎﺕ ﺍﻟﺠﺩﺍﻭل‪ ،‬ﻭﺍﻟﻌﻼﻗﺎﺕ ﻭﺍﻟﻘﻴﻭﺩ( ﺇﻟﻰ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺒﻴﺎﻨﺎﺕ ﺠﺩﻴﺩﺓ ﻭﺘﻌﻴﺩ ﻤﺭﺠﻌﺎ ﺇﻟﻴﻬﺎ‪ ..‬ﻟﻜﻨﻬﺎ ﻻ ﺘﻨﺴﺦ ﺃﻱ ﺴﺠﻼﺕ‪.‬‬

‫ﻨﺴﺦ ‪:Copy‬‬
‫ﺘﻨﺴﺦ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﺎﻤﻠﺔ )ﻤﺨﻁﻁﺎﺕ ﺍﻟﺠﺩﺍﻭل‪ ،‬ﻭﺍﻟﻌﻼﻗـﺎﺕ ﻭﺍﻟﻘﻴـﻭﺩ ﻭﺍﻟﺴـﺠﻼﺕ‬
‫ﺃﻴﻀﺎ( ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻴﺩﺓ ﻭﺘﻌﻴﺩ ﻤﺭﺠﻌﺎ ﺇﻟﻴﻬﺎ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﺘﻐﻴﺭﺍﺕ ‪:GetChanges‬‬


‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻴﺩﺓ‪ ،‬ﺘﺤﺘﻭﻱ ﺠﺩﺍﻭﻟﻬﺎ ﻋﻠﻰ ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﻲ ﺘﻡ‪ ‬ﺘﻌﺩﻴﻠﻬﺎ ﺃﻭ ﺇﻀﺎﻓﺘﻬﺎ‬
‫ﺃﻭ ﺤﺫﻓﻬﺎ ﻤﻨﺫ ﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻷﺼـﻠﻴﺔ‪ ،‬ﺃﻭ ﻤﻨـﺫ ﺁﺨـﺭ ﺍﺴـﺘﺩﻋﺎﺀ ﻟﻠﻭﺴـﻴﻠﺔ‬
‫‪ ..AcceptChanges‬ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ‪ Nothing‬ﺇﺫﺍ ﻟﻡ ﺘﺠـﺩ ﺃﻴـﺔ ﺘﻐﻴـﺭﺍﺕ ﻓـﻲ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻗﺩ ﺘﻀﻴﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺠﺩﻴﺩﺓ ﺒﻌﺽ ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﻲ ﻟﻡ‬
‫ﺘﺘﻐﻴﺭ ﺒﻴﺎﻨﺎﺘﻬﺎ‪ ،‬ﻭﺫﻟﻙ ﻟﻠﻤﺤﺎﻓﻅﺔ ﻋﻠﻰ ﺼﺤﺔ ﺍﻟﻌﻼﻗﺎﺕ ﻭﺍﻟﻘﻴﻭﺩ ﺒﻴﻥ ﺍﻟﺠﺩﺍﻭل‪ ،‬ﻤﻤﺎ ﻴﺘﻴﺢ ﻟﻙ‬
‫ﺇﻋﺎﺩﺓ ﺩﻤﺞ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺠﺩﻴﺩﺓ ﺒﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻷﺼـﻠﻴﺔ ﺇﺫﺍ ﺃﺭﺩﺕ‪ ،‬ﺩﻭﻥ‬
‫ﺤﺩﻭﺙ ﺃﻴﺔ ﺃﺨﻁﺎﺀ‪.‬‬
‫ﻭﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﺘﺴﺘﻘﺒل ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤـﺭﻗﻡ ‪ ،DataRowState‬ﺍﻟﺘـﻲ‬
‫ﺘﻤﻜﻨﻙ ﻤﻥ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺤﺩﺙ ﺒﻬﺎ ﻨﻭﻉ ﻤﺤﺩﺩ ﻤﻥ ﺍﻟﺘﻐﻴﻴﺭ ﺩﻭﻥ ﺴـﻭﺍﻩ‪..‬‬
‫ﻭﻫﺫﻩ ﺍﻟﻘﻴﻡ ﻫﻲ‪:‬‬

‫ﺘﻡ‪ ‬ﺇﻨﺸﺎﺀ ﻫﺫﺍ ﺍﻟﺴﺠلّ ﻭﻟﻜﻨﻪ ﻟﻡ ﻴﻭﻀﻊ ﺒﻌﺩ ﻓﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺴـﺠﻼﺕ‬ ‫ﻤﺴﺘﻘلّ‬
‫‪ Rows‬ﺍﻟﺨﺎﺼﺔ ﺒﺄﻱ ﺠﺩﻭل‪ ،‬ﺃﻭ ﺃﻨﻪ ﺤﺫﻑ ﻟﻠﺘﻭ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺴﺠﻼﺕ‬ ‫‪Detached‬‬
‫ﺃﺤﺩ ﺍﻟﺠﺩﺍﻭل‪.‬‬
‫ﻟﻡ ﺘﺘﻐﻴﺭ ﺒﻴﺎﻨﺎﺕ ﻫﺫﺍ ﺍﻟﺴﺠلّّ‪ ،‬ﻤﻨﺫ ﺃﻥ ﺘﻡ‪ ‬ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻭ‬
‫ﻟﻡ ﻴﺘﻐﻴﺭ‬
‫‪ Unchanged‬ﻤﻨﺫ ﺁﺨﺭ ﺍﺴﺘﺩﻋﺎﺀ ﻟﻠﻭﺴﻴﻠﺔ ‪.AcceptChanges‬‬
‫‪٢١١‬‬
‫ﻫﺫﺍ ﺍﻟﺴﺠلّ ﻟﻴﺱ ﻤﻭﺠﻭﺩﺍ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺇﻨﻤﺎ ﺘﻤ‪‬ـﺕ ﺇﻀـﺎﻓﺘﻪ‬ ‫‪‬ﻤﻀﺎﻑ‬
‫ﻜﺴﺠل ﺠﺩﻴﺩ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬ ‫‪Added‬‬
‫ﺘﻡ‪ ‬ﺤﺫﻑ ﻫﺫﺍ ﺍﻟﺴﺠلّ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻟﻜﻨﻪ ﻤﺎ ﺯﺍل ﻤﻭﺠـﻭﺩﺍ‬ ‫ﻤﺤﺫﻭﻑ‬
‫ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬ ‫‪Deleted‬‬
‫لّ‪ ،‬ﻭﻟﻜﻥ ﻟﻡ ﻴﺘﻡ‪ ‬ﺤﻔﻅ ﺍﻟﺘﻌﺩﻴﻼﺕ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫ﺘﻡ‪ ‬ﺘﻌﺩﻴل ﻫﺫﺍ ﺍﻟﺴﺠ ّ‬ ‫ﻤﻌﺩل‬
‫‪‬‬
‫ﺒﻌﺩ‪.‬‬ ‫‪Modified‬‬

‫ﻭﻴﻤﻜﻨﻙ ﺩﻤﺞ ﺃﻜﺜﺭ ﻤﻥ ﻗﻴﻤﺔ ﻤﻥ ﻗﻴﻡ ﻫﺫﺍ ﺍﻟﻤﺭﻗﻡ ﻤﻌﺎ‪ ،‬ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﻌﺎﻤل ‪.OR‬‬

‫ﺘﻡ ﺘﻐﻴﻴﺭﻫﺎ ‪:HasChanges‬‬


‫ﺘﻌﻴﺩ ‪ ،True‬ﺇﺫﺍ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺴﺠﻼﺕ ﻗﺩ ﺘﻡ ﺘﻌﺩﻴﻠﻬﺎ ﺃﻭ ﺇﻀﺎﻓﺘﻬﺎ‬
‫ﺃﻭ ﺤﺫﻓﻬﺎ‪ ،‬ﻭﻟﻡ ﺘﺤﻔﻅ ﺒﻌﺩ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺤﺩﺙ ﺇﻏـﻼﻕ ﺍﻟﻨﻤـﻭﺫﺝ ‪ ،FormClosing‬ﻟﺴـﺅﺍل‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ ﺇﻥ ﻜﺎﻥ ﻴﺭﻴﺩ ﺤﻔﻅ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻗﺒل ﺇﻏﻼﻕ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺃﻡ ﻻ‪ ،‬ﻭﻫﻭ ﻤـﺎ ﻓﻌﻠﻨـﺎﻩ ﻓـﻲ‬
‫ﺍﻟﻤﺸﺭﻭﻉ ‪ ..CustomDataSet‬ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﺯﺭ "ﺘﺤﻤﻴل ﻤﻥ ﻗﺎﻋﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ" ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪ ،DataSetSample‬ﻟﺤﻔﻅ ﺃﻴﺔ ﺘﻐﻴﻴﺭﺍﺕ ﻗﺒـل ﺇﻋـﺎﺩﺓ ﺘﺤﻤﻴـل‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﺘﺴﺘﻘﺒل ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤـﺭﻗﻡ ‪ DataRowState‬ﺍﻟﺘـﻲ‬
‫ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻤﻥ ﻗﺒل‪ ..‬ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﺼﻴﻐﺔ ‪ True‬ﺇﺫﺍ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺘﺤﺘـﻭﻱ‬
‫ﻋﻠﻰ ﺴﺠﻼﺕ ﻭﻗﻊ ﻋﻠﻴﻬﺎ ﻨﻭﻉ ﺍﻟﺘﻐﻴﻴﺭ ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل‪ ..‬ﻭﺍﻟﺠﻤﻠـﺔ ﺍﻟﺘﺎﻟﻴـﺔ ﺘﺨﺒـﺭﻙ ﺇﻥ‬
‫ﻜﺎﻨﺕ ﻫﻨﺎﻙ ﺴﺠﻼﺕ ﺠﺩﻴﺩﺓ ﺃﻀﻴﻔﺕ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻡ ﻻ‪:‬‬
‫;))‪Console.WriteLine (Ds.HasChanges(DataRowState.Added‬‬

‫ﻗﺒﻭل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ‪:AcceptChanges‬‬


‫ﺘﺠﺒﺭ ﻜل ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ AcceptChanges‬ﺍﻟﺨﺎﺼﺔ‬
‫ﺒﻬﺎ‪.‬‬
‫‪٢١٢‬‬
‫ﺭﻓﺽ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ‪:RejectChanges‬‬
‫ﺘﺠﺒﺭ ﻜل ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ RejectChanges‬ﺍﻟﺨﺎﺼـﺔ‬
‫ﺒﻬﺎ‪.‬‬

‫ﺇﻨﺸﺎﺀ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ‪:CreateDataReader‬‬


‫ﺘﻌﻴﺩ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺠﺩﺍﻭل ‪ ،DataTableReader‬ﺍﻟﺫﻱ ﻴﻤﻜﻨﻙ ﻤﻥ ﺨﻼﻟﻪ ﺍﻟﻤﺭﻭﺭ ﻋﺒﺭ‬
‫ﺴﺠﻼﺕ ﻜل ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﺘﻨﺸﺊ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﺠﻤﻭﻋﺔ ﻨﺘﺎﺌﺞ ‪ Result Set‬ﻟﻜل ﺠﺩﻭل‪ ،‬ﺒﻨﻔﺱ ﺘﺭﺘﻴﺏ ﺍﻟﺠﺩﺍﻭل ﻓﻲ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل ‪ ،DataSet.Tables‬ﻭﺇﺫﺍ ﻜﺎﻥ ﺃﺤﺩ ﺍﻟﺠﺩﺍﻭل ﺨﺎﻟﻴﺎ ﻤـﻥ ﺍﻟﺴـﺠﻼﺕ‪،‬‬
‫ﻓﺴﺘﻭﻀﻊ ﻤﻘﺎﺒﻠﻪ ﻓﻲ ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺠﻤﻭﻋﺔ ﻨﺘﺎﺌﺞ ﻓﺎﺭﻏﺔ‪ ،‬ﻭﺫﻟﻙ ﻟﻠﺤﻔﺎﻅ ﻋﻠﻰ ﺍﻟﺘﺭﺘﻴﺏ‪..‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺍﻻﻨﺘﻘﺎل ﻤﻥ ﻗﺭﺍﺀﺓ ﺴﺠﻼﺕ ﺠﺩﻭل ﺇﻟﻰ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻟﺘﺎﻟﻲ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ‬
‫‪ NextResult‬ﺍﻟﺨﺎﺼﺔ ﺒﻘﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﻤﺎ ﺘﻌﻠﻤﻨﺎ ﻤﻥ ﻗﺒل‪.‬‬
‫ﻭﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺘﺘﻴﺢ ﻟﻙ ﺍﻟﺘﺤﻜﻡ ﻓﻲ ﺘﺭﺘﻴﺏ ﺍﻟﻨﺘـﺎﺌﺞ‪ ،‬ﺤﻴـﺙ ﺘﺴـﺘﻘﺒل‬
‫ﻤﺼﻔﻭﻓﺔ ﺠﺩﺍﻭل ‪ ،DataTable Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﺘـﻲ‬
‫ﺘﺭﻴﺩ ﺃﻥ ﺘﻘﺭﺃﻫﺎ‪ ،‬ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﻴﻅﻬﺭ ﻓﻲ ﻫﺫﻩ ﺍﻟﻤﺼﻔﻭﻓﺔ ﺃﻭﻻ ﺴـﻴﻌﺭﺽ‬
‫ﻗﺎﺭﺉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺴﺠﻼﺘﻪ ﺃﻭﻻ‪.‬‬
‫ﻭﻴﺭﻴﻙ ﺍﻟﺯﺭ "ﺇﻨﺸﺎﺀ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ" ﻜﻴﻑ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﻟﻌـﺭﺽ ﻜـل‬
‫ﻤﺤﺘﻭﻴﺎﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﻤﺨﺭﺠﺎﺕ ‪.Output Window‬‬

‫ﺘﺤﻤﻴل ‪: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‬‬
‫ﺴﺠل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻌﺩ ﺍﻟﻤﺯﺝ‬
‫‪٤‬‬ ‫‪٣‬‬
‫)ﻗﻴﻤﺔ ﺍﻟﻤﻌﺎﻤل ‪(False‬‬

‫ﻻﺤﻅ ﺃﻥ ﺠﻌل ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ‪ ،True‬ﻫﻭ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﻭﺤﻴﺩﺓ ﺍﻟﺘﻲ ﺘﺴﺘﻁﻴﻊ ﺒﻬﺎ ﺘﻐﻴﻴﺭ‬
‫ﺍﻟﻘﻴﻤﺔ ﺍﻷﺼﻠﻴﺔ ﺩﻭﻥ ﺘﻐﻴﻴﺭ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺤﺎﻟﻴﺔ‪ ،‬ﻷﻥ ﺼﻴﻎ ﺍﻟﻭﺴﻴﻠﺔ ‪DataRow.Item‬‬
‫ﺍﻟﺘﻲ ﺘﺘﻴﺢ ﻟﻙ ﺘﺤﺩﻴﺩ ﺍﻟﻨﺴﺨﺔ ﺍﻟﺘﻲ ﺘﺘﻌﺎﻤل ﻤﻌﻬﺎ‪ ،‬ﻗﺎﺒﻠﺔ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘـﻁ‪ ،‬ﻭﻻ ﻴﻤﻜـﻥ‬
‫ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﻠﻜﺘﺎﺒﺔ!‬
‫‪٢١٥‬‬
‫ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﻟﻭﺴﻴﻠﺔ ‪ Merge‬ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ ‪OptimisticConcurrency‬‬
‫ﻤﺭﺘﻴﻥ‪:‬‬
‫‪ -‬ﻤﺭﺓ ﻓﻲ ﺤﺩﺙ ﻀﻐﻁ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ "ﺃﺭﻴﺩ ﺤﻔـﻅ ﺘﻌـﺩﻴﻼﺘﻲ"‪ ،‬ﻭﻗـﺩ‬
‫ﺃﺭﺴﻠﻨﺎ ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻘﻴﻤﺔ ‪ True‬ﻟﺘﻐﻴﻴـﺭ ﺍﻟﻨﺴـﺨﺔ‬
‫ﺍﻷﺼﻠﻴﺔ ﻟﻠﺴﺠل ﺍﻟﻤﺭﺍﺩ ﺇﻋﺎﺩﺓ ﺤﻔﻅﻪ‪ ،‬ﻤﻊ ﺍﻻﺤﺘﻔﺎﻅ ﺒﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﻤﺴـﺘﺨﺩﻡ‬
‫ﻟﺤﻔﻅﻬﺎ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -‬ﻭﻤﺭﺓ ﻓﻲ ﺤﺩﺙ ﻀﻐﻁ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ "ﺇﻟﻐﺎﺀ ﺘﻌﺩﻴﻼﺘﻲ"‪ ،‬ﻭﻗﺩ ﺃﺭﺴـﻠﻨﺎ‬
‫ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻘﻴﻤﺔ ‪ False‬ﻟﻠـﺘﺨﻠﺹ ﻤـﻥ ﺍﻟﺴـﺠل‬
‫ﺍﻟﻘﺩﻴﻡ‪ ،‬ﻭﻭﻀﻊ ﺍﻟﺴﺠل ﺍﻟﻘﺎﺩﻡ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺩﻻ ﻤﻨﻪ )ﻴﺸـﻤل ﻫـﺫﺍ‬
‫ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ ﻭﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ﻟﻠﺴﺠل(‪.‬‬
‫‪ -٣‬ﺒﻌﺽ ﺍﻟﺼﻴﻎ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻎ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻟﺙ‪ ،‬ﻴﺤـﺩﺩ ﺭﺩ‪ ‬ﺍﻟﻔﻌـل ﺍﻟـﺫﻱ‬
‫ﺴﻴﺘﺨﺫ ﻟﻭ ﻜﺎﻥ ﺘﺭﻜﻴﺏ ﻤﺠﻤﻭﻋﺘﻲ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺨﺘﻠﻔﺎ )ﻜﻌﺩﻡ ﻭﺠﻭﺩ ﺒﻌﺽ ﺍﻟﺠﺩﺍﻭل ﺃﻭ‬
‫ﺍﻷﻋﻤﺩﺓ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺤﺎﻟﻴـﺔ(‪ ،‬ﻭﻫـﻭ ﻴﺄﺨـﺫ ﺇﺤـﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ‬
‫‪ MissingSchemaAction‬ﺍﻟﺘﻲ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻤﻥ ﻗﺒـل‪ ..‬ﺘـﺫﻜﺭ ﺃﻥ ﺍﻟﻘﻴﻤـﺔ‬
‫ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻓﻲ ﺍﻟﺼﻴﻎ ﺍﻟﺘﻲ ﻻ ﺘﺴﺘﻘﺒل ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻫﻲ ‪ ،Add‬ﺒﻤﻌﻨـﻰ ﺇﻀـﺎﻓﺔ‬
‫ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺍﻟﻼﺯﻤﺔ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺤﺎﻟﻴـﺔ ﻻﺴـﺘﻘﺒﺎل ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺍﻟﺠﺩﻴﺩﺓ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻀﺎﻓﺔ‪.‬‬
‫ﺤ‪‬ﺔ ﺍﻟﻘﻴﻭﺩ ‪ ،Constrains‬ﺇﻻ ﺒﻌﺩ ﺍﻜﺘﻤﺎل ﻋﻤﻠﻴ‪‬ﺔ ﺍﻟﻤـﺯﺝ‪ ..‬ﻓـﺈﺫﺍ‬
‫ﻭﻻ ﻴﺘﻡ‪ ‬ﺍﻟﺘﺤﻘﻕ ﻤﻥ ﺼ ‪‬‬
‫ﻜﺎﻨﺕ ﻫﻨﺎﻙ ﺴﺠﻼﺕ ﺘﻌﺎﺭﺽ ﺍﻟﻘﻴﻭﺩ ﺍﻟﻤﻔﺭﻭﻀﺔ‪ ،‬ﻴﺤﺩﺙ ﻤﺎ ﻴﻠﻲ‪:‬‬
‫‪ -‬ﻴﻨﻁﻠﻕ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻤﻥ ﺍﻟﻨﻭﻉ ‪.ConstraintException‬‬
‫‪ -‬ﺘﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ ‪ False‬ﻓﻲ ﺍﻟﺨﺎﺼـﻴﺔ ‪ DataSet.EnforceConstraints‬ﻹﻴﻘـﺎﻑ‬
‫ﺘﻁﺒﻴﻕ ﺍﻟﻘﻴﻭﺩ‪ ،‬ﻭﺫﻟﻙ ﺤﺘﻰ ﻴﻤﻜﻥ ﺍﻻﺤﺘﻔﺎﻅ ﺒﺎﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻤﺯﻭﺠﺔ ﺇﻟﻰ ﺃﻥ ﺘﺭﻯ ﻜﻴﻑ‬
‫ﺘﺤل ﺍﻟﻤﺸﻜﻠﺔ‪.‬‬
‫‪ -‬ﻴﻭﻀﻊ ﻨﺹ ﺍﻟﺨﻁﺄ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ RowError‬ﺍﻟﺨﺎﺼﺔ ﺒﻜل ﺴﺠل ﻴﺘﻌﺎﺭﺽ ﻤـﻊ‬
‫ﺍﻟﻘﻴﻭﺩ ﺍﻟﻤﻔﺭﻭﻀﺔ‪ ،‬ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﻓﺤﺹ ﻫﺫﻩ ﺍﻷﺨﻁﺎﺀ ﻭﺇﺼﻼﺤﻬﺎ ﺒﺎﻟﻁﺭﻴﻘﺔ ﺍﻟﻤﻨﺎﺴﺒﺔ‪،‬‬
‫‪٢١٦‬‬
‫ﻗﺒل ﻤﺤﺎﻭﻟﺔ ﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ ‪ True‬ﻓـﻲ ﺍﻟﺨﺎﺼـﻴﺔ ‪ EnforceConstraints‬ﻤـﻥ‬
‫ﺠﺩﻴﺩ ﻟﺘﻁﺒﻴﻕ ﺍﻟﻘﻴﻭﺩ‪.‬‬

‫ﺘﺨﻤﻴﻥ ﺍﻟﻤﺨﻁﻁ ‪:InferXmlSchema‬‬


‫ﺘﻘﺭﺃ ﻜﻭﺩ ‪ ،XML‬ﻭﺘﺤﺎﻭل ﺍﺴﺘﻨﺘﺎﺝ ﻤﺨﻁﻁﺎﺕ ﺍﻟﺠﺩﺍﻭل ﻤﻥ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻭﺠـﻭﺩﺓ‬
‫ﻓﻴﻬﺎ‪ ،‬ﻭﺘﺤﻤل ﻫﺫﺍ ﺍﻟﻤﺨﻁﻁ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺓ ﺼﻴﻎ‪ ،‬ﻜل ﻤﻨﻬـﺎ‬
‫ﻟﻬﺎ ﻤﻌﺎﻤﻼﻥ‪:‬‬
‫‪ -‬ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ﻴﺤﺩﺩ ﺍﻟﻤﻠﻑ ﺍﻟﺫﻱ ﻴﻭﺠﺩ ﺒﻪ ﻜﻭﺩ ‪ ،XML‬ﺴﻭﺍﺀ ﻜـﺎﻥ ﺫﻟـﻙ ﻓـﻲ‬
‫ﺼﻭﺭﺓ ﻤﺴﺎﺭ ﺍﻟﻤﻠﻑ‪ ،‬ﺃﻭ ﻜﺎﺌﻥ ﻤﺠـﺭﻯ ﺒﻴﺎﻨـﺎﺕ ‪ ،Stream‬ﺃﻭ ﻗـﺎﺭﺉ ﻨﺼـﻲ‬
‫‪ ،TextReader‬ﺃﻭ "ﻗﺎﺭﺉ ‪.XmlReader "XML‬‬
‫‪ -‬ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ﻴﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺔ ﻨﺼﻴﺔ‪ ،‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﻋﻨﺎﻭﻴﻥ ﺍﻟﻤﻭﺍﻗﻊ ‪Url‬‬
‫ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﺴﺘﺒﻌﺎﺩﻫﺎ ﻋﻨﺩ ﺍﺴﺘﺨﻼﺹ ﺍﻟﻤﺨﻁﻁ ﻤﻥ ﺍﻟﻤﻠﻑ‪.‬‬

‫ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻜﻭﺩ ﺍﻟﻤﺨﻁﻁ ‪:GetXmlSchema‬‬


‫ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻜﻭﺩ ‪ XML‬ﺍﻟﺫﻱ ﻴﻤﺜل ﻤﺨﻁﻁ ﺍﻟﺠﺩﺍﻭل ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻤﺠﻤﻭﻋﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﺍﻟﻜﻭﺩ ‪:GetXml‬‬


‫ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻜﻭﺩ ‪ XML‬ﺍﻟﺫﻱ ﻴﻤﺜل ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻜﺘﺎﺒﺔ ﻜﻭﺩ ﺍﻟﻤﺨﻁﻁ ‪:WriteXmlSchema‬‬


‫ﺘﺤﻔﻅ ﻜﻭﺩ ‪ XML‬ﺍﻟﺫﻱ ﻴﻤﺜل ﻤﺨﻁﻁ ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻓﻲ ﺍﻟﻤﻠـﻑ ﺍﻟﻤﺭﺴـل‬
‫ﺇﻟﻴﻬﺎ ﻜﻤﻌﺎﻤل‪ ،‬ﺴﻭﺍﺀ ﻜﺎﻥ ﻓﻲ ﺼﻭﺭﺓ ﻤﺴﺎﺭ ﺍﻟﻤﻠﻑ‪ ،‬ﺃﻭ ﻜﺎﺌﻥ ﻤﺠﺭﻯ ﺒﻴﺎﻨـﺎﺕ ‪،Stream‬‬
‫ﺃﻭ ﻗﺎﺭﺉ ﻨﺼﻲ ‪ ،TextReader‬ﺃﻭ "ﻗﺎﺭﺉ ‪.XmlReader "XML‬‬
‫ﻭﺘﻭﺠﺩ ﻋﺩﺓ ﺼﻴﻎ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻎ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﻨـﺩﻭﺏ‬
‫)‪ ،Converter(Of Type, String‬ﻭﻫﻭ ﻴﺴﺘﻘﺒل ﻋﻨﻭﺍﻥ ﺃﻱ ﺩﺍﻟﺔ ﻟﻬﺎ ﻤﻌﺎﻤل ﻤﻥ ﺍﻟﻨـﻭﻉ‬

‫‪٢١٧‬‬
‫‪ Type‬ﻭﺘﻌﻴﺩ ‪ ..String‬ﻫﺫﺍ ﻤﻔﻴﺩ ﺇﺫﺍ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﻋﻤـﻭﺩ‬
‫ﻴﺘﻌﺎﻤل ﻤﻊ ﻨﻭﻉ ﺒﻴﺎﻨﺎﺕ ﻤﺭﻜﺏ ﻻ ﻴﻤﻜﻥ ﺘﺤﻭﻴﻠﻪ ﺇﻟﻰ ﻨﺹ ﻤﺒﺎﺸﺭﺓ‪ ،‬ﻭﻫﻨﺎ ﻴﻤﻜﻨـﻙ ﻜﺘﺎﺒـﺔ‬
‫ﺩﺍﻟﺔ ﻤﻨﺎﺴﺒﺔ ﺘﻭﻀﺢ ﻜﻴﻑ ﻴﻤﻜﻥ ﺘﺤﻭﻴل ﺒﻴﺎﻨﺎﺘﻪ ﺇﻟﻰ ﻨﺹ‪ ،‬ﻭﺘﺭﺴﻠﻬﺎ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل‪.‬‬

‫ﻜﺘﺎﺒﺔ ﺍﻟﻜﻭﺩ ‪:WriteXml‬‬


‫ﻤﻤﺎﺜﻠﺔ ﻟﻠﻭﺴﻴﻠﺔ ﺍﻟﺴﺎﺒﻘﺔ‪ ،‬ﺇﻻ ﺃﻨﻬﺎ ﺘﺤﻔﻅ ﺴﺠﻼﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓـﻲ ﻤﻠـﻑ ‪..XML‬‬
‫ﻭﻫﻨﺎﻙ ﺼﻴﻐﺔ ﺃﺨﺭﻯ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﻟﻬﺎ ﻤﻌﺎﻤل ﺜﺎﻥٍ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﺭﻗﻡ ‪XmlWriteMode‬‬
‫ﺍﻟﺫﻱ ﻴﻤﺘﻠﻙ ﺍﻟﻘﻴﻡ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪ IgnoreSchema‬ﻜﺘﺎﺒﺔ ﺍﻟﺴﺠﻼﺕ ﻓﻘﻁ ﺒﺩﻭﻥ ﻜﺘﺎﺒﺔ ﻤﺨﻁﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻫﺫﻩ ﻫﻲ ﺍﻟﻘﻴﻤﺔ‬


‫ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪.‬‬
‫‪ WriteSchema‬ﻜﺘﺎﺒﺔ ﺍﻟﺴﺠﻼﺕ ﻭﻤﺨﻁﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻌﺎ ﻓﻲ ﺍﻟﻤﻠﻑ‪.‬‬
‫ﻜﺘﺎﺒﺔ ﻜل ﻤﺤﺘﻭﻴﺎﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﻤﻠﻑ‪ ،‬ﺒﻤﺎ ﻓـﻲ ﺫﻟـﻙ‬ ‫‪DiffGram‬‬
‫ﺍﻟﻨﺴـﺨﺔ ﺍﻷﺼـﻠﻴﺔ ‪ Original Version‬ﻭﺍﻟﺤﺎﻟﻴـﺔ ‪Current‬‬
‫‪ Version‬ﻟﻜل ﺍﻟﺴﺠﻼﺕ‪ ،‬ﺤﺘﻰ ﻟﻭ ﻟﻡ ﺘﺘﻐﻴـﺭ ﺍﻟﻨﺴـﺨﺔ ﺍﻟﺤﺎﻟﻴـﺔ‬
‫ﻟﻠﺴﺠل ﻋﻥ ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ‪.‬‬

‫ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟـﺯﺭ "ﺤﻔـﻅ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻓـﻲ ﻤﻠـﻑ" ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ‬
‫‪ ،DataSetSample‬ﻭﺃﺭﺴﻠﻨﺎ ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤـل ﺍﻟﺜـﺎﻨﻲ ﺍﻟﻘﻴﻤـﺔ ‪ WriteSchema‬ﻟﺤﻔـﻅ‬
‫ﺍﻟﻤﺨﻁﻁ ﻤﻊ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻫﺫﺍ ﻴﻀﻤﻥ ﻟﻨﺎ ﺤﻔﻅ ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺠﺩﻭﻟﻴﻥ ﻭﺍﻟﻘﻴـﻭﺩ ﺍﻟﻤﻔﺭﻭﻀـﺔ‬
‫ﻋﻠﻴﻬﻤﺎ‪ ،‬ﻭﺍﻟﻤﻔﺎﺘﻴﺢ ﺍﻷﺴﺎﺴﻴﺔ ﻭﺍﻟﻔﺭﻋﻴﺔ‪.‬‬

‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴــﻴﻠﺔ‬
‫ـﻙ ﺍﺴـ‬
‫ـﻁ‪ ،‬ﻓﻌﻠﻴـ‬
‫ـﻲ ﺘﻐﻴــﺭﺕ ﻓﻘـ‬
‫ـﺠﻼﺕ ﺍﻟﺘـ‬
‫ـﻅ ﺍﻟﺴـ‬
‫ﺇﺫﺍ ﺃﺭﺩﺕ ﺤﻔـ‬
‫‪ DataSet.GetChanges‬ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻴﺩﺓ ﺒﻬﺎ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘـﻲ‬
‫ﺘﻐﻴﺭﺕ ﻓﻘﻁ‪ ،‬ﻭﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ WriteXml‬ﺍﻟﺨﺎﺼﺔ ﺒﻬﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﻴﺩﺓ ﻟﺤﻔـﻅ‬
‫ﺴﺠﻼﺘﻬﺎ‪.‬‬
‫‪٢١٨‬‬
‫ﻗﺭﺍﺀﺓ ﻜﻭﺩ ﺍﻟﻤﺨﻁﻁ ‪:ReadXmlSchema‬‬
‫ﻤﻤﺎﺜﻠﺔ ﻟﻠﻭﺴﻴﻠﺔ ‪ WriteXmlSchema‬ﻓﻲ ﻤﻌﺎﻤﻼﺘﻬﺎ‪ ،‬ﻭﻟﻜﻨﻬﺎ ﺘﻘﻭﻡ ﺒﺎﻟﻭﻅﻴﻔﺔ ﺍﻟﻌﻜﺴـﻴﺔ‪،‬‬
‫ﺤﻴﺙ ﺘﻘﺭﺃ ﺍﻟﻤﺨﻁﻁ ﻤﻥ ﻤﻠﻑ ‪ XML‬ﻭﺘﺤﻤ‪‬ﻠﻪ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻻﺤـﻅ ﺃﻥ ﻫـﺫﻩ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ﻗﺩ ﺘﺘﺴﺒﺏ ﻓﻲ ﺤﺩﻭﺙ ﺃﺨﻁﺎﺀ ﺇﺫﺍ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﺨﻁـﻁ‬
‫ﺒﺎﻟﻔﻌل‪ ،‬ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺍﺴـﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ ‪ DataSet.Reset‬ﺃﻭﻻ ﻟﻤﺤـﻭ ﻜـل ﺒﻴﺎﻨﺎﺘﻬـﺎ‬
‫ﻭﻤﺨﻁﻁﺎﺘﻬﺎ ﺃﻭﻻ‪ ،‬ﻗﺒل ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪.ReadXmlSchema‬‬

‫ﻗﺭﺍﺀﺓ ﺍﻟﻜﻭﺩ ‪:ReadXml‬‬


‫ﻤﻤﺎﺜﻠﺔ ﻟﻠﻭﺴﻴﻠﺔ ﺍﻟﺴﺎﺒﻘﺔ‪ ،‬ﺇﻻ ﺃﻨﻬﺎ ﺘﻘﺭﺃ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﻤﻠﻑ ‪ XML‬ﻭﺘﺤﻤﻠﻬـﺎ ﻓـﻲ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻫﻨﺎﻙ ﺼﻴﻐﺔ ﺃﺨﺭﻯ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﻟﻬﺎ ﻤﻌﺎﻤل ﺜﺎﻥٍ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤـﺭﻗﻡ‬
‫‪ XmlReadMode‬ﺍﻟﺫﻱ ﻴﻤﺘﻠﻙ ﺍﻟﻘﻴﻡ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪.‬‬ ‫‪Auto‬‬


‫ﻗﺭﺍﺀﺓ ﺍﻟﺴﺠﻼﺕ‪ ،‬ﻭﻗﺭﺍﺀﺓ ﺍﻟﻤﺨﻁﻁ ﺇﻥ ﻭﺠﺩ ﻓﻲ ﺍﻟﻤﻠﻑ )ﻴﺠﺏ ﺃﻥ ﺘﺭﺴـل‬ ‫‪Read‬‬
‫‪Schema‬‬
‫ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ﻟﻠﻭﺴﻴﻠﺔ ‪ WriteXml‬ﺍﻟﻘﻴﻤﺔ ‪ WriteSchema‬ﻟﻴـﺘﻡ‬
‫ﺤﻔﻅ ﺍﻟﻤﺨﻁﻁ ﻤﻊ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺒﺎﻟﺘـﺎﻟﻲ ﻴﻤﻜﻨـﻙ ﻗﺭﺍﺀﺘـﻪ(‪ ..‬ﻭﺇﺫﺍ ﻜـﺎﻥ‬
‫ﺒﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺨﻁﻁ ﺒﺎﻟﻔﻌل‪ ،‬ﺘﺘﻡ ﺇﻀﺎﻓﺔ ﺍﻟﺠﺩﺍﻭل ﺍﻟﺠﺩﻴﺩﺓ ﺇﻟﻴﻪ‪ ،‬ﻟﻜﻥ‬
‫ﺨﻁﺄ ﺴﻴﺤﺩﺙ ﻟﻭ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺠﺩﻭل ﻟﻪ ﻨﻔـﺱ‬
‫ﺍﺴﻡ ﺠﺩﻭل ﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﻤﺨﻁﻁ‪.‬‬
‫ﻭﻴﺅﺩﻱ ﻁﻠﺏ ﻗﺭﺍﺀﺓ ﺍﻟﻤﺨﻁﻁ ﻤﻥ ﻤﻠﻑ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻘﻁ‪ ،‬ﺇﻟـﻰ‬
‫ﻋﺩﻡ ﺘﺤﻤﻴل ﺃﻱ ﻤﻨﻬﻤﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ!‬
‫ﻗﺭﺍﺀﺓ ﺍﻟﺴﺠﻼﺕ ﻓﻘﻁ‪ ،‬ﻤﻊ ﺘﺠﺎﻫل ﺃﻱ ﻤﺨﻁﻁ ﻤﻭﺠﻭﺩ‪.‬‬ ‫‪Ignore‬‬
‫‪Schema‬‬
‫ﺘﺘﺠﺎﻫل ﺃﻱ ﻤﺨﻁﻁ ﻓﻲ ﺍﻟﻤﻠﻑ‪ ،‬ﻭﺘﺤﺎﻭل ﺍﺴﺘﻨﺘﺎﺝ ﺍﻟﻤﺨﻁﻁ ﻤـﻥ ﺒﻴﺎﻨـﺎﺕ‬ ‫‪Infer‬‬
‫‪Schema‬‬
‫ﺍﻟﺴﺠﻼﺕ‪ ،‬ﻭﺘﻀﻴﻑ ﺍﻟﻤﺨﻁﻁ ﻭﺍﻟﺴﺠﻼﺕ ﺇﻟـﻰ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪..‬‬

‫‪٢١٩‬‬
‫ﻭﻴﺤﺩﺙ ﺨﻁﺄ ﺇﺫﺍ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﺨﻁﻁ ﺒﺎﻟﻔﻌـل‪،‬‬
‫ﻭﻜﺎﻥ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻤﻭﺩ ﺘﺘﻌﺎﺭﺽ ﺘﻔﺎﺼﻴﻠﻪ ﻤﻊ ﻋﻤـﻭﺩ ﻤﻭﺠـﻭﺩ ﻓـﻲ‬
‫ﺍﻟﻤﺨﻁﻁ ﺍﻟﻤﻀﺎﻑ‪.‬‬
‫ﻤﻤﺎﺜﻠﺔ ﻟﻠﻘﻴﻤﺔ ﺍﻟﺴﺎﺒﻘﺔ‪ ،‬ﺇﻻ ﺃﻨﻬﺎ ﺘﺴﺘﻨﺞ ﻨﻭﻉ ﺒﻴﺎﻨﺎﺕ ﻜل ﻋﻤﻭﺩ‪ ،‬ﻓﺈﻥ ﻓﺸﻠﺕ‬ ‫‪Infer‬‬
‫‪Typed‬‬
‫ﺘﻌﺘﺒﺭ ﺃﻥ ﻨﻭﻉ ﺍﻟﻌﻤﻭﺩ ‪.String‬‬ ‫‪Schema‬‬
‫ﺘﻘﺭﺃ ﺍﻟﺴﺠﻼﺕ ﺍﻷﺼﻠﻴﺔ ﻭﺍﻟﺤﺎﻟﻴﺔ ﻤﻥ ﺍﻟﻤﻠﻑ‪ ،‬ﻭﺫﻟﻙ ﺇﺫﺍ ﻜﻨﺕ ﺤﻔﻅﺘﻬﺎ ﻓﻴﻪ‬ ‫‪Diff‬‬
‫‪Gram‬‬
‫ﺴﺎﺒﻘﺎ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻘﻴﻤﺔ ‪ ..DiffGram‬ﻭﺇﺫﺍ ﻜﺎﻨـﺕ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺴﺠﻼﺕ ﺒﺎﻟﻔﻌل ﻓﺴﺘﺤﺘﻔﻅ ﺒﻬﺎ‪ ،‬ﻭﺴﺘﻀﺎﻑ ﺇﻟﻴﻬﺎ ﺍﻟﺴـﺠﻼﺕ‬
‫ﺍﻟﺠﺩﻴﺩﺓ‪.‬‬
‫‪ Fragment‬ﺍﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻘﻴﻤﺔ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻠﻑ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺠﺯﺍﺀ ﻤﻥ ﻜﻭﺩ ‪XML‬‬
‫ﻭﻟﻴﺱ ﻜﻭﺩ ﻭﺜﻴﻘﺔ ﻜﺎﻤﻠﺔ‪.‬‬

‫ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﺯﺭ "ﻗـﺭﺍﺀﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤـﻥ ﻤﻠـﻑ" ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ‬
‫‪ ،DataSetSample‬ﻭﺃﺭﺴﻠﻨﺎ ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤـل ﺍﻟﺜـﺎﻨﻲ ﺍﻟﻘﻴﻤـﺔ ‪ ReadSchema‬ﻟﻘـﺭﺍﺀﺓ‬
‫ﺍﻟﻤﺨﻁﻁ ﻤﻊ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻫﺫﺍ ﻴﻀﻤﻥ ﻟﻨﺎ ﺇﻨﺸﺎﺀ ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺠﺩﻭﻟﻴﻥ ﻓﻲ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻷﻥ ﻭﻅﻴﻔﺔ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺘﺤﺘﺎﺠﻬﺎ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﺍﻟﻭﺴﻴﻠﺔ ‪ ReadXml‬ﻻ ﺘﺴﺘﺩﻋﻲ ﺍﻟﻭﺴـﻴﻠﺔ ‪ AcceptChanges‬ﺘﻠﻘﺎﺌﻴـﺎ ﻜﻤـﺎ‬
‫ﺘﻔﻌل ﺍﻟﻭﺴﻴﻠﺔ ‪ ،DataAdapter.Fill‬ﻟﻬﺫﺍ ﻓﺈﻥ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﻴﺘﻡ ﺘﺤﻤﻴﻠﻬﺎ ﻓﻲ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺴﺘﻌﺘﺒﺭ ﺴﺠﻼﺕ ﺠﺩﻴﺩﺓ ‪ ،Addedd‬ﻭﻟﻭ ﻀﻐﻁﺕ ﺯﺭ ﺍﻟﺤﻔﻅ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪،‬‬
‫ﻓﺴﻴﺘﻡ ﺇﻀﺎﻓﺔ ﻜل ﻫﺫﻩ ﺍﻟﺴﺠﻼﺕ ﻤﺭﺓ ﺃﺨﺭﻯ ﺇﻟﻰ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺠﺩﻭل ﺍﻟﻜﺘﺏ‪ ،‬ﻭﻫـﺫﺍ‬
‫ﺴﻴﺠﻌل ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤﻜـﺭﺭﺓ!‪ ..‬ﻭﻟﺤـل ﻫـﺫﻩ ﺍﻟﻤﺸـﻜﻠﺔ‪ ،‬ﻋﻠﻴـﻙ ﺍﺴـﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪ AcceptChanges‬ﻤﺒﺎﺸﺭﺓ ﺒﻌﺩ ﺘﺤﻤﻴل ﺍﻟﺴﺠﻼﺕ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺒﻬـﺫﺍ ﻴـﺘﻡ‬
‫ﺍﻋﺘﺒﺎﺭ ﺃﻨﻬﺎ ﻟﻡ ﺘﺘﻐﻴﺭ‪ ،‬ﻭﻻ ﻴﺘﻡ ﺤﻔﻅﻬﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻟﻜﻨﻙ ﻗﺩ ﺘﺭﻴﺩ ﺍﻋﺘﺒﺎﺭ ﺍﻟﺴﺠﻼﺕ ﺠﺩﻴﺩﺓ ﻓﻲ ﺒﻌﺽ ﺍﻟﻤﻭﺍﻗﻑ‪ ،‬ﻭﺫﻟﻙ ﺇﺫﺍ ﻜﻨﺕ ﺘﻤﻠﻙ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫ﻓﻲ ﻤﻠﻑ ‪ XML‬ﻭﺘﺭﻴﺩ ﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻓﺎﺭﻏﺔ‪.‬‬

‫‪٢٢٠‬‬
‫ﻭﻫﻨﺎﻙ ﻤﻼﺤﻅﺔ ﺒﺴﻴﻁﺔ ﺃﺨﺭﻯ‪ ،‬ﻭﻫﻲ ﺃﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻻ ﻴﻬﻤﻬﺎ ﺍﻤﺘﺩﺍﺩ ﺍﻟﻤﻠﻑ‪ ،‬ﺒل ﻴﻬﻤﻬـﺎ‬
‫ﻓﻘﻁ ﺼﺤﺔ ﻤﺤﺘﻭﻴﺎﺘﻪ‪ ..‬ﻟﻬﺫﺍ ﻓﻘـﺩ ﺃﻋﻁﻴﻨـﺎ ﻟﻠﻤﻠﻔـﺎﺕ ﺍﻟﺨﺎﺼـﺔ ﺒﻨـﺎ ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ‬
‫‪ CustomDataSet‬ﺍﻻﻤﺘﺩﺍﺩ ‪ ،.dsf‬ﻭﻫﻲ ﺍﻤﺘﺩﺍﺩ ﻤـﻥ ﺍﺨﺘﺭﺍﻋﻨـﺎ )ﺍﺨﺘﺼـﺎﺭ ﻟﻠﺘﻌﺒﻴـﺭ‬
‫‪ ،(DataSet Format‬ﻭﺠﻌﻠﻨﺎ ﻤﺭﺒﻊ ﺤﻭﺍﺭ ﻓﺘﺢ ﻤﻠﻑ ﻻ ﻴﻌﺭﺽ ﺴﻭﻯ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺘﻲ ﻟﻬـﺎ‬
‫ﻫﺫﺍ ﺍﻻﻤﺘﺩﺍﺩ‪ ،‬ﻭﺒﻬﺫﺍ ﻨﻀﻤﻥ ﺃﻥ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺘﻲ ﻨﺤﺎﻭل ﻗﺭﺍﺀﺘﻬﺎ ﺴﻴﻜﻭﻥ ﻟﻬﺎ ﺍﻟﺼﻴﻐﺔ ﺍﻟﻤﻨﺎﺴﺒﺔ‬
‫ﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻓﻤﻠﻔﺎﺕ ‪ XML‬ﺘﺴﺘﻁﻴﻊ ﺤﻤل ﺃﻱ ﻨﻭﻉ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺒـﺄﻱ ﺘﻨﺴـﻴﻕ‪،‬‬
‫ﻟﻜﻨﻬﺎ ﻟﻥ ﺘﻜﻭﻥ ﺠﻤﻴﻌﺎ ﺼﺎﻟﺤﺔ ﻟﻠﻌﺭﺽ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻨﺎ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺤﺩﺙ ﺍﻟﺘﺎﻟﻲ‪:‬‬

‫ﻓﺸل ﺍﻟﺩﻤﺞ ‪:MergeFailed‬‬


‫ﻴﻨﻁﻠﻕ ﺇﺫﺍ ﻓﺸﻠﺕ ﻋﻤﻠ ‪‬ﻴ‪‬ﺔ ﺩﻤﺞ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻭﻟﻴﻥ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ ..Merge‬ﻴﺤـﺩﺙ ﻫـﺫﺍ‬
‫ﻤﺜﻼ‪ ،‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻜﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﻘﺎﺩﻡ‪ ،‬ﻤﺨﺘﻠﻔﺎ ﻋـﻥ ﺍﻟﻌﻤـﻭﺩ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ ﻜﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﻤﻭﺠﻭﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،MergeFailedEventArgs‬ﻭﻫـﻭ ﻴﻤﺘﻠـﻙ‬
‫ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ‪ DataTable‬ﺍﻟﺫﻱ ﺭﻓﺽ ﻋﻤﻠﻴﺔ ﺍﻟﺩﻤﺞ‪.‬‬ ‫‪Table‬‬


‫‪ Conflict‬ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﺸﺭﺡ ﺴﺒﺏ ﺍﻟﺘﻌﺎﺭﺽ ﺍﻟﺫﻱ ﺃﺩﻯ ﺇﻟﻰ ﻓﺸـل ﻋﻤﻠﻴـﺔ‬
‫ﺍﻟﺩﻤﺞ‪.‬‬

‫‪٢٢١‬‬
‫ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﻹﻨﺸﺎﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫‪Generate DataSet Wezard‬‬

‫ﺘﺘﻴﺢ ﻟﻙ ﺩﻭﺕ ﻨﺕ ﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ﻹﻨﺸﺎﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺁﻟﻴﺎ‪ ..‬ﻟﻔﻌل ﻫـﺫﺍ‪ ،‬ﺃﻀـﻑ ﻤﻬﻴـﺊ‬
‫ﺒﻴﺎﻨﺎﺕ ‪ Data Adapter‬ﺇﻟﻰ ﺼﻴﻨﻴﺔ ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤﻭﺫﺝ‪ ،‬ﻭﺍﻀﺒﻁ ﺨﺼﺎﺌﺼﻪ ﻜﻤﺎ ﺘﻌﻠﻤﻨـﺎ ﻤـﻥ‬
‫ﻗﺒل‪ ،‬ﺜﻡ ﺍﻀﻐﻁﻪ ﺒﺯﺭ‪ ‬ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ‪ ،‬ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴ‪‬ﺔ ﺍﻀﻐﻁ ﺍﻷﻤﺭ "ﺇﻨﺘـﺎﺝ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ" ‪ ..Generate Dataset‬ﻭﺴﺘﺠﺩ ﻨﻔﺱ ﺍﻷﻤﺭ ﻓﻲ ﺍﻟﻘﺎﺌﻤـﺔ ﺍﻟﺭﺌﻴﺴـﻴ‪‬ﺔ ‪ Data‬ﺃﻋﻠـﻰ‬
‫ﺍﻟﻨﺎﻓﺫﺓ‪.‬‬
‫ﺴﻴﻅﻬﺭ ﻟﻙ ﻤﺭ‪‬ﺒﻊ ﺤﻭﺍﺭ "ﺇﻨﺘﺎﺝ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ" ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﺒﺎﻟﺼﻭﺭﺓ‪:‬‬
‫ﻓﻲ ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﻴﻤﻜﻨﻙ ﺍﺨﺘﻴﺎﺭ ﺇﻨﺸـﺎﺀ‬
‫ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻤﺨﻁﻁ ﻤﺠﻤﻭﻋﺔ‬
‫ﺒﻴﺎﻨﺎﺕ ﻤﻭﺠﻭﺩ ﺴﺎﺒﻘﺎ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ،‬ﺃﻭ‬
‫ﺇﻨﺸــﺎﺀ ﻤﺨﻁــﻁ ﺠﺩﻴــﺩ ﺍﺴــﻤﻪ‬
‫‪ ..DataSet1‬ﻻﺤﻅ ﺃﻨـﻙ ﺘﺴـﺘﻁﻴﻊ‬
‫ﺘﻐﻴﻴﺭ ﻫﺫﺍ ﺍﻻﺴﻡ‪ ،‬ﻭﺍﻷﻓﻀل ﺍﺨﺘﻴـﺎﺭ‬
‫ﺍﺴﻡ ﺃﻜﺜﺭ ﺘﻌﺒﻴﺭﺍ ﻋﻥ ﻭﻅﻴﻔﺔ ﻤﺠﻤﻭﻋﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﺘﻌﺭﺽ ﻟﻙ ﺍﻟﻨﺎﻓﺫﺓ ﻗﺎﺌﻤـﺔ ﺒﺄﺴـﻤﺎﺀ‬
‫ﺍﻟﺠﺩﺍﻭل ﺍﻟﺘﻲ ﻴﻭﻓﺭﻫﺎ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‪،‬‬
‫ﻟﻴﻤﻜﻨﻙ ﺍﺨﺘﻴﺎﺭ ﺇﻀﺎﻓﺘﻬﺎ ﺠﻤﻴﻌﺎ ﺇﻟـﻰ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻭ ﺤﺫﻑ ﺒﻌﻀﻬﺎ‪.‬‬
‫ﻭﻴﻭﺠﺩ ﺍﺨﺘﻴﺎﺭ ﺃﺴﻔل ﺍﻟﻨﺎﻓﺫﺓ‪ ،‬ﻴﺤﺩﺩ ﺇﺫﺍ ﻜﻨﺕ ﺘﺭﻴﺩ ﺇﻀﺎﻓﺔ ﻨﺴﺨﺔ ﻤﻥ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺇﻟـﻰ‬
‫ﺍﻟﻨﻤﻭﺫﺝ ﺃﻡ ﻻ‪.‬‬
‫ﺒﻌﺩ ﺃﻥ ﺘﺤﺩﺩ ﺍﺨﺘﻴﺎﺭﺍﺘﻙ ﺍﻀﻐﻁ ‪ Ok‬ﻹﻏﻼﻕ ﺍﻟﻨﺎﻓﺫﺓ‪ ..‬ﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻟﻰ ﻤﺎ ﻴﻠﻲ‪:‬‬
‫‪ -‬ﺇﻀﺎﻓﺔ ﻤﻠﻑﹼ ﺍﺴﻤﻪ ‪ DataSet1.xsd‬ﺇﻟﻰ ﻤﻠﻔﺎﺕ ﺍﻟﻤﺸﺭﻭﻉ ﺍﻟﺘـﻲ ﻴﻌﺭﻀـﻬﺎ ﻤﺘﺼـﻔﺢ‬
‫ﺍﻟﻤﺸﺎﺭﻴﻊ ‪ ..Solution Explorer‬ﻭﺍﻻﻤﺘﺩﺍﺩ ‪ xsd‬ﻫﻭ ﺍﺨﺘﺼﺎﺭ ﻟﻠﺘﻌﺒﻴﺭ "ﻟﻐﺔ ﺘﻌﺭﻴـﻑ‬

‫‪٢٢٢‬‬
‫ﺍﻟﻤﺨﻁﻁ" ‪ ،Xml Schema Definition‬ﻟﻬﺫﺍ ﻟﻭ ﻓﺘﺤﺕ ﻫـﺫﺍ ﺍﻟﻤﻠـﻑ ﻤـﻥ ﻤﺠﻠـﺩ‬
‫ﺍﻟﻤﺸﺭﻭﻉ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺒﺭﻨﺎﻤﺞ ‪ ،Notepad‬ﻓﺴﺘﺠﺩﻩ ﻴﺤﺘﻭﻱ ﻋﻠـﻰ ﻜـﻭﺩ ‪ XML‬ﺍﻟـﺫﻱ‬
‫ﻴﻌﺭﻑ ﻤﺨﻁﻁ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ )ﺍﻟﺠـﺩﺍﻭل ﻭﺍﻷﻋﻤـﺩﺓ ﻭﺍﻟﻌﻼﻗـﺎﺕ ﻭﺍﻟﻘﻴـﻭﺩ ﺍﻟﺘـﻲ‬
‫ﺘﺤﺘﻭﻴﻬﺎ(‪ ..‬ﺃﻤﺎ ﻟﻭ ﻨﻘﺭﺕ ﻫﺫﺍ ﺍﻟﻤﻠﻑ ﻤﺭﺘﻴﻥ ﺒﺎﻟﻔﺄﺭﺓ ﻓﻲ ﻤﺘﺼﻔﺢ ﺍﻟﻤﺸﺎﺭﻴﻊ‪ ،‬ﻓﺴـﺘﻌﺭﺽ‬
‫ﻟﻙ ﺩﻭﺕ ﻨﺕ ﻨﺎﻓﺫﺓ ﻤﺼﻤﻡ ﺍﻟﻤﺨﻁﻁ ‪ ،Schema Designer‬ﻭﺴﺘﺠﺩ ﻓﻴﻬﺎ ﺭﺴﻤﺎ ﻤﺒﺴﻁﺎ‬
‫ﻴﻤﺜل ﺍﻟﺠﺩﺍﻭل ﻭﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﻤﺨﻁﻁ‪ ،‬ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﺒﺎﻟﺼﻭﺭﺓ‪:‬‬

‫‪ -‬ﺇﻨﺸﺎﺀ ﻓﺌﺔ ﺨﺎﺼﺔ ﺍﺴﻤﻬﺎ ‪ DataSet1‬ﺘـﺭﺙ ﻓﺌـﺔ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ‪DataSet‬‬


‫‪ ..Class‬ﻜﻭﺩ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻴﻭﻀﻊ ﻓـﻲ ﺍﻟﻤﻠـﻑ ‪ ،DataSet1.Designer.cs‬ﻭﺍﻟـﺫﻱ‬
‫ﺴﺘﺠﺩﻩ ﻓﻲ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻔﺭﻋﻴﺔ ﻟﻠﻤﺨﻁﻁ ‪ DataSet1.xsd‬ﺇﺫﺍ ﻋﺭﻀـﺕ ﻜـل ﻤﻠﻔـﺎﺕ‬
‫ﺍﻟﻤﺸﺭﻭﻉ ﺒﻀﻐﻁ ﺍﻟﺯﺭ ‪ Show All Files‬ﺍﻟﻤﻭﺠﻭﺩ ﺃﻋﻠﻰ ﻤﺘﺼﻔﺢ ﺍﻟﻤﺸﺎﺭﻴﻊ‪.‬‬
‫ﻭﺘﺴـــﻤﻰ ﺍﻟﻔﺌـــﺔ ‪ DataSet1‬ﺒﻤﺠﻤﻭﻋـــﺔ ﺍﻟﺒﻴﺎﻨـــﺎﺕ ﻤﺤـــﺩﺩﺓ ﺍﻟﻨـــﻭﻉ‬
‫‪ ،Typed DataSet‬ﻭﺴﻨﺘﻌﺭﻑ ﺒﻌﺩ ﻗﻠﻴل ﻋﻠﻰ ﻤﻌﻨﻰ ﻫﺫﺍ ﺍﻟﻤﺴﻤﻰ ﻭﻓﺎﺌﺩﺘﻪ‪.‬‬

‫‪٢٢٣‬‬
‫‪ -‬ﺇﻀﺎﻓﺔ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺍﺴـﻤﻬﺎ ‪ 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‬‬
‫ﻭﻴﻤﻜﻨﻙ ﻤﻥ ﺨﻼﻟﻬﺎ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ‪.‬‬

‫ﻟﻜﻥ ﻟﻤﺎﺫﺍ ﻜل ﻫﺫﺍ؟‪ ..‬ﻭﺒﻡ ﺘﻔﻴﺩﻨﺎ ﺍﻟﻤﺠﻤﻭﻋﺔ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﻴﺎ ﺘﺭﻯ؟‬


‫ﺍﻨﻅﺭ ﻤﺜﻼ ﺇﻟﻰ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ‪ ،‬ﺍﻟﺘﻲ ﺘﻘﺭﺃ ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﺼﻑﹼ ﺍﻟﺜﺎﻟﺙ ﻓﻲ ﺠـﺩﻭل‬
‫ﺍﻟﻤﺅﻟﻔﻴﻥ‪:‬‬
‫;]"‪var A = DataSet11.Tables["Authors"].Rows[2]["Author‬‬
‫ﻭﺍﻀﺢ ﻁﺒﻌﺎ ﺃﻨﻬﺎ ﺠﻤﻠﺔ ﻁﻭﻴﻠﺔ ﺘﺩﻓﻊ ﺇﻟﻰ ﺍﻻﺴﺘﻴﺎﺀ‪ ..‬ﻓﻤﺎ ﺭﺃﻴﻙ ﺇﺫﻥ ﻓﻲ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫;‪var A = DataSet11.Authors[2].Author‬‬
‫ﺇﻥ‪ ‬ﺍﻟﺠﻤﻠﺘﻴﻥ ﻜﻠﺘﻴﻬﻤﺎ ـ ﻭﻴﺎ ﻟﻠﻌﺠﺏ ـ ﻤﺘﻜﺎﻓﺌﺘﺎﻥ‪ ،‬ﻭﺇﻥ ﻜﺎﻨﺕ ﺍﻷﻭﻟﻰ ﻋﺎ ‪‬ﻤ‪‬ﺔ ﺘﺴﺘﺨﺩﻡ ﺨﺼـﺎﺌﺹ‬
‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﻡ ‪ ،DataSet Class‬ﺒﻴﻨﻤﺎ ﺍﻟﺜﺎﻨﻴـﺔ ﺨﺎﺼ‪‬ـﺔ‪ ،‬ﺘﺴـﺘﺨﺩﻡ ﺨﺼـﺎﺌﺹ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataSet1‬ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺜﺎﻨﻴـﺔ ﺘﻤﻨﺤـﻙ ﺍﻟﻤﻴـﺯﺍﺕ‬
‫ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﻤﺨﺘﺼﺭﺓ ﻭﻭﺍﻀﺤﺔ ﻭﻤﻔﻬﻭﻤﺔ‪.‬‬
‫‪ -٢‬ﺃﻗل ﻋﺭﻀﺔ ﻟﻠﺨﻁﺄ‪ ..‬ﻓﻔﻲ ﺍﻟﺠﻤﻠﺔ ﺍﻷﻭﻟﻰ )ﺍﻟﻁﻭﻴﻠﺔ( ﻫﻨﺎﻙ ﺍﺤﺘﻤﺎﻻﻥ ﻟﻠﺨﻁـﺄ‪ ،‬ﻭﺫﻟـﻙ‬
‫ﺃﺜﻨﺎﺀ ﻜﺘﺎﺒﺘﻙ ﻻﺴﻤﻲ ﺍﻟﺠﺩﻭل ‪ Authors‬ﻭﺍﻟﻌﻤﻭﺩ ‪ ،Author‬ﻷﻨـﻙ ﺘﻜﺘﺒﻬﻤـﺎ ﻴـﺩﻭﻴﺎ‬

‫‪٢٢٧‬‬
‫ﻜﻨﺼﻭﺹ‪ ،‬ﻭﻻ ﻴﺘﻡ ﺍﻜﺘﺸﺎﻑ ﺃﻱ ﺨﻁﺄ ﻓﻴﻬﻤﺎ ﺇﻻ ﺃﺜﻨﺎﺀ ﺘﺸﻐﻴل ﺍﻟﺒﺭﻨـﺎﻤﺞ‪ ..‬ﺃﻤـﺎ ﻓـﻲ‬
‫ﺍﻟﺠﻤﻠﺔ ﺍﻟﺜﺎﻨﻴﺔ )ﺍﻟﻘﺼﻴﺭﺓ(‪ ،‬ﻓﺈﻨﻙ ﺘﺘﻌﺎﻤل ﻤﻊ ﺨﺼـﺎﺌﺹ ﻤﻌﺭﻓـﺔ ﺴـﺎﺒﻘﺎ ﻓـﻲ ﺍﻟﻔﺌـﺔ‬
‫‪ ،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‬ﻭﺃﻀﻔﻨﺎ ﺇﻟﻴـﻪ‬
‫ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻭﻅﻴﻔﺘﻪ‬ ‫ﻨﻭﻉ ﺒﻴﺎﻨﺎﺘﻪ‬ ‫ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ‬


‫ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ‪ ،‬ﻭﻫﻭ ﻴﻌﻤل ﺃﻴﻀﺎ ﻜﺘﺭﻗﻴﻡ ﺘﻠﻘﺎﺌﻲ‪.‬‬ ‫‪Int16‬‬ ‫‪ID‬‬
‫ﻴﺤﻔﻅ ﻨﺹ ﻓﺭﻉ ﺍﻟﺸﺠﺭﺓ‪.‬‬ ‫‪String‬‬ ‫‪Text‬‬
‫ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ‪ ،‬ﻭﻫﻭ ﻴﺸﻴﺭ ﺇﻟﻰ ﺭﻗﻡ ﺍﻟﻔﺭﻉ ﺍﻟﺭﺌﻴﺴﻲ ﻟﻠﻔـﺭﻉ‬
‫‪Int16‬‬ ‫‪ParentID‬‬
‫ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﻭﻗﺩ ﺃﻀﻔﻨﺎ ﻋﻼﻗﺔ ﺇﻟﻰ ﻤﺨﻁﻁ ﺍﻟﺠﺩﻭل‪ ،‬ﻟﺘﺭﺒﻁ ﺒﻴﻥ ﺍﻟﺤﻘﻠﻴﻥ ‪ 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‬ﻟﻤﻌﺭﻓﺔ ﺍﻟﺼﻔﻭﻑ ﺍﻟﻔﺭﻋﻴﺔ ﺍﻟﺘﺎﺒﻌﺔ ﻷﻱ ﺼﻑ ﺭﺌﻴﺴﻲ‪ ..‬ﻫﺫﺍ ﻫﻭ ﻜﻭﺩ ﻫـﺫﺍ‬
‫ﺍﻹﺠﺭﺍﺀ‪:‬‬

‫‪public void LoadChildren(TreeNode ParentNode,‬‬


‫)‪TreeDataSet.TreeNodesRow ParentRow‬‬
‫{‬
‫‪foreach (TreeDataSet.TreeNodesRow R in‬‬
‫))"‪ParentRow.GetChildRows("Self_Relation‬‬
‫{‬
‫;)‪var Node = ParentNode.Nodes.Add(R.Text‬‬
‫;)‪LoadChildren(Node, R‬‬
‫}‬
‫}‬
‫ﻫﺫﺍ ﻫﻭ ﻜل ﺸﻲﺀ‪ ..‬ﻴﻤﻜﻨﻙ ﺍﻵﻥ ﺘﺠﺭﺒﺔ ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ،‬ﻭﺇﻀﺎﻓﺔ ﺍﻟﻌﻨﺎﺼﺭ ﺇﻟﻰ ﺍﻟﺸﺠﺭﺓ‪ ،‬ﻭﺤﻔﻅﻬـﺎ‪،‬‬
‫ﺜﻡ ﺍﺴﺘﺭﺠﺎﻋﻬﺎ ﻓﻲ ﺃﻱ ﻭﻗﺕ‪.‬‬
‫ﺭﺍﺌﻌﺔ ﻫﻲ ﺍﻟﻌﻼﻗﺔ ﺍﻟﺫﺍﺘﻴﺔ؟‪ ..‬ﺃﻟﻴﺱ ﻜﺫﻟﻙ؟‬

‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ‬

‫‪٢٣٩‬‬
‫ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ‪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‬ﻹﻨﺸﺎﺀ ﺍﺘﺼﺎل ﺠﺩﻴﺩ‪.‬‬

‫ﻤﺠﺎل ﺍﻻﺘﺼﺎل ‪:ConnectionModifier‬‬


‫ﺘﺤﺩﺩ ﻤﺠﺎل ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﻤﻌﺭﻑ ﻓﻲ ﻤﻬﻴﺊ ﺍﻟﺠـﺩﻭل‪ ..‬ﻭﺍﻟﻘﻴﻤـﺔ ﺍﻻﻓﺘﺭﺍﻀـﻴﺔ ﻫـﻲ‬
‫‪ Friend‬ﻟﺠﻌﻠﻪ ﻤﺭﺌﻴﺎ ﻤﻥ ﺃﻱ ﻤﻭﻀﻊ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ‪.‬‬
‫ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ‪:SelectCommand‬‬
‫ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺩ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻹﺤﻀﺎﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻻﺤﻅ ﺃﻨﻙ ﻟﻭ ﺍﺴـﺘﺨﺩﻤﺕ‬
‫ﺃﻤﺭ ﺘﺤﺩﻴﺩ ﻴﻌﻴﺩ ﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﺠﺩﻭل‪ ،‬ﻓﺴﻴﻌﺠﺯ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻋﻥ ﺇﻨﺘـﺎﺝ ﺃﻭﺍﻤـﺭ‬
‫ﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺤﺫﻑ ﺁﻟﻴﺎ‪ ،‬ﻟﻬﺫﺍ ﻴﺘﻭﺠﺏ ﻋﻠﻴﻙ ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺼﺎﺌﺹ‬
‫ﺍﻟﺘﺎﻟﻴﺔ ﻟﺘﻌﺭﻴﻑ ﻫﺫﻩ ﺍﻷﻭﺍﻤﺭ ﺒﻨﻔﺴﻙ‪.‬‬

‫ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ‪:UpdateCommand‬‬


‫ﺃﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﺘﺤﺩﻴﺙ ﺴﺠﻼﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺃﻤﺭ ﺍﻹﺩﺭﺍﺝ ‪:InsertCommand‬‬


‫ﺃﻤﺭ ﺍﻹﺩﺭﺍﺝ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻹﺩﺭﺍﺝ ﺍﻟﺴﺠﻼﺕ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫‪٢٤٣‬‬
‫ﺃﻤﺭ ﺍﻟﺤﺫﻑ ‪:DeleteCommand‬‬
‫ﺃﻤﺭ ﺍﻟﺤﺫﻑ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﺤﺫﻑ ﺍﻟﺴﺠﻼﺕ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺇﻨﺘﺎﺝ ﻭﺴﺎﺌل ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺒﺎﺸﺭﺓ ‪:GenerateDbDirectMethods‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،True‬ﻓﺴﺘﻀﺎﻑ ﺇﻟﻰ ﻓﺌـﺔ ﻤﻬﻴـﺊ ﺍﻟﺠـﺩﻭل ﺍﻟﻭﺴـﺎﺌل‬
‫‪ Update‬ﻭ ‪ Insert‬ﻭ ‪ Delete‬ﻟﺘﺘﻴﺢ ﻟﻙ ﺘﺤﺩﻴﺙ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺎﺴﺘﺩﻋﺎﺌﻬﺎ ﻤﺒﺎﺸـﺭﺓ‪..‬‬
‫ﺃﻤﺎ ﺇﺫﺍ ﺠﻌﻠﺘﻬﺎ ‪ ،False‬ﻓﺴﻴﻜﻭﻥ ﻋﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻜﺎﺌﻨﺎﺕ ﺍﻷﻭﺍﻤﺭ ﺒﻨﻔﺴﻙ ﻹﺠﺭﺍﺀ ﻋﻤﻠﻴـﺎﺕ‬
‫ﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺤﺫﻑ‪ ،‬ﻭﺫﻟـﻙ ﻤـﻥ ﺨـﻼل ﺍﻟﺨﺼـﺎﺌﺹ ‪،UpdateCommand‬‬
‫‪.DeleteCommand ،InsertCommand‬‬

‫ﻭﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺠـﺩﻭﻟﻴﻥ ﺴـﻴﺘﻡ‬
‫ﺇﻨﺸﺎﺅﻫﺎ ﺘﻠﻘﺎﺌﻴﺎ ﺒﻤﺠﺭﺩ ﺇﻀﺎﻓﺔ ﺠﺩﻭل ﺍﻟﻜﺘﺏ‪ ..‬ﻫﻜﺫﺍ ﺴﻴﻜﻭﻥ ﺍﻟﻤﺨﻁﻁ‪:‬‬

‫ﻭﻟﻜﻥ‪ ،‬ﺃﻴﻥ ﻴﺘﻡ ﺇﻨﺸﺎﺀ ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ؟‬


‫ﻴﺘﻡ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻓﻲ ﻨﻔﺱ ﺍﻟﻤﻠﻑ ﺍﻟﺫﻱ ﺘﻭﺠﺩ ﻓﻴﻪ ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ‪ ،‬ﻭﻫﻭ‬
‫ﻓﻲ ﻤﺜﺎﻟﻨﺎ ﻫﺫﺍ ﺍﻟﻤﻠﻑ ‪ ..DsAuthorsBooks.Designer.cs‬ﻟﻜﻥ ﻓﺌـﺔ ﻤﻬﻴـﺊ ﺍﻟﺠـﺩﻭل ﻻ‬
‫ﺘﻭﻀﻊ ﺩﺍﺨل ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺒل ﺘﻭﻀـﻊ ﺨﺎﺭﺠﻬـﺎ‪ ،‬ﻭﻴـﺘﻡ ﺘﻌﺭﻴـﻑ ﻨﻁـﺎﻕ ﺍﺴـﻡ‬

‫‪٢٤٤‬‬
‫‪ Namespace‬ﺨﺎﺹ ﺒﻬﺎ ﻴﻜﻭﻥ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ‪ ،XTableAdapters‬ﺤﻴﺙ ‪ X‬ﻫﻭ ﺍﺴﻡ ﻓﺌـﺔ‬
‫ﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻫﻜﺫﺍ ﻤﺜﻼ ﺴﻴﻜﻭﻥ ﺍﻟﺸﻜل ﺍﻟﻌﺎﻡ ﻟﻤﻠﻑ ﻜﻭﺩ ﻓﺌﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ﻓـﻲ‬
‫ﻤﺸﺭﻭﻋﻨﺎ ﻫﺫﺍ‪:‬‬
‫{ ‪public partial class DsAuthorsBooks :System.Data.DataSet‬‬
‫ﻜﻭﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ‪//‬‬
‫}‬

‫{ ‪namespace DsAuthorsBooksTableAdapters‬‬
‫{ ‪public partial class AuthorsTableAdapter : Component‬‬
‫ﻜﻭﺩ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ‪//‬‬
‫}‬

‫{ ‪public partial class BooksTableAdapter : Component‬‬


‫ﻜﻭﺩ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ‪//‬‬
‫}‬

‫{ ‪public partial class TableAdapterManager : Component‬‬


‫ﻜﻭﺩ ﻤﺩﻴﺭ ﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ‪//‬‬
‫}‬
‫}‬
‫ﻭﺘﻤﺘﻠﻙ ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻻﺘﺼﺎل ‪:Connection‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﺫﻱ ﻴﺴﺘﺨﺩﻤﻪ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻟﻼﺘﺼﺎل ﺒﻘﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪..‬‬
‫ﻭﻴﻌﺘﻤﺩ ﻨﻭﻉ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻋﻠﻰ ﻨﻭﻉ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﺘﻌﺎﻤل ﻤﻌﻬﺎ‪ ،‬ﻭﻓﻲ ﻤﺜﺎﻟﻨﺎ ﻫـﺫﺍ‬
‫ﺴﺘﻜﻭﻥ ﻤﻥ ﺍﻟﻨﻭﻉ ‪.SqlConnection‬‬
‫ﻭﻴﺴﺘﺨﺩﻡ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺇﺠﺭﺍﺀﺍ ﺨﺎﺼﺎ ‪ Private Sub‬ﺍﺴﻤﻪ ‪ InitConnection‬ﻟﻀﺒﻁ‬
‫ﺨﺼﺎﺌﺹ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل‪.‬‬

‫‪٢٤٥‬‬
‫ﺍﻻﻨﺘﻘﺎﻻﺕ ‪:Transaction‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﺍﻻﻨﺘﻘﺎﻻﺕ ﺍﻟﺫﻱ ﻴﺴﺘﺨﺩﻤﻪ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻟﻠﺘﺤﻜﻡ ﻓﻲ ﺍﻟﻌﻤﻠﻴﺎﺕ ﺍﻟﺘﻲ ﺘﺘﻡ‬
‫ﻋﻠﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ..‬ﻭﻓـﻲ ﻤﺜﺎﻟﻨـﺎ ﻫـﺫﺍ ﺴـﺘﻜﻭﻥ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪.SqlTransaction‬‬

‫ﻤﺤﻭ ﻗﺒل ﺍﻟﻤلﺀ ‪:ClearBeforeFill‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪) True‬ﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ(‪ ،‬ﻓﺴﻴﺘﻡ ﻤﺤﻭ ﺍﻟﺴـﺠﻼﺕ‬
‫ﻤﻥ ﺠﺩﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻭﻻ‪ ،‬ﻗﺒل ﻤﻠﺌﻪ ﺒﺎﻟﻨﺘﺎﺌﺞ ﺍﻟﺠﺩﻴﺩﺓ‪ ..‬ﺃﻤﺎ ﺇﺫﺍ ﺠﻌﻠﺘﻬـﺎ ‪،False‬‬
‫ﻓﺴﻴﺘﻡ ﺘﺤﺩﻴﺙ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﺒﺎﻟﻘﻴﻡ ﺍﻟﺠﺩﻴﺩﺓ‪ ،‬ﻭﺇﻀـﺎﻓﺔ ﺍﻟﺴـﺠﻼﺕ ﺍﻟﺠﺩﻴـﺩﺓ ﺇﻟـﻰ‬
‫ﺍﻟﺠﺩﻭل‪.‬‬

‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻴﺴﺘﺨﺩﻡ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺩﺍﺨﻠﻴﺎ ﻟﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﻬﺫﺍ ﺴﺘﺠﺩ ﻓـﻲ‬
‫ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺨﺎﺼﻴﺔ ﻤﺤﻤﻴـﺔ ‪ Protected Property‬ﺍﺴـﻤﻬﺎ ‪ ،Adapter‬ﻟـﻥ‬
‫ﺘﺭﺍﻫﺎ ﻤﻥ ﺨﺎﺭﺝ ﺍﻟﻔﺌﺔ‪ ،‬ﻟﻜﻥ ﺘﺴﺘﻁﻴﻊ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﺍﻟﻔﺌﺎﺕ ﺍﻟﺘـﻲ ﺘـﺭﺙ ﻓﺌـﺔ ﻤﻬﻴـﺊ‬
‫ﺍﻟﺠﺩﻭل‪ ،‬ﺃﻭ ﻓﻲ ﺃﻱ ﻜﻭﺩ ﺇﻀﺎﻓﻲ ﺘﻜﺘﺒﻪ ﻓﻲ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺒﻨﻔﺴﻙ‪ ..‬ﻭﻴﺴـﺘﺨﺩﻡ ﻤﻬﻴـﺊ‬
‫ﺍﻟﺠﺩﻭل ﺇﺠﺭﺍﺀ ﺨﺎﺼـﺎ ‪ private void‬ﺍﺴـﻤﻪ ‪ InitAdapter‬ﻟﻭﻀـﻊ ﺍﻟﻘـﻴﻡ ﻓـﻲ‬
‫ﺨﺼﺎﺌﺹ ﻤﻬﻴﺊ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻜﻤــﺎ ﻴﺤﺘــﻭﻱ ﻤﻬﻴــﺊ ﺍﻟﺠــﺩﻭل ﻋﻠــﻰ ﺨﺎﺼــﻴﺔ ﻤﺤﻤﻴــﺔ ﺃﺨــﺭﻯ ﺍﺴــﻤﻬﺎ‬
‫‪ ،CommandCollection‬ﻭﻫﻲ ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻜﺎﺌﻨﺎﺕ ﺍﻷﻭﺍﻤـﺭ ﺍﻟﺘـﻲ‬
‫ﻴﺴﺘﺨﺩﻤﻬﺎ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل‪ ،‬ﻭﺒﻬﺫﺍ ﻴﺴﺘﻁﻴﻊ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺃﻜﺜﺭ ﻤﻥ ﺍﺴـﺘﻌﻼﻡ‬
‫ﻜﻤﺎ ﺴﻨﺭﻯ ﻻﺤﻘﺎ‪.‬‬
‫ﻭﻴﺴﺘﺨﺩﻡ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺇﺠﺭﺍﺀ ﺨﺎﺼﺎ ﺍﺴـﻤﻪ ‪ InitCommandCollection‬ﻟﻭﻀـﻊ‬
‫ﻜﺎﺌﻨﺎﺕ ﺍﻷﻭﺍﻤﺭ ﻓﻲ ﻫﺫﻩ ﺍﻟﻤﺼﻔﻭﻓﺔ ﻭﻀﺒﻁ ﺨﺼﺎﺌﺼﻬﺎ‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬


‫‪٢٤٦‬‬
‫ﻤلﺀ ‪:Fill‬‬
‫ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻤﻥ ﻨﻭﻉ ﺍﻟﺠﺩﻭل ﺍﻟﻤﺭﺍﺩ ﻤﻠﺅﻩ ﺒﺎﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺘﻌﻴﺩ ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ ﻴﺨﺒﺭﻙ ﺒﻌﺩﺩ‬
‫ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺃﻀﻴﻔﺕ ﺃﻭ ﺘﻡ ﺘﺤﺩﻴﺜﻬﺎ ﻓﻲ ﻫﺫﺍ ﺍﻟﺠﺩﻭل‪ ..‬ﻭﻓﻲ ﻤﻬﻴﺊ ﺠـﺩﻭل ﺍﻟﻤـﺅﻟﻔﻴﻥ‪،‬‬
‫ﻴﻜﻭﻥ ﻤﻌﺎﻤل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻥ ﻨﻭﻉ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ﺍﻟﻤﻌﺭﻑ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫‪ ،DsAuthorsBooks.AuthorsDataTable‬ﻭﺒﺎﻟﻤﺜل ﻴﻜﻭﻥ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻓﻲ ﻤﻬﻴـﺊ‬
‫ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ‪ ،‬ﻤﻥ ﺍﻟﻨﻭﻉ ‪.DsAuthorsBooks.BooksDataTable‬‬
‫ﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺃﻥ ﺘﺭﺴل ﺠﺩﻭﻻ ﻤﻥ ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ‪ ،‬ﺃﻭ‬
‫ﺘﺭﺴل ﺠﺩﻭل ﺤﺭﺍ ﻟﻴﺱ ﻤﺭﺘﺒﻁﺎ ﺒﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ‪ ،‬ﺍﻟﻤﻬﻡ ﺃﻥ ﻴﻜﻭﻥ ﻤﻥ ﺍﻟﻨﻭﻉ ﺍﻟﺼﺤﻴﺢ‪.‬‬

‫ﻗﺭﺍﺀﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:GetData‬‬


‫ﻻ ﺘﺴﺘﻘﺒل ﺃﻴﺔ ﻤﻌﺎﻤﻼﺕ‪ ،‬ﻟﻜﻨﻬﺎ ﺘﻌﻴﺩ ﺠﺩﻭﻻ ﺠﺩﻴﺩﺍ ﻤﻤﻠﻭﺀﺍ ﺒﺎﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻫﺫﺍ ﺍﻟﺠﺩﻭل ﻴﻜـﻭﻥ‬
‫ﻤﻥ ﺍﻟﻨـﻭﻉ ‪ AuthorsDataTable‬ﻓـﻲ ﻤﻬﻴـﺊ ﺠـﺩﻭل ﺍﻟﻤـﺅﻟﻔﻴﻥ‪ ،‬ﻭﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪ BooksDataTable‬ﻓﻲ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ‪.‬‬

‫ﺘﺤﺩﻴﺙ ‪: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‬‬

‫‪ -‬ﻓﻲ ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ ﺍﻜﺘﺏ ﺠﻤﻠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬


‫*‪SELECT Books.‬‬
‫‪FROM Books INNER JOIN Authors‬‬
‫‪ON Books.AuthorID = Authors.ID‬‬
‫‪AND Author = @Author‬‬
‫ﻭﺍﻀﻐﻁ ‪.Next‬‬
‫‪ -‬ﺍﻟﻨﺎﻓﺫﺓ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺘﻴﺢ ﻟﻙ ﺍﺨﺘﻴﺎﺭ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﻲ ﺴﺘﻀﺎﻑ ﺇﻟﻰ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﻟﺘﻨﻔﻴـﺫ‬
‫ﻫﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ‪ ..‬ﺴﺘﺠﺩ ﻭﺴﻴﻠﺘﻴﻥ ﻫﻤﺎ‪:‬‬
‫ﺃ‪ ،FillBy .‬ﻭﻋﻠﻴﻙ ﺘﻌﺩﻴل ﺍﺴﻤﻬﺎ ﺇﻟﻰ ‪ ،FillByAuthor‬ﻭﻫـﻲ ﺘﺴـﺘﻘﺒل ﺍﺴـﻡ‬
‫ﺍﻟﻤﺅﻟﻑ‪ ،‬ﻭﺘﻤﻸ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻜﺘﺏ ﻫﺫﺍ ﺍﻟﻤﺅﻟﻑ‪.‬‬
‫ﺏ‪ ،GetDataBy .‬ﻭﻋﻠﻴﻙ ﺘﻌﺩﻴل ﺍﺴﻤﻬﺎ ﺇﻟـﻰ ‪ ،GetDataByAuthor‬ﻭﻫـﻲ‬
‫ﺘﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ‪ ،‬ﻭﺘﻌﻴﺩ ﺠﺩﻭل ﻜﺘﺏ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻜﺘﺏ ﻫﺫﺍ ﺍﻟﻤﺅﻟﻑ‪.‬‬
‫‪ -‬ﺍﻀﻐﻁ ‪ Next‬ﻟﻌﺭﺽ ﻨﺎﻓﺫﺓ ﺍﻟﻤﻠﺨﺹ‪ ،‬ﺜﻡ ﺍﻀﻐﻁ ‪ Finish‬ﻹﻨﻬﺎﺀ ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ‪.‬‬
‫ﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻟﻰ ﻅﻬﻭﺭ ﺍﺴﻤﻲ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺠﺩﻴﺩﺘﻴﻥ ﻓﻲ ﻤﺨﻁﻁ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ‪:‬‬

‫‪٢٥٠‬‬
‫ﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﺘﻌﺭﻴﻑ ﻫﺎﺘﻴﻥ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﻗﺩ ﺃﻀﻴﻑ ﺇﻟﻰ ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل‪ ،‬ﻭﺴﻴﻜﻭﻥ ﻟﻜل ﻤﻨﻬﻤﺎ‬
‫ﻤﻌﺎﻤل ﻨﺼﻲ ﻴﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ‪ ..‬ﻭﻋﻤﻭﻤﺎ‪ ،‬ﻴﻘﻭﻡ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﺒﺘﻌﺭﻴﻑ ﺍﻟﻤﻌـﺎﻤﻼﺕ‬
‫ﺍﻟﻤﻨﺎﺴﺒﺔ ﻟﻨﻭﻉ ﺍﻟﺤﻘل ﺍﻟﺫﻱ ﺘﺴﺘﻌﻠﻡ ﻋﻨﻪ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻻﺤﻅ ﺃﻨﻙ ﻟﻭ ﺍﺴﺘﺨﺩﻤﺕ ﺍﺴﺘﻌﻼﻤﺎ ﻴﻌﻴﺩ ﻨﺘﺎﺌﺞ ﻏﻴﺭ ﻤﺭﻏﻭﺒﺔ‪ ،‬ﻓﺴﻴﻌﺭﺽ ﻟﻙ ﻤﻬﻴـﺊ ﺍﻟﺠـﺩﻭل‬
‫ﺭﺴﺎﻟﺔ ﺘﺤﺫﺭﻙ ﻤﻥ ﺃﻥ ﻨﺘﻴﺠﺔ ﺍﻻﺴﺘﻌﻼﻡ ﻻ ﺘﻨﺎﺴﺏ ﻤﺨﻁﻁ ﺍﻟﺠـﺩﻭل‪ ..‬ﻭﻟـﻭ ﺃﺭﺩﺕ ﺘﺼـﺤﻴﺢ‬
‫ﺍﻻﺴﺘﻌﻼﻡ ﻓﺎﻀﻐﻁ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ‪ ،‬ﻓﻭﻕ ﺍﻟﺼﻑ ﺍﻟﺫﻱ ﻴﻌﺭﺽ ﺍﺴﻤﻲ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺠـﺩﻴﺘﻴﻥ‬
‫ﻓﻲ ﻤﺨﻁﻁ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل‪ ،‬ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤـﺭ ‪ ..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‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﺍﻻﺘﺼﺎل ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ‪.‬‬

‫ﻤﺩﻴﺭ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل‪:XTableAdapter ...‬‬


‫ﺍﻟﺤﺭﻑ ‪ X‬ﺍﻟﺫﻱ ﻭﻀﻌﻨﺎﻩ ﻓﻲ ﺒﺩﺍﻴﺔ ﺍﺴﻡ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫﻭ ﺒﺩﻴل ﻋﻥ ﺍﺴﻡ ﺃﺤﺩ ﺍﻟﺠﺩﺍﻭل‪..‬‬
‫ﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﻤﺩﻴﺭ ﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ﻴﻤﺘﻠﻙ ﺨﺎﺼﻴﺔ ﻟﻜل ﻤﻬﻴﺊ ﺠﺩﻭل ﺘـﻡ ﺘﻌﺭﻴﻔـﻪ ﻓـﻲ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻓﻲ ﻤﺸﺭﻭﻋﻨﺎ ﻫﺫﺍ ﺴـﺘﺤﺘﻭﻱ ﻓﺌـﺔ ﺍﻟﻤـﺩﻴﺭ ﻋﻠـﻰ ﺍﻟﺨﺎﺼـﻴﺘﻴﻥ‬
‫‪ AuthorsTableAdapter‬ﻭ ‪.BooksTableAdapter‬‬
‫ﻭﺘﻜﻭﻥ ﻟﻬﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﻘﻴﻤﺔ ‪ Nothing‬ﺇﻟﻰ ﺃﻥ ﺘﻀﻊ ﻓﻲ ﻜل ﻤﻨﻬﺎ ﻤﻬﻴـﺊ ﺍﻟﺠـﺩﻭل‬
‫ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺃﻥ ﻴﺘﺤﻜﻡ ﻓﻴﻪ ﻤﺩﻴﺭ ﺍﻟﻤﻬﻴﺌﺎﺕ‪.‬‬

‫ﻋﺩﺩ ﻨﺴﺦ ﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ‪:TableAdapterInstanceCount‬‬


‫ﺘﻌﻴﺩ ﻋﺩﺩ ﻨﺴﺦ ﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ﺍﻟﺘﻲ ﻟﻬﺎ ﻗﻴﻤﺔ ﻏﻴﺭ ‪.Nothing‬‬

‫ﹶﻨﹶﺴﺦ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﺤﺘﻴﺎﻁﻴﺎ ﻗﺒل ﺍﻟﺘﺤﺩﻴﺙ ‪:BackUpDataSetBeforeUpdate‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،True‬ﻓﺴﻴﺘﻡ ﺤﻔﻅ ﻨﺴﺨﺔ ﺍﺤﺘﻴﺎﻁﻴﺔ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫ﻗﺒل ﺇﺠﺭﺍﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ‪ ..‬ﻴﺤﺩﺙ ﻫﺫﺍ ﺒﺘﻌﺭﻴـﻑ ﻤﺠﻤﻭﻋـﺔ ﺒﻴﺎﻨـﺎﺕ ﺩﺍﺨـل ﺇﺠـﺭﺍﺀ‬
‫ﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﻭﺤﻔﻅ ﺴﺠﻼﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﺼﻠﻴﺔ ﻓﻴﻬﺎ‪ ..‬ﻫﺫﺍ ﻤﻔﻴﺩ ﺇﺫﺍ ﺤـﺩﺙ ﺨﻁـﺄ‬
‫‪٢٥٦‬‬
‫ﺃﺜﻨﺎﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﻓﻔﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﻴﺘﻡ ﺍﻟﺘﺭﺍﺠﻊ ‪ Rollback‬ﻋﻥ ﻜل ﺍﻟﻌﻤﻠﻴﺎﺕ ﺍﻟﺘـﻲ‬
‫ﺃﺠﺭﻴﺕ ﻋﻠﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺴﺘﺴﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﺼﻠﻴﺔ ﺤﺎﻟﺘﻬﺎ ﺍﻟﺴﺎﺒﻘﺔ ﻗﺒـل‬
‫ﺇﺠﺭﺍﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﻭﺫﻟﻙ ﺒﺎﺴﺘﻌﺎﺩﺘﻬﺎ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻻﺤﺘﻴﺎﻁﻴﺔ‪ ..‬ﻻﺤـﻅ ﺃﻥ‬
‫ﺃﺨﺫ ﻨﺴﺨﺔ ﺍﺤﺘﻴﺎﻁﻴﺔ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻀﺨﻤﺔ ﺴﻴﻜﻭﻥ ﻋﺒﺌﺎ ﻋﻠﻰ ﺍﻟﺫﺍﻜﺭﺓ ﻭﺴﻴﺴـﺘﻬﻠﻙ‬
‫ﻭﻗﺘﺎ ﻟﺘﻨﻔﻴﺫﻩ‪ ،‬ﻟﻬﺫﺍ ﻓﺎﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫﻲ ‪.False‬‬

‫ﺘﺭﺘﻴﺏ ﺍﻟﺘﺤﺩﻴﺙ ‪:UpdateOrder‬‬


‫ﺘﺤﺩﺩ ﺘﺭﺘﻴﺏ ﺘﻨﻔﻴﺫ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻹﺩﺭﺍﺝ ﻭﺍﻟﺤﺫﻑ ﻋﻨﺩ ﺇﺠﺭﺍﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﻭﻫـﻲ‬
‫ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻤﺘﻲ ﺍﻟﻤﺭﻗﻡ ‪ UpdateOrderOption‬ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﺘﻨﻔﻴﺫ ﺃﻭﺍﻤﺭ ﺍﻹﺩﺭﺍﺝ ﺜﻡ ﺍﻟﺘﺤﺩﻴﺙ ﺜﻡ ﺍﻟﺤﺫﻑ‪ ..‬ﻫﺫﻩ ﺍﻟﻘﻴﻤـﺔ‬


‫‪InsertUpdateDelete‬‬
‫ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪.‬‬
‫‪ UpdateInsertDelete‬ﺘﻨﻔﻴﺫ ﺃﻭﺍﻤﺭ ﺍﻟﺘﺤﺩﻴﺙ ﺜﻡ ﺍﻹﺩﺭﺍﺝ ﺜﻡ ﺍﻟﺤﺫﻑ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﻭﺤﻴﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﺤﺩﻴﺙ ﺍﻟﻜل ‪:UpdateAll‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻨﻘل ﺍﻟﺘﻐﻴﻴـﺭﺍﺕ ﻤﻨﻬـﺎ‬
‫ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ..‬ﻓـﻲ ﻤﺸـﺭﻭﻋﻨﺎ ﺴـﺘﻜﻭﻥ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪ ..DsAuthorsBooks‬ﻭﻴﺘﻡ ﺇﺠﺭﺍﺀ ﻋﻤﻠﻴﺎﺕ ﺍﻟﺘﺤﺩﻴﺙ ﺒﺎﻟﺘﺭﺘﻴﺏ ﺍﻟﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪ ،UpdateOrder‬ﻭﺇﺫﺍ ﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺃﻱ ﻤﺭﺤﻠﺔ ﻤﻥ ﻤﺭﺍﺤل ﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﻴـﺘﻡ ﺍﻟﺘﺭﺍﺠـﻊ‬
‫‪ Rollback‬ﻋﻥ ﺘﻨﻔﻴﺫ ﺠﻤﻴﻊ ﻋﻤﻠﻴﺎﺕ ﺍﻟﺘﺤﺩﻴﺙ‪ ،‬ﺃﻱ ﺃﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻻ ﻴﺤﺩﺙ ﺒﻬـﺎ ﺃﻱ‬
‫ﺘﻐﻴﻴﺭ‪ ،‬ﻭﺘﻅل ﻜﻤﺎ ﻜﺎﻨﺕ ﻗﺒل ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪.‬‬

‫‪٢٥٧‬‬
‫ﺇﻀﺎﻓﺔ ﺃﻜﻭﺍﺩ ﺨﺎﺼﺔ ﺒﻙ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﻟﺠﺩﺍﻭل ﻭﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل‪:‬‬
‫ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻀﻴﻑ ﺒﻌﺽ ﺍﻟﻭﺴﺎﺌل ﺇﻟﻰ ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺃﻭ ﻓﺌﺔ ﺍﻟﺠﺩﻭل‪ ،‬ﺃﻭ ﻓﺌـﺔ ﻤﻬﻴـﺊ‬
‫ﺍﻟﺠﺩﻭل‪ ..‬ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﻜﺘﺎﺒﺔ ﺇﺠﺭﺍﺀﺍﺕ ﺘﺴﺘﺠﻴﺏ ﻟﺒﻌﺽ ﺃﺤﺩﺍﺙ ﻓﺌﺔ ﺍﻟﺠﺩﻭل‪ ،‬ﺴـﻭﺍﺀ ﺍﻷﺤـﺩﺍﺙ‬
‫ﺍﻟﻤﻌﺭﻓﺔ ﺩﺍﺨل ﻓﺌﺔ ﺍﻟﺠﺩﻭل‪ ،‬ﺃﻭ ﺘﻠﻙ ﺍﻟﻤﻭﺭﻭﺜﺔ ﻤﻥ ﺍﻟﻔﺌﺔ ‪ ،DataTable‬ﻭﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬـﺎ‬
‫ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺘﺎﻟﻲ‪.‬‬
‫ﻟﻜﻥ ﺍﻟﻤﺸﻜﻠﺔ ﺃﻨﻙ ﻟﻭ ﻜﺘﺒﺕ ﺃﻱ ﻜﻭﺩ ﻓﻲ ﺍﻟﻤﻠﻑ ‪ X.Designer.cs‬ﺍﻟﺫﻱ ﻓﻴـﻪ ﺘﻌﺭﻴـﻑ ﻫـﺫﻩ‬
‫ﺍﻟﻔﺌﺎﺕ )ﺤﻴﺙ ‪ X‬ﻫﻭ ﺍﺴﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ(‪ ،‬ﻓﺴﻴﻜﻭﻥ ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﻋﺭﻀﺔ ﻟﻠﻀﻴﺎﻉ ﻋﻨﺩ ﻗﻴﺎﻤﻙ‬
‫ﺒﺄﻱ ﺘﻌﺩﻴﻼﺕ ﻓﻲ ﻤﺼﻤﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻷﻥ ﻫﺫﻩ ﺍﻟﺘﻌﺩﻴﻼﺕ ﺴﺘﻌﻴﺩ ﺇﻨﺘﺎﺝ ﻤﻠﻑ ﺍﻟﻜﻭﺩ ﻤـﻥ‬
‫ﺠﺩﻴﺩ‪ ،‬ﻭﺴﺘﺘﺨﻠﺹ ﻤﻥ ﺃﻱ ﻜﻭﺩ ﺨﺎﺹ ﺒﻙ!‬
‫ﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ‪ ،‬ﺘﻡ ﺘﻌﺭﻴﻑ ﺍﻟﻔﺌﺎﺕ ﻓﻲ ﻫﺫﺍ ﺍﻟﻤﻠﻑ ﺒﺎﻋﺘﺒﺎﺭﻫـﺎ ﺠﺯﺌﻴـﺔ ‪ ،Partial‬ﻟﻴﻤﻜﻨـﻙ‬
‫ﺇﻀﺎﻓﺔ ﺍﻟﻜﻭﺩ ﺇﻟﻴﻬﺎ ﻓﻲ ﻤﻠﻑ ﺁﺨﺭ‪ ..‬ﻟﻔﻌل ﻫﺫﺍ‪ ،‬ﺍﻀﻐﻁ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤـﻥ ﻓـﻭﻕ ﺍﻟﺠـﺩﻭل ﺃﻭ‬
‫ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل‪ ،‬ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﺨﺘﺭ ﺍﻷﻤﺭ ‪ ،View Code‬ﻟﻔـﺘﺢ ﺘﻌﺭﻴـﻑ ﺠﺯﺌـﻲ‬
‫ﻤﺴﺘﻘل ﻟﻔﺌﺔ ﺍﻟﺠﺩﻭل ﺃﻭ ﻓﺌﺔ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل‪ ..‬ﻫﺫﺍ ﺍﻟﺘﻌﺭﻴﻑ ﺴﻴﻀﺎﻑ ﻓﻲ ﻤﻠـﻑ ﺠﺩﻴـﺩ ﺍﺴـﻤﻪ‬
‫‪) X.cs‬ﻓﻲ ﻤﺜﺎﻟﻨﺎ ﻫﺫﺍ ﺴﻴﻜﻭﻥ ﺍﺴﻤﻪ ‪ ..(DsAuthorsBooks.cs‬ﻭﺴﺘﺠﺩ ﻫﺫﺍ ﺍﻟﻤﻠﻑ ﻀـﻤﻥ‬
‫ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﻔﺭﻋﻴﺔ ﻟﻤﺨﻁﻁ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪.DsAuthorsBooks.xsd‬‬
‫ﻭﻋﻨﺩﻤﺎ ﺘﻔﺘﺢ ﻫﺫﺍ ﺍﻟﻤﻠﻑ ﻓﻲ ﻤﺤﺭﺭ ﺍﻟﻜﻭﺩ‪ ،‬ﻴﻤﻜﻨﻙ ﺍﺨﺘﻴﺎﺭ ﺍﻟﻔﺌﺔ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻌﻠﻭﻴـﺔ ﺍﻟﻴﺴـﺭﻯ‪،‬‬
‫ﻭﺍﺨﺘﻴﺎﺭ ﺍﻟﺤﺩﺙ ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺇﻀﺎﻓﺘﻪ ﺇﻟﻴﻬﺎ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻌﻠﻭﻴﺔ ﺍﻟﻴﻤﻨﻰ ﻜﻤﺎ ﻫـﻭ ﻤـﺄﻟﻭﻑ‪ ..‬ﻜﻤـﺎ‬
‫ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻀﻴﻑ ﺃﻴﺔ ﺩﺍﻟﺔ ﺘﺭﻴﺩﻫﺎ ﺇﻟﻰ ﺃﻴﺔ ﻓﺌﺔ‪ ،‬ﺴـﻭﺍﺀ ﻜﺎﻨـﺕ ﺨﺎﺼـﺔ ‪ Private‬ﺃﻭ ﻋﺎﻤـﺔ‬
‫‪ ،Public‬ﻤﻊ ﻗﺩﺭﺘﻙ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻜل ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻤﻌﺭﻓﺔ ﻋﻠﻰ ﻤﺴﺘﻭﻯ ﺍﻟﻔﺌﺔ ﻓﻲ ﻜﺘﺎﺒـﺔ ﻜـﻭﺩ‬
‫ﻫﺫﻩ ﺍﻟﺩﺍﻟﺔ‪ ،‬ﺴﻭﺍﺀ ﻜﺎﻨﺕ ﻫﺫﻩ ﺍﻟﻌﻨﺎﺼﺭ ﻤﺤﻤﻴﺔ ‪ Protected‬ﺃﻭ ﺨﺎﺼﺔ ‪.Private‬‬
‫ﻜﻤﺎ ﻴﻘﺩﻡ ﻟﻙ ﻤﺼﻤﻡ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺜﻴﺭ ﻤﻥ ﺍﻟﺘﺴﻬﻴﻼﺕ‪:‬‬
‫‪ -‬ﻓﺎﻟﻨﻘﺭ ﻤﺭﺘﻴﻥ ﻓﻲ ﺃﻴﺔ ﻤﻨﻁﻘﺔ ﺨﺎﻟﻴﺔ‪ ،‬ﻴﻔﺘﺢ ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -‬ﻭﺍﻟﻨﻘﺭ ﻤﺭﺘﻴﻥ ﻋﻠﻰ ﻋﻨﻭﺍﻥ ﺍﻟﺠﺩﻭل ﻴﻔﺘﺢ ﻓﺌﺔ ﺍﻟﺠﺩﻭل‪ ،‬ﻭﻴﻀﻴﻑ ﺇﻟﻴﻬﺎ ﻤﺴﺘﺠﻴﺒﺎ ﻟﻠﺤـﺩﺙ‬
‫‪ ،XRowChanging‬ﺤﻴﺙ ‪ X‬ﻫـﻭ ﺍﺴـﻡ ﺍﻟﺠـﺩﻭل‪ ..‬ﻭﻗـﺩ ﺍﺴـﺘﺨﺩﻤﻨﺎ ﺍﻟﺤـﺩﺙ‬

‫‪٢٥٨‬‬
‫‪ AuthorsRowDeleting‬ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪ TableAdapter‬ﻟﻌﺭﺽ ﺭﺴـﺎﻟﺔ ﺘﺄﻜﻴـﺩ‬
‫ﻗﺒل ﺤﺫﻑ ﺃﻱ ﺼﻑ ﻤﻥ ﺍﻟﺠﺩﻭل‪.‬‬
‫‪ -‬ﻭﺍﻟﻨﻘﺭ ﻤﺭﺘﻴﻥ ﻋﻠﻰ ﺃﻱ ﺼﻑ ﻓﻲ ﺍﻟﺠﺩﻭل‪ ،‬ﻴﻔﺘﺢ ﻓﺌﺔ ﺍﻟﺠﺩﻭل‪ ،‬ﻭﻴﻀﻴﻑ ﺇﻟﻴﻬـﺎ ﺘﻌﺭﻴﻔـﺎ‬
‫ﻟﻠﺤﺩﺙ ‪ ،ColumnChanging‬ﻭﺸﺭﻁﺎ ﻴﺘﺄﻜﺩ ﺃﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﺘﻐﻴﺭ ﻫﻭ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ‬
‫ﻨﻘﺭﺘﻪ ﺒﺎﻟﻔﺄﺭﺓ‪ ..‬ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ ‪ TableAdapter‬ﻟﻤﻨـﻊ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ ﻤﻥ ﺘﺭﻙ ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ﻓﺎﺭﻏﺎ‪.‬‬
‫‪ -‬ﻭﺍﻟﻨﻘﺭ ﻤﺭﺘﻴﻥ ﻋﻠﻰ ﻋﻨﻭﺍﻥ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻴﻔﺘﺢ ﻜﻭﺩ ﻓﺌﺘﻪ‪.‬‬

‫ﺍﺴﺘﺨﺩﺍﻡ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻓﻲ ﺍﻟﻜﻭﺩ‪:‬‬


‫ﻻﺴﺘﺨﺩﺍﻡ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﻴﺠﺏ ﺃﻥ ﺘﻌﺭﻑ ﻨﺴﺨﺔ ﻤﻨﻪ‪ ..‬ﻤﺜﺎل‪:‬‬
‫‪var TaAuhtors = new‬‬
‫;) (‪DsAuthorsBooksTableAdapters.AuthorsTableAdapter‬‬
‫"(‪MessageBox.Show(TaAuhtors.GetAuthorBooksCount‬‬
‫;))"ﺘﻭﻓﻴﻕ ﺍﻟﺤﻜﻴﻡ‬
‫ﻭﻻﺴﺘﺨﺩﺍﻡ ﻤﺩﻴﺭ ﺍﻟﻤﻬﻴﺌﺎﺕ ﻓﻲ ﺍﻟﻜﻭﺩ‪ ،‬ﻴﺠﺏ ﺃﻥ ﻨﻌﺭﻑ ﻨﺴﺨﺔ ﻤﻨﻪ‪ ،‬ﻭﺘﻀﻊ ﻨﺴﺨﺔ ﻤﻥ ﻜل ﻤﻬﻴﺊ‬
‫ﺠﺩﻭل ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻤﻨﺎﻅﺭﺓ ﻟﻪ ﻓﻲ ﻤﺩﻴﺭ ﺍﻟﻤﻬﻴﺌﺎﺕ‪ ،‬ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫‪var TaBooks = new‬‬
‫;) (‪DsAuthorsBooksTableAdapters.BooksTableAdapter‬‬
‫‪var TaM = new‬‬
‫;) (‪DsAuthorsBooksTableAdapters.TableAdapterManager‬‬
‫;‪TaM.AuthorsTableAdapter = TaAuhtors‬‬
‫;‪TaM.BooksTableAdapter = TaBooks‬‬
‫ﻟﻜﻥ ﺍﻷﺴﻬل ﻫﻭ ﺃﻥ ﺘﺘﻌﺎﻤل ﻤﻊ ﻫﺫﻩ ﺍﻟﻜﺎﺌﻨﺎﺕ ﺒﺸﻜل ﻤﺭﺌﻲ‪ ،‬ﺤﻴﺙ ﺘﻘﺩﻡ ﻟـﻙ ﺩﻭﺕ ﻨـﺕ ﻫـﺫﻩ‬
‫ﺍﻟﺘﺴﻬﻴﻼﺕ‪:‬‬
‫‪ -١‬ﻋﻨﺩ ﺴﺤﺏ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﺼـﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ ﻭﺇﺴـﻘﺎﻁﻬﺎ ﻋﻠـﻰ ﺍﻟﻨﻤـﻭﺫﺝ‪،‬‬
‫ﺴﺘﻅﻬﺭ ﻨﺎﻓﺫﺓ ﺘﺴﺄﻟﻙ ﺇﻥ ﻜﻨﺕ ﺘﺭﻴﺩ ﺇﻀﺎﻓﺔ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻋﺎﺩﻴﺔ ﺃﻡ ﻤﺤﺩﺩﺓ ﺍﻟﻨـﻭﻉ‪،‬‬
‫ﺤﻴﺙ ﺘﺴﺘﻁﻴﻊ ﺍﺨﺘﻴﺎﺭ ﺍﻟﻨﻭﻉ ‪ X.DsAuthorsBooks‬ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ‪ ،‬ﺤﻴﺙ ‪X‬‬
‫ﻫﻭ ﺍﺴﻡ ﺍﻟﻤﺸﺭﻭﻉ )ﻭﻫﻭ ‪ TableAdapter‬ﻓﻲ ﺤﺎﻟﺘﻨﺎ ﻫﺫﻩ(‪.‬‬
‫‪٢٥٩‬‬
‫‪ -٢‬ﻴﻤﻜﻨﻙ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻋﻨﺎﺼﺭ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﻤـﻥ ﺼـﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ‬
‫ﻤﺒﺎﺸﺭﺓ‪ ،‬ﻓﻬﻲ ﻴﻅﻬـﺭ ﻓـﻲ ﺼـﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ ﺘﺤـﺕ ﺸـﺭﻴﻁ ﺠﺩﻴـﺩ ﺍﺴـﻤﻪ ‪X‬‬
‫‪ ،Components‬ﺤﻴﺙ ‪ X‬ﻫﻭ ﺍﺴﻡ ﺍﻟﻤﺸﺭﻭﻉ‪ ..‬ﻭﺇﺫﺍ ﻟﻡ ﺘﺠﺩ ﻫﺫﺍ ﺍﻟﺸـﺭﻴﻁ‪ ،‬ﻓـﺄﻏﻠﻕ‬
‫ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ ﺜﻡ ﺃﻋﺩ ﻓﺘﺤﻪ ﻟﻴﺘﻡ ﺇﻨﻌﺎﺸﻪ‪ ..‬ﻭﺴـﺘﺠﺩ ﺘﺤـﺕ ﺸـﺭﻴﻁ ﺍﻟﻤﺸـﺭﻭﻉ‬
‫‪ TableAdapter‬ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫ﺃ‪ .‬ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ‪.DsAuthorsBooks‬‬
‫ﺏ‪ .‬ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ ‪.AuthorsTableAdapter‬‬
‫ﺝ‪ .‬ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ‪.BooksTableAdapter‬‬
‫ﺩ‪ .‬ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻻﺴﺘﻌﻼﻤﺎﺕ ‪.QueriesTableAdapter‬‬
‫ﻫـ‪ .‬ﻤﺩﻴﺭ ﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل ‪.TableAdapterManger‬‬
‫ﻫﺫﺍ ﻴﺘﻴﺢ ﻟﻙ ﺇﻀﺎﻓﺔ ﺃﻱ ﻤﻥ ﻫﺫﻩ ﺍﻟﻌﻨﺎﺼﺭ ﺇﻟﻰ ﺍﻟﻨﻤﻭﺫﺝ‪ ،‬ﺤﻴﺙ ﺴﺘﻅﻬﺭ ﻓـﻲ ﺼـﻴﻨﻴﺔ‬
‫ﺍﻟﻤﻜﻭﻨﺎﺕ‪ ..‬ﺃﻀﻑ ﻨﺴﺨﺔ ﻤﻥ ﻜل ﻋﻨﺼﺭ ﻤﻥ ﻫﺫﻩ ﺍﻟﻌﻨﺎﺼﺭ‪ ،‬ﻭﺍﻤﻨﺤﻬﺎ ﺍﻷﺴﻤﺎﺀ ﺍﻟﺘﺎﻟﻴـﺔ‬
‫ﻋﻠﻰ ﺍﻟﺘﺭﺘﻴﺏ‪.TaM ،TaQueries ،TaBooks ،TaAuthors ،Ds :‬‬
‫‪ -٣‬ﺍﻀﻐﻁ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ‪ ،‬ﻤﺩﻴﺭ ﺍﻟﻤﻬﻴﺌﺎﺕ ‪ TaM‬ﻓﻲ ﺼﻴﻨﻴﺔ ﺍﻟﻤﻜﻭﻨﺎﺕ‪ ،‬ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ‬
‫ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤﺭ ‪ Properties‬ﻟﻌﺭﺽ ﺨﺼﺎﺌﺼﻪ ﻓﻲ ﻨﺎﻓـﺫﺓ ﺍﻟﺨﺼـﺎﺌﺹ‪..‬‬
‫ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ﻟﻠﺨﺎﺼـﻴﺔ ‪ AuthorsTableAdapter‬ﺍﺨﺘـﺭ ‪،TaAuthors‬‬
‫ﻭﻤﻥ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ﻟﻠﺨﺎﺼـﻴﺔ ‪ BoosTableAdapter‬ﺍﺨﺘـﺭ ‪..TaBooks‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﺒﺎﻗﻲ ﺍﻟﺨﺼﺎﺌﺹ ﻜﻤﺎ ﻴﻨﺎﺴﺒﻙ‪.‬‬
‫‪ -٤‬ﺍﻀﻐﻁ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ‪ ،‬ﺃﻱ ﻤﻬﻴﺊ ﺠﺩﻭل ﻓﻲ ﺼﻴﻨﻴﺔ ﺍﻟﻤﻜﻭﻨـﺎﺕ‪ ،‬ﻭﻤـﻥ ﺍﻟﻘﺎﺌﻤـﺔ‬
‫ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤﺭ ‪ Add Query‬ﻹﻀﺎﻓﺔ ﺍﺴﺘﻌﻼﻡ ﺠﺩﻴﺩ ﺇﻟﻰ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل‪..‬‬
‫ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﺘﻅﻬﺭ ﻨﺎﻓﺫﺓ ﻤﺨﺘﺼﺭﺓ‪ ،‬ﺘﺘﻴﺢ ﻟﻙ ﺘﻌﺭﻴﻑ ﺍﻟﻭﺴﻴﻠﺔ ‪ FillBy‬ﻓﻘﻁ ﻭﻟـﻥ‬
‫ﻴﺘﻡ ﺘﻌﺭﻴﻑ ﺍﻟﻭﺴﻴﻠﺔ ‪ ،GetDataBy‬ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪:‬‬

‫‪٢٦٠‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻐﻴﺭ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﺘﺘﻌﺎﻤل ﻤﻌﻪ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ‪ ،‬ﻭﻴﻤﻜﻨـﻙ ﺃﻥ‬
‫ﺘﻐﻴﺭ ﺍﺴﻡ ﻭﺴﻴﻠﺔ ﺍﻟﻤلﺀ ﺒﺘﺤﺭﻴﺭﻫﺎ ﻓـﻲ ﻤﺭﺒـﻊ ﺍﻟـﻨﺹ )ﻭﺴﻨﺴـﺘﺨﺩﻡ ﻫﻨـﺎ ﺍﻻﺴـﻡ‬
‫‪ ،(FillByPublisher‬ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻜﺘﺏ ﺍﻻﺴﺘﻌﻼﻡ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟـﻨﺹ ﺍﻟﺴـﻔﻠﻲ‪..‬‬
‫ﻭﺇﺫﺍ ﺃﺭﺩﺕ ﺘﻌﺩﻴل ﺍﺴﺘﻌﻼﻡ ﻤﻭﺠﻭﺩ ﺴﺎﺒﻘﺎ‪ ،‬ﻓﺎﻀـﻐﻁ ﺍﻻﺨﺘﻴـﺎﺭ ‪Existing Query‬‬
‫‪ Name‬ﻭﺍﺨﺘﺭ ﺍﺴﻡ ﺍﻻﺴﺘﻌﻼﻡ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ﻟﻌﺭﻀـﻪ ﻓـﻲ ﻤﺭﺒـﻊ ﺍﻟـﻨﺹ‬
‫ﺍﻟﺴﻔﻠﻲ‪ ..‬ﻓﻲ ﺤﺎﻟﺘﻨﺎ ﻫﺫﻩ‪ ،‬ﺴﻨﺴﺘﺨﺩﻡ ﺍﺴﺘﻌﻼﻤﺎ ﺠﺩﻴﺩﺍ ﻟﻠﺤﺼﻭل ﻋﻠـﻰ ﺍﻟﻜﺘـﺏ ﺍﻟﺘـﻲ‬
‫ﻨﺸﺭﻫﺎ ﻨﺎﺸﺭ ﻤﻌﻴﻥ‪.‬‬
‫ﺍﻀﻐﻁ ‪ Ok‬ﻹﻏﻼﻕ ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ‪ ..‬ﺴﻴﺅﺩﻱ ﻫﺫﺍ ﺇﻀﺎﻓﺔ ﺍﻟﻭﺴـﻴﻠﺔ ‪FillByPublisher‬‬
‫ﺇﻟﻰ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ‪.‬‬

‫‪٢٦١‬‬
‫ﻭﺇﺫﺍ ﺃﺭﺩﺕ ﺇﻀﺎﻓﺔ ﺍﻟﻭﺴﻴﻠﺔ ‪ ،GetDataByPublisher‬ﻓﺎﺘﺒﻊ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -‬ﻀﻐﻁ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ‪ ،‬ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤﺭ‬
‫‪ Edit Query In DataSet‬ﻟﻌﺭﺽ ﻤﺨﻁﻁ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -‬ﺍﻀﻐﻁ ﺍﻻﺴﺘﻌﻼﻡ ‪ FillByPublisher‬ﻓﻲ ﻤﻬﻴﺊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ ﺒـﺯﺭ ﺍﻟﻔـﺄﺭﺓ‬
‫ﺍﻷﻴﻤﻥ‪ ،‬ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻭﻀﻌﻴﺔ ﺍﻀﻐﻁ ﺍﻷﻤـﺭ ‪ Configure‬ﻟﻌـﺭﺽ ﻨﺎﻓـﺫﺓ‬
‫ﺘﺤﺭﻴﺭ ﺍﻻﺴﺘﻌﻼﻡ‪.‬‬
‫‪ -‬ﺍﻀﻐﻁ ‪ Next‬ﻟﻌﺭﺽ ﻨﺎﻓﺫﺓ ﻭﺴﺎﺌل ﺍﻻﺴﺘﻌﻼﻡ‪ ،‬ﻭﻀﻊ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴـﺎﺭ ﺃﻤـﺎﻡ‬
‫‪ ،Return a DataTable‬ﻭﻏﻴﺭ ﺍﺴﻡ ﺍﻟﻭﺴـﻴﻠﺔ ﺇﻟـﻰ ‪GetDataByPublisher‬‬
‫ﻭﺍﻀﻐﻁ ‪.Finish‬‬

‫ﺃﻴﻀﺎ‪ ،‬ﺴﻴﻀﺎﻑ ﺭﻑ ﺃﺩﻭﺍﺕ ‪ ToolStrip‬ﺇﻟﻰ ﺍﻟﻨﻤﻭﺫﺝ‪ ،‬ﻋﻠﻴﻪ ﻻﻓﺘـﺔ ﺘﺤﻤـل ﺍﻻﺴـﻡ‬


‫‪) Publisher‬ﻭﻫﻭ ﺍﺴﻡ ﺍﻟﻤﻌﺎﻤل ﺍﻟﻤﺭﺍﺩ ﺇﺩﺨﺎﻟﻪ ﻟﺘﻨﻔﻴﺫ ﺍﻻﺴﺘﻌﻼﻡ( ﻭﻤﺭﺒﻊ ﻨﺹ ﻟﻴﻜﺘـﺏ‬
‫ﻓﻴﻪ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﺴﻡ ﺍﻟﻨﺎﺸﺭ‪ ،‬ﻭﺯﺭﺍ ﻴﺤﻤل ﺍﺴـﻡ ﺍﻟﻭﺴـﻴﻠﺔ ‪ ،FillByPublisher‬ﻭﻋﻨـﺩ‬
‫ﺍﻟﻀﻐﻁ ﻋﻠﻴﻪ ﺴﻴﺘﻡ ﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻜﺘﺏ ﻫﺫﺍ ﺍﻟﻨﺎﺸﺭ‪ ،‬ﻓﺎﻟﻜﻭﺩ ﺍﻟﺫﻱ ﻴﻔﻌل ﻫـﺫﺍ‬
‫ﺘﻡ ﺇﻨﺘﺎﺠﻪ ﺁﻟﻴﺎ ﻓﻲ ﺤﺩﺙ ﻀﻐﻁ ﺍﻟﺯﺭ‪ ..‬ﻟﻜﻥ ﺴﻴﺘﺒﻘﻰ ﻋﻠﻴـﻙ ﺃﻥ ﺘﻌـﺭﺽ ﻤﺤﺘﻭﻴـﺎﺕ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻠﻤﺴﺘﺨﺩﻡ‪ ،‬ﻭﻗﺩ ﻓﻌﻠﻨﺎ ﻫﺫﺍ ﺒﻌﺭﻀﻬﺎ ﻓﻲ ﺠﺩﻭل ﻋـﺭﺽ ﻜﻤـﺎ ﻫـﻭ‬
‫ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪:‬‬

‫‪٢٦٢‬‬
‫ﻭﺘﺴﺘﻁﻴﻊ ﺘﻐﻴﻴﺭ ﻋﻨﻭﺍﻥ ﺍﻟﻼﻓﺘﺔ ﻭﺍﻟﺯﺭ‪ ،‬ﻭﻋﺭﺽ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ﻤﻥ ﺍﻟﻴﻤﻴﻥ ﺇﻟﻰ ﺍﻟﻴﺴﺎﺭ‬

‫ﻭﺍﻟﻤﺸﺭﻭﻉ ‪ TableAdapter‬ﻴﺭﻴﻙ ﺃﻤﺜﻠﺔ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻤﻬﻴﺌﺎﺕ ﺍﻟﺠﺩﺍﻭل‪ ،‬ﻤﻊ ﺍﺴﺘﺨﺩﺍﻡ ﻤـﺩﻴﺭ‬
‫ﺍﻟﻤﻬﻴﺌﺎﺕ ﻓﻲ ﺤﺩﺙ ﻀﻐﻁ ﺍﻟﺯﺭ "ﺤﻔﻅ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ" ﻹﺭﺴﺎل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺃﺠﺭﺍﻫﺎ ﺍﻟﻤﺴـﺘﺨﺩﻡ‬
‫ﻋﻠﻰ ﺍﻟﺴﺠﻼﺕ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ‬

‫‪٢٦٣‬‬
‫‪-١٢-‬‬
‫ﺍﻟﺠﺩﺍﻭل ﻭﺍﻟﻌﻼﻗﺎﺕ ﻭﺍﻟﻘﻴﻭﺩ‬

‫ﺴﻨﺘﻌﺭﻑ ﻓﻲ ﻫﺫﺍ ﺍﻟﻔﺼل ﻋﻠﻰ ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﺩﺍﺨﻠﺔ ﻓﻲ ﺘﻜـﻭﻴﻥ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ ‪،DataSet‬‬
‫ﻭﻫﻲ‪:‬‬
‫‪ -‬ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ‪ DataTable‬ﻭﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﺩﺍﺨﻠﻴﺔ ﺍﻟﻤﻜﻭﻨﺔ ﻟـﻪ ﻤﺜـل ﻜـﺎﺌﻥ ﺍﻟﺼـﻑ‬
‫‪ DataRow‬ﻭﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ‪.DataCoulmn‬‬
‫‪ -‬ﻜﺎﺌﻥ ﺍﻟﻌﻼﻗﺔ ‪.DataRelation‬‬
‫‪ -‬ﻜﺎﺌﻨﺎﺕ ﺍﻟﻘﻴﻭﺩ ‪ Constraints‬ﺍﻟﻤﺨﺘﻠﻔﺔ‪.‬‬
‫ﺇﻀﺎﻓﺔ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺎﺕ ﺍﻟﺘﻲ ﺘﺴﺘﺨﺩﻤﻬﺎ ﻫﺫﻩ ﺍﻟﻔﺌﺎﺕ‪.‬‬

‫‪٢٦٤‬‬
‫ﻓﺌﺔ ﺃﺴﺎﺱ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺩﺍﺨﻠﻴﺔ‬
InternalDataCollectionBase Class

‫ ﻭﻻ ﺘﺯﻴﺩ ﻋﻠﻰ ﺨﺼﺎﺌﺼﻬﺎ ﻭﻭﺴﺎﺌﻠﻬﺎ ﺒﺸﻲﺀ‬،ICollection ‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﻭﺍﺠﻬﺔ ﺍﻟﻤﺠﻤﻭﻋﺔ‬
.‫ﺠﺩﻴﺩ‬
:‫ﻭﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻫﻲ ﺍﻟﻔﺌﺔ ﺍﻷﻡ ﻟﻜل ﻤﻥ ﺍﻟﻤﺠﻤﻭﻋﺎﺕ ﺍﻟﺘﺎﻟﻴﺔ‬
DataTableCollection Class .١
DataColumnCollection Class .٢
DataRowCollection Class .٣
DataRelationCollection Class .٤
ConstraintCollection Class .٥

٢٦٥
‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل ‪DataTableCollection Class‬‬

‫ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ ،InternalDataCollectionBase‬ﻭﻫﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻨﺎﺼـﺭ‬


‫ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ..DataTable‬ﻭﻴﻤﻜﻥ ﺍﻟﺤﺼﻭل ﻋﻠـﻰ ﻫـﺫﻩ ﺍﻟﻤﺠﻤﻭﻋـﺔ ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼـﻴ‪‬ﺔ‬
‫‪.DataSet.Tables‬‬
‫ﻭﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﺽ ﻜلّ ﺃﺴﻤﺎﺀ ﺍﻟﺠﺩﺍﻭل ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:Ds‬‬
‫)‪foreach (DataTable Tbl in Ds.Tables‬‬
‫{‬
‫;)‪MessageBox.Show(Tbl.TableName‬‬
‫}‬
‫ﻭﻻ ﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺠﺩﻴﺩﺍ ﺇﻟﻰ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﻭﺍﺠﻬﺔ ﺍﻟﻤﺠﻤﻭﻋـﺔ ‪،ICollection‬‬
‫ﻭﻟﻜﻨﻬﺎ ﺘﻀﻴﻑ ﺒﻌﺽ ﺍﻟﺼﻴﻎ ﺇﻟﻰ ﺒﻌﺽ ﻫﺫﻩ ﺍﻟﻌﻨﺎﺼﺭ‪ ،‬ﻤﺜل‪:‬‬

‫ﺍﻟﻤﻔﻬﺭﺱ ‪:Indexer‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataTable‬ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻤﻭﻀﻊ ﻤﻌـﻴﻥ ﻓـﻲ ﺍﻟﻘﺎﺌﻤـﺔ‪ ..‬ﻭﻟﻬـﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﺜﻼﺙ ﺼﻴﻎ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﺭﻗﻡ ﺍﻟﺠﺩﻭل ﻓﻲ ﺍﻟﻤﺠﻤﻭﻋﺔ‪.‬‬
‫‪ -٢‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﺠﺩل‪.‬‬
‫‪ -٣‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ‪ ،‬ﻴﺴﺘﻘﺒل ﻨﺼﺎ ﻴﻤﺜل ﺍﺴـﻡ‬
‫ﺍﻟﻨﻁﺎﻕ ‪ Name Space‬ﺍﻟﺫﻱ ﻴﻭﺠﺩ ﺘﺤﺘﻪ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ null‬ﺇﺫﺍ ﻟﻡ ﺘﺠﺩ ﺍﻟﺠﺩﻭل ﺍﻟﻤﻁﻠﻭﺏ ﻓﻲ ﺍﻟﻤﺠﻤﻭﻋﺔ‪.‬‬

‫ﺇﻀﺎﻓﺔ ‪:Add‬‬
‫ﺘﻀﻴﻑ ﺠﺩﻭﻻ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻟﻬﺎ ﺃﺭﺒﻊ ﺼﻴﻎ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪ ،‬ﻭﻫﻲ ﺘﻨﺸﺊ ﺠﺩﻭﻻ ﺒﺎﺴﻡ ﺍﻓﺘﺭﺍﻀﻲ‪ Table1) ‬ﺃﻭ‬
‫‪ Table2‬ﻭﻫﻜﺫﺍ‪ (...‬ﻭﺘﻀﻴﻔﻪ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪٢٦٦‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻨﺼ‪‬ﻴ‪‬ﺎ‪ ،‬ﻫﻭ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﺴﻴﺘﻡ‪ ‬ﺇﻨﺸﺎﺅﻩ ﻭﺇﻀﺎﻓﺘﻪ‬
‫ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ..‬ﻭﻟﻭ ﺃﺭﺴﻠﺕ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻨﺼﺎ ﻓﺎﺭﻏﺎ ""‪ ،‬ﻓﺴﻴﺴﻤﻰ ﺍﻟﺠـﺩﻭل‬
‫ﺒﺎﻻﺴﻡ ﺍﻻﻓﺘﺭﺍﻀﻲ ‪ Table1‬ﺃﻭ ‪ Table2‬ﻭﻫﻜﺫﺍ‪ ...‬ﻻﺤﻅ ﺃﻥ ﺇﻀـﺎﻓﺔ ﺠـﺩﻭل‬
‫ﺒﻨﻔﺱ ﺍﺴﻡ ﺠﺩﻭل ﻤﻭﺠﻭﺩ ﺴﺎﺒﻘﺎ ﺴﻴﺅﺩﻱ ﺇﻟﻰ ﺤﺩﻭﺙ ﺨﻁﺄ‪.‬‬
‫‪ -٣‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ‪ ،‬ﻴﻭﻀﺢ ﻨﻁﺎﻕ ﺍﻻﺴﻡ ﺍﻟﺫﻱ‬
‫ﺴﻴﻀﺎﻑ ﺇﻟﻴﻪ ﺍﻟﺠﺩﻭل ﺩﺍﺨل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻫﺫﺍ ﻴﺘﻴﺢ ﻟﻙ ﺇﻀﺎﻓﺔ ﺃﻜﺜـﺭ ﻤـﻥ‬
‫ﺠﺩﻭل ﺒﻨﻔﺱ ﺍﻻﺴﻡ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل‪ ،‬ﻟﻜﻥ ﻜﻼ ﻤﻨﻬﺎ ﻴﻨﺘﻤـﻲ ﺇﻟـﻰ ﻨﻁـﺎﻕ‬
‫ﻤﺨﺘﻠﻑ‪ ..‬ﻫﺫﺍ ﻤﻔﻴﺩ ﻋﻨﺩﻤﺎ ﺘﻤﻸ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺠﺩﺍﻭل ﻤﺘﺸﺎﺒﻬﺔ ﺍﻷﺴﻤﺎﺀ ﻤـﻥ‬
‫ﺃﻜﺜﺭ ﻤﻥ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ‪ ..‬ﻤﺜﻼ‪:‬‬
‫;)"‪Ds.Tables.Add("MyTable", "Db1‬‬
‫;)"‪Ds.Tables.Add("MyTable", "Db2‬‬
‫‪ -٤‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺴـﺘﻘﺒل ﻜـﺎﺌﻥ ﺠـﺩﻭل ‪ ،DataTable‬ﻟﺘـﺘﻡ ﺇﻀـﺎﻓﺘﻪ ﺇﻟـﻰ‬
‫ﺍﻟﻤﺠﻤﻭﻋﺔ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﺍﻟﺼﻴﻎ ﺍﻟﺜﻼﺙ ﺍﻷﻭﻟﻰ ﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ DataTable‬ﻴﻤﺜل ﺍﻟﺠﺩﻭل ﺍﻟـﺫﻱ‬
‫ﺘﻡ‪ ‬ﺇﻨﺸﺎﺅﻩ‪ ،‬ﺒﻴﻨﻤﺎ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﻫﻲ ﺇﺠﺭﺍﺀ ﻻ ﻴﻌﻴﺩ ﺃﻴﺔ ﻗﻴﻤﺔ‪ ،‬ﻭﺫﻟﻙ ﻷﻨﻙ ﺃﺭﺴﻠﺕ ﺇﻟﻴﻬـﺎ‬
‫ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ﺒﺎﻟﻔﻌل‪ ،‬ﻭﻻ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﻤﺭﺠﻊ ﺁﺨﺭ ﻟﻪ‪.‬‬

‫ﻴﻤﻜﻥ ﺤﺫﻓﻪ ‪:CanRemove‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﻤﻥ ﺍﻟﻤﻤﻜﻥ ﺤﺫﻑ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ‪ DataTable‬ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل ﻤـﻥ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل‪ ..‬ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ‪ false‬ﺇﺫﺍ ﻟـﻡ ﻴﻜـﻥ ﺍﻟﺠـﺩﻭل ﻤﻭﺠـﻭﺩﺍ ﻓـﻲ‬
‫ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﺃﻭ ﻜﺎﻥ ﺩﺍﺨﻼ ﻓﻲ ﻋﻼﻗﺔ‪ ..‬ﻭﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﺴﻴﻌﻴﺩ ‪ false‬ﻷﻥ ﺤـﺫﻑ ﺠـﺩﻭل‬
‫ﺍﻟﻤﺅﻟﻔﻴﻥ ﺴﻴﺩﻤﺭ ﺘﻜﺎﻤل ﺍﻟﻌﻼﻗﺔ ﻤﻊ ﺠﺩﻭل ﺍﻟﻜﺘﺏ‪:‬‬
‫(‪MessageBox.Show(Ds.Tables.CanRemove‬‬
‫)) (‪Ds.Tables["Authors"]).ToString‬‬

‫‪٢٦٧‬‬
‫ﺤﺫﻑ ‪:Remove‬‬
‫ﺘﺤﺫﻑ ﺍﻟﺠﺩﻭل ﺍﻟﻤﺭﺴل ﺇﻟﻴﻬﺎ ﻜﻤﻌﺎﻤل‪ ،‬ﻭﻟﻬﺎ ﻨﻔﺱ ﺼﻴﻎ ﺍﻟﻭﺴﻴﻠﺔ ‪ Add‬ﻤﺎ ﻋﺩﺍ ﺍﻟﺼـﻴﻐﺔ‬
‫ﺍﻷﻭﻟﻰ ﺍﻟﺘﻲ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪ ..‬ﻻﺤﻅ ﺃﻨﻙ ﻻ ﺘﺴﺘﻁﻴﻊ ﺤﺫﻑ ﺠﺩﻭل ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫ﺇﺫﺍ ﻜﺎﻥ ﺩﺍﺨﻼ ﻓﻲ ﻋﻼﻗﺔ‪ ،‬ﻓﻬﺫﺍ ﺴﻴﺅﺩﻱ ﺇﻟﻰ ﺤﺩﻭﺙ ﺨﻁﺄ ﻓـﻲ ﺍﻟﺒﺭﻨـﺎﻤﺞ‪ ..‬ﻭﺍﻟﻤﻘﺼـﻭﺩ‬
‫ﺒﺎﻟﻌﻼﻗﺔ ﻫﻨﺎ‪ ،‬ﺍﻟﻌﻼﻗﺔ ﺍﻟﻤﻌﺭﻓﺔ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻓﻠﻭ ﻭﻀـﻌﺕ ﺠـﺩﻭﻟﻲ ﺍﻟﻤـﺅﻟﻔﻴﻥ‬
‫ﻭﺍﻟﻜﺘﺏ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺩﻭﻥ ﺇﻨﺸﺎﺀ ﻋﻼﻗﺔ ﺒﻴﻨﻬﻤﺎ‪ ،‬ﻓﺴﻴﻤﻜﻨﻙ ﺤﺫﻑ ﺃﻴﻬﻤـﺎ ﺒـﺩﻭﻥ‬
‫ﻤﺸﺎﻜل ﺭﻏﻡ ﺃﻥ ﻫﻨﺎﻙ ﻋﻼﻗﺔ ﺒﻴﻨﻬﻤﺎ ﻓﻌﻼ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﺃﻤﺎ ﻟﻭ ﺃﻨﺸـﺄﺕ ﺍﻟﻌﻼﻗـﺔ‬
‫ﺒﻴﻨﻬﻤﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻓﻌﻠﻴﻙ ﺤﺫﻓﻬﺎ ﺃﻭﻻ ﻗﺒل ﻤﺤﺎﻭﻟﺔ ﺤﺫﻑ ﺃﻱ ﻤﻥ ﺍﻟﺠـﺩﻭﻟﻴﻥ‪..‬‬
‫ﻭﺍﻷﻓﻀل ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ CanRemove‬ﺃﻭﻻ ﻗﺒل ﻤﺤﺎﻭﻟﺔ ﺤﺫﻑ ﺍﻟﺠﺩﻭل‪ ..‬ﻤﺜﺎل‪:‬‬
‫;]"‪var T = Ds.Tables["Authors‬‬
‫))‪if (Ds.Tables.CanRemove(T‬‬
‫;)‪Ds.Tables.Remove(T‬‬
‫ﺘﺤﺘﻭﻱ ﻋﻠﻰ ‪:Contains‬‬
‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺠﺩﻭل ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل ﻤﻭﺠﻭﺩﺍ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ..‬ﻭﻟﻬـﺫﻩ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ﺼﻴﻐﺘﺎﻥ‪ ،‬ﺘﻤﺎﺜﻼﻥ ﺍﻟﺼﻴﻐﺘﻴﻥ ﺍﻟﺜﺎﻨﻴﺔ ﻭﺍﻟﺜﺎﻟﺜﺔ ﻟﻠﻭﺴﻴﻠﺔ ‪.Add‬‬

‫ﺭﻗﻡ ﺍﻟﻌﻨﺼﺭ ‪:IndexOf‬‬


‫ﺘﻌﻴﺩ ﺭﻗﻡ ﺍﻟﺠـﺩﻭل ﺍﻟﻤﺭﺴـل ﺇﻟﻴﻬـﺎ ﻜﻤﻌﺎﻤـل ﺇﺫﺍ ﻜـﺎﻥ ﻤﻭﺠـﻭﺩﺍ ﻓـﻲ ﺍﻟﻤﺠﻤﻭﻋـﺔ‪،‬‬
‫ﻭﺘﻌﻴﺩ ‪ ١-‬ﺇﻥ ﻟﻡ ﻴﻜﻥ ﻤﻭﺠﻭﺩﺍ‪ ،‬ﻭﻟﻬﺎ ﻨﻔﺱ ﺼﻴﻎ ﺍﻟﻭﺴﻴﻠﺔ ‪ Add‬ﻤﺎ ﻋﺩﺍ ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟـﻰ‬
‫ﺍﻟﺘﻲ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل ﺤﺩﺜﻴﻥ ﺠﺩﻴﺩﻴﻥ‪ ،‬ﻫﻤﺎ‪:‬‬

‫‪٢٦٨‬‬
‫ﺍﻟﻤﺠﻤﻭﻋﺔ ﺘﺘﻐﻴﺭ ‪:CollectionChanging‬‬
‫ﻴﻨﻁﻠﻕ ﻋﻨﺩﻤﺎ ﺘﻭﺸﻙ ﺠﺩﺍﻭل ﺍﻟﻤﺠﻤﻭﻋﺔ ﻋﻠﻰ ﺍﻟﺘﻐﻴﺭ‪ ،‬ﻨﺘﻴﺠﺔ ﺇﻀﺎﻓﺔ ﺃﻭ ﺤـﺫﻑ ﺠـﺩﻭل‪..‬‬
‫ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،CollectionChangeEventArgs‬ﻭﻫـﻭ‬
‫ﻴﻤﺘﻠﻙ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤـﺭﻗﻡ ‪ CollectionChangeAction‬ﺍﻟﺘـﻲ‬ ‫‪Action‬‬


‫ﺘﻭﻀﺢ ﻨﻭﻉ ﺍﻟﻔﻌل ﺍﻟﺫﻱ ﺴﺒﺏ ﺘﻐﻴﺭ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ..‬ﻭﻫﺫﻩ ﺍﻟﻘﻴﻡ ﻫﻲ‪:‬‬
‫‪ :Add -‬ﺇﻀﺎﻓﺔ ﻋﻨﺼﺭ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪.‬‬
‫‪ :Remove -‬ﺤﺫﻑ ﻋﻨﺼﺭ ﻤﻥ ﺍﻟﻤﺠﻤﻭﻋﺔ‪.‬‬
‫‪ :Refresh -‬ﺘﻐﻴﺭ ﻋﻨﺎﺼﺭ ﺍﻟﻤﺠﻤﻭﻋﺔ ﻜﻠﻬـﺎ‪ ،‬ﺒﺴـﺒﺏ ﺒﻌـﺽ‬
‫ﺍﻟﻭﺴﺎﺌل ﻤﺜل ‪ Clear‬ﺍﻟﺘﻲ ﺘﻤﺤﻭ ﻜل ﺍﻟﻌﻨﺎﺼﺭ‪.‬‬
‫‪ Element‬ﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ‪ Object‬ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺫﻱ ﺘﻌﺭﺽ ﻟﻠﺘﻐﻴﻴﺭ‪..‬‬
‫ﻻﺤﻅ ﺃﻥ ﻗﻴﻤﺔ ﻫﺫﺍ ﺍﻟﻌﻨﺼﺭ ﺴﺘﻜﻭﻥ ‪ null‬ﺇﺫﺍ ﻜﺎﻨـﺕ ﻟﻠﺨﺎﺼـﻴﺔ‬
‫‪ Action‬ﺍﻟﻘﻴﻤﺔ ‪.Refresh‬‬

‫ﺍﻟﻤﺠﻤﻭﻋﺔ ﺘﻐﻴﺭﺕ ‪:CollectionChanged‬‬


‫ﻴﻨﻁﻠﻕ ﺒﻌﺩ ﺤﺩﻭﺙ ﺍﻟﺘﻐﻴﻴﺭ ﻓﻌﻠﻴﺎ ﻓﻲ ﻋﻨﺎﺼﺭ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ 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‬‬
‫‪٢٧٠‬‬
‫ﻭﻴﻤﺘﻠﻙ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﺴﻡ ﺍﻟﺠﺩﻭل ‪:TableName‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻨﻁﺎﻕ ﺍﻻﺴﻡ ‪:Namespace‬‬
‫ﺘﺤﺩﺩ ﺍﺴﻡ ﺍﻟﻨﻁﺎﻕ ﺍﻟﺫﻱ ﺴﻴﻨﺩﺭﺝ ﺘﺤﺘﻪ ﺍﻟﺠﺩﻭل‪ ..‬ﻫﺫﺍ ﻴﺴﻤﺢ ﺒﻭﺠﻭﺩ ﺃﻜﺜﺭ ﻤﻥ ﺠﺩﻭل ﺒﻨﻔﺱ‬
‫ﺍﻻﺴﻡ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺇﺫﺍ ﻜﺎﻥ ﻜل ﻤﻨﻬﺎ ﻓﻲ ﻨﻁﺎﻕ ﻤﺨﺘﻠﻑ‪.‬‬

‫ﺍﻟﺒﺎﺩﺌﺔ ‪:Prefix‬‬
‫ﺘﺤﺩﺩ ﺍﻟﺒﺎﺩﺌﺔ ﺍﻟﺘﻲ ﺴﺘﻤﻴﺯ ﺍﻟﺠﺩﻭل ﻜﺎﺨﺘﺼﺎﺭ ﻻﺴﻡ ﻨﻁﺎﻗﻪ‪.‬‬

‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataSet‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataSet‬ﺍﻟﺘﻲ ﻴﻨﺘﻤﻲ ﺇﻟﻴﻬﺎ ﻫﺫﺍ ﺍﻟﺠﺩﻭل‪ ..‬ﻭﺘﻌﻴـﺩ ‪ null‬ﺇﺫﺍ‬
‫ﻟﻡ ﻴﻜﻥ ﺍﻟﺠﺩﻭل ﻤﻀﺎﻓﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺤﺎﻟﻴﺎ‪.‬‬

‫ﺘﻨﺴﻴﻕ ﺍﻟﺘﻌﺎﻤل ﻋﻥ ﺒﻌﺩ ‪:RemotingFormat‬‬


‫ﺘﺤﺩﺩ ﺍﻟﺘﻨﺴﻴﻕ ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺒﻪ ﺇﺭﺴﺎل ﺒﻴﺎﻨﺎﺕ ﺍﻟﺠﺩﻭل ﻤﻥ ﺠﻬﺎﺯ ﺇﻟﻰ ﺁﺨﺭ‪ ،‬ﻋﻨﺩﻤﺎ ﺍﻟﺘﻌﺎﻤـل‬
‫ﻤﻊ ﺒﺭﻨﺎﻤﺞ ﻴﺴﺘﺨﺩﻡ ﺍﻟﺘﺤﻜﻡ ﻋﻥ ﺒﻌﺩ ‪ ،Remoting‬ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤـﺩﻯ ﻗﻴﻤﺘـﻲ ﺍﻟﻤـﺭﻗﻡ‬
‫‪ SerializationFormat‬ﺍﻟﻠﺘﻴﻥ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﻤﺎ ﺴﺎﺒﻘﺎ‪.‬‬

‫ﺤﺴﺎﺱ ﻟﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ ‪:CaseSensitive‬‬


‫ﻟــﻭ ﺠﻌﻠــﺕ ﻫــﺫﻩ ﺍﻟﺨﺎﺼــ ‪‬ﻴ‪‬ﺔ ‪ ،true‬ﻓﺴــﺘﺘﻡ‪ ‬ﻤﺭﺍﻋــﺎﺓ ﺤﺎﻟــﺔ ﺍﻟﺤــﺭﻭﻑ‬
‫)ﺼﻐﻴﺭﺓ ‪ Small‬ﺃﻭ ﻜﺒﻴﺭﺓ ‪ (Capital‬ﻋﻨﺩ ﻤﻘﺎﺭﻨﺔ ﺍﻟﻨﺼﻭﺹ ﻓﻲ ﻫﺫﺍ ﺍﻟﺠﺩﻭل‪.‬‬

‫‪٢٧١‬‬
‫ﺍﻟﻤﺤل ‪:Locale‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺜﻘﺎﻓﺔ ‪ ،CultureInfo‬ﺍﻟﺫﻱ ﻴﻤﺜـل ﺍﻟﻠﻐـﺔ ﺍﻟﺘـﻲ ﺘﺭﻴـﺩ‬
‫ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﻤﻘﺎﺭﻨﺔ ﻭﺘﺭﺘﻴﺏ ﺍﻟﻨﺼﻭﺹ‪.‬‬

‫ﺃﻗل ﺴﻌﺔ ‪:MinimumCapacity‬‬


‫ﺘﺤﺩﺩ ﺃﻗل ﻋﺩﺩ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﻴﻤﻜﻥ ﺃﻥ ﻴﺤﺘﻭﻴﻪ ﺍﻟﺠﺩﻭل‪ ..‬ﻭﺍﻟﻘﻴﻤـﺔ ﺍﻻﻓﺘﺭﺍﻀـﻴﺔ ﻟﻬـﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﻫﻲ ‪ ٥٠‬ﺴﺠﻼ‪ ..‬ﻭﺘﻔﻴﺩﻙ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻓﻲ ﺤﺠﺯ ﺍﻟﺫﺍﻜﺭﺓ ﺍﻟﻼﺯﻤﺔ ﻋﻨﺩ ﺍﻟﺘﻌﺎﻤل‬
‫ﻤﻊ ﺠﺩﺍﻭل ﻀﺨﻤﺔ ﺍﻟﺤﺠﻡ‪ ،‬ﻟﺘﺤﺴﻴﻥ ﻜﻔﺎﺀﺓ ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ،‬ﻓﺄﻨﺕ ﺘﺤﺩﺩ ﺴﻌﺔ ﻤﺒﺩﺌﻴﺔ ﻜﺒﻴﺭﺓ ﻟﺤﺠﺯ‬
‫ﺍﻟﺫﺍﻜﺭﺓ ﺍﻟﻤﻨﺎﺴﺒﺔ‪ ،‬ﺒﺩﻻ ﻤﻥ ﺃﻥ ﻴﺘﻡ ﺤﺠﺯ ﺫﺍﻜﺭﺓ ﺼﻐﻴﺭﺓ‪ ،‬ﺜﻡ ﻴﻀﻁﺭ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﻟﻰ ﺯﻴﺎﺩﺘﻬﺎ‬
‫ﺃﻜﺜﺭ ﻤﻥ ﻤﺭﺓ ﺃﺜﻨﺎﺀ ﺘﺸﻐﻴﻠﻪ‪.‬‬

‫ﺍﻷﻋﻤﺩﺓ ‪:Columns‬‬
‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ ‪ DataColumnCollection‬ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓـﻲ ﻫـﺫﺍ ﺍﻟﺠـﺩﻭل‪..‬‬
‫ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬

‫ﺍﻟﺼﻔﻭﻑ ‪:Rows‬‬
‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ ‪ DataRowCollection‬ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓـﻲ ﻫـﺫﺍ ﺍﻟﺠـﺩﻭل‪..‬‬
‫ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬

‫ﺍﻟﻌﻼﻗﺎﺕ ﺍﻟﺭﺌﻴﺴﻴﺔ ‪:ParentRelations‬‬


‫ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻌﻼﻗﺎﺕ ‪ DataRelationCollection‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻌﻼﻗﺎﺕ‬
‫ﺍﻟﺨﺎﺭﺠﺔ ﻤﻥ ﻫﺫﺍ ﺍﻟﺠﺩﻭل )ﺍﻟﻌﻼﻗﺎﺕ ﺍﻟﺘـﻲ ﻴـﺩﺨل ﻓﻴﻬـﺎ ﻜﺠـﺩﻭل ﺭﺌﻴﺴـﻲ‪Master ‬‬
‫‪ ..(Table‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ ‪ DataRelationCollection‬ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬

‫‪٢٧٢‬‬
‫ﺍﻟﻌﻼﻗﺎﺕ ﺍﻟﻔﺭﻋﻴﺔ ‪:ChildRelations‬‬
‫ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﻌﻼﻗـﺎﺕ ‪ ،DataRelationCollection‬ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ‬
‫ﺍﻟﻌﻼﻗﺎﺕ ﺍﻟﻘﺎﺩﻤﺔ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﺠﺩﻭل )ﺍﻟﻌﻼﻗﺎﺕ ﺍﻟﺘﻲ ﻴﺩﺨل ﻓﻴﻬﺎ ﻜﺠﺩﻭل ﻓﺭﻋﻲ ﺃﻭ ﺠـﺩﻭل‬
‫ﺍﻟﺘﻔﺎﺼﻴل ‪.(Details Table‬‬

‫ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ‪:PrimaryKey‬‬


‫ﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﺼﻔﻭﻓﺔ ﺃﻋﻤﺩﺓ ‪ ،DataColumn Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻷﻋﻤـﺩﺓ‬
‫ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻜﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ ﻟﻠﺠﺩﻭل‪ ..‬ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻤﺼـﻔﻭﻓﺔ ﺒﻬـﺎ ﺨﺎﻨـﺔ‬
‫ﻭﺍﺤﺩﺓ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻴﺘﻜﻭﻥ ﻤﻥ ﻋﻤﻭﺩ ﻭﺍﺤﺩ‪ ،‬ﺃﻭ ﺍﺴﺘﺨﺩﺍﻡ ﻤﺼﻔﻭﻓﺔ ﺒﻬﺎ ﺃﻜﺜـﺭ‬
‫ﻤﻥ ﺨﺎﻨﺔ ﺇﺫﺍ ﻜﻨﺕ ﺘﺴﺘﺨﺩﻡ ﻋﻤﻭﺩﻴﻥ ﺃﻭ ﺃﻜﺜﺭ ﻤﻌﺎ ﻜﻤﻔﺘﺎﺡ ﺭﺌﻴﺴﻲ‪ ‬ﻟﻠﺠﺩﻭل‪ ..‬ﻤﺜﻼ‪ :‬ﻟﻭ ﻜﺎﻥ‬
‫ﻟﺩﻴﻙ ﺠﺩﻭل ﺒﻪ ﻋﻤﻭﺩ ﻟﻼﺴﻡ ﺍﻷﻭل ﻟﻠﺸﺨﺹ‪ ،‬ﻭﻋﻤﻭﺩ ﺁﺨﺭ ﻻﺴﻤﻪ ﺍﻷﻭﺴﻁ‪ ،‬ﻭﻋﻤﻭﺩ ﺜﺎﻟﺙ‬
‫ﻻﺴﻤﻪ ﺍﻷﺨﻴﺭ‪ ،‬ﻓﻜل ﻋﻤﻭﺩ ﻤﻥ ﻫﺫﻩ ﺍﻷﻋﻤﺩﺓ ﻻ ﻴﺼﻠﺢ ﺒﻤﻔﺭﺩﻩ ﻜﻤﻔﺘـﺎﺡ ﺃﺴﺎﺴـﻲ ﺒﺴـﺒﺏ‬
‫ﺘﻜﺭﺭ ﺍﻷﺴﻤﺎﺀ ﺒﻪ‪ ،‬ﺒﻴﻨﻤﺎ ﻗﺩ ﺘﺼﻠﺢ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺜﻼﺜﺔ ﻤﻌﺎ ﻜﻤﻔﺘـﺎﺡ ﺃﺴﺎﺴـﻲ‪ ،‬ﻷﻥ ﺍﻻﺴـﻡ‬
‫ﺍﻟﺜﻼﺜﻲ ﻨﺎﺩﺭﺍ ﻤﺎ ﻴﺘﻜﺭﺭ‪ ..‬ﻜل ﻤﺎ ﻋﻠﻴﻙ ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻫﻭ ﻭﻀﻊ ﻜﺎﺌﻨﺎﺕ ﻫـﺫﻩ ﺍﻷﻋﻤـﺩﺓ‬
‫ﻓﻲ ﻤﺼﻔﻭﻓﺔ ﻭﻭﻀﻌﻬﺎ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻟﺘﺼﻴﺭ ﻫﺫﻩ ﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ‪.‬‬

‫ﺍﻟﻘﻴﻭﺩ ‪:Constraints‬‬
‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻘﻴﻭﺩ ‪ ConstraintCollection‬ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻫﺫﺍ ﺍﻟﺠﺩﻭل‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ‬
‫ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬

‫ﺍﻟﻌﺭﺽ ﺍﻻﻓﺘﺭﺍﻀﻲ ‪:DefaultView‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ‪ DataView Object‬ﺍﻟﺫﻱ ﻴﺤﻤل ﻤﺒـﺩﺌﻴﺎ ﻜـل ﺒﻴﺎﻨـﺎﺕ ﺍﻟﺠـﺩﻭل‬
‫ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻟﻜﻨﻙ ﺘﺴﺘﻁﻴﻊ ﻀﺒﻁﻪ ﻟﻌﺭﺽ ﺠﺯﺀ ﻓﻘﻁ ﻤﻥ ﺴـﺠﻼﺕ ﺍﻟﺠـﺩﻭل ﺘﺒﻌـﺎ ﻟﺸـﺭﻁ‬
‫ﻤﻌﻴﻥ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻓﺌﺔ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataView Class‬ﺒﺎﻟﺘﻔﺼﻴل ﻓﻲ ﺍﻟﻔﺼل‬
‫ﺍﻟﺘﺎﻟﻲ‪.‬‬

‫‪٢٧٣‬‬
‫ﺘﻌﺒﻴﺭ ﺍﻟﻌﺭﺽ ‪:DisplayExpression‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﻨﺹ‪ ‬ﺍﻟﺫﻱ ﺴﻴﺘﻡ‪ ‬ﻋﺭﻀﻪ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻜﻌﻨﻭﺍﻥ ﻟﻠﺠﺩﻭل ﻓـﻲ ﺃﺩﻭﺍﺕ ﻋـﺭﺽ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﺎﻷﺩﺍﺓ ‪.DataGridView‬‬

‫ﺒﻪ ﺃﺨﻁﺎﺀ ‪:HasErrors‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻨﺕ ﻫﻨﺎﻙ ﺃ ‪‬ﻴ‪‬ﺔ ﺃﺨﻁﺎﺀ ﻓﻲ ﺃﻱ ﺼﻑﹼ ﻓﻲ ﻫﺫﺍ ﺍﻟﺠﺩﻭل‪.‬‬

‫ﺍﻟﺨﺼﺎﺌﺹ ﺍﻹﻀﺎﻓﻴﺔ ‪:ExtendedProperties‬‬


‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺨﺼﺎﺌﺹ ‪ PropertyCollection‬ﺍﻟﺘﻲ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﺍﻟﺨﺼـﺎﺌﺹ‬
‫ﺍﻹﻀﺎﻓ ‪‬ﻴ‪‬ﺔ ﻟﻠﺠﺩﻭل‪ ..‬ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻀﻴﻑ ﺨﺎﺼ‪‬ﻴ‪‬ﺔ ﺍﺴـﻤﻬﺎ ‪ Password‬ﺇﻟـﻰ ﺠـﺩﻭل‬
‫ﺍﻟﻜﺘﺏ‪ ،‬ﻭﻴﻀﻊ ﻓﻴﻬﺎ ﺍﻟﻘﻴﻤﺔ "ﻜﻠﻤﺔ ﺍﻟﻤﺭﻭﺭ"‪ ،‬ﺜﻡ ﻴﻐﻴﺭﻫﺎ ﺇﻟﻰ "ﺃﺤﻤﺩ‪:"١٢٣‬‬
‫;]"‪DataTable T = Ds.Tables["Books‬‬
‫;‪PropertyCollection EP = T.ExtendedProperties‬‬
‫;)"ﻜﻠﻤﺔ ﺍﻟﻤﺭﻭﺭ" ‪EP.Add("Password",‬‬
‫;"ﺃﺤﻤﺩ‪EP["Password"] = "١٢٣‬‬
‫;)) (‪MessageBox.Show(EP["Password"].ToString‬‬

‫ﻜﻤﺎ ﻴﻤﺘﻠﻙ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻨﺴﺦ ‪:Clone‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺠﺩﻭل ‪ DataTable‬ﺠﺩﻴﺩﺍ‪ ،‬ﻭﺘﻨﺴﺦ ﺇﻟﻴﻪ ﻤﺨﻁﻁ ﺍﻟﺠﺩﻭل ﺍﻟﺤـﺎﻟﻲ ‪Schema‬‬
‫ﺒﻜلّ ﻤﺎ ﻓﻴﻪ ﻤﻥ ﺃﻋﻤﺩﺓ ﻭﻗﻴﻭﺩ‪ ..‬ﻭﻟﻜﻥ‪ ‬ﺍﻟﺠﺩﻭل ﺍﻟﻨﺎﺘﺞ ﻴﻜﻭﻥ ﻓﺎﺭﻏﺎ ﻤﻥ ﺍﻟﺴﺠﻼﺕ‪.‬‬

‫ﻨﺴﺦ ‪:Copy‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺠﺩﻭل ‪ DataTable‬ﺠﺩﻴﺩﺍ‪ ،‬ﻭﺘﻨﺴﺦ ﺇﻟﻴﻪ ﺍﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ ﺒﻤﺨﻁﻁﻪ ﻭﺴـﺠﻼﺘﻪ‪،‬‬
‫ﻟﻴﻜﻭﻥ ﻤﻤﺎﺜﻼ ﻟﻠﺠﺩﻭل ﺍﻷﺼﻠﻲ ﺘﻤﺎﻤﺎ‪.‬‬

‫‪٢٧٤‬‬
‫ﻤﺤﻭ ‪:Clear‬‬
‫ﺘﻤﺤﻭ ﻜلّ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺠﺩﻭل‪.‬‬

‫ﺘﺼﻔﻴﺭ ‪:Reset‬‬
‫ﺘﻔﺭﻍ ﺍﻟﺠﺩﻭل ﺘﻤﺎﻤﺎ ﻤﻥ ﻜل ﺃﻋﻤﺩﺘﻪ ﻭﺴﺠﻼﺘﻪ ﻭﻗﻴﻭﺩﻩ‪.‬‬

‫ﺼﻑ ﺠﺩﻴﺩ ‪:NewRow‬‬


‫ﺘﻨﺸﺊ ﺴﺠﻼ ﺠﺩﻴﺩﺍ ﻟﻪ ﻨﻔﺱ ﻤﺨﻁﻁ ﺍﻟﺠﺩﻭل )ﻨﻔﺱ ﺍﻷﻋﻤﺩﺓ ﺒﻨﻔﺱ ﺃﻨﻭﺍﻉ ﺒﻴﺎﻨﺎﺘﻬـﺎ ﺒـﻨﻔﺱ‬
‫ﺘﺭﺘﻴﺒﻬﺎ(‪ ،‬ﻭﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻜﺎﺌﻥ ﺍﻟﺼﻑ ‪ DataRow‬ﺍﻟﺫﻱ ﻴﺸﻴﺭ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﺴﺠل‪ ،‬ﻟﻜـﻥ ﺩﻭﻥ‬
‫ﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺼﻔﻭﻑ ﺍﻟﺠﺩﻭل ‪ ،Rows‬ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺃﻥ ﺘﻀﻴﻔﻪ ﺇﻟﻴﻬـﺎ ﺒﻨﻔﺴـﻙ‪..‬‬
‫ﻤﺜﺎل‪:‬‬
‫;]"‪DataTable T = Ds.Tables["Authors‬‬
‫;) (‪var R = T.NewRow‬‬
‫;"ﺃﺤﻤﺩ ﺸﻭﻗﻲ" = ]"‪R["Author‬‬
‫;"ﺃﻤﻴﺭ ﺍﻟﺸﻌﺭﺍﺀ" = ]"‪R["About‬‬
‫;‪R["CountryID"] = 21‬‬
‫;)‪T.Rows.Add(R‬‬

‫ﺍﺴﺘﻌﺎﺭﺓ ﺼﻑ ‪:ImportRow‬‬
‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺼﻑ ‪ ،DaraRow‬ﻟﺘﻨﺴﺨﻪ ﻭﺘﻀﻴﻔﻪ ﺇﻟﻰ ﺍﻟﺠـﺩﻭل ﺒﻜـلّ‬
‫ﺒﻴﺎﻨﺎﺘﻪ ﻭﺨﺼﺎﺌﺼﻪ‪ ،‬ﺒﻤﺎ ﻓﻲ ﺫﻟـﻙ ﺍﻟﻨﺴـﺨﺔ ﺍﻷﺼـﻠﻴﺔ ‪ Original Version‬ﻭﺍﻟﻨﺴـﺨﺔ‬
‫ﺍﻟﺤﺎﻟﻴﺔ ‪ Current Version‬ﻟﻘﻴﻡ ﺨﺎﻨﺎﺘﻪ‪.‬‬

‫‪٢٧٥‬‬
‫ﺘﺤﻤﻴل ﺼﻑ ‪:LoadDataRow‬‬
‫ﺍﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻟﺘﺤﺩﻴﺙ ﺃﺤﺩ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل‪ ،‬ﺃﻭ ﺇﻀـﺎﻓﺔ ﺴـﺠل ﺠﺩﻴـﺩ ﺇﻟﻴـﻪ‪..‬‬
‫ﻭﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻠﻴﻥ‪:‬‬
‫‪ -‬ﻤﺼﻔﻭﻓﺔ ﻜﺎﺌﻨﺎﺕ ‪ ،Object Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻗﻴﻡ ﺨﺎﻨﺎﺕ ﺍﻟﺴﺠل ﺒﻨﻔﺱ ﺘﺭﺘﻴﺒﻬﺎ‬
‫ﻓﻲ ﺍﻟﺠﺩﻭل‪ ..‬ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺴﺘﺒﺤﺙ ﻓﻲ ﺍﻟﺠﺩﻭل‪ ،‬ﻟﺘﺭﻯ ﺇﻥ ﻜﺎﻥ ﺍﻟﻤﻔﺘـﺎﺡ‬
‫ﺍﻷﺴﺎﺴﻲ ﻷﻱ ﺤﻘل ﻟﻪ ﻨﻔﺱ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺨﺎﻨﺔ ﺍﻟﻤﻨﺎﻅﺭﺓ ﻓﻲ ﺍﻟﻤﺼﻔﻭﻓﺔ‪..‬‬
‫ﻓﺈﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻤﻭﺠﻭﺩﺍ ﻓﻲ ﺍﻟﺠﺩﻭل‪ ،‬ﻴـﺘﻡ ﻨﺴـﺦ ﺒـﺎﻗﻲ ﺍﻟﻘـﻴﻡ ﻤـﻥ‬
‫ﺍﻟﻤﺼﻔﻭﻓﺔ ﺇﻟﻰ ﺒﺎﻗﻲ ﺤﻘﻭل ﺍﻟﺴﺠلّ ﻟﺘﺤﺩﻴﺜﻬﺎ‪ ..‬ﻭﺇﺫﺍ ﻟﻡ ﻴﻜـﻥ ﺍﻟﻤﻔﺘـﺎﺡ ﺍﻷﺴﺎﺴـﻲ‬
‫ﻤﻭﺠﻭﺩﺍ‪ ،‬ﻴﺘﻡ ﺇﻨﺸﺎﺀ ﺴﺠلّ ﺠﺩﻴﺩ ﻭﺘﻭﻀﻊ ﺒﺤﻘﻭﻟﻪ ﻗﻴﻡ ﺍﻟﻤﺼﻔﻭﻓﺔ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﺘﺭﻙ ﺇﺤﺩﻯ ﺨﺎﻨﺎﺕ ﺍﻟﻤﺼﻔﻭﻓﺔ ﻓﺎﺭﻏﺔ‪ ،‬ﺴـﻴﺅﺩﻱ ﺇﻟـﻰ ﻭﻀـﻊ ﺍﻟﻘﻴﻤـﺔ‬
‫ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻓﻲ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻤﻨﺎﻅﺭ ﻟﻬﺎ ﺇﻥ ﻜﺎﻨﺕ ﻟﻪ ﻗﻴﻤﺔ ﺍﻓﺘﺭﺍﻀﻴﺔ‪ ،‬ﺃﻭ ﺴﻴﺘﻡ ﺘﻭﻟﻴـﺩ‬
‫ﺍﻟﺘﺭﻗﻴﻡ ﺍﻟﺘﻠﻘﺎﺌﻲ ﺇﺫﺍ ﻜﺎﻨﺕ ﻟﻠﺨﺎﺼـﻴﺔ ‪ AutoIncrement‬ﻟﻬـﺫﺍ ﺍﻟﻌﻤـﻭﺩ ﺍﻟﻘﻴﻤـﺔ‬
‫‪ ..true‬ﻓﺈﺫﺍ ﻟﻡ ﻴﻜﻥ ﻫﺫﺍ ﺃﻭ ﺫﺍﻙ‪ ،‬ﻭﻜﺎﻨﺕ ﺍﻟﺨﺎﻨـﺔ ﻻ ﺘﻘﺒـل ﺃﻥ ﺘﻅـل ﻓﺎﺭﻏـﺔ‪،‬‬
‫ﻓﺴﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ..‬ﻭﻴﺤﺩﺙ ﺨﻁﺄ ﺃﻴﻀﺎ ﺇﺫﺍ ﻜﺎﻥ ﻋﺩﺩ ﺨﺎﻨﺎﺕ ﺍﻟﻤﺼﻔﻭﻓﺔ‬
‫ﺃﻜﺒﺭ ﻤﻥ ﻋﺩﺩ ﺃﻋﻤﺩﺓ ﺍﻟﺠﺩﻭل‪.‬‬
‫‪ -‬ﻤﻌﺎﻤل ﻤﻨﻁﻘﻲ ‪ ،Boolean‬ﻟﻭ ﺠﻌﻠﺕ ﻗﻴﻤﺘـﻪ ‪ true‬ﻓﺴـﻴﺘﻡ‪ ‬ﺍﺴـﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪ AcceptChanges‬ﺒﻌﺩ ﺇﻀﺎﻓﺔ ﺍﻟﺴﺠل ﺇﻟﻰ ﺍﻟﺠﺩﻭل ﻭﺒﻬﺫﺍ ﻴﻌﺘﺒﺭ ﻫـﺫﺍ ﺍﻟﺴـﺠل‬
‫ﺴﺠﻼ ﺃﺼﻠﻴﺎ ﻟﻡ ﻴﺤﺩﺙ ﻟﻪ ﺃﻱ ﺘﻐﻴﻴﺭ‪ ..‬ﺃﻤﺎ ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫـﺫﺍ ﺍﻟﻤﻌﺎﻤـل ‪،false‬‬
‫ﻓﺴﻴﻌﺘﺒﺭ ﺍﻟﺴﺠل ﺍﻟﺠﺩﻴﺩ ﺴﺠﻼ ﻤﻀﺎﻓﺎ ‪ Added‬ﻭﻴﻌﺘﺒﺭ ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﺘـﻡ ﺘﺤﺩﻴﺜـﻪ‬
‫ﺴﺠﻼ ﻤﻌﺩﻻ ‪.Modified‬‬
‫ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺼﻑﹼ ‪ DataRow‬ﻴﺤﻤل ﻤﺭﺠﻌﺎ ﺇﻟﻰ ﺍﻟﺼﻑﹼ ﺍﻟﺫﻱ ﺘﻡ‪ ‬ﺘﺤﺩﻴﺜـﻪ‬
‫ﺃﻭ ﺇﻀﺎﻓﺘﻪ‪.‬‬

‫‪٢٧٦‬‬
‫ﻤﻠﺤﻭﻅﺔ‪:‬‬

‫‪٢٧٧‬‬
‫ﺘﻘﻭﻡ ﻓﺌﺔ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ‪ ،Typed DataSet Class‬ﺒﺘﻌﺭﻴﻑ ﻋﺩﺓ ﻭﺴـﺎﺌل‬
‫ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﻓﻲ ﻜل ﺠﺩﻭل ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺼﻔﻭﻓﻪ‪ ..‬ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜﺎل‪ ،‬ﻟـﻭ ﻜـﺎﻥ ﻓـﻲ‬
‫ﺍﻟﻤﺠﻤﻭﻋﺔ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﻓﺌﺔ ﻟﺠﺩﻭل ﺍﻟﻤـﺅﻟﻔﻴﻥ ﺍﺴـﻤﻬﺎ ‪ ،AuthorsDataTable‬ﻭﺘـﻡ‬
‫ﺘﻌﺭﻴﻑ ﻓﺌﺔ ﺍﺴﻤﻬﺎ ‪ AuthorsRow‬ﺘﻤﺜل ﻨﻭﻉ ﺼﻔﻭﻑ ﻫﺫﺍ ﺍﻟﺠﺩﻭل‪ ،‬ﻓﺈﻥ ﻫﺫﺍ ﺍﻟﺠـﺩﻭل‬
‫ﺴﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺼﻑ ﻤﺅﻟﻔﻴﻥ ﺠﺩﻴﺩ ‪:NewAuthorsRow‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ AuthorsRow‬ﻴﻤﺜل ﺼﻔﺎ ﺠﺩﻴـﺩﺍ ﻤـﻥ ﺼـﻔﻭﻑ ﺠـﺩﻭل‬
‫ﺍﻟﻤﺅﻟﻔﻴﻥ‪ ،‬ﺒﺤﻴﺙ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ‪ ..‬ﻤﺜﺎل‪:‬‬
‫;‪var R = Ds.Authors.NewAuthorsRow‬‬
‫;"ﺃﺤﻤﺩ ﺸﻭﻗﻲ" = ‪R.Author‬‬
‫;"ﺃﻤﻴﺭ ﺍﻟﺸﻌﺭﺍﺀ" = ‪R.About‬‬
‫;‪R.CountryID = 21‬‬
‫;)‪Ds.Authors.AddAuthorsRow(R‬‬

‫ﺇﻀﺎﻓﺔ ﺼﻑ ﺍﻟﻤﺅﻟﻔﻴﻥ ‪:AddAuthorsRow‬‬


‫ﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻼ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ AuthorsRow‬ﻴﻤﺜل ﺼﻔﺎ ﻤـﻥ ﺼـﻔﻭﻑ‬
‫ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ‪ ،‬ﻹﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ‪ ،‬ﻜﻤﺎ ﺭﺃﻴﻨﺎ ﻓﻲ ﺍﻟﻤﺜﺎل ﺍﻟﺴﺎﺒﻕ‪.‬‬
‫ﻭﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺃﺨﺭﻯ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺘﺴﺘﻘﺒل ﻗﻴﻡ ﺼـﻑ ﺍﻟﻤـﺅﻟﻔﻴﻥ ﻹﻀـﺎﻓﺘﻪ ﺇﻟـﻰ‬
‫ﺍﻟﺠﺩﻭل ﻓﻲ ﺨﻁﻭﺓ ﻭﺍﺤﺩﺓ‪ ..‬ﻫﻜﺫﺍ ﻤﺜﻼ ﻴﻤﻜﻥ ﺍﺨﺘﺯﺍل ﺍﻟﻤﺜﺎل ﺍﻟﺴﺎﺒﻕ‪:‬‬
‫‪", 21, "",‬ﺃﺤﻤﺩ ﺸﻭﻗﻲ"(‪Ds.Authors.AddAuthorsRow‬‬
‫;)‪", null‬ﺃﻤﻴﺭ ﺍﻟﺸﻌﺭﺍﺀ"‬
‫ﻻﺤﻅ ﺃﻨﻨﺎ ﺃﺭﺴﻠﻨﺎ ﻨﺼﺎ ﻓﺎﺭﻏﺎ ﺇﻟﻰ ﺨﺎﻨﺔ ﺭﻗﻡ ﺍﻟﻬﺎﺘﻑ‪ ،‬ﻜﻤﺎ ﻭﻀﻌﻨﺎ ﺍﻟﻘﻴﻤﺔ ‪ null‬ﻓـﻲ‬
‫ﺨﺎﻨﺔ ﻁﺎﺒﻊ ﺍﻟﻭﻗﺕ ﻷﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﻭﻟﺩﻫﺎ ﺘﻠﻘﺎﺌﻴﺎ‪ ..‬ﺃﻤﺎ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺭﺌﻴﺴـﻲ ﻟﻬـﺫﺍ‬
‫ﺍﻟﺼﻑ )ﻭﻫﻭ ﺍﻟﺤﻘل ‪ (ID‬ﻓﻠﻡ ﺘﻁﺎﻟﺒﻨﺎ ﺒﻪ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺃﺼﻼ ﻷﻨﻬﺎ ﺘﻌﺭﻑ ﺃﻨﻪ ﻴﻭﻟـﺩ‬
‫ﺘﻠﻘﺎﺌﻴﺎ‪.‬‬

‫ﺤﺫﻑ ﺼﻑ ﺍﻟﻤﺅﻟﻔﻴﻥ ‪:RemoveAuthorsRow‬‬


‫ﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻼ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ 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‬ﻫﻭ ﻤﺘﻐﻴﺭ ﻴﺤﻤل ﺭﻗﻡ ﺍﻟﻁﺎﻟﺏ ﺍﻟﻤﺭﺍﺩ ﺤﺴﺎﺏ ﻤﺠﻤﻭﻉ ﺩﺭﺠﺎﺘﻪ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ‪:GetChanges‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺠﺩﻭل ‪ DataTable‬ﺠﺩﻴﺩﺍ‪ ،‬ﻴﺤﺘﻭﻱ ﻓﻘﻁ ﻋﻠﻰ ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﻲ ﺘﻡ‪ ‬ﺘﻌـﺩﻴﻠﻬﺎ ﺃﻭ‬
‫ﺇﻀﺎﻓﺘﻬﺎ ﺃﻭ ﺤﺫﻓﻬﺎ ﻤﻥ ﺍﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻤﻨﺫ ﺃﻥ ﺘﻡ‪ ‬ﺘﺤﻤﻴﻠﻪ ﺃﻭ ﻤﻨﺫ ﺁﺨﺭ ﺍﺴﺘﺩﻋﺎﺀ ﻟﻠﻭﺴـﻴﻠﺔ‬
‫‪.AcceptChanges‬‬
‫ﻭﻫﻨﺎﻙ ﺼﻴﻐﺔ ﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻤﻥ ﻨـﻭﻉ ﺍﻟﻤـﺭﻗﻡ ‪DataRowState‬‬
‫ﺍﻟﺫﻱ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻪ ﻤﻥ ﻗﺒل‪ ،‬ﻟﺘﺘﻤﻜﻥ ﻤﻥ ﺍﺨﺘﻴﺎﺭ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﻭﻗﻊ ﻋﻠﻴﻬﺎ ﻨﻭﻉ ﻤﻌﻴﻥ ﻤـﻥ‬
‫ﺍﻟﺘﻐﻴﻴﺭ ﺩﻭﻥ ﻏﻴﺭﻩ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻷﺨﻁﺎﺀ ‪:GetErrors‬‬


‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺼﻔﻭﻑ ‪ ،DataRow Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﻲ ﺤـﺩﺜﺕ ﺒﻬـﺎ‬
‫ﺃﺨﻁﺎﺀ ﻋﻨﺩ ﻤﺤﺎﻭﻟﺔ ﺤﻔﻅ ﺍﻟﺠﺩﻭل ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﻴﻤﻜﻨﻙ ﺘﺼﺤﻴﺢ ﺃﺨﻁﺎﺌﻬﺎ‪.‬‬

‫ﻗﺒﻭل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ‪:AcceptChanges‬‬


‫ﺘﻘﻭﻡ ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ AcceptChanges‬ﺍﻟﺨﺎﺼﺔ ﺒﻜل ﺼﻑ ﻓﻲ ﺍﻟﺠﺩﻭل‪.‬‬

‫‪٢٨١‬‬
‫ﺭﻓﺽ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ‪:RejectChanges‬‬
‫ﺘﻘﻭﻡ ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ RejectChanges‬ﺍﻟﺨﺎﺼﺔ ﺒﻜل ﺼﻑ ﻓﻲ ﺍﻟﺠﺩﻭل‪.‬‬

‫ﺒﺩﺀ ﺘﺤﻤﻴل ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:BeginLoadData‬‬


‫ﻴﺅﺩﻱ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺇﻟﻰ‪:‬‬
‫‪ -‬ﺇﻴﻘﺎﻑ ﺇﺭﺴﺎل ﺍﻟﺘﻨﺒﻴﻬﺎﺕ ‪ Notifications‬ﺇﻟﻰ ﻓﺌﺎﺕ ‪ ADO.NET‬ﺍﻟﺘﻲ ﺘﺘﻌﺎﻤـل‬
‫ﻤﻊ ﺍﻟﺠﺩﻭل‪ ..‬ﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺇﻴﻘﺎﻑ ﺍﻨﻁﻼﻕ ﺍﻷﺤﺩﺍﺙ ‪ Events‬ﺍﻟﺨﺎﺼﺔ ﺒﻬﺫﻩ ﺍﻟﻔﺌﺎﺕ‪.‬‬
‫‪ -‬ﺇﻴﻘﺎﻑ ﺘﺤﺩﻴﺙ ﺍﻟﻔﻬﺎﺭﺱ ‪.Indexes‬‬
‫‪ -‬ﺇﻴﻘﺎﻑ ﺍﻟﺘﺤﻘﻕ ﻤﻥ ﻗﻭﺍﻋﺩ ﺍﻟﺼﺤ‪‬ﺔ ‪.Constraints‬‬
‫ﻭﻋﻠﻴﻙ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻗﺒل ﺍﻟﺒﺩﺀ ﻓﻲ ﺇﻀﺎﻓﺔ ﻋﺩﺩ ﻜﺒﻴﺭ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﺇﻟﻰ ﺍﻟﺠﺩﻭل‪،‬‬
‫ﻷﻥ ﺘﻜﺭﺍﺭ ﺘﻨﻔﻴﺫ ﺍﻟﻌﻤﻠﻴﺎﺕ ﺍﻟﻤﺫﻜﻭﺭﺓ ﺴﺎﺒﻘﺎ ﺒﻌﺩ ﺇﻀﺎﻓﺔ ﻜل ﺴﺠل ﺇﻟﻰ ﺍﻟﺠـﺩﻭل ﻴﺴـﺘﻬﻠﻙ‬
‫ﻭﻗﺘﺎ ﻤﻠﻤﻭﺴﺎ ﻭﻴﺠﻌل ﺍﻟﺒﺭﻨﺎﻤﺞ ﺒﻁﻴﺌﺎ‪ ،‬ﻟﻬﺫﺍ ﻤﻥ ﺍﻷﺫﻜﻰ ﺇﻴﻘﺎﻓﻬﺎ ﻤﺅﻗﺘﺎ‪ ،‬ﺜﻡ ﺇﻋـﺎﺩﺓ ﺘﺸـﻐﻴﻠﻬﺎ‬
‫ﺒﻌﺩ ﺍﻻﻨﺘﻬﺎﺀ ﻤﻥ ﻤلﺀ ﺍﻟﺠﺩﻭل ﺒﺎﻟﺴﺠﻼﺕ‪.‬‬

‫ﺍﻨﺘﻬﺎﺀ ﺘﺤﻤﻴل ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:EndLoadData‬‬


‫ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺭﺩﻴﻔﺔ ﻟﻠﻭﺴﻴﻠﺔ ‪ ،BeginLoadData‬ﻭﻋﻠﻴﻙ ﺍﺴﺘﺩﻋﺎﺅﻫﺎ ﺒﻌﺩ ﺍﻨﺘﻬﺎﺀ ﺘﺤﻤﻴـل‬
‫ﺍﻟﺴﺠﻼﺕ ﻓﻲ ﺍﻟﺠﺩﻭل‪ ،‬ﻹﻋﺎﺩﺓ ﺘﺸﻐﻴل ﺍﻟﻌﻤﻠﻴﺎﺕ ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﺘﻨﺒﻴﻬﺎﺕ ﻭﺍﻷﺤﺩﺍﺙ ﻭﺍﻟﻔﻬﺎﺭﺱ‬
‫ﻭﺍﻟﻘﻴﻭﺩ‪ ،‬ﻭﺒﻬﺫﺍ ﺘﻀﻤﻥ ﺘﻨﻔﻴﺫﻫﺎ ﻤﺭﺓ ﻭﺍﺤﺩﺓ ﻓﻘﻁ ﺒﻌﺩ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﻤﻴل‪.‬‬

‫ﺩﻤﺞ ‪:Merge‬‬
‫ﺘﻀﻴﻑ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻟﻤﺭﺴل ﺇﻟﻴﻬﺎ ﻜﻤﻌﺎﻤل‪ ،‬ﺇﻟـﻰ ﺍﻟﺠـﺩﻭل ﺍﻟﺤـﺎﻟﻲ‪ ،‬ﻭﺇﻥ ﻜﺎﻨـﺕ‬
‫ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻀﺎﻓﺔ ﻤﻭﺠﻭﺩﺓ ﺴﺎﺒﻘﺎ‪ ،‬ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﺍﻟﺴـﺠﻼﺕ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﺒﻘـﻴﻡ ﺍﻟﺴـﺠﻼﺕ‬
‫ﺍﻟﻘﺎﺩﻤﺔ‪.‬‬
‫ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻨﻔﺱ ﺼﻴﻎ ﺍﻟﻭﺴﻴﻠﺔ ‪ ،DataSet.Merge‬ﻤﻊ ﺍﺨـﺘﻼﻑ ﻭﺍﺤـﺩ‪ ،‬ﻫـﻭ ﺃﻥ‬
‫ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ﻓﻲ ﻫﺫﻩ ﺍﻟﺼﻴﻎ ﻫﻭ ﻜﺎﺌﻥ ﺠﺩﻭل ﺒﻴﺎﻨﺎﺕ ‪ DataTable‬ﻭﻟﻴﺱ ‪.DataSet‬‬
‫‪٢٨٢‬‬
‫ﺇﻨﺸﺎﺀ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ‪:CreateDataReader‬‬
‫ﺘﻌﻴﺩ ﻗﺎﺭﺉ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺠﺩﻭل ‪ ،DataTableReader‬ﺍﻟﺫﻱ ﻴﻤﻜﻨﻙ ﻤﻥ ﺨﻼﻟﻪ ﺍﻟﻤﺭﻭﺭ ﻋﺒﺭ‬
‫ﻜل ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ ‪ DataTableReader‬ﻻﺤﻘﺎ ﻓـﻲ‬
‫ﻫﺫﺍ ﺍﻟﻔﺼل‪.‬‬

‫ﻜﺘﺎﺒﺔ ﻜﻭﺩ ﺍﻟﻤﺨﻁﻁ ‪:WriteXmlSchema‬‬


‫ﺘﻜﺘﺏ ﻜﻭﺩ ‪ XML‬ﺍﻟﺫﻱ ﻴﻌﺒﺭ ﻋﻥ ﻤﺨﻁﻁ ﺍﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻓﻲ ﺍﻟﻤﻠﻑ ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل‪،‬‬
‫ﺴﻭﺍﺀ ﻜﺎﻥ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻓﻲ ﺼﻭﺭﺓ ﻨﺹ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﻤﻠﻑ‪ ،‬ﺃﻭ ﻜﺎﺌﻥ ﻴﺘﻌﺎﻤل ﻤـﻊ ﺍﻟﻤﻠـﻑ‬
‫ﻤﻥ ﺍﻷﻨﻭﺍﻉ ‪ Stream‬ﺃﻭ ‪ TextWriter‬ﺃﻭ ‪.XmlWriter‬‬
‫ﻭﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺜﺎﻨﻴﺔ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﺘﺯﻴﺩ ﻋﻠﻴﻬﺎ ﺒﻤﻌﺎﻤل ﻤﻨﻁﻘﻲ‪ ،‬ﺇﺫﺍ ﺠﻌﻠـﺕ ﻗﻴﻤﺘـﻪ ‪true‬‬
‫ﻓﺴﻴﺘﻡ ﺤﻔﻅ ﻤﺨﻁﻁ ﺍﻟﺠﺩﺍﻭل ﺍﻟﻔﺭﻋﻴﺔ ﺍﻟﺘﺎﺒﻌﺔ ﻟﻬﺫﺍ ﺍﻟﺠﺩﻭل ﺃﻴﻀﺎ ﻤـﻊ ﻤﺨﻁـﻁ ﺍﻟﺠـﺩﻭل‬
‫ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﻜﺘﺎﺒﺔ ﺍﻟﻜﻭﺩ ‪:WriteXml‬‬


‫ﺘﻜﺘﺏ ﻜﻭﺩ ‪ XML‬ﺍﻟﺫﻱ ﻴﻤﺜل ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل‪ ،‬ﻓﻲ ﺍﻟﻤﻠﻑ ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل‪ ،‬ﺴﻭﺍﺀ ﻜﺎﻥ‬
‫ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻓﻲ ﺼﻭﺭﺓ ﻨﺹ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﻤﻠﻑ‪ ،‬ﺃﻭ ﻜﺎﺌﻥ ﻴﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻤﻠﻑ ﻤﻥ ﺍﻷﻨـﻭﺍﻉ‬
‫‪ Stream‬ﺃﻭ ‪ TextWriter‬ﺃﻭ ‪.XmlWriter‬‬
‫ﻭﻟﺒﻌﺽ ﺼﻴﻎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤل ﻤﻨﻁﻘﻲ‪ ،‬ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺘﻪ ‪ true‬ﻓﺴﻴﺘﻡ ﺤﻔﻅ ﺴـﺠﻼﺕ‬
‫ﺍﻟﺠﺩﺍﻭل ﺍﻟﻔﺭﻋﻴﺔ ﺍﻟﺘﺎﺒﻌﺔ ﻟﻬﺫﺍ ﺍﻟﺠﺩﻭل ﺃﻴﻀﺎ‪.‬‬
‫ﻭﻟﺒﻌﺽ ﺼﻴﻎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤل ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﺭﻗﻡ ‪ XmlWriteMode‬ﺍﻟـﺫﻱ ﺘﻌﺭﻓﻨـﺎ‬
‫ﻋﻠﻴﻪ ﺴﺎﺒﻘﺎ‪ ،‬ﻟﺘﺴﺘﻁﻴﻊ ﻤﻥ ﺨﻼﻟﻪ ﺍﺨﺘﻴﺎﺭ ﻁﺭﻴﻘﺔ ﺤﻔﻅ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻗﺭﺍﺀﺓ ﻜﻭﺩ ﺍﻟﻤﺨﻁﻁ ‪:ReadXmlSchema‬‬


‫ﺘﻘﺭﺃ ﻤﺨﻁﻁ ﺠﺩﻭل ﺃﻭ ﺃﻜﺜﺭ ﻤﻥ ﺍﻟﻤﻠﻑ ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل‪ ،‬ﺴﻭﺍﺀ ﻜﺎﻥ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤـل ﻓـﻲ‬
‫ﺼﻭﺭﺓ ﻨﺹ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﻤﻠﻑ‪ ،‬ﺃﻭ ﻜﺎﺌﻥ ﻴﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻤﻠـﻑ ﻤـﻥ ﺍﻷﻨـﻭﺍﻉ ‪ Stream‬ﺃﻭ‬
‫‪٢٨٣‬‬
‫‪ TextWriter‬ﺃﻭ ‪ ..XmlWriter‬ﻭﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻫﺫﻩ ﺍﻟﻤﺨﻁﻁﺎﺕ ﺇﻟﻰ ﺍﻟﺠـﺩﻭل‬
‫ﺍﻟﺤﺎﻟﻲ ﻭﺍﻟﺠﺩﺍﻭل ﺍﻟﻔﺭﻋﻴﺔ ﺍﻟﺘﺎﺒﻌﺔ ﻟﻪ‪.‬‬

‫ﻗﺭﺍﺀﺓ ﺍﻟﻜﻭﺩ ‪:ReadXml‬‬


‫ﺘﻘﺭﺃ ﺴﺠﻼﺕ ﺠﺩﻭل ﺃﻭ ﺃﻜﺜﺭ‪ ،‬ﻤﻥ ﺍﻟﻤﻠﻑ ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل‪ ،‬ﺴﻭﺍﺀ ﻜﺎﻥ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﻓـﻲ‬
‫ﺼﻭﺭﺓ ﻨﺹ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﻤﻠﻑ‪ ،‬ﺃﻭ ﻜﺎﺌﻥ ﻴﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻤﻠـﻑ ﻤـﻥ ﺍﻷﻨـﻭﺍﻉ ‪ Stream‬ﺃﻭ‬
‫‪ TextWriter‬ﺃﻭ ‪ ..XmlWriter‬ﻭﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻫﺫﻩ ﺍﻟﺴﺠﻼﺕ ﺇﻟـﻰ ﺍﻟﺠـﺩﻭل‬
‫ﺍﻟﺤﺎﻟﻲ ﻭﺍﻟﺠﺩﺍﻭل ﺍﻟﺘﺎﺒﻌﺔ ﻟﻪ‪.‬‬

‫ﻜﻤﺎ ﻴﻤﺘﻠﻙ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ﺍﻷﺤﺩﺍﺙ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻟﻌﻤﻭﺩ ﺘﻐﻴﺭ ‪:ColumnChanged‬‬


‫ﻴﻨﻁﻠﻕ ﺒﻌﺩ ﺃﻥ ﺘﺘﻐ ‪‬ﻴ‪‬ﺭ ﺇﺤﺩﻯ ﺍﻟﻘﻴﻡ ﻓﻲ ﺃﺤﺩ ﺃﻋﻤﺩﺓ ﺍﻟﺠﺩﻭل‪ ..‬ﻭﺍﻟﻤﻌﺎﻤـل ﺍﻟﺜـﺎﻨﻲ ‪ e‬ﻟﻬـﺫﺍ‬
‫ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،DataColumnChangeEventArgs‬ﻭﻫـﻭ ﻴﻤﺘﻠـﻙ ﺍﻟﺨﺼـﺎﺌﺹ‬
‫ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ‪ DataColumn‬ﺍﻟـﺫﻱ ﺤـﺩﺙ ﺒـﻪ‬ ‫‪Column‬‬


‫ﺍﻟﺘﻐﻴﻴﺭ‪.‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺼﻑﹼ ‪ ،DataRow‬ﺍﻟﺫﻱ ﺘﻭﺠﺩ ﺒﻪ ﺍﻟﺨﺎﻨـﺔ‬ ‫‪Row‬‬
‫ﺍﻟﺘﻲ ﺘﻐﻴﺭﺕ‪.‬‬
‫‪ ProposedValue‬ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻘﺘﺭﺤﺔ )ﺍﻟﺘﻲ ﺘﻐﻴﺭﺕ(‪ ..‬ﻫﺫﺍ ﻴﺘﻴﺢ‬
‫ﻟﻙ ـ ﻟﻭ ﺃﺭﺩﺕ ـ ﺘﻌﺩﻴل ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﺘﻐﻴﺭﺕ‪.‬‬

‫‪٢٨٤‬‬
‫ﺍﻟﻌﻤﻭﺩ ﻴﺘﻐﻴﺭ ‪:ColumnChanging‬‬
‫ﻤﻤﺎﺜل ﻟﻠﺤﺩﺙ ﺍﻟﺴﺎﺒﻕ‪ ،‬ﻭﻟﻜﻨﻪ ﻴﻨﻁﻠﻕ ﻋﻨﺩ ﻤﺤﺎﻭﻟﺔ ﺇﺠﺭﺍﺀ ﺍﻟﺘﻐﻴﻴﺭ ﻓﻲ ﺃﺤﺩ ﺃﻋﻤﺩﺓ ﺍﻟﺠـﺩﻭل‬
‫)ﺃﻱ ﻗﺒل ﺤﺩﻭﺙ ﺍﻟﺘﻐﻴﻴﺭ ﺒﺎﻟﻔﻌل(‪ ..‬ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ ﻹﻟﻐـﺎﺀ ﺘﻐﻴﻴـﺭ ﻗﻴﻤـﺔ‬
‫ﺍﻟﺨﺎﻨﺔ‪:‬‬
‫;]‪e.ProposedValue = e.Row[e.Column‬‬
‫ﻟﻜﻥ ﻋﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﺠﻤﻠﺔ ﺩﺍﺨل ﺸﺭﻁ‪ ،‬ﻓﻠﻭ ﺍﺴﺘﺨﺩﻤﺘﻬﺎ ﻫﻜـﺫﺍ ﺒﻤﻔﺭﺩﻫـﺎ ﻓﺴـﺘﻤﻨﻊ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ ﻤﻥ ﺘﻐﻴﻴﺭ ﺃﻱ ﺨﺎﻨﺔ ﻓﻲ ﺃﻱ ﻋﻤﻭﺩ ﻓﻲ ﺍﻟﺠﺩﻭل‪ ..‬ﻟﻬﺫﺍ ﻓـﺎﻟﻌﻤﻠﻲ ﺃﻥ ﺘﺴـﺘﺨﺩﻤﻬﺎ‬
‫ﻟﻤﻨﻊ ﺒﻌﺽ ﺍﻟﻘﻴﻡ ﺍﻟﺨﺎﻁﺌﺔ‪ ،‬ﻤﺜل ﺘﺭﻙ ﻋﻤﻭﺩ ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ﻓﺎﺭﻏﺎ ﻷﻥ ﻫﺫﺍ ﻏﻴﺭ ﻤﻘﺒﻭل ﻓـﻲ‬
‫ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺴﺘﺠﺩ ﻫﺫﺍ ﺍﻟﻜﻭﺩ ﻓﻲ ﺍﻟﻔﺌﺔ ﺍﻟﺠﺯﺌﻴﺔ ﻟﺠﺩﻭل ﺍﻟﻤـﺅﻟﻔﻴﻥ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪:TableAdapter‬‬
‫)"" == ‪if (e.Column == AuthorColumn && e.ProposedValue‬‬
‫;]‪e.ProposedValue = e.Row[e.Column‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ‪ ،‬ﺇﻀﺎﻓﺔ ﺃﻱ ﺸﺭﻭﻁ ﺃﺨﺭﻯ ﻟﻠﺘﺄﻜﺩ ﻤﻥ ﺼﻼﺤﻴﺔ ﺍﻟﻘـﻴﻡ ﺍﻟﺘـﻲ ﻴـﺩﺨﻠﻬﺎ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﺒﺎﻗﻲ ﺍﻷﻋﻤﺩﺓ‪.‬‬

‫ﺍﻟﺼﻑ ﺘﻐﻴﺭ ‪:RowChanged‬‬


‫ﻴﻨﻁﻠﻕ ﺒﻌﺩ ﺃﻥ ﺘﺘﻐ ‪‬ﻴ‪‬ﺭ ﺇﺤﺩﻯ ﺍﻟﻘﻴﻡ ﻓﻲ ﺃﺤﺩ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜـﺎﻨﻲ ‪ e‬ﻟﻬـﺫﺍ‬
‫ﺍﻟﺤﺩﺙ ﻤـﻥ ﺍﻟﻨـﻭﻉ ‪ ،DataRowChangeEventArgs‬ﻭﻫـﻭ ﻴﻤﺘﻠـﻙ ﺍﻟﺨﺎﺼـﻴﺘﻴﻥ‬
‫ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺼﻑﹼ ‪ ،DataRow‬ﺍﻟﺫﻱ ﺘﻭﺠﺩ ﺒـﻪ ﺍﻟﺨﺎﻨـﺔ ﺍﻟﺘﺎﺒﻌـﺔ‬ ‫‪Row‬‬


‫ﻟﻠﻌﻤﻭﺩ ﺍﻟﺫﻱ ﺤﺩﺙ ﺒﻪ ﺍﻟﺘﻐﻴﻴﺭ‪.‬‬
‫‪ Action‬ﺘﺨﺒﺭﻙ ﺒﻨﻭﻉ ﺍﻟﺘﻐﻴﻴﺭ ﺍﻟﺫﻱ ﺤﺩﺙ ﻟﻠﺼﻑﹼﹼ‪ ،‬ﻭﻫﻲ ﺘﻌﻴﺩ ﺇﺤـﺩﻯ ﻗـﻴﻡ‬
‫ﺍﻟﻤﺭﻗﻡ ‪ DataRowAction‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ :Add -‬ﺘﻤﺕ ﺇﻀﺎﻓﺔ ﺍﻟﺼﻑ‪.‬‬
‫‪ :Delete -‬ﺘﻡ ﺤﺫﻑ ﺍﻟﺼﻑ‪.‬‬
‫‪ :Change -‬ﻴﺘﻡ ﺘﻐﻴﻴﺭ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﺼﻑ‪.‬‬
‫‪٢٨٥‬‬
‫‪ :ChangeOriginal -‬ﺘﻡ ﺘﻐﻴﻴﺭ ﺍﻟﻨﺴـﺨﺔ ﺍﻷﺼـﻠﻴﺔ ‪Original‬‬
‫‪ Version‬ﻤﻥ ﺍﻟﺴﺠل‪.‬‬
‫‪ :ChangeCurrentAndOriginal -‬ﺘﻡ ﺘﻐﻴﻴﺭ ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼـﻠﻴﺔ‬
‫‪ Original Version‬ﻭﺍﻟﻨﺴـﺨﺔ ﺍﻟﺤﺎﻟﻴـﺔ ‪Current Version‬‬
‫ﻤﻥ ﺍﻟﺴﺠل‪.‬‬
‫‪ :Commit -‬ﺘﻡ ﻨﻘل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺤﺩﺜﺕ ﻋﻠﻰ ﺘﻌﺎﻤﻼﺕ ﺍﻟﺼﻑ‬
‫‪ Transactions‬ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻨﻬﺎﺌﻴﺎ‪.‬‬
‫‪ :Rollback -‬ﺘﻡ ﺍﻟﺘﺭﺍﺠﻊ ﻋﻥ ﺍﻟﺘﻐﻴﻴـﺭﺍﺕ ﺍﻟﺘـﻲ ﺤـﺩﺜﺕ ﻋﻠـﻰ‬
‫ﺘﻌﺎﻤﻼﺕ ﺍﻟﺼﻑ‪.‬‬
‫‪ :Nothing -‬ﻟﻡ ﻴﺤﺩﺙ ﺃﻱ ﺘﻐﻴﻴﺭ ﻋﻠﻰ ﺍﻟﺼﻑ‪.‬‬

‫ﺍﻟﺼﻑ ﻴﺘﻐﻴﺭ ‪:RowChanging‬‬


‫ﻤﻤﺎﺜل ﻟﻠﺤﺩﺙ ﺍﻟﺴﺎﺒﻕ‪ ،‬ﻭﻟﻜ ﹼﻨﹼﻪ ﻴﻨﻁﻠﻕ ﻋﻨﺩ ﻤﺤﺎﻭﻟﺔ ﺇﺠﺭﺍﺀ ﺍﻟﺘﻐﻴﻴﺭ ﻓﻲ ﺃﺤﺩ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل‬
‫)ﻗﺒل ﺤﺩﻭﺙ ﺍﻟﺘﻐﻴﻴﺭ(‪.‬‬

‫ﺼﻑ ﺠﺩﻴﺩ ﻟﻠﺠﺩﻭل ‪:TableNewRow‬‬


‫ﻴﻨﻁﻠﻕ ﺒﻌﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ NewRow‬ﺍﻟﺘﻲ ﺘﻌﻴﺩ ﺴﺠﻼ ﺠﺩﻴﺩﺍ ﻤﻥ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل‪..‬‬
‫ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،DataTableNewRowEventArgs‬ﻭﻫﻭ‬
‫ﻴﻤﺘﻠﻙ ﺍﻟﺨﺎﺼﻴﺔ ‪ Row‬ﺍﻟﺘﻲ ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺼﻑ ‪ DataRow‬ﺍﻟﺫﻱ ﻴﻤﺜل ﺍﻟﺼﻑ ﺍﻟﺠﺩﻴـﺩ‬
‫ﺍﻟﺫﻱ ﺘﻡ ﺇﻨﺸﺎﺅﻩ‪ ..‬ﻫﺫﺍ ﻴﺘﻴﺢ ﻟﻙ ﻭﻀﻊ ﺃﻴﺔ ﻗﻴﻡ ﺍﻓﺘﺭﺍﻀﻴﺔ ﺘﺭﻴﻬﺎ ﻓﻲ ﺨﺎﻨﺎﺕ ﺍﻟﺼﻑ ﺍﻟﺠﺩﻴﺩ‪.‬‬

‫ﺘﻡ ﺤﺫﻑ ﺍﻟﺼﻑ ‪:RowDeleted‬‬


‫ﻴﻨﻁﻠﻕ ﺒﻌﺩ ﺤﺫﻑ ﺃﺤﺩ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤـﺩﺙ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪ DataRowChangeEventArgs‬ﻭﻗﺩ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻪ ﺴﺎﺒﻘﺎ‪.‬‬

‫‪٢٨٦‬‬
‫ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻴﻨﻁﻠﻕ ﻭﺍﻟﺼﻑ ﻤﺎ ﺯﺍل ﻤﻭﺠﻭﺩﺍ ﻓﻌﻼ ﻓـﻲ ﻤﺠﻤﻭﻋـﺔ ﺼـﻔﻭﻑ‬
‫ﺍﻟﺠﺩﻭل ﻟﻜﻥ ﺤﺎﻟﺘﻪ ﺘﻜﻭﻥ ‪ ،DELETED‬ﻭﻫﻭ ﻤﺎ ﺴﻴﺴﺒﺏ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻟﻭ ﺤﺎﻭﻟﺕ‬
‫ﺇﻀﺎﻓﺔ ﺍﻟﺼﻑ ﻤﺭﺓ ﺃﺨﺭﻯ ﺇﻟﻰ ﺍﻟﺠﺩﻭل!!‬

‫ﻴﺘﻡ ﺤﺫﻑ ﺍﻟﺼﻑ ‪:RowDeleting‬‬


‫ﻤﻤﺎﺜل ﻟﻠﺤﺩﺙ ﺍﻟﺴﺎﺒﻕ‪ ،‬ﻭﻟﻜﻨﻪ ﻴﻨﻁﻠﻕ ﻋﻨﺩ ﻤﺤﺎﻭﻟﺔ ﺤﺫﻑ ﺃﺤﺩ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل )ﻗﺒل ﺇﺘﻤﺎﻡ‬
‫ﺍﻟﺤﺫﻑ(‪ ..‬ﻭﺍﻟﺤﻘﻴﻘﺔ ﺃﻥ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻗﻠﻴل ﺍﻟﻔﺎﺌﺩﺓ‪ ،‬ﻷﻨﻪ ﻻ ﻴﻤﺘﻠﻙ ﺨﺎﺼﻴﺔ ﻹﻟﻐـﺎﺀ ﻋﻤﻠﻴـﺔ‬
‫ﺍﻟﺤﺫﻑ ﻗﺒل ﻭﻗﻭﻋﻬﺎ‪ ،‬ﻭﻜﺎﻥ ﺍﻟﻤﻨﺘﻅﺭ ﺃﻥ ﻴﻤﺘﻠﻙ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﺍﻟﺨﺎﺼﻴﺔ ‪ e.Cancel‬ﻟﺘﺠﻌﻠـﻪ‬
‫ﻤﻔﻴﺩﺍ!‬
‫ﻭﻜل ﻤﺎ ﺘﺴﺘﻁﻴﻊ ﻓﻌﻠﻪ ﻓﻴﻪ‪ ،‬ﻫﻭ ﺤﻔﻅ ﺍﻟﺼﻑ ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺤﺫﻓﻪ ﻓـﻲ ﻤﺘﻐﻴـﺭ ﺍﺤﺘﻴـﺎﻁﻲ‪،‬‬
‫ﻟﻴﻤﻜﻨﻙ ﺍﻟﺘﺭﺍﺠﻊ ﻋﻥ ﻋﻤﻠﻴﺔ ﺍﻟﺤﺫﻑ ﺒﻌﺩ ﻫﺫﺍ ﻟﻭ ﺃﺭﺩﺕ‪.‬‬
‫ﻻﺤﻅ ﺃﻨﻙ ﻻ ﺘﺴﺘﻁﻴﻊ ﺤﺫﻑ ﺍﻟﺼﻑ ﺒﻨﻔﺴﻙ ﻤﻥ ﺩﺍﺨل ﻫﺫﺍ ﺍﻟﺤﺩﺙ‪ ،‬ﻓﻠﻭ ﺤﺎﻭﻟﺕ ﺍﺴـﺘﺨﺩﺍﻡ‬
‫ﺍﻟﻭﺴﻴﻠﺔ )‪ DataTable.Rows.Remove(e.Row‬ﻟﺤﺫﻑ ﺍﻟﺼﻑ‪ ،‬ﻓﺴﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ‬
‫ﺍﻟﺒﺭﻨﺎﻤﺞ!‬
‫ﻭﻟﻭ ﺃﺭﺩﺕ ﻤﻨﻊ ﻋﻤﻠﻴﺔ ﺍﻟﺤﺫﻑ ﺒﺄﺒﺴﻁ ﻁﺭﻴﻘﺔ‪ ،‬ﻓﻌﻠﻴﻙ ﻓﻌل ﻫﺫﺍ ﻤﻥ ﺃﺩﺍﺓ ﺍﻟﻌﺭﺽ‪ ..‬ﻤـﺜﻼ‪:‬‬
‫ﻟﻭ ﻭﻀﻌﺕ ﺯﺭﺍ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ﻟﻠﻘﻴﺎﻡ ﺒﻌﻤﻠﻴﺔ ﺍﻟﺤﺫﻑ‪ ،‬ﻓﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻀﻴﻑ ﺇﻟﻴﻪ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ‬
‫ﻴﺴﺄل ﺍﻟﻤﺴﺘﺨﺩﻡ ﺇﻥ ﻜﺎﻥ ﻴﺭﻴﺩ ﺇﺘﻤﺎﻡ ﺍﻟﺤﺫﻑ ﻓﻌﻼ ﺃﻡ ﻻ‪ ..‬ﺃﻤﺎ ﺇﻥ ﻜﻨـﺕ ﺘﺴـﺘﺨﺩﻡ ﺠـﺩﻭل‬
‫ﻋﺭﺽ‪ ،‬ﻓﻌﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻷﺤﺩﺍﺙ ﺍﻟﺨﺎﺼﺔ ﺒﻪ ﻟﻔﻌل ﻫﺫﺍ‪ ،‬ﻭﺇﻟﻐﺎﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺤﺫﻑ ﺇﻥ ﻗـﺭﺭ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ ﻫﺫﺍ‪.‬‬
‫ﺃﻤﺎ ﻟﻭ ﻜﻨﺕ ﻤﺼﺭﺍ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﺍﻟﻌﻘﻴﻡ‪ ،‬ﻓﺴﻴﺘﻭﺠﺏ ﻋﻠﻴﻙ ﻜﺘﺎﺒﺔ ﺒﻌﺽ ﺍﻟﻜﻭﺩ‬
‫ﻟﻔﻌل ﻫﺫﺍ‪ ..‬ﻭﻗﺩ ﻓﻌﻠﻨﺎ ﻫﺫﺍ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ﺍﻟﺘﻌﺭﻴﻑ ﺍﻟﺠﺯﺌﻲ ﻟﻔﺌﺔ ﺠـﺩﻭل ﺍﻟﻤـﺅﻟﻔﻴﻥ ﻓـﻲ‬
‫ﺍﻟﻤﺸﺭﻭﻉ ‪ ،TableAdapter‬ﺤﻴـﺙ ﺍﺴـﺘﺨﺩﻤﻨﺎ ﺍﻟﺤـﺩﺙ ‪AuthorsRowDeleting‬‬
‫ﺍﻟﻤﺸﺘﻕ ﻤﻥ ﺍﻟﺤﺩﺙ ‪ RowDeleting‬ﻟﺴﺅﺍل ﺍﻟﻤﺴﺘﺨﺩﻡ ﺇﻥ ﻜﺎﻥ ﻴﺭﻴﺩ ﺤﺫﻑ ﺍﻟﺼﻑ‪ ،‬ﻓﺈﻥ‬
‫ﻗﺭﺭ ﺇﻟﻐﺎﺀ ﺍﻟﻌﻤﻠﻴﺔ‪ ،‬ﻗﻤﻨﺎ ﺒﺈﻨﺸﺎﺀ ﻨﺴﺨﺔ ﺍﺤﺘﻴﺎﻁﻴﺔ ﻤﻥ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ‪ ،‬ﻭﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪ Merge‬ﻹﻀﺎﻓﺔ ﻨﺴﺨﺔ ﻤﻥ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ ﺇﻟﻰ ﺍﻟﺠﺩﻭل ﺍﻻﺤﺘﻴﺎﻁﻲ‪:‬‬
‫‪٢٨٧‬‬
‫;) (‪TempTable = new AuthorsDataTable‬‬
‫;)‪TempTable.Merge(this‬‬
‫ﺜﻡ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺤﺩﺙ ‪ RowDeleted‬ﻟﺩﻤﺞ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻻﺤﺘﻴﺎﻁﻲ ﺒﺎﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ‬
‫ﻤﺭﺓ ﺃﺨﺭﻯ‪ ،‬ﻭﻫﺫﺍ ﺴﻴﻌﻴﺩ ﺤﺎﻟﺔ ﺍﻟﺴﺠﻼﺕ ﻜﻤﺎ ﻜﺎﻨﺕ‪ ،‬ﺒﻤﺎ ﻓﻲ ﺫﻟﻙ ﺍﻟﺴﺠل ﺍﻟﻤﺤﺫﻭﻑ‪:‬‬
‫{ )‪if (TempTable != null && TempTable.Count > 0‬‬
‫;)‪this.Merge(TempTable‬‬
‫;) (‪TempTable.Clear‬‬
‫}‬
‫ﻻﺤﻅ ﺃﻥ ﺍﻟﺸﺭﻁ ‪ TempTable.Count > 0‬ﻫﻭ ﺍﻟﻤﺅﺸﺭ ﺍﻟﺫﻱ ﻴﺸﻌﺭﻨﺎ ﺒﺄﻥ ﺍﻟﻤﺴـﺘﺨﺩﻡ‬
‫ﺭﻓﺽ ﺇﺘﻤﺎﻡ ﻋﻤﻠﻴﺔ ﺍﻟﺤﺫﻑ‪ ..‬ﻭﻟﻭ ﻟﻡ ﻨﺴﺘﺨﺩﻤﻪ‪ ،‬ﻓﺴﻴﺘﻡ ﺍﻟﺘﺭﺍﺠﻊ ﻋﻥ ﺠﻤﻴﻊ ﻋﻤﻠﻴﺎﺕ ﺍﻟﺤﺫﻑ‬
‫ﺒﻐﺽ ﺍﻟﻨﻅﺭ ﻋﻥ ﺭﺃﻱ ﺍﻟﻤﺴﺘﺨﺩﻡ!‬
‫ﻭﺍﻟﺨﺎﺼﻴﺔ ‪ Count‬ﺒﺎﻟﻤﻨﺎﺴﺒﺔ‪ ،‬ﻫﻲ ﺨﺎﺼﻴﺔ ﻤﻌﺭﻓﺔ ﻓﻲ ﻓﺌﺔ ﺍﻟﺠﺩﻭل ﻤﺤﺩﺩ ﺍﻟﻨﻭﻉ‪ ،‬ﻭﻫـﻲ‬
‫ﻤﺠﺭﺩ ﺍﺨﺘﺼﺎﺭ ﻟﻠﻜﻭﺩ ﺍﻟﺘﺎﻟﻲ‪:‬‬
‫)‪if (TempTable.Rows.Count > 0‬‬
‫ﻭﺭﻏﻡ ﺃﻨﻬﺎ ﺘﻌﻤل ﺒﺸﻜل ﺼﺤﻴﺢ‪ ،‬ﻴﻅل ﺒﻬﺫﻩ ﺍﻟﻁﺭﻴﻘﺔ ﻋﻴﺏ ﺨﻁﻴﺭ‪ ،‬ﻭﻫﻭ ﺍﻀﻁﺭﺍﺭﻨﺎ ﺇﻟـﻰ‬
‫ﻨﺴﺦ ﻜل ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﻟﻠﻤﺤﺎﻓﻅﺔ ﻋﻠﻰ ﺤﺎﻟﺔ ﺴﺠل ﻭﺍﺤﺩ ﻓﻘﻁ‪ ،‬ﻭﻫـﺫﻩ ﻜﺎﺭﺜـﺔ ﻋﻠـﻰ‬
‫ﺍﻟﺫﺍﻜﺭﺓ ﻭﺴﺭﻋﺔ ﺍﻟﺘﻨﻔﻴﺫ ﺇﺫﺍ ﻜﺎﻥ ﻋﺩﺩ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﻀـﺨﻤﺎ!‪ ..‬ﻭﻟﻸﺴـﻑ‪ ،‬ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪ Merge‬ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﺠﺩﻭل ﻻ ﺘﻘﺒل ﺩﻤﺞ ﺴـﺠل ﻤﻨﻔـﺭﺩ‪ ،‬ﻭﻻ ﺘﻘﺒـل ﺇﻻ ﻜـﺎﺌﻥ ﺠـﺩﻭل‬
‫ﻜﻤﻌﺎﻤل‪ ..‬ﻭﻟﻭ ﺃﺭﺩﺕ ﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ‪ ،‬ﻓﻌﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﺍﺤﺘﻴﺎﻁﻴﺔ‪ ،‬ﺜـﻡ‬
‫ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ Merge‬ﺍﻟﺨﺎﺼﺔ ﺒﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻷﻨﻬﺎ ﺘﻘﺒل ﺩﻤﺞ ﻤﺼـﻔﻭﻓﺔ ﻤـﻥ‬
‫ﺍﻟﺴﺠﻼﺕ‪ ،‬ﻭﻤﻥ ﺍﻟﺴﻬل ﻭﻀﻊ ﺍﻟﺴﺠل ﺍﻟﻤﺭﺍﺩ ﺤﺫﻓﻪ ﻓﻲ ﻤﺼﻔﻭﻓﺔ ﻭﺇﺭﺴﺎﻟﻬﺎ ﺇﻟﻴﻬﺎ‪:‬‬
‫;) (‪TempDs = new DsAuthorsBooks‬‬
‫;)}‪TempDs.Merge(new[ ] {e.Row‬‬

‫‪٢٨٨‬‬
‫ﺘﺤﺫﻴﺭ ﻫﺎﻡ‪:‬‬
‫ﻻ ﺘﻌﺭﻑ ﻨﺴﺨﺔ ﺠﺩﻴﺩﺓ ﻤﻥ ﺍﻟﻤﺠﻤﻭﻋﺔ ‪ 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‬ﻓﻲ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻜﻤﺎ ﻫﻭ ﻤﺄﻟﻭﻑ!‬

‫ﺘﻡ ﻤﺤﻭ ﺍﻟﺠﺩﻭل ‪:TableCleared‬‬


‫ﻴﻨﻁﻠﻕ ﻤﺒﺎﺸﺭﺓ ﺒﻌﺩ ﻨﺠﺎﺡ ﺍﻟﻭﺴﻴﻠﺔ ‪ Clear‬ﻓﻲ ﻤﺤﻭ ﻜل ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل‪ ،‬ﻭﻗﺒـل ﺍﻟﻌـﻭﺩﺓ‬
‫ﻟﺘﻨﻔﻴﺫ ﺒﺎﻗﻲ ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﺍﺴﺘﺩﻋﻰ ﺍﻟﻭﺴﻴﻠﺔ ‪ ..Clear‬ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻟﻥ ﻴﻨﻁﻠـﻕ ﺇﺫﺍ‬
‫ﺤﺩﺜﺕ ﺃﻴﺔ ﺃﺨﻁﺎﺀ ﺃﺜﻨﺎﺀ ﺤﺫﻑ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل‪.‬‬
‫ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤـﻥ ﺍﻟﻨـﻭﻉ ‪ ،DataTableClearEventArgs‬ﻭﻫـﻭ‬
‫ﻴﻤﺘﻠﻙ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ‪ DataTable‬ﺍﻟـﺫﻱ ﻴـﺘﻡ ﻤﺤـﻭ‬ ‫‪Table‬‬


‫ﺴﺠﻼﺘﻪ‪.‬‬
‫ﺘﻌﻴﺩ ﺍﺴﻡ ﺍﻟﺠﺩﻭل‪.‬‬ ‫‪TableName‬‬
‫‪ TableNamespace‬ﺘﻌﻴﺩ ﻨﻁﺎﻕ ﺍﺴﻡ ﺍﻟﺠﺩﻭل‪.‬‬

‫ﻴﺘﻡ ﻤﺤﻭ ﺍﻟﺠﺩﻭل ‪:TableClearing‬‬


‫ﻤﻤﺎﺜل ﻟﻠﺤﺩﺙ ﺍﻟﺴـﺎﺒﻕ‪ ،‬ﻭﻟﻜﻨـﻪ ﻴﻨﻁﻠـﻕ ﻋﻨـﺩ ﻤﺤﺎﻭﻟـﺔ ﻤﺤـﻭ ﺴـﺠﻼﺕ ﺍﻟﺠـﺩﻭل‬
‫)ﺒﻌﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ Clear‬ﻟﻜﻥ ﻗﺒل ﺘﻨﻔﻴﺫﻫﺎ(‪ ..‬ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻴﻨﻁﻠـﻕ ﺩﺍﺌﻤـﺎ‪،‬‬
‫ﺤﺘﻰ ﻟﻭ ﻜﺎﻥ ﺍﻟﺠﺩﻭل ﻓﺎﺭﻏﺎ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﻓﻌﻼ‪.‬‬
‫‪٢٩٠‬‬
‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ ‪DataRowCollection Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻤﺠﻤﻭﻋﺔ ‪ ،InternalDataCollectionBase‬ﻭﻫﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻨﺎﺼـﺭ‬


‫ﻤﻥ ﻨﻭﻉ ﻓﺌﺔ ﺼﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪.DataRow Class‬‬
‫ﻭﺒﺨﻼﻑ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺠﺩﻴﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺇﻀﺎﻓﺔ ‪:Add‬‬
‫ﺘﻀﻴﻑ ﺼﻔﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ‪ ،‬ﻭﻟﻬﺎ ﺼﻴﻐﺘﺎﻥ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﺼﻑ ‪ DataRow‬ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺇﻀﺎﻓﺘﻪ‪.‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺔ ﻜﺎﺌﻨﺎﺕ ‪ Object Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻘﻴﻡ ﺍﻟﺘﻲ‬
‫ﺘﺭﻴﺩ ﻭﻀﻌﻬﺎ ﻓﻲ ﺨﺎﻨﺎﺕ ﺍﻟﺴﺠل‪ ..‬ﻻﺤﻅ ﺃﻨﻙ ﺘﺘﻌﺎﻤل ﻤﻊ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ ﻤـﻥ‬
‫ﺨﻼل ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ‪ ،DataTable‬ﻟﻬﺫﺍ ﻓﺈﻥ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋـﺔ ﺘﻌـﺭﻑ ﺘﺭﻜﻴـﺏ‬
‫ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺴﺘﻀﻴﻔﻬﺎ ﺇﻟﻴﻬﺎ‪ ،‬ﻭﻋﻠﻴﻙ ﻤﺭﺍﻋﺎﺓ ﺘﺭﺘﻴﺏ ﺍﻷﻋﻤﺩﺓ ﻭﺃﻨـﻭﺍﻉ ﺒﻴﺎﻨﺎﺘﻬـﺎ‬
‫ﻋﻨﺩ ﻭﻀﻌﻬﺎ ﻓﻲ ﺍﻟﻤﺼﻔﻭﻓﺔ ﺤﺘﻰ ﻻ ﻴﺤﺩﺙ ﺨﻁﺄ‪ ،‬ﻭﻋﻠﻴـﻙ ﻜـﺫﻟﻙ ﺘـﺭﻙ ﺨﺎﻨـﺔ‬
‫ﺍﻟﻤﺼﻔﻭﻓﺔ ﺍﻟﻤﻨﺎﻅﺭﺓ ﻟﺨﺎﻨﺔ ﺍﻟﺘﺭﻗﻴﻡ ﺍﻟﺘﻠﻘﺎﺌﻲ ﻓﺎﺭﻏﺔ‪.‬‬
‫ﻭﺘﻘﻭﻡ ﻫﺫﻩ ﺍﻟﺼﻴﻐﺔ ﺒﺈﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺼﻑ ﺠﺩﻴﺩ ﻭﻭﻀﻊ ﺍﻟﻘـﻴﻡ ﺒـﻪ ﻭﺇﻀـﺎﻓﺘﻪ ﺇﻟـﻰ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ‪ ،‬ﻭﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻜﺎﺌﻥ ﺼﻑ ‪ DataRow‬ﻴﺸﻴﺭ ﺇﻟﻰ ﺍﻟﺼﻑ ﺍﻟﺫﻱ‬
‫ﺘﻤﺕ ﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪.‬‬

‫ﺘﺤﺘﻭﻱ ﻋﻠﻰ ‪:Contains‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻨﺕ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺴﺠل ﺍﻟﺫﻱ ﻟﻪ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴـﻲ‬
‫‪ Primary Key‬ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل‪ ..‬ﻭﻟﻬﺫﺍ ﺍﻟﻭﺴﻴﻠﺔ ﺼﻴﻐﺘﺎﻥ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻨﺎ ‪ Object‬ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﻴﻤﺔ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻟﻠﺴﺠل‬
‫ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺍﻟﺒﺤﺙ ﻋﻨﻪ‪.‬‬

‫‪٢٩١‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺔ ﻜﺎﺌﻨﺎﺕ ‪ ،Objects‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻗـﻴﻡ ﺍﻟﻤﻔﺘـﺎﺡ‬
‫ﺍﻷﺴﺎﺴﻲ‪ ،‬ﻭﺫﻟﻙ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ﻟﻠﺠﺩﻭل ﻴﺘﻜﻭﻥ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﻋﻤﻭﺩ‪.‬‬

‫ﺍﻟﺒﺤﺙ ﻋﻥ ‪:Find‬‬
‫ﻤﻤﺎﺜﻠﺔ ﻟﻠﻭﺴﻴﻠﺔ ﺍﻟﺴﺎﺒﻘﺔ ﻓﻲ ﺼﻴﻐﺘﻴﻬﺎ‪ ،‬ﺇﻻ ﺃﻨﻬﺎ ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺼﻑ ‪ DataRow‬ﺍﻟﺫﻱ ﻴﻤﻠﻙ‬
‫ﻤﻔﺘﺎﺤﺎ ﺃﺴﺎﺴﻴﺎ ﻤﺴﺎﻭﻴﺎ ﻟﻠﻘﻴﻤﺔ ﺍﻟﻤﺭﺴﻠﺔ ﻜﻤﻌﺎﻤل‪ ،‬ﻭﺘﻌﻴﺩ ‪ null‬ﺇﺫﺍ ﻟﻡ ﺘﻌﺜﺭ ﻋﻠﻰ ﺍﻟﺼﻑ‪.‬‬
‫ﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﻟﻡ ﻴﻜﻥ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﺘﺒﺤﺙ ﻓﻴﻪ ﻴﺤﺘﻭﻱ ﻋﻠـﻰ‬
‫ﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ‪ ..‬ﻴﻤﻜﻥ ﺃﻥ ﻴﺤﺩﺙ ﻫﺫﺍ ﺭﻏﻡ ﺍﻥ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ﻓـﻲ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ‪ ،‬ﻭﺫﻟﻙ ﺇﺫﺍ ﺍﺴﺘﺨﺩﻤﺕ ﺍﻟﻭﺴﻴﻠﺔ ‪ Fill‬ﻟﻤلﺀ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪،‬‬
‫ﺩﻭﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ FillSchema‬ﺃﻭﻻ‪ ،‬ﻓﻬﻲ ﺍﻟﺘﻲ ﺘﻨﺸﻲﺀ ﺍﻟﻤﻔﺘـﺎﺡ ﺍﻷﺴﺎﺴـﻲ ﻓـﻲ‬
‫ﺠﺩﺍﻭل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺍﻹﺩﺭﺍﺝ ﻓﻲ ﻤﻭﻀﻊ ‪:InsertAt‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺍﻟﺼﻑ ‪ DataRow‬ﻭﺍﻟﻤﻭﻀﻊ ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺇﺩﺭﺍﺠﻪ ﻓﻴﻪ ﻓـﻲ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺼﻔﻭﻑ‪.‬‬

‫‪٢٩٢‬‬
‫ﻓﺌﺔ ﺼﻑﹼ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪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‬‬
‫ﺍﻟﻘﻴﻡ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪ Original‬ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻨﺴﺨﺔ ﺍﻟﺴﺠل ﺍﻷﺼﻠﻴﺔ ‪.Original Version‬‬


‫‪ Current‬ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ﻟﻠﺴﺠل ‪.Current Version‬‬
‫‪ Proposed‬ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻘﺘﺭﺤﺔ ﻟﻠﺴﺠل‪ ..‬ﻫﺫﺍ ﻤﻔﻴﺩ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴـﺠل‬
‫ﻗﻴﺩ ﺍﻟﺘﺤﺭﻴﺭ ﻭﻟﻡ ﻴﺘﻡ ﺇﻨﻬﺎﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺭﻴﺭ ﺒﻌﺩ‪ ،‬ﻭﺘﺭﻴـﺩ ﻗـﺭﺍﺀﺓ‬
‫ﺍﻟﻘﻴﻤﺔ ﺍﻟﺠﺩﻴﺩﺓ ﻗﺒل ﻗﺒﻭﻟﻬﺎ ﻭﻭﻀـﻌﻬﺎ ﻓـﻲ ﺍﻟﻨﺴـﺨﺔ ﺍﻟﺤﺎﻟﻴـﺔ‬
‫‪.Current Version‬‬
‫ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻨﺴﺨﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻠﺼﻑ‪ ،‬ﻭﻫﻲ ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬ ‫‪Default‬‬
‫‪ -‬ﺍﻟﻨﺴﺨﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻠﺼﻔﻭﻑ ﻏﻴﺭ ﺍﻟﻤﻌﺩﻟـﺔ ﻫـﻲ ﺍﻟﻨﺴـﺨﺔ‬
‫ﺍﻷﺼﻠﻴﺔ ‪.Original Version‬‬
‫‪ -‬ﺍﻟﻨﺴﺨﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻠﺼﻔﻭﻑ ﺍﻟﻤﻌﺩﻟﺔ ﻭﺍﻟﻤﻀﺎﻓﺔ ﻭﺍﻟﻤﺤﺫﻭﻓـﺔ‬
‫ﻫﻲ ﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ‪.Current Version‬‬
‫‪ -‬ﺍﻟﻨﺴﺨﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻠﺼﻔﻭﻑ ﻏﻴﺭ ﺍﻟﻤﺘﺼـﻠﺔ ﺒـﺄﻱ ﺠـﺩﻭل‬
‫‪ ،Detached‬ﻫﻲ ﺍﻟﻨﺴﺨﺔ ﺍﻟﻤﻘﺘﺭﺤﺔ ‪.Proposed‬‬

‫‪٢٩٤‬‬
‫ﻭﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻤﻊ ﺍﻟﺼﻴﻎ ﺍﻟﺘﻲ ﻻ ﺘﻤﺘﻠﻙ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ﻫﻲ ‪.Default‬‬
‫ﺍﻨﻅﺭ ﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ‪:‬‬
‫ﻋﺭﺽ ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ ‪//‬‬
‫‪MessageBox.Show(Row[0,‬‬
‫;)) ( ‪DataRowVersion.Original].ToString‬‬
‫ﻋﺭﺽ ﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ‪//‬‬
‫‪MessageBox.Show(Row["Book",‬‬
‫;)) ( ‪DataRowVersion.Current].ToString‬‬
‫ﻋﺭﺽ ﺍﻟﻨﺴﺨﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ‪//‬‬
‫;)) (‪MessageBox.Show(Row["Book"].ToString‬‬

‫ﻤﺼﻔﻭﻓﺔ ﺍﻟﻌﻨﺼﺭ ‪:ItemArray‬‬


‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﻜﺎﺌﻨﺎﺕ ‪ Object Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻗﻴﻡ ﻜلّ ﺨﺎﻨـﺎﺕ ﺍﻟﺴـﺠلّ ﺍﻟﺤـﺎﻟﻲ‬
‫ﺒﻨﻔﺱ ﺘﺭﺘﻴﺏ ﺍﻷﻋﻤﺩﺓ‪ ..‬ﻭﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺭﺴل ﺇﻟﻴﻬﺎ ﻤﺼﻔﻭﻓﺔ ﺒﻬﺎ ﺍﻟﻘﻴﻡ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻭﻀﻌﻬﺎ ﻓﻲ‬
‫ﺨﺎﻨﺎﺕ ﺍﻟﺴﺠل‪ ..‬ﻤﺜﺎل‪:‬‬
‫;]"‪var TblAuthors = Ds.Tables["Authors‬‬
‫;) (‪var R = TblAuthors.NewRow‬‬
‫‪", 5, "",‬ﻋﻨﺘﺭﺓ ﺒﻥ ﺸﺩﺍﺩ" ‪R.ItemArray = new object [] {null,‬‬
‫;}‪", null‬ﺸﺎﻋﺭ ﺠﺎﻫﻠﻲ"‬
‫;)‪TblAuthors.Rows.Add(R‬‬

‫ﺒﻪ ﺃﺨﻁﺎﺀ ‪:HasErrors‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻨﺕ ﻫﻨﺎﻙ ﺃﺨﻁﺎﺀ ﻤﺘﻌﻠﻘﺔ ﺒﺎﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ‪ ..‬ﻭﻴﻤﻜﻨﻙ ﻤﻌﺭﻓﺔ ﺴﺒﺏ ﺍﻟﺨﻁـﺄ‬
‫ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼﻴﺔ ‪ RowError‬ﺃﻭ ﺍﻟﻭﺴﻴﻠﺔ ‪ ،GetColumnsInError‬ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ‬
‫ﻋﻠﻴﻙ ﻓﺤﺹ ﻜﻠﺘﻴﻬﻤﺎ‪ ،‬ﻷﻨﻬﻤﺎ ﻻ ﺘﺤﺘﻭﻴﺎﻥ ﻋﻠﻰ ﻨﻔﺱ ﺍﻟﺒﻴﺎﻨﺎﺕ!‬

‫‪٢٩٥‬‬
‫ﺨﻁﺄ ﺍﻟﺼﻑ ‪:RowError‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴ‪‬ﺭ ﺍﻟﻨﺹ‪ ‬ﺍﻟﺫﻱ ﻴﺼﻑ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﻫﺫﺍ ﺍﻟﺼﻑﹼ‪ ..‬ﻻﺤـﻅ ﺃﻥ ﻭﻀـﻊ‬
‫ﺃﻱ ﻨﺹ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻴﻐﻴﺭ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪ HasErrors‬ﺇﻟﻰ ‪ ،true‬ﻭﻴﺠﻌل ﺠـﺩﻭل‬
‫ﺍﻟﻌﺭﺽ ﻴﻀﻊ ﺃﻴﻘﻭﻨﺔ ﺍﻟﺨﻁﺄ ﺒﺠﻭﺍﺭ ﻫﺫﺍ ﺍﻟﺼﻑ‪.‬‬

‫ﺤﺎﻟﺔ ﺍﻟﺼﻑ ‪:RowState‬‬


‫ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ DataRowState‬ﺍﻟﺘﻲ ﺘﻭﻀﺢ ﺤﺎﻟﺔ ﺍﻟﺴﺠلّ ﻤﻥ ﺤﻴـﺙ ﻜﻭﻨـﻪ‬
‫ﻤﻀﺎﻓﺎ ﺃﻭ ﻤﺤﺫﻭﻓﺎ ﺃﻭ ﻤﻌﺩ‪‬ﻻ‪ ..‬ﻭﻗﺩ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻰ ﻗﻴﻡ ﻫﺫﺍ ﺍﻟﻤﺭﻗﻡ ﺴﺎﺒﻘﺎ‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻟﻪ ﻨﺴﺨﺔ ‪:HasVersion‬‬


‫ﺘﻌﻴﺩ ‪ ،true‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻴﻤﺘﻠﻙ ﻨﺴﺨﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻭﻀﺤﺔ ﻓﻲ ﺍﻟﻤﻌﺎﻤل ﺍﻟﻤﺭﺴل‬
‫ﺇﻟﻲ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﻭﻫﻭ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﺭﻗﻡ ‪ DataRowVersion‬ﺍﻟﺫﻱ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴـﻪ ﻤـﻥ‬
‫ﻗﺒل‪.‬‬
‫ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﺽ ﻗﻴﻤﺔ ﺍﻟﺨﺎﻨﺔ ﺍﻷﻭﻟﻰ ﻓﻲ ﻨﺴﺨﺔ ﺍﻟﺴﺠل ﺍﻟﻤﻘﺘﺭﺤﺔ ﺇﻥ ﻭﺠﺩﺕ‪:‬‬
‫))‪if (Row.HasVersion(DataRowVersion.Proposed‬‬
‫‪MessageBox.Show(Row[0,‬‬
‫;)) (‪DataRowVersion.Proposed].ToString‬‬

‫ﺘﻐﻴﻴﺭ ﺍﻟﺤﺎﻟﺔ ﺇﻟﻰ ﻤﻀﺎﻑ ‪:SetAdded‬‬


‫ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪ RowState‬ﺇﻟﻰ ‪ ..Addded‬ﻭﺘﺴﺒﺏ ﻫـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺨﻁـﺄ ﻓـﻲ‬
‫ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﻜﺎﻥ ﻜﺎﻨﺕ ﺤﺎﻟﺔ ﺍﻟﺴﺠل ﺘﺸﻴﺭ ﺇﻟﻰ ﺃﻨﻪ ﻤﻌﺩل‪ ..‬ﻭﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸـﻜﻠﺔ‪ ،‬ﻋﻠﻴـﻙ‬
‫ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ AcceptChanges‬ﺃﻭﻻ‪.‬‬

‫‪٢٩٦‬‬
‫ﺘﻐﻴﻴﺭ ﺍﻟﺤﺎﻟﺔ ﺇﻟﻰ ﻤﻌﺩل ‪:SetModified‬‬
‫ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪ RowState‬ﺇﻟﻰ ‪ ..Modified‬ﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺨﻁـﺄ ﻓـﻲ‬
‫ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﻜﺎﻥ ﻜﺎﻨﺕ ﺤﺎﻟﺔ ﺍﻟﺴﺠل ﺘﺸﻴﺭ ﺇﻟﻰ ﺃﻨﻪ ﻤﻌﺩل‪ ..‬ﻭﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸـﻜﻠﺔ‪ ،‬ﻋﻠﻴـﻙ‬
‫ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ AcceptChanges‬ﺃﻭﻻ‪.‬‬

‫ﻗﺒﻭل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ‪:AcceptChanges‬‬


‫ﻴﺅﺩ‪‬ﻱ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺇﻟﻰ ﻗﺒﻭل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺤﺩﺜﺕ ﻋﻠﻰ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ ﻤﻨﺫ ﺃﻥ‬
‫ﺘﻡ‪ ‬ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ،‬ﺃﻭ ﻤﻨـﺫ ﺁﺨـﺭ ﻤـﺭ‪‬ﺓ ﺘـﻡ‪ ‬ﻓﻴﻬـﺎ ﺍﺴـﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪ ..AcceptChanges‬ﻟﻴﺱ ﻤﻌﻨﻰ ﻫﺫﺍ ﺃﻥ‪ ‬ﻫﺫﻩ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺴـﻴﺘﻡ‪ ‬ﺤﻔﻅﻬـﺎ ﻓـﻲ ﻗﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻟﻜﻥ ﺴﻴﺘﻡ‪ ‬ﺍﻟﻨﻅﺭ ﺇﻟﻴﻬﺎ ﻋﻠﻰ ﺃﻨﻬﺎ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﺼـﻠ ‪‬ﻴ‪‬ﺔ ﻟﻠﺴـﺠل‪ ،‬ﻭﻟـﻥ ﻴﻤﻜﻨـﻙ‬
‫ﺍﻟﺘﺭﺍﺠﻊ ﻋﻨﻬﺎ‪ ..‬ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺘﻔﻌل ﻤﺎ ﻴﻠﻲ‪:‬‬
‫‪ -‬ﺘﻀﻊ ﺍﻟﻘﻴﻤﺔ ‪ Unchanged‬ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ RowState‬ﻟﻠﺴﺠل ﺇﺫﺍ ﻜﺎﻨﺕ ﻗﻴﻤﺘﻬـﺎ‬
‫ﺘﺸﻴﺭ ﺇﻟﻰ ﺃﻨﻪ ﻤﻌﺩل ﺃﻭ ﻤﻀﺎﻑ‪.‬‬
‫ـﻪ‬
‫ـﻰ ﺃﻨـ‬
‫ـﻴﺭ ﺇﻟـ‬
‫ـﻪ ﺘﺸـ‬
‫ـﺕ ﺤﺎﻟﺘـ‬
‫ـﺎ ﺇﺫﺍ ﻜﺎﻨـ‬
‫ـﺩﻭل ﻨﻬﺎﺌﻴـ‬
‫ـﻥ ﺍﻟﺠـ‬
‫ـﺠل ﻤـ‬
‫ـل ﺍﻟﺴـ‬
‫‪ -‬ﺘﺯﻴـ‬
‫ﻤﺤﺫﻭﻑ ‪.Deleted‬‬
‫‪ -‬ﺘﻨﻘل ﺍﻟﻘﻴﻡ ﻤﻥ ﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴـﺔ ‪ Current Version‬ﺇﻟـﻰ ﺍﻟﻨﺴـﺨﺔ ﺍﻷﺼـﻠﻴﺔ‬
‫‪ Original Version‬ﻟﻠﺴﺠل‪.‬‬
‫ﻭﻋﻠﻴﻙ ﺃﻥ ﺘﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺒﺤﺫﺭ‪ ،‬ﺤﺘﻰ ﻻ ﺘﻀﻴﻊ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘـﻲ ﺤـﺩﺙ ﻟﻠﺴـﺠل‬
‫ﺍﻟﺤﺎﻟﻲ ﺩﻭﻥ ﺤﻔﻅﻬﺎ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ،‬ﺇﺫﺍ ﺤﺎﻭﻟﺕ ﺍﺴﺘﺩﻋﺎﺌﻬﺎ ﻟﻘﺒﻭل ﺘﻐﻴﻴـﺭﺍﺕ ﺼـﻑ‬
‫ﻟﻴﺱ ﻤﻀﺎﻓﺎ ﺇﻟﻰ ﺃﻱ ﺠﺩﻭل!‬

‫ﺭﻓﺽ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ‪:RejectChanges‬‬


‫ﻴﺅﺩ‪‬ﻱ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺇﻟﻰ ﺭﻓﺽ ﻜلّ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺃﺠﺭﻴﺕ ﻋﻠﻰ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ‪،‬‬
‫ﺒﺤﻴﺙ ﻴﻌﻭﺩ ﺇﻟﻰ ﺍﻟﺤﺎﻟﺔ ﺍﻟﺘﻲ ﻜﺎﻥ ﻋﻠﻴﻬﺎ ﻋﻨﺩ ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺃﻭ ﻋﻨـﺩ ﺁﺨـﺭ‬
‫ﺍﺴﺘﺨﺩﺍﻡ ﻟﻠﻭﺴﻴﻠﺔ ‪ ..AcceptChanges‬ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺘﻔﻌل ﺍﻟﺘﺎﻟﻲ‪:‬‬
‫‪٢٩٧‬‬
‫‪ -‬ﺘﻀﻊ ﺍﻟﻘﻴﻤﺔ ‪ Unchanged‬ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ RowState‬ﻟﻠﺴﺠل ﺇﺫﺍ ﻜﺎﻨﺕ ﺤﺎﻟﺘـﻪ‬
‫ﺘﺸﻴﺭ ﺇﻟﻰ ﺃﻨﻪ ﻤﻌﺩل ﺃﻭ ﻤﺤﺫﻭﻑ‪.‬‬
‫‪ -‬ﺘﺯﻴل ﺍﻟﺴﺠل ﻤﻥ ﺍﻟﺠﺩﻭل ﻨﻬﺎﺌﻴﺎ ﺇﺫﺍ ﻜﺎﻨﺕ ﺤﺎﻟﺘﻪ ﺘﺸﻴﺭ ﺇﻟﻰ ﺃﻨﻪ ﻤﻀﺎﻑ ‪.Added‬‬
‫‪ -‬ﺘﻨﻘل ﺍﻟﻘﻴﻡ ﻤﻥ ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼـﻠﻴﺔ ‪ Original Version‬ﺇﻟـﻰ ﺍﻟﻨﺴـﺨﺔ ﺍﻟﺤﺎﻟﻴـﺔ‬
‫‪ Current Version‬ﻟﻠﺴﺠل‪.‬‬
‫ﻭﺘﺘﻴﺢ ﻟﻙ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﺘﻌﺎﺩﺓ ﺍﻟﻘﻴﻡ ﺍﻷﺼﻠﻴﺔ ﻟﻠﺴﺠل‪ ..‬ﻫﺫﺍ ﻤﻔﻴﺩ ﻓﻲ ﺒﻌـﺽ ﺍﻟﺤـﺎﻻﺕ‪،‬‬
‫ﻤﺜل ﺍﻟﺘﺨﻠﺹ ﻤﻥ ﺍﻟﻘﻴﻡ ﺍﻟﺘﻲ ﺴﺒﺒﺕ ﺨﻁﺄ ﻓﻲ ﺍﻟﺴﺠل‪.‬‬

‫ﺒﺩﺃ ﺍﻟﺘﺤﺭﻴﺭ ‪:BeginEdit‬‬


‫ﺘﺒﺩﺃ ﻋﻤﻠﻴ‪‬ﺔ ﺘﺤﺭﻴﺭ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻭﺘﻌﻁل ﺃﺤﺩﺍﺙ ﺍﻟﺠﺩﻭل ﺍﻟﺘﻲ ﺘﻨﻁﻠـﻕ ﻋﻨـﺩ ﺤـﺩﻭﺙ‬
‫ﺤ‪‬ﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺩﺨﻠﺔ ﻓﻲ ﻜـل‬
‫ﺘﻐﻴﺭﺍﺕ ﻓﻲ ﺍﻟﺴﺠﻼﺕ‪ ،‬ﻜﻤﺎ ﺘﻭﻗﻑ ﻋﻤﻠ‪‬ﻴ‪‬ﺎﺕ ﺍﻟﺘﺤﻘﻕ ﻤﻥ ﺼ ‪‬‬
‫ﺨﺎﻨﺔ ﻤﻥ ﺨﺎﻨﺎﺕ ﺍﻟﺴﺠل‪ ،‬ﻤﻤﺎ ﻴﺘﻴﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺘﺤﺭﻴﺭ ﻜـل ﺨﺎﻨـﺎﺕ ﺍﻟﺴـﺠل ﺒـﺩﻭﻥ ﺃﻱ‬
‫ﺍﻋﺘﺭﺍﺽ‪.‬‬
‫ﻭﻴﺘﻡ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺘﻠﻘﺎﺌﻴﺎ ﻋﻨﺩﻤﺎ ﻴﺤـﺎﻭل ﺍﻟﻤﺴـﺘﺨﺩﻡ ﺘﺤﺭﻴـﺭ ﺒﻴﺎﻨـﺎﺕ ﺍﻟﺴـﺠل‬
‫ﺍﻟﻤﻌﺭﻭﺽ ﻓـﻲ ﺃﺩﻭﺍﺕ ﺭﺒـﻁ ﺍﻟﺒﻴﺎﻨـﺎﺕ ‪ Data-bound Controls‬ﻤﺜـل ﻤﺭﺒﻌـﺎﺕ‬
‫ﺍﻟﻨﺼﻭﺹ ﻭﺠﺩﻭل ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪.DataGridView‬‬
‫ﻻﺤﻅ ﺃﻥ ﺍﻟﺴﺠل ﻴﺤﺘﻔﻅ ﺒﺎﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﻴﺘﻡ ﺇﺩﺨﺎﻟﻬﺎ ﺃﺜﻨﺎﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺭﻴـﺭ ﻓـﻲ ﺍﻟﻨﺴـﺨﺔ‬
‫ﺍﻟﻤﻘﺘﺭﺤﺔ ‪.Proposed Version‬‬

‫ﺇﻟﻐﺎﺀ ﺍﻟﺘﺤﺭﻴﺭ ‪:CancelEdit‬‬


‫ﺘﻠﻐﻲ ﻋﻤﻠ ‪‬ﻴ‪‬ﺔ ﺍﻟﺘﺤﺭﻴﺭ ﺍﻟﺘﻲ ﺒﺩﺃﺕ ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ ،Begin Edit‬ﻭﺘﺘﺨﻠﺹ ﻤﻥ ﻨﺴـﺨﺔ‬
‫ﺍﻟﺴـﺠل ﺍﻟﻤﻘﺘﺭﺤـﺔ ‪ ،Proposed Version‬ﻭﺘﺤـﺘﻔﻅ ﺒﺎﻟﻨﺴـﺨﺔ ﺍﻟﺤﺎﻟﻴـﺔ ‪Current‬‬
‫‪ Version‬ﻜﻤﺎ ﻫﻲ‪ ..‬ﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺇﻟﻐﺎﺀ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺤﺩﺜﺕ ﻋﻠﻰ ﺍﻟﺴﺠل ﺃﺜﻨـﺎﺀ ﻋﻤﻠﻴـﺔ‬
‫ﺍﻟﺘﺤﺭﻴﺭ‪.‬‬

‫‪٢٩٨‬‬
‫ﺇﻨﻬﺎﺀ ﺍﻟﺘﺤﺭﻴﺭ ‪:EndEdit‬‬
‫ﺘﻨﻬﻲ ﻋﻤﻠ ‪‬ﻴ‪‬ﺔ ﺍﻟﺘﺤﺭﻴﺭ ﺍﻟﺘﻲ ﺒﺩﺃﺕ ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ ،Begin Edit‬ﻭﺘﻔﺤﺹ ﺍﻟﻘﻴﻡ ﺍﻟﺘﻲ ﺘﻡ‬
‫ﺇﺩﺨﺎﻟﻬﺎ ﻓﻲ ﺍﻟﺴﺠل ﺃﺜﻨﺎﺀ ﻭﻀﻊ ﺍﻟﺘﺤﺭﻴﺭ‪ ،‬ﻓﺈﻥ ﻜﺎﻨﺕ ﺼﺤﻴﺤﺔ ﺘﻘﻭﻡ ﺒﺤﻔﻅ ﻨﺴﺨﺔ ﺍﻟﺴـﺠل‬
‫ﺍﻟﻤﻘﺘﺭﺤﺔ ‪ Proposed Version‬ﻓﻲ ﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ‪ ..Current Version‬ﻫﺫﺍ ﻤﻌﻨـﺎﻩ‬
‫ﺤﻔﻅ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺤﺩﺜﺕ ﻋﻠﻰ ﺍﻟﺴﺠل ﺃﺜﻨﺎﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺭﻴﺭ‪.‬‬
‫ﻭﻴﺘﻡ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺘﻠﻘﺎﺌﻴﺎ ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪.AcceptChanges‬‬

‫ﻤﻌﺭﻓﺔ ﺨﻁﺄ ﺍﻟﻌﻤﻭﺩ ‪:GetColumnError‬‬


‫ﺘﻌﻴﺩ ﻨﺼ‪‬ﺎ ﻴﺼﻑ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﺇﺤﺩﻯ ﺨﺎﻨﺎﺕ ﺍﻟﺴﺠلّ ﺍﻟﺤﺎﻟﻲ‪ ..‬ﻭﺘﺴـﺘﻘﺒل ﻫـﺫﻩ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻼ ﻴﻭﻀﺢ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﺘﻭﺠﺩ ﻓﻴﻪ ﻫﺫﻩ ﺍﻟﺨﺎﻨﺔ‪ ،‬ﺴﻭﺍﺀ ﻓﻲ ﺼﻭﺭﺓ ﺭﻗﻡ ﺍﻟﻌﻤﻭﺩ‬
‫ﺃﻭ ﺍﺴﻤﻪ ﺃﻭ ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ‪ DataColumn‬ﺍﻟﺫﻱ ﻴﻤﺜﻠﻪ‪.‬‬

‫ﺘﻐﻴﻴﺭ ﺨﻁﺄ ﺍﻟﻌﻤﻭﺩ ‪:SetColumnError‬‬


‫ﺘﺴﻤﺢ ﻟﻙ ﺒﻭﻀﻊ ﻨﺹ‪ ‬ﻴﺼﻑ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﺇﺤﺩﻯ ﺨﺎﻨـﺎﺕ ﺍﻟﺴـﺠلّ ﺍﻟﺤـﺎﻟﻲ‪..‬‬
‫ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻌﺎﻤﻼﻥ‪:‬‬
‫‪ -‬ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ﻴﻭﻀﺢ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﺘﻭﺠﺩ ﻓﻴﻪ ﻫﺫﻩ ﺍﻟﺨﺎﻨﺔ‪ ،‬ﺴﻭﺍﺀ ﻓﻲ ﺼﻭﺭﺓ ﺭﻗـﻡ‬
‫ﺍﻟﻌﻤﻭﺩ ﺃﻭ ﺍﺴﻤﻪ ﺃﻭ ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ‪ DataColumn‬ﺍﻟﺫﻱ ﻴﻤﺜﻠﻪ‪.‬‬
‫‪ -‬ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ﻴﺴﺘﻘﺒل ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﻴﺸﺭﺡ ﺴﺒﺏ ﺍﻟﺨﻁﺄ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺃﻜﺜﺭ ﺘﻔﺼﻴﻼ ﻤﻥ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،RowError‬ﻷﻨﻬﺎ ﺘﺤﺩﺩ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ‬
‫ﺤﺩﺙ ﻓﻲ ﻜل ﺨﺎﻨﺔ ﻋﻠﻰ ﺤﺩﺓ‪ ..‬ﻭﺘﺅﺩﻱ ﺍﻟﻭﺴﻴﻠﺔ ‪ SetColumnError‬ﺇﻟﻰ ﻭﻀﻊ ﺍﻟﻘﻴﻤـﺔ‬
‫‪ true‬ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،HasErrors‬ﻭﺇﻟﻰ ﻅﻬﻭﺭ ﺃﻴﻘﻭﻨﺔ ﺍﻟﺨﻁﺄ ﻓﻲ ﺠﺩﻭل ﺍﻟﻌـﺭﺽ ﻓـﻲ‬
‫ﺍﻟﺨﺎﻨﺔ ﺍﻟﻨﺎﺘﺠﺔ ﻤﻥ ﺘﻘﺎﻁﻊ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ ﻤﻊ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﺃﺭﺴﻠﺘﻪ ﻜﻤﻌﺎﻤل‪ ..‬ﻟﻜـﻥ ﻫـﺫﻩ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ﻻ ﺘﺅﺜﺭ ﻋﻠﻰ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪.RowError‬‬

‫‪٢٩٩‬‬
‫ﻤﻌﺭﻓﺔ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺒﻬﺎ ﺃﺨﻁﺎﺀ ‪:GetColumnsInError‬‬
‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺃﻋﻤﺩﺓ ‪ DataColumn Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺒﻬﺎ ﺃﺨﻁﺎﺀ ﻓﻲ‬
‫ﺍﻟﺴﺠلّ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﻤﺤﻭ ﺍﻷﺨﻁﺎﺀ ‪:ClearErrors‬‬


‫ﺘﻤﺤﻭ ﻜلّ ﺍﻟﻨﺼﻭﺹ ﺍﻟﺘﻲ ﺘﺸﻴﺭ ﺇﻟﻰ ﺤﺩﻭﺙ ﺃﺨﻁﺎﺀ ﻓﻲ ﺍﻟﺴﺠلّ‪ ..‬ﻫﺫﺍ ﺴﻴﺠﻌل ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪ RowError‬ﻭﺍﻟﻭﺴﻴﻠﺔ ‪ GetColumnError‬ﺘﻌﻴﺩﺍﻥ ﻨﺼﻭﺼﺎ ﻓﺎﺭﻏﺔ‪.‬‬

‫ﺤﺫﻑ ‪:Delete‬‬
‫ﺘﻀﻊ ﺍﻟﻘﻴﻤﺔ ‪ Deleted‬ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ RowState‬ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ‪ ..‬ﻫﺫﺍ ﻴﺘـﻴﺢ‬
‫ﻟﻙ ﺍﻟﺘﺭﺍﺠﻊ ﻋﻥ ﺤﺫﻑ ﻫﺫﺍ ﺍﻟﺴﺠل ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ RejectChanges‬ﺃﻭ ﺤﺫﻓﻪ ﻓﻌﻼ‬
‫ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪.AcceptChanges‬‬
‫ﻻﺤﻅ ﺃﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ Delete‬ﻤـﻊ ﺴـﺠل ﻤﻀـﺎﻑ )‪(RowState = Added‬‬
‫ﺴﻴﺅﺩﻱ ﺇﻟﻰ ﺤﺫﻑ ﻫﺫﺍ ﺍﻟﺴﺠل ﻓﻲ ﺍﻟﺤﺎل‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﺎﺒﻌﺔ ‪:GetChildRows‬‬


‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺼﻔﻭﻑ ‪ DataRow Array‬ﺒﻬﺎ ﻜلّ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﻌﻼﻗـﺔ ﺒﻬـﺫﺍ‬
‫ﺍﻟﺴﺠلّ ﻓﻲ ﺠﺩﺍﻭل ﺃﺨﺭﻯ‪ ..‬ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ .١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﻌﻼﻗﺔ ‪ DataRelation‬ﺍﻟﺫﻱ ﺘﺭﻴـﺩ ﺍﺴـﺘﺨﺩﺍﻤﻪ‪..‬‬
‫ﻫﺫﺍ ﻀﺭﻭﺭﻱ‪ ،‬ﻷﻥ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻗﺩ ﻴﻜﻭﻥ ﻟﻪ ﺴﺠﻼﺕ ﻓﺭﻋﻴﺔ ﻓﻲ ﺃﻜﺜـﺭ ﻤـﻥ‬
‫ﺠﺩﻭل‪ ،‬ﻜﻤﺎ ﻫﻭ ﺍﻟﺤﺎل ﻓﻲ ﺠﺩﻭل ﺍﻟﺩﻭل ‪ ،Countries‬ﺍﻟﺫﻱ ﻟﻪ ﺴﺠﻼﺕ ﻓﺭﻋﻴـﺔ‬
‫ﻓﻲ ﺠﺩﻭﻟﻲ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺍﻟﻨﺎﺸﺭﻴﻥ‪.‬‬
‫‪ .٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ‪ ،‬ﻟﺘﺒﺤﺙ ﻋﻨﻬﺎ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻌﻼﻗﺎﺕ ﺍﻟﻔﺭﻋﻴـﺔ‬
‫‪ ChildRelations‬ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﺠﺩﻭل ﺍﻟﺫﻱ ﻴﻭﺠﺩ ﺒﻪ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ‪.‬‬
‫‪ .٣‬ﺍﻟﺼﻴﻐﺘﺎﻥ ﺍﻟﺜﺎﻟﺜﺔ ﻭﺍﻟﺭﺍﺒﻌﺔ ﻤﻤﺎﺜﻠﺘﺎﻥ ﻟﻠﺼﻴﻐﺘﻴﻥ ﺍﻟﺴﺎﺒﻘﺘﻴﻥ‪ ،‬ﻭﻟﻜﻨﻬﻤﺎ ﺘﺯﻴﺩﺍﻥ ﺒﻤﻌﺎﻤل‬
‫ﺜﺎﻥ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﺭﻗﻡ ‪ ،DataRowVersion‬ﻟﻴﻤﻜﻨﻙ ﻤﻥ ﺨﻼﻟﻪ ﺍﺨﺘﻴـﺎﺭ ﻨﺴـﺨﺔ‬
‫ﺍﻟﺴﺠﻼﺕ ‪ Version‬ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻗﺭﺍﺀﺘﻬﺎ ﻤﻥ ﺍﻟﺠﺩﻭل ﺍﻟﻔﺭﻋﻲ‪.‬‬
‫‪٣٠٠‬‬
‫ﺘﻐﻴﻴﺭ ﺍﻟﺼﻑ ﺍﻟﺭﺌﻴﺴﻲ ‪:SetParentRow‬‬
‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺍﻟﺴﺠلّ ‪ DataRow‬ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺠﻌﻠـﻪ ﺍﻟﺴـﺠل ﺍﻟﺭﺌﻴﺴـﻲ‬
‫‪ Master‬ﻟﻠﺴﺠل ﺍﻟﺤﺎﻟﻲ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺍﻟﺴﺠل ﺍﻟﺭﺌﻴﺴﻲ ﻴﻤﻜﻥ ﺃﻥ ﻴﻜﻭﻥ ﻓﻲ ﺠـﺩﻭل ﺁﺨـﺭ‬
‫)ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ(‪ ،‬ﺃﻭ ﺃﻥ ﻴﻜﻭﻥ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ )ﻋﻼﻗﺔ ﺫﺍﺘﻴﺔ ‪.(Self Relation‬‬
‫ﻥٍ‪ ،‬ﻴﺴﺘﻘﺒل ﻜﺎﺌﻥ‬
‫ﻭﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺃﺨﺭﻯ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎ ٍ‬
‫ﺍﻟﻌﻼﻗﺔ ‪ DataRelation‬ﺍﻟﺫﻱ ﻴﺭﺒﻁ ﺒﻴﻥ ﺍﻟﺴﺠﻠﻴﻥ‪.‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺇﺫﺍ ﺃﺭﺩﺕ ﺘﺼﺤﻴﺢ ﺨﻁﺎ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﻔﺭﻋﻲ‪ ،‬ﻜﺄﻥ ﺘﻐﻴـﺭ‬
‫ﻤﺅﻟﻑ ﺃﺤﺩ ﺍﻟﻜﺘﺏ ﺒﻌﺩ ﻨﺴﺒﺘﻪ ﺨﻁﺄ ﺇﻟﻰ ﻤﺅﻟﻑ ﺁﺨﺭ‪ ..‬ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻜل ﻤﺎ ﺴﺘﻔﻌﻠﻪ ﻫﺫﻩ‬
‫ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﻫﻲ ﻭﻀﻊ ﻗﻴﻤﺔ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺭﺌﻴﺴﻲ ‪ ID‬ﻟﻠﻤﺅﻟﻑ‪ ،‬ﻓﻲ ﺨﺎﻨـﺔ ﺍﻟﻤﻔﺘـﺎﺡ ﺍﻟﻔﺭﻋـﻲ‬
‫‪ AuthorID‬ﻟﻠﻜﺘﺎﺏ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﺼﻑ ﺍﻟﺭﺌﻴﺴﻲ ‪:GetParentRow‬‬


‫ﺘﻌﻴﺩ ﺍﻟﺴﺠلّ ﺍﻟﺭﺌﻴﺴﻲ ﺍﻟﺫﻱ ﻴﺭﺘﺒﻁ ﺒﻪ ﺍﻟﺴﺠلّ ﺍﻟﺤﺎﻟﻲ ﺒﻌﻼﻗﺔ‪ ..‬ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺓ ﺼﻴﻎ‪:‬‬
‫‪ .١‬ﺒﻌﺽ ﺍﻟﺼﻴﻎ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻭﺍﺤﺩﺍ‪ ،‬ﻫﻭ ﺍﻟﻌﻼﻗﺔ ﺍﻟﺘـﻲ ﻴﺸـﺘﺭﻙ ﻓﻴﻬـﺎ ﺍﻟﺴـﺠل‬
‫ﺍﻟﺤﺎﻟﻲ‪ ،‬ﺴﻭﺍﺀ ﻓﻲ ﺼﻭﺭﺓ ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ‪ ،‬ﺃﻭ ﻜﺎﺌﻥ ﺍﻟﻌﻼﻗﺔ ‪.DataRelation‬‬
‫‪ .٢‬ﺒﻌﺽ ﺍﻟﺼﻴﻎ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﺜﺎﻨﻴﺎ ﻤـﻥ ﻨـﻭﻉ ﺍﻟﻤـﺭﻗﻡ ‪،DataRowVersion‬‬
‫ﻟﻴﻤﻜﻨﻙ ﻤﻥ ﺨﻼﻟﻪ ﺘﺤﺩﻴﺩ ﺍﻟﻨﺴﺨﺔ ‪ Version‬ﺍﻟﺘﻲ ﺘﺭﻴـﺩ ﻗﺭﺍﺀﺘﻬـﺎ ﻤـﻥ ﺍﻟﺴـﺠل‬
‫ﺍﻟﺭﺌﻴﺴﻲ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﺼﻔﻭﻑ ﺍﻟﺭﺌﻴﺴﻴﺔ ‪:GetParentRows‬‬


‫ﻤﻤﺎﺜﻠﺔ ﻟﻠﻭﺴﻴﻠﺔ ﺍﻟﺴﺎﺒﻘﺔ ﻓﻲ ﺼﻴﻐﻬﺎ‪ ،‬ﻟﻜﻨﻬﺎ ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺼـﻔﻭﻑ ‪،DataRow Array‬‬
‫ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻜلّ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺭﺌﻴﺴ ‪‬ﻴ‪‬ﺔ ﺍﻟﺘﻲ ﺘﺸﻴﺭ ﺇﻟﻰ ﺍﻟﺴﺠلّ ﺍﻟﺤﺎﻟﻲ‪ ..‬ﻓﻲ ﺍﻟﺤﻘﻴﻘﺔ ﻻ ﺘﺒﺩﻭ‬
‫ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺃﻴﺔ ﺃﻫﻤﻴﺔ ﺤﺎﻟﻴﺎ‪ ،‬ﻓﻬﻲ ﺩﺍﺌﻨﺎ ﺘﻌﻴﺩ ﺴﺠﻼ ﺭﺌﻴﺴﻴﺎ ﻭﺍﺤﺩﺍ‪ ،‬ﻭﻫﺫﺍ ﻴﺠﻌل ﺍﺴـﺘﺨﺩﺍﻡ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪ GetParentRow‬ﺃﻜﺜﺭ ﻤﻨﻁﻘﻴﺔ!‬

‫‪٣٠١‬‬
‫ﻫل ﻫﻲ ﻋﺩﻡ ‪:IsNull‬‬
‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﺨﺎﻨﺔ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﺴﺠلّ ﺍﻟﺤﺎﻟﻲ ﻭﺍﻟﻌﻤـﻭﺩ ﺍﻟﻤﺭﺴـل ﻜﻤﻌﺎﻤـل‬
‫ﻓﺎﺭﻏﺔ ‪ ..DbNull‬ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﺒﻌﺽ ﺍﻟﺼﻴﻎ ﻟﻬﺎ ﻤﻌﺎﻤل ﻭﺍﺤﺩ‪ ،‬ﻴﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ﺃﻭ ﺭﻗﻤﻪ ﺃﻭ ﻜـﺎﺌﻥ ﺍﻟﻌﻤـﻭﺩ‬
‫‪ DataCoulmn‬ﺍﻟﺫﻱ ﻴﻤﺜﻠﻪ‪.‬‬
‫‪ -٢‬ﻭﻫﻨﺎﻙ ﺼﻴﻎ ﻟﻬﺎ ﻤﻌﺎﻤل ﺜﺎﻥ ﻤﻥ ﻨﻭﻉ ﺍﻟﻤﺭﻗﻡ ‪ ،DataRowVersion‬ﻟﻴﻤﻜﻨﻙ ﻤﻥ‬
‫ﺨﻼﻟﻪ ﺘﺤﺩﻴﺩ ﺍﻟﻨﺴﺨﺔ ‪ Version‬ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻓﺤﺹ ﻗﻴﻤﻬﺎ‪.‬‬

‫‪٣٠٢‬‬
‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ ‪DataColumnCollection Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻤﺠﻤﻭﻋﺔ ‪ ،InternalDataCollectionBase‬ﻭﻫﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻨﺎﺼـﺭ‬


‫ﻤﻥ ﻨﻭﻉ ﻓﺌﺔ ﻋﻤﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪.DataColumn 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‬ﻹﻀﺎﻓﺔ ﻋﻤﻭﺩ ﺠﺩﻴﺩ‪ ،‬ﺤﻴﺙ ﺴﺘﻅﻬﺭ ﺨﺼـﺎﺌﺹ‬
‫ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﻘﺴﻡ ﺍﻷﻴﻤﻥ‪ ،‬ﻭﻴﻤﻜﻨﻙ ﺘﻐﻴﻴﺭﻫﺎ ﻜﻤﺎ ﺘﺸﺎﺀ‪.‬‬

‫ﻤﻭﻀﻊ ﺍﻟﻌﻤﻭﺩ ‪:IndexOf‬‬


‫ﺘﺒﺤﺙ ﻋﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻤﺭﺴل ﺇﻟﻴﻬﺎ ﻜﻤﻌﺎﻤل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ‪ ،‬ﻭﺘﻌﻴﺩ ﺭﻗﻡ ﺍﻟﺨﺎﻨﺔ ﺍﻟﺘـﻲ‬
‫ﻴﻭﺠﺩ ﺒﻬﺎ ﻓﻲ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﺃﻭ ﺘﻌﻴﺩ ‪ ١-‬ﺇﻥ ﻟﻡ ﻴﻜﻥ ﻤﻭﺠﻭﺩﺍ‪ ..‬ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺼﻴﻐﺘﺎﻥ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ‪.‬‬
‫‪ -٢‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ‪.DataColumn‬‬
‫‪٣٠٥‬‬
‫ﻴﻤﻜﻥ ﺤﺫﻓﻪ ‪:CanRemove‬‬
‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﻤﻥ ﺍﻟﻤﻤﻜﻥ ﺤﺫﻑ ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ‪ DataColumn‬ﺍﻟﻤﺭﺴـل ﻜﻤﻌﺎﻤـل‬
‫ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ‪ ..‬ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ‪ false‬ﺇﺫﺍ ﻟﻡ ﻴﻜـﻥ ﺍﻟﻌﻤـﻭﺩ ﻤﻭﺠـﻭﺩﺍ ﻓـﻲ‬
‫ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﺃﻭ ﺇﺫﺍ ﻜﺎﻥ ﺩﺍﺨﻼ ﻓﻲ ﻋﻼﻗﺔ‪.‬‬

‫ﺤﺫﻑ ‪:Remove‬‬
‫ﺘﺤﺫﻑ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻤﺭﺴل ﺇﻟﻴﻬﺎ ﻜﻤﻌﺎﻤل ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ‪ ..‬ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺼﻴﻐﺘﺎﻥ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ‪.‬‬
‫‪ -٢‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ‪.DataColumn‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺤﺩﺙ ﺍﻟﺘﺎﻟﻲ‪:‬‬

‫ﺍﻟﻤﺠﻤﻭﻋﺔ ﺘﻐﻴﺭﺕ ‪:CollectionChanged‬‬


‫ﻴﻨﻁﻠﻕ ﻋﻨﺩﻤﺎ ﻴﺘﻐﻴﺭ ﻋﺩﺩ ﺍﻷﻋﻤﺩﺓ‪ ،‬ﺴﻭﺍﺀ ﺒﺎﻟﺤﺫﻑ ﺃﻭ ﺍﻹﻀﺎﻓﺔ‪.‬‬
‫ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤـﻥ ﺍﻟﻨـﻭﻉ ‪ CollectionChangeEventArgs‬ﺍﻟـﺫﻱ‬
‫ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻪ ﻤﻥ ﻗﺒل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل ‪.DataTableCollection‬‬

‫‪٣٠٦‬‬
‫ﻓﺌﺔ ﻋﻤﻭﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪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‬ﺍﻟﺫﻱ ﻴﻨﺘﻤﻲ ﺇﻟﻴﻪ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ‪.‬‬

‫ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ ‪:ColumnName‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ‪.‬‬
‫‪٣٠٧‬‬
‫ﻨﻁﺎﻕ ﺍﻻﺴﻡ ‪:Namespace‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻨﻁﺎﻕ ﺍﻻﺴﻡ ﺍﻟﺨﺎﺹ ﺒﺎﻟﺠﺩﻭل ﺍﻟﺫﻱ ﻴﻨﺘﻤﻲ ﺇﻟﻴﻪ ﺍﻟﻌﻤﻭﺩ‪.‬‬

‫ﺍﻟﺒﺎﺩﺌﺔ ‪:Prefix‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﺒﺎﺩﺌﺔ ﺍﻟﺘﻲ ﺘﻤﺜل ﻨﻁﺎﻕ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﻴﻨﺘﻤﻲ ﺇﻟﻴﻪ ﺍﻟﻌﻤﻭﺩ‪.‬‬
‫ﺍﻟﺭﺘﺒﺔ ‪:Ordinal‬‬
‫ﺘﻌﻴﺩ ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ ﻴﻤﺜل ﺘﺭﺘﻴﺏ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ‪.‬‬

‫ﺍﻟﻌﻨﻭﺍﻥ ‪:Caption‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﻭﺍﻥ ﺍﻟﻌﻤﻭﺩ‪ ..‬ﻤﻥ ﺍﻟﻤﻔﺭﻭﺽ ﺃﻥ ﻴﺘﻡ ﻋﺭﺽ ﻫﺫﺍ ﺍﻟﻌﻨﻭﺍﻥ ﺒﺩﻻ ﻤﻥ ﺍﺴـﻡ‬
‫ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺃﺩﻭﺍﺕ ﺭﺒﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ،Data-Bound Controls‬ﻟﻜﻨﻙ ﻟﻭ ﺠﺭﺒﺕ ﻫﺫﺍ ﻤـﻊ‬
‫ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻤﺜﻼ‪ ،‬ﻓﻠﻥ ﺘﺠﺩ ﻟﻪ ﺘﺄﺜﻴﺭﺍ!‪ ..‬ﻴﺒﺩﻭ ﺃﻥ ﻋﻠﻴﻙ ﺭﺒﻁ ﺃﺩﺍﺓ ﺍﻟﻌـﺭﺽ ﺤﺼـﺭﻴﺎ‬
‫ﺒﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻟﻜﻲ ﺘﺭﻯ ﺘﺄﺜﻴﺭﻫﺎ‪ ،‬ﻜﻤﺎ ﺴﻨﺭﻯ ﻻﺤﻘﺎ‪.‬‬

‫ﻨﻭﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataType‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﺍﻟﻨﻭﻉ ‪ Type Object‬ﺍﻟﺫﻱ ﻴﻤﺜل ﻨﻭﻉ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻌﻤﻭﺩ‪.‬‬

‫ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ‪:DefaultValue‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴ‪‬ﺔ ﻟﺨﺎﻨﺎﺕ ﺍﻟﻌﻤﻭﺩ‪ ..‬ﻴﻤﻜﻨﻙ ﻤﺜﻼ ﺃﻥ ﺘﻀﻊ ﺍﻟﺭﻗﻡ ﺼﻔﺭ ﻓـﻲ‬
‫ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺇﺫﺍ ﻜﻨﺕ ﺘﺘﻌﺎﻤل ﻤﻊ ﻋﻤﻭﺩﻱ ﺭﻗﻤﻲ‪.‬‬

‫ﺍﻟﺴﻤﺎﺡ ﺒﺎﻟﻌﺩﻡ ‪:AllowDBNull‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼ ‪‬ﻴ‪‬ﺔ ‪ ،true‬ﻓﺴﻴ‪‬ﺴﻤﺢ ﺒﺘﺭﻙ ﺒﻌﺽ ﺨﺎﻨﺎﺕ ﻫﺫﺍ ﺍﻟﻌﻤـﻭﺩ ﻓﺎﺭﻏـﺔ‬
‫‪.DbNull‬‬

‫‪٣٠٨‬‬
‫ﺃﻗﺼﻰ ﻁﻭل ‪:MaxLength‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺃﻗﺼﻰ ﻋﺩﺩ ﻤﻥ ﺍﻟﺤﺭﻭﻑ ﻴﻤﻜﻨﻙ ﻜﺘﺎﺒﺘﻪ ﻓﻲ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﻴﺘﻌﺎﻤل ﻤﻊ ﺒﻴﺎﻨﺎﺕ‬
‫ﻨﺼﻴﺔ‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ١-‬ﻤﻤﺎ ﻴﻌﻨﻲ ﻋﺩﻡ ﻭﺠﻭﺩ ﻗﻴﻭﺩ ﻋﻠـﻰ ﻋـﺩﺩ‬
‫ﺍﻟﺤﺭﻭﻑ‪ ..‬ﻻﺤﻅ ﺃﻥ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺴﻴﺘﻡ ﺘﺠﺎﻫﻠﻬﺎ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻌﻤﻭﺩ ﻴﺘﻌﺎﻤل ﻤﻊ ﺒﻴﺎﻨﺎﺕ‬
‫ﻤﻥ ﻨﻭﻉ ﺁﺨﺭ ﻏﻴﺭ ﺍﻟﻨﺼﻭﺹ‪.‬‬

‫ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ ‪:ReadOnly‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼ ‪‬ﻴ‪‬ﺔ ‪ ،true‬ﻓﻠﻥ ﻴﻤﻜﻨﻙ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﺃﻱ ﺨﺎﻨﺔ ﻓﻲ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﺒﻌﺩ‬
‫ﺇﻀﺎﻓﺔ ﺍﻟﺼﻑ ﺍﻟﺫﻱ ﺘﻭﺠﺩ ﺒﻪ ﺇﻟﻰ ﺍﻟﺠﺩﻭل‪ ..‬ﻻﺤﻅ ﺃﻥ ﺠﻌل ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ true‬ﻤﻊ‬
‫ﻋﻤﻭﺩ ﻴﺤﻤل ﻨﺎﺘﺞ ﻋﻤﻠﻴﺔ ﺤﺴﺎﺒﻴﺔ ﻤﻭﻀﻭﻋﺔ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،Expression‬ﺴﻴﺅﺩﻱ ﺇﻟـﻰ‬
‫ﺤﺩﻭﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ،‬ﻷﻥ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﺃﻱ ﺨﺎﻨﺔ ﻓﻲ ﻋﻤـﻭﺩ ﺩﺍﺨـل ﻓـﻲ ﺍﻟﻌﻤﻠﻴـﺔ‬
‫ﺍﻟﺤﺴﺎﺒﻴﺔ ﺴﻴﺅﺩﻱ ﺘﻠﻘﺎﺌﻴﺎ ﺇﻟﻰ ﺇﻋﺎﺩﺓ ﺤﺴﺎﺏ ﻗﻴﻤﺔ ﺍﻟﺨﺎﻨﺔ ﺍﻟﻤﻨﺎﻅﺭﺓ ﻓﻲ ﻋﻤﻭﺩ ﺍﻟﻨﺎﺘﺞ‪.‬‬

‫ﻤﺘﻔﺭﺩ ‪:Unique‬‬
‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼ ‪‬ﻴ‪‬ﺔ ‪ ،true‬ﻓﻠﻥ ﻴﺴﻤﺢ ﺒﺘﻜﺭﺍﺭ ﻗﻴﻡ ﺨﺎﻨﺎﺕ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ‪.‬‬

‫ﺘﺭﻗﻴﻡ ﺘﻠﻘﺎﺌﻲ ‪:AutoIncrement‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼ ‪‬ﻴ‪‬ﺔ ‪ ،true‬ﻓﺴﺘﺯﻴﺩ ﻗﻴﻤﺔ ﺨﺎﻨﺎﺕ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﺘﻠﻘﺎﺌﻴ‪‬ﺎ ﻜﻠﻤـﺎ ﺘﻤـﺕ‬
‫ﺇﻀﺎﻓﺔ ﺼﻑ ﺠﺩﻴﺩ ﺇﻟﻰ ﺍﻟﺠﺩﻭل‪ ..‬ﻻﺤﻅ ﺃﻥ ﻋﻤﻭﺩ ﺍﻟﺘﺭﻗﻴﻡ ﺍﻟﺘﻠﻘﺎﺌﻲ ﻴﻜﻭﻥ ﻟﻠﻘـﺭﺍﺀﺓ ﻓﻘـﻁ‬
‫‪ ،ReadOnly‬ﻭﻻ ﻴﻤﻜﻨﻙ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺘﻪ ﺒﻨﻔﺴﻙ‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫـﻲ‬
‫‪.false‬‬

‫ﺒﺫﺭﺓ ﺍﻟﺘﺭﻗﻴﻡ ﺍﻟﺘﻠﻘﺎﺌﻲ ‪:AutoIncrementSeed‬‬


‫ﺘﺤﺩﺩ ﺭﻗﻡ ﺒﺩﺀ ﺍﻟﺘﺭﻗﻴﻡ ﻓﻲ ﻋﻤﻭﺩ ﺍﻟﺘﺭﻗﻴﻡ ﺍﻟﺘﻠﻘﺎﺌﻲ‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻫﻲ ‪.١‬‬

‫ﺨﻁﻭﺓ ﺍﻟﺘﺭﻗﻴﻡ ﺍﻟﺘﻠﻘﺎﺌﻲ ‪:AutoIncrementStep‬‬


‫ﺘﺤﺩ‪‬ﺩ ﻤﻘﺩﺍﺭ ﺍﻟﺯﻴﺎﺩﺓ ﻓﻲ ﻋﻤﻭﺩ ﺍﻟﺘﺭﻗﻴﻡ ﺍﻟﺘﻠﻘﺎﺌﻲ‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻫﻲ ‪.١‬‬
‫‪٣٠٩‬‬
‫ﺍﻟﺨﺼﺎﺌﺹ ﺍﻹﻀﺎﻓﻴﺔ ‪:ExtendedProperties‬‬
‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺨﺼﺎﺌﺹ ‪ PropertyCollection‬ﺍﻟﺘﻲ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﺍﻟﺨﺼـﺎﺌﺹ‬
‫ﺼ‪‬ﺔ ﺒﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل‪.‬‬
‫ﺍﻹﻀﺎﻓ ‪‬ﻴ‪‬ﺔ ﻟﻠﻌﻤﻭﺩ‪ ،‬ﻭﻫﻲ ﻤﻤﺎﺜﻠﺔ ﻟﺘﻠﻙ ﺍﻟﺨﺎ ‪‬‬

‫ﺨﺭﻴﻁﺔ ﺍﻟﻌﻤﻭﺩ ‪:ColumnMapping‬‬


‫ﺘﺤﺩﺩ ﻜﻴﻑ ﻴﺘﻡ ﺘﻤﺜﻴل ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﻜﻭﺩ ‪ XML‬ﻋﻨﺩ ﺤﻔﻅ ﺍﻟﺠﺩﻭل‪ ،‬ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗـﻴﻡ‬
‫ﺍﻟﻤﺭﻗﻡ ‪ MappingType‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻴﺘﻡ ﺘﻤﺜﻴل ﺍﻟﻌﻤﻭﺩ ﻜﻌﻨﺼﺭ ‪ ..XML‬ﻫﻜـﺫﺍ ﻤـﺜﻼ ﺴـﻴﺘﻡ ﺤﻔـﻅ‬ ‫‪Element‬‬


‫ﺍﻟﻌﻤﻭﺩﻴﻥ ‪ ID‬ﻭ ‪ Author‬ﻓﻲ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ‪:‬‬
‫>‪<Authors‬‬
‫>‪<ID>1</ID‬‬
‫ﺘﻭﻓﻴﻕ ﺍﻟﺤﻜﻴﻡ' = >‪<Author‬‬
‫>‪</Authors‬‬
‫ﻴﺘﻡ ﺘﻤﺜﻴل ﺍﻟﻌﻤﻭﺩ ﻜﺴﻤﺔ ‪ ..XML‬ﻫﻜﺫﺍ ﻤﺜﻼ ﺴﻴﺘﻡ ﺤﻔﻅ ﺍﻟﻌﻤـﻭﺩ‬ ‫‪Attribute‬‬
‫‪:ID‬‬
‫>'ﺘﻭﻓﻴﻕ ﺍﻟﺤﻜﻴﻡ' = ‪<Authors ID=1 Author‬‬
‫>‪'/‬ﺸﻬﺭﺯﺍﺩ' = ‪<Book ID=1 Book‬‬
‫>‪</Authors‬‬
‫‪ SimpleContent‬ﻴﺘﻡ ﺘﻤﺜﻴل ﺍﻟﻌﻤﻭﺩ ﻜﻔﺭﻉ ﻤـﻥ ﻨـﻭﻉ ﺍﻟﻔﺌـﺔ ‪ ..XmlText‬ﻫـﺫﺍ‬
‫ﺍﻟﻤﻭﻀﻭﻉ ﺨﺎﺭﺝ ﻨﻁﺎﻕ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ‪.‬‬
‫ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﺨﻔﻲ‪ ،‬ﻭﻴﺴﺘﺨﺩﻡ ﻓﻲ ﺍﻟﺒﻨﺎﺀ ﺍﻟﺩﺍﺨﻠﻲ ﻟﻠﺠﺩﻭل‪ ،‬ﻟﻜـﻥ ﻻ‬ ‫‪Hidden‬‬
‫ﻴﻅﻬﺭ ﻟﻠﻤﺴﺘﺨﺩﻡ ﻋﻨﺩ ﺭﺒﻁ ﺍﻟﺠﺩﻭل ﺒﺄﺩﻭﺍﺕ ﺭﺒﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻨﻅﺎﻡ ﺍﻟﺘﺎﺭﻴﺦ ﻭﺍﻟﻭﻗﺕ ‪:DateTimeMode‬‬


‫ﺘﺤﺩﺩ ﻨﻅﺎﻡ ﺍﻟﺘﻭﻗﻴﺕ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻤﻊ ﻫﺫﺍ ﺍﻟﺠﺩﻭل‪ ،‬ﺇﺫﺍ ﻜﺎﻥ ﻨﻭﻉ ﺒﻴﺎﻨﺎﺘﻪ ﺘـﺎﺭﻴﺦ ﺃﻭ ﻭﻗـﺕ‪..‬‬
‫ﻭﺘﺄﺨﺫ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ DataSetDateTime‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪٣١٠‬‬
‫ﺍﻟﺘﻌﺎﻤل ﺒﺎﻟﺘﺎﺭﻴﺦ ﺍﻟﻤﺤﻠﻲ ﻟﻠﺠﻬﺎﺯ ﺍﻟﺫﻱ ﻴﻭﺠﺩ ﻋﻠﻴﻪ ﺍﻟﺒﺭﻨﺎﻤﺞ‪.‬‬ ‫‪Local‬‬
‫ﺍﻟﺘﻌﺎﻤل ﺒﺎﻟﺘﺎﺭﻴﺦ ﺍﻟﻌﺎﻟﻤﻲ‪.‬‬ ‫‪Utc‬‬
‫ﻏﻴﺭ ﻤﺤﺩﺩ‪.‬‬ ‫‪Unspecified‬‬
‫‪ UnspecifiedLocal‬ﺘﻭﻗﻴﺕ ﻤﺤﻠﻲ ﻏﻴﺭ ﻤﺤﺩﺩ‪ ..‬ﻫﺫﻩ ﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪.‬‬

‫ﻻﺤﻅ ﺃﻨﻙ ﻻ ﺘﺴﺘﻁﻴﻊ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺒﻌﺩ ﺇﻀﺎﻓﺔ ﺍﻟﺨﺎﻨﺎﺕ ﺇﻟﻰ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﺇﻻ ﻓﻲ‬
‫ﺤﺎﻟﺔ ﻭﺍﺤﺩﺓ‪ :‬ﺇﺫﺍ ﻜﻨﺕ ﺘﺤﻭل ﻤﻥ ﺍﻟﻘﻴﻤﺔ ‪ Unspecified‬ﺇﻟـﻰ ‪ UnspecifiedLocal‬ﺃﻭ‬
‫ﺍﻟﻌﻜﺱ‪.‬‬

‫ﺍﻟﺘﻌﺒﻴﺭ ‪:Expression‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﺼﻴﻐﺔ ﺍﻟﻨﺼﻴﺔ ﻟﻠﻌﻤﻭﺩ‪ ،‬ﻭﺍﻟﺘﻲ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻴﻤﺎ ﻴﻠﻲ‪:‬‬
‫‪ -‬ﺤﺴﺎﺏ ﻗﻴﻡ ﺨﺎﻨﺎﺕ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻤﻥ ﻨﺎﺘﺞ ﻋﻤﻠﻴﺔ ﺤﺴﺎﺒﻴﺔ ﻋﻠﻰ ﺃﻋﻤﺩﺓ ﺃﺨـﺭﻯ‪،‬‬
‫ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻴﺴﻤﻰ ﺒﺎﻟﻌﻤﻭﺩ ﺍﻟﻤﺤﺴﻭﺏ ‪.Calculated Column‬‬
‫ـﻰ ﻜــل ﺴــﺠل‪ ،‬ﻻﺴــﺘﺨﺩﺍﻡ ﺍﻟﻭﺴــﻴﻠﺔ‬
‫ـﺎﺏ ﻨــﺎﺘﺞ ﺸــﺭﻁ ﻤﻌــﻴﻥ ﻋﻠـ‬
‫‪ -‬ﺤﺴـ‬
‫‪ DataTable.Select‬ﺒﻌﺩ ﺫﻟﻙ ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﺍﻟﺴـﺠﻼﺕ ﺍﻟﺘـﻲ ﺤﻘﻘـﺕ ﻫـﺫﺍ‬
‫ﺍﻟﺸﺭﻁ‪ ،‬ﻭﺴﻨﺭﻯ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫﺫﺍ ﺒﻌﺩ ﻗﻠﻴل‪.‬‬
‫ﻭﺍﻟﺠﺩﻭل ﺍﻟﺘﺎﻟﻲ ﻴﻠﺨﺹ ﻟﻙ ﺍﻟﻤﻌﺎﻤﻼﺕ ﻭﺍﻟﺩﻭﺍلّ ﺍﻟﺘﻲ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﺘﻜـﻭﻴﻥ ﺼـﻴﻐﺔ‬
‫ﺍﻟﻌﻤﻭﺩ‪:‬‬

‫ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺤﺴﺎﺒﻴﺔ ﺍﻟﺘﺎﻟﻴﺔ ﻋﻠﻰ ﺍﻷﻋﻤﺩﺓ‪:‬‬ ‫ﺍﻟﻤﻌﺎﻤﻼﺕ‬


‫ﺍﻟﺠﻤﻊ‪ ،+ :‬ﺍﻟﻁﺭﺡ‪ ،- :‬ﺍﻟﻀﺭﺏ *‪ ،‬ﺍﻟﻘﺴﻤﺔ ‪ ،/‬ﺒﺎﻗﻲ ﺍﻟﻘﺴـﻤﺔ ‪..%‬‬ ‫ﺍﻟﺤﺴﺎﺒﻴﺔ‬
‫ﻤﺜﺎل‪:‬‬
‫;"‪Col.Expression = "Price * Copies_No‬‬
‫ﻴﻤﻨﻜﻥ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﻨﻁﻘﻴﺔ ﺍﻟﺘﺎﻟﻴﺔ ﺒﻴﻥ ﺍﻷﻋﻤﺩﺓ‪:‬‬ ‫ﺍﻟﻤﻌﺎﻤﻼﺕ‬
‫‪AND, OR, NOT‬‬ ‫ﺍﻟﻤﻨﻁﻘﻴﺔ‬
‫ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﺭﻯ ﺇﻥ ﻜﺎﻥ ﺍﺴﻡ ﺍﻟﻜﺘﺎﺏ ﻴﺒﺩﺃ ﺒﺤﺭﻑ ﻴﺴﺒﻕ ﺍﻟﻤـﻴﻡ‬
‫‪٣١١‬‬
‫ﺃﺒﺠﺩﻴﺎ‪ ،‬ﻭﺃﻥ ﺍﻟﻜﺘﺎﺏ ﻟﻠﻤﺅﻟﻑ ﺭﻗﻡ ‪:٤‬‬
‫= ‪Col.Expression‬‬
‫;"‪' And AuthorID = 4‬ﻡ' < ‪"Book‬‬
‫ﻻﺤﻅ ﺃﻥ ﺍﻟﺘﻌﺒﻴﺭ ﺍﻟﺴﺎﺒﻕ ﺴﻴﻀﻊ ﻓـﻲ ﺨﺎﻨـﺎﺕ ﺍﻟﻌﻤـﻭﺩ ‪ true‬ﺃﻭ‬
‫‪ ،false‬ﻤﻤﺎ ﻴﺴﻤﺢ ﻟﻙ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ Select‬ﺍﻟﺨﺎﺼﺔ ﺒﻜـﺎﺌﻥ‬
‫ﺍﻟﺠﺩﻭل ﻻﺨﺘﻴﺎﺭ ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﻲ ﺘﺤﻘﻕ ﺃﻭ ﻻ ﺘﺤﻘﻕ ﺸﺭﻁﺎ ﻤﻌﻴﻨـﺎ‪..‬‬
‫ﺍﻋﺘﺒﺭ ﺃﻥ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﻤﺜﺎل ﺍﻟﺴﺎﺒﻕ ﺍﺴـﻤﻪ ‪ ..Col‬ﻫـﺫﺍ ﺍﻟﻤﺜـﺎل‬
‫ﻴﺤﺼل ﻋﻠﻰ ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﻲ ﺘﺤﻘﻕ ﺍﻟﺸﺭﻁ ﺍﻟﺴﺎﺒﻕ‪:‬‬
‫;)"‪DataRow[] R = T.Select ("Col = true‬‬
‫ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﻘﺎﺭﻨﺔ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬ ‫ﻤﻌﺎﻤﻼﺕ‬
‫‪= <> > < >= <= IN LIKE‬‬ ‫ﺍﻟﻤﻘﺎﺭﻨﺔ‬
‫ﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺘﻌﺭ‪‬ﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻋﻨﺩ ﺸـﺭﺡ ﺠﻤـل ‪ ،SQL‬ﻤـﻊ‬
‫ﻤﻼﺤﻅ ﺃﻥ ﻋﻼﻤﺎﺕ ﺍﻟﺘﻌﻭﻴﺽ )* ﺃﻭ ‪ (%‬ﻏﻴﺭ ﻤﺴﻤﻭﺡ ﺒﻬـﺎ ﻓـﻲ‬
‫ﻤﻨﺘﺼﻑ ﺍﻟﺘﻌﺒﻴﺭ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻤﻊ ﺍﻟﺩﺍﻟـﺔ ‪ ..LIKE‬ﻤـﺜﻼ‪ ،‬ﺍﻟﺘﻌﺒﻴـﺭ‬
‫ﺍﻟﺘﺎﻟﻲ ﻏﻴﺭ ﻤﻘﺒﻭل‪:‬‬
‫" 'ﺃﺤﻤﺩ*ﺘﻭﻓﻴﻕ' ‪"Author Like‬‬
‫ﻟﻜﻥ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻡ ﻋﻼﻤﺎﺕ ﺍﻟﺘﻌﻭﻴﺽ ﻓﻲ ﺒﺩﺍﻴﺔ ﺍﻟﻨﺹ ﺃﻭ ﻨﻬﺎﻴﺘﻪ‪..‬‬
‫ﻤﺜﺎل‪:‬‬
‫" 'ﻫﻴﻜل*' ‪"Author Like‬‬
‫" '*ﻫﻴﻜل' ‪"Author Like‬‬
‫ﺘﻘﻭﻡ ﺒﺎﻟﺘﺤﻭﻴل ﺒﻴﻥ ﺃﻨﻭﺍﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻤﺜﺎل‪:‬‬ ‫‪CONVERT‬‬
‫= ‪Col.Expression‬‬
‫;")'‪"Convert(ID, 'System.Int32‬‬
‫ﺘﻌﻴﺩ ﻁﻭل ﺍﻟﻨﺹ‪ ‬ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺨﺎﻨﺎﺕ ﺍﻟﻌﻤﻭﺩ‪ ..‬ﻭﺍﻟﻤﺜـﺎل ﺍﻟﺘـﺎﻟﻲ‬ ‫‪LEN‬‬
‫ﺴﻴﺠﻌل ﺍﻟﻌﻤﻭﺩ ‪ Col‬ﻴﻌﺭﺽ ﻁﻭل ﺃﺴﻤﺎﺀ ﺍﻟﻜﺘﺏ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓـﻲ‬
‫ﺍﻟﻌﻤﻭﺩ ‪:Book‬‬
‫;")‪Col.Expression = "Len(Book‬‬
‫‪٣١٢‬‬
‫ﻻﺤﻅ ﺃﻨﹼﹼﻙ ﻏﻴﺭ ﻤﺠﺒﺭ ﻋﻠﻰ ﻗﺼﺭ ﻤﻌﺎﻤل ﻫـﺫﻩ ﺍﻟﺩﺍﻟـﺔ )ﻭﺍﻟـﺩﻭﺍل‬
‫ﺍﻟﺘﺎﻟﻴﺔ ﺃﻴﻀﺎ( ﻋﻠﻰ ﺍﺴﻡ ﺃﺤﺩ ﺤﻘﻭل ﺍﻟﺠﺩﻭل‪ ..‬ﺇﻥ‪ ‬ﺘﻌﺒﻴﺭﺍ ﻜﻬﺫﺍ ﻤﺘﺎﺡ‬
‫ﺃﻴﻀﺎ‪:‬‬
‫;")‪Col.Expression = "Len(Book + Author‬‬
‫ﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﺩﺍﻟﺔ ﻤﻌﺎﻤﻠﻴﻥ‪ :‬ﺍﺴﻡ ﺍﻟﻌﻤـﻭﺩ‪ ،‬ﻭﻗﻴﻤـﺔ ﺍﻓﺘﺭﺍﻀـﻴﺔ‪..‬‬ ‫‪ISNULL‬‬
‫ﻭﺘﻔﺤﺹ ﻫﺫﻩ ﺍﻟﺩﺍﻟﺔ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﻓﺈﻥ ﻭﺠﺩﺕ ﻓﻴﻪ ﻗﻴﻤـﺔ ﺃﻋﺎﺩﺘﻬـﺎ‪ ،‬ﻭﺇﻥ‬
‫ﻭﺠﺩﺘﻪ ﻓﺎﺭﻏﺎ ﺃﻋﺎﺩﺕ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪ ..‬ﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﻴﺩ ﻗﻴﻤـﺔ‬
‫ﺍﻟﻌﻤﻭﺩ ‪ AuthorID‬ﺃﻭ ‪ ١-‬ﺇﺫﺍ ﻜﺎﻥ ﻓﺎﺭﻏﺎ‪:‬‬
‫;")‪Col.Expression = "IsNull(AuthorID, -1‬‬
‫ﺘﺯﻴل ﺍﻟﻤﺴﺎﻓﺎﺕ ﻤﻥ ﺒﺩﺍﻴﺔ ﻭﻨﻬﺎﻴﺔ ﺨﺎﻨﺎﺕ ﺍﻟﺤﻘـل ﺍﻟﻤﺭﺴـل ﺇﻟﻴﻬـﺎ‬ ‫‪TRIM‬‬
‫ﻜﻤﻌﺎﻤل‪ ..‬ﻤﺜﺎل‪:‬‬
‫;")‪Col.Expression = "TRIM(AuthorID‬‬
‫‪ SUBSTRING‬ﺘﻌﻴﺩ ﺠﺯﺀﺍ ﻤﻥ ﺍﻟﻨﺹ‪ ‬ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺨﺎﻨﺎﺕ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻤﺭﺴل ﺇﻟﻴﻬـﺎ‬
‫ﻜﻤﻌﺎﻤل‪ ،‬ﺒﺩﺀﺍ ﻤﻥ ﺍﻟﺤﺭﻑ ﺍﻟﻤﺫﻜﻭﺭ ﺭﻗﻤﻪ ﻓﻲ ﺍﻟﻤﻌﺎﻤـل ﺍﻟﺜـﺎﻨﻲ‪،‬‬
‫ـﺔ‬
‫ـﺔ ﻟﻠﺩﺍﻟـ‬
‫ـﺙ‪) ..‬ﻤﻤﺎﺜﻠـ‬
‫ـل ﺍﻟﺜﺎﻟـ‬
‫ـﻲ ﺍﻟﻤﻌﺎﻤـ‬
‫ـﺎﻟﻁﻭل ﺍﻟﻤﺤ ـﺩ‪‬ﺩ ﻓـ‬
‫ﻭﺒـ‬
‫‪ ..(String.SubString‬ﻤﺜﺎل‪:‬‬
‫= ‪Col.Expression‬‬
‫;")‪"SUBSTRING(Book, 2, 8‬‬
‫ﺘﺄﺨﺫ ﻫﺫﻩ ﺍﻟﺩﺍﻟﺔ ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ‪:‬‬ ‫‪IIF‬‬
‫‪ -‬ﺍﻷ ‪‬ﻭ‪‬ل ﺸﺭﻁ ﺴﻴﺘﻡ‪ ‬ﺍﻟﺘﺤﻘﻕ ﻤﻨﻪ‪.‬‬
‫‪ -‬ﻭﺍﻟﺜﺎﻨﻲ ﻫﻭ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻌﺎﺩﺓ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺸﺭﻁ ﺼﺤﻴﺤﺎ‪.‬‬
‫‪ -‬ﻭﺍﻟﺜﺎﻟﺙ ﻫﻭ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻌﺎﺩﺓ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺸﺭﻁ ﺨﺎﻁﺌﺎ‪.‬‬
‫ﻤﺜﺎل‪:‬‬
‫= ‪Col.Expression‬‬
‫;")'ﺭﺨﻴﺹ' ‪',‬ﻏﺎل' ‪"IIF(LEN(Price)>10),‬‬

‫‪٣١٣‬‬
‫ﺘﺸﻴﺭ ﺇﻟﻰ ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ ﺍﻟﺫﻱ ﻴﺩﺨل ﻓﻲ ﻋﻼﻗﺔ ﻤـﻊ ﺍﻟﺠـﺩﻭل‬ ‫‪Parent‬‬

‫ﺍﻟﺤﺎﻟﻲ‪ ..‬ﺍﻓﺘﺭﺽ ﺃﻥ ﺍﻟﻤﺘﻐﻴﺭ ‪ Col‬ﻴﺘﻌﺎﻤل ﻤﻊ ﻋﻤﻭﺩ ﻓﻲ ﺠـﺩﻭل‬


‫ﺍﻟﻜﺘﺏ‪ ..‬ﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻀﻊ ﻓﻲ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻁﻭل ﺍﺴﻡ ﻜل ﻤﺅﻟﻑ‪:‬‬
‫;")‪Col.Expression = "LEN(Parent.Author‬‬
‫ﻭﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺠﺩﻭل ﻤﺸﺘﺭﻜﺎ ﻓﻲ ﺃﻜﺜﺭ ﻤﻥ ﻋﻼﻗﺔ ﻤﻊ ﺠﺩﺍﻭل ﺭﺌﻴﺴـﻴﺔ‬
‫ﺍﺨﺭﻯ‪ ،‬ﻓﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺭﺴل ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﻗﻭﺴـﻴﻥ ﺒﻌـﺩ ﺍﻟﻜﻠﻤـﺔ‬
‫‪ Parent‬ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫= ‪Col.Expression‬‬
‫;")‪"LEN(Parent(AuthorsBooks).Author‬‬
‫ﺘﺸﻴﺭ ﺇﻟﻰ ﺍﻟﺠﺩﻭل ﺍﻟﺜﺎﻨﻭﻱ‪ ‬ﺍﻟﻤﺭﺘﺒﻁ ﺒﺎﻟﺠﺩﻭل ﺍﻟﺤﺎﻟﻲ‪ ‬ﺒﻌﻼﻗﺔ‪ ،‬ﺤﻴﺙ‬ ‫‪Child‬‬
‫ﺘﺴﺘﺨﺩﻡ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ ‪ Primary Key‬ﻟﻜل ﺴﺠل ﻓﻲ ﺍﻟﺠﺩﻭل‬
‫ﺍﻟﺭﺌﻴﺴﻲ‪ ،‬ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻔﺭﻋﻴﺔ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﻬﺫﺍ ﺍﻟﻤﻔﺘﺎﺡ‬
‫ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﻔﺭﻋﻲ‪ ..‬ﻭﻨﻅﺭﺍ ﻷﻥ ﻨﺎﺘﺞ ﻫﺫﻩ ﺍﻟﺩﺍﻟﺔ ﻗﺩ ﻴﻜﻭﻥ ﺃﻜﺜـﺭ‬
‫ﻤﻥ ﺴﺠل‪ ،‬ﻓﻼ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﺒﻤﻔﺭﺩﻫﺎ‪ ،‬ﻭﺇﻨﻤﺎ ﺘﺴﺘﺨﺩﻡ ﻤﻊ ﺇﺤﺩﻯ‬
‫ﺩﻭﺍل ﺍﻟﺘﺠﻤﻴﻊ‪.‬‬
‫ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻀﻴﻑ ﻋﻤﻭﺩﺍ ﺇﻟﻰ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ‪ ،‬ﻴﻌﺭﺽ ﻋـﺩﺩ‬
‫ﺍﻟﻜﺘﺏ ﺍﻟﺘﻲ ﺃﻟﻔﻬﺎ ﻜلّ ﻤﺅﻟﻑ‪:‬‬
‫‪DataColumn Col = new‬‬
‫;))‪DataColumn("NoOfBooks", typeof(int‬‬
‫= ‪Col.Expression‬‬
‫;")‪"Count(Child.AuthorID‬‬
‫;)‪Ds.Tables["Authors"].Columns.Add(Col‬‬
‫ﻭﻟﻭ ﻜﺎﻥ ﺍﻟﺠﺩﻭل ﺩﺍﺨﻼ ﻓﻲ ﺃﻜﺜﺭ ﻤﻥ ﻋﻼﻗﺔ‪ ،‬ﻓﻴﻤﻜﻨﻙ ﺇﺭﺴﺎل ﺍﺴـﻡ‬
‫ﺍﻟﻌﻼﻗﺔ ﻜﻤﻌﺎﻤل ﺇﻟﻰ ﺍﻟﺘﻌﺒﻴﺭ ‪ ،Child‬ﻤﺜل‪:‬‬
‫= ‪Col.Expression‬‬
‫;")‪"Count(Child(AuthorsBooks).AuthorID‬‬
‫ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺩﻭﺍل ﺍﻟﺘﺠﻤﻴﻊ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬ ‫ﺩﻭﺍل ﺍﻟﺘﺠﻤﻴﻊ‬
‫‪Sum, Avg, Min, Max, Count, StDev, Var‬‬
‫ﻻﺤﻅ ﺃﻥ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﺩﻭﺍل ﻋﻠﻰ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺤﺎﻟﻲ ﺴﻴﺠﻌﻠﻬﺎ ﺘﺘﻌﺎﻤل‬
‫‪٣١٤‬‬
‫ﻤﻊ ﻜل ﺍﻟﺼﻔﻭﻑ ﺒﺩﻭﻥ ﺘﻘﺴﻴﻤﻬﺎ ﺇﻟـﻰ ﻤﺠﻤﻭﻋـﺎﺕ ‪..Grouping‬‬
‫ﻤﺜﻼ‪ ،‬ﻟﻭ ﺍﺴﺘﺨﺩﻤﺕ ﺍﻟﺘﻌﺒﻴﺭ‪ "Count (AuthorID)" :‬ﻓﻲ ﺠﺩﻭل‬
‫ﺍﻟﻜﺘﺏ ﻓﺴﻴﻌﻴﺩ ﻋﺩﺩ ﺼﻔﻭﻑ ﺠﺩﻭل ﺍﻟﻜﺘﺏ‪ ..‬ﻭﻟـﻭ ﺃﺭﺩﺕ ﺘﺠﻤﻴـﻊ‬
‫ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﻲ ﺘﺘﺴﺎﺒﻪ ﻓﻲ ﻗﻴﻤﺔ ﻋﻤﻭﺩ ﻤﻌـﻴﻥ‪ ،‬ﻓﻌﻠﻴـﻙ ﺍﺴـﺘﺨﺩﺍﻡ‬
‫ﺍﻟﺘﻌﺒﻴﺭ ‪ Child‬ﻟﻼﺴﺘﻔﺎﺩﺓ ﻤﻥ ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺠـﺩﻭﻟﻴﻥ ﻓـﻲ ﺘﺠﻤﻴـﻊ‬
‫ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﻲ ﺘﺘﺸﺎﺒﻪ ﻓﻲ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺭﺌﻴﺴـﻲ‪ ..‬ﻭﻟـﻭ ﺍﺴـﺘﺨﺩﻤﺕ‬
‫ﺍﻟﺘﻌﺒﻴﺭ‪ "Count (Child.AuthorID)" :‬ﻓﻲ ﺠﺩﻭل ﺍﻟﻤـﺅﻟﻔﻴﻥ‪،‬‬
‫ﻓﺴﺘﺤﺼل ﻋﻠﻰ ﻤﺠﻤﻭﻉ ﻜﺘﺏ ﻜل ﻤﺅﻟﻑ‪.‬‬
‫ﺤﺭﻑ ﻨﻬﺎﻴﺔ ﺍﻟﺴﻁﺭ‪.‬‬ ‫‪\r‬‬

‫ﺤﺭﻑ ﺒﺩﺍﻴﺔ ﺍﻟﺴﻁﺭ‪.‬‬ ‫‪\n‬‬

‫ﻋﻼﻤﺔ ﺠﺩﻭﻟﺔ ‪.Tab‬‬ ‫‪\t‬‬

‫ﻻﺤﻅ ﺃﻥ ﻋﻠﻴﻙ ﻭﻀﻊ ﺍﻟﻨﺼﻭﺹ ﻭﺍﻟﺘﻭﺍﺭﻴﺦ ﺒﻴﻥ ﺍﻟﻌﻼﻤﺘﻴﻥ ' ' ﻤﺜل‪:‬‬
‫'ﻁﻭﻴل'‬
‫'‪'1/1/2009‬‬

‫ﻜﻤﺎ ﻴﻤﻜﻥ ﻭﻀﻊ ﺍﻟﺘﻭﺍﺭﻴﺦ ﺒﻴﻥ ﺍﻟﻌﻼﻤﺘﻴﻥ ‪ ،# #‬ﻤﺜل‪:‬‬


‫‪#1/1/2009#‬‬

‫ﻭﻴﻤﺘﻠﻙ ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﻐﻴﻴﺭ ﺍﻟﺭﺘﺒﺔ ‪:SetOrdinal‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ‪ ،‬ﻴﻤﺜل ﺍﻟﻤﻭﻀﻊ ﺍﻟﺠﺩﻴﺩ ﺍﻟـﺫﻱ ﺘﺭﻴـﺩ ﺃﻥ ﻴﺼـﻴﺭ‬
‫ﺍﻟﻌﻤﻭﺩ ﻓﻴﻪ ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ‪ ..‬ﻫﺫﺍ ﻫﻭ ﺍﻟﺤل ﺍﻟﻭﺤﻴﺩ ﻟﺘﻐﻴﻴﺭ ﻤﻭﻀـﻊ ﺍﻟﻌﻤـﻭﺩ‪ ،‬ﻷﻥ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻷﻋﻤﺩﺓ ﻻ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻭﺴﻴﻠﺔ ‪ ..Insert‬ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻓﻲ ﺍﻟﺯﺭ‬
‫"ﺤﻔﻅ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ "٢‬ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪ CustomDataSet‬ﻟﺠﻌـل ﺍﻟﻌﻤـﻭﺩ ‪ Subject‬ﻓـﻲ‬
‫ﺍﻟﻤﻭﻀﻊ ﺭﻗﻡ ‪) ٢‬ﺍﻟﻌﻤﻭﺩ ﺍﻟﺜﺎﻟﺙ( ﺒﻌﺩ ﺇﻋﺎﺩﺓ ﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺍﻟﺠﺩﻭل‪ ..‬ﻻﺤﻅ ﺃﻥ ﻫﺫﺍ ﺴـﻴﻐﻴﺭ‬
‫‪٣١٥‬‬
‫ﺘﺭﺘﻴﺏ ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﺠﺩﻭل‪ ،‬ﻟﻜﻨﻪ ﺴﻴﻅﻬﺭ ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻜـﺂﺨﺭ ﻋﻤـﻭﺩ!‪ ..‬ﻫـﺫﺍ ﻻ‬
‫ﻴﺅﺜﺭ ﻓﻲ ﻋﻤل ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ،‬ﻟﻜﻨﻪ ﻴﺯﻋﺞ ﺍﻟﻤﺴﺘﺨﺩﻡ‪ ،‬ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺘﻐﻴﻴﺭ ﺘﺭﺘﻴﺏ ﻋﺭﺽ ﺍﻟﻌﻤﻭﺩ‬
‫ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﺃﻴﻀﺎ‪ ..‬ﻫﺫﺍ ﻫﻭ ﺴﺒﺏ ﺍﺴﺘﺨﺩﺍﻤﻨﺎ ﻟﻠﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ ﻓﻲ ﻨﻬﺎﻴـﺔ ﺍﻹﺠـﺭﺍﺀ‬
‫‪:ShowGrades‬‬
‫;‪GradesCols[2].DisplayIndex = 0‬‬
‫ﻫﺫﻩ ﺍﻟﺠﻤﻠﺔ ﺘﺠﻌﻠﻨﺎ ﻭﺍﺜﻘﻴﻥ ﺃﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺫﻱ ﻴﻌﺭﺽ ﺍﺴﻡ ﺍﻟﻤﺎﺩﺓ ﻴﻅﻬﺭ ﺩﺍﺌﻤﺎ ﻗﺒـل ﺍﻟﻌﻤـﻭﺩ‬
‫ﺍﻟﺫﻱ ﻴﻌﺭﺽ ﺩﺭﺠﺎﺕ ﺍﻟﻁﺎﻟﺏ‪.‬‬

‫‪٣١٦‬‬
‫ﻓﺌﺔ ﻗﺎﺭﺉ ﺠﺩﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ ‪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‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ ،InternalDataCollectionBase‬ﻭﻫﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻜﺎﺌﻨـﺎﺕ ﻤـﻥ‬


‫ﻨﻭﻉ ﻓﺌﺔ ﻋﻼﻗﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪.DataRelation Class‬‬
‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﻭﺍﻟﻭﺴﺎﺌل ﺍﻟﺸـﻬﻴﺭﺓ ﻟﻠﻤﺠﻤﻭﻋـﺎﺕ ‪ ،Collections‬ﻭﻤﻌﻅﻤﻬـﺎ‬
‫ﻴﺴﺘﺨﺩﻡ ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ ﻜﻤﻌﺎﻤل‪ ،‬ﺃﻭ ﻴﺴﺘﺨﺩﻡ ﻜﺎﺌﻥ ﺍﻟﻌﻼﻗﺔ ‪ DataRelation‬ﺍﻟﺫﻱ ﻴﻤﺜﻠﻬﺎ‪ ..‬ﻟﻬـﺫﺍ‬
‫ﻨﺤﺘﺎﺝ ﻫﻨﺎ ﺇﻟﻰ ﺍﻟﺘﺭﻜﻴﺯ ﻋﻠﻰ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﺘﺎﻟﻴﺔ ﻓﻘﻁ‪:‬‬

‫ﺍﻟﻌﻨﺼﺭ ‪:Item‬‬
‫ﻫﺫﻩ ﻫﻲ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪ ،‬ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ ﻜﻤﻌﺎﻤل ﺃﻭ ﺭﻗﻡ ﺍﻟﻌﻼﻗـﺔ ﻓـﻲ‬
‫ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﻭﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻜﺎﺌﻥ ﺍﻟﻌﻼﻗﺔ ‪ DataRelation‬ﺍﻟﺫﻱ ﻴﻤﺜﻠﻬﺎ‪ ..‬ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪ ،DataSetContents‬ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻜﺎﺌﻥ ﺍﻟﻌﻼﻗﺔ ﺍﻟﺘﻲ ﻴﻀﻐﻁ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﺴﻤﻬﺎ ﻓﻲ ﻗﺎﺌﻤﺔ ﺍﻟﻌﻼﻗﺎﺕ‪ ،‬ﻟﻨﻌﺭﺽ ﺨﺼﺎﺌﺼﻬﺎ ﻓﻲ ﻤﺭﺒﻊ ﺭﺴﺎﻟﺔ‪.‬‬

‫ﺇﻀﺎﻓﺔ ‪:Add‬‬
‫ﺘﻀﻴﻑ ﻋﻼﻗﺔ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﻭﻟﻬﺎ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﻌﻼﻗﺔ ‪ DataRelation‬ﺍﻟﻤﺭﺍﺩ ﺇﻀﺎﻓﺘﻪ‪.‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ ﻤﻥ ﺍﻟﻨـﻭﻉ ‪ ،DataColumn‬ﻴﻤـﺜﻼﻥ ﺍﻟﺤﻘـل‬
‫ﺍﻟﺭﺌﻴﺴﻲ ﻭﺍﻟﺤﻘل ﺍﻟﻔﺭﻋﻲ ﻋﻠﻰ ﺍﻟﺘﺭﺘﻴﺏ‪ ،‬ﺤﻴﺙ ﺴـﻴﺘﻡ ﺇﻨﺸـﺎﺀ ﻋﻼﻗـﺔ ﺒﻴﻨﻬﻤـﺎ‪،‬‬
‫ﻭﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪.‬‬
‫‪ -٣‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ‪ ،‬ﺇﻻ ﺃﻨﻬﺎ ﺘﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺘﻴﻥ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪ ،DataColumn‬ﻭﺫﻟﻙ ﻟﻤﺭﺍﻋﺎﺓ ﺍﻟﺤﺎﻟﺔ ﺍﻟﺘﻲ ﻴﺘﻜﻭﻥ ﻓﻴﻬـﺎ ﻜـل ﻤـﻥ ﺍﻟﻤﻔﺘـﺎﺡ‬
‫ﺍﻷﺴﺎﺴﻲ ﻭﺍﻟﻔﺭﻋﻲ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﻋﻤﻭﺩ‪.‬‬

‫‪٣١٩‬‬
‫‪ -٤‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺴﺘﻘﺒل ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ‪ :‬ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ‪ ،‬ﻭﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﺭﺌﻴﺴـﻲ‪،‬‬
‫ﻭﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻔﺭﻋﻲ‪.‬‬
‫‪ -٥‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺨﺎﻤﺴﺔ ﺘﺯﻴﺩ ﺒﻤﻌﺎﻤل ﻤﻨﻁﻘﻲ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ‪ ،‬ﺇﺫﺍ ﺠﻌﻠـﺕ ﻗﻴﻤﺘـﻪ‬
‫‪ false‬ﻓﻠﻥ ﻴﺘﻡ ﺇﻨﺸﺎﺀ ﻗﻴﻭﺩ ﻋﻨﺩ ﺇﻨﺸﺎﺀ ﺍﻟﻌﻼﻗﺔ‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻓﻲ ﺍﻟﺼـﻴﻎ‬
‫ﺍﻟﺘﻲ ﻻ ﺘﺤﺘﻭﻱ ﻫـﺫﺍ ﺍﻟﻤﻌﺎﻤـل ﻫـﻲ ‪ ،true‬ﻟﻬـﺫﺍ ﻴـﺘﻡ ﺇﻨﺸـﺎﺀ ﻗﻴـﺩ ﺍﻟﺘﻔـﺭﺩ‬
‫‪ UniqueConstraint‬ﻋﻠﻰ ﺍﻟﺤﻘل ﺍﻷﺴﺎﺴﻲ ﺇﻥ ﻟﻡ ﻴﻜﻥ ﻤﻭﺠﻭﺩﺍ‪ ،‬ﻭﺇﻨﺸـﺎﺀ ﻗﻴـﺩ‬
‫ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ ‪ ForeignKeyConstraint‬ﻋﻠﻰ ﺍﻟﺤﻘل ﺍﻟﻔﺭﻋﻲ ﺇﻥ ﻟـﻡ ﻴﻜـﻥ‬
‫ﻤﻭﺠﻭﺩﺍ‪ ،‬ﻭﻴﺘﻡ ﺇﻀﺎﻓﺘﻬﻤﺎ ﺇﻟﻰ ﻗﻴﻭﺩ ﺍﻟﺠﺩﻭل‪.‬‬
‫‪ -٦‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺩﺴﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴـﺎﺒﻘﺔ‪ ،‬ﺇﻻ ﺃﻥ ﻤﻌﺎﻤﻠﻴﻬـﺎ ﺍﻟﺜـﺎﻨﻲ ﻭﺍﻟﺜﺎﻟـﺙ‬
‫ﻴﺴﺘﻘﺒﻼﻥ ﻤﺼﻔﻭﻓﺔ ﺤﻘﻭل ‪ DataColumn Array‬ﻟﻤﺭﺍﻋﺎﺓ ﺍﻟﺤﺎﻟﺔ ﺍﻟﺘﻲ ﻴﺘﻜـﻭﻥ‬
‫ﻓﻴﻬﺎ ﻜل ﻤﻥ ﺍﻟﻤﻔﺘﺎﺤﻴﻥ ﺍﻷﺴﺎﺴﻲ ﻭﺍﻟﻔﺭﻋﻲ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﺤﻘل‪.‬‬

‫ﻻﺤﻅ ﺃﻥ ﺠﻤﻴﻊ ﺍﻟﺼﻴﻎ ـ ﻤﺎ ﻋﺩﺍ ﺍﻷﻭﻟﻰ ـ ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﻌﻼﻗﺔ ‪ DataRelation‬ﺍﻟـﺫﻱ‬


‫ﺘﻡ ﺇﻨﺸﺎﺅﻩ ﻭﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪.‬‬

‫ﺍﻟﻤﺠﻤﻭﻋﺔ ﺘﻐﻴﺭﺕ ‪:CollectionChanged‬‬


‫ﻴﻨﻁﻠﻕ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻋﻨﺩﻤﺎ ﻴﺘﻐﻴﺭ ﻋﺩﺩ ﻋﻨﺎﺼﺭ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻌﻼﻗـﺎﺕ‪ ،‬ﺴـﻭﺍﺀ ﺒﺎﻟﺤـﺫﻑ ﺃﻭ‬
‫ﺍﻹﻀﺎﻓﺔ‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨﻭﻉ ‪CollectionChangeEventArgs‬‬
‫ﺍﻟﺫﻱ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻪ ﻤﻥ ﻗﺒل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل ‪.DataTableCollection‬‬

‫‪٣٢٠‬‬
‫ﻓﺌﺔ ﺍﻟﻌﻼﻗﺔ ‪DataRelation Class‬‬

‫ﺘﺤﺘﻭﻱ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻋﻠﻰ ﺘﻔﺎﺼﻴل ﺍﻟﻌﻼﻗﺔ ﺍﻟﻤﻨﺸﺄﺓ ﺒﻴﻥ ﺠﺩﻭﻟﻴﻥ‪.‬‬


‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻨﻔﺱ ﺼﻴﻎ ﺍﻟﻭﺴﻴﻠﺔ ‪ DataRelationCollection.Add‬ﻤـﺎ ﻋـﺩﺍ‬
‫ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺍﻟﺘﻲ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﻋﻼﻗﺔ‪ ..‬ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﺼﻴﻎ‪ ،‬ﻴﻤﺘﻠﻙ ﺤـﺩﺙ ﺍﻹﻨﺸـﺎﺀ‬
‫ﺍﻟﺼﻴﻐﺘﻴﻥ ﺍﻟﺠﺩﻴﺩﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -‬ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ‪.‬‬
‫‪ -‬ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ‪.‬‬
‫‪ -‬ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻟﻔﺭﻋﻲ‪.‬‬
‫‪ -‬ﻤﺼﻔﻭﻓﺔ ﻨﺼﻴﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺃﻋﻤﺩﺓ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ‪.‬‬
‫‪ -‬ﻤﺼﻔﻭﻓﺔ ﻨﺼﻴﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺃﻋﻤﺩﺓ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ‪.‬‬
‫‪ -‬ﻤﻌﺎﻤﻼ ﻤﻨﻁﻘﻴﺎ ‪ ،Boolean‬ﻟﺘﻭﻀﻊ ﻗﻴﻤﺘﻪ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ Nested‬ﺍﻟﺨﺎﺼﺔ ﺒﻜـﺎﺌﻥ‬
‫ﺍﻟﻌﻼﻗﺔ‪.‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -‬ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ‪.‬‬
‫‪ -‬ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ‪.‬‬
‫‪ -‬ﻨﺼﺎ ﻴﻤﺜل ﻨﻁﺎﻕ ﺍﺴﻡ ‪ Namespace‬ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ‪.‬‬
‫‪ -‬ﻨﺼﺎ ﻴﻤﺜل ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻟﻔﺭﻋﻲ‪.‬‬
‫‪ -‬ﻨﺼﺎ ﻴﻤﺜل ﻨﻁﺎﻕ ﺍﺴﻡ ﺍﻟﺠﺩﻭل ﺍﻟﻔﺭﻋﻲ‪.‬‬
‫‪ -‬ﻤﺼﻔﻭﻓﺔ ﻨﺼﻴﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺃﻋﻤﺩﺓ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ‪.‬‬
‫‪ -‬ﻤﺼﻔﻭﻓﺔ ﻨﺼﻴﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺃﻋﻤﺩﺓ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ‪.‬‬
‫‪ -‬ﻤﻌﺎﻤﻼ ﻤﻨﻁﻘﻴﺎ ‪ ،Boolean‬ﻟﺘﻭﻀﻊ ﻗﻴﻤﺘﻪ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ Nested‬ﺍﻟﺨﺎﺼﺔ ﺒﻜـﺎﺌﻥ‬
‫ﺍﻟﻌﻼﻗﺔ‪.‬‬

‫‪٣٢١‬‬
‫ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﺭﻴﻙ ﻜﻴﻑ ﺘﻨﺸﺊ ﻋﻼﻗﺔ ﺒﻴﻥ ﺍﻟﺤﻘـل ‪ 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‬‬
‫ﻟﺠﺩﻭل ﺍﻟﻜﺘﺏ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻓﺌﺔ ﺍﻟﻌﻼﻗﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataSet‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ،DataSet‬ﺍﻟﺫﻱ ﺘﻨﺘﻤﻲ ﺇﻟﻴﻪ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ‪.‬‬

‫ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ ‪:RelationName‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴ‪‬ﺭ ﺍﺴﻡ ﺍﻟﻌ‪‬ﻼﻗﺔ‪.‬‬

‫ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ ‪:ParentTable‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺠﺩﻭل ‪ DataTable‬ﻴﻤﺜل ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ‪ ‬ﻓﻲ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ‪.‬‬

‫ﺍﻟﺠﺩﻭل ﺍﻟﻔﺭﻋﻲ ‪:ChildTable‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺠﺩﻭل ‪ DataTable‬ﻴﻤﺜل ﺍﻟﺠﺩﻭل ﺍﻟﺜﺎﻨﻭﻱ‪ ‬ﻓﻲ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ‪.‬‬

‫‪٣٢٢‬‬
‫ﺍﻷﻋﻤﺩﺓ ﺍﻟﺭﺌﻴﺴﻴﺔ ‪:ParentColumns‬‬
‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ DataColumn‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻷﻋﻤـﺩﺓ ﺍﻟﺭﺌﻴﺴـﻴ‪‬ﺔ ﻓـﻲ ﻫـﺫﻩ‬
‫ﺍﻟﻌﻼﻗﺔ‪.‬‬

‫ﺍﻷﻋﻤﺩﺓ ﺍﻟﻔﺭﻋﻴﺔ ‪:ChildColumns‬‬


‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ DataColumn‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺜﺎﻨﻭﻴ‪‬ﺔ ﻓﻲ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ‪.‬‬

‫ﻗﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺭﺌﻴﺴﻲ ‪:ParentKeyConstraint‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ ‪ ،UniqueConstraint‬ﺍﻟﻤﻔﺭﻭﺽ ﻋﻠﻰ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺭﺌﻴﺴﻲ‪ ‬ﻓﻲ ﻫـﺫﻩ‬
‫ﺍﻟﻌﻼﻗﺔ‪.‬‬

‫ﻗﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ ‪:ChildKeyConstraint‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻗﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ ‪ ،ForeignKeyConstraint‬ﺍﻟﻤﻔﺭﻭﺽ ﻋﻠـﻰ ﺍﻟﻤﻔﺘـﺎﺡ‬
‫ﺍﻟﺜﺎﻨﻭﻱ‪ ‬ﻓﻲ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ‪.‬‬

‫ﻤﺘﺩﺍﺨﻠﺔ ‪:Nested‬‬
‫ﺘﻔﻴﺩ ﻋﻨﺩ ﺤﻔﻅ ﺴﺠﻼﺕ ﺍﻟﺠـﺩﻭل ﺍﻟﺭﺌﻴﺴـﻲ ﻓـﻲ ﻤﻠـﻑ ‪ XML‬ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪ ،WriteXml‬ﻓﻠﻭ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،true‬ﻓﺴﻴﺘﻡ ﺤﻔﻅ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻔﺭﻋﻴﺔ ﻤـﻊ‬
‫ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﺍﻟﺫﻱ ﺘﺭﺒﻁﻬﺎ ﺒﻪ ﺍﻟﻌﻼﻗﺔ ﺍﻟﺤﺎﻟﻴﺔ‪.‬‬

‫ﺍﻟﺨﺼﺎﺌﺹ ﺍﻹﻀﺎﻓﻴﺔ ‪:ExtendedProperties‬‬


‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺨﺼﺎﺌﺹ ‪ PropertyCollection‬ﺍﻟﺘﻲ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﺍﻟﺨﺼـﺎﺌﺹ‬
‫ﺼ‪‬ﺔ ﺒﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل‪.‬‬
‫ﺍﻹﻀﺎﻓ ‪‬ﻴ‪‬ﺔ ﻟﻠﻌﻤﻭﺩ‪ ،‬ﻭﻫﻲ ﻤﻤﺎﺜﻠﺔ ﻟﺘﻠﻙ ﺍﻟﺨﺎ ‪‬‬

‫‪٣٢٣‬‬
‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻘﻴﻭﺩ ‪ConstraintCollection Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻤﺠﻤﻭﻋﺔ ‪ ،InternalDataCollectionBase‬ﻭﻜل ﻋﻨﺼﺭ ﻤﻥ ﻋﻨﺎﺼـﺭﻫﺎ‬


‫ﻤﻥ ﻨﻭﻉ ﻓﺌﺔ ﺍﻟﻘﻴﺩ ‪ Constraint Class‬ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ‪.‬‬
‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﻭﺍﻟﻭﺴﺎﺌل ﺍﻟﺸـﻬﻴﺭﺓ ﻟﻠﻤﺠﻤﻭﻋـﺎﺕ ‪ ،Collections‬ﻭﻤﻌﻅﻤﻬـﺎ‬
‫ﻴﺴﺘﺨﺩﻡ ﺍﺴﻡ ﺍﻟﻘﻴﺩ ﻜﻤﻌﺎﻤل‪ ،‬ﺃﻭ ﻴﺴﺘﺨﺩﻡ ﻜﺎﺌﻥ ﺍﻟﻘﻴﺩ ‪ Constraint‬ﺍﻟﺫﻱ ﻴﻤﺜﻠﻪ‪ ..‬ﻟﻬﺫﺍ ﻨﺤﺘﺎﺝ ﻫﻨـﺎ‬
‫ﺇﻟﻰ ﺍﻟﺘﺭﻜﻴﺯ ﻋﻠﻰ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﺘﺎﻟﻴﺔ ﻓﻘﻁ‪:‬‬

‫ﺍﻟﻌﻨﺼﺭ ‪:Item‬‬
‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺭﻗﻡ ﺍﻟﻘﻴﺩ ﻓﻲ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﺃﻭ ﻨﺼﺎ ﻴﺤﻤل ﺍﺴﻡ ﺍﻟﻘﻴﺩ‪ ،‬ﻟﺘﻌﻴﺩ ﺇﻟﻴـﻙ‬
‫ﻜﺎﺌﻥ ﺍﻟﻘﻴﺩ ‪ Constraint‬ﺍﻟﺫﻱ ﻴﻤﺜﻠﻪ‪.‬‬

‫ﺇﻀﺎﻓﺔ ‪:Add‬‬
‫ﺘﻀﻴﻑ ﻗﻴﺩﺍ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﻭﻟﻬﺎ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﻘﻴﺩ ‪ Constraint‬ﺍﻟﻤﺭﺍﺩ ﺇﻀﺎﻓﺘﻪ‪.‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﻨﺸﺊ ﻗﻴﺩ ﺘﻔﺭﺩ ‪ UniqueConstraint‬ﻭﺘﻀﻴﻔﻪ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋـﺔ‪..‬‬
‫ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ‪:‬‬
‫‪ -‬ﺍﺴﻡ ﺍﻟﻘﻴﺩ‪.‬‬
‫‪ -‬ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ‪ DataColumn‬ﺍﻟﺫﻱ ﻴﺠﺏ ﺃﻥ ﻴﻜﻭﻥ ﻤﺘﻔﺭﺩﺍ‪.‬‬
‫‪ -‬ﻗﻴﻤﺔ ﻤﻨﻁﻘﻴﺔ ﺇﺫﺍ ﺠﻌﻠﺘﻬﺎ ‪ true‬ﻓﺴﻴﺘﻡ ﺠﻌل ﺍﻟﻌﻤﻭﺩ ﺍﻟﻤﺭﺴل ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤـل‬
‫ﺍﻟﺜﺎﻨﻲ ﻤﻔﺘﺎﺤﺎ ﺃﺴﺎﺴﻴﺎ ﻟﻠﺠﺩﻭل‪.‬‬
‫‪ -٣‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ‪ ،‬ﺇﻻ ﺃﻥ ﻤﻌﺎﻤﻠﻬﺎ ﺍﻟﺜﺎﻨﻲ ﻴﺴـﺘﻘﺒل ﻤﺼـﻔﻭﻓﺔ‬
‫ﺃﻋﻤﺩﺓ ‪ ،DataColumn Array‬ﻭﺫﻟﻙ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻤﻁﻠـﻭﺏ ﺘﻔـﺭﺩﻩ ﻓـﻲ‬
‫ﺍﻟﺠﺩﻭل ﻴﺘﻜﻭﻥ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﻋﻤﻭﺩ‪.‬‬
‫‪ -٤‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﻨﺸﺊ ﻗﻴﺩ ﻤﻔﺘﺎﺡ ﻓﺭﻋﻲ ‪ ForeignKeyConstraint‬ﻭﺘﻀـﻴﻔﻪ‬
‫ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ..‬ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -‬ﺍﺴﻡ ﺍﻟﻘﻴﺩ‪.‬‬
‫‪٣٢٤‬‬
‫‪ -‬ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ‪ DataColumn‬ﺍﻷﺴﺎﺴﻲ‪.‬‬
‫‪ -‬ﻜﺎﺌﻥ ﺍﻟﻌﻤﻭﺩ ‪ DataColumn‬ﺍﻟﻔﺭﻋﻲ‪.‬‬
‫‪ -٥‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺨﺎﻤﺴﺔ ﻤﻤﺎﺜﻠﺔ ﻟﻠﺼﻴﻐﺔ ﺍﻟﺴـﺎﺒﻘﺔ‪ ،‬ﺇﻻ ﺃﻥ ﻤﻌﺎﻤﻠﻴﻬـﺎ ﺍﻟﺜـﺎﻨﻲ ﻭﺍﻟﺜﺎﻟـﺙ‬
‫ﻴﺴﺘﻘﺒﻼﻥ ﻤﺼﻔﻭﻓﺔ ﺃﻋﻤﺩﺓ ‪ ،DataColumn Array‬ﻭﺫﻟـﻙ ﺇﺫﺍ ﻜـﺎﻥ ﺍﻟﻤﻔﺘـﺎﺡ‬
‫ﺍﻷﺴﺎﺴﻲ ﻭﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ ﻴﺘﻜﻭﻨﺎﻥ ﻤﻥ ﺃﻜﺜﺭ ﻤﻥ ﻋﻤﻭﺩ‪.‬‬
‫ﻻﺤﻅ ﺃﻥ ﺠﻤﻴﻊ ﺍﻟﺼﻴﻎ ﻤﺎ ﻋﺩﺍ ﺍﻷﻭﻟﻰ‪ ،‬ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﻘﻴﺩ ‪ Constraint‬ﺍﻟﺫﻱ ﺘـﻡ ﺇﻨﺸـﺎﺅﻩ‬
‫ﻭﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪.‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﺍﻟﻘﻴﻭﺩ ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺒﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ‪ ،‬ﻭﺫﻟﻙ ﻤـﻥ‬
‫ﺨﻼل ﻨﺎﻓﺫﺓ ﺨﺼﺎﺌﺹ ﺍﻟﺠﺩﻭل‪ ..‬ﻟﻔﻌل ﻫﺫﺍ ﻴﺠﺏ ﺃﻥ ﻴﻜﻭﻥ ﻟﺩﻴﻙ ﻜﺎﺌﻥ ﺠﺩﻭل ﻓﻲ ﺼـﻴﻨﻴﺔ‬
‫ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤﻭﺫﺝ )ﻭﻫﺫﺍ ﻏﻴﺭ ﺸﺎﺌﻊ(‪ ،‬ﺃﻭ ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨـﺎﺕ ﻋﺎﺩﻴـﺔ ‪Un-‬‬
‫‪ Typed DataSet‬ﻤﻭﻀﻭﻋﺔ ﻓﻲ ﺼﻴﻨﻴﺔ ﺍﻟﻤﻜﻭﻨﺎﺕ‪ ،‬ﻓﻌﻨﺩ ﻋﺭﺽ ﺨﺼﺎﺌﺼﻬﺎ ﻓﻲ ﻨﺎﻓـﺫﺓ‬
‫ﺍﻟﺨﺼﺎﺌﺹ‪ ،‬ﺴﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼﻴﺔ ‪ Tables‬ﻟﻌﺭﺽ ﻤﺤﺭﺭ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺠـﺩﺍﻭل‪،‬‬
‫ﻭﻟﻭ ﺤﺩﺩﺕ ﺃﻱ ﺠﺩﻭل ﻓﻲ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﻓﺴﺘﻅﻬﺭ ﺨﺼﺎﺌﺼﻪ ﻓﻲ ﺍﻟﻘﺴﻡ ﺍﻷﻴﻤـﻥ ﻤـﻥ‬
‫ﺍﻟﻨﺎﻓﺫﺓ‪ ،‬ﻭﺴﺘﺠﺩ ﺒﻴﻨﻬﺎ ﺍﻟﺨﺎﺼﻴﺔ ‪ ..Constraints‬ﻭﻟﻭ ﻀﻐﻁﺕ ﺯﺭ ﺍﻻﻨﺘﻘﺎل ﺍﻟﻤﻭﺠﻭﺩ ﻓـﻲ‬
‫ﺨﺎﻨﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻓﺴﺘﻅﻬﺭ ﻨﺎﻓﺫﺓ ﻤﺤﺭﺭ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻘﻴﻭﺩ‪ ،‬ﻜﻤﺎ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪:‬‬

‫‪٣٢٥‬‬
‫ﺍﻀﻐﻁ ﺍﻟﺯﺭ ‪ Add‬ﻹﻀﺎﻓﺔ ﻗﻴﺩ ﺠﺩﻴﺩ‪ ..‬ﺴﺘﻅﻬﺭ ﻟﻙ ﻗﺎﺌﻤﺔ ﻤﻭﻀﻌﻴﺔ ﻟﺘﺘﻴﺢ ﻟـﻙ ﺍﺨﺘﻴـﺎﺭ‬
‫ﻨﻭﻉ ﺍﻟﻘﻴﺩ‪ ،‬ﻤﻥ ﺒﻴﻥ ﺍﻟﻨﻭﻋﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ‪:‬‬

‫‪ -١‬ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ ‪:Unique Constraint‬‬


‫ﻋﻨﺩ ﻀﻐﻁ ﻫﺫﺍ ﺍﻻﺨﺘﻴﺎﺭ‪ ،‬ﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﺇﻨﺸﺎﺀ ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ‪ ،‬ﻭﻗﺩ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﺎ ﻓـﻲ‬
‫ﻤﺨﻁﻁ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻫﻲ ﺘﺒﺩﻭ ﻜﻤﺎ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪:‬‬

‫‪ -٢‬ﻗﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ ‪:Foreign Key Constraint‬‬


‫ﻋﻨﺩ ﻀﻐﻁ ﻫﺫﺍ ﺍﻻﺨﺘﻴﺎﺭ‪ ،‬ﺴﺘﻅﻬﺭ ﻟﻙ ﻨﺎﻓﺫﺓ ﺇﻨﺸﺎﺀ ﻗﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﻔﺭﻋﻲ‪ ،‬ﻭﻫﻲ ﺘﺸـﺒﻪ‬
‫ﻨﺎﻓﺫﺓ ﺇﻨﺸﺎﺀ ﻋﻼﻗﺔ‪ ،‬ﻭﻻ ﺠﺩﻴﺩ ﻓﻴﻬﺎ‪ ،‬ﻭﺘﺒﺩﻭ ﻜﻤﺎ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪:‬‬

‫‪٣٢٦‬‬
‫ﻭﺒﻌﺩ ﺃﻥ ﺘﻀﻴﻑ ﺍﻟﻘﻴﺩ ﺴﻴﻅﻬﺭ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﺴﺭﻯ‪ ،‬ﻭﺴﺘﻅﻬﺭ ﺨﺼﺎﺌﺼـﻪ ﻓـﻲ ﺍﻟﻘﺎﺌﻤـﺔ‬
‫ﺍﻟﻴﻤﻨﻰ‪.‬‬

‫ﻴﻤﻜﻥ ﺤﺫﻓﻪ ‪:CanRemove‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﻤﻥ ﺍﻟﻤﻤﻜﻥ ﺃﻥ ﺘﺤﺫﻑ ﻤﻥ ﺍﻟﻤﺠﻤﻭﻋـﺔ‪ ،‬ﻜـﺎﺌﻥ ﺍﻟﻘﻴـﺩ ‪Constraint‬‬
‫ﺍﻟﻤﺭﺴل ﺇﻟﻴﻬﺎ ﻜﻤﻌﺎﻤل‪ ،‬ﺒﺩﻭﻥ ﺤﺩﻭﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ..‬ﻤﺜﻼ‪ :‬ﻤﺤﺎﻭﻟﺔ ﺤﺫﻑ ﻗﻴﺩ ﺍﻟﺘﻔﺭﺩ‬
‫‪ UniqueConstraint‬ﻗﺒل ﺤﺫﻑ ﻗﻴﺩ ﺍﻟﻤﻔﺘـﺎﺡ ﺍﻟﻔﺭﻋـﻲ ‪ForeignKeyConstraint‬‬
‫ﺍﻟﻤﺭﺘﺒﻁ ﺒﻪ‪ ،‬ﺘﺅﺩﻱ ﺇﻟﻰ ﺤﺩﻭﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ،‬ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻗﺒـل‬
‫ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ Remove‬ﺃﻭ ‪.RemoveAt‬‬

‫‪٣٢٧‬‬
‫ﺍﻟﻤﺠﻤﻭﻋﺔ ﺘﻐﻴﺭﺕ ‪:CollectionChanged‬‬
‫ﻴﻨﻁﻠﻕ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻋﻨﺩﻤﺎ ﻴﺘﻐﻴﺭ ﻋﺩﺩ ﻋﻨﺎﺼﺭ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﻘﻴـﻭﺩ‪ ،‬ﺴـﻭﺍﺀ ﺒﺎﻟﺤـﺫﻑ ﺃﻭ‬
‫ﺍﻹﻀـــﺎﻓﺔ‪ ..‬ﻭﺍﻟﻤﻌﺎﻤـــل ﺍﻟﺜـــﺎﻨﻲ ‪ e‬ﻟﻬـــﺫﺍ ﺍﻟﺤـــﺩﺙ ﻤـــﻥ ﺍﻟﻨـــﻭﻉ‬
‫‪ CollectionChangeEventArgs‬ﺍﻟﺫﻱ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻪ ﻤﻥ ﻗﺒل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠـﺩﺍﻭل‬
‫‪.DataTableCollection‬‬

‫‪٣٢٨‬‬
‫ﻓﺌﺔ ﺍﻟﻘﻴﺩ ‪Constraint Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺴﺎﺴﺔ ﻤﺠﺭﺩﺓ ‪ Abstract Base Class‬ﻭﺘﺠﺏ ﻭﺭﺍﺜﺘﻬﺎ‪ ،‬ﻭﻫﻲ ﺘﻌﻤل ﻜﻔﺌﺔ ﺃﻡ ﻟﻜـل‬
‫ﻤــﻥ ﻓﺌــﺔ ﻗﻴــﺩ ﺍﻟﺘﻔــﺭﺩ ‪ UniqueConstraint Class‬ﻭﻗﻴــﺩ ﺍﻟﻤﻔﺘــﺎﺡ ﺍﻟﻔﺭﻋــﻲ‬
‫‪.ForeignKeyConstraint Class‬‬
‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﺴﻡ ﺍﻟﻘﻴﺩ ‪:ConstraintName‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﺍﻟﻘﻴﺩ‪.‬‬

‫ﺍﻟﺠﺩﻭل ‪:Table‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ‪ DataTable‬ﺍﻟﺫﻱ ﻴﻨﻁﺒﻕ ﻋﻠﻴﻪ ﺍﻟﻘﻴﺩ‪.‬‬

‫ﺍﻟﺨﺼﺎﺌﺹ ﺍﻹﻀﺎﻓﻴﺔ ‪:ExtendedProperties‬‬


‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺨﺼﺎﺌﺹ ‪ PropertyCollection‬ﺍﻟﺘﻲ ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﺍﻟﺨﺼـﺎﺌﺹ‬
‫ﺼ‪‬ﺔ ﺒﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل‪.‬‬
‫ﺍﻹﻀﺎﻓ ‪‬ﻴ‪‬ﺔ ﻟﻠﻘﻴﺩ‪ ،‬ﻭﻫﻲ ﻤﻤﺎﺜﻠﺔ ﻟﺘﻠﻙ ﺍﻟﺨﺎ ‪‬‬

‫‪٣٢٩‬‬
‫ﻓﺌﺔ ﻗﻴﺩ ﺍﻟﺘﻔﺭ‪‬ﺩ ‪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‬‬
‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﻴﺅﺜﹼﹼﺭ ﻋﻠﻴﻬﺎ ﻫﺫﺍ ﺍﻟﻘﻴﺩ‪.‬‬

‫ﻫل ﻫﻭ ﻤﻔﺘﺎﺡ ﺃﺴﺎﺴﻲ ‪:IsPrimaryKey‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﻫﺫﺍ ﺍﻟﻘﻴﺩ ﻤﻔﺭﻭﻀﺎ ﻋﻠﻰ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻷﺴﺎﺴﻲ‪ ‬ﻟﻠﺠﺩﻭل‪.‬‬

‫‪٣٣١‬‬
‫ﻓﺌﺔ ﻗﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺜﺎﻨﻭﻱ ‪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‬ﻓﻲ ﺠـﺩﻭل‬
‫ﺍﻟﻤﺅﻟﻔﻴﻥ‪ ،‬ﻭﺫﻟﻙ ﺇﺫﺍ ﻟﻡ ﻴﻜﻥ ﻫﺫﺍ ﺍﻟﻘﻴﺩ ﻤﻭﺠﻭﺩﺍ ﻤﺴﺒﻘﺎ‪.‬‬

‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻟﺠﺩﻭل ﺍﻟﻤﺭﺘﺒﻁ ‪:RelatedTable‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺠﺩﻭل ‪ DataTable‬ﻴﻤﺜل ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ‪ ‬ﻓﻲ ﺍﻟﻌﻼﻗﺔ‪.‬‬

‫ﺍﻷﻋﻤﺩﺓ ﺍﻟﻤﺭﺘﺒﻁﺔ ‪:RelatedColumns‬‬


‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺃﻋﻤﺩﺓ ‪ ،DataColumn Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺘﻌﻤل ﻜﻤﻔﺘﺎﺡ‬
‫ﺃﺴﺎﺴﻲ ﻓﻲ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ‪.‬‬

‫‪٣٣٣‬‬
‫ﺍﻷﻋﻤﺩﺓ ‪:Columns‬‬
‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺃﻋﻤﺩﺓ ‪ ،DataColumn Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺘﻌﻤل ﻜﻤﻔﺘﺎﺡ‬
‫ﺜﺎﻨﻭﻱ ﻓﻲ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ‪.‬‬

‫ﻗﺎﻋﺩﺓ ﺍﻟﻘﺒﻭل ﻭﺍﻟﺭﻓﺽ ‪:AcceptRejectRule‬‬


‫ﺘﻭﻀ‪‬ﺢ ﺍﻟﻔﻌل ﺍﻟﺫﻱ ﺴﻴﺘﻡ‪ ‬ﺍﺘﺨﺎﺫﻩ ﻋﻨﺩ ﺍﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴﻴﻠﺔ ‪ DataRow.AcceptChanges‬ﺃﻭ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪ ..DataRow.RejectChanges‬ﻭﺘﺄﺨﺫ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴ‪‬ﺔ ﺇﺤـﺩﻯ ﻗﻴﻤﺘـﻲ ﺍﻟﻤـﺭﻗﻡ‬
‫‪ AcceptRejectRule‬ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬
‫ﻋﻨﺩ ﻗﻴﺎﻡ ﺃﺤﺩ ﺍﻟﺴﺠﻼﺕ ﺒﻘﺒﻭل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺃﻭ ﺭﻓﻀﻬﺎ‪ ،‬ﻻ ﻴﺤﺩﺙ ﺃﻱ ﺸﻲﺀ‬ ‫‪None‬‬
‫ﻟﻠﺴﺠﻼﺕ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﻪ ﻓﻲ ﺍﻟﻌﻼﻗﺔ‪ ..‬ﻫﺫﻩ ﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪.‬‬
‫‪ Cascade‬ﻋﻨﺩ ﻗﻴﺎﻡ ﺃﺤﺩ ﺍﻟﺴﺠﻼﺕ ﺒﻘﺒﻭل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺃﻭ ﺭﻓﻀﻬﺎ‪ ،‬ﻴﺘﻡ ﻗﺒﻭل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ‬
‫ﺃﻭ ﺭﻓﻀﻬﺎ ﻓﻲ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﻪ ﻓﻲ ﺍﻟﻌﻼﻗﺔ‪ ..‬ﻤﺜﻼ‪ :‬ﻟﻭ ﺘـﻡ ﻗﺒـﻭل‬
‫ﺘﻐﻴﻴﺭﺍﺕ ﺴﺠل ﻓﻲ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ‪ ،‬ﻴﺘﻡ ﻗﺒﻭل ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻋﻠﻰ ﺍﻟﺘﻭﺍﻟﻲ ﻟﻜل‬
‫ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻜﺘﺏ ﻫﺫﺍ ﺍﻟﻤﺅﻟﻑ ﻓﻲ ﺠﺩﻭل ﺍﻟﻜﺘﺏ‪.‬‬

‫ﻗﺎﻋﺩﺓ ﺍﻟﺤﺫﻑ ‪:DeleteRule‬‬


‫ﻀ‪‬ﺢ ﺍﻟﻔﻌل ﺍﻟﺫﻱ ﺴﻴﺘﻡ‪ ‬ﺍﺘﺨﺎﺫﻩ ﻤﻊ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻔﺭﻋﻴﺔ‪ ،‬ﻋﻨﺩ ﺤﺫﻑ ﺍﻟﺴﺠل ﺍﻟﺭﺌﻴﺴﻲ ﺍﻟﺫﻱ‬
‫ﺘﻭ ‪‬‬
‫ﺘﻨﺘﻤﻲ ﺇﻟﻴﻪ‪ ،‬ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ Rule‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﺘﺭﻙ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻔﺭﻋﻴﺔ ﻜﻤﺎ ﻫﻲ ﺒﺩﻭﻥ ﺘﻐﻴﻴﺭ‪ ،‬ﻟﻜﻥ ﻫﺫﺍ ﻗﺩ ﻴﺅﺩﻱ ﺇﻟـﻰ‬ ‫‪None‬‬
‫ﺤﺩﻭﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ،‬ﻷﻥ ﺤﺫﻑ ﺃﻭ ﺘﻌـﺩﻴل ﺍﻟﺴـﺠل ﺍﻟﺭﺌﻴﺴـﻲ‬
‫ﺴﻴﻜﺴﺭ ﻗﻴﺩ ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺜﺎﻨﻭﻱ‪ ،‬ﻭﻴﺘﺭﻙ ﺒﻌﺽ ﺍﻟﺴـﺠﻼﺕ ﻓـﻲ ﺍﻟﺠـﺩﻭل‬
‫ﺍﻟﺜﺎﻨﻭﻱ ﺘﺸﻴﺭ ﺇﻟﻰ ﺴﺠل ﻏﻴﺭ ﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻟﺭﺌﻴﺴﻲ‪.‬‬
‫ﻴﺅﺩ‪‬ﻱ ﺤﺫﻑ ﺃﻭ ﺘﻌﺩﻴل ﺍﻟﺴﺠلّ ﺍﻟﺭﺌﻴﺴﻲ ﺇﻟﻰ ﺤـﺫﻑ ﺃﻭ ﺘﻌـﺩﻴل ﻜـلّ‬ ‫‪Cascade‬‬
‫ﺍﻟﺴﺠﻼﺕ ﺍﻟﺜﺎﻨﻭﻴ‪‬ﺔ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﻪ‪ ..‬ﻓﻤﺜﻼ‪ ،‬ﺴﻴﺅﺩ‪‬ﻱ ﺤﺫﻑ ﺃﺤﺩ ﺍﻟﻤـﺅﻟﻔﻴﻥ‬
‫ﻤﻥ ﺠﺩﻭل ﺍﻟﻤﺅﻟﻔﻴﻥ‪ ،‬ﺇﻟﻰ ﺤﺫﻑ ﻜلّ ﻜﺘﺒﻪ ﻤﻥ ﺠﺩﻭل ﺍﻟﻜﺘﺏ‪.‬‬
‫‪٣٣٤‬‬
‫‪ SetDefault‬ﻴﺅﺩ‪‬ﻱ ﺤﺫﻑ ﺃﻭ ﺘﻌﺩﻴل ﺍﻟﺴﺠلّ ﺍﻟﺭﺌﻴﺴﻲ ﺇﻟﻰ ﻭﻀﻊ ﺍﻟﻘـﻴﻡ ﺍﻻﻓﺘﺭﺍﻀـﻴ‪‬ﺔ‬
‫ﻓﻲ ﺤﻘﻭل ﺍﻟﻤﻔﺘﺎﺡ ﺍﻟﺜﺎﻨﻭﻱ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﻪ‪.‬‬
‫ﻴﺅﺩ‪‬ﻱ ﺤﺫﻑ ﺃﻭ ﺘﻌﺩﻴل ﺍﻟﺴﺠلّ ﺍﻟﺭﺌﻴﺴﻲ ﺇﻟﻰ ﺇﻓـﺭﺍﻍ ﺤﻘـﻭل ﺍﻟﻤﻔﺘـﺎﺡ‬ ‫‪SetNull‬‬
‫ﺍﻟﺜﺎﻨﻭﻱ ﺍﻟﻤﺭﺘﺒﻁﺔ ﻟﺘﺼﻴﺭ ﺒﻬﺎ ﺍﻟﻘﻴﻤﺔ ‪.DbNull‬‬

‫ﻗﺎﻋﺩﺓ ﺍﻟﺘﺤﺩﻴﺙ ‪:UpdateRule‬‬


‫ﺘﻭﻀ‪‬ﺢ ﺍﻟﻔﻌل ﺍﻟﺫﻱ ﻴﺘﻡ‪ ‬ﺍﺘﺨﺎﺫﻩ ﻤﻊ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻔﺭﻋﻴﺔ‪ ،‬ﻋﻨﺩ ﺘﻐﻴﻴﺭ ﻗﻴﻡ ﺍﻟﺴـﺠل ﺍﻟﺭﺌﻴﺴـﻲ‬
‫ﺍﻟﺫﻱ ﺘﻨﺘﻤﻲ ﺇﻟﻴﻪ‪ ،‬ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ Rule‬ﺍﻟﺘـﻲ ﺘﻌﺭﻓﻨـﺎ ﻋﻠﻴﻬـﺎ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫ﺍﻟﺴﺎﺒﻘﺔ‪.‬‬

‫‪٣٣٥‬‬
‫‪-١٣-‬‬
‫ﻋـﺭﻭﺽ ﺍﻟﺒﻴـﺎﻨـﺎﺕ‬
‫‪Data Views‬‬

‫ﺘﺘﻴﺢ ﻟﻙ ﺍﻟﻌﺭﻭﺽ ‪ View‬ﻋﺭﺽ ﺒﻌﺽ ﺃﻭ ﻜل ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺒﺎﻟﺘﺭﺘﻴﺏ ﺍﻟﺫﻱ ﺘﺭﻴـﺩﻩ‪ ،‬ﺩﻭﻥ‬
‫ﺍﻟﺘﺄﺜﻴﺭ ﻋﻠﻰ ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ‪ ..‬ﻫﺫﺍ ﻴﻤﻨﺤﻙ ﻤﺭﻭﻨﺔ ﻋﺎﻟﻴـﺔ ﻋﻨـﺩ ﻋـﺭﺽ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﻟﻠﻤﺴﺘﺨﺩﻡ‪ ،‬ﺩﻭﻥ ﺍﻟﺤﺎﺠﺔ ﺇﻟﻰ ﺇﻋﺎﺩﺓ ﺇﺭﺴﺎل ﺍﺴﺘﻌﻼﻤﺎﺕ ﻤﺨﺘﻠﻔﺔ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﻓﻜﺭﺓ ﺍﻟﻌﺭﺽ ﺒﺴﻴﻁﺔ‪ ،‬ﻓﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻓﻬﺭﺱ ‪ Index‬ﻴﺸـﻴﺭ ﺇﻟـﻰ ﺴـﺠﻼﺕ‬
‫ﺍﻟﺠﺩﻭل‪ ،‬ﻭﻻ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺴﺠﻼﺕ ﻨﻔﺴﻬﺎ‪ ..‬ﻫﺫﺍ ﻴﺠﻌل ﻜـﺎﺌﻥ ﺍﻟﻌـﺭﺽ ﺴـﺭﻴﻌﺎ ﻓـﻲ ﺃﺩﺍﺀ‬
‫ﻋﻤﻠﻴﺎﺕ ﺍﻟﺘﺭﺸﻴﺢ ‪ Filtering‬ﻭﺍﻟﺘﺭﺘﻴﺏ ‪ Sorting‬ﻭﺍﻟﺒﺤـﺙ ‪ ،Searching‬ﺩﻭﻥ ﺃﻥ ﻴﺴـﺘﻬﻠﻙ‬
‫ﻤﺴﺎﺤﺔ ﻜﺒﻴﺭﺓ ﻓﻲ ﺍﻟﺫﺍﻜﺭﺓ!‬
‫ﻻﺤﻅ ﺃﻥ ﻓﻬﺭﺱ ﺍﻟﺴﺠﻼﺕ ﻴﺘﻡ ﺇﻨﺸﺎﺅﻩ ﻋﻨﺩ ﺇﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ‪ ،‬ﻭﻴﻌﺎﺩ ﺇﻨﺸﺎﺅﻩ ﻤﺭﺓ ﺃﺨﺭﻯ ﺇﺫﺍ‬
‫ﺘﻡ ﺘﻐﻴﻴﺭ ﻁﺭﻴﻘﺔ ﺍﻟﺘﺭﺘﻴﺏ ﺃﻭ ﺍﻟﺘﺭﺸﻴﺢ‪ ..‬ﻟﺫﺍ ﻤـﻥ ﺍﻷﻓﻀـل ﺃﻥ ﺘﺤـﺩﺩ ﻤﻭﺍﺼـﻔﺎﺕ ﺍﻟﺘﺭﺘﻴـﺏ‬
‫ﻭﺍﻟﺘﺭﺸﻴﺢ ﻋﻨﺩ ﺇﻨﺸﺎﺀ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ﻟﺘﻭﻓﺭ ﻋﻠﻰ ﻨﻔﺴﻙ ﺍﻟﻭﻗﺕ ﺍﻟﻀـﺎﺌﻊ ﻓـﻲ ﺇﻋـﺎﺩﺓ ﺇﻨﺸـﺎﺀ‬
‫ﺍﻟﻔﻬﺭﺱ‪.‬‬

‫ﻭﻓﻲ ﻫﺫﺍ ﺍﻟﻔﺼل ﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﻲ ﺘﺘﻴﺢ ﻟﻨﺎ ﺇﻨﺸﺎﺀ ﺍﻟﻌﺭﻭﺽ ﻭﺍﻟﺘﻌﺎﻤل ﻤﻌﻬﺎ‪.‬‬

‫‪٣٣٦‬‬
‫ﻭﺍﺠﻬﺔ ﻗﺎﺌﻤﺔ ﺍﻟﺭﺒﻁ‬
‫‪IBindingList Interface‬‬

‫ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺘﺭﺙ ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ‪ ،IList‬ﻭﻫﻲ ﺘﻘﺩﻡ ﺍﻟﻭﺴﺎﺌل ﺍﻟﻼﺯﻤﺔ ﻟﻠﺘﻌﺎﻤـل ﻤـﻊ ﻤﺼـﺩﺭ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ Data Source‬ﻤﻥ ﺨﻼل ﺃﺩﻭﺍﺕ ﺭﺒﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪.Data-Bound Controls‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻟﺴﻤﺎﺡ ﺒﺎﻟﺘﺤﺭﻴﺭ ‪:AllowEdit‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﻤﻥ ﺍﻟﻤﻤﻜﻥ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﺃﻱ ﻋﻨﺼﺭ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ‪.‬‬

‫ﺍﻟﺴﻤﺎﺡ ﺒﺎﻟﺠﺩﻴﺩ ‪:AllowNew‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﻤﻥ ﺍﻟﻤﻤﻜﻥ ﺇﻀﺎﻓﺔ ﻋﻨﺼﺭ ﺠﺩﻴﺩ ﺇﻟﻰ ﺍﻟﻘﺎﺌﻤـﺔ ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪.AddNew‬‬

‫ﺍﻟﺴﻤﺎﺡ ﺒﺎﻟﺤﺫﻑ ‪:AllowRemove‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﻤﻥ ﺍﻟﻤﻤﻜﻥ ﺤﺫﻑ ﻋﻨﺼﺭ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ‪Remove‬‬
‫ﻭ ‪.RemoveAt‬‬

‫ﺘﺩﻋﻡ ﺍﻟﺘﺭﺘﻴﺏ ‪:SupportsSorting‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﻘﺎﺌﻤﺔ ﺘﺴﻤﺢ ﺒﺘﺭﺘﻴﺏ ﻋﻨﺎﺼﺭﻫﺎ‪ ..‬ﻭﺇﺫﺍ ﻜﺎﻨﺕ ﻗﻴﻤﺔ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪ ،false‬ﻓﺴﺘﺅﺩﻱ ﻤﺤﺎﻭﻟﺔ ﺍﺴﺘﺨﺩﺍﻡ ﺃﻱ ﻤﻥ ﺨﺼﺎﺌﺹ ﺍﻟﺘﺭﺘﻴﺏ ﺇﻟﻰ ﺤﺩﻭﺙ ﺨﻁﺄ ﻤﻥ ﺍﻟﻨﻭﻉ‬
‫‪.NotSupportedException‬‬

‫‪٣٣٧‬‬
‫ﺘﺩﻋﻡ ﺍﻟﺒﺤﺙ ‪:SupportsSearching‬‬
‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﻘﺎﺌﻤﺔ ﺘﺘﻴﺢ ﺍﻟﺒﺤﺙ ﻓﻲ ﻋﻨﺎﺼﺭﻫﺎ‪ ،‬ﻭﺘﺘﻴﺢ ﺘﺭﺘﻴﺒﻬﺎ‪.‬‬

‫ﻫل ﻫﻲ ﻤﺭﺘﺒﺔ ‪:IsSorted‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻨﺕ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ ﻤﺭﺘﺒﺔ‪.‬‬

‫ﺍﺘﺠﺎﻩ ﺍﻟﺘﺭﺘﻴﺏ ‪:SortDirection‬‬


‫ﺘﻭﻀﺢ ﺍﺘﺠﺎﻩ ﺘﺭﺘﻴﺏ ﺍﻟﻘﺎﺌﻤﺔ‪ ،‬ﻭﻫﻲ ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗﻴﻤﺘـﻲ ﺍﻟﻤـﺭﻗﻡ ‪ListSortDirection‬‬
‫ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬
‫ﺘﺭﺘﻴﺏ ﺘﺼﺎﻋﺩﻱ‪.‬‬ ‫‪Ascending‬‬
‫‪ Descending‬ﺘﺭﺘﻴﺏ ﺘﻨﺎﺯﻟﻲ‪.‬‬

‫ﺨﺎﺼﻴﺔ ﺍﻟﺘﺭﺘﻴﺏ ‪:SortProperty‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ﻤﻥ ﻨﻭﻉ ﻓﺌـﺔ ﻭﺍﺼـﻑ ﺍﻟﺨﺎﺼـﻴﺔ ‪ ،PropertyDescriptor Class‬ﻭﻫـﻭ‬
‫ﻴﺤﺘﻭﻱ ﺍﻟﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﻼﺯﻤﺔ ﻟﻤﻌﺭﻓﺔ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻓﻲ ﺘﺭﺘﻴﺏ ﻋﻨﺎﺼـﺭ ﺍﻟﻘﺎﺌﻤـﺔ‪،‬‬
‫ﻭﺫﻟﻙ ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﻘﺎﺌﻤﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻜﺎﺌﻨﺎﺕ ‪ Objects‬ﺘﻤﺘﻠﻙ ﺨﺼﺎﺌﺹ ﻤﺨﺘﻠﻔﺔ‪ ..‬ﻤـﺜﻼ‬
‫ﻋﻨﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻜﻠﺌﻥ ﻋﺭﺽ ﻓﻴﻪ ﺴﺠﻼﺕ ﺃﺤﺩ ﺍﻟﺠﺩﺍﻭل‪ ،‬ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺤﺩﺩ ﺃﺤـﺩ ﺍﻟﻘـﻭل‬
‫ﻟﺘﺭﺘﻴﺏ ﺍﻟﺴﺠﻼﺕ ﺘﺒﻌﺎ ﻟﻪ‪.‬‬

‫ﺘﺩﻋﻡ ﺍﻟﺘﻨﺒﻴﻪ ﻋﻥ ﺍﻟﺘﻐﻴﻴﺭ ‪:SupportsChangeNotification‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﻘﺎﺌﻤﺔ ﺘﻁﻠﻕ ﺍﻟﺤﺩﺙ ‪ ListChanged‬ﻋﻨـﺩ ﺤـﺩﻭﺙ ﺘﻐﻴـﺭ ﻓـﻲ‬
‫ﻋﻨﺎﺼﺭﻫﺎ‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪٣٣٨‬‬
‫ﺇﻀﺎﻓﺔ ﻓﻬﺭﺱ ‪:AddIndex‬‬
‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻜﺎﺌﻨﺎ ﻤـﻥ ﺍﻟﻨـﻭﻉ ‪ ،PropertyDescriptor‬ﻟﺘﻘـﻭﻡ ﺒﺈﻀـﺎﻓﺔ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﻲ ﻴﺸﻴﺭ ﺇﻟﻴﻬﺎ‪ ،‬ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻔﻬﺎﺭﺱ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻓﻲ ﺍﻟﺒﺤﺙ ﻓـﻲ ﻋﻨﺎﺼـﺭ‬
‫ﺍﻟﻘﺎﺌﻤﺔ‪.‬‬

‫ﺤﺫﻑ ﻓﻬﺭﺱ ‪:RemoveIndex‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻜﺎﺌﻨﺎ ﻤـﻥ ﺍﻟﻨـﻭﻉ ‪ ،PropertyDescriptor‬ﻟﺘﻘـﻭﻡ ﺒﺤـﺫﻑ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﻲ ﻴﺸﻴﺭ ﺇﻟﻴﻬﺎ‪ ،‬ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﻟﻔﻬﺎﺭﺱ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻓﻲ ﺍﻟﺒﺤﺙ ﻓـﻲ ﻋﻨﺎﺼـﺭ‬
‫ﺍﻟﻘﺎﺌﻤﺔ‪.‬‬

‫ﺇﻀﺎﻓﺔ ﻋﻨﺼﺭ ﺠﺩﻴﺩ ‪:AddNew‬‬


‫ﻻ ﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺃﻴﺔ ﻤﻌﺎﻤﻼﺕ‪ ،‬ﻭﻟﻜﻨﻬﺎ ﺘﻨﺸﺊ ﻋﻨﺼﺭﺍ ﺠﺩﻴﺩﺍ ﻤﻥ ﻨﻔﺱ ﻨﻭﻉ ﻋﻨﺎﺼﺭ‬
‫ﺍﻟﻘﺎﺌﻤﺔ‪ ،‬ﻭﺘﻀﻴﻔﻪ ﺇﻟﻴﻬﺎ‪ ،‬ﻭﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻜﺎﺌﻨﺎ ‪ Object‬ﻴﺤﻤل ﻤﺭﺠﻌـﺎ ﺇﻟـﻰ ﻫـﺫﺍ ﺍﻟﻌﻨﺼـﺭ‬
‫ﺍﻟﻤﻀﺎﻑ‪.‬‬

‫ﺘﻨﻔﻴﺫ ﺍﻟﺘﺭﺘﻴﺏ ‪:ApplySort‬‬


‫ﺘﻘﻭﻡ ﺒﺘﺭﺘﻴﺏ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ‪ ،‬ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ‪:‬‬
‫‪ -‬ﻜﺎﺌﻥ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،PropertyDescriptor‬ﻴﻭﻀﺢ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﻲ ﺴـﻴﺘﻡ ﺘﺭﺘﻴـﺏ‬
‫ﺍﻟﻌﻨﺎﺼﺭ ﻋﻠﻰ ﺃﺴﺎﺴﻬﺎ‪.‬‬
‫‪ -‬ﺇﺤﺩﻯ ﻗﻴﻤﺘﻲ ﺍﻟﻤﺭﻗﻡ ‪ ListSortDirection‬ﺘﻭﻀﺢ ﺍﺘﺠﺎﻩ ﺍﻟﺘﺭﺘﻴﺏ‪.‬‬

‫ﺤﺫﻑ ﺍﻟﺘﺭﺘﻴﺏ ‪:RemoveSort‬‬


‫ﺘﻌﻴﺩ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ ﺇﻟﻰ ﺘﺭﺘﻴﺒﻬﺎ ﺍﻷﺼﻠﻲ ﺍﻟﺫﻱ ﻜﺎﻨﺕ ﻋﻠﻴـﻪ ﻗﺒـل ﺍﺴـﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪.ApplySort‬‬

‫‪٣٣٩‬‬
‫ﺒﺤﺙ ‪:Find‬‬
‫ﺘﻘﻭﻡ ﺒﺎﻟﺒﺤﺙ ﻓﻲ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ‪ ،‬ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ‪:‬‬
‫‪ -‬ﻜﺎﺌﻥ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،PropertyDescriptor‬ﻴﻭﻀﺢ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﻲ ﺴـﻴﺘﻡ ﺍﻟﺒﺤـﺙ‬
‫ﻋﻥ ﻗﻴﻤﺘﻬﺎ‪.‬‬
‫‪ -‬ﻜﺎﺌﻥ ‪ Object‬ﻴﺤﻤل ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﺭﺍﺩ ﺍﻟﺒﺤﺙ ﻋﻨﻬﺎ‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺤﺩﺙ ﺍﻟﺘﺎﻟﻲ‪:‬‬

‫ﺍﻟﻘﺎﺌﻤﺔ ﺘﻐﻴﺭﺕ ‪:ListChanged‬‬


‫ﻴﻨﻁﻠﻕ ﻋﻨﺩ ﺤﺩﻭﺙ ﺘﻐﻴﺭ ﻓﻲ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨﻭﻉ‬
‫‪ ،ListChangedEventArgs‬ﻭﻫﻭ ﻴﻤﺘﻠﻙ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ ‪ListChangedType‬‬ ‫‪ListChangedType‬‬


‫ﺍﻟﺘﻲ ﺘﻭﻀﺢ ﻨﻭﻉ ﺍﻟﺘﻐﻴﻴﺭ ﺍﻟﺫﻱ ﺤﺩﺙ‪ ،‬ﻭﻫﻲ‪:‬‬
‫‪ :Reset -‬ﺤﺩﺙ ﺘﻐﻴﺭ ﻓـﻲ ﻋـﺩﺩ ﻜﺒﻴـﺭ ﻤـﻥ‬
‫ﺍﻟﻌﻨﺎﺼﺭ‪.‬‬
‫‪ :ItemAdded -‬ﺇﻀﺎﻓﺔ ﻋﻨﺼﺭ‪.‬‬
‫‪ :ItemDeleted -‬ﺤﺫﻑ ﻋﻨﺼﺭ‪.‬‬
‫‪ :ItemMoved -‬ﺘﻐﻴﺭ ﻤﻭﻀﻊ ﻋﻨﺼﺭ‪.‬‬
‫‪ :ItemChanged -‬ﺘﻐﻴﺭﺕ ﻗﻴﻤﺔ ﻋﻨﺼﺭ‪.‬‬
‫‪ :PropertyDescriptorAdded -‬ﺇﻀـــﺎﻓﺔ‬
‫ﻭﺍﺼﻑ ﺨﺎﺼﻴﺔ‪.‬‬
‫‪ :PropertyDescriptorDeleted -‬ﺤـــﺫﻑ‬
‫ﻭﺍﺼﻑ ﺨﺎﺼﻴﺔ‪.‬‬
‫ـﺭ‬
‫‪ :PropertyDescriptorChanged -‬ﺘﻐﻴﻴــ‬
‫ﻭﺍﺼﻑ ﺨﺎﺼﻴﺔ‪.‬‬
‫‪٣٤٠‬‬
‫ﺘﻌﻴﺩ ﺭﻗﻡ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺫﻱ ﺘﻐﻴﺭ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ‪.‬‬ ‫‪NewIndex‬‬
‫ﺘﻌﻴﺩ ﺭﻗﻡ ﺍﻟﻌﻨﺼﺭ ﻗﺒل ﺘﻐﻴﻴﺭ ﻤﻭﻀﻌﻪ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ‪.‬‬ ‫‪OldIndex‬‬
‫‪ PropertyDescriptor‬ﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ﻤـﻥ ﺍﻟﻨـﻭﻉ ‪،PropertyDescriptor‬‬
‫ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻭﺍﺼﻑ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻭﺫﻟـﻙ ﺇﺫﺍ ﻜـﺎﻥ‬
‫ﺍﻟﺘﻐﻴﻴﺭ ﻗﺩ ﺤﺩﺙ ﻟﻭﺍﺼﻑ ﺇﺤﺩﻯ ﺍﻟﺨﺼﺎﺌﺹ‪.‬‬

‫‪٣٤١‬‬
‫ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ‬
‫‪ITypedList Interface‬‬

‫ﺘﺤﺼل ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﻋﻠﻰ ﺨﺼﺎﺌﺹ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺫﻱ ﺴﻴﺘﻡ ﺍﻻﺭﺘﺒﺎﻁ ‪ Binding‬ﺒﻪ‪ ،‬ﻭﻫﻲ ﺘﻤﺘﻠﻙ‬
‫ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﻤﻌﺭﻓﺔ ﺍﺴﻡ ﺍﻟﻘﺎﺌﻤﺔ ‪:GetListName‬‬


‫ﺘﻌﻴﺩ ﺍﺴﻡ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺘﻲ ﺴﻴﺘﻡ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻬﺎ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺨﺼﺎﺌﺹ ﺍﻟﻌﻨﺼﺭ ‪:GetItemProperties‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﺼﻔﻭﻓﺔ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ PropertyDescriptor‬ﺒﻬﺎ ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﺘﻲ‬
‫ﺴــﻴﺘﻡ ﺍﻻﺭﺘﺒــﺎﻁ ﺒﻬــﺎ ﻓــﻲ ﺍﻟﻘﺎﺌﻤــﺔ‪ ،‬ﻟﺘﻌﻴــﺩ ﺇﻟﻴــﻙ ﻤﺠﻤﻭﻋــﺔ ﻤــﻥ ﺍﻟﻨــﻭﻉ‬
‫‪ ،PropertyDescriptorCollection‬ﺒﻬﺎ ﺨﺼﺎﺌﺹ ﻫﺫﻩ ﺍﻟﻜﺎﺌﻨﺎﺕ‪ ..‬ﻻﺤﻅ ﺃﻨـﻙ ﻟـﻭ‬
‫ﺃﺭﺴﻠﺕ ‪ null‬ﻜﻤﻌﺎﻤل‪ ،‬ﻓﺴﺘﺤﺼل ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﺒﻬﺎ ﻋﻨﺼﺭ ﻭﺍﺤﺩ ﻓﻘﻁ‪ ،‬ﻭﻫﻭ ﻭﺍﺼـﻑ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﻟﻠﻘﺎﺌﻤﺔ ﻨﻔﺴﻬﺎ‪.‬‬

‫‪٣٤٢‬‬
‫ﻓﺌـﺔ ﻤﺩﻴـﺭ ﺍﻟﻌـﺭﺽ‬
‫‪DataViewManager Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜـل ﺍﻟـﻭﺍﺠﻬﺘﻴﻥ ‪ IBindingList‬ﻭ ‪ ،ITypedList‬ﻜﻤـﺎ ﺃﻨﻬـﺎ ﺘـﺭﺙ ﺍﻟﻔﺌـﺔ‬


‫‪ ،MarshalByValueComponent‬ﻤﻤﺎ ﻴﺘﻴﺢ ﻟﻙ ﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﺼﻴﻨﻴﺔ ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤـﻭﺫﺝ‪،‬‬
‫ﻭﺇﻥ ﻜﺎﻥ ﻋﻠﻴﻙ ﺃﻥ ﺘﻀﻴﻔﻬﺎ ﺃﻭﻻ ﺇﻟﻰ ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ‪.‬‬
‫ﻭﺘﺘﻴﺢ ﻟﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻟﻙ ﺍﻟﺘﺤﻜﻡ ﻓﻲ ﻜﻴﻔﻴﺔ ﻋﺭﺽ ﺴﺠﻼﺕ ﻜل ﺠﺩﺍﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﻴﻤﻜﻥ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻤـﺩﻴﺭ ﺍﻟﻌـﺭﺽ ﻤـﻥ ﻤﺠﻤﻭﻋـﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ،‬ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼـ ‪‬ﻴ‪‬ﺔ‬
‫‪ ،DefaultViewManager‬ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫;‪var DVM = Ds.DefaultViewManager‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺼﻴﻐﺘﺎﻥ‪:‬‬
‫‪ -١‬ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﻴﺘﻌﺎﻤل ﻤﻌﻬﺎ ﻤﺩﻴﺭ ﺍﻟﻌﺭﺽ‪ ..‬ﻤﺜﺎل‪:‬‬
‫;)‪var DVM = new DataViewManager (Ds‬‬

‫ﻭﻴﻤﺘﻠﻙ ﻤﺩﻴﺭ ﺍﻟﻌﺭﺽ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataSet‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴ‪‬ﺭ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﻴﺘﻌﺎﻤل ﻤﻌﻬﺎ ﻤﺩﻴﺭ ﺍﻟﻌﺭﺽ‪.‬‬

‫ﺇﻋﺩﺍﺩﺍﺕ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataViewSettings‬‬


‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻌﺭﺽ ‪ ،DataViewSettingCollection‬ﻭﻫﻲ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،ICollection‬ﻭﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺇﻋـﺩﺍﺩﺍﺕ ﺍﻟﻌـﺭﺽ ﺍﻟﺨﺎﺼـﺔ ﺒﺠـﺩﺍﻭل‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻜل ﻋﻨﺼﺭ ﻤﻥ ﻋﻨﺎﺼﺭﻫﺎ ﻤﻥ ﻨـﻭﻉ ﺍﻟﻔﺌـﺔ ‪DataViewSetting‬‬
‫ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ‪.‬‬
‫‪٣٤٣‬‬
‫ﻻﺤﻅ ﺃﻥ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ‪ ،‬ﻟﻬﺫﺍ ﻻ ﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺔ ﺃﻴﺔ ﻋﻨﺎﺼﺭ ﺇﻟﻴﻬﺎ‪ ..‬ﻟﻜﻥ ﻜل‬
‫ﺠﺩﻭل ﻴﻀﺎﻑ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻴﻀﻴﻑ ﻋﻨﺼﺭ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻌﺭﺽ ﺍﻟﺨﺎﺹ ﺒﻪ ﺇﻟﻰ‬
‫ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺘﻠﻘﺎﺌﻴﺎ‪.‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻜﺎﺌﻥ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﺠﺩﻭل ﻤﻥ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﺇﻤﺎ ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺭﻗـﻡ‬
‫ﺍﻟﺠﺩﻭل ﺃﻭ ﺍﺴﻤﻪ ﺃﻭ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ‪ DataTable‬ﺍﻟﺫﻱ ﻴﺸﻴﺭ ﺇﻟﻴﻪ‪ ..‬ﻤﺜﺎل‪:‬‬
‫= ‪DataViewSetting Vs‬‬
‫;]‪Ds.DefaultViewManager.DataViewSettings[0‬‬

‫ﻭﻴﻤﺘﻠﻙ ﻤﺩﻴﺭ ﺍﻟﻌﺭﺽ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺇﻨﺸﺎﺀ ﻋﺭﺽ ﺒﻴﺎﻨﺎﺕ ‪:CreateDataView‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ‪ DataTable‬ﻟﺘﻨﺸﺊ ﻋﺭﻀـﺎ ‪ View‬ﻟﺴـﺠﻼﺘﻪ‪،‬‬
‫ﺘﺒﻌﺎ ﻟﻺﻋﺩﺍﺩﺍﺕ ﺍﻟﺨﺎﺼﺔ ﺒﻬﺫﺍ ﺍﻟﺠﺩﻭل ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻹﻋﺩﺍﺩﺍﺕ ‪..DataViewSettings‬‬
‫ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ‪ DataView‬ﺍﻟﺫﻱ ﺘﻡ ﺇﻨﺸﺎﺅﻩ‪ ..‬ﻤﺜﺎل‪:‬‬
‫;]"‪DataTable TblAuthors = Ds.Tables["Authors‬‬
‫= ‪DataView DV‬‬
‫;)‪Ds.DefaultViewManager.CreateDataView(TblAuthors‬‬

‫‪٣٤٤‬‬
‫ﻓﺌﺔ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻌﺭﺽ ‪DataViewSetting Class‬‬

‫ﺘﺤﺘﻭﻱ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻋﻠﻰ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻌﺭﺽ ﺍﻻﻓﺘﺭﺍﻀ ‪‬ﻴ‪‬ﺔ ﺍﻟﺘﻲ ﻴـﺘﻡ‪ ‬ﺍﺴـﺘﺨﺩﺍﻤﻬﺎ ﻤـﻊ ﺍﻟﺠـﺩﺍﻭل‬
‫ﺍﻟﻤﻌﺭﻭﻀﺔ‪.‬‬
‫ﻭﻟﻴﺱ ﻟﻬﺫﻩ ﺍﻟﻔﺌﺔ ﺤﺩﺙ ﺇﻨﺸﺎﺀ‪ ،‬ﻭﻟﻜﻥ ﻴﻤﻜﻨﻙ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻨﺴﺨﺔ ﻤﻨﻬﺎ ﺨﺎﺼﺔ ﺒﺄﺤﺩ ﺍﻟﺠـﺩﺍﻭل‬
‫ﻤﻥ ﺨﻼل ﻤﺠﻤﻭﻋﺔ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻟﻌﺭﺽ ‪ DataViewSettings‬ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫= ‪DataViewSetting Vs‬‬
‫;]"‪Ds.DefaultViewManager.DataViewSettings["Authors‬‬
‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ‪:‬‬

‫‪:Table‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﺠﺩﻭل ‪ DataTable‬ﺍﻟﺫﻱ ﻴﻨﺘﻤﻲ ﺇﻟﻴﻪ ﻜﺎﺌﻥ ﺍﻹﻋﺩﺍﺩﺍﺕ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﻤﺩﻴﺭ ﺍﻟﻌﺭﺽ ‪:DataViewManager‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻤﺩﻴﺭ ﺍﻟﻌﺭﺽ ‪ DataViewManager‬ﺍﻟـﺫﻱ ﻴﺤﺘـﻭﻱ ﻜـﺎﺌﻥ ﺍﻹﻋـﺩﺍﺩﺍﺕ‬
‫ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﻤﺭﺸﺢ ﺤﺎﻟﺔ ﺍﻟﺼﻔﻭﻑ ‪:RowStateFilter‬‬


‫ﺘﺤﺩﺩ ﺤﺎﻟﺔ ﺍﻟﺼـﻔﻭﻑ ﺍﻟﺘـﻲ ﺘﺭﻴـﺩ ﻋﺭﻀـﻬﺎ‪ ،‬ﻭﻫـﻲ ﺘﺄﺨـﺫ ﺇﺤـﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ‬
‫‪ DataViewRowState‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺃﻱ ﺤﺎﻟﺔ‪.‬‬ ‫‪None‬‬


‫ﻋﺭﺽ ﺍﻟﺼﻔﻭﻑ ﺍﻟﺘﻲ ﻟﻡ ﺘﺘﻐﻴﺭ‪.‬‬ ‫‪Unchanged‬‬
‫ﻋﺭﺽ ﺍﻟﺼﻔﻭﻑ ﺍﻟﻤﻀﺎﻓﺔ‪.‬‬ ‫‪Added‬‬
‫ﻋﺭﺽ ﺍﻟﺼﻔﻭﻑ ﺍﻟﻤﺤﺫﻭﻓﺔ‪.‬‬ ‫‪Deleted‬‬
‫ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ ﻟﻠﺼﻔﻭﻑ ‪.Original Version‬‬ ‫‪OriginalRows‬‬

‫‪٣٤٥‬‬
‫ﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ﻟﻠﺼﻔﻭﻑ ‪.Current Version‬‬ ‫‪CurrentRows‬‬
‫‪ ModifiedOriginal‬ﺍﻟﻨﺴﺨﺔ ﺍﻷﺼﻠﻴﺔ ﻟﻠﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﻡ ﺘﻌﺩﻴﻠﻬﺎ‪.‬‬
‫‪ ModifiedCurrent‬ﺍﻟﻨﺴﺨﺔ ﺍﻟﺤﺎﻟﻴﺔ ﻟﻠﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﻡ ﺘﻌﺩﻴﻠﻬﺎ‪.‬‬

‫ﻤﺭﺸﺢ ﺍﻟﺼﻔﻭﻑ ‪:RowFilter‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﻟﺸﺭﻁ ﺍﻟﺫﻱ ﻴﺘﻡ ﻋﻠﻰ ﺃﺴﺎﺴﻪ ﺍﺨﺘﻴﺎﺭ ﺍﻟﺴـﺠﻼﺕ ﺍﻟﺘـﻲ ﻴﻌﺭﻀـﻬﺎ ﻜـﺎﺌﻥ‬
‫ﺍﻟﻌﺭﺽ‪ ..‬ﻭﻴﺘﻡ ﺘﻜﻭﻴﻥ ﺍﻟﺸﺭﻁ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﺒﻨﻔﺱ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﻤﺴﺘﺨﺩﻤﺔ ﻓﻲ ﺘﻜـﻭﻴﻥ‬
‫ﺸﺭﻁ ﺍﻟﺨﺎﺼﻴﺔ ‪.DataRow.Expression‬‬
‫ﻭﺍﻟﻤﺭﺸﺢ ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﺽ ﺍﻟﻜﺘﺏ ﺍﻟﺘﻲ ﻴﻘل ﺜﻤﻨﻬﺎ ﻋﻥ ‪ ٥‬ﺠﻨﻴﻬﺎﺕ‪:‬‬
‫;"‪Vs.RowFilter = "Price < 5‬‬

‫ﺍﻟﺘﺭﺘﻴﺏ ‪:Sort‬‬
‫ﺘﺤﺩﺩ ﺍﻟﻨﺹ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﺘﺭﺘﻴﺏ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻌﺭﻭﻀﺔ‪ ،‬ﻭﻫﻭ ﻴﺘﻜﻭﻥ ﻤﻥ ﺍﺴـﻡ ﺍﻟﺤﻘـل‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﺍﻟﺘﺭﺘﻴﺏ‪ ،‬ﻤﺘﺒﻭﻋﺎ ﺒﺎﺘﺠﺎﻩ ﺍﻟﺘﺭﺘﻴﺏ‪ ..‬ﻤﺜﻼ‪ :‬ﻟﺘﺭﺘﻴﺏ ﺼﻔﻭﻑ ﺠـﺩﻭل ﺍﻟﻜﺘـﺏ‬
‫ﺘﻨﺎﺯﻟﻴﺎ ﻋﻠﻰ ﺤﺴﻴﺏ ﺍﺴﻡ ﺍﻟﻜﺘﺎﺏ‪ ،‬ﺍﺴﺘﺨﺩﻡ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﺎﻟﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪:‬‬
‫;"‪Vs.Sort = "Book DESC‬‬

‫ﺘﻁﺒﻴﻕ ﺍﻟﺘﺭﺘﻴﺏ ﺍﻻﻓﺘﺭﺍﻀﻲ ‪:ApplyDefaultSort‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،true‬ﻓﺴﻴﺘﻡ ﺘﻁﺒﻴﻕ ﺍﻟﺘﺭﺘﻴﺏ ﺍﻻﻓﺘﺭﺍﻀﻲ ﻟﻠﺼﻔﻭﻑ )ﻜﻤـﺎ‬
‫ﻓﻲ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ(‪ ..‬ﻭﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻫﻲ ‪ ،false‬ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟـﺔ‬
‫ﻴﺘﻡ ﺘﻁﺒﻴﻕ ﺍﻟﺘﺭﺘﻴﺏ ﺍﻟﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪.Sort‬‬

‫ﻭﻴﻌﺘﺒﺭ ﺍﻟﻤﺸﺭﻭﻉ ‪ 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‬‬

‫ﻭﺍﺼﻔﺎﺕ ﺍﻟﺘﺭﺘﻴﺏ ‪:SortDescriptions‬‬


‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ ﻤﻥ ﺍﻟﻨـﻭﻉ ‪ ،ListSortDescriptionCollection‬ﻭﻫـﻲ‬
‫ﺘﻤﺜل ﻭﺍﺠﻬـﺔ ﺍﻟﻘﺎﺌﻤـﺔ ‪ ،IList‬ﻭﻜـل ﻋﻨﺼـﺭ ﻤـﻥ ﻋﻨﺎﺼـﺭﻫﺎ ﻤـﻥ ﻨـﻭﻉ ﺍﻟﻔﺌـﺔ‬
‫‪ ،ListSortDescription‬ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ‪.‬‬

‫ﺘﺩﻋﻡ ﺍﻟﺘﺭﺘﻴﺏ ﺍﻟﻤﺘﻘﺩﻡ ‪:SupportsAdvancedSorting‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺴﺘﻁﻊ ﺘﺭﺘﻴﺏ ﺍﻟﺴﺠﻼﺕ ﺘﺒﻌﺎ ﻟﻘﻴﻡ ﺃﻜﺜﺭ ﻤﻥ ﻋﻤﻭﺩ‪.‬‬

‫ﺘﺩﻋﻡ ﺍﻟﺘﺭﺸﻴﺢ ‪:SupportsFiltering‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺴﻤﺢ ﺒﺎﻨﺘﻘﺎﺀ ﺒﻌﺽ ﺍﻟﺴﺠﻼﺕ ﺘﺒﻌﺎ ﻟﺸﺭﻁ ﻤﻌﻴﻥ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺇﺯﺍﻟﺔ ﺍﻟﻤﺭﺸﺢ ‪:RemoveFilter‬‬


‫ﺘﻠﻐﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺭﺸﻴﺢ‪ ،‬ﻭﺘﻌﻴﺩ ﻋﺭﺽ ﺠﻤﻴﻊ ﺍﻟﺴﺠﻼﺕ ﺒﺩﻭﻥ ﻤﺭﺍﻋﺎﺓ ﺸﺭﻁ ﺍﻟﺘﺭﺸﻴﺢ‪.‬‬

‫‪٣٥٠‬‬
‫ﻓﺌﺔ ﻭﺍﺼﻑ ﺘﺭﺘﻴﺏ ﺍﻟﻘﺎﺌﻤﺔ‬
‫‪ListSortDescription Class‬‬

‫ﺘﺤﺘﻭﻱ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻋﻠﻰ ﺍﻟﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﻼﺯﻤﺔ ﻟﺘﺭﺘﻴﺏ ﻋﻨﺎﺼﺭ ﻗﺎﺌﻤﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﻴﺴﺘﻘﺒل ﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻤﻌﺎﻤﻠﻴﻥ‪:‬‬
‫‪ -‬ﻭﺍﺼﻑ ﺍﻟﺨﺎﺼﻴﺔ ‪ PropertyDescriptor‬ﺍﻟﺘﻲ ﺴﻴﺘﻡ ﺍﻟﺘﺭﺘﻴﺏ ﻋﻠﻰ ﺃﺴﺎﺴﻬﺎ‪.‬‬
‫‪ -‬ﺇﺤﺩﻯ ﻗﻴﻤﺘﻲ ﺍﻟﻤﺭﻗﻡ ‪ ListSortDirection‬ﺍﻟﺘﻲ ﺘﻭﻀﺢ ﺍﺘﺠﺎﻩ ﺍﻟﺘﺭﺘﻴﺏ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﻭﺍﺼﻑ ﺍﻟﺨﺎﺼﻴﺔ ‪:PropertyDescriptor‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻭﺍﺼﻑ ﺍﻟﺨﺎﺼﻴﺔ ‪ PropertyDescriptor‬ﺍﻟﺘﻲ ﺴـﻴﺘﻡ ﺍﻟﺘﺭﺘﻴـﺏ ﻋﻠـﻰ‬
‫ﺃﺴﺎﺴﻬﺎ‪.‬‬

‫ﺍﺘﺠﺎﻩ ﺍﻟﺘﺭﺘﻴﺏ ‪:SortDirection‬‬


‫ﺘﻭﻀﺢ ﺍﺘﺠﺎﻩ ﺍﻟﺘﺭﺘﻴﺏ‪ ،‬ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻤﺘـﻲ ﺍﻟﻤـﺭﻗﻡ ‪ ListSortDirection‬ﻭﻗـﺩ‬
‫ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻬﻤﺎ ﺴﺎﺒﻘﺎ‪.‬‬

‫‪٣٥١‬‬
‫ﻓﺌﺔ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DataView Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺎﺕ ‪ IBindingListView‬ﻭ ‪ IBindingList‬ﻭ ‪ ،ITypedList‬ﻜﻤـﺎ‬


‫ﺃﻨﻬﺎ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ ،MarshalByValueComponent‬ﻤﻤﺎ ﻴﺘﻴﺢ ﻟﻙ ﺇﻀـﺎﻓﺘﻬﺎ ﺇﻟـﻰ ﺼـﻴﻨﻴﺔ‬
‫ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤﻭﺫﺝ‪ ،‬ﻭﺇﻥ ﻜﺎﻥ ﻋﻠﻴﻙ ﺃﻥ ﺘﻀﻴﻔﻬﺎ ﺃﻭﻻ ﺇﻟﻰ ﺼﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ‪.‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻜﺎﺌﻥ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻁﺭﻴﻘـﺔ ﻋـﺭﺽ ﻤﺨﺘﻠﻔـﺔ ﻟﺴـﺠﻼﺕ‬
‫ﺍﻟﺠﺩﻭل‪ ،‬ﺴﻭﺍﺀ ﺒﺘﺭﺘﻴﺒﻬﺎ ﺃﻭ ﺒﺎﺨﺘﻴﺎﺭ ﺠﺯﺀ ﻤﻥ ﺍﻟﺴﺠﻼﺕ ﺘﺒﻌﺎ ﻟﺸﺭﻁ ﻤﻌﻴﻥ‪ ..‬ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺭﺒـﻁ‬
‫ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ ﺒﺄﺩﻭﺍﺕ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﻤﺎ ﺴﻨﺭﻯ ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺘﺎﻟﻲ‪ ،‬ﻭﺒﻬﺫﺍ ﺘﺘﺤﻜﻡ ﻓـﻲ ﻁﺭﻴﻘـﺔ‬
‫ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺒﺭﻨﺎﻤﺠﻙ‪.‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺍﻟﺤﺼـﻭل ﻋﻠـﻰ ﻜـﺎﺌﻥ ﺍﻟﻌـﺭﺽ ﺍﻻﻓﺘﺭﺍﻀـﻲ ﻟﻠﺠـﺩﻭل ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪ DefaultView‬ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫;‪DataView Dv = TblAuthors.DefaultView‬‬
‫ﻭﻤﻥ ﺜﻡ ﻴﻤﻜﻨﻙ ﺘﻐﻴﻴﺭ ﺨﺼﺎﺌﺼﻪ ﻟﺘﻐﻴﻴﺭ ﻁﺭﻴﻘﺔ ﻋﺭﻀﻪ‪.‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻟﺠـﺩﻭل ‪ DataTable‬ﺍﻟـﺫﻱ ﺴـﻴﺘﻌﺎﻤل ﻤﻌـﻪ ﻜـﺎﺌﻥ‬
‫ﺍﻟﻌﺭﺽ‪.‬‬
‫‪ -٣‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ‪ ،‬ﻫﻲ ﺒﺎﻟﺘﺭﺘﻴﺏ‪:‬‬
‫‪ -‬ﻨﺹ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺸﺭﻁ ﺘﺭﺸﻴﺢ ﺍﻟﺴﺠﻼﺕ ‪.RowFilter‬‬
‫‪ -‬ﻨﺹ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺸﺭﻁ ﺘﺭﺘﻴﺏ ﺍﻟﺴﺠﻼﺕ ‪.Sort‬‬
‫‪ -‬ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ ،DataViewRowState‬ﺘﻭﻀﺢ ﺤﺎﻟﺔ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﻴﺘﻡ‬
‫ﻋﺭﻀﻬﺎ‪.‬‬
‫ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻨﺸﺊ ﻜﺎﺌﻥ ﻋﺭﺽ‪ ،‬ﻴﻌﺭﺽ ﻜﺘﺏ ﺘﻭﻓﻴﻕ ﺍﻟﺤﻜﻴﻡ ﻤﺭﺘﺒﺔ ﺘﺼﺎﻋﺩﻴﺎ ﻋﻠﻰ ﺤﺴـﺏ‬
‫ﺃﺴﻤﺎﺌﻬﺎ‪:‬‬

‫‪٣٥٢‬‬
‫‪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‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻓﺌﺔ ﺍﻟﻌﺭﺽ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺇﻀﺎﻓﺔ ﺴﺠل ﺠﺩﻴﺩ ‪:AddNew‬‬


‫ﺘﻀﻴﻑ ﺴﺠﻼ ﺠﺩﻴﺩﺍ ﺇﻟﻰ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ‪ ،‬ﺘﺤﺘﻭﻱ ﺨﺎﻨﺎﺘﻪ ﻋﻠﻰ ﺍﻟﻘـﻴﻡ ﺍﻻﻓﺘﺭﺍﻀـﻴﺔ ﻓـﻲ‬
‫ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﻟﻬﺎ ﻗﻴﻡ ﺍﻓﺘﺭﺍﻀﻴﺔ‪ ،‬ﻭﻋﻠﻰ ﺍﻟﻌﺩﻡ ‪ DbNull‬ﻓﻲ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺘﺴﻤﺢ ﺒﻬـﺫﺍ‪..‬‬
‫ﻻﺤﻅ ﺃﻥ‪ ‬ﻫﺫﺍ ﺍﻟﺴﺠلّ ﺴﻴﻀﺎﻑ ﺃﻴﻀﺎ ﺇﻟﻰ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ‪ ‬ﺃﻴﻀﺎ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻋﻨﺩ ﺍﻟﻘﻴﺎﻡ ﺒﻌﻤﻠﻴﺔ ﺍﻟﺘﺤﺩﻴﺙ ﺴﻴﺘﻡ ﺤﻔﻅﻪ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ..‬ﻫـﺫﺍ ﻴﺠﻌﻠـﻙ‬
‫ﻤﻁﻤﺌﻨﺎ ﺇﻟﻰ ﺃﻥ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺠﺩﻴﺩﺓ ﺍﻟﺘـﻲ ﻴﻀـﻴﻔﻬﺎ ﺍﻟﻤﺴـﺘﺨﺩﻡ ﺇﻟـﻰ ﺠـﺩﻭل ﻋـﺭﺽ‬
‫‪ DataGridView‬ﻤﺭﺘﺒﻁ ﺒﻜﺎﺌﻥ ﻋﺭﺽ‪ ،‬ﺴﺘﺘﻡ ﺇﻀﺎﻓﺘﻬﺎ ﺘﻠﻘﺎﺌﻴﺎ ﺇﻟﻰ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪،‬‬
‫ﻭﺴﺘﻌﺎﻤل ﻤﻌﺎﻤﻠﺔ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻌﺎﺩﻴﺔ‪.‬‬
‫ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻜﺎﺌﻥ ﻋﺭﺽ ﺍﻟﺼﻑ ‪ DataRowView‬ﺍﻟﺫﻱ ﻴﺸﻴﺭ ﺇﻟـﻰ ﺍﻟﺼـﻑ‬
‫ﺍﻟﺠﺩﻴﺩ ﺍﻟﺫﻱ ﺘﻤﺕ ﺇﻀﺎﻓﺘﻪ‪ ،‬ﻤﻤﺎ ﻴﺘﻴﺢ ﻟﻙ ﺘﻐﻴﻴﺭ ﻗﻴﻡ ﺨﺎﻨﺎﺘﻪ‪.‬‬

‫ﺤﺫﻑ ‪: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‬‬

‫ﺒﺤﺙ ﻋﻥ ﺍﻟﺼﻔﻭﻑ ‪:FindRows‬‬


‫ﻤﻤﺎﺜﻠـﺔ ﻟﻠﻭﺴـﻴﻠﺔ ﺍﻟﺴـﺎﺒﻘﺔ ﻓـﻲ ﺼـﻴﻐﺘﻴﻬﺎ‪ ،‬ﻭﻟﻜﻨﻬـﺎ ﺘﻌﻴـﺩ ﻤﺼـﻔﻭﻓﺔ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪ ،DataRowView‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻜلّ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻁﻠﻭﺒﺔ‪.‬‬

‫‪٣٥٧‬‬
‫ﺍﻟﺘﺤﻭﻴل ﺇﻟﻰ ﺠﺩﻭل ‪:ToTable‬‬
‫ﺘﻨﺸﺊ ﺠﺩﻭﻻ ﺠﺩﻴﺩﺍ ﻭﺘﻨﺴﺦ ﺇﻟﻴﻪ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ﺍﻟﺤـﺎﻟﻲ‪ ،‬ﻭﺘﻌﻴـﺩ‬
‫ﺇﻟﻴﻙ ﻜﺎﺌﻥ ﺠﺩﻭل ‪ DataTable‬ﻴﺸﻴﺭ ﺇﻟﻴﻪ‪ ..‬ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻨﺼﺎ ﻟﺘﺴﺘﺨﺩﻤﻪ ﻜﺎﺴﻡ ﻟﻠﺠﺩﻭل‪.‬‬
‫‪ -٣‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻠﻴﻥ‪:‬‬
‫‪ -‬ﻗﻴﻤﺔ ﻤﻨﻁﻘﻴﺔ ﺇﺫﺍ ﺠﻌﻠﺘﻬﺎ ‪ true‬ﻓﺴﻴﺘﻡ ﺍﺴﺘﺒﻌﺎﺩ ﺍﻟﺼﻔﻭﻑ ﺍﻟﻤﻜﺭﺭﺓ ﺍﻟﺘﻲ ﺘﺘﺸـﺎﺒﻪ‬
‫ﻗﻴﻡ ﺃﻱ ﻤﻥ ﺨﺎﻨﺎﺘﻬﺎ ﻤﻊ ﺃﻱ ﺼﻔﻭﻑ ﺃﺨﺭﻯ‪.‬‬
‫‪ -‬ﻤﺼﻔﻭﻓﺔ ﻨﺼﻴﺔ‪ ،‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺍﻷﻋﻤﺩﺓ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻨﺴﺨﻬﺎ ﺇﻟﻰ ﺍﻟﺠﺩﻭل‪..‬‬
‫ﻭﺴﺘﻅﻬﺭ ﺍﻷﻋﻤﺩﺓ ﻓﻲ ﺍﻟﺠﺩﻭل ﺒﻨﻔﺱ ﺘﺭﺘﻴﺒﻬﺎ ﻓﻲ ﺍﻟﻤﺼﻔﻭﻓﺔ‪ ..‬ﻻﺤﻅ ﺃﻥ ﻫـﺫﻩ‬
‫ﻫﻲ ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﻭﺤﻴﺩﺓ ﺍﻟﺘﻲ ﺘﺴﺘﻁﻴﻊ ﺒﻬﺎ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﺠﺩﻭل ﻴﺨﺘﻠﻑ ﻓﻲ ﻋﺩﺩ‬
‫ﺃﻋﻤﺩﺘﻪ ﻭﺘﺭﺘﻴﺒﻬﺎ ﻋﻥ ﺍﻟﺠﺩﻭل ﺍﻷﺼﻠﻲ‪.‬‬
‫‪ -٤‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﻌﺎﻤﻼﺕ ﺍﻟﺼﻴﻐﺘﻴﻥ ﺍﻟﺴﺎﺒﻘﺘﻴﻥ ﻤﻌﺎ‪.‬‬

‫‪٣٥٨‬‬
‫ﻭﺍﺠﻬﺔ ﺍﻟﻜﺎﺌﻥ ﺍﻟﻘﺎﺒل ﻟﻠﺘﺤﺭﻴﺭ‬
‫‪IEditableObject Interface‬‬

‫ـﺭ‬
‫ـﺔ ﺍﻟﺘﺤﺭﻴـ‬
‫ـﻲ ﺤﺎﻟـ‬
‫ـﻌﻪ ﻓـ‬
‫ـﻥ ﻭﻀـ‬
‫ـﺎﺌﻥ ﻴﻤﻜـ‬
‫ـﻊ ﻜـ‬
‫ـل ﻤـ‬
‫ـﺔ ﻟﻠﺘﻌﺎﻤـ‬
‫ـﺎﺌل ﺍﻟﻼﺯﻤـ‬
‫ـﺩﻡ ﺍﻟﻭﺴـ‬
‫ﺘﻘـ‬
‫)ﻤﺜل ﻜﺎﺌﻥ ﻋﺭﺽ ﺼﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ،(DataRowView‬ﻤﻊ ﺍﻟﻘﺩﺭﺓ ﻋﻠﻰ ﺇﻟﻐﺎﺀ ﺍﻟﺘﻐﻴﻴـﺭﺍﺕ ﺃﻭ‬
‫ﻗﺒﻭﻟﻬﺎ‪.‬‬
‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺒﺩﺀ ﺍﻟﺘﺤﺭﻴﺭ ‪:BeginEdit‬‬


‫ﺘﺒﺩﺃ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺭﻴﺭ‪.‬‬

‫ﺇﻟﻐﺎﺀ ﺍﻟﺘﺤﺭﻴﺭ ‪:CancelEdit‬‬


‫ﺘﻠﻐﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺭﻴﺭ ﻭﺘﻌﻴﺩ ﺇﻟﻰ ﺍﻟﻜﺎﺌﻥ ﻗﻴﻤﻪ ﺍﻷﺼﻠﻴﺔ‪.‬‬

‫ﺇﻨﻬﺎﺀ ﺍﻟﺘﺤﺭﻴﺭ ‪:EndEdit‬‬


‫ﺘﻨﻬﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺭﻴﺭ ﻭﺘﺤﻔﻅ ﻓﻲ ﺍﻟﻜﺎﺌﻥ ﺍﻟﻘﻴﻡ ﺍﻟﺘﻲ ﺘﻡ ﺇﺩﺨﺎﻟﻬﺎ ﺃﺜﻨﺎﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺘﺤﺭﻴﺭ‪.‬‬

‫‪٣٥٩‬‬
‫ﻭﺍﺠﻬﺔ ﻤﻌﻠﻭﻤﺎﺕ ﺨﻁﺄ ﺍﻟﺒﻴﺎﻨﺎﺕ‬
‫‪IDataErrorInfo Interface‬‬

‫ﺘﻘﺩﻡ ﻤﻌﻠﻭﻤﺎﺕ ﻋﻥ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﺍﻟﺴﺠل‪ ،‬ﻭﻫﻲ ﺘﻤﺘﻠﻙ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﺍﻟﺨﻁﺄ ‪:Error‬‬
‫ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﺸﺭﺡ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﺍﻟﺴﺠل‪.‬‬

‫ﺨﻁﺄ ﺍﻟﻌﻨﺼﺭ ‪:Item‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﺴﻡ ﺍﻟﻌﻤﻭﺩ‪ ،‬ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻨﺼﺎ ﻴﺸﺭﺡ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﻫـﺫﺍ‬
‫ﺍﻟﻌﻤﻭﺩ ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﻭﺍﺠﻬﺔ ﺍﻟﺘﻨﺒﻴﻪ ﺒﺘﻐﻴﺭ ﺨﺎﺼﻴﺔ‬


‫‪INotifyPropertyChanged Interface‬‬

‫ﺘﻁﻠﻕ ﺍﻟﺤﺩﺙ ‪ PropertyChanged‬ﻋﻨﺩ ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺨﺎﺼﻴﺔ ﻤﻌﻴﻨﺔ‪ ..‬ﻭﺘﻤﺘﻠﻙ ﻫـﺫﻩ ﺍﻟﻭﺍﺠﻬـﺔ‬
‫ﺍﻟﺤﺩﺙ ﺍﻟﺘﺎﻟﻲ‪:‬‬

‫ﺍﻟﺨﺎﺼﻴﺔ ﺘﻐﻴﺭﺕ ‪:PropertyChanged‬‬


‫ﻴﻨﻁﻠﻕ ﻋﻨﺩﻤﺎ ﻴﺤﺩﺙ ﺘﻐﻴﻴﺭ ﻓﻲ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬـﺫﺍ ﺍﻟﺤـﺩﺙ ﻤـﻥ‬
‫ﺍﻟﻨﻭﻉ ‪ ،PropertyChangedEventArgs‬ﻭﻫﻭ ﻴﻤﺘﻠﻙ ﺍﻟﺨﺎﺼـﻴﺔ ‪PropertyName‬‬
‫ﺍﻟﺘﻲ ﺘﻌﻴﺩ ﺍﺴﻡ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﻲ ﺘﻐﻴﺭﺕ‪.‬‬

‫‪٣٦٠‬‬
‫ﻓﺌﺔ ﻋﺭﺽ ﺼﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DataRowView Class‬‬

‫ـﺎﺕ ‪ IEditableObject‬ﻭ ‪IDataErrorInfo‬‬


‫ـل ﺍﻟﻭﺍﺠﻬـــ‬
‫ـﺔ ﺘﺜﻤـــ‬
‫ـﺫﻩ ﺍﻟﻔﺌـــ‬
‫ﻫـــ‬
‫ﻭ ‪ ،INotifyPropertyChanged‬ﻭﻫﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺤﺩ ﺍﻟﺴﺠﻼﺕ ﺍﻟﻤﻌﺭﻭﻀﺔ ﻓﻲ ﻜـﺎﺌﻥ‬
‫ﺍﻟﻌﺭﺽ ‪ ..DataView‬ﻭﻻ ﻴﻭﺠﺩ ﻟﻬﺫﻩ ﺍﻟﻔﺌﺔ ﺤﺩﺙ ﺇﻨﺸﺎﺀ‪ ،‬ﻭﻻ ﻴﻤﻜﻨﻙ ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﻨﺴـﺨﺔ‬
‫ﻤﻨﻬﺎ ﺇﻻ ﻤﻥ ﺨﻼل ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataView‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ‪ DataView‬ﺍﻟﺫﻱ ﻴﻨﺘﻤﻲ ﺇﻟﻴﻪ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﺍﻟﺴﺠل ‪:Row‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺴﺠل ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataRow‬ﺍﻟﺫﻱ ﻴﻌﺭﻀﻪ ﻜﺎﺌﻥ ﻋﺭﺽ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﻓﻲ ﺤﺎﻟﺔ ﺍﻟﺘﺤﺭﻴﺭ ‪:IsEdit‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠلّ ﺍﻟﺤﺎﻟﻲ‪ ‬ﻴﺘﻡ‪ ‬ﺘﺤﺭﻴﺭﻩ‪.‬‬

‫ﻫل ﻫﻭ ﺠﺩﻴﺩ ‪:IsNew‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠلّ ﺍﻟﺤﺎﻟﻲ‪ ‬ﺠﺩﻴﺩﺍ‪ ،‬ﻭﺫﻟـﻙ ﺇﺫﺍ ﺘـﻡ ﺇﻨﺸـﺎﺅﻩ ﺒﻭﺍﺴـﻁﺔ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪ ..DataView.AddNew‬ﻤﺜﺎل‪:‬‬
‫;) (‪var Rv = Dv.AddNew‬‬
‫‪MessageBox.Show(Rv.IsNew.ToString( )) // true‬‬

‫‪٣٦١‬‬
‫ﺍﻟﻤﻔﻬﺭﺱ ‪:Indexer‬‬
‫ﻴﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺇﺤﺩﻯ ﺨﺎﻨﺎﺕ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ‪ ،‬ﺍﻟﺘﻲ ﻴﺤﺩﺩﻫﺎ ﺍﺴـﻡ ﺍﻟﻌﻤـﻭﺩ ﺃﻭ ﺭﻗﻤـﻪ‬
‫ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل‪ ..‬ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﺽ ﻗﻴﻡ ﻜل ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﻜـل ﺼـﻔﻭﻑ‬
‫ﻜﺎﺌﻥ ﺍﻟﻌﺭﺽ ‪:Dv‬‬
‫)‪foreach (DataRowView R in Dv‬‬
‫{‬
‫)‪for (var I = 0; I < Dv.Table.Columns.Count; I++‬‬
‫{‬
‫;)) (‪MessageBox.Show(R[I].ToString‬‬
‫}‬
‫}‬

‫ﻨﺴﺨﺔ ﺍﻟﺴﺠل ‪:RowVersion‬‬


‫ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ ،DataRowVersion‬ﺍﻟﺘﻲ ﺘﻭﻀﺢ ﻨﺴﺨﺔ ﺍﻟﺴﺠل ﺍﻟﻤﻌﺭﻭﻀـﺔ‬
‫ﺤﺎﻟﻴﺎ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻓﺌﺔ ﻋﺭﺽ ﺍﻟﺼﻑ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﺤﺫﻑ ‪:Delete‬‬
‫ـﻴﻠﺔ‬
‫ـﺄﺜﻴﺭ ﺍﻟﻭﺴـ‬
‫ـﺱ ﺘـ‬
‫ـﺎ ﻨﻔـ‬
‫ـﺭﺽ‪ ،‬ﻭﻟﻬـ‬
‫ـﺎﺌﻥ ﺍﻟﻌـ‬
‫ـﻥ ﻜـ‬
‫ـﺎﻟﻲ ﻤـ‬
‫ـﺠل ﺍﻟﺤـ‬
‫ـﺫﻑ ﺍﻟﺴـ‬
‫ﺘﺤـ‬
‫‪ DataView.Delete‬ﻋﻠﻰ ﺍﻟﺴﺠل ﺍﻷﺼﻠﻲ ﻓﻲ ﺠﺩﻭل ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺇﻨﺸﺎﺀ ﻋﺭﺽ ﺘﺎﺒﻊ ‪:CreateChildView‬‬


‫ﺘﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺇﺫﺍ ﻜﺎﻥ ﻋﺎﺭﺽ ﺍﻟﺼﻑ ﺍﻟﺤﺎﻟﻲ ﻴﻨﺘﻤﻲ ﺇﻟﻰ ﺠﺩﻭل ﺃﺴﺎﺴـﻲ‪ ‬ﻴـﺭﺘﺒﻁ‬
‫ﺒﻌﻼﻗﺔ ﺒﺠﺩﻭل ﺜﺎﻨﻭﻱ‪ ..‬ﻭﺘﺴـﺘﻘﺒل ﻫـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺍﺴـﻡ ﺍﻟﻌﻼﻗـﺔ ﺃﻭ ﻜـﺎﺌﻥ ﺍﻟﻌﻼﻗـﺔ‬
‫‪ DataRelation‬ﺍﻟﺫﻱ ﻴﻤﺜﻠﻬﺎ‪ ،‬ﻭﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻋﺭﺽٍ ‪ DataView‬ﻴﺤﺘـﻭﻱ ﻋﻠـﻰ ﻜـلّ‬
‫ﺴﺠﻼﺕ ﺍﻟﺠﺩﻭل ﺍﻟﺜﺎﻨﻭﻱ‪ ‬ﺍﻟﺘﺎﺒﻌﺔ ﻟﻬﺫﺍ ﺍﻟﺴﺠلّ‪ ..‬ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﻴﺩ ﻜﺎﺌﻥ ﻋﺭﺽ ﻴﺤﺘـﻭﻱ‬

‫‪٣٦٢‬‬
‫ﻋﻠﻰ ﻜلّ ﺍﻟﻜﺘﺏ ﺍﻟﺘﻲ ﺃﻟﻔﻬﺎ ﺃﻭل ﻤﺅﻟﻑ ﻓﻲ ﺠﺩﻭل ﺍﻟﻜﺘﺏ‪ ،‬ﺒـﺎﻓﺘﺭﺍﺽ ﺃﻥ ﺍﻟﻌﻼﻗـﺔ ﺒـﻴﻥ‬
‫ﺠﺩﻭﻟﻲ ﺍﻟﻜﺘﺏ ﻭﺍﻟﻤﺅﻟﻔﻴﻥ ﺍﺴﻤﻬﺎ ‪: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‬ﻭﺘﺸـﻤل‬
‫ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪ -١‬ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﺒﺴﻴﻁﺔ ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺒﻌﺽ ﺍﻟﺤﻘﻭل ‪:Fields‬‬


‫ـﻴﻥ ‪Width‬‬
‫ـﻰ ﺍﻟﺤﻘﻠـ‬
‫ـﻭﻱ ﻋﻠـ‬
‫ـﺫﻱ ﻴﺤﺘـ‬
‫ـﻡ ‪ Size Object‬ﺍﻟـ‬
‫ـﺎﺌﻥ ﺍﻟﺤﺠـ‬
‫ـل ﻜـ‬
‫ﻤﺜـ‬
‫ﻭ ‪ ..Height‬ﻭﺍﻟﻤﺸﺭﻭﻉ ‪ BindingToObject‬ﺍﻟﻤﺭﻓﻕ ﺒﺄﻤﺜﻠﺔ ﻫﺫﺍ ﺍﻟﻜﺘـﺎﺏ ﻴﺭﻴـﻙ‬
‫ﻤﺜﺎﻻ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﺭﺒﻁ‪.‬‬

‫‪ -٢‬ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﻤﺭﻜﺒﺔ ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻋﺩﺓ ﻜﺎﺌﻨﺎﺕ‪:‬‬


‫ﻜﺎﻟﻤﺼﻔﻭﻓﺎﺕ ‪ Arrays‬ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﺭﻗﺎﻡ ﺃﻭ ﻨﺼﻭﺹ ﺃﻭ ﻜﺎﺌﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ‪،‬‬
‫ﻭﺍﻟﻤﺠﻤﻭﻋﺎﺕ ‪ Collections‬ﺍﻟﺘﻲ ﺘﻤﺜل ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ‪ ..IList‬ﻭﺘﻌـﺭﺽ ﺍﻷﺩﻭﺍﺕ‬
‫ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻋﻨﺼﺭﺍ ﻭﺍﺤﺩﺍ ﻓﻘﻁ ﻓﻲ ﻨﻔﺱ ﺍﻟﻠﺤﻅﺔ‪ ،‬ﻭﺘﻘﺩﻡ ﺘﻘﻨﻴـﺔ ﺍﻟـﺭﺒﻁ ﺍﻟﻭﺴـﺎﺌل‬
‫‪٣٦٤‬‬
‫ﺍﻟﻼﺯﻤﺔ ﻟﻠﺘﺤﺭﻙ ﺇﻟﻰ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺴﺎﺒﻕ ﺃﻭ ﺍﻟﺘﺎﻟﻲ‪ ..‬ﻭﺍﻟﻤﺸـﺭﻭﻉ ‪BindingToArray‬‬
‫ﻴﺭﻴﻙ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﺭﺒﻁ‪.‬‬

‫‪ -٣‬ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﻤﻌﻘﺩﺓ ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﺠﻤﻭﻋﺎﺕ ﺩﺍﺨﻠﻴﺔ‪:‬‬


‫ﻭﻫﻲ ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﺘﻲ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ IBindingList‬ﺃﻭ ﺍﻟﻭﺍﺠﻬﺔ ‪ ،ITypedList‬ﻤﺜـل‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ DataSet‬ﻭﺠﺩﻭل ﺍﻟﺒﻴﺎﻨـﺎﺕ ‪ DataTable‬ﻭﻋـﺭﺽ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫‪ DataView‬ﻭﻤﺩﻴﺭ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ..DataViewManager‬ﻭﻨﻅﺭﺍ ﻷﻥ ﻤﺼﺩﺭ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺃﻜﺜﺭ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺩﺍﺨﻠﻴﺔ )ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺜﻼ ﺘﺤﺘﻭﻱ ﻋﻠﻰ‬
‫ﺃﻜﺜﺭ ﻤﻥ ﺠﺩﻭل ﻭﺃﻜﺜﺭ ﻤﻥ ﻋﻼﻗﺔ(‪ ،‬ﻓﻴﺠﺏ ﺃﻥ ﻨﺤﺩﺩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﻲ ﺴـﻨﺄﺨﺫ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﻤﻨﻬﺎ )ﻜﺎﺴﻡ ﺍﻟﺠﺩﻭل ﻤﺜﻼ(‪ ..‬ﻭﺘﺴـﻤﻰ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﺒﺎﺴـﻡ ﻋﻨﺼـﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫‪.Data Member‬‬
‫ﻭﺘﻌﺭﺽ ﺍﻷﺩﻭﺍﺕ ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﺠﻼ ﻭﺍﺤﺩﺍ ﻓﻘﻁ ﻓﻲ ﻨﻔﺱ ﺍﻟﻠﺤﻅﺔ‪ ،‬ﻭﺘﻘـﺩﻡ ﺘﻘﻨﻴـﺔ‬
‫ﺍﻟﺭﺒﻁ ﺍﻟﻭﺴﺎﺌل ﺍﻟﻼﺯﻤﺔ ﻟﻠﺘﺤﺭﻙ ﺇﻟـﻰ ﺍﻟﺴـﺠل ﺍﻟﺴـﺎﺒﻕ ﺃﻭ ﺍﻟﺘـﺎﻟﻲ‪ ..‬ﻭﺍﻟﻤﺸـﺭﻭﻉ‬
‫‪ BindingToDataSet‬ﻴﺭﻴﻙ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﺭﺒﻁ‪.‬‬

‫‪٣٦٥‬‬
‫ﻭﺍﺠﻬﺔ ﺍﻟﻤﻜﻭﻥ ﺍﻟﻘﺎﺒل ﻟﻼﺭﺘﺒﺎﻁ‬
‫‪IBindableComponent Interfac‬‬

‫ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻷﺴﺎﺴﻴﺔ ﺍﻟﻼﺯﻤﺔ ﻟﺭﺒﻁ ﺍﻷﺩﻭﺍﺕ ﺒﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻫﻲ‪:‬‬

‫ﺍﺭﺘﺒﺎﻁﺎﺕ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataBindings‬‬


‫ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺍﺭﺘﺒﺎﻁﺎﺕ ﺍﻷﺩﺍﺓ ‪ ،ControlBindingsCollection‬ﺘﺤﺘـﻭﻱ‬
‫ﻋﻠﻰ ﻜﺎﺌﻨﺎﺕ ﺍﻟﺭﺒﻁ ‪ Binding Objects‬ﺍﻟﺘﻲ ﺘﺴﺘﺨﺩﻤﻬﺎ ﺍﻷﺩﺍﺓ ﺍﻟﺤﺎﻟﻴـﺔ‪ ..‬ﻭﺴـﻨﺘﻌﺭﻑ‬
‫ﻋﻠﻰ ﺍﻟﻔﺌﺔ ‪ Binding‬ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬

‫ﻤﺤﺘﻭﻯ ﺍﻟﺭﺒﻁ ‪:BindingContext‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜـﺎﺌﻥ ﻤﺤﺘـﻭﻯ ﺍﻟـﺭﺒﻁ ‪ BindingContext‬ﺍﻟـﺫﻱ ﺘﺴـﺘﺨﺩﻤﻪ ﺍﻷﺩﺍﺓ‪..‬‬
‫ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ ‪ BindingContext‬ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬

‫ﺘﺼﻔﻴﺭ ﺍﻻﺭﺘﺒﺎﻁﺎﺕ ‪:ResetBindings‬‬


‫ﺘﻘﻭﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺒﺈﻨﻌﺎﺵ ﺍﻟﻘﻴﻡ ﺍﻟﺘﻲ ﺘﻌﺭﻀﻬﺎ ﺍﻷﺩﺍﺓ ﻤﻥ ﺨﻼل ﺍﻻﺭﺘﺒﺎﻁ‪ ..‬ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫ﻟﻴﺴﺕ ﻋﻠﻰ ﺩﺭﺠﺔ ﻤﻠﻤﻭﺴﺔ ﻤﻥ ﺍﻷﻫﻤﻴﺔ‪.‬‬

‫ﻤﺤﺘﻭﻯ ﺍﻟﺭﺒﻁ ﺘﻐﻴﺭ ‪:BindingContextChanged‬‬


‫ﻴﻨﻁﻠﻕ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻋﻨﺩ ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪.BindingContext‬‬

‫ﺍﻟﺠﺩﻴﺭ ﺒﺎﻟﺫﻜﺭ ﺃﻥ ﻓﺌﺔ ﺍﻷﺩﺍﺓ ﺍﻷﻡ ‪ Control Class‬ﺍﻟﺘﻲ ﺘﺭﺜﻬﺎ ﺠﻤﻴﻊ ﺍﻷﺩﻭﺍﺕ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬـﺔ‬
‫‪ ،IBindableComponent‬ﻭﻤﻥ ﺜﻡ ﻓﻬﻲ ﺘﻤﺘﻠﻙ ﺠﻤﻴﻊ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﺴـﺎﺒﻘﺔ‪ ..‬ﻫـﺫﺍ ﻤﻌﻨـﺎﻩ ﺃﻥ‬
‫ﺠﻤﻴﻊ ﺃﺩﻭﺍﺕ ﺍﻟﻭﻴﻨﺩﻭﺯ ﺘﺼﻠﺢ ﻟﻼﺭﺘﺒﺎﻁ ﺒﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﺘﺴــﻤﻰ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺘــﻲ ﻴــﺘﻡ ﺍﻻﺭﺘﺒــﺎﻁ ﺒﻬــﺎ ﺒﺎﺴــﻡ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﺭﺘﺒﻁــﺔ ﺒﺎﻟﺒﻴﺎﻨــﺎﺕ‬
‫‪ ،Data-Bound Controls‬ﻭﻨﻅﺭﺍ ﻷﻥ ﻜل ﺃﺩﺍﺓ ﺘﻤﺘﻠﻙ ﺍﻟﻌﺩﻴﺩ ﻤﻥ ﺍﻟﺨﺼـﺎﺌﺹ‪ ،‬ﻓﻴﺠـﺏ‬
‫ﻋﻠﻴﻙ ﺃﻥ ﺘﺤﺩﺩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﻲ ﺘﺭﻴﺩﻫﺎ ﺃﻥ ﺘﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻻ ﻤﺎﻨﻊ ﻤﻥ ﺃﻥ ﺘﺭﺒﻁ ﺃﻜﺜﺭ ﻤـﻥ‬
‫‪٣٦٦‬‬
‫ﺨﺎﺼﻴﺔ ﻤﻥ ﺨﺼﺎﺌﺹ ﺍﻷﺩﺍﺓ‪ ،‬ﺒﺄﻜﺜﺭ ﻤﻥ ﻋﻨﺼﺭ ﻤﻥ ﻋﻨﺎﺼﺭ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻤﺜﻼ‪ :‬ﻴﻤﻜﻨـﻙ‬
‫ﺭﺒﻁ ﺍﻟﺨﺎﺼﻴﺔ ‪ Text‬ﺍﻟﺨﺎﺼﺔ ﺒﺯﺭ ﺍﻻﺨﺘﻴﺎﺭ ‪ CheckBox‬ﺒﻌﻨﺼﺭ ﺒﻴﺎﻨـﺎﺕ ﻨﺼـﻲ ﻭﺭﺒـﻁ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ‪ Checked‬ﺒﻌﻨﺼﺭ ﺒﻴﺎﻨﺎﺕ ﻤﻨﻁﻘﻲ ‪ ..Boolean‬ﻭﺘﺴﻤﻰ ﺨﺎﺼﻴﺔ ﺍﻷﺩﺍﺓ ﺍﻟﺘـﻲ ﻴـﺘﻡ‬
‫ﺍﻻﺭﺘﺒﺎﻁ ﺒﻬﺎ ﺒﺎﺴﻡ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ‪ ،Display Member‬ﻷﻨﻬﺎ ﺘﻌـﺭﺽ ﻗﻴﻤـﺔ ﺨﺎﺼـﻴﺔ‬
‫ﺍﻟﻜﺎﺌﻥ‪.‬‬

‫ﻤﻠﺨﺹ‪:‬‬
‫· ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:Data Source‬‬
‫ﻫﻭ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﻴﺘﻡ ﺭﺒﻁﻬﺎ ﺒﺎﻷﺩﺍﺓ‪ ..‬ﻭﻤﺜـﺎل ﺫﻟـﻙ‪ :‬ﺍﻟﺠـﺩﻭل‬
‫‪ Books‬ﻓﻲ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫· ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:Data Member‬‬


‫ﻫﻭ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘﻲ ﻴﺘﻡ ﺭﺒﻁ ﻗﻴﻤﺘﻬﺎ ﺒﺎﻷﺩﺍﺓ‪ ..‬ﻭﻤﺜﺎل ﺫﻟﻙ ﺍﻟﻌﻤﻭﺩ ‪ Book‬ﻓﻲ ﺠﺩﻭل ﺍﻟﻜﺘﺏ‪.‬‬

‫· ﺍﻷﺩﺍﺓ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﺎﻟﺒﻴﺎﻨﺎﺕ ‪:Data-Bound Control‬‬


‫ﻫﻲ ﺍﻷﺩﺍﺓ ﺍﻟﺘﻲ ﺘﺭﺘﺒﻁ ﺒﻜﺎﺌﻥ ﻭﺘﻌﺭﺽ ﺒﻌﺽ ﺒﻴﺎﻨﺎﺘﻪ‪ ..‬ﻤﺜل ﻤﺭﺒـﻊ ﺍﻟـﻨﺹ ‪ TextBox‬ﺃﻭ‬
‫ﺍﻟﻼﻓﺘﺔ‪ ..‬ﻻﺤﻅ ﺃﻥ ﻜل ﺍﻷﺩﻭﺍﺕ ﺘﺼﻠﺢ ﻟﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻋﻠﻴﻙ ﺍﺨﺘﻴﺎﺭ ﻤﺎ ﻴﻨﺎﺴﺏ ﻭﻅﻴﻔﺔ‬
‫ﺒﺭﻨﺎﻤﺠﻙ ﻤﻨﻬﺎ‪.‬‬

‫· ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ‪:Display Member‬‬


‫ﻫﻭ ﺨﺎﺼﻴﺔ ﺍﻷﺩﺍﺓ ﺍﻟﺘﻲ ﺘﺭﺘﺒﻁ ﺒﻌﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺘﻌﺭﺽ ﻗﻴﻤﺘﻪ‪ ،‬ﻜﺎﻟﺨﺎﺼـﻴﺔ ‪ Text‬ﻓـﻲ‬
‫ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﺃﻭ ﺍﻟﻼﻓﺘﺔ‪ ..‬ﻻﺤﻅ ﺃﻥ ﻜﺜﻴﺭﺍ ﻤﻥ ﺨﺼﺎﺌﺹ ﺍﻷﺩﻭﺍﺕ ﺘﺼﻠﺢ ﻜﻌﻨﺎﺼﺭ ﻋﺭﺽ‪،‬‬
‫ﺤﺘﻰ ﻟﻭ ﻟﻡ ﻴﺭ‪ ‬ﺍﻟﻤﺴﺘﺨﺩﻡ ﻗﻴﻤﺘﻬﺎ‪ ،‬ﻓﺄﺤﻴﺎﻨﺎ ﺘﺭﻴﺩ ﺤﻔﻅ ﻗﻴﻤﺔ ﻤﻥ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻓـﻲ ﺍﻷﺩﺍﺓ‬
‫ﻻﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﻭﻅﻴﻔﺔ ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ،‬ﻜﺄﻥ ﺘﺤﻔﻅ ﺭﻗﻡ ﺍﻟﻤﺅﻟﻑ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪ Tag‬ﺒﻴﻨﻤﺎ ﺘﻌﺭﺽ‬
‫ﺍﺴﻤﻪ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪.Text‬‬

‫ﻭﺘﺴﺘﺨﺩﻡ ﻓـﻲ ﺘﻘﻨﻴـﺔ ﺍﻟـﺭﺒﻁ ﻤﺠﻤﻭﻋـﺔ ﻤـﻥ ﺍﻟﻔﺌـﺎﺕ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓـﻲ ﻨﻁـﺎﻕ ﺍﻻﺴـﻡ‬
‫‪ ..System.Windows.Forms‬ﺩﻋﻨﺎ ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﻔﺌﺎﺕ‪.‬‬

‫‪٣٦٧‬‬
‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻻﺭﺘﺒﺎﻁﺎﺕ‬
‫‪BindingsCollection Class‬‬

‫ﺘﺭﺙ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﻓﺌﺔ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻷﺴﺎﺴﻴﺔ ‪ ،BaseCollection‬ﻭﻫﻲ ﻤﺠﻤﻭﻋـﺔ ﺘﻘﻠﻴﺩﻴـﺔ‬


‫ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،ICollection‬ﻭﺘﺸﺘﻕ ﻤﻨﻬﺎ ﺍﻟﻌﺩﻴﺩ ﻤﻥ ﺍﻟﻤﺠﻤﻭﻋـﺎﺕ ﺍﻟﺨﺎﺼـﺔ ﺒﺎﻻﺭﺘﺒﺎﻁـﺎﺕ‬
‫ﻭﺃﺩﻭﺍﺕ ﺭﺒﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ‪.‬‬
‫ﻭﺘﺤﺘﻭﻱ ﻤﺠﻤﻭﻋﺔ ﺍﻻﺭﺘﺒﺎﻁﺎﺕ ‪ BindingsCollection‬ﻋﻠﻰ ﻜـل ﺍﻻﺭﺘﺒﺎﻁـﺎﺕ ﺍﻟﺘـﻲ ﺘـﻡ‬
‫ﺇﻨﺸﺎﺅﻫﺎ ﺒﻴﻥ ﺃﺩﺍﺓ ﻤﻌﻴﻨﺔ ﻭﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺨﺘﻠﻔﺔ‪ ..‬ﻭﻜل ﻋﻨﺼﺭ ﻤﻥ ﻋﻨﺎﺼﺭ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ‬
‫ﻤﻥ ﻨﻭﻉ ﺍﻟﻔﺌﺔ ‪ Binding‬ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﺒﻌﺩ ﻗﻠﻴل‪.‬‬
‫ـﻼل ﺍﻟﺨﺎﺼـــﻴﺔ‬
‫ـﺔ ﻤـــﻥ ﺨــ‬
‫ـﺫﻩ ﺍﻟﻤﺠﻤﻭﻋــ‬
‫ـﻭل ﻋﻠـــﻰ ﻫــ‬
‫ـﻥ ﺍﻟﺤﺼــ‬
‫ﻭﻴﻤﻜــ‬
‫‪ ،BindingManagerBase.Bindings‬ﻜﻤﺎ ﺴﻨﺭﻯ ﺒﻌﺩ ﻗﻠﻴل‪.‬‬

‫ﻭﻻ ﺠﺩﻴﺩ ﻓﻲ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺇﻻ ﺍﻤﺘﻼﻜﻬﺎ ﻟﻠﺤﺩﺜﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ‪:‬‬

‫ﻴﺘﻡ ﺘﻐﻴﻴﺭ ﺍﻟﻤﺠﻤﻭﻋﺔ ‪:CollectionChanging‬‬


‫ﻴﻨﻁﻠﻕ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻋﻨﺩ ﺘﻐﻴﻴﺭ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤـﺩﺙ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪ ،CollectionChangeEventArgs‬ﻭﻗﺩ ﺘﻌﺭﻓﻨﺎ ﻋﻠﻴﻪ ﻤﻥ ﻗﺒل ﻋﻨﺩ ﺍﻟﺘﻌﺭﻑ ﻋﻠﻰ ﻜﺎﺌﻥ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺠﺩﺍﻭل ‪.DataTableCollection‬‬

‫ﺘﻡ ﺘﻐﻴﻴﺭ ﺍﻟﻤﺠﻤﻭﻋﺔ ‪:CollectionChanged‬‬


‫ﻴﻨﻁﻠﻕ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﺒﻌﺩ ﺤﺩﻭﺙ ﺍﻟﺘﻐﻴﻴﺭ ﻓﻲ ﺍﻟﻤﺠﻤﻭﻋﺔ ﻓﻌﻼ‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ‬
‫ﻤﻤﺎﺜل ﻟﻤﻌﺎﻤل ﺍﻟﺤﺩﺙ ﺍﻟﺴﺎﺒﻕ‪.‬‬

‫‪٣٦٨‬‬
‫ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﺭﺘﺒﺎﻁﺎﺕ ﺍﻷﺩﺍﺓ‬
‫‪ControlBindingsCollection Class‬‬

‫ﺘﺭﺙ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﻔﺌﺔ ‪ ،BindingsCollection‬ﻭﻴﻤﻜﻥ ﺍﻟﺤﺼﻭل ﻋﻠﻴﻬـﺎ ﻤـﻥ ﺨـﻼل‬


‫ﺍﻟﺨﺎﺼﻴﺔ ‪.Control.Bindings‬‬
‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﻘﻠﻴﺩﻴﺔ ﻟﻠﻤﺠﻤﻭﻋﺎﺕ‪ ،‬ﻟﻜﻥ ﺍﻟﻭﺴﻴﻠﺔ ‪ Add‬ﺍﻟﺨﺎﺼﺔ ﺒﻬـﺎ ﻟﻬـﺎ‬
‫ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﺍﻻﺭﺘﺒﺎﻁ ‪ Binding‬ﺍﻟﻤﺭﺍﺩ ﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪.‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ‪:‬‬
‫‪ -‬ﺍﺴﻡ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ‪ ،‬ﻤﺜل "‪."Text‬‬
‫‪ -‬ﺍﻟﻜﺎﺌﻥ ‪ Object‬ﺍﻟﺫﻱ ﻴﻌﻤل ﻜﻤﺼﺩﺭ ﻟﻠﺒﻴﺎﻨﺎﺕ‪ ،‬ﻤﺜل ﻜﺎﺌﻥ ﺍﻟﺤﺠﻡ ‪.Size‬‬
‫‪ -‬ﺍﺴﻡ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻓﻤﺜﻼ‪ ،‬ﻟﻭ ﺃﺭﺩﺕ ﺭﺒﻁ ﺨﺎﺼﻴﺔ ﺍﻻﺭﺘﻔﺎﻉ ﺍﻟﺨﺎﺼـﺔ ﺒﻜـﺎﺌﻥ‬
‫ﺍﻟﺤﺠﻡ ‪ ،Size‬ﻓﺄﺭﺴل ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل ﺍﻟﻨﺹ "‪ .."Height‬ﻭﻴﻤﻜﻨﻙ ﺇﺭﺴﺎل ﻨـﺹ‬
‫ﻓﺎﺭﻍ "" ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻤﻌﺎﻤل‪ ،‬ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﺘﺭﺘﺒﻁ ﺍﻷﺩﺍﺓ ﺒـﺎﻟﻨﺹ ﺍﻟﻨـﺎﺘﺞ ﻤـﻥ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ‪ ToString‬ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﻜﺎﺌﻥ‪ ..‬ﻭﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﻴﺤﺘﻭﻱ ﻋﻠـﻰ ﻜﺎﺌﻨـﺎﺕ‬
‫ﻤﺘﺩﺍﺨﻠﺔ )ﻤﺜل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺠﺩﺍﻭل‪ ،‬ﻭﻜل ﻤﻨﻬـﺎ ﺘﺤﺘـﻭﻱ‬
‫ﻋﻠﻰ ﺃﻋﻤﺩﺓ(‪ ،‬ﻓﻴﺠﺏ ﻋﻠﻴﻙ ﻜﺘﺎﺒﺔ ﻤﺴﺎﺭ ﺍﻟﺨﺎﺼﻴﺔ ﻜﺎﻤﻼ ﺒﺩﻭﻥ ﺍﺴﻡ ﺍﻟﻜﺎﺌﻥ‪ ..‬ﻓﻤﺜﻼ‪:‬‬
‫ﻴﻤﻜﻨﻙ ﺍﻻﺭﺘﺒﺎﻁ ﺒﺤﻘل ﺍﺴﻡ ﺍﻟﻜﺘـﺎﺏ ﻓـﻲ ﺠـﺩﻭل ﺍﻟﻜﺘـﺏ ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﻤﺴـﺎﺭ‬
‫"‪ ،"Books.Book‬ﻤﻊ ﺇﺭﺴﺎل ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻨﻔﺴﻬﺎ ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ‪.‬‬
‫‪ -٣‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺭﺍﺒـﻊ‪ ،‬ﺇﺫﺍ ﺠﻌﻠـﺕ ﻗﻴﻤﺘـﻪ ‪True‬‬
‫ﻓﺴﻴﺘﻡ ﺘﻁﺒﻴﻕ ﺍﻟﺘﻨﺴﻴﻕ ‪ Format‬ﺍﻟﺨﺎﺹ ﺒﻙ ﻋﻨﺩ ﻋﺭﺽ ﺍﻟﻌﻨﺼﺭ ﻓﻲ ﺍﻷﺩﺍﺓ‪.‬‬
‫‪ -٤‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺨﺎﻤﺱ‪ ،‬ﻴﺴﺘﻘﺒل ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ‬
‫‪ ،DataSourceUpdateMode‬ﻟﻭﻀـــــﻌﻬﺎ ﻓـــــﻲ ﺍﻟﺨﺎﺼـــــﻴﺔ‬
‫‪ DataSourceUpdateMode‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﺒﻌﺩ ﻗﻠﻴل‪.‬‬

‫‪٣٦٩‬‬
‫‪ -٥‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺨﺎﻤﺴﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺴﺎﺩﺱ‪ ،‬ﻭﻫﻭ ﻴﺴﺘﻘﺒل ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ‬
‫ﺘﺭﻴﺩ ﻭﻀﻌﻬﺎ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ﺇﺫﺍ ﻜﺎﻥ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﺎﺭﻏﺎ ‪ ..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‬‬
‫ﺘﻌﻴﺩ ﺍﻷﺩﺍﺓ ﺍﻟﺘﻲ ﺘﻨﺘﻤﻲ ﺇﻟﻴﻬﺎ ﻤﺠﻤﻭﻋﺔ ﺍﻻﺭﺘﺒﺎﻁﺎﺕ ﺍﻟﺤﺎﻟﻴ‪‬ﺔ‪.‬‬

‫ﺍﻟﻤﻜﻭﻥ ﺍﻟﻘﺎﺒل ﻟﻼﺭﺘﺒﺎﻁ ‪:BindableComponent‬‬


‫ﺘﻌﻴﺩ ﻭﺍﺠﻬﺔ ﺍﻟﻤﻜﻭﻥ ﺍﻟﻘﺎﺒل ﻟﻼﺭﺘﺒﺎﻁ ‪ IBindableComponent‬ﺍﻟﺘـﻲ ﺘﻨﺘﻤـﻲ ﺇﻟﻴﻬـﺎ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻻﺭﺘﺒﺎﻁﺎﺕ ﺍﻟﺤﺎﻟﻴﺔ‪.‬‬

‫ﺍﻟﻁﺭﻴﻘﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﺘﺤﺩﻴﺙ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DefaultDataSourceUpdateMode‬‬


‫ﺘﺤﺩﺩ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻠﺨﺎﺼﻴﺔ ‪ DataSourceUpdateMode‬ﻟﻜل ﻜﺎﺌﻥ ﺭﺒﻁ ﻓـﻲ‬
‫ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﻭﻫﻲ ﺘﺄﺨﺫ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ DataSourceUpdateMode‬ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ‬
‫ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ‪.‬‬

‫‪٣٧٤‬‬
‫ﻓﺌﺔ ﺍﻻﺭﺘﺒﺎﻁ ‪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‬ﺃﻭ ﻏﻴﺭ ﺫﻟﻙ‪.‬‬

‫ﻭﺘﻤﻨﺤﻙ ﺍﻟﻔﺌﺔ ‪ Binding‬ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻟﻤﻜﻭﻥ ﺍﻟﻘﺎﺒل ﻟﻼﺭﺘﺒﺎﻁ ‪:BindableComponent‬‬


‫ﺘﻌﻴﺩ ﻭﺍﺠﻬﺔ ﺍﻟﻤﻜﻭﻥ ﺍﻟﻘﺎﺒل ﻟﻼﺭﺘﺒﺎﻁ ‪ IBindableComponent‬ﺍﻟﺘﻲ ﺘﻤﺜﻠﻬﺎ ﺍﻷﺩﺍﺓ ﺍﻟﺘـﻲ‬
‫ﻴﻨﺘﻤﻲ ﺇﻟﻴﻬﺎ ﺍﻻﺭﺘﺒﺎﻁ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫‪٣٧٥‬‬
‫ﺍﻷﺩﺍﺓ ‪:Control‬‬
‫ﺘﻌﻴﺩ ﺍﻷﺩﺍﺓ ‪ Control‬ﺍﻟﺘﻲ ﻴﻨﺘﻤﻲ ﺇﻟﻴﻬﺎ ﺍﻻﺭﺘﺒﺎﻁ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﺍﺴﻡ ﺍﻟﺨﺎﺼﻴﺔ ‪:PropertyName‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﺍﺴﻡ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ‪.‬‬

‫ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataSource‬‬


‫ﺘﻌﻴﺩ ﺍﻟﻜﺎﺌﻥ ‪ Object‬ﺍﻟﺫﻱ ﻴﻌﻤل ﻜﻤﺼﺩﺭ ﻟﻠﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻫﺫﺍ ﺍﻻﺭﺘﺒﺎﻁ‪.‬‬

‫ﻤﻌﻠﻭﻤﺎﺕ ﻋﻨﺼﺭ ﺍﻟﺭﺒﻁ ‪:BindingMemberInfo‬‬


‫ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﺴﺠل ﻤﻌﻠﻭﻤﺎﺕ ﻋﻨﺼﺭ ﺍﻟـﺭﺒﻁ ‪،BindingMemberInfo Structure‬‬
‫ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﻌﻠﻭﻤﺎﺕ ﺤﻭل ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﺍ ﺍﻟﺴﺠل ﻻﺤﻘﺎ‪.‬‬

‫ﺃﺴﺎﺱ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ ‪:BindingManagerBase‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺃﺴﺎﺱ ﺍﻻﺭﺘﺒﺎﻁ ‪ ،BindingManagerBase‬ﺍﻟﺫﻱ ﻴﺘﻴﺢ ﻟـﻙ ﺍﻟـﺘﺤﻜﻡ ﻓـﻲ‬
‫ﺍﻻﺭﺘﺒﺎﻁ ﺍﻟﺤﺎﻟﻲ‪ ..‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺍﻟﻔﺌﺔ ‪ BindingManagerBase‬ﺒﺎﻟﺘﻔﺼﻴل ﻻﺤﻘﺎ‪.‬‬

‫ﻫل ﻫﻭ ﻤﺭﺘﺒﻁ ‪:IsBinding‬‬


‫ﺘﻌﻴﺩ ‪ True‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻻﺭﺘﺒﺎﻁ ﻓﻌﺎﻻ‪ ،‬ﻭﺘﻌﻴﺩ ‪ False‬ﺇﺫﺍ ﺘﻡ ﺇﻴﻘﺎﻑ ﺍﻻﺭﺘﺒﺎﻁ ﻋـﻥ ﺍﻟﻌﻤـل‬
‫ﻤﺅﻗﺘﺎ‪.‬‬

‫ﻁﺭﻴﻘﺔ ﺘﺤﺩﻴﺙ ﺍﻷﺩﺍﺓ ‪:ControlUpdateMode‬‬


‫ﺘﺤﺩﺩ ﻜﻴﻔﻴﺔ ﺘﺤﺩﻴﺙ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ‪ ،‬ﻋﻨﺩﻤﺎ ﺘﺘﻐﻴﺭ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺘﺄﺨـﺫ ﻫـﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﺇﺤﺩﻯ ﻗﻴﻤﺘﻲ ﺍﻟﻤﺭﻗﻡ ‪ ControlUpdateMode‬ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫‪٣٧٦‬‬
‫ﻻ ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ﻋﻨـﺩ ﺘﻐﻴـﺭ ﻗﻴﻤـﺔ‬ ‫‪Never‬‬
‫ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ OnPropertyChanged‬ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ﻓﻭﺭ ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﻋﻨﺼـﺭ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻫﺫﻩ ﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪.‬‬

‫ﺍﻟﻘﻴﻤﺔ ﺍﻟﻔﺎﺭﻏﺔ ‪:NullValue‬‬


‫ﺘﺤﺩﺩ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﺴﺘﻭﻀﻊ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ‪ ،‬ﺇﺫﺍ ﻜﺎﻨـﺕ ﻟﻌﻨﺼـﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﻘﻴﻤـﺔ‬
‫‪ ..Nothing‬ﻻﺤﻅ ﺃﻥ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﺴـﺘﻜﻭﻥ ﺒـﻼ ﻓﺎﺌـﺩﺓ ﺇﺫﺍ ﻜﺎﻨـﺕ ﻟﻠﺨﺎﺼـﻴﺔ‬
‫‪ ControlUpdateMode‬ﺍﻟﻘﻴﻤﺔ ‪.None‬‬

‫ﻁﺭﻴﻘﺔ ﺘﺤﺩﻴﺙ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataSourceUpdateMode‬‬


‫ﺘﺤﺩﺩ ﻜﻴﻔﻴﺔ ﺘﺤﺩﻴﺙ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻨﺩ ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ‪ ،‬ﻭﻫـﻲ ﺘﺄﺨـﺫ‬
‫ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ DataSourceUpdateMode‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻻ ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻫﺫﺍ ﻴﻌﻨﻲ ﺃﻥ ﺃﻱ ﺘﻐﻴﻴﺭ ﻴﺤﺩﺙ ﻓﻲ‬ ‫‪Never‬‬
‫ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ )ﻜﺄﻥ ﻴﻜﺘﺏ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﻤﺜﻼ( ﻟـﻥ‬
‫ﻴﺅﺜﺭ ﻋﻠﻰ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻴﺘﻡ ﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﻤﺠـﺭﺩ ﺘﻐﻴﻴـﺭ ﻗﻴﻤـﺔ ﻋﻨﺼـﺭ‬ ‫‪OnProperty‬‬
‫ﺍﻟﻌﺭﺽ‪ ..‬ﻓﻤﺜﻼ ﻟﻭ ﻜﺘﺏ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ‪ ،‬ﻓﺈﻥ ﻤﺎ ﻜﺘﺒـﻪ‬ ‫‪Changed‬‬
‫ﻴﻭﻀﻊ ﻓﻭﺭﺍ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ OnValidation‬ﻻ ﻴﺘﻡ ﻨﻘل ﺍﻟﺘﻐﻴﻴﺭ ﻤﻥ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ﺇﻟﻰ ﻋﻨﺼـﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺇﻻ‬
‫ﻋﻨــﺩ ﺍﻨﻁــﻼﻕ ﺤــﺩﺙ ﺘﻤــﺎﻡ ﺍﻟﺘﺤﻘــﻕ ﻤــﻥ ﺍﻟﺼــﺤﺔ‬
‫‪ ،Control.Validated‬ﻭﺫﻟﻙ ﻋﻨـﺩ ﻤﻐـﺎﺩﺭﺓ ﺍﻷﺩﺍﺓ ﺃﻭ ﻋـﺭﺽ‬
‫ﻋﻨﺼﺭ ﺁﺨﺭ ﻤﻥ ﻋﻨﺎﺼﺭ ﺍﻟﻜﺎﺌﻥ )ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻜـﺎﺌﻥ ﻴﺤﺘـﻭﻱ ﻋﻠـﻰ‬
‫ﻤﺠﻤﻭﻋﺔ ﻤﻥ ﺍﻟﻌﻨﺎﺼﺭ ﻤﺜل ﺍﻟﻤﺼـﻔﻭﻓﺎﺕ(‪ ..‬ﻫـﺫﻩ ﻫـﻲ ﺍﻟﻘﻴﻤـﺔ‬
‫ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪.‬‬

‫‪٣٧٧‬‬
‫ﺍﻟﻘﻴﻤﺔ ﺍﻟﻔﺎﺭﻏﺔ ﻟﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataSourceNullValue‬‬
‫ﺘﺤﺩﺩ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﺴﺘﻭﻀﻊ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺇﺫﺍ ﻜﺎﻨـﺕ ﻟﻌﻨﺼـﺭ ﺍﻟﻌـﺭﺽ ﺍﻟﻘﻴﻤـﺔ‬
‫‪ ..Nothing‬ﻻﺤﻅ ﺃﻥ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ ﺴـﺘﻜﻭﻥ ﺒـﻼ ﻓﺎﺌـﺩﺓ ﺇﺫﺍ ﻜﺎﻨـﺕ ﻟﻠﺨﺎﺼـﻴﺔ‬
‫‪ DataSourceUpdateMode‬ﺍﻟﻘﻴﻤﺔ ‪.None‬‬

‫ﻨﺹ ﺍﻟﺘﻨﺴﻴﻕ ‪:FormatString‬‬


‫ﺘﺴﺘﻘﺒل ﻨﺼﺎ ﻴﻌﺭﻑ ﺼﻴﻐﺔ ﺍﻟﺘﻨﺴﻴﻕ ﺍﻟﺫﻱ ﺴﻴﺴﺘﺨﺩﻡ ﻟﺘﻨﺴﻴﻕ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻗﺒل ﻭﻀـﻌﻬﺎ ﻓـﻲ‬
‫ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ‪ ..‬ﻭﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻨﻔﺱ ﺼﻴﻎ ﺘﻨﺴﻴﻕ ﺍﻷﺭﻗﺎﻡ ﻭﺍﻟﺘﻭﺍﺭﻴﺦ ﺍﻟﺘـﻲ ﺘﻌﺭﻓﻨـﺎ‬
‫ﻋﻠﻴﻬﺎ ﻓﻲ ﻤﻼﺤﻕ ﻜﺘﺎﺏ ﺒﺭﻤﺠﺔ ﺇﻁﺎﺭ ﺍﻟﻌﻤل‪.‬‬

‫ﺘﻔﻌﻴل ﺍﻟﺘﻨﺴﻴﻕ ‪:FormattingEnabled‬‬


‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ True‬ﻓﺴﻴﺘﻡ ﺘﻁﺒﻴﻕ ﺍﻟﺘﻨﺴﻴﻕ ﺍﻟﻤﻭﻀـﺢ ﻓـﻲ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪ FormatString‬ﻋﻠﻰ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻗﺒل ﻭﻀﻌﻬﺎ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ‪.‬‬

‫ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺘﻨﺴﻴﻕ ‪:FormatInfo‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻭﺍﺠﻬﺔ ﻤـﺯﻭﺩ ﺍﻟﺘﻨﺴـﻴﻕ ‪ IFormatProvider‬ﺍﻟﺘـﻲ ﻴﺴـﺘﺨﺩﻤﻬﺎ ﻜـﺎﺌﻥ‬
‫ﺍﻻﺭﺘﺒــﺎﻁ‪ ..‬ﻟﻘــﺩ ﻋﺭﻓﻨــﺎ ﻓــﻲ ﻜﺘــﺎﺏ ﺒﺭﻤﺠــﺔ ﺇﻁــﺎﺭ ﺍﻟﻌﻤــل ﺃﻥ ﺍﻟﻔﺌــﺎﺕ‬
‫ـﺔ‬
‫ـل ﻭﺍﺠﻬـ‬
‫‪ CultureInfo ،DateTimeFormatInfo ،NumberFormatInfo‬ﺘﻤﺜـ‬
‫ﻤﺯﻭﺩ ﺍﻟﺘﻨﺴﻴﻕ ‪ ،IFormatProvider‬ﻟﻜﻥ ﺒﺎﻟﻨﺴﺒﺔ ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻨﺴﺨﺔ‬
‫ﻤﻥ ﺍﻟﻔﺌﺔ ‪ CultureInfo‬ﻟﻠﺘﺤﻜﻡ ﻓﻲ ﺍﻟﻠﻐﺔ ﻭﺍﻟﺜﻘﺎﻓﺔ ﺍﻟﺘﻲ ﺴﺘﺴﺘﺨﺩﻡ ﻋﻨﺩ ﺘﻨﺴﻴﻕ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪..‬‬
‫ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﺘﻌﺎﻤل ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻊ ﺍﻟﺜﻘﺎﻓﺔ )ﺍﻟﻠﻐﺔ( ﺍﻟﻤﻌﺭﻓﺔ ﻋﻠـﻰ ﺠﻬـﺎﺯ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ‪.‬‬

‫ﻭﻴﻤﺘﻠﻙ ﻜﺎﺌﻥ ﺍﻟﺭﺒﻁ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫‪٣٧٨‬‬
‫ﻗﺭﺍﺀﺓ ﺍﻟﻘﻴﻤﺔ ‪:ReadValue‬‬
‫ﺘﺠﺒﺭ ﺍﻷﺩﺍﺓ ﻋﻠﻰ ﻋﺭﺽ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻜﺘﺎﺒﺔ ﺍﻟﻘﻴﻤﺔ ‪:WriteValue‬‬


‫ﺘﺠﺒﺭ ﺍﻷﺩﺍﺓ ﻋﻠﻰ ﻭﻀﻊ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ‪ ،‬ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻭﻴﻤﻨﺤﻙ ﻜﺎﺌﻥ ﺍﻻﺭﺘﺒﺎﻁ ﺍﻷﺤﺩﺍﺙ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﻨﺴﻴﻕ ‪:Format‬‬
‫ﻴﻨﻁﻠﻕ ﻗﺒل ﻜﺘﺎﺒﺔ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ‪ ،‬ﻟﻴﺴﻤﺢ ﻟﻙ ﺒﺘﻨﺴﻴﻕ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﻗﺒل ﺃﻥ ﺘﻌﺭﻀﻬﺎ ﺍﻷﺩﺍﺓ‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨـﻭﻉ ‪،ConvertEventArgs‬‬
‫ﻭﻫﻭ ﻴﻤﺘﻠﻙ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫‪ DesiredType‬ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﻨﻭﻉ ‪ ،Type‬ﺍﻟﺫﻱ ﻴﻤﺜل ﻨﻭﻉ ﺒﻴﺎﻨﺎﺕ ﻋﻨﺼـﺭ‬


‫ﺍﻟﻌﺭﺽ‪.‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﻴﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺴﻴﺘﻡ‪ ‬ﻭﻀـﻌﻬﺎ ﻓـﻲ ﻋﻨﺼـﺭ‬ ‫‪Value‬‬
‫ﺍﻟﻌﺭﺽ‪ ..‬ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ Object‬ﻟﻠﺘﻌﺎﻤل ﻤـﻊ‬
‫ﺃﻨﻭﺍﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺨﺘﻠﻔﺔ‪.‬‬

‫ﻤﺜﻼ‪ ،‬ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻷﺩﺍﺓ ﺒﺘﻨﺴﻴﻕ ﺍﻟﺘﺎﺭﻴﺦ ﺍﻟﻘﺼﻴﺭ ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬


‫;)"‪e.Value = Convert.ToDateTime(e.Value).ToString("d/MM/yy‬‬

‫ﺘﺤﻭﻴل ‪:Parse‬‬
‫ﻴﻨﻁﻠﻕ ﻋﻨﺩﻤﺎ ﺘﺘﻐﻴ‪‬ﺭ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ‪ ..‬ﻓﺈﺫﺍ ﻜﻨﺕ ﻗـﺩ ﻏﻴـﺭﺕ ﺘﻨﺴـﻴﻕ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺤﺩﺙ ‪ ،Format‬ﻓﺎﺴﺘﺨﺩﻡ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﻻﺴﺘﺨﻼﺹ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻷﺼﻠﻴ‪‬ﺔ ﻭﺇﻋﺎﺩﺘﻬﺎ‬
‫ﺇﻟﻰ ﺍﻟﻨﻭﻉ ﺍﻟﻤﻨﺎﺴﺏ ﻟﻭﻀﻌﻬﺎ ﻓﻲ ﺨﺎﺼﻴﺔ ﺍﻟﻜﺎﺌﻥ‪.‬‬

‫‪٣٧٩‬‬
‫ﻭﺍﻟﻤﻌﺎﻤل ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻤﺎﺜل ﻟﺫﻟﻙ ﺍﻟﺨﺎﺹ‪ ‬ﺒﺎﻟﺤﺩﺙ ﺍﻟﺴﺎﺒﻕ‪ ..‬ﺍﻨﻅـﺭ ﻜﻴـﻑ ﻨﺴـﺘﻌﻴﺩ‬
‫ﺍﻟﺘﺎﺭﻴﺦ ﻤﻥ ﺍﻟﻨﺹ‪ ‬ﺍﻟﺫﻱ ﻨﺴﻘﻨﺎﻩ ﻓﻲ ﺍﻟﺤﺩﺙ ﺍﻟﺴﺎﺒﻕ‪:‬‬
‫;)) (‪e.Value = Date.Parse(e.Value.ToString‬‬

‫ﺍﻨﺘﻬﻰ ﺍﻟﺭﺒﻁ ‪:BindingComplete‬‬


‫ﻴﻨﻁﻠﻕ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﺒﻌﺩ ﻭﻀﻊ ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ‪ ،‬ﺃﻭ ﺒﻌﺩ ﻭﻀـﻊ‬
‫ﻗﻴﻤﺔ ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ﻓﻲ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨـﻭﻉ‬
‫‪ ،BindingCompleteEventArgs‬ﻭﻫﻭ ﻴﻤﺘﻠﻙ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻻﺭﺘﺒﺎﻁ ‪ Binding‬ﺍﻟﺫﻱ ﺃﻁﻠﻕ ﺍﻟﺤﺩﺙ‪.‬‬ ‫‪Binding‬‬


‫ﺘﺨﺒﺭﻙ ﺒﺎﺘﺠﺎﻩ ﻋﻤﻠﻴﺔ ﺍﻟﺭﺒﻁ‪ ،‬ﻭﻫﻲ ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗﻴﻤﺘﻲ ﺍﻟﻤـﺭﻗﻡ‬ ‫‪Binding‬‬
‫‪Complete‬‬
‫‪ BindingCompleteContext‬ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬ ‫‪Context‬‬
‫‪ :ControlUpdate -‬ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﺍﻷﺩﺍﺓ‪.‬‬
‫‪ :DataSourceUpdate -‬ﻴﺘﻡ ﺘﺤﺩﻴﺙ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﺘﻭﻀﺢ ﺤﺎﻟﺔ ﻋﻤﻠﻴﺔ ﺍﻟﺭﺒﻁ‪ ،‬ﻭﻫﻲ ﺘﻌﻴﺩ ﺇﺤـﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ‬ ‫‪Binding‬‬
‫‪Complete‬‬
‫‪ BindingCompleteState‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬ ‫‪State‬‬
‫‪ :Success -‬ﻨﺠﺤﺕ ﻋﻤﻠﻴﺔ ﺍﻟﺭﺒﻁ‪.‬‬
‫‪ :DataError -‬ﻓﺸﻠﺕ ﻋﻤﻠﻴﺔ ﺍﻟﺭﺒﻁ ﺒﺴـﺒﺏ ﺨﻁـﺄ ﻓـﻲ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻴﺭﻓﺽ ﻤﺼـﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺃﻭ‬
‫ﺍﻷﺩﺍﺓ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺠﺩﻴﺩﺓ‪ ،‬ﻟﻜﻥ ﻻ ﻴﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ‪.‬‬
‫‪ :Exception -‬ﻓﺸﻠﺕ ﻋﻤﻠﻴﺔ ﺍﻟﺭﺒﻁ ﻭﺤـﺩﺙ ﺨﻁـﺄ ﻓـﻲ‬
‫ﺍﻟﺒﺭﻨﺎﻤﺞ‪.‬‬
‫ﺘﻌﻴﺩ ﻨﺼﺎ ﻴﺼﻑ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ ﻓﻲ ﻋﻤﻠﻴﺔ ﺍﻟﺭﺒﻁ‪.‬‬ ‫‪ErrorText‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻻﺴﺘﺜﻨﺎﺀ ‪ Exception‬ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺘﻔﺎﺼﻴل‬ ‫‪Exception‬‬
‫ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ ﻋﻨﺩ ﺭﺒﻁ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫‪٣٨٠‬‬
‫ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻌﺭﻑ ﻜﺎﺌﻥ ﺭﺒﻁ ‪ 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‬‬

‫ﻭﻴﻤﺘﻠﻙ ﻫﺫﺍ ﺍﻟﺴﺠل ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻋﻨﺼﺭ ﺍﻟﺭﺒﻁ ‪:BindingMember‬‬


‫ﺘﻌﻴﺩ ﺍﻟﻤﺴﺎﺭ ﺍﻟﻜﺎﻤل ﻟﻌﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ...‬ﻭﻓﻲ ﺍﻟﻤﺜﺎل ﺍﻟﺫﻱ ﻀﺭﺒﻨﺎﻩ ﺴﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫ﺍﻟﻨﺹ‪. Books.Book :‬‬

‫ﺤﻘل ﺍﻟﺭﺒﻁ ‪:BindingField‬‬


‫ﺘﻌﻴﺩ ﺍﺴﻡ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ )ﺒﺩﻭﻥ ﺍﻟﻤﺴﺎﺭ ﺍﻟﻜﺎﻤل(‪ ..‬ﻫﺫﺍ ﻴﻌﻨﻲ ﺃﻨﻬﺎ ﺴﺘﻌﻴﺩ ﺍﻟـﻨﺹ ‪Book‬‬
‫ﻓﻲ ﺍﻟﻤﺜﺎل ﺍﻟﺴﺎﺒﻕ‪.‬‬

‫‪٣٨٢‬‬
‫ﻤﺴﺎﺭ ﺍﻟﺭﺒﻁ ‪: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‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﺘﺤﺘﻭﻱ ﻋﻠﻰ ‪:Contains‬‬


‫ﺘﻌﻴﺩ ‪ True‬ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ ﺍﻟﻤﺤـﺩﺩ ﻓـﻲ ﺍﻟﻤﻌـﺎﻤﻼﺕ‪..‬‬
‫ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻨﻔﺱ ﺼﻴﻐﺘﻲ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ‪.Item‬‬

‫ﺘﺤﺩﻴﺙ ﺍﻟﺭﺒﻁ ‪:UpdateBinding‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﺠﻤﻭﻋﺔ ﻤﺤﺘﻭﻯ ﺍﻟﺭﺒﻁ ‪ ،BindingContext‬ﻭﻜـﺎﺌﻥ ﺍﻟـﺭﺒﻁ‬
‫‪ Binding‬ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺇﺯﺍﻟﺘﻪ ﻤﻥ ﻤﺠﻤﻭﻋﺔ ﺃﺨﺭﻯ ﻭﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﻤﺤﺩﺩﺓ ﻓـﻲ‬
‫ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل‪.‬‬
‫‪٣٨٥‬‬
‫ﻓﺌﺔ ﺃﺴﺎﺱ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ ‪BindingManagerBase Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﺴﺎﺴﻴﺔ ﻤﺠﺭﺩﺓ ‪ ،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‬‬

‫ﻫل ﺍﻟﺭﺒﻁ ﻤﺘﻭﻗﻑ ‪:IsBindingSuspended‬‬


‫ﺘﻌﻴﺩ ‪ True‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺭﺒﻁ ﺒﻴﻥ ﺍﻷﺩﺍﺓ ﻭﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺘﻭﻗﻔﺎ ﺤﺎﻟﻴﺎ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪٣٨٨‬‬
‫ﺇﻀﺎﻓﺔ ﺠﺩﻴﺩ ‪:AddNew‬‬
‫ﺘﻀﻴﻑ ﻋﻨﺼﺭﺍ ﺠﺩﻴﺩ ﺇﻟﻰ ﻗﺎﺌﻤﺔ ﻋﻨﺎﺼﺭ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺨﻁـﺄ‬
‫ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻷﺩﺍﺓ ﻤﺭﺘﺒﻁﺔ ﺒﻜﺎﺌﻥ ﺒﺴﻴﻁ ﻻ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﺩﺍﺨﻠﻴـﺔ‪ ،‬ﺃﻭ ﺇﺫﺍ‬
‫ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﻤﺼﻔﻭﻓﺔ‪ ..‬ﻭﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻊ ﺼﻔﻭﻑ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺘﻜـﻭﻥ ﺤﺎﻟـﺔ‬
‫ﺍﻟﺴﺠل ﺍﻟﺠﺩﻴﺩ ﻫﻲ ‪ ،RowState.Added‬ﻭﻟﻥ ﺘﺘﻡ ﺇﻀﺎﻓﺘﻪ ﺇﻟﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻻ ﻋﻨـﺩ‬
‫ﺘﺤﺩﻴﺜﻬﺎ‪.‬‬

‫ﺤﺫﻑ ﻤﻥ ﻤﻭﻀﻊ ‪:RemoveAt‬‬


‫ﺘﺤﺫﻑ ﺍﻟﻌﻨﺼﺭ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﻤﻭﻀﻊ ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل‪ ..‬ﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺨﻁﺄ ﻓـﻲ‬
‫ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻷﺩﺍﺓ ﻤﺭﺘﺒﻁﺔ ﺒﻜﺎﺌﻥ ﺒﺴﻴﻁ ﻻ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﺩﺍﺨﻠﻴـﺔ‪ ،‬ﺃﻭ ﻜـﺎﻥ‬
‫ﺍﻟﻜﺎﺌﻥ ﻤﺼﻔﻭﻓﺔ ﺃﻭ ﻜﺎﻥ ﻻ ﻴﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ..IBindingList‬ﻭﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‬
‫ﻟﻠﺘﻌﺎﻤل ﻟﺤﺫﻑ ﺼﻑ ﺒﻴﺎﻨﺎﺕ‪ ،‬ﻓﺴﻴﺘﻡ ﺘﻐﻴﺭ ﺤﺎﻟﺔ ﺤﺎﻟﺘﻪ ﺇﻟﻰ ‪ ،Deleted‬ﻭﻟﻥ ﻴﺘﻡ ﺤﺫﻓﻪ ﻤﻥ‬
‫ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻻ ﻋﻨﺩ ﺘﺤﺩﻴﺜﻬﺎ‪.‬‬

‫ﺇﻟﻐﺎﺀ ﺍﻟﺘﺤﺭﻴﺭ ﺍﻟﺤﺎﻟﻲ ‪:CancelCurrentEdit‬‬


‫ﹸﺘﹸﻠﻐﻲ ﻋﻤﻠﻴ‪‬ﺔ ﺍﻟﺘﺤﺭﻴﺭ ﺍﻟﺤﺎﻟ ‪‬ﻴ‪‬ﺔ ﻭﺘﻌﻴﺩ ﺇﻟﻰ ﺍﻟﺴﺠل ﻗﻴﻤﻪ ﺍﻷﺼﻠﻴﺔ‪ ..‬ﻭﻟﻴﺱ ﻟﻬﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺃﻱ‬
‫ﺘﺄﺜﻴﺭ ﺇﻻ ﻋﻠﻰ ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﺘﻲ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ IEditableObject‬ﻤﺜل ﻓﺌﺔ ﺴﺠلّ ﺍﻟﻌـﺭﺽ‬
‫‪.DataRowView Class‬‬

‫ﺇﻨﻬﺎﺀ ﺍﻟﺘﺤﺭﻴﺭ ﺍﻟﺤﺎﻟﻲ ‪:EndCurrentEdit‬‬


‫ﹸﺘﹸﻨﻬﻲ ﻋﻤﻠﻴ‪‬ﺔ ﺍﻟﺘﺤﺭﻴﺭ ﻤﻊ ﺇﺒﻘﺎﺀ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺤﺩﺜﺕ ﻟﻠﺴﺠل‪ ..‬ﻭﻟﻴﺱ ﻟﻬﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺃﻱ‬
‫ﺘﺄﺜﻴﺭ ﺇﻻ ﻋﻠﻰ ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﺘﻲ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ IEditableObject‬ﻤﺜل ﻓﺌﺔ ﺴﺠلّ ﺍﻟﻌـﺭﺽ‬
‫‪.DataRowView Class‬‬

‫‪٣٨٩‬‬
‫ﻤﻌﺭﻓﺔ ﺨﺼﺎﺌﺹ ﺍﻟﻌﻨﺼﺭ ‪: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‬ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﺇﻴﻘﺎﻑ ﺍﻻﺭﺘﺒﺎﻁ ‪:SuspendBinding‬‬


‫ﺘﻭﻗﻑ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻴﻥ ﺍﻷﺩﻭﺍﺕ ﻭﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺅﻗﺘﺎ‪ ..‬ﻭﻗﺩ ﺍﺴـﺘﺨﺩﻤﻨﺎ ﻫـﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫ﻹﻴﻘﺎﻑ ﺍﻟﺭﺒﻁ ﻓﻲ ﺍﻟﺘﻁﺒﻴﻕ ‪ ،BindingToArray‬ﻭﺫﻟﻙ ﻋﻨﺩ ﺇﺯﺍﻟﺔ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﻤـﻥ‬
‫ﻤﺭﺒﻊ ﺍﻻﺨﺘﻴﺎﺭ ‪ CheckBox‬ﺍﻟﻤﻭﺠﻭﺩ ﺃﺴﻔل ﺍﻟﻨﻤﻭﺫﺝ‪.‬‬

‫ﻤﻭﺍﺼﻠﺔ ﺍﻻﺭﺘﺒﺎﻁ ‪:ResumeBinding‬‬


‫ﺘﺴﺘﺄﻨﻑ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻴﻥ ﺍﻷﺩﻭﺍﺕ ﻭﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻷﺤﺩﺍﺙ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻟﺭﺒﻁ ﺍﻜﺘﻤل ‪:BindingComplete‬‬


‫ﻤﻤﺎﺜل ﻟﻠﺤﺩﺙ ‪.Binding.BindingComplete‬‬

‫‪٣٩٠‬‬
‫ﺍﻟﻤﻭﻀﻊ ﺘﻐﻴﺭ ‪:PositionChanged‬‬
‫ﻴﻨﻁﻠﻕ ﺇﺫﺍ ﺘﻐﻴ‪‬ﺭﺕ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪ ..Position‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ‪ e‬ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨﻭﻉ‬
‫‪ ،EventArgs‬ﺍﻟﺫﻱ ﻻ ﻴﺤﻤل ﺃﻴﺔ ﻤﻌﻠﻭﻤﺎﺕ ﻫﺎﻤﺔ ﻋﻥ ﺍﻟﺤﺩﺙ‪.‬‬
‫ﻭﻗﺩ ﺍﺴﺘﺨﺩﻤﻨﺎ ﻫﺫﺍ ﺍﻟﺤـﺩﺙ ﻓـﻲ ﺍﻟﺘﻁﺒﻴـﻕ ‪ ،BindingToArray‬ﻟﺘﺤـﺩﻴﺙ ﺍﻟﻤﻭﻀـﻊ‬
‫ﺍﻟﻤﻌﺭﻭﺽ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ‪ TxtPos‬ﺍﻟﻤﻭﺠﻭﺩ ﺃﺴﻔل ﺍﻟﻨﻤﻭﺫﺝ‪ ،‬ﻜﻠﻤـﺎ ﺘﻐﻴـﺭ ﺍﻟﻤﻭﻀـﻊ‬
‫ﺍﻟﺤﺎﻟﻲ ﺒﺴﺒﺏ ﻀﻐﻁ ﺃﺯﺭﺍﺭ ﺍﻟﺘﺤﺭﻙ‪ ..‬ﻻﺤﻅ ﺃﻨﻨﺎ ﺭﺒﻁﻨﺎ ﻫﺫﺍ ﺍﻟﺤﺩﺙ ﺒﺎﻹﺠﺭﺍﺀ ﺍﻟﻤﺴﺘﺠﻴﺏ‬
‫ﻟﻪ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺠﻤﻠﺔ ‪ AddHandler‬ﻓﻲ ﺤﺩﺙ ﺘﺤﻤﻴل ﺍﻟﻨﻤﻭﺫﺝ ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫;‪Bm.PositionChanged += Bm_PositionChanged‬‬

‫ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﺘﻐﻴﺭ ‪:CurrentChanged‬‬


‫ﻴﻨﻁﻠﻕ ﺇﺫﺍ ﺘﻐﻴ‪‬ﺭﺕ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼﻴﺔ ‪.Current‬‬

‫ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ ﺘﻐﻴﺭ ‪:CurrentItemChanged‬‬


‫ﻴﻨﻁﻠﻕ ﺇﺫﺍ ﺘﻐﻴﺭﺕ ﺤﺎﻟﺔ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ ﺍﻟﺫﻱ ﺘﻌﺭﻀﻪ ﺍﻷﺩﺍﺓ‪ ..‬ﻴﺤﺩﺙ ﻫـﺫﺍ ﺇﺫﺍ ﺘﻐﻴـﺭﺕ‬
‫ﻗﻴﻤﺔ ﺇﺤﺩﻯ ﺨﺼﺎﺌﺹ ﻫﺫﺍ ﺍﻟﻌﻨﺼﺭ‪ ،‬ﺃﻭ ﺇﺫﺍ ﺘﻡ ﺍﺴﺘﺒﺩﺍﻟﻪ ﺃﻭ ﺤﺫﻓﻪ‪.‬‬

‫ﺨﻁﺄ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataError‬‬


‫ﻴﻨﻁﻠﻕ ﺇﺫﺍ ﻗﺎﻡ ﻤﺩﻴﺭ ﺍﻟﺭﺒﻁ ﺒﻤﻌﺎﻟﺠﺔ ﺨﻁﺄ ﺤﺩﺙ ﺃﺜﻨﺎﺀ ﻋﻤﻠﻴﺔ ﺍﻟﺭﺒﻁ‪ ..‬ﻭﺍﻟﻤﻌﺎﻤل ﺍﻟﺜـﺎﻨﻲ ‪e‬‬
‫ﻟﻬﺫﺍ ﺍﻟﺤﺩﺙ ﻤﻥ ﺍﻟﻨـﻭﻉ ‪ ،BindingManagerDataErrorEventArgs‬ﻭﻫـﻭ ﻴﻤﺘﻠـﻙ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ‪ Exception‬ﺍﻟﺘﻲ ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻻﺴﺘﺜﻨﺎﺀ ‪ Exception‬ﺍﻟﺫﻱ ﻴﺤﺘﻭﻱ ﻤﻌﻠﻭﻤـﺎﺕ‬
‫ﻋﻥ ﺍﻟﺨﻁﺄ ﺍﻟﺫﻱ ﺤﺩﺙ‪.‬‬

‫‪٣٩١‬‬
‫ﻓﺌﺔ ﻤﺩﻴﺭ ﺍﻟﺨﺎﺼﻴﺔ ‪PropertyManager Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ ،BindingManagerBase‬ﻭﻫﻲ ﺘﻌﻤل ﻜﻤﺩﻴﺭ ﻴﺘﺤﻜﻡ ﻓﻲ ﺭﺒـﻁ ﻜـﺎﺌﻥ‬
‫ﺒﺴﻴﻁ ﻟﻪ ﻋﺩﺓ ﺨﺼﺎﺌﺹ ﻭﻻ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﻋﻨﺎﺼﺭ ﺩﺍﺨﻠﻴﺔ‪.‬‬
‫ﻭﻻ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺃﻴﺔ ﺨﺼﺎﺌﺹ ﺃﻭ ﻭﺴﺎﺌل ﺃﻭ ﺃﺤﺩﺍﺙ ﺠﺩﻴﺩﺓ ﻏﻴﺭ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪.‬‬

‫ﻓﺌﺔ ﻤﺩﻴﺭ ﺍﻟﺘﺴﻠﺴل ‪CurrencyManager Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ ،BindingManagerBase‬ﻭﻫﻲ ﺘﻌﻤل ﻜﻤﺩﻴﺭ ﻴﺘﺤﻜﻡ ﻓﻲ ﺭﺒـﻁ ﻜـﺎﺌﻥ‬
‫ﻤﺭﻜﺏ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﻋﻨﺎﺼﺭ ﺩﺍﺨﻠﻴﺔ‪.‬‬
‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻌﻨﺼﺭﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ‪:‬‬

‫ﺍﻟﻘﺎﺌﻤﺔ ‪:List‬‬
‫ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻥ ﻨﻭﻉ ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ‪ ،IList‬ﻫﻲ ﺘﻌﻴﺩ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ ﺍﻟﺘـﻲ ﻴﺤﺘﻭﻴﻬـﺎ‬
‫ﺍﻟﻜﺎﺌﻥ‪.‬‬

‫ﺇﻨﻌﺎﺵ ‪:Refresh‬‬
‫ﺘﻌﻴﺩ ﻤلﺀ ﻤﺼﻔﻭﻓﺔ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻤﺭﺘﺒﻁﺔ‪ ..‬ﺍﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﻨﺩ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻜﺎﺌﻨـﺎﺕ ﻻ‬
‫ﺘﻌﻁﻲ ﺘﻨﺒﻴﻬﺎ ﻋﻨﺩ ﺘﻐﻴﺭ ﻋﻨﺎﺼﺭﻫﺎ‪ ،‬ﻤﺜل ﺍﻟﻤﺼﻔﻭﻓﺎﺕ ‪.Arrays‬‬

‫‪٣٩٢‬‬
‫ﺭﺒﻁ ﺍﻷﺩﻭﺍﺕ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ‪:‬‬
‫ﻴﻘﺩﻡ ﻟﻙ ﻤﺼﻤﻡ ﺍﻟﻨﻤﺎﺫﺝ ‪ Form Designer‬ﻓﻲ ﺩﻭﺕ ﻨﺕ ﺘﺴﻬﻴﻼﺕ ﻜﺜﻴﺭﺓ ﻟﺭﺒﻁ ﺍﻷﺩﻭﺍﺕ ﻓﻲ‬
‫ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ‪ ،‬ﻟﺘﻘﻠﻴل ﺍﻟﻜﻭﺩ ﺍﻟﺫﻱ ﺘﺤﺘﺎﺠﻪ ﻷﺩﺍﺀ ﻫﺫﻩ ﺍﻟﻌﻤﻠﻴﺔ‪ ..‬ﻭﻟﻜﻲ ﺘﺭﻯ ﻫﺫﺍ ﻋﻤﻠﻴـﺎ‪ ،‬ﺍﺒـﺩﺃ‬
‫ﻤﺸﺭﻭﻋﺎ ﺠﺩﻴﺩﺍ ﻭﺃﺴﻤﻪ ‪ ،ViewAndEditBooks‬ﻭﺼﻤ‪‬ﻡ ﻭﺍﺠﻬﺔ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻜﻤﺎ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‬
‫ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺃﻀﻑ ﺇﻟﻰ ﺍﻟﻨﻤﻭﺫﺝ ﻤﻬﻴﺊ ﺒﻴﺎﻨﺎﺕ ﺍﺴﻤﻪ ‪ ،DaAuthorBooks‬ﻭﺍﺠﻌﻠﻪ ﻴﺴـﺘﺨﺩﻡ ﺍﻻﺴـﺘﻌﻼﻡ‬


‫ﺍﻟﺘﺎﻟﻲ ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﺃﺴﻤﺎﺀ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺃﺴﻤﺎﺀ ﻜﺘﺒﻬﻡ‪:‬‬
‫‪SELECT Authors.Author, Books.ID, Books.Book‬‬
‫‪FROM Authors INNER JOIN‬‬
‫‪Books ON Authors.ID = Books.AuthorID‬‬
‫ﺃﻨﺸﺊ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ‪ Typed DataSet‬ﻤﻥ ﻫﺫﺍ ﺍﻟﻤﻬﻴﺊ ﺒﺎﻟﻁﺭﻴﻘﺔ ﺍﻟﻤﻌﻬـﻭﺩﺓ‪،‬‬
‫ﻭﺃﺴــﻤﻬﺎ ‪ ،DsAuthorBooks‬ﻭﺃﻀــﻑ ﻨﺴــﺨﺔ ﻤﻨﻬــﺎ ﺇﻟــﻰ ﺍﻟﻨﻤــﻭﺫﺝ ﺍﺴــﻤﻬﺎ‬
‫‪.DsAuthorBooks1‬‬
‫ﺍﻵﻥ‪ ،‬ﻨﺭﻴﺩ ﺭﺒﻁ ﻤﺭ‪‬ﺒ‪‬ﻌﺎﺕ ﺍﻟﻨﺼﻭﺹ ﺒﻬﺫﻩ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ..‬ﺍﺘﺒﻊ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -‬ﺤﺩﺩ ﻤﺭﺒ‪‬ﻊ ﺍﻟﻨﺹ‪ ‬ﺍﻟﺫﻱ ﺴﻴﻌﺭﺽ ﺭﻗﻡ ﺍﻟﻜﺘﺎﺏ‪ ،‬ﻭﻤﻥ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ﺤـﺩ‪‬ﺩ ﺍﻟﻤﻘﻁـﻊ‬
‫ﺍﻟﻤﺴﻤ‪‬ﻰ ‪ ..DataBinding‬ﺴﺘﺠﺩﻩ ﻓﻲ ﺒﺩﺍﻴﺔ ﺍﻟﺨﺼﺎﺌﺹ‪ ،‬ﺨﺭﻭﺠـﺎ ﻋـﻥ ﺍﻟﺘﺭﺘﻴـﺏ‬
‫ﺍﻷﺒﺠﺩﻱ‪ ‬ﻟﻠﺨﺼﺎﺌﺹ‪ ،‬ﻭﺴﻴﻜﻭﻥ ﻤﻭﻀﻭﻋﺎ ﺒﻴﻥ ﻗﻭﺴﻴﻥ‪.‬‬

‫‪٣٩٣‬‬
‫‪ -‬ﺍﻀﻐﻁ ﺍﻟﻌﻼﻤﺔ ‪ +‬ﺍﻟﻤﺠﺎﻭﺭﺓ ﻟﻠﻤﻘﻁﻊ ‪ ،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‬‬


‫ﻀﻊ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻪ‪.‬‬

‫ﻋﻨﺼﺭ ﺍﻟﻌﺭﺽ ‪:DisplayMember‬‬


‫ﺘﺴﺘﻘﺒل ﻨﺼﺎ‪ ،‬ﻴﺤﺩﺩ ﺍﺴﻡ ﺨﺎﺼﻴﺔ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺘﻲ ﺴﻴﺘﻡ ﻋﺭﺽ ﻗﻴﻤﺘﻪ ﻓـﻲ ﺍﻟﻘﺎﺌﻤـﺔ‪ ..‬ﻭﻓـﻲ‬
‫ﺍﻟﻤﺸﺭﻭﻉ ‪ BindingListToArray‬ﺠﻌﻠﻨﺎ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ "‪ ،"Name‬ﻟﻬﺫﺍ ﺘﻌـﺭﺽ‬
‫ﺍﻟﻘﺎﺌﻤﺔ ﺃﺴﻤﺎﺀ ﺍﻟﻁﻼﺏ‪.‬‬
‫ﻻﺤﻅ ﺃﻨﻙ ﻟﻭ ﺘﺭﻜﺕ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻓﺎﺭﻏﺔ‪ ،‬ﻓﺴﺘﻌﺭﺽ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻨﺹ ﺍﻟﺫﻱ ﺘﻌﻴﺩﻩ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪ ToString‬ﺍﻟﺨﺎﺼﺔ ﺒﻜل ﻋﻨﺼﺭ ﻤﻥ ﻋﻨﺎﺼﺭ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﺤﺩﺩﺓ ‪:SelectedValue‬‬


‫ﺘﻌﻴﺩ ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﺤـﺩﺩﺓ ﺤﺎﻟﻴـﺎ ﻓـﻲ ﺍﻟﻘﺎﺌﻤـﺔ‪ ،‬ﻭﻫـﻲ ﺘﺘﻭﻗـﻑ ﻋﻠـﻰ ﻗﻴﻤـﺔ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪.ValueMember‬‬
‫‪٣٩٧‬‬
‫ﻋﻨﺼﺭ ﺍﻟﻘﻴﻤﺔ ‪:ValueMember‬‬
‫ﺘﺴﺘﻘﺒل ﻨﺼﺎ‪ ،‬ﻴﺤﺩﺩ ﺍﺴﻡ ﺨﺎﺼﻴﺔ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺘﻲ ﺴﺘﺘﻡ ﻗﺭﺍﺀﺘﻬـﺎ ﻋﻨـﺩ ﺍﺴـﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪ ..SelectedValue‬ﻭﻓﻲ ﺍﻟﻤﺸـﺭﻭﻉ ‪ BindingListToArray‬ﺠﻌﻠﻨـﺎ ﻗﻴﻤـﺔ ﻫـﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ "‪ ،"Id‬ﻟﻬﺫﺍ ﻓﺈﻥ ﺍﻟﺨﺎﺼﻴﺔ ‪ SelectedValue‬ﺘﻌﻴﺩ ﺭﻗﻡ ﺍﻟﻁﺎﻟﺏ ﺍﻟﻤﺤﺩﺩ ﺤﺎﻟﻴﺎ ﻓﻲ‬
‫ﺍﻟﻘﺎﺌﻤﺔ‪ ،‬ﻭﻴﻤﻜﻨﻙ ﺘﺠﺭﺒﺔ ﻫﺫﺍ ﺒﻀﻐﻁ ﺍﻟﺯﺭ ﺍﻟﻤﻭﺠﻭﺩ ﺃﺴﻔل ﺍﻟﻘﺎﺌﻤﺔ‪.‬‬
‫ﻭﻟﻭ ﺘﺭﻜﺕ ﺍﻟﺨﺎﺼﻴﺔ ‪ ValueMember‬ﻓﺎﺭﻏـﺔ‪ ،‬ﻓـﺈﻥ ﺍﻟﺨﺎﺼـﻴﺔ ‪SelectedValue‬‬
‫ﺴﺘﻌﻴﺩ ﺍﻟﻌﻨﺼﺭ ﺍﻟﻤﺤﺩﺩ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺤﺎﻟﻴﺎ ﻤﺜﻠﻬﺎ ﻤﺜل ﺍﻟﺨﺎﺼﻴﺔ ‪.SelectedItem‬‬

‫ﺘﻌﺎل ﻨﺴﺘﺨﺩﻡ ﻫﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ ﻓﻲ ﺘﻁﻭﻴﺭ ﺍﻟﻤﺸﺭﻭﻉ ‪ ،ViewAndEditBooks‬ﻓﻬـﻭ ﻴﺒـﺩﻭ‬


‫ﻋﻘﻴﻤﺎ ﻟﻭ ﺤﺎﻭﻟﺕ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻟﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻀﺨﻤﺔ‪ ،‬ﺤﻴﺙ ﺇﻥ‪ ‬ﺍﻟﺘﺤـ ‪‬ﺭ‪‬ﻙ‬
‫ﺒﻴﻥ ﺁﻻﻑ ﺍﻟﺴﺠﻼﺕ ﻭﺍﺤﺩﺍ ﺒﻌﺩ ﺁﺨﺭ ﻴﺒﺩﻭ ﻨﻭﻋﺎ ﻤﻥ ﺍﻟﻌﺒﺙ‪ ..‬ﻟﻬﺫﺍ ﻻ ﺒﺩ‪ ‬ﻤﻥ ﺇﻨﺸﺎﺀ ﻭﺍﺠﻬﺔ ﺃﻜﺜﺭ‬
‫ﻤﻼﺀﻤﺔ ﻟﻬﺫﺍ ﺍﻟﻭﻀﻊ‪ ..‬ﻭﻜﺤل ﻤﺒﺩﺌﻲ‪ ،‬ﺘﻌﺎل ﻨﺴﺘﺨﺩﻡ ﻗﺎﺌﻤـﺔ ﻤﺭﻜﹼﹼﺒـﺔ ‪ ComboBox‬ﻟﻌـﺭﺽ‬
‫ﺃﺴﻤﺎﺀ ﺍﻟﻜﺘﺏ‪ ،‬ﺒﺤﻴﺙ ﻴﺨﺘﺎﺭ ﺍﻟﻤﺴﺘﺨﺩﻡ ﻤﻨﻬﺎ ﺍﺴﻡ ﺍﻟﻜﺘﺎﺏ ﻤﺒﺎﺸـﺭﺓ ﺒـﺩﻻ ﻤـﻥ ﻀـﻐﻁ ﺃﺯﺭﺍﺭ‬
‫ﺍﻻﻨﺘﻘﺎل‪ ..‬ﺼﻤ‪‬ﻡ ﺍﻟﻨﻤﻭﺫﺝ ﻟﻴﺒﺩﻭ ﻜﻤﺎ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪ ،‬ﻭﻫﻭ ﻤﻭﺠﻭﺩ ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ ‪BookList‬‬
‫ﺍﻟﻤﺭﻓﻕ ﺒﻬﺫﺍ ﺍﻟﻜﺘﺎﺏ‪:‬‬

‫‪٣٩٨‬‬
‫ﺘﻌﺭﻑ ﻁﺒﻌﺎ ﻜﻴﻑ ﺘﺭﺒﻁ ﻤﺭﺒﻌﻲ ﺍﻟﻨﺹ ﺍﻟﻠﺫﻴﻥ ﻴﻌﺭﻀﺎﻥ ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ﻭﺭﻗﻡ ﺍﻟﻜﺘﺎﺏ‪ ..‬ﻤﺎ ﻴﻬﻤﻨـﺎ‬
‫ﺍﻵﻥ ﻫﻭ ﻜﻴﻔﻴﺔ ﺭﺒﻁ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﺭﻜﺒﺔ‪.‬‬
‫ﺤﺩ‪‬ﺩ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﺭ ﹼﻜﹼﺒﺔ‪ ،‬ﻭﻤﻥ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ﺍﺨﺘﺭ ﺍﻟﺨﺎﺼـ ‪‬ﻴ‪‬ﺔ ‪ ،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‬ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪.‬‬

‫ﻫﺫﻩ ﺍﻟﻨﺎﻓﺫﺓ ﺘﻌﺭﺽ ﺠﻤﻴﻊ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬


‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ )ﻤﺜـل ﻤﺠﻤﻭﻋـﺎﺕ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ(‪ ..‬ﻭﺴﺘﺠﺩ ﻓﻴﻬـﺎ ﺍﺴـﻡ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ BoksDataBooks‬ﺍﻟﺘﻲ ﺃﻨﺸـﺄﻫﺎ‬
‫ﻤﻌﺎﻟﺞ ﻤﺼـﺎﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ،‬ﻭﻟـﻭ ﺃﺴـﺩﻟﺕ‬
‫ﻋﻨﺎﺼﺭﻫﺎ‪ ،‬ﻓﺴﺘﺠﺩ ﺘﺤﺘﻬﺎ ﺠﺩﻭﻟﻲ ﺍﻟﻤـﺅﻟﻔﻴﻥ‬
‫ﻭﺍﻟﻜﺘﺏ‪ ،‬ﻭﻟﻭ ﺃﺴﺩﻟﺕ ﻜﻼ ﻤﻨﻬﻤﺎ ﻓﺴﺘﺠﺩ ﺘﺤﺘﻪ‬
‫ﺃﺴﻤﺎﺀ ﺃﻋﻤﺩﺘﻪ‪.‬‬
‫ﻭﻴﺘﻴﺢ ﻟﻙ ﻤﺘﺼﻔﺢ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻀـﺎﻓﺔ‬
‫ﻤﺼﺎﺩﺭ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻴـﺩ ﻭﺘﻌـﺩﻴل ﺍﻟﻤﺼـﺎﺩﺭ‬
‫ﺍﻟﻤﻭﺠﻭﺩﺓ ﺒﻪ‪ ،‬ﻭﺫﻟﻙ ﻤﻥ ﺨﻼل ﺍﻷﺯﺭﺍﺭ ﺍﻟﺘﻲ‬
‫ﺘﻅﻬﺭ ﺃﻋﻼﻩ‪ ،‬ﻭﻫﻲ‪:‬‬

‫‪ :Add New Data Source‬ﻴﺅﺩﻱ ﻀﻐﻁ ﻫﺫﺍ ﺍﻟﺯﺭ ﺇﻟﻰ ﺘﺸﻐﻴل ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴـﺤﺭﻱ‬
‫ﻟﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫‪ :Edit Data Source With Designer‬ﻴﺅﺩﻱ ﻀﻐﻁ ﻫـﺫﺍ ﺍﻟـﺯﺭ ﻓـﺘﺢ ﻤﺼـﻤﻡ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻟﺘﺤﺭﻴﺭﻫﺎ‪.‬‬

‫‪ :Configure Data Source With Wizard‬ﻴﺅﺩﻱ ﻀﻐﻁ ﻫﺫﺍ ﺍﻟﺯﺭ ﺇﻟﻰ ﺘﺸـﻐﻴل‬
‫ﺍﻟﻤﻌﺎﻟﺞ ﺍﻟﺴﺤﺭﻱ ﻟﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﻜﻨﻪ ﻴﻌﺭﺽ ﻨﺎﻓﺫﺓ ﺍﺨﺘﻴﺎﺭ ﻜﺎﺌﻨﺎﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﻤﺒﺎﺸﺭﺓ‪ ،‬ﻟﺘﺴﺘﻁﻴﻊ ﺇﻀﺎﻓﺔ ﺍﻟﺠﺩﺍﻭل ﺃﻭ ﺤﺫﻓﻬﺎ‪.‬‬

‫‪٤٠٨‬‬
‫‪ :Refresh‬ﻴﺅﺩﻱ ﻀﻐﻁ ﻫﺫﺍ ﺍﻟﺯﺭ ﺇﻟﻰ ﺇﻨﻌﺎﺵ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﻠﺘﻔﺎﻋـل ﻤـﻊ ﺃﻴـﺔ‬
‫ﺘﻐﻴﻴﺭﺍﺕ ﺤﺩﺜﺕ ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻭﻴﻘﺩﻡ ﻟﻙ ﻤﺘﺼﻔﺢ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺴﻬﻴﻼﺕ ﻫﺎﺌﻠﺔ ﻟﺘﺼﻤﻴﻡ ﺍﻟﻨﻤﺎﺫﺝ ﺍﻟﺘﻲ ﺘﻌـﺭﺽ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪،‬‬
‫ﻓﺒﻤﺠﺭﺩ ﺴﺤﺏ ﺍﺴﻡ ﺃﻱ ﺤﻘل ﻤﻥ ﻤﺘﺼﻔﺢ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺇﻟﻘﺎﺌﻪ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ‪ ،‬ﻴﺘﻡ ﺇﻀـﺎﻓﺔ‬
‫ﺍﻟﻌﺩﻴﺩ ﻤﻥ ﺍﻷﺩﻭﺍﺕ ﺇﻟﻰ ﺍﻟﻨﻤﻭﺫﺝ ﻜﻤﺎ ﻫﻭ ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪:‬‬

‫ﻭﻜﻤﺎ ﺘﻼﺤﻅ ﻤﻥ ﺍﻟﺼﻭﺭﺓ‪ ،‬ﻓﺈﻥ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺘﻲ ﺃﻀﻴﻔﺕ ﻫﻲ‪:‬‬


‫‪ -١‬ﻻﻓﺘﺔ ﺘﻌﺭﺽ ﺍﺴﻡ ﺍﻟﺤﻘل‪ ،‬ﺍﺴﻤﻬﺎ ﺍﻟﺒﺭﻤﺠﻲ ‪ XLabel‬ﺤﻴﺙ ‪ X‬ﻫﻭ ﺍﺴﻡ ﺍﻟﺤﻘل‪.‬‬
‫‪ -٢‬ﻤﺭﺒﻊ ﻨﺹ ﻴﻌﺭﺽ ﻗﻴﻤﺔ ﺍﻟﺤﻘل‪ ،‬ﺍﺴﻤﻪ ﺍﻟﺒﺭﻤﺠﻲ ‪.XTextBox‬‬
‫‪ -٣‬ﻨﺴﺨﺔ ﻤﻥ ﻓﺌﺔ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ BooksDataSet‬ﻻﺴﺘﺨﺩﺍﻤﻬﺎ ﻓﻲ ﺍﻟﺤﺼﻭل ﻋﻠـﻰ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -٤‬ﻨﺴﺨﺔ ﻤﻥ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ﺍﻟﺫﻱ ﻴﻭﺠﺩ ﺒﻪ ﺍﻟﺤﻘل‪ ..‬ﻓﻤﺜﻼ ﻟـﻭ ﺴـﺤﺒﺕ ﺍﻟﺤﻘـل ‪Book‬‬
‫ﻓﺴﻴﻀﺎﻑ ﻤﻬﻴﺊ ﺍﻟﺠﺩﻭل ‪ BooksTableAdapter‬ﺇﻟﻰ ﺼﻴﻨﻴﺔ ﺍﻟﻤﻜﻭﻨﺎﺕ‪.‬‬

‫‪٤٠٩‬‬
‫‪ -٥‬ﻨﺴﺨﺔ ﻤﻥ ﻤﺩﻴﺭ ﺍﻟﺘﻭﺼﻴل ‪ 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‬ﻓﺎﻟﻤﻨﺎﺴﺏ ﻓﻲ ﻤﺜﺎﻟﻨﺎ ﻫﺫﺍ‪ ،‬ﺍﺴﺘﺨﺩﺍﻡ ﺠﺩﻭل ﻟﻌﺭﺽ ﻜﺘﺏ ﺍﻟﻤﺅﻟـﻑ‬
‫ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻜﻤﺎ ﺘﺭﻯ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪:‬‬

‫ﻭﺴﺘﺠﺩ ﻫﺫﺍ ﺍﻟﺘﺼﻤﻴﻡ ﻓﻲ ﺍﻟﺘﻁﺒﻴﻕ ‪ DataSourceWizard‬ﺍﻟﻤﺭﻓﻕ ﺒﻬﺫﺍ ﺍﻟﻜﺘﺎﺏ‪.‬‬


‫ﻟﻭ ﺸﻐﻠﺕ ﻫﺫﺍ ﺍﻟﺘﻁﺒﻴﻕ ﻓﺴﻴﻤﻜﻨﻙ ﺍﻻﻨﺘﻘﺎل ﺒﻴﻥ ﺍﻟﻤﺅﻟﻔﻴﻥ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺸﺭﻴﻁ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ‪ ،‬ﺤﻴـﺙ‬
‫ﺴﺘﻌﺭﺽ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻌﻠﻭﻴﺔ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺅﻟﻑ ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻭﺴﻴﻌﺭﺽ ﺍﻟﺠﺩﻭل ﺍﻟﺴـﻔﻠﻲ ﻜﺘـﺏ ﻫـﺫﺍ‬
‫ﺍﻟﻤﺅﻟﻑ‪ ..‬ﻫﺫﺍ ﻤﺸﺭﻭﻉ ‪ Master-Details‬ﻜﺎﻤل ﻴﻌﻤل ﺒﻜﻔﺎﺀﺓ ﺩﻭﻥ ﺃﻥ ﻨﻜﺘﺏ ﻓﻴﻪ ﺤﺭﻓﺎ ﻭﺍﺤﺩﺍ‬
‫ﻤﻥ ﺍﻟﻜﻭﺩ!‪ ..‬ﺃﻟﻴﺱ ﺸﻴﺌﺎ ﻤﺜﻴﺭﺍ؟‬
‫ﻻﺤﻅ ﺃﻥ ﻭﺠﻭﺩ ﺍﻟﻌﻤﻭﺩ ‪ AuthorID‬ﻓﻲ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻻ ﻤﻌﻨﻰ ﻟﻪ‪ ..‬ﻟﻜـﻥ ﻟﻸﺴـﻑ‪ ،‬ﻟـﻭ‬
‫ﺤﺎﻭﻟﺕ ﺇﺯﺍﻟﺔ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﺒﺎﺨﺘﻴﺎﺭ ‪ None‬ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ﻟﻠﺤﻘل ‪ AuthorID‬ﻗﺒل ﺴﺤﺏ‬
‫ﻋﻨﺼﺭ ﺍﻟﻌﻼﻗﺔ ‪ Books‬ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ‪ ،‬ﻓﻠﻥ ﺘﻨﺠﺢ‪ ..‬ﻓﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻴﻌﺭﺽ ﻜـل ﺍﻷﻋﻤـﺩﺓ‬
‫ﺸــﺌﺕ ﺃﻡ ﺃﺒﻴــﺕ‪ ،‬ﻭﺘﻜــﻭﻥ ﻜــل ﻫــﺫﻩ ﺍﻷﻋﻤــﺩﺓ ﺃﻋﻤــﺩﺓ ﻤﺭﺒﻌــﺎﺕ ﺍﻟﻨﺼــﻭﺹ‬
‫‪ DataGridViewTextBoxColumn‬ﻤﻬﻤﺎ ﻜﺎﻥ ﻨﻭﻉ ﺍﻷﺩﺍﺓ ﺍﻟﺘﻲ ﺍﺨﺘﺭﺘﻬﺎ ﻟﻌـﺭﺽ ﻗﻴﻤـﺔ‬
‫‪٤١٣‬‬
‫ﺍﻟﺤﻘل!‪ ..‬ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﺘﺤﺩﻴﺩ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ﻭﺍﺴﺘﺨﺩﺍﻡ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ﻟﺤﺫﻑ ﻫﺫﺍ ﺍﻟﻌﻤﻭﺩ ﻤﻥ‬
‫ﻤﺠﻤﻭﻋﺔ ﺃﻋﻤﺩﺓ ﺠﺩﻭل ﺍﻟﻌﺭﺽ ‪ ..Columns Collection‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺠﺩﻭل ﺍﻟﻌـﺭﺽ‬
‫ﺒﺎﻟﺘﻔﺼﻴل ﻓﻲ ﺍﻟﻔﺼل ﺍﻟﺘﺎﻟﻲ‪.‬‬
‫ﻭﺭﻏﻡ ﻜل ﺍﻟﺘﺴﻬﻴﻼﺕ ﺍﻟﺘﻲ ﺘﻤﻨﺤﻬﺎ ﻟﻨﺎ ﻨﺎﻓﺫﺓ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ،‬ﺇﻻ ﺃﻨﻬـﺎ ﺃﺤﻴﺎﻨـﺎ ﻻ ﺘﻌﻁﻴﻨـﺎ‬
‫ﺒﺎﻟﻀﺒﻁ ﻤﺎ ﻨﺭﻴﺩﻩ‪ ..‬ﻤﺜﻼ‪ :‬ﻟﻭ ﺃﺭﺩﺕ ﻋﺭﺽ ﺃﻱ ﺤﻘـل ﻓـﻲ ﻗﺎﺌﻤـﺔ ‪ List‬ﺃﻭ ﻗﺎﺌﻤـﺔ ﻤﺭﻜﺒـﺔ‬
‫‪ ،ComboBox‬ﻓﺈﻥ ﺴﺤﺏ ﺍﻟﺤﻘل ﻭﺇﻟﻘﺎﺌﻪ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ﻴﺭﺒﻁ ﺍﻟﺨﺎﺼﻴﺔ ‪ Text‬ﺍﻟﺘﺎﺒﻌﺔ ﻟﻬـﺎﺘﻴﻥ‬
‫ﺍﻷﺩﺍﺘﻴﻥ ﺒﺎﻟﺤﻘل‪ ،‬ﻭﻻ ﻴﺘﻡ ﻤﻠﺅﻫﻤﺎ ﺒﻘﻴﻡ ﺍﻟﺤﻘل!‬
‫ﻭﻟﺤل ﻫﺫﻩ ﺍﻟﻤﺸﻜﻠﺔ‪ ،‬ﻋﻠﻴﻙ ﺍﻟﺘﺩﺨل ﻴﺩﻭﻴﺎ‪ ،‬ﻭﺍﺴﺘﺨﺩﺍﻡ ﻨﺎﻓـﺫﺓ ﺍﻟﺨﺼـﺎﺌﺹ ﻹﺯﺍﻟـﺔ ﺍﻻﺭﺘﺒـﺎﻁ‬
‫ﺒﺎﻟﺨﺎﺼﻴﺔ ‪ ،Text‬ﻭﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼﻴﺘﻴﻥ ‪ DataSource‬ﻭ ‪ DataMember‬ﺒﺩﻻ ﻤﻨﻬﺎ‪.‬‬
‫ﻭﺍﻟﺘﻁﺒﻴﻕ ‪ MasterDetails‬ﻴﺭﻴﻙ ﻤﺜﺎﻻ ﻋﻠﻰ ﻫﺫﺍ‪ ..‬ﻹﻨﺸﺎﺀ ﻤﺜل ﻫﺫﺍ ﺍﻟﺘﻁﺒﻴﻕ‪ ،‬ﺍﻓﻌل ﻤﺎ ﻴﻠﻲ‪:‬‬
‫‪ -‬ﻤﻥ ﻨﺎﻓﺫﺓ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺍﺴﺤﺏ ﺍﻟﺤﻘل ‪ Authors.Author‬ﻭﺃﻟﻘِِﻪ ﻋﻠﻰ ﺍﻟﻨﻤـﻭﺫﺝ‬
‫ﻟﻌﺭﺽ ﺍﺴﻡ ﺍﻟﻤﺅﻟﻑ ﺍﻟﺤﺎﻟﻲ‪.‬‬
‫‪ -‬ﺍﺨﺘﺭ ﻋﺭﺽ ﺍﻟﺤﻘل ‪ Authors.Books.Book‬ﻓﻲ ﻗﺎﺌﻤـﺔ ﻤﺭﻜﺒـﺔ ‪،ComboBox‬‬
‫ﻭﺃﻟﻘِِﻪ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ ﻟﻴﻌﺭﺽ ﺃﺴﻤﺎﺀ ﺍﻟﻜﺘﺏ‪.‬‬
‫‪ -‬ﻤﻥ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻓﺘﺢ ﺍﻟﺨﺎﺼﻴﺔ )‪ (DataBindings‬ﻭﺃﺯل ﺍﻻﺭﺘﺒﺎﻁ ﻤﻊ ﺍﻟﺨﺎﺼﻴﺔ‬
‫‪ ..Text‬ﻭﻴﻤﻜﻨﻙ ﺒﺩﻻ ﻤﻨﻬﺎ ﺃﻥ ﺘﻨﺸﺊ ﺍﺭﺘﺒﺎﻁﺎ ﻤﻊ ﺍﻟﺨﺎﺼﻴﺔ ‪ SelectedValue‬ﺤﺘـﻰ‬
‫ﻴﺘﻡ ﺤﻔﻅ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﻴﺨﺘﺎﺭﻫﺎ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺁﻟﻴﺎ ﻓﻲ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ‪.‬‬
‫‪ -‬ﺘﻭﺠﻪ ﺇﻟﻰ ﺍﻟﺨﺎﺼﻴﺔ ‪ DataSource‬ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﺭﻜﺒﺔ‪ ،‬ﻭﺍﻀﻐﻁ ﺯﺭ ﺍﻹﺴﺩﺍل‪،‬‬
‫ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﺨﺘﺭ ﻤﺼﺩﺭ ﺍﻟـﺭﺒﻁ ‪ AuthorsBindingSource‬ﻭﺃﺴـﺩل ﻋﻨﺎﺼـﺭﻩ‬
‫ﺍﻟﻔﺭﻋﻴﺔ‪ ..‬ﺴﺘﺠﺩ ﺘﺤﺘﻪ ﺍﺴﻡ ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺠـﺩﻭل ﺍﻟﻤـﺅﻟﻔﻴﻥ ﻭﺠـﺩﻭل ﺍﻟﻜﺘـﺏ ﻭﻫـﻲ‬
‫‪ ..FK_Books_Authors‬ﺍﺨﺘﺭ ﻫﺫﻩ ﺍﻟﻌﻼﻗﺔ ﻜﻤﺼﺩﺭ ﻟﻠﺒﻴﺎﻨـﺎﺕ‪ ..‬ﺴـﻴﺅﺩﻱ ﻫـﺫﺍ‬
‫ـﺎﻤﺞ ﺍﺴـــﻤﻪ‬
‫ـﺩ ﺇﻟـــﻰ ﺍﻟﺒﺭﻨــ‬
‫ـﻁ ﺠﺩﻴــ‬
‫ـﺎﻓﺔ ﻤﺼـــﺩﺭ ﺭﺒــ‬
‫ـﻰ ﺇﻀــ‬
‫ﺇﻟــ‬
‫‪ ،FKBooksAuthorsBindingSource‬ﻭﺴﺘﻭﻀﻊ ﻗﻴﻤﺘﻪ ﺘﻠﻘﺎﺌﻴـﺎ ﻓـﻲ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪!DataSource‬‬

‫‪٤١٤‬‬
‫‪ -‬ﺘﻭﺠﻪ ﺇﻟﻰ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،DataMember‬ﻭﺍﻀﻐﻁ ﺯﺭ ﺍﻹﺴﺩﺍل‪ ،‬ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﺨﺘﺭ ﺍﻟﺤﻘـل‬
‫‪ ..Book‬ﺍﻵﻥ ﺘﺄﻜﺩﻨﺎ ﺃﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﺭﻜﺒﺔ ﺴﺘﻌﺭﺽ ﻜﺘﺏ ﺍﻟﻤﺅﻟﻑ ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻷﻨﻨﺎ ﺭﺒﻁﻨﺎﻫـﺎ‬
‫ﻤﻥ ﺨﻼل ﺍﻟﻌﻼﻗﺔ ﺒﻴﻥ ﺍﻟﻤﺅﻟﻔﻴﻥ ﻭﺍﻟﻜﺘﺏ‪.‬‬
‫‪ -‬ﻤﻥ ﻨﺎﻓﺫﺓ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﺴـﺤﺏ ﺍﻟﺤﻘـل ‪ Authors.Books.Price‬ﻭﺃﻟﻘـﻪ ﻋﻠـﻰ‬
‫ـﻴﺔ‬
‫ـﺘﺢ ﺍﻟﺨﺎﺼـ‬
‫ـﺎﺌﺹ ﻭﺍﻓـ‬
‫ـﺫﺓ ﺍﻟﺨﺼـ‬
‫ـﺘﺢ ﻨﺎﻓـ‬
‫ـﻨﺹ ﻭﺍﻓـ‬
‫ـﻊ ﺍﻟـ‬
‫ـﺩﺩ ﻤﺭﺒـ‬
‫ـﻭﺫﺝ‪ ..‬ﺤـ‬
‫ﺍﻟﻨﻤـ‬
‫)‪ ،(DataBindings‬ﻭﺘﻭﺠﻪ ﺇﻟﻰ ﺍﻟﺨﺎﺼﻴﺔ ‪ ..Text‬ﺍﻀﻐﻁ ﺯﺭ ﺍﻹﺴﺩﺍل‪ ،‬ﻭﺍﺨﺘﺭ ﺍﻟﻌﻨﺼﺭ‬
‫‪ FKBooksAuthorsBindingSource‬ﻟﺭﺒﻁ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﻤﻥ ﺨﻼل ﺍﻟﻌﻼﻗﺔ‪.‬‬
‫‪ -‬ﻴﻤﻜﻨﻙ ﺘﻜﺭﺍﺭ ﻫﺫﺍ ﻤﻊ ﺃﻜﺜﺭ ﻤﻥ ﺤﻘل ﻤﻥ ﺤﻘﻭل ﺠﺩﻭل ﺍﻟﻜﺘﺏ‪ ..‬ﻤﺜﻼ‪ ،‬ﻟﻭ ﺴـﺤﺒﺕ ﺍﻟﺤﻘـل‬
‫‪ Publish_Date‬ﻭﺃﻟﻘﻴﺘﻪ ﻋﻠـﻰ ﺍﻟﻨﻤـﻭﺫﺝ‪ ،‬ﻓﺴـﺘﻅﻬﺭ ﺃﺩﺍﺓ ﺍﺨﺘﻴـﺎﺭ ﺍﻟﺘـﺎﺭﻴﺦ ﻭﺍﻟﻭﻗـﺕ‬
‫‪ DateDateTimePicker‬ﻟﻌﺭﺽ ﻗﻴﻤﺘﻪ‪ ..‬ﻭﺃﻴﻀﺎ ﻋﻠﻴﻙ ﺃﻥ ﺘﻐﻴـﺭ ﺍﺭﺘﺒـﺎﻁ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪ Value‬ﺍﻟﺨﺎﺼــﺔ ﺒﻬــﺫﻩ ﺍﻷﺩﺍﺓ‪ ،‬ﻟﺘﺠﻌﻠﻬــﺎ ﺘــﺭﺘﺒﻁ ﻤــﻥ ﺨــﻼل ﺍﻟﻤﺼــﺩﺭ‬
‫‪ FKBooksAuthorsBindingSource‬ﻜﻤﺎ ﻓﻌﻠﻨﺎ ﻤﻊ ﻤﺭﺒﻊ ﺍﻟﻨﺹ‪.‬‬
‫ﺍﻵﻥ ﺴﻴﻜﻭﻥ ﺸﻜل ﺍﻟﻨﻤﻭﺫﺝ ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬

‫ﻟﻭ ﺸﻐﻠﺕ ﺍﻟﺒﺭﻨﺎﻤﺞ‪ ،‬ﻓﺴﻴﻤﻜﻨﻙ ﺍﻟﺘﺤﺭﻙ ﻋﺒﺭ ﺍﻟﻤﺅﻟﻔﻴﻥ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺸﺭﻴﻁ ﻤﻭﺠﻪ ﺍﻟـﺭﺒﻁ‪ ،‬ﺤﻴـﺙ‬
‫ﺴﺘﻅﻬﺭ ﻜﺘﺏ ﺍﻟﻤﺅﻟﻑ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﺭﻜﺒﺔ‪ ،‬ﻭﻜﻠﻤﺎ ﺍﺨﺘﺭﺕ ﻜﺘﺎﺒﺎ ﻤﻨﻬﺎ‪ ،‬ﻴﻅﻬـﺭ ﺘـﺎﺭﻴﺦ‬

‫‪٤١٥‬‬
‫ﻨﺸﺭﻩ ﻓﻲ ﺃﺩﺍﺓ ﺍﻟﺘﺎﺭﻴﺦ‪ ،‬ﻭﺴﻌﺭﻩ ﻓﻲ ﻤﺭﺒﻊ ﺍﻟﻨﺹ‪ ..‬ﻭﺒﻬﺫﺍ ﻨﻜﻭﻥ ﺤﺼﻠﻨﺎ ﻋﻠﻰ ﻁﺭﻴﻘـﺔ ﺃﺨـﺭﻯ‬
‫ﻟﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺭﺌﻴﺴﻴﺔ ﻭﺍﻟﺘﻔﺎﺼﻴل ﻭﺘﻔﺎﺼﻴل ﺍﻟﺘﻔﺎﺼﻴل!‪ ..‬ﺼـﺤﻴﺢ ﺃﻨﻨـﺎ ﺃﺠﺭﻴﻨـﺎ ﺒﻌـﺽ‬
‫ﺍﻟﺘﻌﺩﻴﻼﺕ ﺍﻟﻴﺩﻭﻴﺔ ﻫﺫﻩ ﺍﻟﻤﺭﺓ‪ ،‬ﻭﻟﻜﻥ ﺼﺤﻴﺢ ﺃﻴﻀﺎ ﺃﻨﻨﺎ ﺇﻟﻰ ﺍﻵﻥ ﻟﻡ ﻨﻜﺘﺏ ﺴﻁﺭﺍ ﻭﺍﺤـﺩﺍ ﻤـﻥ‬
‫ﺍﻟﻜﻭﺩ ﺒﺄﻨﻔﺴﻨﺎ!‪ ..‬ﻤﺭﺤﻰ‪ ،‬ﻤﺎ ﺃﻤﺘﻊ ﺍﻟﻜﺴل!‬

‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ‬

‫‪٤١٦‬‬
‫ﻭﺍﺠﻬﺔ ﻤﺯﻭﺩ ﻤﺩﻴﺭ ﺍﻟﺘﺴﻠﺴل‬
‫‪ICurrencyManagerProvider Interface‬‬

‫ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻌﻨﺼﺭﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ‪:‬‬

‫ﻤﺩﻴﺭ ﺍﻟﺘﺴﻠﺴل ‪:CurrencyManager‬‬


‫ﺘﻌﻴﺩ ﻤﺩﻴﺭ ﺍﻟﺘﺴﻠﺴل ‪ CurrencyManager‬ﺍﻟﺘﺎﺒﻊ ﻟﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺤﺎﻟﻲ‪..‬‬

‫ﻤﻌﺭﻓﺔ ﻤﺩﻴﺭ ﺍﻟﺘﺴﻠﺴل ﺍﻟﺘﺎﺒﻊ ‪:GetRelatedCurrencyManager‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺍﺴﻡ ﺍﻟﻘﺎﺌﻤﺔ ﺃﻭ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﺤـﺎﻟﻲ‪،‬‬
‫ﻟﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻤﺩﻴﺭ ﺍﻟﺘﺴﻠﺴل ‪ CurrencyManager‬ﺍﻟﺨﺎﺹ ﺒﻪ‪ ..‬ﻭﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺭﺴل ﻨﺼـﺎ‬
‫ﻓﺎﺭﻏﺎ "" ﺃﻭ ‪ Nothing‬ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﺴﺘﻌﻴﺩ ﺇﻟﻴﻙ ﻤـﺩﻴﺭ ﺍﻟﺘﺴﻠﺴـل‬
‫ﺍﻟﺨﺎﺹ ﺒﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﻜل‪ ،‬ﻭﻫﻭ ﻨﻔﺱ ﻤﺩﻴﺭ ﺍﻟﺘﺴﻠﺴل ﺍﻟـﺫﻱ ﺘﺤﺼـل ﻋﻠﻴـﻪ ﻤـﻥ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ‪.CurrencyManager‬‬

‫‪٤١٧‬‬
‫ﻭﺍﺠﻬﺔ ﺇﻟﻐﺎﺀ ﺇﻀﺎﻓﺔ ﺍﻟﺠﺩﻴﺩ‬
‫‪ICancelAddNew Interface‬‬

‫ﺘﻀﻴﻑ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺇﻟﻰ ﺍﻟﻔﺌﺔ ﺍﻟﺘﻲ ﺘﻤﺜﻠﻬﺎ ﺍﻟﻘﺩﺭﺓ ﻋﻠﻰ ﻗﺒﻭل ﺍﻟﻌﻨﺼـﺭ ﺍﻟﺠﺩﻴـﺩ ﺍﻟﻤﻀـﺎﻑ ﺃﻭ‬
‫ﺍﻟﺘﺭﺍﺠﻊ ﻋﻥ ﺇﻀﺎﻓﺘﻪ‪ ،‬ﻭﻫﻲ ﺘﻤﻠﻙ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﺇﻟﻐﺎﺀ ﺍﻟﺠﺩﻴﺩ ‪:CancelNew‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺭﻗﻡ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺫﻱ ﺃﻀﻔﺘﻪ ﺴﺎﺒﻘﺎ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﻟﺘﻘﻭﻡ ﺒـﺎﻟﺘﺭﺍﺠﻊ‬
‫ﻋﻥ ﺇﻀﺎﻓﺔ )ﺘﻘﻭﻡ ﺒﺤﺫﻓﻪ(‪.‬‬

‫ﺇﻨﻬﺎﺀ ﺍﻟﺠﺩﻴﺩ ‪:EndNew‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺭﻗﻡ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺫﻱ ﺃﻀﻔﺘﻪ ﺴﺎﺒﻘﺎ ﺇﻟﻰ ﺍﻟﻤﺠﻤﻭﻋﺔ‪ ،‬ﻟﺘﻘـﻭﻡ ﺒﻘﺒﻭﻟـﻪ‬
‫ﻨﻬﺎﺌﻴﺎ‪ ..‬ﻫﺫﺍ ﻴﻌﻨﻲ ﺃﻨﻙ ﻻ ﺘﺴﺘﻁﻴﻊ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ CancelNew‬ﺒﻌﺩ ﻫﺫﺍ ﻟﻠﺘﺭﺍﺠﻊ ﻋﻥ‬
‫ﺇﻀﺎﻓﺔ ﻫﺫﺍ ﺍﻟﻌﻨﺼﺭ‪.‬‬

‫ﻭﺍﺠﻬﺔ ﺇﻁﻼﻕ ﺃﺤﺩﺍﺙ ﺍﻟﺘﻐﻴﺭ‬


‫‪IRaiseItemChangedEvents Interface‬‬

‫ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻭﺤﻴﺩﺓ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺇﻁﻼﻕ ﺃﺤﺩﺍﺙ ﺘﻐﻴﺭ ﺍﻟﻌﻨﺼﺭ ‪:RaisesItemChangedEvents‬‬


‫ﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،True‬ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﻔﺌﺔ ﺍﻟﺘﻲ ﺘﻤﺜل ﻫـﺫﻩ ﺍﻟﻭﺍﺠﻬـﺔ ﺴـﺘﻁﻠﻕ ﺍﻟﺤـﺩﺙ‬
‫‪ ListChanged‬ﺇﺫﺍ ﺤﺩﺙ ﺘﻐﻴﺭ ﻓﻲ ﺃﺤﺩ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ ﺍﻟﺨﺎﺼﺔ ﺒﻬﺎ‪.‬‬

‫‪٤١٨‬‬
‫ﻓﺌﺔ ﻗﺎﺌﻤﺔ ﺍﻟﺭﺒﻁ ﻋﺎﻤﺔ ﺍﻟﻨﻭﻉ ‪BindingList<T> Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻤﻭﺠـﻭﺩﺓ ﻓـﻲ ﺍﻟﻨﻁـﺎﻕ ‪ ،System.ComponentModel‬ﻭﻫـﻲ ﺘـﺭﺙ ﺍﻟﻔﺌـﺔ‬


‫‪ ،<Collection<T‬ﻭﺘﻤﺜل ﺍﻟﻭﺍﺠﻬـﺎﺕ ‪ IbindingList‬ﻭ ‪ IList‬ﻭ ‪ ICancelAddNew‬ﻭ‬
‫‪.IRaiseItemChangedEvents‬‬
‫ﻭﺘﻌﻤل ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻜﻤﺠﻤﻭﻋﺔ ﻋﺎﻤﺔ ﺍﻟﻨﻭﻉ ‪ ،Generic Type Collection‬ﺘﺩﻋﻡ ﺘﻘﻨﻴـﺔ ﺭﺒـﻁ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ‪.Binding‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺼﻴﻐﺘﺎﻥ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﻭﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻗﺎﺌﻤﺔ ﻋﺎﻤﺔ ﺍﻟﻨﻭﻉ ‪ ،<IList<T‬ﻟﺘﻨﺴﺦ ﻋﻨﺎﺼﺭﻫﺎ ﺇﻟﻰ ﻗﺎﺌﻤـﺔ‬
‫ﺍﻟﺭﺒﻁ‪.‬‬

‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺍﻟﻔﺌﺔ ﺍﻷﻡ‪ ،‬ﻭﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﺨﺼﺎﺌﺹ ﻭﻭﺴﺎﺌل ﺍﻟﻭﺍﺠﻬﺎﺕ ﺍﻟﻤـﺫﻜﻭﺭﺓ‪،‬‬
‫ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﻴﻠﺘﻴﻥ ﺍﻟﺠﺩﻴﺩﺘﻴﻥ ﺍﻟﺘﺎﻟﻴﺘﻴﻥ‪:‬‬

‫ﺘﺼﻔﻴﺭ ﺍﻻﺭﺘﺒﺎﻁﺎﺕ ‪:ResetBindings‬‬


‫ﺘﻁﻠــﻕ ﺍﻟﺤــﺩﺙ ‪ ListChanged‬ﻤــﻊ ﺇﺭﺴــﺎل ﺍﻟﻘﻴﻤــﺔ ‪ Reset‬ﺇﻟــﻰ ﺍﻟﺨﺎﺼــﻴﺔ‬
‫‪.e.ListChangedType‬‬

‫ﺘﺼﻔﻴﺭ ﺍﻟﻌﻨﺼﺭ ‪:ResetItem‬‬


‫ﺘﻁﻠﻕ ﺍﻟﺤﺩﺙ ‪ ListChanged‬ﻤﻊ ﺇﺭﺴـﺎل ﺍﻟﻘﻴﻤـﺔ ‪ ItemChanged‬ﺇﻟـﻰ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪.e.ListChangedType‬‬

‫‪٤١٩‬‬
‫ﻭﺍﺠﻬﺔ ﻤﺼﺩﺭ ﺍﻟﻘﺎﺌﻤﺔ‬
‫‪IListSource Interface‬‬

‫ﺘﻌﻤل ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﻜﻤﺼﺩﺭ ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻗﺎﺌﻤﺔ ‪ List‬ﻤﻥ ﻜﺎﺌﻨﺎﺕ ﻻ ﺘﻤﺜل ﻭﺍﺠﻬـﺔ ﺍﻟﻘﺎﺌﻤـﺔ‬
‫‪ ،IList‬ﻤﻤﺎ ﻴﺠﻌل ﻤﻥ ﺍﻟﻤﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻜﺎﺌﻨﺎﺕ ﻜﻤﺼﺩﺭ ﻟﻠﺒﻴﺎﻨﺎﺕ ‪ DataSource‬ﻋﻨـﺩ‬
‫ﺭﺒﻁﻬﺎ ﺒﺄﺩﻭﺍﺕ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻌﻨﺼﺭﻴﻥ ﺍﻟﺘﺎﻟﻴﻴﻥ‪:‬‬

‫ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻤﺠﻤﻭﻋﺔ ﻗﻭﺍﺌﻡ ‪:ContainsListCollection‬‬


‫ﺘﻌﻴﺩ ‪ True‬ﺇﺫﺍ ﻜﺎﻨﺕ ﺍﻟﻤﺠﻤﻭﻋﺔ ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻗﻭﺍﺌﻡ ﺩﺍﺨﻠﻴﺔ‪.‬‬

‫ﺍﻟﺤﺼﻭل ﻋﻠﻰ ﺍﻟﻘﺎﺌﻤﺔ ‪:GetList‬‬


‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﺘﻤﺜل ﻭﺍﺠﻬﺔ ﺍﻟﻘﺎﺌﻤﺔ ‪ ،IList‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻨﺎﺼﺭ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫‪٤٢٠‬‬
‫ﻓﺌﺔ ﻤﺼﺩﺭ ﺍﻟﺭﺒﻁ ‪BindingSource Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﺍﻟﻔﺌﺔ ‪ Component‬ﻟﻬﺫﺍ ﺴﺘﺠﺩﻫﺎ ﻓﻲ ﺼـﻨﺩﻭﻕ ﺍﻷﺩﻭﺍﺕ ‪ Toolbox‬ﺘﺤـﺕ‬


‫ﺍﻟﺸﺭﻴﻁ ‪ ،Data‬ﻭﻴﻤﻜﻨﻙ ﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﺼﻴﻨﻴﺔ ﻤﻜﻭﻨﺎﺕ ﺍﻟﻨﻤﻭﺫﺝ‪.‬‬
‫ـﺎﺕ ‪ IList‬ﻭ ‪IBindingListView‬‬
‫ـﺔ ﺍﻟﻭﺍﺠﻬـــ‬
‫ـﺫﻩ ﺍﻟﻔﺌـــ‬
‫ـل ﻫـــ‬
‫ـﺎ ﺘﻤﺜـــ‬
‫ﻜﻤـــ‬
‫ﻭ ‪ IcurrencyManagerProvider‬ﻭ ‪.ICancelAddNew‬‬
‫ﻭﺘﺤﺘﻭﻱ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﺩﺍﺨﻠﻴﺔ ‪ Internal List‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻋﻨﺎﺼﺭ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪،‬‬
‫ﻟﻴﺘﻡ ﺭﺒﻁﻬﺎ ﺒﺎﻷﺩﻭﺍﺕ ﺍﻟﻤﻭﻀﻭﻋﺔ ﻋﻠﻰ ﺍﻟﻨﻤﻭﺫﺝ‪ ،‬ﻭﺒﻬﺫﺍ ﺘﺴـﻬل ﻫـﺫﻩ ﺍﻟﻔﺌـﺔ ﻋﻤﻠﻴـﺔ ﺍﻟـﺭﺒﻁ‬
‫‪ Binding‬ﻭﺘﺘﻴﺢ ﻟﻙ ﺍﻟﺘﺤﻜﻡ ﻓﻴﻬﺎ ﻜﻤﺎ ﺴﻨﺭﻯ ﺒﻌﺩ ﻗﻠﻴل‪.‬‬

‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬


‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﻟﻬﺎ ﻤﻌﺎﻤل ﻭﺍﺤﺩ ﻤﻥ ﻨﻭﻉ ﺍﻟﻭﺍﺠﻬﺔ ‪ ،IContainer‬ﻭﻫﻭ ﻴﺴـﺘﻘﺒل ﺍﻷﺩﺍﺓ‬
‫ﺍﻟﺤﺎﻭﻴﺔ ﺍﻟﺘﻲ ﺴﻴﻨﺘﻤﻲ ﺇﻟﻴﻬﺎ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﻴﺘﻌﺎﻤل ﻤﻊ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﻭﻀﻭﻋﺔ ﻋﻠﻴﻬﺎ‪..‬‬
‫ﺘﺫﻜﺭ ﺃﻥ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺤﺎﻭﻴﺔ ﺘﺸﻤل ﺍﻟﻨﻤﻭﺫﺝ ‪ Form‬ﻭﺍﻟﻠﻭﺤﺔ ‪ Panel‬ﻭﻤﺭﺒـﻊ ﺍﻟﺘﺠﻤﻴـﻊ‬
‫‪ ...GroupBox‬ﺇﻟﺦ‪.‬‬
‫‪ -٣‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﺍﻟﻜﺎﺌﻥ ‪ Object‬ﺍﻟﺫﻱ ﻴﻌﻤل ﻜﻤﺼﺩﺭ ﻟﻠﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻨﺼـﺎ ﻴﻤﺜـل‬
‫ﺍﺴﻡ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬

‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﺨﺼﺎﺌﺹ ﺍﻟﻭﺍﺠﻬﺎﺕ ﺍﻟﻤﺫﻜﻭﺭﺓ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataSource‬‬


‫ﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻜﺎﺌﻥ ‪ Object‬ﺍﻟﺫﻱ ﻴﻌﻤل ﻜﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻫﺫﺍ ﺴﻴﺅﺩﻱ ﺇﻟـﻰ‬
‫ﻤﺎ ﻴﻠﻲ‪:‬‬
‫‪ -‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،T‬ﻓـﺈﻥ ﻨـﻭﻉ ﻋﻨﺎﺼـﺭ ﺍﻟﻘﺎﺌﻤـﺔ ﺍﻟﺩﺍﺨﻠﻴـﺔ ﻟﻠﻔﺌـﺔ‬
‫‪ BindingSource‬ﺴﻴﺤﺩﺩ ﻋﻠﻰ ﺃﻨﻪ ﻤﻥ ﺍﻟﻨﻭﻉ ‪ ،T‬ﻭﻟﻥ ﺘﻘﺒل ﻫـﺫﻩ ﺍﻟﻘﺎﺌﻤـﺔ ﺃﻱ‬
‫ﺒﻴﺎﻨﺎﺕ ﻻ ﻴﻤﻜﻥ ﺘﺤﻭﻴﻠﻬﺎ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﻨﻭﻉ‪.‬‬
‫‪٤٢١‬‬
‫‪ -‬ﺇﺫﺍ ﻭﻀﻌﺕ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻘﻴﻤﺔ ‪ ،Nothing‬ﻓﺴﺘﻅل ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴـﺔ ﻏﻴـﺭ‬
‫ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ‪ ،‬ﻭﺴﺘﺄﺨﺫ ﻨﻭﻉ ﺃﻭل ﻋﻨﺼﺭ ﺘﻀﻴﻔﻪ ﺇﻟﻴﻬﺎ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ ‪..Add‬‬
‫ﻻﺤﻅ ﺃﻥ ﺨﻁﺄ ﺴـﻴﺤﺩﺙ ﻓـﻲ ﺍﻟﺒﺭﻨـﺎﻤﺞ ﺇﺫﺍ ﻭﻀـﻌﺕ ﻗﻴﻤـﺔ ﻓـﻲ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪ DataMember‬ﺒﻴﻨﻤﺎ ﻟﻠﺨﺎﺼﻴﺔ ‪ DataSource‬ﺍﻟﻘﻴﻤﺔ ‪.Nothing‬‬
‫‪ -‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﺒﺴﻴﻁﺎ ﻻ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﻤﻥ ﺍﻟﻌﻨﺎﺼﺭ‪ ،‬ﻓﺈﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴـﺔ‬
‫ﺴﺘﻜﻭﻥ ﻓﺎﺭﻏﺔ‪.‬‬
‫‪ -‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺫﻱ ﻭﻀﻌﺘﻪ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﺼـﻔﻭﻓﺔ ‪ Array‬ﺃﻭ ﻤﺠﻤﻭﻋـﺔ‬
‫‪ ،Collection‬ﻓﺈﻥ ﻋﻨﺎﺼﺭﻫﺎ ﺴﺘﻭﻀﻊ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ‪.‬‬
‫‪ -‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﻤﻌﻘﺩﺍ ﻭﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﻤﻥ ﺍﻟﻌﻨﺎﺼﺭ ﺃﻭ ﺃﻜﺜﺭ ﻤﻥ ﻗﺎﺌﻤﺔ‪ ،‬ﻓﻴﺠل‬
‫ﻋﻠﻴﻙ ﺫﻜﺭ ﺍﺴﻡ ﺍﻟﻘﺎﺌﻤﺔ ﻓﻲ ﺍﻟﺨﺎﺼـﻴﺔ ‪) DataMember‬ﻜﺎﺴـﻡ ﺍﻟﺠـﺩﻭل ﻓـﻲ‬
‫ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺜﻼ(‪ ،‬ﺤﻴﺙ ﺴﺘﻭﻀﻊ ﻋﻨﺎﺼﺭ ﻫﺫﻩ ﺍﻟﻘﺎﺌﻤﺔ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ‪.‬‬
‫ﻭﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻀﻊ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻨﻭﻉ ﺃﺤﺩ ﺍﻟﻜﺎﺌﻨﺎﺕ ﺒﺩﻻ ﻤﻥ ﺃﻥ ﺘﻀﻊ ﺍﻟﻜﺎﺌﻥ ﻨﻔﺴـﻪ‪..‬‬
‫ﻓﺒﺩﻻ ﻤﻥ ﺃﻥ ﺘﻀﻊ ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫‪Bs.DataSource = Ds‬‬
‫ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﻀﻊ ﻨﻭﻉ ﻤﺠﻤﻭﻋﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫) (‪Bs.DataSource = Ds.GetType‬‬
‫ﻭﺇﺫﺍ ﻜﺎﻨﺕ ﻟﺩﻴﻙ ﻤﺠﻤﻭﻋﺔ ﺒﻴﺎﻨﺎﺕ ﻤﺤﺩﺩﺓ ﺍﻟﻨﻭﻉ ‪ Typed DataSet‬ﺍﺴـﻤﻬﺎ ‪BooksDs‬‬
‫ﻓﻴﻤﻜﻨﻙ ﺍﺴﺘﺨﺩﺍﻡ ﻨﻭﻋﻬﺎ ﻜﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ ﻜﺎﻟﺘﺎﻟﻲ‪:‬‬
‫)‪Bs.DataSource = GetType(BooksDs‬‬
‫ﻭﻟﻜﻥ‪ ،‬ﻓﻴﻡ ﻴﻔﻴﺩﻨﺎ ﻫﺫﺍ؟‬
‫ﻓﻲ ﺒﻌﺽ ﺍﻷﺤﻴﺎﻥ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺘﺼﻤﻴﻡ ﺒﻌﺽ ﺃﺩﻭﺍﺕ ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼـﻤﻴﻡ‬
‫)ﻤﺜل ﺠﺩﻭل ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ،(DataGridView‬ﻭﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻨﻙ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﻋـﺭﺽ‬
‫ﺃﻋﻤﺩﺓ ﺍﻟﺠﺩﺍﻭل ﺍﻟﻤﺭﺘﺒﻁﺔ ﻓﻲ ﻫﺫﻩ ﺍﻷﺩﺍﺓ‪ ..‬ﻟﻜﻥ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ ﻗـﺩ ﻻ ﺘﻜـﻭﻥ ﻫﻨـﺎﻙ‬
‫ﻜﺎﺌﻨﺎﺕ ﻤﻌﺭﻓﺔ ﻤﻥ ﺍﻟﻔﺌﺎﺕ ﺍﻟﺘﻲ ﺘﻌﻤل ﻜﻤﺼﺎﺩﺭ ﻟﻠﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﻬﺫﺍ ﺘﺴﻤﺢ ﻟﻙ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫ﺒﻭﻀﻊ ﻨﻭﻉ ﻫﺫﻩ ﺍﻟﻔﺌﺎﺕ ﻓﻴﻬﺎ‪ ،‬ﻟﺘﺴﺘﻨﺘﺞ ﻤﻨﻪ ﻁﺭﻴﻘﺔ ﺍﻟﻌﺭﺽ ﺍﻟﻤﻁﻠﻭﺒﺔ‪.‬‬

‫‪٤٢٢‬‬
‫ﻭﻴﻤﻜﻨﻙ ﻭﻀﻊ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺒﻁﺭﻴﻘﺔ ﻤﺭﺌﻴﺔ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ‪ ،‬ﻭﺫﻟـﻙ ﺒﺎﺴـﺘﺨﺩﺍﻡ‬
‫ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ‪ ،‬ﺤﻴﺙ ﺴﻴﻌﺭﺽ ﻟﻙ ﺯﺭ ﺍﻹﺴﺩﺍل ﺸﺠﺭﺓ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻤﺘﺎﺤﺔ‪ ..‬ﻓـﻲ ﻫـﺫﻩ‬
‫ﺍﻟﺸﺠﺭﺓ ﺴﺘﺠﺩ ﻋﻨﺼﺭﻴﻥ ﺭﺌﻴﺴﻴﻴﻥ‪:‬‬
‫‪ :None -١‬ﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ‪ ،‬ﻭﻫﻲ ﺘﺠﻌل ﻟﻬﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﻘﻴﻤﺔ ‪.Nothing‬‬
‫‪ :Other Data Sources -٢‬ﻭﺘﺤﺘﻬﺎ ﺍﻻﺨﺘﻴﺎﺭﺍﻥ ﺍﻟﺘﺎﻟﻴﺎﻥ‪:‬‬
‫ﺃ‪ :Project Data Sources .‬ﻭﻴﻭﺠﺩ ﺘﺤﺘﻬﺎ ﻜل ﻓﺌﺎﺕ ﻤﺼﺎﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺘﺎﺤـﺔ‬
‫ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ﻜﻠﻪ‪ ..‬ﻭﻴﺅﺩﻱ ﺍﺨﺘﻴﺎﺭ ﺃﻱ ﻓﺌﺔ ﻤﻥ ﻫﺫﻩ ﺍﻟﻔﺌﺎﺕ‪ ،‬ﺇﻟﻰ ﺇﻨﺸﺎﺀ ﻨﺴـﺨﺔ‬
‫ﺠﺩﻴﺩﺓ ﻤﻨﻬﺎ ﻭﺇﻀﺎﻓﺘﻬﺎ ﺇﻟﻰ ﺍﻟﻨﻤﻭﺫﺝ‪.‬‬
‫ﺏ‪ :Form Data Sources .‬ﻭﻴﻭﺠﺩ ﺘﺤﺘﻬﺎ ﻜل ﺍﻟﻜﺎﺌﻨﺎﺕ ﺍﻟﻤﻌﺭﻓﺔ ﻓـﻲ ﺍﻟﻨﻤـﻭﺫﺝ‬
‫ﺍﻟﺤﺎﻟﻲ ﻭﺘﺼﻠﺢ ﻜﻤﺼﺎﺩﺭ ﺒﻴﺎﻨﺎﺕ‪ ،‬ﻤﺜل ﺍﻟﻘـﻭﺍﺌﻡ ‪ Lists‬ﻭﻤﺠﻤﻭﻋـﺎﺕ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫‪ DataSets‬ﻭﻏﻴﺭﻫﺎ‪.‬‬
‫ﻭﻓﻲ ﺍﻟﻬﺎﻤﺵ ﺍﻟﺴﻔﻠﻲ ﻟﻠﻨﺎﻓﺫﺓ ﺍﻟﻤﺴﺩﻟﺔ‪ ،‬ﻴﻭﺠﺩ ﺭﺍﺒﻁ ﺍﺴﻤﻪ‪:‬‬
‫‪Add Project data Source‬‬
‫ﻋﻨﺩ ﺍﻟﻀـﻐﻁ ﻋﻠﻴـﻪ ﻴـﺘﻡ ﺘﺸـﻐﻴل ﺍﻟﻤﻌـﺎﻟﺞ ﺍﻟﺴـﺤﺭﻱ ﻟﺘﻬﻴﺌـﺔ ﻤﺼـﺎﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫‪ ،Data Source Configuration Wizard‬ﻟﻴﻤﻜﻨﻙ ﺇﻨﺸﺎﺀ ﻤﺼـﺩﺭ ﺒﻴﺎﻨـﺎﺕ ﺠﺩﻴـﺩ‬
‫ﻭﺇﻀﺎﻓﺘﻪ ﺘﺤﺕ ﺍﻟﻔﺭﻉ ‪.Project Data Sources‬‬

‫ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:DataMember‬‬


‫ﺘﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﺨﺎﺼﻴﺔ ﺃﻭ ﺍﺴﻡ ﺍﻟﻘﺎﺌﻤﺔ ﺃﻭ ﺍﻟﻌﻤﻭﺩ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺍﻟﺫﻱ ﻴﺘﻡ‬
‫ﺃﺨﺫ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻨﻪ‪.‬‬

‫ﺍﻟﻘﺎﺌﻤﺔ ‪: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‬‬
‫ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻗﺎﺌﻤﺔ‬

‫ﻋﻨﺩ ﻗﺭﺍﺀﺓ ﺍﻟﺨﺎﺼﻴﺔ ‪ List‬ﺴﻴﺤﺩﺙ ﺨﻁﺄ ﻓـﻲ‬ ‫ﻨﻭﻉ ﻴﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ‬


‫‪ICustomType‬‬
‫ﺍﻟﺒﺭﻨﺎﻤﺞ‪.‬‬
‫‪Descriptor‬‬

‫ﺍﻟﻤﻭﻀﻊ ‪:Position‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻤﻭﻀﻊ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴـﺔ ﻟﻤﺼـﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ،‬ﻭﻫـﻭ‬
‫ﺍﻟﻌﻨﺼﺭ ﺍﻟﺫﻱ ﻴﺘﻡ ﻋﺭﻀﻪ ﻓﻲ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﻤﺼﺩﺭ ﺍﻟﺭﺒﻁ‪.‬‬

‫ﺍﻟﺤﺎﻟﻲ ‪:Current‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻨﺎ ‪ Object‬ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ‪ ،‬ﻭﻫـﻭ ﺍﻟﻌﻨﺼـﺭ‬
‫ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﻤﻭﻀﻊ ﺍﻟﻤﺤﺩﺩ ﻓﻲ ﺍﻟﺨﺎﺼﻴﺔ ‪.Position‬‬

‫ﺍﻟﺘﺭﺘﻴﺏ ‪:Sort‬‬
‫ﺘﺤﺩﺩ ﻁﺭﻴﻘﺔ ﺘﺭﺘﻴﺏ ﺍﻟﻌﻨﺎﺼﺭ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ‪ ،‬ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﻨﺼﺎ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﺴـﻡ ﺍﻟﻌﻤـﻭﺩ‬
‫ﺍﻟﻤﺴﺘﺨﺩﻡ ﻓﻲ ﺍﻟﺘﺭﺘﻴﺏ‪ ،‬ﻤﺘﺒﻭﻋﺎ ﺒﺎﺘﺠﺎﻩ ﺍﻟﺘﺭﺘﻴﺏ )‪ ASC‬ﺃﻭ ‪.(DESC‬‬

‫ﻫل ﺍﻟﺭﺒﻁ ﻤﺘﻭﻗﻑ ‪:IsBindingSuspended‬‬


‫ﺘﻌﻴﺩ ‪ True‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺭﺒﻁ ﻤﺘﻭﻗﻔﺎ ﺤﺎﻟﻴﺎ‪.‬‬

‫‪٤٢٥‬‬
‫ﺇﻁﻼﻕ ﺃﺤﺩﺍﺙ ﺘﻐﻴﺭ ﺍﻟﻘﺎﺌﻤﺔ ‪:RaiseListChangedEvents‬‬
‫ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪) True‬ﻭﻫﻲ ﺍﻟﻘﻴﻤﺔ ﺍﻻﻓﺘﺭﺍﻀـﻴﺔ(‪ ،‬ﻓﺴـﻴﻨﻁﻠﻕ ﺍﻟﺤـﺩﺙ‬
‫‪ BindingSource.ListChanged‬ﻋﻨﺩﻤﺎ ﻴﺤﺩﺙ ﺘﻐﻴﻴﺭ ﻓﻲ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺇﻟﻐﺎﺀ ﺍﻟﺘﺤﺭﻴﺭ ‪:CancelEdit‬‬


‫ﺘﻨﻬﻲ ﻋﻤﻠﻴﺔ ﺘﺤﺭﻴﺭ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻭﺘﻠﻐﻲ ﺃﻱ ﺘﻐﻴﻴﺭ ﺤﺩﺙ ﻟﻪ‪.‬‬

‫ﺇﻨﻬﺎﺀ ﺍﻟﺘﺤﺭﻴﺭ ‪:EndEdit‬‬


‫ﺘﻨﻬﻲ ﻋﻤﻠﻴﺔ ﺘﺤﺭﻴﺭ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻭﺘﺒﻘﻲ ﻋﻠﻰ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺍﻟﺘﻲ ﺤﺩﺜﺕ ﻟﻪ‪.‬‬

‫ﺍﻟﺘﺤﺭﻙ ﺇﻟﻰ ﺍﻷﻭل ‪:MoveFirst‬‬


‫ﺘﺠﻌل ﺃﻭل ﻋﻨﺼﺭ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ ﻫﻭ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ )‪.(Position = 0‬‬

‫ﺍﻟﺘﺤﺭﻙ ﺇﻟﻰ ﺍﻷﺨﻴﺭ ‪:MoveLast‬‬


‫ﺘﺠﻌل ﺁﺨﺭ ﻋﻨﺼﺭ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ ﻫﻭ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ )‪.(Position = Count -1‬‬

‫ﺍﻟﺘﺤﺭﻙ ﺇﻟﻰ ﺍﻟﺘﺎﻟﻲ ‪:MoveNext‬‬


‫ﺘﺠﻌل ﺍﻟﻌﻨﺼﺭ ﺍﻟﺘﺎﻟﻲ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ ﻫﻭ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ )‪.(Position += 1‬‬

‫ﺍﻟﺘﺤﺭﻙ ﺇﻟﻰ ﺍﻟﺴﺎﺒﻕ ‪:MovePrevious‬‬


‫ﺘﺠﻌل ﺍﻟﻌﻨﺼﺭ ﺍﻟﺴﺎﺒﻕ ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ ﻫﻭ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ )‪.(Position -= 1‬‬

‫ﺇﺯﺍﻟﺔ ﺍﻟﺤﺎﻟﻲ ‪:RemoveCurrent‬‬


‫ﺘﺯﻴل ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺨﻁﺄ ﺴﻴﺤﺩﺙ ﻓﻲ ﺍﻟﺤﺎﻻﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -‬ﻟﻭ ﻜﺎﻨﺕ ﻟﻠﺨﺎﺼﻴﺔ ‪ BindingSource.AllowRemove‬ﺍﻟﻘﻴﻤﺔ ‪.False‬‬
‫‪٤٢٦‬‬
‫‪ -‬ﻟﻭ ﻜﺎﻨـﺕ ﺍﻟﻘﺎﺌﻤـﺔ ﺍﻟﺩﺍﺨﻠﻴـﺔ ﻟﻠﻘـﺭﺍﺀﺓ ﻓﻘـﻁ ‪ Read Only‬ﺃﻭ ﺜﺎﺒﺘـﺔ ﺍﻟﺤﺠـﻡ‬
‫‪.Fixed Size‬‬
‫‪ -‬ﻟﻭ ﻜﺎﻥ ﺍﻟﻤﻭﻀﻊ ﺍﻟﺤﺎﻟﻲ ﻏﻴﺭ ﻤﻘﺒﻭل‪ ،‬ﺴﻭﺍﺀ ﻜﺎﻥ ﺃﺼﻐﺭ ﻤﻥ ﺼﻔﺭ ﺃﻭ ﻜﺎﻥ ﺃﻜﺒـﺭ‬
‫ﻤﻥ ﺃﻭ ﻴﺴﺎﻭﻱ ﻋﺩﺩ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ‪.‬‬

‫ﺘﺼﻔﻴﺭ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ ‪:ResetCurrentItem‬‬


‫ﺘﻁﻠﻕ ﺍﻟﺤﺩﺙ ‪ ListChanged‬ﻟﺘﻁﻠﺏ ﻤﻥ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺘﻲ ﺘﻌﺭﺽ ﺍﻟﻌﻨﺼـﺭ ﺍﻟﺤـﺎﻟﻲ ﺃﻥ‬
‫ﺘﻨﻌﺵ ﺍﻟﻘﻴﻡ ﺍﻟﺘﻲ ﺘﻌﺭﻀﻬﺎ‪.‬‬

‫ﺘﺼﻔﻴﺭ ﺍﻻﺭﺘﺒﺎﻁﺎﺕ ‪:ResetBindings‬‬


‫ﺘﻁﻠﻕ ﺍﻟﺤﺩﺙ ‪ ListChanged‬ﻟﺘﻁﻠﺏ ﻤﻥ ﻜل ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﺭﺘﺒﻁﺔ ﺒﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺃﻥ‬
‫ﺘﻨﻌﺵ ﺍﻟﻘﻴﻡ ﺍﻟﺘﻲ ﺘﻌﺭﻀﻬﺎ‪ ،‬ﻭﻫﻲ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻤﻨﻁﻘﻴﺎ‪ ،‬ﺇﺫﺍ ﺠﻌﻠﺕ ﻗﻴﻤﺘـﻪ ‪ True‬ﻓﻬـﺫﺍ‬
‫ﻤﻌﻨﺎﻩ ﺃﻥ ﻫﻨﺎﻙ ﺘﻐﻴﻴﺭﺍ ﻓﻲ ﻤﺨﻁﻁ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻨﻔﺴﻪ )ﻜﺤﺩﻭﺙ ﺘﻐﻴﻴـﺭ ﻓـﻲ ﺃﻋﻤـﺩﺓ‬
‫ﺍﻟﺠﺩﻭل(‪ ،‬ﻭﺇﺫﺍ ﺠﻌﻠﺘﻪ ‪ False‬ﻓﻬﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻥ ﺍﻟﺘﻐﻴﻴﺭ ﻗﺩ ﺤﺩﺙ ﻓﻲ ﺒﻌﺽ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤـﺔ‬
‫ﺍﻟﺩﺍﺨﻠﻴﺔ ﻓﻘﻁ‪.‬‬
‫ﻭﻴﺘﻡ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺁﻟﻴﺎ ﻋﻨﺩ ﺘﻐﻴﺭ ﻗﻴﻤﺔ ﺍﻟﺨﺎﺼـﻴﺔ ‪ DataSource‬ﺃﻭ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪ ،DataMember‬ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺃﻥ ﺘﺴﺘﺩﻋﻴﻬﺎ ﺒﻨﻔﺴﻙ ﺇﺫﺍ ﻗﻤﺕ ﺒﺘﻐﻴﻴﺭ ﺒﻌﺽ ﺍﻟﻌﻨﺎﺼﺭ ﻓـﻲ‬
‫ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫ﻭﻟﻜﻥ‪ ..‬ﻟﻤﺎﺫﺍ ﻨﺤﺘﺎﺝ ﺇﻟﻰ ﺍﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺴﻴﻠﺔ ‪ ResetBindings‬ﻹﻨﻌﺎﺵ ﻜـل ﺍﻟﻌﻨﺎﺼـﺭ‪،‬‬
‫ﺒﻴﻨﻤﺎ ﻴﻜﻔﻴﻨﺎ ﺇﻨﻌﺎﺵ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤـﺎﻟﻲ ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﻭﺴـﻴﻠﺔ ‪ResetCurrentItem‬؟‪ ..‬ﺃﻻ‬
‫ﺘﻌﺭﺽ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ ﻓﻘﻁ؟‬
‫ﻭﺍﻹﺠﺎﺒﺔ ﻫﻲ ﺃﻥ ﺒﻌﺽ ﺍﻷﺩﻭﺍﺕ ﺘﻌﺭﺽ ﺃﻜﺜﺭ ﻤﻥ ﻋﻨﺼﺭ ﻓﻲ ﻨﻔﺱ ﺍﻟﻭﻗـﺕ )ﻜﺎﻟﻘﺎﺌﻤـﺔ‬
‫‪ ListBox‬ﻭﺠﺩﻭل ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ،(DataGridView‬ﺒﻴﻨﻤﺎ ﺒﻌﺽ ﺍﻷﺩﻭﺍﺕ ﺘﻌـﺭﺽ‬
‫ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻓﻘﻁ )ﻤﺜل ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﻭﺍﻟﻼﻓﺘﺔ(‪ ..‬ﻟﻬﺫﺍ ﺇﺫﺍ ﺤﺩﺙ ﺘﻐﻴﻴﺭ ﻓـﻲ ﻋـﺩﺩ ﻤـﻥ‬
‫ﺍﻟﻌﻨﺎﺼﺭ ﻭﻜﻨﺕ ﺘﻌﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻗﺎﺌﻤﺔ ﺃﻭ ﺠﺩﻭل ﻋﺭﺽ‪ ،‬ﻓﻌﻠﻴﻙ ﺒﺎﺴﺘﺩﻋﺎﺀ ﺍﻟﻭﺴـﻴﻠﺔ‬
‫‪ ،ResetBindings‬ﺃﻤﺎ ﺇﺫﺍ ﻜﻨﺕ ﺘﺴﺘﺨﺩﻡ ﺃﺩﻭﺍﺕ ﻋﺭﺽ ﺒﺴﻴﻁﺔ ﻜﻤﺭﺒﻊ ﺍﻟﻨﺹ ﻭﺍﻟﻼﻓﺘـﺔ‬
‫‪٤٢٧‬‬
‫ﻭﺤﺩﺙ ﺘﻐﻴﺭ ﻓﻲ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻓﺎﺴﺘﺨﺩﻡ ﺍﻟﻭﺴـﻴﻠﺔ ‪ ..ResetCurrentItem‬ﺃﻤـﺎ ﺇﺫﺍ‬
‫ﻜﺎﻥ ﺍﻟﺘﻐﻴﺭ ﻓﻲ ﻋﻨﺼﺭ ﻏﻴﺭ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ‪ ،‬ﻓﻼ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺇﻨﻌﺎﺵ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺒﺴـﻴﻁﺔ‪،‬‬
‫ﻷﻨﻬﺎ ﺴﺘﻨﻌﺵ ﻨﻔﺴﻬﺎ ﺘﻠﻘﺎﺌﻴﺎ ﻋﻨﺩ ﺍﻻﻨﺘﻘﺎل ﺇﻟﻰ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺫﻱ ﺘﻐﻴﺭ‪.‬‬

‫ﺘﺼﻔﻴﺭ ﺍﻟﻌﻨﺼﺭ ‪:ResetItem‬‬


‫ﺘﻁﻠﻕ ﺍﻟﺤﺩﺙ ‪ ListChanged‬ﻟﺘﻁﻠﺏ ﻤﻥ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺘﻲ ﺘﻌﺭﺽ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺫﻱ ﺘﻐﻴﺭ ﻓﻲ‬
‫ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﺒﺄﻥ ﺘﻨﻌﺵ ﺍﻟﻘﻴﻤﺔ ﺍﻟﺘﻲ ﺘﻌﺭﻀﻬﺎ‪ ..‬ﻭﺘﺴﺘﻘﺒل ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺭﻗـﻡ ﺍﻟﻌﻨﺼـﺭ‬
‫ﺍﻟﻤﺭﺍﺩ ﺇﻨﻌﺎﺸﻪ‪.‬‬
‫ﻭﻴﺘﻡ ﺍﺴﺘﺩﻋﺎﺀ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺁﻟﻴﺎ ﻜﻠﻤﺎ ﺤﺩﺙ ﺘﻐﻴﻴﺭ ﻷﺤﺩ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﺩﺍﺨﻠﻴـﺔ‪ ،‬ﻟﻜـﻥ‬
‫ﺒﺈﻤﻜﺎﻨﻙ ﺍﺴﺘﺩﻋﺎﺅﻫﺎ ﺒﻨﻔﺴﻙ ﺃﻴﻀﺎ‪.‬‬

‫ﺇﻴﻘﺎﻑ ﺍﻟﺭﺒﻁ ‪:SuspendBinding‬‬


‫ﺘﻭﻗﻑ ﺭﺒﻁ ﺍﻟﻤﺼﺩﺭ ﺍﻟﺤﺎﻟﻲ ﺒﺎﻷﺩﻭﺍﺕ ﻤﺅﻗﺘﺎ‪.‬‬

‫ﻤﻭﺍﺼﻠﺔ ﺍﻟﺭﺒﻁ ‪:ResumeBinding‬‬


‫ﺘﻌﻴﺩ ﺭﺒﻁ ﺍﻷﺩﻭﺍﺕ ﺒﺎﻟﻤﺼﺩﺭ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﻤﺜﻠﻪ ﻤﻥ ﺃﺤﺩﺍﺙ ﺍﻟﻭﺍﺠﻬﺎﺕ ﺍﻟﺘﻲ ﺘﻤﺜﻠﻬﺎ‪ ،‬ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻷﺤـﺩﺍﺙ ﺍﻟﺘﺎﻟﻴـﺔ‪،‬‬
‫ﻭﻜﻠﻬﺎ ﻤﺄﻟﻭﻑ ﻟﻨﺎ ﻟﻬﺫﺍ ﻟﻥ ﻨﻜﺭﺭ ﺸﺭﺤﻬﺎ ﻫﻨﺎ‪:‬‬

‫ﺍﻟﺭﺒﻁ ﺍﻜﺘﻤل ‪BindingComplete‬‬


‫ﺍﻟﺤﺎﻟﻲ ﺘﻐﻴﺭ ‪CurrentChanged‬‬
‫ﺍﻟﻌﻨﺼﺭ ﺍﻟﺤﺎﻟﻲ ﺘﻐﻴﺭ ‪CurrentItemChanged‬‬
‫ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﻐﻴﺭ ‪DataMemberChanged‬‬
‫ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﻐﻴﺭ ‪DataSourceChanged‬‬
‫ﺍﻟﻤﻭﻀﻊ ﺘﻐﻴﺭ ‪PositionChanged‬‬
‫ﺨﻁﺄ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DataError‬‬

‫‪٤٢٨‬‬
‫ﻓﺌﺔ ﻤﺴﺎﻋﺩ ﺭﺒﻁ ﺍﻟﻘﻭﺍﺌﻡ ‪ListBindingHelper Class‬‬

‫ﺘﺤﺘﻭﻱ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻋﻠﻰ ﺒﻌﺽ ﺍﻟﻭﺴﺎﺌل ﺍﻟﻤﺸﺘﺭﻜﺔ ‪ ،Shared Methods‬ﺍﻟﺘﻲ ﺘﺴﺘﺨﺩﻤﻬﺎ ﺍﻟﻔﺌـﺔ‬
‫‪ BindingSource‬ﻓﻲ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل ﻫﻲ‪:‬‬

‫ﻤﻌﺭﻓﺔ ﺍﻟﻘﺎﺌﻤﺔ ‪:GetList‬‬


‫ﺘﺴﺘﻘﺒل ﺍﻟﻜﺎﺌﻥ ﺍﻟﺫﻱ ﻴﻌﻤل ﻜﻤﺼﺩﺭ ﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﺘﻌﻴﺩ ﻗﺎﺌﻤﺔ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺘﻲ ﻴﺤﺘﻭﻴﻬـﺎ‪ ،‬ﻭﺍﻟﺘـﻲ‬
‫ﻴﻤﻜﻥ ﺍﻻﺭﺘﺒﺎﻁ ﺒﻬﺎ ﺇﻥ ﻭﺠﺩﺕ‪ ،‬ﻓﺈﻥ ﻟﻡ ﺘﻭﺠﺩ‪ ،‬ﻓﺈﻥ ﻫﺫﻩ ﺍﻟﻭﺴـﻴﻠﺔ ﺘﻌﻴـﺩ ﻜـﺎﺌﻥ ﻤﺼـﺩﺭ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻨﻔﺴﻪ‪.‬‬
‫ﻭﺘﻭﺠﺩ ﺼﻴﻐﺔ ﺃﺨﺭﻯ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﻟﻬﺎ ﻤﻌﺎﻤل ﺜﺎﻥ‪ ،‬ﻴﺴﺘﻘﺒل ﺍﺴﻡ ﺍﻟﺨﺎﺼﻴﺔ ﺍﻟﺘـﻲ ﺘﻌﻤـل‬
‫ﻜﻌﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻟﻠﺤﺼﻭل ﻋﻠﻰ ﻗﺎﺌﻤﺔ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﺨﺎﺼﺔ ﺒﻬﺎ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺍﺴﻡ ﺍﻟﻘﺎﺌﻤﺔ ‪:GetListName‬‬


‫ﺘﻌﻴﺩ ﺍﺴﻡ ﺍﻟﻘﺎﺌﻤﺔ ﺇﻥ ﻭﺠﺩﺕ‪ ،‬ﺃﻭ ﺍﺴﻡ ﻨﻭﻉ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻟﻬﺎ ﻤﻌﺎﻤﻼﻥ‪:‬‬
‫‪ -‬ﺍﻟﻜﺎﺌﻥ ﺍﻟﺫﻱ ﻴﻌﻤل ﻜﻤﺼﺩﺭ ﻟﻠﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -‬ﻤﺼﻔﻭﻓﺔ ﻤﻥ ﻭﺍﺼﻔﺎﺕ ﺍﻟﺨﺼﺎﺌﺹ ‪ ،PropertyDescriptor‬ﺍﻟﺘﻲ ﺘﺤﺩﺩ ﺍﻟﻘﺎﺌﻤـﺔ‬
‫ﺍﻟﻤﺭﺍﺩ ﻤﻌﺭﻓﺔ ﺍﺴﻤﻬﺎ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﻨﻭﻉ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ ‪:GetListItemType‬‬


‫ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻤﻤﺎﺜﻠﺔ ﻓﻲ ﺼﻴﻐﺘﻴﻬﺎ ﻟﻠﻭﺴﻴﻠﺔ ﺍﻟﺴﺎﺒﻘﺔ‪ ،‬ﺇﻻ ﺃﻨﻬﺎ ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﺍﻟﻨﻭﻉ ‪ ،Type‬ﺍﻟﺫﻱ‬
‫ﻴﻤﺜل ﻨﻭﻉ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ‪.‬‬

‫ﻤﻌﺭﻓﺔ ﺨﺼﺎﺌﺹ ﻋﻨﺎﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ ‪:GetListItemProperties‬‬


‫ﺘﻌﻴﺩ ﻤﺠﻤﻭﻋﺔ ﻭﺍﺼﻔﺎﺕ ﺍﻟﺨﺼﺎﺌﺹ ‪ ،PropertyDescriptorCollection‬ﺍﻟﺘﻲ ﺘﺼﻑ‬
‫ﺨﺼﺎﺌﺹ ﻋﻨﺼﺭ ﺍﻟﻘﺎﺌﻤﺔ‪ ..‬ﻭﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﺜﻼﺜﺔ ﻤﻌﺎﻤﻼﺕ‪:‬‬
‫‪ -‬ﺍﻟﻜﺎﺌﻥ ﺍﻟﺫﻱ ﻴﻌﻤل ﻜﻤﺼﺩﺭ ﻟﻠﺒﻴﺎﻨﺎﺕ‪.‬‬

‫‪٤٢٩‬‬
‫‪ -‬ﺍﺴﻡ ﻋﻨﺼﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -‬ﻤﺼﻔﻭﻓﺔ ﻤﻥ ﻭﺍﺼﻔﺎﺕ ﺍﻟﺨﺼﺎﺌﺹ ‪ ،PropertyDescriptor‬ﺍﻟﺘﻲ ﺘﺤﺩﺩ ﺍﻟﻘﺎﺌﻤـﺔ‬
‫ﺍﻟﻤﺭﺍﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻬﺎ‪.‬‬
‫ﻭﺘﻭﺠﺩ ﺼﻴﻐﺘﺎﻥ ﺃﺨﺭﻴﺎﻥ ﻟﻬﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪ ،‬ﺇﺤﺩﺍﻫﻤﺎ ﺘﺴﺘﻘﺒل ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ﻓﻘﻁ‪ ،‬ﻭﺍﻷﺨـﺭﻯ‬
‫ﺘﺴﺘﻘﺒل ﺍﻟﻤﻌﺎﻤﻠﻴﻥ ﺍﻷﻭل ﻭﺍﻟﺜﺎﻟﺙ ﻓﻘﻁ‪.‬‬

‫‪٤٣٠‬‬
‫ﻓﺌﺔ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ ‪BindingNavigator Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺭﺙ ﻓﺌﺔ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ‪ ،ToolStrip Class‬ﻟﻬﺫﺍ ﻓﻬﻲ ﺘﻌﻤل ﻜﺭﻑ ﺃﺩﻭﺍﺕ ﻴﻌﺭﺽ‬
‫ﻤﺠﻤﻭﻋﺔ ﻤﻥ ﺍﻷﺯﺭﺍﺭ‪ ،‬ﺍﻟﺘﻲ ﺘﺘﻴﺢ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺍﻟﺘﺤﺭﻙ ﻋﺒﺭ ﺴﺠﻼﺕ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻭﺤـﺫﻑ‬
‫ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﺃﻭ ﺇﻀﺎﻓﺔ ﺴﺠل ﺠﺩﻴﺩ‪ ،‬ﻜل ﻫﺫﺍ ﺒﺩﻭﻥ ﺃﻥ ﺘﻜﺘﺏ ﺃﻨﺕ ﺤﺭﻓﺎ ﻤﻥ ﺍﻟﻜﻭﺩ!‬
‫ﻭﻴﺒﺩﻭ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ﺍﻟﺫﻱ ﻴﻌﺭﻀﻪ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ ﻜﻤﺎ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪:‬‬

‫ﻻﺤﻅ ﺃﻥ ﺁﺨﺭ ﺯﺭ ﻋﻠﻰ ﺍﻟﺸﺭﻴﻁ ﻴﺘﻴﺢ ﻟﻙ ﺇﻀﺎﻓﺔ ﺃﺯﺭﺍﺭ ﻭﺃﺩﻭﺍﺕ ﺠﺩﻴﺩﺓ ﺇﻟﻰ ﺍﻟﺸـﺭﻴﻁ‪ ،‬ﺒـﻨﻔﺱ‬
‫ﺍﻟﻁﺭﻴﻘﺔ ﺍﻟﺘﻲ ﺘﻌﻠﻤﻨﺎﻫﺎ ﻓﻲ ﻜﺘﺎﺏ ﺒﺭﻤﺠﺔ ﺍﻟﻭﻴﻨﺩﻭﺯ‪ ..‬ﻫﺫﺍ ﻤﻌﻨﺎﻩ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺍﺴﺘﻐﻼل ﻤﺴـﺎﺤﺔ‬
‫ﺍﻟﺸﺭﻴﻁ ﻹﻀﺎﻓﺔ ﺃﺯﺭﺍﺭ ﻭﻗﻭﺍﺌﻡ ﻭﻤﺭﺒﻌﺎﺕ ﻨﺼﻭﺹ ﻭﻻﻓﺘﺎﺕ ﺘﺅﺩﻱ ﺃﻴﺔ ﻭﻅﺎﺌﻑ ﺃﺨﺭﻯ ﺨﺎﺼـﺔ‬
‫ﺒﻙ )ﻜﺎﻟﻘﺹ ﻭﺍﻟﻠﺼﻕ ﻭﻋﺭﺽ ﺤﺎﻟﺔ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻭﻏﻴﺭ ﻫﺫﺍ(‪ ،‬ﻭﺒﻬﺫﺍ ﻻ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺇﻀـﺎﻓﺔ ﺭﻑ‬
‫ﺃﺩﻭﺍﺕ ﺁﺨﺭ ﺨﺎﺹ ﺒﻙ‪.‬‬
‫ﻭﺍﻟﺼﻭﺭﺓ ﺍﻟﺘﺎﻟﻴﺔ ﺘﺭﻴﻙ ﻜﻴﻑ ﻴﺒﺩﻭ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ ﻋﻨﺩ ﺘﺸﻐﻴل ﺍﻟﻤﺸﺭﻭﻉ ‪ Navigator‬ﺍﻟﻤﺭﻓـﻕ‬
‫ﺒﺄﻤﺜﻠﺔ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ‪:‬‬

‫‪٤٣١‬‬
‫ﻤﺭﺓ ﺃﺨﺭﻯ ﺃﺫﻜﺭﻙ‪ :‬ﻟﻭ ﺃﻀﻔﺕ ﺴﺠﻼ ﺠﺩﻴﺩ ﺒﻀﻐﻁ ﺯﺭ ﺍﻹﻀﺎﻓﺔ ﺍﻟﻤﻭﺠﻭﺩ ﻋﻠﻰ ﺸﺭﻴﻁ ﻤﻭﺠـﻪ‬
‫ﺍﻟﺭﺒﻁ‪ ،‬ﺃﻭ ﺤﺫﻓﺕ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﺒﻀﻐﻁ ﺯﺭ ﺍﻟﺤﺫﻑ‪ ،‬ﺃﻭ ﻏﻴﺭﺕ ﻗﻴﻤﺔ ﺃﻱ ﺤﻘل ﻓـﻲ ﺍﻟﺴـﺠل‬
‫ﺍﻟﺤﺎﻟﻲ ﺒﺘﻐﻴﻴﺭ ﻗﻴﻤﺔ ﺃﺤﺩ ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺹ‪ ،‬ﻓﺈﻥ ﻫﺫﻩ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﺴﺘﺅﺜﺭ ﻓﻘـﻁ ﻋﻠـﻰ ﻤﺠﻤﻭﻋـﺔ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ ،DataSet‬ﻟﻜﻥ ﺘﻅل ﻤﻬﻤﺔ ﺘﺤﺩﻴﺙ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﺘﺭﻭﻜـﺔ ﻟـﻙ‪ ..‬ﻭﺇﺫﺍ ﻜﻨـﺕ ﻻ‬
‫ﺘﺭﻏﺏ ﺃﻥ ﻴﻌﺒﺙ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺒﻘﻴﻡ ﺒﻌﺽ ﺍﻟﺤﻘﻭل‪ ،‬ﻓﺎﺠﻌل ﻤﺭﺒﻌﺎﺕ ﺍﻟﻨﺼـﻭﺹ ﺍﻟﻤﻨـﺎﻅﺭﺓ ﻟﻬـﺎ‬
‫ﻟﻠﻘﺭﺍﺀﺓ ﻓﻘﻁ‪ ،‬ﺃﻭ ﺍﺭﺒﻁ ﻫﺫﻩ ﺍﻟﺤﻘﻭل ﺒﻼﻓﺘﺎﺕ ﻤﻨﺫ ﺍﻟﺒﺩﺍﻴﺔ‪ ..‬ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﺘﻐﻴﻴـﺭ ﺍﻟﻤﺴـﺘﺨﺩﻡ‬
‫ﻟﻘﻴﻤﺔ ﺍﻟﻤﻌﺭﻑ ‪ ID‬ﻟﻥ ﺘﺅﺜﺭ ﻓﻲ ﺸﻲﺀ‪ ،‬ﻷﻥ ﻫﺫﺍ ﺍﻟﺤﻘل ﻤﻭﻟﺩ ﺁﻟﻴﺎ‪ ،‬ﻭﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻻ ﻴﺴﺘﻁﻴﻊ‬
‫ﺘﻐﻴﻴﺭﻩ‪.‬‬
‫ﻭﻟﻭ ﻟﻡ ﺘﻜﻥ ﺘﺭﻏﺏ ﻓﻲ ﺃﻥ ﻴﺤﺫﻑ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻟﺴﺠﻼﺕ ﺃﻭ ﻴﻀﻴﻑ ﺴﺠﻼﺕ ﺠﺩﻴـﺩﺓ‪ ،‬ﻓﻴﻤﻜﻨـﻙ‬
‫ﺇﺯﺍﻟﺔ ﺯﺭ ﺍﻟﺤﺫﻑ ﺃﻭ ﺯﺭ ﺍﻹﻀﺎﻓﺔ ﻤﻥ ﻓﻭﻕ ﺍﻟﺸﺭﻴﻁ ﻓﻲ ﻭﻗﺕ ﺍﻟﺘﺼﻤﻴﻡ‪ ،‬ﺃﻭ ﻴﻤﻜﻨﻙ ﺘﻌﻁﻴﻠﻬﻤـﺎ‪،‬‬
‫ﻭﺴﺘﺭﻯ ﻜﻴﻑ ﻨﻔﻌل ﻫﺫﺍ ﺒﻌﺩ ﻗﻠﻴل ﻭﻨﺤﻥ ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﺨﺼﺎﺌﺹ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ‪.‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﺍﻟﻔﺌﺔ ‪ BindingNavigator‬ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﺍﻟﺼﻴﻐﺔ ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻥ ﻤﺼﺩﺭ ﺍﻟﺭﺒﻁ ‪ ،BindingSource‬ﺍﻟﺫﻱ ﺴـﻴﺘﺤﻜﻡ ﻤـﻥ‬
‫ﺨﻼﻟﻪ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ ﻓﻲ ﺴﺠﻼﺕ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -٣‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﻤﻌﺎﻤﻼ ﻤﻨﻁﻘﻴﺎ‪ ،‬ﺇﺫﺍ ﺠﻌﻠﺘﻪ ‪ False‬ﻓﻠﻥ ﻴﻌﺭﺽ ﻤﻭﺠﻪ ﺍﻟـﺭﺒﻁ‬
‫ﺃﺯﺭﺍﺭ ﺍﻟﺘﺤﻜﻡ ﺍﻟﻘﻴﺎﺴﻴﺔ )ﺃﺯﺭﺍﺭ ﺍﻻﻨﺘﻘﺎل ﻭﺯﺭ ﺍﻟﺤﺫﻑ ﻭﺯﺭ ﺍﻹﻀﺎﻓﺔ(‪.‬‬
‫‪ -٤‬ﺍﻟﺼﻴﻐﺔ ﺍﻟﺭﺍﺒﻌﺔ ﺘﺴﺘﻘﺒل ﻜﺎﺌﻨﺎ ﻤﻥ ﻨﻭﻉ ﺍﻟﻭﺍﺠﻬﺔ ‪) IContainer‬ﻤﺜل ﺍﻟﻨﻤﻭﺫﺝ(‪ ،‬ﻟﻴـﺘﻡ‬
‫ﻋﺭﺽ ﺸﺭﻴﻁ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ ﻋﻠﻴﻪ‪.‬‬

‫ﻭﺇﻀﺎﻓﺔ ﺇﻟﻰ ﻤﺎ ﺘﺭﺜﻪ ﻤﻥ ﺨﺼﺎﺌﺹ ﺍﻟﻔﺌﺔ ‪ ،ToolStrip‬ﺘﻤﺘﻠﻙ ﺍﻟﻔﺌـﺔ ‪BindingNavigator‬‬


‫ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﻤﺼﺩﺭ ﺍﻟﺭﺒﻁ ‪:BindingSource‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻜﺎﺌﻥ ﻤﺼﺩﺭ ﺍﻟﺭﺒﻁ ‪ BindingSource‬ﺍﻟﺫﻱ ﻴﺴـﺘﺨﺩﻤﻪ ﻤﻭﺠـﻪ ﺍﻟـﺭﺒﻁ‬
‫ﻟﻠﺘﺤﻜﻡ ﻓﻲ ﺍﻟﺴﺠﻼﺕ‪.‬‬
‫‪٤٣٢‬‬
‫ﻋﻨﺼﺭ ﺇﻀﺎﻓﺔ ﺠﺩﻴﺩ ‪:AddNewItem‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﺼﺭ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ‪ ToolStripItem‬ﺍﻟﻤﺴﺘﺨﺩﻡ ﻹﻀﺎﻓﺔ ﺴـﺠل ﺠﺩﻴـﺩ‬
‫ﺇﻟﻰ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀـﻲ ﻴﻜـﻭﻥ ﻫـﺫﺍ ﺍﻟﻌﻨﺼـﺭ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪ ..ToolStripButton‬ﻭﻴﻜﻭﻥ ﺯﺭ ﺍﻹﻀﺎﻓﺔ ﻤﻌﻁﻼ ﻋﻠﻰ ﺸﺭﻴﻁ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ‪ ،‬ﺇﺫﺍ ﻜﺎﻨـﺕ‬
‫ﻟﻠﺨﺎﺼﻴﺔ ‪ BindingSource.AllowNew‬ﺍﻟﻘﻴﻤﺔ ‪.False‬‬
‫ﻻﺤﻅ ﺃﻨﻙ ﻗﺩ ﺘﺠﺩ ﺯﺭ ﺍﻹﻀﺎﻓﺔ ﻤﻌﻁﻼ ﻓﻲ ﺒﻌﺽ ﺍﻟﺒـﺭﺍﻤﺞ‪ ..‬ﺇﺫﺍ ﺤـﺩﺜﺕ ﻤﻌـﻙ ﻫـﺫﻩ‬
‫ﺍﻟﻤﺸﻜﻠﺔ‪ ،‬ﻓﻴﻤﻜﻨﻙ ﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ ‪ Nothing‬ﻓﻲ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻤﻥ ﺍﻟﻜﻭﺩ‪ ،‬ﺃﻭ ﺍﺨﺘﻴﺎﺭ ﺍﻟﻘﻴﻤﺔ‬
‫)‪ (None‬ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ‪ ..‬ﻫﺫﺍ ﺴﻴﻌﻁل ﺍﻟﻭﻅﻴﻔﺔ ﺍﻵﻟﻴﺔ ﻟﻠـﺯﺭ‪،‬‬
‫ﻟﻜﻨﻪ ﻟﻥ ﻴﺤﺫﻓﻪ ﻤﻥ ﻋﻠﻰ ﺸﺭﻴﻁ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ‪ ..‬ﻟﻬﺫﺍ ﻴﻤﻜﻨﻙ ﻨﻘﺭﻩ ﻤﺭﺘﻴﻥ ﺒﺎﻟﻔﺄﺭﺓ‪ ،‬ﻭﻜﺘﺎﺒـﺔ‬
‫ﺍﻟﺴﻁﺭ ﺍﻟﻭﺤﻴﺩ ﺍﻟﺘﺎﻟﻲ ﻓﻲ ﺤﺩﺙ ﻀﻐﻁﻪ‪:‬‬
‫;) (‪AuthorsBindingNavigator.BindingSource.AddNew‬‬

‫ﻋﻨﺼﺭ ﺍﻟﺤﺫﻑ ‪:DeleteItem‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﺼﺭ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ‪ ToolStripItem‬ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﺤﺫﻑ ﺍﻟﺴﺠل ﺍﻟﺤـﺎﻟﻲ‬
‫ﻤﻥ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀـﻲ ﻴﻜـﻭﻥ ﻫـﺫﺍ ﺍﻟﻌﻨﺼـﺭ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪ ..ToolStripButton‬ﻭﻴﻜﻭﻥ ﺯﺭ ﺍﻟﺤﺫﻑ ﻤﻌﻁﻼ ﻋﻠﻰ ﺸﺭﻴﻁ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ‪ ،‬ﺇﺫﺍ ﻜﺎﻨـﺕ‬
‫ﻟﻠﺨﺎﺼﻴﺔ ‪ BindingSource.AllowRemove‬ﺍﻟﻘﻴﻤﺔ ‪.False‬‬
‫ﻭﺇﺫﺍ ﻭﺠﺩﺕ ﺯﺭ ﺍﻟﺤﺫﻑ ﻤﻌﻁﻼ ﻓﻲ ﺒﻌﺽ ﺍﻟﺤﺎﻻﺕ‪ ،‬ﻓﻀﻊ ﺍﻟﻘﻴﻤﺔ ‪ Nothing‬ﻓـﻲ ﻫـﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﻤﻥ ﺍﻟﻜﻭﺩ‪ ،‬ﺃﻭ )‪ (None‬ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ‪ ،‬ﺜـﻡ ﺍﻨﻘـﺭ‬
‫ﻤﺭﺘﻴﻥ ﺒﺎﻟﻔﺄﺭﺓ ﻓﻭﻕ ﺯﺭ ﺍﻟﺤﺫﻑ ﺍﻟﻤﻭﺠﻭﺩ ﻋﻠﻰ ﺸﺭﻴﻁ ﻤﻭﺠﻪ ﺍﻟـﺭﺒﻁ ‪ ،‬ﻭﺍﻜﺘـﺏ ﺍﻟﺴـﻁﺭ‬
‫ﺍﻟﻭﺤﻴﺩ ﺍﻟﺘﺎﻟﻲ ﻓﻲ ﺤﺩﺙ ﻀﻐﻁﻪ‪:‬‬
‫;) (‪AuthorsBindingNavigator.BindingSource.RemoveCurrent‬‬

‫‪٤٣٣‬‬
‫ﻋﻨﺼﺭ ﺍﻟﺘﺤﺭﻙ ﺇﻟﻰ ﺍﻷﻭل ‪:MoveFirstItem‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﺼﺭ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ‪ ToolStripItem‬ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﻼﻨﺘﻘﺎل ﺇﻟﻰ ﺃﻭل ﺴﺠل‬
‫ﻓﻲ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀـﻲ ﻴﻜـﻭﻥ ﻫـﺫﺍ ﺍﻟﻌﻨﺼـﺭ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪.ToolStripButton‬‬

‫ﻋﻨﺼﺭ ﺍﻟﺘﺤﺭﻙ ﺇﻟﻰ ﺍﻷﺨﻴﺭ ‪:MoveLastItem‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﺼﺭ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ‪ ToolStripItem‬ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﻼﻨﺘﻘﺎل ﺇﻟﻰ ﺁﺨﺭ ﺴﺠل‬
‫ﻓﻲ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀـﻲ ﻴﻜـﻭﻥ ﻫـﺫﺍ ﺍﻟﻌﻨﺼـﺭ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪.ToolStripButton‬‬

‫ﻋﻨﺼﺭ ﺍﻟﺘﺤﺭﻙ ﺇﻟﻰ ﺍﻟﺘﺎﻟﻲ ‪:MoveNextItem‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﺼﺭ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ‪ ToolStripItem‬ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﻼﻨﺘﻘﺎل ﺇﻟـﻰ ﺍﻟﺴـﺠل‬
‫ﺍﻟﺘﺎﻟﻲ ﻓﻲ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﻴﻜـﻭﻥ ﻫـﺫﺍ ﺍﻟﻌﻨﺼـﺭ ﺯﺭ ﺭﻑ‬
‫ﺍﻷﺩﻭﺍﺕ ‪.ToolStripButton‬‬

‫ﻋﻨﺼﺭ ﺍﻟﺘﺤﺭﻙ ﺇﻟﻰ ﺍﻟﺴﺎﺒﻕ ‪:MovePreviousItem‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﺼﺭ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ‪ ToolStripItem‬ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﻼﻨﺘﻘﺎل ﺇﻟـﻰ ﺍﻟﺴـﺠل‬
‫ﺍﻟﺴﺎﺒﻕ ﻓﻲ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﻴﻜﻭﻥ ﻫﺫﺍ ﺍﻟﻌﻨﺼﺭ ﻤـﻥ ﺍﻟﻨـﻭﻉ‬
‫‪.ToolStripButton‬‬

‫ﻋﻨﺼﺭ ﺍﻟﻤﻭﻀﻊ ‪:PositionItem‬‬


‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﺼﺭ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ‪ ToolStripItem‬ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﻌﺭﺽ ﺭﻗـﻡ ﺍﻟﺴـﺠل‬
‫ﺍﻟﻤﻌﺭﻭﺽ ﺤﺎﻟﻴﺎ‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀـﻊ ﺍﻻﻓﺘﺭﺍﻀـﻲ ﻴﺴـﺘﺨﺩﻡ ﻤﺭﺒـﻊ ﻨـﺹ ﺭﻑ ﺍﻷﺩﻭﺍﺕ‬
‫‪ ToolStripTextBox‬ﻟﻬﺫﺍ ﺍﻟﻐﺭﺽ‪ ،‬ﻭﺫﻟﻙ ﻟﻠﺴﻤﺎﺡ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺒﻜﺘﺎﺒﺔ ﺭﻗﻡ ﺍﻟﺴﺠل ﺍﻟـﺫﻱ‬
‫ﻴﺭﻴﺩﻩ ﻭﻀﻐﻁ ﺯﺭ ﺍﻹﺩﺨﺎل ‪ Enter‬ﻟﻼﻨﺘﻘﺎل ﺇﻟﻴﻪ ﻤﺒﺎﺸﺭﺓ‪ ..‬ﻭﻴﻌﺭﺽ ﻤﺭﺒﻊ ﺍﻟﻨﺹ ﻤﻭﻀﻊ‬
‫ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼﻴﺔ ‪.BindingSource.Position‬‬

‫‪٤٣٤‬‬
‫ﻋﻨﺼﺭ ﺍﻟﻌﺩ ‪:CountItem‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻋﻨﺼﺭ ﺭﻑ ﺍﻷﺩﻭﺍﺕ ‪ ToolStripItem‬ﺍﻟﻤﺴﺘﺨﺩﻡ ﻟﻌﺭﺽ ﺍﻟﻌـﺩﺩ ﺍﻟﻜﻠـﻲ‬
‫ﻟﻠﺴﺠﻼﺕ ﻓﻲ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﺴـﺘﺨﺩﻡ ﻻﻓﺘـﺔ ﺭﻑ ﺃﺩﻭﺍﺕ‬
‫ـﻴﺔ‬
‫ـﺔ ﺍﻟﺨﺎﺼــ‬
‫ـﺭﺽ ﻗﻴﻤــ‬
‫ـﻲ ﺘﻌــ‬
‫ـﺭﺽ‪ ،‬ﻭﻫــ‬
‫ـﺫﺍ ﺍﻟﻐــ‬
‫‪ ToolStripLabel‬ﻟﻬــ‬
‫‪.BindingSource.Count‬‬

‫ﺘﻨﺴﻴﻕ ﻋﻨﺼﺭ ﺍﻟﻌﺩ ‪:CountItemFormat‬‬


‫ﺘﺴﺘﻘﺒل ﻨﺼﺎ ﻴﻤﺜل ﺍﻟﺼﻴﻐﺔ ﺍﻟﺘﻲ ﺴﻴﺴﺘﺨﺩﻤﻬﺎ ﻋﻨﺼﺭ ﺍﻟﻌﺩ ‪ CountItem‬ﻟﻌـﺭﺽ ﻋـﺩﺩ‬
‫ﺍﻟﺴﺠﻼﺕ‪ ..‬ﻭﻓﻲ ﺍﻟﻭﻀﻊ ﺍﻻﻓﺘﺭﺍﻀﻲ ﺘﻜﻭﻥ ﻗﻴﻤـﺔ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ "}‪ .."of {0‬ﻭﻓـﻲ‬
‫ﺍﻟﻤﺸﺎﺭﻴﻊ ﺍﻟﻌﺭﺒﻴﺔ ﻋﻠﻴﻙ ﺘﻐﻴﻴﺭﻫﺎ ﺇﻟﻰ ﺼﻴﻐﺔ ﻤﻨﺎﺴﺒﺔ‪ ،‬ﻤﺜل "ﻤﻥ }‪ "{٠‬ﻟﻴﺒﺩﻭ ﺍﻟﺸﺭﻴﻁ ﻜﻤـﺎ‬
‫ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪:‬‬

‫ﻭﺘﻤﺘﻠﻙ ﺍﻟﻔﺌﺔ ‪ BindingNavigator‬ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺇﻀﺎﻓﺔ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻘﻴﺎﺴﻴﺔ ‪:AddStandardItems‬‬


‫ﺘﻀﻴﻑ ﺃﺯﺭﺍﺭ ﺍﻻﻨﺘﻘﺎل ﻭﺍﻟﺤﺫﻑ ﻭﺍﻹﻀﺎﻓﺔ ﻭﻤﺭﺒﻊ ﻨﺹ ﺍﻟﻤﻭﻀﻊ ﻭﻻﻓﺘﺔ ﻋﺩﺩ ﺍﻟﺴـﺠﻼﺕ‬
‫ﺇﻟﻰ ﺸﺭﻴﻁ ﻤﻭﺠﻪ ﺍﻟﺭﺒﻁ‪ ..‬ﻫﺫﺍ ﻤﻔﻴﺩ ﺇﺫﺍ ﺃﺭﺩﺕ ﺇﻨﺸﺎﺀ ﻤﻭﺠﻪ ﺭﺒﻁ ﻤﻥ ﺍﻟﻜﻭﺩ ﻭﻟـﻴﺱ ﻓـﻲ‬
‫ﻭﻗﺕ ﻟﺘﺼﻤﻴﻡ‪ ..‬ﻭﻓﻲ ﺤﺎﻟﺔ ﻭﺠﻭﺩ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻘﻴﺎﺴﻴﺔ ﺒﺎﻟﻔﻌل ﻋﻠﻰ ﺍﻟﺸـﺭﻴﻁ‪ ،‬ﻓـﺈﻥ ﻫـﺫﻩ‬
‫ﺍﻟﻭﺴﻴﻠﺔ ﻻ ﺘﺤﺫﻓﻬﺎ‪ ،‬ﺒل ﺘﻀﻴﻑ ﻨﺴﺨﺔ ﺃﺨﺭﻯ ﻤﻨﻬﺎ‪ ،‬ﻟﻜﻨﻬﺎ ﻻ ﺘﺼﻴﺭ ﻫﻲ ﺍﻟﻌﻨﺎﺼﺭ ﺍﻟﻔﻌﺎﻟﺔ‪.‬‬

‫‪٤٣٥‬‬
‫ﺇﺠﺎﺯﺓ ‪:Validate‬‬
‫ﺘﺠﻌل ﺍﻟﻨﻤﻭﺫﺝ ﻴﻔﺤﺹ ﻗﻴﻡ ﺍﻷﺩﻭﺍﺕ ﺍﻟﻤﻭﺠﻭﺩﺓ ﻋﻠﻴﻪ‪ ،‬ﻭﺘﻌﻴـﺩ ‪ True‬ﺇﺫﺍ ﻜﺎﻨـﺕ ﺒﻴﺎﻨﺎﺘﻬـﺎ‬
‫ﺼﺤﻴﺤﺔ‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﺍﻟﻔﺌﺔ ‪ BindingNavigator‬ﺍﻟﺤﺩﺙ ﺍﻟﺘﺎﻟﻲ‪:‬‬

‫ﺇﻨﻌﺎﺵ ﺍﻟﻌﻨﺎﺼﺭ ‪:RefreshItems‬‬


‫ﻴﻨﻁﻠﻕ ﺇﺫﺍ ﺤﺩﺜﺕ ﺘﻐﻴﺭﺍﺕ ﻓﻲ ﻤﺼﺩﺭ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺘﺴﺘﺩﻋﻲ ﺘﺤﺩﻴﺙ ﺃﺯﺭﺍﺭ ﻭﻻﻓﺘﺎﺕ ﻤﻭﺠـﻪ‬
‫ﺍﻟﺭﺒﻁ‪.‬‬

‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ‬

‫‪٤٣٦‬‬
‫ﻤﻠﺤﻕ‪٢ :‬‬
‫ﺃﻨﻭﺍﻉ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺍﻟﻤﺩﺍﺭﺓ‬
‫‪Managed SQL Data Types‬‬

‫ﻴﻤﻨﺤﻙ ﺍﻟﻨﻁﺎﻕ ‪ System.Data.SqlTypes‬ﻋﺩﺩﺍ ﻤـﻥ ﺍﻟﺴـﺠﻼﺕ ‪ Structures‬ﻭﺍﻟﻔﺌـﺎﺕ‬


‫‪ Classes‬ﺍﻟﺘﻲ ﺘﻤﺜل ﺃﻨﻭﺍﻉ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺨﺎﺼﺔ ﺒﺨﺎﺩﻡ ﺴﻴﻜﻴﻭل‪ ..‬ﻫـﺫﺍ ﻴﺴـﻬل ﻋﻠﻴـﻙ ﺇﺭﺴـﺎل‬
‫ﻭﺍﺴﺘﻘﺒﺎل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻨﺩ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻴﻭل‪.‬‬
‫ﻭﻓﻴﻤﺎ ﻴﻠﻲ‪ ،‬ﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻫﺫﻩ ﺍﻟﺴﺠﻼﺕ‪ ..‬ﻻ ﺘﻨﺱ‪ ‬ﺇﻀﺎﻓﺔ ﺍﻟﺠﻤﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ ﺃﻋﻠﻰ ﺼﻔﺤﺔ ﺍﻟﻜـﻭﺩ‪،‬‬
‫ﻗﺒل ﺘﺠﺭﺒﺔ ﺃﻱ ﻤﺜﺎل ﻓﻲ ﻫﺫﺍ ﺍﻟﻔﺼل‪:‬‬
‫;‪using System.Data.SqlTypes‬‬
‫ﻭﺴﺘﺠﺩ ﺃﻤﺜﻠﺔ ﻋﻠﻰ ﺒﻌﺽ ﻫﺫﻩ ﺍﻷﻨﻭﺍﻉ ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪.SqlDataTypes‬‬
‫ﻻﺤﻅ ﺃﻥ ﺠﻤﻴﻊ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﺘﻤﺜل ﺍﻟﻭﺍﺠﻬﺔ ‪ ،INullable‬ﻟﻬﺫﺍ ﻓﻬﻲ ﺘﺴـﺘﻁﻴﻊ‬
‫ﺃﻥ ﺘﺤﺘﻭﻱ ﺍﻟﻘﻴﻤﺔ ‪ ،Null‬ﻤﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﺍﻟﺨﺎﻨﺔ ﺍﻟﺘﻲ ﻴﺘﻌﺎﻤل ﻤﻌﻬﺎ ﺍﻟﻜﺎﺌﻥ ﻓﻲ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﻓﺎﺭﻏﺔ‪ ..‬ﻜﻤﺎ ﻴﻌﻨﻲ ﺃﻥ ﺠﻤﻴﻊ ﻫﺫﻩ ﺍﻷﻨﻭﺍﻉ ﺘﻤﺘﻠﻙ ﺍﻟﺨﺎﺼﻴﺔ ‪ ،IsNull‬ﺍﻟﺘﻲ ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜـﺎﻥ‬
‫ﺍﻟﻜﺎﺌﻥ ﻓﺎﺭﻏﺎ )ﻴﺤﺘﻭﻱ ﻋﻠﻰ ‪ ،(Null‬ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻴﺠﺏ ﺃﻻ ﺘﺤﺎﻭل ﻗﺭﺍﺀﺓ ﻗﻴﻤﺔ ﻫﺫﺍ ﺍﻟﻜﺎﺌﻥ‪،‬‬
‫ﻭﺇﻻ ﺤﺩﺙ ﺨﻁﺄ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ‪.‬‬

‫‪٤٣٧‬‬
‫ﺴﺠل ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻨﻁﻘﻴﺔ ‪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‬‬

‫ﻫل ﻫﻭ ﺨﻁﺄ ‪:IsFalse‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻨﺕ ﻗﻴﻤﺔ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ‪.false‬‬
‫‪٤٣٨‬‬
‫ﻫل ﻫﻭ ﺼﻭﺍﺏ ‪:IsTrue‬‬
‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻨﺕ ﻗﻴﻤﺔ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ‪.true‬‬

‫ﺍﻟﻘﻴﻤﺔ ‪:Value‬‬
‫ﺘﻌﻴﺩ ﻗﻴﻤﺔ ﻤﻨﻁﻘﻴﺔ ‪ Boolean‬ﺘﻌﺒﺭ ﻋﻥ ﻗﻴﻤﺔ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ‪ ..‬ﻭﺘﺴﺒﺏ ﻫـﺫﻩ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫ﺨﻁﺄ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠل ﻤﻨﻌﺩﻤﺎ‪ ،‬ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﻓﺤﺼﻪ ﺃﻭﻻ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﺨﺎﺼـﻴﺔ ‪ IsNull‬ﻗﺒـل‬
‫ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪.‬‬

‫ﺍﻟﻘﻴﻤﺔ ﺍﻟﺭﻗﻤﻴﺔ ‪:ByteValue‬‬


‫ﺘﻌﻴﺩ ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ ‪ Byte‬ﺘﻌﺒﺭ ﻋﻥ ﻗﻴﻤﺔ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ )ﺼﻔﺭ ﻟﻠﺨﻁﺄ ﻭ ‪ ١‬ﻟﻠﺼـﻭﺍﺏ(‪..‬‬
‫ﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺨﻁﺄ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠل ﻤﻨﻌﺩﻤﺎ‪ ،‬ﻟﻬﺫﺍ ﻋﻠﻴﻙ ﻓﺤﺼـﻪ ﺃﻭﻻ ﺒﺎﺴـﺘﺨﺩﺍﻡ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ‪ IsNull‬ﻗﺒل ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ..‬ﻤﺜﺎل‪:‬‬
‫)‪if (!Sb.IsNull‬‬
‫{‬
‫‪MessageBox.Show(Sb.ByteValue.ToString ( )); // 1‬‬
‫‪MessageBox.Show(Sb.Value.ToString ( )); // true‬‬
‫}‬
‫ﻭﻴﻤﺘﻠﻙ ﻫﺫﺍ ﺍﻟﺴﺠل ﺍﻟﻤﻌﺎﻤﻼﺕ ‪ Operators‬ﺍﻟﻼﺯﻤﺔ ﻹﺠﺭﺍﺀ ﺍﻟﻌﻤﻠﻴﺎﺕ ﺍﻟﺤﺴـﺎﺒﻴﺔ ﻭﺍﻟﻤﻨﻁﻘﻴـﺔ‬
‫ﺍﻟﻼﺯﻤﺔ‪ ..‬ﻜﻤﺎ ﺃﻨﻪ ﻴﻤﺘﻠﻙ ﻭﺴﺎﺌل ﻤﺸـﺘﺭﻜﺔ ‪ Static Methods‬ﻷﺩﺍﺀ ﻨﻔـﺱ ﻭﻅـﺎﺌﻑ ﻫـﺫﻩ‬
‫ﺍﻟﻤﻌﺎﻤﻼﺕ‪ ..‬ﻭﺍﻟﺠﺩﻭل ﺍﻟﺘﺎﻟﻲ ﻴﻠﺨﺹ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﺘﺎﺤﺔ ﻭﺍﻟﺩﻭﺍل ﺍﻟﻤﻨﺎﻅﺭﺓ ﻟﻬﺎ‪:‬‬

‫ﺍﻟﻭﺴﻴﻠﺔ‬ ‫ﺍﻟﻤﻌﺎﻤل‬
‫‪And‬‬ ‫&‬
‫‪Or‬‬ ‫|‬
‫‪Xor‬‬ ‫^‬
‫‪) OnesComplement‬ﺍﻟﻤﻌﻜﻭﺱ ﺍﻟﺜﻨﺎﺌﻲ(‬ ‫!‬
‫‪Equals‬‬ ‫==‬
‫‪NotEquals‬‬ ‫=!‬
‫‪GreaterThan‬‬ ‫>‬
‫‪٤٣٩‬‬
‫‪GreaterThanOrEquals‬‬ ‫=>‬
‫‪LessThan‬‬ ‫<‬
‫‪LessThanOrEquals‬‬ ‫=<‬

‫ﻜﻤﺎ ﻴﻤﺘﻠﻙ ﻫﺫﺍ ﺍﻟﺴﺠل ﻋﺩﺓ ﻭﺴﺎﺌل ﻟﻠﺘﺤﻭﻴل‪ ،‬ﻤﺜل‪:‬‬


‫‪ToSqlByte‬‬ ‫‪ToString‬‬
‫‪ToSqlDouble‬‬ ‫‪ToSqlDecimal‬‬
‫‪ToSqlInt32‬‬ ‫‪ToSqlInt16‬‬
‫‪ToSqlMoney‬‬ ‫‪ToSqlInt64‬‬
‫‪ToSqlString‬‬ ‫‪ToSqlSingle‬‬
‫‪Parse‬‬

‫ﺍﻨﻅﺭ ﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ‪:‬‬


‫;)"‪var Sb = SqlBoolean.Parse("false‬‬
‫;) (‪SqlByte B = Sb.ToSqlByte‬‬
‫‪MessageBox.Show(B.ToString( )); // 0‬‬
‫ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻨﻙ ﻻ ﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻭﺴﺎﺌل‪ ،‬ﻷﻥ ﻫﺫﺍ ﺍﻟﺴﺠل ﻴﻌﺭﻑ ﺃﻴﻀﺎ ﻤﻌﺎﻤﻼﺕ‬
‫ﺍﻟﺘﺤﻭﻴـل ﺍﻟﻀـﻤﻨﻲ ‪ Implicit Operators‬ﻭﻤﻌـﺎﻤﻼﺕ ﺍﻟﺘﺤﻭﻴـل ﺍﻟﺼـﺭﻴﺢ ‪Explicit‬‬
‫‪ Operators‬ﺍﻟﻼﺯﻤﺔ ﻟﺘﺤﻭﻴل ﺍﻟﻘﻴﻡ ﺍﻷﺨﺭﻯ ﺇﻟﻰ ﻫﺫﺍ ﺍﻟﺴﺠل‪ ،‬ﺃﻭ ﺘﺤﻭﻴل ﻫﺫﺍ ﺍﻟﺴﺠل ﺇﻟﻰ ﻗـﻴﻡ‬
‫ﺃﺨﺭﻯ ﻤﺒﺎﺸﺭﺓ‪ ..‬ﻤﺜﺎل‪:‬‬
‫;‪SqlBoolean Sb1 = true‬‬
‫;‪SqlByte B1 =(SqlByte) Sb1‬‬
‫‪MessageBox.Show(B1.ToString( )); // 1‬‬

‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ﻜل ﺍﻷﻨﻭﺍﻉ ﺍﻟﺘﻲ ﺴﻨﺸﺭﺤﻬﺎ ﻓﻴﻤﺎ ﺒﻌﺩ ﻤﺯﻭﺩﺓ ﺒﺎﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺤﺴﺎﺒﻴﺔ )ﺍﻟﻁﺭﺡ ﻭﺍﻟﺠﻤﻊ ﻭﺍﻟﻀـﺭﺏ‬
‫ﻭﺍﻟﻘﺴﻤﺔ ﻭﺒﺎﻗﻲ ﺍﻟﻘﺴﻤﺔ( ﻭﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﻤﻨﻁﻘﻴﺔ )& ﻭ | ﻭ ^ ﻭ !( ﻭﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﺤﻭﻴل ﺍﻟﻀﻤﻨﻲ‬
‫ﻭﺍﻟﺼﺭﻴﺢ‪ ..‬ﻭﻻ ﻴﺤﺘﻭﻱ ﻜل ﻨﻭﻉ ﺇﻻ ﻋﻠﻰ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﻲ ﺘﻨﺎﺴـﺏ ﺍﻟﻘـﻴﻡ ﺍﻟﻤﻭﺠـﻭﺩﺓ ﻓﻴـﻪ‬
‫)ﺍﻟﻨﺼﻭﺹ ﻤﺜﻼ ﻻ ﺘﻤﻠﻙ ﻤﻌﺎﻤﻼﺕ ﻤﻨﻁﻘﻴﺔ(‪ ،‬ﻟﻬﺫﺍ ﻟﻥ ﻨﻜﺭﺭ ﺫﻜﺭ ﻫﺫﺍ ﻓﻲ ﺒﺎﻗﻲ ﺍﻷﻨﻭﺍﻉ‪ ،‬ﺇﻻ ﺇﺫﺍ‬
‫ﻜﺎﻥ ﻫﻨﺎﻙ ﻤﻌﺎﻤل ﻴﻘﻭﻡ ﺒﻭﻅﻴﻔﺔ ﻤﺨﺘﻠﻔﺔ ﻋﻥ ﺍﻟﻤﺄﻟﻭﻑ‪.‬‬

‫‪٤٤٠‬‬
‫ﺴﺠل ﺍﻟﻭﺤﺩﺓ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪SqlByte Structure‬‬

‫ﻫﺫﺍ ﺍﻟﺴﺠل ﻴﺤﻔﻅ ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ ﺒﺩﻭﻥ ﺇﺸﺎﺭﺓ‪ ،‬ﺃﻱ ﺃﻨﻪ ﻴﺘﻌﺎﻤل ﻤﻊ ﺍﻷﻋﺩﺍﺩ ﻤﻥ ‪ ٠‬ﺇﻟﻰ ‪.٢٥٥‬‬
‫ﻭﻴﺴﺘﻘﺒل ﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﺍ ﺍﻟﺴﺠل ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ ‪ Byte‬ﻟﻨﺴﺦ ﻗﻴﻤﺘﻬﺎ ﺇﻟﻴﻪ‪ ..‬ﻤﺜﺎل‪:‬‬
‫;)‪SqlByte B = new SqlByte(5‬‬

‫ﻭﻴﻤﺘﻠﻙ ﻫﺫﺍ ﺍﻟﺴﺠل ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺃﻗل ﻗﻴﻤﺔ ‪:MinValue‬‬


‫ﺘﻌﻴﺩ ﺃﻗل ﻗﻴﻤﺔ ﻴﻤﻜﻥ ﻭﻀﻌﻬﺎ ﻓﻲ ﺍﻟﺴﺠل‪.‬‬

‫ﺃﻗﺼﻰ ﻗﻴﻤﺔ ‪:MaxValue‬‬


‫ﺘﻌﻴﺩ ﺃﻜﺒﺭ ﻗﻴﻤﺔ ﻴﻤﻜﻥ ﻭﻀﻌﻬﺎ ﻓﻲ ﺍﻟﺴﺠل‪.‬‬

‫ﺼﻔﺭ ‪:Zero‬‬
‫ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﺴﺠل ‪ SqlByte‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻘﻴﻤﺔ ﺼﻔﺭ‪.‬‬

‫ﺍﻟﻌﺩﻡ ‪:Null‬‬
‫ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﺴﺠل ‪ SqlByte‬ﻻ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﻱ ﻗﻴﻤﺔ‪.‬‬

‫ﺍﻟﻘﻴﻤﺔ ‪:Value‬‬
‫ﺘﻌﻴﺩ ﻭﺤﺩﺓ ﺜﻨﺎﺌﻴﺔ ‪ Byte‬ﺘﺤﻤل ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﺤﻔﻭﻅﺔ ﻓﻲ ﺍﻟﺴﺠل‪ ..‬ﻭﺘﺴﺒﺏ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﺨﻁﺄ‬
‫ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﺴﺠل ﻤﻨﻌﺩﻤﺎ‪ ،‬ﻟﺫﺍ ﻋﻠﻴﻙ ﺃﻥ ﺘﺴﺘﺨﺩﻡ ﺍﻟﺨﺎﺼﻴﺔ ‪ IsNull‬ﺃﻭﻻ ﻟﻠﺘﺄﻜﺩ‬
‫ﻤﻥ ﻭﺠﻭﺩ ﻗﻴﻤﺔ ﻓﻲ ﺍﻟﺴﺠل‪.‬‬

‫‪٤٤١‬‬
‫ﻻﺤﻅ ﺃﻥ ﻤﺎ ﻴﻨﻁﺒﻕ ﻋﻠﻰ ﺍﻟﺴﺠل ‪ SqlByte‬ﻴﻨﻁﺒﻕ ﻋﻠﻰ ﺍﻟﺴﺠﻼﺕ ﺍﻟﺭﻗﻤﻴﺔ ﺍﻷﺨـﺭﻯ‪ ،‬ﻓﻬـﻲ‬
‫ﺘﻤﺘﻠﻙ ﻨﻔﺱ ﺍﻟﺨﺼﺎﺌﺹ‪ ،‬ﻭﺘﺴﺘﻁﻴﻊ ﺤﻔﻅ ﻗﻴﻤﺔ ﺃﻭ ‪ ،Null‬ﻭﺍﻻﺨﺘﻼﻑ ﺍﻟﻭﺤﻴﺩ ﻫﻭ ﻨـﻭﻉ ﺍﻟﻘﻴﻤـﺔ‬
‫ﺍﻟﻤﺤﻔﻭﻅﺔ‪ ..‬ﻟﺫﺍ ﻓﻼ ﺩﺍﻋﻲ ﻟﺘﻜﺭﺍﺭ ﻨﻔﺱ ﺍﻟﻜﻼﻡ ﻤﻊ ﺍﻷﻨﻭﺍﻉ ﺍﻟﺘﺎﻟﻴﺔ‪ ،‬ﻓﺄﻨﺕ ﺘﺴﺘﻁﻴﻊ ﻓﻬﻤﻬﺎ ﺒﻤﺠﺭﺩ‬
‫ﺍﻟﻨﻅﺭ‪:‬‬

‫ﺴﺠل ﺍﻷﻋﺩﺍﺩ ﺍﻟﻘﺼﻴﺭﺓ ‪SqlInt16 Structure‬‬


‫ﺴﺠل ﺍﻷﻋﺩﺍﺩ ﺍﻟﺼﺤﻴﺤﺔ ‪SqlInt32 Structure‬‬
‫ﺴﺠل ﺍﻷﻋﺩﺍﺩ ﺍﻟﻁﻭﻴﻠﺔ ‪SqlInt64 Structure‬‬
‫ﺴﺠل ﺍﻷﻋﺩﺍﺩ ﺍﻟﻤﻔﺭﺩﺓ ‪SqlSingle Structure‬‬
‫ﺴﺠل ﺍﻟﻨﻘﻭﺩ ‪SqlMoney Structure‬‬
‫ﺴﺠل ﺍﻷﻋﺩﺍﺩ ﺍﻟﻤﺯﺩﻭﺠﺔ ‪SqlDouble Structure‬‬
‫ﺴﺠل ﺍﻟﺘﺎﺭﻴﺦ ﻭﺍﻟﻭﻗﺕ ‪SqlDateTime Structure‬‬
‫ﺴﺠل ﺍﻟﻤﻌﺭﻑ ﺍﻟﻤﺘﻔﺭﺩ ﺍﻟﻌﺎﻡ ‪SqlGuid Structure‬‬

‫‪٤٤٢‬‬
‫ﺴﺠل ﺍﻷﻋﺩﺍﺩ ﺍﻟﻌﺸﺭﻴﺔ ‪SqlDecimal Structure‬‬

‫ﻴﺤﻔﻅ ﻫﺫﺍ ﺍﻟﺴﺠل ﺍﻷﻋﺩﺍﺩ ﺍﻟﻌﺸﺭﻴﺔ‪ ،‬ﺒﻨﻔﺱ ﻁﺭﻴﻘﺔ ﺍﻟﺴﺠل ‪ ،Decimal‬ﻜﻤﺎ ﺃﻥ ﺤـﺩﺙ ﺇﻨﺸـﺎﺀ‬
‫ﻫﺫﺍ ﺍﻟﺴﺠل ﻴﻤﺘﻠﻙ ﺼﻴﻐﺎ ﺸﺒﻴﻬﺔ ﺒﺤﺩﺙ ﺇﻨﺸﺎﺀ ﺍﻟﺴﺠل ‪ ،Decimal‬ﺍﻟﺘﻲ ﻴﻤﻜﻨﻙ ﻤﺭﺍﺠﻌﺘﻬﺎ ﻓـﻲ‬
‫ﻜﺘﺎﺏ ﺒﺭﻤﺠﺔ ﺇﻁﺎﺭ ﺍﻟﻌﻤل‪.‬‬
‫ﻭﻴﺯﻴﺩ ﻫﺫﺍ ﺍﻟﺴﺠل ﻋﻠﻰ ﺴﺠﻼﺕ ﺍﻷﻨﻭﺍﻉ ﺍﻟﻌﺩﺩﻴﺔ ﺍﻷﺨﺭﻯ ﺒﺎﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺃﻗﺼﻰ ﺩﻗﺔ ‪:MaxPrecision‬‬


‫ﻴﻌﻴﺩ ﺃﻗﺼﻰ ﻋﺩﺩ ﻤﻥ ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﺼﺤﻴﺤﺔ ﻭﺍﻟﻌﺸﺭﻴﺔ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻓﻲ ﺍﻟﻌﺩﺩ‪.‬‬

‫ﺃﻗﺼﻰ ﻤﻘﻴﺎﺱ ‪:MaxScale‬‬


‫ﻴﻌﻴﺩ ﺃﻗﺼﻰ ﻋﺩﺩ ﻤﻥ ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﻌﺸﺭﻴﺔ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻓﻲ ﺍﻟﻌﺩﺩ‪.‬‬

‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪:BinData‬‬


‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺜﻨﺎﺌﻴﺔ ‪ Byte Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺘﻤﺜﻴل ﺍﻟﺜﻨﺎﺌﻲ ﻟﻠﻌﺩﺩ ﺍﻟﻌﺸﺭﻱ‪.‬‬

‫ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:Data‬‬
‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺃﻋﺩﺍﺩ ﺼﺤﻴﺤﺔ ‪ Integer Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﺘﻤﺜﻴـل ﺍﻟﺜﻨـﺎﺌﻲ ﻟﻠﻌـﺩﺩ‬
‫ﺍﻟﻌﺸﺭﻱ‪.‬‬

‫ﻫل ﻫﻭ ﻤﻭﺠﺏ ‪:IsPositive‬‬


‫ﺘﻌﻴﺩ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟﻌﺩﺩ ﺍﻟﻌﺸﺭﻱ ﻤﻭﺠﺒﺎ‪.‬‬

‫ﺍﻟﺩﻗﺔ ‪:Precision‬‬
‫ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﺼﺤﻴﺤﺔ ﻭﺍﻟﻌﺸﺭﻴﺔ ﻓﻲ ﺍﻟﻌﺩﺩ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﺍﻟﻤﻘﻴﺎﺱ ‪:Scale‬‬
‫ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﻌﺸﺭﻴﺔ ﻓﻲ ﺍﻟﻌﺩﺩ ﺍﻟﺤﺎﻟﻲ‪.‬‬
‫‪٤٤٣‬‬
‫ﻓﺌﺔ ﺍﻟﺤﺭﻭﻑ ‪SqlChars Class‬‬

‫ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺘﺘﻌﺎﻤل ﻤﻊ ﻤﺼﻔﻭﻓﺔ ﺤﺭﻭﻑ‪ ،‬ﺒﺤﻴﺙ ﻴﻤﻜﻥ ﺍﺴﺘﺨﺩﺍﻤﻬﺎ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺃﻨـﻭﺍﻉ ﺴـﻴﻜﻴﻭل‬
‫ﺍﻟﺘﺎﻟﻴﺔ‪ ،varchar, nvarchar, char, nchar, text, ntext :‬ﻤﻊ ﻤﻼﺤﻅﺔ ﺃﻥ ﺃﻗﺼﻰ ﻋـﺩﺩ‬
‫ﻤﻥ ﺍﻟﺤﺭﻭﻑ ﻴﻤﻜﻥ ﻭﻀﻌﻪ ﻓﻲ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻫﻭ ﺃﻗﺼﻰ ﻗﻴﻤﺔ ﻟﻠﻌﺩﺩ ﺍﻟﺼﺤﻴﺢ )ﺃﻱ ﺤﻭﺍﻟﻲ ‪ ٢‬ﻤﻠﻴﺎﺭ‬
‫ﺤﺭﻑ(‪.‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺜﻼﺙ ﺼﻴﻎ‪:‬‬
‫‪ .١‬ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪ ،‬ﻭﻫﻲ ﺘﻨﺸﺊ ﻨﺴﺨﺔ ﻗﻴﻤﺘﻬﺎ ‪.Null‬‬
‫‪ .٢‬ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻤﺼﻔﻭﻓﺔ ﺤﺭﻭﻑ ‪.Char Array‬‬
‫‪ .٣‬ﻭﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﺴﺠل ‪ SqlString‬ﻷﺨﺫ ﺍﻟﺤﺭﻭﻑ ﻤﻥ ﺍﻟـﻨﺹ ﺍﻟﻤﻭﺠـﻭﺩ‬
‫ﻓﻴﻬﺎ‪.‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻟﻌﺩﻡ ‪:Null‬‬
‫ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻓﺎﺭﻏﺔ ﻤﻥ ﺍﻟﻔﺌﺔ ‪.SqlChars‬‬

‫ﺍﻟﻤﻔﻬﺭﺱ ‪:Indexer‬‬
‫ﻴﻘﺭﺃ ﺃﻭ ﻴﻐﻴﺭ ﺍﻟﺤﺭﻑ ﺍﻟﻤﻭﺠﻭﺩ ﻓﻲ ﺍﻟﻤﻭﻀﻊ ﺍﻟﻤﺭﺴل ﻜﻤﻌﺎﻤل‪.‬‬

‫ﺍﻟﻁﻭل ‪:Length‬‬
‫ﺘﻌﻴﺩ ﻋﺩﺩ ﺍﻟﺤﺭﻭﻑ ﺍﻟﻤﻭﺠﻭﺩﺓ ﺤﺎﻟﻴﺎ ﻓﻲ ﺍﻟﻜﺎﺌﻥ‪.‬‬

‫ﺃﻗﺼﻰ ﻁﻭل ‪:MaxLength‬‬


‫ﺘﻌﻴﺩ ﺃﻗﺼﻰ ﻋﺩﺩ ﻤﻥ ﺍﻟﺤﺭﻭﻑ ﻴﻤﻜﻥ ﻭﻀﻌﻪ ﻓﻲ ﺍﻟﻜﺎﺌﻥ‪ ..‬ﻫﺫﺍ ﺍﻟﻌـﺩﺩ ﻴﺴـﺎﻭﻱ ﺼـﻔﺭﺍ‬
‫ﻤﺒﺩﺌﻴﺎ‪ ،‬ﻟﻜﻨﻪ ﻴﺴﺎﻭﻱ ﻁﻭل ﺍﻟﻨﺹ ﻋﻨﺩ ﻭﻀﻊ ﻨﺹ ﻓﻲ ﺍﻟﻜﺎﺌﻥ‪ ..‬ﻭﻋﻨـﺩ ﺘﻘﺼـﻴﺭ ﻁـﻭل‬
‫ﺍﻟﻜﺎﺌﻥ‪ ،‬ﻴﻅل ﺃﻗﺼﻰ ﻁﻭل ﻜﻤﺎ ﻫﻭ ﺩﻭﻥ ﺃﻥ ﻴﻨﻘﺹ‪.‬‬

‫‪٤٤٤‬‬
‫ﺍﻟﺘﺨﺯﻴﻥ ‪:Storage‬‬
‫ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ StorageState‬ﺍﻟﺘﻲ ﺘﻭﻀﺢ ﻨﻭﻉ ﺍﻟﻤﺨﺯﻥ ﺍﻟﺫﻱ ﻴﺘﻡ ﻓﻴـﻪ ﺤﻔـﻅ‬
‫ﺍﻟﺤﺭﻭﻑ ﺩﺍﺨل ﺍﻟﻜﺎﺌﻥ‪ ،‬ﻭﻫﺫﻩ ﺍﻟﻘﻴﻡ ﻫﻲ‪:‬‬

‫ﺘﺤﻔﻅ ﺍﻟﺤﺭﻭﻑ ﻓﻲ ﻤﺼﻔﻭﻓﺔ ﺩﺍﺨﻠﻴﺔ‪.‬‬ ‫‪Buffer‬‬


‫‪ UnmanagedBuffer‬ﺘﺤﻔﻅ ﺍﻟﺤﺭﻭﻑ ﻓﻲ ﺍﻟﺫﺍﻜﺭﺓ ﺒﺎﺴﺘﺨﺩﺍﻡ ﻤﺅﺸﺭﺍﺕ ﻏﻴﺭ ﻤـﺩﺍﺭﺓ‬
‫ﺒﺈﻁﺎﺭ ﺍﻟﻌﻤل‪.‬‬
‫ﺘﺤﻔﻅ ﺍﻟﺤﺭﻭﻑ ﻓﻲ ﻤﺠﺭﻯ ﺒﻴﺎﻨﺎﺕ ‪.Stream‬‬ ‫‪Stream‬‬

‫ﺍﻟﻤﺨﺯﻥ ﺍﻟﻭﺴﻴﻁ ‪:Buffer‬‬


‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺍﻟﺤﺭﻭﻑ ﺍﻟﺘﻲ ﻴﺘﻌﺎﻤل ﻤﻌﻬﺎ ﺍﻟﻜﺎﺌﻥ ﺩﺍﺨﻠﻴﺎ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺃﻱ ﺘﻐﻴﻴﺭ ﻓﻲ ﻫـﺫﻩ‬
‫ﺍﻟﻤﺼﻔﻭﻓﺔ ﻴﺅﺜﺭ ﻋﻠﻰ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻜﺎﺌﻥ‪.‬‬

‫ﺍﻟﻘﻴﻤﺔ ‪:Value‬‬
‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺤﺭﻭﻑ ﺒﻬﺎ ﻨﺴﺨﺔ ﻤﻥ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻜﺎﺌﻥ‪ ..‬ﻻﺤﻅ ﺃﻥ ﺃﻱ ﺘﻐﻴﻴﺭ ﻓـﻲ ﻫـﺫﻩ‬
‫ﺍﻟﻤﺼﻔﻭﻓﺔ ﻻ ﻴﺅﺜﺭ ﻋﻠﻰ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻜﺎﺌﻥ‪ ،‬ﻋﻠﻰ ﻋﻜﺱ ﺍﻟﻤﺼﻔﻭﻓﺔ ﺍﻟﺘﻲ ﺘﻌﻴﺩﻫﺎ ﺍﻟﺨﺎﺼـﻴﺔ‬
‫‪.Buffer‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻓﺌﺔ ﺍﻟﺤﺭﻭﻑ ﺍﻟﻭﺴﺎﺌل ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﻐﻴﻴﺭ ﺍﻟﻁﻭل ‪:SetLength‬‬


‫ﺃﺭﺴل ﺇﻟﻰ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ﻋﺩﺩ ﺍﻟﺤﺭﻭﻑ ﺍﻟﺫﻱ ﺘﺭﻴﺩ ﻭﺠﻭﺩﻫﺎ ﻓﻲ ﺍﻟﻜﺎﺌﻥ‪ ..‬ﻻﺤﻅ ﺃﻨﻙ ﻟـﻭ‬
‫ﺃﺭﺴﻠﺕ ﻋﺩﺩﺍ ﺃﻜﺒﺭ ﻤﻥ ﺃﻗﺼﻰ ﻁﻭل ‪ MaxLength‬ﻓﺴﻴﺤﺩﺙ ﺨﻁﺄ‪ ..‬ﻫﺫﺍ ﻤﻌﻨـﺎﻩ ﺃﻨـﻙ‬
‫ﺘﺴﺘﻁﻴﻊ ﺘﺼﻐﻴﺭ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻜﺎﺌﻥ‪ ،‬ﺤﻴﺙ ﺴﻴﺘﻡ ﺤﺫﻑ ﺍﻟﺤـﺭﻭﻑ ﺍﻟﺯﺍﺌـﺩﺓ ﻋـﻥ ﺍﻟﻁـﻭل‬
‫ﺍﻟﺠﺩﻴﺩ‪ ،‬ﻟﻜﻥ ﺴﺘﻅل ﺍﻟﻤﺼﻔﻭﻓﺔ ﺍﻟﺩﺍﺨﻠﻴﺔ ﺘﺤﺠﺯ ﺍﻟﺨﺎﻨﺎﺕ ﺍﻟﺘﻲ ﺘﻡ ﺍﻻﺴﺘﻐﻨﺎﺀ ﻋﻨﻬـﺎ‪ ،‬ﻟﻬـﺫﺍ‬
‫ﺘﺴﺘﻁﻴﻊ ﺃﻥ ﺘﻜﺒﺭ ﺍﻟﻁﻭل ﻤﺭﺓ ﺃﺨﺭﻯ‪ ،‬ﺒﺸﺭﻁ ﻋﺩﻡ ﺘﺠﺎﻭﺯ ﺍﻟﻁﻭل ﺍﻷﻗﺼﻰ‪.‬‬

‫‪٤٤٥‬‬
‫ﻭﻟﻭ ﻜﺎﻥ ﺍﻟﻜﺎﺌﻥ ﻴﺘﻌﺎﻤل ﻤﻊ ﻤﺨﺯﻥ ﻭﺴﻴﻁ ﻏﻴﺭ ﻤـﺩﺍﺭ ‪ Unmanaged Buffer‬ﻓﺴـﻴﺘﻡ‬
‫ﺘﺤﻭﻴﻠﻪ ﺇﻟﻰ ﻤﺨﺯﻥ ﻤﺩﺍﺭ ‪ Managed Buffer‬ﺒﻌﺩ ﺘﻨﻔﻴﺫ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ‪.‬‬

‫ﻭﻀﻊ ﺍﻟﻌﺩﻡ ‪:SetNull‬‬


‫ﺘﻤﺤﻭ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ ﻭﺘﺠﻌل ﻁﻭﻟﻪ ﺼﻔﺭﺍ‪.‬‬
‫ﻗﺭﺍﺀﺓ ‪:Read‬‬
‫ﺘﻨﺴﺦ ﻋﺩﺩﺍ ﻤﻥ ﺍﻟﺤﺭﻭﻑ ﻤﻥ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ ﺇﻟﻰ ﻤﺼﻔﻭﻓﺔ‪ ،‬ﻭﻟﻬﺎ ﺍﻟﻤﻌﺎﻤﻼﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -‬ﻤﻭﻀﻊ ﺒﺩﺍﻴﺔ ﺍﻟﻘﺭﺍﺀﺓ ﻤﻥ ﺍﻟﻜﺎﺌﻥ‪.‬‬
‫‪ -‬ﻤﺼﻔﻭﻓﺔ ﺍﻟﺤﺭﻭﻑ ﺍﻟﺘﻲ ﺴﻴﺘﻡ ﺍﻟﻨﺴﺦ ﺇﻟﻴﻬﺎ‪.‬‬
‫‪ -‬ﻤﻭﻀﻊ ﺒﺩﺍﻴﺔ ﺍﻟﻜﺘﺎﺒﺔ ﻓﻲ ﺍﻟﻤﺼﻔﻭﻓﺔ‪.‬‬
‫‪ -‬ﻋﺩﺩ ﺍﻟﺤﺭﻭﻑ ﺍﻟﻤﻨﺴﻭﺨﺔ‪.‬‬
‫ﻭﺘﻌﻴﺩ ﻫﺫﻩ ﺍﻟﻭﺴﻴﻠﺔ ﻋﺩﺩ ﺍﻟﺤﺭﻭﻑ ﺍﻟﺘﻲ ﺘﻡ ﻨﺴﺨﻬﺎ‪ ..‬ﺍﻟﺤﻜﻤﺔ ﻓﻲ ﻫﺫﺍ ﺃﻥ ﻋـﺩﺩ ﺍﻟﺤـﺭﻭﻑ‬
‫ﺍﻟﻤﻨﺴﻭﺨﺔ ﻗﺩ ﻴﻜﻭﻥ ﺃﻗل ﻤﻥ ﺍﻟﻤﻁﻠﻭﺏ‪ ،‬ﺇﺫﺍ ﻟﻡ ﻴﻜﻥ ﺍﻟﻜﺎﺌﻥ ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻌﺩﺩ ﺍﻟﻤﻁﻠـﻭﺏ‬
‫ﻤﻥ ﺍﻟﺤﺭﻭﻑ‪.‬‬
‫ﻭﺍﻟﻤﺜﺎل ﺍﻟﺘﺎﻟﻲ ﻴﻨﺴﺦ ‪ ٥‬ﺤﺭﻭﻑ ﻤﻥ ﺍﻟﻜﺎﺌﻥ ﺒﺩﺀﺍ ﻤﻥ ﺍﻟﺤﺭﻑ ﺍﻟﺭﺍﺒﻊ‪:‬‬
‫;)"‪SqlChars Sc = new SqlChars("This is a test‬‬
‫;]‪char[] C = new char[5‬‬
‫;)‪Sc.Read(3, C, 0, 5‬‬
‫;)"'" ‪MessageBox.Show("'" + new String (C) +‬‬

‫ﻜﺘﺎﺒﺔ ‪: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));

:ToSqlString ‫ﺍﻟﺘﺤﻭﻴل ﺇﻟﻰ ﻨﺹ ﺴﻴﻜﻭﻴل‬


‫ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﻨﺹ ﻤﻜـﻭﻥ ﻤـﻥ ﺤـﺭﻭﻑ ﺍﻟﻜـﺎﺌﻥ‬SqlString ‫ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﺴﺠل‬
.‫ﺍﻟﺤﺎﻟﻲ‬

٤٤٧
‫ﺴﺠل ﺍﻟﻨﺹ ‪SqlString Structure‬‬

‫ﻴﺨﺘﻠﻑ ﻨﺹ ﺴﻴﻜﻭﻴل ﻓﻲ ﻁﺭﻴﻘﺔ ﺘﻤﺜﻴﻠﻪ ﺍﻟﺩﺍﺨﻠﻴﺔ‪ ،‬ﻋﻥ ﻓﺌﺔ ﺍﻟـﻨﺹ ‪ String Class‬ﺍﻟﻌﺎﺩﻴـﺔ‪..‬‬
‫ﻓﻌﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜﺎل‪ :‬ﻴﺄﺨﺫ ﺍﻟﻨﺹ ﺍﻟﻌﺎﺩﻱ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺜﻘﺎﻓﺔ ﻤﻥ ﺍﻟﻠﻐﺔ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﺍﻟﻤﻌﺭﻓﺔ ﻋﻠـﻰ‬
‫ﺠﻬﺎﺯ ﺍﻟﻤﺴﺘﺨﺩﻡ‪ ،‬ﺒﻴﻨﻤﺎ ﻻ ﻴﻔﻌل ﻨﺹ ﺴﻴﻜﻭﻴل ﻫﺫﺍ‪ ،‬ﻓﻠﻭ ﻟﻡ ﺘﻤﺩﻩ ﺒﻤﻌﺭﻑ ﺍﻟﺜﻘﺎﻓﺔ‪ ،‬ﻓﺈﻨﻪ ﻴﺴـﺘﺨﺩﻡ‬
‫ﻤﻘﺎﻴﻴﺱ ﺩﺍﺨﻠﻴﺔ ﺨﺎﺼﺔ ﺒﻪ ﻟﻤﻘﺎﺭﻨﺔ ﺍﻟﻨﺼﻭﺹ‪ ..‬ﻭﻟﻭ ﺤﺎﻭﻟﺕ ﻤﻘﺎﺭﻨﺔ ﻨﺴﺨﺘﻴﻥ ﻤﻥ ﻨﺹ ﺴﻴﻜﻭﻴل‬
‫ﻟﻜل ﻤﻨﻬﻤﺎ ﻤﻌﺭﻑ ﺜﻘﺎﻓﺔ ‪ LCID‬ﻤﺨﺘﻠﻑ ﻋﻥ ﺍﻵﺨﺭ‪ ،‬ﻓﺈﻥ ﺨﻁﺄ ﺴﻴﺤﺩﺙ ﻓﻲ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺒﺴـﺒﺏ‬
‫ﻋﺩﻡ ﻗﺩﺭﺘﻪ ﻋﻠﻰ ﺇﺠﺭﺍﺀ ﻋﻤﻠﻴﺔ ﺍﻟﻤﻘﺎﺭﻨﺔ‪.‬‬
‫)ﺃﻨﺼﺢ ﺒﻤﺭﺍﺠﻌﺔ ﻓﺼل ﺍﻟﻌﻭﻟﻤﺔ ‪ Globalization‬ﻓﻲ ﻤﺭﺠﻊ ﺒﺭﻤﺠﺔ ﺇﻁﺎﺭ ﺍﻟﻌﻤل(‪.‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﺍ ﺍﻟﺴﺠل ﺍﻟﺼﻴﻎ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ .١‬ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪ ،‬ﻭﻫﻲ ﺘﻨﺸﺊ ﻨﺴﺨﺔ ﻗﻴﻤﺘﻬﺎ ‪.Null‬‬
‫‪ .٢‬ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﻨﺼﺎ ‪ String‬ﻟﻨﺴﺨﻪ ﺇﻟﻰ ﺍﻟﺴﺠل‪.‬‬
‫‪ .٣‬ﻭﺍﻟﺜﺎﻟﺜﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻥ ﻴﺴﺘﻘﺒل ﻤﻌﺭﻑ ﺍﻟﺜﻘﺎﻓﺔ ‪ LCID‬ﺍﻟـﺫﻱ‬
‫ﺘﺭﻴﺩ ﺍﺴﺘﺨﺩﺍﻤﻪ ﻋﻨﺩ ﻤﻘﺎﺭﻨﺔ ﺍﻟﻨﺹ ﺒﺄﻱ ﻨﺹ ﺁﺨﺭ‪ ..‬ﻤﺜﺎل‪:‬‬
‫‪", System.Globalization.‬ﻤﺤﻤﺩ"(‪SqlString Ss = new SqlString‬‬
‫;)‪CultureInfo.CurrentCulture.LCID‬‬
‫‪ .٤‬ﻭﺍﻟﺭﺍﺒﻌﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺜﺎﻟـﺙ‪ ،‬ﻴﺴـﺘﻘﺒل ﺇﺤـﺩﻯ ﻗـﻴﻡ ﺍﻟﻤـﺭﻗﻡ‬
‫‪ SqlCompareOptions‬ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﺘﻡ ﺍﻟﻤﻘﺎﺭﻨﺔ ﺒﺎﻟﺨﻴﺎﺭﺍﺕ ﺍﻻﻓﺘﺭﺍﻀﻴﺔ ﻟﻠﺜﻘﺎﻓﺔ ﺍﻟﺘﻲ ﻴـﺭﺘﺒﻁ‬ ‫‪None‬‬


‫ﺒﻬﺎ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ‪.‬‬
‫ﺘﺘﺠﺎﻫل ﺍﻟﻤﻘﺎﺭﻨﺔ ﺤﺎﻟﺔ ﺍﻷﺤﺭﻑ‪.‬‬ ‫‪IgnoreCase‬‬

‫ﺘﺘﺠﺎﻫل ﺍﻟﻤﻘﺎﺭﻨﺔ ﻜل ﺍﻟﺭﻤﻭﺯ ﺍﻟﺘﻲ ﻻ ﺘﻌﺘﺒﺭ ﻓﻭﺍﺼل ﺒﻴﻥ‬ ‫‪IgnoreNonSpace‬‬


‫ﺍﻟﺤﺭﻭﻑ‪ ،‬ﻤﺜل ﻋﻼﻤﺎﺕ ﺍﻟﺘﺸﻜﻴل ﻓﻲ ﺍﻟﻠﻐﺔ ﺍﻟﻌﺭﺒﻴﺔ‪ ..‬ﻫﺫﺍ‬
‫ﻤﻔﻴﺩ ﻋﻨﺩ ﺍﻟﺒﺤﺙ ﻋﻥ ﻜﻠﻤﺔ ﻤﻊ ﺘﺠﺎﻫل ﺍﻟﺘﺸﻜﻴل‪.‬‬

‫‪٤٤٨‬‬
‫ﺘﺘﺠﺎﻫل ﺍﻟﻤﻘﺎﺭﻨﺔ ﺍﻟﺭﻤﻭﺯ ﺍﻟﺼﻭﺘﻴﺔ ﻓﻲ ﺍﻟﻠﻐﺔ ﺍﻟﻴﺎﺒﺎﻨﻴﺔ‪.‬‬ ‫‪IgnoreKanaType‬‬

‫ﺘﺘﺠﺎﻫل ﺍﻟﻤﻘﺎﺭﻨﺔ ﺇﻥ ﻜﺎﻨﺕ ﺍﻟﺤﺭﻭﻑ ﺍﻟﻴﺎﺒﺎﻨﻴـﺔ ﻤﻜﺘﻭﺒـﺔ‬ ‫‪IgnoreWidth‬‬


‫ﺒﺎﻟﻌﺭﺽ ﺍﻟﻜﺎﻤل ﺃﻡ ﺒﻨﺼﻑ ﺍﻟﻌﺭﺽ‪.‬‬
‫ﻴﺘﻡ ﺘﺭﺘﻴﺏ ﺍﻟﺤﺭﻭﻑ ﺘﺒﻌﺎ ﻟﻘﻴﻤﻬﺎ ﺍﻟﺭﻗﻤﻴـﺔ ﻓـﻲ ﺘﺭﻤﻴـﺯ‬ ‫‪BinarySort‬‬
‫‪ ASCII‬ﺃﻭ ‪ ،Unicode‬ﻭﻟﻴﺱ ﺘﺒﻌﺎ ﻟﻠﺘﺭﺘﻴﺏ ﺍﻟﻬﺠﺎﺌﻲ‪.‬‬
‫ﻴﺘﻡ ﺘﺭﺘﻴﺏ ﺍﻟﺤﺭﻭﻑ ﺜﻨﺎﺌﻴﺎ‪ ،‬ﺒﺎﺴﺘﺨﺩﺍﻡ ﻗﻴﻤﻬﺎ ﻓﻲ ﺍﻟﺘﺭﻤﻴﺯ‪.‬‬ ‫‪BinarySort2‬‬

‫ﻻﺤﻅ ﺃﻨﻙ ﺘﺴﺘﻁﻴﻊ ﺩﻤﺞ ﺃﻜﺜﺭ ﻤﻥ ﻗﻴﻤﺔ ﻤﻌﺎ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻤﻌﺎﻤل |‪.‬‬
‫ـﺔ‬
‫ـﺎﺭﺍﺕ ﺍﻟﻤﻘﺎﺭﻨــ‬
‫ـﺔ ‪ LCID‬ﻭﺨﻴــ‬
‫ـﺔ ﺍﻟﺜﻘﺎﻓــ‬
‫ـﺘﻘﺒل ﻤﻌﺭﻓــ‬
‫ـﺔ ﺘﺴــ‬
‫‪ .٥‬ﻭﺍﻟﺨﺎﻤﺴــ‬
‫‪ SqlCompareOptions‬ﻭﻤﺼﻔﻭﻓﺔ ﺜﻨﺎﺌﻴﺔ ‪ Byte Array‬ﺘﺤﺘـﻭﻱ ﻋﻠـﻰ ﺍﻟﺘﻤﺜﻴـل‬
‫ﺍﻟﺜﻨﺎﺌﻲ ﻟﻠﻨﺹ‪.‬‬
‫‪ .٦‬ﻭﺍﻟﺴﺎﺩﺴﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺭﺍﺒﻊ‪ ،‬ﻋﻠﻴﻙ ﺠﻌﻠﻪ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟـﻨﺹ‬
‫ﻤﻤﺜﻼ ﺒﺎﻟﺘﺭﻤﻴﺯ ﺍﻟﻤﻭﺴﻊ ‪.Unicode‬‬
‫‪ .٧‬ﻭﺍﻟﺴﺎﺒﻌﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺨﺎﻤﺴﺔ ﺒﻤﻌﺎﻤل ﺭﺍﺒﻊ ﻴﺴﺘﻘﺒل ﻤﻭﻀﻊ ﺒﺩﺍﻴﺔ ﺍﻟﻘﺭﺍﺀﺓ ﻤـﻥ‬
‫ﺍﻟﻤﺼﻔﻭﻓﺔ‪ ،‬ﻭﻤﻌﺎﻤل ﺨﺎﻤﺱ ﻴﺴﺘﻘﺒل ﻋﺩﺩ ﺍﻟﺤﺭﻭﻑ ﺍﻟﺘﻲ ﺘﺭﻴﺩ ﻗﺭﺍﺀﺘﻬﺎ ﻤﻨﻬﺎ‪.‬‬
‫‪ .٨‬ﻭﺍﻟﺜﺎﻤﻨﺔ ﺘﺯﻴﺩ ﻋﻠﻰ ﺍﻟﺼﻴﻐﺔ ﺍﻟﺴﺎﺒﻘﺔ ﺒﻤﻌﺎﻤل ﺭﺍﺒﻊ‪ ،‬ﻋﻠﻴﻙ ﺠﻌﻠﻪ ‪ true‬ﺇﺫﺍ ﻜﺎﻥ ﺍﻟـﻨﺹ‬
‫ﻤﻤﺜﻼ ﺒﺎﻟﺘﺭﻤﻴﺯ ﺍﻟﻤﻭﺴﻊ ‪.Unicode‬‬

‫ﻭﻴﻤﺘﻠﻙ ﺴﺠل ﺍﻟﻨﺹ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺘﺭﺘﻴﺏ ﺜﻨﺎﺌﻲ ‪:BinarySort‬‬


‫ﺘﻌﻤل ﻜﺜﺎﺒﺕ ﻴﻌﻨﻲ ﺃﻥ ﺘﺭﺘﻴﺏ ﺍﻟﺤﺭﻭﻑ ﻴﺘﻡ ﺘﺒﻌﺎ ﻟﻘﻴﻤﻬﺎ ﺍﻟﺭﻗﻤﻴـﺔ ﻓـﻲ ﺘﺭﻤﻴـﺯ ‪ASCII‬‬
‫ﻭﻟﻴﺱ ﺘﺒﻌﺎ ﻟﻠﺘﺭﺘﻴﺏ ﺍﻟﻬﺠﺎﺌﻲ‪.‬‬

‫‪٤٤٩‬‬
‫ﺘﺭﺘﻴﺏ ﺜﻨﺎﺌﻲ ‪:BinarySort2‬‬
‫ﺘﻌﻤل ﻜﺜﺎﺒﺕ ﻴﻌﻨﻲ ﺃﻥ ﺘﺭﺘﻴﺏ ﺍﻟﺤﺭﻭﻑ ﻴﺘﻡ ﺘﺒﻌﺎ ﻟﻘﻴﻤﻬﺎ ﺍﻟﺭﻗﻤﻴﺔ ﻓﻲ ﺍﻟﺘﺭﻤﻴﺯ‪.‬‬

‫ﺘﺠﺎﻫل ﺍﻟﺤﺎﻟﺔ ‪:IgnoreCase‬‬


‫ﺘﻌﻤل ﻜﺜﺎﺒﺕ ﻴﻌﻨﻲ ﺃﻥ ﻤﻘﺎﺭﻨﺔ ﺍﻟﺤﺭﻭﻑ ﺘﺘﺠﺎﻫل ﺤﺎﻟﺘﻬﺎ )ﺼﻐﻴﺭﺓ ﺃﻡ ﻜﺒﻴﺭﺓ(‪.‬‬

‫ﺘﺠﺎﻫل ﺍﻟﺤﺭﻭﻑ ﻏﻴﺭ ﺍﻟﻔﺎﺼﻠﺔ ‪:IgnoreNonSpace‬‬


‫ﺘﻌﻤل ﻜﺜﺎﺒﺕ ﻴﻌﻨﻲ ﺃﻥ ﺍﻟﻤﻘﺎﺭﻨﺔ ﺘﺘﺠﺎﻫل ﻜل ﺍﻟﺭﻤﻭﺯ ﺍﻟﺘﻲ ﻻ ﺘﻌﺘﺒﺭ ﻓﻭﺍﺼل ﺒﻴﻥ ﺍﻟﺤﺭﻭﻑ‪،‬‬
‫ﻤﺜل ﻋﻼﻤﺎﺕ ﺍﻟﺘﺸﻜﻴل ﻓﻲ ﺍﻟﻠﻐﺔ ﺍﻟﻌﺭﺒﻴﺔ‪.‬‬

‫ﺘﺠﺎﻫل ﻨﻭﻉ ﺍﻟﻜﺎﻨﺎ ‪:IgnoreKanaType‬‬


‫ﺘﻌﻤل ﻜﺜﺎﺒﺕ ﻴﻌﻨﻲ ﺃﻥ ﻤﻘﺎﺭﻨﺔ ﺍﻟﺤﺭﻭﻑ ﺘﺘﺠﺎﻫل ﺍﻟﺭﻤﻭﺯ ﺍﻟﺼﻭﺘﻴﺔ ﻓﻲ ﺍﻟﻠﻐﺔ ﺍﻟﻴﺎﺒﺎﻨﻴﺔ‪.‬‬

‫ﺘﺠﺎﻫل ﺍﻟﻌﺭﺽ ‪:IgnoreWidth‬‬


‫ﺘﻌﻤل ﻜﺜﺎﺒﺕ ﻴﻌﻨﻲ ﺃﻥ ﻤﻘﺎﺭﻨﺔ ﺍﻟﺤﺭﻭﻑ ﺘﺘﺠﺎﻫل ﺇﻥ ﻜﺎﻨﺕ ﺍﻟﺤـﺭﻭﻑ ﺍﻟﻴﺎﺒﺎﻨﻴـﺔ ﻤﻜﺘﻭﺒـﺔ‬
‫ﺒﺎﻟﻌﺭﺽ ﺍﻟﻜﺎﻤل ﺃﻡ ﺒﻨﺼﻑ ﺍﻟﻌﺭﺽ‪.‬‬

‫ﺍﻟﻌﺩﻡ ‪:Null‬‬
‫ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻓﺎﺭﻏﺔ ﻤﻥ ﺍﻟﺴﺠل ‪.SqlString‬‬

‫ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺜﻘﺎﻓﺔ ‪:CultureInfo‬‬


‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﺜﻘﺎﻓﺔ ‪ CultureInfo‬ﺍﻟﺫﻱ ﻴﺴﺘﺨﺩﻤﻪ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻓـﻲ ﺘﻨﺴـﻴﻕ‬
‫ﻭﺘﺭﺘﻴﺏ ﻭﻤﻘﺎﺭﻨﺔ ﺍﻟﻨﺼﻭﺹ‪.‬‬

‫ﺍﻟﻤﻌﺭﻑ ﺍﻟﻤﺤﻠﻲ ﻟﻠﺜﻘﺎﻓﺔ ‪:LCID‬‬


‫ﺘﻌﻴﺩ ﻋﺩﺩﺍ ﺼﺤﻴﺤﺎ ﻴﺴﺘﺨﺩﻡ ﻜﻤﻌﺭﻑ ﻟﻠﺜﻘﺎﻓﺔ ﺍﻟﺘﻲ ﻴﺘﻌﺎﻤل ﻤﻌﻬﺎ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫‪٤٥٠‬‬
‫ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﻤﻘﺎﺭﻨﺔ ‪:CompareInfo‬‬
‫ﺘﻌﻴﺩ ﻜﺎﺌﻥ ﻤﻌﻠﻭﻤﺎﺕ ﺍﻟﻤﻘﺎﺭﻨﺔ ‪ CompareInfo‬ﺍﻟﺫﻱ ﻴﺴﺘﺨﺩﻤﻪ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ ﻓﻲ ﻤﻘﺎﺭﻨﺔ‬
‫ﺍﻟﻨﺼﻭﺹ‪.‬‬

‫ﺨﻴﺎﺭﺍﺕ ﺍﻟﻤﻘﺎﺭﻨﺔ ‪:SqlCompareOptions‬‬


‫ﺘﻌﻴﺩ ﺇﺤﺩﻯ ﻗﻴﻡ ﺍﻟﻤﺭﻗﻡ ‪ SqlCompareOptions‬ﺍﻟﺘﻲ ﺘﻭﻀﺢ ﺍﻟﺨﻴـﺎﺭﺍﺕ ﺍﻟﻤﺴـﺘﺨﺩﻤﺔ‬
‫ﻟﻤﻘﺎﺭﻨﺔ ﺍﻟﻨﺼﻭﺹ‪.‬‬

‫ﺍﻟﻘﻴﻤﺔ ‪: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‬ﺒﻬﺎ ﻨﻔﺱ ﻗﻴﻤﺔ ﺍﻟﺴﺠل ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﻗﺭﺍﺀﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻏﻴﺭ ﺍﻟﻤﻭﺴﻌﺔ ‪:GetNonUnicodeBytes‬‬


‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺜﻨﺎﺌﻴﺔ ‪ ،Bytes Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺘﻤﺜﻴل ﺍﻟﻨﺹ ﺍﻟﺤـﺎﻟﻲ ﻓـﻲ ﺘﺭﻤﻴـﺯ‬
‫‪.ASCII‬‬

‫ﻗﺭﺍﺀﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻭﺴﻌﺔ ‪:GetUnicodeBytes‬‬


‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﺜﻨﺎﺌﻴﺔ ‪ ،Bytes Array‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺘﻤﺜﻴل ﺍﻟﻨﺹ ﺍﻟﺤـﺎﻟﻲ ﻓـﻲ ﺘﺭﻤﻴـﺯ‬
‫‪.Unicode‬‬

‫‪٤٥٢‬‬
‫ﺴﺠل ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪SqlBinary Structure‬‬

‫ﻴﻤﺜل ﻫﺫﺍ ﺍﻟﺴﺠل ﻤﺼﻔﻭﻓﺔ ﻤﻥ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪ ..Byte Array‬ﻭﻴﻤﻜﻨﻙ ﻤلﺀ ﻫـﺫﺍ ﺍﻟﺴـﺠل‬
‫ﺒﺎﻟﺒﻴﺎﻨﺎﺕ ﺒﺈﺭﺴﺎل ﻤﺼﻔﻭﻓﺔ ﺜﻨﺎﺌﻴﺔ ﺇﻟﻰ ﺤﺩﺙ ﺇﻨﺸﺎﺌﻪ‪ ..‬ﻤﺜﺎل‪:‬‬
‫;)}‪SqlBinary Sb1 = new SqlBinary(new byte[] {100, 220, 3‬‬
‫ﻭﻨﻅﺭﺍ ﻷﻥ ﻫﺫﺍ ﺍﻟﺴﺠل ﻴﻌﺭﻑ ﻤﻌﺎﻤل ﺍﻟﺘﺤﻭﻴـل ﺍﻟﻀـﻤﻨﻲ ‪ ،Implicit Operator‬ﻓﻴﻤﻜﻨـﻙ‬
‫ﻭﻀﻊ ﻤﺼﻔﻭﻓﺔ ﺜﻨﺎﺌﻴﺔ ﻓﻲ ﻫﺫﺍ ﺍﻟﺴﺠل ﻤﺒﺎﺸﺭﺓ‪:‬‬
‫;}‪SqlBinary Sb1 = new byte[] {100, 220, 3‬‬

‫ﻭﻴﻤﺘﻠﻙ ﻫﺫﺍ ﺍﻟﺴﺠل ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻟﻘﻴﻤﺔ ﺍﻟﻤﻨﻌﺩﻤﺔ ‪:Null‬‬


‫ﺘﻌﻴﺩ ﺴﺠﻼ ﻓﺎﺭﻏﺎ‪ ..‬ﻤﺜﺎل‪:‬‬
‫;‪var Sb3 = SqlBinary.Null‬‬

‫ﺍﻟﻁﻭﺍل ‪: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‬ﻟﻬـﺫﺍ ﻻ‬
‫ﻨﺤﺘﺎﺝ ﺇﻟﻰ ﺇﻋﺎﺩﺓ ﺸﺭﺡ ﻫﺫﻩ ﺍﻟﺨﺼﺎﺌﺹ ﻭﺍﻟﻭﺴﺎﺌل‪:‬‬

‫ﻫل ﻫﻭ ﻤﻨﻌﺩﻡ ‪IsNull‬‬ ‫ﺍﻟﻌﺩﻡ ‪Null‬‬


‫ﺍﻟﻁﻭل ‪Length‬‬ ‫ﺍﻟﻤﻔﻬﺭﺱ ‪Indexer‬‬
‫ﺍﻟﺘﺨﺯﻴﻥ ‪Storage‬‬ ‫ﺃﻗﺼﻰ ﻁﻭل ‪MaxLength‬‬
‫ﺍﻟﻘﻴﻤﺔ ‪Value‬‬ ‫ﺍﻟﻤﺨﺯﻥ ﺍﻟﻭﺴﻴﻁ ‪Buffer‬‬
‫ﻭﻀﻊ ﻋﺩﻡ ‪SetNull‬‬ ‫ﺘﻐﻴﻴﺭ ﺍﻟﻁﻭل ‪SetLength‬‬
‫ﻜﺘﺎﺒﺔ ‪Write‬‬ ‫ﻗﺭﺍﺀﺓ ‪Read‬‬

‫ﺍﻟﺠﺩﻴﺩ ﻓﻘﻁ‪ ،‬ﻫﻭ ﺍﻟﺨﺎﺼﻴﺔ ﻭﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺘﺎﻟﻴﺘﺎﻥ‪:‬‬

‫‪٤٥٥‬‬
‫ﻤﺠﺭﻯ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪:Stream‬‬
‫ﺘﻘﺭﺃ ﺃﻭ ﺘﻐﻴﺭ ﻤﺠﺭﻯ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﻴﺘﻌﺎﻤل ﻤﻌﻪ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ‪ ..‬ﻭﻴﺅﺩﻱ ﺍﺴـﺘﺨﺩﺍﻡ ﻫـﺫﻩ‬
‫ﺍﻟﺨﺎﺼﻴﺔ ﺇﻟﻰ ﺘﺤﻤﻴل ﻜل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻤﺠﺭﻯ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺇﻟﻰ ﺍﻟﺫﺍﻜﺭﺓ‪ ،‬ﻭﻟـﻭ ﻜﺎﻨـﺕ ﻫـﺫﻩ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ ﻀﺨﻤﺔ ﻟﻠﻐﺎﻴﺔ‪ ،‬ﻓﻘﺩ ﺘﺅﺩﻱ ﺇﻟﻰ ﺍﺴﺘﻬﻼﻙ ﻤﺴﺎﺤﺔ ﺍﻟﺫﺍﻜﺭﺓ ﻭﺤﺩﻭﺙ ﺨﻁﺄ ﻤﻥ ﺍﻟﻨﻭﻉ‬
‫‪.OutOfMemoryException‬‬

‫ﺍﻟﺘﺤﻭﻴل ﺇﻟﻰ ﺒﻴﺎﻨﺎﺕ ﺜﻨﺎﺌﻴﺔ ‪:ToSqlBinary‬‬


‫ﺘﻌﻴﺩ ﺴﺠل ﺒﻴﺎﻨﺎﺕ ﺜﻨﺎﺌﻴﺔ ‪ SqlBinary‬ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻟﻭﺤﺩﺍﺕ ﺍﻟﺜﻨﺎﺌﻴﺔ ‪ Bytes‬ﺍﻟﻤﻭﺠـﻭﺩﺓ‬
‫ﻓﻲ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫‪٤٥٦‬‬
‫ﻓﺌﺔ ‪XML‬‬
‫‪SqlXml Class‬‬

‫ﺘﺤﻔﻅ ﻫﺫﻩ ﺍﻟﻔﺌـﺔ ﻭﺜﻴﻘـﺔ ‪ ،XML‬ﻭﻫـﻲ ﺘﻌﺘﻤـﺩ ﺩﺍﺨﻠﻴـﺎ ﻋﻠـﻰ "ﻗـﺎﺭﺉ ﺒﻴﺎﻨـﺎﺕ ‪"XML‬‬
‫‪ ،XmlReader‬ﻟﻬﺫﺍ ﻴﺠﺏ ﻤﺭﺍﻋﺎﺓ ﺃﻥ ﻴﻜﻭﻥ ﺘﻨﺴﻴﻕ ﺒﻴﺎﻨﺎﺕ ‪ XML‬ﺍﻟﺫﻱ ﺘﻀﻌﻬﺎ ﻓـﻲ ﻫـﺫﻩ‬
‫ﺍﻟﻔﺌﺔ ﻤﻭﺍﻓﻘﺎ ﻟﻠﻤﻌﺎﻴﻴﺭ ﺍﻟﺘﻲ ﺘﻘﺒﻠﻬﺎ ﺍﻟﻔﺌﺔ ‪ ..XmlReader‬ﻭﺴﻨﺘﻌﺭﻑ ﻋﻠﻰ ﻜﻴﻔﻴﺔ ﺍﻟﺘﻌﺎﻤـل ﻤـﻊ‬
‫ﺒﻴﺎﻨﺎﺕ ‪ XML‬ﻭﻓﺌﺎﺘﻬﺎ ﻓﻲ ﻜﺘﺎﺏ ﻤﺴﺘﻘل ﺒﺈﺫﻥ ﺍﷲ‪.‬‬
‫ﻭﻟﺤﺩﺙ ﺇﻨﺸﺎﺀ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺜﻼﺙ ﺼﻴﻎ‪:‬‬
‫‪ -١‬ﺍﻷﻭﻟﻰ ﺒﺩﻭﻥ ﻤﻌﺎﻤﻼﺕ‪.‬‬
‫‪ -٢‬ﻭﺍﻟﺜﺎﻨﻴﺔ ﺘﺴﺘﻘﺒل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ ﻤﺠﺭﻯ ﺒﻴﺎﻨﺎﺕ ‪.Stream‬‬
‫‪ -٣‬ﻭﺍﻟﺜﺎﻟﺜﺔ ﺘﺴﺘﻘﺒل ﺍﻟﺒﻴﺎﻨﺎﺕ ﻤﻥ "ﻗﺎﺭﺉ ‪.XmlReader "XML‬‬

‫ﻭﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﺨﺼﺎﺌﺹ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺍﻟﻌﺩﻡ ‪:Null‬‬
‫ﺘﻌﻴﺩ ﻨﺴﺨﺔ ﻤﻥ ﺍﻟﻔﺌﺔ ‪ SqlXml‬ﻻ ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺃﻱ ﻗﻴﻤﺔ‪.‬‬

‫ﺍﻟﻘﻴﻤﺔ ‪:Value‬‬
‫ﺘﻌﻴﺩ ﻨﺼﺎ ‪ String‬ﻴﺤﺘﻭﻱ ﻋﻠﻰ ﻭﺜﻴﻘﺔ ‪ XML‬ﺍﻟﻤﺤﻔﻭﻅﺔ ﻓﻲ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫ﻜﻤﺎ ﺘﻤﺘﻠﻙ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﺍﻟﻭﺴﻴﻠﺔ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫ﺇﻨﺸﺎﺀ ﻗﺎﺭﺉ ‪:CreateReader‬‬


‫ﺘﻌﻴﺩ "ﻗﺎﺭﺉ ‪ XmlReader "XML‬ﻻﺴﺘﺨﺩﺍﻤﻪ ﻓﻲ ﻗﺭﺍﺀﺓ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻜﺎﺌﻥ ﺍﻟﺤﺎﻟﻲ‪.‬‬

‫‪٤٥٧‬‬
‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ﻜل ﺃﻨﻭﺍﻉ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺍﻟﺴﺎﺒﻘﺔ ﺘﺩﻋﻡ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ‪ ،XML‬ﻤﻥ ﺨﻼل‪:‬‬
‫‪ -‬ﺘﻤﺜﻴل ﺍﻟﻭﺍﺠﻬﺔ ‪ IXmlSerializable‬ﻟﺤﻔﻅ ﻤﺤﺘﻭﻴﺎﺕ ﺍﻟﻜـﺎﺌﻥ ﻓـﻲ ﻭﺜﻴﻘـﺔ ‪XML‬‬
‫ﻭﻗﺭﺍﺀﺘﻬﺎ ﻤﻨﻬﺎ ﻓﻲ ﺃﻱ ﻭﻗﺕ‪.‬‬
‫‪ -‬ﺍﻤﺘﻼﻙ ﻭﺴﻴﻠﺔ ﻤﺸﺘﺭﻜﺔ ‪ Shared Method‬ﺍﺴﻤﻬﺎ ‪ ،GetXsdType‬ﺘﺴﺘﻘﺒل "ﻨـﻭﻉ‬
‫ﻤﺨﻁـــﻁ ‪ ،XmlSchemaSet "XML‬ﻭﺘﻌﻴـــﺩ ﻨﺴـــﺨﺔ ﻤـــﻥ ﺍﻟﻔﺌـــﺔ‬
‫‪ XmlQualifiedName‬ﺘﺤﺘﻭﻱ ﻋﻠﻰ ﺍﻻﺴﻡ ﺍﻟﻜﺎﻤل ﻟﻨﻭﻉ ‪ XML‬ﺍﻟﻤﻨﺎﻅﺭ‪.‬‬

‫‪٤٥٨‬‬
‫ﺤﻔﻅ ﺍﻟﻤﻠﻔﺎﺕ ﺨﺎﺭﺝ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪:‬‬
‫ﻴﻘﺩﻡ ﻟﻙ ﺴﻴﻜﻴﻭل ﺴﻴﺭﻓﺭ ‪ ٢٠٠٨‬ﺇﻤﻜﺎﻨﻴﺔ ﺭﺍﺌﻌﺔ‪ ،‬ﻭﻫﻲ ﻗﺩﺭﺘﻙ ﻋﻠﻰ ﺤﻔـﻅ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﺍﻟﺜﻨﺎﺌﻴـﺔ‬
‫ﺍﻟﻀﺨﻤﺔ ‪) BLOB‬ﺘﻜﻭﻥ ﻓﻲ ﺍﻟﻐﺎﻟﺏ ﺃﻜﺒﺭ ﻤﻥ ‪ ١‬ﻤﻴﺠﺎ ﺒﺎﻴﺕ( ﺍﻟﺘﻲ ﺘﺭﺴﻠﻬﺎ ﺇﻟﻰ ﻋﻤـﻭﺩ ﻤـﻥ‬
‫ﺍﻟﻨﻭﻉ )‪ varbinary(MAX‬ﻓﻲ ﻤﻠﻑ ﺨﺎﺹ ﻤﺴﺘﻘل ﻋﻥ ﻤﻠﻑ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻴﺘﻡ ﺤﻔﻅﻪ ﻓﻲ‬
‫ﻤﺠﻠﺩ ﺨﺎﺹ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ‪ ..‬ﻫﺫﺍ ﻴﺤﻘﻕ ﻟﻙ ﺍﻟﻔﻭﺍﺌﺩ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬
‫‪ -١‬ﻴﻀﻤﻥ ﻋﺩﻡ ﺘﻀﺨﻡ ﺤﺠﻡ ﻤﻠﻑ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺼﻭﺭﺓ ﻜﺒﻴﺭﺓ‪.‬‬
‫‪ -٢‬ﻴﻘﻠل ﻤﻥ ﺍﻟﺯﻤﻥ ﺍﻟﻼﺯﻡ ﻟﻘﺭﺍﺀﺓ ﻫﺫﻩ ﺍﻟﺒﻴﺎﻨﺎﺕ‪.‬‬
‫‪ -٣‬ﻴﺴﺘﻁﻴﻊ ﺍﻟﻨﻭﻉ )‪ varbinary(MAX‬ﺤﻔﻅ ﺒﻴﺎﻨﺎﺕ ﺤﺠﻤﻬﺎ ﺘﻘﺭﻴﺒﺎ ‪ ٢‬ﺠﻴﺠﺎ ﺒﺎﻴﺕ ﻓـﻲ‬
‫ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺒﻴﻨﻤﺎ ﻋﻨﺩ ﺍﺴﺘﺨﺩﺍﻡ ﻤﻠﻔﺎﺕ ﺨﺎﺭﺠﻴﺔ ﻻ ﻴﻜﻭﻥ ﻫﻨﺎﻙ ﺍﻱ ﺤﺩ ﻟﺠﻡ ﺍﻟﻤﻠﻑ‪،‬‬
‫ﺇﻻ ﻤﻘﺩﺍﺭ ﺍﻟﻤﺴﺎﺤﺔ ﺍﻟﻤﺘﻭﻓﺭﺓ ﻋﻠﻰ ﺍﻟﻘﺭﺹ ﺍﻟﺼﻠﺏ!‬
‫‪ -٤‬ﻗﺩﺭﺘﻙ ﻋﻠﻰ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﻫﺫﻩ ﺍﻟﻤﻠﻔﺎﺕ ﻤﻥ ﺨﻼل ﺍﺴـﺘﻌﻼﻤﺎﺕ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‪ ،‬ﺃﻭ‬
‫ﺍﻟﺘﻌﺎﻤل ﻤﻌﻬﺎ ﻤﺒﺎﺸﺭﺓ ﻤﻥ ﺨﻼل ﻨﻅﺎﻡ ﻤﻠﻔﺎﺕ ﺍﻟﻭﻴﻨﺩﻭﺯ ‪.Windows File System‬‬
‫‪ -٥‬ﺘﻘﺩﻡ ﻟﻙ ﺩﻭﺕ ﺕ ‪ ٢٠١٠‬ﻓﺌﺔ ﺨﺎﺼﺔ ﻟﻠﺘﻌﺎﻤل ﻤﻊ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺤﻔﻭﻅﺔ ﺨـﺎﺭﺝ ﻗﺎﻋـﺩﺓ‬
‫ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﻭﻫﻲ ﺍﻟﻔﺌﺔ ‪ SqlFileStream‬ﺍﻟﺘﻲ ﺴﻨﺘﻌﺭﻑ ﻋﻠﻴﻬﺎ ﻻﺤﻘﺎ‪.‬‬
‫ﻭﻫﻨﺎﻙ ﺃﺭﺒﻊ ﺨﻁﻭﺍﺕ ﻋﻠﻴﻙ ﺍﺘﺒﺎﻋﻬﺎ‪ ،‬ﺤﺘﻰ ﺘﺴﺘﻁﻴﻊ ﺤﻔﻅ ﻗﻴﻡ ﺍﻷﻋﻤﺩﺓ ﺍﻟﻀـﺨﻤﺔ ﻓـﻲ ﻤﻠﻔـﺎﺕ‬
‫ﻤﺴﺘﻘﻠﺔ‪ ..‬ﻫﺫﻩ ﺍﻟﺨﻁﻭﺍﺕ ﻫﻲ‪:‬‬

‫‪ -١‬ﺘﻔﻌﻴل ﺍﺴﺘﺨﺩﺍﻡ ﻤﺠﺭﻯ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ FILESTREAM‬ﻓﻲ ﺨﺩﻤﺎﺕ ﺍﻟﻭﻴﻨﺩﻭﺯ‪:‬‬


‫ﻴﺘﻡ ﻫﺫﺍ ﻜﻤﺎ ﻴﻠﻲ‪:‬‬
‫‪ -‬ﻤﻥ ﻗﺎﺌﻤﺔ ﺍﻟﺒﺭﺍﻤﺞ ‪ ،Programs Menu‬ﺍﻀﻐﻁ‪:‬‬
‫\‪Microsoft SQL Server 2008\Configuration Tools‬‬
‫‪SQL Server Configuration Manager‬‬
‫ـﺭ‬
‫ـﺭ ﺍﻟﻌﻨﺼـ‬
‫ـﻴﻜﻭﻴل‪ ،‬ﺍﻨﻘـ‬
‫ـﺎﺩﻡ ﺴـ‬
‫ـﺔ ﺨـ‬
‫ـﺫﺓ ﺘﻬﻴﺌـ‬
‫ـﻲ ﻨﺎﻓـ‬
‫ـﺭﻯ ﻓـ‬
‫ـﺠﺭﺓ ﺍﻟﻴﺴـ‬
‫ـﻲ ﺍﻟﺸـ‬
‫‪ -‬ﻓـ‬
‫‪ SQL Server Services‬ﻤﺭﺘﻴﻥ ﺒﺎﻟﻔﺄﺭﺓ‪.‬‬
‫‪ -‬ﻓﻲ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﻤﻨﻰ‪ ،‬ﺤﺩﺩ ﺍﺴﻡ ﺨﺎﺩﻡ ﺴﻴﻜﻭﻴل ﺍﻟﺫﻱ ﺘﺘﻌﺎﻤل ﻤﻌﻪ‪ ..‬ﻓﻲ ﺤﺎﻟﺘﻨﺎ ﻫﺫﻩ ﺴﻴﻜﻭﻥ‬
‫)‪ ،SQL Server(SQLEXPRESS‬ﻭﺍﻨﻘﺭﻩ ﻤﺭﺘﻴﻥ ﺒﺎﻟﻔﺄﺭﺓ ﻟﻌﺭﺽ ﺨﺼﺎﺌﺼﻪ‪.‬‬

‫‪٤٥٩‬‬
‫‪ -‬ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ‪ ،‬ﺍﻀﻐﻁ ﺍﻟﺸﺭﻴﻁ ﺍﻟﻌﻠـﻭﻱ ‪ Tab‬ﺍﻟﻤﺴـﻤﻰ ‪FILESTREAM‬‬
‫ﻟﻌﺭﺽ ﺼﻔﺤﺔ ﺨﺼﺎﺌﺼﻪ‪.‬‬
‫‪ -‬ﻀﻊ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﺃﻤﺎﻡ ﺍﻻﺨﺘﻴﺎﺭ‪:‬‬
‫‪Enable FILESTREAM for Transact-SQL access‬‬
‫ﻫﺫﺍ ﺴﻴﺘﻴﺢ ﻟﻙ ﻗﺭﺍﺀﺓ ﻭﻜﺘﺎﺒﺔ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺨﺎﺭﺠﺤﻴﺔ ﻤﻥ ﺨﻼل ﺍﻻﺴﺘﻌﻼﻤﺎﺕ‪.‬‬

‫‪ -‬ﺇﺫﺍ ﻭﻀﻌﺕ ﻋﻼﻤﺔ ﺍﻻﺨﺘﻴﺎﺭ ﺃﻤﺎﻡ ﺍﻻﺨﺘﻴﺎﺭ‪:‬‬


‫‪Enable FILESTREAM for file I/O streaming access‬‬
‫ﻓﺴﻴﺘﻴﺢ ﻫﺫﺍ ﻟﻙ ﺍﻟﻘﺭﺍﺀﺓ ﻤﻥ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺨﺎﺭﺠﻴﺔ ﻤﻥ ﺨﻼل ﻨﻅـﺎﻡ ﻤﺸـﺎﺭﻜﺔ ﺍﻟﻤﻠﻔـﺎﺕ‬
‫‪ Sharing‬ﻋﺒﺭ ﺸﺒﻜﺎﺕ ﺍﻟﻭﻴﻨﺩﻭﺯ‪ ..‬ﺃﻱ ﺃﻨﻙ ﺴﺘﺘﻁﻴﻊ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻤﻠﻑ ﻤﺒﺎﺸﺭﺓ ﺒﺩﻭﻥ‬
‫ﺍﺴﺘﻌﻼﻤﺎﺕ‪ ،‬ﻜﺄﻨﻙ ﺘﺘﻌﺎﻤل ﻤﻊ ﺃﻱ ﻤﻠﻑ ﻋﺎﺩﻱ ﻋﻠﻰ ﺍﻟﺸﺒﻜﺔ‪ ،‬ﻭﻫﻭ ﻤﺎ ﺴﻨﻔﻌﻠﻪ ﺒﺎﺴﺘﺨﺩﺍﻡ‬

‫‪٤٦٠‬‬
‫ﺍﻟﻔﺌﺔ ‪ ..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‬ﻓﻲ ﺨﺎﺩﻡ ﺴﻴﻜﻴﻭل‪:‬‬


‫ﻟﻔﻌل ﻫﺫﺍ‪ ،‬ﺍﻓﺘﺢ ﻤﺩﻴﺭ ﺴﻴﻜﻭﻴل ‪ ،SQL Server Management Studio‬ﻭﻓﻲ ﻤﺘﺼـﻔﺢ‬
‫ﺍﻟﻜﺎﺌﻨﺎﺕ ‪ Object Browser‬ﺤﺩﺩ ﺍﻟﻌﻨﺼﺭ ﺍﻟﺭﺌﻴﺴﻲ ﻓﻲ ﺍﻟﺸﺠﺭﺓ )ﺍﻟﺫﻱ ﻴﺤﻤل ﺍﺴﻡ ﺨﺎﺩﻡ‬
‫ﺴﻴﻜﻭﻴل ‪ ،(SQLEXPRESS‬ﻭﺍﻀﻐﻁﻪ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ‪ ،‬ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤـﺔ ﺍﻟﻤﻭﻀـﻌﻴﺔ‬
‫ﺍﻀﻐﻁ ﺍﻷﻤﺭ ‪ ..Properties‬ﺴﻴﻌﺭﺽ ﻫﺫﺍ ﻨﺎﻓﺫﺓ ﺨﺼﺎﺌﺹ ﺨﺎﺩﻡ ﺴـﻴﻜﻴﻭﻴل‪ ..‬ﺍﻀـﻐﻁ‬
‫ﺍﻟﻌﻨﺼﺭ ‪ Advanced‬ﻤﻥ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻴﺴﺭﻯ‪ ،‬ﻟﻌﺭﺽ ﺍﻟﺨﺼـﺎﺌﺹ ﺍﻟﻤﺘﻘﺩﻤـﺔ‪ ،‬ﻜﻤـﺎ ﻫـﻭ‬
‫ﻤﻭﻀﺢ ﻓﻲ ﺍﻟﺼﻭﺭﺓ‪:‬‬

‫‪٤٦٢‬‬
‫ﺴﺘﺠﺩ ﺃﻭل ﺨﺎﺼﻴﺔ ﻓﻴﻬﺎ ﻫﻲ ‪ ،Filestream Access Level‬ﺍﻟﺘﻲ ﺘﻭﻀﺢ ﻜﻴﻔﻴﺔ ﺍﻟﺘﻌﺎﻤل‬
‫ﻤﻊ ﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﺨﺎﺭﺠﻴﺔ‪ ..‬ﺴﺘﺠﺩ ﻗﻴﻤﺔ ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ ‪ Disabled‬ﺃﻱ ﺃﻥ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻤﻠﻔﺎﺕ‬
‫ﺍﻟﺨﺎﺭﺠﻴﺔ ﻤﻤﻨﻭﻉ!‪ ..‬ﺍﻀﻐﻁ ﺍﻟﻘﺎﺌﻤﺔ ﺍﻟﻤﻨﺴﺩﻟﺔ‪ ،‬ﻭﺍﺨﺘﺭ ﺍﻟﺘﻌﺎﻤـل ﺍﻟﻜﺎﻤـل ‪Full Access‬‬
‫‪ ..Enabled‬ﻫﺫﺍ ﻴﺴﻤﺢ ﺒﺎﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻤﻠﻔﺎﺕ ﻤﺒﺎﺸﺭﺓ‪ ،‬ﺃﻭ ﻤﻥ ﺨﻼل ﺍﻻﺴﺘﻌﻼﻤﺎﺕ‪ ..‬ﺃﻤـﺎ‬
‫ﺇﺫﺍ ﺃﺭﺩﺕ ﻗﺼﺭ ﺍﻟﺘﻌﺎﻤل ﻤﻊ ﺍﻟﻤﻠﻔﺎﺕ ﻤﻥ ﺨﻼل ﺍﺴﺘﻌﻼﻤﺎﺕ ‪ SQL‬ﻓﻘﻁ‪ ،‬ﻓﺎﺨﺘﺭ ﺍﻻﺨﺘﻴـﺎﺭ‬
‫ﺍﻟﺜﺎﻨﻲ‪ ..Transact-SQL Access Enabled :‬ﺍﻀﻐﻁ ﺍﻟﺯﺭ ‪ Ok‬ﻹﻏـﻼﻕ ﺍﻟﻨﺎﻓـﺫﺓ‪..‬‬
‫ﺴﺘﻅﻬﺭ ﻟﻙ ﺭﺴﺎﻟﺔ ﺘﺨﺒﺭﻙ ﺒﺄﻥ ﺒﻌﺽ ﺍﻟﺘﻐﻴﻴﺭﺍﺕ ﻟﻥ ﺘﺤﺩﺙ ﺇﻻ ﺇﺫﺍ ﺃﻭﻗﻔﺕ ﺨﺎﺩﻡ ﺴـﻴﻜﻭﻴل‬
‫ﻋﻥ ﺍﻟﻌﻤل ﻭﺃﻋﺩﺕ ﺘﺸﻐﻴﻠﻪ ‪ ..Restart‬ﻴﻤﻜﻨﻙ ﻓﻌل ﻫﺫﺍ ﻤﻥ ﻤﺩﻴﺭ ﺘﻬﻴﺌﺔ ﺨـﺎﺩﻡ ﺴـﻴﻜﻭﻴل‬
‫‪ SQL Server Configuration Manager‬ﻜﻤﺎ ﺘﻌﻠﻤﻨﺎ ﻤﻥ ﻗﺒل‪.‬‬

‫‪ -٣‬ﺘﻔﻌﻴل ﺍﺴﺘﺨﺩﺍﻡ ﻤﺠﺭﻯ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ FILESTREAM‬ﻓﻲ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪:‬‬


‫ﻴﻤﻜﻨﻙ ﻓﻌل ﻫﺫﺍ ﻋﻨﺩ ﺇﻨﺸﺎﺀ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒﺠﻤـل ‪ T-SQL‬ﻭﺫﻟـﻙ ﺒﺎﺴـﺘﺨﺩﺍﻡ ﺍﻟﺴـﻤﺔ‬
‫‪) FILESTREAM‬ﻫﺫﺍ ﺨﺎﺭﺝ ﻨﻁﺎﻕ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ(‪ ..‬ﻜﻤﺎ ﻴﻤﻜﻨﻙ ﺘﻌﺩﻴل ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ‬
‫ﺒﻌﺩ ﺇﻨﺸﺎﺌﻬﺎ ﻟﺘﻔﻌﻴل ﻫﺫﻩ ﺍﻟﺨﺎﺼﻴﺔ‪ ،‬ﻭﺫﻟﻙ ﺒﺎﺴﺘﺨﺩﺍﻡ ﺍﻟﻭﺍﺠﻬﺔ ﺍﻟﻤﺭﺌﻴﺔ ﻟﻤﺩﻴﺭ ﺴﻴﻜﻭﻴل ‪SQL‬‬
‫‪ ..Server Management Studio‬ﺍﻓﺘﺭﺽ ﺃﻨﻨﺎ ﻨﺘﻌﺎﻤل ﻤـﻊ ﻗﺎﻋـﺩﺓ ﺒﻴﺎﻨـﺎﺕ ﺍﻟﻜﺘـﺏ‬
‫ﻜﻤﺜﺎل‪ ..‬ﺍﺘﺒﻊ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ ﻟﺘﻔﻌﻴل ﺤﻔﻅ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺨﺎﺭﺠﻬﺎ‪:‬‬
‫‪ -‬ﺍﻓﺘﺢ ﻤﺘﺼﻔﺢ ﺍﻟﻜﺎﺌﻨﺎﺕ ‪ Object Explorer‬ﻓﻲ ﻤﺩﻴﺭ ﺴﻴﻜﻭﻴل‪.‬‬
‫‪ -‬ﺃﻟﺤﻕ ‪ Attach‬ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪ C:\Books.mdf‬ﺇﻥ ﻟﻡ ﺘﻜﻥ ﻤﻭﺠﻭﺩﺓ‪.‬‬
‫‪ -‬ﺍﻀﻐﻁ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻜﺘﺏ ﺒﺯﺭ ﺍﻟﻔﺄﺭﺓ ﺍﻷﻴﻤﻥ‪ ،‬ﻭﻤﻥ ﺍﻟﻘﺎﺌﻤـﺔ ﺍﻟﻤﻭﻀـﻌﻴﺔ ﺍﻀـﻐﻁ‬
‫‪.Properties‬‬

‫‪٤٦٣‬‬
‫‪ -‬ﻓﻲ ﻨﺎﻓﺫﺓ ﺍﻟﺨﺼﺎﺌﺹ‪ ،‬ﺍﻀﻐﻁ ﺍﻟﻌﻨﺼﺭ ‪ 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‬‬
‫ﺘﻌﻴﺩ ﻤﺴﺎﺭ ﺍﻟﻤﻠﻑ ﺍﻟﻤﺭﺴل ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻷﻭل ﻓﻲ ﺤﺩﺙ ﺍﻹﻨﺸﺎﺀ‪.‬‬

‫ﻤﺤﺘﻭﻯ ﺍﻟﺘﻌﺎﻤل ‪:TransactionContext‬‬


‫ﺘﻌﻴﺩ ﻤﺼﻔﻭﻓﺔ ﻤﺤﺘﻭﻯ ﺍﻟﺘﻌﺎﻤل ﺍﻟﻤﺭﺴﻠﺔ ﺇﻟﻰ ﺍﻟﻤﻌﺎﻤل ﺍﻟﺜﺎﻨﻲ ﻓﻲ ﺤﺩﺙ ﺍﻹﻨﺸﺎﺀ‪.‬‬

‫ﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻓﻲ ﺍﻟـﺯﺭ ‪ SqlFileStream.Read‬ﻓـﻲ ﺍﻟﻤﺸـﺭﻭﻉ‬


‫‪ ..ReadLargeData‬ﻓﻲ ﻫﺫﺍ ﺍﻟﺯﺭ ﻨﺴﺘﺨﺩﻡ ﺍﻻﺴﺘﻌﻼﻡ ﺍﻟﺘﺎﻟﻲ‪:‬‬
‫‪SELECT ID, Logo3.PathName( ),‬‬
‫) ( ‪GET_FILESTREAM_TRANSACTION_CONTEXT‬‬
‫‪From Publishers‬‬
‫ﻫﺫﺍ ﺍﻻﺴﺘﻌﻼﻡ ﻴﻌﻴﺩ ﺜﻼﺜﺔ ﺤﻘﻭل‪ ،‬ﻫﻲ ﺒﺎﻟﺘﺭﺘﻴﺏ‪ :‬ﺭﻗﻡ ﺍﻟﻨﺎﺸﺭ‪ ،‬ﻤﺴﺎﺭ ﻤﻠﻑ ﺍﻟﺼﻭﺭﺓ‪ ،‬ﻤﺼـﻔﻭﻓﺔ‬
‫ﻤﺤﺘﻭﻯ ﺍﻟﺘﻌﺎﻤل ‪ Transaction Context‬ﺍﻟﺨﺎﺼﺔ ﺒﺎﻟﻤﻠﻑ‪ ..‬ﻭﻴﺴﺘﺨﺩﻡ ﺍﻟﻜﻭﺩ ﻫـﺫﻩ ﺍﻟﺤﻘـﻭل‬
‫ﻟﻔﺘﺢ ﺍﻟﻤﻠﻑ ﻭﻗﺭﺍﺀﺓ ﻤﺤﺘﻭﻴﺎﺘﻪ‪ ،‬ﻭﺤﻔﻅﻬﺎ ﻓﻲ ﻤﻠﻑ ﺠﺩﻴﺩ ﻋﻠﻰ ﺍﻟﻤﺴﺎﺭ ‪.C:‬‬
‫ﻭﺴﺘﺠﺩ ﻤﺜﺎﻻ ﻋﻠﻰ ﺍﺴﺘﺨﺩﺍﻡ ﻫﺫﻩ ﺍﻟﻔﺌﺔ ﻟﺤﻔﻅ ﺼﻭﺭﺓ ﻓﻲ ﺍﻟﺨﺎﻨـﺔ ‪ Logo3‬ﺍﻟﺨﺎﺼـﺔ ﺒﺎﻟﻨﺎﺸـﺭ‬
‫ﺍﻟﺜﺎﻨﻲ‪ ،‬ﻭﺫﻟﻙ ﻓﻲ ﺍﻟﺯﺭ ‪ SqlFileStream.Write‬ﻓﻲ ﺍﻟﻤﺸﺭﻭﻉ ‪ ..WriteLargeData‬ﻓـﻲ‬
‫ﻫﺫﺍ ﺍﻟﺯﺭ ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﺴﺘﻌﻼﻤﺎ ﺸﺒﻴﻬﺎ ﺒﺎﻻﺴﺘﻌﻼﻡ ﺍﻟﺴﺎﺒﻕ‪ ،‬ﻟﻜﻨﻨﺎ ﻟﺠﺄﻨﺎ ﺃﻭﻻ ﺇﻟﻰ ﺤﻴﻠﺔ ﺼﻐﻴﺭﺓ‪ ،‬ﻓﻘﺩ‬

‫‪٤٧٠‬‬
‫ﺍﺴﺘﺨﺩﻤﻨﺎ ﺍﺴﺘﻌﻼﻤﺎ ﺁﺨﺭ ﻟﻭﻀﻊ ﺍﻟﻘﻴﻤﺔ ‪ 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‬ﻓﻲ ﺍﻟﺨﺎﻨﺔ‪ ،‬ﻷﻨـﻙ ﺘﺭﻴـﺩ‬
‫ﺍﻟﻤﺤﺎﻓﻅﺔ ﻋﻠﻰ ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﻠﻑ ﺍﻟﻤﻭﺠﻭﺩ‪.‬‬

‫‪٤٧١‬‬
‫ﻤﻠﺤﻕ‪٣ :‬‬
‫ﺇﻋﺩﺍﺩ ﺘﻁﺒﻴﻕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل‬

‫ﺒﻌﺩ ﺃﻥ ﺘﻨﺘﻬﻲ ﻤﻥ ﻜﺘﺎﺒﺔ ﻤﺸﺭﻭﻉ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺴﺘﺤﺘﺎﺝ ﺇﻟﻰ ﺘﺸﻐﻴﻠﻪ ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل‪..‬‬
‫ﻟﻔﻌل ﻫﺫﺍ ﺍﺘﺒﻊ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺘﺎﻟﻴﺔ‪:‬‬

‫‪ -١‬ﻗﻡ ﺒﺈﻋﺩﺍﺩ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ‪ .Net Frame work‬ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل‪:‬‬


‫ﺍﺴﺘﺨﺩﻡ ﺇﺼﺩﺍﺭ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﺍﻟﺫﻱ ﺃﻨﺸﺄﺕ ﺍﻟﺒﺭﻨﺎﻤﺞ ﻋﻠﻴﻪ ﻓﻲ ﺩﻭﺕ ﻨﺕ‪ ..‬ﺴﺘﺠﺩ ﻤﻠﻑ‬
‫ﺇﻋﺩﺍﺩ ﺇﻁﺎﺭ ﺍﻟﻌﻤل ﻋﻠﻰ ﺍﻟﻘﺭﺹ ﺍﻟﺨﺎﺹ ﺒﺩﻭﺕ ﻨﺕ ﺤﻴﺙ ﺴﻴﺒﺩﺃ ﺍﺴـﻤﻪ ﺒـﺎﻟﺤﺭﻭﻑ‪:‬‬
‫‪ ..dotNetFx‬ﺃﻭ ﻴﻤﻜﻨﻙ ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻤﻭﻗﻊ ﻤﻴﻜﺭﻭﺴﻭﻓﺕ‪.‬‬

‫‪ -٢‬ﻗﻡ ﺒﺈﻋﺩﺍﺩ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪:‬‬


‫ﺇﺫﺍ ﻜﻨﺕ ﺘﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪ ،‬ﻓﻴﺠﺏ ﺇﻋﺩﺍﺩ ﻨﺴﺨﺔ ﻤﻨﺎﺴﺒﺔ ﻤـﻥ‬
‫ﺘﻁﺒﻴﻕ ‪ SQL Server‬ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴـل )ﺇﻥ ﻜﺎﻨـﺕ ﻗﺎﻋـﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤﺤﻠﻴـﺔ‬
‫‪ ،(Local‬ﺃﻭ ﺇﻋﺩﺍﺩﻩ ﻋﻠﻰ ﺍﻟﺨﺎﺩﻡ ‪ Server‬ﺇﻥ ﻜﺎﻨﺕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺴﺘﺨﺩﻡ ﺃﻜﺜﺭ ﻤﻥ‬
‫ﻤﺴﺘﺨﺩﻡ‪ ،‬ﻭﻓﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻋﻠﻴﻙ ﻀﺒﻁ ﺇﻋﺩﺍﺩﺍﺕ ﺍﻻﺘﺼﺎل ﺒﺎﻟﺨﺎﺩﻡ ﻭﺍﻟــ ‪ IP‬ﺍﻟـﺫﻱ‬
‫ﺴﻴﺘﻴﺢ ﺍﻻﺘﺼﺎل ﺒﻪ ﻤﻥ ﺍﻷﺠﻬﺯﺓ ﺍﻷﺨﺭﻯ )ﻓﻲ ﺍﻟﻐﺎﻟﺏ ﻫﺫﻩ ﻤﺴﺌﻭﻟﻴﺔ ﻤﺩﻴﺭ ﺍﻟﺸـﺒﻜﺔ(‪..‬‬
‫ﻭﻓﻲ ﻜﻠﺘﺎ ﺍﻟﺤﺎﻟﺘﻴﻥ‪ ،‬ﻴﺠﺏ ﺃﻥ ﺘﻀﻊ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﺍﻟﻌﻨﻭﺍﻥ ﺍﻟﺫﻱ ﻴﺘﻭﻗﻊ ﺒﺭﻨﺎﻤﺠـﻙ‬
‫ﺃﻥ ﻴﺠﺩﻫﺎ ﻓﻴﻪ )ﻜﻤﺎ ﺤﺩﺩﺘﻪ ﻓﻲ ﻨﺹ ﺍﻻﺘﺼﺎل ‪ ،(Connection String‬ﺃﻭ ﺍﻷﻓﻀـل‬
‫ﻤﻥ ﻫﺫﺍ ﺃﻥ ﻴﺴﻤﺢ ﺒﺭﻨﺎﻤﺠﻙ ﻟﻠﻤﺴﺘﺨﺩﻡ ﺒﺎﺨﺘﻴﺎﺭ ﻤﻭﻀﻊ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨـﺎﺕ ﻤـﻥ ﻋﻠـﻰ‬
‫ﺍﻟﺠﻬﺎﺯ ﺃﻭ ﺘﺴﻤﺢ ﻟﻪ ﺒﻜﺘﺎﺒﺔ ﻋﻨﻭﺍﻥ ﺍﻟﺨﺎﺩﻡ ‪ IP‬ﻓﻲ ﻨﺎﻓﺫﺓ ﻤﺨﺼﺼﺔ ﻟﻬﺫﺍ ﺍﻟﻐﺭﺽ‪.‬‬
‫ﻭﺇﺫﺍ ﻜﻨﺕ ﺘﺴﺘﺨﺩﻡ ﻨﺴﺨﺔ ‪ SQL Server Express‬ﻓﺎﻨﻅﺭ ﺍﻟﻤﻘﻁﻊ ﺍﻟﺨﺎﺹ ﺒﻪ ﻓـﻲ‬
‫ﻨﻬﺎﻴﺔ ﻫﺫﺍ ﺍﻟﻤﻠﺤﻕ‪.‬‬
‫‪٤٧٢‬‬
‫‪ -٣‬ﻗﻡ ﺒﺈﻋﺩﺍﺩ ﻋﺎﺭﺽ ﺍﻟﺘﻘﺎﺭﻴﺭ‪:‬‬
‫ﺇﺫﺍ ﻜﺎﻥ ﺒﺭﻨﺎﻤﺠـﻙ ﻴﻌـﺭﺽ ﺒﻌـﺽ ﺍﻟﺘﻘـﺎﺭﻴﺭ ﺒﺎﺴـﺘﺨﺩﺍﻡ ‪ Report Viewer‬ﺃﻭ‬
‫‪ ،Crystal Report‬ﻓﻌﻠﻴﻙ ﺒﺈﻋﺩﺍﺩ ﺍﻟﻤﻜﺘﺒﺎﺕ ﺍﻟﻼﺯﻤﺔ ﻟﻬﺫﻩ ﺍﻟﺘﻘـﺎﺭﻴﺭ ﻋﻠـﻰ ﺠﻬـﺎﺯ‬
‫ﺍﻟﻌﻤﻠﻲ‪ ..‬ﻋﻠﻰ ﺴﺒﻴل ﺍﻟﻤﺜﺎل‪ ،‬ﺘﺤﺘﺎﺝ ﺍﻟﺘﻘﺎﺭﻴﺭ ﺍﻟﺘﻲ ﺘﺴﺘﺨﺩﻡ ﺍﻷﺩﺍﺓ ‪Report Viewer‬‬
‫ﺇﻟﻰ ﺒﺭﻨﺎﻤﺞ ﺇﻋﺩﺍﺩ ﺍﺴﻤﻪ‪:‬‬
‫‪Microsoft Report Viewer 2012 Runtime‬‬
‫ﻭﻫﻭ ﺒﺩﻭﺭﻩ ﻴﺤﺘﺎﺝ ﻟﻭﺠﻭﺩ ﺇﻋﺩﺍﺩﺍﺕ ﻤﺴﺒﻘﺔ ﻷﻨﻭﺍﻉ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﺍﻟﻤﺩﺍﺭﺓ ﻋﻠﻰ ﺠﻬﺎﺯ‬
‫ﺍﻟﻤﺴــﺘﺨﺩﻡ‪ ،‬ﻓــﺈﻥ ﻟــﻡ ﺘﻜــﻥ ﻤﻭﺠــﻭﺩﺓ‪ ،‬ﻓﻴﻠﺯﻤﻬــﺎ ﺒﺭﻨــﺎﻤﺞ ﺇﻋــﺩﺍﺩ ﺍﺴــﻤﻪ‬
‫‪ ..SQLSysClrTypes‬ﻭﻜﻼﻫﻤﺎ ﻴﻤﻜﻥ ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻤﻭﻗﻊ ﻤﻴﻜﺭﻭﺴﻭﻓﺕ‪.‬‬

‫‪ -٤‬ﻗﻡ ﺒﺈﻋﺩﺍﺩ ﺒﺭﻨﺎﻤﺠﻙ‪:‬‬


‫ﻟﻭ ﻨﻔﺫﺕ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺴﺎﺒﻘﺔ‪ ،‬ﻓﻔﻲ ﺍﻟﻐﺎﻟﺏ ﺴﻴﻌﻤل ﺍﻟﻤﻠﻑ ﺍﻟﺘﻨﻔﻴﺫﻱ ﺍﻟﺨﺎﺹ ﺒـﻙ ﻋﻠـﻰ‬
‫ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل ﺒﺩﻭﻥ ﺍﻟﺤﺎﺠﺔ ﻷﻱ ﺇﻋﺩﺍﺩﺍﺕ ﺃﺨﺭﻯ‪ ..‬ﻜل ﻤﺎ ﻋﻠﻴﻙ ﻓﻌﻠﻪ ﻫﻭ ﻭﻀﻊ ﻜـل‬
‫ﺍﻟﺼﻭﺭ ﻭﺍﻟﻤﻠﻔﺎﺕ ﺍﻟﻼﺯﻤﺔ ﻟﻌﻤل ﺒﺭﻨﺎﻤﺠﻙ ﻓﻲ ﻤﺠﻠﺩ ﻭﺍﺤﺩ ﻤﻊ ﺍﻟﻤﻠﻑ ﺍﻟﺘﻨﻔﻴـﺫﻱ )ﻤـﻊ‬
‫ﻤﺭﺍﻋﺎﺓ ﻜﺘﺎﺒﺔ ﺍﻟﻜﻭﺩ ﻤﻨﺫ ﺍﻟﺒﺩﺍﻴﺔ ﻟﻴﻘﺭﺃ ﻫﺫﻩ ﺍﻟﻤﻠﻔﺎﺕ ﻤﻥ ﻨﻔﺱ ﻤﺠﻠﺩ ﺍﻟﻤﻠﻑ ﺍﻟﺘﻨﻔﻴﺫﻱ(‪ ،‬ﺜﻡ‬
‫ﻨﺴﺨﻪ ﺇﻟﻰ ﺠﻬﺎﺯ ﺍﻟﻌﻤﻴل‪.‬‬
‫ﺃﻤﺎ ﺇﺫﺍ ﻜﻨﺕ ﺘﺘﻌﺎﻤل ﻤﻊ ﺃﺩﻭﺍﺕ ﺨﺎﺼﺔ ﺘﺤﺘﺎﺝ ﻹﻋﺩﺍﺩ ﻭﻭﻀﻊ ﻗﻴﻡ ﻓﻲ ﻤﺴﺠل ﺍﻟﻭﻴﻨﺩﻭﺯ‬
‫‪ ،Registry‬ﻓﻔﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻋﻠﻴﻙ ﺇﻨﺸﺎﺀ ﺒﺭﻨﺎﻤﺞ ﺤﺯﻡ ﻭﺘﻭﺯﻴـﻊ ‪Setup Package‬‬
‫ﻴﻘﻭﻡ ﺒﺈﻋﺩﺍﺩ ﺒﺭﻨﺎﻤﺠﻙ ﻋﻠﻰ ﺠﻬﺎﺯ ﺍﻟﻤﺴﺘﺨﺩﻡ‪ ..‬ﻭﻗﺩ ﺸﺭﺤﺕ ﻫﺫﺍ ﺍﻟﻤﻭﻀﻭﻉ ﺒﺎﻟﺘﻔﺼـﻴل‬
‫ﻓﻲ ﺍﻟﻔﻀل ﺍﻷﺨﻴﺭ ﻤﻥ ﻤﺭﺠﻊ "ﻤﻥ ﺍﻟﺼﻔﺭ ﺇﻟﻰ ﺍﻻﺤﺘﺭﺍﻑ ﺒﺭﻤﺠﺔ ﻨﻤﺎﺫﺝ ﺍﻟﻭﻨﺩﻭﺯ"‪.‬‬

‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ﺍﻟﺸﺭﺡ ﺍﻟﺴﺎﺒﻕ ﻴﺨﺹ ﺘﻁﺒﻴﻘﺎﺕ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺨﺎﺼﺔ ﺒﺸﺭﻜﺔ ﺃﻭ ﻋﻤﻴل ﻤﺤﺩﺩ‪ ،‬ﻷﻨﻙ ﻫﻨﺎ ﺘﻌﺩ‬
‫ﺍﻟﺒﺭﻨﺎﻤﺞ ﻤﺭﺓ ﻭﺍﺤﺩﺓ ﻓﻘﻁ ﻟﻌﻤﻴل ﻭﺍﺤﺩ‪ ،‬ﻭﺒﺎﻟﺘﺎﻟﻲ ﺘﺴﺘﻁﻴﻊ ﺃﻥ ﺘﺭﻴﻪ ﻫﺫﻩ ﺍﻟﺨﻁﻭﺍﺕ ﻋﻤﻠﻴﺎ ﻭﺘﺩﺭﺒﻪ‬
‫‪٤٧٣‬‬
‫ﻋﻠﻴﻬﺎ ﻟﻴﻘﻭﻡ ﺒﻬﺎ ﺒﻨﻔﺴﻪ ﺒﻌﺩ ﺫﻟﻙ‪ ،‬ﻤﻊ ﻜﺘﺎﺒﺔ ﻤﻠﻑ ﺘﻌﻠﻴﻤﺎﺕ ﻴﺸﺭﺡ ﻟﻪ ﻫﺫﻩ ﺍﻟﺨﻁﻭﺍﺕ‪ ،‬ﺒﺤﻴـﺙ ﻻ‬
‫ﻴﺤﺘﺎﺝ ﺇﻟﻴﻙ ﺒﻌﺩ ﻫﺫﺍ‪.‬‬
‫ﻟﻜﻥ ﺍﻷﻤﺭ ﺴﻴﺨﺘﻠﻑ ﻤﻊ ﺍﻟﺒﺭﺍﻤﺞ ﺍﻟﺘﻲ ﺘﺒﺎﻉ ﻓﻲ ﺍﻟﺴﻭﻕ ﻟﻤﺴﺘﺨﺩﻤﻴﻥ ﻜﺜﺭ‪ ،‬ﻓﻔﻲ ﻫﺫﻩ ﺍﻟﺤﺎﻟﺔ ﻋﻠﻴﻙ‬
‫ﺃﻥ ﺘﻜﺘﺏ ﺒﺭﻨﺎﻤﺞ ﺇﻋﺩﺍﺩ ﻴﻘﻭﻡ ﺒﻜل ﺃﻭ ﻤﻌﻅﻡ ﺍﻟﺨﻁﻭﺍﺕ ﺍﻟﺴﺎﺒﻘﺔ ﺁﻟﻴـﺎ )ﺃﻱ ﺃﻨـﻙ ﺴـﺘﺠﻤﻊ ﻜـل‬
‫ﺍﻟﺨﻁﻭﺍﺕ ﻓﻲ ﺍﻟﺨﻁﻭﺓ ﺭﻗﻡ ‪ ٤‬ﻓﻲ ﺍﻟﺸﺭﺡ ﺍﻟﺴﺎﺒﻕ(‪ ..‬ﻭﻓﻲ ﺍﻟﻐﺎﻟﺏ ﺴﻴﺴﺘﺜﻨﻰ ﻤﻥ ﺍﻷﻤـﺭ ﺇﻋـﺩﺍﺩ‬
‫ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ ﻷﻨﻪ ﺘﻁﺒﻴﻕ ﻤﺴﺘﻘل ﺒﺘﺭﺨﻴﺹ‪ ،‬ﻜﻤﺎ ﺃﻥ ﺇﻋﺩﺍﺩﻩ ﻋﻠﻰ ﺨﺎﺩﻡ ﻴﻌﻤل ﻋﻠـﻰ ﺸـﺒﻜﺔ‬
‫ﺃﻤﺭ ﻴﺨﺹ ﺍﻟﻤﺴﺌﻭﻟﻴﻥ ﻋﻥ ﺇﺩﺍﺭﺓ ﻫﺫﻩ ﺍﻟﺸﺒﻜﺔ ﻭﺘﺄﻤﻴﻨﻬﺎ ﻭﻤﻨﺢ ﺍﻟﺼﻼﺤﻴﺎﺕ ﻟﻤﺴﺘﺨﺩﻤﻴﻬﺎ‪.‬‬

‫ﻫل ﻴﻤﻜﻥ ﺍﻻﻋﺘﻤﺎﺩ ﻋﻠﻰ ﻨﺴﺨﺔ ‪ SQL Server Express‬ﻋﻨﺩ ﺘﻭﺯﻴﻊ ﺍﻟﺒﺭﻨﺎﻤﺞ؟‬
‫‪ SQL Server Express‬ﻫﻲ ﺍﻟﻨﺴﺨﺔ ﺍﻟﻤﺠﺎﻨﻴﺔ ﻤﻥ ﺴﻴﻜﻭﻴل ﺴـﻴﺭﻓﺭ‪ ،‬ﻭﻫـﻲ ﺘﺴـﻤﺢ ﻟـﻙ‬
‫ﺒﺎﻟﺘﻌﺎﻤل ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻴﺼل ﺤﺠﻤﻬﺎ ﺍﻷﻗﺼﻰ ﺇﻟﻰ ‪ ١٠‬ﺠﻴﺠﺎ ﺒﺎﻴﺕ‪ ،‬ﻭﻋﻤﻠﻴـﺎﺕ ﻤﺘﺯﺍﻤﻨـﺔ‬
‫ﺘﺴﺘﻬﻠﻙ ‪ ١‬ﺠﻴﺠﺎ ﻤﻥ ﺍﻟﺫﺍﻜﺭﺓ ﺒﺤﺩ ﺃﻗﺼﻰ‪ ..‬ﻭﺭﻏﻡ ﺃﻥ ﻫﺫﻩ ﺍﻟﻘﻴﻭﺩ ﺘﺒﺩﻭ ﻓﻘﻴﺭﺓ ﻟﻠﻐﺎﻴﺔ ﺒﺠﻭﺍﺭ ﻤـﺎ‬
‫ﻴﻤﻜﻥ ﺃﻥ ﺘﻔﻌﻠﻪ ﺍﻟﻨﺴﺨﺔ ﺍﻟﻜﺎﻤﻠﺔ ﻤﻥ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪ ،‬ﺘﻅلّ ﺍﻟﻨﺴـﺨﺔ ﺍﻟﻤﺠﺎﻨﻴـﺔ ﻤﻨﺎﺴـﺒﺔ ﺠـﺩﺍ‬
‫ﻟﻠﺘﻁﺒﻴﻘﺎﺕ ﺍﻟﺘﻲ ﺘﻌﻤل ﻋﻠﻰ ﺠﻬﺎﺯ ﺸﺨﺼﻲ ﺃﻭ ﺸﺒﻜﺔ ﺼﻐﻴﺭﺓ ﺃﻭ ﻤﻭﻗﻊ ﺇﻨﺘﺭﻨﺕ ﺼﻐﻴﺭ‪ ،‬ﻓﻤـلﺀ‬
‫ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺒـ‪ ١٠‬ﺠﻴﺠﺎ ﻤﻥ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺃﻤﺭ ﺼﻌﺏ‪ ،‬ﻤﺎ ﻟﻡ ﺘﻜﻥ ﺘﺘﻌﺎﻤل ﻤﻊ ﺸـﺭﻜﺔ ﻋﻤﻼﻗـﺔ‬
‫ﺘﺨﺩﻡ ﺁﻻﻑ ﺍﻟﻌﻤﻼﺀ ﻴﻭﻤﻴﺎ‪ ،‬ﻭﺘﺤﻔﻅ ﺒﻌﺽ ﺍﻟﺼﻭﺭ ﻭﺍﻟﻤﻠﻔﺎﺕ ﻜﺒﻴﺭﺓ ﺍﻟﺤﺠﻡ‪.‬‬
‫ﻟﻜﻥ‪ ..‬ﻤﺎﺫﺍ ﻟﻭ ﺯﺍﺩ ﺍﻟﻀﻐﻁ ﻋﻠﻰ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻭﺍﻤﺘﻸﺕ ﻓﻌﻼ ﻭﺘﻭﻗﻑ ﺍﻟﺘﻌﺎﻤل ﻤﻌﻬـﺎ )ﻓﻠﻨﻘـل‬
‫ﺒﻌﺩ ﻋﺎﻤﻴﻥ ﺃﻭ ﺜﻼﺜﺔ ﻤﺜﻼ(؟!‬
‫ﻓﻲ ﺍﻟﺤﻘﻴﻘﺔ‪ ،‬ﺃﻨﺎ ﻟﺴﺕ ﻤﻥ ﺃﻨﺼﺎﺭ ﺘﺭﻙ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺘﺘﻀﺨﻡ ﺒﻼ ﺤﺩ ﺤﺘﻰ ﻟﻭ ﻜﻨﺎ ﻨﺘﻌﺎﻤل ﻤـﻊ‬
‫ﺍﻟﻨﺴﺨﺔ ﺍﻟﻜﺎﻤﻠﺔ ﻤﻥ ﺴﻴﻜﻭﻴل ﺴﻴﺭﻓﺭ‪ ..‬ﻓﺯﻴﺎﺩﺓ ﺤﺠﻡ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻴﺴﺒﺏ ﺴﻠﺒﻴﺎﺕ ﻜﺜﻴﺭﺓ ﻤﻨﻬﺎ‪:‬‬
‫‪ -‬ﻭﻗﺕ ﺃﻁﻭل ﻓﻲ ﺍﻟﺒﺤﺙ ﻭﺍﻟﻔﻬﺭﺴﺔ ﻭﺍﻟﺘﺤﺩﻴﺙ ﻭﺍﻟﻀﻐﻁ ﻭﺍﻹﺼﻼﺡ‪.‬‬
‫‪ -‬ﻤﺸﺎﻜل ﺃﻜﺜﺭ ﻋﻨﺩ ﺤﻔﻅ ﻨﺴﺦ ﺍﺤﺘﻴﺎﻁﻴﺔ ﻤﻥ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ‪ ،‬ﺘﺘﻌﻠﻕ ﺒﻤﺴـﺎﺤﺔ ﺍﻟﺘﺨـﺯﻴﻥ‬
‫ﻭﺒﻁﺀ ﺍﻟﻨﻘل‪.‬‬
‫‪ -‬ﻭﺠﻭﺩ ﺒﻴﺎﻨﺎﺕ ﻜﺜﻴﺭﺓ ﻗﺩﻴﻤﺔ ﻗﺩ ﺘﻜﻭﻥ ﺍﻟﺤﺎﺠﺔ ﺇﻟﻴﻬﺎ ﻗﺩ ﺍﻨﺘﻬﺕ ﺃﻭ ﻗﻠﺕ‪ ،‬ﺘﻅل ﺘﺩﺨل ﻓـﻲ‬
‫ﻋﻤﻠﻴﺎﺕ ﺤﺴﺎﺒﻴﺔ ﺃﻭ ﻨﻘﺩﻴﺔ ﺃﻭ ﺇﺤﺼﺎﺌﻴﺎﺕ ﺃﻭ ﻨﺘﺎﺌﺞ ﺒﺤﺙ ﺃﻭ ﻏﻴﺭ ﺫﻟﻙ‪.‬‬
‫‪٤٧٤‬‬
‫ﻟﻬﺫﺍ ﻴﻭﺠﺩ ﺤلّ ﻋﻤﻠﻲ ﺒﺴﻴﻁ‪ ،‬ﻴ‪‬ﺴﺘﺨﺩﻡ ﺤﺘﻰ ﺨﺎﺭﺝ ﺍﻟﻨﻅﻡ ﺍﻟﺭﻗﻤﻴﺔ ﻓـﻲ ﺃﻱ ﺘﻌـﺎﻤﻼﺕ ﻭﺭﻗﻴـﺔ‬
‫ﺤﻜﻭﻤﻴﺔ ﺃﻭ ﺘﺠﺎﺭﻴﺔ‪ ،‬ﻭﻫﻭ ﺇﻏﻼﻕ ﻗﺎﻋﺩﺓ ﺍﻟﺒﻴﺎﻨﺎﺕ ﻓﻲ ﻨﻬﺎﻴﺔ ﻜل ﻋﺎﻡ ﻭﺤﻔﻅﻬﺎ ﻜﻨﺴﺨﺔ ﺃﺭﺸـﻴﻔﻴﺔ‪،‬‬
‫ﻭﺇﻨﺸﺎﺀ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺠﺩﻴﺩﺓ ﺒﺘﺎﺭﻴﺦ ﺍﻟﺴﻨﺔ ﺍﻟﺘﺎﻟﻴﺔ‪ ..‬ﻭﻫﻜﺫﺍ ﻴﺤﺘﻔﻅ ﺍﻟﺒﺭﻨﺎﻤﺞ ﺒﻘﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﻤﻐﻠﻘﺔ‬
‫ﻟﻜل ﺴﻨﺔ ﻤﻀﺕ‪ ،‬ﻭﻴﺘﻌﺎﻤل ﻓﻘﻁ ﻤﻊ ﻗﺎﻋﺩﺓ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺴﻨﺔ ﺍﻟﺤﺎﻟﻴﺔ‪ ،‬ﻤﻊ ﺘﻘﺩﻴﻡ ﺇﻤﻜﺎﻨﻴـﺔ ﻟﻠﻤﺴـﺘﺨﺩﻡ‬
‫ﻟﻔﺘﺢ ﻗﻭﺍﻋﺩ ﺒﻴﺎﻨﺎﺕ ﺍﻟﺴﻨﻭﺍﺕ ﺍﻟﻤﺎﻀﻴﺔ ﻟﻠﺒﺤﺙ ﻓﻴﻬﺎ )ﺒﺼﻼﺤﻴﺔ ﻤﺴﺘﺨﺩﻡ( ﺃﻭ ﺘﻌﺩﻴﻠﻬﺎ )ﺒﺼـﻼﺤﻴﺔ‬
‫ﺍﻟﻤﺩﻴﺭ(‪.‬‬

‫ﻨﺼﻴﺤﺔ‪ :‬ﻓﻲ ﺍﻟﻨﻅﻡ ﺍﻟﺘﻲ ﻴﻤﻠﻙ ﻓﻴﻬﺎ ﺃﻜﺜﺭ ﻤﻥ ﻤﺴﺘﺨﺩﻡ ﺼﻼﺤﻴﺔ ﺍﻟﺘﻌﺩﻴل‪ ،‬ﺍﺤﻔﻅ ﻓﻲ ﻜل ﺴـﺠل‪،‬‬
‫ﺒﻴﺎﻨﺎﺕ ﺍﻟﻤﺴﺘﺨﺩﻡ ﺍﻟﺫﻱ ﺃﺩﺨﻠﻪ ﻭﺁﺨﺭ ﻤﺴﺘﺨﺩﻡ ﻋﺩ‪‬ﻟﻪ‪ ،‬ﻟﻤﻨﻊ ﺃﻱ ﺘﻼﻋﺒﺎﺕ‪.‬‬

‫ﻁﹼﻁﺕ ﺒﺭﻨﺎﻤﺠﻙ ﺒﻬﺫﻩ ﺍﻟﻁﺭﻴﻘـﺔ‪ ،‬ﻓﺴـﺘﻜﻭﻥ ﻨﺴـﺨﺔ ‪ SQL Server Express‬ﻜﺎﻓﻴـﺔ‬


‫ﻟﻭ ﺨ ﹼ‬
‫ﻟﻸﻋﻤﺎل ﺍﻟﺼﻐﻴﺭﺓ ﻭﺍﻟﻤﺘﻭﺴﻁﺔ ﺍﻟﺘﻲ ﻻ ﻴﺯﻴﺩ ﺤﺠﻡ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺍﻟﺫﻱ ﺘﺤﻔﻅﻪ ﺴﻨﻭﻴﺎ ﻋﻥ ‪ ١٠‬ﺠﻴﺠـﺎ‬
‫ﺒﺎﻴﺕ‪.‬‬
‫ﺃﻤﺎ ﺍﻟﺸﺭﻜﺎﺕ ﺍﻟﻜﺒﻴﺭﺓ ﺍﻟﺘﻲ ﺘﺤﺘﺎﺝ ﺃﻜﺜﺭ ﻤﻥ ﻫﺫﺍ‪ ،‬ﻓﻼ ﺃﻅﻥ ﺃﻨﻬﺎ ﺴﺘﻤﺎﻨﻊ ﺸﺭﺍﺀ ﻨﺴـﺨﺔ ﺴـﻴﻜﻭﻴل‬
‫ﺴﻴﺭﻓﺭ ﻤﺭﺨﺼﺔ ﻜﺎﻤﻠﺔ ﺍﻹﻤﻜﺎﻨﻴﺎﺕ ‪J‬‬

‫ﻭﻴﻤﻜﻨﻙ ﺘﺤﻤﻴل ﻨﺴﺨﺔ ‪ Microsoft® SQL Server® Express‬ﻤﻥ ﻤﻭﻗﻊ ﻤﻴﻜﺭﻭﺴـﻭﻓﺕ‬


‫)ﻋﻠﻰ ﺤﺴﺏ ﺍﻹﺼﺩﺍﺭ ﺍﻟﺫﻱ ﺘﺭﻴﺩﻩ(‪ ،‬ﻜﻤﺎ ﺃﻭﻀﺤﻨﺎ ﻓﻲ ﺒﺩﺍﻴﺔ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ‪.‬‬

‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ‬

‫‪٤٧٥‬‬
‫ﻤﻠﺤﻭﻅﺔ‪:‬‬
‫ﺘﻡ ﻨﺸﺭ ﺍﻟﻔﺼﻭل ‪ ١٥‬ﻭ ‪ ١٦‬ﻭ ‪ ١٧‬ﻭﺍﻟﻤﻠﺤﻕ ‪ ١‬ﻓﻲ ﻜﺘﺎﺏ ﻤﺴﺘﻘل ﺒﻌﻨﺎﻭﺍﻥ‪:‬‬
‫ﺠﺩﻭل ﻋﺭﺽ ﺍﻟﺒﻴﺎﻨﺎﺕ ‪DataGridView‬‬
‫ﻴﻤﻜﻨﻡ ﺘﺤﻤﻴﻠﻪ ﻤﻥ ﻫﻨﺎ‪:‬‬
‫‪https://drive.google.com/file/d/1tm27L0yGX1RA__vxXng0t5ny3XuJUFv_/view?fbclid=IwAR0IM7zdX9dqkWPXttQyQU6s-‬‬
‫‪FPna2m0hshLq9itiog9SS6PISFdes7Gm_0‬‬

‫ﺃﺨﻲ ﺍﻟﻔﺎﻀل‪:‬‬
‫ﺇﺫﺍ ﻜﻨﺕ ﻗﺩ ﺍﺴﺘﻔﺩﺕ ﺒﺒﻌﺽ ﺃﻭ ﻜل ﻤﺎ ﻗﺭﺃﺘﻪ ﻓﻲ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ‪ ،‬ﻓﻼ ﺘﻨﺴﻨﻲ ﻤﻥ ﺩﻋﺎﺌﻙ ﺒﺎﻟﻬﺩﺍﻴﺔ‬
‫ﻭﺍﻟﺘﻭﻓﻴﻕ ﻭﺍﻟﺴﺩﺍﺩ ﻭﺍﻟﺭﺯﻕ ﻭﺤﺴﻥ ﺍﻟﺨﺎﺘﻤﺔ‪.‬‬
‫ﻭﺍﺩﻉ ﻷﺒﻲ ﺭﺤﻤﻪ ﺍﷲ ﺒﺎﻟﺭﺤﻤﺔ‪ ،‬ﻓﻘﺩ ﻨﺸﺭﺕ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ﻤﺠﺎﻨﺎ ﻜﺼﺩﻗﺔ ﺠﺎﺭﻴﺔ ﻟﻪ‪ ..‬ﻭﺇﺫﺍ ﻜﻨﺕ‬
‫ﻓﻲ ﺍﻟﺤﺭﻡ ﺃﻭ ﻋﻠﻰ ﻤﻘﺭﺒﺔ ﻤﻨﻪ‪ ،‬ﻓﻼ ﺘﺒﺨل ﺒﻌﻤل ﻋﻤﺭﺓ ﺴﺭﻴﻌﺔ ﻟﻪ ﻭﺍﻟﺩﻋﺎﺀ ﻟﻪ ﻓﻲ ﺍﻟﺤﺭﻡ‬
‫ﺍﻟﻤﻜﻲ ﻭﺍﻟﺤﺭﻡ ﺍﻟﻨﺒﻭﻱ ﺍﻟﺸﺭﻴﻔﻴﻥ‪.‬‬

‫‪٤٧٦‬‬

You might also like