(function($, kendo) { $.extend(true, kendo.ui.validator, { rules: { remote: function(input) { var remoteAttr = input.attr("data-val-remote-url"); if (typeof remoteAttr === typeof undefined || remoteAttr === false) { return true; } var isInvalid = true; var data = {}; data[input.attr('name')] = input.val(); $.ajax({ url: remoteAttr, mode: "abort", port: "validate" + input.attr('name'), dataType: "json", type: input.attr("data-val-remote-type"), data: data, async: false, success: function(response) { isInvalid = response; } }); return !isInvalid; } }, messages: { remote: function(input) { return input.data('val-remote'); } } }); })(jQuery, kendo);
ASP.NET Web API - قسمت اول
الف) Key-Value stores که پایه بانکهای اطلاعاتی NoSQL را تشکیل داده و اهدافی عمومی را دنبال میکنند.
ب) Wide column stores که در شرکتهای بزرگ اینترنتی بیشتر مورد استفاده قرار گرفتهاند.
ج) Document stores یا بانکهای اطلاعاتی NoSQL سندگرا.
د) Graph databases که بیشتر برای ردیابی ارتباطات بین موجودیتها بکار میروند.
و در تمام این گروهها، مکانیزمهای Key-Value به شدت مورد استفادهاند.
الف) Key-Value stores
Key-Value stores یکی از عمومیترین و پایهایترین گروههای بانکهای اطلاعاتی NoSQL را تشکیل میدهند. البته این مورد بدین معنا نیست که این رده، جزو محبوبترینها نیز بهشمار میروند.
این نوع بانکهای اطلاعاتی شامل جداولی از اطلاعات هستند. هر جدول نیز شامل تعدادی ردیف است؛ چیزی همانند بانکهای اطلاعاتی رابطهای. اما در هر ردیف، یک Dictionary یا آرایهای از اطلاعات key-value شکل را شاهد خواهید بود. در اینجا ساختار و اسکیمای ردیفها میتوانند نسبت به یکدیگر کاملا متفاوت باشند (دید لیبرال نسبت به اسکیما، که در قسمت قبل به آن پرداخته شد). در این بین، تنها تضمین خواهد شد که هر ردیف، Id منحصربفردی دارد.
از این نوع بانکهای اطلاعاتی، در سکوهای کاری ابری زیاد استفاده میشود. دو مثال مهم در اینباره شامل Amazon SimpleDB و Azure Table Storage هستند.
سایر نمونههای مهم دیگری از بانکهای اطلاعاتی NoSQL که بر مبنای مفهوم Key-Value stores کار میکنند، عبارتند از MemcacheDB و Voldemort. به علاوه در Amazon web services بانک اطلاعاتی دیگری به نام DynamoDB به عنوان یک سرویس عمومی در دسترس است. همچنین Dynomite نیز به عنوان نمونه سورس باز Dynamo مطرح است.
Redis و Riak نیز جزو بانکهای اطلاعاتی Key-Value store بسیار معروف بهشمار میروند.
همانطور که در تصویر فوق ملاحظه میکنید، Key-Value stores دارای بانکهای اطلاعاتی شامل جداول مختلف هستند. در اینجا همچنین ساختار ردیفهایی از اطلاعات این جداول نیز مشخص شدهاند. هر ردیف، یک کلید دارد به همراه تعدادی جفت کلید-مقدار. در این جداول، اسکیما ثابت نگه داشته شده است و از ردیفی به ردیف دیگر متفاوت نیست؛ اما این مساله اختیاری است. برای مثال میتوان در ردیف اطلاعات یک مشتری خاص، کلید-مقدارهایی خاص او را نیز درج کرد که لزوما در سایر ردیفها، نیازی به وجود آنها نیست.
به علاوه باید به خاطر داشت که هرچند به ظاهر last_orderها به شماره Id سفارشات مرتبط هستند، اما مفاهیمی مانند کلیدهای خارجی بانکهای اطلاعاتی رابطهای، در اینجا وجود خارجی ندارند. بیشتر در اینجا هدف سهولت جستجوی اطلاعات است.
ب) Wide column stores
Wide column stores دارای جداولی است که درون آنها ستونهایی قابل تعریف است. درون این ستونها که یادآور بانکهای اطلاعاتی رابطهای هستند، اطلاعات به شکل key-value با ساختاری متفاوت، قابل ذخیره سازی هستند. در اینجا هر ستون، میتواند شامل گروهی از ستونها که بر اساس مفاهیم جفتهای key-value کار میکنند، باشد.
این نوع بانکهای اطلاعاتی عموما در سایتهای اینترنتی بسیار بزرگ و برنامههای «Big data» استفاده میشوند. برای مثال:
- BigTable گوگل که یک محصول اختصاصی و غیرعمومی است؛ اما جزئیات آن را به عنوان مقالات علمی منتشر کرده است.
- دنیای سورس باز به رهبری Yahoo، نمونه سورس باز BigTable را به نام Hbase ارائه داده است.
- در فیس بوک، از بانک اطلاعاتی دیگری به نام Cassandra استفاده میکنند. در اینجا به گروهی از ستونها super columns و جداول super column families گفته میشود.
در اینجا نیز جداول و ردیفها وجود دارند و هر ستون باید عضوی از خانواده یک super column باشد. ساختار ردیفها در این تصویر یکسان درنظر گرفته شدهاند، اما اگر نیاز بود، برای مثال میتوان در ردیفی خاص، ساختار را تغییر داد و مثلا middle name را نیز بر اساس نیاز، به ردیفی اضافه کرد.
ج) Document stores
Document stores بجای جداول، دارای بانکهای اطلاعاتی مختلفی هستند و در اینجا بجای ردیفها، سند یا document دارند. ساختار سندها نیز عموما بر مبنای اشیاء JSON تعریف میگردد (که البته این مورد الزامی نبوده و از هر محصول، به محصول دیگری ممکن است متفاوت باشد؛ اما عمومیت دارد). بنابراین هر سند دارای تعدادی خاصیت است (چون اشیاء JSON به این نحو تعریف میگردند) که دارای مقدار هستند. در نگاه اول، شاید این نوع اسناد، بسیار شبیه به key-value stores به نظر برسند. اما در حین تعریف اشیاء JSON، یک مقدار میتواند خود یک شیء کامل دیگر باشد و نه صرفا یک مقدار ساده. به همین جهت عدهای به این نوع بانکهای اطلاعاتی، بانکهای اطلاعاتی Key-value store سفارشی و خاص نیز میگویند.
این نوع ساختار منعطف، برای ذخیره سازی اطلاعات اشیاء تو در تو و درختی بسیار مناسب است. همچنین این اسناد میتوانند حاوی پیوستهایی نیز باشد؛ مانند پیوست یک فایل به یک سند.
در Document stores، نگارشهای قدیمی اسناد نیز نگهداری میگردند. به همین جهت این نوع بانکهای اطلاعاتی برای ایجاد برنامههای مدیریت محتوا نیز بسیار مطلوب میباشند.
با توجه به مزایایی که برای این رده از بانکهای اطلاعاتی NoSQL ذکر گردید، Document stores در بین برنامه نویسها بسیار محبوب و پرکاربرد هستند.
از این دست بانکهای اطلاعاتی NoSQL، میتوان به CouchDB ، MongoDB و RavenDB اشاره کرد.
سایر مزایای Document stores که به پرکاربرد شدن آنها کمک کردهاند به شرح زیر هستند:
- هر سند را میتوان با یک URI آدرس دهی کرد.
- برای نمونه CouchDB از یک full REST interface برای دسترسی و کار با اسناد پشتیبانی میکند (چیزی شبیه به ASP.NET WEB API در دات نت). در اینجا با استفاده از یک وب سرور توکار و بکارگیری HTTP Verbs مانند Put، Delete، Get و غیره، امکان کار با اسناد وجود دارد.
- اغلب بانکهای اطلاعاتی Document stores از JavaScript به عنوان native language خود بهره میبرند (جهت سهولت کار با اشیاء JSON).
در اینجا دو دیتابیس، بجای دو جدول وجود دارند. همچنین در مقایسه با بانکهای اطلاعاتی key-value، برای نمونه، مقدار خاصیت آدرس، خود یک شیء است که از دو خاصیت تشکیل شده است. به علاوه هر خاصیت Most_Recent یک Order، به سند دیگری در بانک اطلاعاتی Orders لینک شده است.
د) Graph databases
Graph databases نوع خاصی از بانکهای اطلاعاتی NoSQL هستند که جهت ردیابی ارتباطات بین اطلاعات طراحی شدهاند و برای برنامههای شبکههای اجتماعی بسیار مفید هستند.
در واژه نامه این بانکهای اطلاعاتی Nodes و Edges (اتصال دهندههای نودها) تعریف شدهاند. در اینجا نودها میتوانند دارای خاصیتها و مقادیر متناظر با آنها باشند.
یکی از معروفترین Graph databases مورد استفاده، Neo4j نام دارد.
در اینجا یک شخص را که دارای رابطه آدرس با شیء آدرس ذکر شده است را مشاهده میکنید. همچنین این شخص دارای رابطه دوستی با سه شخص دیگر است.
پس از آن، برای تبدیل صفحات یک فایل PDF به تصویر، مراحل زیر باید طی شود:
الف) وهله سازی از شیء AcroExch.PDDoc
در صورتیکه SDK یاد شده بر روی سیستم نصب نباشد، این وهله سازی با شکست مواجه خواهد شد و همچنین باید دقت داشت که این SDK به همراه نگارش رایگان Adobe reader ارائه نمیشود.
ب) گشودن فایل PDF به کمک شیء Com وهله سازی شده (pdfDoc.Open)
ج) دریافت اطلاعات صفحه مورد نظر (pdfDoc.AcquirePage)
د) کپی این اطلاعات به درون clipboard ویندوز (pdfPage.CopyToClipboard)
به این ترتیب به یک تصویر Bmp قرار گرفته شده در clipboard ویندوز خواهیم رسید
ه) مرحله بعد تغییر ابعاد و ذخیره سازی این تصویر نهایی است.
کدهای زیر، روش انجام این مراحل را بیان میکنند:
using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; using Acrobat; //Add a Com Object ref. to "Adobe Acrobat 10.0 Type Library" => Program Files\Adobe\Acrobat 10.0\Acrobat\acrobat.tlb using Microsoft.Win32; namespace PdfThumbnail.Lib { public static class PdfToImage { const string AdobeObjectsErrorMessage = "Failed to create the PDF object."; const string BadFileErrorMessage = "Failed to open the PDF file."; const string ClipboardError = "Failed to get the image from clipboard."; const string SdkError = "This operation needs the Acrobat SDK(http://www.adobe.com/devnet/acrobat/downloads.html), which is combined with the full version of Adobe Acrobat."; public static byte[] PdfPageToPng(string pdfFilePath, int thumbWidth = 600, int thumbHeight = 750, int pageNumber = 0) { byte[] imageData = null; runJob((pdfDoc, pdfRect) => { imageData = pdfPageToPng(thumbWidth, thumbHeight, pageNumber, pdfDoc, pdfRect); }, pdfFilePath); return imageData; } public static void AllPdfPagesToPng(Action<byte[], int, int> dataCallback, string pdfFilePath, int thumbWidth = 600, int thumbHeight = 750) { runJob((pdfDoc, pdfRect) => { var numPages = pdfDoc.GetNumPages(); for (var pageNumber = 0; pageNumber < numPages; pageNumber++) { var imageData = pdfPageToPng(thumbWidth, thumbHeight, pageNumber, pdfDoc, pdfRect); dataCallback(imageData, pageNumber + 1, numPages); } }, pdfFilePath); } static void runJob(Action<CAcroPDDoc, CAcroRect> job, string pdfFilePath) { if (!File.Exists(pdfFilePath)) throw new InvalidOperationException(BadFileErrorMessage); var acrobatPdfDocType = Type.GetTypeFromProgID("AcroExch.PDDoc"); if (acrobatPdfDocType == null || !isAdobeSdkInstalled) throw new InvalidOperationException(SdkError); var pdfDoc = (CAcroPDDoc)Activator.CreateInstance(acrobatPdfDocType); if (pdfDoc == null) throw new InvalidOperationException(AdobeObjectsErrorMessage); var acrobatPdfRectType = Type.GetTypeFromProgID("AcroExch.Rect"); var pdfRect = (CAcroRect)Activator.CreateInstance(acrobatPdfRectType); var result = pdfDoc.Open(pdfFilePath); if (!result) throw new InvalidOperationException(BadFileErrorMessage); job(pdfDoc, pdfRect); releaseComObjects(pdfDoc, pdfRect); } public static byte[] ResizeImage(this Image image, int thumbWidth, int thumbHeight) { var srcWidth = image.Width; var srcHeight = image.Height; using (var bmp = new Bitmap(thumbWidth, thumbHeight, PixelFormat.Format32bppArgb)) { using (var gr = Graphics.FromImage(bmp)) { gr.SmoothingMode = SmoothingMode.HighQuality; gr.PixelOffsetMode = PixelOffsetMode.HighQuality; gr.CompositingQuality = CompositingQuality.HighQuality; gr.InterpolationMode = InterpolationMode.High; var rectDestination = new Rectangle(0, 0, thumbWidth, thumbHeight); gr.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel); using (var memStream = new MemoryStream()) { bmp.Save(memStream, ImageFormat.Png); return memStream.ToArray(); } } } } static bool isAdobeSdkInstalled { get { return Registry.ClassesRoot.OpenSubKey("AcroExch.PDDoc", writable: false) != null; } } private static Bitmap pdfPageToBitmap(int pageNumber, CAcroPDDoc pdfDoc, CAcroRect pdfRect) { var pdfPage = (CAcroPDPage)pdfDoc.AcquirePage(pageNumber); if (pdfPage == null) throw new InvalidOperationException(BadFileErrorMessage); var pdfPoint = (CAcroPoint)pdfPage.GetSize(); pdfRect.Left = 0; pdfRect.right = pdfPoint.x; pdfRect.Top = 0; pdfRect.bottom = pdfPoint.y; pdfPage.CopyToClipboard(pdfRect, 0, 0, 100); Bitmap pdfBitmap = null; var thread = new Thread(() => { var data = Clipboard.GetDataObject(); if (data != null && data.GetDataPresent(DataFormats.Bitmap)) pdfBitmap = (Bitmap)data.GetData(DataFormats.Bitmap); }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); thread.Join(); Marshal.ReleaseComObject(pdfPage); return pdfBitmap; } private static byte[] pdfPageToPng(int thumbWidth, int thumbHeight, int pageNumber, CAcroPDDoc pdfDoc, CAcroRect pdfRect) { var pdfBitmap = pdfPageToBitmap(pageNumber, pdfDoc, pdfRect); if (pdfBitmap == null) throw new InvalidOperationException(ClipboardError); var pdfImage = pdfBitmap.GetThumbnailImage(thumbWidth, thumbHeight, null, IntPtr.Zero); // (+ 7 for template border) var imageData = pdfImage.ResizeImage(thumbWidth + 7, thumbHeight + 7); return imageData; } private static void releaseComObjects(CAcroPDDoc pdfDoc, CAcroRect pdfRect) { pdfDoc.Close(); Marshal.ReleaseComObject(pdfRect); Marshal.ReleaseComObject(pdfDoc); } } }
using System; using System.IO; using System.Windows.Forms; using PdfThumbnail.Lib; namespace PdfThumbnail { class Program { static void Main(string[] args) { var pdfPath = Application.StartupPath + @"\test.pdf"; PdfToImage.AllPdfPagesToPng((pageImageData, pageNumber, numPages) => { Console.WriteLine("Page {0}/{1}", pageNumber, numPages); File.WriteAllBytes(string.Format("{0}\\page-{1}.png", Application.StartupPath, pageNumber), pageImageData); }, pdfPath); } } }
کدهای این قسمت را از اینجا نیز میتوانید دریافت کنید:
PdfThumbnail.zip
کلاس ArrayList
using System; using System.Collections; class ProgrArrayListExample { static void Main() { ArrayList list = new ArrayList(); list.Add("Hello"); list.Add(5); list.Add(3.14159); list.Add(DateTime.Now); for (int i = 0; i < list.Count; i++) { object value = list[i]; Console.WriteLine("Index={0}; Value={1}", i, value); } } }
Index=0; Value=Hello Index=1; Value=5 Index=2; Value=3.14159 Index=3; Value=29.02.2015 23:17:01
ArrayList list = new ArrayList(); list.Add(2); list.Add(3.5f); list.Add(25u); list.Add(" ریال"); dynamic sum = 0; for (int i = 0; i < list.Count; i++) { dynamic value = list[i]; sum = sum + value; } Console.WriteLine("Sum = " + sum); // Output: Sum = 30.5ریال
مجموعههای جنریک Generic Collections
GenericType<T> instance = new GenericType<T>();
List<int> intList = new List<int>(); List<bool> boolList = new List<bool>(); List<double> realNumbersList = new List<double>();
List<int> intList = new List<int>();
- استفاده از index برای دسترسی به یک مقدار، صرف نظر از اینکه چه میزان دادهای در آن وجود دارد، بسیار سریع انجام میگیرد.
- جست و جوی یک عنصر بر اساس مقدار: جست و جو خطی است در نتیجه اگر مقدار مورد نظر در آخرین خانهها باشد بدترین وضعیت ممکن رخ میدهد و بسیار کند عمل میکند. داده هر چی کمتر بهتر و هر چه بیشتر بدتر. البته اگر بخواهید مجموعهای از مقدارهای برابر را برگردانید هم در بدترین وضعیت ممکن خواهد بود.
- حذف و درج (منظور insert) المانها به خصوص موقعی که انتهای آرایه نباشید، شیفت پیدا کردن در آرایه عملی کاملا کند و زمانبر است.
- موقعی که عنصری را بخواهید اضافه کنید اگر ظرفیت آرایه تکمیل شده باشد، نیاز به عمل زمانبر افزایش ظرفیت خواهد بود که البته این عمل به ندرت رخ میدهد و عملیات افزودن Add هم هیچ وابستگی به تعداد المانها ندارد و عملی سریع است.
- افزودن به انتهای لیست به خاطر این که همیشه گره آخر در tail وجود دارد بسیار سریع است.
- عملیات درج insert در هر موقعیتی که باشد اگر یک اشاره گر به آن محل باشد یک عملیات سریع است یا اینکه درج در ابتدا یاانتهای لیست باشد.
- جست و جوی یک مقدار چه بر اساس اندیس باشد و چه مقدار، کار جست و جو کند خواهد بود. چرا که باید تمامی المانها از اول به آخر اسکن بشن.
- عملیات حذف هم به خاطر اینکه یک عمل جست و جو در ابتدای خود دارد، یک عمل کند است.
استفاده از لیست پیوندی برای پیاده سازی پشته:
Stack<string> stack = new Stack<string>(); stack.Push("A"); stack.Push("B"); stack.Push("C"); while (stack.Count > 0) { string letter= stack.Pop(); Console.WriteLine(letter); } //خروجی //C //B //A
ابتدای آرایه مکانی است که عنصر از آنجا برداشته میشود و Head به آن اشاره میکند و tail هم به انتهای آرایه که جهت درج عنصر جدید مفید است. با برداشتن هر خانهای که head به آن اشاره میکند، head یک خانه به سمت جلو حرکت میکند و زمانی که Head از tail بیشتر شود، یعنی اینکه دیگر عنصری یا المانی در صف وجود ندارد و head و Tail به ابتدای صف حرکت میکنند. در این حالت موقعی که المان جدیدی قصد اضافه شدن داشته باشد، افزودن، مجددا از اول صف آغاز میشود و به این صفها، صف حلقوی میگویند.
عملیات اصلی صف دو مورد هستند enqueue که المان جدید را در انتهای صف قرار میدهد و dequeue اولین المان صف را بیرون میکشد.
پیاده سازی صف به صورت پویا با لیستهای پیوندی
برای پیاده سازی صف، لیستهای پیوندی یک طرفه کافی هستند:
در این حالت عنصر جدید مثل سابق به انتهای لیست اضافه میشود و برای حذف هم که از اول لیست کمک میگیریم و با حذف عنصر اول، متغیر Head به عنصر یا المان دوم اشاره خواهد کرد.
کلاس از پیش آمده صف در دات نت <Queue<T است و نحوهی استفاده آن بدین شکل است:
static void Main() { Queue<string> queue = new Queue<string>(); queue.Enqueue("Message One"); queue.Enqueue("Message Two"); queue.Enqueue("Message Three"); queue.Enqueue("Message Four"); while (queue.Count > 0) { string msg = queue.Dequeue(); Console.WriteLine(msg); } } //خروجی //Message One //Message Two //Message Thre //Message Four
مدیران گیتلب با آغوش باز پذیرای توسعه دهندگان بوده و برای بهبود سیستمهای خود زیربنای این سایت را از مایکروسافت آژور به پلتفرم ابری گوگل (GCP) منتقل کردهاند.
گوگل دلیل این مساله (قطع دسترسی) را تحریمهای وزارت خزانه داری ایالات متحده عنوان کرده و گیتلب هم از کاربران خواسته کدهای خود را دانلود کرده یا از پروژهها نسخه پشتیبان تهیه کنند.
کشورهای، ایران - سودان - کوبا - سوریه - کره شمالی در لیست تحریمها میباشند!
از 11 آگوست شروع خواهد شد!
البته همیشه درخواست کنترل این جدول واسط که کاملا از دیدگاه ORMها (تمام آنها) مخفی است، وجود داشتهاست و قرار است این پشتیبانی توسط مفهوم ویژهای به نام shadow properties به نگارشهای بعدی EF Core اضافه شود.
اما فعلا در نگارش اول آن، توصیه شدهاست که رابطهی many-to-many را به صورت دو رابطهی one-to-many مدلسازی کنید که در ادامه آنرا بررسی خواهیم کرد. بنابراین پیشنیاز آن مطالعهی مطلب «شروع به کار با EF Core 1.0 - قسمت 7 - بررسی رابطهی One-to-Many» میباشد.
مدلسازی موجودیتهای یک رابطهی چند به چند در EF Core 1.0 RTM توسط Fluent API
در اینجا نحوهی مدلسازی یک رابطهی چند به چند را توسط دو رابطهی one-to-many مشاهده میکنید. تنها تفاوت آن با EF 6.x، قید صریح جدول واسط BlogPostsJoinTags است که یک چنین جدولی در EF 6.x به صورت خودکار تشکیل شده و مدیریت میشود و کاملا از دید برنامه مخفی است. اما در اینجا (در نگارش اول EF Core) نیاز است این جدول واسط را از حالت مخفی خارج کرد و سپس دو رابطهی یک به چند را به جداول مطالب و تگهای آنها تشکیل داد.
مزیت این حالت، دسترسی کامل به طراحی جدول واسط، توسط برنامه است. بنابراین اگر به هر دلیلی نیاز به افزودن خواص بیشتری به این جدول ویژه دارید، اکنون امکان آن میسر است.
public class Tags { public Tags() { BlogPostsJoinTags = new HashSet<BlogPostsJoinTags>(); } public int Id { get; set; } public string Name { get; set; } public virtual ICollection<BlogPostsJoinTags> BlogPostsJoinTags { get; set; } }
public class BlogPostsJoinTags { public virtual BlogPosts BlogPost { get; set; } public int BlogPostId { get; set; } public virtual Tags Tag { get; set; } public int TagId { get; set; } }
public class BlogPosts { public BlogPosts() { BlogPostsJoinTags = new HashSet<BlogPostsJoinTags>(); } public int Id { get; set; } public string Title { get; set; } public string Body { get; set; } public virtual ICollection<BlogPostsJoinTags> BlogPostsJoinTags { get; set; } }
به علاوه در اینجا تعریف یک composite key را هم بر روی خواص کلید خارجی جدول واسط مشاهده میکنید. وجود این کلید ترکیبی سبب خواهد شد که ملزم به ثبت هر دو Id (کلیدهای جداول مطلب و تگ) در حین ثبت در این جدول شویم (یا قید اجباری هر دو طرف رابطه).
public class MyDBDataContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Data Source=(local);Initial Catalog=testdb2;Integrated Security = true"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<BlogPosts>(entity => { entity.Property(e => e.Title) .IsRequired() .HasMaxLength(450); }); modelBuilder.Entity<Tags>(entity => { entity.Property(e => e.Name) .IsRequired() .HasMaxLength(450); }); modelBuilder.Entity<BlogPostsJoinTags>(entity => { entity.HasKey(e => new { e.TagId, e.BlogPostId }) .HasName("PK_dbo.BlogPostsJoinTags"); entity.HasIndex(e => e.BlogPostId) .HasName("IX_BlogPostId"); entity.HasIndex(e => e.TagId) .HasName("IX_TagId"); entity.HasOne(d => d.BlogPost) .WithMany(p => p.BlogPostsJoinTags) .HasForeignKey(d => d.BlogPostId) .HasConstraintName("FK_dbo.BlogPostsJoinTags_dbo.BlogPosts_BlogPostId"); entity.HasOne(d => d.Tag) .WithMany(p => p.BlogPostsJoinTags) .HasForeignKey(d => d.TagId) .HasConstraintName("FK_dbo.BlogPostsJoinTags_dbo.Tags_TagId"); }); } public virtual DbSet<BlogPosts> BlogPosts { get; set; } public virtual DbSet<BlogPostsJoinTags> BlogPostsJoinTags { get; set; } public virtual DbSet<Tags> Tags { get; set; } }
مدلسازی موجودیتهای یک رابطهی چند به چند در EF Core 1.0 RTM توسط Data Annotations
در حالت مدلسازی توسط ویژگیها، ذکر InversePropertyها و همچنین ForeignKeyها مقداری واضحتر به نظر میرسند. به علاوه، یک سری از تنظیمات هم معادل data annotations ایی ندارند؛ مانند composite key تعریف شدهی بر روی خواص جدول واسط و همچنین ایندکسهای تعریف شده، که حتما باید توسط Fluent API تنظیم شوند.
public class Tags { public Tags() { BlogPostsJoinTags = new HashSet<BlogPostsJoinTags>(); } public int Id { get; set; } [Required] [MaxLength(450)] public string Name { get; set; } [InverseProperty("Tag")] public virtual ICollection<BlogPostsJoinTags> BlogPostsJoinTags { get; set; } }
public class BlogPostsJoinTags { [ForeignKey("BlogPostId")] [InverseProperty("BlogPostsJoinTags")] public virtual BlogPosts BlogPost { get; set; } public int BlogPostId { get; set; } [ForeignKey("TagId")] [InverseProperty("BlogPostsJoinTags")] public virtual Tags Tag { get; set; } public int TagId { get; set; } }
public class BlogPosts { public BlogPosts() { BlogPostsJoinTags = new HashSet<BlogPostsJoinTags>(); } public int Id { get; set; } [Required] [MaxLength(450)] public string Title { get; set; } public string Body { get; set; } [InverseProperty("BlogPost")] public virtual ICollection<BlogPostsJoinTags> BlogPostsJoinTags { get; set; } }
public class MyDBDataContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Data Source=(local);Initial Catalog=testdb2;Integrated Security = true"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<BlogPostsJoinTags>(entity => { entity.HasKey(e => new { e.TagId, e.BlogPostId }) .HasName("PK_dbo.BlogPostsJoinTags"); entity.HasIndex(e => e.BlogPostId) .HasName("IX_BlogPostId"); entity.HasIndex(e => e.TagId) .HasName("IX_TagId"); }); } public virtual DbSet<BlogPosts> BlogPosts { get; set; } public virtual DbSet<BlogPostsJoinTags> BlogPostsJoinTags { get; set; } public virtual DbSet<Tags> Tags { get; set; } }
نحوهی ثبت اطلاعات در دو رابطهی یک به چند به همراه جدول واسط
در EF 6.x، کار مقدار دهی Idهای جدول واسط به صورت خودکار انجام میشود. در اینجا این مقدار دهی را باید به صورت صریح انجام داد:
var post = new BlogPosts { ... }; context.BlogPosts.Add(post); var tag = new Tags { ... }; context.Tags.Add(tag); var postTag = new BlogPostsJoinTags { Tag = tag, BlogPost = post }; context.PostsTags.Add(postTag); context.SaveChanges();
نحوهی واکشی اطلاعات به هم مرتبط در دو رابطهی یک به چند به همراه جدول واسط
در مورد متدهای Include و ThenInclude در مطلب «شروع به کار با EF Core 1.0 - قسمت 7 - بررسی رابطهی One-to-Many» پیشتر بحث شد.
BlogPosts post1 = this.BlogPosts .Include(blogPosts => blogPosts.BlogPostsJoinTags) .ThenInclude(joinTags => joinTags.Tag) .First(blogPosts => blogPosts.Id == 1); IEnumerable<Tags> post1Tags = post1.BlogPostsJoinTags.Select(x => x.Tag);
یک نکتهی تکمیلی
وضعیت پشتیبانی از رابطهی many-to-many را همانند EF 6.x در EF Core، در اینجا میتوانید پیگیری کنید.
بومیسازی چیست؟
بومیسازی محصول الزاما به این معنی نیست که یک محصول خارجی را طوری تغییر دهیم که بشود از آن در داخل کشور استفاده کرد. بومیسازی صرفا تغییر نوشتههای یک محصول نیست که با ترجمه آن، برنامه بومی شده باشد. بومیسازی یک پروسه زمانبر و پیچیده است و بطرز شگفتآوری با جهانیسازی مرتبط است.
در واقع ما زمانی یک برنامه را بومی میکنیم که بخواهیم در کشورها یا بومهای مختلف از آن استفاده کنیم. از آنجا که این تغییرات بعضا از هم تاثیر پذیر هستند کاربردپذیری هر برنامه را تغییر خواهند داد. برای مثال در نظر بگیرید یک برنامه را که روی تولید نام کاربری به زبانهای مختلف محدودیتی ایجاد نکرده و قرار است در چندین کشور مختلف (وب) از آن استفاده شود. حروف مشابه ولی در باطن متفاوت در بومهای مختلف چه بلایی سر برنامه خواهند آورد؟ فرض کنید شبکه اجتماعی توئیتر شما را در تولید کلمه کاربری با زبان فارسی و عربی آزاد بگذارد. وقتی به @علی یک پیغام میفرستید، چه کسی این پیغام را تحویل خواهد گرفت؟ صاحب این اکانت در عربستان یا ایران؟
با اینکه بومیسازی لزوما ارتباط چندانی با کاربردپذیری ندارد اما در کاربردپذیر کردن برنامه و استانداردهای برنامه بطرز چشمگیری تاثیرگذار است. موارد مختلفی را باید در هنگام تولید یک برنامه چندزبانه در نظر داشت از جمله:
- از این برنامه در چه کشورهایی استفاده خواهد شد؟
- چه نوع تقویمهایی را باید پشتیبانی نماید؟
- نحوه افزودن زبان جدید به برنامه چگونه خواهد بود؟
- تکمیل فیلدهای حساس مانند نام کاربری به چه زبانی مجاز است؟
- تولید صفحات برنامه در جهات مختلف به چه صورت خواهد بود؟
- تجربه کاربری مناسب برای کاربری که راست به چپ مینویسد و کاربرزی که چپ به راست مینویسد چگونه است؟
- شرکتهای بزرگ چطور با این مشکلات روبرو میشوند؟
- ذخیره دادهها در پایگاه داده باید به چه صورتی و با چه زبان مرجعی صورت پذیرد؟
- تبدیل و تغییر دادهها چقدر لازم است؟
بزودی با هر یک از این موارد بیشتر آشنا خواهیم شد و هر مورد را بصورت جداگانه بررسی خواهیم کرد.
تعاریف :
بوم : Local
بومی : Localized
بومیسازی : Localization
جهانیسازی : Globalization
کاربردپذیر : Usability
در ابتدای کار نیاز است تا repository خود را ایجاد کنیم. بدین منظور از طریق محیط command prompt به آدرس پوشه مورد نظر رفته و دستور git init را اجرا میکنیم. این کار سبب میشود تا پوشه git. در داخل فولدر جاری ایجاد شود. این پوشه در واقع همان repository و پوشه جاری، همان working tree ما خواهند بود. حال با استفاده از یک ادیتور نظیر notepad یک فایل متنی جدید را با نام readme1.txt در پوشه ایجاد کنید (توجه کنید در working tree، نه در پوشه git.؛ محتویات این پوشه جز در مورد برخی فایلها نباید توسط کاربر تغییر کند)
اکنون دستور زیر را اجرا کنید:
git status
برای آنکه این فایل را در repository ذخیره کنیم همانطور که قبلا گفته شد باید ابتدا آنرا به index اضافه کنیم این کار با استفاده از دستور زیر انجام میشود:
git add readme1.txt
git commit
git commit -m “commit descriptions”
git add .
git add -u
git log
git log --until [date] git log --since [date] git log -[number]
چگونگی حذف فایلها:
تا اینجا با نحوه چگونگی ایجاد فایلهای جدید و یا ویرایش فایلهای قدیمی آشنا شدید. برای حذف یک فایل میتوان به دو صورت عمل کرد:
1) ابتدا فایل را را مستقیما حذف نموده، سپس با استفاده از دستور زیر ابتدا فایل حذف شده را به stage آورده و سپس آن را commit میکنیم:
git rm [filename]
چگونگی تغییر نام و یا جابجایی یک فایل:
برای تغییر نام و جابجایی یک فایل نیز مانند حذف، دو روش وجود دارد:
۱) ابتدا فایل مورد نظر را تغییر نام داده و یا جابجا میکنیم. در این حالت اگر status بگیریم خواهیم دید که git به ما میگوید فایلی با نام قبلی حذف شده و فایلی با نام جدید اضافه شده است. یعنی git تشخیص نمیدهد که این دو فایل یکی هستند و تنها تغییر نام داده شده است. اما به محض آنکه فایل اول را با دستور rm حذف و فایل دوم را با دستور add اضافه کنیم، git متوجه میشود که این دو فایل در واقع یک فایل تغییر نام یافته هستند. البته در صورتیکه حداقل ۵۰ درصد فایل دوم با فایل اول شباهت داشته باشد، بعد از انجام عملیات فوق از دستور commit استفاده میکنیم.
۲) در این روش از دستور زیر استفاده کرده و سپس commit را انجام میدهیم:
git mv [firstname][secondname]
روش اول :