کوکیها به اندازهی خود اینترنت قدیمی هستند و تا سالها، تنها گزینهی شخصی سازی تجربهی کاربری در وب و انتقال آن از یک صفحه به صفحهی دیگری بهشمار میآمدند؛ به این نوع کوکیها، First-party cookies هم میگویند و توسط خود سایت ارائه دهندهی محتوا و برنامهها، تنظیم میشوند. در مقابل آن، third-party cookies یا کوکیهای ثالث هم وجود دارند که از طریق دومین دیگری بجز دومین اصلی برنامه، ارائه و تنظیم میشوند؛ به همین جهت به آنها Cross-site cookies هم میگویند. یکی از اهداف کوکیهای ثالث، ردیابی فعالیت کاربران در بین سایتهای مختلف است و این روزها به علت سوء استفادههای زیادی که از آنها میشوند، یکی از وسایل به اشتراکگذاری و جمع آوری اطلاعات خصوصی کاربران شدهاند.
البته کوکیهای ثالث، کاربردهای مفیدی هم دارند؛ مانند امکان پیاده سازی لاگین و اعتبارسنجی یکپارچهی بین چندین برنامه که به آن SSO یا Single Sign On هم گفته میشود؛ نمونهی آن، استفاده از Identity server و یا OpenId dict در دنیای داتنت است.
اما ... در کل به علت مشکلات یاد شده، اکثر مرورگرها تصمیم به عدم پذیرش و پردازش آنها گرفتهاند. برای مثال مرورگر Safari سالها است که اینگونه کوکیها را بلاک میکند و یا مرورگر فایرفاکس، کوکیهای ثالث ردیابها را بلاک میکند و ... مرورگر کروم نیز تصمیم گرفتهاست، تا پایان سال 2024، به این جمع محلق شود. هرچند اخیرا اعلام کردهاند، بجای اینکه مرورگر کروم، راسا این کار را انجام دهد، قرار است امکان انتخاب این گزینه به خود کاربر واگذار شود (چون ... خود گوگل از این نوع کوکیها منتفع است!). البته این تصمیم، بهنظر W3C خوش نیامده و اعلام کردهاند که این نوع کوکیها باید بروند!
روش آزمایش برنامهها برای بررسی تاثیر ممنوعیت کوکیهای ثالث
برای اینکه پیش از موعد، امکان آزمایش برنامهی خود را داشته باشید و بتوانید تاثیر ممنوعیت بکارگیری کوکیهای ثالث را بررسی کنید، در مرورگر کروم به آدرس زیر مراجعه کرده:
chrome://flags/#test-third-party-cookie-phaseout
و سپس این گزینه را فعال کنید و یا روش دوم فعالسازی آن، اجرای مرورگر کروم با سوئیچ test-third-party-cookie-phaseout-- از طریق خط فرمان است.
کدام برنامهها با ممنوعیت کوکیهای ثالث مشکل پیدا میکنند؟
اگر برنامهی SSO شما، مبتنی بر اعتبارسنجی یکپارچهی از نوع implicit flow است، حتما مشکل پیدا میکنید. در این حالت بهتر است به نوع امنتر authorization flow + PKCE مهاجرت کنید.
علت مسدود شدن نوع اعتبارسنجی و احراز هویت implicit flow که در برنامههای تک صفحهای وب (SPA/single-page apps) مرسوم است، شباهت بسیار زیاد آن به کاری است که ردیابهای اینترنتی انجام میدهند. در اینحالت، سایتهای ثالث، یک iframe مخفی را در پشت صحنه، درون سایت جاری باز کرده و توسط آن شروع به استفادهی از امکانات مرتبط با کوکیها میکنند. این الگو دقیقا همان کاری است که توسط implicit flow هم انجام میشود. بنابراین مرورگری که کوکیهای ثالث از این دست را مسدود میکند، قابلیت اعتبارسنجی یکپارچهی برنامههای SPA را هم غیرفعال خواهد کرد.
اینطور که در این مطلب عنوان شده، ماوسهای قدیمی در اثر مشکلات سخت افزاری، میتوانند بهازای هر کلیک کاربر، دو سیگنال کلیک، ظرف مدت کوتاهی (برای مثال 5 میلی ثانیه) تولید کنند. برنامههای مبتنی بر Blazor، توسط متدهای نامتقارن میتوانند هردوی این سیگنالها را دریافت کرده و بنابراین متد مربوطه در کسری از ثانیه دوبار اجرا خواهد شد.
برای رهایی از این مشکل میتوان از کدی شبیه زیر بهره جست:
<button disabled="@_busy" Value="do-stuff" /> code{ private bool _busy = false; public async Task Handler() { if(_busy) return; _busy = true; try { // do your thing } finally { _busy = false; } } }
منطق آن ساده است؛ تا زمانی که اجرای متد، پایان نپذیرفتهاست، دکمهی مربوطه غیرفعال میگردد، تا نتوان دوباره روی آن کلیک کرد.
اگر نمیخواهید به ازای هر کامپوننت، این کدهای تکراری را ایجاد کنید، میتوانید کدهای فوق را در قالب یک کامپوننت مانند زیر ایجاد کنید (با نام دلخواه HandleValidSubmitForm.razor):
<EditForm Model="Model" OnValidSubmit="HandleValidSubmit"> @ChildContent?.Invoke(context) <button disabled="@_busy">Submit</button> </EditForm> @code { private bool _busy; [Parameter] public object? Model { get; set; } [Parameter] public EventCallback<EditContext> OnValidSubmit { get; set; } [Parameter] public RenderFragment<EditContext>? ChildContent { get; set; } private async Task HandleValidSubmit(EditContext editContext) { if (_busy) return; _busy = true; try { await OnValidSubmit.InvokeAsync(editContext); } finally { _busy = false; } } }
سپس میتوانید در دیگر کامپوننتها به شکل زیر از آن بهره ببرید.
<HandleValidSubmitForm Model="_customer" OnValidSubmit="HandleValidSubmit"> <InputText @bind-Value="_customer.FirstName" /> <InputText @bind-Value="_customer.LastName" /> </HandleValidSubmitForm> @code { private Customer _customer = new Customer(); private async Task HandleValidSubmit() { // do your thing } public class Customer { public string? FirstName { get; set; } public string? LastName { get; set; } } }
dotnet tool install -g upgrade-assistant
dotnet tool update -g upgrade-assistant
- Back up project
- Convert project file to SDK style
- Clean up NuGet package references
upgrade-assistant upgrade MyProject.csproj
- اگر پروژه از جنس Library باشد، فایل csproj را به شکل زیر درآورید:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net472</TargetFramework> <OutputType>Library</OutputType> </PropertyGroup> . . . </Project>
- اگر پروژه از جنس Console باشد، فایل csproj را به شکل زیر درآورید:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net472</TargetFramework> <OutputType>Exe</OutputType> <ApplicationIcon>logo.ico</ApplicationIcon> <AppConfig>App.config</AppConfig> </PropertyGroup> . . . </Project>
- اگر پروژه از جنس Web باشد، فایل csproj را به شکل زیر درآورید. احتمالاً پیچیدهترین و سختترین فایلها، متعلق به پروژههای وب باشد. اگر دقت کنید نوع SDK از نوع MSBuild.SDK.SystemWeb میباشد. نسخه این SDK ممکن است در زمانیکه شما در حال خواندن این مطلب میباشد آپدیت شده باشد و بهتر است قبل از استفاده، آخرین نسخه را از نیوگت برداشت کنید. (باید ذکر کنم که این hack کوچک را از یک comment در issueهای گیتهاب پیدا کردم.)
<Project Sdk="MSBuild.SDK.SystemWeb/4.0.54"> <PropertyGroup> <OutputType>Library</OutputType> <TargetFramework>net472</TargetFramework> <AppConfig>Web.config</AppConfig> </PropertyGroup> . . . </Project>
هنگامیکه خطاهای غیر منتظرهای در برنامهی مدیریت شدهی شما رخ میدهند، شما اطلاعات کمی را در مورد این مساله دارید. اگرچه شما میتوانید تا حدودی جلوی این نوع خطاهای غیرمنتظره را با ابزارهای خطایابی و یا لاگر، رصد کنید ولی همیشه اینطور نیست؛ در این حال ذخیره، تجزیه و تحلیل Dumpهای حافظه، ممکن است آخرین گزینه برای شما باشد. خوشبختانه ویژوال استودیو، ابزاری عالی برای تجزیه و تحلیل Dumpهای حافظه است! در این مطلب به شما نشان میدهیم که چگونه Dumpهای حافظه را جمع آوری کرده و توسط ویژوال استودیو راه حل مشکلات درج شدهی در آنها را پیدا کنید.
ابزارهایی وجود دارند که حافظه را مورد کاوش قرار داده و فعالیتهایی را که یک پروسس انجام میدهد، مانیتور میکنند. در حال حاضر ابزارهای مختلفی برای اینکار وجود دارند؛ از جمله Visual Studio ،ProcDump ،DebugDiag و WinDbg که ما در این پست از ProcDump استفاده میکنیم.
برای شروع، من یک برنامهی ساده را ایجاد کردم که شامل یک button است و با فشردن آن، یک خطای نامشخص اتفاق میافتد. برنامه را اجرا میکنیم. سپس به TaskManager رفته و آیدی پروسس برنامه را پیدا میکنیم:
آیدی پروسس ما، 10896 میباشد.
ProcDump را دانلود کرده و آنرا توسط CMD، به این صورت اجرا میکنیم تا تمامی فعالیتهای پروسس موردنظر را زیرنظر بگیرد و فایل Dump ای را تولید کند:
procdump.exe -ma -e 10896
حالا نوبت به کلیک بر روی Button، جهت ایجاد خطا میرسد. بر روی دکمه کلیک کرده و منتظر میشویم تا Dump، از حافظه جمع آوری و در سیستم تولید شود. عملیات با موفقیت انجام شده و فایل Dump در آدرس مشخص شده، ایجاد میشود.
پیدا کردن منشاء خطا
بعد از ایجاد فایل Dump، نوبت به پیدا کردن منشا خطا و رسیدن به کد موردنظر میرسد. ویژوال استودیو را باز کنید و فایل Dump را درون VS درگ/دراپ کنید.
در پنجرهای که باز میشود، میتوانید مشخصات کاملی از برنامه را مشاهده کنید. سمت راست، چند گزینه وجود دارند که با توجه به نوع برنامه (مدیریت شده یا محلی) و زبان برنامه نویسی، باید آنها را انتخاب کنید. از آنجائیکه برنامهی ما با زبان سی شارپ ایجاد شده، گزینهی اول یعنی Debug with Managed only را انتخاب میکنیم.
بعد از انتخاب این گزینه، بلافاصله به کدی که باعث ایجاد خطا میشود، هدایت میشویم:
کلام آخر اینکه سعی کنید تا حد ممکن، خودتان خطاها را مدیریت کنید و از ابزارهای خطایاب مانند AppCenter نیز استفاده کنید. اخیرا WPF و WinForm نیز به AppCenter اضافه شدهاند.
- شماره تلفن، به صورت رشته کاراکتری
- آدرس، به عنوان رشته کاراکتری
- نام مسئول رسیدگی به تیکت، به صورت رشته کاراکتری
اما بعد از سپری شدن مدتی از توسعه محصول ممکن است اقلام اطلاعاتی خاصی بر روی هر یک از آیتمهای بالا نیاز شود. به طور مثال برای آدرس نیاز باشد اطلاعات استان و شهر جداگانه قابل ذخیره سازی و گزارش گیری باشند و یا در کنار نام مسئول رسیدگی به تیکت، شماره تلفن او نیز وجود داشته باشد.
در چنین شرایطی، یک اقدام ممکن، افزودن اقلام اطلاعاتی مورد نیاز در همان مکان آیتم قبلی است؛ به طور مثال اگر نام مسئول بر روی موجودیت تیکت باشد، شماره تلفن مسئول نیز در همان موجودیت تیکت اضافه شود.
راه حل مناسبتر برای حل این نوع مشکلات ایجاد کلاس خاص آیتم اطلاعاتی و استفاده از شیء آن بهجای مقدار مربوطه است. به طور مثال به طراحی زیر دقت نمایید. در طراحی زیر کلاس دیگری به نام Agent ایجاد و در کلاس تیکت از آن استفاده کردهایم.
این بازسازی کد دو مزیت کلی دارد:
- راه را برای توسعه آینده آیتمهای دادهای باز میکند
- از تکرار آیتمهای دادهای جلوگیری میکند (به طور مثال زمانیکه از پایگاه دادههای رابطهای جهت ذخیره سازی، استفاده شود)
الف) چون کوکیها و یا توکنهای آنها دیگر قابل رمزگشایی نیستند (به علت باز تولید کلیدهای رمزنگاری و رمزگشایی اطلاعات)، مجبور به لاگین مجدد خواهند شد (تا کوکیهای جدیدی برای آنها تولید شوند). همچنین آنتیفورجری توکنهای آنها نیز مجددا باید تولید شوند.
ب) تمام اطلاعات محافظت شدهی توسط Data protection API قابل رمزگشایی نخواهند بود.
تنظیم Data protection API مخصوص برنامههای هاست شدهی توسط IIS
برای اینکه کلیدهای رمزنگاری اطلاعات برنامههای وب به صورت دائمی ذخیره شوند و با ریاستارت سرور از دست نروند، یکی از سه روش ذیل را میتوان بکار گرفت:
1) اسکریپت پاور شل ذیل را اجرا کنید:
نحوهی اجرای آن نیز به صورت ذیل است و پس از آن، نام Application pool مخصوص برنامه ذکر میشود:
.\Provision-AutoGenKeys.ps1 DefaultAppPool
2) به تنظیمات پیشرفتهی Application pool برنامه در IIS مراجعه کرده و خاصیت Load user profile آنرا true کنید.
در این حالت کلیدها به صورت دائمی در پوشهی پروفایل کاربر مخصوص Application pool برنامه، به صورت رمزنگاری شدهی توسط مکانیزم DPAPI ویندوز، ذخیره خواهند شد.
3) یک SSL Certificate معتبر را تهیه کنید و یا اگر از یک self signed certificate استفاده میکنید باید آنرا در Trusted Root store ویندوز قرار دهید. سپس از روش PersistKeysToFileSystem استفاده کنید.
public void ConfigureServices(IServiceCollection services) { services.AddDataProtection() .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\")) .ProtectKeysWithCertificate("thumbprint"); }
اگر از یک web farm استفاده میکنید، روش سوم ذکر شده، تنها روشی است که از آن میتوانید استفاده کنید. یک پوشهی اشتراکی قابل دسترسی بین سرورها را ایجاد کنید که دربرگیرندهی X509 certificate شما باشد. سپس این پوشه و مجوز موجود در آنرا توسط روش فوق به برنامه معرفی کنید.
1:data pages اساسیترین واحد نگهداری داده در اس کیوال سرور، صفحه نام دارد. فضای دیسک اختصاص یافته به فایل داده بانک، برای یک بانک اطلاعاتی به صورت منطقی به صفحات پیوسته از صفر تا n تقسیم بندی میشود. همچنین لازم به ذکر است عملیات خواندن و یا نوشتن در دیسک، در سطح این صفحهها صورت میگیرد که در تصویر زیر قابل مشاهده است:
لازم به ذکر است در sql server هر صفحه، 8 کیلوبایت است. این مورد به این معنی است که هر بانک اطلاعاتی، دارای 128 صفحه به ازای هر یک مگابایت است. هر صفحه دارای 96 بایت با عنوان header یا سرصفحه است که شامل اطلاعات سیستمی در مورد صفحه است. این اطلاعات سیستمی شامل مواردی چون page number یا شماره صفحه و نوع صفحه یا page type و مقدار فضای خالی آن صفحه و شماره شناسایی یک واحد اختصاص یافته یا به اختصار allocation unit id و.... هستند میباشد. نکته جالب و قابل توجه این است که فایلهای ثبت وقایع یا Log files از صفحه استفاده نمیکنند؛ بلکه شامل یکسری رکورد log هستند.
برای بدست آوردن اطلاعات در مورد فایلهای دیتابیس میتوانید از کد زیر استفاده نمایید SELECT * FROM sys.database_files که خروجی زیر را به شما نشان میدهد:
extents: به ابتداییترین قسمتی که sql server امکان مدیریت بر آن را دارد extent گویند. هر extent شامل 8 صفحهی به هم پیوسته است. لازم به ذکر است که sql server هر 1 مگابایت را به شانزده extent اختصاص میدهد. sql server شامل دونوع extent است که عبارتند از : uniform,mixed uniform extent متعلق به یک شیء است و هر هشت صفحهی آن فقط توسط یک شیء قابل استفادهاست. mixed extent میتواند حداکثر بین هشت شیء به اشتراک گذاشته شود؛ به نحوی که هر یک از هشت صفحه میتوانند متعلق به یک شیء باشند. همانطور که در شکل زیر میبینید به طور پیش فرض با ایجاد یک جدول، یک mixed extent به آن اختصاص داده میشود. در صورتیکه این شیء به اندازهی هشت صفحه رشد کند، به آن یک uniform extent اختصاص داده میشود.
فایلهای بانک اطلاعاتی
هر بانک اطلاعاتی در sql server دارای سه نوع فایل است
فایلهای داده اولیه یا به اختصار primary data files
فایلهای دادههای ثانویه یا به اختصار secondary data files
فایلهای ثبت وقایع یا به اختصار log file
فایل ثبت وقایع برای نگهداری و ثبت وقایع که برای عملیات recovery مورد نیاز است. معمولا یک بانک اطلاعاتی یک log file دارد؛ ولی میتواند بیشتر هم داشته باشد. پسوند این نوع فایلها ldf است .
ایجاد یک شاخهی جدید در Visual Studio و انتشار آن
به برگهی Team explorer مراجعه کرده و سپس گزینهی Branches آنرا انتخاب کنید:
در برگهی باز شده، انشعاب و شاخهی جاری با فونت ضخیم نمایش داده میشود. برای مثال در اینجا، انشعاب کاری همان master است:
برای ایجاد یک شاخهی جدید، بر روی لینک new branch کلیک کنید تا بتوان نامی را برای این منظور وارد کرد. بهتر است از نامهای با مفهومی مانند feature-X و یا fix-Y استفاده کنید (افزودن قابلیت X و یا رفع مشکل Y) و در آخر بر روی دکمهی Create branch کلیک کنید.
در اینجا میتوان مشخص کرد که انشعاب ایجاد شده باید بر اساس کدام انشعاب فعلی نیز تهیه شود (دراپ داون ذیل قسمتی که میتوان نام انشعاب را وارد کرد). برای مثال پروژههای مایکروسافت در GitHub، دارای سه شاخهی master، dev و release هستند. شاخهی dev (یا توسعه) جایی است که انشعابات pull requests را قبول خواهند کرد. بنابراین بر اساس ساختار و طراحی پروژهی جاری به این موضوع نیز باید دقت داشت.
پس از ایجاد شاخهی جدید، تصویر ذیل نمایان خواهد شد:
همانطور که ملاحظه میکنید، اینبار شاخهی جدید ایجاد شده به صورت bold و ضخیم نمایش داده شدهاست. این bold بودن به معنای شاخهی کاری جاری بودن است. همچنین این شاخه در قسمت unpublished branches قرار دارد. بنابراین کلیهی تغییرات واقع شدهی در آن، محلی بوده و هنوز با سرور هماهنگ نشدهاند.
برای انتشار و publish این شاخه، تنها کافی است تا بر روی آن کلیک راست کرده و گزینهی publish branch را انتخاب کنیم:
این انتشار سبب نمایش لیستی از تغییرات جدید در برگهی branches پروژه، در GitHub خواهد شد:
یک نکته: برای تغییر branch فعال جاری، فقط کافی است در برگهی branches در ویژوال استودیو، دوبار بر روی لینک نام آن شاخه کلیک کنید تا به صورت bold ظاهر شود.
ارسال تغییرات انجام شدهی در Branch به سرور
پس از کار بر روی شاخهی جدید ایجاد شده، اکنون نوبت به ارسال و هماهنگ سازی این تغییرات با سرور است. این مورد نیز همانند قبل بوده و ابتدا باید به برگهی Home و گزینهی changes آن مراجعه کرد:
و سپس تغییرات را به همراه توضیحی commit کرد:
اینکار سبب sync محلی میشود. سپس بر روی لینک sync کلیک نمائید و تغییرات را با سرور هماهنگ کنید.
یکی کردن تغییرات شاخهی جدید با شاخهی اصلی
هرچند این تغییرات به سرور ارسال شدهاند، اما چون در یک انشعاب کاری دیگر قرار دارند، با انشعاب اصلی یکی نخواهند شد. برای انجام عملیات merge، ابتدا به برگهی Home و سپس گزینهی branches مراجعه کنید. در ادامه بر روی لینک merge کلیک نمائید (تصاویر اول و دوم بحث).
در اینجا میخواهیم اطلاعات موجود در شاخهی افزودن توضیحات را با شاخهی اصلی یکی کنیم (انتخاب منبع و مقصد). سپس بر روی دکمهی merge کلیک نمائید.
اکنون برای ارسال این تغییرات به سرور، به برگهی Home و سپس گزینهی unsynced commits مراجعه کرده و بر روی دکمهی sync کلیک نمائید تا تغییرات یکی شده به سرور ارسال شوند.
public class Personel { [Key] public int PersonelID { get; set; } [MaxLength(15)] public string Name { get; set; } [MaxLength(25)] public string Family { get; set; } [MaxLength(10)] public string CodeMelli { get; set; } }
public class PersonalContext : DbContext { public DbSet<Personel> Personel { get; set; } public override int SaveChanges() { return base.SaveChanges(); } }
Install-Package EntityFramework.BulkInsert-ef6
public ActionResult Insert() { int Counter = 1000; List<Personel> Lst = new List<Personel>(); // شبیه سازی خواندن رکوردها از فایل اکسل for (int i = 0; i < Counter; i++) { Lst.Add(new Personel { CodeMelli = "0000000000", Family = "Karimi", Name = "Mohammad" }); } PersonalContext db = new PersonalContext(); db.BulkInsert(Lst); db.SaveChanges(); return View(); }