0% found this document useful (0 votes)
37 views476 pages

C# Ado - Net 2010

Uploaded by

aaltnazfti
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
37 views476 pages

C# Ado - Net 2010

Uploaded by

aaltnazfti
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 476

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

‫‪ADO.NET‬‬
‫ﻟﻤﺒﺭﻤﺠﻲ ﺴﻲ ﺸﺎﺭﺏ ‪٢٠١٠‬‬

‫ﺒﻘﻠﻡ‪:‬‬

‫ﻡ‪ .‬ﻤﺤﻤﺩ ﺤﻤﺩﻱ ﻏﺎﻨﻡ‬

‫ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ﺼﺩﻗﺔ ﺠﺎﺭﻴﺔ ﻋﻠﻰ ﺭﻭﺡ ﻭﺍﻟﺩﻱ‪:‬‬


‫ﺃ‪ .‬ﺤﻤﺩﻱ ﻜﺎﻤل ﺍﻟﺤﺩﻴﺩﻱ ﻏﺎﻨﻡ‬
‫ﺭﺤﻤﻪ ﺍﷲ ﻭﻏﻔﺭ ﻟﻪ ﻭﺠﻌل ﻤﺜﻭﺍﻩ ﺍﻟﺠﻨﺔ‬

‫‪٢‬‬
‫ﻟﻬﺫﺍ ﺃﺭﺠﻭ ﻤﻥ ﻜل ﻤﻥ ﻴﺴﺘﻔﻴﺩ ﺒﻪ ﺃﻥ ﻴﺘﺫﻜﺭ ﺃﻥ ﺃﺒﻲ ﻫﻭ ﺍﻟـﺫﻱ ﺭﺒـﺎﻨﻲ ﻭﻋﻠﻤﻨـﻲ‬
‫ﻭﻟﻭﻻﻩ ﺒﻌﺩ ﺘﻭﻓﻴﻕ ﺍﷲ ﻤﺎ ﺨﺭﺝ ﺇﻟﻰ ﺍﻟﻭﺠﻭﺩ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ﻭﻏﻴﺭﻩ ﻤﻥ ﺍﻟﻜﺘﺏ‪.‬‬
‫ﻓﺎﺩﻋﻭﺍ ﻟﻪ ﺒﺎﻟﺭﺤﻤﺔ ﻭﺍﻟﻤﻐﻔﺭﺓ‬
‫ﻭﻤﻥ ﻜﺎﻥ ﻤﻨﻜﻡ ﻓﻲ ﺍﻟﺤﺭﻤﻴﻥ ﺍﻟﺸﺭﻴﻔﻴﻥ ﻭﻜﺎﻥ ﻗﺎﺩﺭﺍ ﻋﻠﻰ ﻋﻤل ﻋﻤﺭﺓ ﻟﻪ‪ ،‬ﻓﺠﺯﺍﻩ‬
‫ﺍﷲ ﺨﻴﺭﺍ‪.‬‬

‫ﺃﺩﻋﻭ ﺍﷲ ﺃﻥ ﻴﻜﻭﻥ ﻫﺫﺍ ﺍﻟﻜﺘﺎﺏ ﻭﺒﺎﻗﻲ ﻜﺘﺒﻲ ﻤﻥ ﺍﻟﻌﻠﻡ ﺍﻟﺫﻱ ﻴﻨﺘﻔﻊ ﺒﻪ‪ ،‬ﻭﺃﻥ ﻴﺠﻌل‬
‫ﺍﷲ ﻷﺒﻲ ﻨﺼﻴﺒﺎ ﻤﻥ ﺜﻭﺍﺒﻪ‪ ،‬ﻓﻴﻜﻭﻥ ﻤﻥ ﻋﻤﻠﻪ ﺍﻟﺫﻱ ﻻ ﻴﻨﻘﻁﻊ ﺒﻤﻭﺘﻪ‪.‬‬

‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﺃﺒﻲ ﻭﺍﻏﻔﺭ ﻟﻪ ﻭﻜﻔﺭ ﻋﻨﻪ ﺴﻴﺌﺎﺘﻪ ﻭﻗﻪ ﻤﻥ ﻋﺫﺍﺏ ﺍﻟﻘﺒﺭ ﻭﻗﻪ ﻤﻥ ﻋﺫﺍﺏ ﺍﻟﻨﺎﺭ‪،‬‬
‫ﻭﺃﺩﺨﻠﻪ ﺍﻟﺠﻨﺔ ﻭﺃﻋلِ ﻤﻨﺯﻟﺘﻪ ﻓﻴﻬﺎ‬
‫ﻭﺍﺤﻔﻅ ﻭﺍﻟﺩﺘﻲ ﻭﺒﺎﺭﻙ ﻓﻲ ﻋﻤﺭﻫﺎ‬
‫ﺍﻟﻠﻬﻡ ﺍﺭﺤﻡ ﻭﺍﻟﺩﻱ‪ ‬ﻜﻤﺎ ﺭﺒﻴﺎﻨﻲ ﺼﻐﻴﺭﺍ‬
‫ﺁﻤﻴﻥ ﻴﺎ ﺭﺏ ﺍﻟﻌﺎﻟﻤﻴﻥ‬

‫‪٣‬‬
‫ﻟﻠﺘﻭﺍﺼل ﻤﻊ ﺍﻟﻜﺎﺘﺏ‪:‬‬
‫‪ -‬ﺒﺭﻴﺩﻱ ﺍﻻﻟﻜﺘﺭﻭﻨﻲ‪:‬‬
‫‪msvbnet@hotmail.com‬‬
‫‪ -‬ﻤﺩﻭﻨﺘﻲ‪:‬‬
‫‪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‬‬
‫ﻟﺸﺭﺍﺀ ﻫﺫﻩ ﺍﻟﻜﺘﺏ‪ ،‬ﻴﺘﻡ ﺘﺤﻭﻴل ﺍﻟﺜﻤﻥ ﺒﺤﻭﺍﻟﺔ ﺒﺭﻴﺩﻴﺔ ﺩﺍﺨل ﻤﺼﺭ‪ ،‬ﺃﻭ ﺒﻭﻴﺴﺘﺭﻥ ﻴﻭﻨﻴﻭﻥ ﻤﻥ‬
‫ﺨﺎﺭﺝ ﻤﺼﺭ‪ ،‬ﻭﻴﺘﻡ ﺇﺭﺴﺎل ﺍﻟﻜﺘﺏ ﺒﻁﺭﺩ ﺒﺎﻟﺒﺭﻴﺩ ﺍﻟﺴﺭﻴﻊ‪ ..‬ﻟﻤﺯﻴﺩ ﻤﻥ ﺍﻟﺘﻔﺎﺼﻴل ﺃﺭﺴل ﺭﺴﺎﻟﺔ‬
‫ﺒﺎﻟﻜﺘﺏ ﺍﻟﻤﻁﻠﻭﺒﺔ ﺇﻟﻰ‪:‬‬
‫‪msvbnet@hotmail.com‬‬

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


‫‪ -‬ﺒﺭﻤﺠﺔ ﻗﻭﺍﻋﺩ ﺍﻟﺒﻴﺎﻨﺎﺕ ﺒـ ‪.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

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy