در دنیای توسعه نرمافزار با استفاده از ORMها، معمولاً عادت داریم که هر ستون در جدول پایگاه داده، دقیقاً یک معادل (Property) در کلاس سیشارپ داشته باشد. اما Shadow Properties این قاعده را تغییر میدهند.
Shadow Property چیست؟
Shadow Property به ویژگیهایی گفته میشود که در کلاس مدل (Entity) شما تعریف نشدهاند، اما در مدلِ Entity Framework Core وجود دارند. به عبارت سادهتر، این ویژگیها در کد شما دیده نمیشوند اما در جدول پایگاه داده حضور دارند و EF Core مسئولیت مدیریت، خواندن و نوشتن آنها را بر عهده دارد.
چرا به آنها نیاز داریم؟
هدف اصلی Shadow Properties، پاک نگه داشتن مدل دامنه (Domain Model) است. گاهی اوقات دادههایی وجود دارند که برای زیرساخت پایگاه داده یا مسائل مدیریتی (Metadata) حیاتی هستند، اما منطق تجاری برنامه (Business Logic) نیازی به دانستن درباره آنها ندارد.
نحوه تعریف
از آنجایی که این ویژگیها در کلاس وجود ندارند، نمیتوان آنها را با Data Annotations تعریف کرد. تنها راه تعریف آنها استفاده از Fluent API در متد OnModelCreating است:
modelBuilder.Entity()
.Property("LastUpdated");
در این مثال، جدول Products دارای ستونی به نام LastUpdated خواهد بود، در حالی که کلاس Product در سیشارپ هیچ فیلدی با این نام ندارد.
تصمیمگیری برای استفاده از Shadow Properties به معماری پروژه شما بستگی دارد. در اینجا سه سناریوی طلایی برای استفاده از آنها آورده شده است:
۱. فیلدهای حسابرسی (Auditing)
شایعترین کاربرد، ثبت زمان ایجاد یا ویرایش رکوردهاست. شما میتوانید فیلدهایی مثل CreatedDate یا ModifiedBy را به صورت Shadow تعریف کنید. این کار باعث میشود کلاسهای شما با فیلدهای تکراری که ربطی به بیزنس اصلی ندارند، شلوغ نشوند.
۲. کلیدهای خارجی پنهان (Hidden Foreign Keys)
گاهی میخواهید بین دو موجودیت رابطه برقرار کنید، اما نمیخواهید "شناسه" (ID) موجودیت مرتبط در کلاس شما نمایش داده شود. EF Core به طور خودکار اگر کلید خارجی را تعریف نکنید، آن را به صورت Shadow Property ایجاد میکند. این کار به کپسولهسازی (Encapsulation) بهتر کمک میکند.
۳. امنیت و دادههای زیرساختی
دادههایی که فقط توسط لایه دیتا دستکاری میشوند و برنامهنویس نباید به طور تصادفی در لایههای بالاتر (مثل UI) به آنها دسترسی داشته باشد، کاندیدای مناسبی برای Shadow Property هستند.
نکته حرفهای: برای دسترسی به مقدار یک Shadow Property در کد، باید از DbContext.Entry استفاده کنید:
context.Entry(myEntity).Property("LastUpdated").CurrentValue = DateTime.Now;
یکی از مفاهیم پیشرفته در EF Core، بحث Owned Entities (موجودیتهای تحت مالکیت) است. درک ارتباط این دو مفهوم برای طراحی دیتابیسهای بهینه ضروری است.
Owned Entity چیست؟
موجودیتهایی هستند که هویت مستقلی ندارند و فقط به عنوان بخشی از یک موجودیت دیگر (Owner) معنا پیدا میکنند. مثل کلاس Address برای موجودیت User.
نقش Shadow Properties در اینجا چیست؟
وقتی شما یک موجودیت را به صورت Owned تعریف میکنید (با استفاده از .OwnsOne())، EF Core باید راهی برای مرتبط کردن این دو در پایگاه داده پیدا کند.
کلید خارجی مخفی: اگر Owned Entity در یک جدول مجزا ذخیره شود، EF Core به صورت خودکار یک Shadow Property برای ذخیره کلید خارجی (FK) به موجودیت اصلی ایجاد میکند.
مدیریت وضعیت: بسیاری از فیلدهای کنترلی که برای مدیریت این رابطه لازم است، به صورت Shadow تعریف میشوند تا مدلِ سادهی Address شما آلوده به منطقهای پایگاه داده نشود.
بسیاری از توسعهدهندگان Shadow Properties را با Backing Fields اشتباه میگیرند. در این مقاله تفاوتهای کلیدی آنها را بررسی میکنیم.
| ویژگی | Shadow Property | Backing Field |
| وجود در کلاس | خیر (فقط در EF) | بله (فیلد private) |
| دسترسی مستقیم | فقط از طریق Change Tracker | از طریق متدهای کلاس |
| کاربرد اصلی | جداسازی کامل مدل از دیتا | کنترل نحوه خواندن/نوشتن دیتا |
| پیچیدگی | بالاتر (نیاز به Fluent API) | کمتر |
چه موقع از کدام استفاده کنیم؟
اگر میخواهید فیلد در کلاس باشد اما منطق خاصی برای گت و ست داشته باشد (مثلاً Encapsulation)، از Backing Field استفاده کنید. اما اگر میخواهید فیلد "کلاً" در کلاس نباشد، Shadow Property گزینه شماست.
استفاده از Shadow Properties همیشه بدون هزینه نیست. در این مقاله به محدودیتهایی میپردازیم که باید قبل از پیادهسازی بدانید.
چالشهای اصلی:
خوانایی کد: از آنجایی که این ویژگیها در کلاس نیستند، برنامهنویسان جدید ممکن است با دیدن ستونهایی در دیتابیس که در کد معادل ندارند، گیج شوند. مستندسازی در اینجا حیاتی است.
پرسوجو (Querying): برای استفاده از این فیلدها در LINQ، باید از تابع EF.Property استفاده کنید که میتواند کد را کمی طولانی کند:
context.Blogs.OrderBy(b => EF.Property(b, "LastUpdated"))
بهترین شیوهها:
استفاده برای کارهای عرضی (Cross-cutting Concerns): فقط برای مواردی مثل Auditing، Multi-tenancy (مثل TenantId) و Soft Delete از آنها استفاده کنید.
یکپارچگی در Base Entity: اگر از Shadow Properties برای Auditing استفاده میکنید، منطق مقداردهی آنها را در متد SaveChanges قرار دهید تا به صورت خودکار برای تمام موجودیتها اعمال شود.
اجتناب در منطقهای پیچیده تجاری: اگر یک فیلد قرار است مدام در محاسبات ریاضی یا منطقهای شرطی بیزنس استفاده شود، آن را به صورت یک ویژگی معمولی در کلاس تعریف کنید.
Shadow Properties ابزاری قدرتمند برای حفظ تمیزی مدل دامنه و جداسازی دغدغههای زیرساختی از منطق تجاری هستند. درک عمیق آنها در کنار Owned Entities به شما اجازه میدهد تا دیتابیسهایی منعطف و کدهایی خواناتر داشته باشید.
0 نظر
هنوز نظری برای این مقاله ثبت نشده است.