مطالب دوره‌ها
الگوریتم HiLo در RavenDB چیست؟
یکی از مشکلاتی که با اکثر سیستم‌های رابطه‌ای وجود دارد، نیاز به تکمیل یک تراکنش برای دریافت Id رکورد ثبت شده است. همین مساله علاوه بر خاتمه یک تراکنش و شروع تراکنشی دیگر، به معنای رفت و برگشت اضافه‌ای به بانک اطلاعاتی نیز می‌باشد. به همین جهت در RavenDB برای ارائه راه حلی برای اینگونه مشکلات از الگوریتم HiLo برای تولید کلیدهای اسناد استفاده می‌گردد.

HiLo چیست؟

HiLo روشی است برای ارائه idهای عددی افزایش یابنده، جهت استفاده در محیط‌های چندکاربری. در این حالت، هنوز هم سرور تولید idها را کنترل می‌کند، اما هربار بازه‌ای از Idها را در اختیار کلاینت‌ها قرار می‌دهد. به این ترتیب کلاینت، بدون نیاز به رفت و برگشت اضافه‌ای به سرور، می‌تواند Id مورد نیاز خود را از بازه ارائه شده تامین کرده و هر زمانیکه این بازه به پایان رسید، سری دیگری را درخواست کند.
بدیهی است در این حالت، ارائه کلیه Idهای یک بازه از طرف سرور ممکن است کاری سنگین باشد. به همین جهت سرور در درخواست ابتدایی کلاینت، یک تک id را به نام «Hi» جهت مشخص سازی ابتدای بازه تولید idها، در اختیار او قرار می‌دهد. قسمت «Lo» توسط خود کلاینت مدیریت می‌شود و در این بین به هر کلاینت یک capacity یا تعداد مجاز idهایی را که می‌تواند تولید کند، از پیش اختصاص داده شده است:
 (currentHi - 1)*capacity + (++currentLo)
بنابراین فرمول تولید idهای جدید در سمت کلاینت به نحو فوق خواهد بود. currentLo تا جایی افزایش می‌یابد که capacity آن کلاینت تنظیم شده است. سپس Hi جدیدی درخواست شده و Lo صفر می‌شود.

حالت پیش فرض کار کلاینت‌های RavenDB نیز به همین نحو است و الگوریتم HiLo در DocumentConvention آن، از قبل تنظیم شده است و مزیت مهم روش HiLo این است که با هربار فراخوانی متد Store یک سشن، بدون رفت و برگشت اضافه‌تری به سرور، Idهای لازم در اختیار شما قرار خواهد گرفت و نیازی نیست تا زمان SaveChanges صبر کرد. به این ترتیب batch operations در RavenDB کارآیی بسیار بالایی خواهند یافت.
 
مطالب
استفاده از افزونه‌ی jQuery Autocomplete در ASP.NET

با استفاده از AutoComplete TextBoxes می‌توان گوشه‌ای از زندگی روزمره‌ی کاربران یک برنامه را ساده‌تر کرد. مشکل مهم dropDownList ها دریک برنامه‌ی وب، عدم امکان تایپ قسمتی از متن مورد نظر و سپس نمایان شدن آیتم‌های متناظر با آن در اسرع وقت می‌باشد. همچنین با تعداد بالای آیتم‌ها هم حجم صفحه و زمان بارگذاری را افزایش می‌دهند. راه حل‌های بسیار زیادی برای حل این مشکل وجود دارند و یکی از آن‌ها ایجاد AutoComplete TextBoxes است. پلاگین‌های متعددی هم جهت پیاده سازی این قابلیت نوشته‌ شده‌اند منجمله jQuery Autocomplete . این پلاگین دیگر توسط نویسنده‌ی اصلی آن نگهداری نمی‌شود اما توسط برنامه نویسی دیگر در github ادامه یافته است. در ادامه نحوه‌ی استفاده از این افزونه‌ را در ASP.NET Webforms بررسی خواهیم کرد.

الف) دریافت افزونه

لطفا به آدرس GitHub ذکر شده مراجعه نمائید.

سپس برای مثال پوشه‌ی js را به پروژه افزوده و فایل‌های jquery-1.5.min.js ، jquery.autocomplete.js ، jquery.autocomplete.css و indicator.gif را در آن کپی کنید. فایل indicator.gif به همراه مجموعه‌ی دریافتی ارائه نمی‌شود و یک آیکن loading معروف می‌تواند باشد.
علاوه بر آن یک فایل جدید custom.js را نیز جهت تعاریف سفارشی خودمان اضافه خواهیم کرد.


ب) افزودن تعاریف افزونه به صفحه

در ذیل نحوه‌ی افزودن فایل‌های فوق به یک master page نمایش داده شده است.
در اینجا از قابلیت‌های جدید ScriptManager (موجود در سرویس پک یک دات نت سه و نیم و یا دات نت چهار) جهت یکی کردن اسکریپت‌ها کمک گرفته شده است. به این صورت تعداد رفت و برگشت‌ها به سرور به‌جای سه مورد (تعداد فایل‌های اسکریپت مورد استفاده)، یک مورد (نهایی یکی شده) خواهد بود و همچنین حاصل نهایی به صورت خودکار به شکلی فشرده شده به مرورگر تحویل داده شده، سرآیندهای کش شدن اطلاعات به آن اضافه می‌گردد (که در سایر حالات متداول اینگونه نیست)؛ به علاوه Url نهایی آن هم بر اساس hash فایل‌ها تولید می‌شود. یعنی اگر محتوای یکی از این فایل‌ها تغییر کرد، چون Url نهایی تغییر می‌کند، دیگر لازم نیست نگران کش شدن و به روز نشدن اسکریپت‌ها در سمت کاربر باشیم.

<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="AspNetjQueryAutocompleteTest.Site" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<asp:PlaceHolder Runat="server">
<link href="<%= ResolveClientUrl("~/js/jquery.autocomplete.css")%>" rel="stylesheet" type="text/css" />
</asp:PlaceHolder>
<asp:ContentPlaceHolder ID="head" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<CompositeScript>
<Scripts>
<asp:ScriptReference Path="~/js/jquery-1.5.min.js" />
<asp:ScriptReference Path="~/js/jquery.autocomplete.js" />
<asp:ScriptReference Path="~/js/custom.js" />
</Scripts>
</CompositeScript>
</asp:ScriptManager>
<div>
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>
علت استفاده از ResolveClientUrl در حین تعریف فایل css در اینجا به عدم مجاز بودن استفاده از ~ جهت مسیر دهی فایل‌های css در header صفحه بر می‌گردد.


ج) افزودن یک صفحه‌ی ساده به برنامه
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
CodeBehind="default.aspx.cs" Inherits="AspNetjQueryAutocompleteTest._default" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<asp:TextBox ID="txtShenas" runat="server" />
</asp:Content>

فرض کنید می‌خواهیم افزونه‌ی ذکر شده را به TextBox استاندارد فوق اعمال کنیم. ID این TextBox در نهایت به شکل ContentPlaceHolder1_txtShenas رندر خواهد شد. البته در ASP.NET 4.0 با تنظیم ClientIDMode=Static می‌توان ID انتخابی خود را به جای این ID خودکار درنظر گرفت و اعمال کرد. اهمیت این مساله در قسمت (ه) مشخص می‌گردد.


د) فراهم آوردن اطلاعات مورد استفاده توسط افزونه‌ی AutoComplete به صورت پویا

مهم‌ترین قسمت استفاده از این افزونه، تهیه‌ی اطلاعاتی است که باید نمایش دهد. این اطلاعات باید به صورت فایلی که هر سطر آن حاوی یکی از آیتم‌های مورد نظر است، تهیه گردد. برای این منظور می‌توان از فایل‌های ASHX یا همان Generic handlers استفاده کرد:

using System;
using System.Data.SqlClient;
using System.Text;
using System.Web;

namespace AspNetjQueryAutocompleteTest
{
public class AutoComplete : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string prefixText = context.Request.QueryString["q"];
var sb = new StringBuilder();

using (var conn = new SqlConnection())
{
//todo: این مورد باید از فایل کانفیگ خوانده شود
conn.ConnectionString = "Data Source=(local);Initial Catalog=MyDB;Integrated Security = true";
using (var cmd = new SqlCommand())
{
cmd.CommandText = @" select Field1 ,Field2 from tblData where Field1 like @SearchText + '%' ";
cmd.Parameters.AddWithValue("@SearchText", prefixText);
cmd.Connection = conn;
conn.Open();
using (var sdr = cmd.ExecuteReader())
{
if (sdr != null)
while (sdr.Read())
{
string field1 = sdr.GetValue(0) == DBNull.Value ? string.Empty : sdr.GetValue(0).ToString().Trim();
string field2 = sdr.GetValue(1) == DBNull.Value ? string.Empty : sdr.GetValue(1).ToString().Trim();
sb.AppendLine(field1 + "|" + field2);
}
}
}
}

context.Response.Write(sb.ToString());
}

public bool IsReusable
{
get
{
return false;
}
}
}
}

در این مثال از ADO.NET کلاسیک استفاده شده است تا به عمد نحوه‌ی تعریف پارامترها یکبار دیگر مرور گردند. اگر از LINQ to SQL یا Entity framework یا NHibernate و موارد مشابه استفاده می‌کنید، جای نگرانی نیست؛ زیرا کوئری‌های SQL تولیدی توسط این ORMs به صورت پیش فرض از نوع پارامتری هستند (+).
در این مثال اطلاعات دو فیلد یک و دوی فرضی از جدولی با توجه به استفاده از like تعریف شده دریافت می‌گردد. به عبارتی همان متد StartsWith معروف LINQ بکارگرفته شده است.
به صورت خلاصه افزونه، کوئری استرینگ q را به این فایل ashx ارسال می‌کند. سپس کلیه آیتم‌های شروع شده با مقدار دریافتی، از بانک اطلاعاتی دریافت شده و هر کدام قرارگرفته در یک سطر جدید بازگشت داده می‌شوند.
اگر دقت کرده باشید در قسمت sb.AppendLine ، با استفاده از "|" دو مقدار دریافتی از هم جدا شده‌اند. عموما یک مقدار کفایت می‌کند (در 98 درصد موارد) ولی اگر نیاز بود تا توضیحاتی نیز نمایش داده شود از این روش نیز می‌توان استفاده کرد. برای مثال یک مقدار خاص به همراه توضیحات آن به عنوان یک آیتم نمایش داده شده مد نظر است.


ه) اعمال نهایی افزونه به TextBox

در ادامه پیاده سازی فایل custom.js برای استفاده از امکانات فراهم شده در قسمت‌های قبل ارائه گردیده است:

function formatItem(row) {
return row[0] + "<br/><span style='text-align:justify;' dir='rtl'>" + row[1] + "</span>";
}

$(document).ready(function () {
$("#ContentPlaceHolder1_txtShenas").autocomplete('AutoComplete.ashx', {
//Minimum number of characters a user has to type before the autocompleter activates
minChars: 0,
delay: 5,
//Only suggested values are valid
mustMatch: true,
//The number of items in the select box
max: 20,
//Fill the input while still selecting a value
autoFill: false,
//The comparison doesn't looks inside
matchContains: false,
formatItem: formatItem
});
});

پس از این مقدمات، اعمال افزونه‌ی autocomplete به textBox ایی با id مساوی ContentPlaceHolder1_txtShenas ساده است. اطلاعات از فایل AutoComplete.ashx دریافت می‌گردد و تعدادی از خواص پیش فرض این افزونه در اینجا مقدار دهی شده‌اند. لیست کامل آن‌ها را در فایل jquery.autocomplete.js می‌توان مشاهده کرد.
تنها نکته‌ی مهم آن استفاده از پارامتر اختیاری formatItem است. اگر در حین تهیه‌ی AutoComplete.ashx خود تنها یک آیتم را در هر سطر نمایش می‌دهید و از "|" استفاده نکرده‌اید، نیازی به ذکر آن نیست. در این مثال ویژه، فیلد یک در یک سطر و فیلد دو در سطر دوم یک آیتم نمایش داده می‌شوند:



مسیرراه‌ها
ASP.NET MVC
              مطالب دوره‌ها
              نگاهی به افزونه‌های کار با اسناد در RavenDB
              توانمندی‌های RavenDB جهت کار با اسناد، صرفا به ذخیره و ویرایش آن‌ها محدود نمی‌شوند. در ادامه، مباحثی مانند پیوست فایل‌های باینری به اسناد، نگهداری نگارش‌های مختلف آن‌ها، حذف آبشاری اسناد و وصله کردن آن‌ها را مورد بررسی قرار خواهیم داد. تعدادی از این قابلیت‌ها توکار هستند و تعدادی دیگر توسط افزونه‌های آن فراهم شده‌اند.


              پیوست و بازیابی فایل‌های باینری

              امکان پیوست فایل‌های باینری نیز به اسناد RavenDB وجود دارد. برای مثال به کلاس سؤالات قسمت اول این سری، خاصیت FileId را اضافه کنید:
              public class Question
              {
                  public string FileId { set; get; }
              }
              اکنون برای ذخیره فایلی و همچنین انتساب آن به یک سند، به روش ذیل باید عمل کرد:
                          using (var store = new DocumentStore
                          {
                              Url = "http://localhost:8080"
                          }.Initialize())
                          {
                              using (var session = store.OpenSession())
                              {
                                  store.DatabaseCommands.PutAttachment(key: "file/1",
                                                                       etag: null,
                                                                       data: System.IO.File.OpenRead(@"D:\Prog\packages.config"),
                                                                       metadata: new RavenJObject
                                                                       { 
                                                                          { "Description", "توضیحات فایل" }
                                                                       });
                                  var question = new Question
                                  {
                                      By = "users/Vahid",
                                      Title = "Raven Intro",
                                      Content = "Test....",
                                      FileId = "file/1"
                                  };
                                  session.Store(question);
              
                                  session.SaveChanges();
                              }
                          }
              کار متد store.DatabaseCommands.PutAttachment، ارسال اطلاعات یک استریم به سرور RavenDB است که تحت کلید مشخصی ذخیره خواهد شد. متد استاندارد System.IO.File.OpenRead روش مناسبی است برای دریافت استریم‌ها و ارسال آن به متد PutAttachment. در قسمت metadata این فایل، توسط شیء RavenJObject، یک دیکشنری از key-valueها را جهت درج اطلاعات اضافی مرتبط با هر فایل می‌توان مقدار دهی کرد. پس از آن، جهت انتساب این فایل ارسال شده به یک سند، تنها کافی است کلید آن‌را به خاصیت FileId انتساب دهیم.
              در این حالت اگر به خروجی دیباگ سرور نیز دقت کنیم، مسیر ذخیره سازی این نوع فایل‌ها مشخص می‌شود:
               Request # 2: PUT   - 200 ms - <system> - 201 - /static/file/1
              بازیابی فایل‌های همراه با اسناد نیز بسیار ساده است:
                          using (var store = new DocumentStore
                          {
                              Url = "http://localhost:8080"
                          }.Initialize())
                          {
                              using (var session = store.OpenSession())
                              {
                                  var question = session.Load<Question>("questions/97");
                                  var file1 = store.DatabaseCommands.GetAttachment(question.FileId);
                                  Console.WriteLine(file1.Size);
                              }
                          }
              فقط کافی است سند را یکبار Load کرده و سپس از متد store.DatabaseCommands.GetAttachment برای دستیابی به فایل پیوست شده استفاده نمائیم.


              وصله کردن اسناد

              سند سؤالات قسمت اول و پاسخ‌های آن، همگی داخل یک سند هستند. اکنون برای اضافه کردن یک آیتم به این لیست، یک راه، واکشی کل آن سند است و سپس افزودن یک آیتم جدید به لیست پاسخ‌ها و یا در این حالت، جهت کاهش ترافیک سرور و سریعتر شدن کار، RavenDB مفهوم Patching یا وصله کردن اسناد را ارائه داده است. در این روش بدون واکشی کل سند، می‌توان قسمتی از سند را وصله کرد و تغییر داد.
                          using (var store = new DocumentStore
                          {
                              Url = "http://localhost:8080"
                          }.Initialize())
                          {
                              using (var session = store.OpenSession())
                              {
                                  store.DatabaseCommands.Patch(key: "questions/97",
                                                               patches: new[]
                                                                        {
                                                                           new PatchRequest
                                                                           {
                                                                              Type = PatchCommandType.Add,
                                                                              Name = "Answers",
                                                                              Value = RavenJObject.FromObject(new Answer{ By= "users/Vahid", Content="data..."})
                                                                           }
                                                                        });
                              }
                          }
              برای وصله کردن اسناد از متد store.DatabaseCommands.Patch استفاده می‌شود. در اینجا ابتدا Id سند مورد نظر مشخص شده و سپس آرایه‌ای از تغییرات لازم را به صورت اشیاء PatchRequest ارائه می‌دهیم. در هر PatchRequest، خاصیت Type مشخص می‌کند که حین عملیات وصله کردن چه کاری باید صورت گیرد؛ برای مثال اطلاعات ارسالی اضافه شوند یا ویرایش و امثال آن. خاصیت Name، نام خاصیت در حال تغییر را مشخص می‌کند. برای مثال در اینجا می‌خواهیم به مجموعه پاسخ‌های یک سند، آیتم جدیدی را اضافه کنیم. خاصیت Value، مقدار جدید را دریافت خواهد کرد. این مقدار باید با فرمت JSON تنظیم شود؛ به همین جهت از متد توکار RavenJObject.FromObject برای اینکار استفاده شده است.


              افزونه‌های RavenDB

              قابلیت‌های ذکر شده فوق جهت کار با اسناد به صورت توکار در RavenDB مهیا هستند. این سیستم افزونه پذیر است و تاکنون افزونه‌های متعددی برای آن تهیه شده‌اند که در اینجا به آن‌ها Bundles گفته می‌شوند. برای استفاده از آن‌ها تنها کافی است فایل DLL مرتبط را درون پوشه Plugins سرور، کپی کنیم. دریافت آن‌ها نیز از طریق NuGet پشتیبانی می‌شود؛ و یا سورس آن‌ها را دریافت کرده و کامپایل کنید. در ادامه تعدادی از این افزونه‌ها را بررسی خواهیم کرد.


              حذف آبشاری اسناد

               PM> Install-Package RavenDB.Bundles.CascadeDelete -Pre
              فایل افزونه حذف آبشاری اسناد را از طریق دستور نیوگت فوق می‌توان دریافت کرد. سپس فایل Raven.Bundles.CascadeDelete.dl دریافتی را درون پوشه plugins کنار فایل exe سرور RavenDB کپی کنید تا قابل استفاده شود.
              استفاده مهم این افزونه، حذف پیوست‌های باینری اسناد و یا حذف اسناد مرتبط با یک سند، پس از حذف سند اصلی است (که به صورت پیش فرض انجام نمی‌شود).
              یک مثال:
              var comment = new Comment
              {
                 PostId = post.Id
              };
              session.Store(comment);
              
              session.Advanced.GetMetadataFor(post)["Raven-Cascade-Delete-Documents"] = RavenJToken.FromObject(new[] { comment.Id });
              session.Advanced.GetMetadataFor(post)["Raven-Cascade-Delete-Attachments"] =  RavenJToken.FromObject(new[] { "picture/1" });
              
              session.SaveChanges();
              برای استفاده از آن باید از متد session.Advanced.GetMetadataFor استفاده کرد. در اینجا شیء post که دارای تعدادی کامنت است، مشخص می‌شود. سپس با مشخص سازی Raven-Cascade-Delete-Documents و ذکر Id کامنت‌های مرتبطی که باید حذف شوند، تمام این اسناد با هم پس از حذف post، حذف خواهند شد. همچنین دستور Raven-Cascade-Delete-Attachments سبب حذف فایل‌های مشخص شده با Id مرتبط با یک سند، می‌گردد.


              نگهداری و بازیابی نگارش‌های مختلف اسناد

               PM> Install-Package RavenDB.Bundles.Versioning
              فایل افزونه Versioning اسناد را از طریق دستور نیوگت فوق می‌توان دریافت کرد. سپس فایل dll دریافتی را درون پوشه plugins کنار فایل exe سرور RavenDB کپی کنید تا قابل استفاده شود. فایل Raven.Bundles.Versioning.dll باید در پوشه افزونه‌ها کپی شود و فایل Raven.Client.Versioning.dll به برنامه ما ارجاع داده خواهد شد.
              با استفاده از قابلیت document versioning می‌توان تغییرات اسناد را در طول زمان، ردیابی کرد؛ همچنین حذف یک سند، این سابقه را از بین نخواهد برد.
               تنظیمات اولیه آن به این صورت است که توسط شیء VersioningConfiguration به سشن جاری اعلام می‌کنیم که چند نگارش از اسناد را ذخیره کند. اگر Exclude آن به true تنظیم شود، اینکار صورت نخواهد گرفت.
              session.Store(new VersioningConfiguration
              {
                Exclude = false,
                Id = "Raven/Versioning/DefaultConfiguration",
                MaxRevisions = 5
              });
              تنظیم Id به Raven/Versioning/DefaultConfiguration، سبب خواهد شد تا VersioningConfiguration فوق به تمام اسناد اعمال شود. اگر نیاز است برای مثال تنها به BlogPosts اعمال شود، این Id را باید به Raven/Versioning/BlogPosts تنظیم کرد.
              بازیابی نگارش‌های مختلف یک سند، صرفا از طریق متد Load میسر است و در اینجا شماره Id نگارش به انتهای Id سند اضافه می‌شود. برای مثال "blogposts/1/revisions/1" به نگارش یک مطلب شماره یک اشاره می‌کند.
              برای بدست آوردن سه نگارش آخر یک سند باید از متد ذیل استفاده کرد:
               var lastThreeVersions = session.Advanced.GetRevisionsFor<BlogPost>(post.Id, 0, 3);
              نظرات مطالب
              پیاده سازی InstanceProvider برای سرویس های WCF
              صرف نظر از اینکه برنامه شما از چند DLL نهایتا تشکیل میشه، تمام این‌ها داخل یک Application Domain اجرا می‌شن. یعنی عملا یک برنامه‌ی واحد شما دارید که از اتصال قسمت‌های مختلف با هم کار می‌کنه. IoC Container هم تنظیماتش اول کار برنامه کش می‌شه. یعنی یکبار که تنظیم شد، در سراسر آن برنامه قابل دسترسی هست. بنابراین نیازی نیست همه جا تکرار بشه. یکبار آغاز کار برنامه اون رو تنظیم کنید کافی هست.
              مطالب
              استفاده از دیتابیس Sqlite در الکترون (قسمت دوم)
              در مقاله قبلی با یکی از کتابخانه‌های مدیریت دیتابیس sqlite آشنا شدیم و و یاد گرفتیم که چگونه یک دیتابیس جدید را بسازیم و اطلاعات را از آن دریافت کنیم. در این مقاله قصد داریم، بیشتر در مورد دستورات این کتابخانه بدانیم و بفهمیم که چگونه باید آن‌ها را به کار بست.

              دستورات بدون خروجی:
              یک سری از دستورات هستند که خروجی ندارند و رکوردی را باز نمی‌گردانند و برای اجرای دستوراتی چون افزودن، به روزرسانی و حذف بسیار مناسبند. اجرای  این دستورات را ما به متدی به نام run می‌سپاریم. در دفعه قبل که از این دستور استفاده کردیم، پارامتری برای تعیین کردن نداشت؛ ولی در این مقاله، دستور با پارامتر آن را اجرا می‌کنیم:
              ابتدا کدهای زیر را به فایل html، برای درج رکورد جدید اضافه می‌کنیم:
              First Name:<br/>
              <input type="text" id="txtfname" /><br/>
              Last Name:<br/>
              <input type="text" id=txtlname /><br/>
              Number:<br/>
              <input type="tel" id="txttel" /><br/>
              <button id="btnsubmit">Save</button><br/>
              برای خواندن مقادیر هم از این تابع کمک می‌گیریم:
              function GetValues()
              {
                let fname=$("#txtfname").val();
                let lname=$("#txtlname").val()
                let tel=$("#txttel").val();
                let row=
                {
                  fname:fname,
                  lname:lname,
                  number:tel
                };
                return row;
              }
              سپس تکه کد زیر را  با کمک  جی کوئری اضافه می‌کنیم:
              $("#btnsubmit").click((e)=>{
                e.preventDefault();
                let row=GetValues();
              
                //save in db
                //get last id
                let statement==db.prepare("select id from numbers order by id desc limit 1");
                let lastRecord=statement.getAsObject({}).id;
                row.id=lastRecord++;
                let count=db.prepare("select count(*) as count from numbers order by id desc").getAsObject({}).count;
                statement.free();
                let insertCommand="insert into numbers values(?,?,?,?)";
                db.run(insertCommand,[row.id,row.fname,row.lname,String(row.number)])
                let newcount=db.prepare("select count(*) as count from numbers order by id desc").getAsObject({}).count;
                SaveChanges();
              
              //show in the table
              if(count<newcount)
              {
                AddToTable(row);
              }
              });
              });
              متد GetValues  مقادیر را از input‌ها دریافت و در متغیر row نگهداری می‌کند. سپس برای درج رکورد، از آنجاکه ما فیلد id را افزایشی تعیین نکرده‌ایم، باید آخرین رکورد موجود در جدول را واکشی کنیم که برای این منظور از متد prepare کمک می‌گیریم. این متد در عوض کوئری داده شده، یک شیء statement را بر می‌گرداند که حاوی نتایج کوئری داده شده است؛ ولی هنوز به مرحله اجرا نرسیده است. برای اجرای دستور کوئری، دو متد وجود دارند که یکی step است و دیگری getAsObject می‌باشد. متد getAsObject برای زمانی خوب است که شما در حد یک رکورد خروجی دارید و می‌خواهید همان یک رکورد را به صورت شیء برگردانید؛ یعنی با "نام شی.نام خصوصیت" به آن دسترسی پیدا کنید. همانطور که می‌بینید ما درخواست فیلد id را کرده‌ایم و تنها یک رکورد را درخواست کرده‌ایم که باعث می‌شود به راحتی به فیلد id دسترسی داشته باشیم. وجود علامت {} داخل این متد به این معنی است که پارامتری برای ارسال وجود ندارد. ولی اگر قرار بود پارامتری را داشته باشیم، به این شکل می‌نوشتیم:
              var statement= db.prepare("SELECT * FROM NUMBERS WHERE fname=@fname AND lname=@lname");
              
              var result = statement .getAsObject({'@fname' :'ali', '@lname' : 'yeganeh'});
              اگر دوست داشتید دوباره از این دستور کوئری بگیرید ولی با مقادیر متفاوت، می‌توانید از متد bind کمک بگیرید:
              statement.bind(['hossein','yeganeh']);
              متد دیگری هم برای پاکسازی پارامترها به نام reset، وجود دارد که فضای اختصاصی و گرفته شده توسط پارامترها را باز می‌گرداند.

              متد step همانند متدهای next در cursor یا read در datareader عمل میکند و با هر بار صدا زدن، یک رکورد، به سمت جلو حرکت می‌کند. دریافت هر رکورد جاری توسط متد get و نوع خروجی آرایه انجام می‌شود:
              while(statement.step())
              {
                   var rec=statement.get();
              }
              بعد از اینکه کارمان با آن تمام شد، برای پاکسازی حافظه از متد free استفاده می‌کنیم. در دستورات بعد، شیء statement را مستقیما مورد استفاده قرار داده‌ایم و توسط آن تعداد رکوردها را دریافت کرده‌ایم. سپس با استفاده از متد run دستور درج را داده‌ایم. اینبار این متد را به شکل متفاوتی استفاده کردیم و به آن پارامتری هم دادیم. نحوه ارائه پارامتر به این متد، باید به صورت آرایه و به ترتیب علامت‌های ؟ باشد. نهایتا با دریافت تعداد رکوردهای جاری و مقایسه با تعداد رکوردهای سابق متوجه می‌شویم که آیا رکوردی اضافه شده است یا خیر؟ در صورتی که اضافه شده است، باید رکورد جدید در جدول، توسط جی کوئری نمایش داده شود و تغییرات دیتابیس هم روی دیسک سخت ذخیره شوند. چون دیتابیس مورد استفاده به صورت in-memory یعنی مقیم در حافظه مورد استفاده قرار میگیرد، باید کل دیتابیس، بر روی دیسک سخت رونویسی شود. متد SaveChanges شامل کد زیر است که حاوی کد ارسال پیام به Main Thread یا Main Process می‌باشد تا در دیسک سخت بنویسد:
                const {ipcRenderer} = require('electron');
              function SaveChanges()
              {
                  ipcRenderer.send("SaveToDb");
              }
              و برای ترد اصلی:
              const {ipcMain} = require('electron');
              ipcMain.on("SaveToDb", (event, arg) => {
                SaveToDb();
              });
              
              function SaveToDb()
              {
                var data=db.export();
                var buffer=new Buffer(data);
                fs.writeFileSync(dbPath,buffer);
              }

              پی نوشت :یکی از بهترین روش‌ها این است که از همین تعداد سطرها برای id رکورد استفاده کنیم. چرا که فیلد id به عنوان کلید اصلی است و چینش فیزیکی دارد و از لحاظ کارایی بهتر است ولی به علت آموزشی بودن مطلب و آشنایی با دیگر دستورات، این شیوه را انتخاب کردیم.

              ویرایش رکورد
              ابتدا template string سطر جدول را به شکل زیر تغییر می‌دهیم:
              function AddToTable(row)
              {
                let tableBody=$("#people");
                let rowTemplate=`<tr><td>${row.fname}</td><td>${row.lname}</td><td>${row.number}</td><td><button class=
                "btn btn-success btnupdate" data-id="${row.id}" >Edit</button></td></tr>`;
                tableBody.append(rowTemplate);
              }
              تغییری که رخ داده است این است که یک دکمه ویرایش، به صفحه اضافه شده‌است که خصوصیت data-id آن با id رکورد پر خواهد شد.
              سپس در تگ اسکریپت، در رویداد ready جی کوئری، این دستورات را اضافه می‌کنیم:
              $("#people").on('click','.btnupdate',function(e)
              {
                e.preventDefault();
              
                row.id=$(this).data("id");
                let row=GetValues();
                db.run("UPDATE NUMBERS SET FNAME=?,LNAME=?,NUMBER=? WHERE ID=?",[row.fname,row.lname,row.number,row.id]);
                SaveChanges();
                
                tr=$(this).closest("tr");
                let column=0;
                tr.find("td").each(function(index)
                {
                  oldRow=$(this);
                  switch(column)
                  {
                    case 0: //fname
                      oldRow.text(row.fname);
                      break;
                    case 1: //lname
                      oldRow.text(row.lname);
                      break;
                    case 2: //number
                      oldRow.text(row.number);
                      break;
                  }
                  column++;
                });
              });
              ابتدای id ذخیره شده در المان و مقادیر جدید را دریافت می‌کنیم. با استفاده از متد run کوئری به روزرسانی را به همراه پارامترها ارسال میکنیم و نتیجه را بر روی دیسک سخت ذخیره می‌کنیم. از اینجا به بعد نقش جی کوئری پر رنگ‌تر می‌شود و به خوبی می‌توانیم اهمیت آن را درک کنیم. سطر دکمه جاری را پیدا می‌کنیم و مقادیر جدید را ستون به ستون تغییر می‌دهیم.

              خواندن و بازگردانی رکوردها

              در مقاله قبلی با دستور each آشنا شدیم که یک متد غیرهمزمان بود و نتیجه هر رکورد را با یک callback به ما بازگشت میداد. در اصل این متد شامل 4 پارامتر است: پارامتر اول آن، کوئری ارسالی است. پارامتر دوم آن، پارامتر کوئری‌ها ، پارامتر سوم، تابع callback که به ازای هر رکورد اجرا می‌شود و پارامتر چهارم، تابع done می باشد. یعنی زمانی که کلیه رکوردها بازگشت داده شدند. شکل کامل آن به این صورت است:
              db.each("SELECT name,age FROM users WHERE age >= $majority",
                {$majority:18},
                function(row){console.log(row.name)},
                                              function(){console.log("done");}
                );


              در این مقاله با متد دیگری به نام exec نیز آشنا می‌شویم که بازگردانی مقادیر در آن به صورت همزمان صورت می‌گیرد و توانایی آن را دارد که چندین دستور select را بازگردانی کند. به عنوان مثال دستور زیر را در نظر بگیرید:
              SELECT ID FROM NUMBERS;SELECT FNAME,LNAME FROM NUMBERS
              شکل خروجی آن به این صورت خواهد بود:
               [
                       {columns: ['id'], values:[[1],[2],[3]]},
                       {columns: ['fname','lname'], values:[['ali','yeganeh'],['hossein','yeganeh'],['mohammad','yeganeh']]}
                  ]
              پس اگر بخواهیم کد بازیابی رکورهای جدول numbers را در این پروژه با این متد بازنویسی کنیم، از کد زیر استفاده می‌کنیم:
              var records=db.exec("select * from numbers");
              let values=records[0].values;
              let length=values.length;
              
              for(let i=0;i<length;i++)
              {
                let object=values[i];
                let row={
                  id:object[0],
                  fname:object[1],
                  lname:object[2],
                  number:object[3]
                };
                AddToTable(row);
              }
              در کد بالا از آنجا که تنها یک data result یا query result داریم، فقط اندیس 0 آن را می‌خوانیم و سپس تعداد آرایه‌ها را که برابر تعداد رکوردهاست، دریافت می‌کنیم و در یک حلقه، رکورد به رکورد، در جدول اضافه می‌کنیم.
              نظرات مطالب
              OutputCache در ASP.NET MVC
              درود بر شما؛ من می‌خواهم صفحه home سایتم رو روی سرور کش کنم تا زمانیکه کاربر میخواهد سایت رو باز کنه نیاز نباشه مدام بیاد تو برنامه واعمال دیتابیس انجام بده. با این کد تونستم این کار رو بکنم:
              [ OutputCache (CacheProfile = "FirstPageIndex" ,Location=OutputCacheLocation.Server) ]
              ولی زمانیکه یک مطلب جدید ثبت میکنم، کش پاک نمیشه و اون خبر رو نمایش نمیده. از کد زیر برای حذف کش استفاده میکنم:
              "Response. RemoveOutputCacheItem (Url. Action ( "index" , "home

              و سوال دوم می‌خواهم کش برای مرورگر قرار بدم تا زمانیکه کاربر مهمان وارد سایت شد و استان و شهر خودشو انتخاب کرد، تا زمانیکه کاربر مهمان خودش نرفته شهرش رو عوض نکرده، تو همون شهر بمونه ( حتی با بستن مرورگر ) و بر اساس اون کش دیتابیس مربوط به استان خودشو نشون بده ( برای هر استان یک دیتابیس دارم ). فقط میدونم باید از کش مرورگر استفاده کرد. میشه یه توضیحی بدید ممنون میشم ( به خصوص امنیت این کاری که می‌خواهم بکنم ).
               
              نظرات مطالب
              اجرای برنامه‌های ASP.NET به کمک وب سرور Apache توسط Mono در Ubuntu
              با سلام - تا اونجایی که من میدونم در مورد بانک‌های اطلاعاتی در لینوکس باید از mysql به جای sqlserver استفاده کرد.
              آیا راهی وجود داره که وب سایت طراحی شده با asp.net mvc که دارای بانک اطلاعاتی sqlserver هست رو در سرور لینوکس راه اندازی کرد؟
              نظرات مطالب
              نحوه صحیح تولید Url در ASP.NET MVC
              فرم ادیت رو به صورت strongly typed از نوع یک partial view مستقل درست کنید.
              سپس در کنترلر مرتبط، یک اکشن متد را مخصوص رندر کردن این partial view در نظر بگیرید. کار آن دریافت اطلاعات مرتبط با Model ارسالی به همین partial view است. سپس در آخر کار آن هم خواهیم داشت:

              return PartialView("_MyPartialViewName", data);
              حالا فراخوانی این اکشن متد توسط jQuery Ajax سبب پر شدن خودکار فیلدهای فرم strongly typed شما هم می‌شود. در حین درخواست، اطلاعات مدل از بانک اطلاعاتی دریافت شده و به Partial view ارسال می‌شود. چون strongly typed است، فیلدهای آن به صورت خودکار پر شده و نهایتا کل partial view به صورت یک رشته آماده شده در اختیار شما خواهد بود. بنابراین، قسمت عمده‌ای از کدهای سمت کاربر فوق کاهش پیدا می‌کنند. چون یک view کامل حاضر و آماده از سرور دریافت شده است که باید به صفحه توسط jQuery اضافه شود.

              +
              لطفا اینجا رو تبدیل به یک انجمن عمومی نکنید. عنوان بحث ساخت Url بود ... بعد تغییر جهت پیدا کرد به یک عنوان دیگر.
              با تشکر از توجه شما.
               
              مطالب
              ایجاد جداول بهینه سازی شده برای حافظه در SQL Server 2014
              پس از نگاهی به مفاهیم مقدماتی OLTP درون حافظه‌ای در SQL Server 2014، در ادامه به نحوه‌ی انجام تنظیمات خاص جداول بهینه سازی شده برای حافظه خواهیم پرداخت.


              ایجاد یک بانک اطلاعاتی با پشتیبانی از جداول بهینه سازی شده برای حافظه

              برای ایجاد جداول بهینه سازی شده برای حافظه، ابتدا نیاز است تا تنظیمات خاصی را به بانک اطلاعاتی آن اعمال کنیم. برای اینکار می‌توان یک بانک اطلاعاتی جدید را به همراه یک filestream filegroup ایجاد کرد که جهت جداول بهینه سازی شده برای حافظه، ضروری است؛ یا اینکه با تغییر یک بانک اطلاعاتی موجود و افزودن filegroup یاد شده نیز می‌توان به این مقصود رسید.
              در اینگونه جداول خاص، اطلاعات در حافظه‌ی سیستم ذخیره می‌شوند و برخلاف جداول مبتنی بر دیسک سخت، صفحات اطلاعات وجود نداشته و نیازی نیست تا به کش بافر وارد شوند. برای مقاصد ذخیره سازی نهایی اطلاعات جداول بهینه سازی شده برای حافظه، موتور OLTP درون حافظه‌ای آن، فایل‌های خاصی را به نام checkpoint در یک filestream filegroup ایجاد می‌کند که از آن‌ها جهت ردیابی اطلاعات استفاده خواهد کرد و نحوی ذخیره سازی اطلاعات در آن‌ها از شیوه‌ی با کارآیی بالایی به نام append only mode پیروی می‌کند.
              با توجه به متفاوت بودن نحوه‌ی ذخیره سازی نهایی اطلاعات اینگونه جداول و دسترسی به آن‌ها از طریق استریم‌ها، توصیه شده‌است که filestream filegroup‌های تهیه شده را در یک SSD یا Solid State Drive قرار دهید.

              پس از اینکه بانک اطلاعاتی خود را به روش‌های معمول ایجاد کردید، به برگه‌ی خواص آن در management studio مراجعه کنید. سپس صفحه‌ی file groups را در آن انتخاب کرده و در پایین برگه‌ی آن، در قسمت جدید memory optimized data، بر روی دکمه‌ی Add کلیک کنید. سپس نام دلخواهی را وارد نمائید.


              پس از ایجاد یک گروه فایل جدید، به صفحه‌ی files خواص بانک اطلاعاتی مراجعه کرده و بر روی دکمه‌ی Add کلیک کنید. سپس File type این ردیف اضافه شده را از نوع file stream data و file group آن‌را همان گروه فایلی که پیشتر ایجاد کردیم، تنظیم کنید. در ادامه logical name دلخواهی را وارد کرده و در آخر بر روی دکمه‌ی Ok کلیک کنید تا تنظیمات مورد نیاز جهت تعاریف جدول بهینه سازی شده برای حافظه به پایان برسد.


              این مراحل را توسط دو دستور T-SQL ذیل نیز می‌توان سریعتر انجام داد:
              USE [master]
              GO
              ALTER DATABASE [testdb2] 
                    ADD FILEGROUP [InMemory_InMemory] CONTAINS MEMORY_OPTIMIZED_DATA 
              GO
              ALTER DATABASE [testdb2] 
                    ADD FILE ( NAME = N'InMemory_InMemory', FILENAME = N'D:\SQL_Data\MSSQL11.MSSQLSERVER\MSSQL\DATA\InMemory_InMemory' ) 
                    TO FILEGROUP [InMemory_InMemory]
              GO

              ساختار گروه فایل بهینه سازی شده برای حافظه

              گروه فایل بهینه سازی شده برای حافظه، دارای چندین دربرگیرنده است که هر کدام چندین فایل را در خود جای خواهند داد:
              - Root File که در برگیرنده‌ی متادیتای اطلاعات است.
              - Data File که شامل ردیف‌های اطلاعات ثبت شده در جداول بهینه سازی شده‌ی برای حافظه هستند. این ردیف‌ها همواره به انتهای data file اضافه می‌شوند و دسترسی به آن‌ها ترتیبی است. کارآیی IO این روش نسبت به روش دسترسی اتفاقی به مراتب بالاتر است. حداکثر اندازه این فایل 128 مگابایت است و پس از آن یک فایل جدید ساخته می‌شود.
              - Delta File شامل ردیف‌هایی است که حذف شده‌اند. به ازای هر ردیف، حداقل اطلاعاتی از آن را در خود ذخیره خواهد کرد؛ شامل ID ردیف حذف شده و شماره تراکنش آن. همانطور که پیشتر نیز ذکر شد، این موتور جدید درون حافظه‌ای، برای یافتن راه چاره‌ای جهت به حداقل رسانی قفل گذاری بر روی اطلاعات، چندین نگارش از ردیف‌ها را به همراه timestamp آن‌ها در خود ذخیره می‌کند. به این ترتیب، هر به روز رسانی به همراه یک حذف و سپس ثبت جدید است. به این ترتیب دیگر بانک اطلاعاتی نیازی نخواهد داشت تا به دنبال رکورد موجود برگردد و سپس اطلاعات آن‌را به روز نماید. این موتور جدید فقط اطلاعات به روز شده را در انتهای رکوردهای موجود با فرمت خود ثبت می‌کند.


              ایجاد جداول بهینه سازی شده برای حافظه

              پس از آماده سازی بانک اطلاعاتی خود و افزودن گروه فایل استریم جدیدی به آن برای ذخیره سازی اطلاعات جداول بهینه سازی شده برای حافظه، اکنون می‌توانیم اینگونه جداول خاص را در کنار سایر جداول متداول موجود، تعریف و استفاده نمائیم:
              -- It is not a Memory Optimized
              CREATE TABLE tblNormal
              (
                 [CustomerID] int NOT NULL PRIMARY KEY NONCLUSTERED, 
                 [Name] nvarchar(250) NOT NULL,
                 CustomerSince DATETIME not NULL
                    INDEX [ICustomerSince] NONCLUSTERED
              )
              
              --  DURABILITY = SCHEMA_AND_DATA
              CREATE TABLE tblMemoryOptimized_Schema_And_Data
              (
                  [CustomerID] INT NOT NULL 
              PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 1000000),
                  [Name] NVARCHAR(250) NOT NULL,
                  [CustomerSince] DATETIME NOT NULL
              INDEX [ICustomerSince] NONCLUSTERED
              ) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)
              
              
              -- DURABILITY = SCHEMA_ONLY
              CREATE TABLE tblMemoryOptimized_Schema_Only
              (
                  [CustomerID] INT NOT NULL 
              PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 1000000),
                  [Name] NVARCHAR(250) NOT NULL,
                  [CustomerSince] DATETIME NOT NULL
              INDEX [ICustomerSince] NONCLUSTERED
              ) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY)
              در اینجا سه جدول را مشاهده می‌کنید که در بانک اطلاعاتی آماده شده در مرحله‌ی قبل، ایجاد خواهند شد. مورد اول یک جدول معمولی است که از آن برای مقایسه سرعت ثبت اطلاعات با سایر جداول ایجاد شده، استفاده خواهد شد.
              همانطور که مشخص است، دو جدول بهینه سازی شده برای حافظه، همان سه ستون جدول معمولی مبتنی بر دیسک سخت را دارا هستند؛ اما با این تفاوت‌ها:
              - دارای ویژگی MEMORY_OPTIMIZED = ON می‌باشند. به این ترتیب اینگونه جداول نسبت به جداول متداول مبتنی به دیسک سخت متمایز خواهند شد.
              - دارای ویژگی DURABILITY بوده و توسط مقدار SCHEMA_AND_DATA آن مشخص می‌کنیم که آیا قرار است اطلاعات و ساختار جدول، ذخیره شوند یا تنها قرار است ساختار جدول ذخیره گردد (حالت SCHEMA_ONLY).
              - بر روی ستون Id آن‌ها یک hash index ایجاد شده‌است که وجود آن ضروری است و در کل بیش از 8 ایندکس را نمی‌توان تعریف کرد.
              برخلاف ایندکس‌های B-tree جداول مبتنی بر سخت دیسک، ایندکس‌های جداول بهینه سازی شده برای حافظه، اطلاعات را تکرار نمی‌کنند. این‌ها صرفا اشاره‌گرهایی هستند به ردیف‌های اصلی اطلاعات. به این معنا که این ایندکس‌ها لاگ نشده و همچنین بر روی سخت دیسک ذخیره نمی‌شوند. کار بازسازی مجدد آن‌ها در اولین بار بازیابی بانک اطلاعاتی و آغاز آن به صورت خودکار انجام می‌شود. به همین جهت مباحثی مانند index fragmentation و نگهداری ایندکس‌ها دیگر در اینجا معنا پیدا نمی‌کنند.
              دو نوع ایندکس را در اینجا می‌توان تعریف کرد. اولین آن‌ها hash index است و دومین آن‌ها range index. هش ایندکس‌ها برای حالاتی که در کوئری‌ها از عملگر تساوی استفاده می‌شود بسیار مناسب هستند. برای عملگرهای مقایسه‌ای از ایندکس‌های بازه‌ای استفاده می‌شود.
              همچنین باید دقت داشت که پس از ایجاد ایندکس‌ها، دیگر امکان تغییر آن‌ها و یا تغییر ساختار جدول ایجاد شده نیست.
              همچنین ایندکس‌های تعریف شده در جداول بهینه سازی شده برای حافظه، تنها بر روی ستون‌هایی غیرنال پذیر از نوع BIN2 collation مانند int و datetime قابل تعریف هستند. برای مثال اگر سعی کنیم بر روی ستون Name ایندکسی را تعریف کنیم، به این خطا خواهیم رسید:
               Indexes on character columns that do not use a *_BIN2 collation are not supported with indexes on memory optimized tables.
              - در حین تعریف هش ایندکس‌ها، مقدار BUCKET_COUNT نیز باید تنظیم شود. هر bucket توسط مقداری که حاصل هش کردن یک ستون است مشخص می‌شود. کلیدهای منحصربفرد دارای هش‌های یکسان در bucketهای یکسانی ذخیره می‌شوند. به همین جهت توصیه شده‌است که حداقل مقدار bucket تعیین شده در اینجا مساوی یا بیشتر از مقدار تعداد کلیدهای منحصربفرد یک جدول باشد؛ مقدار پیش فرض 2 برابر توسط مایکروسافت توصیه شده‌است.
              - نوع‌های قابل تعریف ستون‌ها نیز در اینجا به موارد ذیل محدود هستند و جمع طول آن‌ها از 8060 نباید بیشتر شود:
               bit, tinyint, smallint, int, bigint, money, smallmoney, float, real, datetime, smalldatetime, datetime2,
              date, time, numberic, decimal, char(n),  varchar(n) ,nchar(n),  nvarchar(n), sysname, binary(n),
              varbinary(n), and Uniqueidentifier


              همچنین در management studio، گزینه‌ی جدید new -> memory optimized table نیز اضافه شده‌است و انتخاب آن سبب می‌شود تا قالب T-SQL ایی برای تهیه این نوع جداول، به صورت خودکار تولید گردد.


              البته این گزینه تنها برای بانک‌های اطلاعاتی که دارای گروه فایل استریم مخصوص جداول بهینه سازی شده برای حافظه هستند، فعال می‌باشد.


              ثبت اطلاعات در جداول معمولی و بهینه سازی شده برای حافظه و مقایسه کارآیی آن‌ها

              در مثال زیر، 100 هزار رکورد را در سه جدولی که پیشتر ایجاد کردیم، ثبت کرده و سپس مدت زمان اجرای هر کدام از مجموعه عملیات را بر حسب میلی ثانیه بررسی می‌کنیم:
              set statistics time off
              SET STATISTICS IO Off
              set nocount on
              go
              -----------------------------
              
              Print 'insert into tblNormal'
              
              DECLARE @start datetime = getdate()
              declare @insertCount int = 100000
              declare @startId int = 1
              declare @customerID int = @startId
              
              while @customerID < @startId + @insertCount
              begin
                  insert into tblNormal values (@customerID, 'Test', '2013-01-01T00:00:00')
                  set @customerID +=1
              end
              
              Print DATEDIFF(ms,@start,getdate());
              go
              -----------------------------
              
              Print 'insert into tblMemoryOptimized_Schema_And_Data'
              
              DECLARE @start datetime = getdate()
              declare @insertCount int = 100000
              declare @startId int = 1
              declare @customerID int = @startId
              
              while @customerID < @startId + @insertCount
              begin
                  insert into tblMemoryOptimized_Schema_And_Data values (@customerID, 'Test', '2013-01-01T00:00:00')
                  set @customerID +=1
              end
              Print DATEDIFF(ms,@start,getdate());
              Go
              -----------------------------
              
              Print 'insert into tblMemoryOptimized_Schema_Only'
              
              DECLARE @start datetime = getdate()
              declare @insertCount int = 100000
              declare @startId int = 1
              declare @customerID int = @startId
              
              while @customerID < @startId + @insertCount
              begin
                  insert into tblMemoryOptimized_Schema_Only values (@customerID, 'Test', '2013-01-01T00:00:00')
                  set @customerID +=1
              end
              Print DATEDIFF(ms,@start,getdate());
              
              Go
              با این خروجی تقریبی که بر اساس توانمندی‌های سخت افزاری سیستم می‌تواند متفاوت باشد:
               insert into tblNormal
              36423
              
              insert into tblMemoryOptimized_Schema_And_Data
              30516
              
              insert into tblMemoryOptimized_Schema_Only
              3176
              و برای حالت select خواهیم داشت:
               set nocount on
              print 'tblNormal'
              set statistics time on
              select count(CustomerID) from tblNormal
              set statistics time off
              go
              print 'tblMemoryOptimized_Schema_And_Data'
              set statistics time on
              select count(CustomerID) from tblMemoryOptimized_Schema_And_Data
              set statistics time off
              go
              print 'tblMemoryOptimized_Schema_Only'
              set statistics time on
              select count(CustomerID) from tblMemoryOptimized_Schema_Only
              set statistics time off
              go
              با این خروجی
               tblNormal
               SQL Server Execution Times:
              CPU time = 46 ms,  elapsed time = 52 ms.
              
              tblMemoryOptimized_Schema_And_Data
               SQL Server Execution Times:
              CPU time = 32 ms,  elapsed time = 33 ms.
              
              tblMemoryOptimized_Schema_Only
               SQL Server Execution Times:
              CPU time = 31 ms,  elapsed time = 30 ms.
              تاثیر جداول بهینه سازی شده برای حافظه را در 350K inserts بهتر می‌توان با نمونه‌های متداول مبتنی بر دیسک مقایسه کرد.


              برای مطالعه بیشتر

              Getting started with SQL Server 2014 In-Memory OLTP
              Introduction to SQL Server 2014 CTP1 Memory-Optimized Tables
              Overcoming storage speed limitations with Memory-Optimized Tables for SQL Server
              Memory-optimized Table – Day 1 Test
              Memory-Optimized Tables – Insert Test
              Memory Optimized Table – Insert Test …Again