اشتراک‌ها
آزادسازی خودکار منابع در TypeScript 5.2 با واژه‌ی کلیدی جدید using

A Look at TypeScript 5.2's New Keyword: using — using brings something akin to Python’s with context management into TypeScript with a way to automatically run a function when an object leaves scope. You could use it to shut down a database connection, close file handles, etc. 

آزادسازی خودکار منابع در TypeScript 5.2 با واژه‌ی کلیدی جدید using
نظرات مطالب
تغییر عملکرد و یا ردیابی توابع ویندوز با استفاده از Hookهای دات نتی
همه این روش هارو دیدم که بعضی هاشون از جاوااسکریپت هم استفاده میکنند. ولی این روشها مشکلات زیادی دارن. شیرپوینت فقط یک سایت ساده نیست، Object Model داره کلی سرویس داره، فرم اینوپس و... . شیرپوینت میتونه همزمان از چند تا تقویم پشتیبانی بکنه. این روش‌ها خیلی سرسری مشکلو حل میکنن درکل نمیشه با این روشها یک راه کار درست حسابی ارائه داد.
اگه بشه از NET Profiling API. استفاده کنم میتونم تقویم  Exchange Outlook Web App  رو هم شمسی کنم. 
مطالب
شروع به کار با DNTFrameworkCore - قسمت 6 - پیاده‌سازی عملیات CRUD موجودیت‌ها با استفاده از ASP.NET Core MVC
به عنوان آخرین قسمت مرتبط با تفکر مبتنی‌بر CRUD‏ ‎(‎CRUD-based thinking)‎، روش پیاده‌سازی عملیات CRUD موجودیت‌ها را با استفاده از ASP.NET Core MVC و افزونه jquery-unobtrusive-ajax بررسی خواهیم کرد. 


برای شروع لازم است بسته نیوگت زیر را نصب کنید: 
PM> Install-Package DNTFrameworkCore.Web

قراردادها، مفاهیم و نکات اولیه 
  1. Refactor کردن فرم‌های ثبت و ویرایش مرتبط با یک Aggregate، به یک PartialView که با یک ViewModel کار می‌کند. برای موجودیت‌های ساده و پایه، همان Model/DTO، به عنوان Model متناظر با یک ویو یا به اصطلاح ViewModel استفاده می‌شود؛ ولی برای سایر موارد، از مدلی که نام آن با نام موجودیت + کلمه ModalViewModel یا FormViewModel تشکیل می‌شود، استفاده خواهیم کرد.
  2. یک فرم، در قالب یک پارشال‌ویو، به صورت Ajaxای با استفاده از افزونه  jquery-unobtrusive-ajax بارگذاری شده و به سرور ارسال خواهد شد.
  3. یک فرم براساس طراحی خود می‌تواند در قالب یک مودال باز شود، یا به منظور inline-editing آن را بارگذاری و به قسمتی از صفحه که مدنظرتان می‌باشد اضافه شود.
  4. وجود ویو Index به همراه پارشال‌ویو ‎_List برای نمایش لیستی و یک پارشال‌ویو برای عملیات ثبت و ویرایش الزامی ‌می‌باشد. البته اگر از مکانیزمی که در مطلب « طراحی یک گرید با jQuery Ajax و ASP.NET MVC به همراه پیاده سازی عملیات CRUD»  مطرح شد، استفاده نمی‌کنید و نیاز دارید تا اطلاعات صفحه‌بندی شده، مرتب شده و فیلتر شده‌ای را در قالب JSON دریافت کنید، از اکشن‌متد ReadPagedList کنترلر پایه استفاده کنید.

مثال اول: پیاده‌سازی BlogsController و طراحی فرم ثبت و ویرایش
کار با ارث‌بری از کنترلر پایه طراحی شده در زیرساخت به شکل زیر آغاز می‌شود:
public class BlogsController : CrudController<IBlogService, int, BlogModel>
{
    public BlogsController(IBlogService service) : base(service)
    {
    }

    protected override string CreatePermissionName => PermissionNames.Blogs_Create;
    protected override string EditPermissionName => PermissionNames.Blogs_Edit;
    protected override string ViewPermissionName => PermissionNames.Blogs_View;
    protected override string DeletePermissionName => PermissionNames.Blogs_Delete;
    protected override string ViewName => "_BlogModal";
}
در اینجا مشخص کردن ViewName متناظر با موجودیت جاری، الزامی می‌باشد. همانطور که عنوان شد، یک پارشال‌ویو برای عملیات ثبت و ویرایش کفایت می‌کند و طراحی پشت این کنترلر پایه نیز بر این اساس می‌باشد. گام بعدی، طراحی پارشال‌ویو مذکور می‌باشد. 

<div>
    <h4 asp-if="Model.IsNew()">Create New Blog</h4>
    <h4 asp-if="!Model.IsNew()">Edit Blog</h4>
    <button type="button" data-dismiss="modal">&times;</button>
</div>
در اینجا با استفاده از تگ‌هلپر asp-if موجود در زیرساخت و متد IsNew متناظر با مدل جاری، عنوان نمایشی مودال را مشخص کرده‌ایم. 
 ‌


<form asp-action="@(Model.IsNew() ? "Create" : "Edit")" asp-controller="Blogs" asp-modal-form="BlogForm">
    <div>
        <input type="hidden" name="save-continue" value="true"/>
        <input asp-for="RowVersion" type="hidden"/>
        <input asp-for="Id" type="hidden"/>
        <div>
            <div>
                <label asp-for="Title"></label>
                <input asp-for="Title" autocomplete="off"/>
                <span asp-validation-for="Title"></span>
            </div>
        </div>
        <div>
            <div>
                <label asp-for="Url"></label>
                <input asp-for="Url" type="url"/>
                <span asp-validation-for="Url"></span>
            </div>
        </div>
    </div>

...

</form>
با استفاده از متد IsNew مدل جاری، اکشن متد متناظر با درخواست POST مشخص شده و سپس از طریق تگ‌هلپر asp-modal-form، فرآیند افزودن ویژگی‌های data-ajax به فرم را خودکار کرده‌ایم. سپس با استفاده از یک Hidden Input با نام save-continue و مقدار true به فرم، عملیات ذخیره کردن بدون بسته شدن فرم را شبیه سازی کرده‌ایم؛ این Input می‌تواند به صورت شرطی به فرم اضافه شود (دکمه‌های ذخیره/ذخیره و بستن و ...) که در سمت سرور با توجه به مقدار ارسالی تصمیم گرفته می‌شود که دوباره فرم با اطلاعات جدید (در اینجا Id و RowVersion) به کلاینت ارسال شود یا خیر.

<div>
    <a asp-modal-delete-link asp-model-id="@Model.Id" asp-modal-toggle="false"
       asp-controller="Blogs" asp-action="Delete" asp-if="!Model.IsNew()" asp-permission="@PermissionNames.Blogs_Delete"
       title="Delete Blog">
        <i></i>
    </a>
    <a title="Refresh Blog" asp-if="!Model.IsNew()" asp-modal-link asp-modal-toggle="false"
       asp-controller="Blogs" asp-action="Edit" asp-route-id="@Model.Id">
        <i></i>
    </a>
    <a title="New Blog" asp-modal-link asp-modal-toggle="false"
       asp-controller="Blogs" asp-action="Create">
        <i></i>
    </a>
    <button type="button" data-dismiss="modal">
        <i></i>&nbsp; Cancel
    </button>
    <button type="submit">
        <i></i>&nbsp; Save Changes
    </button>
</div>
بخش فوتر این مودال در برگیرنده دکمه‌های متناظر با اکشن‌های قابل انجام بر روی موجودیت جاری می‌باشد.
در این فرم ابتدا 3 دکمه میانبر برای عملیات حذف، بازنشانی و جدید درنظر گرفته شده‌اند؛ برای حذف که از طریق تگ‌هلپر asp-modal-delete-link کار افزودن خودکار ویژگی‌های data-ajax انجام شده و در نهایت با توجه به اکشن‌متد و کنترلر مشخص شده، یک مودال، برای گرفتن تأیید از کاربر باز می‌شود و در زمان پذیرش درخواست، حذف مدل جاری به سرور ارسال خواهد شد. با استفاده از تگ‌هلپر asp-permission نیز دسترسی مورد نیاز برای مشاهده دکمه مورد نظر را مشخص کرده‌ایم. دکمه‌های بازنشانی و جدید، در اینجا از طریق تگ‌هلپر asp-modal-link تبدیل به یک لینک Ajaxای شده است که کار بارگذاری یک پارشال‌ویو را انجام می‌دهند؛ همچنین با استفاده از ویژگی asp-modal-toggle متناظر با تگ‌هلپر مذکور با مقدار false، مشخص کرده‎ایم که صرفا محتوای مودال جاری جایگزین شود و نیاز به گشوده شدن دوباره آن نیست.


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

‌‌
مثال دوم: پیاده‌سازی RolesController و طراحی فرم ثبت و ویرایش
‌‌نکته قابل توجه در طراحی فرم‌های ثبت و ویرایش موجودیت‌های پیچیده‌تر مربوط است به ارسال اطلاعات اضافه‌تر از هر آنچه در مدل وجود دارد به کلاینت و همچنین دریافت اطلاعات در ساختار تودرتو یا به اصطلاح یک گراف از اشیاء. به عنوان مثال برای ثبت یک گروه کاربری نیاز است لیست دسترسی‌ها را نیز در فرم نمایش دهیم تا توسط کاربر انتخاب شود؛ در این حالت نیاز است یک مدل متناظر و متناسب را به شکل زیر طراحی کرد:
public class RoleModalViewModel : RoleModel
{
    public IReadOnlyList<LookupItem> PermissionList { get; set; }
}
در اینجا با ارث‌بری از RoleModel متناظر با موجودیت گروه کاربری، لیست دسترسی‌ها نیز در طریق خصوصیت PermissionList قابل استفاده می‌باشد. حال برای مقداردهی خصوصیت اضافه شده و به طور کلی برای ارسال اطلاعات اضافه به کلاینت در زمان بارگذاری فرم، نیاز است متد RenderView را به شکل زیر بازنویسی کنید:
protected override IActionResult RenderView(RoleModel role)
{
    var model = _mapper.Map<RoleModalViewModel>(role);
    model.PermissionList = ReadPermissionList();

    return PartialView(ViewName, model);
}
به عنوان مثال در اینجا از AutoMapper برای وهله سازی از RoleModalViewModel و نگاشت خصوصیات RoleModel، استفاده شده است. به این ترتیب خروجی نهایی در حالت ویرایش به شکل زیر می‌باشد:



برای مدیریت سناریوهای Master-Detail به مانند قسمت مدیریت دسترسی‌ها در تب Permissions فرم بالا، امکاناتی در زیرساخت تعبیه شده است ولی پیاده‌سازی آن را به عنوان یک تمرین و با توجه به سری مطالب «Editing Variable Length Reorderable Collections in ASP.NET MVC» به شما واگذار می‌کنم.


نکته تکمیلی: برای ارسال اطلاعات اضافی به ویو Index متناظر با یک موجودیت می‌توانید متد RenderIndex را به شکل زیر بازنویسی کنید:

protected override IActionResult RenderIndex(IPagedQueryResult<RoleReadModel> model)
{
    var indexModel = new RoleIndexViewModel
    {
        Items = model.Items,
        TotalCount = model.TotalCount,
        Permissions = ReadPermissionList()
    };

    return Request.IsAjaxRequest()
        ? (IActionResult) PartialView(indexModel)
        : View(indexModel);
}

مدل RoleIndexViewModel استفاده شده در تکه کد بالا نیز به شکل زیر خواهد بود:

public class RoleIndexViewModel : PagedQueryResult<RoleReadModel>
{
    public IReadOnlyList<LookupItem> Permissions { get; set; }
}


فرآیند بارگذاری یک پارشال‌ویو در مودال

به عنوان مثال برای استفاده از مودال‌های بوت استرپ، ایده کار به این شکل است که یک مودال را به شکل زیر در فایل Layout قرار دهید:

<div class="modal fade" @*tabindex="-1"*@ id="main-modal" data-keyboard="true" data-backdrop="static" role="dialog" aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered" role="document">
        <div class="modal-content">
            <div class="modal-body">
                Loading...
            </div>
        </div>
    </div>
</div>

سپس در زمان کلیک بروی یک دکمه Ajaxای، ابتدا main-modal را نمایش داده و بعد از دریافت پارشال‌ویو از سرور، آن را با محتوای modal-content جایگزین می‌کنیم. به همین دلیل Tag Halperهای مطرح شده در مطلب جاری، callbackهای failure/complete/success متناظر با unobtrusive-ajax را نیز مقداردهی می‌کنند. برای این منظور نیاز است تا متدهای جاوااسکریپتی زیر نیز در سطح شیء window تعریف شده باشند:‌‌

    /*----------------------------------  asp-modal-link ---------------------------*/
    window.handleModalLinkLoaded = function (data, status, xhr) {
        prepareForm('#main-modal.modal form');
    };
    window.handleModalLinkFailed = function (xhr, status, error) {
        //....
    };
    /*----------------------------------  asp-modal-form ---------------------------*/
    window.handleModalFormBegin = function (xhr) {
        $('#main-modal a').addClass('disabled');
        $('#main-modal button').attr('disabled', 'disabled');
    };
    window.handleModalFormComplete = function (xhr, status) {
        $('#main-modal a').removeClass('disabled');
        $('#main-modal button').removeAttr('disabled');
    };
    window.handleModalFormSucceeded = function (data, status, xhr) {
        if (xhr.getResponseHeader('Content-Type') === 'text/html; charset=utf-8') {
            prepareForm('#main-modal.modal form');
        } else {
            hideMainModal();
        }
    };
    window.handleModalFormFailed = function (xhr, status, error, formId) {
        if (xhr.status === 400) {
            handleBadRequest(xhr, formId);
        }
    };


برای بررسی بیشتر، پیشنهاد می‌کنم پروژه DNTFrameworkCore.TestWebApp موجود در مخزن این زیرساخت را بازبینی کنید. 
مطالب
OpenCVSharp #14
تشخیص BLOBs در تصویر به کمک OpenCV

  BLOB یا Binary Large OBject به معنای گروهی از نقاط به هم پیوسته‌ی در یک تصویر باینری هستند. Large در اینجا به این معنا است که اشیایی با اندازه‌هایی مشخص، مدنظر هستند و اشیاء کوچک، به عنوان نویز درنظر گرفته خواهند شد و پردازش نمی‌شوند.
برای نمونه در تصویر ذیل، تصویر سمت چپ، تصویر اصلی است و تصویر سمت راست، نمونه‌ی باینری سیاه و سفید آن. سپس عملیات تشخیص Blobs بر روی تصویر اصلی انجام شده و نتیجه‌ی نهایی که مشخص کننده‌ی اشیاء تشخیص داده شده‌است، با دوایری قرمز در تصویر سمت راست، مشخص هستند.



معرفی کلاس SimpleBlobDetector

در کدهای ذیل، نحوه‌ی کار با کلاس SimpleBlobDetector را مشاهده می‌کنید. این کلاس کار تشخیص Blobs را در تصویر اصلی انجام می‌دهد:
var srcImage = new Mat(@"..\..\Images\cvlbl.png");
Cv2.ImShow("Source", srcImage);
Cv2.WaitKey(1); // do events
 
 
var binaryImage = new Mat(srcImage.Size(), MatType.CV_8UC1);
 
Cv2.CvtColor(srcImage, binaryImage, ColorConversion.BgrToGray);

Cv2.Threshold(binaryImage, binaryImage, thresh: 100, maxval: 255, type: ThresholdType.Binary);

 
var detectorParams = new SimpleBlobDetector.Params
{
    //MinDistBetweenBlobs = 10, // 10 pixels between blobs
    //MinRepeatability = 1,
 
    //MinThreshold = 100,
    //MaxThreshold = 255,
    //ThresholdStep = 5,
 
    FilterByArea = false,
    //FilterByArea = true,
    //MinArea = 0.001f, // 10 pixels squared
    //MaxArea = 500,
 
    FilterByCircularity = false,
    //FilterByCircularity = true,
    //MinCircularity = 0.001f,
 
    FilterByConvexity = false,
    //FilterByConvexity = true,
    //MinConvexity = 0.001f,
    //MaxConvexity = 10,
 
    FilterByInertia = false,
    //FilterByInertia = true,
    //MinInertiaRatio = 0.001f,
 
    FilterByColor = false
    //FilterByColor = true,
    //BlobColor = 255 // to extract light blobs
};
var simpleBlobDetector = new SimpleBlobDetector(detectorParams);
var keyPoints = simpleBlobDetector.Detect(binaryImage);
 
Console.WriteLine("keyPoints: {0}", keyPoints.Length);
foreach (var keyPoint in keyPoints)
{
    Console.WriteLine("X: {0}, Y: {1}", keyPoint.Pt.X, keyPoint.Pt.Y);
}
 
var imageWithKeyPoints = new Mat();
Cv2.DrawKeypoints(
        image: binaryImage,
        keypoints: keyPoints,
        outImage: imageWithKeyPoints,
        color: Scalar.FromRgb(255, 0, 0),
        flags: DrawMatchesFlags.DrawRichKeypoints);
 
 
Cv2.ImShow("Key Points", imageWithKeyPoints);
Cv2.WaitKey(1); // do events
 
 
Cv2.WaitKey(0);
 
Cv2.DestroyAllWindows();
srcImage.Dispose();
imageWithKeyPoints.Dispose();
توضیحات

در اینجا ابتدا تصویر منبع به صورتی متداول باگذاری شده‌است. سپس توسط متد CvtColor به نمونه‌ای سیاه و سفید و به کمک متد Threshold به یک تصویر باینری تبدیل شده‌است.
اگر به تصویر ابتدای بحث دقت کنید، اشیاء موجود در منبع مفروض، رنگ‌های متفاوتی دارند؛ اما در تصویر نهایی تولید شده، تمام آن‌ها تبدیل به اشیایی سفید رنگ شده‌اند. این کار را متد Cv2.Threshold انجام داده‌است، تا کلاس SimpleBlobDetector قابلیت تشخیص بهتری را پیدا کند. تشخیص اشیایی یک دست، بسیار ساده‌تر هستند از تشخیص اشیایی با رنگ‌ها و شدت نور متفاوت.
اگر بخواهیم الگوریتم Threshold را بیان کنیم به pseudo code زیر خواهیم رسید:
 if src(x,y) > thresh
    dst(x,y) = maxValue
else
    dst(x,y) = 0
در اینجا رنگ نقطه‌ی x,y با مقدار thresh (پارامتر سوم متد Cv2.Threshold) مقایسه می‌شود. اگر مقدار آن بیشتر بود، با پارامتر چهارم جایگزین خواهد شد. مقدار 255 در اینجا روشن‌ترین حالت ممکن است؛ اگر خیر با صفر یا تیره‌ترین رنگ، جایگزین می‌شود. به همین دلیل است که در تصویر سمت راست، تمام اشیاء را با روشن‌ترین رنگ ممکن مشاهده می‌کنید.

سپس پارامتر اصلی سازنده‌ی کلاس SimpleBlobDetector مقدار دهی شده‌است. این تنظیمات در تشخیص اشیاء موجود در تصویر بسیار مهم هستند. برای درک بهتر واژه‌هایی مانند Circularity، Convexity، Inertia و امثال آن، به تصویر ذیل دقت کنید:



در اینجا می‌توان حداقل و حداکثر تقعر و تحدب اشیاء و یا حتی حداقل و حداکثر اندازه‌ی اشیاء مدنظر را مشخص کرد.
اگر سازنده‌ی کلاس SimpleBlobDetector به نال تنظیم شود، از مقادیر پیش فرض آن استفاده خواهند شد که در اینجا به معنای تشخیص اشیاء کدر دایره‌ای شکل هستند. به همین جهت در تنظیمات فوق FilterByColor به false تنظیم شده‌است تا اشکال سفید رنگ تصویر اصلی را بتوان تشخیص داد.

در ادامه متد simpleBlobDetector.Detect بر روی تصویر باینری بهبود داده شده، فراخوانی گشته‌است. خروجی آن نقاط کلیدی اشیاء یافت شده‌است. این نقاط را می‌توان توسط متد DrawKeypoints ترسیم کرد که حاصل آن دوایر قرمز رنگ موجود در مرکز اشیاء یافت شده‌ی در تصویر سمت راست هستند.



کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید.
اشتراک‌ها
WebWindow؛ جایگزین الکترون برای برنامه‌های NET Core.

My last post investigated ways to build a .NET Core desktop/console app with a web-rendered UI without bringing in the full weight of Electron. This seems to have interested a lot of people, so I decided to upgrade it to newer technologies and add cross-platform support.

The result is a little NuGet package called WebWindow that you can add to any .NET Core console app. It can open a native OS window (Windows/Mac/Linux) containing web-based UI, without your app having to bundle either Node or Chromium. 

WebWindow؛ جایگزین الکترون برای برنامه‌های NET Core.
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 6 - سرویس‌ها و تزریق وابستگی‌ها
سلام
من می‌خوستم از Scrutor   استفاده کنم 
کد سرویس من به صورت زیر است
namespace FirstProjectServices
{
    public interface IMessagesService
    {
        string GetSiteName();
    }

    public class MessagesService : IMessagesService
    {
        public string GetSiteName()
        {
            return "DNT";
        }
    }
}

namespace FirstProjectServices
{
    public interface IMessagesService2
    {
        string GetSiteName();
    }

    public class MessagesService2 : IMessagesService2
    {
        public string GetSiteName()
        {
            return "DNT";
        }
    }
}
و در کلاس Startup کد زیر رو نوشتم
public class Startup
    {
       public void ConfigureServices(IServiceCollection services)
        {
            var collection = new ServiceCollection();

            services.Scan(scan => scan
                // We start out with all types in the assembly of ITransientService
                .FromAssemblyOf<IMessagesService>()
                .AddClasses(classes => classes.AssignableTo<MessagesService>())
                .AsImplementedInterfaces()
                .WithTransientLifetime()
               );

         }

       
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, IMessagesService2 _messagesService)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            // app.UseDefaultFiles();
            // app.UseStaticFiles();

            app.Run(async (context) =>
            {
                string siteName = _messagesService.GetSiteName();
                await context.Response.WriteAsync($"Site Name {siteName}");
            });
        }
    }
در متد ConfigureServices نحوه Register رو مشخص کردم (مپ کردن IMessagesService  به  MessagesService ) و در متد Configure انتظار دارم چون کلاس MessagesService  و MessagesService2 کنار هم هستند این نگاشت به درستی انجام شود و خطا میده که   نگاشت I MessagesService  تعریف نشده است. در صورتی که هدف ما اسکن کل اسمبلی هست. ممنون میشم راهنمایی کنید.
اشتراک‌ها
مقدمه‌ای بر NET MAUI.

An Introduction to .NET MAUI For Mobile Development

.NET Multi-platform App UI (.NET MAUI) is a cross-platform framework for creating native mobile and desktop apps with C# and XAML.
.NET MAUI is open-source and is the evolution of Xamarin.Forms, extended from mobile to desktop scenarios, with UI controls rebuilt from the ground up for performance and extensibility. If you've previously used Xamarin.Forms to build cross-platform user interfaces, you'll notice many similarities with .NET MAUI. However, there are also some differences. Using .NET MAUI, you can create multi-platform apps using a single project, but you can add platform-specific source code and resources if necessary. One of the key aims of .NET MAUI is to enable you to implement as much of your app logic and UI layout as possible in a single code-base.

0:00 - Setup Visual Studio and MAUI Project
00:16:25 - Create MAUI Pages with C#
00:27:42 - Create MAUI Pages with XAML
00:32:28 - Explore MAUI Layouts
00:39:38 - Static Shared Resources
00:44:36 - Platform Specific Values
00:50:11 - Page Navigation  

مقدمه‌ای بر NET MAUI.