فعال‌سازی Multiple Active Result Sets
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: دو دقیقه

(Multiple Active Result Sets (MARS یکی از قابلیتهای SQL SERVER است. این قابلیت در واقع این امکان را برای ما فراهم می‌کند تا بر روی یک Connection همزمان چندین کوئری را به صورت موازی ارسال کنیم. در این حالت برای هر کوئری یک سشن مجزا در نظر گرفته می‌شود. 
مدل:
namespace EnablingMARS.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Desc { get; set; }
        public float Price { get; set; }
        public Category Category { get; set; }

    }

    public enum Category
    {
        Cate1,
        Cate2,
        Cate3
    }
}
کلاس Context:
namespace EnablingMARS.Models
{
    public class ProductDbContext : DbContext
    {
        public ProductDbContext() : base("EnablingMARS") {}
        public DbSet<Product> Products { get; set; }

    }
}
ابتدا یک سطر جدید را توسط کد زیر به دیتابیس اضافه می‌کنیم:
MyContext.Products.Add(new Product()
 {
                Title = "title1",
                Desc = "desc",
                Price = 4500f,
                Category = Category.Cate1
   });
MyContext.SaveChanges();
اکنون می‌خواهیم قیمت محصولاتی را که در دسته‌بندی Cate1 قرار دارند، تغییر دهیم:
foreach (var product in _dvContext.Products.Where(category => category.Category == Category.Cate1))
{
     product.Price = 50000;
     MyContext.SaveChanges();
}
خوب؛ اکنون اگر برنامه را اجرا کنیم با خطای زیر مواجه می‌شویم:
There is already an open DataReader associated with this Command which must be closed first.
این استثناء زمانی اتفاق می‌افتد که بر روی نتایج حاصل از یک کوئری، یک کوئری دیگر را ارسال کنیم. البته استثنای صادر شده بستگی به کوئری دوم شما دارد ولی در حالت کلی و با مشاهده Stack Trace، پیام فوق نمایش داده می‌شود. همانطور که در کد بالا ملاحظه می‌کنید درون حلقه‌ی forach ما به پراپرتی Price دسترسی پیدا کرده‌ایم، در حالیکه کوئری اصلی ما هنوز فعال (Active) است. MARS در اینجا به ما کمک می‌کند که بر روی یک Connection، بیشتر از یک کوئری فعال داشته باشیم. در حالت عادی Entity Framework Code First این ویژگی را به صورت پیش‌فرض برای ما فعال نمی‌کند. اما اگر خودمان کانکشن‌استرینگ را اصلاح کنیم، این ویژگی SQL SERVER فعال می‌گردد. برای حل این مشکل کافی است به کانکشن‌استرینگ، MultipleActiveResultSets=true را اضافه کنیم:
"Data Source=(LocalDB)\v11.0;Initial Catalog=EnablingMARS; MultipleActiveResultSets=true"
لازم به ذکر است که این قابلیت از نسخه SQL SERVER 2005 به بالا در دسترس می‌باشد. همچنین در هنگام استفاده از این قابلیت می‌بایستی موارد زیر را در نظر داشته باشید:
  • وقتی کانکشنی در حالت MARS برقرار می‌شود، یک سشن نیز همراه با یکسری اطلاعات اضافی برای آن ایجاد شده که باعث ایجاد Overhead خواهد شد.
  • دستورات مارس  thread-safe  نیستند.
  • #
    ‫۹ سال و ۱۱ ماه قبل، پنجشنبه ۲۴ مهر ۱۳۹۳، ساعت ۱۴:۰۲
    با تشکر. یک سؤال: اگر بجای dvContext.Products.Where بنویسیم dvContext.Products.Include(x=>x.Category).Where باز هم این مشکل هست؟
    • #
      ‫۹ سال و ۱۱ ماه قبل، پنجشنبه ۲۴ مهر ۱۳۹۳، ساعت ۱۴:۱۱
      بله می‌شود به صورت eager loading این مشکل را نیز حل کرد. ولی مسئله performance را نیز باید در نظر داشته باشید.
      البته می‌بایست به این صورت نوشته شود:
      foreach (var product in _dvContext.Products.Where(x => x.Category.CateName == "Cate1").Include(x => x.Category).ToList())
      {
                      product.Price = 50000;
                      _dvContext.SaveChanges();
      }
      حالت فوق در صورتی است که یک Navigation property با نام Category داشته باشید.

  • #
    ‫۹ سال و ۶ ماه قبل، شنبه ۱ فروردین ۱۳۹۴، ساعت ۱۷:۴۷
    اگر با استفاده از DNTProfiler برنامه را بررسی کنیم، خواهیم داشت:

    - در اینجا MARS دارای یک connection است (با ID مساوی 21) اما 15 دستور روی آن تک اتصال اجرا شده‌اند.
    - همچنین یک شیء کانکشن با ID مساوی 19 داریم که هر بار یک دستور روی آن اجرا شده‌است (یک شیء اتصالی پیش از dispose نهایی، چندین بار باز و بسته شده‌است). این مورد نشانه‌ی lazy loading اشتباه است که با استفاده از متد Include قابل حل است. این مشکل در برگه‌ی duplicate commands per method هم قابل مشاهده‌است:


    همانطور که مشاهده می‌کنید، یک دستور SQL مشابه، مدام به صورت تکراری بر روی شیء اتصالی شماره 19 در حال اجرا است.