لیست جلسات کنفرانس Build 2016
Microsoft continues to focus on enabling developers to do amazing work as businesses and industries transform in support of the shift to a cloud-first, mobile-first world. At Build 2016 we will present the latest tools and technologies and how they can help today's developers be their most creative and productive.
مثالی جهت توضیح علت نیاز به اعضای static abstract در اینترفیسها
فرض کنید قصد داریم حاصل جمع اعضای یک آرایهی int را محاسبه کنیم:
namespace CS11Tests; public class StaticAbstractMembers { public static void Test() { var sum = AddAll(new[] { 1, 2, 3, 4 }); Console.WriteLine(sum); } private static int AddAll(int[] values) { int result = 0; foreach (var value in values) { result += value; } return result; } }
var sum = AddAll(new[] { 1, 2, 3, 4, 0.68 });
Argument 1: cannot convert from 'double[]' to 'int[]' [CS11Tests]csharp(CS1503)
private static T AddAll<T>(T[] values) { T result = 0; foreach (var value in values) { result += value; } return result; }
T result = T.Zero;
در C# 11 و دات نت 7، با معرفی اینترفیس جدید INumber، میتوان قید <where T : INumber<T را به T اعمال کرد (مانند نمونهی زیر) و همچنین با استفاده از اعضای static abstract این اینترفیس، به مقدار T.Zero هم دسترسی یافت و اینبار قطعه کد زیر، بدون مشکل در C# 11 کامپایل میشود:
using System.Numerics; namespace CS11Tests; public class StaticAbstractMembers { public static void Test() { //var sum = AddAll(new[] { 1, 2, 3, 4 }); var sum = AddAll(new[] { 1, 2, 3, 4, 0.68 }); Console.WriteLine(sum); } private static T AddAll<T>(T[] values) where T : INumber<T> { T result = T.Zero; foreach (var value in values) { result += value; } return result; } }
abstract static TSelf One { get; } abstract static TSelf Zero { get; }
abstract static TResult operator +(TSelf left, TOther right);
مثال دیگری از کاربرد اعضای abstract static در اینترفیسها
فرض کنید اینترفیس ISport را به همراه دو پیاده سازی از آن، به صورت زیر تعریف کردهایم:
public interface ISport { bool IsTeamSport(); } public class Swimming : ISport { public bool IsTeamSport() => false; } public class Football : ISport { public bool IsTeamSport() => true; }
public class StaticAbstractMembers { public static void Display<T>(T sport) where T : ISport { Console.WriteLine("Is Team Sport:" + sport.IsTeamSport()); } }
Display(new Football());
پاسخ: تا پیشاز C# 11 یکی از روشهای انجام اینکار، استفاده از reflection بود. اما در C# 11 با کمک static abstractها میتوان تعاریف این اینترفیس و پیاده سازیهای آنرا به صورت زیر تغییر داد:
public interface ISport { static abstract bool IsTeamSport(); } public class Swimming : ISport { public static bool IsTeamSport() => false; } public class Football : ISport { public static bool IsTeamSport() => true; }
public class StaticAbstractMembers { public static void Display<T>() where T : ISport { Console.WriteLine("Is Team Sport:" + T.IsTeamSport()); } }
در ادامه روشهای مختلف ارسال درخواستهای Ajax را توسط jQuery و همچنین معادلهای XMLHttpRequest و Fetch API آنها بررسی میکنیم.
ارسال درخواستهای GET
توسط استاندارد جدید Fetch API | توسط XMLHttpRequest استاندارد |
توسط jQuery |
fetch('/my/name').then(function(response) { if (response.ok) { return response.text(); } else { throw new Error(); } }).then( function success(name) { console.log('my name is ' + name); }, function failure() { console.error('Name request failed!'); } ); | var xhr = new XMLHttpRequest(); xhr.open('GET', '/my/name'); xhr.onload = function() { if (xhr.status >= 400) { console.error('Name request failed!'); } else { console.log('my name is ' + xhr.responseText); } }; xhr.onerror = function() { console.error('Name request failed!'); }; xhr.send(); | $.get('/my/name').then( function success(name) { console.log('my name is ' + name); }, function failure() { console.error('Name request failed!'); } ); |
jQuery برای پشتیبانی از درخواستهای Ajax، متد ویژهای را به نام ()ajax ارائه میکند که برای ارسال درخواستهایی از هر نوع، مانندGET، POST و غیره کاربرد دارد. همچنین برای بعضی از نوعها، متدهای کوتاهتری را مانند ()get و ()post نیز در اختیار برنامه نویس قرار میدهد. جاوا اسکریپت خالص و Web API مرورگرها نیز دو شیء XMLHttpRequest و fetch را برای ارسال درخواستهای غیرهمزمان، ارائه میکند. XMLHttpRequest در تمام مرورگرهای قدیمی و جدید پشتیبانی میشود، اما fetch API مدتی است که در غالب مرورگرهای امروزی در دسترس است. در جدول فوق روش ارسال درخواستهای Ajax از نوع GET را توسط این سه روش مشاهده میکنید.
در این مثالها درخواستی به آدرس my/name سمت سرور ارسال شده و انتظار میرود که یک plaintext حاوی متن کاربر بازگشت داده شود که در نهایت در console لاگ میشود.
- در حالت استفادهی از jQuery در صورت بازگشت موفقیت آمیز پاسخی از طرف سرور، متد success و در غیراینصورت متد failure اجرا میشود. باید درنظر داشت که متد ajax جیکوئری، چیزی بیشتر از یک محصور کنندهی اشیاء XMLHttpRequest نیست.
- در حالت کار با XMLHttpRequest باید اندکی بیشتر تایپ کرد؛ اما اصول کار یکی است. در اینجا onload زمانی فراخوانی میشود که پاسخی از سرور دریافت شده و عملیات خاتمه یافتهاست؛ هرچند این پاسخ میتواند یک خطا نیز باشد. به همین جهت باید status آنرا بررسی کرد. اگر رخداد onerror فراخوانی شد، یعنی درخواست، در سطوح بسیار پایین آن مانند بروز یک خطای CORS با شکست مواجه شدهاست.
همانطور که مشاهده میکنید در حالت کار با XMLHttpRequest جاوا اسکریپت از اشیاء Promise پشتیبانی نمیکند که این کمبود با معرفی fetch API برطرف شدهاست که نمونهای از آنرا با متد then متصل به fetch مشاهده میکنید؛ دقیقا مشابه متد ajax جیکوئری که آن نیز یک Promise را بازگشت میدهد.
تفاوت Promise جیکوئری با fetch API در این است که جیکوئری در صورتیکه یک status code بیانگر خطا را دریافت کند، قسمت failure را اجرا میکند؛ اما fetch API مانند اشیاء XMLHttpRequest تنها در صورت بروز خطاهای سطح پایین درخواست، این متد را فراخوانی خواهد کرد. هرچند اگر در اینجا response.ok نبود، میتوان با صدور یک استثناء به رفتاری شبیه به jQuery رسید و قسمت then failure را به صورت خودکار اجرا کرد.
ارسال درخواستهای Ajax از نوع POST ، PUT و DELETE
توسط استاندارد جدید Fetch API | توسط XMLHttpRequest استاندارد | توسط jQuery |
fetch('/user/name', { method: 'POST', body: 'some data' }); | var xhr = new XMLHttpRequest(); xhr.open('POST', '/user/name'); xhr.send('some data'); | $.ajax({ method: 'POST', url: '/user/name', contentType: 'text/plain', data: 'some data' }); |
اگر از شیء XMLHttpRequest استفاده شود، Content-Type آن به صورت پیشفرض به text/plain تنظیم شدهاست. در اینجا بدنهی درخواست به متد send ارسال میشود.
متد fetch نیز همانند شیء XMLHttpRequest دارای Content-Type پیشفرض از نوع text/plain است. در اینجا بدنهی درخواست به خاصیت body انتساب داده میشود.
توسط استاندارد جدید Fetch API | توسط XMLHttpRequest استاندارد | توسط jQuery |
fetch('/user/1', { method: 'PUT', body: //record including new mobile number }); | var xhr = new XMLHttpRequest(); xhr.open('PUT', '/user/1'); xhr.send(/* record including new data */); | $.ajax({ method: 'PUT', url: '/user/1', contentType: 'text/plain', data: //record including new data }); |
توسط استاندارد جدید Fetch API | توسط XMLHttpRequest استاندارد | توسط jQuery |
fetch('/user/1', {method: 'DELETE'}); | var xhr = new XMLHttpRequest(); xhr.open('DELETE', '/user/1'); xhr.send(); | $.ajax('/user/1', {method: 'DELETE'}); |
مدیریت Encoding درخواستهای Ajax
در مثالهای قبل، اطلاعاتی از نوع text/plain را به سمت سرور ارسال کردیم که به آن encoding type نیز گفته میشود. برای تکمیل بحث میتوان حالتهای دیگری مانند application/x-www-form-urlencoded، application/json و یا multipart/form-data را که در برنامههای کاربردی زیاد مورد استفاده قرار میگیرند، بررسی کرد.
کار با URL Encoding
عموما URL encoding در دو قسمت آدرس درخواستی به سرور و یا حتی بدنهی درخواست ارسالی تنظیم میشود. MIME type آن نیز application/x-www-form-urlencoded است و اطلاعات آن شامل یکسری جفت کلید/مقدار است. برای متدهای ارسال از نوع GET و DELETE، اطلاعات آن در انتهای آدرس درخواستی و برای سایر حالات در بدنهی درخواست ذکر میشوند.
در جیکوئری با استفاده از متد param آن میتوان یک شیء جاوا اسکریپتی را به معادل URL-encoded string آنها تبدیل کرد:
$.param({ key1: 'some value', 'key 2': 'another value' });
key1=some+value&key+2=another+value
$.ajax({ method: 'POST', url: '/user', data: { name: 'VahidN', address: 'Address 1', phone: '555-555-5555' } });
برخلاف جیکوئری در حین کار با روشهای جاوا اسکریپتی خالص این encoding باید به صورت دستی و صریحی انجام شود. برای این منظور دو متد استاندارد encodeURI و encodeURIComponent در جاوا اسکریپت مورد استفاده قرار میگیرند. هدف متد encodeURI اعمال آن بر روی یک URL کامل است و یا کلید/مقدارهای جدا شدهی توسط & مانند first=Vahid&last=N. اما متد encodeURIComponent صرفا جهت اعمال بر روی یک تک مقدار طراحی شدهاست.
به این ترتیب معادل جاوا اسکریپتی قطعه کد جیکوئری فوق به صورت زیر است:
var xhr = new XMLHttpRequest(); var data = encodeURI('name=VahidN&address=Address 1&phone=555-555-5555'); xhr.open('POST', '/user'); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send(data);
روش انجام اینکار توسط fetch API به صورت زیر است:
var data = encodeURI('name=VahidN&address=Address 1&phone=555-555-5555'); fetch('/user', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: data });
function param(object) { var encodedString = ''; for (var prop in object) { if (object.hasOwnProperty(prop)) { if (encodedString.length > 0) { encodedString += '&'; } encodedString += encodeURI(prop + '=' + object[prop]); } } return encodedString; }
کار با JSON Encoding
در عمل JSON نمایش رشتهای یک شیء جاوا اسکریپتی است و هدف آن سهولت نقل و انتقالات این اشیاء به سرور و برعکس است. برخلاف حالت application/x-www-form-urlencoded که اطلاعات آن مسطح است، حالت application/json امکان ارسال اطلاعات سلسله مراتبی را نیز میسر میکند (مانند مثال زیر که phone آن دیگر مسطح نیست و خود آن نیز یک شیء جاوا اسکریپتی است).
در جیکوئری برای ارسال اشیاء جاوا اسکریپتی JSON Encoded به سمت سرور از روش زیر استفاده میشود:
$.ajax({ method: 'POST', url: '/user', contentType: 'application/json', data: JSON.stringify({ name: 'VahidN', address: 'Address 1', phone: { home: '555-555-5555', mobile: '444-444-4444' } }); });
پیاده سازی همین مثال با جاوا اسکریپت خالص و XMLHttpRequest استاندارد به صورت زیر است:
var xhr = new XMLHttpRequest(); var data = JSON.stringify({ name: 'VahidN', address: 'Address 1', phone: { home: '555-555-5555', mobile: '444-444-4444' } }); xhr.open('POST', '/user'); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(data);
در این حالت اگر خروجی سرور نیز JSON باشد، روش دریافت و پردازش آن به صورت زیر است:
var xhr = new XMLHttpRequest(); xhr.open('GET', '/user/1'); xhr.onload = function() { var user = JSON.parse(xhr.responseText); // do something with this user JavaScript object }; xhr.send();
اگر از مرورگر IE صرفنظر کنیم، تمام مرورگرهای دیگر دارای خاصیتی به نام xhr.response نیز هستند که نیاز به تبدیل و Parse دستی رشتهی دریافتی را حذف میکند؛ از این جهت که این خاصیت حاوی شیء جاوا اسکریپتی معادل بدنهی response دریافتی از سمت سرور است. البته با این شرط که سرور، Content-Type مساوی application/json را برای response تنظیم کرده باشد.
و روش انجام این عملیات توسط fetch API به صورت زیر است:
fetch('/user', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ name: 'VahidN', address: 'Address 1', phone: { home: '555-555-5555', mobile: '444-444-4444' } }); });
و یا اگر بخواهیم اطلاعات JSON دریافتی از سمت سرور را در اینجا پردازش کنیم، روش کار به صورت زیر است:
fetch('/user/1').then(function(response) { return response.json(); }).then(function(userRecord) { // do something with this user JavaScript object });
همین مثال را اگر بخواهیم توسط ECMAScript 2016 و Arrow functions آن بازنویسی کنیم، به قطعه کد زیر میرسیم:
fetch('/user/1') .then(response => response.json()) // Transform the data into json .then(userRecord => { // do something with this userRecord object });
کار با Multipart Encoding
نوع دیگری از encoding که بیشتر با فرمهای HTML بکار میرود، multipart/form-data نام دارد:
<form action="my/server" method="POST" enctype="multipart/form-data"> <label>First Name: <input name="first"> </label> <label>Last Name: <input name="last"> </label> <button>Submit</button> </form>
-----------------------------1686536745986416462127721994 Content-Disposition: form-data; name="first" Vahid -----------------------------1686536745986416462127721994 Content-Disposition: form-data; name="last" N -----------------------------1686536745986416462127721994--
روش ارسال اطلاعات با این نوع encoding خاص به سمت سرور توسط متد ajax جیکوئری به صورت زیر است:
var formData = new FormData(); formData.append('name', 'VahidN'); formData.append('address', 'Address 1'); formData.append('phone', '555-555-5555'); $.ajax({ method: 'POST', url: '/user', contentType: false, processData: false, data: formData });
در اینجا ذکر processData: false ضروری است. در غیراینصورت jQuery این اطلاعات را به یک URL-encoded string تبدیل میکند. همچنین با اعلام contentType: false، جیکوئری در کار مرورگر دخالت نمیکند. از این جهت که هدر ویژهی این نوع درخواستها توسط خود مرورگر تنظیم میشود و برای مثال یک چنین شکلی را دارد:
multipart/form-data; boundary=—————————1686536745986416462127721994
روش انجام اینکار با XMLHttpRequest و همچنین fetch API به صورت زیر است:
توسط استاندارد جدید Fetch API | توسط XMLHttpRequest استاندارد |
var formData = new FormData(); formData.append('name', 'VahidN'); formData.append('address', 'Address 1'); formData.append('phone', '555-555-5555'); fetch('/user', { method: 'POST', body: formData }); | var formData = new FormData(), xhr = new XMLHttpRequest(); formData.append('name', 'VahidN'); formData.append('address', 'Address 1'); formData.append('phone', '555-555-5555'); xhr.open('POST', '/user'); xhr.send(formData); |
آپلود فایلها توسط درخواستهای Ajax ایی
تنها راه آپلود فایلها در مرورگرهای قدیمی که شامل IE 9.0 هم میشود، تعریف المان <"input type="file> در داخل المان <form> و سپس submit مستقیم آن فرم است. برای رفع این مشکل در مرورگرهای پس از IE 9.0 و پشتیبانی از Ajax، جهت آپلود فایلها، استاندارد XMLHttpRequest Level 2 معرفی شدهاست. در این حالت اگر المان <input type=file> در صفحه وجود داشته باشد، روش ارسال Ajax ایی آن به سمت سرور به صورت زیر است:
توسط استاندارد جدید Fetch API | توسط XMLHttpRequest استاندارد | توسط jQuery |
var file = document.querySelector( 'INPUT[type="file"]').files[0]; fetch('/uploads', { method: 'POST', body: file }); } | var file = document.querySelector( 'INPUT[type="file"]').files[0], xhr = new XMLHttpRequest(); xhr.open('POST', '/uploads'); xhr.send(file); | var file = $('INPUT[type="file"]')[0].files[0]; $.ajax({ method: 'POST', url: '/uploads', contentType: false, processData: false, data: file }); |
یک نکته: اگر نیاز به آپلود بیش از یک فایل را داشتید و همچنین در اینجا نیاز به اطلاعات دیگری مانند سایر فیلدهای فرم نیز وجود داشت، از همان روش تعریف new FormData و افزودن اطلاعات مورد نیاز به آن استفاده کنید. امکان افزودن شیء file نیز به FormData پیش بینی شدهاست.
دانلود فایلها توسط درخواستهای Ajax ایی
پیشتر در حین بررسی JSON encoding توسط fetch API از متد ()json برای تبدیل اطلاعات دریافتی از سرور به json و بازگشت آن به صورت یک Promise استفاده کردیم:
fetch(url) .then((resp) => resp.json()) // Transform the data into json .then(function(data) { // use data object }) })
- ()clone یک کپی از response را تهیه میکند.
- ()redirect یک response جدید را با URL دیگری ایجاد میکند.
- ()arrayBuffer یک Promise را بازگشت میدهد که پس از پایان درخواست، response را به یک شیء ArrayBuffer تبدیل میکند.
- ()formData یک Promise را بازگشت میدهد که پس از پایان درخواست، response را به یک شیء FormData تبدیل میکند.
- ()blob یک Promise را بازگشت میدهد که پس از پایان درخواست، response را به یک شیء Blob تبدیل میکند.
- ()text یک Promise را بازگشت میدهد که پس از پایان درخواست، response را به string تبدیل میکند.
- ()json یک Promise را بازگشت میدهد که پس از پایان درخواست، response را به یک شیء جاوا اسکریپتی تبدیل میکند.
در اینجا متدی که میتواند برای تبدیل یک byte array بازگشتی از سرور به فایل قابل دریافت در سمت کلاینت مورد استفاده قرار گیرد، متد blob است:
function downloadBlob() { fetch('/Home/InMemoryReport') .then(function(response) { return response.blob(); }) .then(function(xlsxBlob) { var a = document.createElement("a"); document.body.appendChild(a); a.style = "display: none"; let url = window.URL.createObjectURL(xlsxBlob); a.href = url; a.download = "report.xlsx"; a.click(); window.URL.revokeObjectURL(url); }); }
ارسال درخواستهای Ajax به دومینهای دیگر (CORS)
گاهی از اوقات نیاز است اطلاعاتی را توسط درخواستهای Ajax، به سروری دیگر در دومینی دیگر ارسال و یا دریافت کرد. هرچند انجام اینکار به صورت مستقیم و خارج از مرورگر بدون مشکل قابل انجام است، اما مرورگرها برای درخواستهای جاوا اسکریپتی محدودیت «same-origin policy» را اعمال میکنند. به این معنا که XMLHttpRequest بین دومینها به صورت پیشفرض ممنوع است. برای ارسال درخواستهای مجاز و از پیش مشخص شدهی Ajax بین دومینها، تاکنون دو روش پیش بینی شدهاست:
الف) روش JSONP
«same-origin policy» از شروع ارسال درخواستی به خارج از دومین جاری، جلوگیری میکند. هرچند این مورد به درخواستهای XMLHttpRequest اعمال میشود، اما در مورد المانهایی از نوع <a>، <img> و <script> صادق نیست و آنها محدود به این سیاست امنیتی نیستند. روش «JavaScript Object Notation with Padding» و یا به اختصار JSONP از یکی از همین استثناءها جهت ارسال درخواستهایی به سایر دومینها استفاده میکند. البته نام این روش کمی غلط انداز است؛ از این جهت که در این فرآیند اصلا JSON ایی مورد استفاده قرار نمیگیرد؛ خروجی سرور در این حالت یک تابع جاوا اسکریپتی است و نه JSON.
روش انجام این نوع درخواستها را توسط جیکوئری در ذیل مشاهده میکنید:
$.ajax('http://jsonp-aware-endpoint.com/user/1', { jsonp: 'callback', dataType: 'jsonp' }).then(function(response) { // handle user info from server });
انجام اینکار بدون jQuery و در حقیقت کاری که jQuery در پشت صحنه برای ایجاد تگ script انجام میدهد، چنین چیزی است:
window.myJsonpCallback = function(data) { // handle user info from server }; var scriptEl = document.createElement('script'); scriptEl.setAttribute('src', 'http://jsonp-aware-endpoint.com/user/1?callback=myJsonpCallback'); document.body.appendChild(scriptEl);
ب) روش CORS
CORS و یا Cross Origin Resource Sharing روش مدرن و پذیرفته شدهی ارسال درخواستهای Ajax در بین دومینها است و دارای دو نوع ساده و غیرساده است. نوع سادهی آن به همراه هدر مخصوص Origin است که جهت بیان دومین ارسال کنندهی درخواست بکار میرود و تنها از encodingهای “text/plain” و “application/x-www-form-urlencoded” پشتیبانی میکند. نوع غیرسادهی آن که این روزها بیشتر بکار میرود، از نوع «preflight» است. Preflight در اینجا به این معنا است که زمانیکه درخواست Ajax ایی را به دومین دیگری ارسال کردید، پیش از ارسال، مرورگر یک درخواست از نوع OPTIONS را به سمت سرور مقصد ارسال میکند. در این حالت اگر سرور مجوز مناسبی را صادر کرد، آنگاه مرورگر اصل درخواست را به سمت آن سرور ارسال میکند. به همین جهت در این حالت به ازای هر درخواستی که در برنامه ارسال میشود، در برگهی network مرورگر، دو درخواست را مشاهده خواهید کرد. درخواست preflight از نوع OPTIONS به صورت خودکار توسط مرورگر مدیریت میشود و نیازی به کدنویسی خاصی ندارد.
مدیریت کوکیها در درخواستهای Ajax
اگر درخواست Ajax ایی را به دومین دیگری ارسال کنید، به صورت پیشفرض به همراه کوکیهای مرتبط نخواهد بود. برای رفع این مشکل نیاز است خاصیت withCredentials را به true تنظیم کنید:
توسط استاندارد جدید Fetch API | توسط XMLHttpRequest استاندارد | توسط jQuery |
fetch('http://someotherdomain.com', { method: 'POST', headers: { 'Content-Type': 'text/plain' }, credentials: 'include' }); | var xhr = new XMLHttpRequest(); xhr.open('POST', 'http://someotherdomain.com'); xhr.withCredentials = true; xhr.setRequestHeader('Content-Type', 'text/plain'); xhr.send('sometext'); | $.ajax('http://someotherdomain.com', { method: 'POST', contentType: 'text/plain', data: 'sometext', beforeSend: function(xmlHttpRequest) { xmlHttpRequest.withCredentials = true; } }); |
یک نکتهی مهم: در fetch API حتی برای درخواستهای ساده نیز کوکیها ارسال نمیشوند. در این حالت برای کار با دومین جاری و ارسال کوکیهای کاربر به سمت سرور، باید از تنظیم 'credentials: 'same-origin استفاده کرد؛ زیرا مقدار پیشفرض آن omit است.
اگر تعداد کاراکترهای ناخواسته محدود و مشخص هستند میتوانید با دستور REPLACE آنها را حذف کنید، مثلا میخواهیم هر سه کاراکتر ~!@ از رشته حذف شوند:
DECLARE @s VARCHAR(50) = '~~~~~~!@@@@@@@ salam'; SET @s = REPLACE(REPLACE(REPLACE(@s, '~', ''), '!', ''), '@', ''); SELECT @s AS new_string
ولی هنگامی که کاراکترها نامحدود بوده امکان نوشتن تابع REPLACE به کرات بی معنا است در این حالت باید دنبال روشی پویا و تعمیم پذیر بود.
با جستجویی که در اینترنت انجام دادم متوجه شدم تکنیک WHILE یا همون loop یکی از روشهای رایج برای انجام اینکار هست، که احتمالا به دلیل سهولت در بکارگیری و سادگی آن بوده که عمومیت پیدا کرده است.
مستقل از این صحبتها هدف معرفی یک روش مجموعه گرا (set-based) برای این مساله میباشد.
حذف کاراکترها ناخواسته با تکنیک Recursive CTE
راه حل بر اساس جدول زیر است:
CREATE TABLE test_string (id integer not null primary key, string_value varchar(500) not null); INSERT INTO test_string VALUES (1, '@@@@ #### salam 12345'), (2, 'good $$$$$ &&&&&& bye 00000');
حالا فرض کنید میخواهیم هر کاراکتری غیر از حروف الفبای انگلیسی و فاصله(space) از رشته حذف شود. پس دو داده فوق به صورت salam و good bye در انتها در خواهند آمد.
برای حذف کاراکترهای ناخواسته فوق query زیر را اجرا کنید.
WITH CTE (ID, MyString, Ix) AS ( SELECT id, string_value, PATINDEX('%[^a-z ]%', string_value) FROM test_string UNION ALL SELECT id, CAST(REPLACE(MyString, SUBSTRING(MyString,Ix , 1), '') AS VARCHAR(500)), PATINDEX('%[^a-z ]%', REPLACE(MyString, SUBSTRING(MyString,Ix , 1), '')) FROM CTE WHERE Ix > 0 ) SELECT * FROM cte --WHERE Ix = 0; ORDER BY id, CASE WHEN Ix = 0 THEN 1 ELSE 0 END, Ix;
توضیح query:
در قسمت anchor اندیس اولین کاراکتر ناخواسته (خارج از رنج حروف الفبا و فاصله) بدست میآید. سپس در قسمت recursive هر کاراکتری که برابر باشد با کاراکتر ناخواسته ای که در مرحله قبل بدست آمده از رشته حذف میشود این عملیات توسط تابع replace صورت میگیرد و اندیس کاراکتر ناخواسته بعدی بعد از حذف کاراکتر ناخواسته قبلی بدست میآید که به مرحله بعد منتقل میشود. این مراحل تا آنجایی پیش میرود که دیگر کاراکتر ناخواسته ای در رشته وجود نداشته باشد.
به جدول زیر توجه بفرمایید (خروجی query فوق)
نتیجه مطلوب ما آن دو سطری است که در کادر بنفش هستند. که اگر به ستون Ix اشان توجه کنید مقدارش برابر با 0 است.
لطفا به سطر اول جدول توجه بفرمایید مشاهده میشود که هر 4 کاراکتر @ یکباره از رشته حذف شدند که بدلیل استفاده از تابع REPLACE میباشد.
حال بایستی توسط ویزارد، ساختار داده کاوی مورد نظر را ایجاد نمود. در صفحه اول ویزارد، مخزن داده را مشخص مینماییم.
در صفحه بعد الگوریتم موردنظر را انتخاب مینماییم.
بدیهی است که پس از ساخت ساختار داده کاوی میتوان الگوریتمهای دیگری را نیز برای مدل کردن مخزن داده به کار برد.
در این مقاله فرض شده است که خواننده نحوه ساخت Cube و Dimension را در یک پروژه SSAS-M توسط SSDT ، میداند. در صورتیکه به داده کاوی و هوش تجاری علاقمند هستید و به مقدمات بیشتری در رابطه با مطالب فوق نیاز دارید، پیشنهاد میشود که فصلهای یک، سه و چهار کتاب فوق را جهت آشنایی بیشتر مطالعه نمایید.
- Microsoft_Naive_Bayes
- Microsoft_Decision_Trees
- Microsoft_Linear_Regression
- Microsoft_Clustering
- Microsoft_ Association_Rules
- Microsoft_Neural_Network
- Microsoft_Logistic_Regression
اگر SQL Server و MySQL بر روی سیستم شما نصب است، روشی ساده برای انتقال اطلاعات بین این دو وجود دارد که نیازی به دخالت هیچ نوع برنامهی جانبی نداشته و با امکانات موجود قابل مدیریت است.
ایجاد یک Linked server
برای اینکه SQL Server را به MySQL متصل کنیم میتوان بین این دو یک Linked server تعریف کرد و سپس دسترسی به بانکهای اطلاعاتی MySQL همانند یک بانک اطلاعاتی محلی SQL Server خواهد شد که شرح آن در ادامه ذکر میشود.
ابتدا نیاز است تا درایور ODBC مربوط به MySQL دریافت و نصب شود. آنرا میتوانید از اینجا دریافت کنید : (+)
سپس management studio را گشوده و در قسمت Server objects ، بر روی گزینهی Linked servers کلیک راست نمائید. از منوی ظاهر شده، گزینهی New linked server را انتخاب کنید:
در ادامه، باید تنظیمات زیر را در صفحهی باز شده وارد کرد:
در قسمت Linked server و Product name ، نام دلخواهی را وارد کنید.
Provider انتخابی باید از نوع Microsoft OLE DB Provider for ODBC Drivers باشد.
مهمترین تنظیم آن، قسمت Provider string است که باید به صورت زیر وارد شود (در غیر اینصورت کار نمیکند):
DRIVER={MySQL ODBC 5.1 Driver}; SERVER=localhost; DATABASE=testdb; USER=root; PASSWORD=mypass; OPTION=3;PORT=3306; CharSet=UTF8;
پس از انجام این تنظیمات بر روی دکمهی Ok کلیک کنید تا Linked server ساخته شود:
اگر لیست بانکهای اطلاعاتی را مشاهده نمودید، یعنی اتصال به درستی برقرار شده است.
تنظیمات ثانویه:
تا اینجا اس کیوال سرور به MySQL متصل شده است، اما برای استفاده بهینه از امکانات موجود نیاز است تا یک سری تغییرات دیگر را هم اعمال کرد.
تنظیم MSDASQL Provider :
در همان قسمت Linked provider ، ذیل قسمت Providers ، گزینهی MSDASQL را انتخاب کرده و بر روی آن کلیک راست نمائید. سپس صفحهی خواص آنرا انتخاب کنید تا بتوان تنظیمات زیر را به آن اعمال کرد. این پروایدر جهت اتصال به MySQL مورد استفاده قرار میگیرد.
فعال سازی RPC :
برای اینکه بتوان از طریق SQL Server رکوردی را در یکی از جداول بانکهای اطلاعاتی MySQL متصل شده ثبت نمود، میتوان از دستور زیر استفاده کرد:
EXECUTE('insert into testdb.testtable(f1,f1) values(1,''data'')') at mysql
اینجا testdb نام بانک اطلاعاتی اتصالی MySQL است و testTable هم نام جدول مورد نظر. MySQL ایی که در آخر عبارت ذکر شده همان نام linked server ایی است که پیشتر تعریف کردیم.
به محض سعی در اجرای این کوئری خطای زیر ظاهر میشود:
Server 'mysql' is not configured for RPC.
برای رفع این مشکل، مجددا به صفحهی خواص همان liked server ایجاد شده مراجعه کنید. در قسمت Server options دو گزینه مرتبط به RPC باید فعال شوند:
و اکنون برای کوئری گرفتن از اطلاعات ثبت شده هم از عبارت زیر میتوان استفاده کرد:
SELECT * FROM OPENQUERY(mysql, 'SELECT * FROM testdb.testtable')
در این کوئری، MySQL نام Linked server ثبت شده است و testdb هم یکی از بانکهای اطلاعاتی MySQL مورد نظر.
انتقال تمام اطلاعات یک جدول از بانک اطلاعاتی MySQL به SQL Server
پس از برقراری اتصال، اکنون import کامل یک جدول MySQL به SQL Server به سادگی اجرای کوئری زیر میباشد:
SELECT * INTO MyDb.dbo.testtable FROM openquery(MYSQL, 'SELECT * FROM testdb.testtable')
در این کوئری، MySQL همان Linked server تعریف شده است. MyDB نام بانک اطلاعاتی موجود در SQL Server جاری است و testtable هم جدولی است که قرار است اطلاعات testdb.testtable بانک اطلاعاتی MySQL به آن وارد شود.
با اطلاعات فارسی هم (در سمت SQL Server) مشکلی ندارد. همانطور که مشخص است، در اطلاعات provider string ذکر شده، مقدار charset به utf8 تنظیم شده و همچنین اگر نوع collation فیلدهای تعریف شده در MySQL نیز به utf8_persian_ci تنظیم شده باشد، با مشکل ثبت اطلاعات فارسی به صورت ???? مواجه نخواهید شد.
نکته:
اگر بانک اطلاعاتی MySQL شما بر روی local host نصب نیست، جهت فعال سازی دسترسی ریموت به آن، میتوان به یکی از نکات زیر مراجعه کرد و سپس این اطلاعات جدید باید در همان قسمت provider string مرتبط با تعریف linked server وارد شوند:
مطالب مشابه:
هیچکدام از این روشها قابل استفاده نبودند چون provider string صحیحی را نهایتا تولید نمیکنند. همچنین تمام این روشها مبتنی است بر ایجاد DSN در کنترل پنل که اصلا نیازی به آن نیست و اضافی است.
SELECT * FROM tb1 WHERE x1 = '12';
SELECT * FROM tb1 WHERE x1 = '12';
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Microsoft.SqlServer.TransactSql.ScriptDom; namespace SqlDomAnalyzer.Core { public static class PrettyPrintTSql { public static string FormatTSql(string tSql) { IList<ParseError> errors; TSqlScript sqlFragment; using (var reader = new StringReader(tSql)) { var parser = new TSql120Parser(initialQuotedIdentifiers: true); sqlFragment = (TSqlScript)parser.Parse(reader, out errors); } if (errors != null && errors.Any()) { var sb = new StringBuilder(); foreach (var error in errors) sb.AppendLine(error.Message); throw new InvalidOperationException(sb.ToString()); } var sql110ScriptGenerator = new Sql120ScriptGenerator(new SqlScriptGeneratorOptions { SqlVersion = SqlVersion.Sql120 }); string finalScript; sql110ScriptGenerator.GenerateScript(sqlFragment, out finalScript); return finalScript; } } }
نکتهی جالب دیگری که در اینجا وجود دارد، تهیهی یک خروجی همواره یک شکل است. برای نمونه سه عبارت SQL زیر را در نظر بگیرید:
SELECT * from tb1 WHERE x1 = '12'; SELECT * from tb1 where x1 = '12'; select * from tb1 WHERE x1 = '12';
در هر سه حالت یا هر حالت قابل تصور دیگری، خروجی SQL فرمت شدهی حاصل یک چنین شکلی را دارد:
SELECT * FROM tb1 WHERE x1 = '12';
موارد کاربرد آن؟
علاوه بر نمایش زیبای SQL فرمت نشده، احتمالا برنامههای Profiler ایی را دیدهاید که عنوان میکنند قادرند عبارات SQL همانند را تشخیص دهند (جهت یافتن Lazy loading اشتباه). یک چنین خروجی یکسانی، قابلیت تهیه Hash عبارات SQL دریافتی را میسر میکند؛ چون دیگر اینبار مهم نیست که اجزای تشکیل دهندهی یک عبارت SQL با حروف بزرگ هستند یا کوچک و فاصلهی بین آنها چقدر است و آیا در این بین خطوط جدیدی نیز وجود دارند و امثال آن. خروجی نهایی نرمال شدهی توسط Sql120ScriptGenerator همواره یک شکل است. از این دو قابلیت در برنامهی DNTProfiler استفاده شدهاست.