در اساس با پروژهی غیروبی که عنوان شد تفاوتی نمیکند و بیشتر مرتبط است به مطلب «اعتبارسنجی مبتنی بر کوکیها در ASP.NET Core 2.0 بدون استفاده از سیستم Identity» که با ASP.NET Core Identity مبتنی بر کوکیها بیشتر شباهت دارد تا مطلب جاری که مبتنی بر توکنها است. کوکیها را در HttpClient فعال میکنید. زمانیکه درخواست لاگین را توسط آن ارسال کردید و عملیات لاگین در سمت سرور با موفقیت به پایان رسید، یکسری کوکی رمزنگاری شده، حاوی User Claims به سمت کلاینت ارسال میشوند. در دفعات بعدی، فقط این کوکیها را به همراه HttpClient ارسال میکنید. به این صورت قسمتهایی که نیاز به اعتبارسنجی دارند کار میکنند.
نظرات مطالب
ایجاد یک Repository در پروژه برای دستورات EF
IQueryable فقط یک expression است. هنوز اجرا نشده. (expose آن از طریق وب سرویس اشتباه است و به مشکلات serialization برخواهید خورد.)
زمانیکه ToList، First و امثال آن روی این عبارت فراخوانی شود تبدیل به SQL شده و سپس بر روی بانک اطلاعاتی اجرا میشود. به این deferred execution یا اجرای به تعویق افتاده گفته میشود.
اگر این عبارت را در اختیار لایههای دیگر قرار دهید، یعنی انتهای کار را بازگذاشتهاید و حد و حدود سیستم شما مشخص نیست. شما اگر IQueryable بازگشت دهید، در لایهای دیگر میشود یک join روی آن نوشت و اطلاعات چندین جدول دیگر را استخراج کرد؛ درحالیکه نام متد شما GetUsers بوده. بنابراین بهتر است به صورت صریح اطلاعات را به شکل List بازگشت دهید، تا انتهای کار باز نمانده و طراحی شما نشتی نداشته باشد.
زمانیکه ToList، First و امثال آن روی این عبارت فراخوانی شود تبدیل به SQL شده و سپس بر روی بانک اطلاعاتی اجرا میشود. به این deferred execution یا اجرای به تعویق افتاده گفته میشود.
اگر این عبارت را در اختیار لایههای دیگر قرار دهید، یعنی انتهای کار را بازگذاشتهاید و حد و حدود سیستم شما مشخص نیست. شما اگر IQueryable بازگشت دهید، در لایهای دیگر میشود یک join روی آن نوشت و اطلاعات چندین جدول دیگر را استخراج کرد؛ درحالیکه نام متد شما GetUsers بوده. بنابراین بهتر است به صورت صریح اطلاعات را به شکل List بازگشت دهید، تا انتهای کار باز نمانده و طراحی شما نشتی نداشته باشد.
نظرات مطالب
رمزنگاری کانکشن استرینگ در ASP.Net
@ نیما
- در وب برای اینکه این کاراکترهای عجیب و غریب مشکل ساز نشوند یکبار دیگر هم اطلاعات رمزنگاری شده را از فیلتر base64 encoding عبور میدهند. به این صورت مشکلی برای نگهداری آنها در فایلها وجود نخواهد داشت.
- نگهداری اطلاعات حساس در حافظه به صورت plain کار اشتباهی است چون دامپ حافظه ویندوز و یا تمام سیستم عاملهای دیگر کار سادهای است. برنامههای زیادی هستند که پروسسهای ویندوز را لیست میکنند و به شما اجازه میدهند حافظه آنها را دامپ کنید (به راحتی چند کلیک). استخراج اطلاعات حساس هم از یک فایل دامپ تر و تازه زیاد مشکل نیست.
- سرعت الگوریتمهای رمزنگاری واقعا بالا است. پیاده سازیهای خیلی خوبی هم دارند. بنابراین زیاد نگران این سربار نباشید. چون در حد یک رشته ساده و امثال آن اصلا سرباری به حساب نمیآیند و بسیار سریع عمل میکنند.
- در وب برای اینکه این کاراکترهای عجیب و غریب مشکل ساز نشوند یکبار دیگر هم اطلاعات رمزنگاری شده را از فیلتر base64 encoding عبور میدهند. به این صورت مشکلی برای نگهداری آنها در فایلها وجود نخواهد داشت.
- نگهداری اطلاعات حساس در حافظه به صورت plain کار اشتباهی است چون دامپ حافظه ویندوز و یا تمام سیستم عاملهای دیگر کار سادهای است. برنامههای زیادی هستند که پروسسهای ویندوز را لیست میکنند و به شما اجازه میدهند حافظه آنها را دامپ کنید (به راحتی چند کلیک). استخراج اطلاعات حساس هم از یک فایل دامپ تر و تازه زیاد مشکل نیست.
- سرعت الگوریتمهای رمزنگاری واقعا بالا است. پیاده سازیهای خیلی خوبی هم دارند. بنابراین زیاد نگران این سربار نباشید. چون در حد یک رشته ساده و امثال آن اصلا سرباری به حساب نمیآیند و بسیار سریع عمل میکنند.
سلام
من از تنظیم کوکی برای تغییر Culture جاری کاربر استفاده کردم. در مرورگر pc کار میکنه ولی وقتی با مرورگر موبایل تغییر زبان رو انجام میدم کار نمیکنه
نظرات مطالب
متغیرها در ES 6
این تصویر مربوط به developer tools مرورگر کروم هست. این برگهی Console در developer tools مرورگر فایرفاکس هم وجود دارد.
نظرات مطالب
ASP.NET Web API - قسمت پنجم
کار معمولی با یک آدرس در مرورگر یعنی حالت Get. میشه این رو تغییر داد به Post که با بازکردن ساده آدرس در مرورگر کار نکنه.
پیش از شروع به کار توسعهی برنامههای مبتنی بر Blazor، باید با مبانی Razor آشنایی داشت. Razor امکان ترکیب کدهای #C و HTML را در یک فایل میسر میکند. دستور زبان آن از @ برای سوئیچ بین کدهای #C و HTML استفاده میکند. کدهای Razor را میتوان در فایلهای cshtml. نوشت که عموما مخصوص صفحات و Viewها هستند و یا در فایلهای razor. که برای توسعهی کامپوننتهای Balzor بکار گرفته میشوند. در اینجا مهم نیست که پسوند فایل مورد استفاده چیست؛ چون اصول razor بکار گرفته شده در آنها یکی است. البته در اینجا تاکید ما بیشتر بر روی فایلهای razor. است که در برنامههای مبتنی بر Blazor بکار گرفته میشوند.
ایجاد یک پروژهی جدید Blazor WASM
برای پیاده سازی و اجرای مثالهای این قسمت، نیاز به یک پروژهی جدید Blazor WASM را داریم که میتوان آنرا با اجرای دستور dotnet new blazorwasm --hosted در یک پوشهی خالی، ایجاد کرد.
یک نکته: دستور فوق به همراه یک سری پارامتر اختیاری مانند hosted-- نیز هست. برای مشاهدهی لیست آنها دستور dotnet new blazorwasm --help را صادر کنید. برای مثال ذکر پارامتر hosted-- سبب میشود تا یک ASP.NET Core host نیز برای Blazor WebAssembly app ایجاد شده تولید شود.
حالت hosted-- آن یک چنین ساختاری را دارد که از سه پروژه و پوشهی Client ،Server و Shared تشکیل میشود:
در اینجا یک پروژهی خالی WASM ایجاد شده که برخلاف حالت معمولی dotnet new blazorwasm که در قسمت قبل آنرا بررسی کردیم، دیگر از فایل استاتیک wwwroot\sample-data\weather.json در آن خبری نیست. بجای آن، یک پروژهی استاندارد ASP.NET Core Web API را در پوشهی جدید Server ایجاد کرده که کار ارائهی اطلاعات این سرویس آب و هوا را انجام میدهد و برنامهی WASM ایجاد شده، این اطلاعات را توسط HTTP Client خود، از سرور Web API دریافت میکند.
بنابراین اگر مدل برنامهای که قصد دارید تهیه کنید، ترکیبی از یک Web API و WASM است، روش hosted--، آغاز آنرا بسیار ساده میکند.
نکته: روش اجرای این نوع برنامهها با اجرای دستور dotnet run در داخل پوشهی Server پروژه، انجام میشود. با اینکار هم سرور ASP.NET Core آغاز میشود و هم برنامهی WASM توسط آن ارائه میگردد. در این حالت اگر آدرس https://localhost:5001 را در مرورگر باز کنیم، هم قسمتهای بدون نیاز به سرور پروژهی WASM قابل دسترسی است (مانند کار با شمارشگر آن) و هم قسمت دریافت اطلاعات از سرور آن، در منوی Fetch Data.
شروع به کار با Razor
پس از ایجاد یک پروژهی جدید WASM، به فایل Client\Pages\Index.razor آن مراجعه کرده و محتوای پیشفرض آنرا بجز سطر اول زیر، حذف میکنیم:
این سطر، بیانگر مسیریابی منتهی به کامپوننت جاری است. یعنی با گشودن برنامهی WASM در مرورگر و مراجعه به ریشهی سایت، محتوای این کامپوننت را مشاهده خواهیم کرد.
در فایلهای razor. میتوان ترکیبی از کدهای #C و HTML را نوشت. برای مثال:
در اینجا قصد داریم مقدار یک متغیر را در یک پاراگراف درج کنیم. به همین جهت برای تعریف آن و شروع به کدنویسی میتوان با تعریف یک قطعه کد که در فایلهای razor با code@ شروع میشود، اینکار را انجام داد. در این قطعه کد، نوشتن هر نوع کد #C ای مجاز است که نمونهای از آنرا در اینجا با تعریف یک متغیر مشاهده میکنید. اکنون برای درج مقدار این متغیر در بین کدهای HTML از حرف @ استفاده میکنیم؛ مانند name@ در اینجا. نمونهای از خروجی تغییرات فوق را در تصویر زیر مشاهده میکنید:
یک نکته: با توجه به اینکه تغییرات زیادی را در فایل جاری اعمال خواهیم کرد، بهتر است برنامه را با دستور dotnet watch run اجرا کرد، تا این تغییرات را تحت نظر قرار داده و آنها را به صورت خودکار کامپایل کند. به این صورت دیگر نیازی نخواهد بود به ازای هر تغییر، یکبار دستور dotnet run اجرا شود.
در زمان درج متغیرهای #C در بین کدهای HTML توسط razor، استفاده از تمام متدهای الحاقی زبان #C نیز مجاز هستند؛ مانند:
بنابراین درج حرف @ در بین کدهای HTML به این معنا است که به کامپایلر razor اعلام میکنیم، پس از این حرف، هر عبارتی که قرار میگیرد، یک عبارت معتبر #C است.
یا حتی میتوان یک متد جدید را مانند CustomToUpper در قطعه کد razor، تعریف کرد و از آن به صورت زیر استفاده نمود:
در این مثالها، ابتدای عبارت #C تعریف شده با حرف @ شروع میشود و انتهای آنرا خود کامپایلر razor بر اساس بسته شدن تگ p تعریف شده، تشخیص میدهد. اما اگر قصد داشته باشیم برای مثال جمع دو عدد را در اینجا محاسبه کنیم چطور؟
در این حالت امکان تشخیص ابتدا و انتهای عبارت #C توسط کامپایلر میسر نیست. برای رفع این مشکل میتوان از پرانتزها استفاده کرد:
نمونهی دیگر نیاز به تعریف ابتدا و انتهای یک قطعه کد، در حین تعریف مدیریت کنندگان رویدادها است:
در اینجا onclick@ مشخص میکند که با کلیک بر روی این دکمه قرار است قطعه کد #C ای اجرا شود. سپس با استفاده از ()@ محدودهی این قطعه کد، مشخص میشود و اکنون در داخل آن میتوان یک anonymous function را تعریف کرد که خروجی آن را در قسمت console ابزارهای توسعه دهندگان مرورگر میتوان مشاهده کرد:
در اینجا اگر از Console.WriteLine("Test")@ استفاده میشد، به معنای انتساب یک رشتهی محاسبه شده به رویداد onclick بود که مجاز نیست.
روش دیگر انجام اینکار به صورت زیر است:
میتوان یک متد void را تعریف کرد و سپس فقط نام آنرا توسط @ به onlick انتساب داد. ذکر این نام، اشارهگری خواهد بود به متد اجرا نشدهی WriteLog. در این حالت اگر نیاز به ارسال پارامتری به متد WriteLog بود، چطور؟
در این حالت نیز میتوان از روش بکارگیری anonymous functionها برای تعریف پارامتر استفاده کرد.
یک نکته: اگر به اشتباه بجای WriteLogWithParam، همان WriteLog قبلی را بنویسیم، کامپایلر (در حال اجرای توسط دستور dotnet watch run) خطای زیر را نمایش میدهد؛ پیش از اینکه برنامه در مرورگر اجرا شود:
امکان تعریف کلاسها در فایلهای razor.
در فایلهای razor.، محدود به تعریف یک سری متدها و متغیرهای ساده نیستیم. در اینجا امکان تعریف کلاسها نیز وجود دارد و همچنین میتوان از کلاسهای خارجی (کلاسهایی که خارج از فایل razor جاری تعریف شدهاند) نیز استفاده کرد.
برای نمونه در اینجا یک کلاس کمکی را جهت تعریف متد MyCustomToUpper، اضافه کردهایم. در ادامه نحوهی استفاده از این متد را در پاراگراف تعریف شده، مشاهده میکنید که همانند کار با کلاس و متدهای متداول #C است.
البته این کلاس را تنها میتوان داخل همین کامپوننت استفاده کرد. برای اینکه بتوان از امکانات این کلاس، در سایر کامپوننتها نیز استفاده کرد، میتوان آنرا در پروژهی Shared قرار داد. اگر به تصویر ابتدای مطلب جاری دقت کنید، سه پروژه ایجاد شدهاست:
الف) پروژهی کلاینت: که همان WASM است.
ب) پروژهی سرور: که یک پروژهی ASP.NET Core Web API ارائه کنندهی سرویس و API آب و هوا است و همچنین هاست کنندهی WASM ما.
ج) پروژهی Shared: کدهای این پروژه، بین هر دو پروژه به اشتراک گذاشته میشوند و برای مثال محل مناسبی است برای تعریف DTO ها. برای نمونه WeatherForecast.cs قرار گرفتهی در آن، DTO یا data transfer object سرویس API برنامه است که قرار است به کلاینت بازگشت داده شود. به این ترتیب دیگر نیازی نخواهد بود تا این تعاریف را در پروژههای سرور و کلاینت تکرار کنیم و میتوان کدهای اینگونه را به اشتراک گذاشت.
کاربرد دیگر آن تعریف کلاسهای کمکی است؛ مانند StringUtils فوق. به همین به پروژهی Shared مراجعه کرده و کلاس StringUtils را به صورت زیر در آن تعریف میکنیم (و یا حتی میتوان این قطعه کد را داخل یک پوشهی جدید، در همان پروژهی WASM نیز قرار داد):
اگر به فایلهای csproj دو پروژهی سرور و کلاینت جاری مراجعه کنیم، از پیش، مدخلی را به فایل Shared\BlazorRazorSample.Shared.csproj دارند. بنابراین جهت معرفی این اسمبلی به آنها، نیاز به کار خاصی نیست و از پیش، ارجاعی به آن تعریف شدهاست.
پس از آن روش استفادهی از این کلاس کمکی خارجی اشتراکی به صورت زیر است:
ابتدا فضای نام این کلاس را با استفاده از using@ مشخص میکنیم و سپس امکان دسترسی به امکانات آن میسر میشود.
یک نکته: میتوان به فایل Client\_Imports.razor مراجعه و مدخل زیر را به انتهای آن اضافه کرد:
به این ترتیب دیگر نیازی به ذکر این using@ تکراری، در هیچکدام از فایلهای razor. پروژهی کلاینت نخواهد بود؛ چون تعاریف درج شدهی در فایل Client\_Imports.razor سراسری هستند.
کار با حلقهها در فایلهای razor.
همانطور که عنوان شد، یکی از کاربردهای پروژهی Shared، امکان به اشتراک گذاشتن مدلها، در برنامههای کلاینت و سرور است. برای مثال یک پوشهی جدید Models را در این پروژه ایجاد کرده و کلاس MovieDto را به صورت زیر در آن تعریف میکنیم:
سپس به فایل Client\_Imports.razor مراجعه کرده و فضای نام این پوشه را اضافه میکنیم؛ تا دیگر نیازی به تکرار آن در تمام فایلهای razor. برنامهی کلاینت نباشد:
اکنون میخواهیم لیستی از فیلمها را در فایل Client\Pages\Index.razor نمایش دهیم:
در اینجا در ابتدا لیستی از MovieDtoها در قسمت code@ تعریف شده و سپس روش استفادهی از یک حلقهی foreach سیشارپ را در کدهای razor نوشته شده، مشاهده میکنید که این خروجی را ایجاد میکند:
یک نکته: در حین تعریف فیلدهای code@، امکان استفادهی از var وجود ندارد؛ مگر اینکه از آن بخواهیم در داخل بدنهی یک متد استفاده کنیم.
و یا نمونهی دیگری از حلقههای #C مانند for را میتوان به صورت زیر تعریف کرد:
در اینجا روش تغییر پویای background-color هر ردیف را نیز به کمک کدهای razor، مشاهده میکنید. اگر شمارهی ردیفی زوج بود، با آبی نمایش داده میشود؛ در غیراینصورت با قرمز. در اینجا نیز از ()@ برای تعیین محدودهی کدهای #C نوشته شده، کمک گرفتهایم.
نمایش شرطی عبارات در فایلهای razor.
اگر به مثال توکار Client\Pages\FetchData.razor مراجعه کنیم (مربوط به حالت host-- که در ابتدای مطلب عنوان شد)، کدهای زیر قابل مشاهده هستند:
در این مثال، روش کار با یک سرویس تزریق شدهی async که قرار است از Web API اطلاعاتی را دریافت کند، مشاهده میکنید. در اینجا برخلاف مثال قبلی ما، از روال رویدادگردان OnInitializedAsync برای مقدار دهی لیست یا آرایهای از اطلاعات وضعیت هوا استفاده شدهاست (و نه به صورت مستقیم در یک فیلد قسمت code@). این مورد جزو life-cycleهای کامپوننتهای razor است که در قسمتهای بعد بیشتر بررسی خواهد شد. متد OnInitializedAsync برای بارگذاری اطلاعات یک سرویس از راه دور استفاده میشود و در اولین بار اجرای کامپوننت فراخوانی خواهد شد. نکتهی مهمی که در اینجا وجود دارد، نال بودن فیلد forecasts در زمان رندر اولیهی کامپوننت جاری است؛ از این جهت که کار دریافت اطلاعات از سرور زمانبر است ولی رندر کامپوننت، به صورت آنی صورت میگیرد. در این حالت زمانیکه نوبت به اجرای foreach (var forecast in forecasts)@ میرسد، برنامه با یک استثنای نال بودن forecasts، متوقف خواهد شد؛ چون هنوز کار OnInitializedAsync به پایان نرسیدهاست:
برای رفع این مشکل، ابتدا یک if@ مشاهده میشود، تا نال بودن forecasts را بررسی کند:
و همچنین عبارت در حال بارگذاری را نمایش میدهد. سپس در قسمت else آن، نمایش اطلاعات دریافت شده را توسط یک حلقهی foreach مشاهده میکنید. با مقدار دهی forecasts در متد OnInitializedAsync، مجددا کار رندر جدول انجام خواهد شد.
روش نمایش عبارات HTML در فایلهای razor.
فرض کنید عنوان اول فیلم مثال جاری، به همراه یک تگ HTML هم هست:
در این حالت اگر برنامه را اجرا کنیم، خروجی آن دقیقا به صورت <Title: <i>Movie 1</i خواهد بود. این مورد به دلایل امنیتی انجام شدهاست. اگر پیشتر تگهای HTML را تمیز کردهاید و مطمئن هستید که خطری را ایجاد نمیکنند، میتوانید با استفاده از روش زیر، آنها را رندر کرد:
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-03.zip
برای اجرای آن وارد پوشهی Server شده و دستور dotnet run را اجرا کنید.
ایجاد یک پروژهی جدید Blazor WASM
برای پیاده سازی و اجرای مثالهای این قسمت، نیاز به یک پروژهی جدید Blazor WASM را داریم که میتوان آنرا با اجرای دستور dotnet new blazorwasm --hosted در یک پوشهی خالی، ایجاد کرد.
یک نکته: دستور فوق به همراه یک سری پارامتر اختیاری مانند hosted-- نیز هست. برای مشاهدهی لیست آنها دستور dotnet new blazorwasm --help را صادر کنید. برای مثال ذکر پارامتر hosted-- سبب میشود تا یک ASP.NET Core host نیز برای Blazor WebAssembly app ایجاد شده تولید شود.
حالت hosted-- آن یک چنین ساختاری را دارد که از سه پروژه و پوشهی Client ،Server و Shared تشکیل میشود:
در اینجا یک پروژهی خالی WASM ایجاد شده که برخلاف حالت معمولی dotnet new blazorwasm که در قسمت قبل آنرا بررسی کردیم، دیگر از فایل استاتیک wwwroot\sample-data\weather.json در آن خبری نیست. بجای آن، یک پروژهی استاندارد ASP.NET Core Web API را در پوشهی جدید Server ایجاد کرده که کار ارائهی اطلاعات این سرویس آب و هوا را انجام میدهد و برنامهی WASM ایجاد شده، این اطلاعات را توسط HTTP Client خود، از سرور Web API دریافت میکند.
بنابراین اگر مدل برنامهای که قصد دارید تهیه کنید، ترکیبی از یک Web API و WASM است، روش hosted--، آغاز آنرا بسیار ساده میکند.
نکته: روش اجرای این نوع برنامهها با اجرای دستور dotnet run در داخل پوشهی Server پروژه، انجام میشود. با اینکار هم سرور ASP.NET Core آغاز میشود و هم برنامهی WASM توسط آن ارائه میگردد. در این حالت اگر آدرس https://localhost:5001 را در مرورگر باز کنیم، هم قسمتهای بدون نیاز به سرور پروژهی WASM قابل دسترسی است (مانند کار با شمارشگر آن) و هم قسمت دریافت اطلاعات از سرور آن، در منوی Fetch Data.
شروع به کار با Razor
پس از ایجاد یک پروژهی جدید WASM، به فایل Client\Pages\Index.razor آن مراجعه کرده و محتوای پیشفرض آنرا بجز سطر اول زیر، حذف میکنیم:
@page "/"
در فایلهای razor. میتوان ترکیبی از کدهای #C و HTML را نوشت. برای مثال:
@page "/" <p>Hello, @name</p> @code { string name = "Vahid N."; }
یک نکته: با توجه به اینکه تغییرات زیادی را در فایل جاری اعمال خواهیم کرد، بهتر است برنامه را با دستور dotnet watch run اجرا کرد، تا این تغییرات را تحت نظر قرار داده و آنها را به صورت خودکار کامپایل کند. به این صورت دیگر نیازی نخواهد بود به ازای هر تغییر، یکبار دستور dotnet run اجرا شود.
در زمان درج متغیرهای #C در بین کدهای HTML توسط razor، استفاده از تمام متدهای الحاقی زبان #C نیز مجاز هستند؛ مانند:
<p>Hello, @name.ToUpper()</p>
یا حتی میتوان یک متد جدید را مانند CustomToUpper در قطعه کد razor، تعریف کرد و از آن به صورت زیر استفاده نمود:
@page "/" <p>Hello, @name.ToUpper()</p> <p>Hello, @CustomToUpper(name)</p> @code { string name = "Vahid N."; string CustomToUpper(string value) => value.ToUpper(); }
<p>Let's add 2 + 2 : @2 + 2 </p>
<p>Let's add 2 + 2 : @(2 + 2) </p>
<button @onclick="@(()=>Console.WriteLine("Test"))">Click me</button>
در اینجا اگر از Console.WriteLine("Test")@ استفاده میشد، به معنای انتساب یک رشتهی محاسبه شده به رویداد onclick بود که مجاز نیست.
روش دیگر انجام اینکار به صورت زیر است:
@page "/" <button @onclick="@WriteLog">Click me 2</button> @code { void WriteLog() { Console.WriteLine("Test"); } }
@page "/" <button @onclick="@(()=>WriteLogWithParam("Test 3"))">Click me 3</button> @code { void WriteLogWithParam(string value) { Console.WriteLine(value); } }
یک نکته: اگر به اشتباه بجای WriteLogWithParam، همان WriteLog قبلی را بنویسیم، کامپایلر (در حال اجرای توسط دستور dotnet watch run) خطای زیر را نمایش میدهد؛ پیش از اینکه برنامه در مرورگر اجرا شود:
BlazorRazorSample\Client\Pages\Index.razor(12,25): error CS1501: No overload for method 'WriteLog' takes 1 arguments
امکان تعریف کلاسها در فایلهای razor.
در فایلهای razor.، محدود به تعریف یک سری متدها و متغیرهای ساده نیستیم. در اینجا امکان تعریف کلاسها نیز وجود دارد و همچنین میتوان از کلاسهای خارجی (کلاسهایی که خارج از فایل razor جاری تعریف شدهاند) نیز استفاده کرد.
@page "/" <p>Hello, @StringUtils.MyCustomToUpper(name)</p> @code { public class StringUtils { public static string MyCustomToUpper(string value) => value.ToUpper(); } }
البته این کلاس را تنها میتوان داخل همین کامپوننت استفاده کرد. برای اینکه بتوان از امکانات این کلاس، در سایر کامپوننتها نیز استفاده کرد، میتوان آنرا در پروژهی Shared قرار داد. اگر به تصویر ابتدای مطلب جاری دقت کنید، سه پروژه ایجاد شدهاست:
الف) پروژهی کلاینت: که همان WASM است.
ب) پروژهی سرور: که یک پروژهی ASP.NET Core Web API ارائه کنندهی سرویس و API آب و هوا است و همچنین هاست کنندهی WASM ما.
ج) پروژهی Shared: کدهای این پروژه، بین هر دو پروژه به اشتراک گذاشته میشوند و برای مثال محل مناسبی است برای تعریف DTO ها. برای نمونه WeatherForecast.cs قرار گرفتهی در آن، DTO یا data transfer object سرویس API برنامه است که قرار است به کلاینت بازگشت داده شود. به این ترتیب دیگر نیازی نخواهد بود تا این تعاریف را در پروژههای سرور و کلاینت تکرار کنیم و میتوان کدهای اینگونه را به اشتراک گذاشت.
کاربرد دیگر آن تعریف کلاسهای کمکی است؛ مانند StringUtils فوق. به همین به پروژهی Shared مراجعه کرده و کلاس StringUtils را به صورت زیر در آن تعریف میکنیم (و یا حتی میتوان این قطعه کد را داخل یک پوشهی جدید، در همان پروژهی WASM نیز قرار داد):
namespace BlazorRazorSample.Shared { public class StringUtils { public static string MyNewCustomToUpper(string value) => value.ToUpper(); } }
پس از آن روش استفادهی از این کلاس کمکی خارجی اشتراکی به صورت زیر است:
@page "/" @using BlazorRazorSample.Shared <p>Hello, @StringUtils.MyNewCustomToUpper(name)</p>
یک نکته: میتوان به فایل Client\_Imports.razor مراجعه و مدخل زیر را به انتهای آن اضافه کرد:
@using BlazorRazorSample.Shared
کار با حلقهها در فایلهای razor.
همانطور که عنوان شد، یکی از کاربردهای پروژهی Shared، امکان به اشتراک گذاشتن مدلها، در برنامههای کلاینت و سرور است. برای مثال یک پوشهی جدید Models را در این پروژه ایجاد کرده و کلاس MovieDto را به صورت زیر در آن تعریف میکنیم:
using System; namespace BlazorRazorSample.Shared.Models { public class MovieDto { public string Title { set; get; } public DateTime ReleaseDate { set; get; } } }
@using BlazorRazorSample.Shared.Models
@page "/" <div> <h3>Movies</h3> @foreach(var movie in movies) { <p>Title: <b>@movie.Title</b></p> <p>ReleaseDate: @movie.ReleaseDate.ToString("dd MMM yyyy")</p> } </div> @code { List<MovieDto> movies = new List<MovieDto> { new MovieDto { Title = "Movie 1", ReleaseDate = DateTime.Now.AddYears(-1) }, new MovieDto { Title = "Movie 2", ReleaseDate = DateTime.Now.AddYears(-2) }, new MovieDto { Title = "Movie 3", ReleaseDate = DateTime.Now.AddYears(-3) } }; }
یک نکته: در حین تعریف فیلدهای code@، امکان استفادهی از var وجود ندارد؛ مگر اینکه از آن بخواهیم در داخل بدنهی یک متد استفاده کنیم.
و یا نمونهی دیگری از حلقههای #C مانند for را میتوان به صورت زیر تعریف کرد:
@for(var i = 0; i < movies.Count; i++) { <div style="background-color: @(i % 2 == 0 ? "blue" : "red")"> <p>Title: <b>@movies[i].Title</b></p> <p>ReleaseDate: @movies[i].ReleaseDate.ToString("dd MMM yyyy")</p> </div> }
نمایش شرطی عبارات در فایلهای razor.
اگر به مثال توکار Client\Pages\FetchData.razor مراجعه کنیم (مربوط به حالت host-- که در ابتدای مطلب عنوان شد)، کدهای زیر قابل مشاهده هستند:
@page "/fetchdata" @using BlazorRazorSample.Shared @inject HttpClient Http <h1>Weather forecast</h1> <p>This component demonstrates fetching data from the server.</p> @if (forecasts == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <tr> <td>@forecast.Date.ToShortDateString()</td> <td>@forecast.TemperatureC</td> <td>@forecast.TemperatureF</td> <td>@forecast.Summary</td> </tr> } </tbody> </table> } @code { private WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast"); } }
برای رفع این مشکل، ابتدا یک if@ مشاهده میشود، تا نال بودن forecasts را بررسی کند:
@if (forecasts == null) { <p><em>Loading...</em></p> }
روش نمایش عبارات HTML در فایلهای razor.
فرض کنید عنوان اول فیلم مثال جاری، به همراه یک تگ HTML هم هست:
new MovieDto { Title = "<i>Movie 1</i>", ReleaseDate = DateTime.Now.AddYears(-1) },
<p>Title: <b>@((MarkupString)movie.Title)</b></p>
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-03.zip
برای اجرای آن وارد پوشهی Server شده و دستور dotnet run را اجرا کنید.
postgres یک بانک اطلاعاتی متن باز، قدرتمند و relational میباشد که پس از 30 سال توسعهی فعال، به کارآیی بالا، قابل اطمینان بودن و قدرتمند بودن شهرت دارد. همچنین در بنچمارکهای مربوط به وبسایت techempower نیز استفاده از این پایگاه داده در کنار asp.net core باعث شدهاست تا جایگاه خوبی کسب شود. علاوه بر این ویژگیها، انعطاف نوع دادهها (data type) سبب تفاوت بین رقبا شده است. برای مثال فرض کنید که یک جدول به اسم Blog و قصد ذخیره تگهای مقاله را داریم. کار به چه صورت خواهد بود؟
- ساخت جدول و رابطه چند به چند
ایراد قطعه کد اول عدم امکان سرچ اصولی در بین کلمات کلیدی میباشد؛ زیرا شما مجبور به جستجو در یک فیلد هستید که واژهها با کاراکتری از یکدیگر جدا شدهاند. پس شما سرچ دقیقی نخواهید داشت. پس از این مشکل میتوان به وجود تگهای تکراری در یک رکورد و یا نداشتن تگها به صورت یکپارچه اشاره کرد و به عنوان مشکل آخر میتواند به یک سربار برای سیستم تبدیل شود. چون هر بار که دیتا واکشی میشود، باید یکبار کلمات را از یکدیگر جدا و سپس به یک آرایه تبدیل و هنگام ذخیره شدن نیز مجددا این رشتهها را به هم بچسبانیم.
در مورد کد دوم هم شاید بتوان به انباشته شدن زیاد سطرها و یا عدم ساختار مدرن اشاره کرد.
اگر پیشتر با postgres آشنایی نداشته باشید، یکی از دو سناریو زیر را پیاده سازی خواهید کرد:
- ذخیره در یک فیلد به صورت nvarchar و جدا کردن حرفها با یک کاراکتر (برای مثال: dntips, csharp با کارکتر , از هم جدا شده اند)
Blog ID BlogID int Title nvarchar(250) Tags nvarchar(500)
Blog Tags Tbl BlogID int TagID int
در مورد کد دوم هم شاید بتوان به انباشته شدن زیاد سطرها و یا عدم ساختار مدرن اشاره کرد.
اما در postgres به راحتی میتوان این گونه دیتاها را به صورت آرایه ذخیره سازی کرد:
CREATE TABLE sal_emp ( name text, pay_by_quarter integer[], schedule text[][] );
که سبب سرچ اصولی و واکشی سریعتر اطلاعات خواهد شد.
راجع به نوع داده array در postgres در بیشتر مطالعه کنید.
راجع به نوع داده array در postgres در بیشتر مطالعه کنید.
در همین بحث دیتا تایپها میتوان به نوع text اشاره کرد که جایگزینی برای nvarchar و یا varchar میباشد. در اینجا نیازی به مشخص کردن سایز رشته نیز نیست و تمام فرآیند، به صورت خودکار در پسزمینه انجام خواهد شد. مثلا بجای nvarcahr 500 و یا MAX تنها، نوع داده را برابر text قرار میدهید.
از نظر ساختار زبانی و syntax بسیار شبیه به سایر providerها میباشد و تنها تفاوت اساسی آن، حساس بودن به حروف کوچک و بزرگ است.
سایر مزیتهای postgres را میتوانید از زبان shayro jan sky، در کانال دات نت و در ویدیو لینک شده مشاهده کنید.
مزیت بزرگ آن که باعث میشود تا از آن بتوانیم در پروژههای خود استفاده کنیم، سازگاری آن با ef core میباشد. یعنی اگر کل برنامهی شما با ef core پیاده سازی شده باشد، با عوض کردن متد UseSqlServer به UseNpgsql در کلاس program، مشکلی در برنامه رخ نخواهد داد و اپلیکیشن شما بجای استفاده از sql server به راحتی از postgres استفاده خواهد کرد.
متد نام برده شده در پکیج زیر قابل دسترسی میباشد:
کارها تماما مانند ef و حتی با کتابخانههای مربوط به آن انجام خواهد شد و تنها تغییر در کدها، همین متد UseNpgsql میباشد که provider را عوض خواهد کرد.
در قسمت بعد به نصب و راه اندازی postrgress و دشبردهای مدیریتی آن از طریق داکر و پیاده سازی CRUD خواهیم پرداخت.
سایر مزیتهای postgres را میتوانید از زبان shayro jan sky، در کانال دات نت و در ویدیو لینک شده مشاهده کنید.
مزیت بزرگ آن که باعث میشود تا از آن بتوانیم در پروژههای خود استفاده کنیم، سازگاری آن با ef core میباشد. یعنی اگر کل برنامهی شما با ef core پیاده سازی شده باشد، با عوض کردن متد UseSqlServer به UseNpgsql در کلاس program، مشکلی در برنامه رخ نخواهد داد و اپلیکیشن شما بجای استفاده از sql server به راحتی از postgres استفاده خواهد کرد.
متد نام برده شده در پکیج زیر قابل دسترسی میباشد:
Npgsql.EntityFrameworkCore.PostgreSQL
کارها تماما مانند ef و حتی با کتابخانههای مربوط به آن انجام خواهد شد و تنها تغییر در کدها، همین متد UseNpgsql میباشد که provider را عوض خواهد کرد.
در قسمت بعد به نصب و راه اندازی postrgress و دشبردهای مدیریتی آن از طریق داکر و پیاده سازی CRUD خواهیم پرداخت.
مزیت استفاده از یوزر کنترلها، ماژولار کردن برنامه است. برای مثال اگر صفحه جاری شما قرار است از چهار قسمت اخبار، منوی پویا ، سخن روز و آمار کاربران تشکیل شود، میتوان هر کدام را توسط یک یوزر کنترل پیاده سازی کرده و سپس صفحه اصلی را از کنار هم قرار دادن این یوزر کنترلها تهیه نمود.
با این توضیحات اکنون میخواهیم یک یوزکنترل ASP.Net را توسط jQuery Ajax بارگذاری کرده و نمایش دهیم. حداقل دو مورد کاربرد را میتوان برای آن متصور شد:
الف) در اولین باری که یک صفحه در حال بارگذاری است، قسمتهای مختلف آنرا بتوان از یوزر کنترلهای مختلف خواند و تا زمان بارگذاری کامل هر کدام، یک عبارت لطفا منتظر بمانید را نمایش داد. نمونهی آنرا شاید در بعضی از CMS های جدید دیده باشید. صفحه به سرعت بارگذاری میشود. در حالیکه مشغول مرور صفحه جاری هستید، قسمتهای مختلف صفحه پدیدار میشوند.
ب) بارگذاری یک قسمت دلخواه صفحه بر اساس درخواست کاربر. مثلا کلیک بر روی یک دکمه و امثال آن.
روش کلی کار:
1) تهیه یک متد وب سرویس که یوزر کنترل را بر روی سرور اجرا کرده و حاصل را تبدیل به یک رشته کند.
2) استفاده از متد Ajax جیکوئری برای فراخوانی این متد وب سرویس و افزودن رشته دریافت شده به صفحه.
بدیهی است زمانیکه متد Ajax فراخوانی میشود میتوان عبارت یا تصویر منتظر بمانید را نمایش داد و پس از پایان کار این متد، عبارت (یا تصویر) را مخفی نمود.
پیاده سازی:
قسمت تبدیل یک یوزر کنترل به رشته را قبلا در مقاله "تهیه قالب برای ایمیلهای ارسالی یک برنامه ASP.Net" مشاهده کردهاید. در اینجا برای استفاده از این متد در یک وب سرویس نیاز به کمی تغییر وجود داشت (KeyValuePair ها درست سریالایز نمیشوند) که نتیجه نهایی به صورت زیر است. یک فایل Ajax.asmx را به برنامه اضافه کرده و سپس در صفحه Ajax.asmx.cs کد آن به صورت زیر میتواند باشد:
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Script.Services;
using System.Web.Services;
using System.Web.UI;
using System.Web.UI.HtmlControls;
namespace AjaxTest
{
public class KeyVal
{
public string Key { set; get; }
public object Value { set; get; }
}
/// <summary>
/// Summary description for Ajax
/// </summary>
[ScriptService]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class Ajax : WebService
{
/// <summary>
/// Removes Form tags using Regular Expression
/// </summary>
private static string cleanHtml(string html)
{
return Regex.Replace(html, @"<[/]?(form)[^>]*?>", string.Empty, RegexOptions.IgnoreCase);
}
/// <summary>
/// تبدیل یک یوزر کنترل به معادل اچ تی ام ال آن
/// </summary>
/// <param name="path">مسیر یوزر کنترل</param>
/// <param name="properties">لیست خواص به همراه مقادیر مورد نظر</param>
/// <returns></returns>
/// <exception cref="NotImplementedException"><c>NotImplementedException</c>.</exception>
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string RenderUserControl(string path,
List<KeyVal> properties)
{
Page pageHolder = new Page();
UserControl viewControl =
(UserControl)pageHolder.LoadControl(path);
viewControl.EnableViewState = false;
Type viewControlType = viewControl.GetType();
if (properties != null)
foreach (var pair in properties)
{
if (pair.Key != null)
{
PropertyInfo property =
viewControlType.GetProperty(pair.Key);
if (property != null)
{
if (pair.Value != null) property.SetValue(viewControl, pair.Value, null);
}
else
{
throw new NotImplementedException(string.Format(
"UserControl: {0} does not have a public {1} property.",
path, pair.Key));
}
}
}
//Form control is mandatory on page control to process User Controls
HtmlForm form = new HtmlForm();
//Add user control to the form
form.Controls.Add(viewControl);
//Add form to the page
pageHolder.Controls.Add(form);
//Write the control Html to text writer
StringWriter textWriter = new StringWriter();
//execute page on server
HttpContext.Current.Server.Execute(pageHolder, textWriter, false);
// Clean up code and return html
return cleanHtml(textWriter.ToString());
}
}
}
چند نکته:
الف) وب کانفیگ برنامه ASP.Net شما اگر با VS 2008 ایجاد شده باشد مداخل لازم را برای استفاده از این وب سرویس توسط jQuery Ajax دارد در غیر اینصورت موفق به استفاده از آن نخواهید شد.
ب) هنگام بازگرداندن این اطلاعات با فرمت json = ResponseFormat.Json جهت استفاده در jQuery Ajax ، گاهی از اوقات بسته به حجم بازگردانده شده ممکن است خطایی حاصل شده و عملیات متوقف شد. این طول پیش فرض را (maxJsonLength) در وب کانفیگ به صورت زیر تنظیم کنید تا مشکل حل شود:
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="10000000"></jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
برای پیاده سازی قسمت Ajax آن برای اینکه کار کمی تمیزتر و با قابلیت استفاده مجدد شود یک پلاگین تهیه شده (فایلی با نام jquery.advloaduc.js) که سورس آن به صورت زیر است:
$.fn.advloaduc = function(options) {
var defaults = {
webServiceName: 'Ajax.asmx', //نام فایل وب سرویس ما
renderUCMethod: 'RenderUserControl', //متد وب سرویس
ucMethodJsonParams: '{path:\'\'}',//پارامترهایی که قرار است پاس شوند
completeHandler: null //پس از پایان کار وب سرویس این متد جاوا اسکریپتی فراخوانی میشود
};
var options = $.extend(defaults, options);
return this.each(function() {
var obj = $(this);
obj.prepend("<div align='center'> لطفا اندکی تامل بفرمائید... <img src=\"images/loading.gif\"/></div>");
$.ajax({
type: "POST",
url: options.webServiceName + "/" + options.renderUCMethod,
data: options.ucMethodJsonParams,
contentType: "application/json; charset=utf-8",
dataType: "json",
success:
function(msg) {
obj.html(msg.d);
// if specified make callback and pass element
if (options.completeHandler)
options.completeHandler(this);
},
error:
function(XMLHttpRequest, textStatus, errorThrown) {
obj.html("امکان اتصال به سرور در این لحظه مقدور نیست. لطفا مجددا سعی کنید.");
}
});
});
};
عمده کاری که در این پلاگین صورت میگیرد فراخوانی متد Ajax جیکوئری است. سپس به متد وب سرویس ما (که در اینجا نام آن به صورت پارامتر نیز قابل دریافت است)، پارامترهای لازم پاس شده و سپس نتیجه حاصل به یک شیء در صفحه اضافه میشود.
completeHandler آن اختیاری است و پس از پایان کار متد اجکس فراخوانی میشود. در صورتیکه به آن نیازی نداشتید یا مقدار آن را null قرار دهید یا اصلا آنرا ذکر نکنید.
مثالی در مورد استفاده از این وب سرویس و همچنین پلاگین جیکوئری نوشته شده:
الف) یوزر کنترل ساده زیر را به پروژه اضافه کنید:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="part1.ascx.cs" Inherits="TestJQueryAjax.part1" %>
<asp:Label runat="server" ID="lblData" ></asp:Label>
سپس کد آنرا به صورت زیر تغییر دهید:
using System;
using System.Threading;
namespace TestJQueryAjax
{
public partial class part1 : System.Web.UI.UserControl
{
public string Text1 { set; get; }
public string Text2 { set; get; }
protected void Page_Load(object sender, EventArgs e)
{
Thread.Sleep(3000);
if (!string.IsNullOrEmpty(Text1) && !string.IsNullOrEmpty(Text2))
lblData.Text = Text1 + "<br/>" + Text2;
}
}
}
عمدا یک sleep سه ثانیهای در اینجا در نظر گرفته شده تا اثر آنرا بهتر بتوان مشاهده کرد.
ب) اکنون کد مربوط به صفحهای که قرار است این یوزر کنترل را به صورت غیرهمزمان بارگذاری کند به صورت زیر خواهد بود (مهمترین قسمت آن نحوه تشکیل پارامترها و مقدار دهی خواص یوزر کنترل است):
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestJQueryAjax._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="js/jquery.js" type="text/javascript"></script>
<script src="js/jquery.advloaduc.js" type="text/javascript"></script>
<script src="js/json2.js" type="text/javascript"></script>
<script type="text/javascript">
function showAlert() {
alert('finished!');
}
//تشکیل پارامترهای متد وب سرویس جهت ارسال به آن
var fileName = 'part1.ascx';
var props = [{ 'Key': 'Text1', 'Value': 'سطر یک' }, { 'Key': 'Text2', 'Value': 'سطر 2'}];
var jsonText = JSON.stringify({ path: fileName, properties: props });
$(document).ready(function() {
$("#loadMyUc").advloaduc({
webServiceName: 'Ajax.asmx',
renderUCMethod: 'RenderUserControl',
ucMethodJsonParams: jsonText,
completeHandler: showAlert
});
});
</script>
</head>
<body>
<form id="form1" runat="server">
<div id="loadMyUc">
</div>
</form>
</body>
</html>
برای ارسال صحیح و امن اطلاعات json به سرور، از اسکریپت استاندارد json2.js استفاده شد.
ابزار دیباگ:
بهترین ابزار برای دیباگ این نوع اسکریپتها استفاده از افزونه فایرباگ فایرفاکس است. برای مثال مطابق تصویر زیر، یوزر کنترلی فراخوانی شده است که در سرور وجود ندارد:
دریافت مثال فوق
با عرض سلام و خسته نباشید و تبریک سال نو :
من با نرم افزار StimulSoft کار میکنم ولی مشکلی در نمایش فونت گزارشات تحت وب دارم به طوری که وقتی مثلاً برای اطلاعات فونت Lotus را تنظیم میکنم در خروجی گزارش تحت وب به فرمت Pdf یا Excel فونت مذکور به یکی از فونتهای پیش فرض تبدیل میشه، البته در دوحالت هم تست کردم حالتی که سرور هاستینک دردسترس خودم بود و فونتها نیز نصب شد و حالتی که بررروی هاستهای خریداری شده که در هر دوحالت مثلا فونت arial نمایش میدهد، ناگفته نماند در سیستمهای تحت ویندوز این مشکل وجود ندارد و همچنین همین گزارش را از طریق ویژوال استدیو سیستم خودم که صدا میزنم مشکلی در فونت ندارد.
ممنون میشم اگر کمک بفرمایید.