مطالب
ایده‌ی ثبت خودکار سرویس‌ها، به همراه تنظیمات؛ بدون نوشتن هیچ کدی در ConfigureServices با روش Installer
خودکارسازی، در قسمت‌های مختلف یک پروژه می‌تواند انجام شود. نمونه‌های مختلف این خودکارسازی‌ها که اکثرا توسط رفلکشن انجام می‌شوند شامل نگاشت خودکار Dto به Entity و بالعکس (توسط AutoMapper)، ثبت خودکار تمام Entityها در DbContext بدون نیاز به ثبت تک تک آن‌ها به صورت  public DbSet<Person> People { get; set; }  (که در این روش خودکار، اسم جداول می‌تواند به صورت جمع ثبت شود)، ثبت خودکار EntityTypeConfigurationها، ثبت خودکار کلیه‌ی کلاس‌های Profile برای کانفیگ AutoMapper و رجیستر خودکار DI سرویس‌ها، تا نیازی به نوشتن کدهای تکراری و مشابه   ;()<services.AddTransient<IUserService, UserService نداشته باشیم. 
 برای مشاهده‌ی عملی پیاده‌سازی این نمونه‌ها می‌توانید به پروژه‌ی ASP.NET Core WebAPI مراجعه کنید. در این مقاله می‌خواهیم همین سناریو را برای ثبت سرویس‌هایمان در متد ConfigureServices انجام دهیم، تا نیازی به نوشتن هیچ کدی برای آن‌ها نداشته باشیم. 

ثبت سرویس‌های مختلف، به همراه تنظیمات آن‌ها (مانند Authentication، Swagger، DbContext، ApiVersioning و ...) در استارتاپ می‌تواند به چندین صورت انجام شود.
روش اول اینکه به صورت دستی تمام کدهای مربوط به رجیستر کردن سرویس‌ها و تنظیمات آن‌ها، در متد ConfigureServices نوشته شود که خیلی جالب نیست و موجب شلوغ شدن سریع این متد می‌شود. نمونه‌ی این شیوه را برای ثبت سرویس مربوط به DbContext می‌بینیم:
public void ConfigureServices(IServiceCollection services) {
      // DbContext Service
      services.AddDbContext<AppDbContext>(options =>
            {
                options
             .UseSqlServer(appSettings.ConnectionStrings.MyConnectionString, sqlServerOptionsBuilder =>
                {     sqlServerOptionsBuilder.CommandTimeout((int)TimeSpan.FromMinutes(1).TotalSeconds); //Default is 30 seconds
                    sqlServerOptionsBuilder.EnableRetryOnFailure();
                    sqlServerOptionsBuilder.MigrationsAssembly(typeof(AppDbContext).Assembly.FullName);
                })
                    //Tips
                    .ConfigureWarnings(warning => warning.Throw(RelationalEventId
                        .QueryPossibleExceptionWithAggregateOperatorWarning));

                // Activate EF Second Level Cache
                options.AddInterceptors(new SecondLevelCacheInterceptor());
            });

      // register other services ....

}


راه دوم روش استفاده از متدهای الحاقی است؛ طوریکه برای هر سرویس، یک متد الحاقی را تعریف کنیم و از آن، در این متد استفاده کنیم که حجم کدها را تا حد زیادی کم می‌کند. برای مثال ثبت سرویس بالا را می‌توانیم در کلاس دیگری با نام DbContextServiceCollectionExtensions.cs ثبت کنیم:
public static class DbContextServiceCollectionExtensions
    {
        public static void AddDbContext(this IServiceCollection services)
        {
              services.AddDbContext<AppDbContext>(options =>
            {
                options
                    .UseSqlServer(appSettings.ConnectionStrings.MyConnectionString, sqlServerOptionsBuilder =>
                {
                    sqlServerOptionsBuilder.CommandTimeout((int)TimeSpan.FromMinutes(1).TotalSeconds); //Default is 30 seconds
                    sqlServerOptionsBuilder.EnableRetryOnFailure();
                    sqlServerOptionsBuilder.MigrationsAssembly(typeof(AppDbContext).Assembly.FullName);
                })
                    //Tips
                    .ConfigureWarnings(warning => warning.Throw(RelationalEventId
                        .QueryPossibleExceptionWithAggregateOperatorWarning));

                // Activate EF Second Level Cache
                options.AddInterceptors(new SecondLevelCacheInterceptor());
            });
        }
    }
و سپس در متد ConfigureServices می‌توان آن را به صورت زیر استفاده کرد:
public void ConfigureServices(IServiceCollection services) {
// Add DbContext
 services.AddDbContext();

//.... Register other services
}

ولی اگه پروژه‌ی ما متوسط به بالا باشد، کم‌کم تعداد سرویس‌های ما زیاد می‌شود (برای مثال چند نمونه از سرویس‌های رایج مورد استفاده، شامل سرویس‌های لاگ خطاها مثل Elmah و سرویس HttpClientFactory و AutoRegisterDi (توضیح در ادامه مقاله) و AutoMapper و Cache و EFSecondLevelCache و Hangfire و ....) می‌بینیم که تعداد این سرویس‌ها هم زیاد است و حتی به صورت اکستنشن هم به مرور زمان باعث شلوغ شدن استارتاپ می‌شوند. ضمن اینکه یک کار تکراری است که باید هر بار انجام شود.

راه سوم ثبت سرویس، استفاده از یک اینترفیس به نام IServiceInstaller و استفاده از آن در کلاس‌های مختلف مربوط به ثبت سرویس و بعد خواندن خودکار این تنظیمات با یک خط کد ساده‌ی رفلکشن است که در ادامه می‌بینیم: 
اینترفیس IServiceInstaller را تعریف می‌کنیم: 
public interface IServiceInstaller
    {
        void InstallServices(IServiceCollection services, AppSettings appSettings, Assembly startupProjectAssembly);
    }
توضیح: پارامتر appSettings کلاسی شامل کلیه‌ی مقادیر فایل appsettings.json است. شما می‌توانید بجای آن از IConfiguration استفاده کنید و مقدار آن را در Startup پاس دهید. پارامتر آخر برای حالتی است که این فایل‌ها را در لایه‌ی دیگری به غیر از لایه‌ی اصلی Api (مثل لایه‌ی WebFamewrk) پیاده سازی می‌کنید.
سپس کلاس‌های ثبت سرویس‌هایمان را با ارث بری از این اینترفیس می‌سازیم. برای نمونه رجیستر DbContext را با ایجاد کلاسی به نام DbContextInstaller انجام می‌دهیم:
public class DbContextInstaller : IServiceInstaller
    {
        public void InstallServices(IServiceCollection services, AppSettings appSettings, Assembly startupProjectAssembly)
        {
            services.AddDbContext<AppDbContext>(options =>
            {
                options
               .UseSqlServer(appSettings.ConnectionStrings.MyConnectionString, sqlServerOptionsBuilder =>
                {
                    sqlServerOptionsBuilder.CommandTimeout((int)TimeSpan.FromMinutes(1).TotalSeconds); //Default is 30 seconds
                    sqlServerOptionsBuilder.EnableRetryOnFailure();
                    sqlServerOptionsBuilder.MigrationsAssembly(typeof(AppDbContext).Assembly.FullName);
                })
                    //Tips
                    .ConfigureWarnings(warning => warning.Throw(RelationalEventId
                        .QueryPossibleExceptionWithAggregateOperatorWarning));

                // Activate EF Second Level Cache
                options.AddInterceptors(new SecondLevelCacheInterceptor());
            });
        }
    }

حالا برای ثبت این کلاس و کلاس‌های مشابه Installer، می‌آییم یک متد الحاقی را برای متد ConfigureServices می‌نویسیم که در آن از رفلکشن استفاده می‌کنیم: 
public static class ServiceInstallerExtensions
    {
        public static void InstallServicesInAssemblies(this IServiceCollection services, AppSettings appSettings)
        {
            var startupProjectAssembly = Assembly.GetCallingAssembly();
            var assemblies = new[] { startupProjectAssembly, Assembly.GetExecutingAssembly() };
            var installers = assemblies.SelectMany(a => a.GetExportedTypes())
                .Where(c => c.IsClass && !c.IsAbstract && c.IsPublic && typeof(IServiceInstaller).IsAssignableFrom(c))
                .Select(Activator.CreateInstance).Cast<IServiceInstaller>().ToList();
            installers.ForEach(i => i.InstallServices(services, appSettings, startupProjectAssembly));
        }
    }

در نهایت متد ConfigureServices ما به صورت زیر خواهد بود (بعد از اضافه کردن تمام سرویس‌ها!):
public void ConfigureServices(IServiceCollection services)
        {
            //* HttpContextAccessor
            // services.AddHttpContextAccessor();

            //* Controllers
            services.AddControllers(options => { options.Filters.Add(new AuthorizeFilter()); })
                .AddNewtonsoftJson();

            //* Installers
            services.InstallServicesInAssemblies(_appSettings);
        }
کار تمام شد. حالا تمام سرویس‌های ما با ایجاد کلاس مرتبط و implement شدن از اینترفیس IServiceInstaller، به طور خودکار در استارتاپ و متد ConfigureServies ثبت خواهند شد.

فقط یک نکته آخر اینکه برای رجیستر خودکار DI سرویس‌ها (و ننوشتن کدهایی مانند   ()<services.AddTransient<IUserService, UserService برای رجیستر هر سرویس) می‌توانیم از Autofac استفاده کنیم (در پروژه‌ی بالا آمده است) و یا از پکیج AutoRegisterDi استفاده کنیم (متعلق به Jon P Smith) که از خود Container داخلی Core استفاده می‌کند و از Autofac سبکتر است. کلاسی می‌سازیم به نام RegisterServicesUsingAutoRegisterDiInstaller: 
public class RegisterServicesUsingAutoRegisterDiInstaller : IServiceInstaller
    {
        public void InstallServices(IServiceCollection services, AppSettings appSettings, Assembly startupProjectAssembly)
        {
            var dataAssembly = typeof(SomeRepository).Assembly;
            var serviceAssembly = typeof(SomeService).Assembly;
            var webFrameworkAssembly = Assembly.GetExecutingAssembly();
            var startupAssembly = startupProjectAssembly;
            var assembliesToScan = new[] { dataAssembly, serviceAssembly, webFrameworkAssembly, startupAssembly };

            #region Generic Type Dependencies
            services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
            #endregion

            #region Scoped Dependency Interface
            services.RegisterAssemblyPublicNonGenericClasses(assembliesToScan)
                .Where(c => c.GetInterfaces().Contains(typeof(IScopedDependency)))
                .AsPublicImplementedInterfaces(ServiceLifetime.Scoped);
            #endregion

            #region Singleton Dependency Interface
            services.RegisterAssemblyPublicNonGenericClasses(assembliesToScan)
                .Where(c => c.GetInterfaces().Contains(typeof(ISingletonDependency)))
                .AsPublicImplementedInterfaces(ServiceLifetime.Singleton);
            #endregion

            #region Transient Dependency Interface
            services.RegisterAssemblyPublicNonGenericClasses(assembliesToScan)
                .Where(c => c.GetInterfaces().Contains(typeof(ITransientDependency)))
                .AsPublicImplementedInterfaces(); // Default is Transient
            #endregion

            #region Register DIs By Name
            services.RegisterAssemblyPublicNonGenericClasses(dataAssembly)
                .Where(c => c.Name.EndsWith("Repository")
                            && !c.GetInterfaces().Contains(typeof(ITransientDependency))
                            && !c.GetInterfaces().Contains(typeof(IScopedDependency))
                            && !c.GetInterfaces().Contains(typeof(ISingletonDependency)))
                .AsPublicImplementedInterfaces(ServiceLifetime.Scoped);

            services.RegisterAssemblyPublicNonGenericClasses(serviceAssembly)
                .Where(c => c.Name.EndsWith("Service")
                            && !c.GetInterfaces().Contains(typeof(ITransientDependency))
                            && !c.GetInterfaces().Contains(typeof(IScopedDependency))
                            && !c.GetInterfaces().Contains(typeof(ISingletonDependency)))
                .AsPublicImplementedInterfaces();
            #endregion
        }
    }
 (رجیستر در اینجا با اولویت اینترفیس‌های ITransiantDependency، IScopedDependency، ISingletonDependency و سپس اتمام نام سرویس با کلمه‌های Repository و Service انجام می‌شود که شما می‌توانید با منطق و نیاز خودتان آن‌ها را تغییر دهید)
اشتراک‌ها
یک Business Apps ساخته شده با Asp.net Core Mvc و , TypeScript

یک CMS تجاری بزرگ با قابلیت‌های زیر

A modular, service based web application model
Code generator to produce initial services / user interface code for an SQL table
T4 based code generation on server to reference script widgets with intellisense / compile time validation
T4 based code generation to provide compile time type safety and intellisense while calling AJAX services from script side.
An attribute based form definition system (prepare UI in server side with a simple C# class)
Automatic seamless data-binding through form definitions (form <-> entity <-> service).
Caching Helpers (Local / Distributed)
Automatic cache validation
Configuration System (storage medium independent. store settings in database, file, whatever...)
Simple Logging
Reporting (reports just provide data, has no dependency on rendering, similar to MVC)
Script bundling, minification (making use of Node / UglifyJS / CleanCSS) and content versioning (no more F5 / clear browser cache)
Fluent SQL Builder (SELECT/INSERT/UPDATE/DELETE)
Micro ORM (also Dapper is integrated)
Customizable handlers for REST like services that work by reusing information in entity classes and do automatic validation.
Attribute based navigation menu
UI Localization (store localized texts in json files, embedded resource, database, in memory class, anywhere)
Data Localization (using an extension table mechanism helps to localize even data entered by users, like lookup tables)
Script widget system (inspired by jQueryUI but more suitable for C# code)
Client side and server side validation (based on jQuery validate plugin, but abstracts dependency)
Audit logging (where CDC is not available)
System for data based integration tests
Dynamic scripts
Script side templates 
یک Business Apps ساخته شده با Asp.net Core Mvc و , TypeScript
نظرات مطالب
مهارت‌های تزریق وابستگی‌ها در برنامه‌های NET Core. - قسمت دوم - الگوی Service Locator
یک نکته‌ی تکمیلی: طراحی یک کلاس ServiceLocator برای NET Core.

گاهی از اوقات مجبور به کار با کتابخانه‌هایی هستید که برای کار با تزریق وابستگی‌ها طراحی نشده‌اند. برای مثال این کتابخانه‌ها کلاسی را از شما دریافت می‌کنند، این کلاس را خودشان وهله سازی کرده و در نهایت استفاده خواهند کرد. چون وهله سازی این کلاس در اختیار شما نیست و همچنین کتابخانه‌ی فراخوان نیز از تزریق وابستگی‌های در سازنده‌ی کلاس دریافتی، پشتیبانی نمی‌کند، تنها راه حل باقیمانده، استفاده از الگوی Service Locator خواهد بود. برای این منظور می‌توانید از دو کلاس زیر کمک بگیرید:
using System;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;

namespace Utils
{
    public static class ServiceLocatorProvider
    {
        private static readonly Lazy<IServiceProvider> _serviceProviderBuilder =
            new Lazy<IServiceProvider>(GetServiceProvider, LazyThreadSafetyMode.ExecutionAndPublication);

        /// <summary>
        /// A lazy loaded thread-safe singleton
        /// </summary>
        public static IServiceProvider Current { get; } = _serviceProviderBuilder.Value;

        private static IServiceProvider GetServiceProvider()
        {
            var services = new ServiceCollection();
            ConfigureServices(services);
            return services.BuildServiceProvider();
        }

        private static void ConfigureServices(IServiceCollection services)
        {
            // TODO: add other services here ... services.AddSingleton ....
        }
    }

    public static class ServiceLocator
    {
        public static object GetService(Type serviceType)
        {
            return ServiceLocatorProvider.Current.GetService(serviceType);
        }

        public static TService GetService<TService>()
        {
            return ServiceLocatorProvider.Current.GetService<TService>();
        }

        public static object GetRequiredService(Type serviceType)
        {
            return ServiceLocatorProvider.Current.GetRequiredService(serviceType);
        }

        public static TService GetRequiredService<TService>()
        {
            return ServiceLocatorProvider.Current.GetService<TService>();
        }

        public static void RunScopedService<T, S>(Action<S, T> callback)
        {
            using (var serviceScope = ServiceLocatorProvider.Current.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<S>();

                callback(context, serviceScope.ServiceProvider.GetRequiredService<T>());
                if (context is IDisposable disposable)
                {
                    disposable.Dispose();
                }
            }
        }

        public static void RunScopedService<S>(Action<S> callback)
        {
            using (var serviceScope = ServiceLocatorProvider.Current.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<S>();
                callback(context);
                if (context is IDisposable disposable)
                {
                    disposable.Dispose();
                }
            }
        }

        public static T RunScopedService<T, S>(Func<S, T> callback)
        {
            using (var serviceScope = ServiceLocatorProvider.Current.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<S>();
                return callback(context);
            }
        }
    }
}
در اینجا باید متد ConfigureServices کلاس ServiceLocatorProvider را همانند قبل تنظیم و تعاریف سرویس‌های مدنظر خود را اضافه کنید. سپس در هر قسمتی از برنامه می‌توانید از متدهایی مانند ()<ServiceLocator.GetRequiredService<TService استفاده نمائید. در مورد متدهای RunScopedService آن در قسمت سوم بیشتر بحث شده‌است.
مطالب
بالا بردن سرعت DbContext هنگام ثبت داده های زیاد
تشریح مسئله :شاید شما هم هنگام ثبت، ویرایش و حتی حذف داده‌های زیاد در Code First متوجه کاهش چشمگیر کارایی پروژه خود شده  باشید.(برای مثال ثبت 5000 داده یا بیشتر به صورت هم زمان).برای رفع مشکل بالا چه باید کرد؟

نکته : آشنایی اولیه با مفاهیم EF CodeFirst برای درک بهتر مفاهیم الزامی است.

EntityFramework Code First هنگام کار با Poco Entities برای اینکه مشخص شود که چه داده هایی باید به دیتابیس  ارسال شود مکانیزمی به نام Detect Changed معرفی کرده است که وظیفه آن بررسی تفاوت‌های بین مقادیر خواص CurrentValue و OriginalValue هر Entity است که باعث افت چشمگیر سرعت هنگام اجرای عملیات CRUD می‌شود.
هنگامی که از یک Entity کوئری گرفته می‌شود یا از دستور Attach برای یک Entity استفاده می‌کنیم مقادیر مورد نظر در حافظه ذخیره می‌شوند. استفاده از هر کدام دستورات زیر DbContext را مجبور به فراخوانی الگوریتم Automatic Detect Changed می‌کند.

  • DbSet.Find
  • DbSet.Local
  • DbSet.Remove
  • DbSet.Add
  • DbSet.Attach
  • DbContext.SaveChanges
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries 

البته Code First امکانی را فراهم کرده است که هنگام پیاده سازی عملیات CRUD  اگر تعداد داده‌های شرکت کننده زیاد است برای رفع مشکل کاهش سرعت بهتر است این رفتار را غیر فعال کنیم . به صورت زیر:

using (var context = new BookContext())
{
    try
    {
        context.Configuration.AutoDetectChangesEnabled = false;
 
        foreach (var book in aLotOfBooks)
        {
            context.Books.Add(book);
        }
    }
    finally
    {
        context.Configuration.AutoDetectChangesEnabled = true;
    }
در پایان هم وضعیت را به حالت قبل بر می‌گردانیم.
در مورد کاهش مصرف حافظه EF CodeFirst  هنگام واکشی داده‌های زیاد هم می‌تونید از این مقاله استفاده کنید.
نظرات مطالب
امن سازی برنامه‌های ASP.NET Core توسط IdentityServer 4x - قسمت پنجم - پیاده سازی ورود و خروج از سیستم
در سمت IDP:همان قسمت «نصب و راه اندازی - تعریف کاربران، منابع و کلاینت‌ها» است. در این دمو از نگارش درون حافظه‌ای استفاده شده. امکان تعویض آن با « IdentityServer4.EntityFramework » وجود دارد که نیاز به یک بحث مجزا برای آن هست.