عنوان:

‫ایجاد ایندکس منحصربفرد بر روی چند فیلد با هم در EF Code first


نویسنده: وحید نصیری
تاریخ: ۱۳۹۳/۰۵/۱۸ ۸:۵
آدرس: www.dntips.ir
در EF 6 امکان تعریف ساده‌تر ایندکس‌ها توسط data annotations میسر شده‌است. برای مثال:
public abstract class BaseEntity
{
    public int Id { get; set; }
}

public class User : BaseEntity
{
   [Index(IsUnique = true)]
   public string EmailAddress { get; set; }
}
در اینجا توسط ویژگی Index، خاصیت آدرس ایمیل به صورت منحصربفرد تعریف شده‌است.

سؤال: چگونه می‌توان شبیه به composite keys، اما نه دقیقا composite keys، بر روی چند فیلد با هم ایندکس منحصربفرد تعریف کرد؟
    public class UserRating : BaseEntity
    {
        public VoteSectionType SectionType { set; get; }

        public double RatingValue { get; set; }

        public int SectionId { get; set; }

        [ForeignKey("UserId")]
        public virtual User User { set; get; }
        public int UserId { set; get; }
    }
در اینجا جدول رای‌های ثبت شده‌ی یک سیستم را مشاهده می‌کنید. می‌خواهیم یک کاربر نتواند بیش از یک رای به یک مطلب خاص بدهد. به عبارتی نیاز است بر روی SectionType (مطلب، اشتراک‌ها، دوره‌ها و ...)، SectionId (شماره مطلب) و UserId (شماره کاربر) یک کلید منحصربفرد ترکیبی تعریف کرد. ترکیب این سه مورد باید در کل جدول منحصربفرد باشند (Multiple column indexes).
همچنین نمی‌خواهیم Composite key هم تعریف کنیم. می‌خواهیم Id و Primary key این جدول مانند قبل برقرار باشد.
انجام چنین کاری در EF 6.1 به نحو ذیل میسر شده‌است:
    public class UserRating : BaseEntity
    {
        [Index("IX_Single_UserRating", IsUnique = true, Order = 1)] //کلید منحصربفرد ترکیبی روی سه ستون
        public VoteSectionType SectionType { set; get; }

        public double RatingValue { get; set; }

        [Index("IX_Single_UserRating", IsUnique = true, Order = 2)]
        public int SectionId { get; set; }

        [ForeignKey("UserId")]
        public virtual User User { set; get; }

        [Index("IX_Single_UserRating", IsUnique = true, Order = 3)]
        public int? UserId { set; get; }
    }
نکته‌ی انجام اینکار، تعریف Indexها با یک نام یکسان صریحا مشخص شده‌است. در اینجا سه ایندکس تعریف شده‌اند؛ اما نام آن‌ها یکی است و مساوی IX_Single_UserRating قرار داده شده‌است. هر سه مورد نیز IsUnique تعریف شده‌اند و Order آن‌ها نیز باید مشخص گردد.
خروجی SQL چنین تنظیمی به صورت زیر است:
 CREATE UNIQUE INDEX [IX_Single_UserRating]
ON [UserRatings] ([SectionType] ASC,[SectionId] ASC,[UserId] ASC);
 

نظرات

  • مهمان در ۱۳۹۳/۰۵/۱۸ ۱۶:۵۱
    با سلام :
    تفاوت این کار با تعریف در دیتابیس چه می‌باشد.؟
    اگر بخواهیم به جای ASC مقدار Desc قرار بگیرد چه کار کنیم؟
    • وحید نصیری در ۱۳۹۳/۰۵/۱۸ ۱۸:۲۴
      - EF جزو خانواده‌ی ابزارهایی به نام ORMs است. زمانیکه از یک ORM استفاده می‌کنید و مستقیما SQL نویسی نمی‌کنید، کدهای شما قابل انتقال می‌شوند. می‌توانید به سادگی بانک اطلاعاتی برنامه را عوض کنید بدون اینکه نیازی باشد در کدهای اصلی برنامه تغییری حاصل شود. اهمیت این مساله در اینجا است که نهایتا پروایدر آن بانک اطلاعاتی خاص، بر اساس تعاریف برنامه و ORM مورد استفاده می‌داند که چگونه باید SQL صحیح و مرتبطی را تولید کند که ممکن است از یک بانک اطلاعاتی به بانک اطلاعاتی دیگری متفاوت باشد.
      - فعلا از طریق ویژگی فوق پشتیبانی نمی‌شود.
  • داوود در ۱۳۹۳/۰۶/۲۰ ۱۵:۱۸
    با سلام 
    آیا راهی وجود داره که بشه این کار رو تو فایل کانفیگ انجام داد 
    • وحید نصیری در ۱۳۹۳/۰۶/۲۰ ۱۵:۳۷
      با روش Fluent
      - تک ستونی:
      modelBuilder.Entity<People>()  
          .Property(x => x.Firstname)
          .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("ix_people_firstname")));
      - چند ستونی:
      modelBuilder.Entity<People>()  
          .Property(x => x.Firstname)
          .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("ix_people_fullname", 1)));
      modelBuilder.Entity<People>()  
          .Property(x => x.Lastname)
          .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("ix_people_fullname", 2)));
      - منحصربفرد:
      modelBuilder.Entity<People>()  
          .Property(x => x.NationalInsuranceNo)
          .HasColumnAnnotation("Index",
              new IndexAnnotation(new IndexAttribute("ix_people_nationalinsurance") {IsUnique = true}));
      - نوع Clustered 
      modelBuilder.Entity<People>()  
          .Property(x => x.Id)
          .HasColumnAnnotation("Index",
              new IndexAnnotation(new IndexAttribute("ix_people_nationalinsurance") { IsClustered = true}));
  • م علیزاده در ۱۳۹۴/۰۸/۰۲ ۱:۳۸
    با سلام.
    میخواستم بدونم برای ایندکس منحصر به فرد هیچ validation توکاری درEF وجود داره؟ که برای مثال وقتی دو تا رکورد تکراری رو به dbset اضافه میکنیم قبل از savechange عمل validation رو انجام بده، یا اینکه باید به صورت دستی validate کنیم.و اگه آره بهترین روش از نظر بهینه بودن برای این کار چی میتونه باشه؟ValidateEntity یا Ivalidation object؟ یا...؟
    با تشکر از وقتی که میگذارید.
    • وحید نصیری در ۱۳۹۴/۰۸/۰۲ ۱:۴۶
      - خیر. این مورد توسط بانک اطلاعاتی بررسی می‌شود و EF در آن نقشی ندارد و در نهایت در صورت شکست، استثنای مرتبط را از بانک اطلاعاتی به برنامه منتقل می‌کند.
      - روش مناسب اعتبار سنجی آن، استفاده از Remote validation است (مثلا در حین ثبت نام بررسی کند که آیا ایمیل وارد شده در بانک اطلاعاتی موجود است یا خیر).