از خاصیت OnlineVisitorsCount آن، برای نمایش اطلاعات استفاده کنید.
چک لیست تهیه یک برنامه ASP.NET MVC
از خاصیت OnlineVisitorsCount آن، برای نمایش اطلاعات استفاده کنید.
(void Add(object | افزودن المان به آخر لیست |
(void Remove(object | حذف یک المان خاص از لیست |
()void Clear | حذف کلیه المانها |
( bool Contains(object | شامل این داده میشود یا خیر؟ |
( void RemoveAt(int | حذف یک المان بر اساس جایگاه یا اندیسش |
(void Insert(int, object | افزودن یک المان در جایگاهی (اندیس) خاص بر اساس مقدار position |
(int IndexOf(object | اندیس یا جایگاه یک عنصر را بر میگرداند |
[this[int | ایندکسر ، برای دستریس به عنصر در اندیس مورد نظر |
public class CustomArrayList<T> { private T[] arr; private int count; public int Count { get { return this.count; } } private const int INITIAL_CAPACITY = 4; public CustomArrayList(int capacity = INITIAL_CAPACITY) { this.arr = new T[capacity]; this.count = 0; }
public void Add(T item) { GrowIfArrIsFull(); this.arr[this.count] = item; this.count++; } public void Insert(int index, T item) { if (index > this.count || index < 0) { throw new IndexOutOfRangeException( "Invalid index: " + index); } GrowIfArrIsFull(); Array.Copy(this.arr, index, this.arr, index + 1, this.count - index); this.arr[index] = item; this.count++; } private void GrowIfArrIsFull() { if (this.count + 1 > this.arr.Length) { T[] extendedArr = new T[this.arr.Length * 2]; Array.Copy(this.arr, extendedArr, this.count); this.arr = extendedArr; } } public void Clear() { this.arr = new T[INITIAL_CAPACITY]; this.count = 0; }
برای پیاده سازی آن به دو کلاس نیاز داریم. کلاس ListNode برای نگهداری هر المان و اطلاعات المان بعدی به کار میرود که از این به بعد به آن Node یا گره میگوییم و دیگری کلاس <DynamicList<T برای نگهداری دنباله ای از گرهها و متدهای پردازشی آن.
public class DynamicList<T> { private class ListNode { public T Element { get; set; } public ListNode NextNode { get; set; } public ListNode(T element) { this.Element = element; NextNode = null; } public ListNode(T element, ListNode prevNode) { this.Element = element; prevNode.NextNode = this; } } private ListNode head; private ListNode tail; private int count; // … }
از آن جا که نیازی نیست کاربر با کلاس ListNode آشنایی داشته باشد و با آن سر و کله بزند، آن را داخل همان کلاس اصلی به صورت خصوصی استفاده میکنیم. این کلاس دو خاصیت دارد؛ یکی برای المان اصلی و دیگر گره بعدی. این کلاس دارای دو سازنده است که اولی تنها برای عنصر اول به کار میرود. چون اولین بار است که یک گره ایجاد میشود، پس باید خاصیت NextNode یعنی گره بعدی در آن Null باشد و سازندهی دوم برای گرههای شماره 2 به بعد به کار میرود که همراه المان داده شده، گره قبلی را هم ارسال میکنیم تا خاصیت NextNode آن را به گره جدیدی که میسازیم مرتبط سازد. سه خاصیت کلاس اصلی به نامهای Count,Tail,Head به ترتیب برای اشاره به اولین گره، آخرین گره و تعداد گرهها، به کار میروند که در ادامه کد آنرا در زیر میبینیم:
public DynamicList() { this.head = null; this.tail = null; this.count = 0; } public void Add(T item) { if (this.head == null) { this.head = new ListNode(item); this.tail = this.head; } else { ListNode newNode = new ListNode(item, this.tail); this.tail = newNode; } this.count++; }
سازنده مقدار دهی پیش فرض را انجام میدهد. در متد Add المان جدیدی باید افزوده شود؛ پس چک میکند این المان ارسالی قرار است اولین گره باشد یا خیر؟ اگر head که به اولین گره اشاره دارد Null باشد، به این معنی است که این اولین گره است. پس اولین سازندهی کلاس ListNode را صدا میزنیم و آن را در متغیر Head قرار میدهیم و چون فقط همین گره را داریم، پس آخرین گره هم شناخته میشود که در tail نیز قرار میگیرد. حال اگر فرض کنیم المان بعدی را به آن بدهیم، اینبار دیگر Head برابر Null نخواهد بود. پس دومین سازندهی ListNode صدا زده میشود که به غیر از المان جدید، باید آخرین گره قبلی هم با آن ارسال شود و گره جدیدی که ایجاد میشود در خاصیت NextNode آن نیز قرار بگیرد و در نهایت گره ایجاد شده به عنوان آخرین گره لیست در متغیر Tail نیز قرار میگیرد. در خط پایانی هم به هر مدلی که المان جدید به لیست اضافه شده باشد متغیر Count به روز میشود.
public T RemoveAt(int index) { if (index >= count || index < 0) { throw new ArgumentOutOfRangeException( "Invalid index: " + index); } int currentIndex = 0; ListNode currentNode = this.head; ListNode prevNode = null; while (currentIndex < index) { prevNode = currentNode; currentNode = currentNode.NextNode; currentIndex++; } RemoveListNode(currentNode, prevNode); return currentNode.Element; } private void RemoveListNode(ListNode node, ListNode prevNode) { count--; if (count == 0) { this.head = null; this.tail = null; } else if (prevNode == null) { this.head = node.NextNode; } else { prevNode.NextNode = node.NextNode; } if (object.ReferenceEquals(this.tail, node)) { this.tail = prevNode; } }
برای حذف یک گره شماره اندیس آن گره را دریافت میکنیم و از Head، گره را بیرون کشیده و با خاصیت nextNode آنقدر به سمت جلو حرکت میکنیم تا متغیر currentIndex یا اندیس داده شده برابر شود و سپس گره دریافتی و گره قبلی آن را به سمت تابع RemoveListNode ارسال میکنیم. کاری که این تابع انجام میدهد این است که مقدار NextNode گره فعلی که قصد حذفش را داریم به خاصیت Next Node گره قبلی انتساب میدهد. پس به این ترتیب پیوند این گره از لیست از دست میرود و گره قبلی به جای اشاره به این گره، به گره بعد از آن اشاره میکند. مابقی کد از قبیل جست و برگردان اندیس یک عنصر و ... را به خودتان وگذار میکنم.
در روشهای بالا ما خودمان 2 عدد ADT را پیاده سازی کردیم و متوجه شدیم برای دخیره دادهها در حافظه روشهای متفاوتی وجود دارند که بیشتر تفاوت آن در مورد استفاده از حافظه و کارآیی این روش هاست.
لیستهای پیوندی دو طرفه Doubly Linked_List
لیستهای پیوندی بالا یک طرفه بودند و اگر ما یک گره را داشتیم و میخواستیم به گره قبلی آن رجوع کنیم، اینکار ممکن نبود و مجبور بودیم برای رسیدن به آن از ابتدای گره حرکت را آغاز کنیم تا به آن برسیم. به همین منظور مبحث لیستهای پیوندی دو طرفه آغاز شد. به این ترتیب هر گره به جز حفظ ارتباط با گره بعدی از طریق خاصیت NextNode، ارتباطش را با گره قبلی از طریق خاصیت PrevNode نیز حفظ میکند.
این مبحث را در اینجا میبندیم و در قسمت بعدی آن را ادامه میدهیم.
در اکثر موارد در یک Landscape عملیاتی، چنانچه به تجمیع و انتقال دادهها از بانکهای اطلاعاتی مختلف نیاز باشد، از SSIS Package اختصار (SQL Server Integration Service) استفاده میشود و معمولاً با تعریف یک Job در سطح SQL Server به اجرای Package در زمانهای مشخص میپردازند. چنانچه در موقعیتی لازم باشد که از طریق برنامه کاربردی توسعه یافته، به اجرای Package مبادرت ورزیده شود و البته نخواهیم Job تعریف شده را از طریق کد برنامه، اجرا کنیم و در واقع این امکان را داشته باشیم که همانند یک رویه ذخیره شده تعریف شده در سطح بانک اطلاعاتی به اجرای عمل فوق بپردازیم، یک راه حل میتواند تعریف یک CLR Stored Procedures باشد. در این مقاله به بررسی این موضوع پرداخته میشود، در ابتدا لازم است به بیان تئوری موضوع پرداخته شود (قسمتهای 1 الی 5) در ادامه به ذکر پیاده سازی روش پیشنهادی پرداخته میشود.
• command-line ابزار خط فرمان dtexec.exeتوجه: همچنین یک Package را در زمان طراحی در Business Intelligence Development Studio) BIDS) میتوان اجرا نمود.
• ابزار اجرائی پکیج dtexecui.exe
• استفاده از SQL Server Agent job
مقدار | توصیف |
0 | Package با موفقیت اجرا شده است. |
1 | Package با خطا مواجه شده است. |
3 | Package در حال اجرا توسط کاربر لغو شده است. |
4 | Package پیدا نشده است. |
5 | Package بارگذاری نشده است. |
6 | ابزار با یک خطای نحوی یا خطای معنایی در خط فرمان برخورد کرده است. |
dtexec /option [value] [/option [value]]…
• تعدادی از گزینههای خط فرمان dtsrun به طور مستقیم در dtexec معادل دارند برای مثال نام Server و نام Package.
• تعدادی از گزینههای dtsrun به طور مستقیم در dtexec معادل ندارند.
• تعدادی گزینههای خط فرمان جدید dtsexec وجود دارد که در ویژگیهای جدید Integration Service پشتیبانی میشود.
dtexec /sq <Package Name> /ser <Server Name>
dtexec /dts “\File System\<Package File Name>”
dtexec /f “c:\<Package File Name>” /l “DTS.LogProviderTextFile; <Log File Name>”
dtexec /server “<Server Name>” /sql “<Package Name>” / user “ssis” /Password “ssis@ssis” /De “123”
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 |
- 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 کنید.
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
SP_CONFIGURE 'clr enabled',1 GO RECONFIGURE
ALTER DATABASE <Database Name> SET TRUSTWORTHY ON GO RECONFIGURE
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
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
<style> :checked { width: 50px; height: 50px; } </style> <input type="checkbox" checked="checked"/> <input type="checkbox"/> <input type="radio" name="test"/> <input type="radio" name="test" checked="checked"/> <input type="radio" name="test"/>
|
|
|
|
| Selector | نسخه CSS |
3.2 | 9.6 | 9.0 | 3.5 | 4.0 | :checked | 3 |
<style> :indeterminate { width: 50px; height: 50px; } </style> <input type="checkbox" id="chk1"/> <input type="checkbox" id="chk2" checked="checked"/> <input type="checkbox" id="chk3"/> <script> document.getElementById("chk1").indeterminate=true; </script>
|
|
|
|
| Selector | نسخه CSS |
No | No | No | No | No | :indeterminate | 4 |
<style> :default { width: 50px; height: 50px; background: lime; } </style> <form> <input type="checkbox" checked="checked" /> <input type="checkbox" /> <input type="radio" name="radio1" /> <input type="radio" name="radio1" checked="checked" /> <input type="radio" name="radio1" /> <input type="submit" value="Default" /> <input type="submit" value="Submit 2" /> </form>
|
|
|
|
| Selector | نسخه CSS |
No | No | No | No | No | :default | 4 |
<style> :root { background: khaki; } </style>
|
|
|
|
| Selector | نسخه CSS |
3.2 | 9.6 | 9.0 | 3.5 | 4.0 | :root | 3 |
<style> form:after { content: "[ * : Required]"; color: red; } input+span:after { content: ' * '; color: red; } </style> <form> <div><input type="text" /><span></span></div> <div><input type="password" /><span></span></div> <div><input type="email" /><span></span></div> </form>
|
|
|
|
| Selector | نسخه CSS |
3.1 | 7.0 | 9.0 | 3.5 | 4.0 | :after | 2 |
<style> form:before { content: "[ * : Required]"; color: red; } input + span:before { content: ' * '; color: red; } </style> <form> <div><input type="text" /><span></span></div> <div><input type="password" /><span></span></div> <div><input type="email" /><span></span></div> </form>
|
|
|
|
| Selector | نسخه CSS |
3.1 | 7.0 | 9.0 | 3.5 | 4.0 | :before | 2 |
<style> ::selection { background: navy; color: orange; } </style> <p> Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna. Nunc viverra imperdiet enim. Fusce est. </p>
|
|
|
|
| Selector | نسخه CSS |
3.1 | 9.6 | 9.0 | 2.0 -moz- | 4.0 | ::selection | در CSS 3 معرفی و سپس حذف شد ولی امکان بازگشت مجدد وجود دارد |
<style> :not([readonly]) { background: yellow; } </style> <input type="text"/> <input type="text" readonly="readonly"/> <input type="text"/> <input type="text" readonly="readonly" />
|
|
|
|
| Selector | نسخه CSS |
3.2 | 9.6 | 9.0 | 3.5 | 4.0 | :not(S1,S2) | 3 |
<style> :matches([readonly]) { background: yellow; } </style> <input type="text" /> <input type="text" readonly="readonly" /> <input type="text" /> <input type="text" readonly="readonly" />
|
|
|
|
| Selector | نسخه CSS |
No | No | No | No | No | :matches(S1,S2) | 4 |
<style> :has(>span) { color: red; } :has(+div) { color: blue; } </style> <div> <p> Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas <span>porttitor</span> congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna. Nunc viverra imperdiet enim. Fusce est. </p> <h1>Header 1</h1> <div> Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna. Nunc viverra imperdiet enim. Fusce est. </div>
|
|
|
|
| Selector | نسخه CSS |
No | No | No | No | No | :has(S1,S2) | 4 |
C:\Program Files\Microsoft SQL Server\140\LocalDB\Binn>sqllocaldb info MSSQLLocalDB
Name: MSSQLLocalDB Version: 13.1.4202.2
C:\Program Files\Microsoft SQL Server\140\LocalDB\Binn>sqllocaldb delete MSSQLLocalDB LocalDB instance "MSSQLLocalDB" deleted.
C:\Program Files\Microsoft SQL Server\140\LocalDB\Binn>sqllocaldb create MSSQLLocalDB LocalDB instance "MSSQLLocalDB" created with version 14.0.1000.169.
C:\Program Files\Microsoft SQL Server\140\LocalDB\Binn>sqllocaldb info MSSQLLocalDB Name: MSSQLLocalDB Version: 14.0.1000.169
Media Type یا MIME Type نشان دهنده فرمت یک مجموعه داده است. در HTTP، مدیا تایپ بیان کننده فرمت message body یک درخواست / پاسخ است و به دریافت کننده اعلام میکند که چطور باید پیام را بخواند. محل استاندارد تعیین Mime Type در هدر Content-Type است. درخواست کننده میتواند با استفاده از هدر Accept لیستی از MimeTypeهای قابل قبول را به عنوان پاسخ، به سرور اعلام کند.
Asp.net Web API از MimeType برای تعیین نحوه serialize یا deserialize کردن محتوای دریافتی / ارسالی استفاده میکند
مسئله
یک پروژه Web API بسازید و view model زیر را در آن تعریف کنید:
public class NewProduct { [Required] public string Name { get; set; } public double Price { get; set; } public byte[] Pic { get; set; } }
همانطور که میبینید یک فیلد از نوع byte[] برای تصویر محصول در نظر گرفته شده است.
حالا یک کنترلر API ساخته و اکشنی برای دریافت اطلاعات محصول جدید از کاربر مینویسیم :public class ProductsController : ApiController { [HttpPost] public HttpResponseMessage PostProduct(NewProduct model) { if (ModelState.IsValid) { // ثبت محصول return new HttpResponseMessage(HttpStatusCode.Created); } return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState); } }
و یک صفحه html به نام index.html که حاوی یک فرم برای ارسال اطلاعات باشد :
<!DOCTYPE html> <html> <head> <title></title> </head> <body> <h1>ساخت MediaTypeFormatter برای Multipart/form-data</h1> <h2>محصول جدید</h2> <form id="newProduct" method="post" action="/api/products" enctype="multipart/form-data"> <div> <label for="name">نام محصول : </label> <input type="text" id="name" name="name" /> </div> <div> <label for="price">قیمت : </label> <input type="number" id="price" name="price" /> </div> <div> <label for="pic">تصویر : </label> <input type="file" id="pic" name="pic" /> </div> <div> <button type="submit">ثبت</button> </div> </form> </body> </html>
زمانی که فرم حاوی فایلی برای آپلود باشد مشخصه encType باید برابر با Multipart/form-data مقداردهی شود تا اطلاعات فایل به درستی کد شوند. در زمان ارسال فرم Content-type درخواست برابر با Multipart/form-data و فرمت اطلاعات درخواست ارسالی به شکل زیر خواهد بود :
همانطور که میبینید هر فیلد در فرم، در یک بخش جداگانه قرار گرفته است که با خط چین هایی از هم جدا شده اند. هر بخش، headerهای جداگانه خود را دارد.
- Content-Disposition که نام فیلد و نام فایل را شامل میشود .
- content-type که mime type مخصوص آن بخش از دادهها را مشخص میکند.
پس از اینکه فرم را تکمیل کرده و ارسال کنید ، با پیام خطای زیر مواجه میشوید :
خطای روی داده اعلام میکند که Web API فاقد MediaTypeFormatter برای خواندن اطلاعات ارسال شده با فرمتMultiPart/Form-data است. Web API برای خواندن و بایند کردن پارامترهای complex Type از درون بدنه پیام یک درخواست از MediaTypeFormatter استفاده میکند و همانطور که گفته شد Web API فاقد Formatter توکار برای deserialize کردن دادههای با فرمت Multipart/form-data است.
راه حلها :روشی که در سایت asp.net برای آپلود فایل در web api استفاده شده، عدم استفاده از پارامترها و خواندن محتوای Request در درون کنترلر است. که به طبع در صورتی که بخواهیم کنترلرهای تمیز و کوچکی داشته باشیم روش مناسبی نیست. از طرفی امتیاز parameter binding و modelstate را هم از دست خواهیم داد.
روش دیگری که میخواهیم در اینجا پیاده سازی کنیم ساختن یک MediaTypeFormatter برای خواندن فرمت Multipart/form-data است. با این روش کد موردنیاز کپسوله شده و امکان استفاده از binding و modelstate را خواهیم داشت.
برای ساختن یک MediaTypeFormatter یکی از 2 کلاس MediaTypeFormatter یا BufferedMediaTypeFormatter را باید پیاده سازی کنیم . تفاوت این دو در این است که BufferedMediaTypeFormatter برخلاف MediaTypeFormatter از متدهای synchronous استفاده میکند.
یک کلاس به نام MultiPartMediaTypeFormatter میسازیم و کلاس MediaTypeFormatter را به عنوان کلاس پایه آن قرار میدهیم .
public class MultiPartMediaTypeFormatter : MediaTypeFormatter { ... }
ابتدا در تابع سازنده کلاس فرمت هایی که میخواهیم توسط این کلاس خوانده شوند را تعریف میکنیم :
public MultiPartMediaTypeFormatter() { SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data")); }
سپس با پیاده سازی توابع CanReadType و CanWriteType مربوط به کلاس MediaTypeFormatter مشخص میکنیم که چه مدلهایی را میتوان توسط این کلاس serialize / deserialize کرد. در اینجا چون میخواهیم این کلاس محدود به یک مدل خاص نباشد، از یک اینترفیس برای شناسایی کلاسهای مجاز استفاده میکنیم .
public interface INeedMultiPartMediaTypeFormatter { }
و آنرا به کلاس newProduct اضافه میکنیم :
public class NewProduct : INeedMultiPartMediaTypeFormatter { ... }
public override bool CanReadType(Type type) { return typeof(INeedMultiPartMediaTypeFormatter).IsAssignableFrom(type); } public override bool CanWriteType(Type type) { return false; }
و اما تابع ReadFromStreamAsync که کار خواندن محتوای ارسال شده و بایند کردن آنها به پارامترها را برعهده دارد
public async override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger)
ابتدا محتوای ارسال شده را خوانده و اطلاعات فرم را استخراج میکنیم و از طرف دیگر با استفاده از کلاس Activator یک نمونه از مدل جاری را ساخته و لیست propertyهای آنرا استخراج میکنیم.
MultipartMemoryStreamProvider provider = await content.ReadAsMultipartAsync(); IEnumerable<HttpContent> formData = provider.Contents.AsEnumerable(); var modelInstance = Activator.CreateInstance(type); IEnumerable<PropertyInfo> properties = type.GetProperties();
سپس در یک حلقه به ترتیب برای هر property متعلق به مدل، در میان اطلاعات فرم جستجو میکنیم. برای پیدا کردن اطلاعات متناظر با هر property در هدر Content-Disposition که در بالا توضیح داده شد، به دنبال فیلد همنام با property میگردیم .
foreach (PropertyInfo prop in properties) { var propName = prop.Name.ToLower(); var propType = prop.PropertyType; var data = formData.FirstOrDefault(d => d.Headers.ContentDisposition.Name.ToLower().Contains(propName));
گفتیم که هر فیلد یک هدر، Content-Type هم میتواند داشته باشد. این هدر به صورت پیش فرض معادل text/plain است و برای فیلدهای عادی قرار داده نمیشود . در این مثال چون فقط یک
فیلد غیر رشته ای داریم فرض را بر این گرفته ایم که در صورت وجود Content-Type، فیلد مربوط به تصویر است. در صورتیکهContentType وجود داشته باشد، محتوای فیلد را به شکل Stream
خوانده به byte[] تبدیل و با استفاده از متد SetValue در property مربوطه قرار میدهیم.
if (data != null) { if (data.Headers.ContentType != null) { using (var fileStream = await data.ReadAsStreamAsync()) { using (MemoryStream ms = new MemoryStream()) { fileStream.CopyTo(ms); prop.SetValue(modelInstance, ms.ToArray()); } } }
در صورتی که Content-Type غایب باشد بدین معنی است که محتوای فیلد از نوع رشته است ( عدد ، تاریخ ، guid ، رشته ) و باید به نوع مناسب تبدیل شود. ابتدا آن را به صورت یک رشته میخوانیم و با استفاده از Convert.ChangeType آنرا به نوع مناسب تبدیل میکنیم و در property متناظر قرار میدهیم .
if (data != null) { if (data.Headers.ContentType != null) { //... } else { string rawVal = await data.ReadAsStringAsync(); object val = Convert.ChangeType(rawVal, propType); prop.SetValue(modelInstance, val); } }
return modelInstance;
config.Formatters.Add(new MultiPartMediaTypeFormatter());
الگوی Composite در عمل یک Collection Pattern (الگوی مجموعه ای) است. که میتوان در درون آن ترکیبی از زیر مجموعههای مختلف را قرار داد و سپس هر زیر مجموعه را به نوبه خود فراخوانی نمود.به بیان دیگر الگوی Composite به ما کمک میکند که در یک ساختار درختی بتوانیم مجموعه ای (Collection ی)،از بخشی از آبجکتهای سلسله مراتبی را نمایش دهیم. این الگو به Client اجازه میدهد، که رفتار یکسانی نسبت به یک Collection ی از آبجکتها یا یک آبجکت تنها داشته باشد.
مثالهای متعددی میتوان از الگوی Composite زد، که در ذیل به چند نمونه از آنها میپردازیم:
نمونه اول: همانطور که میدانیم یک سازمان از بخشهای مختلفی تشکیل شده است، که بصورت سلسله مراتبی با یکدیگر در ارتباط میباشند، چنانچه بخواهیم بخشها و زیر مجموعههای تابعه آنها را بصورت آبجکت نگهداری نماییم، یکی از بهترین الگوهای پیشنهاد شده الگوی Composite میباشد.
نمونه دوم: در بحث حسابداری،یک حساب کل از چندین حساب معین تشکیل شده است و هر حساب معین نیز از چندین سرفصل حسابداری تشکیل میشود. بنابراین برای نگهداری آبجکتهای معین مرتبط به حساب کل، میتوان آنها را در یک Collection قرار داد. و هر حساب معین را میتوان،در صورت داشتن چندین سرفصل در مجموعه خود به عنوان یک Collection در نظر گرفت. برای دسترسی به هر حساب معین و سرفصلهای زیر مجموعه آن نیز میتوان از الگوی Composite استفاده نمود.
نمونه سوم: یک File System را در نظر بگیرید،که ساختارش از File و Folder تشکیل شده است. و میتواند یک ساختار سلسله مراتبی داشته باشد.بطوریکه درون هر Folder میتواند یک یا چند File یا Folder قرار گیرد. و در درون Folderهای زیر مجموعه میتوان چندین File یا Folder دیگر قرار داد.اگر بخواهیم به عنوان نمونه شکل ساختار درختی File و فولدر را نمایش دهیم بصورت زیر خواهد بود:
در ساختار درختی به Folder شاخه یا Branch گویند، چون میتواند زیر شاخههای دیگری نیز در خود داشته باشد. و به File برگ یا Leaf گویند.برگ نمیتواند زیر مجموعه ای داشته باشد. در واقع برگ (Leaf) بیانگر انتهای یک شاخه میباشد.
نمونه آخر:می توان به ساختار منوها در برنامهها اشاره نمود.هر منو میتواند شامل چندین زیر منو باشد. و همان زیر منوها میتوانند از چندین زیر منوی دیگر تشکیل شوند. این ساختار نیز یک ساختار سلسله مراتبی میباشد، و برای نگهداری آبجکتهای یک مجموعه میتوان از الگوی Composite استفاده نمود.
الگوی Composite از سه Component اصلی تشکیل شده است،که یکایک آنها را بررسی میکنیم:
public interface Icomponent { void Display(int depth); }
public class Leaf:Icomponent { private String name = string.Empty; public Leaf(string name) { this.name = name; } public void Display(int depth) { Console.WriteLine(new String('-', depth) + ' ' + name); } }
public class Composite:Icomponent { private List<Icomponent> _children = new List<Icomponent>(); private String name = String.Empty; public Composite(String sname) { this.name = sname; } public void Add(Icomponent component) { _children.Add(component); } public void Remove(Icomponent component) { _children.Remove(component); } public void Display(int depth) { Console.WriteLine(new String('-', depth) + ' ' + name); // Recursively display child nodes foreach (Icomponent component in _children) { component.Display(depth + 2); } } }
class Program { static void Main(string[] args) { // Create a tree structure Composite root = new Composite("root"); root.Add(new Leaf("Leaf A")); root.Add(new Leaf("Leaf B")); Composite comp = new Composite("Composite X"); comp.Add(new Leaf("Leaf XA")); comp.Add(new Leaf("Leaf XB")); root.Add(comp); root.Add(new Leaf("Leaf C")); // Add and remove a leaf Leaf leaf = new Leaf("Leaf D"); root.Add(leaf); root.Remove(leaf); // Recursively display tree root.Display(1); Console.ReadKey(); } }