خواندنیهای 12 خرداد
امنیت
توسعه وب
ویندوز 7
Silverlight و WPF
PHP
دات نت فریم ورک
سی شارپ
دلفی
شیرپوینت
متفرقه
بررسی Semantic Search و FTS Table-valued functions
توابع Predicates مختص به FTS مانند Contains و Freetext، تنها ردیفهای متناظر با جستجوی انجام شده را باز میگردانند و رتبهای به نتایج جستجو اعمال نمیگردد. برای مثال، مشخص نیست اولین ردیف بازگشت داده شده بهترین تطابق را با جستجوی انجام شده دارد یا بدترین نتیجهی ممکن است. برای رفع این مشکل FTS table-valued functions معرفی شدهاند. حاصل اینها یک جدول با دو ستون است. ستون اول کلید متناظر با جدول تطابق یافته بوده و ستون دوم، Rank نام دارد که بیانگر میزان مفید بودن و درجهی اعتبار ردیف بازگشت داده شدهاست.
Semantic Search نیز به کمک سه table-valued functions پیاده سازی میشود. همچنین باید دقت داشت که تمام زبانهای پشتیبانی شده توسط FTS در حالت Semantic Search پشتیبانی نمیشوند. برای بررسی این مورد، دو کوئری ذیل را اجرا نمائید:
-- Full text Languages SELECT * FROM sys.fulltext_languages ORDER BY name; -- Semantic Search Languages SELECT * FROM sys.fulltext_semantic_languages ORDER BY name; GO
بررسی table-valued functions مختص به FTS
دو متد ویژهی CONTAINSTABLE و FREETEXTTABLE خروجی از نوع جدول دارند؛ با ستونهایی به نامهای key و rank. اگر قسمت ایجاد کاتالوگ FTS و ایندکس آنرا بخاطر داشته باشید، در حین ایجاد ایندکس FTS میبایستی KEY INDEX PK_Documents را نیز ذکر کرد. کاربرد آن در همین table-valued functions است.
مقدار rank، عددی است بین 0 و 1000 که هر چقدر مقدار آن بیشتر باشد، یعنی نتیجهای نزدیکتر، به عبارت جستجو شده، یافت گردیدهاست. باید دقت داشت که این عدد فقط در زمینهی یک کوئری معنا پیدا میکند و مقایسهی rank دو کوئری مختلف با هم، بیمعنا است.
عملکرد CONTAINSTABLE بسیار شبیه به متد Contains است با این تفاوت که قابلیتهای بیشتری دارد. برای مثال در اینجا میتوان برای قسمتی از جستجو، وزن و اهمیت بیشتری را قائل شد و این حالت تنها زمانی معنا پیدا میکند که خروجی جستجو، دارای rank باشد.
متد FREETEXTTABLE نیز بسیار شبیه به FREETEXT عمل کرده و نسبت به CONTAINSTABLE بسیار سادهتر است. برای نمونه امکان تعریف وزن، formsof، near و غیره در اینجا وجود ندارد. به علاوه عملگرهای منطقی مانند and و or نیز در اینجا کاربردی نداشته و صرفا یک noise word درنظر گرفته میشوند.
چند مثال جهت بررسی عملکرد دو متد CONTAINSTABLE و FREETEXTTABLE
استفاده از متد CONTAINSTABLE
-- Rank with CONTAINSTABLE SELECT D.id, D.title, CT.[RANK], D.docexcerpt FROM CONTAINSTABLE(dbo.Documents, docexcerpt, N'data OR level') AS CT INNER JOIN dbo.Documents AS D ON CT.[KEY] = D.id ORDER BY CT.[RANK] DESC;
این متد ابتدا نام جدول مورد بررسی را دریافت میکند. سپس ستونی که باید جستجو بر روی آن انجام شود و در ادامه عبارت جستجو شونده، مشخص میگردد. اگر این متد را به تنهایی اجرا کنیم:
SELECT * FROM CONTAINSTABLE(dbo.Documents, docexcerpt, N'data OR level')
استفاده از متد FREETEXTTABLE
-- Rank with FREETEXTTABLE SELECT D.id, D.title, FT.[RANK], D.docexcerpt FROM FREETEXTTABLE (dbo.Documents, docexcerpt, N'data level') AS FT INNER JOIN dbo.Documents AS D ON FT.[KEY] = D.id ORDER BY FT.[RANK] DESC;
در اینجا اگر نیاز باشد تا تعداد نتایج را شبیه به کوئریهای top n محدود نمود، میتوان از پارامتر عددی بعدی که برای نمونه به 2 تنظیم شدهاست، استفاده کرد:
-- Rank with FREETEXTTABLE and top_n_by_rank SELECT D.id, D.title, FT.[RANK], D.docexcerpt FROM FREETEXTTABLE (dbo.Documents, docexcerpt, N'data level', 2) AS FT INNER JOIN dbo.Documents AS D ON FT.[KEY] = D.id ORDER BY FT.[RANK] DESC;
تعیین وزن و اهمیت کلمات در حال جستجو
-- Weighted terms SELECT D.id, D.title, CT.[RANK], D.docexcerpt FROM CONTAINSTABLE (dbo.Documents, docexcerpt, N'ISABOUT(data weight(0.8), level weight(0.2))') AS CT INNER JOIN dbo.Documents AS D ON CT.[KEY] = D.id ORDER BY CT.[RANK] DESC;
انجام جستجوهای Proximity
-- Proximity term SELECT D.id, D.title, CT.[RANK] FROM CONTAINSTABLE (dbo.Documents, doccontent, N'NEAR((data, row), 30)') AS CT INNER JOIN dbo.Documents AS D ON CT.[KEY] = D.id ORDER BY CT.[RANK] DESC; GO
بررسی Semantic Search key valued functions
متد SEMANTICKEYPHRASETABLE کار بازگشت واژههای کلیدی آنالیز شده توسط FTS را انجام داده و جدولی حاوی 4 ستون را باز میگرداند. این چهار ستون عبارتند از:
- column_id: شماره ستون واژه کلیدی یافت شدهاست. تفسیر آن نیاز به استفاده از تابع سیستمی COL_NAME دارد (مانند مثال زیر).
- document_key: متناظر است با کلید اصلی جدولی که بر روی آن کوئری گرفته میشود.
- keyphrase: همان واژه کلیدی است.
- score: رتبهی واژه کلیدی است در بین سایر واژههایی که بازگشت داده شده و عددی است بین صفر تا یک.
مثالی از آنرا در ادامه ملاحظه میکنید:
-- Top 100 semantic key phrases SELECT TOP (100) D.id, D.title, SKT.column_id, COL_NAME(OBJECT_ID(N'dbo.Documents'), SKT.column_id) AS column_name, SKT.document_key, SKT.keyphrase, SKT.score FROM SEMANTICKEYPHRASETABLE (dbo.Documents, doccontent) AS SKT INNER JOIN dbo.Documents AS D ON SKT.document_key = D.id ORDER BY SKT.score DESC;
در متد جدولی SEMANTICKEYPHRASETABLE، ابتدا جدول مورد نظر و سپس ستونی که نیاز است واژههای کلیدی آنالیز شدهی آن بازگشت داده شوند، قید میگردند. document_key آن به تنهایی شاید مفید نباشد. به همین جهت join شدهاست به جدول اصلی، تا بتوان رکوردهای متناظر را نیز بهتر تشخیص داد.
به این ترتیب مهمترین واژههای کلیدی ستون doccontent را به همراه درجهی اهمیت و رتبهی آنها، میتوان گزارش گرفت.
متد SEMANTICSIMILARITYTABLE برای یافتن سندهای مشابه با یک سند مشخص بکار میروند؛ چیزی شبیه به گزارش «مقالات مشابه مطلب جاری» در بسیاری از سایتهای ارائهی محتوا. ستونهای خروجی آن عبارتند از:
- source_column_id: شماره ستون منبع انجام کوئری.
- matched_column_id: شماره ستون سند مشابه یافت شده.
- matched_document_key: متناظر است با کلید اصلی جدولی که بر روی آن کوئری گرفته میشود.
- score: رتبهی نسبی سند مشابه یافت شده.
-- Documents that are similar to document 1 SELECT S.source_document_title, SST.matched_document_key, D.title AS matched_document_title, SST.score FROM (SEMANTICSIMILARITYTABLE (dbo.Documents, doccontent, 1) AS SST INNER JOIN dbo.Documents AS D ON SST.matched_document_key = D.id) CROSS JOIN (SELECT title FROM dbo.Documents WHERE id=1) AS S(source_document_title) ORDER BY SST.score DESC;
در این کوئری، اسناد مشابه با سند شماره 1 یافت شدهاند. مبنای جستجو نیز ستون doccontent، جدول dbo.Documents است. از join بر روی matched_document_key و id جدول اصلی، مشخصات سند یافت شده را میتوان استخراج کرد. کار CROSS JOIN تعریف شده، صرفا افزودن یک ستون مشخص به نتیجهی خروجی کوئری است.
همانطور که در تصویر مشخص است، سند شماره 4 بسیار شبیه است به سند شماره 1. در ادامه قصد داریم بررسی کنیم که علت این شباهت چه بودهاست؟
متد SEMANTICSIMILARITYDETAILSTABLE واژههای کلیدی مهم مشترک بین دو سند را بازگشت میدهد (سند منبع و سند مقصد). به این ترتیب میتوان دریافت، چه واژههای کلیدی سبب شدهاند تا این دو سند به هم شبیه باشند. ستونهای خروجی آن عبارتند از:
- keyphrase: واژهی کلیدی
- score: رتبهی نسبی واژهی کلیدی
-- Key phrases that are common across two documents SELECT SSDT.keyphrase, SSDT.score FROM SEMANTICSIMILARITYDETAILSTABLE (dbo.Documents, doccontent, 1, doccontent, 4) AS SSDT ORDER BY SSDT.score DESC;
در کوئری فوق قصد داریم بررسی کنیم چه واژههای کلیدی، سبب مشابهت سندهای شماره 1 و 4 شدهاند و بین آنها مشترک میباشند.
<form action="/rest/login" method="POST"> <input name="username" required> <input type="password" name="password" required> </form>
محتوا یا content به معنای المانهایی هستند که درون یک المان قرار میگیرند. برای مثال در اینجا المان <form> دارای محتوایی متشکل از دو المان <input> است و دون المان <input> دارای محتوایی نیستند.
از ویژگیها و یا attributes، برای ارائهی اطلاعات بیشتر و یا تعیین حالت عناصر استفاده میشود. برای نمونه در اینجا المان <form> دارای دو ویژگی action و method است که برای ارائهی اطلاعاتی بیشتر جهت تعیین روش و محل ارسال اطلاعات به سرور از آنها استفاده میشود. همچنین میتوان ویژگیهای سفارشی را نیز توسط ویژگیهای شروع شدهی با -data نیز به المانها اضافه کرد که جزئی از استاندارد HTML 5 است. هرچند میتوان ویژگی کاملا غیرمتعارفی مانند myproject-uuid را نیز به یک المان اضافه کرد. این مورد مشکل خاصی را ایجاد نکرده و صفحه بدون مشکل رندر میشود؛ اما یک چنین صفحهای دیگر به عنوان یک صفحهی استاندارد HTML ارزیابی نخواهد شد؛ از این جهت که از ویژگی استفاده کردهاست که در هیچکدام از استانداردهای HTML ذکر نشدهاست.
آخرین نگارش HTML5 به همراه 4 ویژگی جدید دیگر نیز هست:
- Boolean attribute : مانند ویژگی required که به المانهای <input> قابل انتساب است. حضور این نوع ویژگیها بدون مقداری در یک المان
<input name=username required>
نمونهی دیگری از این نوع ویژگیها، ویژگی hidden است که به HTML 5.1 اضافه شدهاست و اگر به عنصری اضافه شود، آن المان رندر نخواهد شد.
- unquoted attribute: به این معنا که میتوان "" را از اطراف مقدار یک ویژگی حذف کرد:
<input name=username required>
- single-quoted and double-quoted attributes: که به معنای استفاده از "" و یا '' جهت تعیین مقدار یک ویژگی است.
تفاوت attributes با خواص المانها چیست؟
Attributes در تعریف تگ HTML یک عنصر ظاهر میشوند اما خواص، جزئی از تعریف شیء یک عنصر هستند.
<div class="bold">I'm a bold element</div>
<a href="http://www.site.com/blog/">Read the blog</a>
<script> document.querySelector('A').href = 'https://www.dntips.ir'; </script>
- برای مثال واژهی class در زبان جاوا اسکریپت یک واژهی کلیدی است. به همین جهت در اینجا اگر بخواهیم مقدار ویژگی class را تغییر دهیم باید از خاصیت className آن المان استفاده کنیم.
- مورد دوم ویژگی checked المانهای radio و checkbox است. این ویژگی فقط در ابتدای کار این المانها به آنها متصل میشود:
<input type="checkbox" checked> <script> // this does not remove the checked attribute document.querySelector('INPUT').checked = false; </script>
- تمام ویژگیهای غیراستانداردی که تعریف شوند، دارای خاصیت متناظری در آن المان نخواهند بود، اما به آنها expando properties گفته میشود.
یافتن المانها بر اساس ویژگیهای آنها
از آنجائیکه attribute selectors در استاندارد W3C CSS 2 معرفی شدهاند، امکان کار با آنها از زمان IE 8.0 میسر بودهاست و برای کار با آنها الزاما نیازی به استفاده از jQuery نیست!
یافتن المانها بر اساس نام ویژگیها
<form action="/submitHandler" method="POST"> <input name="first-name"> <input name="last-name" required> <input type="email" name="email" required> <button disabled>submit</button> </form>
var $result = $('[required], [disabled]');
var result = document.querySelectorAll('[required], [disabled]');
در اینجا باید دقت داشت که این جستجو صرفا بر اساس نام ویژگیها انجام میشود؛ حتی اگر این ویژگی دارای مقداری نباشد:
<div class="bold">I'm bold</div> <span class>I'm not</span>
var result = document.querySelectorAll('[class]');
یافتن المانها بر اساس نام و مقدار ویژگیها
<section> <h2>Sites</h2> <ul> <li> <a href="https://www.dntips.ir/">dotnettips</a> </li> <li> <a href="https://google.com/">Google</a> </li> </ul> </section>
var $result = $('A[href="https://www.dntips.ir/"]');
var result = document.querySelectorAll('A[href="https://www.dntips.ir/"]');
و یا اگر اینبار بخواهیم تمام ویژگیهای کلاسی که دارای مقدار هستند را انتخاب کنیم، روش آن با استفاده از exclusion selector به صورت زیر است:
var result = document.querySelectorAll('[class]:not([class=""]');
یافتن المانها بر اساس نام و قسمتی از مقدار ویژگیها
<a href="https://www.dntips.ir/">home page</a> <a href="https://www.dntips.ir/postsarchive">articles</a> <a href="https://www.dntips.ir/newsarchive">news</a>
var result = document.querySelectorAll('A[href*="www.dotnettips.info"]');
یافتن المانها بر اساس نام و کلمهای مشخص در مقدار ویژگیها
<div class="one two three">1 2 3</div> <div class="onetwothree">123</div>
var result = document.querySelectorAll('[class∼=two]');
در اینجا نوع دیگری از کوئری را هم میتوان مطرح کرد: آیا المانی مشخص، دارای کلاس two است؟
روش انجام آن در jQuery به صورت زیر است:
var hasTwoClass = $divEl.hasClass('two');
var hasTwoClass = divEl.classList.contains('two');
همچنین خاصیت classList به همراه متدهای استاندارد add و remove نیز هست که معادل متدهای addClass و removeClass جیکوئری هستند.
divEl.classList.remove('red'); divEl.classList.add('blue');
// removes "hide" class divEl.classList.toggle('hide'); // re-adds "hide" class divEl.classList.toggle('hide');
یافتن المانها بر اساس نام و شروع یا خاتمهی عبارتی مشخص در مقدار ویژگیها
<img id="cat" src="/images/cat.gif"> <img id="zebra" src="zebra.gif"> <a href="#zebra">watch the zebra</a> <a href="/logout">logout</a>
var result = document.querySelectorAll('A[href^="#"], [src$=".gif"]');
خواندن مقادیر ویژگیها
<input type="password" name="user-password" required>
// returns "password" $inputEl.attr('type'); // returns "true" $inputEl.is('[required]');
// returns "password" inputEl.getAttribute('type'); // returns "true" inputEl.hasAttribute('required');
تغییر مقدار ویژگیها
<input name="temp" required>
روش انجام اینکار در جیکوئری:
$inputEl .attr('type', 'email') // #1 .removeAttr('required') // #2 .attr('name', 'userEmail'); // #3
inputEl.setAttribute('type', 'email'); // #1 inputEl.removeAttribute('required'); // #2 inputEl.setAttribute('name', 'userEmail'); // #3
EF Core چیست؟
EF Core یک ORM یا object-relational mapper چندسکویی است که امکان کار با بانکهای اطلاعاتی مختلف را از طریق اشیاء دات نتی میسر میکند. توسط آن قسمت عمدهی کدهای مستقیم کار با بانکهای اطلاعاتی حذف شده و تبدیل به کدهای دات نتی میشوند. مزیت این لایهی Abstraction اضافی (لایهای بر روی کدهای مستقیم لایه ADO.NET زیرین)، امکان تعویض بانک اطلاعاتی مورد استفاده، تنها با تغییر کدهای آغازین برنامهاست؛ بدون نیاز به تغییری در سایر قسمتهای برنامه. همچنین کار با اشیاء دات نتی و LINQ، مزایایی مانند تحت نظر قرار گرفتن کدها توسط کامپایلر و برخورداری از ابزارهای Refactoring پیشرفته را میسر میکنند. به علاوه SQL خودکار تولیدی توسط آن نیز همواره پارامتری بوده و مشکلات حملات تزریق SQL در این حالت تقریبا به صفر میرسند (اگر مستقیما SQL نویسی نکنید و صرفا از LINQ استفاده کنید). مزیت دیگر همواره پارامتری بودن کوئریها، رفتار بسیاری از بانکهای اطلاعاتی با آنها همانند رویههای ذخیره شده است که به عدم تولید Query planهای مجزایی به ازای هر کوئری رسیده منجر میشود که در نهایت سبب بالا رفتن سرعت اجرای کوئریها و مصرف حافظهی کمتری در سمت سرور بانک اطلاعاتی میگردد.
تفاوت EF Core با نگارشهای دیگر Entity framework در چیست؟
سورس باز بودن
EF از نگارشهای آخر آن بود که سورس باز شد؛ اما EF Core از زمان نگارشهای پیش نمایش آن به صورت سورس باز در GitHub قابل دسترسی است.
چند سکویی بودن
EF Core برخلاف EF 6.x (آخرین نگارش مبتنی بر Full Framework آن)، نه تنها چندسکویی است و قابلیت اجرای بر روی Mac و لینوکس را نیز دارا است، به علاوه امکان استفادهی از آن در انواع و اقسام برنامههای دات نتی مانند UWP یا Universal Windows Platform و Windows phone که پیشتر با EF 6.x میسر نبود، وجود دارد. لیست این نوع سکوها و برنامههای مختلف به شرح زیر است:
• All .NET application (Console, ASP.NET 4, WinForms, WPF) • Mac and Linux applications (Mono) • UWP (Universal Windows Platform) • ASP.NET Core applications • Can use EF Core in Windows phone and Windows store app
افزایش تعداد بانکهای اطلاعاتی پشتیبانی شده
در EF Full یا EF 6.x، هدف اصلی، تنها کار با بانکهای اطلاعاتی رابطهای بود و همچنین مایکروسافت صرفا نگارشهای مختلف SQL Server را به صورت رسمی پشتیبانی میکرد و برای سایر بانکهای اطلاعاتی دیگر باید از پروایدرهای ثالث استفاده کرد.
در EF Core علاوه بر افزایش تعداد پروایدرهای رسمی بانکهای اطلاعاتی رابطهای، پشتیبانی از بانکهای اطلاعاتی NoSQL هم اضافه شدهاست؛ به همراه پروایدر ویژهای به نام In Memory جهت انجام سادهتر Unit testing. کاری که با نگارشهای پیشین EF به سادگی و از روز اول پشتیبانی نمیشد.
حذف و یا عدم پیاده سازی تعدادی از قابلیتهای EF 6.x
اگر موارد فوق جزو مهمترین مزایای کار با EF Core باشند، باید درنظر داشت که به علت حذف و یا تقلیل یافتن یک سری از ویژگیها در NET Core.، مانند Reflection (جهت پشتیبانی از دات نت در سکوهای مختلف کاری و خصوصا پشتیبانی از حالتی که کامپایلر مخصوص برنامههای UWP نیاز دارد تمام نوعها را همانند زبانهای C و ++C، در زمان کامپایل بداند)، یک سری از قابلیتهای EF 6.x مانند Lazy loading هنوز در EF Core پشتیبانی نمیشوند. لیست کامل و به روز شدهی این موارد را در اینجا میتوانید مطالعه کنید.
بنابراین امکان انتقال برنامههای EF 6.x به EF Core 1.0 عموما وجود نداشته و نیاز به بازنویسی کامل دارند. هرچند بسیاری از مفاهیم آن با EF Code First یکی است.
برپایی تنظیمات اولیهی EF Core 1.0 در یک برنامهی ASP.NET Core 1.0
برای نصب تنظیمات اولیهی EF Core 1.0 در یک برنامهی ASP.NET Core 1.0، جهت کار با مشتقات SQL Server (و SQL LocalDB) نیاز است سه بستهی ذیل را نصب کرد (از طریق منوی Tools -> NuGet Package Manager -> Package Manager Console):
PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer PM> Install-Package Microsoft.EntityFrameworkCore.Tools -Pre PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer.Design
پس از اجرای سه دستور فوق، تغییرات مداخل فایل project.json برنامه به صورت ذیل خواهند بود:
{ "dependencies": { // same as before "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0", "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final", "Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.0" } }
{ "dependencies": { // same as before "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0", "Microsoft.EntityFrameworkCore.Tools": { "version": "1.0.0-preview2-final", "type": "build" }, "Microsoft.EntityFrameworkCore.SqlServer.Design": { "version": "1.0.0", "type": "build" } }, "tools": { // same as before "Microsoft.EntityFrameworkCore.Tools": { "version": "1.0.0-preview2-final", "imports": [ "portable-net45+win8" ] } } }
بنابراین از همین ابتدای کار، بدون مراجعهی به Package Manager Console، چهار تغییر فوق را به فایل project.json اعمال کرده و آنرا ذخیره کنید؛ تا کار به روز رسانی و نصب بستهها، به صورت خودکار و همچنین صحیحی انجام شود.
فعال سازی صفحات مخصوص توسعه دهندههای EF Core 1.0
در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 5 - فعال سازی صفحات مخصوص توسعه دهندهها» با تعدادی از اینگونه صفحات آشنا شدیم. برای EF Core نیز بستهی مخصوصی به نام Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore وجود دارد که امکان فعال سازی صفحهی نمایش خطاهای بانک اطلاعاتی را میسر میکند. بنابراین ابتدا به فایل project.json مراجعه کرده و این بسته را اضافه کنید:
{ "dependencies": { // same as before "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0" } }
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDatabaseErrorPage(); }
تعریف اولین Context برنامه و مشخص سازی رشتهی اتصالی آن
در این تصویر، زیر ساخت نگاشتهای EF Core را مشاهده میکنید. در سمت چپ، ظرفی را داریم به نام DB Context که در برگیرندهی Db Setها است. در سمت راست که بیانگر ساختار کلی یک بانک اطلاعاتی است، معادل اینها را مشاهده میکنیم. هر Db Set به یک جدول بانک اطلاعاتی نگاشت خواهد شد و متشکل است از کلاسی به همراه یک سری خواص که اینها نیز به فیلدها و ستونهای آن جدول در سمت بانک اطلاعاتی نگاشت میشوند.
بنابراین برای شروع کار، پوشهای را به نام Entities به پروژه اضافه کرده و سپس کلاس ذیل را به آن اضافه میکنیم:
namespace Core1RtmEmptyTest.Entities { public class Person { public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } }
using Microsoft.EntityFrameworkCore; namespace Core1RtmEmptyTest.Entities { public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<Person> Persons { get; set; } } }
سازندهی این کلاس نیز به نحو خاصی تعریف شدهاست. اگر به سورسهای EF Core مراجعه کنیم، کلاس پایهی DbContext دارای دو سازندهی با و بدون پارامتر است:
protected DbContext() : this((DbContextOptions) new DbContextOptions<DbContext>()) { } public DbContext([NotNull] DbContextOptions options) { // … }
using Microsoft.EntityFrameworkCore; namespace Core1RtmEmptyTest.Entities { public class ApplicationDbContext : DbContext { public DbSet<Person> Persons { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"... connection string ..."); } } }
اما چون در یک برنامهی ASP.NET Core، کار ثبت سرویس مربوط به EF Core، در کلاس آغازین برنامه انجام میشود و در آنجا به سادگی میتوان به خاصیت Configuration برنامه دسترسی یافت و توسط آن رشتهی اتصالی را دریافت کرد، مرسوم است از سازندهی با پارامتر DbContext به نحوی که در ابتدا عنوان شد، استفاده شود.
بنابراین در ادامه، پس از مطالعهی مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 7 - کار با فایلهای config» به فایل appsettings.json مراجعه کرده و تنظیمات رشتهی اتصالی برنامه را به صورت ذیل در آن مشخص میکنیم:
{ "ConnectionStrings": { "ApplicationDbContextConnection": "Data Source=(local);Initial Catalog=TestDbCore2016;Integrated Security = true" } }
در اینجا به وهلهی پیش فرض SQL Server اشاره شدهاست؛ از حالت اعتبارسنجی ویندوزی SQL Server استفاده میشود و بانک اطلاعاتی جدیدی به نام TestDbCore2016 در آن مشخص گردیدهاست.
پس از تعریف رشتهی اتصالی، متد OnConfiguring را از کلاس ApplicationDbContext حذف کرده و از همان نگارش دارای سازندهی با پارامتر آن استفاده میکنیم. برای اینکار به کلاس آغازین برنامه مراجعه کرده و توسط متد AddDbContext این Context را به سرویسهای ASP.NET Core معرفی میکنیم:
public class Startup { public IConfigurationRoot Configuration { set; get; } public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", reloadOnChange: true, optional: false) .AddJsonFile($"appsettings.{env}.json", optional: true); Configuration = builder.Build(); } public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IConfigurationRoot>(provider => { return Configuration; }); services.AddDbContext<ApplicationDbContext>(options => { options.UseSqlServer(Configuration["ConnectionStrings:ApplicationDbContextConnection"]); });
بنابراین قسمت options.UseSqlServer را یا در اینجا مقدار دهی میکنید و یا از طریق بازنویسی متد OnConfiguring کلاس Context برنامه.
یک نکته: امکان تزریق IConfigurationRoot به کلاس Context برنامه وجود دارد
با توجه به اینکه Context برنامه را به صورت یک سرویس به ASP.NET Core معرفی کردیم، امکان تزریق وابستگیها نیز در آن وجود دارد. یعنی بجای روش فوق، میتوان IConfigurationRoot را به سازندهی کلاس Context برنامه نیز تزریق کرد:
using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; namespace Core1RtmEmptyTest.Entities { public class ApplicationDbContext : DbContext { private readonly IConfigurationRoot _configuration; public ApplicationDbContext(IConfigurationRoot configuration) { _configuration = configuration; } //public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) //{ //} public DbSet<Person> Persons { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(_configuration["ConnectionStrings:ApplicationDbContextConnection"]); } } }
در این حالت متد ConfigureServices کلاس آغازین برنامه، چنین شکلی را پیدا میکند و ساده میشود:
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApplicationDbContext>();
یک نکته: امکان تزریق ApplicationDbContext به تمام کلاسهای برنامه وجود دارد
همینقدر که ApplicationDbContext را به عنوان سرویسی در ConfigureServices تعریف کردیم، امکان تزریق آن در اجزای مختلف یک برنامهی ASP.NET Core نیز وجود دارد:
using System.Linq; using Core1RtmEmptyTest.Entities; using Microsoft.AspNetCore.Mvc; namespace Core1RtmEmptyTest.Controllers { public class TestDBController : Controller { private readonly ApplicationDbContext _ctx; public TestDBController(ApplicationDbContext ctx) { _ctx = ctx; } public IActionResult Index() { var name = _ctx.Persons.First().FirstName; return Json(new { firstName = name }); } } }
در این حالت پس از اجرای برنامه، خطای ذیل را مشاهده خواهیم کرد:
علت اینجا است که هنوز این بانک اطلاعاتی ایجاد نشدهاست و همچنین ساختار جداول را به آن منتقل نکردهایم که این موارد را در قسمتهای بعدی مرور خواهیم کرد.
قسمت قبل از دید یک مصرف کننده بود؛ این قسمت جهت توسعه دهندهها تهیه شده است. کسانی که قصد دارند تا بستههای NuGet ایی از کارشان تهیه کنند. مراحل اینکار به شرح زیر است:
الف) برای این منظور نیاز است تا برنامهی خط فرمان NuGet.exe معرفی شده در قسمت قبل را ابتدا دریافت کنید : (+)
ب) برای بسته نرم افزاری خود یک پوشه جدید درست کنید. سپس فرمان nuget.exe spec را در این پوشه صادر نمائید. بلافاصله فایلی به نام Package.nuspec تشکیل خواهد شد:
D:\Prog\1389\CodePlex\slpdatepicker\SlPDatePickerNuGet>NuGet.exe spec
Created 'Package.nuspec' successfully.
فایل Package.nuspec، یک فایل XML ساده است. آنرا با یک ادیتور متنی باز کرده و تغییرات لازم را اعمال نمائید. برای مثال من جهت پروژه Silverlight 4 Persian DatePicker ، محتویات آنرا به صورت زیر تغییر دادهام:
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Silverlight.4.Persian.DatePicker</id>
<version>1.0</version>
<authors>Vahid Nasiri</authors>
<owners>Vahid Nasiri</owners>
<licenseUrl>http://slpdatepicker.codeplex.com/license</licenseUrl>
<projectUrl>http://slpdatepicker.codeplex.com/</projectUrl>
<iconUrl>https://slpdatepicker.svn.codeplex.com/svn/SilverlightPersianDatePicker/Views/Images/date.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Silverlight 4 Persian DatePicker Control</description>
<tags>Silverlight WPF Persian DatePicker</tags>
</metadata>
<files>
<file src="..\SilverlightPersianDatePicker\Bin\Release\*.dll" target="lib" />
<file src="..\SilverlightPersianDatePicker\Bin\Release\*.pdb" target="lib" />
<file src="..\SilverlightPersianDatePicker\Bin\Release\*.xml" target="lib" />
</files>
</package>
همانطور که ملاحظه میکنید یک سری اطلاعات عمومی از پروژه مورد نظر درخواست شده است؛ برای مثال آدرس آیکن آن چیست یا کجا میتوان آنرا یافت؟ مجوز استفاده از آن چیست و مواردی از این دست. به کمک تگ files هم فایلهای کتابخانه در اینجا لحاظ شدهاند. فایل آیکن معرفی شده باید در اندازهی 32*32 و با فرمت png باشد. باید دقت داشت که در سایت nuget.org ، بسته شما بر اساس id ذکر شده معرفی خواهد شد و آدرسی بر این اساس تشکیل میگردد. بنابراین از فاصله یا موارد مشکل ساز در این بین استفاده نکنید.
در مورد نحوهی ایجاد قدم به قدم یک پروژه جدید در سایت کدپلکس میتوان به این مطلب مراجعه نمود: (+)
ج) اکنون نوبت به تهیه بسته نهایی میرسد. برای این منظور دستور زیر را در خط فرمان صادر کنید:
NuGet.exe pack Package.nuspec
د) قبل از اینکه این فایل نهایی را در سایت nuget.org آپلود کنیم، میتوان مشخصات آنرا به صورت محلی نیز یکبار مرور کرد. برای این منظور در VS.NET به منوی Tools گزینهی Options مراجعه کرده و در قسمت package manager ، آدرس پوشه بسته مورد نظر را وارد کنید. برای مثال:
اکنون اگر کنسول پاورشل توضیح داده شده در قسمت قبل را باز نمائید، منبع جدید اضافه شده مشخص است یا میتوان توسط دستور ذیل از آن کوئری گرفت:
get-package -remote -filter silverlight
و یا اگر همانند توضیحات قبل به صفحهی دیالوگ add library package reference مراجعه کنیم، مشخصات کامل بسته به همراه منبع محلی باید قابل مشاهده باشند:
ه) پس از بررسی محلی بسته مورد نظر، اکنون نوبت به ارائه عمومی آن میباشد. برای این منظور ابتدا باید در سایت nuget.org ثبت نام کرد : (+). اگر آدرس ایمیل شما را نپذیرفت، از مرورگر IE استفاده کنید!
پس از ثبت نام تنها کافی است به قسمت contribute سایت مراجعه کرده و فایل بسته نهایی را در آنجا آپلود کرد. به این صورت بسته نهایی در سایت پدیدار خواهد شد :(+)
همچنین بلافاصله در قسمت گالری آنلاین صفحه add library package reference نیز قابل دسترسی خواهد بود.
در آینده جهت توزیع به روز رسانیهای جدید، همین مراحل باید تکرار شوند. البته در نظر داشته باشید که version ذکر شده در فایل Package.nuspec را باید حتما تغییر داد تا بستهها از یکدیگر متمایز شوند. امکان اتوماسیون این توزیع نیز وجود دارد. همان فایل nuget.exe ، امکان ارسال بسته نهایی را به سایت nuget.org نیز دارد:
nuget push name.nupkg key
اگر علاقمند به مشاهده جزئیات بیشتری از این پروسه هستید، میتوان به سایت رسمی آن مراجعه کرد: (+)
<!-- BEGIN MOMENT --> <script src="/bower_components/moment/moment.js"></script> <script src="/bower_components/moment/locale/fa.js"></script> <script src="/bower_components/moment-jalaali/build/moment-jalaali.js"></script> <script> moment.locale('fa'); // Set the default/global locale // ... </script> <!-- END MOMENT -->
app.filter('jalaliDateFromNow', function () { return function (inputDate) { var date = moment(inputDate); return date.fromNow(); } });
بررسی بهبودهای پروسهی Build در داتنت 8
یک نکتهی تکمیلی: چگونه تاریخ Build را به اسمبلی برنامه اضافه کنیم؟
شاید علاقمند باشید که بجای نمایش شماره نگارش برنامه، تاریخ Build آنرا در قسمتی خاص، نمایش دهید. برای اینکار میتوان یک ویژگی جدید را به صورت زیر به اسمبلی برنامه اضافه کرد:
[AttributeUsage(AttributeTargets.Assembly)] public sealed class BuildDateAttribute : Attribute { public BuildDateAttribute(string buildDateTime) => BuildDateTime = buildDateTime; public string BuildDateTime { get; } }
تا در زمان کامپایل برنامه، فایل obj\Debug\net8.0\DntSite.Web.AssemblyInfo.cs را به این صورت تکمیل کند:
using System; using System.Reflection; [assembly: DNTCommon.Web.Core.BuildDateAttribute("1403.05.28.15.17")] [assembly: System.Reflection.AssemblyCompanyAttribute("DntSite.Web")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
مقدار دهی این این ویژگی جدید، در زمان Build و توسط تنظیمات زیر در فایل csproj. برنامه انجام میشود:
<Project> <ItemGroup> <AssemblyAttribute Include="DNTCommon.Web.Core.BuildDateAttribute"> <_Parameter1>$([System.DateTime]::Now.ToString("yyyy.MM.dd.HH.mm"))</_Parameter1> </AssemblyAttribute> </ItemGroup>
با استفاده از AssemblyAttribute میتوان پارامترهای دلخواهی را به ویژگی Include شده، ارسال کرد؛ برای مثال، تاریخ جاری سیستم را. اگر تعداد پارامترهای سازنده بیشتر بود، میتوان Parameter2_ و Parameter3_ و ... را هم تنظیم کرد.
همین اندازه تنظیم برای اضافه شدن خودکار این ویژگی جدید به اسمبلی نهایی برنامه کافی است (و همانطور که عنوان شد، محل درج خودکار اولیهی آن، در فایل AssemblyInfo.cs پوشهی obj برنامهاست). برای خواندن و نمایش آن هم میتوان به صورت زیر عمل کرد:
public static class BuildDateAttributeExtensions { public static string? GetBuildDateTime(this Assembly? assembly) => assembly?.GetCustomAttribute<BuildDateAttribute>()?.BuildDateTime ?? assembly?.GetName().Version?.ToString(); }
برای نمونه میتوان اطلاعات BuildDateAttribute اسمبلی جاری را به صورت زیر استخراج کرد:
private static string? GetVersionInfo() => Assembly.GetExecutingAssembly().GetBuildDateTime();
همانطور که مطلع هستید از ویندوز ویستا به بعد، فرمت قدیمی فایلهای راهنمای ویندوز (فایلهای hlp) منسوخ شده تلقی میشود و فرمت پیشنهادی، chm است. نرم افزارهای زیادی برای تهیه فایلهای compiled html help یا همان chm های معروف وجود دارند که معروفترین آنها برنامهی Help & Manual است.
اما تمام این برنامهها در حقیقت پوستهای هستند برای برنامهی رایگان html help work shop مایکروسافت و در نهایت از کامپایلر آن استفاده میکنند. بنابراین چرا از برنامهی رایگان اصلی استفاده نشود؟
اگر تا به حال با html help work shop کار نکردهاید، در ادامه مروری سریع بر آن خواهیم داشت:
الف) درست کردن فایلهای صفحات راهنما
برنامهی Help & Manual ایی که معرفی شد و تمام نمونههای مشابه آن، تنها کار مهمی را که انجام میدهند این است که شما را از یک html editor بینیاز میکنند. بنابراین زمانیکه میخواهیم از برنامهی اصلی html help work shop استفاده کنیم نیاز به یک html editor خارجی برای تهیه فایلهای راهنما خواهیم داشت. مثلا مرحوم front page یا نگارش جدید آن به نام Microsoft expression web یا Dreamweaver یا Aptana یا حتی notepad !
در اینجا تنها مشخص کردن نوع encoding نمایش صفحه برای صحیح نمایش داده شدن متون فارسی کافی است. اما این تمام ماجرا نیست.
ب) کامپایل کردن فایلهای راهنمای ایجاد شده
برای این منظور بجای آپلود بیش از 30 تصویر جهت توضیحات قدم به قدم نحوهی انجام اینکار، یک فایل ویدیویی درست کردهام که آنرا از آدرس زیر میتوانید دریافت کنید.
دریافت فایل
همانطور که در این فایل آموزشی مقدماتی هم تاکید شده، به صورت پیش فرض جستجوی فارسی کار نمیکند. همچنین اگر یک متن انگلیسی را جستجو کنید، صفحات یاد شده با عنوانی به هم ریخته نمایش داده میشوند. در صفحه افزودن به علاقمندیها نیز یک چنین مشکلی با عنوانها وجود دارد.
در قسمت دوم، نحوهی رفع این دو مشکل (مشکل جستجوی عبارات فارسی و مشکل عنوانهای به هم ریخته) را بررسی خواهیم کرد.
ادامه دارد ...