مطالب دوره‌ها
کوئری نویسی مقدماتی در RavenDB
با شروع کوئری نویسی مقدماتی در RavenDB، در قسمت اول این مباحث، توسط فراخوانی متد Load یک سشن، آشنا شدید. در ادامه مباحث تکمیلی آن‌را مرور خواهیم کرد.

امکان استفاده از 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);
                    }
                }
            }
        }
    }
}
در RavenDB برای دسترسی به امکانات LINQ، کار با متد Query یک سشن آغاز می‌شود و پس از آن، امکان استفاده از متدهای متداول LINQ مانند مثال فوق وجود خواهد داشت. البته بدیهی است مباحثی مانند JOIN و امثال آن در یک بانک اطلاعاتی NoSQL پشتیبانی نمی‌شود. ضمنا باید درنظر داشت که مبحث safe by default در اینجا نیز اعمال می‌شود. برای مثال اگر به کنسول سرور RavenDB که در حال اجرا است مراجعه کنید، یک چنین خروجی را حین اجرای مثال فوق می‌توان مشاهده کرد که در آن pageSize پیش فرضی اعمال شده است:
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.
یعنی در عمل کوئری‌را که اجرا کرده است، شبیه به کوئری ذیل می‌باشد و یک Take پیش فرض بر روی آن اعمال شده است:
var questions = session.Query<Question>().Where(x => x.Title.StartsWith("Raven")).Take(128);
علت این مساله نیز به تصمیم نویسنده اصلی آن بر می‌گردد؛ ایشان پیش از شروع به تهیه 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 ذیل می‌توان استفاده کرد:
var questions = session.Advanced.LuceneQuery<Question>().Where("Title:Raven*").ToList();
و یا اگر علاقمند به حفظ کردن Syntax خاص لوسین نیستید، یک سری متد الحاقی خاص نیز در اینجا برای LuceneQuery تدارک دیده شده است. برای مثال کوئری رشته‌ای فوق، معادل کوئری strongly typed ذیل است:
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)
به این معنا که زمانیکه به eyeQuery رسیدیم، نتیجه ageQuery با آن ترکیب نمی‌شود؛ چون متد Query از نوع immutable است.
در ادامه اگر همین سه کوئری فوق را با فرمت 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
همانطور که مشاهده می‌کنید، کوئری سوم، عبارت کوئری دوم را نیز به همراه دارد؛ این مورد دقیقا مفهوم اشیاء mutable یا تک وهله‌ای است مانند LuceneQuery در اینجا.


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، کافی است از متدهای Skip و Take بر اساس محاسباتی که مشاهده می‌کنید، استفاده گردد.


دریافت اطلاعات آماری کوئری اجرا شده

در 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);
                }
            }
        }
    }
}
متد الحاقی Statistics پس از متد Query که نقطه آغازین نوشتن کوئری‌های LINQ است، فراخوانی شده و یک پارامتر out از نوع RavenQueryStatistics تعریف شده در فضای نام Raven.Client را دریافت می‌کند. پس از پایان کوئری می‌توان از این خروجی جهت نمایش اطلاعات آماری کوئری استفاده کرد.


امکانات ویژه فضای نام Raven.Client.Linq

یک سری متد الحاقی خاص جهت تهیه ساده‌تر کوئری‌های LINQ در فضای نام Raven.Client.Linq قرار دارند که پس از تعریف آن قابل دسترسی خواهند بود:
var list = session.Query<Question>().Where(q => q.By.In<string>(arrayOfUsers))).ToArray()
برای مثال در اینجا متد الحاقی جدید In را مشاهده می‌کنید که شبیه به کوئری SQL ذیل در دنیای بانک‌های اطلاعاتی رابطه‌ای عمل می‌کند:
 SELECT * FROM tbl WHERE data IN (1, 2, 3)

اتصال به RavenDB با استفاده از برنامه معروف LINQPad

اگر علاقمند باشید که کوئری‌های خود را در محیط برنامه معروف LINQPad نیز آزمایش کنید، درایور مخصوص RavenDB آن‌را از آدرس ذیل می‌توانید دریافت نمائید:
مطالب
WPF و قالب‌هایی جهت کنترل DataGrid

در مورد معرفی WPF Extended toolkit چندی قبل مطلبی منتشر شد. در ادامه این بی مهری‌ها (!) می‌توان به عدم به روز رسانی قالب‌های ارائه شده برای WPF اشاره کرد. در WPF4 ، کنترل DataGrid از WPF toolkit به مجموعه‌ی کنترل‌های اصلی WPF منتقل شده است، اما قالب‌های منتشر شده‌ی آن جهت لحاظ کردن این مورد به روز نشده‌اند. یعنی اگر برای مثال یکی از قالب‌های موجود را به برنامه خود اعمال کنید و سپس DataGrid را بر روی فرم قرار دهید، وصله‌ی ناهماهنگی را مشاهده خواهید نمود. این مشکلات در Silverlight وجود ندارند و قالب‌های ارائه شده‌ی برای آن به روز بوده و همچنین روز به روز هم تعدادشان بیشتر می‌شوند.
اما باز هم نمی‌توان ایراد گرفت چون کار ارائه شده سورس باز است. به عبارتی اگر مایکروسافت این قالب‌ها را به روز نکرده، خوب، لطفا خود شما وقت بگذارید و این کار را انجام داده و سپس یک patch ارائه دهید. ایرادی دارد؟!
برای این منظور پروژه‌ای در سایت CodePlex ایجاد شده است و تنها به پوشش دات نت سه و نیم و دیتاگرید متعلق به WPF Toolkit پرداخته است :


اگر علاقمند باشید که از دیتاگرید بومی دات نت 4 استفاده کنید می‌توانید از این patch استفاده کنید.

دوره‌ها
طراحی یک فریم ورک برای کار با WPF و EF Code First توسط الگوی MVVM
در این دوره، قالب تهیه یک پروژه جدید WPF مبتنی بر EF Code first را دریافت خواهید کرد که دارای این مشخصات است:

1- اعتبارسنجی یکپارچه با EF Code first

2- دارای سیستم راهبری (Navigation) بین صفحات با قابلیت تزریق خودکار وابستگی‌ها توسط کتابخانه StructureMap
3- به همراه مباحثی مانند تعریف کاربران، تعریف سطوح دسترسی و همچنین راهبری بین صفحات برنامه با درنظر گرفتن این مسایل به کمک تنها افزودن یک ویژگی به نام PageAuthorization به ابتدای تعریف کلاس یک صفحه



4- دارای سیستم خودکار پیغام دهی به کاربر در صورتیکه قصد حرکت به صفحه‌ای دیگر را داشته باشد؛ اما تغییرات صفحه جاری ذخیره نشده‌اند.


5- قالب پروژه جدید تدارک دیده شده، به صورت خودکار لایه بندی‌های برنامه را تدارک خواهد دید (شامل DataLayer، DomainClasses، ServiceLayer و غیره)
6- به همراه سیستم DbContext یکپارچه با مباحثی مانند یکسان سازی ی و ک در برنامه به صورت خودکار و نمایش مشکلات اعتبارسنجی داده‌ها به کاربر بدون نیازی به کد نویسی اضافه.
7- این قالب پروژه با کتابخانه‌های زیر یکپارچه است:
Entity Framework Code First
Fody (جهت اعمال مسایل AOP برای کاهش تدارک کدهای INotifyPropertyChanged در برنامه)
MahApps.Metro (برای نمایش قالب مترو سازگار با دات نت 4)
Microsoft.SqlServer.Compact.4 (بانک اطلاعاتی پیش فرض برنامه دسکتاپ تدارک دیده شده)
MvvmLight (پایه مباحث MVVM بکارگرفته شده در برنامه)
StructureMap (جهت پیاده سازی مباحث تزریق وابستگی‌ها در برنامه)
نظرات مطالب
فعال سازی عملیات CRUD در Kendo UI Grid
- هرچندتا پروژه مربوط به kendo Ui که پیوست شده بود رو دانلود میکنم  نمیتونم اجراش کنم به خاطر نبود فایل به اسم  NuGet.exe   
- آیا امکانش هست که توی لیست نمایش چندتا فیلد نمایش داده بشه ولی توی ایجاد یه فیلد‌های دیگه هم اضافه بشه که نیاز به نمایش در ویو نباشد؟ 
- آیا امکانش هست یه فیلد که مثلا عکس وقتی که میخوایم یه رکورد جدید بسازیم، جوری سفارشی سازی بشه که عکس دریافت کنه و یا حالا مثلا چک باکس و کمبو باکس.
نمایش دادن عکس توی ویو رو مشکلی نداشتم و با استفاده از template تونستم درست کنم. ولی موقع ویرایش و ایجاد نمیدونم چطوری باید انجام بدم.
مطالب
نمایش یک فایل PDF پویا در یک iframe در ASP.NET
عموما در برنامه‌های وب برای نمایش فایل‌های پویای باینری تولید شده، یا ابتدا آن‌ها را بر روی سخت دیسک ذخیره کرده و مسیر نهایی را به نحوی به کاربر نمایش می‌دهند و یا فایل را بدون ذخیره سازی، در مرورگر کاربر اصطلاحا Flush می‌کنند. حالت Flush سبب نمایش صفحه دیالوگ ذخیره سازی فایل گردیده و در همینجا Response خاتمه خواهد یافت.
برای نمونه در اینجا توسط متد inMemoryFile، یک فایل PDF در حافظه تشکیل شده و سپس به صورت یک Byte Array بازگشت داده می‌شود. در ادامه کار، این اطلاعات در مرورگر کاربر Flush خواهد شد:
using System.IO;
using System.Net.Mime;
using System.Web;

namespace WebApplication
{
    public class PdfHandler : IHttpHandler
    {
        private static byte[] inMemoryFile()
        {
            //تولید پویای فایل در حافظه و یا حتی خواندن از یک نمونه موجود
            return File.ReadAllBytes(@"D:\path\DynamicCrosstabSampleRpt.pdf");
        }

        public void ProcessRequest(HttpContext context)
        {
            var pdf = inMemoryFile();

            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            context.Response.ContentType = MediaTypeNames.Application.Pdf;
            context.Response.AddHeader("Content-Length", pdf.Length.ToString());
            context.Response.AddHeader("content-disposition", "attachment;filename=test.pdf");
            context.Response.Buffer = true;
            context.Response.Clear();
            context.Response.OutputStream.Write(pdf, 0, pdf.Length);
            context.Response.OutputStream.Flush();
            context.Response.OutputStream.Close();
            context.Response.End();
        }

        public bool IsReusable
        {
            get  {  return false;  }
        }
    }
}
و برای نمایش آن در یک iframe در صفحه:
 <iframe width="100%" src="PdfHandler.ashx" height="200px"></iframe>
نتیجه کار، نمایش صفحه دیالوگ ذخیره سازی فایل به کاربر است:


سؤال: فرض کنید Adobe reader بر روی سیستم نصب است و مرورگر با استفاده از Active-X آن می‌تواند این نوع فایل‌ها را نمایش دهد. آیا راهی وجود دارد تا بجای نمایش save popup dialog، این فایل توسط مرورگر نمایش داده شود؟
پاسخ: بلی. در کدهای فوق تنها کافی است یک سطر آن تغییر کند:
 Response.AddHeader("content-disposition", "inline;filename=test.pdf");
در اینجا تنها نحوه مقدار دهی content-disposition تفاوت کرده است. حالت attachment سبب نمایش save popup dialog می‌شود و مقدار inline، فایل را در مرورگر نمایش خواهد داد.
اینبار اگر برنامه را اجرا کنیم، iframe ایی که به PdfHandler.ashx اشاره می‌کند، فایل PDF را در صفحه نمایش می‌دهد.
بازخوردهای دوره
متدهای async تقلبی
در این مثال عملیات تبدیل sync به async درست انجام شده ؟
کد sync :
        public string AESEncrypt(string Text)
        {
            byte[] dataToEncrypt = Encoding.UTF8.GetBytes(Text);
            byte[] key = Convert.FromBase64String(AES_Key);
            byte[] iv = Convert.FromBase64String(AES_Iv);

            using (var aes = new AesManaged())
            {
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;
                aes.Key = key;
                aes.IV = iv;
                using (var ms = new MemoryStream())
                {
                    using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(dataToEncrypt, 0, dataToEncrypt.Length);
                        cs.FlushFinalBlock();
                        return Convert.ToBase64String(ms.ToArray());
                    }
                }
            }
        }
کد async :
        public async Task<string> AESEncryptAsync(string Text)
        {
            byte[] dataToEncrypt = Encoding.UTF8.GetBytes(Text);
            byte[] key = Convert.FromBase64String(AES_Key);
            byte[] iv = Convert.FromBase64String(AES_Iv);

            using (var aes = new AesManaged())
            {
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;
                aes.Key = key;
                aes.IV = iv;
                using (var ms = new MemoryStream())
                {
                    using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        await cs.WriteAsync(dataToEncrypt, 0, dataToEncrypt.Length);
                        cs.FlushFinalBlock();
                        return Convert.ToBase64String(ms.ToArray());
                    }
                }
            }
        }
من این دو متد رو از نظر "زمان اجرا" ، "ترد مشغول" ، "هسته cpu مشغول" در asp.net mvc تست گرفتم {1000 بار فرخوانی} .
در این تست متد async  هر بار زمان بیشتری برای محاسبه برد (که خوب چون ترد جدید باز میکنه طبیعی هست) و در ترد‌ها و هسته‌های مختلف cpu اجرا میشد ، که این هم طبیعی هست ، طرف نظر از این که طبق مقاله حاضر کلا اینجا async نباید اجرا بشه چون خارج از مرز سیستم نیست ولی عملیات async کامل انجام میشه (فکر میکنم).
اما متد sync که بررسی شد ، خروجی به صورتی بود که آن هم هر بار در ترد و هسته مختلف سی پی یو اجرا میشد ! سوال دلیل چیست ؟
مگر تفاوت sync و async  دقیقا به اجرای آنها نیست ؟ پس چرا در هر دو حالت ما اجرا‌های یکسانی داریم ؟
ممنونم.
نظرات اشتراک‌ها
TFS یا GIT؟ از کدامیک استفاده کنم؟
TFS یک ابزار همه چی تمام است که طبق اصول مهندسی نرم افزار کلیه فرآیندهای تولید نرم افزار را پشنیبانی می‌کند. در واقع TFS تنها یک سورس کنترل نیست و گسترده‌تر از این حرفهاست.
پیشنهاد می‌کنم در پروژه‌های تجاری که تیمی کار می‌کنید حتماً از TFS استفاده کنید مخصوصاً اگر Scrum باز هستید از آن لذت خواهید برد.
سازگاری با بسته آفیس و MS Project و همچنین پیاده سازی کامل Scrum + سازگاری با Sharepoint و بسیاری موارد دیگر TFS را یک ابزار حرفه ای برای مدیریت پروژه کرده است.
اشتراک‌ها
عدم پشتیبانی W2G از ویژوال استودیو
قابلیت Windwos to Go که در ویندوز 8 نسخه Enterprise وجود دارد توانایی هایی جالبی را ارائه میکند که شما می‌توانید ویندوز 8 را به صورت همراه در یک فلش نصب کنید. اما متاسفانه اگر فلش شما از نوع فلش‌های تایید شده توسط مایکروسافت نباشه نمی‌توان ویژوال استودیو و MS-SQL Server را روی آن نصب کرد. پیشنهاد: بنده به این نکته دقت نکردم و حالا دنبال راه حلی برای نصب ویژوال استودیو برروی فلش خود هستم که احتمالا به پرتابل کردن آن دست بزنم، لذا پیشنهاد می‌کنم هرکسی بخواهد این کار را انجام دهد حتما فلش خود را از بین لیست فلش‌های تایید شده مایکروسافت انتخاب کند(این لیست را در لینک مرجع که در لینک این پست قرار دارد می‌توان یافت). موفق باشید.
عدم پشتیبانی W2G از ویژوال استودیو
نظرات مطالب
VS Code برای توسعه دهندگان ASP.NET Core - قسمت دوم - ایجاد و اجرای اولین برنامه
لینک بالا رو دانلود و نصب کردم ولی جواب نداد. که لاگش میگه شما 2 تا msbuild تو سیستم نصب داری و بنظر باید مسیر msbuild رو توی vscode تنظیم کرد.
 Located 2 MSBuild instance(s)
            1: Visual Studio Enterprise 2019 16.10.31424.327 16.10.2 - "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin"
            2: StandAlone 17.0.0 - "c:\Users\meysams\.vscode\extensions\ms-dotnettools.csharp-1.23.13\.omnisharp\1.37.12\.msbuild\Current\Bin"
جایی برای تنظیمش وجود داره ؟ یعنی باید تو تنظیمات workspace و یا user قابل انجامه؟