اشتراک‌ها
ثبت نام در Modern Workplace
Learn what makes for a highly productive work environment from the Founder and Principal Researcher of Programmable Habitats, Jennifer Magnolfi Astill. And hear from David Fullerton, an expert on setting up highly successful remote work programs and the author of “Seven Great Reasons to Encourage Working Remotely.” Tune in to help you determine the best mobile work environment solution for your company. 
ثبت نام در Modern Workplace
مطالب
پیاده سازی CQRS توسط MediatR - قسمت چهارم
در این قسمت قصد داریم به بررسی Behavior‌ ها در فریمورک MediatR بپردازیم. کدهای این قسمت به‌روزرسانی و از این ریپازیتوری قابل دسترسی است.

با استفاده از Behavior‌ها امکان پیاده سازی AOP را براحتی خواهید داشت. Behavior‌ها، مانند Filter‌‌ ها در ASP.NET MVC هستند. همانطور که با استفاده از متدهای OnActionExecuting و OnActionExecuted میتوانستیم اعمالی را قبل و بعد از اجرای یک اکشن‌متد انجام دهیم، چنین قابلیتی را با Behavior‌ها در MediatR نیز خواهیم داشت. مزیت اینکار این است که شما میتوانید کدهای Cross-Cutting-Concern خود را یکبار نوشته و چندین بار بدون تکرار مجدد، از آن استفاده کنید.


Performance Counter Behavior

فرض کنید میخواهید زمان انجام کار یک متد را اندازه گیری کرده و در صورت طولانی بودن زمان انجام آن، لاگی را مبنی بر کند بودن بیش از حد مجاز این متد، ثبت کنید. شاید اولین راهی که برای انجام اینکار به ذهنتان بیاید این باشد که داخل تمام متدهایی که میخواهیم زمان انجام آنها را  محاسبه کنیم، چنین کدی را تکرار کنیم:
public class SomeClass
{
    private readonly ILogger _logger;

    public SomeClass(ILogger logger)
    {
        _logger = logger;
    }

    public void SomeMethod()
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        // TODO: Do some work here

        stopwatch.Stop();

        if (stopwatch.ElapsedMilliseconds > TimeSpan.FromSeconds(5).Milliseconds)
        {
            // This method has taken a long time, So we log that to check it later.
            _logger.LogWarning($"SomeClass.SomeMethod has taken {stopwatch.ElapsedMilliseconds} to run completely !");
        }
    }
}
در این صورت تمام متدهایی که نیاز به محاسبه زمان پردازش را دارند، باید به کلاسشان Logger تزریق شود. Stopwatch باید ایجاد، Start و Stop شود و در نهایت، بررسی کنیم که آیا زمان انجام این متد از حداکثری که برای آن مشخص کرده‌ایم گذشته است یا خیر.

علاوه بر این تصور کنید روزی تصمیم بگیرید که حداکثر زمان برای Log کردن را از 5 ثانیه به 10 ثانیه تغییر دهید. در این صورت بدلیل اینکه در همه متدها این قطعه کد تکرار شده‌است، مجبور به تغییر تمام کدهای برنامه برای اصلاح این بخش خواهید شد. در اینجا اصل DRY نقض شده‌است.

 

برای حل این مشکل از Behavior‌ها استفاده میکنیم. برای پیاده سازی Behavior‌ها داخل MediatR، کافیست از interface ای بنام IPipelineBehavior ارث بری کنیم:
public class RequestPerformanceBehavior<TRequest, TResponse> :
    IPipelineBehavior<TRequest, TResponse>
{
    private readonly ILogger<RequestPerformanceBehavior<TRequest, TResponse>> _logger;

    public RequestPerformanceBehavior(ILogger<RequestPerformanceBehavior<TRequest, TResponse>> logger)
    {
        _logger = logger;
    }

    public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        TResponse response = await next();

        stopwatch.Stop();

        if (stopwatch.ElapsedMilliseconds > TimeSpan.FromSeconds(5).Milliseconds)
        {
            // This method has taken a long time, So we log that to check it later.
            _logger.LogWarning($"{request} has taken {stopwatch.ElapsedMilliseconds} to run completely !");
        }

        return response;
    }
}
همانطور که میبینید منطق کد ما تغییری نکرده‌است. از IPipelineBehavior ارث بری کرده و متد Handle آن را پیاده سازی کرده‌ایم. همانند Middleware‌ ها در ASP.NET Core، در اینجا نیز یک RequestHandlerDelegate بنام next داریم که با اجرا و return آن، روند اجرای بقیه Command/Query‌ها ادامه پیدا خواهد کرد.

سپس باید Behavior‌های خود را از طریق DI به MediatR معرفی کنیم. داخل Startup.cs به این صورت RequestPerformanceBehavior خود را Register میکنیم:
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(RequestPerformanceBehavior<,>));

در نهایت برای تست کارکرد این Behavior، در کوئری GetCustomerByIdQueryHandler خود 5 ثانیه Delay ایجاد میکنیم تا طول اجرای آن، از Maximum زمان مشخص شده بیشتر و Log انجام شود:
public class GetCustomerByIdQueryHandler : IRequestHandler<GetCustomerByIdQuery, CustomerDto>
{
    private readonly ApplicationDbContext _context;
    private readonly IMapper _mapper;

    public GetCustomerByIdQueryHandler(ApplicationDbContext context, IMapper mapper)
    {
        _context = context;
        _mapper = mapper;
    }

    public async Task<CustomerDto> Handle(GetCustomerByIdQuery request, CancellationToken cancellationToken)
    {
        Customer customer = await _context.Customers
            .FindAsync(request.CustomerId);

        if (customer == null)
        {
            throw new RestException(HttpStatusCode.NotFound, "Customer with given ID is not found.");
        }

        // For testing PerformanceBehavior
        await Task.Delay(5000, cancellationToken);

        return _mapper.Map<CustomerDto>(customer);
    }
}

پس از اجرای برنامه و فراخوانی GetCustomerById ، داخل Console این پیغام را خواهید دید:



Transaction Behavior


یکی دیگر از استفاده‌های Behavior‌ها میتواند پیاده سازی Transaction و Rollback باشد. فرض کنید میخواهیم افزودن یک مشتری به دیتابیس فقط زمانی صورت گیرد که تمام کارهای داخل Command با موفقیت و بدون رخ دادن Exception انجام شود. برای انجام اینکار میتوان یک TransactionBehavior نوشت تا بدنه Command‌ها را داخل یک TransactionScope قرار دهد و در صورت وقوع Exception ، عمل Rollback صورت گیرد :
public class TransactionBehavior<TRequest, TResponse> :
    IPipelineBehavior<TRequest, TResponse>
{
    public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    {
        var transactionOptions = new TransactionOptions
        {
            IsolationLevel = IsolationLevel.ReadCommitted,
            Timeout = TransactionManager.MaximumTimeout
        };

        using (var transaction = new TransactionScope(TransactionScopeOption.Required, transactionOptions,
            TransactionScopeAsyncFlowOption.Enabled))
        {
            TResponse response = await next();

            transaction.Complete();

            return response;
        }
    }
}

سپس این Behavior را داخل DI Container خود Register میکنیم :
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(TransactionBehavior<,>));

در نهایت متد Handle در CreateCustomerCommandHandler را که در قسمت‌های قبل ایجاد کردیم، تغییر داده و بعد از SaveChanges مربوط به Entity Framework، یک Exception را صادر میکنیم:
public class CreateCustomerCommandHandler : IRequestHandler<CreateCustomerCommand, CustomerDto>
{
    readonly ApplicationDbContext _context;
    readonly IMapper _mapper;
    readonly IMediator _mediator;

    public CreateCustomerCommandHandler(ApplicationDbContext context,
        IMapper mapper,
        IMediator mediator)
    {
        _context = context;
        _mapper = mapper;
        _mediator = mediator;
    }

    public async Task<CustomerDto> Handle(CreateCustomerCommand createCustomerCommand, CancellationToken cancellationToken)
    {
        Domain.Customer customer = _mapper.Map<Domain.Customer>(createCustomerCommand);

        await _context.Customers.AddAsync(customer, cancellationToken);
        await _context.SaveChangesAsync(cancellationToken);

        throw new Exception("======= MY CUSTOM EXCEPTION =======");

        // Raising Event ...
        await _mediator.Publish(new CustomerCreatedEvent(customer.FirstName, customer.LastName, customer.RegistrationDate), cancellationToken);

        return _mapper.Map<CustomerDto>(customer);
    }
}

اگر برنامه را اجرا کنید خواهید دید با اینکه Exception ما بعد از SaveChanges رخ داده است، اما بدلیل استفاده از Transaction Behavior ای که نوشتیم، عملیات Rollback صورت گرفته و داخل دیتابیس رکوردی ثبت نشده‌است.

* نکته : قبل از استفاده از Transaction‌ها حتما این مطالب ( 1 , 2 ) را مطالعه کنید.

MediatR دارای 2 اینترفیس IRequestPreProcessor و IRequestPostProcessor نیز هست که اگر نیاز داشته باشید یک عمل فقط قبل یا بعد از انجام یک Command/Query صورت گیرد، میتوانید از آنها استفاده کنید.

همچنین پیاده سازی‌های پیشفرضی از این 2 اینترفیس با نام‌های RequestPreProcessorBehavior و RequestPostProcessorBehavior داخل فریمورک، بطور پیشفرض وجود دارد که قبل و بعد از تمامی Handler‌ها اجرا خواهند شد.
اشتراک‌ها
رهانش ASP.NET Core updates in .NET 8 Preview 4

.NET 8 Preview 4 is now available and includes many great new improvements to ASP.NET Core.


Here’s a summary of what’s new in this preview release:


Blazor

Streaming rendering with Blazor components

Handling form posts with Blazor SSR

Route to named elements in Blazor

Webcil packaging for Blazor WebAssembly apps

API authoring

Expanded support for form binding in minimal APIs

API project template includes .http file

Native AOT

Logging and exception handling in compile-time generated minimal APIs

ASP.NET Core top-level APIs annotated for trim warnings

Reduced app size with configurable HTTPS support

Worker Service template updates

Additional default services configured in the slim builder

API template JSON configuration changes

Support for JSON serialization of compiler-generated IAsyncEnumerable unspeakable types

Authentication and authorization

Identity API endpoints

Improved support for custom authorization policies with IAuthorizationRequirementData

ASP.NET Core metrics

For more details on the ASP.NET Core work planned for .NET 8 see the full ASP.NET Core roadmap for .NET 8 on GitHub. 

رهانش ASP.NET Core updates in .NET 8 Preview 4
اشتراک‌ها
ReSharper Ultimate 2019.2 منتشر شد
  • Smart support for C# 8.0 language features such as indices, ranges, and unmanaged generic structs, as well as many new quick-fixes, inspections, and context actions. 
ReSharper Ultimate 2019.2 منتشر شد
اشتراک‌ها
ویژگیهای برنامه ریزی شده C# 8.0

In this C# 8 tutorial, I am going to introduce a selection of currently planned features which will most probably end up in the final release. All of the C# 8 features are still early in development and are likely to change. 

ویژگیهای برنامه ریزی شده C# 8.0
مطالب
طراحی شیء گرا: OO Design Heuristics - قسمت پنجم

(The God Class Problem (Behavioral Form 

یکی از مخاطراتی که ممکن است موجب عدم بروز مزایای شیء گرایی در طرح شما شود، بحث God Class می‌باشد. شکل رفتاری آن (Behavioral Form) بیشتر در اثر یک خطای مشترک بین توسعه دهندگان پارادایم action-oriented و در جریان مهاجرت به سمت پارادایم شیء گرا، رخ می‌دهد.

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

قاعده شهودی 3.1

تا حد ممکن هوشمندی سیستم را به صورت افقی و به طور یکنواخت توزیع کنید. به این معنی که کلاس‌های سطح بالای موجود در طراحی، باید کار را به طور یکسان مابین خود به اشتراک بگذارند. (Distribute system intelligence horizontally as uniformly as possible, that is, the top-level classes in a design should share the work uniformly)
منظور اینکه Businessای را که سیستم قرار است پیاده سازی کند، بین کلاس‌های سطح بالا تقسیم کنید. در حالت vertical یا عمودی می‌توان در نظر گرفت که کلاسی این Business را توسط یکسری متد در دل خود پیاده سازی کند و این متدها یکدیگر را فراخوانی خواهند کرد. 
قاعده شهودی 3.2
در سیستم خود God Class ایجاد نکنید. به کلاس هایی که نام آنها شامل Driver، Manager و یا Subsystem می‌باشد، مشکوک باشید. (Do not create god classes/objects in your system. Be very suspicious of a class whose name contains Driver, Manager, System, or Subsystem)

مانند: BlahBlahSystem یا BlahManager

قاعده شهودی 3.3
مراقب کلاس هایی باشید که در واسط عمومی آنها تعداد زیادی Accessor Method تعریف شده است؛ وجود آنها نشان از این دارد که داده و رفتار، در یکجا نگه داشته نشده اند. (Beware of classes that have many accessor methods defined in their public interface. Having many implies that related data and behavior are not being kept in one place)
ازدیاد عملیات get و set در واسط عمومی کلاس‌ها که Accessor Method نامیده می‌شوند، نشان دهنده ایجاد شکل رفتاری God Class می‌باشند. منظور این است که یک کلاس، رفتارهایی برای کار کردن با داده‌های خود در نظر نگرفته است و این داده‌ها را از طریق accessor method‌ها در اختیار کلاس دیگری قرار می‌دهد تا عملیات روی داده‌ها را انجام دهد. در اینجا هم مقصود God Class شدن کلاسی است که از این accessor method‌ها استفاده می‌کند و نشان از این دارد که تعداد رفتارهای آن زیاد خواهد شد. 
قاعده شهودی 3.4
مراقب کلاس هایی باشید که تعداد خیلی زیادی رفتار نامرتبط دارند؛ یعنی رفتارهایی که فقط برروی زیر مجموعه ای از داده‌های کلاس کار می‌کنند. God Class‌ها اغلب دارای اینگونه رفتارهای نامرتبط به هم هستند. (Beware of classes that have too much noncommunicating behavior, that is, methods that operate on a proper subset of the data members of a class. God classes often exhibit much noncommunicating behavior)
منظور اینکه کلاس مورد نظر را می‌توان شکست و تبدیل به دو کلاس مختلف کرد. به عنوان اولین مثال، دامنه مربوط به سیستم برنامه ریزی دوره‌های آموزشی را در نظر بگیرید. در این دامنه، ما با وهله هایی از «Student» ،«Course» و «CourseOffering» سروکار خواهیم داشت. 
قصد داریم با فراخوانی متد ()add_student مربوط به CourseOffering، یک دانشجو را به لیست شرکت کنندگان یک دوره اضافه کنیم. همچنین در این زمان لازم است مطمئن شویم که دانشجوی مورد نظر تمام پیش نیاز‌های دوره انتخاب شده را گذرانده باشد. به نظر شما کدام کلاس می‌بایست عملیات چک کردن پیش نیازها را انجام دهد؟
کلاس دانشجو از دوره‌هایی که گذرانده است آگاه است و کلاس دوره هم از پیش نیاز‌های خود. در بهترین حالت شاید یکی از طراحی‌های زیر را ارائه دهید. به شکلی که یا کلاس دوره با استفاده از متد get_courses مربوط به کلاس دانشجو، داده مورد نیاز را بدست آورده و عملیات چک کردن را در دل خود بگنجاند یا برعکس.

در هر دو طراحی بالا، بخشی از اطلاعات مربوط به policy (سیاست) در کلاس هایی قرار دارد که موضوع تصمیمات سیاست‌ها هستند. این کار از آن جهت که کلاس‌های مورد نظر ما را به دامنه خاصی که این policy در آن دامنه معنا دارد وابسته می‌کند و امکان استفاده مجدد از آن کلاس‌ها را از دست خواهید داد.

راه حل‌های پیشنهادی برای مشکل مطرح شده به شکل زیر می‌باشند:

با توجه به طراحی شکل بالا، یا خود کلاس CourseOffering با استفاده از لیست دوره‌های گذرانده شده توسط دانشجو و لیست دوره‌های پیش نیاز دوره انتخابی، چک کردن را انجام دهد و یا کلاسی با عنوان PrereqChecker که یک Controller Class (کلاسی که فقط رفتار دارد و داده مورد نظر خود را توسط سایر کلاس‌ها و از طریق accessor methodهای آنها تأمین می‌کند) می‌باشد، وظیفه چک کردن را برعهده بگیرد.


علاوه بر اینکهaccessor method ها، داده را برای کلاس‌های کنترلر مهیا می‌کنند (مانند مثال بالا)، وظیفه‌ی مهیا کردن داده برای UI (رابط کاربری) را نیاز بر عهده خواهند داشت. به این صورت که رابط کاربری، با استفاده از این متدها، مشخصات درونی مدل را نمایش دهد و یا این امکان را به کاربر می‌دهد که این مشخصات درونی مدل را ویرایش کرده و به سمت مدل ارسال نماید.

قاعده شهودی 3.5

در برنامه‌هایی که شامل یک مدل شی گرایی می‌باشند که با رابط کاربری تعامل دارند، مدل نباید به رابط کاربری وابسته باشد. رابط کاربری می‌بایست وابسته به مدل باشد. (In applications that consist of an object-oriented model interacting with a user interface, the model should never be dependent on the interface. The interface should be dependent on the model)

برای عدم نقض این قاعده شهودی، لازم است در مدل یکسری accessor method در نظر گرفته شود تا رابط کاربری از آن استفاده کند؛ ولی باید مراقب بود که این accessor method‌ها صرفا توسط رابط کاربری استفاده شود و عدم توجه به این قضیه، احتمالا شما را به سمت نقض قاعده 3.3 متمایل خواهد کرد.

اشتراک‌ها
NET 8 Preview 2. منتشر شد
Microsoft's second preview of .NET 8 is just "a quick follow-up" to Preview 1, highlighted by a couple of improvements to Blazor, for creating web apps in C# instead of JavaScript.
NET 8 Preview 2. منتشر شد
اشتراک‌ها
معرفی 3 ویژگی جدید در C# 8.0

C# is rapidly approaching its third decade of life. Originally released in 2000, the language has grown and changed across 7 major versions. Once a knock off of Java in all but name has jumped out ahead on many aspects. 

معرفی 3 ویژگی جدید در C# 8.0