مطالب
امکان استفاده از یک هارد SSD بجای RAM در SQL Server 2014
Buffer Pool یکی از مصرف کنندگان اصلی حافظه در SQL Server است. برای مثال زمانیکه اطلاعاتی را از بانک اطلاعاتی دریافت می‌کنید، این داده‌ها در Buffer Pool کش می‌شوند. همچنین SQL Server اطلاعات کلیه Execution Plans را نیز در Plan Cache که جزئی از Buffer Pool است، برای استفاده‌ی مجدد نگهداری می‌کند. هر چقدر حافظه‌ی فیزیکی سرور شما بیشتر باشد، مقدار Buffer Pool نیز به همین میزان افزایش خواهد یافت که البته حداکثر آن‌را می‌توان در تنظیمات حافظه‌ی سرور محدود کرد (Max Server Memory setting).
در دنیای واقعی میزان حافظه‌ی فیزیکی سرورها محدود است. در SQL Server 2014 راه حلی برای این مشکل تحت عنوان Buffer Pool Extensions ارائه شده‌است که محل قرارگیری آن‌را در تصویر ذیل مشاهده می‌کنید:


Buffer Pool Extensions از یک فایل ساده که به آن Extension File نیز گفته می‌شود، تشکیل شده‌است و امکان ذخیره سازی آن بر روی هاردهای سریعی مانند SSD Driveها میسر است. این فایل، ساختاری را همانند page file، در سیستم عامل ویندوز دارد. در این حالت بجای اضافه کردن RAM بیشتر به سرور، یک Extension File را می‌توان بکار گرفت. هر زمان که Buffer Pool اصلی تحت فشار قرار گیرد (به میزان حافظه‌ای بیش از حافظه‌ی فیزیکی سرور نیاز باشد)، از این افزونه‌ی فایلی استفاده خواهد شد.
اطلاعات جزئیات Buffer Pool را توسط کوئری ذیل می‌توان بدست آورد:
 Select * from sys.dm_os_buffer_descriptors


نحوه‌ی فعال سازی و تنظیم Buffer Pool Extensions

قبل از هر کاری بهتر است وضعیت افزونه‌ی Buffer pool را بررسی کرد:
 select * from sys.dm_os_buffer_pool_extension_configuration


همانطور که ملاحظه می‌کنید، در حالت پیش فرض غیرفعال است.
سپس یک فایل یک گیگابایتی را به عنوان افزونه‌ی Buffer pool ایجاد می‌کنیم.
 ALTER SERVER CONFIGURATION
SET BUFFER POOL EXTENSION ON
 (FILENAME = 'd:\BufferPoolExt.BPE', SIZE = 1GB);
توصیه شده‌است که این فایل را در یک درایور پر سرعت SSD قرار دهید؛ ولی محدودیتی از لحاظ محل قرارگیری ندارد (هر چند به نظر فقط در حالتیکه از SSD Drive استفاده شود واقعا کار می‌کند).
اینبار اگر کوئری اول را اجرا کنیم، چنین خروجی قابل مشاهده است:


این فایل به صورت خودکار در حین ری‌استارت یا خاموش شدن سرور، حذف شده و با راه اندازی مجدد آن، باز تولید خواهد شد.


تغییر اندازه‌ی افزونه‌ی Buffer pool

اگر سعی کنیم، یک گیگابایت را مثلا به 10 گیگابایت افزایش دهیم:
 ALTER SERVER CONFIGURATION
SET BUFFER POOL EXTENSION ON
 (FILENAME = 'd:\BufferPoolExt.BPE', SIZE = 10GB);
با خطای ذیل مواجه خواهیم شد:
 Could not change the value of the 'BPoolExtensionPath' property
برای رفع این مشکل، ابتدا باید افزونه‌ی Buffer pool را غیرفعال کرد:
 ALTER SERVER CONFIGURATION
SET BUFFER POOL EXTENSION OFF
سپس می‌توان مجددا اندازه و یا مسیر دیگری را مشخص کرد. بهتر است اندازه‌ی این فایل را حدود 16 برابر حداکثر میزان حافظه‌ی سرور (Max Server Memory) تعیین کنید.
همچنین توصیه شده‌است که پس از غیرفعال کردن این افزونه، بهتر است یکبار instance جاری را ری استارت کنید.


چه زمانی بهتر است از افزونه‌ی Buffer pool استفاده شود؟
در محیط‌های read-heavy OLTP، استفاده از یک چنین افزونه‌ای می‌تواند میزان کارآیی و پاسخگویی سیستم را به شدت افزایش دهد (تا 50 درصد).


سؤال: آیا غیرفعال کردن افزونه‌ی Buffer pool سبب از دست رفتن اطلاعات می‌شود؟
خیر. BPE، تنها clean pages را در خود ذخیره می‌کند؛ یعنی تنها اطلاعاتی که Commit شده‌اند در آن حضور خواهند داشت و در این حالت حذف آن یا ری استارت کردن سرور، سبب از دست رفتن اطلاعات نخواهند شد.


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

Buffer Pool Extension
SQL Server 2014 Buffer Pool Extensions
Do you require a SSD to use the Buffer Pool Extension feature in SQL Server 2014
Buffer Pool Extensions in SQL Server 2014
SQL Server 2014 – Buffer Pool Extension
نظرات مطالب
خواندنی‌های 17 فروردین
سلام،
- خیر. اگر قصد تست آن‌را دارید بهتر است یک VPC نصب کنید و بعد روی آن ویندوز 2003 بریزید و شیرپوینت را نصب کنید. این روش توصیه شده است.
- روی ویستا با یک سری ترفند می‌شود:
http://community.bamboosolutions.com/blogs/bambooteamblog/archive/2008/05/21/how-to-install-windows-sharepoint-services-3-0-sp1-on-vista-x64-x86.aspx
مطالب
آشنایی با Contained Databases در SQL Server 2012
مقدمه:
بعضی وقت‌ها به هر دلیلی لازم است پایگاه داده برنامه هایمان را به سرور دیگری انتقال دهیم . برخلاف Oracle که انتقال پایگاه داده به سرور دیگر کار مشکل و تخصصی می‌باشد در SQL Server براحتی با یک Detach و Attach مجدد این انتقال انجام خواهد شد. اما اطلاعات Logins در پایگاه داده Master  سرور ذخیره شده است و با فایل پایگاه داده انتقال نمی‌یابد و به هیمن خاطر برخی مواقع پس از Attach پایگاه داده با پیغام The database is not accessible یا Cannot open user default database مواجع خواهیم شد که مشکل بخاطر همین نبود کاربران سرور قبلی و Owner متفاوت پایگاه داده می‌باشد که با استفاده از رویه ذخیره شده sp_change_users_login این مشکل قابل حل می‌باشد اما در SQL Server 2012 راحل بهتری فراهم شده است.

Contained Databases در Sql Server 2012
یکی از امکانات اضافه شده در SQL Server 2012  امکان Contained Databases  هست که برای حل همین مشکل و رفع کامل وابستی پایگاه داده با Instance نصب شده و خاصیت قابل حمل کامل database‌ها می‌باشد. در ادامه نحوه استفاده از این امکان را بررسی خواهیم کرد:
  1. فعال کردن خاصیت Contained Databases 

    قبل از استفاده از Contained Databases می بایست این امکان را فعال کرد. برای این کار می‌توانید از SQL Server Management Studio یا T-SQL commands استفاده نمایید. بر روی نام Instance راست کلیت کنید و گزینه Properties را انتخاب نمایید. از گزینه Advanced که در شکل زیر مشاهده می‌نمایید خاصیت Enable Contained Databases را بر روی True قرار دهید.

    یا می‌توایند از sp_configure این کار را انجام دهید.دستورات زیر این موضوع را نشان می‌دهد.
    sp_configure 'show advanced options',1
    GO
    RECONFIGURE WITH OVERRIDE
    GO
    sp_configure 'contained database authentication',1
    GO
    RECONFIGURE WITH OVERRIDE
    GO

  2. ایجاد یا تغییر یک پایگاه داده از نوع Contained Databases

    برای ایجاد یک پایگاه داده با این خاصیت یا تغییر پایگاه داده موجود کافیست مقدار گزینه Containment type را بر روی Partial قرار دهید. برای پایگاه داده موجود از پنجره Properties پایگاه داده صفحه Options را انتخاب کنید.


  3. ایجاد یک کاربر برای پایگاه داده Contained Databases

    برای تعریف یک کاربر در سطح پایگاه داده پوشه Security پایگاه داده خود را باز کنید بر روی پوشه Users راست کیلک و گزینه New User را انتخاب نمایید از گزینه User type که در شکل زیر نشان داده شده است SQL user with password را انتخاب نمایید و نام کاربر و رمز عبور و تکرار آن را وارد نمایید. کاربر ایجاد شده در سطح پایگاه داده می‌باشد و با انتقال به سرور دیگر نیر قابل دسترسی می‌باشد.

  4.  اتصال به پایگاه داده Contained Databases

    برای اتصال به پایگاه داده کافیست در حالت SQL Server Authentication نام کاربری و رمز عبور جدید را وارد و گزینه Options  را انتخاب و از برگه Additional Connection Parameters نام پایگاه داده مورد نظر را مانند شکل زیر وارد نمایید پس از ورود تنها پایگاه داده خود را مشاهده می‌نمایید. یکی از کاربرهای این قابلیت برای مدیران سرور پایگاه داده می‌باشد که بدون استفاده از مجوز sysadmin  به کاربران اجازه دسترسی را می‌دهد.



مطالب
کوئری نویسی در EF Core - قسمت اول - تشکیل بانک اطلاعاتی و مقدار دهی اولیه‌ی آن
عموم کسانیکه برای بار اول با LINQ آشنا می‌شوند، مشکل ترجمه‌ی کوئری‌های قبلی SQL خود را به آن دارند. به همین جهت پس از چند سعی و خطا ترجیح می‌دهند تا از ORMها استفاده نکنند؛ چون در کوئری نویسی با آن‌ها مشکل دارند. در این سری، تمام مثال‌های سایت PostgreSQL Exercises با EF Core و LINQ to Entities آن پیاده سازی خواهند شد تا بتواند به عنوان راهنمایی برای تازه‌کاران مورد استفاده قرار گیرد.


بررسی ساختار بانک اطلاعاتی تمرین‌های سایت PostgreSQL Exercises

بانک اطلاعاتی مثال‌های سایت PostgreSQL Exercises از سه جدول با مشخصات زیر تشکیل می‌شود:

جدول کاربران
 CREATE TABLE cd.members
    (
       memid integer NOT NULL, 
       surname character varying(200) NOT NULL, 
       firstname character varying(200) NOT NULL, 
       address character varying(300) NOT NULL, 
       zipcode integer NOT NULL, 
       telephone character varying(20) NOT NULL, 
       recommendedby integer,
       joindate timestamp not null,
       CONSTRAINT members_pk PRIMARY KEY (memid),
       CONSTRAINT fk_members_recommendedby FOREIGN KEY (recommendedby)
            REFERENCES cd.members(memid) ON DELETE SET NULL
    );
هر کاربر در اینجا به همراه یک ID و آدرس است. همچنین به همراه اطلاعات کاربری که او را توصیه کرده‌است (یک جدول خود ارجاع دهنده‌است).


جدول امکانات قابل ارائه‌ی به کاربران
   CREATE TABLE cd.facilities
    (
       facid integer NOT NULL, 
       name character varying(100) NOT NULL, 
       membercost numeric NOT NULL, 
       guestcost numeric NOT NULL, 
       initialoutlay numeric NOT NULL, 
       monthlymaintenance numeric NOT NULL, 
       CONSTRAINT facilities_pk PRIMARY KEY (facid)
    );
در این جدول، امکاناتی مانند «زمین تنیس» و امثال آن ثبت می‌شوند؛ به همراه اطلاعاتی مانند هزینه‌ی اجاره‌ی آن توسط کاربران و یا مهمان‌ها که این دو هزینه، با هم متفاوت هستند. همچنین اطلاعاتی مانند هزینه‌ی راه‌اندازی اولیه‌ی آن‌ها، به همراه هزینه‌ی نگهداری ماهیانه‌ی هر کدام از امکانات نیز ثبت می‌شوند؛ تا در آینده بتوان یک سری محاسبات مالی را نیز در مورد امکانات مهیای مجموعه انجام داد تا مشخص شود که آیا برای مثال داشتن مجموعه‌ای خاص، مقرون به صرفه هست یا خیر.


جدول سوابق استفاده‌ی کاربران از امکانات مجموعه
CREATE TABLE cd.bookings
    (
       bookid integer NOT NULL, 
       facid integer NOT NULL, 
       memid integer NOT NULL, 
       starttime timestamp NOT NULL,
       slots integer NOT NULL,
       CONSTRAINT bookings_pk PRIMARY KEY (bookid),
       CONSTRAINT fk_bookings_facid FOREIGN KEY (facid) REFERENCES cd.facilities(facid),
       CONSTRAINT fk_bookings_memid FOREIGN KEY (memid) REFERENCES cd.members(memid)
    );
در این جدول با ثبت ID کاربر و امکاناتی را که درخواست داده، سوابق رزرو آن‌ها نگهداری می‌شوند.
هر رزرو کردن مکان و امکاناتی در این مجموعه، «نیم ساعته» است. بنابراین Slots در اینجا به معنای تعداد نیم ساعت‌های رزرو کردن یک مکان خاص است؛ که به آن «half hour slots» نیز گفته می‌شود و زمان شروع این رزرو نیز ثبت می‌شود.


تبدیل ساختار بانک اطلاعاتی سایت PostgreSQL Exercises به EF Core Code First


در این دیاگرام، دیتابیس متشکل از سه جدول یاد شده را ملاحظه می‌کنید. برای تبدیل آن‌ها به موجودیت‌های EF Core، می‌توان به صورت زیر عمل کرد:

موجودیت کاربران

namespace EFCorePgExercises.Entities
{
    public class Member
    {
        public int MemId { set; get; }

        public string Surname { set; get; }

        public string FirstName { set; get; }

        public string Address { set; get; }

        public int ZipCode { set; get; }

        public string Telephone { set; get; }

        public virtual ICollection<Member> Children { get; set; }
        public virtual Member Recommender { set; get; }
        public int? RecommendedBy { set; get; }

        public DateTime JoinDate { set; get; }

        public virtual ICollection<Booking> Bookings { set; get; }
    }
}
خواص این کلاس دقیقا بر اساس فیلدهای جدول کاربران مثال‌های سایت تهیه شده‌است. تنها تفاوت آن، داشتن خواص راهبری (navigation properties) مانند Children، Member و Bookings است که نوع روابط این موجودیت را با سایر موجودیت‌ها مشخص می‌کنند:
- خاصیت‌های Children و Recommender برای تعریف رابطه‌ی «خود ارجاعی» اضافه شده‌اند. در اینجا هر کاربر می‌تواند توسط کاربر دیگری توصیه شده باشد.
- خاصیت Bookings برای بیان رابطه‌ی یک به چند با موجودیت Booking، تعریف شده‌است؛ هر یک کاربر می‌تواند به هر تعدادی رزرو امکانات داشته باشد.


موجودیت Facility

namespace EFCorePgExercises.Entities
{
    public class Facility
    {
        public int FacId { set; get; }

        public string Name { set; get; }

        public decimal MemberCost { set; get; }

        public decimal GuestCost { set; get; }

        public decimal InitialOutlay { set; get; }

        public decimal MonthlyMaintenance { set; get; }

        public virtual ICollection<Booking> Bookings { set; get; }
    }
}
- در این جدول، خواص از نوع پولی، توسط نوع decimal معرفی شده‌اند. برای این موارد هیچگاه از double و یا float استفاده نکنید؛ اطلاعات بیشتر.
- خاصیت راهبری Bookings، بیانگر رابطه‌ی یک به چند هرکدام از امکانات مجموعه با تعداد بار و سوابق رزرو شدن آن‌ها است.


موجودیت Booking

namespace EFCorePgExercises.Entities
{
    public class Booking
    {
        public int BookId { set; get; }

        public int FacId { set; get; }
        public virtual Facility Facility { set; get; }

        public int MemId { set; get; }
        public virtual Member Member { set; get; }

        public DateTime StartTime { set; get; }

        public int Slots { set; get; }
    }
}
در جدول ثبت وقایع این مجموعه، اطلاعات کاربر و اطلاعات امکانات درخواستی توسط او ثبت می‌شوند. به همین جهت دو خاصیت راهبری Facility و Member نیز به ازای هر کدام از این Idها تعریف شده‌اند. وجود آن‌ها، جوین نویسی را در آینده بسیار ساده خواهند کرد.


تنظیمات هر کدام از موجودیت‌ها و روابط بین آن‌ها در EF Core Code First

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

تنظیمات موجودیت کاربران

namespace EFCorePgExercises.Entities
{
    public class MemberConfiguration : IEntityTypeConfiguration<Member>
    {
        public void Configure(EntityTypeBuilder<Member> builder)
        {
            builder.HasKey(member => member.MemId);
            builder.Property(member => member.MemId).IsRequired().UseIdentityColumn(seed: 0, increment: 1);

            builder.Property(member => member.Surname).HasMaxLength(200).IsRequired();
            builder.Property(member => member.FirstName).HasMaxLength(200).IsRequired();
            builder.Property(member => member.Address).HasMaxLength(300).IsRequired();
            builder.Property(member => member.ZipCode).IsRequired();
            builder.Property(member => member.Telephone).HasMaxLength(20).IsRequired();

            builder.HasIndex(member => member.RecommendedBy);
            builder.HasOne(member => member.Recommender)
                    .WithMany(member => member.Children)
                    .HasForeignKey(member => member.RecommendedBy);

            builder.Property(member => member.JoinDate).IsRequired();

            builder.HasIndex(member => member.JoinDate).HasName("IX_JoinDate");
            builder.HasIndex(member => member.RecommendedBy).HasName("IX_RecommendedBy");
        }
    }
}
- در اینجا بر اساس تعاریفی که در ابتدای بحث مشاهده کردید، برای مثال طول هر کدام از فیلدهای رشته‌ای متناظر تعریف شده‌اند.
- سپس نحوه‌ی تعریف رابطه‌ی خود راجاعی این موجودیت را مشاهده می‌کنید.
- دو ایندکس هم در اینجا تعریف شده‌اند که جزو اطلاعات موجود در فایل SQL این سری از مثال‌ها هستند.

نکته‌ی مهم: در اینجا یک UseIdentityColumn(seed: 0, increment: 1) را نیز مشاهده می‌کنید که شاید برای شما تازگی داشته باشد. فیلد ID تمام جداول این مجموعه برخلاف معمول که از 1 شروع می‌شود، از صفر شروع می‌شود و ID مساوی صفر را برای کاربران مهمان درنظر گرفته‌است. روش تعریف چنین تنظیم خاصی را توسط متد UseIdentityColumn و دو پارامتر آن در اینجا مشاهده می‌کنید. این ID مساوی صفر، نکات خاصی را هم در حین ثبت اطلاعات اولیه‌ی هر جدول، به همراه دارد که در ادامه بررسی خواهد شد.


تنظیمات موجودیت امکانات مجموعه

namespace EFCorePgExercises.Entities
{
    public class FacilityConfiguration : IEntityTypeConfiguration<Facility>
    {
        public void Configure(EntityTypeBuilder<Facility> builder)
        {
            builder.HasKey(facility => facility.FacId);
            builder.Property(facility => facility.FacId).IsRequired().UseIdentityColumn(seed: 0, increment: 1);

            builder.Property(facility => facility.Name).HasMaxLength(100).IsRequired();

            builder.Property(facility => facility.MemberCost).IsRequired().HasColumnType("decimal(18, 6)");

            builder.Property(facility => facility.GuestCost).IsRequired().HasColumnType("decimal(18, 6)");

            builder.Property(facility => facility.InitialOutlay).IsRequired().HasColumnType("decimal(18, 6)");

            builder.Property(facility => facility.MonthlyMaintenance).IsRequired().HasColumnType("decimal(18, 6)");
        }
    }
}
تنها نکته‌ی مهم این تنظیمات، ذکر دقت نوع decimal است؛ بدون تنظیم آن، EF Core در حین اجرای Migrations، اخطاری را صادر می‌کند.


تنظیمات موجودیت سوابق رزرو‌های امکانات مجموعه

namespace EFCorePgExercises.Entities
{
    public class BookingConfiguration : IEntityTypeConfiguration<Booking>
    {
        public void Configure(EntityTypeBuilder<Booking> builder)
        {
            builder.HasKey(booking => booking.BookId);
            builder.Property(booking => booking.BookId).IsRequired().UseIdentityColumn(seed: 0, increment: 1);

            builder.Property(booking => booking.FacId).IsRequired();
            builder.HasOne(booking => booking.Facility)
                    .WithMany(facility => facility.Bookings)
                    .HasForeignKey(booking => booking.FacId);

            builder.Property(booking => booking.MemId).IsRequired();
            builder.HasOne(booking => booking.Member)
                    .WithMany(member => member.Bookings)
                    .HasForeignKey(booking => booking.MemId);

            builder.Property(booking => booking.StartTime).IsRequired();

            builder.Property(booking => booking.Slots).IsRequired();

            builder.HasIndex(booking => new { booking.MemId, booking.FacId }).HasName("IX_memid_facid");
            builder.HasIndex(booking => new { booking.FacId, booking.StartTime }).HasName("IX_facid_starttime");
            builder.HasIndex(booking => new { booking.MemId, booking.StartTime }).HasName("IX_memid_starttime");
            builder.HasIndex(booking => booking.StartTime).HasName("IX_starttime");
        }
    }
}
روابط یک به چند بین امکانات و رزروها و کاربران و رزروها، در تنظیمات فوق بیان شده‌اند و ذکر آن‌ها در یک سمت رابطه کافی است.


ایجاد Context و معرفی موجودیت‌ها و تنظیمات آن‌ها

در ادامه توسط ApplicationDbContext که از DbContext ارث‌بری می‌کند، سه موجودیت تعریف شده را در معرض دید EF Core قرار می‌دهیم:
namespace EFCorePgExercises.DataLayer
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions options)
            : base(options)
        {
        }

        public DbSet<Member> Members { get; set; }

        public DbSet<Booking> Bookings { get; set; }

        public DbSet<Facility> Facilities { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.ApplyConfigurationsFromAssembly(typeof(MemberConfiguration).Assembly);
        }
    }
}
همچنین تمام تنظیماتی را که تعریف کردیم، توسط یک سطر ApplyConfigurationsFromAssembly می‌توان از اسمبلی دربرگیرنده‌ی آن‌ها خواند و به Context اضافه کرد.


اجرای Migrations جهت تشکیل ساختار بانک اطلاعاتی

اکنون که موجودیت‌ها، روابط بین آن‌ها و Context برنامه مشخص شدند، می‌توان با اجرای دستوارت زیر، سبب تولید کدهای Migration شد که با اجرای آن‌ها، بانک اطلاعاتی متناظری به صورت خودکار تولید می‌شود:
dotnet tool install --global dotnet-ef --version 3.1.6
dotnet tool update --global dotnet-ef --version 3.1.6
dotnet build
dotnet ef migrations add Init --context ApplicationDbContext
در نگارش EF Core 3x، نیاز است ابزار dotnet-ef را به صورت جداگانه‌ای دریافت و یا به روز رسانی کرد (دو دستور اول) و سپس دستور dotnet ef را اجرا نمود.


مقدار دهی اولیه‌ی بانک اطلاعاتی

سایت PostgreSQL Exercises به همراه فایل SQL ایجاد جداول و مقدار دهی اولیه‌ی آن‌ها نیز هست. شاید عنوان کنید که چرا این اطلاعات به صورت متدهای HasData، به تنظیمات موجودیت‌ها اضافه نشدند؟ علت آن به همان ID مساوی صفر بر می‌گردد! در حین استفاده‌ی از متد HasData نمی‌توانید ID ای داشته باشید که مقدار آن با مقدار پیش‌فرض آن نوع، یکی باشد. برای مثال مقدار پیش فرض int، مساوی صفر است. به همین جهت حتی با تنظیم UseIdentityColumn(seed: 0, increment: 1)، اجازه‌ی ثبت Id مساوی صفر را نمی‌دهد؛ چون نمی‌تواند تشخیص دهد که این مقدار، یک مقدار صریح است یا خیر (^). بنابراین مجبور هستیم تا آن‌ها را به صورت معمولی ثبت کنیم:
context.Facilities.Add(new Facility { Name = "Tennis Court 1", MemberCost = 5, GuestCost = 25, InitialOutlay = 10000, MonthlyMaintenance = 200 });
// مابقی موارد
context.SaveChanges();
در این حالت، اول رکورد ثبت شده، Id مساوی صفر را خواهد داشت و مابقی هم یکی یکی افزایش می‌یابند.
این روش برای ثبت اطلاعات Facilities و Booking کار می‌کند؛ اما ... چون Idهای کاربران پشت سر هم نیست و بین آن‌ها فاصله وجود دارد، دیگر نمی‌توان از روش فوق استفاده کرد و نیاز است بتوان مقدار Id را به صورت صریحی تعیین کرد که این مورد نکات جالبی را به همراه دارد:
- در حین کار با SQL Server نیاز است دستور SET IDENTITY_INSERT Members ON را در ابتدای کار، فراخوانی کرد تا بتوان مقدار فیلد ID خود افزایش دهنده را به صورت دستی مقدار دهی کرد.
- در هر زمان، فقط یک جدول و فقط یک سشن (یک اتصال) را می‌توان توسط IDENTITY_INSERT در حالت ثبت و مقدار دهی ID آن قرار داد.
- EF Core، به ازای هر batch اطلاعاتی که ثبت می‌کند، یکبار اتصال را باز و بسته می‌کند. این مورد سبب می‌شود که فراخوانی ExecuteSqlCommand با دستور یاد شده، تاثیری نداشته باشد. برای رفع این مشکل باید یک تراکنش را باز کرد، تا اتصال به بانک اطلاعاتی، در طول آن باز باقی بماند.

در اینجا برای ثبت کاربر با ID مساوی صفر، باز هم می‌توان به صورت معمولی عمل کرد:
context.Members.Add(new Member { ... });
context.SaveChanges(); // For id = 0 = Int's CLR Default Value!
چون اولین رکورد است، ID آن مساوی صفر خواهد شد. برای مابقی از روش ویژه‌ی زیر استفاده می‌کنیم:
using (var transaction = context.Database.BeginTransaction())
{
    try
    {
        context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT Members ON");

        context.Members.Add(new Member { ... });
        // مابقی موارد

        context.SaveChanges();

        transaction.Commit();
    }
    catch
    {
        transaction.Rollback();
        throw;
    }
    finally
    {
        context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT Members OFF");
    }
}
ابتدا یک تراکنش را بر روی context ایجاد می‌کنیم تا اتصال باز شده، در طول آن ثابت باقی بماند. اکنون اجرای دستور SET IDENTITY_INSERT، مؤثر واقع می‌شود. سپس تمام رکوردها را با ذکر ID صریح آن‌ها به context اضافه کرد، آن‌ها را ذخیره نموده و تراکنش را Commit می‌کنیم. در پایان کار هم باید دستور خاموش کردن SET IDENTITY_INSERT صادر شود.


کدهای کامل موجودیت‌های این قسمت به همراه تنظیمات آن‌ها
کدهای کامل تنظیم Context و همچنین مقدار دهی اولیه‌ی بانک اطلاعاتی
مطالب
بررسی دستور Truncate Table و Delete

قبل از اینکه این موضوع را بررسی کنیم باید با دستور Truncate و Delete آشنا شویم.

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

DELETE FROM table_name
WHERE some_column=some_value
برای مثال در صورتیکه بخواهیم مشتریانی را حذف کنیم که کشور (Country) آنها USA است باید از دستور زیر استفاده کنیم.
DELETE FROM Customers
WHERE Country=’USA’
GO
با اجرای این دستور هر رکوردی که در شرط مربوطه صدق کند حذف خواهد شد. (خوب این رو که همه می‌دانند)

اما نکته مهمی که دستور Delete دارد این است کار این دستور به شکل Transactional می‌باشد. یعنی یا کلیه رکوردهایی که Country آنها USA است حذف می‌شود و یا هیچکدام از آنها. پس اگر شما 200000 رکورد داشته باشید که در این شرط صدق کند اگر وسط کار Delete (البته اگر عملیات حذف طولانی باشد) منصرف شوید می‌توانید با Cancel کردن این دستور عملیات Rollback Transaction  را به خودکار توسط SQL Server داشته باشید. در صورتیکه عملیات Cancel را انجام دهید SQL Server از Log File برای بازگرداندن مقادیر حذف شده استفاده خواهد کرد.

اما نکته دیگری که دستور Delete دارد این است که این دستور Log کلیه رکوردهایی را که قرار است حذف کند در Log File می‌نویسد. این Log شامل اصل رکورد، تاریخ و زمان حذف، نام کاربر و... می‌باشد. شاید الان متوجه شوید که دستور Delete چرا در برخی از مواقع که قرار است حجم زیادی از اطلاعات را حذف نماید به کٌندی این کار را انجام می‌دهد. (چون باید Log رکوردهای حذف شده در Log File نوشته شود.)

بررسی دستور Truncate Table: 
Truncate در لغت به معنی بریدن و کوتاه کردن می‌باشد. با استفاده دستور Truncate Table می‌توانید محتوای کلیه رکوردهای موجود در یک جدول را در کسری از ثانیه حذف کنید.
نکته مهمی که باید درباره دستور Truncate Table بدانید این است که تاثیر استفاده از این دستور بر روی کلیه رکوردها بوده و به هیچ عنوان نمی‌توان برای این دستور شرط (Where Clause) اعمال نمود.

 

شکل کلی استفاده از این دستور به صورت زیر می‌باشد.

TRUNCATE TABLE  table_name
GO
برای مثال اگر بخواهیم کلیه رکوردهای موجود در جدول Customers را حذف نمایید کافی است با استفاده از این دستور اینکار را انجام دهید
TRUNCATE TABLE Customers
GO
با اجرای این دستور در کسری از ثانیه کلیه رکوردهای جدول Customers حذف خواهد شد. (بهتر است از این دستور زمانی استفاده کنید که بخواهید ساختار جدول شما باقی بماند)

اما  در مورد دستور Truncate Table و  Delete باید به نکات زیر توجه کنید.
 1- دستور Truncate Table فاقد قسمت شرط (Where Clause) می‌باشد در صورتیکه دستور Delete دارای قسمت شرط (Where Clause) است
2- دستور Truncate Table در Log File آدرس Page و مقدار فضای آزاد شده (کمترین میزان Log) را می‌نویسد  اما در صورتیکه دستور Delete در Log هر رکوردی را که
قرار است حذف شود را در Log File ثبت می‌نماید.
3- دستور Truncate Table باعث می‌شود که Pageهای متعلق به جدول deallocate شوند. deallocate شدن Pageها این معنی را می‌دهد که رکوردهای موجود در جدول واقعاً حذف نشوند بلکه Extentهای مربوط به آن Pageها علامت Empty خورده تا دفعات بعد مورد استفاده قرار گیرند اما دستور Delete به طور فیزیکی محتوای Pageها  مربوط به جدول را خالی می‌کند.
نکته : پس از Truncate شدن رکوردها امکان بازگشت آنها وجود ندارد.
4- در صورتیکه جدول شما دارای ایندکس باشد. دستور Truncate Table آزاد کردن فضای مربوط به ایندکس را در یک مرحله انجام می‌دهد(مطابق بند 3) همچنین Log مربوط به این حذف به شکل حداقل (مطابق بند 2) در Log File ثبت می‌شود. اما دستور Delete هر رکوردی را که از ایندکس حذف می‌کند در Log File ثبت می‌کند.
5-  Trigger مربوط به دستور Delete به هیچ عنوان هنگام اجرای دستور Truncate Table فعال نمی‌شود. در صورتیکه با اجرای دستور Delete تریگر آن فعال خواهد شد.
6- در صورتیکه جدول شما دارایRelation)  Reference)  باشد امکان استفاده از دستور Truncate Table وجود ندارد. لازم به ذکر است حتی اگر Reference را غیر فعال کنید
باز هم امکان استفاده از دستور Truncate Table وجود نخواهد داشت و تلاش برای اجرای دستور Truncate Table باعث نمایش خطای زیر خواهد شد. 



در صورتیکه در دستور Delete امکان حذف رکوردها به ازای جداولی که دارای Relation هستند وجود دارد. فقط باید به این نکته توجه کنید که ترتیب حذف رکوردها از جداول Master و Detail را رعایت کنید.
 
7- دستور Truncate Table مقدار Identity را Reset کرده و آن را به Seed (هسته/مقدار اولیه) بر می‌گرداند. در صورتیکه دستور Delete تاثیری بر روی مقدار Identity ندارد
8- دستور Truncate Table تنها توسط کاربرانی قابل اجرا است که نقش DB_Owner و یا SysAdmin را داشته باشند در صورتیکه دستور Delete توسط هر کاربری که مجوز Delete بر روی جدول را  داشته باشد قابل اجرا می‌باشد.
9- پس از اجرای دستور Truncate Table تعداد رکوردهای حذف شده نمایش داده نمی‌شود. در صورتیکه هنگام اجرای دستور Delete تعداد رکوردهای حذف شده نمایش داده می‌شود.
 


مطالب
بررسی روش نصب و استفاده از پیش نمایش NET Core 2.2.

نسخه net core 2.2. Preview 3.، روز چهارشنبه 25 مهر، مطابق با 17 اکتبر منتشر شد. این نسخه شامل ویژگی‌های جدیدی از جمله موارد زیر می‌باشد:

  • تغییرات عمده در API 
  • Authorization Server
  • بهبود کارآیی و سرعت
  • پشتیبانی از Spatial Data برای SQL Server و SQLite
  • پشتیبانی از Cosmos DB
  • جایگزینی Bootstrap 4, Angular 6

مهمترین ویژگی مربوط به EF Core برای گروهی از برنامه نویسان، پشتیبانی از فیلدهای جغرافیایی یا همان Spatial Data می‌باشد.

نسخه‌ی net core 2.2 RTM.،  قبل از انتهای سال میلادی جاری منتشر خواهد شد و پس از آن آغاز به کار بر روی net core 3. خواهد بود. برای  نسخه‌ی net Core 3.، هدف بهبود برنامه‌های مبتنی بر Desktop و سازگاری آن با فرمت جدید csproj، اعلام شده‌است.


برای استفاده از ویژگی‌های net core 2.2. باید نسخه‌ی visual studio 2017 preview، یعنی ورژن Version 15.9.0 به بعد را نصب کنید. اگر صرفا dotnet core 2.2 را نصب کنید و از ویژوال استودیوی جاری قصد استفاده را داشته باشید، به شما پیغام می‌دهد که این نسخه قابلیت استفاده از دات نت 2.2 به بعد را ندارد.



برای تست و استفاده از net core 2.2 preview 3. مراحل زیر را طی کنید:

1- ابتدا آخرین انتشار ویژوال استودیو 2017، یعنی نسخه‌ی ‌پیش‌نمایش preview را از اینجا دریافت و نصب کنید. نسخه Enterprise را انتخاب کنید تا installer دانلود شود وشروع به نصب کند. حجم این نسخه، از نسخه‌ی اصلی کمتر می‌باشد.

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

2- سپس net core 2.2. را به یکی از روش‌های زیر نصب کنید:
  • برای نصب کلی بر روی سیستم،  اینجا کلیک کنید و نسخه‌ی SDK Installer مناسب دستگاه خود را دانلود کنید (64 یا 32 بیتی).
         در صورت موفقیت آمیز بودن، صفحه‌ی پایانی را به شکل زیر خواهید دید:

  • برای استفاده‌ی از EF Core آن در پروژه جاری می‌توانید آن‌‌را از اینجا  دانلود و یا بوسیله‌ی دستور زیر نصب کنید:
Install-Package Microsoft.EntityFrameworkCore -Version 2.2.0-preview3-35497

3- پس از نصب، ویژوال استودیوی preview را از همان مسیر visual studio، اجرا کنید. یک پروژه‌ی جدید از نوع وب را ایجاد کنید و فریمورک را بر روی بالاترین مقدار آن قرار دهید؛ مثل تصویر زیر:


بعد از ایجاد پروژه، می‌توانید در solution Explorer، ارجاعات جدید را مشاهده کنید:


مطالب
جستجوی غیر حساس به بزرگی و کوچکی حروف در SQLite توسط EF-Core
اگر پیشتر با SQL Server کار کرده باشید، حالت پیش‌فرض حساس بودن جستجوی SQLite به بزرگی و کوچکی حروف را انتظار نخواهید داشت؛ تا زمانیکه هنگام لاگین، اکانت Admin بتواند وارد سیستم شود و اکانت admin خیر. در این مطلب نحوه‌ی انجام تنظیمات مرتبط با جستجوی غیرحساس به بزرگی و کوچکی حروف را در SQLite و EF-Core، بررسی خواهیم کرد.


Collations و حساسیت به بزرگی و کوچکی حروف

پردازش متون در بانک‌های اطلاعاتی پیچیده‌است و عموما فراتر است از انتظارات ساده‌ی اولیه، خصوصا اینکه بانک‌های اطلاعاتی متفاوت، روش‌های متفاوتی را هم در این زمینه بکار می‌گیرند. برای مثال بانک‌های اطلاعاتی مانند SQLite و PostgreSQL به صورت پیش‌فرض به بزرگی و کوچکی حروف حساس هستند، اما بانک‌هایی مانند SQL Server و MySQL خیر. همچنین این حساسیت، بر روی کارآیی جستجو نیز بسیار تاثیر گذار است. برای مثال می‌توان از متدهایی مانند string.ToLower برای انجام جستجوهای غیرحساس به بزرگی و کوچکی حروف استفاده کرد، اما بکارگیری آن‌ها بلافاصله استفاده‌ی از ایندکس‌ها را غیرفعال می‌کنند و سبب انجام جستجوهایی بسیار کند خواهند شد.

برای مواجه شدن با یک چنین حالت‌هایی بدون افت کارآیی برنامه، مفهوم پایه‌ای به نام collation در بانک‌های اطلاعاتی ارائه شده‌است که مشخص می‌کند مقادیر رشته‌ای چگونه باید مرتب شده یا مقایسه شوند. برای مثال یک collation غیرحساس به بزرگی و کوچکی حروف، در حین مقایسه‌ی رشته‌ها، به بزرگی و کوچکی حروف بکار گرفته شده‌ی در عبارت اهمیتی نمی‌دهد. همچنین باید دقت داشت که یک چنین مقایسه‌ای بسته به فرهنگ بکار گرفته شده، می‌توان متفاوت باشد؛ برای مثال در زبان ترکی، i و I حروف متفاوتی هستند و نباید در حین مقایسه‌ی غیرحساس به بزرگی و کوچکی حروف، یکی در نظر گرفته شوند. به همین جهت تعداد قابل ملاحظه‌ای case-insensitive collations از پیش تعریف شده، بسته به فرهنگ‌های مختلف وجود دارند؛ نمونه‌ی دیگر آن فرهنگ آلمانی است که در آن عموما ä و ae را یکسان درنظر می‌گیرند. به علاوه collation بر روی نحوه‌ی مرتب سازی حروف نیز تاثیر دارد؛ برای مثال در فرهنگ آلمانی، ä پس از a قرار می‌گیرد، اما در فرهنگ سوئدی در انتهای حروف الفباء واقع شده‌است.

تمام پردازش‌های متنی در بانک‌های اطلاعاتی (چه به صورت صریح و یا ضمنی) از collations استفاده می‌کنند و نام آن‌ها از هر بانک اطلاعاتی به بانک اطلاعاتی دیگری متفاوت است. عموما می‌توان این collations را در سطح کل بانک اطلاعاتی و یا در سطح یک ستون مشخص از آن و یا حتی در سطح یک کوئری مشخص، تعیین کرد.


روش تعیین collation در سطح بانک اطلاعاتی

در اغلب بانک‌های اطلاعاتی، یک collation پیش‌فرض، در سطح کل آن‌ها تعریف شده‌است و بر روی تمام پردازش‌های متنی و تمام ستون‌های جداول تاثیرگذار است. برای مثال حالت پیش‌فرض collation در SQL Server (اگر هیچ تنظیم پیش‌فرض دیگری در حین تعریف بانک اطلاعاتی وجود نداشته باشد) مقدار SQL_Latin1_General_CP1_CI_AS است. این مقدار یک collation غیرحساس به بزرگی و کوچکی حروف است. مقدار CI آن به معنای case-insensitive و AS آن مخفف accent-sensitive (حساس به لهجه) است.
از زمان EF-Core 5x، امکان کار با collations و تعیین آن‌ها نیز میسر شده‌است. برای مثال برای تعیین یک چنین collation ای در سطح بانک اطلاعاتی می‌توان به صورت زیر در متد OnModelCreating عمل کرد:
modelBuilder.UseCollation("SQL_Latin1_General_CP1_CS_AS");
البته بهتر است یک چنین تنظیماتی را از ابتدای کار و پیش از تعریف و ایجاد بانک اطلاعاتی درنظر داشت؛ چون تغییر collation پس از ایجاد بانک اطلاعاتی، تداخلات زیادی را ایجاد می‌کند. برای مثال SQL Server حتی اجازه‌ی join دو جدول با collation متفاوت را نمی‌دهد؛ هرچند راه‌حل‌هایی برای آن وجود دارد اما بهتر است این مقدار یکبار و آن هم در ابتدای کار تعیین شود.


روش تعیین collation در سطح جداول بانک اطلاعاتی

Collations را همچنین می‌توان در سطح جداول نیز مشخص کرد تا بتوان در صورت نیاز، collation پیش‌فرض بانک اطلاعاتی را بازنویسی نمود. برای مثال شاید نیاز داشته باشید جداولی case-insensitive و تعدادی دیگر case-sensitive باشند.
در EF-Core 5x به بعد، روش انجام اینکار به صورت زیر است:
modelBuilder.Entity<Customer>().Property(c => c.Name)
   .UseCollation("SQL_Latin1_General_CP1_CI_AS");
در اینجا collation ستون Name جدول Customer، به صورت صریحی مشخص شده‌است.


روش تعیین پویای collation در سطح کوئری‌های بانک اطلاعاتی

یک جدول می‌تواند collation پیش‌فرضی داشته باشد، اما در حین کوئری گرفتن، collation آن‌را به صورت موقت و پویا تغییر داد. برای مثال بجای استفاده از متد ToLower که سبب می‌شود از ایندکس‌ها استفاده نشود، می‌توان از collation خاصی در حین کوئری گرفتن استفاده کرد:
var customers = context.Customers
   .Where(c => EF.Functions.Collate(c.Name, "SQL_Latin1_General_CP1_CS_AS") == "John").ToList();
البته باید دقت داشت که تعیین collation در این حالت نیز سبب می‌شود تا از ایندکس‌ها استفاده نشود. از این جهت که ایندکس‌ها به صورت پیش‌فرض بر اساس collation یک ستون یا جدول تهیه می‌شوند. هرچند بانک اطلاعاتی‌هایی مانند PostgreSQL, Sqlite امکان تعیین collation را در حین تهیه‌ی ایندکس‌ها نیز میسر می‌کنند. برای مثال می‌توان ایندکس‌های حساس و غیر حساس به بزرگی و کوچکی حروف را در این بانک‌های اطلاعاتی، به صورت جداگانه‌ای تعریف کرد تا در صورت نیاز، از آن‌ها استفاده شود.

یک نکته: هر چند کوئری‌های سمت دات نت به صورت پیش‌فرض حساس به بزرگی و کوچکی حروف هستند (مانند s1 == s2)، اما EF-Core هیچ تلاشی را برای انجام یک کوئری case-sensitive در سمت بانک اطلاعاتی انجام نخواهد داد و == سی شارپ به صورت مستقیمی به تساوی SQL ترجمه می‌شود که بسته به collation جاری، می‌تواند یا حتی نمی‌تواند حساس به بزرگی و کوچکی حروف باشد. بنابراین حالت پیش‌فرض کوئری‌های EF-Core استفاده از collation پیش‌فرض ستون‌ها است. هرچند متدهایی مانند string.Equals امکان مقایسه‌ی غیرحساس به بزرگی و کوچکی حروف را در دات نت میسر می‌کنند (چون به همراه پارامتر StringComparison هستند)، اما EF-Core سعی در ترجمه‌ی آن‌ها به SQL نخواهد کرد و تعیین صریح collation توسط متد EF.Functions.Collate به شما واگذار شد‌ه‌است.
 

تعیین collation غیرحساس به بزرگی و کوچکی حروف در SQLite، توسط EF-Core

با توجه به توضیحات فوق، متد زیر، collation ویژه‌ی nocase را که در SQLite به معنای collation غیرحساس به بزرگی و کوچکی حروف است، به کل بانک اطلاعاتی و همچنین تمام ستون‌های رشته‌ای آن به صورت خودکار اعمال می‌کند:
public static void SetCaseInsensitiveSearchesForSQLite(this ModelBuilder modelBuilder)
{
    if (modelBuilder == null)
    {
        throw new ArgumentNullException(nameof(modelBuilder));
    }

    modelBuilder.UseCollation("NOCASE");
    foreach (var property in modelBuilder.Model.GetEntityTypes()
                                            .SelectMany(t => t.GetProperties())
                                            .Where(p => p.ClrType == typeof(string)))
    {
        property.SetCollation("NOCASE");
    }
}
سپس روش استفاده‌ی از آن به صورت زیر خواهد بود:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    if (modelBuilder == null)
    {
        throw new ArgumentNullException(nameof(modelBuilder));
    }

    modelBuilder.SetCaseInsensitiveSearchesForSQLite();
}
مطالب
گرفتن خروجی XML از جداول در SQL Server 2012
فرض کنید که می‌خواهیم خروجی از جدول خود را به صورت XML نمایش یا از طریق وب سرویس در برنامه مان استفاده نماییم. اولین راهی که به ذهنمان می‌رسد خودمان  رشته xml را با حلقه ای ایجاد نماید یا استفاده از فضای نام System.Xml و کلاس‌های نوشته شده برای این کار . اما خود Sql Server امکانات ویژه ای برای کار با ساختار xml مهیا نموده که براحتی می‌توانید خروجی xml از داده هایتان ایجاد نمایید.

برای این کار از عبارت For XML در Select می‌توان استفاده نمود. برای مثال برای بدست آوردن ساختار ساده از For Xml Auto استفاده نمایید
SELECT BusinessEntityID, PersonType, Title, FirstName, MiddleName, LastName
FROM Person
WHERE BusinessEntityID = 10001
FOR XML AUTO
که خروجی بصورت node attribute زیر می‌باشد:

اما اگر بخواهیم خروجی به صورت node Elements باشد کافیست از پارامتر Elements استفاده نمایید 

SELECT BusinessEntityID, PersonType, Title, FirstName, MiddleName, LastName
FROM Person
WHERE BusinessEntityID = 10001
FOR XML AUTO, ELEMENTS

خروجی بصورت زیر می‌باشد:

اگر بخواهیم node attributes و node elements با هم ترکیب کنیم بصورت زیر عمل می‌کنیم:

SELECT BusinessEntityID AS '@ID', PersonType, Title,  FirstName, MiddleName, LastName
FROM Person
WHERE BusinessEntityID = 10001
FOR XML ELEMENTS

خروجی بصورت زیر است:

حال می‌خواهیم همه node‌ها را یک node ریشه قرار دهیم برای این کار از پارامتر ROOT در کنار AUTO به صورت زیر استفاده نمایید:

SELECT *
FROM Person
WHERE BusinessEntityID = 15291
FOR XML AUTO , ROOT('Persons')

اما اگر بخواهیم نام جدول را با نام دلخواه خود تغییر دهیم از پارامتر PATH به جای  AUTO به صورت زیر استفاده نمایید:

SELECT *
FROM Person
WHERE BusinessEntityID = 15291
FOR XML PATH('P') , ROOT('Persons')
نظرات مطالب
Cookie - قسمت سوم
در دات نت 4.5 ، مشکل طولانی بودن حاصل BinaryFormatter serialization برطرف شده (January 2013). این مشکل سبب می‌شده تا حاصل RolePrincipal.ToEncryptedTicket بسیار طولانی شده و بیشتر از حد مجاز اندازه قابل ذخیره سازی در یک کوکی شود.
- وصله‌ی نسخه‌ی ویندوز 8 و ویندوز سرور 2012 آن از اینجا قابل دریافت است؛ نسخه‌ی ویندوز 7 و ویندوز سرور 2008 از اینجا
+ آپدیت ویندوز را روشن کنید تا آخرین به روز رسانی‌ها و نگارش‌های دات نت نصب شده را به صورت خودکار دریافت کنید.