نظرات مطالب
استخراج تمام XPathهای یک محتوای HTMLایی به کمک کتابخانه HtmlAgilityPack
استفاده کردم حق با شما بود سایت کامل لود می‌کنه اما در خواست‌های Ajax پردازش نمی‌شه، یعنی این کامل شدن لود ربطی به درخواست‌های ای جکس نداره، خروجی ای جکسی وجود داره توی صفحه ولی در سورس html وجود ندارد
مطالب
Zen Coding در Visual Studio 2012
Zen Coding روشی سریع برای نوشتن کدهای HTML با استفاده از گرامر selector‌های CSS است. این روش برای اولین بار در سال 2009 توسط Sergey Chikuyonok  معرفی شد و در طول این مدت، تبدیل به روشی عالی برای نوشتن کد‌های HTML تکراری و یکنواخت شده است.
برای استفاده از این روش در ویژوال استادیو 2012، احتیاج به نصب افزونه‌ی محبوب و پر طرفدار Web Essentials است. این افزونه که توسط  Mads Kristensen  توسعه پیدا کرده است،؛ علاوه بر Zen Coding ، ویژگی‌های بسیار زیادی در زمینه‌ی توسعه وب به ویژوال استادیو اضافه می‌کند و توصیه‌ی اکید دارم که حتما این افزونه‌ی فوق العاده را نصب کنید و از امکانات دیگر آن بهره برید. 
گرامر Zen Coding، یک سری عملگر دارد که مرجعی از همه‌ی آن‌ها را در زیر مشاهده می‌کنید.
#   ایجاد خاصیت id 
.    ایجاد خاصیت class
[]  ایجاد خاصیت دلخواه
<  ایجاد عنصر فرزند
+  ایجاد عنصر برادر یا خواهر (Sibling)
^  رجوع به والد
*  تکثیر کننده‌ی عنصر است. هر عنصری را می‌تواند n بار تکرار کند
$  با شمارنده جایگزین می‌شود
$$  با شمارنده دارای padding جایگزین می‌شود
{}  متن داخل آکولاد را در عنصر مورد نظر قرار می‌دهد
()  امکان گروه بندی عبارات را می‌دهد.
lorem  اطلاعات ساختگی برای آزمایش برنامه ایجاد می‌کند
مشخص است که بسیاری از عملگر‌ها با selector‌های CSS یکسان اند. قبل از اینکه به بررسی دقیق‌تر هر کدام از این عملگر‌ها بپردازیم، دو مثال را با هم بررسی می‌کنیم تا به قدرت آن پی ببریم.
 پس از نصب افزونه‌ی Web Essentials ، یک صفحه‌ی html یا cshtml و یا هر قسمتی که بتوان در آن کد html  نوشت را گشوده و سپس کد زیر را در آن بنویسید و در نهایت کلید Tab را فشار دهید. 
table#tblUsers.table.table-striped.table-bordered.table-hover>thead>tr>th{row}+th{Name}+th{Last Name}+th{Operations}^^tbody>tr*6>(td{row}+td{FirstName}+td{LastName}+td>button.btn.btn-primary{Edit}+button.btn.btn-danger{Delete})
پس از فشردن کلید Tab، خروجی زیر را مشاهده خواهید کرد: 
<table id="tblUsers" class="table table-striped table-bordered table-hover">
        <thead>
            <tr>
                <th>row</th>
                <th>Name</th>
                <th>Last Name</th>
                <th>Operations</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>row</td>
                <td>FirstName</td>
                <td>LastName</td>
                <td>
                    <button>Edit</button>
                    <button>Delete</button></td>
            </tr>
            <tr>
                <td>row</td>
                <td>FirstName</td>
                <td>LastName</td>
                <td>
                    <button class="btn btn-primary">Edit</button>
                    <button class="btn btn-danger">Delete</button></td>
            </tr>
            <tr>
                <td>row</td>
                <td>FirstName</td>
                <td>LastName</td>
                <td>
                    <button class="btn btn-primary">Edit</button>
                    <button class="btn btn-danger">Delete</button></td>
            </tr>
            <tr>
                <td>row</td>
                <td>FirstName</td>
                <td>LastName</td>
                <td>
                    <button class="btn btn-primary">Edit</button>
                    <button class="btn btn-danger">Delete</button></td>
            </tr>
            <tr>
                <td>row</td>
                <td>FirstName</td>
                <td>LastName</td>
                <td>
                    <button class="btn btn-primary">Edit</button>
                    <button class="btn btn-danger">Delete</button></td>
            </tr>
            <tr>
                <td>row</td>
                <td>FirstName</td>
                <td>LastName</td>
                <td>
                    <button class="btn btn-primary">Edit</button>
                    <button class="btn btn-danger">Delete</button></td>
            </tr>
        </tbody>
    </table>
به نظر پیچیده می‌آید و حتما با خودتان می‌گویید که همان HTML خام را بنویسم، راحت‌تر هستم. شاید الان برای شما پیچیده به نظر آید ولی اگر تا انتهای این مقاله را مطالعه کنید، این قول را به شما می‌دهم که برای شما نوشتن این گونه کدها بسیار ساده خواهد بود و بعید به نظر می‌آید که شما دیگر HTML را به شیوه‌های قدیمی بنویسید، همان طور که بنده نیز با چند بار آزمون و خطا، توانستم کد بالا را بنویسیم.
یک مثال دیگر می‌تواند به این شکل باشد:
ul>li[ng-repeat="user in Users"]>p[ng-bind="{{user.UserName}}"]+a{Details}
که خروجی زیر را در بر دارد:
<ul>
        <li ng-repeat="user in Users">
            <p ng-bind="{{user.UserName}}"></p>
            <a href="">Details</a>
        </li>
</ul>
از کدهای بالا این نتیجه را می‌توان گرفت که Zen Coding؛ بسیار انعطاف پذیر، ساده و قدرتمند است.
در ادامه نگاهی دقیق‌تر به عملگرهای استفاده شده در کدهای بالا می‌اندازیم.
 
خواص id (#) و class (.)
به مانند گرامر CSS، به راحتی می‌توانید id یا class خاصی را به عنصر مورد نظر نسبت دهید:
<!-- Type this -->
div.container

<!-- Creates this -->
<div class="container"></div>
<!-- Type this -->
ul#userList

<!-- Creates this -->
<ul id="userList"></ul>
<!-- Type this -->
div.container#wrapper

<!-- Creates this -->
<div class="container" id="wrapper"></div>
<!-- Type this -->
button.btn.btn-primary

<!-- Creates this -->
<button class="btn btn-primary"></button>
نکته: اگر شما عنصر مورد نظری را که می‌خواهید بر روی آن کلاس و یا آیدی را اعمال کنید، مشخص نکنید به طور پیش فرض، Zen Coding آن عنصر را div در نظر می‌گیرد.
مثال:
<!-- Type this -->
.container

<!-- Creates this -->
<div class="container"></div>
< عناصر فرزند (Child Elements)
با استفاده از عملگر (<)، عنصر مورد نظر و عناصر فرزند درون آن را می‌توانید ایجاد کنید.
<!-- Type this -->
div.container>div#header

<!-- Creates this -->
<div class="container">
        <div id="header"></div>
</div>
<!-- Type this -->
div.navbar>div.navbar-inner>ul.navbar

<!-- Creates this -->
<div class="navbar">
        <div class="navbar-inner">
            <ul class="navbar"></ul>
        </div>
</div>

[] خواص سفارشی (Custom Attributes)
با نوشتن خاصیت سفارشی خود درون براکت []، به راحت می‌توانید آن عنصر و خاصیت سفارشی مورد نظر خود را ایجاد کنید.
<!-- Type this -->
div[title]

<!-- Creates this -->
<div title=""></div>
و یا  اگر بخواهید چندین خاصیت را به صورت همزمان اعمال کنید:
<!-- Type this -->
input[type="text" placeholder="First Name"]

<!-- Creates this -->
<input type="text" value="" placeholder="First Name" />
<!-- Type this -->
ul[ng-repeat="user in Users"]>li[ng-model="user.FirstName"]

<!-- Creates this -->
<ul ng-repeat="user in Users">
        <li ng-model="user.FirstName"></li>
</ul>

{} متن (Text)

این عملگر، متن مورد نظر شما را داخل عنصر قرار می‌دهد.

<!-- Type this -->
div>h1{My Title}

<!-- Creates this -->
<div>
      <h1>My Title</h1>
</div>

+ عناصر خواهر و برادر (Siblings Elements)
با قرار دادن عملگر + ، عناصر خواهر و برادر را ایجاد کنید.
<!-- Type this -->
form>input[type="text"]+input[type="checkbox"]

<!-- Creates this -->
<form>
        <input type="text" value="" />
        <input type="checkbox" value="" />
</form>
<!-- Type this -->
div#header>img+h1{Title}

<!-- Creates this -->
<div id="header">
        <img src="" alt="" />
        <h1>Title</h1>
</div>

 ^  بالا رفتن از عناصر (Climbing Elements)
این عملگر تقریبا برعکس عملگر < عمل می‌کند. با عملگر ^ شما می‌توانید به والد یک عنصر تا هر تعداد سطحی که بخواهید برسید. اگر شما به طور مثال بخواهید که دو سطح به والد بالاتر برگردید، باید از ^^ استفاده کنید. 
<!-- Type this -->
table>thead>tr>th{row}^^tbody>tr>td{row1}

<!-- Creates this -->
<table>
        <thead>
            <tr>
                <th>row</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>row1</td>
            </tr>
        </tbody>
</table>

* تکثیر عناصر (Elements Multiplication)
با عملگر *، هر تعداد عنصر را که می‌خواهید تکثیر کنید.
<!-- Type this -->
ul>li*3>p{Hello}

<!-- Creates this -->
<ul>
        <li>
            <p>
                Hello
            </p>
        </li>
        <li>
            <p>
                Hello
            </p>
        </li>
        <li>
            <p>
                Hello
            </p>
        </li>
</ul>

<!-- Type this -->
(div.container>h1{title}+div.content{Some Text Here})*2

<!-- Creates this -->
<div class="container">
        <h1>title</h1>
        <div class="content">
            Some Text Here
        </div>
    </div>
    <div class="container">
        <h1>title</h1>
        <div class="content">
            Some Text Here
        </div>
</div>
$ شماره گذاری آیتم‌ها (Item Numbering)
هنگامی که شما با کمک *، از روی یک عنصر n عنصر را تکثیر می‌کنید، می‌توانید با استفاده ازعملگر $ ، به عدد متناظر با آن حلقه‌ی تکرار دست پیدا کنید.
<!-- Type this -->
ul>li*2>p{item $}

<!-- Creates this -->
<ul>
        <li>
            <p>
                item 1
            </p>
        </li>
        <li>
            <p>
                item 2
            </p>
        </li>
</ul>
نکته: استفاده از چند عملگر $، باعث قرارگرفتن همان تعداد صفر، پیش از عدد می‌شود.
<!-- Type this -->
ul>li*2>p{item $$$}

<!-- Creates this -->
<ul>
        <li>
            <p>
                item 001
            </p>
        </li>
        <li>
            <p>
                item 002
            </p>
        </li>
</ul>
() گروه بندی (Grouping)
گروه بندی یکی از امکانات فوق العاده‌ی Zen Coding است که به شما امکان نوشتن عبارات پیچیده را می‌دهد. کاربرد آن در مثال زیر کاملا واضح است، و به راحتی با یک عبارت، کل ساختار Dom را پیاده سازی شده است. 
<!-- Type this -->
div>(header>div)+section>(ul>li*2>a)+footer>(div>span)

<!-- Creates this -->
<div>
        <header>
            <div></div>
        </header>
        <section>
            <ul>
                <li><a href=""></a></li>
                <li><a href=""></a></li>
            </ul>
            <footer>
                <div>
                    <span></span>
                </div>
            </footer>
        </section>
</div>
Lorem داده‌های ساختگی ( Dummy Data) 
حتما بارها و بارها این اتفاق افتاده است که برای تست قالب برنامه‌ی خود، آن را با داده‌های ساختگی پر کرده باشید. معمولا شما این کار را به صورت دستی انجام می‌دادید. اما این بار عبارت lorem را نوشته و کلید Tab را فشار دهید. به طور پیش فرض برای شما 30 کلمه می‌نویسد. اگر تعداد مشخصی کلمه می‌خواهید کافی است تعداد آن را جلوی کلمه‌ی lorem بنویسید. برای مثال lorem4.
حتی از lorem می‌توانید در عبارات Zen Coding استفاده کنید: 
<!-- Type this -->
div>(h1>lorem5)+(h3>lorem3)

<!-- Creates this -->
<div>
      <h1>Lorem ipsum dolor sit amet.</h1>
      <h3>Lorem ipsum dolor.</h3>
</div>

مطالب
اتصال Node.js به SQL Server با استفاده از Edge.js
اگر خواسته باشید که با استفاده از Node.js به SQL Server متصل شوید، احتمالا متوجه شده‌اید ماژولی که مایکروسافت منتشر کرده است، ناقص بوده و به صورت پیش نمایش است که بسیاری از ویژگی‌ها و مسائل مهم، در آن در نظر گرفته نشده است.

یکی دیگر از ماژول‌هایی که امکان اتصال Node.js را به SQL Server ممکن می‌کند، Edge.js است. Edge.js یک ماژول Node.js است که امکان اجرای کدهای دات نت را در همان پروسه توسط Node.js فراهم می‌کند. این مسئله، توسعه دهندگان Node.js را قادر می‌سازد تا از فناوری‌هایی که به صورت سنتی استفاده‌ی از آنها سخت یا غیر ممکن بوده است را به راحتی استفاده کنند. برای نمونه:
  • SQL Server
  • Active Directory
  • Nuget packages
  • استفاده از سخت افزار کامپیوتر (مانند وب کم، میکروفن و چاپگر)


نصب Node.js

اگر Node.js را بر روی سیستم خود نصب ندارید، می‌توانید از اینجا آن را دانلود کنید. بعد از نصب برای اطمینان از کارکرد آن، command prompt را باز کرده و دستور زیر را تایپ کنید:

node -v
شما باید نسخه‌ی نصب شده‌ی Node.js را مشاهده کنید.

ایجاد پوشه پروژه

سپس پوشه‌ای را برای پروژه Node.js خود ایجاد کنید. مثلا با استفاده از command prompt و دستور زیر:

md \projects\node-edge-test1
cd \projects\node-edge-test1

نصب Edge.js

Node با استفاده از package manager خود دانلود و نصب ماژول‌ها را خیلی آسان کرده است. برای نصب، در command prompt عبارت زیر را تایپ کنید:

npm install edge
npm install edge-sql
فرمان اول باعث نصب Edge.js و دومین فرمان سبب نصب پشتیبانی از SQL Server می‌شود.

Hello World

ایجاد یک فایل متنی با نام server.js و نوشتن کد زیر در آن:
var edge = require('edge');

// The text in edge.func() is C# code
var helloWorld = edge.func('async (input) => { return input.ToString(); }');

helloWorld('Hello World!', function (error, result) {
    if (error) throw error;
    console.log(result);
});
حالا برای اجرای این Node.js application از طریق command prompt کافی است به صورت زیر عمل کنید:
node server.js
همانطور که مشاهده می‌کنید "!Hello World" در خروجی چاپ شد.

ایجاد پایگاه داده تست

در مثال‌های بعدی، نیاز به یک پایگاه داده داریم تا query‌ها را اجرا کنیم. در صورتی که SQL Server بر روی سیستم شما نصب نیست، می‌توانید نسخه‌ی رایگان آن را از اینجا دانلود و نصب کنید. همچنین SQL Management Studio Express را نیز نصب کنید.

  1. در SQL Management Studio، یک پایگاه داده را با نام node-test با تنظیمات پیش فرض ایجاد کنید.
  2. بر روی پایگاه داده node-test راست کلیک کرده و New Query را انتخاب کنید.
  3. اسکریپت زیر را copy کرده و در آنجا paste کنید، سپس بر روی Execute کلیک کنید.
IF EXISTS(SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('SampleUsers')) BEGIN; DROP TABLE SampleUsers; END; GO

CREATE TABLE SampleUsers ( Id INTEGER NOT NULL IDENTITY(1, 1), FirstName VARCHAR(255) NOT NULL, LastName VARCHAR(255) NOT NULL, Email VARCHAR(255) NOT NULL, CreateDate DATETIME NOT NULL DEFAULT(getdate()), PRIMARY KEY (Id) ); GO

INSERT INTO SampleUsers(FirstName,LastName,Email,CreateDate) VALUES('Orla','Sweeney','nunc@convallisincursus.ca','Apr 13, 2014');
INSERT INTO SampleUsers(FirstName,LastName,Email,CreateDate) VALUES('Zia','Pickett','porttitor.tellus.non@Duis.com','Aug 31, 2014');
INSERT INTO SampleUsers(FirstName,LastName,Email,CreateDate) VALUES('Justina','Ayala','neque.tellus.imperdiet@temporestac.com','Jul 28, 2014');
INSERT INTO SampleUsers(FirstName,LastName,Email,CreateDate) VALUES('Levi','Parrish','adipiscing.elit@velarcueu.com','Jun 21, 2014');
INSERT INTO SampleUsers(FirstName,LastName,Email,CreateDate) VALUES('Pearl','Warren','In@dignissimpharetra.org','Mar 3, 2014');
نتیجه‌ی اجرای کد بالا، ایجاد جدولی با نام SampleUsers و درج 5 رکورد در آن می‌شود.

تنظیمات ConnectionString

قبل از استفاده از Edge.js با SQL Server، باید متغیر محیطی (environment variable) با نام EDGE_SQL_CONNECTION_STRING را تعریف کنید.

set EDGE_SQL_CONNECTION_STRING=Data Source=localhost;Initial Catalog=node-test;Integrated Security=True
این متغیر تنها برای command prompt جاری تعریف شده است و با بستن آن از دست می‌رود. در صورتیکه از Node.js Tools for Visual Studio استفاده می‌کنید، نیاز به ایجاد یک متغیر محیطی دائمی و راه اندازی مجدد VS دارید. همچنین در صورتیکه بخواهید متغیر محیطی دائمی ایجاد کنید، فرمان زیر را اجرا کنید:
SETX EDGE_SQL_CONNECTION_STRING "Data Source=localhost;Initial Catalog=node-test;Integrated Security=True"


روش اول: اجرای مستقیم SQL Server Query در Edge.js

فایلی با نام server-sql-query.js را ایجاد کرده و کد زیر را در آن وارد کنید:

var http = require('http');
var edge = require('edge');
var port = process.env.PORT || 8080;

var getTopUsers = edge.func('sql', function () {/*
    SELECT TOP 3 * FROM SampleUsers ORDER BY CreateDate DESC
*/});

function logError(err, res) {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.write("Error: " + err);
    res.end("");
}    

http.createServer(function (req, res) {
    res.writeHead(200, { 'Content-Type': 'text/html' });

    getTopUsers(null, function (error, result) {
        if (error) { logError(error, res); return; }
        if (result) {
            res.write("<ul>");
            result.forEach(function(user) {
                res.write("<li>" + user.FirstName + " " + user.LastName + ": " + user.Email + "</li>");
            });
            res.end("</ul>");
        }
        else {
        }
    });
}).listen(port);
console.log("Node server listening on port " + port);
سپس با استفاده از command prompt، فرمان زیر را اجرا کنید:
node server-sql-query.js
حال مرورگر خود را باز و سپس آدرس http://localhost:8080 را باز کنید. در صورتی که همه چیز به درستی انجام گرفته باشد لیستی از 3 کاربر را خواهید دید.

روش دوم: اجرای کد دات نت برای SQL Server Query

Edge.js تنها از دستورات Update، Insert، Select و Delete پشتیبانی می‌کند. در حال حاضر از store procedures و مجموعه‌ای از کد SQL پشتیبانی نمی‌کند. بنابراین، اگر چیزی بیشتر از عملیات CRUD می‌خواهید انجام دهید، باید از دات نت برای این کار استفاده کنید.

یادتان باشد، همیشه async

مدل اجرایی Node.js به صورت یک حلقه‌ی رویداد تک نخی است. بنابراین این بسیار مهم است که کد دات نت شما به صورت async باشد. در غیر اینصورت یک فراخوانی به دات نت سبب مسدود شدن و ایجاد خرابی در Node.js می‌شود.

ایجاد یک Class Library

اولین قدم، ایجاد یک پروژه Class Library در Visual Studio که خروجی آن یک فایل DLL است و استفاده از آن در Edge.js است. پروژه Class Library با عنوان EdgeSampleLibrary ایجاد کرده و فایل کلاسی با نام Sample1 را به آن اضافه کنید و سپس کد زیر را در آن وارد کنید:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;

namespace EdgeSampleLibrary
{
     public class Sample1
    {
        public async Task<object> Invoke(object input)
        {
            // Edge marshalls data to .NET using an IDictionary<string, object>
            var payload = (IDictionary<string, object>) input;
            var pageNumber = (int) payload["pageNumber"];
            var pageSize = (int) payload["pageSize"];
            return await QueryUsers(pageNumber, pageSize);
        }

        public async Task<List<SampleUser>> QueryUsers(int pageNumber, int pageSize)
        {
            // Use the same connection string env variable
            var connectionString = Environment.GetEnvironmentVariable("EDGE_SQL_CONNECTION_STRING");
            if (connectionString == null)
                throw new ArgumentException("You must set the EDGE_SQL_CONNECTION_STRING environment variable.");

            // Paging the result set using a common table expression (CTE).
            // You may rather do this in a stored procedure or use an 
            // ORM that supports async.
            var sql = @"
DECLARE @RowStart int, @RowEnd int;
SET @RowStart = (@PageNumber - 1) * @PageSize + 1;
SET @RowEnd = @PageNumber * @PageSize;

WITH Paging AS
(
    SELECT  ROW_NUMBER() OVER (ORDER BY CreateDate DESC) AS RowNum,
            Id, FirstName, LastName, Email, CreateDate
    FROM    SampleUsers
)
SELECT  Id, FirstName, LastName, Email, CreateDate
FROM    Paging
WHERE   RowNum BETWEEN @RowStart AND @RowEnd
ORDER BY RowNum;
";
            var users = new List<SampleUser>();

            using (var cnx = new SqlConnection(connectionString))
            {
                using (var cmd = new SqlCommand(sql, cnx))
                {
                    await cnx.OpenAsync();

                    cmd.Parameters.Add(new SqlParameter("@PageNumber", SqlDbType.Int) { Value = pageNumber });
                    cmd.Parameters.Add(new SqlParameter("@PageSize", SqlDbType.Int) { Value = pageSize });

                    using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
                    {
                        while (await reader.ReadAsync())
                        {
                            var user = new SampleUser
                            {
                                Id = reader.GetInt32(0), 
                                FirstName = reader.GetString(1), 
                                LastName = reader.GetString(2), 
                                Email = reader.GetString(3), 
                                CreateDate = reader.GetDateTime(4)
                            };
                           users.Add(user);
                        }
                    }
                }
            }
            return users;
        } 
    }

    public class SampleUser
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public DateTime CreateDate { get; set; }
    }
}
سپس ذخیره و کامپایل کنید. فایل DLL خروجی که در مسیر
[project]/bin/Debug/EdgeSampleLibrary.dll
قرار دارد را در پوشه‌ی پروژه Node کپی کنید. فایل جدیدی را با نام server-dotnet-query.js در پروژه Node ایجاد کنید و کد زیر را در آن وارد کنید:
var http = require('http');
var edge = require('edge');
var port = process.env.PORT || 8080;

// Set up the assembly to call from Node.js 
var querySample = edge.func({ assemblyFile: 'EdgeSampleLibrary.dll', typeName: 'EdgeSampleLibrary.Sample1', methodName: 'Invoke' });

function logError(err, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write("Got error: " + err); res.end(""); }

http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/html' });

    // This is the data we will pass to .NET
    var data = { pageNumber: 1, pageSize: 3 };

    // Invoke the .NET function
    querySample(data, function (error, result) {
        if (error) { logError(error, res); return; }
        if (result) {
            res.write("<ul>");
            result.forEach(function(user) {
                res.write("<li>" + user.FirstName + " " + user.LastName + ": " + user.Email + "</li>");
            });
            res.end("</ul>");
        }
        else {
            res.end("No results");
        }
    });

}).listen(port);

console.log("Node server listening on port " + port);
سپس از طریق command prompt آن را اجرا کنید:
node server-dotnet-query.js
حال مرورگر خود را باز کرده و به آدرس http://localhost:8080 بروید. در صورتیکه همه چیز به درستی انجام گرفته باشد، لیستی از 3 کاربر را خواهید دید. مقادیر pageNumber و pageSize را در فایل جاوااسکریپت تغییر دهید و تاثیر آن را بر روی خروجی مشاهده کنید.
 
نکته: برای ایجاد pageNumber و pageSize داینامیک با استفاده از ارسال مقادیر توسط QueryString، می‌توانید از ماژول connect استفاده کنید.
مطالب
ویژگی های کمتر استفاده شده در NET. - بخش هفتم

DebuggerStepThroughAttribute

ویژگی DebuggerStepThroughAttribute باعث می‌شود که در زمان دیباگ کردن کد، با کلید F11، متدهایی که این ویژگی را دارند، بدون رفتن به داخل متد (همانند دیباگ با کلید F10 عمل می‌کند، به جز زمانی که در داخل متد break point گذاشته باشید) ، تنها اجرا می‌شوند.
به مثال زیر توجه کنید:
class Program
{
    public static void Main(string[] args)
    {
        DebuggerStepThroughMethod1();
    }

    [DebuggerStepThrough]
    public static void DebuggerStepThroughMethod1()
    {
        Console.WriteLine( "Method 1" );
        DebuggerStepThroughMethod2();
    }

    [DebuggerStepThrough]
    public static void DebuggerStepThroughMethod2()
    {
        Console.WriteLine( "Method 2" );
    }
}
و نتیجه دیباگ با استفاده از F11 به صورت زیر می‌شود:

همانطور که مشاهده می‌کنید برنامه را با کلید F11 اجرا کردم. بعد از ورود به Method1، با زدن کلید F11 دستور بعدی، break point درون Method2 است.

ConditionalAttribute

شما با استفاده از Conditional می توانید اجرای یک متد را به شناساننده پیش پردازشی ( pre-processing identifier ) وابسته کنید. ConditionalAttribute می‌تواند بر روی یک کلاس یا یک متد بکار برده شود.
class Program
{
    public static void Main(string[] args)
    {
        DebugMode();
    }

    [Conditional("DEBUG")]
    public static void DebugMode()
    {
        Console.WriteLine( "Debug mode" );
    }
}
در صورتی که مثال بالا را در حالت Debug اجرا کنید، خروجی کنسول پیام Debug mode است و در صورتی که در حالت Release اجرا کنید، متد DebugMode اجرا نخواهد شد.
نکته: شما می‌توانید با استفاده از دستور define# (در بیرون از فضای نام) مقدار سفارشی خود را تعریف کنید.
#define ReleaseMode


Flags Enum Attribute

ویژگی Flags برای پوشش فیلدهای بیتی و انجام مقایسه بیتی استفاده می‌شود. از این ویژگی باید برای زمانیکه یک داده شمارشی می‌تواند چندین مقدار را به صورت همزمان داشته باشد، استفاده کرد.
[System.Flags]
public enum Permission
{
    View = 1,
    Insert = 2,
    Update = 4,
    Delete = 8
}
این نکته خیلی مهم است که Flags به صورت خودکار، مقادیر enum را به توان دو نمی‌رساند و شما باید به صورت دستی این مقادیر را تعیین کنید. در صورتیکه مقادیر عددی را تعیین نکنید، enum در عملیات بیتی به درستی کار نخواهد کرد، چرا که مقدار enum از 0 شروع می‌شود و افزایش پیدا می‌کند.  
public static void Main( string[] args )
{
    var permission = ( Permission.View | Permission.Insert ).ToString();
    Console.WriteLine( permission ); // Displays ‘View, Insert’

    var userPermission = Permission.View | Permission.Insert | Permission.Update | Permission.Delete;
    // To retrieve the value from property you can do this
    if ( ( userPermission & Permission.Delete ) == Permission.Delete )
    {
        Console.WriteLine( "کاربر دارای مجوز دسترسی به عملیات حذف می‌باشد" );
    }

    // In .NET 4 and later
    Console.WriteLine( userPermission.HasFlag( Permission.Delete )
                            ? "کاربر دارای مجوز دسترسی به عملیات حذف می‌باشد"
                            : "کاربر مجوز دسترسی به عملیات حذف را ندارد");
}

نکته: در صورتیکه مقداری را برای enum تعریف کرده باشید، نمی‌توانید آن را با مقدار 0 مشخص کنید (در زمانی که ویژگی flags را بر روی enum اضافه کرده باشید)، چرا که با استفاده از عملیات بیتی AND نمی‌توانید دارا بودن آن مقدار را تست کنید و همیشه نتیجه صفر خواهد بود.


Dynamically Compile and Execute C# Code

CodeDOM

با استفاده از CodeDOM می‌توانید یک سورس کد را به صورت یک فایل اسمبلی کامپایل و ذخیره کنید.
public static void Main( string[] args )
{
    var sourceCode = @"class DotNetTips
                        {
                            public void Print()
                            {
                                System.Console.WriteLine("".Net Tips"");
                            }
                        }";
    var compiledAssembly = CompileSourceCodeDom( sourceCode );
    ExecuteFromAssembly( compiledAssembly );
}

static Assembly CompileSourceCodeDom( string sourceCode )
{
    CodeDomProvider csharpCodeProvider = new CSharpCodeProvider();
    var cp = new CompilerParameters
                {
                    GenerateExecutable = false
                };
    cp.ReferencedAssemblies.Add( "System.dll" );
    var cr = csharpCodeProvider.CompileAssemblyFromSource( cp,
                                                            sourceCode );
    return cr.CompiledAssembly;
}
همانطور که در مثال بالا مشاهده می‌کنید، متغیر sourceCode حاوی کد مربوط به یک کلاس #C می‌باشد که یک متد Print در آن تعریف شده است.


Roslyn

سکوی کامپایلر دات نت " Roslyn "،  کامپایلرهای متن باز #C و  VB.NET را به همراه APIهای تجزیه و تحلیل کد ارائه کرده است که با استفاده از این APIها می‌توان ابزارهای آنالیز کد جهت استفاده در ویژوال استودیو را ایجاد کرد.

برای استفاده از Roslyn باید این کتابخانه را نصب کنید

Install-Package Microsoft.CodeAnalysis

حال مثال قبل را با استفاده از Roslyn بازنویسی می‌کنیم:

public static void Main(string[] args)
{
    var sourceCode = @"class DotNetTips
                        {
                            public void Print()
                            {
                                System.Console.WriteLine("".Net Tips"");
                            }
                        }";
    var compiledAssembly = CompileSourceRoslyn( sourceCode );
    ExecuteFromAssembly( compiledAssembly );
}

private static Assembly CompileSourceRoslyn(string sourceCode)
{
    using ( var memoryStream = new MemoryStream() )
    {
        var assemblyFileName = string.Concat( Guid.NewGuid().ToString(),
                                                ".dll" );
        var compilation = CSharpCompilation.Create( assemblyFileName,
                                                    new[]
                                                    {
                                                        CSharpSyntaxTree.ParseText( sourceCode )
                                                    },
                                                    new[]
                                                    {
                                                        MetadataReference.CreateFromFile( typeof( object ).Assembly.Location )
                                                    },
                                                    new CSharpCompilationOptions( OutputKind.DynamicallyLinkedLibrary ) );
        compilation.Emit( memoryStream );
        var assembly = Assembly.Load( memoryStream.GetBuffer() );
        return assembly;
    }
}

و جهت فراخوانی اسمبلی ساخته شده به هر دو روش بالا، از کد زیر استفاده می‌کنیم.

static void ExecuteFromAssembly( Assembly assembly )
{
    var helloKittyPrinterType = assembly.GetType( "DotNetTips" );
    var printMethod = helloKittyPrinterType.GetMethod( "Print" );
    var kitty = assembly.CreateInstance( "DotNetTips" );
    printMethod.Invoke( kitty,
                        BindingFlags.InvokeMethod,
                        null,
                        null,
                        CultureInfo.CurrentCulture );
}


مطالب
آشنایی با ساختار یک Pull Request خوب
در مطلب «نحوه‌ی مشارکت در پروژه‌های GitHub به کمک Visual Studio» با مفهوم pull request آشنا شدیم. اما ... یک pull request خوب چه خصوصیاتی دارد و فرهنگ ارسال یک PR خوب چیست؟

اخلاق مشارکت در یک پروژه‌ی سورس باز

بعضی از توسعه دهنده‌ها در حین مشارکت در یک پروژه‌ی سورس باز، برای مثال جهت افزودن قابلیتی جدید و یا رفع مشکلی، ابتدا سعی می‌کنند تا کدهای فعلی را برای خودشان «قابل فهم‌تر» کنند. این قابل فهم‌تر کردن پروژه، شامل تغییر نام متغیرها و متدهای فعلی، انتقال کدهای موجود به فایل‌هایی دیگر یا حتی یکی کردن چندین فایل با هم، مرتب سازی متدهای یک کلاس بر اساس حروف الفباء و امثال آن می‌شود.
این کارها را نباید در حین مشارکت و توسعه‌ی پروژه‌های سورس باز دیگران انجام دهید! اگر هدفتان رفع مشکلی است یا افزودن قابلیتی جدید، باید نحوه‌ی کدنویسی فعلی را حفظ کنید. از این جهت که نگهدارنده‌ی اصلی پروژه، پیش از شما این‌کار را شروع کرده‌است و زمانیکه شما به پروژه‌ای دیگر رجوع خواهید کرد، باز نیز باید همین کار را ادامه دهد.
اگر refactoring گسترده‌ی شما به هر نحوی سبب بهبود پروژه‌ی اصلی می‌شود، ابتدا این مورد را با مسئول اصلی پروژه مطرح کنید. اگر او قبول کرد، سپس اقدام به چنین کاری نمائید.


بحث در مورد تغییرات پیش از ارسال PR

قبل از اینکه PR ایی را ارسال کنید، بهتر است یک issue یا ticket جدید را باز کرده و در مورد آن بحث کنید یا توضیح دهید. در این حالت ممکن است توضیحات بهتری را در مورد سازگار سازی تغییرات خود با کدهای فعلی دریافت کنید.


Pull requestها را کوچک نگه‌دارید

برای اینکه شانس قبول شدن PR خود را بالا ببرید، حجم و تمرکز آن‌را کوچک نگه دارید. بسیاری از توسعه دهنده‌های سورس باز اگر با یک PR حجیم روبرو شوند، آن‌را رد می‌کنند چون مشکل اصلی، مدت زمان بالایی است که باید جهت بررسی این PR اختصاص داد. هرچقدر حجم آن بیشتر باشد، زمان بیشتری را خواهد برد.


فقط یک کار را انجام دهید

شبیه به اصل تک مسئولیتی کلاس‌ها، یک PR نیز باید تنها یک کار را انجام دهد و بر روی یک موضوع خاص تمرکز داشته باشد. فرض کنید PR ایی را ارسال کرده‌اید که سه مشکل A، B و C را برطرف می‌کند. از دیدگاه مسئول اصلی پروژه، موارد A و C قابل قبول هستند؛ اما نه مورد C مطرح شده. در این حالت کل PR شما برگشت خواهد خورد. به همین جهت بهتر است بجای یک PR، سه PR مختلف و مجزا را جهت رفع مشکلات A، B و C ارسال کنید.


سازگاری تغییرات ارسالی را بررسی کنید

حداقل کاری را که پیش از ارسال PR باید انجام دهید این است که بررسی کنید آیا این تغییرات قابل Build هستند یا خیر. همچنین اگر پروژه دارای یک سری Unit tests است، حتما آن‌ها را یکبار بررسی کنید تا مطمئن شوید جای دیگری را به هم نریخته‌اید. ضمنا وجود این تست‌ها به صورت ضمنی به این معنا است که تغییرات جدید شما نیز باید به همراه تست‌های مرتبطی باشند تا پذیرفته شوند.


PR ایی را بر روی شاخه‌ی master ارسال نکنید

پس از اینکه یک fork از پروژه‌ای سورس باز را ایجاد کردید و سپس آن‌را clone نمودید تا به صورت Local بتوانید با آن کار کنید، فراموش نکنید که در همینجا باید یک branch و انشعاب جدید را جهت کار بر روی ویژگی مدنظر خود ایجاد کنید (برای مثال feature-X, fix-Y). بسیاری از پروژه‌های سورس باز به هیچ عنوان PRهای کار شده‌ی بر روی انشعاب master را قبول نمی‌کنند.


برای مطالعه بیشتر
Open Source Contribution Etiquette  
ten tips for better Pull Requests 
Getting a Pull Request Accepted 
Optimize Your Pull-request 

مطالب
پروسیجرها و شنود پارامترها در SQL Server - قسمت سوم
در مطلب قبلی راجع به اثرات منفی شنود پارامترها، در صورت عدم توجه به آن‌ها بیان شد و در این مطلب قصد داریم به راه‌های کاهش اثرات منفی و مقابله با آن‌ها بپردازیم:
نکته: راه‌های اشاره شده برای مقابله با شنود پارامترها برای تمام شرایط قابل استفاده نیستند.


راه حل اول: استفاده از دستور With Recompile

مشکل شنود پارامتر این است که در اولین اجرای پروسیجر، پلن اجرایی را بر اساس پارامترهای ارسالی اولیه ایجاد می‌کند. راه حل غلبه بر این مشکل، کامپایل مجدد پروسیجر، بعد از هر اجرای آن است. بهمین جهت از دستور WITH RECOMPILE هنگامیکه قصد ایجاد پروسیجر را دارید استفاده نمایید. مانند کد زیر:

CREATE PROC [dbo].[DisplayBillingInfo]
  @BeginDate DATETIME,
  @EndDate DATETIME
WITH RECOMPILE
AS 
SELECT BillingDate, BillingAmt
  FROM BillingInfo
  WHERE BillingDate between @BeginDate AND @EndDate;
مجددا 2 آزمایش اشاره شده در مطلب قبلی (اشاره شده در زیر) را تکرار می‌کنیم.
DBCC FREEPROCCACHE;
EXEC dbo.DisplayBillingInfo 
  @BeginDate = '2005-01-01',  
  @EndDate  = '2005-01-03';
  
EXEC dbo.DisplayBillingInfo 
  @BeginDate = '1999-01-01',  
  @EndDate  = '1999-12-31';
هنگامیکه کد بالا را اجرا نمایید، فراخوانی اول، عملیات Index Seek و فراخوانی دوم، Index Scan را موجب خواهد شد. نقص این روش، کامپایل مجدد با هر بار اجرای پروسیجر است که باعث تحمیل سربار اضافه‌ای می‌شود.


راه حل دوم: غیر فعال نمودن شنود پارامتر

روش دیگر برطرف کردن مشکلات مرتبط با شنود پارامتر، غیر فعال کردن آن است. البته منظور از غیر فعال کردن، غیر فعال نمودن گزینه‌ای در بانک اطلاعاتی نیست؛ بلکه با تغییر متن و نحوه‌ی اجرا، می‌توان شنود را غیر فعال نمود. در کد زیر با تغییر نحوه اجرای پروسیجر، قابلیت شنود پارامتر غیر فعال شده است:

CREATE PROC [dbo].[DisplayBillingInfo]
  @BeginDate DATETIME,
  @EndDate DATETIME
WITH RECOMPILE
AS 
DECLARE @StartDate DATETIME;
DECLARE @StopDate DATETIME;
SET @StartDate = @BeginDate;
SET @StopDate = @EndDate;
SELECT BillingDate, BillingAmt
  FROM BillingInfo
  WHERE BillingDate between @StartDate AND @StopDate;
برای غیرفعال نمودن، تمام کاری که انجام شده، نحوه استفاده از پارامترهای ارسالی تغییر داده شده است. در کد بالا دو متغیر محلی با نامهای StartDate@ و EndDate@ ایجاد شده‌است. پارامترهای ارسالی درون متغیرهای محلی ذخیره می‌شوند و سپس از متغیرهای محلی در شرط between استفاده خواهد شد. بدین صورت شنود غیر فعال می‌شود. دلیل غیر فعال شدن شنود این است که بهبود دهنده (optimizer) قادر به شناسایی مقادیر پارامترهای ورودی در بدنه دستور Select نمی‌باشد. بدلیل عدم رهگیری محل مصرف مقادیر پارامترهای ارسالی توسط اس کیو ال سرور، بهبود دهنده یک پلن جنریک را براساس اطلاعات آماری ایجاد خواهد کرد.


راه حل سوم: ایجاد چند نوع پروسیجر

راه دیگر، ایجاد پروسیجرهای متفاوت برای پارامترهایی با کاردینالیتی متفاوت است. به‌عبارت دیگر، دسته بندی پارامترهای ارسالی و ایجاد پروسیجرهایی خاص همان دسته. در مثال‌های این سری از مطالب، دو دسته پارامتر 1) بازه زمانی کوتاه، مثلا چند روز و 2) بازه زمانی بلند، مثلا ماهیانه وجود داشت که می‌توانید 2 دسته پروسیجر را یکی برای بازه‌های روزانه و دیگری برای بازه‌های زمانی ماهیانه ایجاد نمایید.

CREATE PROC [dbo].[DisplayBillingInfoNarrow]
  @BeginDate DATETIME,
  @EndDate DATETIME
AS 
SELECT BillingDate, BillingAmt
  FROM BillingInfo
  WHERE BillingDate between @BeginDate AND @EndDate;  
GO
CREATE PROC [dbo].[DisplayBillingInfoWide]
  @BeginDate DATETIME,
  @EndDate DATETIME
AS 
SELECT BillingDate, BillingAmt
  FROM BillingInfo
  WHERE BillingDate between @BeginDate AND @EndDate;  
GO  
DROP PROCEDURE [dbo].[DisplayBillingInfo];
GO  
CREATE PROC [dbo].[DisplayBillingInfo]
  @BeginDate DATETIME,
  @EndDate DATETIME
AS 
IF DATEDIFF(DD,@BeginDate, @EndDate) < 4
  EXECUTE DisplayBillingInfoNarrow @BeginDate, @EndDate
ELSE
  EXECUTE DisplayBillingInfoWide @BeginDate, @EndDate
GO
در کد بالا، دو گروه پروسیجر (برای بازه زمانی کوتاه و بلند) به‌همراه یک پروسیجر تصمیم گیرنده جهت تشخیص استفاده از پروسیجر مناسب، بر اساس پارامترهای ورودی ایجاد شده است. یکی از مزایای این روش استفاده پروسیجر از پلن اجرایی مناسب، فارغ از پارامترهای ارسالی خواهد بود. البته نگهداری کد در این روش به مرور زمان، کمی دشوار و سخت خواهد شد.