EF Code First #2
ضمن اینکه تا این سری 15 قسمتی که به عمد با برنامه کنسول جلو رفته رو تموم نکنید، درک صحیحی از اجزای مختلف آن پیدا نخواهید کرد.
هر زمانی هم خواستید مطلبی را در این سطح آموزش دهید با برنامهی کنسول کار کنید چون هدف در اینجا نحوه نمایش آن با سیلورلایت یا asp.net یا winforms و غیره نیست. هدف آشنایی با زیرساختهای اصلی یک فناوری است؛ صرفنظر از نحوه نمایش آن به کاربر.
چگونه باید کلاسها را به بانک اطلاعاتی نگاشت کرد. چگونه باید پس از تغییر کلاسها، بانک اطلاعاتی را با برنامه هماهنگ کرد. چطور باید حالتهای یک به چند و امثال آنرا تعریف کرد. چطور باید یک Context را صحیح مدیریت کرد و غیره. هدف این سری، این نوع مباحث پایهای بوده نه فناوری نمایش نهایی آن.
حذف موجودیتهای منفصل
فرض کنید موجودیتی را از یک سرویس WCF دریافت کرده اید و میخواهید آن را برای حذف علامت گذاری کنید. مدل زیر را در نظر بگیرید.
همانطور که میبینید مدل ما صورت حسابها و پرداختهای متناظر را ارائه میکند. در اپلیکیشن جاری یک سرویس WCF پیاده سازی کرده ایم که عملیات دیتابیسی کلاینتها را مدیریت میکند. میخواهیم توسط این سرویس آبجکتی را (در اینجا یک موجودیت پرداخت) حذف کنیم. برای ساده نگاه داشتن مثال جاری، مدلها را در خود سرویس تعریف میکنیم. برای ایجاد سرویس مذکور مراحل زیر را دنبال کنید.
- در ویژوال استودیو پروژه جدیدی از نوع WCF Service Library بسازید و نام آن را به Recipe5 تغییر دهید.
- روی پروژه کلیک راست کنید و گزینه Add New Item را انتخاب کنید. سپس گزینههای Data -> ADO.NET Entity Data Model را برگزینید.
- از ویزارد ویژوال استودیو برای اضافه کردن یک مدل با جداول Invoice و Payment استفاده کنید. برای ساده نگه داشتن مثال جاری، فیلد پیمایشی Payments را از موجودیت Invoice حذف کرده ایم (برای این کار روی خاصیت پیمایشی Payments کلیک راست کنید و گزینه Delete From Model را انتخاب کنید.) روی خاصیت TimeStamp موجودیت Payment کلیک راست کنید و گزینه Properties را انتخاب کنید. سپس مقدار Concurrency Mode آن را به Fixed تغییر دهید. این کار باعث میشود که مقدار این فیلد برای کنترل همزمانی بررسی شود. بنابراین مقدار TimeStamp در عبارت WHERE تمام دستورات بروز رسانی و حذف درج خواهد شد.
- فایل IService1.cs را باز کنید و تعریف سرویس را مانند لیست زیر تغییر دهید.
[ServiceContract] public interface IService1 { [OperationContract] Payment InsertPayment(); [OperationContract] void DeletePayment(Payment payment); }
- فایل Service1.cs را باز کنید و پیاده سازی سرویس را مانند لیست زیر تغییر دهید.
public class Service1 : IService1 { public Payment InsertPayment() { using (var context = new EFRecipesEntities()) { // delete the previous test data context.Database.ExecuteSqlCommand("delete from [payments]"); context.Database.ExecuteSqlCommand("delete from [invoices]"); var payment = new Payment { Amount = 99.95M, Invoice = new Invoice { Description = "Auto Repair" } }; context.Payments.Add(payment); context.SaveChanges(); return payment; } } public void DeletePayment(Payment payment) { using (var context = new EFRecipesEntities()) { context.Entry(payment).State = EntityState.Deleted; context.SaveChanges(); } } }
- برای تست این سرویس به یک کلاینت نیاز داریم. یک پروژه جدید از نوع Console Application به راه حل جاری اضافه کنید و کد آن را مطابق لیست زیر تغییر دهید. فراموش نکنید که ارجاعی به سرویس هم اضافه کنید. روی پروژه کلاینت کلیک راست کرده و Add Service Reference را انتخاب نمایید. ممکن است پیش از آنکه بتوانید سرویس را ارجاع کنید، نیاز باشد پروژه سرویس را ابتدا اجرا کنید (کلیک راست روی پروژه سرویس و انتخاب گزینه Debug -> Start Instance).
class Program { static void Main() { var client = new Service1Client(); var payment = client.InsertPayment(); client.DeletePayment(payment); } }
شرح مثال جاری
در مثال جاری برای بروز رسانی و حذف موجودیتهای منفصل از الگویی رایج استفاده کرده ایم که در سرویسهای WCF و Web API استفاده میشود.
در کلاینت با فراخوانی متد InsertPayment یک پرداخت جدید در دیتابیس ذخیره میکنیم. این متد، موجودیت Payment ایجاد شده را باز میگرداند. موجودیتی که به کلاینت باز میگردد از DbContext منفصل (disconnected) است، در واقع در چنین وضعیتی آبجکت context ممکن است در فضای پروسس دیگری قرار داشته باشد، یا حتی روی کامپیوتر دیگری باشد.
برای حذف موجودیت Payment از متد DeletePayment استفاده میکنیم. این متد به نوبه خود با فراخوانی متد Entry روی آبجکت context و پاس دادن موجودیت پرداخت بعنوان آرگومان، موجودیت را پیدا میکند. سپس وضعیت موجودیت را به EntityState.Deleted تغییر میدهیم که این کار آبجکت را برای حذف علامت گذاری میکند. فراخوانیهای بعدی متد ()SaveChanges موجودیت را از دیتابیس حذف خواهد کرد.
آبجکت پرداختی که برای حذف به context الحاق کرده ایم تمام خاصیت هایش مقدار دهی شده اند، درست مانند هنگامی که این موجودیت به دیتابیس اضافه شده بود. اما از آنجا که از foreign key association استفاده میکنیم، تنها فیلدهای کلید موجودیت، خاصیت همزمانی (concurrency) و TimeStamp برای تولید عبارت where مناسب لازم هستند که نهایتا منجر به حذف موجودیت خواهد شد. تنها استثنا درباره این قاعده هنگامی است که موجودیت شما یک یا چند خاصیت از نوع پیچیده یا Complex Type داشته باشد. از آنجا که خاصیتهای پیچیده، اجزای ساختاری یک موجودیت محسوب میشوند نمیتوانند مقادیر null بپذیرند. یک راه حل ساده این است که هنگامی که EF مشغول ساختن عبارت SQL Delete لازم برای حذف موجودیت بر اساس کلید و خاصیت همزمانی آن است، وهله جدیدی از نوع داده پیچیده خود بسازید. اگر فیلدهای complex type را با مقادیر null رها کنید، فراخوانی متد ()SaveChanges با خطا مواجه خواهد شد.
اگر از یک independent association استفاده میکنید که در آن کثرت (multiplicity) موجودیت مربوطه یک، یا صفر به یک است، EF انتظار دارد که کلیدهای موجودیتها بدرستی مقدار دهی شوند تا بتواند عبارت where مناسب را برای دستورات بروز رسانی و حذف تولید کند. اگر در مثال جاری از یک رابطه independent association بین موجودیتهای Invoice و Payment استفاده میکردیم، لازم بود تا خاصیت پیمایشی Invoice را با وهله ای از صورت حساب مقدار دهی کنیم که خاصیت InvoiceId آن نیز بدرستی مقدار دهی شده باشد. در این صورت عبارت where نهایی شامل فیلدهای PaymentId, TimeStamp و InvoiceId خواهد بود.
نکته: هنگام پیاده سازی معماریهای n-Tier با Entity Framework، استفاده از رویکرد Foreign Key Association برای موجودیتهای مرتبط باید با ملاحظات جدی انجام شود. پیاده سازی رویکرد Independent Association مشکل است و میتواند کد شما را بسیار پیچیده کند. برای مطالعه بیشتر درباره این رویکردها و مزایا و معایب آنها به این لینک مراجعه کنید که توسط یکی از برنامه نویسان تیم EF نوشته شده است.
اگر موجودیت شما تعداد متعددی Independent Association دارد، مقدار دهی تمام آنها میتواند خسته کننده شود. رویکردی سادهتر این است که وهله مورد نظر را از دیتابیس دریافت کنید و آن را برای حذف علامت گذاری نمایید. این روش کد شما را سادهتر میکند، اما هنگامی که آبجکت را از دیتابیس دریافت میکنید EF کوئری جاری را بازنویسی میکند تا تمام روابط یک، یا صفر به یک بارگذاری شوند. مگر آنکه از گزینه NoTracking روی context خود استفاده کنید. اگر در مثال جاری رویکرد Independent Association را پیاده سازی کرده بودیم، هنگامی که موجودیت Payment را از دیتابیس دریافت میکنیم (قبل از علامت گذاری برای حذف) EF یک Object state entry برای موجودیت پرداخت و یک Relationship entry برای رابطه بین Payment و Invoice میساخت. سپس وقتی که موجودیت پرداخت را برای حذف علامت گذاری میکنیم، EF رابطه بین پرداخت و صورت حساب را هم برای حذف علامت گذاری میکند. در اینجا عبارت where تولید شده مانند قبل، شامل فیلدهای PaymentId, TimeStamp و InvoiceId خواهد بود.
یک گزینه دیگر برای حذف موجودیتها در Independent Associations این است که تمام موجودیتهای مرتبط را مشخصا بارگذاری کنیم (eager loading) و کل Object graph را برای حذف به سرویس WCF یا Web API بفرستیم. در مثال جاری میتوانستیم موجودیت صورتحساب مرتبط با موجودیت پرداخت را مشخصا بارگذاری کنیم. اگر میخواستیم موجودیت Payment را حذف کنیم، میتوانستیم کل گراف را که شامل هر دو موجودیت میشود به سرویس ارسال کنیم. اما هنگام استفاده از چنین روشی باید بسیار دقت کنید، چرا که این رویکرد پهنای باند بیشتری مصرف میکند و زمان پردازش بیشتری هم برای مرتب سازی (serialization) صرف میکند. بنابراین هزینه این رویکرد نسبت به سادگی کدی که بدست میآید به مراتب بیشتر است.
CREATE DATABASE DNTSampleDB GO USE DNTSampleDB GO CREATE TABLE Cars( ID int PRIMARY KEY, Name VARCHAR(255) NOT NULL, NumberPlate VARCHAR(255) UNIQUE,--شماره پلاک Model INT);
INSERT INTO Cars VALUES (1,'Mazda','ABC 123',199), (2,'Mazda','ABC 345',207), (3,'Mazda','ABC 758',305), (4,'Mazda','ABC 741',306), (5,'Mazda','ABC 356',124)
INSERT INTO Cars VALUES (2,'Mazda','ABC 123111',199)
INSERT INTO Cars VALUES (6,'Mazda','ABC 741',200)
USE DNTSampleDB GO sp_help cars
OutputCaching باعث میشود خروجیِ یک اکشن متد در حافظه نگهداری شود. با اعمال این نوع کشینگ، ASP.NET در خواستهای بعدی به این اکشن را تنها با بازگرداندن همان مقدار قبلی ِ نگهداری شده در کش، پاسخ میدهد. در حقیقت با OutputCaching از تکرار چند باره کد درون یک اکشن در فراخوانیهای مختلف جلوگیری کردهایم. کش کردن باعث میشود که کارایی و سرعت سایت افزایش یابد؛ اما باید دقت کنیم که چه موقع و چرا از کَش کردن استفاده میکنیم و چه موقع باید از این کار امتناع کرد.
فواید کَش کردن
- انجام عملیات هزینه دار فقط یکبار صورت میگیرد. (هزینه از لحاظ فشار روی حافظه سرور و کاهش سرعت بالا آمدن سایت)
- بار روی سرور در زمانهای پیک کاهش مییابد.
- سرعت بالا آمدن سایت بیشتر میشود.
چه زمانی باید کَش کرد؟
- وقتی محتوای نمایشی برای همه کاربران یکسان است.
- وقتی محتوای نمایشی برای نمایش داده شدن، فشار زیادی روی سرور تحمیل میکند.
- وقتی محتوای نمایشی به شکل مکرر در طول روز باید نمایش داده شود.
- وقتی محتوای نمایشی به طور مکرر آپدیت نمیشود. (در مورد تعریف کیفیت "مکرر"، برنامه نویس بهترین تصمیم گیرنده است)
طرح مساله
فرض کنید صفحه اول سایت شما دارای بخشهای زیر است :
خلاصه اخبار بخش علمی، خلاصه اخبار بخش فرهنگی ، ده کامنت آخر، لیستی از کتگوریهای موجود در سایت.
روشهای مختلفی برای کوئری گرفتن وجود دارد، به عنوان مثال ما به کمک یک یا چند کوئری و توسط یک ViewModel جامع، میخواهیم اطلاعات را به سمت ویو ارسال کنیم. پس در اکشن متد Index ، حجم تقریبا کمی از اطلاعات را باید به کمک کوئری(کوئری های) تقریبا پیچیده ای دریافت کنیم و اینکار به ازای هر ریکوئست هزینه دارد و فشار به سرور وارد خواهد شد. از طرفی میدانیم صفحه اول ممکن است در طول یک یا چند روز تغییر نکند و همچنین شاید در طول یکساعت چند بار تغییر کند! به هر حال در جایی از سایت قرار داریم که کوئری (کوئری های) مورد نظر زیاد صدا زده میشوند ، در حقیقت صفحه اول احتمالا بیشترین فشار ترافیکی را در بین صفحات ما دارد، البته این فقط یک احتمال است و ما دقیقا از این موضوع اطلاع نداریم.
یکی از راههای انجام یک کش موفق و دانستن لزوم کش کردن، این است که دقیقا بدانیم ترافیک سایت روی چه صفحه ای بیشتر است. در واقع باید بدانیم در کدام صفحه "هزینهی اجرای عملیات موجود در کد" بیشترین است.
فشار ترافیکی(ریکوئستهای زیاد) و آپدیتهای روزانهی دیتابیس را، در دو کفه ترازو قرار دهید؛ چه کار باید کرد؟ این تصمیمی است که شما باید بگیرید. نگرانی خود را در زمینه آپدیتهای روزانه و ساعتی کمتر کنید؛ در ادامه راهی را معرفی میکنیم که آپدیتهای هر از گاهِ شما، در پاسخِ ریکوئستها دیده شوند. کمی کفهی کش کردن را سنگین کنید.
به هر حال، فعال کردن قابلیت کش کردن برای یک اکشن، بسیار ساده است، کافیست ویژگی ( attribute ) آن را بالای اکشن بنویسید :
[OutputCache(Duration = "60", Location = OutputCacheLocation.Server)] public ActionResult Index() { //کوئری یا کوئریهای لازم برای استفاده در صفحه اصلی و تبدیل آن به یک ویو مدل جامع }
[OutputCache(CacheProfile = "FirstPageIndex",Location=OutputCacheLocation.Server)] public ActionResult Index() { //کوئری یا کوئریهای لازم برای استفاده در صفحه اصلی و تبدیل آن به یک ویو مدل جامع }
دو روش فوق برای کش کردن خروجی Index از لحاظ عملکرد یکسان است، به شرطی که در حالت دوم در وب کانفیگ و در بخش system.web آن ، یک پروفایل ایجاد کنیم کنیم :
<caching> <outputCacheSettings> <outputCacheProfiles> <add name="FirstPageIndex" duration="60"/> </outputCacheProfiles> </outputCacheSettings> </caching>
در حالت دوم ما یک پروفایل برای کشینگ ساخته ایم و در ویژگی بالای اکشن متد، آن پروفایل را صدا زده ایم. از لحاظ منطقی در حالت دوم، چون امکان استفاده مکرر از یک پروفایل در جاهای مختلف فراهم شده، روش بهتری است. محل ذخیره کش نیز در هر دو حالت سرور تعریف شده است.
برای تست عملیات کشینگ، کافیست یک BreakPoint درون Index قرار دهید و برنامه را اجرا کنید. پس از اجرا، برنامه روی Break Point میایستد و اگر F5 را بزنیم، سایت بالا میآید. بار دیگر صفحه را رفرش کنیم، اگر این "بار دیگر" در کمتر از 60 ثانیه پس از رفرش قبلی اتفاق افتاده باشد برنامه روی Break Point متوقف نخواهد شد، چون خروجی اکشن، در کش بر روی سرور ذخیره شده است و این یعنی ما فشار کمتری به سرور تحمیل کرده ایم، صفحه با سرعت بالاتری در دسترس خواهد بود.
ما از تکرار اجرای کد جلوگیری کرده ایم و عدم اجرای کد بهترین نوع بهینه سازی برای یک سایت است. [اسکات الن، پلورال سایت]
چطور زمان مناسب برای کش کردن یک اکشن را انتخاب کنیم؟
- کشینگ با زمان کوتاه؛ فرض کنید زمان کش را روی 1 ثانیه تنظیم کرده اید. این یعنی اگر ریکوئست هایی به یک اکشن ارسال شود و همه در طول یک ثانیه اتفاق بیفتد، آن اکشن فقط برای بار اول اجرا میشود، و در بارهای بعد(در طول یک ثانیه) فقط محتوای ذخیره شده در آن یک اجرا، بدون اجرای جدید، نمایش داده میشود. پس سرور شما فقط به یک ریکوئست در ثانیه در طول روز جواب خواهد داد و ریکوئستهای تقریبا همزمان دیگر، در طول همان ثانیه، از نتایج آن ریکوئست (اگر موجود باشد) استفاده خواهند کرد
- کشینگ با زمان طولانی؛ ما در حقیقت با اینکار منابع سرور را حفاظت میکنیم، چون عملیاتِ هزینه دار(مثل کوئریهای حجیم) تنها یکبار در طول زمان کشینگ اجرا خواهند شد. مثلا اگر تنظیم زمان روی عدد 86400 تنظیم شود(یک روز کامل)، پس از اولین ریکوئست به اکشن مورد نظر، تا 24 ساعت بعد، این اکشن اجرا نخواهد شد و فقط خروجی آن نمایش داده خواهد شد. آیا دلیلی دارد که یک کوئری هزینه دار را که قرار نیست خروجی اش در طول روز تغییر کند به ازای هر ریکوئست یک بار اجرا کنیم؟
اگر اطلاعات موجود در دیتابیس را تغییر دهیم چه کار کنیم که کشینگ رفرش شود؟
فرض کنید در همان مثال ابتدای این مقاله، شما یک پست به دیتابیس اضافه کرده اید، اما چون مثلا duration مربوط به کشینگ را روی 86400 تعریف کرده اید تا 24 ساعت از زمان ریکوئست اولیه نگذرد، سایت آپدیت نخواهد شد و محتوا همان چیزهای قبلی باقی خواهند ماند. اما چاره چیست؟
کافیست در بخش ادمین، وقتی که یک پست ایجاد میکنید یا پستی را ویرایش میکند در اکشنهای مرتبط با Create یا Edit یا Delete چنین کدی را پس از فرمان ذخیره تغییرات در دیتابیس، بنویسید:
Response.RemoveOutputCacheItem(Url.Action("index", "home"));
واضح است که ما داریم کشینگ مرتبط با یک اکشن متد مشخص را پاک میکنیم. با اینکار در اولین ریکوئست پس از تغییرات اعمال شده در دیتابیس، ASP.NET MVC چون میبیند کَشی برای این اکشن وجود ندارد، متد را اجرا میکند و کوئریهای درونش را خواهد دید و اولین ریکوئست پیش از کَش شدن را انجام خواهد داد. با اینکار کشینگ ریست شده است و پس از این ریکوئست و استخراج اطلاعات جدید، زمان کشینگ صفر شده و آغاز میشود.
میتوانید یک دکمه در بخش ادمین سایت طراحی کنید که هر موقع دلتان خواست کلیه کشها را به روش فوق پاک کنید! تا اپلیکیشن منتظر ریکوئستهای جدید بماند و کشها دوباره ایجاد شوند.
جمع بندی
ویژگی OutputCatch دارای پارامترهای زیادیست و در این مقاله فقط به توضیح عملکرد این اتریبیوت اکتفا شده است. بطور کلی این مبحث ظاهر ساده ای دارد، ولی نحوه استفاده از کشینگ کاملا وابسته به هوش برنامه نویس است و پیچیدگیهای مرتبط با خود را دارد. در واقع خیلی مشکل است که بتوانید یک زمان مناسب برای کش کردن تعیین کنید. باید برنامه خود را در یک محیط شبیه سازی تحت بار قرار دهید و به کمک اندازه گیری و محاسبه به یک قضاوت درست از میزان زمان کش دست پیدا کنید. گاهی متوجه خواهید شد، از مقدار زیادی از حافظه سیستم برای کش کردن استفاده کرده اید و در حقیقت آنقدر ریکوئست ندارید که احتیاج به این هزینه کردن باشد.
یکی از روشهای موثر برای دستیابی به زمان بهینه برای کش کردن استفاده از CacheProfile درون وب کانفیگ است. وقتی از کشینگ استفاده میکنید، در همان ابتدا مقدار زمانی مشخص برای آن در نظر نگرفته اید(در حقیقت مقدار زمان مشخصی نمیدانید) پس مجبور به آزمون و خطا و تست و اندازه گیری هستید تا بدانید چه مقدار زمانی را برای چه پروفایلی قرار دهید. مثلا پروفایل هایی به شکل زیر تعریف کرده اید و نام آنها را به اکشنهای مختلف نسبت داده اید. به راحتی میتوانید از طریق دستکاری وب کانفیگ مقادیر آن را تغییر دهید تا به حالت بهینه برسید، بدون آنکه کد خود را دستکاری کنید.
<caching> <outputCacheSettings> <outputCacheProfiles> <add name="Long" duration="86400"/> <add name="Average" duration="43600"/> <add name="Short" duration="600"/> </outputCacheProfiles> </outputCacheSettings> </caching>
برای مطالعه جزئیات بیشتر در مورد OutputCaching مقالات زیر منابع مناسبی هستند.
مروری بر نحوهی توزیع برنامههای Blazor بر روی IIS
1- پیش از هر کاری باید مطابق نگارش ASP.NET Core در حال استفاده (که به عنوان هاست Blazor Server و یا ارائه دهندهی قسمت Web API برنامهی سمت کلاینت WASM مطرح است)، بستهی NET Core hosting bundle. را نصب کرد که عموما تحت عنوان «Hosting Bundle Installer» قابل دریافت است.
نکتهی مهم: همانند تمام نگارشهای دات نت، در اینجا نیز باید Hosting Bundle را پس از نصب IIS، بر روی سیستم نصب کرد. اگر این ترتیب تغییر کند، یکبار دیگر نصاب آنرا اجرا کرده و گزینهی ترمیم نصب را انتخاب کنید تا یکپارچگی آن با IIS صورت گیرد.
2- نیاز است برنامهی خود را اصطلاحا publish کرد تا به همراه فایلهای نهایی قابل کپی باشد که در پوشهای توسط IIS هاست خواهند شد. برای اینکار اگر از نگارش کامل ویژوال استودیو استفاده میکنید، فقط کافی است بر روی پروژهی مدنظر کلیک راست کرده و از منوی باز شده، گزینهی publish را انتخاب کنید و مراحل آنرا طی نمائید و یا این مراحل را میتوان توسط دستور خط فرمان زیر نیز خلاصه کرد که وابستگی خاصی، به IDE ویژهای ندارد و چند سکویی است:
dotnet publish -o "c:\dir1\dir2" -c Release
و یا اگر فقط دستور dotnet publish -c Release را در ریشهی پروژه اجرا کنیم، خروجی نهایی را در پوشهی bin\Release\net5.0\publish میتوان مشاهده کرد که به همراه یک web.config مخصوص برنامههای blazor هم هست و در آن mime typeهای متناظری، به همراه URL rewriting مناسب برنامههای تک صفحهای وب از پیش تنظیم شدهاست. بنابراین در اینجا نصب ماژول URL rewrite بر روی IIS نیز الزامی است.
3- در اینجا نیز همانند تنظیمات برنامههای ASP.NET Core، باید application pool منتسب به برنامه را ویرایش کرده و NET Clr Version. آنرا بر روی No Managed Code قرار داد.
روش فعالسازی توزیع مبتنی بر فشرده سازی Brotli در IIS
در حین عملیات publish استاندارد، به صورت پیشفرض از تمام فایلها، سه نسخهی اصلی، gz شده (gzip) و یا br شده (فشرده سازی Brotli که فایلهای کم حجمتری را نسبت به gz ارائه میدهد) نیز تهیه میشوند که بسته به نوع مرورگر و پشتیبانی آن از روشهای مختلف فشرده سازی، یکی از آنها در اختیار کلاینت قرار خواهد گرفت که به این صورت کاربران، تجربهی دریافت کم حجمتر و سریعتری را خواهند داشت.
باید دقت داشت Web.config ای که به همراه دستور dotnet publish ایجاد میشود، روش توزیع پیشفرض فایلهای br. تولیدی را ندارد. برای اینکار نیاز است تنظیمات این فایل web.config توصیه شدهی توسط تیم Blazor را به web.config خود اضافه کرد تا در نهایت حجم دریافتی از سرور به شدت کاهش یابد.
یک نکته: اگر میخواهید فایل web.config سفارشی خودتان را داشته باشید، نمونهای از آنرا در ریشهی پروژه قرار داده و سپس فایل csproj را به نحو زیر ویرایش کنید تا از آن در حین publish استفاده کند:
<PropertyGroup> <PublishIISAssets>true</PublishIISAssets> </PropertyGroup>
در حین publish برنامههای Blazor WASM کار IL trimming نیز انجام میشود
برای کاهش حجم نهایی برنامههای Blazor WASM، در حین publish در حالت release، کار IL Trimming نیز به صورت خودکار انجام میشود تا کدهای IL ای که در برنامه نقش نداشتهاند و مستقیما در جائی استفاده نشدهاند، به صورت خودکار حذف شوند و به این ترتیب حجم ارائهی نهایی به شدت کاهش یابد.
فقط باید دقت داشت که در این حالت اگر عملیات پویایی مانند reflection در کدهای شما صورت میگیرد، به علت نداشتن ارجاع استاتیکی به منابع مورد استفاده، در زمان اجرا با مشکل مواجه خواهد شد. اگر میخواهید اخطارهایی را در این زمینه مشاهده کنید، گزینهی زیر را به فایل csproj اضافه نمائید:
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
حذف مباحث بومی سازی در صورت عدم نیاز
اگر در برنامهی خود از مباحث time-zones استفاده نمیکنید، میتوانید با غیرفعال کردن آن در فایل csproj، حداقل 100 کیلوبایت از حجم برنامهی نهایی را کاهش دهید:
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
<InvariantGlobalization>true</InvariantGlobalization>
راهنمایی در مورد سفارش و جزییات سفارش و اتصال به درگاه بانک
گفتم جدول پرداخت
جدول سفارش از پرداخت جداست. رکوردهای پرداخت به ازای هر رفت به درگاه ایجاد و هنگام برگشت به روزرسانی میشه که تنها شامل اطلاعات پرداخت در اون لحظه هست . ارتباطی که با سفارش داره تنها از طریق یک کلید خارجی مهیا میشه که باعث میشه تشخیص داده بشه که با کدوم سفارش در ارتباط است.
سعی کنید این مباحث را در انجمنها پیگیری کنید.
بستهی نصب SQL Server CE 3.5 SP2 وجود دارد:
اما برای برنامههای جدیدتر نیاز به افزودن بستهی نصب دیتابیس SQL Server CE نسخه 4 است که با عدم وجود این بسته روبرو هستیم. در ادامه با نحوهی افزودن این بستهها آشنا خواهید شد.
اینگونه بستهها در کنار برنامهی ساخت نصاب و در پوشهی SetupPrerequisites نگهداری شده و با نوع *.prq ذخیره میشوند. این نوع فایلها از نوع xml هستند و در واقع یک نوع کار نگاشت را انجام میدهند. برای نمونه محتویات یکی از این فایلها را در زیر میبینید:
<?xml version="1.0" encoding="UTF-8"?> <SetupPrereq> <conditions> <condition Type="32" Comparison="2" Path="HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server Compact Edition\v3.5\ENU" FileName="DesktopRuntimeVersion" ReturnValue="3.5.8080.0"></condition> </conditions> <operatingsystemconditions> <operatingsystemcondition MajorVersion="5" MinorVersion="0" PlatformId="2" CSDVersion="" ServicePackMajorMin="3"></operatingsystemcondition> <operatingsystemcondition MajorVersion="5" MinorVersion="1" PlatformId="2" CSDVersion="" Bits="1" ProductType="1"></operatingsystemcondition> <operatingsystemcondition MajorVersion="6" MinorVersion="0" PlatformId="2" CSDVersion="" Bits="1"></operatingsystemcondition> <operatingsystemcondition MajorVersion="5" MinorVersion="2" PlatformId="2" CSDVersion="" Bits="1" ProductType="2|3"></operatingsystemcondition> <operatingsystemcondition MajorVersion="6" MinorVersion="1" PlatformId="2" CSDVersion="" Bits="1"></operatingsystemcondition> <operatingsystemcondition MajorVersion="6" MinorVersion="0" PlatformId="2" CSDVersion="" Bits="1" ProductType="2|3"></operatingsystemcondition> </operatingsystemconditions> <files> <file LocalFile="<ISProductFolder>\SetupPrerequisites\SQL CE 3.5\SSCERuntime_x86-ENU.msi" URL="http://go.microsoft.com/fwlink/?LinkId=166085&clcid=0x409" CheckSum="86AF6D36DFF214718DCD35D851249D3D" FileSize="0,3164160"></file> </files> <execute file="SSCERuntime_x86-ENU.msi" cmdline="/q /norestart" cmdlinesilent="/q /norestart" returncodetoreboot="1641,3010,4123" requiresmsiengine="1"></execute> <properties Id="{A7C4B3C0-F3A0-426A-A043-E13DBA123E52}" Description="This prerequisite installs the Microsoft SQL Server Compact 3.5 SP2." AltPrqURL="http://saturn.installshield.com/is/prerequisites/microsoft sql ce 3.5 sp2.prq"></properties> <behavior Reboot="2"></behavior> </SetupPrereq>
برای نسخه 32 بیتی(Microsoft SQL CE 4.0 x86.prq ):
<?xml version="1.0" encoding="UTF-8"?> <SetupPrereq> <conditions> <condition Type="32" Comparison="2" Path="HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server Compact Edition\v4.0\ENU" FileName="DesktopRuntimeVersion" ReturnValue="4.0.8482.1"></condition> </conditions> <operatingsystemconditions> <operatingsystemcondition CSDVersion="" Bits="1"></operatingsystemcondition> </operatingsystemconditions> <files> <file LocalFile=".\SSCERuntime_x86-ENU.exe" URL="http://download.microsoft.com/download/0/5/D/05DCCDB5-57E0-4314-A016-874F228A8FAD/SSCERuntime_x86-ENU.exe" CheckSum="0A55733CF406FBD05DFCFF5A27A0B4F7" FileSize="0,2379544"></file> </files> <execute file="SSCERuntime_x86-ENU.exe"></execute> <properties Id="{2754916B-119B-4428-9F94-DC9E45072CCC}"></properties> <behavior Failure="4" Reboot="2"></behavior> </SetupPrereq>
<?xml version="1.0" encoding="UTF-8"?> <SetupPrereq> <conditions> <condition Type="32" Comparison="2" Path="HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server Compact Edition\v4.0\ENU" FileName="DesktopRuntimeVersion" ReturnValue="4.0.8482.1"></condition> </conditions> <operatingsystemconditions> <operatingsystemcondition CSDVersion="" Bits="2"></operatingsystemcondition> </operatingsystemconditions> <files> <file LocalFile=".\SSCERuntime_x64-ENU.exe" URL="http://download.microsoft.com/download/0/5/D/05DCCDB5-57E0-4314-A016-874F228A8FAD/SSCERuntime_x64-ENU.exe" CheckSum="A417082ECAEDD95AFB41F73DC140C350" FileSize="0,2621240"></file> </files> <execute file="SSCERuntime_x64-ENU.exe"></execute> <properties Id="{7CB7BE3C-614A-403F-94D9-5652285A3EDF}"></properties> <behavior Failure="4" Reboot="2"></behavior> </SetupPrereq>
EF Code First #1
- در قسمت سوم این سری، در مورد فیلدهای محاسباتی بحث شده «6) NotMapped و DatabaseGenerated»