خواندنیهای 19 تیر
امنیت
توسعه وب
دات نت فریم ورک
دبلیو سی اف
دبلیو پی اف و سیلور لایت
متفرقه
محیطهای مجتمع توسعه
مرورگرها
مسایل انسانی، اجتماعی و مدیریتی برنامه نویسی
ویندوز
پی اچ پی
اخیراً مشخص شده آنتی ویروس Windows Defender مایکروسافت به مدت نزدیک به 12 سال دارای یک رخنه امنیتی بوده است که به هکرها امکان بازنویسی فایلها و اجرای کدهای مخرب را میداده است. با این حال هنوز روشن نیست آیا هکرها و مجرمین موفق به شناسایی و استفاده از این باگ شدهاند یا نه، هرچند که 12 سال مدت بسیار طولانی به نظر میرسد.
خواندنیهای 8 مرداد
اس کیوال سرور
امنیت
توسعه وب
دات نت فریم ورک
دبلیو پی اف و سیلور لایت
دلفی
سی و مشتقات
لینوکس
متفرقه
محیطهای مجتمع توسعه
ویندوز
همان طور که ممکن است بدانید، اکشن متدها در کنترلرهای MVC میتوانند انواع مختلفی را برگشت دهند که شرح آن در مطالب این سایت به مفصل گذشته است. یکی از این انواع، نوع ActionResult میباشد. این یک کلاس پایه برای انواع برگشتی توسط اکشن متدها مثل JsonResult، FileResult میباشد. (اطلاعات بیشتر را اینجا بخوانید) اما ممکن است مواقعی پیش بیاید که بخواهید نوعی را توسط یک اکشن متد برگشت دهید که به صورت توکار تعریف نشده باشد. مثلا زمانی را در نظر بگیرید که بخواهید یک تصویر امنیتی را برگشت دهید. یکی از راه حلهای ممکن به این صورت است که کلاسی ایجاد شود که از کلاس پایه ActionResult ارث بری کرده باشد. بدین صورت:
using System; using System.Web.Mvc; namespace MVCPersianCaptcha.Models { public class CaptchaImageResult : ActionResult { public override void ExecuteResult(ControllerContext context) { throw new NotImplementedException(); } } }
کدهای اولیه برای ایجاد یک تصویر امنیتی به صورت خیلی ساده از کلاسهای فراهم شده توسط +GDI ، که در دات نت فریمورک وجود دارند استفاده خواهند کرد. برای این کار ابتدا یک شیء از کلاس Bitmap با دستور زیر ایجاد خواهیم کرد:
// Create a new 32-bit bitmap image. Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
سپس شیئی از نوع Graphics برای انجام عملیات ترسیم نوشتههای فارسی روی شیء bitmap ساخته میشود:
// Create a graphics object for drawing. Graphics gfxCaptchaImage = Graphics.FromImage(bitmap);
gfxCaptchaImage.PageUnit = GraphicsUnit.Pixel; gfxCaptchaImage.SmoothingMode = SmoothingMode.HighQuality; gfxCaptchaImage.Clear(Color.White);
سپس یک عدد اتفاقی بین 1000 و 9999 با دستور زیر تولید میشود:
// Create a Random Number from 1000 to 9999 int salt = CaptchaHelpers.CreateSalt();
public int CreateSalt() { Random random = new Random(); return random.Next(1000, 9999); }
HttpContext.Current.Session["captchastring"] = salt;
string randomString = (salt).NumberToText(Language.Persian);
به صورت پیش فرض نوشتههای تصویر امنیتی به صورت چپ چین نوشته خواهند شد، و با توجه به این که نوشته ای که باید در تصویر امنیتی قرار بگیرد فارسی است، پس بهتر است آنرا به صورت راست به چپ در تصویر بنویسیم، بدین صورت:
// Set up the text format. var format = new StringFormat(); int faLCID = new System.Globalization.CultureInfo("fa-IR").LCID; format.SetDigitSubstitution(faLCID, StringDigitSubstitute.National); format.Alignment = StringAlignment.Near; format.LineAlignment = StringAlignment.Near; format.FormatFlags = StringFormatFlags.DirectionRightToLeft;
// Font of Captcha and its size Font font = new Font("Tahoma", 10);
// Create a path for text GraphicsPath path = new GraphicsPath();
path.AddString(randomString, font.FontFamily, (int)font.Style, (gfxCaptchaImage.DpiY * font.SizeInPoints / 72), new Rectangle(0, 0, width, height), format);
gfxCaptchaImage.DrawPath(Pens.Navy, path);
//-- using a sin ware distort the image int distortion = random.Next(-10, 10); using (Bitmap copy = (Bitmap)bitmap.Clone()) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int newX = (int)(x + (distortion * Math.Sin(Math.PI * y / 64.0))); int newY = (int)(y + (distortion * Math.Cos(Math.PI * x / 64.0))); if (newX < 0 || newX >= width) newX = 0; if (newY < 0 || newY >= height) newY = 0; bitmap.SetPixel(x, y, copy.GetPixel(newX, newY)); } } }
//-- Draw the graphic to the bitmap gfxCaptchaImage.DrawImage(bitmap, new Point(0, 0)); gfxCaptchaImage.Flush();
HttpResponseBase response = context.HttpContext.Response; response.ContentType = "image/jpeg"; bitmap.Save(response.OutputStream, ImageFormat.Jpeg);
// Clean up. font.Dispose(); gfxCaptchaImage.Dispose(); bitmap.Dispose();
public CaptchaImageResult CaptchaImage() { return new CaptchaImageResult(); }
<img src="@Url.Action("CaptchaImage")"/>
[HttpPost] public ActionResult Index(LogOnModel model) { if (!ModelState.IsValid) return View(model); if (model.CaptchaInputText == Session["captchastring"].ToString()) TempData["message"] = "تصویر امنتی را صحیح وارد کرده اید"; else TempData["message"] = "تصویر امنیتی را اشتباه وارد کرده اید"; return View(); }
کدهای کامل مربوط به این مطلب را به همراه یک مثال از لینک زیر دریافت نمائید:
MVC-Persian-Captcha
غنی سازی کامپایلر C# 9.0 با افزونهها
<PackageReference Include="SecurityCodeScan" Version="3.5.3"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference>
مدل سازی دادهها در RavenDB
تفاوتهای دوره ما با زمانیکه بانکهای اطلاعاتی رابطهای پدیدار شدند
- دنیای بانکهای اطلاعاتی رابطهای برای Write بهینه سازی شدهاند؛ از این جهت که تاریخچه پیدایش آنها به دهه 70 میلادی بر میگردد، زمانیکه برای تهیه سخت دیسکها باید هزینههای گزافی پرداخت میشد. به همین جهت الگوریتمها و روشهای بسیاری در آن دوره ابداع شدند تا ذخیره سازی اطلاعات، حجم کمتری را به خود اختصاص دهند. اینجا است که مباحثی مانند Normalization بوجود آمدند تا تضمین شود که دادهها تنها یکبار ذخیره شده و دوبار در جاهای مختلفی ذخیره نگردند. جهت اطلاع در سال 1980 میلادی، یک سخت دیسک 10 مگابایتی حدود 4000 دلار قیمت داشته است.
- تفاوت مهم دیگر دوره ما با دهههای 70 و 80 میلادی، پدیدار شدن UI و روابط کاربری بسیار پیچیده، در مقایسه با برنامههای خط فرمان یا حداکثر فرمهای بسیار ساده ورود اطلاعات در آن زمان است. برای مثال در دهه 70 میلادی تصور UI ایی مانند صفحه ابتدایی سایت Stack overflow احتمالا به ذهن هم خطور نمیکرده است.
تهیه چنین UI ایی نه تنها از لحاظ طراحی، بلکه از لحاظ تامین دادهها از جداول مختلف نیز بسیار پیچیده است. برای مثال برای رندر صفحه اول سایت استک اورفلو ابتدا باید تعدادی سؤال از جدول سؤالات واکشی شوند. در اینجا در ذیل هر سؤال نام شخص مرتبط را هم مشاهده میکنید. بنابراین اطلاعات نام او، از جدول کاربران نیز باید دریافت گردد. یا در اینجا تعداد رایهای هر سؤال را نیز مشاهده میکنید که به طور قطع اطلاعات آن در جدول دیگری نگه داری میشود. در گوشهای از صفحه، برچسبهای مورد علاقه و در ذیل هر سؤال، برچسبهای اختصاصی هر مطلب نمایش داده شدهاند. تگها نیز در جدولی جداگانه قرار دارند. تمام این قسمتهای مختلف، نیاز به واکشی و رندر حجم بالایی از اطلاعات را دارند.
- تعداد کاربران برنامهها در دهههای 70 و 80 میلادی نیز با دوره ما متفاوت بودهاند. اغلب برنامههای آن دوران تک کاربره طراحی میشدند؛ با بانکهای اطلاعاتی که صرفا جهت کار بر روی یک سیستم طراحی شده بودند. اما برای نمونه سایت استک اور فلویی که مثال زده شده، توسط هزاران و یا شاید میلیونها نفر مورد استفاده قرار میگیرد؛ با توزیع و تقسیم اطلاعات آن بر روی سرورها مختلف.
معرفی مفهوم Unit of change
همین پیچیدگیها سبب شدند تا جهت سادهسازی حل اینگونه مسایل، حرکتی به سمت دنیای NoSQL شروع شود. ایده اصلی مدل سازی دادهها در اینجا کم کردن تعداد اعمالی است که باید جهت رسیدن به یک نتیجه واحد انجام داد. اگر قرار است یک سؤال به همراه تگها، اطلاعات کاربر، رایها و غیره واکشی شوند، چرا باید تعداد اعمال قابل توجهی جهت مراجعه به جداول مختلف مرتبط صورت گیرد؟ چرا تمام این اطلاعات را یکجا نداشته باشیم تا بتوان همگی را در طی یک واکشی به دست آورد و به این ترتیب دیگر نیازی نباشد انواع و اقسام JOINها را به چند ده جدول موجود نوشت؟
اینجا است که مفهومی به نام Unit of change مطرح میشود. در هر واحد تغییر، کلیه اطلاعات مورد نیاز برای رندر یک شیء قرار میگیرند. برای مثال اگر قرار است با شیء محصول کار کنیم، تمام اطلاعات مورد نیاز آنرا اعم از گروهها، نوعها، رنگها و غیره را در طی یک سند بانک اطلاعاتی NoSQL سندگرا، ذخیره میکنیم.
محدودههای تراکنشی یا Transactional boundaries
محدودههای تراکنشی در Domain driven design به Aggregate root نیز معروف است. هر محدود تراکنشی حاوی یک Unit of change قرار گرفته داخل یک سند است. ابتدا بررسی میکنیم که در یک Read به چه نوع اطلاعاتی نیاز داریم و سپس کل اطلاعات مورد نیاز را بدون نوشتن JOIN ایی از جداول دیگر، داخل یک سند قرار میدهیم.
هر محدوده تراکنشی میتواند به محدوده تراکنشی دیگری نیز ارجاع داده باشد. برای مثال در RavenDB شمارههای اسناد، یک سری رشته هستند؛ برخلاف بانکهای اطلاعاتی رابطهای که بیشتر از اعداد برای مشخص سازی Id استفاده میکنند. در این حالت برای ارجاع به یک کاربر فقط کافی است برای مثال مقدار خاصیت کاربر یک سند به "users/1" تنظیم شود. "users/1" نیز یک Id تعریف شده در RavenDB است.
مزیت این روش، سرعت واکشی بسیار بالای دریافت اطلاعات آن است؛ دیگر در اینجا نیازی به JOINهای سنگین به جداول دیگر برای تامین اطلاعات مورد نیاز نیست و همچنین در ساختارهای پیچیدهتری مانند ساختارهای تو در تو، دیگر نیازی به تهیه کوئریهای بازگشتی و استفاده از روشهای پیچیده مرتبط با آنها نیز وجود ندارد و کلیه اطلاعات مورد نظر، به شکل یک شیء JSON داخل یک سند حاضر و آماده برای واکشی در طی یک Read هستند.
به این ترتیب میتوان به سیستمهای مقیاس پذیری رسید. سیستمهایی که با بالا رفتن حجم اطلاعات در حین واکشیهای دادههای مورد نیاز، کند نبوده و بسیار سریع پاسخ میدهند.
Denormalization دادهها
اینجا است که احتمالا ذهن رابطهای تربیت شدهی شما شروع به واکنش میکند! برای مثال اگر نام یک محصول تغییر کرد، چطور؟ اگر آدرس یک مشتری نیاز به ویرایش داشت، چطور؟ چگونه یکپارچگی اطلاعاتی که اکنون به ازای هر سند پراکنده شدهاست، مدیریت میشود؟
زمانیکه به این نوع سؤالات رسیدهایم، یعنی Denormalization رخ داده است. در اینجا سندهایی را داریم که کلیه اطلاعات مورد نیاز خود را یکجا دارند. به این مساله از منظر نگاه به دادهها در طی زمان نیز میتوان پرداخت. به این معنا که صحیح است که آدرس مشتری خاصی امروز تغییر کرده است، اما زمانیکه سندی برای او در سال قبل صادر شده است، واقعا آدرس آن مشتری که سفارشی برایش ارسال شده، دقیقا همان چیزی بوده است که در سند مرتبط، ثبت شده و موجود میباشد. بنابراین سند قبلی با اطلاعات قبلی مشتری در سیستم موجود خواهد بود و اگر سند جدیدی صادر شد، این سند بدیهی است که از اطلاعات امروز مشتری استفاده میکند.
ملاحظات اندازههای دادهها
زمانیکه سندها بسیار بزرگ میشوند چه رخ خواهد داد؟ از لحاظ اندازه دادهها سه نوع سند را میتوان متصور بود:
الف) سندهای محدود، مانند اغلب اطلاعاتی که تعداد فیلدهای مشخصی دارند با تعداد اشیاء مشخصی.
ب) سندهای نامحدود اما با محدودیت طبیعی. برای مثال اطلاعات فرزندان یک شخص را درنظر بگیرید. هرچند این اطلاعات نامحدود هستند، اما به صورت طبیعی میتوان فرض کرد که سقف بالایی آن عموما به 20 نمیرسد!
ج) سندهای نامحدود، مانند سندهایی که آرایهای از اطلاعات را ذخیره میکنند. برای مثال در یک سایت فروشگاه، اطلاعات فروش یک گروه از اجناس خاص را درنظر بگیرید که عموما نامحدود است. اینجا است که باید به اندازه اسناد نیز دقت داشت. برای مدیریت این مساله حداقل از دو روش استفاده میشود:
- محدود کردن تعداد اشیاء. برای مثال در هر سند حداکثر 100 اطلاعات فروش یک محصول بیشتر ثبت نشود. زمانیکه به این حد رسیدیم، یک سند جدید ایجاد شده و Id سند قبلی مثلا "products/1" در سند دوم ذکر خواهد شد.
- محدود کردن تعداد اطلاعات ذخیره شده بر اساس زمان
RavenDB برای مدیریت این مساله، مفهوم Includes را معرفی کرده است. در اینجا با استفاده از متد الحاقی Include، کار زنجیر کردن سندهای مرتبط صورت خواهد گرفت.
یک مثال عملی: مدل سازی دادههای یک بلاگ در RavenDB
پس از این بحث مقدماتی که جهت معرفی ذهنیت مدل سازی دادهها در دنیای غیر رابطهای NoSQL ضروری بود، در ادامه قصد داریم مدلهای دادههای یک بلاگ را سازگار با ساختار بانک اطلاعاتی NoSQL سندگرای RavenDB طراحی کنیم.
در یک بلاگ، تعدادی مطلب، نظر، برچسب (گروههای مطالب) و امثال آن وجود دارند. اگر بخواهیم این اطلاعات را به صورت رابطهای مدل کنیم، به ازای هر کدام از این موجودیتها یک جدول نیاز خواهد بود و برای رندر صفحه اصلی بلاگ، چندین و چند کوئری برای نمایش اطلاعات مطالب، نویسنده(ها)، برچسبها و غیره باید به بانک اطلاعاتی ارسال گردد، که تعدادی از آنها مستقیما بر روی یک جدول اجرا میشوند و تعدادی دیگر نیاز به JOIN دارند.
مشکلاتی که روش رابطهای دارد:
- تعداد اعمالی که باید برای نمایش صفحه اول سایت صورت گیرد، بسیار زیاد است و این مساله با تعداد بالای کاربران از دید مقیاس پذیری سیستم مشکل ساز است.
- دادههای مرتبط در جداول مختلفی پراکندهاند.
- این سیستم برای Write بهینه سازی شده است و نه برای Read. (همان بحث گران بودن سخت دیسکها در دهههای قبل که در ابتدای بحث به آن اشاره شد)
مدل سازی سازگار با دنیای NoSQL یک بلاگ
در اینجا چند کلاس مقدماتی را مشاهده میکنید که تعریف آنها به همین نحو صحیح است و نیاز به جزئیات و یا روابط بیشتری ندارند.
namespace RavenDBSample01.BlogModels { public class BlogConfig { public string Id { set; get; } public string Title { set; get; } public string Description { set; get; } // ... more items here } public class User { public string Id { set; get; } public string FullName { set; get; } public string Email { set; get; } // ... more items here } }
اما این طراحی نیز یک مشکل دارد. درست است که ساختار یک صفحه مطلب، از مطالب وبلاگ به همین نحوی است که توضیح داده شد؛ اما در صفحه اول سایت، هیچگاه کامنتهای مطالب درج نمیشوند. بنابراین نیازی نیست تا تمام کامنتها را داخل یک مطلب ذخیره کرد. به این ترتیب برای نمایش صفحه اول سایت، حجم کمتری از اطلاعات واکشی خواهند شد.
public class Post { public string Id { set; get; } public string Title { set; get; } public string Body { set; get; } public ICollection<string> Tags { set; get; } public string AuthorId { set; get; } public string PostCommentsId { set; get; } public int CommentsCount { set; get; } } public class Comment { public string Id { set; get; } public string Body { set; get; } public string AuthorName { set; get; } public DateTime CreatedAt { set; get; } } public class PostComments { public List<Comment> Comments { set; get; } public string LastCommentId { set; get; } }
به این ترتیب برای نمایش صفحه اول سایت، فقط یک کوئری صادر میشود. برای نمایش یک مطلب و کلیه کامنتهای متناظر با آن دو کوئری صادر خواهند شد.
بنابراین همانطور که مشاهده میکنید، در دنیای NoSQL، طراحی مدلهای دادهای بر اساس «سناریوهای Read» صورت میگیرد و نه صرفا طراحی یک مدل رابطهای بهینه سازی شده برای حالت Write.
سورس کامل ASP.NET MVC این بلاگرا که «راکن بلاگ» نام دارد، از GitHub نویسندگان اصلی RavenDB میتوانید دریافت کنید.
- Pre-alpha
این مرحله شامل تمام فعالیتهای انجام شده قبل از مرحله تست میباشد. در این دوره آنالیز نیازمندیها، طراحی نرم افزار، توسعه نرم افزار و حتی تست واحد باشد. در نرم افزارهای سورس باز چندین نسخه قبل از آلفا ممکن است عرضه شوند. - Alpha
این مرحله شامل همه فعالیتها از زمان شروع تست میباشد. البته منظور از تست، تست تیمی و تست خود نرم افزار میباشد. نرم افزارهای آلفا هنوز ممکن است خطا و اشکالاتی داشته باشند و ممکن است اطلاعات شما از بین رود. در این مرحله امکانات جدیدی مرتبا به نرم افزار اضافه میگردد. - Beta
نرم افزار بتا، همه قابلیتهای آن تکمیل شده و خطاهای زیادی برای کامل شدن نرم افزار وجود دارد. در این مرحله بیشتر به تست کاهش تاثیرات به کاربران و تست کارایی دقت میشود. نسخه بتا، اولین نسخهای خواهد بود که بیرون شرکت و یا سازمان در دسترس قرار میگیرد. برخی توسعه دهندگان به این مرحله preview، technical preview یا early access نیز میگویند. - Release candidate
در این مرحله نرم افزار، آماده عرضه به مصرف کنندگان است و نرم افزارهایی مثل سیستم عاملهای ویندوز در دسترس تولید کنندگان قرار گرفته تا با جدیدترین سخت افزار خود یکپارچه شوند. -
General availability (GA)
در این مرحله، عرضه عمومی نرم افزار و بازاریابی و فروش نرم افزار مد نظر است و علاوه بر این تست امنیتی و در نرم افزارهای خیلی بزرگ عرضه جهانی صورت میگیرد
- Major Version
وقتی افزایش مییابد که تغییرات قابل توجهی در نرم افزار ایجاد شود - Minor Version
وقتی افزایش یابد که ویژگی جزئی یا اصلاحات قابل توجهی به نرم افزار ایجاد شود. - Build Number
به ازای هر بار ساخته شدن پروژه افزایش مییابد. - Revision
وقتی افزایش مییابد که نواقص و باگهای کوچکی رفع شوند.
major.minor[.build[.reversion]]
major.minor[.maintenance[.build]]
به عنوان یک راه حل، مجموعهی سادهای از قوانین و الزامات که چگونگی طراحی شمارههای نسخه و افزایش آن را مشخص میکند، وجود دارد. برای کار کردن با این سیستم، شما ابتدا نیاز به اعلام API عمومی دارید. این خود ممکن است شامل مستندات و یا اجرای کد باشد.
علیرغم آن، مهم است که این API، روشن و دقیق باشد. هنگامیکه API عمومی خود را تعیین کردید، تغییرات برنامه شما بر روی نسخه API عمومی تاثیر خواهد داشت و آنرا افزایش خواهد داد. بر این اساس، این مدل نسخهبندی را در نظر بگیرید: X.Y.Z یعنی (Major.Minor.Patch).
رفع حفرههایی که بر روی API عمومی تاثیر نمیگذارند، مقدار Patch را افزایش میدهند، تغییرات جدیدی که سازگار با نسخه قبلی است، مقدار Minor را افزایش میدهند و تغییرات جدیدی که کاملا بدیع هستند و به نحوی با تغییرات قبلی سازگار نیستند مقدار Major را افزایش میدهند.
- نرمافزارهایی که از نسخه بندی معنایی استفاده میکنند، باید یک API عمومی داشته باشند. این API میتواند در خود کد یا و یا به طور صریح در مستندات باشد که باید دقیق و جامع باشد.
- یک شماره نسخه صحیح باید به شکل X.Y.Z باشد که در آن X،Y و Z اعداد صحیح غیر منفی هستند. X نسخهی Major میباشد، Y نسخهی Minor و Z نسخهی Patch میباشد. هر عنصر باید یک به یک و بصورت عددی افزایش پیدا کند. به عنوان مثال: 1.9.0 -> 1.10.0 -> 1.11.0
- هنگامی که به یک نسخهی Major یک واحد اضافه میشود، نسخهی Minor و Patch باید به حالت 0 (صفر) تنظیم مجدد گردد. هنگامی که به شماره نسخهی Minor یک واحد اضافه میشود، نسخهی Patch باید به حالت 0 (صفر) تنظیم مجدد شود. به عنوان مثال: 1.1.3 -> 2.0.0 و 2.1.7 -> 2.2.0
- هنگامیکه یک نسخه از یک کتابخانه منتشر میشود، محتوای کتابخانه مورد نظر نباید به هیچ وجه تغییری داشته باشد. هر گونه تغییر جدیدی باید در قالب یک نسخه جدید انتشار پیدا کند.
- نسخهی Major صفر (0.Y.Z) برای توسعهی اولیه است. هر چیزی ممکن است در هر زمان تغییر یابد. API عمومی را نباید پایدار در نظر گرفت.
- نسخه 1.0.0 در حقیقت API عمومی را تعریف میکند. چگونگی تغییر و افزایش هر یک از نسخهها بعد از انتشار این نسخه، وابسته به API عمومی و تغییرات آن میباشد.
- نسخه Patch یا (x.y.Z | x > 0) فقط در صورتی باید افزایش پیدا کند که تغییرات ایجاد شده در حد برطرف کردن حفرههای نرمافزار باشد. برطرف کردن حفرههای نرمافزار شامل اصلاح رفتارهای اشتباه در نرمافزار میباشد.
- نسخه Minor یا (x.Y.z | x > 0) فقط در صورتی افزایش پیدا خواهد کرد که تغییرات جدید و سازگار با نسخه قبلی ایجاد شود. همچنین این نسخه باید افزایش پیدا کند اگر بخشی از فعالیتها و یا رفتارهای قبلی نرمافزار به عنوان فعالیت منقرض شده اعلام شود. همچنین این نسخه میتواند افزایش پیدا کند اگر تغییرات مهم و حیاتی از طریق کد خصوصی ایجاد و اعمال گردد. تغییرات این نسخه میتواند شامل تغییرات نسخه Patch هم باشد. توجه به این نکته ضروری است که در صورت افزایش نسخه Minor، نسخه Patch باید به 0 (صفر) تغییر پیدا کند.
- نسخه Major یا (X.y.z | X > 0) در صورتی افزایش پیدا خواهد کرد که تغییرات جدید و ناهمخوان با نسخه فعلی در نرمافزار اعمال شود. تغییرات در این نسخه میتواند شامل تغییراتی در سطح نسخه Minor و Patch نیز باشد. باید به این نکته توجه شود که در صورت افزایش نسخه Major، نسخههای Minor و Patch باید به 0 (صفر) تغییر پیدا کنند.
- یک نسخه قبل از انتشار میتواند توسط یک خط تیره (dash)، بعد از نسخه Patch (یعنی در انتهای نسخه) که انواع با نقطه (dot) از هم جدا میشوند، نشان داده شود. نشانگر نسخه قبل از انتشار باید شامل حروف، اعداد و خط تیره باشد [0-9A-Za-z-]. باید به این نکته دفت داشت که نسخههای قبل از انتشار خود به تنهایی یک انتشار به حساب میآیند اما اولویت و اهمیت نسخههای عادی را ندارد. برای مثال: 1.0.0-alpha ، 1.0.0-alpha.1 ، 1.0.0-0.3.7 ، 1.0.0-x.7.z.92
- یک نسخه Build میتواند توسط یک علامت مثبت (+)، بعد از نسخه Patch یا نسخه قبل از انتشار (یعنی در انتهای نسخه) که انواع آن با نقطه (dot) از هم جدا میشوند، نشان داده شود. نشانگر نسخه Build باید شامل حروف، اعداد و خط تیره باشد [0-9A-Za-z-]. باید به این نکته دقت داشت که نسخههای Build خود به تنهایی یک انتشار به حساب میآیند و اولویت و اهمیت بیشتری نسبت به نسخههای عادی دارند. برای مثال: 1.0.0+build.1 ، 1.3.7+build.11.e0f985a
- اولویتبندی نسخهها باید توسط جداسازی بخشهای مختلف یک نسخه به اجزای تشکیل دهنده آن یعنی Minor، Major، Patch، نسخه قبل از انتشار و نسخه Build و ترتیب اولویت بندی آنها صورت گیرد. نسخههای Minor، Major و Patch باید بصورت عددی مقایسه شوند. مقایسه نسخههای قبل از انتشار و نسخه Build باید توسط بخشهای مختلف که توسط جداکنندهها (نقطههای جداکننده) تفکیک شده است، به این شکل سنجیده شود:
بخشهایی که فقط حاوی عدد هستند، بصورت عددی مقایسه میشوند و بخشهایی که حاری حروف و یا خط تیره هستند بصورت الفبایی مقایسه خواهند شد.
بخشهای عددی همواره اولویت پایینتری نسبت به بخشهای غیر عددی دارند. برای مثال:
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0-rc.1+build.1 < 1.0.0 < 1.0.0+0.3.7 < 1.3.7+build < 1.3.7+build.2.b8f12d7 < 1.3.7+build.11.e0f985a
منبع نسخه بندی معنایی : semver.org
مدتی هست که در حال تهیه یک کتابخانه گزارشگیری بر پایه iTextSharp هستم. برای تهیه backlog هم چه جایی بهتر از بررسی سؤالات موجود در انجمنها؛ چیزی مثل این:
بله، تاپیکی با 13 صفحه که حتی یک مورد از درخواستهای آن هم دارای پاسخ نبود؛ اما باز هم کاربران با علاقه هرچه تمامتر یا میدونید، از روی عجز درخواستشون رو مطرح میکردند و کسی نبود که جواب بده. حقیقتش این است که مشکل از افراد نیست یا اینکه «کسی نبود» یا «کسی نخواست» که جواب بده. مشکل این است که اکثر برنامههای گزارشگیری یا گزارش سازی موجود در حد یک Demo ware هستند. «نمیتونند» با مشکلات واقعی کاری موجود (در طی 13 صفحه که ذکر شد) راحت کنار بیان و راه حل بدرد بخوری رو ارائه بدن.
تعریف جدول در openOffice , و پرکردن فیلدهای آن در itextSharp
- جداول تعریف شده در Open office پویا نیستند. این نوع جداول رو باید با کدنویسی توسط خود iTextSharp از صفر تولید کنید (^). یا اینکه گزارش رو کاملا با PdfReport طراحی کنید.