اشتراک‌ها
پشتیبانی رسمی RethinkDB از Windows

RethinkDB is an open source NoSQL JSON database designed for web apps. Its main differentiator is that you can tell it to continuously push updated query results to applications in real time, rather than having your app poll for changes. 

پشتیبانی رسمی RethinkDB از Windows
اشتراک‌ها
طراحی افزونه پذیر و ماژولار در Asp.net core

ExtCore allows you to decouple your application into the modules (or extensions) and reuse that modules in other applications in various combinations. Each ExtCore extension may consist of one or more projects and each project may include everything you want (as any other ASP.NET Core project). Controllers, view components, views (added as resources and/or precompiled), static content (added as resources) will be resolved automatically. These projects (extension pieces) may be added to the application directly as dependencies in project.json of your main application project (as source code or NuGet packages), or by copying compiled DLL-files to the Extensions folder. ExtCore supports both of these approaches out of the box and at the same time. 

طراحی افزونه پذیر و ماژولار در Asp.net core
اشتراک‌ها
Template string types در TypeScript 4.1

Anders Hejlsberg has been working on interesting new type system capabilities: template string types as well as mapped type as clauses. Both features are slated for the upcoming TypeScript 4.1 release. 

Template string types در TypeScript 4.1
اشتراک‌ها
Visual Studio 2017 15.5.7 منتشر شد

Team Explorer support for TLSv1.2

  • We have updated the Git and the Git Credential Manager components that ship in Visual Studio.
  • The optional Git for Windows component has also been updated.
  • This update allows Git to connect to services that have deprecated support for TLSv1 and TLSv1.1 in favor of TLSv1.2.

Issues Fixed in this Release

These are the customer-reported issues addressed in this release:

  • Projects targeting .NET Core 2.1 or newer are not supported by Visual Studio 2017 version 15.5.
  • Fixed issue where installation of the SDK for .NET Core 2.1 or newer would cause the option to create ASP.NET Core 2.0 Web applications to disappear. 
Visual Studio 2017 15.5.7 منتشر شد
مطالب
شرط گذاری روی Include ها در EF Core 5x
سناریویی را در نظر بگیرید که میخواهید لیست Blog‌ها را به همراه Post هایشان که شامل کلمه‌ی خاصی است، به کلاینت باز گردانید. در این حالت احتمالا چنین کدی به نظرتان خواهد آمد:
// -- FilteredInclude_EFCore5
var list = dbContext.Blogs
    .AsNoTracking()
    .Include(p => p.Posts.Where(p => p.Title.Contains("test title")))
    .ToList();
return Json(list);
این کد تا قبل از EFCore 5.0 پیش نمایش 3، به خطای زیر منجر میشود؛ چرا که EFCore از شرط گذاری روی Include‌ها پشتیبانی نمی‌کند:
System.InvalidOperationException: 'Lambda expression used inside Include is not valid.'
پس مجبوریم همه‌ی رکورد‌های Include را از دیتابیس خوانده و سپس آنها را در حافظه فیلتر کنیم:
// -- NonFilteredInclude
var list = dbContext.Blogs
    .AsNoTracking()
    .Include(e => e.Posts)
    .ToList();
list.ForEach(p => p.Posts = p.Posts.Where(p => p.Title.Contains("test title")).ToList());
این روش سربار بسیار زیادی دارد و بسته به تعداد رکورد‌ها و ستون‌های Post، حجم زیادی از دیتای غیر لازم را از دیتابیس میخواند و تخصیص حافظه (memory allocation) اضافی و زیادی را به همراه دارد. مثلا اگر 100 Blog داشته باشیم که هرکدام 100 Post داشته باشند و فقط یکی از Post‌ها شرط مورد نظر را داشته باشد، بدین ترتیب 100 * 100 منهای 1 رکورد اضافی واکشی خواهد شد؛ یعنی برابر ‭9,999‬! (می توان با لحاظ کردن تعداد و حجم ستون‌های اضافی نیز وخامت اوضاع را درک کرد)
همچنین اگر به صورت غیر read-only (عدم استفاده از AsNoTracking)  داده‌ها را لود کرده باشید، با شرطی که داخل ForEach اعمال می‌شود، رکوردهایی که فیلتر میشوند به صورت Deleted در ChangeTracker علامت گذاری میشوند که میتواند مشکل ساز نیز باشد.
برای حل این مشکل چندین روش وجود دارد:
1- توسط یک تایپ دلخواه (anonymous یا dto) واکشی را به صورت Projection انجام دهیم و Post‌ها را فیلتر کنیم:
// -- Projection_Manually
var list = dbContext.Blogs
    .AsNoTracking()
    .Select(p => new
    {
        p.Id,
        p.Name,
        Posts = p.Posts.Where(p => p.Title.Contains("test title")).ToList()
    }).ToList();
این دستور، کوئری SQL زیر را تولید میکند:
SELECT [b].[Id], [b].[Name], [t].[Id], [t].[BlogId], [t].[Description], [t].[Title]
FROM [Blogs] AS [b]
LEFT JOIN (
    SELECT [p].[Id], [p].[BlogId], [p].[Description], [p].[Title]
    FROM [Posts] AS [p]
    WHERE CHARINDEX(N'test title', [p].[Title]) > 0
) AS [t] ON [b].[Id] = [t].[BlogId]
ORDER BY [b].[Id], [t].[Id]
معایب این روش:
  • در صورت نیاز به ویرایش (عدم استفاده از AsNoTracking) بدلیل استفاده از anonymous بجای Blog، هیچ شیء Blog ایی در ChangeTracker ثبت نخواهد شد، ولی اشیا Post در ChangeTracker ثبت می‌شوند. در نتیجه تنها 1 شیء در ChangeTracker اضافه خواهد شد.
  • کد نویسی را کثیف میکند؛ مخصوصا اگر نیاز به شرط گذاری بر روی چندین Navigation Collection تو در تو را داشته باشید.
برای جلوگیری از این کثیف شدن میتوان از قابلیت Projection کتابخانه‌ی AutoMapper استفاده کرد. کوئری تولید شده و عملکرد آن عینا مشابه همین روش است، ولی کد تمیز‌تری را موجب می‌شود ( از نظر سرعت، مقدار کمی کند‌تر است. انتهای مقاله، بنچمارک آن را میتوانید مشاهده کنید)

2- از قابلیت IncludeFilter کتابخانه‌ی  Z.EntityFramework.Plus.EFCore استفاده کنیم.
این کتابخانه امکانات بسیار مفیدی را ارائه میدهد و شخصا برای پروژه‌های واقعی و بزرگ آن را پیشنهاد میدهم. اگر از امکانات آن بجا استفاده شود، تاثیر بسیار زیادی را بر روی پرفرمنس پروژه خواهد گذاشت (توصیه میکنم حتما داکیومنت آن را مطالعه کنید). این کتابخانه کاملا رایگان است و از EFCore و EF6 (در یک پکیج جداگانه) پشتیبانی میکند. شرکت مالک آن (ZZZ) یک کتابخانه‌ی دیگر را نیز به نام  Z.EntityFramework.Extensions.EFCore دارد که امکانات بیشتری را ارائه میدهد؛ ولی رایگان نیست.
در این روش خواهیم داشت:
// -- IncludeFilter_EFCorePlus
var list = dbContext.Blogs
    .AsNoTracking()
    .IncludeFilter(e => e.Posts.Where(p => p.Title.Contains("test tile")))
    .ToList();
این دستور کوئری SQL زیر را تولید میکند:
-- EF+ Query Future: 1 of 2
SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
;

-- EF+ Query Future: 2 of 2
SELECT [t].[Id], [t].[BlogId], [t].[Description], [t].[Title]
FROM [Blogs] AS [b]
INNER JOIN (
    SELECT [p].[Id], [p].[BlogId], [p].[Description], [p].[Title]
    FROM [Posts] AS [p]
    WHERE CHARINDEX(N'test title', [p].[Title]) > 0
) AS [t] ON [b].[Id] = [t].[BlogId]
;
  • همانطور که می‌بینید این دستور، 2 کوئری را اجرا میکند. سرعت آن از روش قبلی کمی کند‌تر است و memory allocation بیشتری را انجام میدهد.
  • در صورت عدم استفاده از AsNoTracking، اشیاء Blog را نیز ثبت میکند؛ درنتیجه تعداد 101 آبجکت (100 Blog و 1 Post) به ChangeTracker اضافه خواهند شد.
  • کد نویسی تمیزتر و راحت‌تری در سمت سی شارپ دارد. 
  • این روش در EF6 نیز قابل استفاده است.

3- کمبود این قابلیت در EFCore بسیار حس میشد (در NHibernate از قدیم این امکان وجود داشت) تا اینکه نهایتا در EFCore 5.0 پیش نمایش 3 (آخرین نسخه‌ی در حال حاضر) این قابلیت به EFCore اضافه شده‌است.
برای استفاده از آن نیاز به هیچ کد اضافه‌ای نیست و به صورت معمول میتوان از متد Include، همراه با شرط استفاده کرد:
// -- FilteredInclude_EFCore5
var list = dbContext.Blogs
    .AsNoTracking()
    .Include(p => p.Posts.Where(p => p.Title.Contains("test title")))
    .ToList();
این دستور، کوئری SQL زیر را تولید میکند:
SELECT [b].[Id], [b].[Name], [t].[Id], [t].[BlogId], [t].[Description], [t].[Title]
FROM [Blogs] AS [b]
LEFT JOIN (
    SELECT [p].[Id], [p].[BlogId], [p].[Description], [p].[Title]
    FROM [Posts] AS [p]
    WHERE CHARINDEX(N'test title', [p].[Title]) > 0
) AS [t] ON [b].[Id] = [t].[BlogId]
ORDER BY [b].[Id], [t].[Id]
  • این روش بسیار بهینه است و از روش قبلی (دوم) کمی سریع‌تر بوده و memory allocation کمتری (نزدیک به روش اول) دارد.
  • در صورت عدم استفاده از AsNoTracking، مانند قبلی عمل میکند؛ درنتیجه تعداد 101 آبجکت به ChangeTracker اضافه خواهند شد. 
  • کد نویسی تمیزتر و راحت‌تری در سمت سی شارپ دارد.

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