مطالب
اتصال ویژوال پارادایم به ساب ورژن
در فرآیند تولید نرم افزار بصورت تیمی، یکپارچگی در انجام کارها بسیار حائز اهمیت است. بطوریکه می‌توان نقطه‌ی اتکای تیم را بر اساس یکپارچگی ابزارها و نرم افزارها دانست. در این بین برای استفاده‌ی از ابزارهای مدلینگ بصورت تیمی و پشتیبانی از این ابزارها، استفاده‌ی از ابزار‌های سورس کنترل در اولویتی بالاتر قرار دارند. دراین مقاله سعی شده‌است تا روش اتصال ابزار بسیار قدرتمند ویژوال پارادایم، به سورس کنترلر Subversion توضیح داده شود.
در ابتدا در سرور svn  که من از برنامه‌ی VisualSVN استفاده کرده‌ام، یک repository ایجاد کرده و یا قسمتی از repository موجود را برای پروژه ویژوال پارادایم اختصاص میدهیم:

سپس طبق استاندارد، فولدرهای استاندارد برای کار با svn را در حین ایجاد repository میسازیم: 


در ادامه آدرس فولدر trunk را کپی کرده و برنامه ویژوال پارادایم را باز میکنیم: 

از منوی Teamwork گزینه‌ی Open Teamwork Client را انتخاب می‌کنیم (همانطور که در تصویر مشخص است فعالیت‌های svn در این منو وجود دارد مانند branch و tag):  

لیستی از سورس کنترلرها در اینجا وجود دارد. تمام فیلدهای لازم را برای کار با ساب ورژن تکمیل میکنیم:  

نکته: فیلد آخر، آدرس برنامه ساب ورژن است که همراه برنامه‌ی ویژوال پارادایم نصب شده است.

در پنجره‌ی Teamwork Client منوی Project گزینه Import Project to Repository را انتخاب میکنیم:

در پنجره بعدی دو حالت برای اتمام اتصال داریم:
1) افزودن یک پروژه‌ی جدید ویژوال پارادایم
2) افزودن پروژه در دست

و بعد از انتخاب (در اینجا من برای سهولت کار از Create new Project استفاده کردم) گزینه‌ی Open Project را انتخاب میکنیم:

اکنون همانطور که می‌بینید اعمال مربوط به کار کردن با svn در دسترس می‌باشند:

نظرات مطالب
استفاده از توابع Scalar بجای case
در نسخه 2012 جهت سهولت در مهاجرت پایگاه داده‌های Access به SQL Server از توابع CHOOSE و IIF حمایت شده.
 
منتها تابع IIF چندان انعطاف پذیر نیست. مثلا اگر بخواهید به ازای چند حالت مشخص از داده‌های یک فیلد یک مقدار را برگردانید مجبورید چند تابع IIF تودرتو بنویسید. تودرتو بودن این تابع هم به 10 سطح محدود میشه.
اما CASE قابلیت‌ها و انعطاف پذیری بیشتری داره.
سوال میشه گاها کدام یک از این دو Performance یا کارایی بهتر دارد، در جواب میشه گفت هر دو برابر اند. در واقع IIF هنگام اجرا تبدیل به فرم CASE خواهد شد.
فرض کنید یک نظر سنجی تلوزیونی تنظیم کردیم که مردم از طریق پیامک نظر خودشان را به ما اعلام میکنند. شش گزینه هم داریم. برای انتخاب هر گزینه کافیست از اعداد 1 تا 6 استفاده کنیم. حال هنگام نمایش می‌خواهیم به جای اعداد مقدار متناظر ظاهر شود:

Use Tempdb
Go

CREATE TABLE [Sample] (value int);
INSERT INTO [Sample] VALUES (1),(2),(3),(4),(5),(6);
Go

--simple CASE Expression
SELECT value,
       CASE Value 
        WHEN 1 THEN 'Very Bad'
        WHEN 2 THEN 'Bad'
        WHEN 3 THEN 'Not Bad'
        WHEN 4 THEN 'Good'
        WHEN 5 THEN 'Very Good'
        WHEN 6 THEN 'Excellent'
       ELSE NULL
END AS [Result]
FROM [Sample];

--CHOOSE Scalar Function
SELECT value,
       CHOOSE(value,'Very Bad','Bad','Not Bad','Good','Very Good','Excellent')
FROM [Sample];


--nested IIF Scalr Function
SELECT value, 
IIF(value = 1, 'Very Bad',
  IIF(value = 2, 'Bad',
    IIF(value = 3, 'Not Bad',
      IIF(value = 4, 'Good',
        IIF(value = 5, 'Very Good', 'Excellent'
           )
         )
       )
     )
   )
FROM [Sample];
مطالب
روش ذخیره‌ی لاگ‌های ILogger در پایگاه داده در Blazor

مقدمه

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

ایجاد کلاس لاگ

برای این منظور ابتدا کلاسی را برای ذخیره‌ی لاگ‌ها در پایگاه داده به شکل زیر ایجاد می‌نماییم:
public class DBLog
    {
        public int  DBLogId { get; set; }
        public string? LogLevel { get; set; }
        public string? EventName { get; set; }
        public string? Message { get; set; }
        public string? StackTrace { get; set; }
        public DateTime CreatedDate { get; set; }=DateTime.Now;
    }


ایجاد دیتابیس لاگر

کلاس DBLogger از اینترفیس ILogger ارث بری می‌کند و دارای سه متد است که مهمترین آنها متد Log می‌باشد که درحقیقت با هر بار فراخوانی Logger در برنامه فراخوانی می‌شود. برای مطالعه‌ی بیشتر در رابطه با دو متد دیگر می‌توانید به اینجا مراجعه نمایید.
public class DBLogger:ILogger

    {
        private bool _isDisposed;
        private readonly ApplicationDbContext _dbContext;
        public DBLogger(ApplicationDbContext dbContext)
        {
            _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
        }


        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
        {
            var dblLogItem = new DBLog()
            {
                EventName = eventId.Name,
                LogLevel = logLevel.ToString(),
                Message = exception?.Message,
                StackTrace=exception?.StackTrace                
            }; 
            _dbContext.DBLogs.Add(dblLogItem);
            _dbContext.SaveChanges();
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }

        public IDisposable BeginScope<TState>(TState state)
        {
            return null;
        }
    }


ایجاد یک لاگ پروایدر سفارشی

حال باید یک لاگ پروایدر سفارشی را ایجاد کنیم تا بتوان یک نمونه از دیتابیس لاگر سفارشی بالا (DBLogger) را ایجاد کرد.

public class DbLoggerProvider:ILoggerProvider
    {
        private bool _isDisposed;
        private readonly ApplicationDbContext _dbContext;
        public DbLoggerProvider(ApplicationDbContext dbContext)
        {
            _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
        }

        public ILogger CreateLogger(string categoryName)
        {
            return new D‌BLogger(_dbContext);
        }

        public void Dispose()
        {
        }
    }  

همانطور که ملاحظه می‌نمایید، این لاگ پروایدر، از اینترفیس ILoggerProvider ارث بری کرده‌است که دارای متد CreateLogger می‌باشد ئ این متد با شروع برنامه، یک نمونه از دیتابیس لاگر سفارشی ما را ایجاد می‌کند. در سازنده‌ی این کلاس، DatabaseContext را مقدار دهی نموده‌ایم تا آنرا به کلاس DBLogger ارسال نماییم.

در انتها کافیست در کلاس Startup.cs این لاگ پروایدر سفارشی (DbLoggerProvider  ) را صدا بزنیم.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
        {
            .
            .
            .
            #region CustomLogProvider
            var serviceProvider = app.ApplicationServices.CreateScope().ServiceProvider;
            var appDbContext = serviceProvider.GetRequiredService<ApplicationDbContext>();
            loggerFactory.AddProvider(new DbLoggerProvider(appDbContext));
            #endregion
            .
            .
            .
در اینجا ابتدا DBContext پروژه را گرفته سپس DbLoggerProvider   را صدا زده و مقدار DBContext را به آن پاس می‌دهیم.


مشکل!

منطق کدهای بالا کاملا صحیح می‌باشد، اما با اجرای یک اپلیکیشن واقعی، در ابتدای کار اینقدر تعداد فراخوانی ثبت لاگ‌ها در پایگاه داده بالا می‌رود که اپلیکیشن هنگ می‌کند. برای حل این مشکل باید یک صف همزمانی برای ثبت لاگ‌ها تشکیل شود. برای این منظور من از این مطلب پروژه‌ی DNTIdentity بهره بردم. بنابراین باید پروایدر را به شکل زیر تصحیح کنیم:
public class DbLoggerProvider:ILoggerProvider
    {
        private readonly CancellationTokenSource _cancellationTokenSource = new();
        private readonly IList<DBLog> _currentBatch = new List<DBLog>();
        private readonly TimeSpan _interval = TimeSpan.FromSeconds(2);

        private readonly BlockingCollection<DBLog> _messageQueue = new(new ConcurrentQueue<DBLog>());

        private readonly Task _outputTask;
        private readonly ApplicationDbContext _dbContext;
        private bool _isDisposed;

        public DbLoggerProvider(ApplicationDbContext dbContext)
        {
            _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
            _outputTask = Task.Run(ProcessLogQueue);
        }
        public ILogger CreateLogger(string categoryName)
        {
            return new DBLogger(this,categoryName);
        }
        private async Task ProcessLogQueue()
        {
            while (!_cancellationTokenSource.IsCancellationRequested)
            {
                while (_messageQueue.TryTake(out var message))
                {
                    try
                    {
                        _currentBatch.Add(message);
                    }
                    catch
                    {
                        //cancellation token canceled or CompleteAdding called
                    }
                }

                await SaveLogItemsAsync(_currentBatch, _cancellationTokenSource.Token);
                _currentBatch.Clear();

                await Task.Delay(_interval, _cancellationTokenSource.Token);
            }
        }
        internal void AddLogItem(DBLog appLogItem)
        {
            if (!_messageQueue.IsAddingCompleted)
            {
                _messageQueue.Add(appLogItem, _cancellationTokenSource.Token);
            }
        }
        private async Task SaveLogItemsAsync(IList<DBLog> items, CancellationToken cancellationToken)
        {
            try
            {
                if (!items.Any())
                {
                    return;
                }

                // We need a separate context for the logger to call its SaveChanges several times,
                // without using the current request's context and changing its internal state.
                foreach (var item in items)
                {
                    var addedEntry = _dbContext.DbLogs.Add(item);
                }

                await _dbContext.SaveChangesAsync(cancellationToken);

            }
            catch
            {
                // don't throw exceptions from logger
            }
        }

        [SuppressMessage("Microsoft.Usage", "CA1031:catch a more specific allowed exception type, or rethrow the exception",
            Justification = "don't throw exceptions from logger")]
        private void Stop()
        {
            _cancellationTokenSource.Cancel();
            _messageQueue.CompleteAdding();

            try
            {
                _outputTask.Wait(_interval);
            }
            catch
            {
                // don't throw exceptions from logger
            }
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_isDisposed)
            {
                try
                {
                    if (disposing)
                    {
                        Stop();
                        _messageQueue.Dispose();
                        _cancellationTokenSource.Dispose();
                        _dbContext.Dispose();
                    }
                }
                finally
                {
                    _isDisposed = true;
                }
            }
        }
    }  
همچنین دیتابیس لاگر نیز به شکل زیر تغییر خواهد کرد:
public class DBLogger:ILogger
    {
        private readonly LogLevel _minLevel;
        private readonly DbLoggerProvider _loggerProvider;
        private readonly string _categoryName;

        public DBLogger(
            DbLoggerProvider loggerProvider,
            string categoryName
        )
        {
            _loggerProvider= loggerProvider ?? throw new ArgumentNullException(nameof(loggerProvider));
            _categoryName= categoryName;
        }

        public IDisposable BeginScope<TState>(TState state)
        {
            return new NoopDisposable();
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            return logLevel >= _minLevel;
        }

        public void Log<TState>(
            LogLevel logLevel,
            EventId eventId,
            TState state,
            Exception exception,
            Func<TState, Exception, string> formatter)
        {
            if (!IsEnabled(logLevel))
            {
                return;
            }

            if (formatter == null)
            {
                throw new ArgumentNullException(nameof(formatter));
            }

            var message = formatter(state, exception);

            if (exception != null)
            {
                message = $"{message}{Environment.NewLine}{exception}";
            }

            if (string.IsNullOrEmpty(message))
            {
                return;
            }

            var dblLogItem = new DBLog()
            {
                EventName = eventId.Name,
                LogLevel = logLevel.ToString(),
                Message = $"{_categoryName}{Environment.NewLine}{message}",
                StackTrace=exception?.StackTrace
            };
            _loggerProvider.AddLogItem(dblLogItem);
        }

      

        private class NoopDisposable : IDisposable
        {
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }

            protected virtual void Dispose(bool disposing)
            {
            }
        }
    }  
خلاصه تغییرات اینگونه‌است که بجای ذخیره لاگ‌ها در دیتابیس در داخل کلاس DBLogger، آنها را در یک صف همزمانی اضافه می‌کنیم و پس از اتمام پروسه، ذخیره سازی لاگ‌ها را در لاگ پروایدر انجام می‌دهیم.
توسط ترکیب روش توضیح داده شده در این مقاله با مدیریت استثناءها در Blazor Server - قسمت دوم، علاوه برلاگ‌های معمولی، می‌توان تمامی استثناءهای یک اپلیکیشن را نیز به راحتی در پایگاه داده ذخیره نمود.
مطالب
سیستم‌های توزیع شده در NET. - بخش اول - نیازمندی
در حوزه کاری ما همیشه نیازمندی‌های جدید باعث پیشرفت، ارتقاء و پیچیده‌تر شدن سیستم‌های سخت افزاری و نرم افزاری می‌شوند. بطور مثال زمانیکه نیاز شد چندین سیستم از داده‌های مشترکی استفاده کنند، در معماری Single Tier قسمت Database از سایر قسمت‌ها جدا شد و در سخت افزار دیگری قرار گرفت. به این صورت این معماری تبدیل به Two Tier شد و سپس برای اینکه تغییرات، کمترین تاثیر را در سیستم ما داشته باشد و با کمترین هزینه به Platform‌های دیگر نیز سرویس بدهیم، قسمت Presentation از سایر قسمت‌ها جدا شد و در سخت افزارهای دیگری قرار گرفت. به این صورت  تبدیل به معماری Three Tier شد و همینطور نیازهای جدید باعث شد معماری N Tier پیچیده‌تر شود. البته پیچیدگی که باعث تکامل آن شد یا بطور مثال نیاز به پردازش تعداد بیشتری عملیات بصورت همزمان، که باعث شد سیستم‌های ما از حالت Single Task تبدیل به Parallel System و سپس Distributed system شوند. واقعیت این است که در دنیای امروز، نیازهای جدیدی بوجود آمده‌اند؛ نیازهایی که یک سخت افزار به تنهایی قادر به ارائه راهکاری برای آنها نیست. واقعا یک سخت افزار که یک سرویس را ارائه می‌دهد، چه خصوصیاتی باید داشته باشد که مثلا در ثانیه حدود 50 میلیون عملیات را بصورت همزمان انجام دهد؛ یا مثلا سرویس مورد نظر حدود 400 میلیون کاربر فعال که روزانه بیشتر وقت خود را به استفاده از این سرویس اختصاص می‌دهند داشته باشد؟

آیا میدانستید که Facebook در حال حاضر بیشتر از 400 میلیون کاربر فعال دارد که حدود 200 میلیون از این کاربران هر روز از این سرویس استفاده می‌کنند؟ آماری که این سرویس داده به این صورت است که تا سال 2010، کاربرانش در هر روز حدود 16 بیلیون دقیقه از وقت خودشان را به استفاده از این سرویس اختصاص داده‌اند. در هر هفته کاربران این سرویس حدود 6 بیلیون مطلب را که شامل عکس و متن بوده را به اشتراک گذاشته‌اند. هر ماه بیشتر از 3 بیلیون عکس توسط این سرویس Upload شده‌است. کاربرانش روزانه بیشتر از 1 بیلیون عکس را توسط این سرویس مشاهده کرده‌اند. این سرویس در ثانیه حدود 50 میلیون عملیات را انجام می‌دهد!

آیا میدانستید که سرویس Twitter در حال حاضر 350 میلیون کاربر فعال دارد که حدود 100 میلیون از این کاربران روزانه از این سرویس استفاده می‌کنند و هر روز کاربران این سرویس 500 میلیون توییت را ارسال می‌کنند. این سرویس در فینال جام جهانی حدود 618,725 توییت را در یک دقیقه دریافت کرده‌است.
و یا سرویس Telegram حدود 100 میلیون کاربر فعال دارد که بصورت متوسط در هر روز 220 هزار کاربر جدید در آن ثبت نام می‌کنند. کاربران این سرویس روزانه 15 بیلیون پیام را ارسال می‌کنند و 700 میلیون عکس را به اشتراک می‌گذارند.
چه سروری به تنهایی می‌تواند این آمار و ارقام را پوشش دهد؟ هزینه خرید و نگهداری آن چقدر است؟ چقدر باید هزینه کنیم تا این سرور از دسترس خارج نشود؟ یک سرور به تنهایی چه راهکاری را می‌تواند ارائه دهد که هیچوقت از دسترس خارج نشود؟
بهتر است بدانید که سرویس Facebook روی بیش از 60,000 سرور ارائه می‌شود! چه سروری به تنهایی می‌تواند کارآیی 60,000 سرور را داشته باشد؟
چه نوع پایگاه داده ای که روی یک سرور سرویس ارائه می‌دهد، قادر است روزانه به بیشتر از 1 تریلیون درخواست، پاسخ دهد؟ اصلا چه سروری به تنهایی قادر است این حجم داده را که هر روز رو به افزایش است، نگهداری کند؟

بهتر است بدانید پایگاه داده سرویس‌های شرکت Apple، روی بیش از 75,000  Node قرار دارند که روزانه حدود 10 پتابایت داده ذخیره می‌کنند. یا Netflix که از 2,500  Node استفاده می‌کند و روزانه 420 ترابایت داده را در قالب 1 تریلیون درخواست دریافت می‌کند یا Easou که پایگاه داده آن روی 270  Node قرار دارد و روزانه 300 ترابایت داده را در قالب 800 میلیون درخواست دریافت می‌کند! اینها اعداد و ارقامی نیستند که ما بتوانیم با SqlServerی که روی یک سرور قرار دارد، پوشش دهیم.

چه تعداد سرویس را در کشورمان می‌شناسید که در زمان بالا رفتن تعداد درخواست از دسترس خارج می‌شوند؟ چه تعداد سرویس را می‌شناسید که تنها راهکاری را که می‌توان در این زمان برای آنها ارائه داد، ارتقاء سخت افزار آنهاست؟ پس از مدتی با بالا رفتن تعداد کاربران، دوباره سخت افزار را ارتقاء می‌دهیم. تا کجا باید این کار را تکرار کنیم؟ چقدر هزینه کنیم برای سخت افزاری که ممکن است به هر دلیلی در هر لحظه از دسترس خارج شود؟ آیا با توجه به آمار تعداد کاربران، تعداد درخواست و حجم داده‌ی سرویس‌هایی که می‌شناسید و قابل مقایسه با آمار ذکر شده است، باز هم از دسترس خارج می‌شوند؟

در سری مقالات Distributed systems in .NET من سعی می‌کنم شما را با خصوصیات و اصطلاحات موجود در سیستمهای توزیع شده آشنا کنم و ابزارهایی را که در NET. برای پیاده سازی این نوع سیستم‌ها وجود دارد، به شما معرفی کنم و با هم ببینیم که این نوع سیستم‌ها چه راهکاری را برای رفع نیازمندی‌های ما ارائه می‌دهند.


نمونه‌ای از طراحی یک سیستم توزیع شده

مطالب
معرفی 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 برگزار میشود شرکت کنید. 
اشتراک‌ها
کلاس های هوش مصنوعی مایکروسافت

Artificial Intelligence (AI) will define the next generation of software solutions. Human-like capabilities such as understanding natural language, speech, vision, and making inferences from knowledge will extend software beyond the app. 

کلاس های هوش مصنوعی مایکروسافت
اشتراک‌ها
مفهوم Loose Coupling

Anyone can write software, but it takes a seasoned developer to write decoupled software. Today, I cover the definition of loose coupling, how to detect it, and ways to improve your code. 

مفهوم Loose Coupling
اشتراک‌ها
گوگل سیستم عامل فیوژا را به‌صورت آزمایشی روی آنر پلی هواوی اجرا کرد

ویژگی‌های جذاب و سیاست‌های درست گوگل در توسعه‌ی اندروید باعث شده تا این سیستم‌عامل به مؤفقیت قابل‌توجهی در بازار گوشی‌های هوشمند دست پیدا کند. محبوبیت گوشی‌های اندرویدی به اندازه‌ای در بین جوامع مختلف رشد پیدا کرده که کمتر کسی پیدا می‌شود با واژه‌ی اندروید بیگانه باشد. 

گوگل سیستم عامل فیوژا را به‌صورت آزمایشی روی آنر پلی هواوی اجرا کرد
اشتراک‌ها
پلتفرم Codename One امکان کدنویسی به زبان جاوا برای اندروید، iOS‌ و ویندوزفون

استفاده از یک پلتفرم واحد برای توسعه‌ی اپلیکیشن‌ها و سرویس‌ها برای انواع پلتفرم‌ها و سیستم‌عامل‌ها یکی از آرزو‌های دیرین توسعه‌دهندگان است. استارت‌آپ Codename One وعده داده که با استفاده از پلتفرم این کمپانی می‌توان به زبان جاوا کدنویسی کرده و نتیجه را برای سایر پلتفرم‌ها کامپایل کرد. 

پلتفرم Codename One امکان کدنویسی به زبان جاوا برای اندروید، iOS‌ و ویندوزفون
اشتراک‌ها
آشنایی با رویدادهای سراسری ajax در jQuery

یکی از ویژگی‌های مفید jQuery در هنگام توسعه‌ی نرم‌افزارهای تحت وب، رویدادهای( event ) سراسری است. با تعریف این رویدادها می‌توانید در هنگام هر فراخوانی ajax در هر بخش از نرم‌افزار، آگاه شوید و عملیات دلخواه مانند نمایش عبارت «loading» یا جلوگیری از بارگزاری مجدد صفحه یا هر عمل متناسب دیگر را به انجام برسانید 

آشنایی با رویدادهای سراسری ajax در jQuery