اشتراکها
از دغدغههای همیشگی در راه اندازی پرتالهای مبتنی بر شیرپوینت سیستم احراز هویت آن است. این سیستم بصورت پیش فرض بر مبنای Windows Authentication است و ناگفته پیداست این نوع احراز هویت تنها در شبکههای محلی کاربرد دارد آنهم در صورتی که همه کاربران و سطوح دسترسی، بدرستی در AD تعریف شده باشد و نیز
یک سری مشکلات دیگر که بیشتر به توسعه شیرپوینت در شرکت و انتقال آن و انطباق آن با محیط پروژه برمیگردد.
به عبارت دیگر شما به عنوان یک توسعه دهنده ویا نَصاب(!) شیرپوینت خیلی نباید درگیر سیستم احراز هویت پیش فرض مشتری بشوید. برای اینکار بهترین گزینه استفاده از احراز هویت بر مبنای فرم (Form Based Authentication - FBA) است که برای ما برنامه نویسان Asp.net بسیار آشناست؛ سیستم احراز هویتی خوش دست و فراگیر با قاعدههای مشخص.
متاسفانه اکثر راهکارهایی که در وب پیرامون راه اندازی FBA در شیرپوینت معرفی شدهاند دارای اشکالات ریز و درشتی هستند و یا اینکه یک یا چند گام از فرایند را توضیح ندادهاند و معمولا در پایان یکجای کار لنگ میزند و FBA بخوبی عملیاتی نمیشود.
بر همین اساس بر آن شدم تا با بررسی چندتا از این مقالات موجود و نیز تجربه عملی خودم این راهکارها را ترکیب کنم که نتیجه اش فرایند شش مرحله ای زیر شده است.
در ادامه تنها کافی است Connection string را جهت اتصال به بانکی که در ابتدا ساختیم ایجاد کرده و Providerها را نیز مطابق قبل اضافه کنیم. فقط توجه شود Connection string و Providerها همنام قبلیها نباشند ولی Application Name همچنان برابر با "/" مقدار دهی شود.
هیچ تغییر دیگری در این Application ایجاد نشود.مثلا Authentication به هیچ وجه تغییر نکند و در حالت ویندوزی باقی بماند.
به عبارت دیگر شما به عنوان یک توسعه دهنده ویا نَصاب(!) شیرپوینت خیلی نباید درگیر سیستم احراز هویت پیش فرض مشتری بشوید. برای اینکار بهترین گزینه استفاده از احراز هویت بر مبنای فرم (Form Based Authentication - FBA) است که برای ما برنامه نویسان Asp.net بسیار آشناست؛ سیستم احراز هویتی خوش دست و فراگیر با قاعدههای مشخص.
متاسفانه اکثر راهکارهایی که در وب پیرامون راه اندازی FBA در شیرپوینت معرفی شدهاند دارای اشکالات ریز و درشتی هستند و یا اینکه یک یا چند گام از فرایند را توضیح ندادهاند و معمولا در پایان یکجای کار لنگ میزند و FBA بخوبی عملیاتی نمیشود.
بر همین اساس بر آن شدم تا با بررسی چندتا از این مقالات موجود و نیز تجربه عملی خودم این راهکارها را ترکیب کنم که نتیجه اش فرایند شش مرحله ای زیر شده است.
برای راه اندازی FBA بر روی SharePoint 2010 باید مراحل زیر را به ترتیب انجام داد.
1. ساخت بانک اطلاعاتی FBA بر روی MSSQL
بانک اطلاعاتی FBA ساختار مشخصی از جداول و SPها دارد که تا حد امکان بهتر است در ساختار پیش فرض آن تغییری ایجاد نکنیم. برای ایجاد این بانک کافی است به محل نصب دات نت فریم 2.0 که بصورت پیش فرض در مسیر"C:\Windows\Microsoft.NET\Framework64\v2.0.50727 " قرار دارد بروید و روی فایل "aspnet_regsql.exe " کلیک کنید و مراحل نصب را تا آخر پیش بروید.
در ادامه MSSQL را باز کنید و مطئمن شوید که بانک FBA به درستی ایجاد شده باشد.
2.وارد IIS شده و در بخش روت سرور ،توجه شود که حتما این تغییرات در روت سرور اعمال شود تا همه سایتها از آن ارث بری کنند و تنظیمات شما یکسان در همه Web Applicationها اعمال شود.
این مرحله Connection String و Role provider و Membership provider مورد نظرتان را اضافه کنید.
این مرحله Connection String و Role provider و Membership provider مورد نظرتان را اضافه کنید.
موقع اضافه کردن Providerها باید توجه داشت که Application Name برابر "/" قرار داده شود.
3.به بخش مدیریت شیرپوینت رفته و یک WebApplication جدید ایجاد کنید.
توجه داشت که در بخش Authentication بایدگزینه Claims Authentication را انتخاب کنید.
و نیز برای تنظیمات مربوط به FBA باید نام Provider هایی را که در مرحله قبل ایجاده کرده بودید را در این بخش قرار دهید.
اگر پرتال شما بازدید کنندههای ناشناس(بازدید کنندگانی که عضو نیستند و در سیستم FBA نام کاربری ندارند) را نیز پشتیبانی میکند باید گزینه اول(Allow anonymous) را Yes کنید.
اگر تمایلی ندارید که دیگر احراز هویت مبتنی بر ویندوز فعال باشد تیک Enable Windows Authentication را بردارید.
اگر پرتال شما بازدید کنندههای ناشناس(بازدید کنندگانی که عضو نیستند و در سیستم FBA نام کاربری ندارند) را نیز پشتیبانی میکند باید گزینه اول(Allow anonymous) را Yes کنید.
اگر تمایلی ندارید که دیگر احراز هویت مبتنی بر ویندوز فعال باشد تیک Enable Windows Authentication را بردارید.
4.حال دوباره سراغ IIS میرویم و روی وب جدیدی که ایجاد کرده ایم کلیک میکنیم و سپس روی آیکون Net Users کلیک میکنیم
در ادامه چند کاربر و رول هم به عنوان کاربران اولیه و مدیران پرتال ایجاد میکنیم .
اگر با پیغامی مبنی بر تعیین Provider پیش فرض مواجه شدیم مقادیر پیش فرض Providerها را برابر با نام Provider هایی که خودمان تعریف کرده ایم قرار میدهیم.
5.حال دوباره به بخش مدیریت برمیگردیم و یک Site Collection جدید روی Web Application مورد نظر ایجاد میکنیم.
در اینجا بهتر است مدیر اول را از کاربران ویندوزی انتخاب کرد و مدیر دوم را از بخش کاربران FBA
نکته بی ربط:اگر تمپلتی از قبل برای سایتتان دارید در بخش انتخاب نوع سایت بعد از انتخاب زبان باید به تب سوم(Custom) بروید و تنها گزینه موجود را انتخاب کنید.با این کار در مراحل بعد تمپلت مورد نظرتان را در سایت آپلود میکنید و آن را بر سایت جاری اعمال میکنید.
نکته بی ربط:اگر تمپلتی از قبل برای سایتتان دارید در بخش انتخاب نوع سایت بعد از انتخاب زبان باید به تب سوم(Custom) بروید و تنها گزینه موجود را انتخاب کنید.با این کار در مراحل بعد تمپلت مورد نظرتان را در سایت آپلود میکنید و آن را بر سایت جاری اعمال میکنید.
در این مرحله بعد از ایجاد موفقیت آمیز Site collection دوباره به IIS سری بزنید و پس از انتخاب Web Application مورد نظر بروی Authentication کلیک کنید و مطمئن شوید که Form Authentication مقدارش Enable میباشد.
در این بخش اگر هر دو حالت Form Base Authentication و Windows Authentication فعال باشد IIS در گوشه سمت راست به شما خطایی نمایش میدهد مبنی بر اینکه شما نباید هر دو حالت را همزمان فعال کنید.
البته خیلی این تذکر را جدی نگیرید و بکارتان ادامه دهید و در نهایت بعد از اینکه مراحل را کامل انجام دادید و از اجرای کامل FBA اطمینان حاصل نمودید،میتوانید برگردید و حالت Windows Authentication را غیر فعال کنید.
6.تنظیمات STS:
علاوه بر تنظیمات مربوط به بخش Web Application باید تنظیمات FBA را روی Application سرویسها نیز انجام داد تا سرویسهای WCF نیز پشتیبانی از FBA را بپذیرند. برای تنظیمات این بخش روی Security Token Service Application که زیر مجموعه SharePoint Web Services قرار دارد کلیک کنید.
علاوه بر تنظیمات مربوط به بخش Web Application باید تنظیمات FBA را روی Application سرویسها نیز انجام داد تا سرویسهای WCF نیز پشتیبانی از FBA را بپذیرند. برای تنظیمات این بخش روی Security Token Service Application که زیر مجموعه SharePoint Web Services قرار دارد کلیک کنید.
در ادامه تنها کافی است Connection string را جهت اتصال به بانکی که در ابتدا ساختیم ایجاد کرده و Providerها را نیز مطابق قبل اضافه کنیم. فقط توجه شود Connection string و Providerها همنام قبلیها نباشند ولی Application Name همچنان برابر با "/" مقدار دهی شود.
هیچ تغییر دیگری در این Application ایجاد نشود.مثلا Authentication به هیچ وجه تغییر نکند و در حالت ویندوزی باقی بماند.
کار تقریبا به پایان رسیده است،میتوانید در پرتال لاگین کنید!
آنچنانکه که در تصویر میبینید هر دو حالت ویندوزی و FBA برای احراز هویت فعال میباشد.
آنچنانکه که در تصویر میبینید هر دو حالت ویندوزی و FBA برای احراز هویت فعال میباشد.
پی نوشت:
1.همانطور که احتمالا متوجه شده اید این آموزش با راهکارهای حاضر یک سری تفاوتها داشت که عمدهترین آن عدم تغییر در بخش احراز هویت مدیریت شیرپوینت بود. علت این امر نیز به این خاطر است که اساسا هر کاربری به این بخش دسترسی ندارد و تنها مدیر سیستم است که باید به این بخش دسترسی داشته باشد، بر همین اساس ترجیح میدهم احراز هویت آن به همان شکل اولیه(Windows Authentication ) باقی بماند.
2.در این نوشته من از شرح تنظیمات و نکات ریز و بدیهی خوداری کردم با این پیش فرض که خواننده مطلب، بر اصول پایه شیرپوینت و Asp.net آگاهی دارد. در غیر این صورت بهتر است از لینک هایی مرجع زیر کمک بگیرید.
مراجع:
در این مقاله مروری سریع و کاربردی خواهیم داشت بر تواناییهای مقدماتی LINQ to XML .
فایل Employee.XML را با محتویات زیر در نظر بگیرید:
<Employees>
<Employee>
<Name>Vahid</Name>
<Phone>11111111</Phone>
<Department>IT</Department>
<Age>52</Age>
</Employee>
<Employee>
<Name>Farid</Name>
<Phone>124578963</Phone>
<Department>Civil</Department>
<Age>35</Age>
</Employee>
<Employee>
<Name>Mehdi</Name>
<Phone>1245788754</Phone>
<Department>HR</Department>
<Age>30</Age>
</Employee>
</Employees>
1- چگونه یک فایل XML را جهت استفاده توسط LINQ بارگذاری کنیم؟
قبل از شروع، اسمبلی System.Xml.Linq باید به ارجاعات برنامه اضافه شود. سپس:
using System.Xml.Linq;
XDocument xDoc = XDocument.Load("Employee.xml");
2- اگر محتویات XML دریافتی به صورت رشته بود (مثلا از یک دیتابیس دریافت شد)، اکنون چگونه باید آنرا بارگذاری کرد؟
اینکار را با استفاده از یک StringReader به صورت زیر میتوان انجام داد:
// loading XML from string
StringReader sr = new StringReader(stringXML);
XDocument xDoc = XDocument.Load(sr);
3- چگونه یک کوئری ساده شامل تمامی رکوردهای Employee مجموعه Employees را تهیه کنیم؟
using System.Collections;
IEnumerable<XElement> empList = from e in xDoc.Root.Elements("Employee") select e;
اکنون که مجموعه کارکنان توسط متغیر empList در اختیار ما است، دسترسی به محتویات آن به سادگی زیر خواهد بود:
foreach (XElement employee in empList)
{
foreach (XElement e in employee.Elements())
{
Console.WriteLine(e.Name + " = " + e.Value);
}
}
4- کوئری بنویسید که اطلاعات تمامی کارکنان بخش HR را باز گرداند.
IEnumerable<XElement> hrList = from e in xDoc.Root.Elements("Employee")
where e.Element("Department").Value == "HR"
select e;
همانطور که ملاحظه میکنید همانند عبارات SQL ، در تمامی عناصر متعلق به کارکنان، عناصری که دپارتمان آنها مساوی HR است بازگشت داده میشود.
5- کوئری بنویسید که لیست تمامی کارکنان بالای 30 سال را ارائه دهد.
IEnumerable<XElement> tList = from e in xDoc.Root.Elements("Employee")
where int.Parse(e.Element("Age").Value) > 30
select e;
چون حاصل e.Element("Age").Value یک رشته است، برای اعمال فیلترهای عددی باید این رشتهها تبدیل به عدد شوند. به همین جهت از int.Parse استفاده شده است.
6- کوئری بنویسید که لیست تمامی کارکنان بالای 30 سال را مرتب شده بر اساس نام باز گرداند.
IEnumerable<XElement> tList = from e in xDoc.Root.Elements("Employee")
where int.Parse(e.Element("Age").Value) > 30
orderby e.Element("Name").Value
select e;
7- تبدیل نتیجهی یک کوئری LINQ به لیستی از اشیاء
مفهومی به سی شارپ 3 اضافه شده است به نام anonymous types . برای مثال:
توسط این قابلیت میتوان یک شیء را بدون نیاز به تعریف ابتدایی آن ایجاد کرد و حتی از intelliSense موجود در IDE نیز بهره مند شد. این نوعهای ناشناس توسط واژههای کلیدی new و var تولید میشوند. کامپایلر به صورت خودکار برای هر anonymous type یک کلاس ایجاد میکند.
دقیقا از همین توانایی در LINQ نیز میتوان استفاده نمود:
var empList = from e in xDoc.Root.Elements("Employee")
orderby e.Element("Name").Value
select new
{
Name = e.Element("Name").Value,
Phone = e.Element("Phone").Value,
Department = e.Element("Department").Value,
Age = int.Parse(e.Element("Age").Value)
};
foreach (var employee in empList)
{
Console.WriteLine("Name = " + employee.Name);
Console.WriteLine("Dep = " + employee.Department);
Console.WriteLine("Phone = " + employee.Phone);
Console.WriteLine("Age = " + employee.Age);
}
public class Employee
{
public string Name { get; set; }
public string Phone { get; set; }
public string Department { get; set; }
public int Age { get; set; }
}
برای مثال اگر بخواهیم لیست دریافتی را به صورت یک لیست جنریک بازگشت دهیم خواهیم داشت:
public class Employee
{
public string Name { get; set; }
public string Phone { get; set; }
public string Department { get; set; }
public int Age { get; set; }
}
List<Employee> Get()
{
XDocument xDoc = XDocument.Load("Employee.xml");
var items =
from e in xDoc.Root.Elements("Employee")
orderby e.Element("Name").Value
select new Employee
{
Name = e.Element("Name").Value,
Phone = e.Element("Phone").Value,
Department = e.Element("Department").Value,
Age = int.Parse(e.Element("Age").Value)
};
return items.ToList();
}
با سلام
T4MVC یکسری تغییرات در برنامه ما ایجاد میکنه از جمله مثلا یک کانستراکتور برای کنترلر Home اضافه میکنه و من چگونه باید کدی مثل کد زیر رو داخل کنترل ام بنویسیم که خطای تکراری نگیره؟
T4MVC یکسری تغییرات در برنامه ما ایجاد میکنه از جمله مثلا یک کانستراکتور برای کنترلر Home اضافه میکنه و من چگونه باید کدی مثل کد زیر رو داخل کنترل ام بنویسیم که خطای تکراری نگیره؟
private readonly ICacheService _cacheService; public HomeController() : this(MvcApplication.CacheService) { } public HomeController(ICacheService cacheService) { _cacheService = cacheService; }
بازخوردهای دوره
آشنایی با نحوه ایجاد یک IoC Container
پاسخ به این سؤال نیاز به مطالعه قسمتهای بعدی دارد.
- هدف از قسمت جاری آشنایی اولیه با مراحل ابتدایی کار با یک IoC Container است.
- در حالت اول هنوز شما هستید که مسئول وهله سازیهای اولیه میباشید و کار وهله سازی را به لایهای دیگر واگذار نکردهاید.
- در کد حالت اول نمیشود این وابستگی ارسالی به سازنده کلاس را به سادگی تعویض کرد. در حالیکه با استفاده از یک IoC container فقط کافی است تنظیمات اولیه نگاشتهای آنرا مشخص کنیم تا نوع کلاسی که باید در سازندهها تزریق شوند مشخص شود. مزیت اینکار سادهتر شدن نوشتن آزمونهای واحد و تهیه کلاسهای Fake است؛ بدون نیازی به تغییری حتی در حد یک سطر در کدهای اصلی برنامه.
- در مورد وهله سازی خودکار چند سطح وابستگیها، در قسمتهای بعد تحت عنوان Object graph بیشتر بحث شده است و مثال زده شده. همیشه با یک کلاس ساده ویزا مانند مثال فوق سر و کار نداریم. عموما با سرویسهایی سر و کار داریم که خودشان نیز از سرویسهای دیگری استفاده میکنند. برای مثال یک سرویس ارسال ایمیل از سرویس کاربران برای دریافت ایمیلهای کاربران کمک میگیرد. وهله سازی تمام این وابستگیها را در چند سطح میشود با استفاده از IoC Containers خودکار کرد و به کدهای نهایی بسیار تمیزتری رسید.
- در حالت اول، طول عمر یک شیء را نمیشود مشخص کرد (یا حداقل نیاز به کد نویسی قابل توجهی دارد). برای مثال با استفاده از یک IoC Container میشود وهله ایجاد شده را Singleton کرد تا در سراسر برنامه یک وهله از آن استفاده شود یا حتی میشود در طول یک درخواست رسیده وب، یک وهله را در اختیار تمام کلاسهای درگیر قرار داد. به این ترتیب به سربار کمتری در حالتهای خاصی مانند وهله سازی ObjectContext یا DbContext در EF خواهیم رسید.
- زمان استفاده از IoC Containerها کارهای فراتری از تزریق وابستگیها را هم میشود انجام داد. برای مثال فراخوانیهای متدها را هم تحت نظر قرار داد (برنامه نویسی AOP یا جنبه گرا) و مثلا بدون نوشتن کد اضافهای در برنامه، خروجی متدها را کش کرد. AOP یک سری بحث مفصل را در طی یک دوره جدا به همراه دارد.
- هدف از قسمت جاری آشنایی اولیه با مراحل ابتدایی کار با یک IoC Container است.
- در حالت اول هنوز شما هستید که مسئول وهله سازیهای اولیه میباشید و کار وهله سازی را به لایهای دیگر واگذار نکردهاید.
- در کد حالت اول نمیشود این وابستگی ارسالی به سازنده کلاس را به سادگی تعویض کرد. در حالیکه با استفاده از یک IoC container فقط کافی است تنظیمات اولیه نگاشتهای آنرا مشخص کنیم تا نوع کلاسی که باید در سازندهها تزریق شوند مشخص شود. مزیت اینکار سادهتر شدن نوشتن آزمونهای واحد و تهیه کلاسهای Fake است؛ بدون نیازی به تغییری حتی در حد یک سطر در کدهای اصلی برنامه.
- در مورد وهله سازی خودکار چند سطح وابستگیها، در قسمتهای بعد تحت عنوان Object graph بیشتر بحث شده است و مثال زده شده. همیشه با یک کلاس ساده ویزا مانند مثال فوق سر و کار نداریم. عموما با سرویسهایی سر و کار داریم که خودشان نیز از سرویسهای دیگری استفاده میکنند. برای مثال یک سرویس ارسال ایمیل از سرویس کاربران برای دریافت ایمیلهای کاربران کمک میگیرد. وهله سازی تمام این وابستگیها را در چند سطح میشود با استفاده از IoC Containers خودکار کرد و به کدهای نهایی بسیار تمیزتری رسید.
- در حالت اول، طول عمر یک شیء را نمیشود مشخص کرد (یا حداقل نیاز به کد نویسی قابل توجهی دارد). برای مثال با استفاده از یک IoC Container میشود وهله ایجاد شده را Singleton کرد تا در سراسر برنامه یک وهله از آن استفاده شود یا حتی میشود در طول یک درخواست رسیده وب، یک وهله را در اختیار تمام کلاسهای درگیر قرار داد. به این ترتیب به سربار کمتری در حالتهای خاصی مانند وهله سازی ObjectContext یا DbContext در EF خواهیم رسید.
- زمان استفاده از IoC Containerها کارهای فراتری از تزریق وابستگیها را هم میشود انجام داد. برای مثال فراخوانیهای متدها را هم تحت نظر قرار داد (برنامه نویسی AOP یا جنبه گرا) و مثلا بدون نوشتن کد اضافهای در برنامه، خروجی متدها را کش کرد. AOP یک سری بحث مفصل را در طی یک دوره جدا به همراه دارد.
مطالب دورهها
کوئری نویسی مقدماتی در RavenDB
با شروع کوئری نویسی مقدماتی در RavenDB، در قسمت اول این مباحث، توسط فراخوانی متد Load یک سشن، آشنا شدید. در ادامه مباحث تکمیلی آنرا مرور خواهیم کرد.
امکان استفاده از LINQ در RavenDB
RavenDB از LINQ جهت کوئری نویسی پشتیبانی میکند. برای استفاده از آن، در ادامه مطلب اول، ابتدا سرور RavenDB را اجرا نموده و سپس برنامه کنسول را به نحو ذیل تغییر دهید:
در RavenDB برای دسترسی به امکانات LINQ، کار با متد Query یک سشن آغاز میشود و پس از آن، امکان استفاده از متدهای متداول LINQ مانند مثال فوق وجود خواهد داشت. البته بدیهی است مباحثی مانند JOIN و امثال آن در یک بانک اطلاعاتی NoSQL پشتیبانی نمیشود. ضمنا باید درنظر داشت که مبحث safe by default در اینجا نیز اعمال میشود. برای مثال اگر به کنسول سرور RavenDB که در حال اجرا است مراجعه کنید، یک چنین خروجی را حین اجرای مثال فوق میتوان مشاهده کرد که در آن pageSize پیش فرضی اعمال شده است:
یعنی در عمل کوئریرا که اجرا کرده است، شبیه به کوئری ذیل میباشد و یک Take پیش فرض بر روی آن اعمال شده است:
علت این مساله نیز به تصمیم نویسنده اصلی آن بر میگردد؛ ایشان پیش از شروع به تهیه RavenDB، کار تهیه انواع و اقسام پروفایلرهای مهم ORMهای معروف مانند NHibernate و Entity framework را انجام داده است و در این حین، یکی از مهمترین مشکلاتی را که با آنها در کدهای متداول برنامه نویسها یافته است، unbounded queries است. کوئریهایی که حد و مرزی برای بازگشت اطلاعات قائل نمیشوند. داشتن این نوع کوئریها با تعداد بالای کاربر، یعنی مصرف بیش از حد RAM بر روی سرور، به همراه بار پردازشی بیش از حد و غیر ضروری. چون عملا حتی اگر 10 هزار رکورد بازگشت داده شوند، عموم برنامه نویسها حداکثر 100 رکورد آنرا در یک صفحه نمایش میدهند و نه تمام رکوردها را.
ارتباط Lucene.NET و RavenDB
کل LINQ API تهیه شده در RavenDB یک محصور کننده امکانات Lucene.NET است. اگر پیشتر با Lucene.NET کار کرده باشید، در خروجی حالت دیباگ کنسول سرور فوق، سطر «Query: Title:Raven*» آشنا به نظر خواهد رسید. دقیقا کوئری LINQ نوشته شده به یک کوئری با Syntax مخصوص Lucene.NET ترجمه شدهاست. برای نمونه اگر علاقمند باشید که مستقیما کوئریهای خاص لوسین را در RavenDB اجرا کنید، از Syntax ذیل میتوان استفاده کرد:
و یا اگر علاقمند به حفظ کردن Syntax خاص لوسین نیستید، یک سری متد الحاقی خاص نیز در اینجا برای LuceneQuery تدارک دیده شده است. برای مثال کوئری رشتهای فوق، معادل کوئری strongly typed ذیل است:
استفاده مجدد از کوئریها در RavenDB
در RavenDB، متد Query به صورت immutable تعریف شده است و متد LuceneQuery حالت mutable دارد (ترکیبات آن نیز یک وهله است).
یک مثال:
در اینجا از کوئری ابتدایی، در دو کوئری مجزا استفاده مجدد شده است. ترجمه خروجی سه کوئری فوق به نحو زیر است:
به این معنا که زمانیکه به eyeQuery رسیدیم، نتیجه ageQuery با آن ترکیب نمیشود؛ چون متد Query از نوع immutable است.
در ادامه اگر همین سه کوئری فوق را با فرمت LuceneQuery تهیه کنیم، به عبارات ذیل خواهیم رسید:
در خروجیهای این سه کوئری، مورد سوم مهم است:
همانطور که مشاهده میکنید، کوئری سوم، عبارت کوئری دوم را نیز به همراه دارد؛ این مورد دقیقا مفهوم اشیاء mutable یا تک وهلهای است مانند LuceneQuery در اینجا.
And و Or شدن کوئریهای ترکیبی در RavenDB
در مثال استفاده مجدد از کوئریها، زمانیکه از Where استفاده شد، بین عبارات حاصل AND قرار گرفته است. این مورد را به نحو ذیل میتوان تنظیم کرد و مثلا به OR تغییر داد:
صفحه بندی اطلاعات در RavenDB
در ابتدای بحث عنوان شد که کوئری LINQ اجرا شده در RavenDB، یک Take مخفی و پیش فرض تنظیم شده به 128 آیتم را دارد. اکنون سؤال این خواهد بود که چگونه میتوان اطلاعات را به صورت صفحه بندی شده، بر اساس شماره صفحه خاصی نمایش داد.
برای انجام صفحه بندی در RavenDB، کافی است از متدهای Skip و Take بر اساس محاسباتی که مشاهده میکنید، استفاده گردد.
دریافت اطلاعات آماری کوئری اجرا شده
در RavenDB امکان دریافت یک سری اطلاعات آماری از کوئری اجرا شده نیز وجود دارد؛ برای مثال یک کوئری چند ثانیه طول کشیده است، چه تعدادی رکورد را بازگشت داده است و امثال آن. برای پیاده سازی آن، نیاز است از متد الحاقی Statistics به نحو ذیل استفاده کرد:
متد الحاقی Statistics پس از متد Query که نقطه آغازین نوشتن کوئریهای LINQ است، فراخوانی شده و یک پارامتر out از نوع RavenQueryStatistics تعریف شده در فضای نام Raven.Client را دریافت میکند. پس از پایان کوئری میتوان از این خروجی جهت نمایش اطلاعات آماری کوئری استفاده کرد.
امکانات ویژه فضای نام Raven.Client.Linq
یک سری متد الحاقی خاص جهت تهیه سادهتر کوئریهای LINQ در فضای نام Raven.Client.Linq قرار دارند که پس از تعریف آن قابل دسترسی خواهند بود:
برای مثال در اینجا متد الحاقی جدید In را مشاهده میکنید که شبیه به کوئری SQL ذیل در دنیای بانکهای اطلاعاتی رابطهای عمل میکند:
اتصال به RavenDB با استفاده از برنامه معروف LINQPad
اگر علاقمند باشید که کوئریهای خود را در محیط برنامه معروف LINQPad نیز آزمایش کنید، درایور مخصوص RavenDB آنرا از آدرس ذیل میتوانید دریافت نمائید:
امکان استفاده از LINQ در RavenDB
RavenDB از LINQ جهت کوئری نویسی پشتیبانی میکند. برای استفاده از آن، در ادامه مطلب اول، ابتدا سرور RavenDB را اجرا نموده و سپس برنامه کنسول را به نحو ذیل تغییر دهید:
using System; using System.Linq; using Raven.Client.Document; using RavenDBSample01.Models; namespace RavenDBSample01 { class Program { static void Main(string[] args) { using (var store = new DocumentStore { Url = "http://localhost:8080" }.Initialize()) { using (var session = store.OpenSession()) { var questions = session.Query<Question>().Where(x => x.Title.StartsWith("Raven")); foreach (var question in questions) { Console.WriteLine(question.Title); } } } } } }
Available commands: cls, reset, gc, q Request # 1: GET - 179 ms - <system> - 404 - /docs/Raven/Replication/Destinations Request # 2: GET - 3,818 ms - <system> - 200 - /indexes/dynamic/Questions?&query=Title%3ARaven*&pageSize=128 Query: Title:Raven* Time: 3,494 ms Index: Auto/Questions/ByTitle Results: 2 returned out of 2 total.
var questions = session.Query<Question>().Where(x => x.Title.StartsWith("Raven")).Take(128);
ارتباط Lucene.NET و RavenDB
کل LINQ API تهیه شده در RavenDB یک محصور کننده امکانات Lucene.NET است. اگر پیشتر با Lucene.NET کار کرده باشید، در خروجی حالت دیباگ کنسول سرور فوق، سطر «Query: Title:Raven*» آشنا به نظر خواهد رسید. دقیقا کوئری LINQ نوشته شده به یک کوئری با Syntax مخصوص Lucene.NET ترجمه شدهاست. برای نمونه اگر علاقمند باشید که مستقیما کوئریهای خاص لوسین را در RavenDB اجرا کنید، از Syntax ذیل میتوان استفاده کرد:
var questions = session.Advanced.LuceneQuery<Question>().Where("Title:Raven*").ToList();
var questions = session.Advanced.LuceneQuery<Question>().WhereStartsWith(x => x.Title, "Raven").ToList();
استفاده مجدد از کوئریها در RavenDB
در RavenDB، متد Query به صورت immutable تعریف شده است و متد LuceneQuery حالت mutable دارد (ترکیبات آن نیز یک وهله است).
یک مثال:
var query = session.Query<User>().Where(x => x.Name.StartsWith("A")); var ageQuery = query.Where(x => x.Age > 21); var eyeQuery = query.Where(x => x.EyeColor == "blue");
query - Name:A* ageQuery - (Name:A*) AND (Age_Range:{Ix21 TO NULL}) eyeQuery - (Name:A*) AND (EyeColor:blue)
در ادامه اگر همین سه کوئری فوق را با فرمت LuceneQuery تهیه کنیم، به عبارات ذیل خواهیم رسید:
var luceneQuery = session.Advanced.LuceneQuery<User>().WhereStartsWith(x => x.Name, "A"); var ageLuceneQuery = luceneQuery.WhereGreaterThan(x => x.Age, 21); var eyeLuceneQuery = luceneQuery.WhereEquals(x => x.EyeColor, "blue");
luceneQuery - Name:A* ageLuceneQuery - Name:A* Age_Range:{Ix21 TO NULL} eyeLuceneQuery - Name:A* Age_Range:{Ix21 TO NULL} EyeColor:blue
And و Or شدن کوئریهای ترکیبی در RavenDB
در مثال استفاده مجدد از کوئریها، زمانیکه از Where استفاده شد، بین عبارات حاصل AND قرار گرفته است. این مورد را به نحو ذیل میتوان تنظیم کرد و مثلا به OR تغییر داد:
session.Advanced.LuceneQuery<User>().UsingDefaultOperator(QueryOperator.And);
صفحه بندی اطلاعات در RavenDB
در ابتدای بحث عنوان شد که کوئری LINQ اجرا شده در RavenDB، یک Take مخفی و پیش فرض تنظیم شده به 128 آیتم را دارد. اکنون سؤال این خواهد بود که چگونه میتوان اطلاعات را به صورت صفحه بندی شده، بر اساس شماره صفحه خاصی نمایش داد.
using System; using System.Linq; using Raven.Client.Document; using RavenDBSample01.Models; namespace RavenDBSample01 { class Program { static void Main(string[] args) { using (var store = new DocumentStore { Url = "http://localhost:8080" }.Initialize()) { using (var session = store.OpenSession()) { int pageNumber = 0; int resultsPerPage = 2; var questions = session.Query<Question>() .Where(x => x.Title.StartsWith("Raven")) .Skip(pageNumber * resultsPerPage) .Take(resultsPerPage); foreach (var question in questions) { Console.WriteLine(question.Title); } } } } } }
دریافت اطلاعات آماری کوئری اجرا شده
در RavenDB امکان دریافت یک سری اطلاعات آماری از کوئری اجرا شده نیز وجود دارد؛ برای مثال یک کوئری چند ثانیه طول کشیده است، چه تعدادی رکورد را بازگشت داده است و امثال آن. برای پیاده سازی آن، نیاز است از متد الحاقی Statistics به نحو ذیل استفاده کرد:
using System; using System.Linq; using Raven.Client.Document; using RavenDBSample01.Models; using Raven.Client; namespace RavenDBSample01 { class Program { static void Main(string[] args) { using (var store = new DocumentStore { Url = "http://localhost:8080" }.Initialize()) { using (var session = store.OpenSession()) { int pageNumber = 0; int resultsPerPage = 2; RavenQueryStatistics stats; var questions = session.Query<Question>() .Statistics(out stats) .Where(x => x.Title.StartsWith("Raven")) .Skip(pageNumber * resultsPerPage) .Take(resultsPerPage); foreach (var question in questions) { Console.WriteLine(question.Title); } Console.WriteLine("TotalResults: {0}", stats.TotalResults); } } } } }
امکانات ویژه فضای نام Raven.Client.Linq
یک سری متد الحاقی خاص جهت تهیه سادهتر کوئریهای LINQ در فضای نام Raven.Client.Linq قرار دارند که پس از تعریف آن قابل دسترسی خواهند بود:
var list = session.Query<Question>().Where(q => q.By.In<string>(arrayOfUsers))).ToArray()
SELECT * FROM tbl WHERE data IN (1, 2, 3)
اتصال به RavenDB با استفاده از برنامه معروف LINQPad
اگر علاقمند باشید که کوئریهای خود را در محیط برنامه معروف LINQPad نیز آزمایش کنید، درایور مخصوص RavenDB آنرا از آدرس ذیل میتوانید دریافت نمائید:
- سرور 32 بیتی نمیتونه از حداکثر میزان RAM سرور شما (بیشتر از 2GB) نهایت استفاده رو انجام بده. تمهیداتی هم در این زمینه هست ولی ... بهتره به یک سرور 64 بیتی کوچ کنید. بدون این تمهیدات، میزان حافظه مهیای جهت یک پروسه 32 بیتی به اندازه address space آن یعنی 2GB محدود است.
- همچنین باید کش کردن اطلاعات رو فعال کنید و اجازه بدید IIS بجای برنامه این مسایل رو راسا مدیریت کنه؛ یا از یک کش سرور مجزا استفاده کنید.
- همچنین باید کش کردن اطلاعات رو فعال کنید و اجازه بدید IIS بجای برنامه این مسایل رو راسا مدیریت کنه؛ یا از یک کش سرور مجزا استفاده کنید.
نظرات مطالب
Senior Developer به چه کسی گفته می شود؟
اکثر این موارد به خاطر داشتن اعتماد به نفس بیش از اندازه است. یا توجه به رزومه شخص، به راحتی میتوان حدس زد که فرد آیا واقعا در جایگاه Senior Developer است یا نه؟دونستن این مطلب که جایگاه شما در بین برنامه نویسان کجاست از این نظر حائز اهمیت است که تفاوت بین حقوق دریافتی Software Developer و Senior Developer تقریبا زیاد است و قابل چشم پوشی نیست.
نظرات مطالب
بررسی علت CPU Usage بالای برنامه در حال اجرا
سلام، نه. تمام سرویسها و تمام برنامهها هم در نهایت یک پروسه در ویندوز هستند. شما در کد بالا بجای MyApp ، نام پروسه دات نتی مورد نظر را قرار دهید (حتی اگر پروسه مثل w3wp باشد (یعنی یک سری برنامه دات نتی رو هاست کرده) هم کار میکند). سپس میتونید شروع به دیباگ کنید. البته قسمت اندازه گیری KernelTime + UserTime را که ذکر شد خودتون باید لحاظ کنید. به این ترتیب ( به کمک مرتب سازی بر اساس KernelTime + UserTime) میتونید نام متدی که الان سبب بروز این CPU usage بالا شده رو دقیقا تشخیص بدید.
ممنون از مطالب مفید شما واقعا بر روی لبه تکنولوژی های دات نت قدم بر می دارید. در مورد COLLECTION های OBSERVABLE این نکته هم حائز اهمیت است که این لیست ها دقیقا به اندازه نیاز کاربر در UI برنامه مقادیر را در حافظه لود کرده و اصطلاحا می توان با بکاربری این COLLECTIONها در یک کنترل خاصیت LazyLoading به کنترل بخشید.