مطالب
jQuery Tips #2
چگونگی استفاده از Cookie در jQuery
در این پست قصد دارم نحوه‌ی کاربا Cookie را با استفاده از jQuery برسی کنم و در پست بعدی یک مثال عملی را برسی می‌کنیم.

همانطور که می‌دانید کوکی یکی از اشیاء بسیار مهم برای نگه داری داده‌ها در بحث وب می‌باشد که یک فایل متنی است که سمت Client ذخیره می‌شود. و ما زمانی که از کتابخانه jQuery استفاده می‌کنیم خیلی مهم است که بدانیم چگونه باید با Cookie‌ها کار کرد.

برای کار با کوکی‌ها در jQuery باید از Plugin ‌های موجود استفاده کرد . برای ایجاد یک Cookie ابتدا فایل jQuery و سپس این کتابخانه را به صفحه مورد نظر اضافه نموده و کد زیر را برای ایجاد یک کوکی می‌نویسیم
<script src="jquery-1.7.1.min.js" type="text/javascript"></script>
    <script src="jquery.cookie.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $.cookie("TestCookie", "Test Cookie By Mohsen Bahrzadeh ");
        });
    </script>
در اینجا یک کوکی با نام TestCookie با مقدار Test Cookie By Mohsen Bahrzadeh ایجاد می‌کنیم
حال پروژه را اجرا می‌کنیم. و در تصویر زیر مشاهده می‌کنید که کوکی ما ایجاد شده است

یکی از آیتم‌های بسیار مهم در کوکی‌ها تعریف زمان انقضاء کوکی است برای ست کردن تاریخ از کد زیر استفاده می‌کنیم
  $(function () {
            $.cookie("TestCookie", "Test Cookie By Mohsen Bahrzadeh ", { expires: 7 });
        });
و برای خواندن مقدار کوکی از کد زیر استفاده می‌کنیم
 $(function () {
          
            alert($.cookie("TestCookie"));
        });
و برای حذف کوکی از کد زیر استفاده می‌کنیم

 $(function () {

            $.cookie("TestCookie", null);
        });

مطالب
تاریخ شمسی برای blogger !

تاریخ میلادی بلاگر واقعا روی اعصاب بود! این مشکل با استفاده از jQuery به صورت زیر قابل حل است.

تاریخ انگلیسی بلاگر به صورت زیر است:
البته در قسمت تنظیمات تاریخ بلاگ ، فرمت را به این صورت انتخاب کردم تا بدون مشکل تبدیل شود.
<h2 class='date-header'>2008/12/17</h2>

یعنی ما باید متن هرچی heading شروع شده با h2 و دارای کلاس date-header را پیدا کنیم و بعد معادل فارسی آن‌را جایگزین کنیم.
این‌کار را با استفاده از jQuery به صورت زیر می‌توان انجام داد:
<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js' type='text/javascript'></script>
<script src='http://vahid.nasiri.googlepages.com/farsidate.js' type='text/javascript'></script>
<script type='text/javascript'>
$(document).ready(function() {
$("h2.date-header").each(function() {
var obj = $(this);
obj.html(ToPersianDate(new Date(obj.text())));
});

$("a[title='comment permalink']").each(function(){
var obj = $(this);
obj.html(ToPersianDateLong(obj.text()));
});

$("a.post-count-link").each(function() {
var obj = $(this);
obj.html(getBloggerPMonthNames(obj.text()));
});

$("a.post-count-link").each(function() {
var obj = $(this);
obj.html(getBloggerPYear(obj.text()));
});
});
</script>

قسمت ویرایش html چیدمان وبلاگ را باید ویرایش و چند سطر بالا را به آن اضافه کرد (بعد از title صفحه).

پ.ن.
Farsidate.js برای تبدیل تاریخ میلادی به شمسی جاوا اسکریپتی از اینجا قرض گرفته شد.

به روز رسانی،
  • نام ماه و سال سمت راست صفحه هم فارسی شد.
  • تاریخ کامنت‌ها هم فارسی شد.
مطالب
امکان تغییر رشته‌ی اتصالی به بانک اطلاعاتی در EF Core در زمان اجرای برنامه
تغییر پویای رشته‌ی اتصالی به بانک اطلاعاتی در نگارش‌های پیشین EF، مشکل بودند که نمونه‌هایی از آن را پیشتر در مطالب زیر مشاهده کرده‌اید:
- «تنظیم رشته اتصالی Entity Framework به بانک اطلاعاتی به وسیله کد»
- «استفاده از چندین بانک اطلاعاتی به صورت همزمان در EF Code First»

اما EF Core نه تنها این مشکل را پوشش را داده‌است، بلکه امکان تزریق وابستگی‌ها و استفاده‌ی از سرویس‌های مختلف را نیز در این حین، پیش بینی کرده‌است که در ادامه جزئیات آن‌را مرور می‌کنیم.


نیاز به تغییر رشته‌ی اتصالی به بانک اطلاعاتی در زمان اجرا

دلایل نیاز به امکان تغییر رشته‌ی اتصالی در زمان اجرا شامل موارد زیر هستند:
- در برنامه‌هایی کمی پیچیده‌تر و سابقه دار، ممکن است عملیات تجاری یکسال را در بانک اطلاعاتی سال 98 و دیگری را در بانک اطلاعاتی سال 99 ثبت کنید. در این حالت کاربران باید بتوانند در زمان اجرا به هر بانک اطلاعاتی که پیشتر با آن کار کرده‌اند، متصل شده و از آن استفاده کنند.
- یکی از روش‌های پیاده سازی برنامه‌های چند مستاجری، داشتن یک بانک اطلاعاتی مجزا، به ازای هر مستاجر است. در این حالت نیز تک برنامه‌ی ما باید بتواند بر اساس Id مشتری، بانک اطلاعاتی متناظری را در زمان اجرا انتخاب کند.
- نیاز به داشتن چندین context در برنامه و کار با بانک‌های اطلاعاتی متفاوت در زمان اجرا؛ مانند کار با SQL Server، اوراکل و یا SQLite


روش تغییر رشته‌ی اتصالی به بانک اطلاعاتی در EF Core در زمان اجرای برنامه

اگر به روش ثبت متداول سرویس DbContext برنامه و پروایدر آن دقت کنیم:
services.AddDbContext<ApplicationDbContext>(options =>
      options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")
));
یک action delegate قابل مشاهده‌است. کار این اکشن، تنظیم پروایدر و تمام نیازهای یک رشته‌ی اتصالی به بانک اطلاعاتی، جهت شروع به کار با Context برنامه است. نکته‌ی مهمی که در اینجا وجود دارد، فراخوانی هرباره‌ی این action، به ازای هر اتصال تشکیل شده‌است. یعنی کدهای داخل این action delegate کش نمی‌شوند و همین مساله امکان تغییر پویای آن‌ها را میسر می‌کند.

یک نکته: چون این اطلاعات کش نمی‌شوند، اگر رشته‌ی اتصالی شما ثابت است (و نیازی به تغییر آن در زمان اجرای برنامه نیست)، محل تامین آن‌را به پیش از سطر services.AddDbContext انتقال دهید و فقط نتیجه‌ی محاسبه شده‌ی نهایی را استفاده کنید تا کارآیی برنامه افزایش یابد؛ در غیراینصورت فراخوانی Configuration.GetConnectionString مدام تکرار خواهد شد.


دریافت یک قالب قابل تغییر از تنظیمات برنامه و تغییر آن با هدرهای درخواست رسیده‌ی به آن

فرض کنید قالب رشته‌ی اتصالی برنامه در فایل appsettings.json به صورت زیر است:
"ConnectionStrings": {
    "ConnectionTemplate": "Data Source=.;Initial Catalog={db_Name};Integrated Security=True",
}
و db_Name آن قرار است برای مثال از یک query string، سشن، کوکی و یا فیلد خاصی در هدر HTTP رسیده تامین شود. برای مثال سال مالی انتخابی و یا شماره مستاجر انتخابی به صورت یک فیلد خاص HTTP به سمت برنامه ارسال می‌شوند.
بنابراین اکنون نیاز است به ازای هر درخواست رسیده بتوان به سرویس IHttpContextAccessor و شیء HttpContext.Request جاری دسترسی یافت و سپس از هدرهای رسیده، برای مثال هدر ویژه‌ی tenantId و یا year را پردازش کرد؛ اما در تعریف services.AddDbContext فوق چگونه می‌توان اینکار را انجام داد؟
خوشبختانه متد services.AddDbContext، دارای یک overload دیگر نیز هست که امکان دسترسی به تمام سرویس‌های جاری سیستم را میسر می‌کند:
services.AddDbContext<ApplicationDbContext>((serviceProvider, dbContextBuilder) =>
{
   var connectionStringTemplate = Configuration.GetConnectionString("ConnectionTemplate");
   var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
   var dbName = httpContextAccessor.HttpContext.Request.Headers["tenantId"].First();
   var connectionString = connectionStringTemplate.Replace("{db_Name}", dbName);
   dbContextBuilder.UseSqlServer(connectionString);
});
همانطور که مشاهده می‌کنید، overload دوم متد services.AddDbContext، امکان ارسال serviceProvider را نیز به این action delegate دارد. پس از آن می‌توان توسط متد GetRequiredService آن به هر سرویس مدنظری که در سیستم ثبت شده‌است، دسترسی یافت و برای مثال در اینجا فیلد هدر سفارشی tenantId را از آن استخراج نمود و در قالب رشته‌ی اتصالی به بانک اطلاعاتی، در زمان اجرا به صورت پویایی جایگزین کرد.
همچنین در صورت نیاز می‌توان UseSqlServer آن‌را نیز در این action delegate به هر پروایدر دیگری در زمان اجرا تغییر داد و از این لحاظ محدودیتی وجود ندارد.

یک نکته: البته برنامه نباید هر tenantId ای را پردازش کند و این خودش می‌تواند تبدیل به یک نقیصه‌ی امنیتی شود. به همین جهت برای مثال می‌توان tenantId را در یک JWT قرار داد و در حین تعیین اعتبار آن و کاربر جاری، این مقدار را نیز بررسی کرد.
مطالب
روش‌های مختلف انجام چند کار به صورت همزمان در C# .NET - قسمت دوم
در قسمت قبل دیدیم که انجام کارهای همزمان، با Objectهایی که به اصطلاح Thread Safe نیستند (مانند DbContext) خروجی چندان جالبی ندارد و برای مثال اگر در یک Service یک DbContext را Inject کنیم (مثلا با Constructor injection) و از آن در متدی استفاده کنیم که آن متد یا با TPL یا RX و ... به صورت چندتایی و همزمان اجرا شود، DbContext به مشکل می‌خورد؛ یعنی نمی‌توان یک وهله از DbContext را بین چند Thread همزمان پردازش موازی، به اشتراک گذاشت.
در کدهای فرضی مثال‌های قسمت قبل، متدی داشتیم با نام DoSomethingWithCustomer که مثلا همان متدی بود که قرار است همزمان اجرا شود. یکی از ساده‌ترین کارهایی که برای رفع این مشکل می‌توان انجام داد، نوشتن چنین کدی است:
public async Task DoSomethingWithCustomer(Customer customer)
{
    using var dbContext = new AppDbContext();

    // ...
}
در این حالت، اگر متد DoSomethingWithCustomer به صورت همزمان اجرا شود، به ازای هر بار اجرا، یک DbContext جدید ساخته میشود؛ پس از یک DbContext مشترک، به صورت همزمان توسط چندین Thread پردازش موازی، استفاده نخواهد شد. ولی مشکل اینجاست که از Dependency Injection برای ساختن DbContext استفاده نشده‌است و ما خودمان، هم new کرده‌ایم و هم Dispose!

این روش ابدا توصیه نمی‌شود؛ برای اینکه Dependency Injection این روزها مسائل خیلی زیادی را مدیریت می‌کند. مثلا DbContext در EF Core وقتی با Dependency Injection ساخته شود، Logging اش هم فعال می‌شود و یا مثلا اگر از متد زیر
services.AddDbContextPool<AppDbContext>();
برای Register کردن DbContext استفاده کرده باشیم، تعدادی DbContext ساخته می‌شود و در پروژه به صورت امن از همان چندتا استفاده می‌شود؛ بجای اینکه Objectها مدام ساخته و از بین برده شوند که این مهم، روی کارآیی تاثیر گذار است.
و یا مثلا برای HttpClient فقط در همین سایت نزدیک به یک دوجین مقاله توضیح داده‌اند که چرا new کردن و Dispose کردن HTTP Client مناسب نیست و بهتر است برای Register کردن HttpClient، از services.AddHttpClient استفاده و از IHttpClientFactory و سایر روشها برای Resolve کردن HttpClient استفاده کنیم که اینها نیز توسط Dependency Injection قابل استفاده هستند. از مسائلی مانند Polly و ... نیز می‌گذریم.
راه حل ایجاد یک Context جدید، تقریبا در تمامی کتابخانه‌های Dependency Injection دیده شده‌است و آن ساختن یک Child Scope است. در ادامه با Microsoft.Extensions.DependencyInjection یک پیاده سازی آن‌را خواهیم داشت؛ ولی مشابه این روش در سایر کتابخانه‌ها همچون Autofac نیز شدنی است.

برای شروع System.IServiceProvider را inject کنید. سپس کد قبلی را به این شکل بنویسید:
(نیاز به ;using Microsoft.Extensions.DependencyInjection در بالای فایل کد است)
public async Task DoSomethingWithCustomer(Customer customer)
{
    using var scope = _serviceProvider.CreateScope();
    var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    var httpClient = scope.ServiceProvider.GetRequiredService<IHttpClientFactory>().CreateClient();
    // ...
}
در این روش فقط نیاز به using نوشتن برای خط اول است؛ یعنی scope که Dispose شود، Objectهایی که به وسیله آن scope ساخته شده‌اند، آزاد خواهند شد. تمام آن چیزهایی را که قبلا با Constructor یا Property injection می‌توانستید بگیرید را اکنون می‌توانید با متد GetRequiredService بگیرید.
همچنین می‌توانید برای داشتن کدی بهتر، یک interface و class را ایجاد کنید و logic مربوطه را در آن سرویس قرار دهید و در آن سرویس با constructor یا property injection از DbContext و HttpClient و سایر سرویس‌ها استفاده کنید و در نهایت آن interface/class را رجیستر کنید و در DoSomethingWithCustomer به کمک child scope، یک object از آن سرویس بسازید و متدش را فراخوانی کنید. برای مثال اگر هدف ساختن Excel تاریخچه خریدهای مشتری است، داریم:
public interface IOrderHistoryService
{
    Task BuildCustomerHistory();
}

public class OrderHistoryService : IOrderHistoryService
{
    private AppDbContext _dbContext;

    public OrderHistoryService(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task BuildCustomerHistory()
    {
        // ...
    }
}
سپس اگر جایی لازم شد روی لیستی از مشتری‌ها این متد اجرا شود، از TPL استفاده می‌کنیم و متدی را فراخوانی می‌کنیم که در آن child scope می‌سازیم و scope.GetRequiredService را برای گرفتن IOrderHistoryService استفاده می‌کنیم. در این روش هم مشکلی در استفاده از Object هایی که thread safe نیستند (مانند DbContext) رخ نخواهد داد و از Dependency injection و مزیت‌های بی‌شمار آن نیز بهره برده‌ایم.
مطالب
امکان یافتن پیش از موعد مشکلات قالب‌های Angular در نگارش 5 آن
مشکلات کامپوننت‌های Angular را چون با زبان TypeScript تهیه می‌شوند، می‌توان بلافاصله در ادیتور مورد استفاده و یا در حین کامپایل برنامه مشاهده کرد؛ اما یک چنین بررسی در مورد قالب‌های HTML ایی آن در زمان کامپایل انجام نمی‌شود و اگر مشکلی وجود داشته باشد، این مشکلات را صرفا در زمان اجرای برنامه در مرورگر می‌توان مشاهده کرد. برای رفع این مشکل و بهبود این وضعیت، در نگارش 5.2.0 فریم ورک Angular (و همچنین Angular CLI 1.7 به بعد)، پرچم جدیدی به تنظیمات کامپایلر آن اضافه شده‌است که با فعالسازی آن، مشکلات binding احتمالی در قالب‌های کامپوننت‌ها را می‌توان یافت. زمانیکه توسط Angular CLI یک برنامه‌ی Angular را در حالت AoT کامپایل می‌کنیم، کامپایلر مراحلی را طی می‌کند که توسط آن کدهای یک قالب کامپوننت، تبدیل به دستور العمل‌هایی قابل اجرای در مرورگر می‌شوند. در طی یکی از این مراحل، کامپایلر قالب‌های Angular، از کامپایلر TypeScript برای اعتبارسنجی عبارت‌های binding استفاده می‌کند. اکنون می‌توان خروجی این مرحله را نیز در حین کار با Angular CLI، مشاهده و مشکلات گزارش شده‌ی توسط آن‌را برطرف کرد.


فعالسازی بررسی مشکلات قالب‌های کامپوننت‌ها

برای فعالسازی بررسی مشکلات قالب‌های کامپوننت‌ها، نیاز است به فایل تنظیمات کامپایلر TypeScript و یا همان tsconfig.json مراجعه کرد و سپس قسمت جدیدی را به آن به نام angularCompilerOptions، افزود:
{
  "compilerOptions": {
    "experimentalDecorators": true,
    ...
   },
   "angularCompilerOptions": {
     "fullTemplateTypeCheck": true,
     "preserveWhiteSpace": false,
     ...
   }
 }
- در اینجا با معرفی خاصیت fullTemplateTypeCheck و تنظیم آن به true، مشکلات موجود در قالب‌ها را در زمان کامپایل برنامه می‌توانید مشاهده کنید.
- البته این خاصیت در حین استفاده‌ی از یکی از دستورات ng serve --aot  و یا  ng build --prod انتخاب می‌شود.
- مقدار این پرچم در نگارش‌های 5x به صورت پیش‌فرض به false تنظیم شده‌است؛ اما در نگارش 6 آن به true تنظیم خواهد شد. بنابراین بهتر است از هم اکنون کار با آن‌را شروع کنید.


یک مثال: بررسی خاصیت fullTemplateTypeCheck

فرض کنید اینترفیس یک مدل را به صورت زیر تعریف کرده‌اید که فقط دارای خاصیت name است:
export interface PonyModel {
   name: string;
}
سپس یک خاصیت عمومی را بر همین مبنا در کامپوننتی، تعریف و مقدار دهی اولیه کرده‌اید:
import { PonyModel } from "./pony";

@Component({
  selector: "app-detect-common-errors-test",
  templateUrl: "./detect-common-errors-test.component.html",
  styleUrls: ["./detect-common-errors-test.component.css"]
})
export class DetectCommonErrorsTestComponent implements OnInit {

  ponyModel: PonyModel = { name: "Pony1" };
اکنون در قالب این کامپوننت، به شکل زیر از این وهله استفاده شده‌است:
 <p>Hello {{ponyModel.age}}

در این حالت اگر fullTemplateTypeCheck فعال شده باشد و دستور ng build --prod را صادر کنیم، به خروجی ذیل خواهیم رسید:
 \detect-common-errors-test.component.html(5,4): : Property 'age' does not exist on type 'PonyModel'.
همانطور که ملاحظه می‌کنید اینبار خطاهای کامپایل فایل html نیز در خروجی کامپایلر ظاهر شده‌است و عنوان می‌کند خاصیت age در اینترفیس PonyModel وجود خارجی ندارد.

برای اینکه بتوانید به حداکثر کارآیی این قابلیت برسید، بهتر است گزینه‌ی strict را در تنظیمات کامپایلر TypeScript روشن کنید و خودتان را به کار با نوع‌های نال نپذیر عادت دهید. به این ترتیب می‌توانید تعداد خطاهای احتمالی بیشتری را پیش از موعد و پیش از وقوع آن‌ها در زمان اجرا، در زمان کامپایل، پیدا و رفع کنید.


یک نکته‌ی تکمیلی
افزونه‌ی Angular Language service نیز یک چنین قابلیتی را به همراه دارد (و حتی در نگارش‌های پیش از 5 نیز قابل استفاده است).
مطالب
Includeهای صرفنظر شده در EF Core
یکی از روش‌های Join نوشتن درEF ، استفاده از Includeها است. اما ... آیا تمام Includeهایی که تعریف شده‌اند ضروری بوده‌اند؟ آیا تمام Joinهای حاصل از Include‌های تعریف شده مورد استفاده قرار گرفته‌اند و بی‌جهت کارآیی برنامه را پایین نیاورده‌اند؟ EF Core برای مقابله با یک چنین مواردی که بسیار هم متداول هستند، پس از بررسی کوئری، سعی می‌کند Includeهای اضافی و بی‌مصرف را حذف کرده و سبب تولید Joinهای اضافی نشود.


یک مثال: بررسی تنظیمات نمایش خطاهای Includeهای صرفنظر شده

موجودیت‌های Blog و Postهای آن‌را درنظر بگیرید:
public class Blog
{
  public int BlogId { get; set; }
  public string Url { get; set; }

  public List<Post> Posts { get; set; }
}

public class Post
{
  public int PostId { get; set; }
  public string Title { get; set; }
  public string Content { get; set; }

  public int BlogId { get; set; }
  public Blog Blog { get; set; }
}

به همراه Context آن که به صورت ذیل تعریف شده‌است:
    public class BloggingContext : DbContext
    {
        public BloggingContext()
        { }

        public BloggingContext(DbContextOptions options)
            : base(options)
        { }

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Demo.Includes;Trusted_Connection=True;");
                optionsBuilder.ConfigureWarnings(warnings => warnings.Log(CoreEventId.IncludeIgnoredWarning));
                optionsBuilder.UseLoggerFactory(new LoggerFactory().AddConsole((message, logLevel) =>
                {
                    return true;
                    /*return logLevel == LogLevel.Debug &&
                           message.StartsWith("Microsoft.EntityFrameworkCore.Database.Command");*/
                }));
            }
        }
    }
در اینجا در تنظیمات ConfigureWarnings، نمایش IncludeIgnoredWarning نیز لحاظ شده‌است. به این ترتیب می‌توان از وقوع Includeهای صرفنظر شده مطلع شد.
همچنین ذکر UseLoggerFactory، به نحوی که مشاهده می‌کنید، یکی دیگر از روش‌های لاگ کردن خروجی‌های EF است. در اینجا اگر true تنظیم شود، تمام اتفاقات مرتبط با EF Core لاگ می‌شوند. اگر می‌خواهید تنها کوئری‌های SQL آن‌را مشاهده کنید، روش فیلتر کردن آن‌ها در سطر بعدی نمایش داده شده‌است.

در این حالت اگر کوئری ذیل را اجرا کنیم:
var blogs = context.Blogs
    .Include(blog => blog.Posts)
    .Select(blog => new
    {
       Id = blog.BlogId,
       Url = blog.Url
    })
    .ToList();
ابتدا خطای ذیل نمایش داده می‌شود:
 warn: Microsoft.EntityFrameworkCore.Query[100106]
The Include operation for navigation '[blog].Posts' is unnecessary and was ignored because the navigation is not reachable in the final query results. See https://go.microsoft.com/fwlink/?linkid=850303 for more information.
سپس کوئری نهایی تشکیل شده نیز به صورت ذیل است:
SELECT [blog].[BlogId] AS [Id], [blog].[Url]
FROM [Blogs] AS [blog]
همانطور که ملاحظه می‌کنید در اینجا خبری از join به جدول posts نیست و یک کوئری ساده را تشکیل داده‌است. چون خروجی نهایی که در Select قید شده‌است، ارتباطی به جدول posts ندارد. به همین جهت، تشکیل join بر روی آن غیرضروری است و با این خطای ذکر شده می‌توان دریافت که بهتر است Include ذکر شده را از کوئری حذف کرد.


یک نکته: اگر تنظیم نمایش IncludeIgnoredWarning را به نحو ذیل به Throw تنظیم کنیم:
 optionsBuilder.ConfigureWarnings(warnings => warnings.Throw(CoreEventId.IncludeIgnoredWarning));
اینبار برنامه با رسیدن به یک چنین کوئری‌هایی، متن اخطار ذکر شده را به صورت یک استثناء صادر خواهد کرد. این حالت در زمان توسعه‌ی برنامه می‌تواند مفید باشد.

کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید: IgnoredIncludes.zip
مطالب
ckeditor در برابر tinymce
این دو ادیتور  یعنی CKEditor و TinyMCE  دو تا از محبوب‌ترین ادیتورهای موجود تحت وب هستند که به صورت متن باز ارائه می‌شوند و از لحاظ قدرت و کارآیی در رده بالایی قرار دارند. همچنین مستندات و api‌های خوبی هم در مورد آنها موجود است؛ ولی در هنگام استفاده خیلی‌ها شاید این سؤال را داشته باشند که کدام ادیتور را انتخاب کنند؟ حتی اگر هر دو ادیتور امکاناتی بیش از نیاز ما را فراهم کنند، باز هم انسان در پی بهترین هاست. 
در این مطلب به مزایا و معایب اشاره نشده و قصد تخریب ادیتور خاصی را نداریم؛ بلکه خصوصیات آن‌ها بررسی شده و در نهایت توسعه دهنده می‌تواند بر اساس این‌ها، ابزار درستی را برای کارش، انتخاب کند.

رابط کاربری یا user interface
ckeditor داری رابط کاربری است که برای همه ادیتورها به صورت پیش فرض وجود دارد و این حالت برای خیلی از کاربرها شناخته شده هست.

در tinymce رابط کاربری به غیر از نوار ابزار میتواند شامل منو هم باشد؛ با اینکه استفاده از منو آنچنان در ادیتور مورد استقبال قرار نمی‌گیرد ولی ممکن است بعضی‌ها این ویژگی را دوست داشته باشند:

برای حذف منو در این ادیتور یا مشخص کردن چه عناصری نمایش داده شود، این کد در سایت سازنده نوشته شده:
// Disable all menus
tinymce.init({
    menubar : false
});

// Configure different order
tinymce.init({ 
    menubar: "tools table format view insert edit"
});

پلاگین‌ها Plugins
وقتی بحث پلاگین پیش کشیده می‌شود، انتخابی بین ساده بودن و اختصاصی بودن پیش خواهد آمد. به نظر می‌رسد پلاگین‌های ck در حالت عادی بیشتر از tiny هستند و امکانات بیشتری را نسبت به tiny ارائه می‌کنند؛ برای مثال به دیالوگ درج تصویر در دو شکل زیر نگاه کنید: در ck شما امکانات بیشتری برای درج یک تصویر دارید تا tiny

آپلود تصویر در ckeditor

CKEditor

tinymce

Tinymce

در نسخه‌های پیشین tiny این دیالوگ‌ها به صورت پنجره‌های popup باز می‌شدند، ولی در تمامی نسخه‌های ck بسیاری از این پنجره‌ها بدین صورت باز نشده و سفارشی هستند. درست است که در ظاهر مثل هم باز می‌شوند ولی مکانیزم و روش باز شدن آن‌ها با یکدیگر متفاوت است و در واقع ck با روشی هوشمندانه و با استفاده از کدهای جاوا اسکریپت و ترکیب لایه‌های html این روش را پیاده سازی می‌کند.

شاید بگویید خوب چه فرقی می‌کند که از چه روشی برای پیاده سازی استفاده شود؟ این مورد از دو جنبه مورد بررسی قرار می‌گیرد:

1 - اگر به مرورگرهایی چون IE دقت کرده باشید، می‌بینید که موقعی‌که یک popup باز می‌شود، به عنوان یک پنجره‌ی جدید باز می‌شود و باعث شلوغ شدن صفحه می‌گردد و بیشتر موجب آزار کاربر می‌گردد.
2- ساخت واقعی یک صفحه popup منجر شدن به ایجاد یک پنجره ای جدید و طی شدن فرآیندهای مربوط به بارگذاری و رندر شدن یک صفحه‌ی جدید می‌باشد؛ ولی در مورد پنجره‌های سفارشی این فرآیندها صورت نمی‌گیرد. این مورد بر روی بسیاری از هاست‌ها خود را به وضوح نمایش می‌دهد و ck در صرفه جویی از زمان، به مراتب بهتر عمل می‌کند. 


از نسخه tinymce 4 به بعد مکانیزم سفارشی بودن دیالوگ‌ها بر روی این ادیتور نیز صورت گرفته است.

کپی و پیست Copy - Paste

بسیاری از کاربران گاهی اوقات مطالب خود را از جایی یا از یک صفحه‌ی وب دیگر به ادیتور کپی می‌کنند. برای مثال در اینجا صفحه‌ی گوگل را به داخل ادیتورها کپی می‌کنیم و به نظر میرسد ckeditor در این مواقع رفتار بهتری نسبت به tiny از خود نشان می‌دهد.

در تاینی لوگوی گوگل دقیقا در وسط قرار گرفته و بعضی موارد خاص مانند فرم‌ها به خوبی نمایش داده می‌شوند؛ ولی در ck شما متن تمیزتری دارید و طوری نشان داده می‌شود که یک css کاربرپسند به آن اضافه شده است.

ckeditor

Ckeditor


انعطاف پذیری Flexibility

هر دو ادیتور در این زمینه به خوبی طراحی شده‌اند و با استفاده از جاوا اسکریپت می‌توان آن‌ها را به سادگی توسعه داد و امکانات دلخواهتان را به آن اضافه کنید ولی در کل ckeditor در  زمینه apiهای برنامه نویسی، پیشروتر از رقیبش tinymce است.

حجم یا سایز ادیتورها Size

با توجه به رشد اینترنت در گوشی‌های همراه و دیگر وسایل همراه، حجم منابع اینترنتی بیش از قبل مورد توجه قرار می‌گیرد. در این زمینه tinymce برگ برنده‌ای داشته و بهتر است بدانید که حجم این ویرایشگر تقریبا نصف رقیبش یعنی ckeditor می‌باشد.

حالا شما با توجه به موارد بالا می‌توانید ادیتور خود را انتخاب کنید.

در اینجا هم می‌توانید فایل‌های مربوط به زبان را نیز دانلود کنید که هر دو از زیان فارسی پشتیبانی می‌کنند.

فایل‌های زبان TinyMCE 

فایل‌های زبان CKeditor 



منابع:

http://www.krizalys.com/article/ckeditor-vs-tinymce  

http://www.tinymce.com/wiki.php/Configuration:menubar

مطالب
مروری بر چند تجربه‌ی کاری با SQLite




SQLite بانک اطلاعاتی سریع، کم حجم و سورس بازی است که استفاده از آن در دات نت فریم ورک بسیار ساده است. فقط کافی است پروایدر مربوط به آن را دریافت کنید و در کدهای قدیمی خود هر جایی مثلا sqlconnection داشتید آن‌را تبدیل به sqliteconnection کنید و امثال آن (به بیان دیگر، پروایدر تهیه شده از معماری ADO.NET پیروی می‌کند و عملا دانش قبلی شما به سادگی قابل استفاده و ارتقاء است). علاوه بر آن پروایدر ADO.NET‌ تهیه شده برای آن، پشتیبانی از Entity framework را هم ارائه می‌دهد.
این دیتابیس تحت سیستم عامل‌های مختلف مهیا است و مهم‌ترین مزیت آن عدم نیاز به نصب آن می‌باشد.

مزایا:
- سورس باز و رایگان
- مهیا بودن آن در سایر پلتفرم‌ها (ویندوز، لینوکس و ...)
- نیازی به نصب ندارد و فقط یک DLL بومی است. این مورد برای کاربرانی که در مدیریت بانک‌های اطلاعاتی پیچیده مشکل دارند، یک مزیت مهم است.
- امکان تشیکل دیتابیس در حافظه. این نکته و توانایی، در آزمون‌های واحد بسیار جالب توجه است. می‌توانید با سرعت بالا دیتابیسی واقعی را در حافظه تشکیل داده، کلیه آزمون‌های واحد خود را اجرا کرده و پس از پایان کار، اثری از دیتابیس و تغییر داده‌ها و مشکلات بازگردانی اطلاعات به حالت اول وجود نخواهد داشت.


ملاحظات:
الف) مرتب سازی SQLite حساس به حروف کوچک و بزرگ است.
برای برگشت به عادت متداولی که وجود دارد می‌شود به صورت زیر عمل کرد:
select f1 from tbl1 order by f1 COLLATE NOCASE
یک COLLATE NOCASE اضافه شده است.

ب) رعایت نکات مرتبط با سیستم‌های 64 بیتی
در مورد سیستم‌های 64 بیتی و دات نت قبلا مطلبی را نوشته بودم : {+}. این مطلب دقیقا اینجا کاربرد پیدا می‌کند، از این لحاظ که SQLite یک بانک اطلاعاتی Native است. اگر برنامه‌ی دات نت شما برای حالت Any CPU تهیه شده است، در سیستم‌های 32 بیتی نیاز است تا DLL مرتبط SQLite را توزیع کنید و در سیستم‌های 64 بیتی DLL مرتبط 64 بیتی آن نیاز خواهد بود. در غیراینصورت برنامه‌ی شما در بدو امر کرش کرده و اجرا نخواهد شد.

مشکلات:
الف) کلید خارجی بی خاصیت!
SQLite از کلید خارجی پشتیبانی می‌کند اما آن‌را اعمال نمی‌کند! برای اینکه کلید خارجی را اعمال کنید باید خودتان تریگر بنویسید تا این‌کار را انجام دهد.

ب) پشتیبانی در حد صفر از مباحث همزمانی و تردینگ.
اگر برنامه شما مالتی ترد است، در بد مخمصه‌ای گرفتار شده‌اید. مدام با پیغام database is locked مواجه خواهید شد. (چه انتظاری داشتید؟ یک dll کمتر از 2 مگابایت که قرار نیست کار غول‌های دیتابیسی را انجام دهد)
بنابراین اصلا تصورش را هم نکنید که از این دیتابیس به عنوان بانک اطلاعاتی یک سایت (و محیط‌های چند کاربره) بتوان استفاده کرد و کاربران دچار مشکل نشوند.

ج)حجم بالای دیتابیس و عدم کش
از مباحث caching که در دیتابیس‌های معظم دیگر به صورت توکار وجود دارد خبری نیست. برای مثال اگر یک کوئری قرار است تعدادی را شمارش نماید، حاصلی کش نشده و اگر صدبار هم به صورت متوالی آن‌را فراخوانی کنید باز هم از نو محاسبات آن انجام خواهد شد.
این مورد در حجم بالای دیتابیس واقعا مهم است و نمودش را با دیتابیسی با حجم بالای یک گیگ به وضوح مشاهده خواهید. افت کارآیی و همچنین قرچ و قرچ مداوم هارد دیسک سیستم! (چون به کش رجوع نمی‌شود)

د) امنیت
روی بانک‌های اطلاعاتی اکسس حداقل می‌توان یک کلمه‌ی عبور را قرار داد (که در کسری از ثانیه قابل شکستن است!). در SQLite استاندارد هیچ خبری از این مباحث نبوده و امنیت را باید خودتان تامین کنید. (البته یک نسخه‌ی تجاری هم از این بانک اطلاعاتی با پشتیبانی از رمزنگاری اطلاعات موجود است : +)

ه) مرتب سازی فارسی
هر چند SQLite هیچ مشکلی در ثبت اطلاعات یونیکد و خصوصا متون فارسی ندارد، اما با مرتب سازی کلمات یونیکد مشکل داشته و بر اساس کد اسکی آن‌ها عمل می‌کند. هر چند امکان تعریف Collation سفارشی در آن ممکن است : + (البته ممکن بودن با موجود بودن متفاوت است)


نتیجه گیری:
- SQLite برای دیتابیسی تا حدود یک گیگ که فقط یک نفر قرار باشد از آن استفاده کند انتخاب بسیار مناسبی است (برای مثال فایرفاکس از آن برای ذخیره سازی تنظیمات خودش استفاده می‌کند).

اشتراک‌ها
میزان محبوبیت زبان‌های برنامه نویسی بر اساس GitHub pull requests

The gist of the story goes as follow:

  1. The most popular languages are JavaScript/TypeScript and Python with roughly 20% of all pull requests each. In effect, if you put JavaScript/TypeScript and Python together, you get about 40% of all pull requests.
  2. Then you get the second tier languages: Java and Scala, C/C++, and Go. They all are in the 10% to 15% range.
  3. Finally, you have PHP, Ruby and C# that all manage to get about 5% of all pull requests.
  4. Other languages are typically far below 5%. 
میزان محبوبیت زبان‌های برنامه نویسی بر اساس GitHub pull requests
اشتراک‌ها
نکات افزایش پرفرمنس برای وبسایت

In this post, I'll talk about what the different things I've made on this website to improve the performance. Some of the optimizations are just some configuration flags to turn on, others require more changes in your code.

  • Enable HTTP/2
  • Enable TLS 1.3
  • Compress responses using Brotli or gzip
  • Add caching information
  • Optimize JavaScript / CSS files
  • Reduce the number of redirections
  • Optimize images
  • Move your servers near your visitors (GeoDNS, CDN)
  • Resource Hints: Prefetch resources
  • Remove unused resources / features
  • Minify HTML
  • Optimize JavaScript code
  • Automate almost everything! 
نکات افزایش پرفرمنس برای وبسایت