بازخوردهای پروژه‌ها
Report Viewer

سلام جناب آقای نصیری

با تشکر از زحمتی که کشیدید. بسیار پروژه ارزشمندی هست pdfreport

موقعی که شما درباره itextsharp مطلب مینوشتید به این موضوع اشاره کردید که این گزارشات هم به نوعی html هستند .آیا این مقدور هست که متدی برای تبدیل این pdf به صورت html هم وجود داشته باشد تا بتوانیم یک preview به کاربر نشان بدهیم؟

با تشکر

مطالب
HTML5 Offline Web Applications
وب به سمتی پیش رفته که کاربران زیادی از تلفن همراه ، تبلت‌ها و دیگر عامل ها(Agent) جهت مرور صفحات وب استفاده می‌کنند. در نتیجه تعداد کاربرانی که مدام در حال حرکت به مرور صفحات وب و استفاده از سرویس‌های برخط می‌پردازند رو به افزایش است. برنامه‌های خارج از شبکه‌ی HTML 5 یا به عبارتی HTML5 Offline Web Applications توسعه دهنگان را قادر می‌سازد تا نرم افزار‌های تحت وبی ارائه دهند که در حالت قطع بودن اینترنت و یا شبکه همچنان به سرویس دادن به کاربران ادامه دهد. دیگر اینگونه نیست که وب تنها در حالت برخط بودن معنی پیدا کند. یک نرم افزار مدیریت هزینه‌ی تحت وب را بررسی کنید که روی تلفن همراه شما اجرا شده ، در محلی که دسترسی به اینترنت نیست قصد استفاده از آن را دارید. چه قدر خوب می‌شود که این نرم افزار به گونه ای پیاده سازی شده باشد که بتواند در حالت برون خطی (Offline) به سرویس دهی ادامه دهد به طور مثال قادر به ذخیره‌ی داده‌های شما به صورت برون خط و همزمان سازی آنها پس از اتصال به اینترنت باشد.
برنامه‌های تحت وب برون خط با کمک قابلیتی به نام نهانگاه برنامه (Application Cache) کار می‌کنند. این قابلیت می‌تواند تمامی بخش‌های سایت را به شکل برون خط و خارج از شبکه، ذخیره کند. با به کار گیری این ویژگی می‌توان تمامی فایل‌های ایستا (JavaScript , HTML , CSS , Image) بر روی ابزار کاربر ذخیره نمود.

 نهانگاه برنامه چه تفاوتی با نهانگاه مرورگر (Browser cache) دارد ؟

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

برای استفاده از این ویژگی اولین باید فایلی به نام cache.manifest ایجاد کرد. این فایل باید با نوع محتوای (Mime type) مناسب برای کاربر ارسال شود. این فایل یک فایل متنی می‌باشد که لیست فایل‌های مورد نظر ما با قواعد خاصی در آن قرار می‌گیرد.
همیشه اولین خط این فایل عبارت CACHE MANIFEST قرار دارد ، پس از این خط عبارت CACHE: وارد می‌شود و فایل‌های مد نظر ما لیست می‌شود.
CACHE MANIFEST
 
#Cache Section
CACHE:
/Content/Images/icons-18-white.png
/Content/Images/icons-36-white.png
/Content/Images/ajax-loader.png
/Content/css
/Scripts/js
ذخیره‌ی برخی فایل‌ها روی سیستم کاربر ضرورتی ندارد ، برای مثال اسکریپتی که از یک وب سرویس برخط اطلاعات آب و هوا را دریافت می‌کند در حالت Offline کاربردی ندارد. برای مشخص کردن این فایل‌ها یک لیست سفید آماده می‌کنیم.
NETWORK
webService.Js
در بخش معرفی فایل‌های آفلاین بازید همه‌ی فایل‌های مد نظر ما معرفی شوند اما در لیست سفید با گذاشتن ستاره به مرورگر اعلام می‌کنیم که هر فایل و مسیری که در بخش قبلی (CACHE) نیامده را همواره از سرور درخواست کن. درج ستاره در بخش NETWORK ضروری است زیرا همه‌ی آدرس‌های سایت باید در یکی از بخش‌های cache.manifest قرار بگیرد ، اگر آدرسی در cache.manifest قرار نگیرد هیچگاه بارگذاری نخواهد شد.
در فایل cache.manifest می‌توان یادداشت هم اضافه کرد ، تمامی کاراکتر هایی که بعد از # قرار گیرند پردازش نمی‌شوند. یادداشت‌ها مفید هستند ، می‌تونید اطلاعات نسخه‌ی فعلی فایل cache.manifest را در آنها قرار دهید.
مثال :
CACHE MANIFEST
# 2010-06-18:v2

# Explicitly cached 'master entries'.
CACHE:
/favicon.ico
index.html
css
images/
stylesheet
.logo.png
scripts/main.js

# Resources that require the user to be online.
NETWORK:
login.php
/myapi
http://api.twitter.com
گفته شد فایل Cache.manifest باید با نوع محتوای مناسب ارسال شود ، برای تعیین نوع محتوا در وب سرور apache باید خط زیر به فایل .htaaccess اضافه شود :
AddType text/cache-manifest .appcache
در ASP.NET Web Forms می‌توان از یک Generic Handler برای ارسال فایل با نوع محتوای مناسب استفاده کرد : 
using System.Web;
 
namespace JavaScriptReference {
 
    public class Manifest : IHttpHandler {
 
        public void ProcessRequest(HttpContext context) {
            context.Response.ContentType = "text/cache-manifest";
            context.Response.WriteFile(context.Server.MapPath("Manifest.txt"));
        }
 
        public bool IsReusable {
            get {
                return false;
            }
        }
    }
}
یا می‌توان در یک فایل ASPX به صورتی دستی نوع محتوا را مشخص کرد : 
CACHE MANIFEST

# Version Jesus 3!

CACHE:

index.html
js/Custom.js
js/Utility.js
styles/index.css
styles/kendo.common.min.css
styles/BlueOpal/loading.gif
styles/BlueOpal/slider-h.gif
styles/BlueOpal/slider-v.gif

NETWORK:
*

<%@ Page Language="VB" ContentType="text/cache-manifest"  ResponseEncoding="utf-8" AutoEventWireup="true" CodeFile="manifest.aspx.vb" Inherits="Configuration_manifest" %>
در ASP.NET MVC علاوه بر اینکه می‌توان دستی نوع محتوای Response را مشخص کرد، می‌توان یک ActionResult منحصر به فرد ایجاد کرد. یک نمونه پیاده سازی شده را اینجا مشاهده کنید.
پس از انجام همه‌ی این پیش نیاز‌ها باید فایل cache manifest را به خصیصه‌ی manifest برچسب html ارجاع دهیم. 
<!DOCTYPE html>
<html manifest="/manifest.aspx">
اکنون قسمت‌های مد نظر سایت ما در حالت عدم دسترسی به شبکه نیز قابل استفاده می‌باشد. حتی این ویژگی در حالت برخط صفحات ما با سرعت بالاتری بارگذاری شوند.
خصیصه‌ی manifest تمامی فایل‌های HTML باید مقدار گیرد در غیر این صورت ممکن است صفحات درون نهانگاه قرار نگیرند.  
برای بررسی مرورگرهایی که این ویژگی را پشتیبانی می‌کنند این لینک  را مشاهده کنید.
مطالب
بررسی Bad code smell ها: الگوی Shotgun Surgery
برای مشاهده طبقه بندی Bad code smell‌ها می‌توانید به اینجا مراجعه کنید.
زمانیکه به ازای هر تغییر، نیاز باشد تغییرات کوچکی در تعداد کلاس‌های زیادی انجام شود، این بوی بد کد بوجود آمده است. این الگو از دسته بندی «جلوگیری کنندگان از تغییر» است. نام این دسته بندی به طور واضح گویای مشکلی است که این الگوی بد ایجاد می‌کند. 

چرا چنین بویی به راه می‌افتد؟ 

یکی از نشانه‌های وجود چنین الگوی بدی در کدها، مشاهده کدهای تکراریست. ریشه اصلی این بوی بد، پراکنده کردن مسئولیت‌ها در کلاس‌های مختلف است. مسئولیت‌هایی که بهتر بود در یک کلاس جمع شوند. معمولا برای رفع این بوی بد اقدام به جمع کردن مسئولیت‌ها از نقاط مختلف به یک کلاس می‌کنند.  
با توجه به توضیحات ارائه شده، این بوی بد عملا یکی از علایم اجرایی نکردن اصل Single responsibility  و Open closed از اصول طراحی شیء گرایی است. موارد دیگری که در ایجاد چنین مشکلی کمک می‌کنند به صورت زیر هستند:
  • استفاده نادرست از الگوهای طراحی شیء گرا 
  • عدم درک درست مسئولیت‌های کلاس‌های ایجاد شده 
  • عدم تشخیص مکانیزم‌های مشترک در کد و جداسازی مناسب آنها 
برای بررسی بیشتر این موضوع فرض کنید کلاس‌هایی در نرم افزار خود دارید که شماره تلفن کاربر را به صورت ورودی دریافت و روی آن کار خاصی را انجام می‌دهند. در ابتدای تولید نرم افزار فرمت صحیح شماره تلفن به صورت "04135419999" تشخیص داده شده است و مکانیزم اعتبارسنجی آن نیز با استفاده regular express‌ionها پیاده سازی شده‌است.  بعدا نیازمندی دیگری بوجود می‌آید که شماره تلفن‌هایی با کد بین المللی نیز در نرم افزار قابل استفاده باشند. مانند "984135410000+" دو نوع پیاده سازی (از میان روش‌های فراوان پیاده سازی) برای تشریح این موضوع می‌توان متصور بود. فرض کنید در دو موجودیت «کاربر» و «آدرس» نیاز به ذخیره سازی شماره تلفن وجود دارد. 

اول: هر جائیکه نیاز به اعتبارسنجی شماره تلفن وجود داشته باشد؛ این کار تماما در همان مکان انجام شود.
public class UserService 
{ 
        public void SaveUser(dynamic userEntity) { 
            var regEx = "blablabla"; 
            var phoneIsValid = Regex.IsMatch(userEntity.PhoneNumber, regEx); 
            if (!phoneIsValid) 
                return; 
            // ... 
        } 
}  

public class AddressService 
{ 
        public void SaveAddress(dynamic addressEntity) 
        { 
            var regEx = "blablabla"; 
            var phoneIsValid = Regex.IsMatch(addressEntity.PhoneNumber, regEx); 
            if (!phoneIsValid) 
                return; 
        } 
}
در این روش پیاده سازی اگر دقت کرده باشید روال مربوط به اعتبارسنجی در دو متد «ذخیره کاربر» و «ذخیره آدرس» تکرار شده‌است . این الگوی کد نویسی، علاوه بر این که خود نوعی بوی بد کد محسوب می‌شود، باعث ایجاد الگوی Shotgun surgery نیز است.  
در اینجا اگر قصد اعمال تغییری در منطق مربوط به اعتبارسنجی شماره تلفن وجود داشته باشد، نیاز خواهد بود تمامی مکان‌هایی که این منطق پیاده سازی شده‌است، بسته به شرایط جدید تغییر کند. یعنی برای تغییر یک منطق اعتبارسنجی نیاز خواهد بود کلاس‌های زیادی تغییر کنند.  

دوم: راه بهتر در انجام چنین کاری، جداسازی منطق مربوط به اعتبارسنجی شماره تلفن و انتقال آن به کلاسی جداگانه‌است؛ به صورت زیر: 
public class PhoneValidator
{ 
        public bool IsValid(string phoneNumber) 
        { 
            var regEx = "blablabla"; 
            var phoneIsValid = Regex.IsMatch(phoneNumber, regEx); 
            if (!phoneIsValid) 
                return false; 
            return true; 
        } 
 } 
 
public class UserService 
{ 
        public void SaveUser(dynamic userEntity) 
        { 
            var validator = new PhoneValidator(); 
            var phoneIsValid  = validator.IsValid(userEntity.PhoneNumber); 
            if (!phoneIsValid) 
                return; 
            // ... 
        } 
 } 
 
public class AddressService 
{ 
        public void SaveAddress(dynamic addressEntity) 
        { 
            var validator = new PhoneValidator(); 
            var phoneIsValid = validator.IsValid(addressEntity.PhoneNumber); 
            if (!phoneIsValid) 
                return; 
           // ... 
        } 
}

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

شکل دیگر 

شکل دیگر این بوی بد کد، Divergent Change است. با این تفاوت که در الگوی Divergent Change تغییرات در یک کلاس اتفاق می‌افتند نه در چندین کلاس به طور همزمان. 

جمع بندی

تشخیص چنین الگوی بد کد نویسی ای همیشه به این سادگی نیست. یکی از راه‌های تشخیص سریع چنین بوی بد کدی این است که به کارهای تکراری عادت نکنید! و زمانیکه متوجه شدید کار خاصی را در کد به صورت تکراری انجام می‌دهید، دقت لازم را برای تغییر آن داشته باشید؛ به صورتیکه نیاز به اعمال تغییرات تکراری در مکان‌های مختلف کد وجود نداشته باشد. راه دیگر زمانی است که کدی تکراری را مشاهده کردید. زمانیکه کدی تکراری در کدها وجود داشته باشد، اطمینان داشته باشید هنگام تغییر آن به این مشکل دچار خواهید شد. برای رفع موضوع کد تکراری می‌توانید از روش‌های مختلفی که عنوان شد استفاده کنید. 
مطالب
مروری بر کاربردهای Action و Func - قسمت دوم
در قسمت قبل از  Func و Actionها برای ساده سازی طراحی‌های مبتنی بر اینترفیس‌هایی با یک متد استفاده کردیم. این مورد خصوصا در حالت‌هایی که قصد داریم به کاربر اجازه‌ی فرمول نویسی بر روی اطلاعات موجود را بدهیم، بسیار مفید است.

مثال دوم) به استفاده کننده از API کتابخانه خود، اجازه فرمول نویسی بدهید

برای نمونه مثال ساده زیر را درنظر بگیرید که در آن قرار است یک سری عدد که از منبع داده‌ای دریافت شده‌اند، بر روی صفحه نمایش داده شوند:
public static void PrintNumbers()
{
    var numbers = new[] { 1,2,3,5,7,90 }; // from a data source
    foreach(var item in numbers)
    {
        Console.WriteLine(item);
    }    
}
قصد داریم به برنامه نویس استفاده کننده از کتابخانه گزارش‌سازی خود، این اجازه را بدهیم که پیش از نمایش نهایی اطلاعات، بتواند توسط فرمولی که مشخص می‌کند، فرمت اعداد نمایش داده شده را تعیین کند.
روال کار اکثر ابزارهای گزارش‌سازی موجود، ارائه یک زبان اسکریپتی جدید برای حل این نوع مسایل است. اما با استفاده از Func و ... روش‌های Code first (بجای روش‌های Wizard first)، خیلی از این رنج و دردها را می‌توان ساده‌تر و بدون نیاز به اختراع و یا آموزش زبان جدیدی حل کرد:
public static void PrintNumbers(Func<int,string> formula)
{
    var numbers = new[] { 1,2,3,5,7,90 };  // from a data source
    foreach(var item in numbers)
    {
        var data = formula(item);
        Console.WriteLine(data);
    }    
}
اینبار با استفاده از Func، امکان فرمول نویسی را به کاربر استفاده کننده از API ساده گزارش ساز فرضی خود داده‌ایم. Func تعریف شده در اینجا یک عدد int را در اختیار استفاده کننده قرار می‌دهد. در این بین، برنامه نویس می‌تواند هر نوع تغییر یا هر نوع فرمولی را که مایل است بر روی این عدد به کمک دستور زبان جاری مورد استفاده، اعمال کند و در آخر تنها باید نتیجه این عملیات را به صورت یک string بازگشت دهد. برای مثال:
 PrintNumbers(number => string.Format("{0:n0}",number));
البته سطر فوق ساده شده فراخوانی زیر است:
 PrintNumbers((number) =>{ return string.Format("{0:n0}",number); });
به این ترتیب اعداد نهایی با جدا کننده سه رقمی نمایش داده خواهند شد.
از این نوع طراحی، در ابزارها و کتابخانه‌های جدید گزارش سازی مخصوص ASP.NET MVC زیاد مشاهده می‌شوند.


مثال سوم) حذف کدهای تکراری برنامه

فرض کنید قصد دارید در برنامه وب خود مباحث caching را پیاده سازی کنید:
using System;
using System.Web;
using System.Web.Caching;
using System.Collections.Generic;

namespace WebToolkit
{
    public static class CacheManager
    {
        public static void CacheInsert(this HttpContextBase httpContext, string key, object data, int durationMinutes)
        {
            if (data == null) return;
            httpContext.Cache.Add(
                key,
                data,
                null,
                DateTime.Now.AddMinutes(durationMinutes),
                TimeSpan.Zero,
                CacheItemPriority.AboveNormal,
                null);
        }
    }
}
در هر قسمتی از برنامه که قصد داشته باشیم اطلاعاتی را در کش ذخیره کنیم، الگوی تکراری زیر باید طی شود:
var item = httpContext.Cache[key];
if (item == null)
{
    item = ReadDataFromDataSource();
    if (item == null)
          return null;

    CacheInsert(httpContext, key, item, durationMinutes);
}
ابتدا باید وضعیت کش جاری بررسی شود؛ اگر اطلاعاتی در آن موجود نبود، ابتدا از منبع داده‌ای مورد نظر خوانده شده و سپس در کش درج شود.
می‌توان در این الگوی تکراری، خواندن اطلاعات را از منبع داده، به یک Func واگذار کرد و به این صورت کدهای ما به نحو زیر بازسازی خواهند شد:
using System;
using System.Web;
using System.Web.Caching;
using System.Collections.Generic;

namespace WebToolkit
{
    public static class CacheManager
    {
        public static void CacheInsert(this HttpContextBase httpContext, string key, object data, int durationMinutes)
        {
            if (data == null) return;
            httpContext.Cache.Add(
                key,
                data,
                null,
                DateTime.Now.AddMinutes(durationMinutes),
                TimeSpan.Zero,
                CacheItemPriority.AboveNormal,
                null);
        }

        public static T CacheRead<T>(this HttpContextBase httpContext, string key, int durationMinutes, Func<T> ifNullRetrievalMethod)
        {
            var item = httpContext.Cache[key];
            if (item == null)
            {
                item = ifNullRetrievalMethod();
                if (item == null)
                    return default(T);

                CacheInsert(httpContext, key, item, durationMinutes);
            }
            return (T)item;
        }
    }
}
و استفاده از آن نیز به نحو زیر خواهد بود:
var user = HttpContext.CacheRead(
                            "Key1",
                            15,
                            () => _usersService.FindUser(userId));
پارامتر سوم متد CacheRead به صورت خودکار تنها زمانیکه اطلاعات کش متناظری با کلید Key1 وجود نداشته باشند، اجرا شده و نتیجه در کش ثبت می‌گردد. در اینجا دیگر از if و else و کدهای تکراری بررسی وضعیت کش خبری نیست.
 
اشتراک‌ها
ساده ترین روش برای اضافه کردن Dark-Mode به هر وبسایتی با استفاده از DarkReader
گوگل کروم افزونه بسیار خوبی برای طرفداران dark-mode دارد که میتوانید با استفاده از آن حدودا بدون مشکل در ۹۵ درصد از وبسایت هایی که این حالت را پشتیبانی نمیکنند استفاده نمایید. با کمی بررسی نحوه کار آن متوجه شدم که اسکریپتی توسط تیم توسعه دهنده آن تولید شده برای افزودن این  قابلیت به وبسایت خودتان,  پس از تست و اضافه کردن آن به چند  وبسایت مختلف که درحال توسعه آن بودیم نتیجه بسیار خوبی داشت. این شد گفتم این مطلب را با دوستان به اشتراک بگذارم شاید به سایت .NetTips  هم اضافه بشه که نیازی به داشتن افزونه کروم در این سایت هم نداشته باشیم. برای نصب این پکیج میتوانید از npm  استفاده نمایید‌:
npm install darkreader
پس از دریافت این پکیج و افزودن آن به صفحه html خودتان میتوانید به این صورت از آن استفاده نمایید‌:
// فعال کردن
DarkReader.enable({
    brightness: 100,
    contrast: 90,
    sepia: 10
});
// غیر فعال کردن 
DarkReader.disable();
و یا اگر از فریم ورک‌های جاوا اسکریپتی استفاده میکنید به این روش ابتدا دو متد فعال و غیر فعال کردن آن را import کرده و از آن استفاده نمایید. 
import {
    enable as enableDarkMode,
    disable as disableDarkMode,
} from 'darkreader';
// فعال کردن
enableDarkMode({
    brightness: 100,
    contrast: 90,
    sepia: 10,
});
// غیر فعال کردن
disableDarkMode();
متد enable در این پکیج تنظیمات زیر را دریافت میکند‌:
var defaultTheme = {
        mode: 1,  // (0 1) for enabling or disabling dark mode
        brightness: 100,
        contrast: 100,
        grayscale: 0,
        sepia: 0,
        useFont: false,
        fontFamily: '', // add custom font
        textStroke: 0,
        engine: ThemeEngines.dynamicTheme,
        stylesheet: '',  // add custom css styles
    };
اگر افزونه کروم را نصب کرده باشید این تنظیمات برای شما مفهوم‌تر خواهد بود که دقیقا هر کدام برای چه کاری هستند چرا که در این افزونه این موارد قابل تنظیم است. در آخر تنها کاری که باید انجام دهید اضافه کردن یک button به صفحه خود برای فعال و غیر فعال کردن darkmode و ذخیره حالت مورد نظر کاربر در یک کوکی یا local-Storage مرورگر میباشد تا در مراجعه بعدی بتوانید حالت مورد نظر را برای کاربر فعال کنید. چندین اسکریپت و پکیج مشابه هم وجود دارد مانند darkmodejs که با آنها نسبت به darkreader به مراتب به مشکلات زیادی در css‌های خود مواجه خواهید شد. و نیاز دارند که  حدودا ۵۰ درصد از css‌های خود را برای داشتن darkmode بازنویسی نمایید. برای کسب اطلاعات بیشتر در مورد darkreader میتوانید به این لینک مراجعه نمایید.
ساده ترین روش برای اضافه کردن Dark-Mode به هر وبسایتی با استفاده از DarkReader
مطالب
معرفی Bit Platform
پلتفرم Bit یک پروژه تماما Open Source در GitHub میباشد که هدف آن تسهیل توسعه نرم افزار با کیفیت و پرفرمنس بالا بر بستر ASP.NET Core و زبان #C است که با آن بتوان فقط با یکبار کدنویسی و با کمک استانداردهای وب همچون HTML / CSS و Web Assembly ، خروجی‌هایی چون Android / iOS / Windows را با دسترسی کامل به امکانات سیستم‌عامل به همراه برنامه‌های تحت وب SPA و PWA (با یا بدون Pre-Rendering) گرفت.
پلتفرم Bit تا به اینجا از دو قسمت Bit Blazor Components (شامل بیش از ۳۰ کمپوننت کاربردی، کم حجم و High Performance مانند Tree / Multi Select / Data Grid / Date Picker / Color Picker و...) به همراه Bit Project Templates (قالب پروژه‌های حاوی امکانات پر استفاده) تشکیل شده است.
برخی مواردی که در رابطه با آنها صحبت شد، ممکن است برای شما آشنا نباشند، بنابراین قبل از بررسی مفصل‌‌تر Bit Platform، نگاهی به آن می‌اندازیم:


وب اسمبلی چیست؟

برای درک بهتر وب اسمبلی ابتدا باید بدانیم این تکنولوژی اصلا از کجا آمده و هدف آن چیست؟
میدانیم که مرورگر‌ها پروایدر صفحات وب هستند و ما برای اینکه بتوانیم اپلیکیشنی که ساختیم را در محیط وب برای کاربران به اشتراک بگذاریم باید از این مرورگر‌ها و زبان ارتباطی آن‌ها پیروی کنیم. این زبان‌های ارتباطی مشخصا سه چیز است: HTML CSS JavaScript
اما آیا راهی هست که بتوان بجای JavaScript از زبان‌های دیگر هم در سمت مرورگر استفاده کرد؟
وب اسمبلی یا همان WASM، آمده تا به ما اجازه دهد از هر زبانی که خروجی به Web Assembly دارد، برای تعاملات UI استفاده کنیم. یعنی با زبان هایی مثل #C / C++ / C و... میتوان کدی نوشت که مرورگر آن را اجرا کند. این یک تحول بزرگ است که امروزه تمامی مرورگرها (به جز مرورگرهایی که از دور خارج شده اند) از آن پشتیانی می‌کنند چرا که Web Assembly به یکی از اجزای استاندارد وب تبدیل شده است.
اطلاعات بیشتر در رابطه با وب اسمبلی را میتوانید از این مقاله بخوانید. 


تعریف SPA و PWA:
SPA: Single Page Application
PWA: Progressive Web Application
در گذشته برای رندر کردن صفحات وب با عوض شدن URL یا درخواست کاربر برای دریافت اطلاعات جدید از سرور و نمایش آن ، صفحه مرورگر ملزم به رفرش شدن مجدد و از سر گیری کل فرایند تولید HTML میشد. طبیعتا این تکرار برای کاربر هنگام استفاده از اپلیکیشن خیلی خوشایند نبود چرا که هربار میبایست زمانی بیشتر صرف تولید مجدد صفحات را منتظر میماند. اما در مقابل آن Single Page Application (SPA)‌‌ها این پروسه را تغیر داد.
در رویکرد SPA، کل CSS , HTML و JS ای که برای اجرای هر صفحه ای از اپلیکیشن نیاز هست در همان لود اولیه برنامه توسط مرورگر دانلود خواهد شد. با این روش هنگام تغییر URL صفحات مرورگر دیگر نیازی به لود دوباره اسکریپت‌ها ندارد. همچنین وقتی قرار است اطلاعات جدیدی از سرور آپدیت و نمایش داده شود این درخواست بصورت یک دستور Ajax به سرور ارسال شده و سرور با فرمت JSON اطلاعات درخواست شده را پاسخ میدهد. در نهایت مرورگر نیز اطلاعات برگشتی از سرور را مجدد جای گذاری میکند و کل این روند بدون هیچگونه رفرش دوباره صفحه انجام میشود.
در نتیجه‌ی این امر، کاربر تجربه خوشایند‌تری هنگام کار کردن با SPA‌ها خواهد داشت. اما همانقدر که این تجربه در طول زمان استفاده از برنامه بهبود یافت، لود اولیه اپلیکیشن را کند‌تر کرد، چرا که اپلیکیشن میبایست همه کدهای مورد نیاز خود برای صفحاتش را در همان ابتدا دانلود کند.(در ادامه با قابلیت Pre-Rendering این اشکال را تا حدود زیادی رفع میکنیم)
با استفاده از PWA میتوانید وبسایت‌های SPA را بصورت یک برنامه نصبی و تمام صفحه، با آیکن مجزایی همانند اپلیکیشن‌های دیگر در موبایل و دسکتاپ داشته باشید. همچنین وقتی از PWA استفاده میکنیم برنامه وب میتواند به صورت آفلاین نیز کار کند.
البته حتی در برنامه‌هایی که لازم نیست آفلاین کار کنند، در صورت قطعی ارتباط کاربر با شبکه، به جای دیدن دایناسور معروف، اینکه برنامه در هر حالتی باز شود و به صورتی کاربر پسند و قطعی نت به وی اعلام شود ایده خیلی بهتری است (": 


قابلیت Pre-Rendering:
هدف Pre-Rendering بهبود گشت گذار کاربر در سایت است. شیوه کارکرد آن به این صورت است که وقتی کاربر وارد وبسایت میشود، سرور در همان ابتدای کار و جلوتر از اتمام دانلود اسمبلی‌ها، فایلی حاوی HTML ، CSS‌های صفحه ای که کاربر درخواست کرده را در سمت سرور می‌سازد و به مرورگر ارسال میکند. در همین حین، اسمبلی‌ها نیز توسط مرورگر دانلود می‌شوند و برنامه از محتوای صرف خارج شده و حالت تعاملی می‌گیرد. اصطلاحا به این قابلیت Server-Side Rendering(SSR) نیز میگویند. در این حالت کاربر زودتر محتوایی از برنامه را میبیند و تجربه بهتری خواهد داشت. این امر در بررسی Search Engine‌‌ها و سئو وبسایت نیز تاثیر بسزایی دارد. 


نگاهی به Blazor:
تا اینجا هر آنچه که نیاز بود برای درک بهتر از Blazor بدانیم را فهمیدیم، اما خود Blazor چیست؟ در یک توضیح کوتاه، فریمورکی ارائه شده توسط مایکروسافت میباشد برای پیاده‌سازی UI و منطق برنامه‌ها شامل امکانات Routing ، Binding و...
بلیزر در انواع مختلفی ارائه شده که هرکدام کاربرد مشخصی دارد:

Blazor Server
در بلیزر سرور پردازش‌ها جهت تعامل UI درون سرور اجرا خواهد شد. برای مثال وقتی کاربر روی دکمه ای کلیک میکند و آن دکمه مقداری عددی را افزایش می‌دهد که از قضا متن یک Label به آن عدد وابسته است، رویداد کلیک شدن این دکمه توسط SignalR WebSocket به سرور ارسال شده و سرور تغیر متن Label را روی همان وب سوکت به کلاینت ارسال می‌کند.
با توجه به این که تعامل کاربر با صفحات برنامه، بسته به میزان کندی اینترنت کاربر، ممکن است کند شود و همچنین Blazor Server قابلیت PWA شدن ندارد و علاوه بر این بار پردازش زیادی روی سرور می‌اندازد (بسته به پیچیدگی پروژه) و در نهایت ممکن است در آن از Component هایی استفاده کنیم که چون در حالت Blazor Server پردازش سمت سرور بوده، متوجه حجم دانلود بالای آنها نشویم و کمی بعدتر که با Blazor Hybrid می‌خواهیم خروجی Android / iOS بگیریم متوجه حجم بالای آنها شویم، استفاده از Blazor Server را در Production توصیه نمی‌کنیم، ولی این حالت برای Debugging بهترین تجربه را ارائه می‌دهد، بالاخص با امکان Hot Reload و دیدن آنی تغییرات C# / HTML / CSS در ظاهر و رفتار برنامه موقع کدنویسی.

Blazor WebAssembly
در بلیزر وب اسمبلی منطق مثال قبلی که با C# .NET نوشته شده است، روی مرورگر و با کمک Web Assembly اجرا می‌شود و نیازی به ارتباط جاری با سرور توسط SignalR نیست. این باعث میشود که با بلیزر وب اسمبلی بتوان اپلیکیشن‌های PWA نیز نوشت.
یک برنامه Blazor Web Assembly می‌تواند چیزی در حدود دو الی سه مگ حجم داشته باشد که در دنیای امروزه حجم بالایی به حساب نمیاید، با این حال با کمک Pre Rendering و CDN می‌توان تجربه کاربر را تا حدود زیادی بهبود داد.
برای مثال سایت Component‌های Bit Platform جزو معدود دموهای Component‌های Blazor است که در حالت Blazor Web Assembly ارائه می‌شود و شما می‌توانید با باز کردن آن، تجربه حدودی کاربرانتان را در حین استفاده از Blazor Web Assembly ببینید. به علاوه، در dotnet 7 سرعت عملکرد Blazor Web Assembly بهبود قابل توجهی پیدا کرده است.

Blazor Hybrid
از Blazor Hybrid زمانی استفاده می‌کنیم که بخواهیم برنامه‌های موبایل را برای Android , iOS و برنامه‌های Desktop را برای ویندوز، با کمک HTML , CSS توسعه دهیم. نکته اصلی در Blazor Hybrid این است که اگر چه از Web View برای نمایش HTML / CSS استفاده شده، اما منطق سمت کلاینت برنامه که با C# .NET توسعه داده شده است، بیرون Web View و به صورت Native اجرا می‌شود که ضمن داشتن Performance بالا، به تمامی امکانات سیستم عامل دسترسی دارد. علاوه بر دسترسی به کل امکانات Android / iOS Sdk در همان C# .NET ، عمده کتابخانه‌های مطرح مانند Firebase، با ابزار Binding Library ارائه شده توسط مایکروسافت، دارای Wrapper قابل استفاده در C# .NET هستند و ساختن Wrapper برای هر کتابخانه Objective-C ، Kotlin، Java، Swift با این ابزار فراهم است.

اگر شما در حال حاضر فقط #HTML , CSS , C بدانید، اکنون با بلیزر میتوانید هر اپلیکیشنی که بخواهید توسعه دهید. از وبسایت‌های SPA گرفته تا اپ‌های موبایل Android ، IOS و برنامه‌های دسکتاپی برای Windows , Mac و بزودی نیز برای Linux
سری آموزش بلیزر را میتوانید از این لینک‌ها (1 ، 2) دنبال کنید. 


معرفی پکیج Bit Blazor UI:
پکیج Bit Blazor UI مجموعه ای از کامپوننت‌های مرسومی است که بر پایه بلیزر نوشته شده و به ما این امکان را میدهد تا المان‌های پیچیده ای مثل Date Picker , Grid , Color Picker , File Upload , DropDown و بسیاری از المان‌های دیگر را با شکلی بهینه، بدون نیاز به اینکه خودمان بخواهیم برای هر یک از اینها از نو کدنویسی کنیم، آن را در اختیار داشته باشیم.
عمده مشکل کامپوننت‌های ارائه شده برای بلیزر حجم نسبتا بالای آن است که باعث میشد بیشتر در مصارفی از قبیل ایجاد Admin Panel کارایی داشته باشد. اما این موضوع به خوبی در Bit Blazor UI مدیریت شده و در حال حاضر با بیش از 30 کامپوننت با حجم بسیار پایینی، چیزی حدود 200 کیلوبایت قابل نصب است. از لحاظ حجمی نسبت به رقبای خود برتری منحصر به فردی دارد که باعث میشود به راحتی حتی در اپلیکیشن‌های موبایل هم قابل استفاده باشد و کماکان پرفرمنس خوبی ارائه دهد.
این کامپوننت‌ها با ظاهر Fluent پیاده سازی شده و میتوانید لیست کامپوننت‌های بلیزر Bit را از این لینک ببینید. 


معرفی Bit TodoTemplate:
قبل از اینکه به معرفی Bit TodoTemplate بپردازیم باید بدانیم که اصلا پروژه‌های Template چه هستند. در واقع وقتی شما Visual Studio را باز میکنید و روی گزینه Create New Project کلیک میکنید با لیستی از پروژه‌های تمپلیت روبرو میشوید که هرکدام چهارچوب خاصی را با اهدافی متفاوت در اختیارتان قرار میدهند.
Bit Platform هم Project Template ای با نام TodoTemplate توسعه داده که میتوانید پروژه‌های خودتان را از روی آن بسازید، اما چه امکاناتی به ما میدهد؟
در یک جمله، هر آنچه تا به اینجا توضیح داده شد بصورت یکجا در TodoTemplate وجود دارد.
در واقع TodoTemplate چهارچوبی را فراهم کرده تا شما تنها با دانستن همین مفاهیمی که در این مقاله خواندید، از همان ابتدا امکاناتی چون صفحات SignUp، SignIn یا Email Confirmation و... را داشته باشید و در نهایت بتوانید تمامی خروجی‌های قابل تصور را بگیرید.
اما چگونه؟
در TodoTemplate همه این قابلیت‌ها تنها درون یک فایل و با کمترین تغیر ممکن نوع خروجی کدی که نوشته اید را مشخص میکند. این تنظیمات به شکل زیر است :
<BlazorMode> ... </BlazorMode>
<WebAppDeploymentType> ... </WebAppDeploymentType>
شما میتوانید با تنظیم <BlazorMode> بین انواع hosting model‌های بلیزر سوییچ کنید. مثلا برای زمانی که در محیط development هستید نوع بلیزر را Blazor Server قرار دهید تا از قابلیت‌های debugging بهتری برخوردار باشید ، وقتی میخواهید وبسایت تکمیل شده تان را بصورت SPA / PWA پابلیش کنید نوع بلیزر را به Blazor WebAssembly و برای پابلیش‌های Android ، IOS ، Windows ، Mac نوع بلیزر را به Blazor Hybrid تغیر دهید.
به علاوه، شما تنها با تغیر <WebAppDeploymentType> قادر خواهید بود بین SPA (پیش فرض)، SSR و PWA سوئیچ کنید.
قابلیت‌های TodoTemplate در اینجا به پایان نمیرسد و بخشی دیگر از این قابلیت‌ها به شرح زیر است :
  • وجود سیستم Exception handling در سرور و کلاینت (این موضوع به گونه ای بر اساس Best Practice‌‌ها پیاده سازی شده که اپلیکیشن را از بروز هر خطایی که بخواهد موجب Crash کردن برنامه شود ایزوله کرده)
  • وجود سیستم User Authentication بر اساس JWT که شما در همان ابتدا که از این تمپلیت پروژه جدیدی میسازید صفحات SignIn ، SignUp را خواهید داشت.
  • پکیج Bit Blazor UI که بالاتر درمورد آن صحبت کرده ایم از همان ابتدا در TodoTemplate نصب و تنظیم شده تا بتوانید به راحتی صفحات جدید با استفاده از آن بسازید.
  • کانفیگ استاندارد Swagger در سمت سرور.
  • ارسال ایمیل در روند SignUp.
  • وجود خاصیت AutoInject برای ساده‌سازی تزریق وابستگی ها.
  • و بسیاری موراد دیگر که در داکیومنت‌های پروژه میتوانید آنهارا ببینید.
با استفاده از TodoTemplate پروژه ای با نام Todo ساخته شده که میتوانید چندین مدل از خروجی‌های این پروژه را در لینکهای پایین ببینید و پرفرمنس آن را بررسی کنید.
توجه داشته باشید هدف TodoTemplate ارائه ساختار Clean Architecture نبوده ، بلکه هدف ارائه بیشترین امکانات با ساده‌ترین حالت کدنویسی ممکن بوده که قابل استفاده برای همگان باشد و شما میتوانید از هر پترنی که میخواهید براحتی در آن استفاده کنید.
پلتفرم Bit یک تیم توسعه کاملا فعال تشکیل داده که بطور مداوم در حال بررسی و آنالیز خطاهای احتمالی ، ایشو‌های ثبت شده و افزودن قابلیت‌های جدید میباشد که شما به محض استفاده از این محصولات میتوانید در صورت بروز هر اشکال فنی برای آن ایشو ثبت کنید تا تیم مربوطه آن را بررسی و در دستور کار قرار دهد. در ادامه پلتفرم Bit قصد دارد بزودی تمپلیت جدیدی با نام Admin Panel Template با امکاناتی مناسب برای Admin Panel مثل Dashboard و Chart و... با تمرکز بر Clean Architecture نیز ارائه کند. چیزی که مشخص است اوپن سورس بودن تقریبا %100 کارها میباشد از جلسات و گزارشات کاری گرفته تا جزئیات کارهایی که انجام میشود و مسیری که در آینده این پروژه طی خواهد کرد.
میتوانید اطلاعات بیشتر و مرحله به مرحله برای شروع استفاده از این ابزار‌ها را در منابعی که معرفی میشود دنبال کنید.

منابع:

مشارکت در پروژه:
  • شما میتوانید این پروژه را در گیتهاب مشاهده کنید.
  • برای اشکالات یا قابلیت هایی که میخواهید برطرف شود Issue ثبت کنید.
  • پروژه را Fork کنید و Star دهید.
  • ایشوهایی که وجود دارد را برطرف کنید و Pull Request ارسال کنید.
  • برای در جریان بودن از روند توسعه در جلسات برنامه ریزی (Planning Meeting) و گزارشات هفتگی (Standup Meeting ) که همه اینها در Microsoft Teams برگزار میشود شرکت کنید. 
بازخوردهای دوره
بررسی قسمت‌های مختلف قالب پروژه WPF Framework تهیه شده
فقط سه مورد (DbEntityValidationException, DbUpdateException, DbUpdateConcurrencyException)  از استثناهای ویژه EF Code first در متد ApplyAllChanges بررسی شدند به همراه خطاهای اعتبارسنجی. مابقی استثناها در صورت رخ دادن به لایه‌های بالاتر منتشر خواهند شد (چون catch نشدند).
همچنین کدهای تکراری نحوه نمایش فقط این سه مورد ویژه و بررسی خطاهای اعتبارسنجی، ضرورتی به تکرار یا بررسی در کل برنامه را ندارد. نیازی نیست در کل برنامه if/else نوشت که بررسی شود آیا خطای اعتبارسنجی هست یا خیر، زمانیکه می‌شود آن‌را به صورت مرکزی و پاکیزه، مدیریت کرد و مدیریت این‌ها هم حالت خاص دیگری ندارد (باید لاگ شوند و باید به اطلاع کاربر رسانده شوند که هر دو مورد در اینجا خودکار است). حداکثر این است که از نحوه نمایش آن راضی نیستید. کار سورس باز است. تغییرش بدید. این روش و این صفحه دیالوگ مطابق سلیقه من طراحی شده.
به علاوه در لایه‌های بالاتر نیز نیازی به بررسی سایر استثناها نیست چون این موارد در فایل App.xaml.cs در بالاترین سطح ممکن دریافت و لاگ می‌شوند؛ همچنین به کاربر هم نمایش داده خواهند شد (در متدهای appDispatcherUnhandledException و currentDomainUnhandledException).

البته این برنامه دسکتاپ است که یک چنین اجازه‌ای رو می‌ده. در برنامه‌های وب این موارد توسط ELMAH لاگ خواهند شد و به کاربر پیغام کلی خطایی رخ داده نمایش داده می‌شود.
اشتراک‌ها
چگونه بفهمیم فایل انتخابی واقعا عکسه یا نه ؟

موقعی که از کاربر یک عکس رو دریافت میکنیم نیازه که بررسی شه که آیا این فایل واقعا عکسه یا نه.

برای کنترل این موضوع Attribute‌های اعتبارسنجی بهترین انتخاب هستن و با یکبار افزودن میتوان از آنها در هر جایی استفاده کرد.

سمت سرور اینکار به این صورت هست ^

اما سمت کلاینت حتی اگه بیایم فرمت فایل رو هم بررسی کنیم بازهم جوابگو نیست و میشه به راحتی فرمت یک فایل ZIP رو به PNG تغییر داد و ارسالش کرد و عملا اعتبارسنجی سمت کلاینت کار خاصی رو انجام نمیده.

در این پروژه Attribute اعتبارسنجی برای بررسی فایل که آیا واقعا عکس هست یا نه نوشته شده، هم سمت کلاینت و هم سمت سرور.

نحوه انجام کار: برای سمت کلاینت عکسی که کاربر وارد میکنه رو در داخل تگ IMG نمایش میده و اگه عکس به درستی لود بشه رخداد OnLoad تگ IMG فراخوانی میشه و اعتبارسنجی پاس میشه.

تگ IMG به کاربر نمایش داده نمیشه و به صورت display: none هستش.

از این Attribute اعتبارسنجی در کنار Attribute‌های کنترل حجم فایل هم میشه استفاده کرد که قبل از اینکه بخوایم بررسی کنیم که آیا فایل واقعا عکسه یا نه ابتدا حجم فایل مورد بررسی قرار بگیره.

چگونه بفهمیم فایل انتخابی واقعا عکسه یا نه ؟
نظرات مطالب
پیاده سازی JSON Web Token با ASP.NET Web API 2.x
- کاربر شناسایی نمی‌شود یعنی هنوز توکن قبلی منقضی شده را به سمت سرور ارسال می‌کنید. توکن جدید دریافت شده را با توکن قبلی جایگزین کنید.
- به مثال این مطلب نمایش محتویات توکن دریافتی از سرور هم اضافه شده‌است. یکبار اجرایش کنید تا ریز محتویات توکن دریافتی از سرور را به همراه تمام Claims سفارشی آن بتوانید بررسی کنید. در نهایت این جزئیات هستند که به سمت سرور ارسال می‌شوند و دسترسی‌ها را ایجاد می‌کنند.
نظرات مطالب
EF Code First #12
این مساله ارتباطی به الگوی واحد کار ندارد. شما به عنوان برنامه نویس باید پس از بررسی تشخیص دهید که آیا خطر mass assignment در حین کار با شیء در حال دریافت از کاربر (هر نامی که دارد)، برنامه را تهدید می‌کند یا خیر. همچنین آیا View در حال استفاده نیاز به چند Model برای کار کردن دارد یا خیر. در این حالات استفاده از ViewModel توصیه می‌شود. در غیراینصورت استفاده از Domain modelها نه مشکل امنیتی را به همراه خواهند داشت و نه برای صرفا گزارش گیری، کم و کسری دارند.