مطالب
سرورهای متصل شده‌ی SQL Server و مبحث تراکنش‌ها

یکی از قابلیت‌های جالب SQL Server در یک شبکه محلی امکان link و اتصال آن‌ها به یکدیگر است. به این صورت امکان کوئری گرفتن (و یا اعمال متداول SQL ایی) از دو یا چند سرور مختلف با دستورات T-SQL میسر می‌شود؛ به نحوی که حس یکپارچگی دیتابیس‌های این سرورها را حین کوئری نوشتن خواهیم داشت.
برای مثال فرض کنید دو سرور SQL1 و SQL2 را در شبکه داریم. می‌خواهیم در سرور SQL1 اتصالی را به سرور SQL2 ایجاد کنیم.

USE master

EXEC sp_addlinkedserver
'SQL2',
N'SQL Server'

sp_addlinkedsrvlogin @useself='false ', @rmtsrvname = 'SQL2',
@rmtuser = 'sa',
@rmtpassword = 'pass#'

دستورات T-SQL فوق کار ثبت یک liked server جدید و اعمال مشخصات کاربری که توسط آن قرار است به سرور SQL2 دسترسی داشت، انجام می‌دهند.
اکنون جهت بررسی این اتصال در سرور SQL1 کوئری زیر را اجرا می‌کنیم:

select * from sql2.faxManager.dbo.tblErja

که نحوه‌ی فراخوانی جدول مورد نظر باید به صورت Server.DatabaseName.dbo.TableName در آن رعایت شود.
تا اینجا همه چیز خوب است. مشکل از زمانی شروع می‌شود که بخواهیم تراکنش‌ها را نیز دخالت دهیم و اصولی کار کنیم. برای مثال:

begin distributed tran
select * from sql2.faxManager.dbo.tblErja
commit tran

خطایی که در ویندوز سرور 2003 با آخرین به روز رسانی‌ها ظاهر می‌شود به صورت زیر است:

The operation could not be performed because OLE DB provider for linked server was unable to begin a distributed transaction.
OLE DB provider for linked server returned message "The partner transaction manager has disabled its support for remote/network transactions.".


به صورت پیش فرض این نوع تراکنش‌های توزیع شده غیرفعال هستند مگر اینکه فعال شوند و روش حل مشکل نیز به صورت زیر می‌باشد:
قبل از هر کاری به کنسول سرویس‌های ویندوز مراجعه کرده و از در حال اجرا بودن سرویس Distribute Transaction Coordinator اطمینان حاصل کنید.
سپس به قسمت زیر مراجعه نمائید:
Control Panel > Administrative Tools > Component Services


نود مربوط به Component Service را گشوده و سپس بر روی My Computer کلیک راست کرده و گزینه‌ی خواص را انتخاب کنید.
در صفحه‌ی بازه شده به برگه‌ی MSTDC مراجعه کرده و بر روی دکمه‌ی Security Configuration کلیک نمائید.
اکنون تنظیمات آن‌را مطابق شکل زیر تغییر دهید.


این تنظیم باید بر روی هر دو سرور SQL1 و SQL2 انجام شود.

پس از این تغییرات که شامل راه اندازی مجدد سرویس Distribute Transaction Coordinator نیز خواهد شد، مشکل خطای فوق برطرف شده و امکان استفاده از تراکنش‌ها در linked servers نیز میسر می‌شود.

مشکل دیگری که به آن برخوردم خطای زیر است:

Unable to start a nested transaction for OLE DB provider for linked server . A nested transaction was required because the XACT_ABORT option was set to OFF.
OLE DB provider for linked server returned message "Cannot start more transactions on this session.".


برای حل این مشکل یک سطر زیر را باید به ابتدای کوئری خود اضافه کرد که جزو الزامات تراکنش‌های توزیع شده است و به این صورت از rollback کامل تمامی دستورات موجود فراخوانی شده T-SQL در صورت بروز کوچکترین خطایی اطمینان حاصل می‌کند:
SET XACT_ABORT ON


برای مطالعه بیشتر:
MSDTC Troubleshooting

اشتراک‌ها
کتابخانه DoFilter

DoFilter is a Full Responsive Bootstrap Multipurpose Filtering and Shorting plugin built on Latest Version of Bootstrap Framework (v3.3.7), HTML5, CSS3 and jQuery. Its wonderful features can be used to Filtering and Shorting your website content as like Portfolio, Service, Team, Blog, Products, Pricing, FAQ etc. Also you can customize it for Filtering and Shorting any your website content.

DoFilter is a Full Responsive Isotope Multipurpose Filtering and Shorting plugin. Filtering can hide and show item elements with the filter option. Items that match that filter will be shown. DoFilter Responsive Isotope Multipurpose Filtering and Shorting can rearrange the order of item elements based on their data. Items that do not match will be hidden.

There are 19+ filter styles with 100% responsive design. Indeed, you will be able to showcase your content on mobile devices such as smart phones and tablets. It has many features like CSS3 and jQuery animations, single and multiple Shorting and Filtering. All the codes are clean and well organized. Also have proper help documentation. It looks great with all types of devices. 

کتابخانه DoFilter
مطالب
JQuery 1.3 ارائه شد

روز قبل، نگارش رسمی و نهایی jQuery 1.3 ارائه شد و بلافاصله هم فایل مخصوص آن جهت بکارگیری intellisense ویژوال استودیو، توسط علاقمندان تهیه و عرضه گردید.

تازه‌های این نگارش:
  • Sizzle : یک موتور CSS selector قدرتمند (400 درصد بهبود سرعت نسبت به عملکرد کتابخانه قبلی در این مورد)
  • بازنگری در نحوه مدیریت رخدادها
  • موتور تزریق HTML‌ بسیار سریع (تا 15 برابر سریعتر نسبت به کتابخانه قبلی)
  • موتور بسیار سریع موقعیت یابی
  • تشخیص نوع مرورگر در آن متوقف شده و بجای آن از تشخیص ویژگی‌ها برای ماندگاری بیشتر این کتابخانه در سال‌های آتی استفاده گردیده است. (بجای بررسی userAgent ، از باگ‌های شناخته شده یا ویژگی‌های خاص، جهت تشخیص مرورگر استفاده می‌شود تا بازه وسیعی از محصولات مشابه را بتوانند پوشش دهند)
لیست کامل این تغییرات را در این آدرس می‌توانید ملاحظه نمائید.
  • جهت مشاهده و آزمایش بهبود کارآیی این کتابخانه لطفا به این صفحه مراجعه کنید. (لیست کامل این آزمایشات در انتهای صفحه لیست تغییرات، قابل دسترسی است)
همچنین تیم مربوطه در پی آن است تا در طی یکی دو هفته آینده، نگارش jQuery 1.3.1 را نیز بر اساس بازخوردهای جدید جامعه برنامه نویس‌ها ارائه دهد.

مانند همیشه این کتابخانه در دو نگارش فشرده شده (جهت استفاده در سایت‌ها) و نگارش عادی و حجیم‌تر (جهت برنامه نویسی و دیباگ کردن کدها) ارائه شده است. چون کدها در Google code هاست شده احتمالا دسترسی به آن مشکل خواهد بود. این مجموعه را از این آدرس می‌توانید دریافت کنید.

توصیه می‌شود از سرورهای سریع گوگل بعنوان هاست کتابخانه جی‌کوئری سایت‌های خود استفاده کنید. برای این منظور از آدرس زیر می‌توان استفاده کرد:
http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js


مطالب
استفاده از BroadcastChannel در جاوا اسکریپت

یکی از API ‌های کاربردی و جدید در دنیای وب، BroadcastChannel است که امکان ارسال اطلاعات بین window‌ها ، Tab‌ها و iframe‌های مختلف را که در یک دامنه هستند، فراهم می‌کند.  برای مثال اگر شما در مرورگری در پنجره‌های مختلف یک سایت را باز کرده باشید، با تغییر در یکی از این پنجره‌ها، قادر خواهید بود سایر پنجر‌ها را هم مطلع کنید تا در صورت نیاز، مجددا بارگذاری شوند. 


چرا از این API استفاده کنیم؟ 

یکی از وب سایت‌های مورد علاقه‌ی خود را در مرورگر باز کنید. مثلا یوتیوب و لاگین کنید. حالا در پنجره‌ی جدیدی، همین وب سایت را مجددا باز کرده و لاگین کنید. حالا در یکی از پنجره‌ها، از یوتیوب Logout کنید. خب شما حالا در یکی از پنجره‌ها لاگین هستید و در یکی دیگر Logout کرده‌اید. حالا پنجره‌های مرورگر شما دارای دو وضعیت متفاوت هستند. Logged-in در برابر Logged-out و این گاهی باعث دردسر خواهد شد.

این وضعیت حتی میتواند باعث خطرهای امنیتی نیز بشود. تصور کنید که کاربری در یک فضای عمومی مثل یک کافی شاپ وارد سایت شما شده‌است و داشبرد مخصوص به خود را باز کرده‌است. بنا به دلایلی کاربر قصد ترک محل را کرده و طبیعتا از برنامه شما Logout خواهد کرد . در این حالت اگر این کاربر برنامه شما را در صفحات مختلف مرورگر باز کرده باشد و لاگین نیز کرده باشد، هر کسی که بعد از او قصد استفاده از این کامپیوتر را داشته باشد ،  میتواند به اطلاعات کاربر مورد نظر در آن صفحات دسترسی پیدا کند؛ چه این اطلاعات روی صفحه باشد و چه مثلا اطلاعات یک JWT token.  چون کاربر فراموش کرده در صفحات دیگر هم Logout کند.


کد نویسی BroadcastChannel API 

در نگاه اول، استفاده از این API  ممکن است سخت به نظر برسد؛ ولی در واقع خیلی راحت است. برای نمونه قطعه کد زیر را درنظر بگیرید: 

<!DOCTYPE html>
<body>
  <!-- The title will change to greet the user -->
  <h1 id="title">Hey</h1>
  <input id="name-field" placeholder="Enter Your Name"/>
</body>
<script>
var bc = new BroadcastChannel('gator_channel');
(()=>{
  const title = document.getElementById('title');
  const nameField = document.getElementById('name-field');
  const setTitle = (userName) => {
    title.innerHTML = 'Hey ' + userName;
  }
  bc.onmessage = (messageEvent) => {
    // If our broadcast message is 'update_title' then get the new title from localStorage
    if (messageEvent.data === 'update_title') {
      // localStorage is domain specific so when it changes in one window it changes in the other
      setTitle(localStorage.getItem('title'));
    }
  }

  // When the page loads check if the title is in our localStorage
  if (localStorage.getItem('title')) {
    setTitle(localStorage.getItem('title'));
  } else {
    setTitle('please tell us your name');
  }
  nameField.onchange = (e) => {
    const inputValue = e.target.value;
    // In the localStorage we set title to the user's input
    localStorage.setItem('title', inputValue);
    // Update the title on the current page
    setTitle(inputValue);
    // Tell the other pages to update the title
    bc.postMessage('update_title');
  }
})()
</script>

این کد شامل یک Input باکس و یک title است. وقتی کاربر نام خود را در Input باکس وارد می‌کند، برنامه آن را در Localstorage با کلیدی به نام userName ذخیره می‌کند و بعد title صفحه جاری را به سلام + userName تغییر می‌دهد. مثلا اگر کاربر در Input باکس، عبارت بابک را وارد کند، title صفحه به سلام بابک تغییر داده میشود.

بدون BroadcastChannel، چنانچه کاربر در پنجره‌های مختلف مرورگر، برنامه را باز کرده باشد، تغییری در Title آن صفحات داده نخواهد شد؛ مگر اینکه مجددا توسط کاربر بارگذاری شود. 

در کد فوق ما یک وهله از BroadcastChannel را به نام gator_channel ایجاد کرده‌ایم و بعد onmessage را مساوی متدی با یک آرگومان جهت دریافت پیام قرار داده‌ایم. در این متد چک شده که اگر نام پیام، مساوی update_title باشد، متغیر ذخیره شده‌ی در LocalStorage خوانده شود.

هربار که متد postMessage ، از BroadcastChannel را فراخوانی میکنیم، این متد، باعث اجرای متد onmessage در سایر پنجره‌ها می‌شود. پس اگر در پنجره‌ی جاری در Input باکس، کلمه فرهاد را بنویسیم، متد bc.postMessage('update_title') در پنجره جاری اجرا شده و باعث اجرای متد onmessage در سایر پنجره‌هایی که سایتمان در آن باز است میشود.


این API در چه حالت‌هایی کار میکند

برخلاف سایر API‌ها مثل window.postMessage، شما لازم نیست چیزی در مورد اینکه چند تا صفحه از سایتتان بر روی مرورگر جاری باز شده را بدانید. (توجه کنید که روی عبارت «مرورگر جاری» تاکید میکنم. چون اگر برنامه روی دو مرورگر مثلا Chrome و Firefox به صورت همزمان باز باشد، این API فقط روی صفحات باز مرورگر جاری فراخوانی خواهد شد و نه مرورگر دوم؛ توضیحات بیشتر در ادامه داده شده است)

BroadcastChannel فقط روی مرورگر جاری و صفحاتی از یک دامنه، اجرا خواهد شد. این به این معنا است که شما می‌توانید پیام‌هایتان را از دامنه مثلا : https://alligator.io به دامنه https://alligator.io/js/broadcast_channels ارسال کنید. تنها نکته‌ای که لازم است تا رعایت شود این است که آبجکت BroadcastChannel در هر دو صفحه، از یک نام برای channel استفاده کرده باشند:

const bc = new BroadcastChannel('alligator_channel');
bc.onmessage = (eventMessage) => {
  // do something different on each page
}


در حالتهای زیر این API کار نخواهد کرد:

هاست‌های متفاوت:

https://alligator.io 

https://www.aligator.io

پورت‌های متفاوت:

https://alligator.io 

https://alligator.io :8080

پروتکل‌های متفاوت:

https://alligator.io 

http://alligator.io 

و یا برای مثال اگر مثلا در مرورگر Chrome یکی از صفحات به صورت Incognito باز شده باشد.


سازگاری این API با مرورگرهای مختلف

با توجه به اطلاعات سایت caniuse.com، این API در 75.6% مرورگرها پشتیبانی میشود. ولی مرورگرهای Safari و Internet Explorer از این API پشتیبانی نمی‌کنند. همچنین امکان استفاده از این API توسط کتابخانه sysend.js نیز فراهم شده‌است.


چه نوع پیامهایی را می‌توانید به کمک این API ارسال کنید

  • تمامی تایپ‌ها (Boolean,Null, Undefined,Number,BigInt, String) به غیر از symbol
  • آبجکتهای Boolean و String
  • Dates
  • Regular Expressions
  • Blobs
  • Files, File Lists
  • Array Buffer, ArrayBufferViews
  • ImageBitmaps, ImageDates
  • Arrays,Objects,Maps and Sets
در صورت ارسال تایپی از نوع symbol، با خطایی در سمت ارسال کننده مواجه خواهید شد.

قطعه کد زیر، بجای string، یک object را ارسال می‌کند:

bc.onmessage = (messageEvent) => {
  const data = messageEvent.data
  // If our broadcast message is 'update_title' then get the new title from localStorage
  switch (data.type) {
    case 'update_title':
      if (data.title){
        setTitle(data.title);
      }
      else
        setTitle(localStorage.getItem('title'));
      break
    default:
      console.log('we received a message')
  }
};
// ... Skipping Code
bc.postMessage({type: 'update_title', title: inputValue});


چه کارهایی را میتوانید به کمک این API انجام دهید

چیزهای زیادی را میتوان مجسم کرد. محتمل‌ترین گزینه، به اشتراک گذاری state جاری برنامه است. برای مثال اگه از کتابخانه‌های flux یا redux برای مدیریت state برنامه استفاده می‌کنید، به کمک این API می‌توانید state جاری را در تمامی صفحات باز برنامه، بروز رسانی کنید. حتی می‌توانید به چیزی شبیه به machine state فکر کنید.

یا مثلا آخرین وضعیت سبد خرید کالای مشتری و یا موجودی کالاها، در یک سایت خرید آنلاین. همچنین به اشتراک گذاری فایلهای حجیم مثل عکس و غیره جهت جلوگیری از دانلود مجدد آن‌‌ها در سایر صفحات.

به کمک دستور ()bc.close در هر زمانی میتوانید channel باز شده را ببندید و مجددا بسته به وضعیت برنامه، آن را باز کنید.

نظرات مطالب
شروع به کار با EF Core 1.0 - قسمت 10 - استفاده از امکانات بومی بانک‌های اطلاعاتی
ارتقاء به EF Core 3.0
کمی بالاتر در مورد «یک نکته‌ی تکمیلی: اهمیت دقت داشتن به امضای متد FromSql در EF Core 2.0 » بحث شد. به صورت خلاصه مشکل آن چنین چیزی است:
v1 = context.Customers.FromSql($"SELECT * FROM Customers WHERE City = {city}");

var sql = $"SELECT * FROM Customers WHERE City = {city}";
v2 = context.Customers.FromSql(sql);
حالت v1 یک کوئری پارامتری نهایی را ایجاد می‌کند و حالت v2 خیر که مستعد به حملات تزریق اس‌کیوال است. برای رفع این مشکل، متدهای FromSql ،ExecuteSql و ExecuteSqlAsync در EF Core 3.0 تغییر نام یافته‌اند و دیگر وجود خارجی ندارند. اینبار متدهای FromSqlRaw، ExecuteSqlRaw و ExecuteSqlRawAsync و همچنین FromSqlInterpolated، ExecuteSqlInterpolated و ExecuteSqlInterpolatedAsync معرفی شده‌اند تا هدف از کاربرد آن‌ها دقیقا مشخص شود:
context.Products.FromSqlRaw(
    "SELECT * FROM Products WHERE Name = {0}",
    product.Name);

context.Products.FromSqlInterpolated(
    $"SELECT * FROM Products WHERE Name = {product.Name}");
یعنی جائیکه Raw است، نباید از Interpolated strings استفاده کنید.
نظرات مطالب
کوئری هایی با قابلیت استفاده ی مجدد
من یک دور بازخورد‌های شما را خواندم اما متوجه موردی که برای شما ابهام ایجاد کرده نشدم.
آیا شما از Entity Framework استفاده می‌کنید؟ اگر پاسخ مثبت است، خود EF لایه‌ی Repository را پیاده سازی کرده است، و این پیاده سازی یک IQueryable جهت انجام Query‌های متفاوت در اختیار شما قرار می‌دهد. شما می‌توانید مستقیما از DbContext سمت لایه‌ی سرویس استفاده کنید و داده‌ها را جهت استفاده برای استفاده کننده‌ی لایه‌ی سرویس فراهم کنید. 
لایه‌ی سرویس باید داده‌ها را درون حافظه برگرداند، نه اینکه یک IQueryable برگرداند که استفاده کننده آن را اجرا کند.
از Repository در لایه‌ی سرویس استفاده کنید.
مطالب
پرسش و پاسخ‌های متداول ایجاد یک وبلاگ بلاگری
هر از چندگاهی دوستان پیغام می‌گذارند که چگونه تاریخ وبلاگم را فارسی کنم یا یک قالب فارسی خوب را از کجا می‌شود تهیه کرد و امثال آن. تمام این‌ها را به صورت یک FAQ کوچک در اینجا قرار می‌دهم تا برای عموم علاقمندان قابل استفاده باشد.

پرسش پاسخ
برای ایجاد یک وبلاگ بلاگری از کجا شروع کنم؟ با داشتن یک اکانت جی میل، به آدرس زیر مراجعه کرده و هر تعداد که مایل بودید می‌توانید وبلاگ ایجاد نمائید:
قالب‌های پیش فرض گوگل چنگی به دل نمی‌زنند و بود و نبود آن‌ها احساس نمی‌شود! چه باید کرد؟ برای تهیه قالب‌های فارسی به آدرس زیر مراجعه نمائید:
تاریخ فارسی را چه کنم؟ روش دوستان ، روش من
برنامه نویسم! کدهای من یا نمایش داده نمی‌شود یا فرمت آن‌ها هنگام ارسال به هم می‌ریزد. چه باید کرد؟ روش مورد علاقه‌ی من ، روش مایکروسافت (+ و + و افزونه‌ای برای این‌کار)
آخرین نظرات کاربران سایت را چگونه در ستون کناری سایت نمایش دهم؟ یکی از ویجت‌های پرکاربرد گوگل در بلاگر، ویجت فید است. هر بلاگ دارای دو فید مطالب و نظرات است. ویجت فید را اضافه کرده و سپس آدرس فید نظرات سایت خود را به آن معرفی کنید. همیشه آخرین 5 نظر ارسالی را نمایش می‌دهد. این ویجت کاربردهای قابل توجهی می‌تواند داشته باشد.
آمار مراجعان به سایت را باید در کجا ملاحظه کرد؟ جای پیش فرضی وجود ندارد! یک اکانت Analytics برای خود تهیه کرده و آن‌را به سایت اضافه کنید. همچنین پس از آن یک اکانت فید برنر را نیز برای خود تهیه کنید. فیدبرنر اکنون جزیی از گوگل است و پس از معرفی آدرس فید سایت خود به آن، یک آدرس جدید به همراه آیکونی برای نمایش در سایت به شما می‌دهد که می‌توانید تگ‌های آن تصویر ‌را توسط ویجت html نمایش دهید. همچنین از همین طریق نیز می‌توان اشتراک از طریق ایمیل را به وبلاگ اضافه کرد.
کامنت‌های سایت من در بیرون از سایت باز می‌شوند. چرا؟ به این مطلب مراجعه نمائید:
چگونه برای بلاگ خود قسمت ارسال نظرات را تهیه نمایم؟ من از سرویس فرم ساز Zoho‌ استفاده می‌کنم که در سال 2008 جزو برترین‌های وب شناخته شده بود. بعد از ایجاد فرم خود و دریافت کد مربوطه، یک مطلب جدید را ارسال کنید و در بدنه‌ی آن ، کدی را که Zoho به شما ارائه می‌دهد قرار دهید. سپس لینک این مطلب را توسط یک ویجت html به کنار سایت اضافه کنید تا همیشه در دسترس باشد.

اشتراک‌ها
طراحی جدید Entity Framework Profiler
نکته جالب آن، استفاده از قابلیت اجرای خارج از مرورگر برنامه‌های سیلورلایت است. کل برنامه با سیلورلایت تهیه شده. هم نسخه دسکتاپ را فراهم آورده و هم نسخه تحت وب را.
طراحی جدید Entity Framework Profiler
مطالب
مکان دریافت درایور‌های جدید SQLite مخصوص دات نت

کتابخانه‌ی System.Data.SQLite مدت مدیدی توسط Robert Simpson در آدرس http://sourceforge.net/projects/sqlite-dotnet2 به روز می‌شد و قابل دسترسی بود. اما این پروژه از سال 2010 متوقف شده و آخرین نسخه‌ی موجود آن 1.0.66.0 است. به همین جهت fork جدیدی از این پروژه در آدرس ذیل (که جزو دومین SQLite نیز می‌باشد) جهت به روز نگه داشتن آن تشکیل شده است:

در زمان نگارش این مطلب، نگارش 1.0.73.0 این پروژه که در بردارنده‌ی SQLite 3.7.6.3 است، از آدرس فوق قابل دریافت می‌باشد.

مطالب
ASP.NET MVC #6

آشنایی با انواع ActionResult

در قسمت چهارم، اولین متد یا اکشنی که به صورت خودکار توسط VS.NET به برنامه اضافه شد، اینچنین بود:

using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
}

توضیحات تکمیلی مرتبط با خروجی از نوع ActionResult ایی را که مشاهده می‌کنید، در این قسمت ارائه خواهد شد.
رفتار یک کنترلر توسط متدهایی که در آن کلاس تعریف می‌شوند، مشخص می‌گردد. هر متد هم از طریق یک URL مجزا قابل دسترسی و فراخوانی خواهد بود. این متدها که به آن‌ها اکشن نیز گفته می‌شود باید عمومی بوده، استاتیک یا متد الحاقی (extension method) نباشند و همچنین دارای پارامترهایی از نوع ref و out نیز نباشند.
هر درخواست رسیده، به یک کنترلر و متدی عمومی در آن توسط سیستم مسیریابی، نگاشت خواهد شد. اگر علاقمند باشید که در یک کلاس کنترلر، متدی عمومی را از این سیستم خارج کنید، تنها کافی است آن‌را با ویژگی (attribute) به نام NonAction مزین کنید:

using System.Web.Mvc;

namespace MvcApplication2.Controllers
{
public class HomeController : Controller
{
[NonAction]
public string ShowData()
{
return "Text";
}

public ActionResult Index()
{
ViewBag.Message = string.Format("{0}/{1}/{2}",
RouteData.Values["controller"],
RouteData.Values["action"],
RouteData.Values["id"]);
return View();
}

public ActionResult Search(string data = "*")
{
// do something ...
return View();
}
}
}

چند نکته در این مثال قابل ذکر است:
الف) در اینجا اگر شخصی آدرس http://localhost/home/showdata را درخواست نماید، با توجه به استفاده از ویژگی NonAction، با پیغام یافت نشد یا 404 مواجه می‌گردد.
ب) صرفنظر از پارامترهای یک متد و ساختار کلاس جاری، اطلاعات مسیریابی از طریق شیء RouteData.Values نیز در دسترس هستند که نمونه‌ای از آن‌را در اینجا بر اساس مقادیر پیش فرض تعاریف مسیریابی یک پروژه ASP.NET MVC ملاحظه می‌نمائید.
ج) در متد Search، از قابلیت امکان تعریف مقداری پیش فرض جهت آرگومان‌ها در سی شارپ 4 استفاده شده است. به این ترتیب اگر شخصی آدرس http://localhost/home/search را وارد کند، چون پارامتری را ذکر نکرده است، به صورت خودکار از مقدار پیش فرض آرگومان data استفاده می‌گردد.


انواع Action Results در ASP.NET MVC

در ASP.NET MVC بجای استفاده مستقیم از شیء Response، از شیء ActionResult جهت ارائه خروجی یک متد استفاده می‌شود و مهم‌ترین دلیل آن هم مشکل بودن نوشتن آزمون‌های واحد برای شیء Response است که وهله سازی آن مساوی است با به کار اندازی موتور ASP.NET و Http Runtime آن توسط یک وب سرور (بنابراین در ASP.NET MVC سعی کنید شیء Response را فراموش کنید).
سلسه مراتب ActionResult‌های قابل استفاده در ASP.NET در تصویر زیر مشخص شده‌اند:


و در مثال زیر تقریبا انواع و اقسام ActionResult‌های مهم و کاربردی ASP.NET MVC را می‌توانید مشاهده کنید:

using System.Web.Mvc;

namespace MvcApplication2.Controllers
{
public class ActionResultsController : Controller
{
//http://localhost/actionresults/welcome
public string Welcome()
{
return "Hello, World";
}

//http://localhost/actionresults/index
public ActionResult Index() // or ContentResult
{
return Content("Hello, World");
}

//http://localhost/actionresults/SendMail
public void SendMail()
{
}

public ActionResult SendMailCompleted() // or EmptyResult
{
// do whatever
return new EmptyResult();
}

public ActionResult GetFile() // or FilePathResult
{
return File(Server.MapPath("~/content/site.css"), "text/css", "mySite.css");
}

public ActionResult UnauthorizedStatus() // or HttpStatusCodeResult/HttpUnauthorizedResult
{
return new HttpUnauthorizedResult("You need to login first.");
}

public ActionResult Status() // or HttpStatusCodeResult
{
return new HttpStatusCodeResult(501, "Server Error");
}

public ActionResult GetJavaScript() // or JavaScriptResult
{
return JavaScript("...JavaScript...");
}

public ActionResult GetJson() // or JsonResult
{
var obj = new { prop1 = 1, prop2 = "data" };
return Json(obj, JsonRequestBehavior.AllowGet);
}

public ActionResult RedirectTo() // or RedirectResult
{
return RedirectPermanent("http://www.site.com");
//return RedirectToAction("Home", "Index");
}

public ActionResult ShowView() // or ViewResult
{
return View();
}
}
}

چند نکته در این مثال وجود دارد:
1) مثلا متد GetJavaScript را درنظر بگیرید. در این متد خاص، چه بنویسید public ActionResult GetJavaScript یا بنویسید public JavaScriptResult GetJavaScript تفاوتی نمی‌کند. در سایر موارد هم به همین ترتیب است. علت را در تصویر سلسله مراتبی ActionResult‌ها می‌توان جستجو کرد. تمام این کلاس‌ها نوعی ActionResult هستند و از یک کلاس پایه به ارث رسیده‌اند.
2) مثلا ContentResult شبیه به همان Response.Write سابق ASP.NET عمل می‌کند. علت وجودی آن هم عدم وابستگی مستقیم به شیء Response و ساده‌تر سازی نوشتن آزمون‌های واحد برای این نوع اکشن متدها است.
3) منهای متد آخری که نمایش داده شده (ShowView)، هیچکدام از متدهای دیگر نیازی به View متناظر ندارند. یعنی نیازی نیست تا روی متد کلیک راست کرده و Add view را انتخاب کنیم. چون در همین متد کنترلر، کار Response به پایان می‌رسد و مرحله بعدی ندارد. مثلا در حالت return File، یک فایل به درون مرورگر کاربر Flush خواهد شد و تمام.
4) متد Welcome و متد Index در اینجا به یک صورت تفسیر می‌شوند. به این معنا که اگر خروجی متد تعریف شده در یک کنترلر از نوع ActionResult نباشد، به صورت پیش فرض درون یک ContentResult محصور خواهد شد.
5) اگر خروجی متدی در اینجا از نوع void باشد، با ActionResult ایی به نام EmptyResult یکسان خواهد بود. بنابراین با متدهای SendMail و SendMailCompleted به یک نحو رفتار می‌گردد.
6) return Json یاد شده که خروجی‌اش از نوع JsonResultاست در پیاده سازی‌های Ajax ایی کاربرد دارد.
7) جهت بازگرداندن حالت وضعیت 403 یا غیرمجاز می‌توان از return new HttpUnauthorizedResult استفاده کرد.
8) یا جهت اعلام مشکلی در سمت سرور به کمک return new HttpStatusCodeResultکد ویژه‌ای را می‌توان به کاربر نمایش داد.
9) به کمک return RedirectToAction می‌توان به یک کنترلر و متدی خاص در آن، کاربر را هدایت کرد.

و خلاصه اینکه تمام کارهایی را که پیشتر در ASP.NET Web forms ، مستقیما به کمک شیء Response انجام می‌دادید (Response.Write، Response.End، Response.Redirect و غیره)، اینبار به کمک یکی از ActionResult‌های یاد شده انجام دهید تا بتوان بدون نیاز به راه اندازی یک وب سرور، برای متدهای کنترلرها آزمون واحد نوشت. برای مثال:

[TestMethod]
public void TestMethod1()
{
    // Arrange
    var controller = new ActionResultsController();

    // Act
    var result = controller.Index() as ContentResult;

    // Assert
    Assert.NotNull(result);
    Assert.AreEqual( "Hello, World", result.Content);
}