نظرات مطالب
بازگردانی پایگاه داده بدون فایل لاگ
لطفا کاملتر توضیح بدید.
من دیتابیسی دارم که فایل لاگ آن پاک شده باید چیکار کنم؟
نظرات مطالب
استفاده از GitHub Actions برای Build و توزیع خودکار پروژه‌های NET Core.
یک نکته تکمیلی:
بعد از Build کردن ریپازیتوری یا Pack کردن پکیج خود می‌توانید فایل‌های تولید شده را در جایی به نام "Artifacts" ذخیره کنید تا در زمان دلخواه بتوانید آنها را دانلود کنید
به جهت انجام این کار باید از اکشن upload-artifact استفاده کنید. مثال:
name: Build
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v2
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
    - name: Build (Release)
      run: dotnet build src --configuration Release
    - name: Pack (Release)
      run: dotnet pack src --configuration Release

    - name: Upload Artifact
      uses: actions/upload-artifact@v1.0.0
      with:
        name: Release_Artifacts
        path: src\bin\Release
  • عبارت src به پوشه پروژه اصلی اشاره میکند. (در صورت نیاز آن را تغییر دهید)
  • عبارت "Release_Artifacts" نام فایل Artifacts خروجی شماست که با این نام ذخیره می‌شود.
  • مسیر "src\bin\Release" پوشه ای است که میخواهید محتوای آن را در Artifacts ذخیره کنید.
بعد از اتمام فرایند در صورت موفقیت آمیز بودن فرایند، فایل Artifacts را میتوانید از این قسمت دانلود کنید. 

نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 6 - سرویس‌ها و تزریق وابستگی‌ها
تکمیل نکته‌ی «نکته‌ای مهم در مورد تزریق وابستگی‌ها در کلاس ویژه‌ی آغازین برنامه » که در بالا عنوان شد

در ASP.NET Core 2.0 در صورت عدم رعایت این نکته و عدم ایجاد یک scope جدید، ممکن است خطای ذیل را دریافت کنید:
Unhandled Exception: System.InvalidOperationException: Cannot resolve scoped service from root provider.
تنظیم این بررسی در فایل program.cs صورت می‌گیرد:
namespace ASPNETCoreIdentitySample
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
               // ...
                .UseDefaultServiceProvider((context, options) =>
                {
                    options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
                })
               // ...

            host.Run();
        }
    }
}
در اینجا اگر ValidateScopes را false کنید این استثناء را دریافت نخواهید کرد. اما بهتر است این تنظیم وجود داشته باشد تا مشکلاتی از این دست پیش از ارائه‌ی نهایی برنامه مشخص شوند. راه حل صحیح آن هم ایجاد یک scope جدید توسط scopeFactory.CreateScope است که به آن اشاره شد.
مطالب
راهنمای تغییر بخش احراز هویت و اعتبارسنجی کاربران سیستم مدیریت محتوای IRIS به ASP.NET Identity – بخش دوم
در بخش اول، کارهایی که انجام دادیم به طور خلاصه عبارت بودند از:
1-  حذف کاربرانی که نام کاربری و ایمیل تکراری داشتند
2-  تغییر نام فیلد Password به PasswordHash در جدول User
 
سیستم مدیریت محتوای IRIS، برای استفاده از Entity Framework، از الگوی واحد کار (Unit Of Work) و تزریق وابستگی استفاده کرده است و اگر با نحوه‌ی پیاده سازی این الگو‌ها آشنا نیستید، خواندن مقاله EF Code First #12  را به شما توصیه می‌کنم.
برای استفاده از ASP.NET Identity نیز باید از الگوی واحد کار استفاده کرد و برای این کار، ما از مقاله اعمال تزریق وابستگی‌ها به مثال رسمی ASP.NET Identity استفاده خواهیم کرد.
نکته مهم: در ادامه اساس کار ما بر پایه‌ی مقاله اعمال تزریق وابستگی‌ها به مثال رسمی ASP.NET Identity است و چیزی که بیشتر برای ما اهمیت دارد کدهای نهایی آن هست؛ پس حتما به مخزن کد آن مراجعه کرده و کدهای آن را دریافت کنید.
 
تغییر نام کلاس User به ApplicationUser

اگر به کدهای مثال رسمی ASP.NET Identity نگاهی بیندازید، می‌بینید که کلاس مربوط به جدول کاربران ApplicationUser نام دارد، ولی در سیستم IRIS نام آن User است. بهتر است که ما هم نام کلاس خود را از User به ApplicationUser تغییر دهیم چرا که مزایای زیر را به دنبال دارد:

1- به راحتی می‌توان کدهای مورد نیاز را از مثال Identity کپی کرد.
2- در سیستم Iris، بین کلاس User متعلق به پروژه خودمان و User مربوط به HttpContext تداخل رخ می‌داد که با تغییر نام کلاس User دیگر این مشکل را نخواهیم داشت.
 
برای این کار وارد پروژه Iris.DomainClasses شده و نام کلاس User را به ApplicationUser تغییر دهید. دقت کنید که این تغییر نام را از طریق Solution Explorer انجام دهید و نه از طریق کدهای آن. پس از این تغییر ویژوال استودیو می‌پرسد که آیا نام این کلاس را هم در کل پروژه تغییر دهد که شما آن را تایید کنید.

برای آن که نام جدول Users در دیتابیس تغییری نکند، وارد پوشه‌ی Entity Configuration شده و کلاس UserConfig را گشوده و در سازنده‌ی آن کد زیر را اضافه کنید:
ToTable("Users");

نصب ASP.NET Identity

برای نصب ASP.NET Identity دستور زیر را در کنسول Nuget وارد کنید:
Get-Project Iris.DomainClasses, Iris.Datalayer, Iris.Servicelayer, Iris.Web | Install-Package Microsoft.AspNet.Identity.EntityFramework
از پروژه AspNetIdentityDependencyInjectionSample.DomainClasses کلاس‌های CustomUserRole، CustomUserLogin، CustomUserClaim و CustomRole را به پروژه Iris.DomainClasses منتقل کنید. تنها تغییری که در این کلاس‌ها باید انجام دهید، اصلاح namespace آنهاست.
همچنین بهتر است که به کلاس CustomRole، یک property به نام Description اضافه کنید تا توضیحات فارسی نقش مورد نظر را هم بتوان ذخیره کرد:

 
    public class CustomRole : IdentityRole<int, CustomUserRole>
    {
        public CustomRole() { }
        public CustomRole(string name) { Name = name; }

        public string Description { get; set; }

    }

نکته: پیشنهاد می‌کنم که اگر می‌خواهید مثلا نام CustomRole را به IrisRole تغییر دهید، این کار را از طریق find and replace انجام ندهید. با همین نام‌های پیش فرض کار را تکمیل کنید و سپس از طریق خود ویژوال استودیو نام کلاس را تغییر دهید تا ویژوال استودیو به نحو بهتری این نام‌ها را در سرتاسر پروژه تغییر دهد.
 
سپس کلاس ApplicationUser پروژه IRIS را باز کرده و تعریف آن را به شکل زیر تغییر دهید:
public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>

اکنون می‌توانید property‌های Id،  UserName، PasswordHash و Email را حذف کنید؛ چرا که در کلاس پایه IdentityUser تعریف شده اند.
 
تغییرات DataLayer

وارد Iris.DataLayer شده و کلاس IrisDbContext را به شکل زیر ویرایش کنید:
public class IrisDbContext : IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>,
        IUnitOfWork

اکنون می‌توانید property زیر را نیز حذف کنید چرا که در کلاس پایه تعریف شده است: 
 public DbSet<ApplicationUser> Users { get; set; }

 

نکته مهم: حتما برای کلاس IrisDbContext سازنده ای تعریف کنید که صراحتا نام رشته اتصالی را ذکر کرده باشد، اگر این کار را انجام ندهید با خطاهای عجیب غریبی روبرو می‌شوید. 

        public IrisDbContext()
            : base("IrisDbContext")
        {
        }

همچنین درون متد OnModelCreating کدهای زیر را پس از فراخوانی متد (base.OnModelCreating(modelBuilder  جهت تعیین نام جداول دیتابیس بنویسید:
            modelBuilder.Entity<CustomRole>().ToTable("AspRoles");
            modelBuilder.Entity<CustomUserClaim>().ToTable("UserClaims");
            modelBuilder.Entity<CustomUserRole>().ToTable("UserRoles");
            modelBuilder.Entity<CustomUserLogin>().ToTable("UserLogins");
از این جهت نام جدول CustomRole را در دیتابیس AspRoles انتخاب کردم تا با نام جدول Roles نقش‌های کنونی سیستم Iris تداخلی پیش نیاید.
اکنون دستور زیر را در کنسول Nuget وارد کنید تا کدهای مورد نیاز برای مهاجرت تولید شوند:
Add-Migration UpdateDatabaseToAspIdentity
public partial class UpdateDatabaseToAspIdentity : DbMigration
{
        public override void Up()
        {
            CreateTable(
                "dbo.UserClaims",
                c => new
                    {
                        Id = c.Int(nullable: false, identity: true),
                        UserId = c.Int(nullable: false),
                        ClaimType = c.String(),
                        ClaimValue = c.String(),
                        ApplicationUser_Id = c.Int(),
                    })
                .PrimaryKey(t => t.Id)
                .ForeignKey("dbo.Users", t => t.ApplicationUser_Id)
                .Index(t => t.ApplicationUser_Id);
            
            CreateTable(
                "dbo.UserLogins",
                c => new
                    {
                        LoginProvider = c.String(nullable: false, maxLength: 128),
                        ProviderKey = c.String(nullable: false, maxLength: 128),
                        UserId = c.Int(nullable: false),
                        ApplicationUser_Id = c.Int(),
                    })
                .PrimaryKey(t => new { t.LoginProvider, t.ProviderKey, t.UserId })
                .ForeignKey("dbo.Users", t => t.ApplicationUser_Id)
                .Index(t => t.ApplicationUser_Id);
            
            CreateTable(
                "dbo.UserRoles",
                c => new
                    {
                        UserId = c.Int(nullable: false),
                        RoleId = c.Int(nullable: false),
                        ApplicationUser_Id = c.Int(),
                    })
                .PrimaryKey(t => new { t.UserId, t.RoleId })
                .ForeignKey("dbo.Users", t => t.ApplicationUser_Id)
                .ForeignKey("dbo.AspRoles", t => t.RoleId, cascadeDelete: true)
                .Index(t => t.RoleId)
                .Index(t => t.ApplicationUser_Id);
            
            CreateTable(
                "dbo.AspRoles",
                c => new
                    {
                        Id = c.Int(nullable: false, identity: true),
                        Description = c.String(),
                        Name = c.String(nullable: false, maxLength: 256),
                    })
                .PrimaryKey(t => t.Id)
                .Index(t => t.Name, unique: true, name: "RoleNameIndex");
            
            AddColumn("dbo.Users", "EmailConfirmed", c => c.Boolean(nullable: false));
            AddColumn("dbo.Users", "SecurityStamp", c => c.String());
            AddColumn("dbo.Users", "PhoneNumber", c => c.String());
            AddColumn("dbo.Users", "PhoneNumberConfirmed", c => c.Boolean(nullable: false));
            AddColumn("dbo.Users", "TwoFactorEnabled", c => c.Boolean(nullable: false));
            AddColumn("dbo.Users", "LockoutEndDateUtc", c => c.DateTime());
            AddColumn("dbo.Users", "LockoutEnabled", c => c.Boolean(nullable: false));
            AddColumn("dbo.Users", "AccessFailedCount", c => c.Int(nullable: false));
        }
        
        public override void Down()
        {
            DropForeignKey("dbo.UserRoles", "RoleId", "dbo.AspRoles");
            DropForeignKey("dbo.UserRoles", "ApplicationUser_Id", "dbo.Users");
            DropForeignKey("dbo.UserLogins", "ApplicationUser_Id", "dbo.Users");
            DropForeignKey("dbo.UserClaims", "ApplicationUser_Id", "dbo.Users");
            DropIndex("dbo.AspRoles", "RoleNameIndex");
            DropIndex("dbo.UserRoles", new[] { "ApplicationUser_Id" });
            DropIndex("dbo.UserRoles", new[] { "RoleId" });
            DropIndex("dbo.UserLogins", new[] { "ApplicationUser_Id" });
            DropIndex("dbo.UserClaims", new[] { "ApplicationUser_Id" });
            DropColumn("dbo.Users", "AccessFailedCount");
            DropColumn("dbo.Users", "LockoutEnabled");
            DropColumn("dbo.Users", "LockoutEndDateUtc");
            DropColumn("dbo.Users", "TwoFactorEnabled");
            DropColumn("dbo.Users", "PhoneNumberConfirmed");
            DropColumn("dbo.Users", "PhoneNumber");
            DropColumn("dbo.Users", "SecurityStamp");
            DropColumn("dbo.Users", "EmailConfirmed");
            DropTable("dbo.AspRoles");
            DropTable("dbo.UserRoles");
            DropTable("dbo.UserLogins");
            DropTable("dbo.UserClaims");
        }
}

بهتر است که در کدهای تولیدی فوق، اندکی متد Up را با کد زیر تغییر دهید: 
AddColumn("dbo.Users", "EmailConfirmed", c => c.Boolean(nullable: false, defaultValue:true));

چون در سیستم جدید احتیاج به تایید ایمیل به هنگام ثبت نام است، بهتر است که ایمیل‌های قبلی موجود در سیستم نیز به طور پیش فرض تایید شده باشند.
در نهایت برای اعمال تغییرات بر روی دیتابیس دستور زیر را در کنسول Nuget وارد کنید:
Update-Database
 
تغییرات ServiceLayer

ابتدا دستور زیر را در کنسول Nuget  وارد کنید: 
Get-Project Iris.Servicelayer, Iris.Web | Install-Package Microsoft.AspNet.Identity.Owin
سپس از فولدر Contracts پروژه AspNetIdentityDependencyInjectionSample.ServiceLayer فایل‌های IApplicationRoleManager، IApplicationSignInManager، IApplicationUserManager، ICustomRoleStore و ICustomUserStore را در فولدر Interfaces پروژه Iris.ServiceLayer کپی کنید. تنها کاری هم که نیاز هست انجام بدهید اصلاح namespace هاست.

باز از پروژه AspNetIdentityDependencyInjectionSample.ServiceLayer کلاس‌های ApplicationRoleManager، ApplicationSignInManager،  ApplicationUserManager، CustomRoleStore، CustomUserStore، EmailService و SmsService را به پوشه EFServcies پروژه‌ی Iris.ServiceLayer کپی کنید.
نکته: پیشنهاد می‌کنم که EmailService را به IdentityEmailService تغییر نام دهید چرا که در حال حاضر سیستم Iris دارای کلاسی به نامی EmailService هست.
 
تنظیمات StructureMap برای تزریق وابستگی ها
پروژه Iris.Web  را باز کرده، به فولدر DependencyResolution بروید و به کلاس IoC کدهای زیر را اضافه کنید:
                x.For<IIdentity>().Use(() => (HttpContext.Current != null && HttpContext.Current.User != null) ? HttpContext.Current.User.Identity : null);

                x.For<IUnitOfWork>()
                    .HybridHttpOrThreadLocalScoped()
                    .Use<IrisDbContext>();

                x.For<IrisDbContext>().HybridHttpOrThreadLocalScoped()
                   .Use(context => (IrisDbContext)context.GetInstance<IUnitOfWork>());
                x.For<DbContext>().HybridHttpOrThreadLocalScoped()
                   .Use(context => (IrisDbContext)context.GetInstance<IUnitOfWork>());

                x.For<IUserStore<ApplicationUser, int>>()
                    .HybridHttpOrThreadLocalScoped()
                    .Use<CustomUserStore>();

                x.For<IRoleStore<CustomRole, int>>()
                    .HybridHttpOrThreadLocalScoped()
                    .Use<RoleStore<CustomRole, int, CustomUserRole>>();

                x.For<IAuthenticationManager>()
                      .Use(() => HttpContext.Current.GetOwinContext().Authentication);

                x.For<IApplicationSignInManager>()
                      .HybridHttpOrThreadLocalScoped()
                      .Use<ApplicationSignInManager>();

                x.For<IApplicationRoleManager>()
                      .HybridHttpOrThreadLocalScoped()
                      .Use<ApplicationRoleManager>();

                // map same interface to different concrete classes
                x.For<IIdentityMessageService>().Use<SmsService>();
                x.For<IIdentityMessageService>().Use<IdentityEmailService>();

                x.For<IApplicationUserManager>().HybridHttpOrThreadLocalScoped()
                   .Use<ApplicationUserManager>()
                   .Ctor<IIdentityMessageService>("smsService").Is<SmsService>()
                   .Ctor<IIdentityMessageService>("emailService").Is<IdentityEmailService>()
                   .Setter<IIdentityMessageService>(userManager => userManager.SmsService).Is<SmsService>()
                   .Setter<IIdentityMessageService>(userManager => userManager.EmailService).Is<IdentityEmailService>();

                x.For<ApplicationUserManager>().HybridHttpOrThreadLocalScoped()
                   .Use(context => (ApplicationUserManager)context.GetInstance<IApplicationUserManager>());

                x.For<ICustomRoleStore>()
                      .HybridHttpOrThreadLocalScoped()
                      .Use<CustomRoleStore>();

                x.For<ICustomUserStore>()
                      .HybridHttpOrThreadLocalScoped()
                      .Use<CustomUserStore>();

اگر ()HttpContext.Current.GetOwinContext شناسایی نمی‌شود دلیلش این است که متد GetOwinContext یک متد الحاقی است که برای استفاده از آن باید پکیج نیوگت زیر را نصب کنید:
Install-Package Microsoft.Owin.Host.SystemWeb

تغییرات Iris.Web
در ریشه پروژه‌ی Iris.Web  یک کلاس به نام Startup  بسازید و کدهای زیر را در آن بنویسید:
using System;
using Iris.Servicelayer.Interfaces;
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.DataProtection;
using Owin;
using StructureMap;

namespace Iris.Web
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            configureAuth(app);
        }

        private static void configureAuth(IAppBuilder app)
        {
            ObjectFactory.Container.Configure(config =>
            {
                config.For<IDataProtectionProvider>()
                      .HybridHttpOrThreadLocalScoped()
                      .Use(() => app.GetDataProtectionProvider());
            });

            //ObjectFactory.Container.GetInstance<IApplicationUserManager>().SeedDatabase();

            // Enable the application to use a cookie to store information for the signed in user
            // and to use a cookie to temporarily store information about a user logging in with a third party login provider
            // Configure the sign in cookie
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                Provider = new CookieAuthenticationProvider
                {
                    // Enables the application to validate the security stamp when the user logs in.
                    // This is a security feature which is used when you change a password or add an external login to your account.
                    OnValidateIdentity = ObjectFactory.Container.GetInstance<IApplicationUserManager>().OnValidateIdentity()
                }
            });
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
            app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));

            // Enables the application to remember the second login verification factor such as phone or email.
            // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
            // This is similar to the RememberMe option when you log in.
            app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);

            app.CreatePerOwinContext(
               () => ObjectFactory.Container.GetInstance<IApplicationUserManager>());

            // Uncomment the following lines to enable logging in with third party login providers
            //app.UseMicrosoftAccountAuthentication(
            //    clientId: "",
            //    clientSecret: "");

            //app.UseTwitterAuthentication(
            //   consumerKey: "",
            //   consumerSecret: "");

            //app.UseFacebookAuthentication(
            //   appId: "",
            //   appSecret: "");

            //app.UseGoogleAuthentication(
            //    clientId: "",
            //    clientSecret: "");

        }
    }
}

تا به این جای کار اگر پروژه را اجرا کنید نباید هیچ مشکلی مشاهده کنید. در بخش بعدی کدهای مربوط به کنترلر‌های ورود، ثبت نام، فراموشی کلمه عبور و ... را با سیستم Identity پیاده سازی می‌کنیم.
مطالب
babel چیست؟ lebab چیست؟
قطعا به عنوان برنامه نویس JavaScript کم و بیش با ecmaScript 6 آشنایی دارید.
با وجود ویژگی‌های منحصر به فردی که دارد شاید تنها دلیلی که برخی از برنامه نویسان هنوز تصمیم به عدم استفاده از این زبان را دارند، مرورگرهایی میباشند که هنوز از es6 پشتیبانی نمیکنند: es6 compatibility table 
اما راهکاری مناسب، برای اینکه بتوان هم از es6 استفاده کرد و هم کاربران را مجبور به استفاده‌ی از مرورگر‌های مدرن نکنیم نیز وجود دارد:به صورت مستقیم میتوان با استفاده از Babel، کد‌های نوشته شده‌ی با es6 را کامپایل و تبدیل به es5 کنیم. برای اینکار معمولا از gulp استفاده میکنیم. 
gulp در وبسایت رسمی آن، خودش را اینچنین تعریف کرده است : automate and enhance your workflow
حال کافیست با چند خط کد es6 شروع به کار کنیم. بنده معمولا از visual studio code استفاده میکنم.
class firstCLs
{
    doFirst(str)
    {
        console.log(str);
    }
}

class secondCls extends firstCLs
{
    doSecond(str)
    {
        super.doFirst(str);
    }
}

let str = 'string';
new secondCls().doSecond(`this is some ${str}`);
فکر میکنم کد‌های فوق احتیاج به توضیح بیشتری نداشته باشند. اکنون فرض کنید نام فایل آن code.js است و در پوشه‌ی src قرار دارد. همانطور که پیشتر عرض کردم، برای تبدیل آن به کدی از نوع es5 از gulp استفاده می‌کنیم.
ابتدا از طریق خط فرمان، خود gulp را نصب میکنیم. 
npm install -g gulp
بعد بر روی پوشه‌ی root پروژه‌تان رفته و با استفاده از خط فرمان، npm init را ارسال کنید تا package.json برای شما ساخته شود.
برای نصب gulp در پروژه‌ی local خود نیز این فرمان را ارسال کنید:
npm install gulp --save-dev
با استفاده از این فرمان، gulp را در مسیر جاری پروژه‌تان و در پوشه‌ی node-modules نصب کرده و همچنین فرمان --save-dev نیز آن را به وابستگی‌های پروژه اضافه میکند.
حال احتیاج به نصب gulp-babel داریم که با استفاده از خط فرمان، خود آن را نصب مینماییم:
npm install --save-dev gulp-babel babel-preset-es2015
حال در مسیر اصلی پروژه، فایل gulpfile.js را ساخته و کدهای زیر را مینویسیم:
const gulp = require('gulp');
const babel = require('gulp-babel');
 
gulp.task('default', () => {
return gulp.src('src/code.js')
.pipe(babel({
presets: ['es2015']
}))
.pipe(gulp.dest('dist'));
});
برای توضیح کد‌های بالا باید عرض کنم در قسمت gulp.task، عملیاتی را که لازم میدانیم gulp برای ما انجام دهد، شرح میدهیم.
gulp.src مسیر فایلی را که تصمیم داریم کامپایل شده و به es5 تبدیل شود، معین می‌کند ( لازم به ذکر است که میتوان بطور مثال همه‌ی اسکریپت‌های درون یک پوشه را انتخاب کرد که توضیح آن در این مقاله نمیگنجد).
بعد در قسمت اولین pipe فوق است که میخواهیم تبدیلی را به es2015 داشته باشیم و در آخرین pipe نیز با استفاده از متد dest، آدرس مسیری را که میخواهیم آن فایل کامپایل شده در آن قرار بگیرد، مینویسیم. بطور مثال من پوشه‌ی dist را به عنوان آدرس قرار داده‌ام.
مراحل انجام شده را save نمایید. خط فرمان خود را باز کرده و دستور gulp را type نمایید. بعد از پایان یافتن عملیات، به پوشه‌ی dist رجوع کنید. فایل مورد نظر مشاهد میشود.

اما حال فرض کنید پروژه ای در اختیار داشته و کد‌ها از نوع es5 میباشند و تصمیم به تبدیل آن‌ها به es6 داشته باشید.
برای اینکار از lebab (که دقیقا عکس نوشتاری bable میباشد) استفاده میکنیم.
ابتدا از طریق خط فرمان خود آن را نصب مینماییم
npm install -g lebab
به صورت مستقیم میتوانیم هر فایلی را که به صورت استاندارد نوشته شده است، کامپایل کرده و تبدیل به es6 نماییم.
کد‌های زیر را در نظر بگیرید
'use strict';

// Let/const
var name = 'Bob', time = 'yesterday';
time = 'today';

// Template string
console.log('Hello ' + name + ', how are you ' + time + '?');

var bob = {
  // Object shorthand
  name: name,
  // Object method
  sayMyName: function () {
    console.log(this.name);
  }
};

// Classes
var SkinnedMesh = function SkinnedMesh() {
};

SkinnedMesh.prototype.update = function (camera) {
  camera = camera || createCamera();
  this.camera = camera;
};

Object.defineProperty(SkinnedMesh.prototype, 'name', {
  set: function (geometry) {
    this.geometry = geometry;
  },
  get: function () {
    return this.geometry;
  }
});

// Commonjs
var lebab = require('lebab');
module.exports = SkinnedMesh;

// Arrow functions
var render = function () {
  // ...
  requestAnimationFrame(render);
};
فرض کنید آن را با نام es5.js در مسیر اصلی پروژه ذخیره کرده‌ایم. با استفاده از خط فرمان خود آن را تبدیل به es6 خواهیم کرد؛ بدین شکل:
lebab es5.js -o es6.js
فایل مورد نظر ما به نام es6.js ذخیره خواهد شد و بدین صورت کامپایل خواهد شد
const name = 'Bob';
let time = 'yesterday';
time = 'today';

// Template string
console.log(`Hello ${name}, how are you ${time}?`);

const bob = {
  // Object shorthand
  name,
  // Object method
  sayMyName() {
    console.log(this.name);
  }
};

class SkinnedMesh {
  update(camera=createCamera()) {
    this.camera = camera;
  }

  set name(geometry) {
    this.geometry = geometry;
  }

  get name() {
    return this.geometry;
  }
}

import lebab from 'lebab';
export default SkinnedMesh;

// Arrow functions
const render = () => {
  // ...
  requestAnimationFrame(render);
};
تبدیل es5 به es6 انجام شد؛ اما باید خدمتتان عرض کنم آنچنان هم نباید انتظار داشت که بعد از این تبدیل و فشار دادن دکمه‌ی F5، پروژه‌ی شما بدون هیچ خطایی اجرا شود (البته در صورتیکه هنوز در اوایل پروژه هستید شاید اینگونه شود) اما کمی refactoring برای کد‌های کامپایل شده را به es 6، در خاطر داشته باشید.
مطالب
استفاده از چندین بانک اطلاعاتی به صورت همزمان در EF Code First
یکی از روش‌های تهیه‌ی برنامه‌های چند مستاجری، ایجاد بانک‌های اطلاعاتی مستقلی به ازای هر مشتری است؛ یا نمونه‌ی دیگر آن، برنامه‌هایی هستند که اطلاعات هر سال را در یک بانک اطلاعاتی جداگانه نگه‌داری می‌کنند. در ادامه قصد داریم، نحوه‌ی کار با این بانک‌های اطلاعاتی را به صورت همزمان، توسط EF Code first و در حالت استفاده از الگوی واحد کار و تزریق وابستگی‌ها، به همراه فعال سازی خودکار مباحث migrations و به روز رسانی ساختار تمام بانک‌های اطلاعاتی مورد استفاده، بررسی کنیم.


مشخص سازی رشته‌های متفاوت اتصالی

فرض کنید برنامه‌ی جاری شما قرار است از دو بانک اطلاعاتی مشخص استفاده کند که تعاریف رشته‌های اتصالی آن‌ها در وب کانفیگ به صورت ذیل مشخص شده‌اند:
  <connectionStrings>
    <clear />
    <add name="Sample07Context" connectionString="Data Source=(local);Initial Catalog=TestDbIoC;Integrated Security = true" providerName="System.Data.SqlClient" />
    <add name="Database2012" connectionString="Data Source=(local);Initial Catalog=testdb2012;Integrated Security = true" providerName="System.Data.SqlClient" />
  </connectionStrings>
البته، ذکر این مورد کاملا اختیاری است و می‌توان رشته‌های اتصالی را به صورت پویا نیز در زمان اجرا مشخص و مقدار دهی کرد.


تغییر Context برنامه جهت پذیرش رشته‌های اتصالی پویای قابل تغییر در زمان اجرا

اکنون که قرار است کاربران در حین ورود به برنامه، بانک اطلاعاتی مدنظر خود را انتخاب کنند و یا سیستم قرار است به ازای کاربری خاص، رشته‌ی اتصالی خاص او را به Context ارسال کند، نیاز است Context برنامه را به صورت ذیل تغییر دهیم:
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using EF_Sample07.DomainClasses;
 
namespace EF_Sample07.DataLayer.Context
{
    public class Sample07Context : DbContext, IUnitOfWork
    {
        public DbSet<Category> Categories { set; get; }
        public DbSet<Product> Products { set; get; }
 
        /// <summary>
        /// It looks for a connection string named Sample07Context in the web.config file.
        /// </summary>
        public Sample07Context()
            : base("Sample07Context")
        {
        }
 
        /// <summary>
        /// To change the connection string at runtime. See the SmObjectFactory class for more info.
        /// </summary>
        public Sample07Context(string connectionString)
            : base(connectionString)
        {
            //Note: defaultConnectionFactory in the web.config file should be set.
        }
 
 
        public void SetConnectionString(string connectionString)
        {
            this.Database.Connection.ConnectionString = connectionString;
        }
    }
}
در اینجا دو متد سازنده را مشاهده می‌کنید. سازنده‌ی پیش فرض، از رشته‌ای اتصالی با نامی مساوی Sample07Context استفاده می‌کند و سازنده‌ی دوم، امکان پذیرش یک رشته‌ی اتصالی پویا را دارد. مقدار پارامتر ورودی آن می‌تواند نام رشته‌ی اتصالی و یا حتی مقدار کامل رشته‌ی اتصالی باشد. حالت پذیرش نام رشته‌ی اتصالی زمانی مفید است که همانند مثال ابتدای بحث، این نام‌ها را پیشتر در فایل کانفیگ برنامه ثبت کرده باشید و حالت پذیرش مقدار کامل رشته‌ی اتصالی، جهت مقدار دهی پویای آن بدون نیاز به ثبت اطلاعاتی در فایل کانفیگ برنامه مفید است.

یک متد دیگر هم در اینجا در انتهای کلاس به نام SetConnectionString تعریف شده‌است. از این متد در حین ورود کاربر به سایت می‌توان استفاده کرد. برای مثال حداقل دو نوع طراحی را می‌توان درنظر گرفت:
الف) کاربر با برنامه‌ای کار می‌کند که به ازای سال‌های مختلف، بانک‌های اطلاعاتی مختلفی دارد و در ابتدای ورود، یک drop down انتخاب سال کاری برای او درنظر گرفته شده‌است (علاوه بر سایر ورودی‌های استانداردی مانند نام کاربری و کلمه‌ی عبور). در این حالت بهتر است متد SetConnectionString نام رشته‌ی اتصالی را بر اساس سال انتخابی، در حین لاگین دریافت کند که اطلاعات آن در فایل کانفیگ سایت پیشتر مشخص شده‌است.
ب) کاربر یا مشتری پس از ورود به سایت، نیاز است صرفا از بانک اطلاعاتی خاص خودش استفاده کند. بنابراین اطلاعات تعریف کاربران و مشتری‌ها در یک بانک اطلاعاتی مجزا قرار دارند و پس از لاگین، نیاز است رشته‌ی اتصالی او به صورت پویا از بانک اطلاعاتی خوانده شده و سپس توسط متد SetConnectionString تنظیم گردد.


مدیریت سشن‌های رشته‌ی اتصالی جاری

پس از اینکه کاربر، در حین ورود مشخص کرد که از چه بانک اطلاعاتی قرار است استفاده کند و یا اینکه برنامه بر اساس اطلاعات ثبت شده‌ی او تصمیم‌گیری کرد که باید از کدام رشته‌ی اتصالی استفاده کند، نگهداری این رشته‌ی اتصالی نیاز به سشن دارد تا به ازای هر کاربر متصل به سایت منحصربفرد باشد. در مورد مدیریت سشن‌ها در برنامه‌های وب، از نکات مطرح شده‌ی در مطلب «مدیریت سشن‌ها در برنامه‌های وب به کمک تزریق وابستگی‌ها» استفاده خواهیم کرد:
using System;
using System.Threading;
using System.Web;
using EF_Sample07.DataLayer.Context;
using EF_Sample07.ServiceLayer;
using StructureMap;
using StructureMap.Web;
using StructureMap.Web.Pipeline;
 
namespace EF_Sample07.IoCConfig
{
    public static class SmObjectFactory
    {
        private static readonly Lazy<Container> _containerBuilder =
            new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);
 
        public static IContainer Container
        {
            get { return _containerBuilder.Value; }
        }
 
        public static void HttpContextDisposeAndClearAll()
        {
            HttpContextLifecycle.DisposeAndClearAll();
        }
 
        private static Container defaultContainer()
        {
            return new Container(ioc =>
            {
                // session manager setup
                ioc.For<ISessionProvider>().Use<DefaultWebSessionProvider>();
                ioc.For<HttpSessionStateBase>()
                   .Use(ctx => new HttpSessionStateWrapper(HttpContext.Current.Session));
 
                ioc.For<IUnitOfWork>()
                   .HybridHttpOrThreadLocalScoped()
                   .Use<Sample07Context>()
                    // Remove these 2 lines if you want to use a connection string named Sample07Context, defined in the web.config file.
                   .Ctor<string>("connectionString")
                   .Is(ctx => getCurrentConnectionString(ctx));
 
                ioc.For<ICategoryService>().Use<EfCategoryService>();
                ioc.For<IProductService>().Use<EfProductService>();
 
                ioc.For<ICategoryService>().Use<EfCategoryService>();
                ioc.For<IProductService>().Use<EfProductService>();
 
                ioc.Policies.SetAllProperties(properties =>
                {
                    properties.OfType<IUnitOfWork>();
                    properties.OfType<ICategoryService>();
                    properties.OfType<IProductService>();
                    properties.OfType<ISessionProvider>();
                });
            });
        }
 
        private static string getCurrentConnectionString(IContext ctx)
        {
            if (HttpContext.Current != null)
            {
                // this is a web application
                var sessionProvider = ctx.GetInstance<ISessionProvider>();
                var connectionString = sessionProvider.Get<string>("CurrentConnectionString");
                if (string.IsNullOrWhiteSpace(connectionString))
                {
                    // It's a default connectionString.
                    connectionString = "Database2012";
                    // this session value should be set during the login phase
                    sessionProvider.Store("CurrentConnectionStringName", connectionString);
                }
 
                return connectionString;
            }
            else
            {
                // this is a desktop application, so you can store this value in a global static variable.
                return "Database2012";
            }
        }
    }
}
در اینجا نحوه‌ی پویا سازی تامین رشته‌ی اتصالی را مشاهده می‌کنید. در مورد اینترفیس ISessionProvider و کلاس پایه HttpSessionStateBase پیشتر در مطلب «مدیریت سشن‌ها در برنامه‌های وب به کمک تزریق وابستگی‌ها» بحث شد.
نکته‌ی مهم این تنظیمات، قسمت مقدار دهی سازنده‌ی کلاس Context برنامه به صورت پویا توسط IoC Container جاری است. در اینجا هر زمانیکه قرار است وهله‌ای از Sample07Context ساخته شود، از سازنده‌ی دوم آن که دارای پارامتری به نام connectionString است، استفاده خواهد شد. همچنین مقدار آن به صورت پویا از متد getCurrentConnectionString که در انتهای کلاس تعریف شده‌است، دریافت می‌گردد.
در این متد ابتدا مقدار HttpContext.Current بررسی شده‌است. این مقدار اگر نال باشد، یعنی برنامه‌ی جاری یک برنامه‌ی دسکتاپ است و مدیریت رشته‌ی اتصالی جاری آن‌را توسط یک خاصیت Static یا Singleton تعریف شده‌ی در برنامه نیز می‌توان تامین کرد. از این جهت که در هر زمان، تنها یک کاربر در App Domain جاری برنامه‌ی دسکتاپ می‌تواند وجود داشته باشد و Singleton یا Static تعریف شدن اطلاعات رشته‌ی اتصالی، مشکلی را ایجاد نمی‌کند. اما در برنامه‌های وب، چندین کاربر در یک App Domain به سیستم وارد می‌شوند. به همین جهت است که مشاهده می‌کنید در اینجا از تامین کننده‌ی سشن، برای نگهداری اطلاعات رشته‌ی اتصالی جاری کمک گرفته شده‌است.

کلید این سشن نیز در این مثال مساوی CurrentConnectionStringName تعریف شده‌است. بنابراین در حین لاگین موفقیت آمیز کاربر، دو مرحله‌ی زیر باید طی شوند:
 sessionProvider.Store("CurrentConnectionString", "Sample07Context");
uow.SetConnectionString(WebConfigurationManager.ConnectionStrings[_sessionProvider.Get<string>("CurrentConnectionString")].ConnectionString);
ابتدا باید سشن CurrentConnectionStringName به بانک اطلاعاتی انتخابی کاربر تنظیم شود. برای نمونه در این مثال خاص، از نام رشته‌ی اتصالی مشخص شده‌ی در وب کانفیگ برنامه (مثال ابتدای بحث) به نام Sample07Context استفاده شده‌است.
سپس از متد SetConnectionString برای خواندن مقدار نام مشخص شده در سشن CurrentConnectionStringName کمک گرفته‌ایم. هرچند سازنده‌ی کلاس Context برنامه، هر دو حالت استفاده از نام رشته‌ی اتصالی و یا مقدار کامل رشته‌ی اتصالی را پشتیبانی می‌کند، اما خاصیت this.Database.Connection.ConnectionString تنها رشته‌ی کامل اتصالی را می‌پذیرد (بکار رفته در متد SetConnectionString).

تا اینجا کار پویا سازی انتخاب و استفاده از رشته‌ی اتصالی برنامه به پایان می‌رسد. هر زمانیکه قرار است Context برنامه توسط IoC Container نمونه سازی شود، به متد getCurrentConnectionString رجوع کرده و مقدار رشته‌ی اتصالی را از سشن تنظیم شده‌‌ای به نام CurrentConnectionStringName دریافت می‌کند. سپس از مقدار آن جهت مقدار دهی سازنده‌ی دوم کلاس Context استفاده خواهد کرد.


مدیریت migrations خودکار برنامه در حالت استفاده از چندین بانک اطلاعاتی

یکی از مشکلات کار با برنامه‌های چند دیتابیسی، به روز رسانی ساختار تمام بانک‌های اطلاعاتی مورد استفاده، پس از تغییری در ساختار مدل‌های برنامه است. از این جهت که اگر تمام بانک‌های اطلاعاتی به روز نشوند، کوئری‌های جدید برنامه که از خواص و فیلدهای جدید استفاده می‌کنند، دیگر کار نخواهند کرد. پویا سازی اعمال این تغییرات را می‌توان به صورت ذیل انجام داد:
using System;
using System.Data.Entity;
using System.Web;
using EF_Sample07.DataLayer.Context;
using EF_Sample07.IoCConfig;
 
namespace EF_Sample07.WebFormsAppSample
{
    public class Global : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            initDatabases();
        }
 
        private static void initDatabases()
        {
            // defined in web.config
            string[] connectionStringNames =
            {
                "Sample07Context",
                "Database2012"
            };
 
            foreach (var connectionStringName in connectionStringNames)
            {
                Database.SetInitializer(
                    new MigrateDatabaseToLatestVersion<Sample07Context, Configuration>(connectionStringName));
 
                using (var ctx = new Sample07Context(connectionStringName))
                {
                    ctx.Database.Initialize(force: true);
                }
            }
        }
 
        void Application_EndRequest(object sender, EventArgs e)
        {
            SmObjectFactory.HttpContextDisposeAndClearAll();
        } 
    }
}
نکته‌ی مهمی که در اینجا بکار گرفته شده‌است، مشخص سازی صریح سازنده‌ی شیء MigrateDatabaseToLatestVersion است. به صورت معمول در اکثر برنامه‌های تک دیتابیسی، نیازی به مشخص سازی پارامتر سازنده‌ی این کلاس نیست و در این حالت از سازنده‌ی بدون پارامتر کلاس Context برنامه استفاده خواهد شد. اما اگر سازنده‌ی آن‌را مشخص کنیم، به صورت خودکار از متد سازنده‌ای در کلاس Context استفاده می‌کند که پارامتر رشته‌ی اتصالی را به صورت پویا می‌پذیرد.
در این مثال خاص، متد initDatabases در حین آغاز برنامه فراخوانی شده‌است. منظور این است که اینکار در طول عمر برنامه تنها کافی است یکبار انجام شود و پس از آن است که EF Code first می‌تواند از رشته‌های اتصالی متفاوتی که به آن ارسال می‌شود، بدون مشکل استفاده کند. زیرا اطلاعات نگاشت کلاس‌های مدل برنامه به جداول بانک اطلاعاتی به این ترتیب است که کش می‌شوند و یا بر اساس کلاس Configuration به صورت خودکار به بانک اطلاعاتی اعمال می‌گردند.


کدهای کامل این مثال را که در حقیقت نمونه‌ی بهبود یافته‌ی مطلب «EF Code First #12» است، از اینجا می‌توانید دریافت کنید:
UoW-Sample
نظرات مطالب
تنظیم رشته اتصالی Entity Framework به بانک اطلاعاتی به وسیله کد

«به چه صورت جایگزین کنم»

مثل مثال بالا که از فایل کانفیگ گذاشته شد، قسمت provider connection string رو پیدا کن و جایگزینش کن با رشته اتصالی که به شما دادند.

«ازین روشی که گفتین اگه بخوام استفاده کنم»

این روش کاری به سناریوی شما نداره. برای جایی هست که برنامه قراره مثلا برای هر سال به یک دیتابیس مجزا وصل بشه. اول کار، کاربر باید انتخاب کنه که مثلا به دیتابیس سال 89 وصل بشه یا دیتابیس سال 92.

مطالب
مروری بر تاریخچه محدودیت حافظه مصرفی برنامه‌های ASP.NET در IIS

زمانیکه اولین نگارش ASP.NET‌ حدود 10 سال قبل منتشر شد،‌ تنها سیستم عاملی که از آن پشتیبانی می‌کرد، ویندوز سرور 2000 بود، تنها پروسه‌ی اجرایی آن aspnet_wp نام داشت و تنها معماری پشتیبانی شده هم X86 بود. به پروسه‌ی aspnet_wp محدودیت مصرف حافظه‌ای اعمال شده بود که در حین آغاز آن بر اساس مقدار قابل تغییر processModel memoryLimit محاسبه و اعمال می‌شد (تعریف شده در فایل ماشین کانفیگ). این عدد به صورت درصدی از ظرفیت RAM فیزیکی سیستم، قابل تعریف و به صورت پیش فرض به 60 درصد تنظیم شده بود. به این ترتیب این پروسه مجاز نبود تا تمام حافظه‌ی فیزیکی مهیا را مصرف کند و در صورت وجود نشتی حافظه‌ای در برنامه‌ای خاص، این پروسه امکان بازیابی مجدد حافظه را پیدا می‌کرد (recycling). همچنین یک مورد دیگر را هم باید در نظر داشت و آن هم وجود قابلیتی است به نام ASP.NET Cache است که امکان ذخیره سازی مقادیر اشیاء را در حافظه‌ی مصرفی این پروسه مهیا می‌سازد. هر زمان که میزان این حافظه‌ی مصرفی به حد نزدیکی از محدودیت تعریف شده برسد، این پروسه به صورت خودکار شروع به حذف آن‌ها خواهد کرد.
محدودیت 60 درصدی تعریف شده، برای سیستم‌هایی با میزان RAM کم بسیار مفید بود اما در سیستم‌هایی با میزان RAM بیشتر، مثلا 4 گیگ به 2.4GB حافظه مهیا (60 درصد حافظه فیزیکی سیستم) محدود می‌شد و همچنین باید در نظر داشت که میزان user mode virtual address space مهیا نیز تنها 2 گیگابایت بود. بنابراین هیچگاه استفاده مؤثری از تمام ظرفیت RAM مهیا صورت نمی‌گرفت و گاها مشاهده می‌شد که یک برنامه تنها با مصرف 1.5GB RAM می‌توانست پیغام OutOfMemoryException را صادر کند. در این حالت مطابق بررسی‌های صورت گرفته مشخص شد که اگر مقدار processModel memoryLimit به حدود 800 مگابایت تنظیم شود، بهترین عملکرد را برای سیستم‌های مختلف می‌توان مشاهده کرد.

با ارائه‌ی ویندوز سرور 2003 و همچنین ارائه‌ی نسخه‌ی 1.1 دات نت فریم ورک و ASP.NET ، این وضعیت تغییر کرد. پروسه‌ی جدید در اینجا w3wp نام دارد و این پروسه تعاریف مرتبط با محدودیت حافظه‌ی خود را از تنظیمات IIS دریافت می‌کند (قسمت Maximum Used Memory در برگه‌ی Recycling مربوط به خواص Application Pool مرتبط). متاسفانه این عدد به صورت پیش فرض محدودیتی ندارد و به ظاهر برنامه مجاز است تا حد امکان از حافظه‌ی مهیا استفاده کند. به همین جهت یکی از مواردی را که باید در نظر داشت، مقدار دهی Maximum Used Memory ذکر شده است. خصوصا اینکه در نگارش 1.1 ، تنظیمات میزان مصرف RAM مرتبط با ASP.NET Cache نیز با برنامه یکی است.

در نگارش 2.0 دات نت فریم ورک، تنظیمات مرتبط با ASP.NET cache از تنظیمات میزان RAM مصرفی یک برنامه‌ی ASP.NET جدا شد و این مورد توسط قسمت cache privateBytesLimit قابل تنظیم و مدیریت است (در فایل IIS Metabase و همچنین فایل web.config برنامه).

نکته!
اگر process memory limit و همچنین cache memory limit را تنظیم نکنید، باز به همان عدد 60 درصد سابق بازخواهیم گشت و این مورد به صورت خودکار توسط IIS محاسبه و اعمال می‌شود. البته محدودیت ذکر شده برای پروسه‌های 64 بیتی در این حالت بسیار بهتر خواهد بود. اگر هر دوی این‌ها را تنظیم کنید، عدد حداقل بکارگرفته شده، مبنای کار خواهد بود و اگر تنها یکی را تنظیم کنید ، این عدد به هر دو حالت اعمال می‌گردد. برای بررسی بهتر می‌توان به مقدار Cache.EffectivePrivateBytesLimit و Cache.EffectivePercentagePhysicalMemoryLimit مراجعه کرد.

و ... اکنون بهتر می‌توانید به این سؤال پاسخ دهید که «سرور ما بیشتر از 4 گیگ رم دارد و برنامه‌ی ASP.NET من الان فقط 850 مگ رم مصرف کرده (که البته این هم نشانی از عدم dispose صحیح منابع است یا عدم تعیین تقدم و تاخر و زمان منقضی شدن، حین تعریف اشیاء کش)، اما پیغام out of memory exception را دریافت می‌کنم. چرا؟!»


بنابراین ایجاد یک Application pool جدید به ازای هر برنامه‌ی ASP.NET امری است بسیار مهم زیرا:
- به این ترتیب هر برنامه‌ی ASP.NET در پروسه‌ای ایزوله از پروسه‌ی دیگر اجرا خواهد شد (این مساله از لحاظ امنیتی هم بسیار مهم است). در اینجا هر برنامه، از پروسه‌ی w3wp.exe مجزای خاص خود استفاده خواهد کرد (شبیه به مرورگرهایی که هر tab را در یک پروسه جدید اجرا می‌کنند).
- اگر پروسه‌ای به حد بالای مصرف حافظه‌ی خود رسید با تنظیمات انجام شده در قسمت recycling مرتبط با Application pool اختصاصی آن، به صورت خودکار کار بازیابی حافظه صورت می‌گیرد و این امر بر روی سایر برنامه‌ها تاثیر نخواهد داشت (کاربران سایر برنامه‌ها مدام شکایت نمی‌کنند که سشن‌ها پرید. کش خالی شد. زیرا در حالت وجود application pool اختصاصی به ازای هر برنامه، مدیریت حافظه برنامه‌ها از هم ایزوله خواهند بود)
- کرش صورت گرفته در یک برنامه به دلیل عدم مدیریت خطاها، بر روی سایر برنامه‌ها تاثیر منفی نخواهد گذاشت. (زمانیکه ASP.NET worker process به دلیل استثنایی مدیریت نشده خاتمه یابد بلافاصله و به صورت خودکار مجددا «وهله‌ی دیگری» از آن شروع به کار خواهد کرد؛ یعنی تمام سشن‌های قبلی از بین خواهند رفت؛ که در صورت ایزوله سازی ذکر شده، سایر برنامه‌ها در امان خواهند ماند؛ چون در پروسه ایزوله‌ی خود مشغول به کار هستند)
- با وجود application pool اختصاصی به ازای هر برنامه، می‌توان برای سایت‌های کم ترافیک و پرترافیک، زمان‌های recycling متفاوتی را اعمال کرد. به این ترتیب مدیریت حافظه‌ی بهتری قابل پیاده سازی می‌باشد. همچنین در این حالت می‌توان مشخص کرد کدام سایت از تعداد worker process بیشتر یا کمتری استفاده کند.
- کاربری که پروسه‌ی ASP.NET تحت آن اجرا می‌شود نیز همینجا تعریف می‌گردد. بنابراین به این ترتیب می‌توان به برنامه‌ای دسترسی بیشتر و یا کمتر داد، بدون تاثیر گذاری بر روی سایر برنامه‌های موجود.

نتیجه گیری:
- از IIS استفاده می‌کنید؟ آیا می‌دانید Application pool چیست؟
- آیا می‌دانید در صورت عدم مقدار دهی پارامترهای حافظه‌ی یک Application pool ، به صورت پیش فرض چند درصد از حافظه‌ی فیزیکی مهیا در اختیار شما است؟


برای مطالعه بیشتر:

مطالب
ASP.NET MVC #3

تهیه پیش‌نیازهای شروع به کار با ASP.NET MVC

در زمان نگارش این مطلب، نگارش نهایی ASP.NET MVC 3 در دسترس است و همچنین نگارش بتای 4 آن نیز قابل دریافت و نصب می‌باشد. بنابراین فعلا اساس را بر مبنای نگارشی قرار خواهیم داد که در محیط کاری قابل استفاده باشد.
ASP.NET MVC 3 پس از ارائه Visual Studio 2010، منتشر شد و VS.NET به صورت پیش فرض به همراه ASP.NET MVC 2 است. ساده‌ترین روش نصب ASP.NET MVC 3 بر روی VS 2010 استفاده از برنامه رایگانی است به نام Web Platform Installer. این برنامه را از این آدرس می‌توان دریافت کرد: http://microsoft.com/web/downloads
پس از دریافت آن حداقل دو راه برای نصب ASP.NET MVC 3 وجود دارد. یا گزینه‌ی نصب ASP.NET MVC 3 Tools Update را انتخاب کنید و یا سرویس پک یک VS 2010 را از طریق این برنامه یا جداگانه (بسته کامل و مستقل) دریافت و نصب نمائید. VS 2010 SP1 نیز به همراه ASP.NET MVC 3 است؛ همچنین IIS Express را که نسخه ساده شده IIS 7.5 مخصوص توسعه دهنده‌ها است، می‌توان با این نگارش یکپارچه کرد.


بنابراین به صورت خلاصه بهترین کار این است که سرویس پک یک VS 2010 را یکبار نصب نمائید. اگر این نصب از طریق برنامه Web Platform Installer باشد، به صورت خودکار IIS Express را هم انتخاب و نصب خواهد کرد. اگر فقط SP1 را به صورت مستقل دریافت کرده‌اید، حاوی IIS Express نیست و باید جداگانه آن‌را دریافت و نصب نمائید (^). البته نصب IIS Express در اینجا یک گزینه اختیاری است و الزامی نیست.



مروری بر ساختار یک پروژه ASP.NET MVC

پس از نصب پیش نیازها، امکان انتخاب یک پروژه وب ASP.NET MVC 3 در VS 2010 میسر خواهد شد:


در اینجا گزینه‌ی ASP.NET MVC 3 Web Application را انتخاب می‌کنیم. در صفحه بعدی که ظاهر می‌شود:


حالت Internet Application به همراه یک سری مدل و کنترلر از پیش نوشته شده جهت مدیریت ورود به سایت و ثبت نام در سایت است و حالت Empty تنها به همراه ساختار پیش فرض پوشه‌های یک پروژه ASP.NET MVC است.
فعلا جهت توضیحات اولیه بیشتر، گزینه‌ی Internet Application و نوع View Engine را هم ASPX انتخاب می‌کنیم. کار View Engine، رندر یک View به شکل HTML و ارائه نهایی اطلاعات آن به کاربر است. این نوع‌های متفاوت هم فقط در Syntax تفاوت دارند (به آن templating language هم گفته می‌شود). نوع ASPX همان Syntax متداول قدیمی ASP.NET را تداعی می‌کند و نوع Razor به صورت اختصاصی برای ASP.NET MVC تهیه شده است.
باید در نظر داشت که گزینه مرجح از نگارش 3 به بعد، Razor است (البته این هم سلیقه‌ای است. اگر هیچکدام از این دو را هم نخواهید استفاده کنید مشکلی نیست! می‌شود کلا آن را عوض کرد). هدفم هم از انتخاب ASPX نمایش یک سری ریزه کاری است که شاید برای برنامه نویس‌های ASP.NET Web forms جالب باشد. این موارد را در حالت انتخاب Razor به این وضوح مشاهده نخواهید کرد و محیط خیلی ساده شده است.


همانطور که ملاحظه می‌کنید این فریم ورک یک سری پوشه پیش فرض را توصیه می‌کند. بدیهی است که ضرورتی ندارد تا پوشه Models یا پوشه Controllers حتما در همین پروژه قرار داشته باشند؛ چون زمانیکه پروژه کامپایل شد، محل این پوشه بندی‌ها آنچنان اهمیتی ندارد.
نکته جالب در این تصویر، فایل Site.Master است. بله، این فایل شبیه به همان فایل master page موجود در ASP.NET Web form است که قالب کلی سایت را به همراه داشته و سایر صفحات، قالب خود را از آن به ارث می‌برند. حتی تگ runat=server هم به وضوح در این فایل، در چندین جای آن قابل مشاهده است. تنها تفاوت آن نداشتن فایل code behind است. asp:ContentPlaceHolder نیز در آن تعریف شده است. خلاصه این محیط جدید به معنای دور ریختن تمام آنچیزی که در Web forms وجود دارد نیست. برای نمونه اگر فایل ChangePassword.aspx موجود در پوشه Account را باز کنید، باز هم همان asp:Content معروف به همراه تگ runat=server قابل مشاهده است. برای مثال این محتوای صفحه Error.aspx پیش فرض آن است:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>" %>

<asp:Content ID="errorTitle" ContentPlaceHolderID="TitleContent" runat="server">
Error
</asp:Content>

<asp:Content ID="errorContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Sorry, an error occurred while processing your request.
</h2>
</asp:Content>

اگر از قسمت Inherits آن صرفنظر کنیم، «هیچ» تفاوتی با ASP.NET Web forms ندارد؛ علت هم به این بر می‌گردد که موتوری که Web forms و MVC از آن استفاده می‌کنند، یکی است. هر دو بر فراز موتور ASP.NET معنا پیدا خواهند کرد.


قرار دادهای پوشه‌های پیش فرض یک پروژه ASP.NET MVC

  • پوشه Controllers حاوی کلاس‌های کنترلری است که درخواست‌های رسیده را مدیریت می‌کنند.
  • پوشه Models حاوی کلاس‌هایی است که اشیاء تجاری و همچنین کار با اطلاعات را تعریف و مدیریت می‌کنند.
  • در پوشه Views، فایل‌های قالب‌های رابط کاربری که مسئول ارائه خروجی به کاربر هستند قرار می‌گیرند. همچنین مطابق قرارداد دیگری، اگر نام کنترلر ما مثلا ProductController باشد (با توجه به اینکه نام کلاس آن هم مطابق قرارداد، مختوم به کلمه Controller است)، فایل‌های Viewهای مرتبط با آن در پوشه Views/Product قرار خواهند گرفت.
  • در پوشه Scripts،‌ فایل‌های جاوا اسکریپت مورد استفاده در سایت قرار خواهند گرفت.
  • پوشه Content محل قرارگیری فایل‌های CSS و تصاویر است.
  • پوشه App_Data جایی است که فایل‌هایی با قابلیت read/write در آن قرار می‌گیرند (و باید دقت داشت که فقط همینجا هم باید قرار گیرند و گرنه این نوشتن‌ها در مکان‌های متفرقه، ممکن است سبب ری استارت شدن برنامه شوند:(^)).