Let’s dive in and see what’s new in TypeScript 3.4!
- Faster subsequent builds with the --incremental flag
- Higher order type inference from generic functions
- Improvements for ReadonlyArray and readonly tuples
- const assertions
- Type-checking for globalThis
- Convert parameters to destructured object
- Breaking changes
توسعه تطابق الگو با C#8
C# 7 introduced pattern matching with the extension of the switch statement and the is operator offering the const pattern, the type pattern, and the var pattern. With C# 8 an extension of pattern matching is planned, including the property pattern, the recursive pattern, and a new switch – the switch expression.
TypeScript 2.7 منتشر شد
- Stricter class property checks
- Definite assignment assertions
- Easier ECMAScript module interoperability
- unique symbol types and const-named properties
- Cleaner output in --watch mode
- Prettier --pretty output
- Numeric Separators
- Fixed Length Tuples
- in operator narrowing and accurate instanceof
- Smarter object literal inference
احراز هویت و اعتبارسنجی کاربران در برنامههای Angular - قسمت چهارم - به روز رسانی خودکار توکنها
... .post(`${this.appConfig.apiEndpoint}/${this.appConfig.refreshTokenPath}`, model, { headers: headers }) .finally(() => { this.scheduleRefreshToken(); }) ...
... const initialDelay = Math.max(1, expiresAtUtc - nowUtc); //return 1 ...
تقویم شمسی در ویندوز 10
در ویندوز 10 که تقویم شمسی آن انتخاب شده، فرض کنید قصد داریم رشتهی زیر را به تاریخ معادل آن تبدیل کنیم:
const string dateTime = "Wed, 17 Apr 2013 12:10:18 GMT"; Console.WriteLine(DateTime.Parse(dateTime));
String was not recognized as a valid DateTime because the day of week was incorrect.
شبیه به مطلب «تاثیر فرهنگ جاری سیستم بر روی اعداد در دات نت» در اینجا نیز باید از CultureInfo.InvariantCulture استفاده کرد:
Console.WriteLine(DateTime.Parse(dateTime, CultureInfo.InvariantCulture));
ASP.NET MVC #19
- سپس یک کنترلر مخصوص آن ایجاد میکنید که این partial view را ارائه دهد:
public partial class SidebarMenuController : Controller { const int Min15 = 900; [ChildActionOnly] [OutputCache(Duration = Min15)] public virtual ActionResult Index() { return PartialView(viewName: MVC.Shared.Views._SidebarMenu); } }
- در آخر در فایل layout خواهید داشت:
@{Html.RenderAction(result: MVC.SidebarMenu.Index());}
یک نکتهی تکمیلی:
مسیردهیهای خاص فوق از T4MVC استفاده میکنند.
مقدمه
در اکثر موارد در یک Landscape عملیاتی، چنانچه به تجمیع و انتقال دادهها از بانکهای اطلاعاتی مختلف نیاز باشد، از SSIS Package اختصار (SQL Server Integration Service) استفاده میشود و معمولاً با تعریف یک Job در سطح SQL Server به اجرای Package در زمانهای مشخص میپردازند. چنانچه در موقعیتی لازم باشد که از طریق برنامه کاربردی توسعه یافته، به اجرای Package مبادرت ورزیده شود و البته نخواهیم Job تعریف شده را از طریق کد برنامه، اجرا کنیم و در واقع این امکان را داشته باشیم که همانند یک رویه ذخیره شده تعریف شده در سطح بانک اطلاعاتی به اجرای عمل فوق بپردازیم، یک راه حل میتواند تعریف یک CLR Stored Procedures باشد. در این مقاله به بررسی این موضوع پرداخته میشود، در ابتدا لازم است به بیان تئوری موضوع پرداخته شود (قسمتهای 1 الی 5) در ادامه به ذکر پیاده سازی روش پیشنهادی پرداخته میشود.
1- اجرای Integration Service Package
جهت اجرای یک Package از ابزارهای زیر میتوان استفاده کرد:• command-line ابزار خط فرمان dtexec.exeتوجه: همچنین یک Package را در زمان طراحی در Business Intelligence Development Studio) BIDS) میتوان اجرا نمود.
• ابزار اجرائی پکیج dtexecui.exe
• استفاده از SQL Server Agent job
2- استفاده از dtexec جهت اجرای Package
با استفاده از ابزار dtexec میتوان Packageهای ذخیره شده در فایل سیستم، یک SQL Instance و یا Packageهای ذخیره شده در Integration Service را اجرا نمود.توجه: در سیستم عاملهای 64 بیتی، ابزار dtexec موجود در Integration Service با نسخه 64 بیتی نصب میشود. چنانچه بایست Packageهای معینی را در حالت 32 بیتی اجرا کنید، لازم است ابزار dtexec نسخه 32 بیتی نصب شود. ابزار dtexec دستیابی به تمامی ویژگیهای پیکربندی و اجرای Package از قبیل اتصالات، مشخصات(Properties)، متغیرها، logging و شاخصهای پردازشی را فراهم میکند.
توجه: زمانی که از نسخهی ابزار dtexec که با SQL Server 2008 ارائه شده استفاده میکنید برای اجرای یک SSIS Package نسخه 2005، Integration Service به صورت موقت Package را به نسخه 2008 ارتقا میدهد، اما نمیتوان از ابزار dtexec برای ذخیره این تغییرات استفاده کرد.
2-1- ملاحظات نصب dtexec روی سیستمهای 64 بیتی
به صورت پیش فرض، یک سیستم عامل 64 بیتی که هر دو نسخه 64 بیتی و 32 بیتی ابزار خط فرمان Integration Service را دارد، نسخه 32 بیتی نصب شده را در خط فرمان اجرا خواهد کرد. نسخه 32 بیتی بدین دلیل اجرا میشود که در متغیر محیطی (Path (Path environment variable مسیر directory نسخه 32 بیتی قرار گرفته است.به طور معمول:2-2- تفسیر کدهای خروجی
هنگامی که یک Package اجرا میشود، dtexec یک کد خروجی (Return Code) بر میگرداند:مقدار | توصیف |
0 | Package با موفقیت اجرا شده است. |
1 | Package با خطا مواجه شده است. |
3 | Package در حال اجرا توسط کاربر لغو شده است. |
4 | Package پیدا نشده است. |
5 | Package بارگذاری نشده است. |
6 | ابزار با یک خطای نحوی یا خطای معنایی در خط فرمان برخورد کرده است. |
2-3- قوانین نحوی dtexec
تمامی گزینهها (Options) باید با یک علامت Slash (/) و یا Minus (-) شروع شوند.یک آرگومان باید در یک quotation mark محصور شود چنانچه شامل یک فاصله خالی باشد.
گزینهها و آرگومانها بجز رمزعبور حساس به حروف کوچک و بزرگ نیستند.
2-3-1- Syntax
dtexec /option [value] [/option [value]]…
2-3-2- Parameters
نکته: در Integration Service، ابزار خط فرمان dtsrun که برایData Transformation Service) DTS)های نسخه SQL Server 2000 استفاده میشد، با ابزار خط فرمان dtexec جایگزین شده است.• تعدادی از گزینههای خط فرمان dtsrun به طور مستقیم در dtexec معادل دارند برای مثال نام Server و نام Package.
• تعدادی از گزینههای dtsrun به طور مستقیم در dtexec معادل ندارند.
• تعدادی گزینههای خط فرمان جدید dtsexec وجود دارد که در ویژگیهای جدید Integration Service پشتیبانی میشود.
2-3-3- مثال
1) به منظور اجرای یک SSIS Package که در SQL Server ذخیره شده است، با استفاده از Windows Authentication :dtexec /sq <Package Name> /ser <Server Name>
2) به منظور اجرای یک SSIS Package که در پوشه File System در SSIS Package Store ذخیره شده است :
dtexec /dts “\File System\<Package File Name>”
3) به منظور اجرای یک SSIS Package که در سیستم فایل ذخیره شده است و مشخص کردن گزینه logging:
dtexec /f “c:\<Package File Name>” /l “DTS.LogProviderTextFile; <Log File Name>”
4) به منظور اجرای یک SSIS Package که در SQL Server ذخیره شده با استفاده از SQL Server Authentication برای نمونه(user:ssis;pwd:ssis@ssis)و رمز (Package(123:
dtexec /server “<Server Name>” /sql “<Package Name>” / user “ssis” /Password “ssis@ssis” /De “123”
3- تنظیمات سطح حفاظتی یک Package
به منظور حفاظت از دادهها در Packageهای Integration Service میتوانید یک سطح حفاظتی (protection level) را تنظیم کنید که به حفاظت از دادههای صرفاً حساس یا تمامی دادههای یک Package کمک نماید. به علاوه میتوانید این دادهها را با یک Password یا یک User Key رمزگذاری نمائید یا به رمزگذاری دادهها در بانک اطلاعاتی اعتماد کنید. همچنین سطح حفاظتی که برای یک Package استفاده میکنید، الزاماً ایستا (static) نیست و در طول چرخه حیات یک Package میتواند تغییر کند. اغلب سطح حفاظتی در طول توسعه یا به محض (deploy) استقرار Package تنظیم میشود.توجه: علاوه بر سطوح حفاظتی که توصیف شد، Packageها در بانک اطلاعاتی msdb ذخیره میشوند که همچنین میتوانند توسط نقشهای ثابت در سطح بانک اطلاعاتی (fixed database-level roles) حفاظت شوند. Integration Service شامل 3 نقش ثابت بانک اطلاعاتی برای نسبت دادن مجوزها به Package است که عبارتند از db_ssisadmin ،db_ssisltduser و db_ssisoperator
3-1- درک سطوح حفاظتی
در یک Package اطلاعات زیر به عنوان حساس تعریف میشوند:• بخش password در یک connection string. گرچه، اگر گزینه ای را که همه چیز را رمزگذاری کند، انتخاب کنید تمامی connection string حساس در نظر گرفته میشود.
• گرههای task-generated XML که برچسب (tagged) هایی حساس هستند.
• هر متغییری که به عنوان حساس نشان گذاری شود.
3-1-1- Do not save sensitive
هنگامی که Package ذخیره میشود از ذخیره مقادیر ویژگیهای حساس در Package جلوگیری میکند. این سطح حفاظتی رمزگذاری نمیکند اما در عوض از ذخیره شدن ویژگی هایی که حساس نشان گذاری شده اند به همراه Package جلوگیری میکند.3-1-2- Encrypt all with password
به منظور رمزگذاری تمامی Package از یک Password استفاده میشود. Package توسط Password ای رمزگذاری میشود که کاربر هنگامی که Package را ایجاد یا Export میکند، ارائه میدهد. به منظور باز کردن Package در SSIS Designer یا اجرای Package توسط ابزار خط فرمان dtexec کاربر بایست رمز Package را ارائه نماید. بدون رمز کاربر قادر به دستیابی و اجرای Package نیست.3-1-3- Encrypt all with user key
به منظور رمزگذاری تمامی Package از یک کلید که مبتنی بر Profile کاربر جاری میباشد، استفاده میشود. تنها کاربری که Package را ایجاد یا Export میکند، میتواند Package را در SSIS Designer باز کند و یا Package را توسط ابزار خط فرمان dtexec اجرا کند.3-1-4- Encrypt sensitive with password
به منظور رمزگذاری تنها مقادیر ویژگیهای حساس در Package از یک Password استفاده میشود. برای رمزگذاری از DPAPI استفاده میشود. دادههای حساس به عنوان بخشی از Package ذخیره میشوند اما آن دادهها با استفاده از Password رمزگذاری میشوند. به منظور باز نمودن Package در SSIS Designer کاربر باید رمز Package را ارائه دهد. اگر رمز ارائه نشود، Package بدون دادههای حساس باز میشود و کاربر باید مقادیر جدیدی برای دادههای حساس فراهم کند. اگر کاربر سعی نماید Package را بدون ارائه رمز اجرا کند، اجرای Package با خطا مواجه میشود.3-1-5- Encrypt sensitive with user key
به منظور رمزگذاری تنها مقادیر ویژگیهای حساس در Package از یک کلید که مبتنی بر Profile کاربر جاری میباشد، استفاده میشود. تنها کاربری که از همان Profile استفاده میکند، Package را میتواند بارگذاری (load) کند. اگر کاربر متفاوتی Package را باز نماید، اطلاعات حساس با مقادیر پوچی جایگزین میشود و کاربر باید مقادیر جدیدی برای دادههای حساس فراهم کند. اگر کاربر سعی نماید Package را بدون ارائه رمز اجرا کند، اجرای Package با خطا مواجه میشود. برای رمزگذاری از DPAPI استفاده میشود.3-1-6- (Rely on server storage for encryption (ServerStorage
با استفاده از نقشهای بانک اطلاعاتی، SQL Server تمامی Package را حفاظت میکند. این گزینه تنها زمانی پشتیبانی میشود که Package در بانک اطلاعاتی msdb ذخیره شده است.4- استفاده از نقشهای Integration Service
برای کنترل کردن دستیابی به Package، SSIS شامل 3 نقش ثابت در سطح بانک اطلاعاتی است. نقشها میتوانند تنها روی Package هایی که در بانک اطلاعاتی msdb ذخیره شده اند، بکار روند. با استفاده از SSMS میتوانید نقشها را به Packageها نسبت دهید، این انتساب نقشها در بانک اطلاعاتی msdb ذخیره میشود.Write action | Read action | Role |
Import packages Delete own packages Delete all packages Change own package roles Change all package roles * به نکته رجوع شود | Enumerate own packages Enumerate all packages View own packages View all packages Execute own packages Execute all packages Export own packages Export all packages Execute all packages in SQL Server Agent | db_ssisadmin or sysadmin |
Import packages Delete own packages Change own package roles | Enumerate own packages Enumerate all packages View own packages Execute own packages Export own packages | db_ssisltduser |
None | Enumerate all packages View all packages Execute all packages Export all packages Execute all packages in SQL Server Agent | db_ssisoperator |
Stop all currently running packages | View execution details of all running packages | Windows administrators |
همچنین جدول sysssispackages در بانک اطلاعاتی msdb شامل Package هایی است که در SQL Server ذخیره میشوند. این جدول شامل ستون هایی که اطلاعاتی درباره نقش هایی که به Packageها نسبت داده شده است، میباشد.
به صورت پیش فرض، مجوزهای نقشهای ثابت بانک اطلاعاتی db_ssisadmin و db_ssisoperator و شناسه منحصر به فرد کاربری (unique security identifier) که Package را ایجاد کرده برای خواندن Package بکار میرود، و مجوزهای نقش db_ssisadmin و شناسه منحصر به فرد کاربری که Package را ایجاد کرده برای نوشتن Package به کار میرود. یک User باید عضو نقش db_ssisadmin و db_ssisltduser یا db_ssisoperator برای داشتن دسترسی خواندن Package باشد. یک User باید عضو نقش db_ssisadmin برای داشتن دسترسی نوشتن Package باشد.
5- اتصال به صورت Remote به Integration Service
زمانی که یک کاربر بدون داشتن دسترسی کافی تلاش کند به یک Integration Service به صورت Remote متصل شود، با پیغام خطای "Access is denied" مواجه میشود. برای اجتناب از این پیغام خطا میتوان تضمین کرد که کاربر مجوز مورد نیاز DCOM را دارد.به منظور پیکربندی کردن دسترسی کاربر به صورت Remote به سرویس Integration مراحل زیر را دنبال کنید:
- Component Service را باز نمایید ( در Run عبارت dcomcnfg را تایپ کنید).مجوز دسترسی Lunch به منظور شروع و خاتمه سرویس، اعطا یا رد میشود و مجوز دسترسیActivation به منظور متصل شدن به سرویس، اعطا (grant) یا رد (deny) میشود.
- گره Component Service را باز کنید، گره Computer و سپس My Computer را باز نمایید و روی DCOM Config کلیک نمایید.
- گره DCOM Config را باز کنید و از لیست برنامه هایی که میتوانند پیکربندی شوند MsDtsServer را انتخاب کنید.
- روی Properties برنامه MsDtsServer رفته و قسمت Security را انتخاب کنید.
- در قسمت Lunch and Activation Permissions، مورد Customize را انتخاب و سپس روی Edit کلیک نمایید تا پنجره Lunch Permission باز شود.
- در پنجره Lunch Permission، کاربران را اضافه و یا حذف کنید و مجوزهای مناسب را به کاربران یا گروههای مناسب نسبت دهید. مجوزهای موجود عبارتند از Local Lunch، Remote Lunch، Local Activation و Remote Activation .
- در قسمت Access Permission مراحل فوق را به منظور نسبت دادن مجوزهای مناسب به کاربران یا گروههای مناسب انجام دهید.
- سرویس Integration را Restart کنید.
6- پیاده سازی
در ابتدا به ایجاد یک CLR Stored Procedures پرداخته میشود نام اسمبلی ساخته شده به این نام RunningPackage.dll میباشد و حاوی کد زیر است:Partial Public Class StoredProcedures '------------------------------------------------ 'exec dbo.Spc_NtDtexec 'Package','ssis','ssis@ssis','1234512345' '------------------------------------------------ <Microsoft.SqlServer.Server.SqlProcedure()> _ Public Shared Sub Spc_NtDtexec(ByVal PackageName As String, _ ByVal UserName As String, _ ByVal Password As String, _ ByVal Decrypt As String) Dim p As New System.Diagnostics.Process() p.StartInfo.FileName = "C:\Program Files\Microsoft SQL Server\100\DTS\Binn\DTExec.exe" p.StartInfo.RedirectStandardOutput = True p.StartInfo.Arguments = "/sql " & PackageName & " /User " & UserName & " /Password " & Password & " /De " & Decrypt p.StartInfo.UseShellExecute = False p.Start() p.WaitForExit() Dim output As String output = p.StandardOutput.ReadToEnd() Microsoft.SqlServer.Server.SqlContext.Pipe.Send(output) End Sub End Class
در قدم بعدی نیاز به Register کردن dll ساخته شده در سطح بانک اطلاعاتی SQL Server است، این گامها پس از اتصال به SQL Server Management Studio به شرح زیر است:
1- فعال کردن CLR در سرویس SQL Server
SP_CONFIGURE 'clr enabled',1 GO RECONFIGURE
2- فعال کردن ویژگی TRUSTWORTHY در بانک اطلاعاتی مورد نظر
ALTER DATABASE <Database Name> SET TRUSTWORTHY ON GO RECONFIGURE
3- ایجاد Assembly و Stored Procedure در بانک اطلاعاتی مورد نظر
Assembly ساخته شده با نام RunningPacakge.dll در ریشه :C کپی شود. بعد از ثبت نمودن این Assembly لزومی به وجود آن نمیباشد.
USE <Database Name> GO CREATE ASSEMBLY [RunningPackage] AUTHORIZATION [dbo] FROM 'C:\RunningPackage.dll' WITH PERMISSION_SET = UNSAFE Go CREATE PROCEDURE [dbo].[Spc_NtDtexec] @PackageName [nvarchar](50), @UserName [nvarchar](50), @Password [nvarchar](50), @Decrypt [nvarchar](50) WITH EXECUTE AS CALLER AS EXTERNAL NAME [RunningPackage].[RunningPackage.StoredProcedures].[Spc_NtDtexec] GO
در برنامه کاربردی تان کافی است متدی به شکل زیر ایجاد و با توجه به نیازتان در برنامه به فراخوانی آن و اجرای Package بپردازید.
Private Sub ExecutePackage() Dim oSqlConnection As SqlClient.SqlConnection Dim oSqlCommand As SqlClient.SqlCommand Dim strCnt As String = String.Empty strCnt = "Data Source=" & txtServer.Text & ";User ID=" & txtUsername.Text & ";Password=" & txtPassword.Text & ";Initial Catalog=" & cmbDatabaseName.SelectedValue.ToString() & ";" Try oSqlConnection = New SqlClient.SqlConnection(strCnt) oSqlCommand = New SqlClient.SqlCommand With oSqlCommand .Connection = oSqlConnection .CommandType = System.Data.CommandType.StoredProcedure .CommandText = "dbo.Spc_NtDtexec" .Parameters.Clear() .Parameters.Add("@PackageName", System.Data.SqlDbType.VarChar, 50) .Parameters.Add("@UserName", System.Data.SqlDbType.VarChar, 50) .Parameters.Add("@Password", System.Data.SqlDbType.VarChar, 50) .Parameters.Add("@Decrypt", System.Data.SqlDbType.VarChar, 50) .Parameters("@PackageName").Value = txtPackageName.Text.Trim() .Parameters("@UserName").Value = txtUsername.Text.Trim() .Parameters("@Password").Value = txtPassword.Text.Trim() .Parameters("@Decrypt").Value = txtDecrypt.Text.Trim() End With If (oSqlCommand.Connection.State <> System.Data.ConnectionState.Open) Then oSqlCommand.Connection.Open() oSqlCommand.ExecuteNonQuery() System.Windows.Forms.MessageBox.Show("Success") End If If (oSqlCommand.Connection.State = System.Data.ConnectionState.Open) Then oSqlCommand.Connection.Close() End If Catch ex As Exception MessageBox.Show(ex.Message, "Error") End Try End Sub 'ExecutePackage
هر کامپوننتی در React یک چرخه زندگی دارد. زمانیکه یک کامپوننت را به روش React.createClass یا React.Component تعریف میکنیم و در ReactDOM.render نمونهای از کامپوننت را برای نمایش در مرورگر میسازیم، چرخه حیات آن شروع میشود.
ReactDOMServer
کتابخانه ReactDOMServer جهت ساخت یا render کردن کامپوننتها در سمت سرور استفاده میشود. توسط این کتابخانه میتوانیم کامپوننتها را در سمت سرور ایجاد کنیم و نتیجه آن را که تگهای HTML هستند به مرورگر ارسال کنیم. این روش جهت داشتن صفحههای وب سریعتر و اهداف SEO مفید است. جهت اطلاعات بیشتر و روشهای استفاده به مستندات آن رجوع کنید. در مثال زیر روش استفاده از این کتابخانه به اختصار آمده.
var persons = [ { id: 1, personName: "Parham", personContact: "parhamda@gmail.com" }, { id: 2, personName: "Roham", personContact: "roham@yahoo.com" }, { id: 3, personName: "Raha", personContact: "raha@live.com" } ]; class Person extends React.Component{ render(){ return ( <div> <p>{this.props.personName}</p> <p>{this.props.personContact}</p> </div> ) } } let person1 = persons[0]; let personElement = <Person personName={person1.personName} personContact={person1.personContact}/> console.log(ReactDOMServer.renderToStaticMarkup(personElement));
در کد بالا مواردی که جدید هستند، یکی ساخت یک نمونه از کامپوننت Person است و دیگری ساخت آن در سمت سرور، بدون آن که فعلا نمایشی در مرورگر داشته باشیم. در کنسول میتوانیم خروجی کتابخانه را که تگهای HTML هستند ببینیم. ReactDOMServer دو متد را فراهم کرده که کارکردی مشابه دارند؛ اما در جزئیات متفاوت هستند.
- renderStaticMarkup یک خروجی استاتیک و بدون attributeهای اضافه را تولید میکند که بیشتر برای بررسی یا استفاده در صفحههای وب ایستا مفید هستند.
- renderToString یک خروجی به صورت HTML String ایجاد میکند که برای HTML DOM در سمت کاربر سازگارتر است و مناسب برای صفحات پویا.
در نهایت خروجی از هر نوع که بود، برای اینکه در سمت کاربر قابل مشاهده باشد باید از همان متد ReactDOM.render استفاده کنیم. از آنجایی که این مجموعه جهت معرفی و بررسی ابزارهای اصلی React به صورت مختصر است، از آوردن مثالهای زیاد و پیچیده پرهیز میکنم. در اینجا میتوانید یک نمونه ساده برای استفاده از ReactDOMServer به صورت استاندارد و با جزئیات را بررسی کنید.
متدهای چرخه حیات در React
React چند متد را برای زمانهای قبل و بعد از ساخت شدن یک کامپوننت در DOM دارد که میشود رفتارهایی را برای کامپوننت، در این متدها در نظر گرفت تا در زمان مناسب اجرا شوند. در ادامه این متدها معرفی و کاربرد هر یک بیان میشود.
componentWillMount: این متد قبل از اینکه کامپوننت، تگهای متد render را بسازد اجرا میشود. این متد هم در سمت کلاینت کاربرد دارد و هم در سمت سرور. به همین جهت برای گرفتن log از دادههای کامپوننت و کار با پایگاه داده مکان مناسبی است. به عنوان مثال در قطعه کد زیر دادههای کامپوننت، توسط Ajax ارسال شدهاند.
componentWillMount() { Ajax.post("/componentLog", { name: this.constructor.name, props: this.props }); }
componentDidMount: این متد بعد از اینکه بخش render اجرا شد فراخوانی میشود. همچنین فقط در سمت کلاینت و زمانیکه از ReactDOM.render استفاده میکنیم کاربرد دارد. این متد مناسب برای تعامل کامپوننت با افزونهها و APIها است؛ مانند دریافت اطلاعات مورد نیاز کامپوننت از سایتی دیگر توسط یک API. از این متد در قسمت چهارم مثالی آورده شده.
(componentWillReciveProps(nextProps: این متد زمانی اجرا میشود که دادههای ورودی کامپوننت با مقادیری جدید تغییر کنند.
componentWillReceiveProps(nextProps) { // Do something with new received data and change the state. } ReactDOM.render( <TestComponent someData={newDataEveryFiveSecond()}/>, document.getElementById("divTest") );
در مثال بالا یک کامپوننت داریم که دادههای ورودی خود را از یک تابع میگیرد. این تابع هر پنج ثانیه یک بار یک داده تازه ایجاد میکند و به کامپوننت ارسال میکند. میتوانیم داخل کامپوننت، از متد componentWillReceiveProps جهت دستکاری دادههای رسیده و تغییر وضعیت کامپوننت توسط setState استفاده کنیم.
(shouldComponentUpdate(nextProps, nextState: این متد شبیه به متد componentWillReceiveProps است، البته با تفاوتهایی. این متد هم مقدار ورودی جدید برای پارامترهای کامپوننت میگیرد و هم مقداری برای وضعیتی که کامپوننت دارد. این متد باید یک مقدار بازگشتی false یا true داشته باشد. با این مقدار بازگشتی میتوان کنترل کرد که آیا کامپوننت بر اساس دادههای جدید بروز بشود یا نه.
class ComponentExample extends React.Component { shouldComponentUpdate(nextProps, nextState) { return notEqual(this.props, nextProps) || notEqual(this.state, nextState); } }
در مثال بالا پارامترها و وضعیت جاری کامپوننت، با مقدارهای تازه تغییر یافته و وضعیت جدید مقایسه میشوند. اگر مقادیر مقایسه شده برابر نباشند (یعنی داده تکراری وارد نشده) مقدار بازگشتی true خواهد بود و React کامپوننت را بر اساس وضعیت جدید و دادههای تازه دوباره میسازد.
(componentWillUpdate(nextProps, nextState: این متد زمانیکه کامپوننت ساخته شده، دادههای جدیدی را دریافت کند و یا وضعیت آن تغییر کند و دقیقا قبل از اجرای render فراخوانی میشود. اگر از متد shouldComponentUpdate مقدار false بازگشت داده شود، این متد دیگر اجرا نخواهد شد. باید توجه داشته باشیم که setState را نمیشود در این متد پیادهسازی کرده. به این علت که، زمانیکه وضعیت کامپوننت تغییر میکند، React متد componentWillUpdate و بلافاصله بعد از آن render را اجرا میکند و برای تغییر وضعیت دیگر دیر شده! تفاوت componentWillUpdate با componentWillMount این است که Will Mount در اولین وهله سازی از کامپوننت اجرا میشود، ولی Will Update بعد از هر دوباره سازی (rerender).
(componentDidUpdate(prevProps, prevStat: احتمالا میشود به راحتی حدس زد که این متد دقیقا بعد از دوباره سازی کامپوننتی که ساخته شده فراخوانی میشود.
componentWillUnmount: این متد زمانی اجرا میشود که یک کامپوننت از DOM پاک شود. برای پاک کردن نمونهای از یک کامپوننت که در DOM در حال نمایش است میتوانیم از دستور زیر استفاده کنیم.
ReactDOM.unmountComponentAtNode(document.getElementById("react"));
ایجاد برنامههای ابتدایی مورد نیاز
در ابتدا دو پوشهی جدید BlazorServerApp و WinFormsApp را ایجاد میکنیم. سپس از طریق خط فرمان در اولی دستور dotnet new blazorserver و در دومی دستور dotnet new winforms را اجرا میکنیم تا دو برنامهی خالی Blazor Server و همچنین Windows Forms، ایجاد شوند. برنامهی WinForms ایجاد شده مبتنی بر NET Core. و یا همان NET 6x. است؛ بجای اینکه مبتنی بر دات نت فریمورک 4x باشد.
ایجاد یک پروژهی کتابخانهی Razor
چون میخواهیم کدهای برنامهی BlazorServerApp ما در برنامهی WinForms قابل استفاده باشد، نیاز است فایلهای اصلی آنرا به یک پروژهی razor class library منتقل کنیم. به همین جهت برای این پروژه، یک پوشهی جدید را به نام BlazorClassLibrary ایجاد کرده و درون آن دستور dotnet new razorclasslib را اجرا میکنیم.
انتقال فایلهای پروژهی Blazor به پروژهی کتابخانهی Razor
در ادامه این فایلها را از پروژهی BlazorServerApp به پروژهی BlazorClassLibrary منتقل میکنیم:
- کل پوشهی Data
- کل پوشهی Pages
- کل پوشهی Shared
- فایل App.razor
- فایل Imports.razor_
- کل پوشهی wwwroot
پس از اینکار، نیاز است فایل csproj کتابخانهی class lib را اندکی ویرایش کرد تا بتواند فایلهای اضافه شده را کامپایل کند:
<Project Sdk="Microsoft.NET.Sdk.Razor"> <PropertyGroup> <AddRazorSupportForMvc>true</AddRazorSupportForMvc> </PropertyGroup> <ItemGroup> <FrameworkReference Include="Microsoft.AspNetCore.App" /> </ItemGroup> </Project>
- به علاوه فایل Error.cshtml.cs انتقالی، نیاز به افزودن فضای نام using Microsoft.Extensions.Logging را خواهد داشت.
- در فایل Imports.razor_ انتقالی نیاز است دو using آخر آنرا که به BlazorServerApp قبلی اشاره میکنند، به BlazorClassLibrary جدید ویرایش کنیم:
@using BlazorClassLibrary @using BlazorClassLibrary.Shared
@namespace BlazorClassLibrary.Pages
<link rel="stylesheet" href="_content/BlazorClassLibrary/css/bootstrap/bootstrap.min.css" /> <link href="_content/BlazorClassLibrary/css/site.css" rel="stylesheet" />
پس از این تغییرات، برای اینکه برنامهی BlazorServerApp موجود، به کار خود ادامه دهد، نیاز است ارجاعی از پروژهی class lib را به فایل csproj آن اضافه کنیم:
<Project Sdk="Microsoft.NET.Sdk.Web"> <ItemGroup> <ProjectReference Include="..\BlazorClassLibrary\BlazorClassLibrary.csproj" /> </ItemGroup> </Project>
ویرایش برنامهی WinForms جهت اجرای کدهای Blazor
تا اینجا برنامهی Blazor Server ما تمام فایلهای مورد نیاز خود را از BlazorClassLibrary دریافت میکند و بدون مشکل اجرا میشود. در ادامه میخواهیم کار هاست این class lib را در برنامهی WinForms نیز انجام دهیم. به همین جهت در ابتدا ارجاعی را به class lib به آن اضافه میکنیم:
<Project Sdk="Microsoft.NET.Sdk"> <ItemGroup> <ProjectReference Include="..\BlazorClassLibrary\BlazorClassLibrary.csproj" /> </ItemGroup> </Project>
<Project Sdk="Microsoft.NET.Sdk"> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="6.0.101-preview.11.2349" /> </ItemGroup> </Project>
در ادامه نیاز است فایل Form1.Designer.cs را به صورت دستی جهت افزودن این WebView اضافه شده، تغییر داد:
namespace WinFormsApp; partial class Form1 { private void InitializeComponent() { this.blazorWebView1 = new Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView(); this.SuspendLayout(); this.blazorWebView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.blazorWebView1.Location = new System.Drawing.Point(13, 181); this.blazorWebView1.Name = "blazorWebView1"; this.blazorWebView1.Size = new System.Drawing.Size(775, 257); this.blazorWebView1.TabIndex = 20; this.Controls.Add(this.blazorWebView1); this.components = new System.ComponentModel.Container(); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(800, 450); this.Text = "Form1"; this.ResumeLayout(false); } private Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView blazorWebView1; }
هاست برنامهی Blazor در برنامهی WinForm
پس از تغییرات فوق، نیاز است فایلهای wwwroot را از پروژهی class lib به پروژهی WinForms کپی کرد. از این جهت که این فایلها از طریق index.html جدیدی خوانده خواهند شد. پس از کپی کردن این پوشه، نیاز است فایل csproj پروژهی WinForm را به صورت زیر اصلاح کرد:
<Project Sdk="Microsoft.NET.Sdk.Razor"> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="6.0.101-preview.11.2349" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\BlazorClassLibrary\BlazorClassLibrary.csproj" /> </ItemGroup> <ItemGroup> <Content Update="wwwroot\**"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> </ItemGroup> </Project>
در ادامه داخل این پوشهی wwwroot که از پروژهی class lib کپی کردیم، نیاز است فایل index.html جدیدی را که قرار است blazor.webview.js را اجرا کند، به صورت زیر ایجاد کنیم:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>Blazor WinForms app</title> <base href="/" /> <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" /> <link href="css/site.css" rel="stylesheet" /> <link href="css/app.css" rel="stylesheet" /> <link href="WinFormsApp.styles.css" rel="stylesheet" /> </head> <body> <div id="app"></div> <div id="blazor-error-ui"> An unhandled error has occurred. <a href="">Reload</a> <a>🗙</a> </div> <script src="_framework/blazor.webview.js"></script> </body> </html>
- همچنین در این فایل باید مداخل css.های مورد نیاز را هم مجددا ذکر کرد.
مرحلهی آخر کار، استفاده از کامپوننت webview جهت نمایش فایل index.html فوق است:
using System; using System.Windows.Forms; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebView.WindowsForms; using Microsoft.Extensions.DependencyInjection; using BlazorServerApp.Data; using BlazorClassLibrary; namespace WinFormsApp; public partial class Form1 : Form { private readonly AppState _appState = new(); public Form1() { var serviceCollection = new ServiceCollection(); serviceCollection.AddBlazorWebView(); serviceCollection.AddSingleton<AppState>(_appState); serviceCollection.AddSingleton<WeatherForecastService>(); InitializeComponent(); blazorWebView1.HostPage = @"wwwroot\index.html"; blazorWebView1.Services = serviceCollection.BuildServiceProvider(); blazorWebView1.RootComponents.Add<App>("#app"); //blazorWebView1.Dock = DockStyle.Fill; } }
نکتهی مهم! حتما نیاز است WebView2 Runtime را جداگانه دریافت و نصب کرد. در غیر اینصورت در حین اجرای برنامه، با خطای نامفهوم زیر مواجه خواهید شد:
System.IO.FileNotFoundException: The system cannot find the file specified. (0x80070002)
در اینجا یک ServiceCollection را ایجاد کرده و توسط آن سرویسهای مورد نیاز کامپوننت WebView را تامین میکنیم. همچنین مسیر فایل index.html نیز توسط آن مشخص شدهاست. این تنظیمات شبیه به فایل Program.cs برنامهی Blazor هستند.
تا اینجا اگر برنامه را اجرا کنیم، چنین خروجی قابل مشاهدهاست:
اکنون برنامهی کامل Blazor Server ما توسط یک WinForms هاست شدهاست و کاربر برای کار با آن، نیاز به نصب IIS یا هیچ وب سرور خاصی ندارد.
تعامل بین برنامهی WinForm و برنامهی Blazor
میخواهیم یک دکمه را بر روی WinForm قرار داده و با کلیک بر روی آن، مقدار شمارشگر حاصل در برنامهی Blazor را نمایش دهیم؛ مانند تصویر فوق.
برای اینکار در کدهای فوق، ثبت سرویس جدید AppState را هم مشاهده میکنید:
serviceCollection.AddSingleton<AppState>(_appState);
namespace BlazorServerApp.Data; public class AppState { public int Counter { get; set; } }
builder.Services.AddSingleton<AppState>();
@inject BlazorServerApp.Data.AppState AppState // ... @code { private void IncrementCount() { // ... AppState.Counter++; } }
private void button1_Click(object sender, EventArgs e) { MessageBox.Show( owner: this, text: $"Current counter value is: {_appState.Counter}", caption: "Counter"); }
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: BlazorDesktopHybrid.zip
از SQL Server 2012 SP1 به بعد، ایندکس جدیدی به نام Selective XML Indexes به مجموعهی ایندکسهای قابل تعریف بر روی یک ستون XML ایی اضافه شدهاست و این مزایا را به همراه دارد:
- برخلاف ایندکسهای اولیه و ثانویه بحث شده در مطلب جاری، کل محتوای سند را ایندکس نمیکنند. به همین جهت حجم کمتری را اشغال کرده و سرعت Insert و Update را کاهش نمیدهند.
- با استفاده از Selective XML Indexes تنها XPathهایی را که مشخص میکنید، ایندکس خواهند شد. بنابراین بر اساس کوئریهای موجود، میتوان ایندکسهای بهتری را تعریف کرد.
این نوع ایندکسها به صورت پیش فرض فعال نبوده و نیاز است از طریق رویه ذخیره شده سیستمی sp_db_selective_xml_index، فعال شوند.
sys.sp_db_selective_xml_index @dbname = 'dbname', @selective_xml_index = 'action: on|off|true|false'
create selective xml index index_name on table_name(column_name) for (<path>)
for( pathColor = '/Item/Product/Color' as SQL nvarchar(20), pathSize = '/Item/Product/Size' as SQL int )