ذخیره Array در Ef core 3.1 با استفاده از Value Conversions

خوب دوستان ساید برای شما اتفاق افتاده باشد که بخواهید یک Property از جنس List در مدل خودتان تعریف کنید. مشکل از آنجایی شروع می‌شود که Ef core نمی‌تواند نوع array را در دیتابیس ذخیره کند ! و شما نمی‌توانید اینگونه عمل کنید: public string[] Tags { get; set; }برای حل این مشکل ما باید از Value Conversions استفاده کنیم. Value Conversions در نسخه 2.1 به بالا معرفی شدن که کارش تبدیل نوع مقدار و ذخیره کردن در دیتابیس و برعکس(یعنی گرفتن مقدار از دیتابیس و تبدیل به نوع مقدار ما) می‌باشد. مبدل مقدار (Value Conversions) دو نوع را می‌گیرد ModelClrType و دیگری ProviderClrType .نوع مدل Model Type : این همون نوع .Net که ما در مدل موجودیت (entity model) استفاده می‌کنیم.نوع فراهم‌کننده کننده Provider Type : این هم نوع .Net که قابل فهم برای database provider (فراهم کننده پایگاه داده) می‌باشد.برای استفاده از Value Conversions ما دو راه داریم یکی نوشتن مستقیم در modelBuilder و دیگری نوشتن غیر مستقیم در modelBuilder .نوشتن مستقیم: public class VideoConfiguration:IEntityTypeConfiguration<Video> { public void Configure(EntityTypeBuilder<Video> builder) { builder.Property(video => video.Url).IsRequired().IsUnicode().HasMaxLength(200); builder.Property(video => video.Title).IsRequired().HasMaxLength(100); builder.Property(video => video.Description).IsRequired().HasMaxLength(100); builder.Property(video => video.Tags).HasConversion( v => string.Join(',', v), v => v.Split(',', StringSplitOptions.RemoveEmptyEntries)); builder.HasOne(video => video.Playlist) .WithMany(Playlist => Playlist.Videos) .HasForeignKey(video => video.PlaylistId); } }به این قسمت توجه کنید:builder.Property(video => video.Tags).HasConversion(v => string.Join(',', v), v => v.Split(',', StringSplitOptions.RemoveEmptyEntries));و نوشتن غیرمستقیم: public class VideoConfiguration:IEntityTypeConfiguration<Video> { public void Configure(EntityTypeBuilder<Video> builder) { builder.Property(video => video.Url).IsRequired().IsUnicode().HasMaxLength(200); builder.Property(video => video.Title).IsRequired().HasMaxLength(100); builder.Property(video => video.Description).IsRequired().HasMaxLength(100); var converter = new ValueConverter<IEnumerable<string>,string>(v => string.Join(',', v), v => v.Split(',', StringSplitOptions.RemoveEmptyEntries)); builder.Property(video => video.Tags).HasConversion(converter); builder.HasOne(video => video.Playlist) .WithMany(Playlist => Playlist.Videos) .HasForeignKey(video => video.PlaylistId); } }به این قسمت توجه کنید:var converter = new ValueConverter<IEnumerable<string>,string>(v => string.Join(',', v), v => v.Split(',', StringSplitOptions.RemoveEmptyEntries)); builder.Property(video => video.Tags).HasConversion(converter);و تمام ! با اینکار موقع ذخیره کردن string[] در پایگاه داده آنها به صورت یک string (به یک رشته هر آیتم , از هم جدا شده‌اند) تبدیل می‌شود و زمانی که ما اطلاعات را از دیتابیس فراخوانی می‌کنیم ، آن رشته ذخیره شده را به صورت List<string> (رشته را با , جدا کرده و به array تبدیل کرده) برای ما بر می‌گرداند.یک مثال دیگر ذخیره یک enum به صورت رشته در دیتابیسفرض کنید ما همچین مدلی داریم:public class Rider { public int Id { get; set; } public EquineBeast Mount { get; set; } } public enum EquineBeast { Donkey, Mule, Horse, Unicorn }طبق چیزی که قبلا گفتیم بطور مستقیم اینطوری می‌نویسیم:protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder .Entity<Rider>() .Property(e => e.Mount) .HasConversion( v => v.ToString(), v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v)); }و بطور غیری مستقیم:var converter = new ValueConverter<EquineBeast, string>( v => v.ToString(), v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v)); modelBuilder .Entity<Rider>() .Property(e => e.Mount) .HasConversion(converter);نکات مهم:نکته اول: مقدار null هرگز به مبدل مقدار منتقل نمی شود.این امر باعث می شود اجرای تبدیل‌ها آسان تر شود و به آنها اجازه می دهد تا بین خصوصیات nullable و non-nullable به اشتراک بگذارند. (پس مراقب باشید اگر نیاز بود مقداری در دیتابیس null باشد نباید از Value Conversions استفاده کنید. چون مقدار null ارسال نمی‌کند)نکته دوم: در حال حاضر هیچ راهی برای تعیین یک مکان که هر Property یک نوع خاص باید از یک مبدل یک‌سان استفاده کند، وجود ندارد. این ویژگی برای نسخه‌های آینده در نظر گرفته خواهد شد.تعدادی مبدل توکار نیز وجود دارد که عبارت‌انداز :BoolToZeroOneConverter - Bool to zero and oneBoolToStringConverter - Bool to strings such as "Y" and "N"BoolToTwoValuesConverter - Bool to any two valuesBytesToStringConverter - Byte array to Base64-encoded stringCastingConverter - Conversions that require only a type castCharToStringConverter - Char to single character stringDateTimeOffsetToBinaryConverter - DateTimeOffset to binary-encoded 64-bit valueDateTimeOffsetToBytesConverter - DateTimeOffset to byte arrayDateTimeOffsetToStringConverter - DateTimeOffset to stringDateTimeToBinaryConverter - DateTime to 64-bit value including DateTimeKindDateTimeToStringConverter - DateTime to stringDateTimeToTicksConverter - DateTime to ticksEnumToNumberConverter - Enum to underlying numberEnumToStringConverter - Enum to stringGuidToBytesConverter - Guid to byte arrayGuidToStringConverter - Guid to stringNumberToBytesConverter - Any numerical value to byte arrayNumberToStringConverter - Any numerical value to stringStringToBytesConverter - String to UTF8 bytesTimeSpanToStringConverter - TimeSpan to stringTimeSpanToTicksConverter - TimeSpan to ticksپس کد قبلی که ما نوشیتم را میتوانیم اینگونه تغییر دهیم:var converter = new EnumToStringConverter<EquineBeast>(); modelBuilder .Entity<Rider>() .Property(e => e.Mount) .HasConversion(converter);مبدل‌های از پیش تعریف شده:برای تبدیل‌های عمومی که یک مبدل توکار وجود دارد، نیازی به مشخص کردن صریح مبدل وجود ندارد.در عوض، فقط پیکربندی نوع Provider را باید مورد استفاده قرار داد و EF به طور خودکار از مبدل ساخت مناسب استفاده خواهد کرد.در مورد مثال بالا برای تبدیل enum به string (در پایگاه داده) می توانیم بصورت زیر هم عمل کنیم:public class Rider { public int Id { get; set; } [Column(TypeName = &quotnvarchar(24)&quot)] public EquineBeast Mount { get; set; } }سپس مقادیر enum به عنوان رشته‌ای در پایگاه‌داده بدون هیچ پیکربندی بیشتر در OnModelCreating ذخیره می‌شوند.محدودیت هاهمانطور که در بالا ذکر شد ، null قابل تبدیل نیست.در حال حاضر هیچ راهی برای تبدیل یک Property به چندین ستون یا بالعکس وجود ندارد.استفاده از تبدیل مقادیر ممکن است توانایی EF Core را برای ترجمه عبارات به SQL تحت تأثیر قرار دهد.اخطار برای چنین مواردی ثبت خواهد شد. حذف این محدودیت ها برای نسخه بعدی در نظر گرفته شده است.کلام آخر من دیگر به هیچ وقت کد کپی پیست نمی‌کنم چون دهنم برای یکی کردن کد (کپی که می‌کنی ویرگول خط به خط جدا میکند! )سرویس میشود ? و کلی وقتم تلف می‌شود از این به بعد فقط عکس از کدها قرار می‌دهم.منبع اصلی منبع فرعی۱ منبع فرعی۲