من همیشه در مورد EF یک نظری داشتم و آن اینست که با اینکه یک ORM، یک هدف مهم را در نظر دارد و آن اینست که تا حد ممکن استانداردهایی را که بین تمامی دیتابیسها مشترک است، رعایت کند، ولی باز قابل قبول است اگر بگوییم که کاربران EF انتظار داشته باشند تا اطلاعات بیشتری در مورد sql server در آن نهفته باشد. از یک سو هر دو محصول مایکروسافت هستند و از سوی دیگر مطمئنا توسعه گران محصولات دات نت بیش از هر چیزی به sql server نگاه ویژهتری دارند. پس مایکروسافت در کنار حفظ آن ویژگیهای مشترک، باید به حفظ استانداردهای جدایی برای sql server هم باشد.
تعدادی از برنامه نویسان در هنگام ایجاد Domain Model کم لطفیهای زیادی را میکنند که یکی از آنها عدم کنترل نوع دادههای خود است. مثلا برای رشتهها هیچ محدودیتی را در نظر نمیگیرند. شاید در سمت کلاینت اینکار را انجام میدهند؛ ولی نکتهی مهم در طرف دیتابیس است که چگونه تعریف میشود. در این حالت (nvarchar(MAX در نظر گرفته میشود که به معنی اشاره به منطقه دوگیگابایتی از اطلاعات است. در نکات بعدی، قصد داریم این مرحله را یک گام به جلوتر پیش ببریم و آن هم ایجاد نوع دادههای بهینهتر در Sql Server است.
نکته مهم: بدیهی است که تغییرات زیر، ORM شما را تنها به sql server مقید میکند که بعدها در صورت تغییر دیتابیس نیاز به حذف موارد زیر را خواهید داشت؛ در غیر اینصورت به مشکل عدم ایجاد دیتابیس برخواهید خورد.
اولین مورد مهم بحث تاریخ و زمان است؛ وقتی ما یک نوع داده را تنها DateTime در نظر بگیریم، در Sql Server هم همین نوع داده وجود دارد و انتخاب میشود. ولی اگر شما واقعا نیازی به این نوع داده نداشته باشید چطور؟ در حال حاضر من بر روی یک برنامهی کارخانه کار میکنم که بخش کارمندان و گارگران آن سه داده زمانی زیر را شامل میشود:
public DateTime BirthDate { get; set; } public DateTime HireDate { get; set; } public DateTime? LeaveDate { get; set; }
حال به جدول زیر نگاه کنید که هر نوع داده چه مقدار فضا را به خود اختصاص میدهد:
SmallDateTime | 4 بایت |
DateTime | 8 بایت |
DateTime2 | 6 تا 8 بایت |
DateTimeOffset | 8 تا 10 بایت |
Date | 3 بایت |
Time | 3 تا 5 بایت |
از این جدول چه میفهمید؟ با یک نگاه میتوان فهمید که ساختار بالای من باید 24 بایت گرفته باشد؛ برای ساختاری که هم تاریخ و هم زمان (ساعت) را پشتیبانی میکند. ولی با نگاه دقیقتر به نام پراپرتیها این نکته روشن میشود که ما یک گپ Gap (فضای بیهوده) داریم چون زمان تولد، استخدام و ترک سازمان اصلا نیازی به ساعت ندارند و همان تاریخ کافی است. یعنی نوع Date با حجم کلی 9 بایت؛ که در نتیجه 15 بایت صرفه جویی در یک رکورد صورت خواهد گرفت.
پس کد بالا را به شکل زیر تغییر میدهم:
[Column(TypeName = "date")] public DateTime BirthDate { get; set; } [Column(TypeName = "date")] public DateTime HireDate { get; set; } [Column(TypeName = "date")] public DateTime? LeaveDate { get; set; }
خصوصیت Column از نسخه 4.5دات نت فریم ورک اضافه شده و در فضای نام System.ComponentModel.DataAnnotations.Schema قرار گرفته است.نوعهایی که در بالا با سایز متغیر هستند، به نسبت دقتی که برای آن تعیین میکنید، سایز میگیرند. مثل (time(0 که 3 بایت از حافظه را میگیرد. در صورتی که time معرفی کنید، به جای اینکه از شیء DateTime استفاده کنید، از شی Timespan استفاده کنید، تا در پشت صحنه از نوع داده time استفاده کند. در این حالت حداکثر حافظه یعنی 5 بایت را برخواهد داشت و بهترین حالت ممکن این هست که نیاز خود را بسنجید و خودتان دقت آن را مشخص کنید. دو شکل زیر نحوهی تعریف نوع زمان را مشخص میکنند. یکی حالت پیش فرض و دیگری انتخاب دقت:
public class Testtypes { public TimeSpan CloseTime { get; set; } public TimeSpan CloseTime2 { get; set; } } public class TestConfig : EntityTypeConfiguration<Testtypes> { public TestConfig() { this.Property(x => x.CloseTime2).HasPrecision(3); } }
مورد دوم در مورد دادههای اعشاری است:
float flExample=23.2f;
//real public float FloatData { get; set; } //real public Single SingleData { get; set; } //float public double DoubleData { get; set; }
4235.254
decimal d1=4545.112m;
modelBuilder.Entity<Class>().Property(object => object.property).HasPrecision(7, 3);
public class Testtypes { public Decimal Decimal1 { get; set; } public Decimal Decimal2 { get; set; } } public class TestConfig : EntityTypeConfiguration<Testtypes> { public TestConfig() { this.Property(x => x.Decimal2).HasPrecision(7, 3); } }
مورد سوم مبحث رشته هاست:
[StringLength(25)] public string FirstName { get; set; } [StringLength(30)] [Column(TypeName = "varchar")] public string EnProductTitle { get; set; } public string ArticleContent { get; set; } [Column(TypeName = "varchar(max)")] public string ArticleContentEn { get; set; }
this.Property(e => e.EnProductTitle).HasColumnType("VARCHAR").HasMaxLength(30);
modelBuilder.Properties<string>().Configure(c => c.HasColumnType("varchar")); //=========== یا modelBuilder.Properties<string>().Configure(c => c.IsUnicode(false));
//tinyInt public byte Age { get; set; } //smallInt public Int16 OldInt { get; set; } //int public int Int32 { get; set; } //Bigint public Int64 HighNumbers { get; set; }