اشتراک‌ها
پروژه CRUD دات نت 7 با Angular و Web API

.NET 7 💥 - CRUD with Angular & .NET Web API, EF Core & MongoDb

In this video we will be going to be exploring creating a full application from frontend with Angular and backend with WebApi and database as MongoDb
 

پروژه CRUD دات نت 7 با Angular و Web API
اشتراک‌ها
احراز هویت (Identity) در ASP.Net Core با استفاده از بانک اطلاعاتی MongoDB

برای انتقال جداول احراز هویت (Identity) از SQL Server به بانک اطلاعاتی MongoDB و نحوه استفاده از آن در ASP.Net Core از سورس نمونه در لینک بالا استفاده کنید.
همچنین می‌توانید از پکیج  AspNetCore.Identity.Mongo استفاده کنید.

This is a MongoDB provider for the ASP.NET Core 2 Identity framework 
 
احراز هویت (Identity) در  ASP.Net Core با استفاده از بانک اطلاعاتی MongoDB
نظرات نظرسنجی‌ها
شما کدامیک از بانک های nosql سندگرا را ترجیح می دهید؟
با تشکر از نظرسنجی مفید

MongoDb یکی از بهترین noSQL هایی هست که تا حالا کار کردم و مزیت آن نسبت به بقیه رایگان بودن و وجود منابع آموزشی و رفع اشکال زیاد آن است.
البته بقیه هم خوب هستند ولی MongoDb آموزشهای زیادی هم داره که همه کاربران بدون هیچ زحمت و هزینه میتوانند از آن استفاده کنند.
وجود Driver‌های مناسب و استفاده آسان آن در هر زبانی هم یکی دیگه از دلایل استفاده بنده از آن شده است مخصوصا در asp.net mvc , php براحتی میتوان از آن استفاده کرد.
مطالب
مهارت‌های تزریق وابستگی‌ها در برنامه‌های NET Core. - قسمت هفتم - کار با سرویس‌های متفاوتی با یک امضاء
فرض کنید قرارداد IService را تدارک دیده‌اید و بر اساس آن سرویس‌های A و B را به سیستم تزریق وابستگی‌های برنامه‌های NET Core. تزریق کرده‌اید (برای مثال در برنامه دو DbContext را تعریف کرده‌اید و یک اینترفیس IUnitOfWork را دارید). اکنون اگر از این سیستم، یک پیاده سازی IService را درخواست کنید، چه اتفاقی رخ می‌دهد؟ در حالت معمول آن، آخرین سرویسی را که ثبت کرده‌اید، یعنی وهله‌ای از سرویس B را بازگشت خواهد داد. در ادامه قصد داریم با این قابلیت امکان ثبت چندین سرویس مشتق شده‌ی از یک اینترفیس، بیشتر آشنا شویم.


ثبت پیاده سازی‌های متعدد یک اینترفیس در سیستم تزریق وابستگی‌های NET Core.

داشتن چندین پیاده سازی از یک اینترفیس، شاید یکی از اهداف اصلی وجود آن‌ها باشد. برای نمونه قرار داد ارسال پیامی را در برنامه، مانند IMessageService به صورت زیر طراحی و سپس بر اساس آن، دو پیاده سازی ارسال پیام‌ها را از طریق ایمیل و SMS، اضافه می‌کنیم:
namespace CoreIocServices
{
    public interface IMessageService
    {
        void Send(string message);
    }

    public class EmailService : IMessageService
    {
        public void Send(string message)
        {
            // ...
        }
    }

    public class SmsService : IMessageService
    {
        public void Send(string message)
        {
            //todo: ...
        }
    }
}
در ادامه برای معرفی آن‌ها به سیستم تزریق وابستگی‌های NET Core.، به نحو متداول زیر عمل خواهیم کرد و هر دوی این پیاده سازی‌ها را بر اساس اینترفیس آن‌ها معرفی می‌کنیم:
namespace CoreIocSample02
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IMessageService, EmailService>();
            services.AddTransient<IMessageService, SmsService>();
        }
در این حالت اگر این سرویس‌ها را به صورت زیر به یک کنترلر تزریق کنیم، انتظار دریافت کدام سرویس‌ها را خواهید داشت؟
using CoreIocServices;
using Microsoft.AspNetCore.Mvc;

namespace CoreIocSample02.Controllers
{
    public class MessagesController : Controller
    {
        private readonly IMessageService _emailService;
        private readonly IMessageService _smsService;

        public MessagesController(IMessageService emailService, IMessageService smsService)
        {
            _emailService = emailService;
            _smsService = smsService;
        }
    }
}
در این حالت اگر بر روی سازنده‌ی این کنترلر یک break-point را قرار دهیم، پارامترهای تزریق شده‌ی در سازنده‌ی کلاس به صورت زیر خواهند بود:


همانطور که مشاهده می‌کنید، هر دو پارامتر، با وهله‌ای از آخرین سرویس معرفی شده‌ی از نوع IMessageService مقدار دهی شده‌اند؛ یعنی SmsService در اینجا. در ادامه روش‌های مختلفی را برای رفع این مشکل بررسی می‌کنیم.


روش اول: سیستم تزریق وابستگی‌های NET Core. از سازنده‌های IEnumerable پشتیبانی می‌کند

برای مدیریت دریافت سرویس‌هایی که از یک اینترفیس مشتق شده‌اند، خود NET Core. بدون نیاز به تنظیمات اضافه‌تری، روش تزریق IEnumerableها را در سازنده‌های کلاس‌ها پشتیبانی می‌کند:
using System.Collections.Generic;
using CoreIocServices;
using Microsoft.AspNetCore.Mvc;

namespace CoreIocSample02.Controllers
{
    public class MessagesController : Controller
    {
        private readonly IEnumerable<IMessageService> _messageServices;

        public MessagesController(IEnumerable<IMessageService> messageServices)
        {
            _messageServices = messageServices;
        }
در اینجا نیز اگر بر روی سازنده‌ی این کنترلر یک break-point را قرار دهیم، پارامتر تزریق شده‌ی در سازنده‌ی کلاس به صورت زیر خواهد بود:


به این ترتیب لیست وهله‌های تمام سرویس‌هایی از نوع IMessageService در اختیار ما قرار گرفته‌است و برای اهدافی مانند پیاده سازی الگوهایی در رده‌ی chain of responsibility و یا الگوی استراتژی، مفید است. در این حالت اگر نیاز به سرویس از نوع خاصی وجود داشت، می‌توان از روش زیر استفاده کرد:
var emailService = _messageServices.OfType<EmailService>().First();
و یا اگر از روش Service Locator استفاده می‌کنید، serviceProvider به همراه متد ویژه‌ی GetServices که لیست تمام سرویس‌هایی از نوعی خاص را بر می‌گرداند نیز هست:
var messageServices = serviceProvider.GetServices<IMessageService>();


روش دوم: دریافت شرطی وهله‌های مورد نیاز با «دخالت در مراحل وهله سازی اشیاء توسط IoC Container»

روش «دخالت در مراحل وهله سازی اشیاء توسط IoC Container» را در قسمت قبل بررسی کردیم. یکی دیگر از مثال‌هایی را که در این مورد می‌توان ارائه داد، شرطی کردن بازگشت وهله‌ها است که برای سناریوی مطلب جاری بسیار مفید است:
الف) برای شرطی کردن بازگشت وهله‌ها، بهتر است این شرط را بجای رشته‌ها و یا اعدادی خاص، بر اساس یک enum مشخص انجام دهیم:
namespace CoreIocServices
{
    public enum MessageServiceType
    {
        EmailService,
        SmsService
    }
در اینجا یک enum جدید را تعریف کرده‌ایم تا مقایسه‌ها را در زمان بازگشت سرویسی خاص، بر اساس اعضای مشخص آن انجام دهیم.

ب) سپس هر دو سرویس مشتق شده‌ی از یک اینترفیس را به صورت معمولی ثبت می‌کنیم:
namespace CoreIocSample02
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<EmailService>();
            services.AddTransient<SmsService>();
اینکار سبب خواهد شد تا بتوانیم در حین بررسی شرط‌های رسیده، برای مثال دستور ()<serviceProvider.GetService<EmailService را صادر کنیم.

ج) اکنون می‌توان مرحله‌ی شرطی کردن بازگشت این وهله‌ها را به صورت زیر، با دخالت در مراحل وهله سازی اشیاء، پیاده سازی کرد:
namespace CoreIocSample02
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<EmailService>();
            services.AddTransient<SmsService>();
            services.AddTransient<Func<MessageServiceType, IMessageService>>(serviceProvider => key =>
            {
                switch (key)
                {
                    case MessageServiceType.EmailService:
                        return serviceProvider.GetRequiredService<EmailService>();
                    case MessageServiceType.SmsService:
                        return serviceProvider.GetRequiredService<SmsService>();
                    default:
                        throw new NotImplementedException($"Service of type {key} is not implemented.");
                }
            });
در اینجا پارامتر ارسالی به متد AddTransient را از نوع <Func<MessageServiceType, IMessageService انتخاب کرده‌ایم. این مورد نیز دقیقا مانند «مثال 2: وهله سازی در صورت نیاز وابستگی‌های یک سرویس به کمک Lazy loading» قسمت قبل عمل می‌کند. یعنی تا زمانیکه این Func، در قسمتی از کدهای برنامه فراخوانی نشود، سرویسی را نیز بازگشت نخواهد داد.

د) مرحله‌ی آخر کار، روش استفاده‌ی از این امضایء ویژه‌ی Lazy load شده‌است:
namespace CoreIocSample02.Controllers
{
    public class MessagesController : Controller
    {
        private readonly Func<MessageServiceType, IMessageService> _messageServiceResolver;

        public MessagesController(Func<MessageServiceType, IMessageService> messageServiceResolver)
        {
            _messageServiceResolver = messageServiceResolver;
        }
دقیقا همان امضای Func ای را که در متد AddTransient معرفی تنظیمات تزریق وابستگی‌ها تعریف کردیم، به سازنده‌ی یک کنترلر تزریق می‌کنیم. تا اینجای کار هنوز هیچکدام از سرویس‌های از نوع IMessageService وهله سازی نشده‌اند (روش دیگر پیاده سازی وهله سازی با تاخیر و در صورت نیاز). اکنون برای وهله سازی آن‌ها باید به صورت زیر عمل کرد:
public IActionResult Index()
{
   var emailService = _messageServiceResolver(MessageServiceType.EmailService);
   //use emailService ...
   return View();
}

کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: CoreDependencyInjectionSamples-07.zip
مطالب
مدیریت پیشرفته‌ی حالت در React با Redux و Mobx - قسمت دوم - بررسی توابع Redux
همانطور که در مقدمه‌ی قسمت قبل نیز عنوان شد، در این سری ابتدا کتابخانه‌ی Redux را به صورت مجزایی از React بررسی می‌کنیم؛ چون در اصل، یک کتابخانه‌ی مدیریت حالت عمومی است و وابستگی خاصی را به React ندارد و در بسیاری از برنامه و فریم‌ورک‌های دیگر نیز قابل استفاده‌است.


ایجاد یک برنامه‌ی خالی React برای آزمایش توابع Redux

در اینجا برای بررسی توابع Redux، یک پروژه‌ی جدید React را ایجاد می‌کنیم:
> npm i -g create-react-app
> create-react-app state-management-redux-mobx
> cd state-management-redux-mobx
> npm start
در ادامه کتابخانه‌ی Redux را نیز نصب می‌کنیم. برای این منظور پس از باز کردن پوشه‌ی اصلی برنامه توسط VSCode، دکمه‌های ctrl+` را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
> npm install --save redux
البته برای کار با Redux، الزاما نیازی به طی مراحل فوق نیست؛ ولی چون این قالب، یک محیط آماده‌ی کار با ES6 را فراهم می‌کند، به سادگی می‌توان فایل index.js آن‌را خالی کرد و سپس شروع به کدنویسی و آزمایش Redux نمود.


معرفی سریع توابع Redux

Redux، کتابخانه‌ی کوچکی است و تنها از 5 تابع تشکیل شده‌است:
applyMiddleware: function()
bindActionCreators: function()
combineReducers: function()
compose: function()
createStore: function()
و سه مورد از آن‌ها بیشتر کمکی هستند. برای مثال تابع compose مانند متد flow و یا pipe در کتابخانه‌ی lodash است و اصلا به Redux مرتبط نیست. تابع combineReducers، اشیاء موجود در state را با هم ترکیب می‌کند. bindActionCreators نیز یک تابع کمکی است جهت ایجاد ساده‌تر ActionCreators. بنابراین کتابخانه‌ی Redux، آنچنان گسترده نیست.


بررسی تابع compose با یک مثال

پس از ایجاد پروژه‌ی React و افزودن کتابخانه‌ی Redux به آن، به فایل src\index.js این پروژه مراجعه کرده و محتویات آن‌را با قطعه کد ذیل، تعویض می‌کنیم:
import { compose } from "redux";

const makeLouder = text => text.toUpperCase();
const repeatThreeTimes = text => text.repeat(3);
const embolden = text => text.bold();

const makeLouderAndRepeatThreeTimesAndEmbolden = compose(
  embolden,
  repeatThreeTimes,
  makeLouder
);
console.log(makeLouderAndRepeatThreeTimesAndEmbolden("Hello"));
- در ابتدای این ماژول، تابع compose را از کتابخانه‌ی redux دریافت کرده‌ایم.
- سپس سه تابع ساده را برای ضخیم کردن، تکرار و با حروف بزرگ نمایش دادن یک متن ورودی، تعریف کرده‌ایم.
- اکنون با استفاده از متد compose کتابخانه‌ی redux، این سه متد را به صورت ترکیبی، بر روی متن ورودی Hello، اعمال کرده‌ایم.
- در آخر اگر برای مشاهده‌ی نتیجه‌ی اجرای console.log بر روی آن، به کنسول توسعه دهندگان مرورگر مراجعه کنیم، به خروجی زیر خواهیم رسید:
 <b>HELLOHELLOHELLO</b>
همانطور که مشاهده می‌کنید، متن Hello را سه بار با حروف بزرگ تکرار نموده و در نهایت آن‌را با تک b محصور کرده‌است.


بررسی تابع createStore با یک مثال

Store در Redux، جائی است که در آن state برنامه ذخیره می‌شود. تابع createStore که کار ایجاد store را انجام می‌دهد، یک پارامتر را دریافت می‌کند و آن‌هم تابع reducer است که در قسمت قبل معرفی شد و در ساده‌ترین حالت آن، به نحو زیر با یک متد خالی، قابل فراخوانی است:
import { createStore } from "redux";

createStore(() => {});
تابع reducer دو پارامتر را دریافت می‌کند: وضعیت فعلی برنامه (state در اینجا) و رخ‌دادی که واقع شده‌است (action در اینجا). خروجی آن نیز یک state جدید بر این اساس است:
import { createStore } from "redux";

const reducer = (state, action) => {
  return state;
};
const store = createStore(reducer);
console.log(store);
در این مثال، تابع reducer را طوری تعریف کرده‌ایم که بر اساس هر نوع رخ‌دادی که به آن برسد، همان وضعیت قبلی را بازگشت دهد. سپس بر اساس آن یک store را ایجاد کرده‌ایم. اگر به خروجی console.log بررسی محتوای این شیء دقت کنیم، به صورت زیر خواهد بود:


در شیء store، چهار متد dispatch, subscribe, getState, replaceReducer مشخص هستند:
- کارکرد متد replaceReducer مشخص است؛ یک reducer جدید را به آن می‌دهیم و reducer قبلی را جایگزین می‌کند.
- متد dispatch آن مرتبط است به مبحث dispatch actions که در قسمت قبل به آن پرداختیم. هدف آن تغییر state، بر اساس یک action رسیده‌است.
- متد getState، وضعیت فعلی state را باز می‌گرداند.
- متد subscribe با هر تغییری در state، سبب بروز رخ‌دادی می‌شود. یکی از کاربردهای آن می‌تواند به روز رسانی UI، بر اساس تغییرات state باشد. برای مثال کتابخانه‌ی دیگری به نام react redux، دقیقا همین کار را به کمک متد subscribe، انجام می‌دهد. در این قسمت، هدف ما بررسی پشت صحنه‌ی کتابخانه‌هایی مانند react redux است که چه متدهایی را محصور کرده‌اند و دقیقا چگونه کار می‌کنند.

در مثال زیر، مقدار ابتدایی پارامتر state متد reducer را به یک شیء که دارای خاصیت value و مقدار 1 است، تنظیم کرده‌ایم:
import { createStore } from "redux";

const reducer = (state = { value: 1 }, action) => {
  return state;
};
const store = createStore(reducer);
console.log(store.getState());
سپس بر این اساس، store را ایجاد کرده و متد getState آن‌را فراخوانی کرده‌ایم. خروجی آن به صورت زیر است، که معادل وضعیت فعلی state در store می‌باشد:


در کامپوننت‌های React، این نوع خواص به صورت props ارسال می‌شوند. کل state در Redux ذخیره شده و سپس قابل دسترسی و خواندن خواهد شد.


بررسی متد dispatch یک store با مثال

در اینجا مثالی را از کاربرد متد dispatch ملاحظه می‌کنید که یک شیء را می‌پذیرد:
import { compose, createStore } from "redux";

const reducer = (state = { value: 1 }, action) => {
  console.log("Something happened!", action);
  return state;
};
const store = createStore(reducer);
console.log(store.getState());
store.dispatch({ type: "ADD" });
متد reducer با یک state ابتدایی تنظیم شده‌ی به شیء { value: 1 } تعریف شده و سپس با ارسال آن به createStore و ایجاد store، اکنون می‌توانیم رخ‌دادهایی را به آن dispatch کنیم.
فرمت شیءای که به متد dispatch ارسال می‌شود، حتما باید به همراه خاصیت type باشد؛ در غیر اینصورت با صدور استثنائی، این نکته را گوشزد می‌کند. مقدار آن نیز یک رشته‌است که بیانگر وقوع رخدادی در برنامه می‌باشد؛ برای مثال کاربر درخواست دریافت اطلاعاتی را کرده‌است و یا کاربر از سیستم خارج شده‌است و امثال آن.

خروجی قطعه کد فوق، به صورت زیر است:


با اجرای برنامه، یک رخ‌داد درونی از نوع redux/INITo.2.g.b.y.i@@ صادر شده‌است که هدف آن آغاز و مقدار دهی اولیه‌ی store است. پس از آن زمانیکه متد store.dispatch فراخوانی شده‌است، مجددا console.log داخل متد reducer فراخوانی شده‌است که اینبار به همراه type ای است که ما مشخص کرده‌ایم.

در حالت کلی، اینکه شیء ارسالی توسط dispatch را چگونه طراحی می‌کنید، اختیاری است؛ اما الگوی پیشنهادی در این زمینه، redux standard actions نام دارد و به صورت زیر است که هدف از آن، یک‌دست شدن طراحی و پیاده سازی برنامه است:
store.dispatch({ type: "ADD", payload: { amount: 2 }, meta: {} });
- نوع action باید همواره قید شود و عموما رشته‌ای است.
- سپس به خاصیت payload، تمام داده‌های مرتبط با آن اکشن، مانند نتیجه‌ی بازگشتی از یک API، به صورت یک شیء، انتساب داده می‌شود.
- خاصیت meta، مرتبط با متادیتا و اطلاعات اضافی است که عموما استفاده نمی‌شود.

اکنون که طراحی شیء ارسالی به پارامتر action متد reducer مشخص شد، می‌توان بر اساس خاصیت type آن، یک switch را طراحی کرد و نسبت به نوع‌های مختلف رسیده، واکنش نشان داد:
import { createStore } from "redux";

const reducer = (state = { value: 1 }, action) => {
  console.log("Something happened!", action);

  if (action.type === "ADD") {
    const value = state.value;
    const amount = action.payload.amount;
    state.value = value + amount;
  }

  return state;
};
const store = createStore(reducer);
const firstState = store.getState();
console.log(firstState);
store.dispatch({ type: "ADD", payload: { amount: 2 }, meta: {} });
const secondState = store.getState();
console.log(secondState);
console.log("secondState === firstState", secondState === firstState);
در اینجا اکشن از نوع ADD، با یک payload با محتوای شیءای که دارای خاصیت amount با مقدار 2 است، به متد reducer مربوط به store، ارسال می‌شود. سپس در متد reducer، بر اساس مقدار action.type، تصمیم‌گیری خواهد شد. اگر این مقدار مساوی اکشن خاص ADD بود، آنگاه مقدار value شیء state را با مقدار amount جمع زده و به state.value انتساب می‌دهیم. اکنون پس از فراخوانی متد store.dispatch، اگر خروجی متد ()store.getState را بررسی کنیم، به صورت {value: 3} در آمده‌است ... و این کاری است که نباید انجام شود! نباید مقدار شیء state را به صورت مستقیم تغییر داد. چون در آخرین بررسی صورت گرفته که کار مقایسه‌ی بین state پیش و پس از فراخوانی store.dispatch است (secondState === firstState)، حاصل true است. یعنی اگر با این روش با React کار کنیم، نمی‌تواند محاسبه کند که چه چیزی در state تغییر کرده‌است، تا بر اساس آن UI را به روز رسانی کند.

یک روش حل این مشکل، حذف سطر state.value = value + amount و جایگزینی آن با یک return است که شیء state جدیدی را بازگشت می‌دهد:
return {
  value: value + amount
};
اکنون نتیجه‌ی مقایسه‌ی secondState === firstState دیگر true نخواهد بود.


بررسی متد subscribe یک store با مثال

در ادامه به store همان مثال فوق، متد subscribe را نیز اضافه می‌کنیم و سپس دوبار، کار store.dispatch را انجام خواهیم داد:
const store = createStore(reducer);

const unsubscribe = store.subscribe(() => console.log(store.getState()));

store.dispatch({ type: "ADD", payload: { amount: 2 }, meta: {} });
store.dispatch({ type: "ADD", payload: { amount: 2 }, meta: {} });

unsubscribe();
- هر بار که متد store.dispatch فراخوانی می‌شود، یکبار callback function ارسالی به متد store.subscribe، فراخوانی خواهد شد که در اینجا کار نمایش مقدار شیء state را به عهده دارد. در یک چنین حالتی مشترکین به store، متوجه خواهند شد که احتمالا state جدیدی توسط متد reducer بازگشت داده شده‌است و سپس بر اساس آن برای مثال تصمیم خواهند گرفت تا UI را به روز رسانی کنند.
- خروجی مستقیم متد store.subscribe نیز یک متد است که unsubscribe نام دارد و می‌توان در پایان کار، برای جلوگیری از نشتی‌های حافظه، آن‌را فراخوانی کرد.

خروجی حاصل از console.log منتسب به متد store.subscribe، با دوبار فراخوانی متد store.dispatch پس از آن، به صورت زیر است:
{value: 3}
{value: 5}


بررسی تابع combineReducers با یک مثال

اگر قرار باشد در متد reducer، صدها if action.type را تعریف کرد ... پس از مدتی از کنترل خارج می‌شود و غیرقابل نگهداری خواهد شد. تابع combineReducers به همین جهت طراحی شده‌است تا چندین متد مجزای reducer را با هم ترکیب کند. بنابراین می‌توان در برنامه صدها متد کوچک reducer را که قابلیت توسعه و نگهداری بالایی را دارند، پیاده سازی کرد و سپس با استفاده از تابع combineReducers، آن‌ها را یکی کرده و به متد createStore ارسال کرد. نکته‌ی مهم اینجا است که هرچند اینبار می‌توان تعداد زیادی reducer را طراحی کرد، اما توسط تابع combineReducers، مقدار action ارسالی، از تمام این reducerها عبور داده خواهد شد. به این ترتیب اگر نیاز بود می‌توان به یک action، در چندین متد مختلف reducer گوش فرا داد و بر اساس آن تصمیم گیری کرد. بنابراین بهتر است هر reducer تعریف شده در صورتیکه action.type آن با action رسیده تطابق نداشته باشد، همان state قبلی را بازگشت دهد تا بتوان زنجیره‌ی reducerها را تشکیل داد و بهتر مدیریت کرد.
برای نمونه اگر متد reducer فعلی را به calculatorReducer تغییر نام دهیم، روش معرفی آن توسط متد combineReducers به متد createStore به صورت زیر است:
import { combineReducers, createStore } from "redux";
  // ...

const calculatorReducer = (state = { value: 1 }, action) => {
  // ...
};
const store = createStore(
  combineReducers({
    calculator: calculatorReducer
  })
);
به شیء ارسالی به متد combineReducers، هر reducer موجود به صورت یک خاصیت جدید اضافه می‌شود.


بررسی تابع bindActionCreators با یک مثال

فرض کنید می‌خواهیم متد dispatch را چندین بار فراخوانی کنیم:
store.dispatch({ type: "ADD", payload: { amount: 2 }, meta: {} });
store.dispatch({ type: "ADD", payload: { amount: 2 }, meta: {} });
بجای اینکه شیء ارسالی به آن‌را به این صورت ایجاد کنیم، می‌توان یک تابع را برای آن ایجاد کرد تا مقدار amount را گرفته و شیء action مدنظر را بازگشت دهد:
const createAddAction = amount => {
  return {
    type: "ADD",
    payload: {
      amount // = amount: amount
    },
    meta: {}
  };
};
و سپس می‌توان آن‌را به صورت زیر مورد استفاده قرار داد:
store.dispatch(createAddAction(2));
store.dispatch(createAddAction(2));
و یا توسط تابع bindActionCreators، می‌توان فراخوانی فوق را به صورت زیر نیز انجام داد و نتیجه یکی است:
import { bindActionCreators, combineReducers, compose, createStore } from "redux";

// ...
const dispatchAdd = bindActionCreators(createAddAction, store.dispatch);
dispatchAdd(2);
dispatchAdd(2);
همانطور که ملاحظه می‌کنید، bindActionCreators فقط یک تابع کمکی است تا کار dispatch action را ساده کند.


میان‌افزارها (Middlewares) در Redux

پس از اینکه یک اکشن، به سمت reducer ارسال شد و پیش از رسیدن آن به reducer، می‌توان کدهای دیگری را نیز اجرا کرد. برای مثال چون این توابع خالص هستند، نمی‌توان اعمالی را داخل آن‌ها اجرا کرد که به همراه اثرات جانبی مانند کار با یک API خارجی باشند. با استفاده از میان‌افزارها در این بین می‌توان کدهایی را که با دنیای خارج تعامل دارند نیز اجرا کرد.
یک میان‌افزار در Redux، همانند سایر قسمت‌های آن، تنها یک تابع ساده‌ی جاوا اسکریپتی است:
const logger = ({ getState }) => {
  return next => action => {
    console.log(
      'MIDDLEWARE',
      getState(),
      action
    );
    const value = next(action);
    console.log({value});
    return value;
  }
}
این تابع میان‌افزار، امکان دریافت وضعیت فعلی store را دارد و در نهایت یک تابع دیگر را باز می‌گرداند. داخل این تابع بازگشت داده شده می‌توان اعمال دارای اثرات جانبی را نیز اجرا کرد؛ مانند فراخوانی console.log و یا صدور یک درخواست Ajax ای. یکی دیگر از کاربردهای میان‌افزارها، انجام کارهای آماری بر روی اکشن‌های ارسالی است. بجای اینکه کدهای متناظر را داخل  reducerها قرار داد، می‌توان به رسیدن اکشن‌های خاصی در این بین گوش فرا داد و برای مثال آن‌ها را لاگ کرد و یا آماری را از آن‌ها تهیه نمود.
در پایان کار میان‌افزار باید متد next آن‌را فراخوانی کرد تا بتوان میان‌افزارهای متعددی را زنجیروار اجرا کرد.
در آخر برای معرفی آن به یک store می‌توان از تابع applyMiddleware استفاده کرد:
import { applyMiddleware, bindActionCreators, combineReducers, compose, createStore } from "redux";

// ...

const store = createStore(
  combineReducers({
    calculator: calculatorReducer
  }),
  applyMiddleware(logger)
);

کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: state-management-redux-mobx-part02.zip
مطالب
سیستم‌های توزیع شده در NET. - بخش چهارم - تعاریف، مزایا و معایب
بدلیل اینکه یکی از مهمترین معایب سیستم‌های توزیع شده، پیچیدگی در طراحی و پیاده سازی این نوع از سیستمها می‌باشد و آشنا بودن ما با تعاریف، خصوصیات، مزایا، معایب، اهداف و اصطلاحات موجود در این نوع سیستمها، باعث کاهش این پیچیدگیها و مدیریت و کنترل بیشتری بر روی این پیچیدگی‌ها می‌شود، پیش نیاز ورود به دنیای سیستم‌های توزیع شده و استفاده از ابزارهای مرتبط با آنها، آشنا بودن با مفاهیم فوق است.

 در این بخش تعاریف، خصوصیات، مزایا و معایب مرتبط با این نوع سیستم‌ها بررسی می‌شوند.


تعریف سیستمهای توزیع شده

تعاریف مختلفی توسط اشخاص و گروه‌های مختلف، از سیستم‌های توزیع شده وجود دارد. در اینجا سعی شده که اکثر این تعاریف معرفی شوند؛ بگونه‌ای که با ارائه نکات کلیدی این تعاریف به درک بهتری از خصوصیات سیستم‌های توزیع شده برسیم.


تعاریف سیستم‌های توزیع شده

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

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


نکات کلیدی یا خصوصیات سیستمهای توزیع شده

1- داشتن یک هدف واحد در سیستم‌های توزیع شده، زیر سیستمهایی که از نظر کاربر یک سیستم واحد و متمرکزند:  این یکی از خصوصیات و نکات کلیدی سیستم‌های توزیع شده‌است که تمام اجزاء و زیرسیستم‌ها در راه رسیدن به یک هدف واحد با یکدیگر در ارتباط اند. ارتباطی که باعث همکاری آنها می‌شود و همکاری که باید بصورت کامل از دید کاربر مخفی بماند (داشتن یک هدف واحد یکی از تفاوتهای سیستم‌های توزیع شده با Cloud distributed systemها می‌باشد. چون در Cloud distributed systemها لزوما تمام اجزا برای رسیدن به یک هدف مشترک با یکدیگر کار نمی‌کنند).

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

3- تقسیم بندی وظایف: که یکی از نکات کلیدی این نوع سیستمها می‌باشد. تقسیم بندی می‌تواند به هر دلیلی که شما درنظر می‌گیرید صورت بپذیرد. دلایلی برای بالا بردن کارآیی، امنیت، در دسترس بودن، یا حتی دلایل مربوط به Business سیستم شما.

4- ارتباط از طریق شبکه، ارتباطی که از طریق Message passing صورت می‌پذیرد: این خصوصیت در واقع پایه تمام تعاریف سیستم‌های توزیع شده‌است. در سیستم‌های توزیع شده همه چیز از ارتباط‌هایی که از طریق شبکه صورت می‌پذیرد، شروع می‌شود .


مزایای استفاده از سیستم‌های توزیع شده

1- کارآیی بسیار بالاتر: بدلیل همزمان اجرا شدن کارها در سخت افزارهای مختلف، کارآیی این نوع سیستم‌ها بسیار بیشتر از سیستم‌های متمرکز است.
2- قابلیت همکاری بیشتر: بدلیل اینکه این سیستمها ذاتا توزیع شده هستند، با کمترین هزینه و پیچیدگی می‌توانند با سایر سیستمها همکاری لازم را داشته باشند.
3- قابلیت در دسترس بودن و اطمینان بیشتر: در این سیستم‌ها با روشهای مختلفی مانند replication، به راحتی می‌توان این دو قابلیت را در بالاترین سطح قرار داد.
4- مقیاس پذیری: مقیاس پذیری در این سیستمها با قرار دادن کامپوننت‌هایی که قابلیت استفاده مجدد بالایی را دارند در سرور‌های جدید به راحتی و بدون از دسترس خارج شدن سیستم صورت می‌پذیرد.
5- قابلیت گسترش: برای گسترش سیستم و اضافه کردن نیازمندی‌های جدید در این سیستم‌ها، به راحتی می‌توان کامپوننت‌ها و زیرسیستمهای جدیدی را پیاده سازی کرد و بدون از دسترس خارج شدن سیستم و به گونه‌ای که به راحتی با سایر قسمت‌های موجود در ارتباط باشند، آنها را به سیستم اضافه کرد.
6- بهره وری بالاتر و زمان توسعه کمتر: بدلیل تقسیم بندی قسمتهای بزرگ به قسمتهای کوچکتر، تیمهای مختلف می‌توانند بصورت همزمان قسمتهای کوچک را توسعه دهند.
7- قابلیت استفاده مجدد بسیار بالا: در این نوع سیستم‌ها به راحتی می‌توانید از یک زیرسیستم یا کامپوننت خاص که یکبار پیاده سازی شده و در سخت افزار جداگانه‌ای اجرا شده، در تمام Application‌ها استفاده کنید.
8- کاهش هزینه: در مواردی مانند قابلیت استفاده‌ی مجدد بالا و توسعه پذیری سیستم، می‌توانند باعث کاهش هزینه‌ها شوند (در صورت انتخاب نادرست این نوع سیستم، این مزیت می‌تواند تبدیل به یکی از معایب سیستم شود).
9- امنیت: بدلیل اینکه هر زیرسیستم در یک سخت افزار جداگانه اجرا می‌شود که مکان آن از قبل مشخص نیست و همچنین تقسیم بندی قسمت‌هایی که نیاز به امنیت بالایی دارند، می‌تواند بر اساس نیاز و در سخت افزارهایی که حتی به اینترنت هم متصل نیستند، صورت بپذیرد. این نوع سیستم می‌تواند از امنیت بالایی برخوردار باشد (البته در صورت طراحی نادرست، امنیت می‌تواند بعنوان یکی از معایب این نوع سیستمها نیز مطرح شود).


معایب  استفاده از سیستم‌های توزیع شده

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

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

3- هزینه طراحی و پیاده سازی بیشتر: دلایل 1 و 2 می‌توانند باعث بالا رفتن هزینه‌های طراحی و پیاده سازی این نوع سیستم‌ها شوند.

4- هزینه‌های بیشتر مرتبط با شبکه: در این نوع سیستم‌ها بدلیل استفاده بیشتر از منابع، مانند سخت افزار‌ها و ابزارهای مرتبط با شبکه، هزینه‌های مرتبط با استفاده از منابع، مانند برق و شبکه بیشتر از سیستم‌های متمرکز است.

5- کاهش امنیت: توزیع نادرست منابع سیستم در سخت افزارهای متفاوت و مدیریت نادرست این منابع باعث کاهش امنیت این نوع سیستم‌ها می‌شود.

6- مدیریت دشوارتر: بدلیل اینکه سیستمهای توزیع شده از زیرسیستم‌های زیادی تشکیل شده و هر یک از این زیر سیستمها در سخت افزارهای متفاوتی اجرا شده‌اند، مدیریت و سازماندهی این نوع سیستم‌ها دشوار‌تر از سیستم‌های متمرکز است.

بدلیل اینکه همیشه قسمتهای مرتبط با پیاده سازی این نوع سیستم‌ها از تعاریف آنها جذابتر است، سعی کرده‌ام در این بخش و بخش بعد که اهداف و اصطلاحات مرتبط با سیستمهای توزیع شده را بررسی میکند، کمتر وارد جزئیات مفاهیم شوم. در صورت نیاز به توضیح بیشتر در مورد قسمت خاصی از این مفاهیم، در قسمت نظرات آن‌ها را مطرح کنید.
بازخوردهای دوره
کار با RavenDB از طریق REST API آن

با تشکر فراوان از شما

برای کار با حجم زیاده داده آیا حالت نصب embedded را پیشنهاد میکنید؟

بین RavenDB و MongoDB در پلت فورم Asp.net MVC 4 کدامیک را پیشنهاد میکنید؟

اشتراک‌ها
DbContext مخصوص MongoDb (مشابه Entity Framework)

اگه قبلا با EF/EFCore کار میکردین و الان میخواین از MongoDb استفاده کنین و میخواین از دانش قبلیتون + مزایای Entity Framework استفاده کنین بهترین گزینه  MongoFramework هست

قبلا یه پروایدر مخصوص MongoDb برای EFCore ساخته شد که متاسفانه زیاد توسعه داده نشد (مخزن گیتهابش) و حتی به انتشار نسخه Stable هم نرسید

از بین کتابخونه هایی که تجربه مشابه Entity Framework رو برای MongoDb فراهم میکنن یکی MongoFramework هست و دیگری MongoDbContext (که البته به پای اون یکی نمیرسه)

یه MongoDbGenericRepository هم هست که صرفا یه Wrapper رو MongoDb هست و سعی کرده یه پیاده سازی از Repository ارائه بده ولی امکانات EF و DbContext طوری نداره

خلاصه اینکه من همه معروفاشونو بررسی کردم و بهترینشون همین MongoFramework هست که البته همه امکانات EF رو نداره ولی بازم امکانات خوبی داره که توی گیتهابش توضیح داده و پیشنهاد میکنم یه نگاه به صفحه گیتهابش بندازین 

DbContext مخصوص MongoDb (مشابه Entity Framework)
اشتراک‌ها
بررسی معادل‌های SQL در MongoDB

The following table presents the various SQL statements related to inserting records into tables and the corresponding MongoDB statements. 

بررسی معادل‌های SQL در MongoDB