اشتراکها
نظرات مطالب
SQL Injection چیست؟
یعنی در صورت استفاده از linq هم باز این مشکل پا برجاست؟!
آیا LINQ در زبانهای دیگر مثل جاوا معادلی دارد؟
نظرات مطالب
نمایش تاریخ بر حسب تعداد روزهای گذشته
سلام؛ در قسمتی از سایت، بخش مطالب این ماه قرار داره. شما برای به دست آوردن مطالب این ماه، چطور تاریخ رو محاسبه میکنید؟ من خودم به این روش رسیدم:
این روش بهینه هست ؟
public class Post { public int Id { get; set; } public string Title { get; set; } public DateTime dt { get; set; } } static void Main(string[] args) { List<Post> ListOfPost = new List<Post>(); DateTime dt = DateTime.Now; PersianCalendar pc = new PersianCalendar(); int day = pc.GetDayOfMonth(dt); int month = pc.GetMonth(dt); int year = pc.GetYear(dt); int DaysInMonth = pc.GetDaysInMonth(year, month); DateTime FirstDayOfCurrentMonth = dt.AddDays(-day).Date; DateTime LastDayOfCurrentMonth = FirstDayOfCurrentMonth.AddDays(DaysInMonth); var query = ListOfPost .Where(x => x.dt.Date > FirstDayOfCurrentMonth.Date) .Where(x => x.dt.Date <= LastDayOfCurrentMonth.Date) .ToList(); }
شاید برای شما هم پیش آمده باشد که بخواهید در هر بار واکشی لیستی از اطلاعات، مثلا از دیتابیس، آیتمهای آن را بصورت تصادفی مرتب کنید.
من در پروژه اخیرم برای نمایش یک سری سوال مجبور بودم که در هر بار نمایش سوالات، لیست را به صورت رندوم مرتب کنم و به کاربر نمایش بدم. برای حصول این مهم، یک extension method به شکل زیر نوشتم:
در این تابع که اسمش را Shuffle گذاشتم، با دریافت یک لیست از نوع T، آیتمهای درون لیست را به صورت تصادفی مرتب میکند.
مثال :
در این مثال لیست x که از نوع int میباشد پس از فراخوانی Shuffle به یک لیست نامرتب تبدیل میشود که نحوه چیدمان در هر بار فراخوانی، تصادفی خواهد بود.
من در پروژه اخیرم برای نمایش یک سری سوال مجبور بودم که در هر بار نمایش سوالات، لیست را به صورت رندوم مرتب کنم و به کاربر نمایش بدم. برای حصول این مهم، یک extension method به شکل زیر نوشتم:
public static class RandomExtentions { public static void Shuffle<T>(this IList<T> list) { Random rng = new Random(); Thread.Sleep(100); int n = list.Count; while (n > 1) { n--; int k = rng.Next(n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; } } }
مثال :
var x =new List<int>(); x.Add(1); x.Add(2); x.Add(3); x.Add(4); x.Add(5); x.Shuffle();
یکی دیگر از تغییرات ASP.NET Core با نگارشهای قبلی آن، تغییرات اساسی در مورد نحوهی کار با تنظیمات برنامه و فایلهای مرتبط با آنها است. در ASP.NET Core میتوانید:
- تنظیمات برنامه را از چندین منبع مختلف خوانده و آنها را یکی کنید.
- تنظیمات را بر اساس تنظیمات مختلف محیطی برنامه، بارگذاری کنید.
- امکان نگاشت اطلاعات خوانده شدهی از فایلهای کانفیگ به کلاسها پیش بینی شدهاست.
- امکان بارگذاری مجدد فایلهای کانفیگ درصورت تغییر، بدون ریاستارت کل برنامه وجود دارد.
- امکان تزریق وابستگیهای تنظیمات برنامه، به قسمتهای مختلف آن پیش بینی شدهاست.
نصب پیشنیاز خواندن تنظیمات برنامه از یک فایل JSON
برای شروع به کار با خواندن تنظیمات برنامه در ASP.NET Core، نیاز است ابتدا بستهی نیوگت Microsoft.Extensions.Configuration.Json را نصب کنیم.
برای این منظور بر روی گره references کلیک راست کرده و گزینهی manage nuget packages را انتخاب کنید. سپس در برگهی browse آن Microsoft.Extensions.Configuration.Json را جستجو کرده و نصب نمائید:
البته همانطور که در تصویر مشاهده میکنید، اگر صرفا Microsoft.Extensions.Configuration را جستجو کنید (بدون ذکر JSON)، بستههای مرتبط با خواندن فایلهای کانفیگ از نوع XML و یا حتی INI را هم خواهید یافت.
انجام این مراحل معادل هستند با افزودن یک سطر ذیل به فایل project.json برنامه:
افزودن یک فایل کانفیگ JSON دلخواه
بر روی پروژه کلیک راست کرده و از طریق منوی add->new item یک فایل خالی جدید را به نام appsettings.json ایجاد کنید (نام این فایل دلخواه است)؛ با این محتوا:
در نگارشهای پیشین ASP.NET که از web.config برای تعریف تنظیمات برنامه استفاده میشد، حالت پیش فرض ذکر تنظیمات برنامه میتوانست تنها یک سطحی و با ساختار ذیل باشد (البته امکان کدنویسی و نوشتن مداخل سفارشی هم وجود داشت؛ ولی حالت پیش فرض appSettings، تنها key/valueهای یک سطحی هستند):
اما اکنون یک فایل JSON را با هر تعداد سطح مورد نیاز میتوان تعریف و استفاده کرد و برای اینکار نیازی به نوشتن کدهای سفارشی تعریف مداخل خاص، وجود ندارد.
در فایل JSON فوق، نمونهای از key/valueها، آرایهها و اطلاعات چندین سطحی را مشاهده میکنید.
خواندن فایل تنظیمات appsettings.json در برنامه
پس از نصب پیشنیاز خواندن فایلهای کانفیگ از نوع JSON، به فایل آغازین برنامه مراجعه کرده و سازندهی جدیدی را به آن اضافه کنید:
در اینجا نحوهی خواندن فایل کانفیگ جدید appsettings.json را مشاهده میکنید. چند نکته در اینجا حائز اهمیت هستند:
الف) این خواندن، در سازندهی کلاس آغازین برنامه و پیش از تمام تنظیمات دیگر باید انجام شود.
ب) جهت در معرض دید قرار دادن اطلاعات خوانده شده، آنرا به یک خاصیت عمومی انتساب دادهایم.
ج) متد SetBasePath جهت مشخص کردن محل یافتن فایل appsettings.json ذکر شدهاست. این اطلاعات را میتوان از سرویس توکار IHostingEnvironment و خاصیت ContentRootPath آن دریافت کرد. همانطور که ملاحظه میکنید، این تزریق وابستگی نیز به صورت خودکار توسط ASP.NET Core مدیریت میشود.
دسترسی به تنظیمات خوانده شده توسط اینترفیس IConfigurationRoot
تا اینجا موفق شدیم تا تنظیمات خوانده شده را به خاصیت عمومی Configuration از نوع IConfigurationRoot انتساب دهیم. اما ساختار ذخیره شدهی در این اینترفیس به چه صورتی است؟
همانطور که مشاهده میکنید، هر سطح از سطح قبلی آن با : جدا شدهاست. همچنین اعضای آرایه، دارای ایندکسهای 0: و 1: و 2: هستند. بنابراین برای خواندن این اطلاعات میتوان نوشت:
خاصیت Configuration نیز در نهایت بر اساس key/valueها کار میکند و این keyها اگر چند سطحی بودند، با : از هم جدا میشوند و اگر نیاز به دسترسی اعضای خاصی از آرایهها وجود داشت میتوان آن ایندکس خاص را در انتهای زنجیره ذکر کرد. همچنین در اینجا نحوهی استخراج تمام اعضای یک آرایه را نیز مشاهده میکنید.
یک نکته: خاصیت Configuration، دارای متد GetValue نیز هست که توسط آن میتوان نوع مقدار دریافتی و یا حتی مقدار پیش فرضی را در صورت عدم وجود این key، مشخص کرد:
در متد GetValue، آرگومان جنریک آن، یک کلاس را نیز میپذیرد. یعنی میتوان خواص تو در توی مشخص شدهی با : را به یک کلاس نیز نگاشت کرد. در اینجا مقدار کلید معرفی شده، اولین سطحی خواهد بود که باید این اطلاعات از آن استخراج و نگاشت شوند.
سرویس IConfigurationRoot قابل تزریق است
در قسمت قبل، سرویسها و تزریق وابستگیها را بررسی کردیم. نکتهی جالبی را که میتوان به آن اضافه کرد، قابلیت تزریق خاصیت عمومی
به تمام قسمتهای برنامه است. برای نمونه در همان مثال قسمت قبل، قصد داریم تنظیمات برنامه را در لایه سرویس آن خوانده و مورد استفاده قرار دهیم. برای اینکار باید مراحل ذیل طی شوند:
الف) اعلام موجودیت IConfigurationRoot به IoC Container
اگر از استراکچرمپ استفاده میکنید، باید مشخص کنید، زمانیکه IConfigurationRoot درخواست شد، آنرا چگونه باید از خاصیت مرتبط با آن دریافت کند:
و یا اگر از همان IoC Container توکار ASP.NET Core استفاده میکنید، روش انجام اینکار در متد ConfigureServices به صورت زیر است:
طول عمر آن هم singleton مشخص شدهاست تا تنها یکبار وهله سازی و سپس کش شود (مناسب برای کار با تنظیمات سراسری برنامه).
ب) فایل project.json کتابخانهی Core1RtmEmptyTest.Services را گشوده و وابستگی Microsoft.Extensions.Configuration.Abstractions را به آن اضافه کنید:
این وابستگی امکان دسترسی به اینترفیس IConfigurationRoot را در اسمبلیهای دیگر میسر میکند.
ج) سپس فایل MessagesService.cs را گشوده و این اینترفیس را به سازندهی سرویس MessagesService تزریق میکنیم:
در ادامه، نحوهی استفادهی از آن، همانند نکاتی است که در قسمت «دسترسی به تنظیمات خوانده شده توسط اینترفیس IConfigurationRoot» عنوان شد.
اکنون اگر برنامه را اجرا کنید، با توجه به اینکه میان افزار Run از این سرویس سفارشی استفاده میکند:
چنین خروجی را خواهیم داشت:
خواندن تنظیمات از حافظه
الزاما نیازی به استفاده از فایلهای JSON و یا XML در اینجا وجود ندارد. ابتداییترین حالت کار با بستهی Microsoft.Extensions.Configuration، متد AddInMemoryCollection آن است که در اینجا میتوان لیستی از key/valueها را ذکر کرد:
و نحوهی کار با آن نیز همانند قبل است:
امکان بازنویسی تنظیمات انجام شده، بسته به شرایط محیطی
در اینجا محدود به یک فایل JSON و یک فایل تنظیمات برنامه، نیستیم. برای کار با ConfigurationBuilder میتوان از Fluent interface آن استفاده کرد و به هر تعدادی که نیاز بود، متدهای خواندن از فایلهای کانفیگ دیگر را اضافه کرد:
و نکتهی مهم اینجا است که تنظیمات فایل دوم، تنظیمات مشابه فایل اول را بازنویسی میکند.
برای مثال در اینجا آخرین AddJsonFile تعریف شده، بنابر متغیر محیطی فعلی به appsettings.development.json تفسیر شده و در صورت وجود این فایل (با توجه به optional بودن آن) اطلاعات آن دریافت گردیده و اطلاعات مشابه فایل appsettings.json قبلی را بازنویسی میکند.
امکان دسترسی به متغیرهای محیطی سیستم عامل
در انتهای زنجیرهی ConfigurationBuilder میتوان متد AddEnvironmentVariables را نیز ذکر کرد:
این متد سبب میشود تا تمام اطلاعات قسمت Environment سیستم عامل، به مجموعهی تنظیمات جاری اضافه شوند (در صورت نیاز) که نمونهای از آنرا در تصویر ذیل مشاهده میکنید:
امکان نگاشت تنظیمات برنامه به کلاسهای متناظر
کار کردن با key/valueهای رشتهای، هرچند روش پایهای استفادهی از تنظیمات برنامه است، اما آنچنان refactoring friendly نیست. در ASP.NET Core امکان تعریف تنظیمات strongly typed نیز پیش بینی شدهاست. برای این منظور باید مراحل زیر طی شوند:
به عنوان نمونه تنظیمات فرضی smtp ذیل را به انتهای فایل appsettings.json اضافه کنید:
مثال جاری که بر اساس ASP.NET Core Web Application و با قالب خالی آن ایجاد شدهاست، دارای نام فرضی Core1RtmEmptyTest است. در همین پروژه بر روی پوشهی src کلیک راست کرده و گزینهی Add new project را انتخاب کنید و سپس یک پروژهی جدید از نوع NET Core -> Class library. را به آن با نام Core1RtmEmptyTest.ViewModels اضافه کنید (تصویر ذیل).
در این کتابخانهی جدید که محل نگهداری ViewModelهای برنامه خواهد بود، کلاس معادل قسمت smtp فایل config فوق را اضافه کنید:
از این جهت این کلاس را در یک library جداگانه قرار دادهایم تا بتوان از آن در لایهی سرویس و همچنین خود برنامه استفاده کرد. اگر این کلاس را در برنامهی اصلی قرار میدادیم، امکان دسترسی به آن در لایهی سرویس میسر نمیشد.
سپس به پروژهی Core1RtmEmptyTest مراجعه کرده و بر روی گره references آن کلیک راست کنید. در اینجا گزینهی add reference را انتخاب کرده و سپس Core1RtmEmptyTest.ViewModels را انتخاب کنید، تا اسمبلی آنرا بتوان در پروژهی جاری استفاده کرد.
انجام اینکار معادل است با افزودن یک سطر ذیل به فایل project.json پروژه:
اکنون با فرض وجود تنظیمات خواندن فایل appsettings.json در سازندهی کلاس آغازین برنامه، نیاز است بستهی نیوگت Microsoft.Extensions.Configuration.Binder را نصب کنید:
و سپس در کلاس آغازین برنامه و متد ConfigureServices آن، نحوهی نگاشت قسمت Smtp فایل کانفیگ را مشخص کنید:
در اینجا مشخص شدهاست که کار وهله سازی کلاس SmtpConfig بر اساس اطلاعات قسمت smtp فایل کانفیگ تامین میشود. متغیر Configuration ایی که در اینجا استفاده شدهاست همان خاصیت عمومی public IConfigurationRoot Configuration کلاس آغازین برنامه است.
سپس برای استفاده از این تنظیمات strongly typed (برای نمونه در لایه سرویس برنامه)، ابتدا ارجاعی را به پروژهی Core1RtmEmptyTest.ViewModels به لایهی سرویس برنامه اضافه میکنیم (بر روی گره references آن کلیک راست کنید. در اینجا گزینهی add reference را انتخاب کرده و سپس Core1RtmEmptyTest.ViewModels را انتخاب کنید).
در ادامه نیاز است بستهی نیوگت جدیدی را به نام Microsoft.Extensions.Options به لایهی سرویس برنامه اضافه کنیم. به این ترتیب قسمت وابستگیهای فایل project.json این لایه چنین شکلی را پیدا میکند:
پس از ذخیره سازی این کلاس و بازیابی خودکار وابستگیهای آن، اکنون برای دسترسی به این تنظیم باید از اینترفیس ویژهی IOptions استفاده کرد (به همین جهت بستهی جدید نیوگت Microsoft.Extensions.Options را نصب کردیم):
همانطور که ملاحظه میکنید <IOptions<SmtpConfig به سازندهی کلاس تزریق شدهاست و سپس از طریق خاصیت Value آن میتوان به تمام اطلاعات کلاس SmtpConfig به شکل strongly typed دسترسی یافت.
اکنون اگر برنامه را جرا کنید، این خروجی را میتوان مشاهده کرد (که در آن آدرس Server دریافت شدهی از فایل کانفیگ نیز مشخص است):
البته همانطور که در قسمت قبل نیز عنوان شد، این تزریق وابستگیها در تمام قسمتهای برنامه کار میکند. برای مثال در کنترلرها هم میتوان <IOptions<SmtpConfig را به همین نحو تزریق کرد.
نحوهی واکنش به تغییرات فایلهای کانفیگ
در نگارشهای قبلی ASP.NET، هر تغییری در فایل web.config، سبب ریاستارت شدن کل برنامه میشد که این مساله نیز خود سبب بروز مشکلات زیادی مانند از دست رفتن سشن تمام کاربران میشد.
در ASP.NET Core، برنامهی وب ما دیگر متکی به فایل web.config نبوده و همچنین میتوان چندین و چند نوع فایل config داشت. به علاوه در اینجا متدهای مرتبط معرفی فایلهای کانفیگ دارای پارامتر مخصوص reloadOnChange نیز هستند:
این پارامتر در صورت true بودن، به صورت خودکار سبب بارگذاری مجدد اطلاعات فایل کانفیگ میشود (بدون ریاستارت کل برنامه).
- تنظیمات برنامه را از چندین منبع مختلف خوانده و آنها را یکی کنید.
- تنظیمات را بر اساس تنظیمات مختلف محیطی برنامه، بارگذاری کنید.
- امکان نگاشت اطلاعات خوانده شدهی از فایلهای کانفیگ به کلاسها پیش بینی شدهاست.
- امکان بارگذاری مجدد فایلهای کانفیگ درصورت تغییر، بدون ریاستارت کل برنامه وجود دارد.
- امکان تزریق وابستگیهای تنظیمات برنامه، به قسمتهای مختلف آن پیش بینی شدهاست.
نصب پیشنیاز خواندن تنظیمات برنامه از یک فایل JSON
برای شروع به کار با خواندن تنظیمات برنامه در ASP.NET Core، نیاز است ابتدا بستهی نیوگت Microsoft.Extensions.Configuration.Json را نصب کنیم.
برای این منظور بر روی گره references کلیک راست کرده و گزینهی manage nuget packages را انتخاب کنید. سپس در برگهی browse آن Microsoft.Extensions.Configuration.Json را جستجو کرده و نصب نمائید:
البته همانطور که در تصویر مشاهده میکنید، اگر صرفا Microsoft.Extensions.Configuration را جستجو کنید (بدون ذکر JSON)، بستههای مرتبط با خواندن فایلهای کانفیگ از نوع XML و یا حتی INI را هم خواهید یافت.
انجام این مراحل معادل هستند با افزودن یک سطر ذیل به فایل project.json برنامه:
{ "dependencies": { //same as before "Microsoft.Extensions.Configuration.Json": "1.0.0" },
افزودن یک فایل کانفیگ JSON دلخواه
بر روی پروژه کلیک راست کرده و از طریق منوی add->new item یک فایل خالی جدید را به نام appsettings.json ایجاد کنید (نام این فایل دلخواه است)؛ با این محتوا:
{ "Key1": "Value1", "Auth": { "Users": [ "Test1", "Test2", "Test3" ] }, "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } }
<appSettings> <add key="Logging-IncludeScopes" value="false" /> <add key="Logging-Level-Default" value="verbose" /> <add key="Logging-Level-System" value="Information" /> <add key="Logging-Level-Microsoft" value="Information" /> </appSettings>
در فایل JSON فوق، نمونهای از key/valueها، آرایهها و اطلاعات چندین سطحی را مشاهده میکنید.
خواندن فایل تنظیمات appsettings.json در برنامه
پس از نصب پیشنیاز خواندن فایلهای کانفیگ از نوع JSON، به فایل آغازین برنامه مراجعه کرده و سازندهی جدیدی را به آن اضافه کنید:
public class Startup { public IConfigurationRoot Configuration { set; get; } public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json"); Configuration = builder.Build(); }
الف) این خواندن، در سازندهی کلاس آغازین برنامه و پیش از تمام تنظیمات دیگر باید انجام شود.
ب) جهت در معرض دید قرار دادن اطلاعات خوانده شده، آنرا به یک خاصیت عمومی انتساب دادهایم.
ج) متد SetBasePath جهت مشخص کردن محل یافتن فایل appsettings.json ذکر شدهاست. این اطلاعات را میتوان از سرویس توکار IHostingEnvironment و خاصیت ContentRootPath آن دریافت کرد. همانطور که ملاحظه میکنید، این تزریق وابستگی نیز به صورت خودکار توسط ASP.NET Core مدیریت میشود.
دسترسی به تنظیمات خوانده شده توسط اینترفیس IConfigurationRoot
تا اینجا موفق شدیم تا تنظیمات خوانده شده را به خاصیت عمومی Configuration از نوع IConfigurationRoot انتساب دهیم. اما ساختار ذخیره شدهی در این اینترفیس به چه صورتی است؟
همانطور که مشاهده میکنید، هر سطح از سطح قبلی آن با : جدا شدهاست. همچنین اعضای آرایه، دارای ایندکسهای 0: و 1: و 2: هستند. بنابراین برای خواندن این اطلاعات میتوان نوشت:
var key1 = Configuration["Key1"]; var user1 = Configuration["Auth:Users:0"]; var authUsers = Configuration.GetSection("Auth:Users").GetChildren().Select(x => x.Value).ToArray(); var loggingIncludeScopes = Configuration["Logging:IncludeScopes"]; var loggingLoggingLogLevelDefault = Configuration["Logging:LogLevel:Default"];
یک نکته: خاصیت Configuration، دارای متد GetValue نیز هست که توسط آن میتوان نوع مقدار دریافتی و یا حتی مقدار پیش فرضی را در صورت عدم وجود این key، مشخص کرد:
var val = Configuration.GetValue<int>("key-name", defaultValue: 10);
سرویس IConfigurationRoot قابل تزریق است
در قسمت قبل، سرویسها و تزریق وابستگیها را بررسی کردیم. نکتهی جالبی را که میتوان به آن اضافه کرد، قابلیت تزریق خاصیت عمومی
public class Startup { public IConfigurationRoot Configuration { set; get; }
الف) اعلام موجودیت IConfigurationRoot به IoC Container
اگر از استراکچرمپ استفاده میکنید، باید مشخص کنید، زمانیکه IConfigurationRoot درخواست شد، آنرا چگونه باید از خاصیت مرتبط با آن دریافت کند:
var container = new Container(); container.Configure(config => { config.For<IConfigurationRoot>().Singleton().Use(() => Configuration);
public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddSingleton<IConfigurationRoot>(provider => { return Configuration; });
ب) فایل project.json کتابخانهی Core1RtmEmptyTest.Services را گشوده و وابستگی Microsoft.Extensions.Configuration.Abstractions را به آن اضافه کنید:
{ "dependencies": { //same as before "Microsoft.Extensions.Configuration.Abstractions": "1.0.0" }
ج) سپس فایل MessagesService.cs را گشوده و این اینترفیس را به سازندهی سرویس MessagesService تزریق میکنیم:
public interface IMessagesService { string GetSiteName(); } public class MessagesService : IMessagesService { private readonly IConfigurationRoot _configurationRoot; public MessagesService(IConfigurationRoot configurationRoot) { _configurationRoot = configurationRoot; } public string GetSiteName() { var key1 = _configurationRoot["Key1"]; return $"DNT {key1}"; } }
اکنون اگر برنامه را اجرا کنید، با توجه به اینکه میان افزار Run از این سرویس سفارشی استفاده میکند:
public void Configure( IApplicationBuilder app, IHostingEnvironment env, IMessagesService messagesService) { app.Run(async context => { var siteName = messagesService.GetSiteName(); await context.Response.WriteAsync($"Hello {siteName}"); }); }
خواندن تنظیمات از حافظه
الزاما نیازی به استفاده از فایلهای JSON و یا XML در اینجا وجود ندارد. ابتداییترین حالت کار با بستهی Microsoft.Extensions.Configuration، متد AddInMemoryCollection آن است که در اینجا میتوان لیستی از key/valueها را ذکر کرد:
var builder = new ConfigurationBuilder() .AddInMemoryCollection(new[] { new KeyValuePair<string,string>("the-key", "the-value"), });
var theValue = Configuration["the-key"];
امکان بازنویسی تنظیمات انجام شده، بسته به شرایط محیطی
در اینجا محدود به یک فایل JSON و یک فایل تنظیمات برنامه، نیستیم. برای کار با ConfigurationBuilder میتوان از Fluent interface آن استفاده کرد و به هر تعدادی که نیاز بود، متدهای خواندن از فایلهای کانفیگ دیگر را اضافه کرد:
public class Startup { public IConfigurationRoot Configuration { set; get; } public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddInMemoryCollection(new[] { new KeyValuePair<string,string>("the-key", "the-value"), }) .AddJsonFile("appsettings.json", reloadOnChange: true, optional: false) .AddJsonFile($"appsettings.{env}.json", optional: true); Configuration = builder.Build(); }
برای مثال در اینجا آخرین AddJsonFile تعریف شده، بنابر متغیر محیطی فعلی به appsettings.development.json تفسیر شده و در صورت وجود این فایل (با توجه به optional بودن آن) اطلاعات آن دریافت گردیده و اطلاعات مشابه فایل appsettings.json قبلی را بازنویسی میکند.
امکان دسترسی به متغیرهای محیطی سیستم عامل
در انتهای زنجیرهی ConfigurationBuilder میتوان متد AddEnvironmentVariables را نیز ذکر کرد:
var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables();
امکان نگاشت تنظیمات برنامه به کلاسهای متناظر
کار کردن با key/valueهای رشتهای، هرچند روش پایهای استفادهی از تنظیمات برنامه است، اما آنچنان refactoring friendly نیست. در ASP.NET Core امکان تعریف تنظیمات strongly typed نیز پیش بینی شدهاست. برای این منظور باید مراحل زیر طی شوند:
به عنوان نمونه تنظیمات فرضی smtp ذیل را به انتهای فایل appsettings.json اضافه کنید:
{ "Key1": "Value1", "Auth": { "Users": [ "Test1", "Test2", "Test3" ] }, "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } }, "Smtp": { "Server": "0.0.0.1", "User": "user@company.com", "Pass": "123456789", "Port": "25" } }
در این کتابخانهی جدید که محل نگهداری ViewModelهای برنامه خواهد بود، کلاس معادل قسمت smtp فایل config فوق را اضافه کنید:
namespace Core1RtmEmptyTest.ViewModels { public class SmtpConfig { public string Server { get; set; } public string User { get; set; } public string Pass { get; set; } public int Port { get; set; } } }
سپس به پروژهی Core1RtmEmptyTest مراجعه کرده و بر روی گره references آن کلیک راست کنید. در اینجا گزینهی add reference را انتخاب کرده و سپس Core1RtmEmptyTest.ViewModels را انتخاب کنید، تا اسمبلی آنرا بتوان در پروژهی جاری استفاده کرد.
انجام اینکار معادل است با افزودن یک سطر ذیل به فایل project.json پروژه:
{ "dependencies": { // same as before "Core1RtmEmptyTest.ViewModels": "1.0.0-*" },
و سپس در کلاس آغازین برنامه و متد ConfigureServices آن، نحوهی نگاشت قسمت Smtp فایل کانفیگ را مشخص کنید:
public IServiceProvider ConfigureServices(IServiceCollection services) { services.Configure<SmtpConfig>(options => Configuration.GetSection("Smtp").Bind(options));
سپس برای استفاده از این تنظیمات strongly typed (برای نمونه در لایه سرویس برنامه)، ابتدا ارجاعی را به پروژهی Core1RtmEmptyTest.ViewModels به لایهی سرویس برنامه اضافه میکنیم (بر روی گره references آن کلیک راست کنید. در اینجا گزینهی add reference را انتخاب کرده و سپس Core1RtmEmptyTest.ViewModels را انتخاب کنید).
در ادامه نیاز است بستهی نیوگت جدیدی را به نام Microsoft.Extensions.Options به لایهی سرویس برنامه اضافه کنیم. به این ترتیب قسمت وابستگیهای فایل project.json این لایه چنین شکلی را پیدا میکند:
"dependencies": { "Core1RtmEmptyTest.ViewModels": "1.0.0-*", "Microsoft.Extensions.Configuration.Abstractions": "1.0.0", "Microsoft.Extensions.Options": "1.0.0", "NETStandard.Library": "1.6.0" }
public interface IMessagesService { string GetSiteName(); } public class MessagesService : IMessagesService { private readonly IConfigurationRoot _configurationRoot; private readonly IOptions<SmtpConfig> _settings; public MessagesService(IConfigurationRoot configurationRoot, IOptions<SmtpConfig> settings) { _configurationRoot = configurationRoot; _settings = settings; } public string GetSiteName() { var key1 = _configurationRoot["Key1"]; var server = _settings.Value.Server; return $"DNT {key1} - {server}"; } }
اکنون اگر برنامه را جرا کنید، این خروجی را میتوان مشاهده کرد (که در آن آدرس Server دریافت شدهی از فایل کانفیگ نیز مشخص است):
البته همانطور که در قسمت قبل نیز عنوان شد، این تزریق وابستگیها در تمام قسمتهای برنامه کار میکند. برای مثال در کنترلرها هم میتوان <IOptions<SmtpConfig را به همین نحو تزریق کرد.
نحوهی واکنش به تغییرات فایلهای کانفیگ
در نگارشهای قبلی ASP.NET، هر تغییری در فایل web.config، سبب ریاستارت شدن کل برنامه میشد که این مساله نیز خود سبب بروز مشکلات زیادی مانند از دست رفتن سشن تمام کاربران میشد.
در ASP.NET Core، برنامهی وب ما دیگر متکی به فایل web.config نبوده و همچنین میتوان چندین و چند نوع فایل config داشت. به علاوه در اینجا متدهای مرتبط معرفی فایلهای کانفیگ دارای پارامتر مخصوص reloadOnChange نیز هستند:
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
در کتابخانهی iTextSharp به جهت سازگاری با کتابخانهی اصلی، رنگها را بر اساس کلاسی به نام BaseColor تعریف کردهاند؛ که ایکاش به جای اینکار، همه را با کلاس Color فضای نام استاندارد System.Drawing جایگزین میکردند. همین مشکل با فونت هم هست. یک کلاس فونت در فضای نام iTextSharp.text وجود دارد به علاوه کلاس فونت تعریف شده در فضای نام استاندارد System.Drawing دات نت؛ که خیلی سریع میتواند به خطای کامپایل زیر ختم شود:
'Font' is an ambiguous reference between 'iTextSharp.text.Font' and 'System.Drawing.Font'
و در نهایت مجبور خواهیم شد که به صورت صریح علام کنیم، iTextSharp.text.Font منظور ما است و نه آن یکی.
در کل اگر با کلاس Color فضای نام استاندارد System.Drawing بیشتر راحت هستید به صورت زیر هم میتوان رنگهای متداول را مورد استفاده قرار داد:
تعریف رنگها بر اساس نام آنها:
var color = new BaseColor(Color.LightGray);
تعریف رنگها بر اساس مقادیر Hex متداول در المانهای HTML :
var color = new BaseColor(ColorTranslator.FromHtml("#1C5E55"));
این نکتهای است که شاید خیلیها از وجود آن بیاطلاع باشند. به صورت پیش فرض در کلاس استاندارد ColorTranslator، امکان دریافت رنگهای بکاررفته در المانهای HTML به کمک متد ColorTranslator.FromHtml مهیا است.
البته اگر زمانی خواستید خودتان این متد را پیاده سازی کنید، نکتهی آن به صورت زیر است:
string htmlColor = "#1C5E55";
int x = Convert.ToInt32(htmlColor.Replace("#", "0x"), 16);
byte red = (byte)((x & 0xff0000) >> 16);
byte green = (byte)((x & 0xff00) >> 8);
byte blue = (byte)(x & 0xff);
سپس کلاسهای Color و همچنین BaseColor امکان پذیرش این اجزای حاصل را دارند (به کمک متد Color.FromRgb و یا سازندهی BaseColor).
علت ذکر ColorTranslator.FromHtml به این بر میگردد که ترکیبات رنگ جالبی را میتوان از جداول HTML ایی موجود در سایتهای مختلف ایده گرفت و یا حتی از قالبهای پیش فرض GridView در ASP.NET مثلا.
اکنون برای ساخت جدولی مانند شکل فوق، به ازای هر سلولی که مشاهده میکنید باید یکبار BorderColor و BackgroundColor تنظیم شوند. رنگ متن هم از رنگ فونت دریافت میشود:
var pdfCell = new PdfPCell(new Phrase(Text, Font))
{
RunDirection = ...,
BorderColor = ...,
BackgroundColor = ...
};
نظرات مطالب
CheckBoxList در ASP.NET MVC
با سلام مجدد. با تشکر از جواب شما. من مشکلمو توی تابع Edit() حل کردم. به این صورت که ابتدا همه نقشها رو از پایگاه داده میخونم و توی یه List<SelectListItem> نگهداری میکنم. و برای هر کاربر هم نقش هاشو میخونم. سپس مقایسه میکنم که هر نقش که کاربر داره رو توی اون List<SelectListItem> خاصیت Selected رو true میکنم. این روش جواب داد
اما ببخشید متوجه نشدم روشی که شما میگین چطوری هستش. فکر میکنم روش شما سادهتر باشه. کدهای من اینه:
اما ببخشید متوجه نشدم روشی که شما میگین چطوری هستش. فکر میکنم روش شما سادهتر باشه. کدهای من اینه:
//this method is in "UserController" class that select all available roles form database [NonAction] public List<SelectListItem> GetRoleList() { var usrMgmt = new UserManagement(); var RoleList = new List<SelectListItem>(); foreach (KeyValuePair<string, string> pair in usrMgmt.GetAllRoles()) { RoleList.Add(new SelectListItem { Text = pair.Value, Value = pair.Key, Selected = false }); } return RoleList; } //--------------------------------------------------------------------------- //this method is in "UserController" class that use for editing a user [HttpGet] public ActionResult Edit(string Username) { var roles = GetRoleList(); UserManagement usrMgmt = new UserManagement(); var query = usrMgmt.Select(Username); foreach (string role in query.Roles) { int index = roles.FindIndex(x=>x.Value == role); roles[index].Selected = true; } ViewBag.Roles = roles; return View(query); } //-------------------------------------------------------------- //this method is in "UserManagement" class in model layer, return a //dictionary<string,string> that first string is english role name and second one is persian role //name public Dictionary<string,string> GetAllRoles() { Dictionary<string, string> roles = new Dictionary<string, string>(); var db = new SamaEntities(); string query = ""; string[] roleNames = Roles.GetAllRoles(); foreach (string role in roleNames) { query = db.aspnet_Roles.Single(x => x.RoleName == role).Description; roles.Add(role, query); } return roles; }
اگر از روش Fluent-API برای تنظیم و افزودن نگاشتهای کلاسها استفاده کنیم، با زیاد شدن آنها ممکن است در این بین، افزودن یکی فراموش شود یا کلا اضافه کردن دستی آنها در متد OnModelCreating آنچنان جالب نیست. میشود اینکار را به کمک Reflection سادهتر و خودکار کرد:
در این متد، در یک اسمبلی مشخص و فضای نامی در آن، به دنبال کلاسهایی از نوع EntityTypeConfiguration خواهیم گشت. در ادامه این کلاسها وهله سازی شده و به صورت خودکار به modelBuilder اضافه میشوند.
یک مثال کامل که بیانگر نحوه استفاده از متد فوق است:
در این مثال، در متد OnModelCreating بجای اضافه کردن دستی تک تک تنظیمات تعریف شده، از متد loadEntityConfigurations جهت یافتن آنها در اسمبلی جاری و فضای نام مشخصی به نام EFGeneral استفاده شده است.
void loadEntityConfigurations(Assembly asm, DbModelBuilder modelBuilder, string nameSpace) { var configurations = asm.GetTypes() .Where(type => type.BaseType != null && type.Namespace == nameSpace && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)) .ToList(); configurations.ForEach(type => { dynamic instance = Activator.CreateInstance(type); modelBuilder.Configurations.Add(instance); }); }
یک مثال کامل که بیانگر نحوه استفاده از متد فوق است:
using System; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Data.Entity.ModelConfiguration; using System.Linq; using System.Reflection; namespace EFGeneral { public class User { public int UserNumber { get; set; } public string Name { get; set; } } public class UserConfig : EntityTypeConfiguration<User> { public UserConfig() { this.HasKey(x => x.UserNumber); this.Property(x => x.Name).HasMaxLength(450).IsRequired(); } } public class MyContext : DbContext { public DbSet<User> Users { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { // modelBuilder.Configurations.Add(new UserConfig()); var asm = Assembly.GetExecutingAssembly(); loadEntityConfigurations(asm, modelBuilder, "EFGeneral"); } void loadEntityConfigurations(Assembly asm, DbModelBuilder modelBuilder, string nameSpace) { var configurations = asm.GetTypes() .Where(type => type.BaseType != null && type.Namespace == nameSpace && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)) .ToList(); configurations.ForEach(type => { dynamic instance = Activator.CreateInstance(type); modelBuilder.Configurations.Add(instance); }); } } public class Configuration : DbMigrationsConfiguration<MyContext> { public Configuration() { AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = true; } protected override void Seed(MyContext context) { context.Users.Add(new User { Name = "name-1" }); context.Users.Add(new User { Name = "name-2" }); context.Users.Add(new User { Name = "name-3" }); base.Seed(context); } } public static class Test { public static void RunTests() { Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>()); using (var context = new MyContext()) { var user1 = context.Users.Find(1); if (user1 != null) Console.WriteLine(user1.Name); } } } }
در مطلب «بررسی تفصیلی رابطه Many-to-Many در EF Code first» نحوهی مدلسازی رابطهی چند به چند را در EF 6.x بررسی کردیم. یک چنین رابطهای که به همراه مدیریت خودکار join table آن است (جدول BlogPostsJoinTags در شکل زیر)، در EF Core 1.0 RTM پشتیبانی نمیشود.
البته همیشه درخواست کنترل این جدول واسط که کاملا از دیدگاه ORMها (تمام آنها) مخفی است، وجود داشتهاست و قرار است این پشتیبانی توسط مفهوم ویژهای به نام shadow properties به نگارشهای بعدی EF Core اضافه شود.
اما فعلا در نگارش اول آن، توصیه شدهاست که رابطهی many-to-many را به صورت دو رابطهی one-to-many مدلسازی کنید که در ادامه آنرا بررسی خواهیم کرد. بنابراین پیشنیاز آن مطالعهی مطلب «شروع به کار با EF Core 1.0 - قسمت 7 - بررسی رابطهی One-to-Many» میباشد.
مدلسازی موجودیتهای یک رابطهی چند به چند در EF Core 1.0 RTM توسط Fluent API
در اینجا نحوهی مدلسازی یک رابطهی چند به چند را توسط دو رابطهی one-to-many مشاهده میکنید. تنها تفاوت آن با EF 6.x، قید صریح جدول واسط BlogPostsJoinTags است که یک چنین جدولی در EF 6.x به صورت خودکار تشکیل شده و مدیریت میشود و کاملا از دید برنامه مخفی است. اما در اینجا (در نگارش اول EF Core) نیاز است این جدول واسط را از حالت مخفی خارج کرد و سپس دو رابطهی یک به چند را به جداول مطالب و تگهای آنها تشکیل داد.
مزیت این حالت، دسترسی کامل به طراحی جدول واسط، توسط برنامه است. بنابراین اگر به هر دلیلی نیاز به افزودن خواص بیشتری به این جدول ویژه دارید، اکنون امکان آن میسر است.
پس از آن باید Context برنامه را نیز جهت ذکر صریح رابطهی یک به چند، ویرایش کرد. با متدهای HasOne و WithMany در مطلب قبل «شروع به کار با EF Core 1.0 - قسمت 7 - بررسی رابطهی One-to-Many» آشنا شدیم و علت نیاز به ذکر صریح آنها، وجود بیش از یک سر رابطه در جدول واسط BlogPostsJoinTags است. در این حالت یافتن InversePropertyها توسط EF Core میسر نیست و حتما باید از یک طرف شروع و سمت دیگر را مشخص کرد.
به علاوه در اینجا تعریف یک composite key را هم بر روی خواص کلید خارجی جدول واسط مشاهده میکنید. وجود این کلید ترکیبی سبب خواهد شد که ملزم به ثبت هر دو Id (کلیدهای جداول مطلب و تگ) در حین ثبت در این جدول شویم (یا قید اجباری هر دو طرف رابطه).
مدلسازی موجودیتهای یک رابطهی چند به چند در EF Core 1.0 RTM توسط Data Annotations
در حالت مدلسازی توسط ویژگیها، ذکر InversePropertyها و همچنین ForeignKeyها مقداری واضحتر به نظر میرسند. به علاوه، یک سری از تنظیمات هم معادل data annotations ایی ندارند؛ مانند composite key تعریف شدهی بر روی خواص جدول واسط و همچنین ایندکسهای تعریف شده، که حتما باید توسط Fluent API تنظیم شوند.
همانطور که در مطلب «شروع به کار با EF Core 1.0 - قسمت 6 - تعیین نوعهای داده و ویژگیهای آنها» نیز عنوان شد، علت تنظیم MaxLength به عدد 450 این است که بتوان بر روی این ستون ایندکس ایجاد کرد.
نحوهی ثبت اطلاعات در دو رابطهی یک به چند به همراه جدول واسط
در EF 6.x، کار مقدار دهی Idهای جدول واسط به صورت خودکار انجام میشود. در اینجا این مقدار دهی را باید به صورت صریح انجام داد:
نحوهی واکشی اطلاعات به هم مرتبط در دو رابطهی یک به چند به همراه جدول واسط
در مورد متدهای Include و ThenInclude در مطلب «شروع به کار با EF Core 1.0 - قسمت 7 - بررسی رابطهی One-to-Many» پیشتر بحث شد.
در اینجا اگر میخواهیم به لیست تمام برچسبهای اولین مطلب دسترسی پیدا کنیم، ابتدا باید رابطهی جدول واسط را Include کنیم. سپس چون در همین سطح میخواهیم به سطح بعدی تگهای مرتبط برسیم، باید از متد الحاقی جدید ThenInclude استفاده کرد. در غیراینصورت در سطر بعدی، post1.BlogPostsJoinTags نال خواهد بود و همچنین حاوی لیست تگها نیز نخواهد بود.
یک نکتهی تکمیلی
وضعیت پشتیبانی از رابطهی many-to-many را همانند EF 6.x در EF Core، در اینجا میتوانید پیگیری کنید.
البته همیشه درخواست کنترل این جدول واسط که کاملا از دیدگاه ORMها (تمام آنها) مخفی است، وجود داشتهاست و قرار است این پشتیبانی توسط مفهوم ویژهای به نام shadow properties به نگارشهای بعدی EF Core اضافه شود.
اما فعلا در نگارش اول آن، توصیه شدهاست که رابطهی many-to-many را به صورت دو رابطهی one-to-many مدلسازی کنید که در ادامه آنرا بررسی خواهیم کرد. بنابراین پیشنیاز آن مطالعهی مطلب «شروع به کار با EF Core 1.0 - قسمت 7 - بررسی رابطهی One-to-Many» میباشد.
مدلسازی موجودیتهای یک رابطهی چند به چند در EF Core 1.0 RTM توسط Fluent API
در اینجا نحوهی مدلسازی یک رابطهی چند به چند را توسط دو رابطهی one-to-many مشاهده میکنید. تنها تفاوت آن با EF 6.x، قید صریح جدول واسط BlogPostsJoinTags است که یک چنین جدولی در EF 6.x به صورت خودکار تشکیل شده و مدیریت میشود و کاملا از دید برنامه مخفی است. اما در اینجا (در نگارش اول EF Core) نیاز است این جدول واسط را از حالت مخفی خارج کرد و سپس دو رابطهی یک به چند را به جداول مطالب و تگهای آنها تشکیل داد.
مزیت این حالت، دسترسی کامل به طراحی جدول واسط، توسط برنامه است. بنابراین اگر به هر دلیلی نیاز به افزودن خواص بیشتری به این جدول ویژه دارید، اکنون امکان آن میسر است.
public class Tags { public Tags() { BlogPostsJoinTags = new HashSet<BlogPostsJoinTags>(); } public int Id { get; set; } public string Name { get; set; } public virtual ICollection<BlogPostsJoinTags> BlogPostsJoinTags { get; set; } }
public class BlogPostsJoinTags { public virtual BlogPosts BlogPost { get; set; } public int BlogPostId { get; set; } public virtual Tags Tag { get; set; } public int TagId { get; set; } }
public class BlogPosts { public BlogPosts() { BlogPostsJoinTags = new HashSet<BlogPostsJoinTags>(); } public int Id { get; set; } public string Title { get; set; } public string Body { get; set; } public virtual ICollection<BlogPostsJoinTags> BlogPostsJoinTags { get; set; } }
به علاوه در اینجا تعریف یک composite key را هم بر روی خواص کلید خارجی جدول واسط مشاهده میکنید. وجود این کلید ترکیبی سبب خواهد شد که ملزم به ثبت هر دو Id (کلیدهای جداول مطلب و تگ) در حین ثبت در این جدول شویم (یا قید اجباری هر دو طرف رابطه).
public class MyDBDataContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Data Source=(local);Initial Catalog=testdb2;Integrated Security = true"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<BlogPosts>(entity => { entity.Property(e => e.Title) .IsRequired() .HasMaxLength(450); }); modelBuilder.Entity<Tags>(entity => { entity.Property(e => e.Name) .IsRequired() .HasMaxLength(450); }); modelBuilder.Entity<BlogPostsJoinTags>(entity => { entity.HasKey(e => new { e.TagId, e.BlogPostId }) .HasName("PK_dbo.BlogPostsJoinTags"); entity.HasIndex(e => e.BlogPostId) .HasName("IX_BlogPostId"); entity.HasIndex(e => e.TagId) .HasName("IX_TagId"); entity.HasOne(d => d.BlogPost) .WithMany(p => p.BlogPostsJoinTags) .HasForeignKey(d => d.BlogPostId) .HasConstraintName("FK_dbo.BlogPostsJoinTags_dbo.BlogPosts_BlogPostId"); entity.HasOne(d => d.Tag) .WithMany(p => p.BlogPostsJoinTags) .HasForeignKey(d => d.TagId) .HasConstraintName("FK_dbo.BlogPostsJoinTags_dbo.Tags_TagId"); }); } public virtual DbSet<BlogPosts> BlogPosts { get; set; } public virtual DbSet<BlogPostsJoinTags> BlogPostsJoinTags { get; set; } public virtual DbSet<Tags> Tags { get; set; } }
مدلسازی موجودیتهای یک رابطهی چند به چند در EF Core 1.0 RTM توسط Data Annotations
در حالت مدلسازی توسط ویژگیها، ذکر InversePropertyها و همچنین ForeignKeyها مقداری واضحتر به نظر میرسند. به علاوه، یک سری از تنظیمات هم معادل data annotations ایی ندارند؛ مانند composite key تعریف شدهی بر روی خواص جدول واسط و همچنین ایندکسهای تعریف شده، که حتما باید توسط Fluent API تنظیم شوند.
public class Tags { public Tags() { BlogPostsJoinTags = new HashSet<BlogPostsJoinTags>(); } public int Id { get; set; } [Required] [MaxLength(450)] public string Name { get; set; } [InverseProperty("Tag")] public virtual ICollection<BlogPostsJoinTags> BlogPostsJoinTags { get; set; } }
public class BlogPostsJoinTags { [ForeignKey("BlogPostId")] [InverseProperty("BlogPostsJoinTags")] public virtual BlogPosts BlogPost { get; set; } public int BlogPostId { get; set; } [ForeignKey("TagId")] [InverseProperty("BlogPostsJoinTags")] public virtual Tags Tag { get; set; } public int TagId { get; set; } }
public class BlogPosts { public BlogPosts() { BlogPostsJoinTags = new HashSet<BlogPostsJoinTags>(); } public int Id { get; set; } [Required] [MaxLength(450)] public string Title { get; set; } public string Body { get; set; } [InverseProperty("BlogPost")] public virtual ICollection<BlogPostsJoinTags> BlogPostsJoinTags { get; set; } }
public class MyDBDataContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Data Source=(local);Initial Catalog=testdb2;Integrated Security = true"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<BlogPostsJoinTags>(entity => { entity.HasKey(e => new { e.TagId, e.BlogPostId }) .HasName("PK_dbo.BlogPostsJoinTags"); entity.HasIndex(e => e.BlogPostId) .HasName("IX_BlogPostId"); entity.HasIndex(e => e.TagId) .HasName("IX_TagId"); }); } public virtual DbSet<BlogPosts> BlogPosts { get; set; } public virtual DbSet<BlogPostsJoinTags> BlogPostsJoinTags { get; set; } public virtual DbSet<Tags> Tags { get; set; } }
نحوهی ثبت اطلاعات در دو رابطهی یک به چند به همراه جدول واسط
در EF 6.x، کار مقدار دهی Idهای جدول واسط به صورت خودکار انجام میشود. در اینجا این مقدار دهی را باید به صورت صریح انجام داد:
var post = new BlogPosts { ... }; context.BlogPosts.Add(post); var tag = new Tags { ... }; context.Tags.Add(tag); var postTag = new BlogPostsJoinTags { Tag = tag, BlogPost = post }; context.PostsTags.Add(postTag); context.SaveChanges();
نحوهی واکشی اطلاعات به هم مرتبط در دو رابطهی یک به چند به همراه جدول واسط
در مورد متدهای Include و ThenInclude در مطلب «شروع به کار با EF Core 1.0 - قسمت 7 - بررسی رابطهی One-to-Many» پیشتر بحث شد.
BlogPosts post1 = this.BlogPosts .Include(blogPosts => blogPosts.BlogPostsJoinTags) .ThenInclude(joinTags => joinTags.Tag) .First(blogPosts => blogPosts.Id == 1); IEnumerable<Tags> post1Tags = post1.BlogPostsJoinTags.Select(x => x.Tag);
یک نکتهی تکمیلی
وضعیت پشتیبانی از رابطهی many-to-many را همانند EF 6.x در EF Core، در اینجا میتوانید پیگیری کنید.