- https://blog.greglow.com/2018/02/12/sql-design-entity-attribute-value-tables-part-1/
- https://blog.greglow.com/2018/02/19/sql-design-entity-attribute-value-tables-part-2-pros-cons/
- https://ekobit.com/blog/designing-an-entity-attribute-value-model-with-ef-core/
- https://sqlblog.org/2009/11/19/what-is-so-bad-about-eav-anyway
- https://modern-sql.com/use-case/pivot
- https://mariadb.com/kb/en/entity-attribute-value-implementation/
- https://vladmihalcea.com/how-to-store-schema-less-eav-entity-attribute-value-data-using-json-and-hibernate/
- https://www.2ndquadrant.com/en/blog/postgresql-anti-patterns-unnecessary-jsonhstore-dynamic-columns/
- https://coussej.github.io/2016/01/14/Replacing-EAV-with-JSONB-in-PostgreSQL/
- https://davidlaprade.github.io/storing-arbitrary-model-attributes
- https://inviqa.com/blog/understanding-eav-data-model-and-when-use-it
- https://mikesmithers.wordpress.com/2013/12/22/the-anti-pattern-eavil-database-design/
- http://duhallowgreygeek.com/entity-attribute-value-eav-model-sql-smell/
تفکر درباره mvc 5,mvc 6,mvc flash 3
Over five years ago I released MvcFlash, and soon after I released the second iteration MvcFlash2. Don't let the terrible naming and versioning fool you, this is one of my favorite creations. As ASP.NET 5 starts to take root and MVC 6 blossoms, I begin to feel more confident about creating the next version of MvcFlash. Honestly, I can't see developing an MVC application without it. Before I do, I thought I would muse about the possible challenges ahead.
So when Microsoft acquired Xamarin in 2016 and started integrating the Xamarin Visual Studio plugins more with the standard VS features, I knew I had to try and switch over to take advantage of the powerful IDE and language. Some of the immediate benefits I gained from the switch are:
- Simple asynchronous programming
- Access to powerful plugins like ReSharper
- Freedom to work in Windows or OSX
- Access to powerful debugging tools for the Android on Windows (debugging iOS on Mac side is good but can be buggy).
- Access to built in NuGet package management for third party libraries
OpenCVSharp #18
این مطلب را میتوان به عنوان جمع بندی مطالبی که تاکنون بررسی شدند درنظر گرفت و در اساس مطلب جدیدی ندارد و صرفا ترکیب یک سری تکنیک است؛ برای مثال:
چطور یک تصویر را به نمونهی سیاه و سفید آن تبدیل کنیم؟
کار با متد Threshold جهت بهبود کیفیت یک تصویر جهت تشخیص اشیاء
تشخیص کانتورها (Contours) و اشیاء موجود در یک تصویر
آشنایی با نحوهی گروه بندی تصاویر مشابه و مفاهیمی مانند برچسبهای تصاویر که بیانگر یک گروه از تصاویر هستند.
تهیه تصاویر اعداد انگلیسی جهت آموزش دادن به الگوریتم CvKNearest
در اینجا نیز از یکی دیگر از الگوریتمهای machine learning موجود در OpenCV به نام CvKNearest برای تشخیص اعداد انگلیسی استفاده خواهیم کرد. این الگوریتم نزدیکترین همسایهی اطلاعاتی مفروض را در گروهی از دادههای آموزش داده شدهی به آن پیدا میکند. خروجی آن شمارهی این گروه است. بنابراین نحوهی طبقهی بندی اطلاعات در اینجا چیزی شبیه به شکل زیر خواهد بود:
مجموعهای از تصاویر 0 تا 9 را جمع آوری کردهایم. هر کدام از پوشهها، بیانگر اعدادی از یک خانواده هستند. این تصویر را با فرمت ذیل جمع آوری میکنیم:
public class ImageInfo { public Mat Image { set; get; } public int ImageGroupId { set; get; } public int ImageId { set; get; } }
public IList<ImageInfo> ReadTrainingImages(string path, string ext) { var images = new List<ImageInfo>(); var imageId = 1; foreach (var dir in new DirectoryInfo(path).GetDirectories()) { var groupId = int.Parse(dir.Name); foreach (var imageFile in dir.GetFiles(ext)) { var image = processTrainingImage(new Mat(imageFile.FullName, LoadMode.GrayScale)); if (image == null) { continue; } images.Add(new ImageInfo { Image = image, ImageId = imageId++, ImageGroupId = groupId }); } } return images; }
private static Mat processTrainingImage(Mat gray) { var threshImage = new Mat(); Cv2.Threshold(gray, threshImage, Thresh, ThresholdMaxVal, ThresholdType.BinaryInv); // Threshold to find contour Point[][] contours; HiearchyIndex[] hierarchyIndexes; Cv2.FindContours( threshImage, out contours, out hierarchyIndexes, mode: ContourRetrieval.CComp, method: ContourChain.ApproxSimple); if (contours.Length == 0) { return null; } Mat result = null; var contourIndex = 0; while ((contourIndex >= 0)) { var contour = contours[contourIndex]; var boundingRect = Cv2.BoundingRect(contour); //Find bounding rect for each contour var roi = new Mat(threshImage, boundingRect); //Crop the image //Cv2.ImShow("src", gray); //Cv2.ImShow("roi", roi); //Cv.WaitKey(0); var resizedImage = new Mat(); var resizedImageFloat = new Mat(); Cv2.Resize(roi, resizedImage, new Size(10, 10)); //resize to 10X10 resizedImage.ConvertTo(resizedImageFloat, MatType.CV_32FC1); //convert to float result = resizedImageFloat.Reshape(1, 1); contourIndex = hierarchyIndexes[contourIndex].Next; } return result; }
ابتدا تصویر اصلی بارگذاری میشود؛ همان تصویر سمت چپ. سپس با استفاده از متد Threshold، شدت نور نواحی مختلف آن یکسان شده و آماده میشود برای تشخیص کانتورهای موجود در آن. در ادامه با استفاده از متد FindContours، شیء مرتبط با عدد جاری یافت میشود. سپس متد Cv2.BoundingRect مستطیل دربرگیرندهی این شیء را تشخیص میدهد (تصویر سمت راست). بر این اساس میتوان تصویر اصلی ورودی را به یک تصویر کوچکتر که صرفا شامل ناحیهی عدد مدنظر است، تبدیل کرد. در ادامه برای کار با الگوریتم CvKNearest نیاز است تا این تصویر بهبود یافته را تبدیل به یک ماتریس یک بعدی کردی که روش انجام کار توسط متد Reshape مشاهده میکنید.
از همین روش پردازش و بهبود تصویر ورودی، جهت پردازش اعداد یافت شدهی در یک تصویر با تعداد زیادی عدد نیز استفاده خواهیم کرد.
آموزش دادن به الگوریتم CvKNearest
تا اینجا تصاویر گروه بندی شدهای را خوانده و لیستی از آنها را مطابق فرمت الگوریتم CvKNearest تهیه کردیم. مرحلهی بعد، معرفی این لیست به متد Train این الگوریتم است:
public CvKNearest TrainData(IList<ImageInfo> trainingImages) { var samples = new Mat(); foreach (var trainingImage in trainingImages) { samples.PushBack(trainingImage.Image); } var labels = trainingImages.Select(x => x.ImageGroupId).ToArray(); var responses = new Mat(labels.Length, 1, MatType.CV_32SC1, labels); var tmp = responses.Reshape(1, 1); //make continuous var responseFloat = new Mat(); tmp.ConvertTo(responseFloat, MatType.CV_32FC1); // Convert to float var kNearest = new CvKNearest(); kNearest.Train(samples, responseFloat); // Train with sample and responses return kNearest; }
سپس نیاز است لیست گروههای متناظر با تصاویر اعداد را تبدیل به فرمت مورد انتظار متد Train کنیم. در اینجا صرفا لیستی از اعداد صحیح را داریم. این لیست نیز باید تبدیل به یک Mat شود که روش انجام آن در متد فوق بیان شدهاست. کلاس Mat سازندهی مخصوصی را جهت تبدیل لیست اعداد، به همراه دارد. این Mat نیز باید تبدیل به یک ماتریس یک بعدی شود که برای این منظور از متد Reshape استفاده شدهاست.
انجام عملیات OCR نهایی
پس از تهیهی لیستی از تصاویر و آموزش دادن آنها به الگوریتم CvKNearest، تنها کاری که باید انجام دهیم، یافتن اعداد در تصویر نمونهی مدنظر و سپس معرفی آن به متد FindNearest الگوریتم CvKNearest است. روش انجام اینکار بسیار شبیه است به روش معرفی شده در متد processTrainingImage که پیشتر بررسی شد:
public void DoOCR(CvKNearest kNearest, string path) { var src = Cv2.ImRead(path); Cv2.ImShow("Source", src); var gray = new Mat(); Cv2.CvtColor(src, gray, ColorConversion.BgrToGray); var threshImage = new Mat(); Cv2.Threshold(gray, threshImage, Thresh, ThresholdMaxVal, ThresholdType.BinaryInv); // Threshold to find contour Point[][] contours; HiearchyIndex[] hierarchyIndexes; Cv2.FindContours( threshImage, out contours, out hierarchyIndexes, mode: ContourRetrieval.CComp, method: ContourChain.ApproxSimple); if (contours.Length == 0) { throw new NotSupportedException("Couldn't find any object in the image."); } //Create input sample by contour finding and cropping var dst = new Mat(src.Rows, src.Cols, MatType.CV_8UC3, Scalar.All(0)); var contourIndex = 0; while ((contourIndex >= 0)) { var contour = contours[contourIndex]; var boundingRect = Cv2.BoundingRect(contour); //Find bounding rect for each contour Cv2.Rectangle(src, new Point(boundingRect.X, boundingRect.Y), new Point(boundingRect.X + boundingRect.Width, boundingRect.Y + boundingRect.Height), new Scalar(0, 0, 255), 2); var roi = new Mat(threshImage, boundingRect); //Crop the image var resizedImage = new Mat(); var resizedImageFloat = new Mat(); Cv2.Resize(roi, resizedImage, new Size(10, 10)); //resize to 10X10 resizedImage.ConvertTo(resizedImageFloat, MatType.CV_32FC1); //convert to float var result = resizedImageFloat.Reshape(1, 1); var results = new Mat(); var neighborResponses = new Mat(); var dists = new Mat(); var detectedClass = (int)kNearest.FindNearest(result, 1, results, neighborResponses, dists); //Console.WriteLine("DetectedClass: {0}", detectedClass); //Cv2.ImShow("roi", roi); //Cv.WaitKey(0); //Cv2.ImWrite(string.Format("det_{0}_{1}.png",detectedClass, contourIndex), roi); Cv2.PutText( dst, detectedClass.ToString(CultureInfo.InvariantCulture), new Point(boundingRect.X, boundingRect.Y + boundingRect.Height), 0, 1, new Scalar(0, 255, 0), 2); contourIndex = hierarchyIndexes[contourIndex].Next; } Cv2.ImShow("Segmented Source", src); Cv2.ImShow("Detected", dst); Cv2.ImWrite("dest.jpg", dst); Cv2.WaitKey(); }
ابتدا تصویر اصلی که قرار است عملیات OCR روی آن صورت گیرد، بارگذاری میشود. سپس کانتورها و اعداد موجود در آن تشخیص داده میشوند. مستطیلهای قرمز رنگ در برگیرندهی این اعداد را در تصویر دوم مشاهده میکنید. سپس این کانتورهای یافت شده را که شامل یکی از اعداد تشخیص داده شدهاست، تبدیل به یک ماتریس یک بعدی کرده و به متد FindNearest ارسال میکنیم. خروجی آن نام گروه یا پوشهای است که این عدد در آن قرار دارد. در همینجا این خروجی را تبدیل به یک رشته کرده و در تصویر سوم با رنگ سبز رنگ نمایش میدهیم.
بنابراین در این تصویر، پنجرهی segmented image، همان اشیاء تشخیص داده شدهی از تصویر اصلی هستند.
پنجرهی با زمینهی سیاه رنگ، نتیجهی نهایی OCR است که نسبتا هم دقیق عمل کردهاست.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید.
Here are some additional changes in SQL Server 2016.
Feature | SQL 2014 | SQL 2016 |
---|---|---|
Foreign Keys | Not supported | Supported |
Check/Unique Constraints | Not supported | Supported |
Parallelism | Not supported | Supported |
Indexes on NULLable columns | Not supported | Supported |
Maximum size of durable table | 256 GB | 2 TB |
ALTER PROCEDURE / sp_recompile | Not supported | Supported |
SSMS Table Designer | Not supported | Supported |
Check/Unique Constraints | Not supported | Supported |
ابزار Memory Optimization Advisor
Memory Optimization Advisor یک Wizard مانند است که از آن برای گرفتن مشاوره در مورد تبدیل جداول موجود مبتنی بر دیسک سخت، به نمونههای بهینه سازی شده برای حافظه میتوان استفاده کرد. کار آن بررسی ساختار جداولی است که قصد مهاجرت آنها را دارید. برای مثال همانطور که پیشتر نیز عنوان شد، جداول بهینه سازی شده برای حافظه محدودیتهایی دارند؛ مثلا نباید کلید خارجی داشته باشند. این Wizard یک چنین مواردی را آنالیز کرده و گزارشی را ارائه میدهد. پس از اینکه مراحل آنرا به پایان رساندید و مشکلاتی را که گزارش میدهد، برطرف نمودید، کد تبدیل جدول را نیز به صورت خودکار تولید میکند.
برای دسترسی به آن، فقط کافی است بر روی نام جدول خود کلیک راست کرده و گزینهی memory optimization advisor را انتخاب کنید.
در دو قسمت اول این Wizard، کار بررسی ساختار جدول در حال مهاجرت صورت میگیرد. اگر نوع دادهای در آن پشتیبانی نشود یا قیود ویژهای در آن تعریف شده باشند، گزارشی را جهت رفع، دریافت خواهید کرد. پس از رفع آن، به صفحهی گزینههای مهاجرت میرسیم:
همانطور که ملاحظه میکنید، گروه فایل ایجاد شده در قسمت قبل، به صورت خودکار انتخاب شدهاست.
در ادامه میتوان نام دیگری را برای جدول مبتنی بر دیسک وارد کرد. در اینجا به صورت خودکار کلمهی old به آخر نام جدول اضافه شدهاست. در حین تولید جدول جدید بهینه سازی شدهی بر اساس ساختار جدول فعلی، این جدول قدیمی به صورت خودکار تغییر نام خواهد یافت و کلیه اطلاعات آن حفظ میشود.
همچنین تخمینی را نیز از مقدار حافظهی مورد نیاز برای نگهداری این جدول جدید درون حافظهای نیز ارائه میدهد. در این مثال چون رکوردی در جدول انتخابی وجود نداشتهاست، تخمین آن صفر است. عدد ارائه شده توسط آن بسیار مهم است و باید به همین میزان برای سیستم خود حافظه تهیه نمائید و یا از حافظهی موجود استفاده کنید.
در پایین صفحه میتوان انتخاب کرد که آیا دادههای جدول فعلی، به جدول درون حافظهای انتقال یابند یا خیر. به علاوه نوع ماندگاری اطلاعات آن نیز قابل تنظیم است. اگر گزینهی آخر را انتخاب کنید به معنای حالت SCHEMA_ONLY است. حالت پیش فرض آن SCHEMA_AND_DATA میباشد که در قسمتهای قبل بیشتر در مورد آن بحث شد.
در دو صفحهی بعد، کار انتخاب hash index و range index انجام میشود:
در اینجا hash index بر روی فیلد ID تولید شدهاست، به همراه تعیین bucket count آن و در صفحهی بعدی range index بر روی فیلد تاریخ تعریف گردیدهاست:
در آخر میتوان با کلیک بر روی دکمهی Script، صرفا دستورات T-SQL تغییر ساختار جدول را دریافت کرد و یا با کلیک بر روی دکمهی migrate به صورت خودکار کلیه موارد تنظیم شده را اجرا نمود.
خلاصهی این مراحل که توسط دکمهی Script آن تولید میشود، به صورت زیر است:
USE [testdb2] GO EXEC dbo.sp_rename @objname = N'[dbo].[tblNormal]', @newname = N'tblNormal_old', @objtype = N'OBJECT' GO USE [testdb2] GO SET ANSI_NULLS ON GO CREATE TABLE [dbo].[tblNormal] ( [CustomerID] [int] NOT NULL, [Name] [nvarchar](250) COLLATE Persian_100_CI_AI NOT NULL, [CustomerSince] [datetime] NOT NULL, INDEX [ICustomerSince] NONCLUSTERED ( [CustomerSince] ASC ), CONSTRAINT [tblNormal_primaryKey] PRIMARY KEY NONCLUSTERED HASH ( [CustomerID] )WITH ( BUCKET_COUNT = 131072) )WITH ( MEMORY_OPTIMIZED = ON , DURABILITY = SCHEMA_AND_DATA ) GO INSERT INTO [testdb2].[dbo].[tblNormal] ([CustomerID], [Name], [CustomerSince]) SELECT [CustomerID], [Name], [CustomerSince] FROM [testdb2].[dbo].[tblNormal_old] GO
علاوه بر memory optimization advisor مخصوص جداول، ابزار دیگری نیز به نام Native compilation advisor برای آنالیز رویههای ذخیره شده تهیه شدهاست:
آیا سیستم فعلی ما واقعا نیازی به ارتقاء به جداول درون حافظهای دارد؟
تا اینجا در مورد نحوهی ایجاد جداول درون حافظهای و یا نحوهی تبدیل جداول موجود را به ساختار جدید بررسی کردیم. ولی آیا واقعا یک چنین تغییراتی برای ما سودمند هستند؟ برای پاسخ دادن به این سؤال ابزاری به نام AMR به management studio 2014 اضافه شدهاست (Analyze, Migrate, Report). کار آن تحت نظر قرار دادن جداول و رویههای ذخیره شدهی بانک اطلاعاتی است و سپس بر اساس بار سیستم، تعداد درخواستهای همزمان و میزان استفاده از جداول و تراکنشهای مرتبط با آنها، گزارشی را ارائه میدهد. بر این اساس بهتر میتوان تصمیم گرفت که کدام جداول بهتر است به جداول درون حافظهای تبدیل شوند.
برای تنظیم آن باید مراحل ذیل طی شوند:
در Management Studio، به برگهی Object Explorer آن مراجعه کنید. سپس پوشهی Management آنرا یافته و بر روی گزینهی Data Collection کلیک راست نمائید:
در اینجا گزینهی Configure Management Data Warehouse را انتخاب نمائید. در صفحهی باز شده، ابتدا بانک اطلاعاتی مدنظر را انتخاب نمائید. همچنین بهتر است بر روی دکمهی new کلیک کرده و یک بانک اطلاعاتی جدید را برای آن ایجاد نمائید، تا دچار تداخل اطلاعاتی و ساختاری نگردد:
در ادامه نام کاربری را که قرار است کار مدیریت ثبت و جمع آوری اطلاعات را انجام دهد، به همراه نقشهای آن انتخاب نمائید:
و در آخر در صفحهی بعدی بر روی دکمهی Finish کلیک کنید.
پس از ایجاد و انتخاب بانک اطلاعاتی Management Data Warehouse، نوبت به تنظیم گزینههای جمع آوری اطلاعات است:
در اینجا ابتدا سرور جاری را انتخاب کنید. پس از آن به صورت خودکار در لیست بانکهای اطلاعاتی قابل انتخاب، تنها همان بانک اطلاعاتی جدیدی را که برای مرحلهی قبل ایجاد کردیم، میتوان مشاهده کرد.
در صفحهی بعد، گزینهی «Transaction Performance Collection Sets» را انتخاب نمائید که دقیقا گزینهی مدنظر ما جهت یافتن آماری از وضعیت تراکنشهای سیستم است.
در ادامه بر روی گزینههای next و finish کلیک کنید تا کار تنظیمات به پایان برسد.
اکنون اگر به لیست وظایف تعریف شده در SQL Server agent مراجعه کنید، میتوانید، وظایف مرتبط با جمع آوری دادهها را نیز مشاهده نمائید:
وظایف Stored Procedure Usage Analysis هر نیم ساعت یکبار و وظایف Table Usage Analysis هر 15 دقیقه یکبار اجرا میشوند. البته امکان اجرای دستی این وظایف نیز مانند سایر وظایف SQL Server وجود دارند.
همچنین در پوشهی management، گزینهی Data collection نیز دو زیر شاخه اضافه شدهاند که نمایانگر آنالیز میزان مصرف جداول و رویههای ذخیره شده میباشند:
پس از این کارها باید مدتی صبر کنید (مثلا یک ساعت) تا سیستم به صورت معمول کارهای متداول خودش را انجام دهد. پس از آن میتوان به گزارشات AMR مراجعه کرد.
برای اینکار بر روی بانک اطلاعاتی Management Data Warehouse که در ابتدای عملیات ایجاد شد، کلیک راست نمائید و سپس مراحل ذیل را طی کنید:
Reports > Management Data Warehouse > Transaction Performance Analysis Overview
در گزارش ایجاد شده، ذیل گزینهی usage analysis لینکهایی وجود دارند که با مراجعه به آنها، چارتهایی از میزان مصرف بانکهای اطلاعاتی مختلف سیستم ارائه میشود. اگر پیام No data available را مشاهده کردید، یعنی هنوز باید مقداری صبر کنید تا کار جمع آوری اطلاعات به پایان برسد.
در این چارتها بانکهای اطلاعاتی که در سمت راست، بالای تصویر قرار میگیرند، انتخاب مناسبی برای تبدیل به بانکهای اطلاعاتی درون حافظهای هستند. محور افقی آن از چپ به راست بیانگر میزان کاهش سختی انتقال یک جدول به جدول درون حافظهای است (با درنظر گرفتن تمام مسایلی که باید تغییر کنند یا نوعهای دادهای که باید اصلاح شوند) و محور عمودی آن نمایانگر میزان بالا رفتن پاسخ دهی سیستم در جهت انجام کار بیشتر است.
هر زمان هم که کار تصمیمگیری شما به پایان رسید، میتوانید بر روی گزینهی Data collection کلیک راست کرده و آنرا غیرفعال نمائید.
برای مطالعه بیشتر
SQL Server 2014 Field Benchmarking In-Memory OLTP and Buffer Pool Extension Features
New AMR Tool: Simplifying the Migration to In-Memory OLTP
A Tour of the Hekaton AMR Tool
SQL Server 2014 Memory Optimization Advisor
Getting started with the AMR tool for migration to SQL Server In-memory OLTP Tables
How to Use Microsoft's AMR Tool
SQL Server 2014's Analysis, Migrate, and Report Tool
<services> <service name="MyNewsWCFLibrary.MyNewsService"> <host> <baseAddresses> <add baseAddress="http://localhost:8080/MyNewsWCFLibrary/MyNewsService/" /> </baseAddresses> </host> <!-- Service Endpoints --> <!-- Unless fully qualified, address is relative to base address supplied above --> <endpoint address="" binding="basicHttpBinding" contract="MyNewsWCFLibrary.IMyNewsService"> <!-- Upon deployment, the following identity element should be removed or replaced to reflect the identity under which the deployed service runs. If removed, WCF will infer an appropriate identity automatically. --> <identity> <dns value="localhost" /> </identity> </endpoint> <!-- Metadata Endpoints --> <!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. --> <!-- This endpoint does not use a secure binding and should be secured or removed before deployment --> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services>
در صورت مشاهده پیام خطا، ویژوال استودیو را ببندید و اینبار به صورت Run as administrator باز کنید.
برای نمونه روی متد AddCategory کلیک کنید. در پنجره نشان داده شده همانند شکل در برابر فیلد CatName مقداری وارد کنید و روی دکمه Invoke کلیک کنید. متد مورد نظر اجرا شده و مقداری که وارد کرده ایم در پایگاه دادهها ذخیره میشود. مقداری که در قسمت پایین دیده میشود خروجی متد است که در اینجا شناسه رکورد درجشده است.
بار دیگر برای مشاهده رکورد درجشده روی متد GetAllCategory کلیک کنید. به علت اینکه این متد ورودی ندارد در قسمت بالا چیزی نشان داده نمیشود. روی دکمه Invoke کلیک کنید. با پیغام خطای زیر روبهرو خواهید شد:
افزودن ویژگی Virtual به tblNews و tblCategory در بخش دوم خواندید؛ باعث میشود که Entity Framework در هنگام اجرا کلاسهایی با عنوان "پروکسیهای پویا" به کلاسهای Address و Customer بیفزاید و بنابراین قابلیت Lazy Loading برای این کلاسها در زمان اجرای برنامه فراهم میگردد.
ولی با افزودن پروکسیهای پویا به کلاسهای ما، این کلاسها قابلیت انتقال خود از طریق سرویسهای WCF را از دست میدهند زیرا پروکسیهای پویا به طور پیشگزیده قابلیت سریالایز و دیسریالایز شدن را ندارند!
خوشبختانه میتوانیم این ویژگی را در کلاس DBContext غیرفعال کنیم. برای این منظور قالب سازندهی آن یا MyNewsModel.Context.tt را از Solution Explorer باز کنید و کد زیر را در آن پیدا کنید:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext { public <#=code.Escape(container)#>() : base("name=<#=container.Name#>") {
سپس در ادامهی آن کدغیرفعالکردن پروکسی پویا را به این شکل بنویسید:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext { public <#=code.Escape(container)#>() : base("name=<#=container.Name#>") { Configuration.ProxyCreationEnabled = false;
اکنون اگر فایل را ذخیره کنیم سپس فایل MyNewsModel.Context.cs را از Solution Explorer باز کنید؛ خواهید دید که این خط کد در جای خود قرارگرفته است.
بار دیگر پروژه را اجرا کنید روی متد GetAllCategory کلیک کنید. این بار اگر دکمه Invoke را بفشارید با همانند شکل زیر را خواهید دید:
در بخش ششم پیرامون ارتباط جدولهای tblNews و tblCategory و نمایش محتویات وابسته جدول خبر به دسته و تنظیمات آن در t4 و کلاس Service
در بخش هفتم پیرامون میزبانی WCFLibrary در یک Web Application
و در بخش هشتم پیرامون ایجاد یک برنامهی ویندوزی جهت استفاده از سرویسهای WCF خواهم نوشت.