مطالب
آشنایی با Refactoring - قسمت 6

در ادامه بحث «حذف کدهای تکراری»، روش Refactoring دیگری به نام "Extract Superclass" وجود دارد که البته در بین برنامه نویس‌های دات نت به نام Base class بیشتر مشهور است تا Superclass. هدف آن هم انتقال کدهای تکراری بین چند کلاس، به یک کلاس پایه و سپس ارث بری از آن می‌باشد.

یک مثال:
در WPF و Silverlight جهت مطلع سازی رابط کاربری از تغییرات حاصل شده در مقادیر داده‌ها، نیاز است کلاس مورد نظر، اینترفیس INotifyPropertyChanged را پیاده سازی کند:

using System.ComponentModel;

namespace Refactoring.Day6.ExtractSuperclass.Before
{
public class User : INotifyPropertyChanged
{
string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value) return;
_name = value;
raisePropertyChanged("Name");
}
}

public event PropertyChangedEventHandler PropertyChanged;
void raisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}


و نکته‌ی مهم این است که اگر 100 کلاس هم داشته باشید، باید این کدهای تکراری اجباری مرتبط با raisePropertyChanged را در آن‌ها قرار دهید. به همین جهت مرسوم است برای کاهش حجم کدهای تکرای، قسمت‌های تکراری کد فوق را در یک کلاس پایه قرار می‌دهند:

using System.ComponentModel;

namespace Refactoring.Day6.ExtractSuperclass.After
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

و سپس از آن ارث بری می‌کنند:

namespace Refactoring.Day6.ExtractSuperclass.After
{
public class User : ViewModelBase
{
string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value) return;
_name = value;
RaisePropertyChanged("Name");
}
}
}
}


به این ترتیب این کلاس پایه در ده‌ها و صدها کلاس قابل استفاده خواهد بود، بدون اینکه مجبور شویم مرتبا یک سری کد تکراری «اجباری» را copy/paste کنیم.

مثالی دیگر:
اگر با ORM های Code first کار کنید، نیاز است تا ابتدا طراحی کار توسط کلاس‌های ساده دات نتی انجام شود؛ که اصطلاحا به آن‌ها POCO یا Plain old CLR objects یا Plain old .NET Classes هم گفته می‌شود. در بین این کلاس‌ها، متداول است که یک سری از خصوصیات، تکراری و مشترک باشد؛ مثلا تمام کلاس‌ها تاریخ ثبت رکورد را هم داشته باشند به همراه نام کاربر و مشخصاتی از این دست. اینجا هم برای حذف کدهای تکراری، یک Base class طراحی می‌شود: (+)

نظرات مطالب
EF Code First #12
در صورتی که بخواهیم Context را به ازای هر ویندوز فرم زنده نگه داریم در WinForm، پیاده سازی زیر صحیح است:
static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Database.SetInitializer<LoanContext>(null);

            // تنظیمات اولیه برنامه که فقط یکبار باید در طول عمر برنامه انجام شود
            ObjectFactory.Initialize(x =>
            {
                x.For<IUnitOfWork>().Use<LoanContext>();
                x.For<IEmployeeService>().Use<EmployeeService>();
            });

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new frmMain());
        }
    }
و در frmMain به شکل زیر پیاده سازی را انجام دهیم:
 public partial class frmMain : Form
    {
        private IContainer _container;
        private IUnitOfWork _uow;

        public frmMain()
        {
            InitializeComponent();

            _container = ObjectFactory.Container.GetNestedContainer();
            _uow = _container.GetInstance<IUnitOfWork>();
        }

        private void btnHomeLoanRequest_Click(object sender, System.EventArgs e)
        {
            var employeeService = _container.GetInstance<IEmployeeService>();
            .
            .
            .
            _uow.SaveChanges();
        }
        private void btnEditLoan_Click(object sender, System.EventArgs e)
        {
            var categoryService = container.GetInstance<ICategoryService>();
            .
            .
            .
            _uow.SaveChanges();
        }
نظرات مطالب
چگونگی گزارشگیری از Business Objects مانند List توسط StimulSoft
با کد زیر
    public class Book
    {
        public string Title { get; set; }
        public int Id { get; set; }
        public Author Author { get; set; }
        public Publisher Publisher { get; set; }

        public IList<Book> GetBooks()
        {
            var books = new List<Book>();

            for (int i = 0; i < 10; i++)
            {
                var book = new Book()
                {
                    Id = i,
                    Title = $"Title {i}",
                    Author = new Author()
                    {
                        FirstName = $"Author {i} First Name",
                        LastName = $"Author {i} Last Name",
                        Person = new Person()
                        {
                            Type = $"Type {i}",
                            Value = $"Value {i}"
                        }
                    },
                    Publisher = new Report.Publisher()
                    {
                        Name = $"Publiser {i}"
                    }
                };
                books.Add(book);
            }
            return books;
        }
    }

    public class Author
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Person Person { get; set; }
    }

    public class Person
    {
        public string Type { get; set; }
        public string Value { get; set; }
    }

    public class Publisher
    {
        public string Name { get; set; }
    }
و فایل mrt زیر مشکلی دیده نشد.
مطالب
بررسی تفصیلی رابطه Many-to-Many در EF Code first
رابطه چند به چند در مطالب EF Code first سایت جاری، در حد تعریف نگاشت‌های آن بررسی شده، اما نیاز به جزئیات بیشتری برای کار با آن وجود دارد که در ادامه به بررسی آن‌ها خواهیم پرداخت:


1) پیش فرض‌های EF Code first در تشخیص روابط چند به چند

تشخیص اولیه روابط چند به چند، مانند یک مطلب موجود در سایت و برچسب‌های آن؛ که در این حالت یک برچسب می‌تواند به چندین مطلب مختلف اشاره کند و یا برعکس، هر مطلب می‌تواند چندین برچسب داشته باشد، نیازی به تنظیمات خاصی ندارد. همینقدر که دو طرف رابطه توسط یک ICollection به یکدیگر اشاره کنند، مابقی مسایل توسط EF Code first به صورت خودکار حل و فصل خواهند شد:
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Data.Entity.ModelConfiguration;

namespace Sample
{
    public class BlogPost
    {
        public int Id { set; get; }

        [StringLength(maximumLength: 450, MinimumLength = 1), Required]
        public string Title { set; get; }

        [MaxLength]
        public string Body { set; get; }

        public virtual ICollection<Tag> Tags { set; get; } // many-to-many

        public BlogPost()
        {
            Tags = new List<Tag>();
        }
    }

    public class Tag
    {
        public int Id { set; get; }

        [StringLength(maximumLength: 450), Required]
        public string Name { set; get; }

        public virtual ICollection<BlogPost> BlogPosts { set; get; } // many-to-many

        public Tag()
        {
            BlogPosts = new List<BlogPost>();
        }
    }

    public class MyContext : DbContext
    {
        public DbSet<BlogPost> BlogPosts { get; set; }
        public DbSet<Tag> Tags { get; set; }
    }

    public class Configuration : DbMigrationsConfiguration<MyContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = true;
        }

        protected override void Seed(MyContext context)
        {
            var tag1 = new Tag { Name = "Tag1" };
            context.Tags.Add(tag1);

            var post1 = new BlogPost { Title = "Title...1", Body = "Body...1" };
            context.BlogPosts.Add(post1);

            post1.Tags.Add(tag1);

            base.Seed(context);
        }
    }

    public static class Test
    {
        public static void RunTests()
        {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());

            using (var ctx = new MyContext())
            {
                var post1 = ctx.BlogPosts.Find(1);
                if (post1 != null)
                {
                    Console.WriteLine(post1.Title);
                }
            }
        }
    }
}
در این مثال، رابطه بین مطالب ارسالی در یک سایت و برچسب‌های آن به صورت many-to-many تعریف شده است و همینقدر که دو طرف رابطه توسط یک ICollection به هم اشاره می‌کنند، رابطه زیر تشکیل خواهد شد:


در اینجا تمام تنظیمات صورت گرفته بر اساس یک سری از پیش فرض‌ها است. برای مثال نام جدول واسط تشکیل شده، بر اساس تنظیم پیش فرض کنار هم قرار دادن نام دو جدول مرتبط تهیه شده است.
همچنین بهتر است بر روی نام برچسب‌ها، یک ایندکس منحصربفرد نیز تعیین شود: (^ و ^)


2) تنظیم ریز جزئیات روابط چند به چند در EF Code first

تنظیمات پیش فرض انجام شده آنچنان نیازی به تغییر ندارند و منطقی به نظر می‌رسند. اما اگر به هر دلیلی نیاز داشتید کنترل بیشتری بر روی جزئیات این مسایل داشته باشید، باید از Fluent API جهت اعمال آن‌ها استفاده کرد:
    public class TagMap : EntityTypeConfiguration<Tag>
    {
        public TagMap()
        {
            this.HasMany(x => x.BlogPosts)
                .WithMany(x => x.Tags)
                .Map(map =>
                    {
                        map.MapLeftKey("TagId");
                        map.MapRightKey("BlogPostId");
                        map.ToTable("BlogPostsJoinTags");
                    });
        }
    }

    public class MyContext : DbContext
    {
        public DbSet<BlogPost> BlogPosts { get; set; }
        public DbSet<Tag> Tags { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new TagMap());
            base.OnModelCreating(modelBuilder);
        }
    }
در اینجا توسط متد Map، نام کلیدهای تعریف شده و همچنین جدول واسط تغییر داده شده‌اند:


3) حذف اطلاعات چند به چند

برای حذف تگ‌های یک مطلب، کافی است تک تک آن‌ها را یافته و توسط متد Remove جهت حذف علامتگذاری کنیم. نهایتا با فراخوانی متد SaveChanges، حذف نهایی انجام و اعمال خواهد شد.
            using (var ctx = new MyContext())
            {
                var post1 = ctx.BlogPosts.Find(1);
                if (post1 != null)
                {
                    Console.WriteLine(post1.Title);
                    foreach (var tag in post1.Tags.ToList())
                        post1.Tags.Remove(tag);
                    ctx.SaveChanges();
                }
            }
در اینجا تنها اتفاقی که رخ می‌دهد، حذف اطلاعات ثبت شده در جدول واسط BlogPostsJoinTags است. Tag1 ثبت شده در متد Seed فوق، حذف نخواهد شد. به عبارتی اطلاعات جداول Tags و BlogPosts بدون تغییر باقی خواهند ماند. فقط یک رابطه بین آن‌ها که در جدول واسط تعریف شده است، حذف می‌گردد.

در ادامه اینبار اگر خود post1 را حذف کنیم:
                var post1 = ctx.BlogPosts.Find(1);
                if (post1 != null)
                {
                    ctx.BlogPosts.Remove(post1);
                    ctx.SaveChanges();
                }
علاوه بر حذف post1، رابطه تعریف شده آن در جدول BlogPostsJoinTags نیز حذف می‌گردد؛ اما Tag1 حذف نخواهد شد.
بنابراین دراینجا cascade delete ایی که به صورت پیش فرض وجود دارد، تنها به معنای حذف تمامی ارتباطات موجود در جدول میانی است و نه حذف کامل طرف دوم رابطه. اگر مطلبی حذف شد، فقط آن مطلب و روابط برچسب‌های متعلق به آن از جدول میانی حذف می‌شوند و نه برچسب‌های تعریف شده برای آن.
البته این تصمیم هم منطقی است. از این لحاظ که اگر قرار بود دو طرف یک رابطه چند به چند با هم حذف شوند، ممکن بود با حذف یک مطلب، کل بانک اطلاعاتی خالی شود! فرض کنید یک مطلب دارای سه برچسب است. این سه برچسب با 20 مطلب دیگر هم رابطه دارند. اکنون مطلب اول را حذف می‌کنیم. برچسب‌های متناظر آن نیز باید حذف شوند. با حذف این برچسب‌ها طرف دوم رابطه آن‌ها که چندین مطلب دیگر است نیز باید حذف شوند!


4) ویرایش و یا افزودن اطلاعات چند به چند

در مثال فوق فرض کنید که می‌خواهیم به اولین مطلب ثبت شده، تعدادی تگ جدید را اضافه کنیم:
                var post1 = ctx.BlogPosts.Find(1);
                if (post1 != null)
                {
                    var tag2 = new Tag { Name = "Tag2" };                    
                    post1.Tags.Add(tag2);
                    ctx.SaveChanges();
                }
در اینجا به صورت خودکار، ابتدا tag2 ذخیره شده و سپس ارتباط آن با post1 در جدول رابط ذخیره خواهد شد.

در مثالی دیگر اگر یک برنامه ASP.NET را درنظر بگیریم، در هربار ویرایش یک مطلب، تعدادی Tag به سرور ارسال می‌شوند. در ابتدای امر هم مشخص نیست کدامیک جدید هستند، چه تعدادی در لیست تگ‌های قبلی مطلب وجود دارند، یا اینکه کلا از لیست برچسب‌ها حذف شده‌اند:
                //نام تگ‌های دریافتی از کاربر  
                var tagsList = new[] { "Tag1", "Tag2", "Tag3" };

                //بارگذاری یک مطلب به همراه تگ‌های آن
                var post1 = ctx.BlogPosts.Include(x => x.Tags).FirstOrDefault(x => x.Id == 1);
                if (post1 != null)
                {
                    //ابتدا کلیه تگ‌های موجود را حذف خواهیم کرد
                    if (post1.Tags != null && post1.Tags.Any())
                        post1.Tags.Clear();

                    //سپس در طی فقط یک کوئری بررسی می‌کنیم کدامیک از موارد ارسالی موجود هستند
                    var listOfActualTags = ctx.Tags.Where(x => tagsList.Contains(x.Name)).ToList();
                    var listOfActualTagNames = listOfActualTags.Select(x => x.Name.ToLower()).ToList();

                    //فقط موارد جدید به تگ‌ها و ارتباطات موجود اضافه می‌شوند
                    foreach (var tag in tagsList)
                    {
                        if (!listOfActualTagNames.Contains(tag.ToLowerInvariant().Trim()))
                        {
                            ctx.Tags.Add(new Tag { Name = tag.Trim() });
                        }
                    }
                    ctx.SaveChanges(); // ثبت موارد جدید

                    //موارد قبلی هم حفظ می‌شوند
                    foreach (var item in listOfActualTags)
                    {
                        post1.Tags.Add(item);
                    }
                    ctx.SaveChanges();
                }
در این مثال فقط تعدادی رشته از کاربر دریافت شده است، بدون Id آن‌ها. ابتدا مطلب متناظر، به همراه تگ‌های آن توسط متد Include دریافت می‌شود. سپس نیاز داریم به سیستم ردیابی EF اعلام کنیم که اتفاقاتی قرار است رخ دهد. به همین جهت تمام تگ‌های مطلب یافت شده را خالی خواهیم کرد. سپس در یک کوئری، بر اساس نام تگ‌های دریافتی، معادل آن‌ها را از بانک اطلاعاتی دریافت خواهیم کرد؛ کوئری tagsList.Contains به where in در طی یک رفت و برگشت، ترجمه می‌شود:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name]
FROM [dbo].[Tags] AS [Extent1]
WHERE [Extent1].[Name] IN (N'Tag1',N'Tag2',N'Tag3')
 آن‌هایی که جدید هستند به بانک اطلاعاتی اضافه شده (بدون نیاز به تعریف قبلی آن‌ها)، آن‌هایی که در لیست قبلی برچسب‌های مطلب بوده‌اند، حفظ خواهند شد.
لازم است لیست موارد موجود را (listOfActualTags) از بانک اطلاعاتی دریافت کنیم، زیرا به این ترتیب سیستم ردیابی EF آن‌ها را به عنوان رکوردی جدید و تکراری ثبت نخواهد کرد.


5) تهیه کوئری‌های LINQ بر روی روابط چند به چند

الف) دریافت یک مطلب خاص به همراه تمام تگ‌های آن:
 ctx.BlogPosts.Where(p => p.Id == 1).Include(p => p.Tags).FirstOrDefault()
ب) دریافت کلیه مطالبی که شامل Tag1 هستند:

var posts = from p in ctx.BlogPosts
                 from t in p.Tags
                 where t.Name == "Tag1"
                 select p;
و یا :
 var posts = ctx.Tags.Where(x => x.Name == "Tag1").SelectMany(x => x.BlogPosts);
مطالب
پَرباد - راهنمای اتصال و پیاده‌سازی درگاه‌های پرداخت اینترنتی (شبکه شتاب)

پَرباد چیست؟

همانطور که همه ما میدانیم، اتصال و راه اندازی درگاه‌های پرداخت اینترنتی (شبکه شتاب)، از همان ابتدا کاری مشکل و  پر دردسر برای برنامه نویسان بود. هر بانک، سیستم متفاوت و مخصوص به خود را دارد و این بدان معنا است که برنامه نویسان باید کدهای کاملا متفاوت و همچنین پیاده سازی‌های متفاوتی را از روی فایل‌های PDF راهنمای بانکی، که در نهایت منجر به بی نظمی در پروژه‌ها می‌شود، بنویسند و البته مشکل بزرگتر آن است که پس از پیاده سازی هم اطمینان کاملی از صحت کدهای نوشته شده وجود ندارد؛ چه بسا که واحد‌های پشتیبانی درگاه‌های پرداخت هم افراد حرفه‌ای و آشنا با توسعه نرم افزار نیستند و اکثر اوقات نمی‌توان به آنها تکیه کرد.
برای راحتی کار برنامه نویسان حوضه فریم ورک دات نت، سیستمی جامع، اوپن سورس و کاملا رایگان، بدون نیاز به اضافه کردن هیچ گونه وب سرویسی تهیه شده است که به برنامه نویسان اجازه می‌دهد تنها با نوشتن چند خط کد، وب سایت خود را به پرداخت اینترنتی مجهز کنند. لطفا پیشنهادات، بحث‌ها و نظرات خود را در صفحه مخصوص این پروژه ارسال کنید.  
این سیستم در حال حاضر متشکل از درگاه‌های پرداخت اینترنتی بانک‌های ملت، سامان، پارسیان، تجارت و پاسارگاد است.
همچنین این سیستم در قالب یک Nuget Package برای نصب راحت در اپلیکیشن آماده شده است.


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

  • طریقه نصب
  • ایجاد صورتحساب و ارسال کاربر به درگاه پرداخت
  • تایید صورتحساب
  • مردود کردن صورتحساب قبل از انتقال وجه از مشتری به فروشنده
  • برگشت وجه به حساب مشتری پس از تأیید صورتحساب
  • درگاه مجازی پرداخت (برای تست وب اپلیکیشن، بدون داشتن حساب واقعی در درگاه‌های بانکی)
  • تنظیمات
  • ذخیره سازی اطلاعات پرداخت


طریقه نصب

PM> Install-Package Parbad

برای وب سایت‌های بر پایه فریم ورک MVC

PM> Install-Package Parbad.MVC5


ایجاد صورتحساب و ارسال کاربر به درگاه پرداخت

ابتدا یک شیٔ صورتحساب را به صورت زیر ایجاد کنید
var invoice = new Invoice( [Order Number], [Amount], [Verify URL]);

- Order Number شماره صورتحساب است و باید همیشه یک عدد یکتا باشد (تکراری نباشد).
- Amount مبلغ قابل پرداخت به ریال است.
- Verify URL یک آدرس در وب سایت شما، برای بازگشت مشتری پس از پرداخت و تأیید صورتحساب است.
برای مثال:
var invoice = new Invoice(1, 30000, "http://www.mywebsite.com/payment/verify" );
سپس صورتحساب را به درگاه مورد نظر ارسال میکنیم.
var result = Payment.Request(Gateways.Mellat, invoice);

شیٔ result حاوی شماره یکتا رجوع و وضعیت درخواست (موفقیت یا عدم موفقیت درخواست) است.
if (result.Status == RequestResultStatus.Success)
{
    // این متد، کاربر را به سمت وب سایت درگاه پرداخت هدایت میکند
    result.Process(Context);
}
else
{
    // در صورت تمایل می‌توانید پیغام مورد نظر از درگاه پرداخت را نمایش دهید
    var msg = result.Message;
}

در وب سایت‌های MVC می‌توانید به روش زیر عمل کنید

if (result.Status == RequestResultStatus.Success)
{
   // کاربر را به سمت وب سایت درگاه پرداخت هدایت میکند 
   return new RequestActionResult(result);
}
else
{
   return View("Error");
}


تأیید صورتحساب

پس از بازگشت کاربر از وب سایت بانک، باید از پرداخت صورتحساب توسط کاربر اطمینان حاصل کنید. کد زیر را باید در آدرسی که هنگام ساخت صورتحساب ذکر کرده بودید، قرار دهید.
var result = Payment.Verify(System.Web.HttpContext.Current);

شیٔ result در اینجا حاوی اطلاعاتی مانند: درگاه بانکی (که کاربر در آن صورتحساب را پرداخت کرده)، شماره رجوع، شماره تراکنش یکتای بانکی، وضعیت پرداخت و پیام درگاه است.
شما می‌توانید با بررسی این شیٔ، تصمیمات لازم را بگیرید.
if(result.Status == VerifyResultStatus.Success)
{
    // کاربر، صورتحساب را پرداخت کرده است و شما میتوانید ادامه عملیات خرید را انجام دهید
}
else
{
    // کاربر بنا به دلایلی صورتحساب را پرداخت نکرده است
    // شما همچنین میتوانید علت را در قالب یک پیام از پراپرتی پیام مشاهده کنید

    // بنابراین شما میتوانید این صورتحساب را در پایگاه داده خود مردود اعلام کنید
}


مردود کردن صورتحساب قبل از انتقال وجه از مشتری به فروشنده

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



همانطور که در تصویر می‌بینید، در هنگام بازگشت مشتری به وب سایت شما و تأیید کردن صورتحساب، شما می‌توانید اطلاعات تراکنش مورد نظر را که شامل، درگاه پرداخت بانکی، شماره سفارش و شماره رجوع است را دریافت کنید و سپس با استفاده از این اطلاعات، پایگاه داده خود را بررسی کرده و در صورت لزوم، متد Cancel را فراخوانی کنید. به این ترتیب به درگاه بانکی، هیچگونه تأییدیه ای اعلام نمی‌شود و این بدان معناست که اگر وجهی به حساب فروشگاه واریز شده باشد، پس از چند دقیقه (معمولا ۱۵ دقیقه) به حساب مشتری برگشت داده خواهد شد.


برگشت وجه به حساب مشتری پس از تأیید صورتحساب

var refundResult = Payment.Refund(new RefundInvoice([Order Number], [Amount]));
در اینجا، Order Number همان شماره سفارش صورتحساب و Amount مقداری از وجه و یا کل وجه برای برگشت به حساب مشتری است.
پس از آن شما می‌توانید نتیجه این عملیات را در شیٔ refundResult بررسی کنید.


درگاه مجازی پرداخت

درصورتیکه شما نیاز به تست عملکرد اپلیکیشن خود داشته باشید، نیازی به داشتن یک حساب واقعی در بانک‌های اینترنتی ندارید و می‌توانید اپلیکیشن خود را با یک درگاه مجازی بسیار ساده تست کنید. برای انجام این کار در هنگام ارسال صورتحساب، از میان درگاه‌های بانکی، درگاه مجازی پَرباد را انتخاب کنید.
var result = Payment.Request(Gateways.ParbadVirtualGateway, invoice);


در نتیجه در هنگام هدایت کاربر به درگاه پرداخت، کاربر به درگاه مجازی هدایت خواهد شد.

اما قبل از کار با درگاه مجازی باید در فایل web.config وب اپلیکیشن خود، تنظیمات زیر را قرار دهید:
<system.webServer>
  <handlers>
   <add name="ParbadGatewayPage" verb="*" path="Parbad.axd" type="Parbad.Web.Gateway.ParbadVirtualGatewayHandler" />
  </handlers>
</system.webServer>
در اینجا، درگاه مجازی به عنوان یک HttpHandler معرفی شده است. مقداری که در مشخصه path ذکر شده، در واقع آدرس درگاه مجازی است که شما می‌توانید به دلخواه خود آن را وارد کنید. ما در این مثال از آدرس parbad.axd استفاده کرده ایم.
و در نهایت در وب اپلیکیشن خود، مسیر ذکر شده را به صورت زیر معرفی کنید:
ParbadConfiguration.Gateways.ConfigureParbadVirtualGateway(new ParbadVirtualGatewayConfiguration("Parbad.axd"));
در نتیجه در هنگام هدایت کاربر به درگاه مجازی، شما باید در نوار آدرس مرورگر خود، مقداری را که تنظیم کرده اید مشاهده کنید.


نکته مهم: فراموش نکنید، قبل از انتشار نهایی وب سایت بر روی سرور (نمایش عمومی)، تنظیمات HttpHandler مربوط به این درگاه مجازی را از درون فایل web.config حذف کنید. بدین صورت، این درگاه از دسترس عموم خارج خواهد بود.

تنظیمات پَرباد

بهترین مکان برای درج این تنظیمات در اپلیکیشن‌های ASP.NET WebForms فایل Global.asax.cs و در اپلیکیشن‌های ASP.NET MVC فایل Startup.cs است.
ASP.NET Web Forms
public class Global : HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        // configurations
    }
}

ASP.NET MVC
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // configurations
    }
}

تنظیمات درگاه‌های پرداخت

قبل از ارتباط با درگاه‌های بانکی شبکه شتاب، باید مشخصات درگاه بانکی را که استفاده می‌کنید، تنظیم کنید.
برای مثال: تنظیم درگاه پرداخت بانک ملت


تنظیمات ذخیره سازی اطلاعات پرداخت

پَرباد برای ذخیره و بازیابی اطلاعات پرداخت، نیاز به یک منبع ذخیره سازی دارد.
منبع پیش فرض پَرباد، کلاس TemporaryMemoryStorage است که همانطور که از نام آن پیداست، اطلاعات را به صورت موقت در حافظه رَم سرور ذخیره میکند. اگر شما خودتان اطلاعات پرداخت را در پایگاه داده ذخیره میکنید، این منبع، گزینه مناسبی است به دلیل سرعت بسیار بالای حافظه رَم.
توجه: در نظر داشته باشید که اگر به هر دلیلی سرور و یا وب سایت شما، ری‌استارت شود، کلیه اطلاعات موجود در این منبع هم از بین خواهد رفت.
ذخیره و بازیابی توسط SQL Server
برای این منظور در قسمت تنظیمات، کد زیر را قرار داده و رشته اتصال و نام جدول پرداخت را معرفی کنید.
ParbadConfiguration.Storage = new SqlServerStorage("Connection String", "MyPaymentTableName");

فیلد‌های مورد نیاز در این جدول:

ذخیره و بازیابی اطلاعات توسط روش مورد نظر شما:
در صورتیکه مایلید ذخیره و بازیابی را به روش خود انجام دهید، کلاس Storage را پیاده سازی کنید
public class MyStorage : Storage
{
    // Implement methods here...
}

و کلاس مورد نظر را در تنظیمات به عنوان منبع، معرفی کنید.
ParbadConfiguration.Storage = new MyStorage();

لازم به ذکر است که این کلاس شامل متد‌های synchronous و همچنین asynchronous است. بنابراین در صورتیکه برای مثال در هنگام ارسال درخواست به بانک، از متد‌های async استفاده می‌کنید، نیازی به پیاده سازی کردن متد‌های synchronous نیست.
در صورتیکه هر گونه پیشنهاد یا انتقاد نسبت به کارکرد این سیستم دارید، صمیمانه منتظر شنیدن آن در راستای توسعه این سیستم هستم.
همچنین در صورت تمایل به توسعه آن، می‌توانید آن را در گیت هاب دنبال کنید و یا لطفا پیشنهادات، بحث‌ها و نظرات خود را در صفحه مخصوص این پروژه ارسال کنید. 
با تشکر.
مطالب
مقدمه‌ای بر یادگیری ماشین در #C با استفاده از ML.NET

هنگامی که درباره‌ی علم و یادگیری ماشینی فکر می‌کنیم، دو زبان برنامه‌نویسی بلافاصله به ذهن متبادر می‌شوند: پایتون و R. این دو زبان به شکل عمومی از بسیاری از الگوریتم‌های یادگیری ماشین رایج، تکنیکهای پیش‌پردازش داده‌ها و خیلی بیشتر از اینها پشتیبانی می‌کنند؛ بنابراین برای -تقریباً- هر مساله‌ی یادگیری ماشینی مورد استفاده قرار می‌گیرند. 
 با این‌حال، گاهی فرد یا شرکتی نمی‌تواند از پایتون یا R استفاده کند که می‌تواند به یکی از دلایل متعدد، از جمله وجود کد منبع در زبان دیگر یا نداشتن هیچ تجربه‌ای در پایتون یا R باشد. یکی از محبوب‌ترین زبان‌های امروزی، #C است که برای بسیاری از کاربردها مورد استفاده قرار می‌گیرد. مایکروسافت برای استفاده از قدرت یادگیری ماشین در #C، یک بسته را به نام ML.NET ایجاد کرده که همه‌ی قابلیت‌های یادگیری ماشین پایه را فراهم می‌کند. 
در این مقاله، به شما نشان خواهم داد که چگونه از ML.NET برای ایجاد یک مدل دسته‌بندی دوتایی بهره ببرید؛ قابلیت‌های AutoML را مورد استفاده قرار داده و از یک مدل Tensorflow با ML.NET استفاده کنید. کد کامل مخصوص مدل دسته‌بندی دوتایی را می‌توانید در GitHub بیابید.

افزودن ML.NET به پروژه‌ی #C
اضافه کردن ML.NET به یک پروژه‌ی #C یا #F آسان است. تنها کار لازم، اضافه کردن بسته‌ی Microsoft.ML یا در برخی موارد، -بسته به نیازمندی‌های پروژه- بسته‌های اضافی مانند: Microsoft.ML.ImageAnalytics, Microsoft.ML.TensorFlow یا Microsoft.ML.OnnxTransformer است. 


بارگذاری در یک دیتاست و ایجاد pipeline داده‌ها
بارگذاری و پیش‌پردازش یک مجموعه داده در ML.NET کاملا ً متفاوت از زمانی است که با دیگر بسته‌ها / چارچوب‌های یادگیری ماشین کار می‌کنیم. چون ما نیاز داریم به طور واضح، ساختار داده‌ها را بیان کنیم. برای انجام این کار، فایلی به نام ModelInput.cs را درون یک پوشه به نام DataModels ایجاد کرده و داخل این فایل، همه‌ی ستون‌های مجموعه داده‌های خود را ثبت خواهیم کرد. برای این مقاله، ما از مجموعه داده‌های ردیابی کلاه‌برداری کارت اعتباری استفاده می‌کنیم که می‌تواند آزادانه از Kaggle بارگیری شود. این مجموعه‌داده‌ها شامل ۳۱ ستون است. کلاس تراکنش (۰ یا ۱)، مقدار تراکنش، زمان تراکنش و نیز ۲۸ ویژگی بی‌نام (anonymous). 


using Microsoft.ML.Data;

namespace CreditCardFraudDetection.DataModels
{
    public class ModelInput
    {
        [ColumnName("Time"), LoadColumn(0)]
        public float Time { get; set; }

        [ColumnName("V1"), LoadColumn(1)]
        public float V1 { get; set; }

        [ColumnName("V2"), LoadColumn(2)]
        public float V2 { get; set; }

        [ColumnName("V3"), LoadColumn(3)]
        public float V3 { get; set; }

        [ColumnName("V4"), LoadColumn(4)]
        public float V4 { get; set; }

        [ColumnName("V5"), LoadColumn(5)]
        public float V5 { get; set; }

        [ColumnName("V6"), LoadColumn(6)]
        public float V6 { get; set; }

        [ColumnName("V7"), LoadColumn(7)]
        public float V7 { get; set; }

        [ColumnName("V8"), LoadColumn(8)]
        public float V8 { get; set; }

        [ColumnName("V9"), LoadColumn(9)]
        public float V9 { get; set; }

        [ColumnName("V10"), LoadColumn(10)]
        public float V10 { get; set; }

        [ColumnName("V11"), LoadColumn(11)]
        public float V11 { get; set; }

        [ColumnName("V12"), LoadColumn(12)]
        public float V12 { get; set; }

        [ColumnName("V13"), LoadColumn(13)]
        public float V13 { get; set; }

        [ColumnName("V14"), LoadColumn(14)]
        public float V14 { get; set; }

        [ColumnName("V15"), LoadColumn(15)]
        public float V15 { get; set; }

        [ColumnName("V16"), LoadColumn(16)]
        public float V16 { get; set; }

        [ColumnName("V17"), LoadColumn(17)]
        public float V17 { get; set; }

        [ColumnName("V18"), LoadColumn(18)]
        public float V18 { get; set; }

        [ColumnName("V19"), LoadColumn(19)]
        public float V19 { get; set; }

        [ColumnName("V20"), LoadColumn(20)]
        public float V20 { get; set; }

        [ColumnName("V21"), LoadColumn(21)]
        public float V21 { get; set; }

        [ColumnName("V22"), LoadColumn(22)]
        public float V22 { get; set; }

        [ColumnName("V23"), LoadColumn(23)]
        public float V23 { get; set; }

        [ColumnName("V24"), LoadColumn(24)]
        public float V24 { get; set; }

        [ColumnName("V25"), LoadColumn(25)]
        public float V25 { get; set; }

        [ColumnName("V26"), LoadColumn(26)]
        public float V26 { get; set; }

        [ColumnName("V27"), LoadColumn(27)]
        public float V27 { get; set; }

        [ColumnName("V28"), LoadColumn(28)]
        public float V28 { get; set; }

        [ColumnName("Amount"), LoadColumn(29)]
        public float Amount { get; set; }

        [ColumnName("Class"), LoadColumn(30)]
        public bool Class { get; set; }
    }
} 
در اینجا یک فیلد را برای هر یک از ستون‌های داخل مجموعه داده‌مان ایجاد می‌کنیم. نکته‌ی مهم، تعیین شاخص (Index)، نوع و ستون، به شکل صحیح است. حالا که داده‌های ما مدل‌سازی شده‌اند، باید قالب و شکل داده‌های خروجی خود را مدل کنیم. این کار می‌تواند به روشی مشابه با کدهای بالا انجام شود. 
 using Microsoft.ML.Data;

namespace CreditCardFraudDetection.DataModels
{
    public class ModelOutput
    {
        [ColumnName("PredictedLabel")]
        public bool Prediction { get; set; }

        public float Score { get; set; }
    }
}  
ما در این‌جا ۲ فیلد داریم. فیلد score نشان‌دهنده‌ی خروجی به شکل درصد است؛ در حالیکه فیلد prediction از نوع بولی است. اکنون که هر دو داده ورودی و خروجی را مدل‌سازی کرده‌ایم، می‌توانیم داده‌های واقعی خود را با استفاده از روش مونت‌کارلو بارگذاری کنیم.
IDataView trainingDataView = mlContext.Data.LoadFromTextFile<ModelInput>(
                                            path: dataFilePath,
                                            hasHeader: true,
                                            separatorChar: ',',
                                            allowQuoting: true,
                                            allowSparse: false);

ساخت و آموزش مدل
برای ایجاد و آموزش مدل، نیاز به ایجاد یک pipeline داریم که شامل پیش‌پردازش داده‌های مورد نیاز و الگوریتم آموزش است. برای این مجموعه داده‌ی خاص، انجام هر پیش‌پردازش بسیار دشوار است زیرا ۲۸ ویژگی بی‌نام دارد. بنابراین تصمیم گرفتم که آن را ساده نگه دارم و تنها همه‌ی ویژگی‌ها را الحاق کنم (این کار باید در ML.NET انجام شود).
var dataProcessPipeline = mlContext.Transforms.Concatenate("Features", new[] { "Time", "V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8", "V9", "V10", "V11", "V12", "V13", "V14", "V15", "V16", "V17", "V18", "V19", "V20", "V21", "V22", "V23", "V24", "V25", "V26", "V27", "V28", "Amount" });
برای مدل، الگوریتم LightGBM را انتخاب می‌کنم. این الگوریتم در واقع در Microsoft.ML از ابتدا وجود ندارد؛ بنابراین شما باید Microsoft.ML.LightGbm را نصب کنید تا قادر باشید از آن استفاده کنید.
// Choosing algorithm
var trainer = mlContext.BinaryClassification.Trainers.LightGbm(labelColumnName: "Class", featureColumnName: "Features");

// Appending algorithm to pipeline
var trainingPipeline = dataProcessPipeline.Append(trainer);
اکنون می‌توانیم مدل را با متد Fit، آموزش داده سپس با استفاده از mlContext.model.save ذخیره کنیم:
ITransformer model = trainingPipeline.Fit(trainingDataView);
mlContext.Model.Save(model , trainingDataView.Schema, <path>);

ارزیابی مدل
حالا که مدل ما آموزش دیده است، باید عملکرد آن را بررسی کنیم. ساده‌ترین راه برای انجام این کار، استفاده از اعتبارسنجی متقاطع (cross-validation) است. ML.Net به ما روش‌های اعتبارسنجی متقاطع را برای انواع مختلف داده‌های مختلف، ارایه می‌دهد. از آنجا که مجموعه داده‌های ما یک مجموعه داده دسته‌بندی دودویی است، ما از روش mlContext.BinaryClassification.CrossValidateNonCalibrated برای امتیازدهی به مدل خود استفاده خواهیم کرد:
var crossValidationResults = mlContext.BinaryClassification.CrossValidateNonCalibrated(trainingDataView, trainingPipeline, numberOfFolds: 5, labelColumnName: "Class");

انجام پیش‌بینی
پیش بینی داده‌های جدید با استفاده از ML.NET واقعاً سرراست و راحت است. ما فقط باید یک PredictionEngine، نمایشی دیگر را از مدل خود که به طور خاص، برای استنباط ساخته شده است، ایجاد کنیم و متد Predict آن را به عنوان یک شی ModelInput فراخوانی کنیم. 
var predEngine = mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(mlModel);

ModelInput sampleData = new ModelInput() {
    time = 0,
    V1 = -1.3598071336738,
    ...
};

ModelOutput predictionResult = predEngine.Predict(sampleData);

Console.WriteLine($"Actual value: {sampleData.Class} | Predicted value: {predictionResult.Prediction}"); 

Auto-ML 
نکته جالب دیگر در مورد ML.NET اجرای عالی Auto ML است. با استفاده از  Auto ML  فقط با مشخص کردن اینکه روی چه مشکلی کار می‌کنیم و ارائه داده‌های خود، می‌توانیم راه‌حل‌های اساسی و پایه‌ی یادگیری ماشین را بسازیم.
برای شروع کار با ML خودکار در ML.NET، باید  Visual Studio Extension - ML.NET Model Builder (Preview)  را بارگیری کنیم. این کار را می‌توان از طریق تب extensions انجام داد.
پس از نصب موفقیت آمیز افزونه، با کلیک راست روی پروژه‌ی خود در داخل Solution Ex می‌توانیم از Auto ML استفاده کنیم.

با این کار پنجره Model Builder باز می‌شود. سازنده‌ی مدل، ما را در روند ساخت یک مدل یادگیری ماشین راهنمایی می‌کند.
   
برای کسب اطلاعات در مورد چگونگی گذراندن مراحل مختلف، حتماً آموزش رسمی شروع کار را در سایت مایکروسافت، بررسی کنید. بعد از تمام مراحل، Model Builder به طور خودکار کد را تولید می‌کند.
 
استفاده از یک مدل پیش‌آموزش‌داده‌شده‌ی تنسورفلو (pre-trained) 
نکته‌ی جالب دیگر در مورد ML.NET این است که به ما امکان استفاده از مدل‌های Tensorflow و ONNX را برای استنباط ( inference ) می‌دهد. برای استفاده از مدل Tensorflow باید Microsoft.ML.TensorFlow را با استفاده از NuGet نصب کنیم. پس از نصب بسته‌های لازم، می‌توانیم با فراخوانی متد Model.LoadTensorFlowModel، یک مدل Tensorflow را بارگذاری کنیم. پس از آن، باید متد ScoreTensorFlowModel را فراخوانی کرده و نام لایه‌ی ورودی و خروجی را به آن ارسال کنیم.  
private ITransformer SetupMlnetModel(string tensorFlowModelFilePath)
{
    var pipeline = _mlContext.<preprocess-data>
           .Append(_mlContext.Model.LoadTensorFlowModel(tensorFlowModelFilePath)
                                               .ScoreTensorFlowModel(
                                                      outputColumnNames: new[]{TensorFlowModelSettings.outputTensorName },
                                                      inputColumnNames: new[] { TensorFlowModelSettings.inputTensorName },
                                                      addBatchDimensionInput: false));
 
    ITransformer mlModel = pipeline.Fit(CreateEmptyDataView());
 
    return mlModel;
}

اطلاعات بیشتر در مورد نحوه استفاده از مدل Tensorflow در ML.NET:
مطالب
بررسی ORM های مناسب جهت استفاده در اندروید
با آمدن ORM‌ها به دنیای برنامه نویسی، کار برنامه نویسی نسبت به قبل ساده‌تر و راحت‌تر شد. عدم استفاده کوئری‌های دستی، پشتیبانی از چند دیتابیس و از همه مهمتر و اصلی‌ترین هدف این ابزار "تنها درگیری با اشیا و مدل شیء گرایی" کار را پیش از پیش آسان‌تر نمود.
در این بین به راحتی می‌توان چندین نمونه از این ORM‌ها را  نام برد مثل IBatis , Hibernate ,Nhibernate و EF که از معروفترین آن‌ها هستند.
من در حال حاضر قصد شروع یک پروژه اندرویدی را دارم و دوست دارم بجای استفاده‌ی از Sqlitehelper، از یک ORM مناسب بهره ببرم که چند سوال برای من پیش می‌آید. آیا ORM ای برای آن تهیه شده است؟ اگر آری چندتا و کدامیک از آن‌ها بهتر هستند؟ شاید در اولین مورد کتابخانه‌ی Hibernate جاوا را نام ببرید؛ ولی توجه به این نکته ضروری است که ما در مورد پلتفرم موبایل و محدودیت‌های آن صحبت می‌کنیم. یک کتابخانه همانند Hibernate مطمئنا برای یک برنامه اندروید چه از نظر حجم نهایی برنامه و چه از نظر حجم بزرگش در اجرا، مشکل زا خواهد بود و وجود وابستگی‌های متعدد و دارا بودن بسیاری از قابلیت‌هایی که اصلا در بانک‌های اطلاعاتی موبایل قابل اجرا نیست، باعث می‌شود این فریمورک انتخاب خوبی برای یک برنامه اندروید نباشد.

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

OrmLight

این فریمورک مختص اندروید طراحی نشده ولی سبک بودن آن موجب شده‌است که بسیاری از برنامه نویسان از آن در برنامه‌های اندرویدی استفاده کنند. این فریم ورک جهت اتصالات JDBC و Spring و اندروید طراحی شده است.

نحوه معرفی جداول در این فریمورک به صورت زیر است:
@DatabaseTable(tableName = "users")
public class User {
    @DatabaseField(id = true)
    private String username;
    @DatabaseField
    private String password;
 
    public User() {
        // ORMLite needs a no-arg constructor
    }
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
 
    // Implementing getter and setter methods
    public String getUserame() {
        return this.username;
    }
    public void setName(String username) {
        this.username = username;
    }
    public String getPassword() {
        return this.password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
با استفاده از کلمات کلیدی DatabaseTable@ در بالای کلاس و DatabaseField@ در بالای هر پراپرتی به معرفی جدول و فیلدهای جدول می‌پردازیم.
سورس این فریمورک را می‌توان در گیت هاب یافت و مستندات آن در این آدرس قرار دارند.


SugarORM
این فریمورک مختص اندروید طراحی شده است. یادگیری آن بسیار آسان است و به راحتی به یاد می‌ماند. همچنین جداول مورد نیاز را به طور خودکار خواهد ساخت. روابط یک به یک و یک به چند را پشتیبانی می‌کند و عملیات CURD را با سه متد Save,Delete و Find که البته FindById هم جزء آن است، پیاده سازی می‌کند.

برای استفاده از این فریمورک نیاز است ابتدا متادیتا‌های زیر را به فایل manifest اضافه کنید:
<meta-data android:name="DATABASE" android:value="my_database.db" />
<meta-data android:name="VERSION" android:value="1" />
<meta-data android:name="QUERY_LOG" android:value="true" />
<meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="com.my-domain" />
برای تبدیل یک کلاس به جدول هم از کلاس این فریم ورک ارث بری می‌کنیم:
public class User extends SugarRecord<User> {
    String username;
    String password;
    int age;
    @Ignore
    String bio; //this will be ignored by SugarORM
 
    public User() { }
 
    public User(String username, String password,int age){
        this.username = username;
        this.password = password;
        this.age = age;
    }
}
بر خلاف OrmLight که باید فیلد جدول را معرفی می‌کردید، اینجا تمام پراپرتی‌ها به اسم فیلد شناخته می‌شوند؛ مگر اینکه در بالای آن از عبارت Ignore@ استفاده کنید.

باقی عملیات آن از قبیل اضافه کردن یک رکورد جدید یا حذف رکورد(ها) به صورت زیر است:
User johndoe = new User(getContext(),"john.doe","secret",19);
johndoe.save(); //ذخیره کاربر جدید در دیتابیس


//حذف تمامی کاربرانی که سنشان 19 سال است
List<User> nineteens = User.find(User.class,"age = ?",new int[]{19});
foreach(user in nineteens) {
    user.delete();
}
برای اطلاعات بیشتر به مستندات آن رجوع کنید.


GreenDAO
موقعیکه بحث کارآیی و سرعت پیش می‌آید نام GreenDAO هست که می‌درخشد. طبق گفته‌ی سایت رسمی آن این فریمورک میتواند در ثانیه چند هزار موجودیت را اضافه و به روزرسانی و بارگیری نماید. این لیست حاوی برنامه‌هایی است که از این فریمورک استفاده می‌کنند. جدول زیر مقایسه‌ای است بین این کتابخانه و OrmLight که نشان میدهد 4.5 برابر سریعتر از OrmLight عمل می‌کند.

غیر از این‌ها در زمینه‌ی حجم هم حرف‌هایی برای گفتن دارد. حجم این کتابخانه کمتر از 100 کیلوبایت است که در اندازه‌ی APK اثر چندانی نخواهد داشت.

آموزش راه اندازی  آن در اندروید استادیو، سورس آن در گیت هاب و مستندات رسمی آن.


Active Android
این کتابخانه از دو طریق فایل JAR و به شیوه maven قابل استفاده است که می‌توانید روش استفاده‌ی از آن را در این لینک ببینید و سورس اصلی آن هم در این آدرس قرار دارد. بعد از اینکه کتابخانه را به پروژه اضافه کردید، دو متادیتای زیر را که به ترتیب نام دیتابیس و ورژن آن هستند، به manifest اضافه کنید:
<meta-data android:name="AA_DB_NAME" android:value="my_database.db" />
<meta-data android:name="AA_DB_VERSION" android:value="1" />
بعد از آن  عبارت ;()ActiveAndroid.Initialize را در اکتیویتی‌های مدنظر اعمال کنید:
public class MyActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActiveAndroid.initialize(this);
 
        //ادامه برنامه
    }
}
برای معرفی کلاس‌ها به جدول هم از دو اعلان Table و Column مانند کد زیر به ترتیب برای معرفی جدول و فیلد استفاده می‌کنیم.
@Table(name = "User")
public class User extends Model {
    @Column(name = "username")
    public String username;
 
    @Column(name = "password")
    public String password;
 
    public User() {
        super();
    }
 
    public User(String username,String password) {
        super();
        this.username = username;
        this.password = password;
    }
}
جهت اطلاعات بیشتر در مورد این کتابخانه به مستندات آن رجوع کنید.


ORMDroid
 از آن دست کتابخانه‌هایی است که سادگی و کم حجم بودن شعار آنان است و سعی دارند تا حد ممکن همه چیز را خودکار کرده و کمترین کانفیگ را نیاز داشته باشد. حجم فعلی آن حدود 20 کیلوبایت بوده و نمی‌خواهند از 30 کیلوبایت تجاوز کند.

برای استفاده‌ی از آن ابتدا دو خط زیر را جهت معرفی تنظیمات به manifest اضافه کنید:
<meta-data
  android:name="ormdroid.database.name"
  android:value="your_database_name" />

<meta-data
  android:name="ormdroid.database.visibility"
  android:value="PRIVATE||WORLD_READABLE||WORLD_WRITEABLE" />
برای آغاز کار این کتابخانه، عبارت زیر را در هرجایی که مایل هستید مانند کلاس ارث بری شده از Application یا در ابتدای هر اکتیویتی که مایل هستید بنویسید. طبق مستندات آن صدا زدن چندباره این متد هیچ اشکالی ندارد.
ORMDroidApplication.initialize(someContext);
معرفی مدل جدول بانک اطلاعاتی هم از طریق ارث بری از کلاس Entity می‌باشد .
public class Person extends Entity {
  public int id;
  public String name;
  public String telephone;
}

//====================

Person p = Entity.query(Person.class).where("id=1").execute();
p.telephone = "555-1234";
p.save();

// یا

Person person = Entity.query(Person.class).where(eql("id", id)).execute();
p.telephone = "555-1234";
p.save();
کد بالا دقیقا یادآوری به EF هست ولی حیف که از Linq پشتیبانی نمی‌شود.
سورس آن در گیت هاب

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


نکته نهایی اینکه خوب می‌شود دوستانی که از این ORM‌های مختص اندروید استفاده کرده اند؛ نظراتشان را در مورد آن‌ها بیان کنند و مزایا و معایب آن‌ها را بیان کنند.
نظرات مطالب
شروع به کار با EF Core 1.0 - قسمت 14 - لایه بندی و تزریق وابستگی‌ها
من مشابه روشی که در مقاله jwt ارائه فرموده بودین، در کنترلر سرویس Product  رو تزریق کردم، و در اکشن متد Add سعی کردم یه نمونه از Product در جدول ثبت کنم. 
ولی ب این خطا مواجه شدم: «متاسفانه در حین پردازش درخواست جاری خطایی رخ داده‌است. »
1. چطور سیستم Error handling  رو خاموش کنم که خود Exception رو بتونم ببینم ؟
2. اشتباه من کجا بوده ؟ متن کد من اینه :
using Common.GuardToolkit;
using Entities;
using Microsoft.AspNetCore.Mvc;
using Services.Contracts;
using ViewModels;

namespace web.Controllers
{
    public class ProductController : Controller
    {
        private readonly IProductService _ProductService;

        public ProductController(IProductService ProductService)
        {
            _ProductService = ProductService;
            _ProductService.CheckArgumentIsNull(nameof(ProductService));
        }

        public IActionResult Add()
        {
            return View("ProductAdd");
        }

        [HttpPost] 
        public IActionResult Add(ProductAddModel model)
        {
            var product = new Product() { Name = model.Title, Price = 1, CategoryId = 1};
            
            _ProductService.AddNewProduct(product);
            return Json(model);

        }
    }
}

مطالب
تهیه خروجی PDF و اکسل از حاصل جستجوی پویای jqGrid به کمک PDF Report
پیشنیازها
- صفحه بندی و مرتب سازی خودکار اطلاعات به کمک jqGrid در ASP.NET MVC
- فعال سازی و پردازش جستجوی پویای jqGrid در ASP.NET MVC
- سفارشی سازی عناصر صفحات پویای افزودن و ویرایش رکوردهای jqGrid در ASP.NET MVC
- آشنایی با کتابخانه‌ی PDF Report


اضافه کردن دکمه‌ی خروجی به jqGrid

برای تهیه خروجی از jqGrid نیاز است بدانیم، اکنون در چه صفحه‌ای از اطلاعات قرار داریم؟ بر روی چه ستونی، مرتب سازی صورت گرفته‌است؟ بر روی کدام فیلدها با چه مقادیری جستجو انجام شده‌است؟ تا ... بتوانیم بر این مبنا، منبع داده‌ی موجود را فیلتر کرده و لیست نهایی را تبدیل به گزارش کنیم. گزارشی که دقیقا با اطلاعاتی که کاربر در صفحه مشاهده می‌کند، تطابق داشته باشد.
خوشبختانه تمام این سؤالات توسط متد توکار excelExport در سمت سرور قابل دریافت است:
@section Scripts
{
    <script type="text/javascript">
    $(document).ready(function () {
        $('#list').jqGrid({
            caption: "آزمایش ششم",
                 // مانند قبل
            }).navGrid(
                 // مانند قبل
                    }).jqGrid('navButtonAdd', '#pager', {
                        caption: "", buttonicon: "ui-icon-print", title: "خروجی پی دی اف",
                        onClickButton: function () {
                            $("#list").jqGrid('excelExport', { url: '@Url.Action("GetProducts", "Home")' });
                        }
                    });
        });
    </script>
}

در اینجا توسط متد navButtonAdd یک دکمه‌ی جدید را اضافه کرده‌ایم که کلیک بر روی آن سبب فراخوانی متد excelExport و ارسال اطلاعات گزارش به url تنظیم شده‌است. باید دقت داشت که این اطلاعات از طریق Http Get به سرور ارسال می‌شوند و دقیقا اجزای آن همان اجزای جستجوی پویای jqGrid است:
public ActionResult GetProducts(string sidx, string sord, int page, int rows,
                                             bool _search, string searchField, string searchString,
                                             string searchOper, string filters, string oper)
با این تفاوت که یک oper نیز به مجموعه‌ی پارامترهای ارسالی به سرور اضافه شده‌است. این oper در اینجا با excel مقدار دهی می‌شود.
البته چون تعداد این پارامترها بیش از اندازه شده‌است، بهتر است آن‌ها را تبدیل به یک کلاس کرد:
namespace jqGrid06.Models
{
    public class JqGridRequest
    {
        public string sidx { set; get; }
        public string sord { set; get; }
        public int page { set; get; }
        public int rows { set; get; }
        public bool _search { set; get; }
        public string searchField { set; get; }
        public string searchString { set; get; }
        public string searchOper { set; get; }
        public string filters { set; get; }
        public string oper { set; get; }
    }
}
و متد جستجوی پویا را به نحو ذیل بازنویسی نمود:
        public ActionResult GetProducts(JqGridRequest request)
        {
            var list = ProductDataSource.LatestProducts;

            var pageIndex = request.page - 1;
            var pageSize = request.rows;
            var totalRecords = list.Count;
            var totalPages = (int)Math.Ceiling(totalRecords / (float)pageSize);

            var productsQuery = list.AsQueryable();

            productsQuery = new JqGridSearch().ApplyFilter(productsQuery, request, this.Request.Form);
            productsQuery = productsQuery.OrderBy(request.sidx + " " + request.sord);

            if (string.IsNullOrWhiteSpace(request.oper))
            {
                productsQuery = productsQuery
                                    .Skip(pageIndex * pageSize)
                                    .Take(pageSize);
            }
            else if (request.oper == "excel")
            {
                productsQuery = productsQuery
                                    .Skip(pageIndex * pageSize);
            }

            var productsList = productsQuery.ToList();

            if (!string.IsNullOrWhiteSpace(request.oper) && request.oper == "excel")
            {
                new ProductsPdfReport().CreatePdfReport(productsList);
            }

            var productsData = new JqGridData
            {
                Total = totalPages,
                Page = request.page,
                Records = totalRecords,
                Rows = (productsList.Select(product => new JqGridRowData
                {
                    Id = product.Id,
                    RowCells = new List<string>
                    {
                        product.Id.ToString(CultureInfo.InvariantCulture),
                        product.Name,
                        product.AddDate.ToPersianDate(),
                        product.Price.ToString(CultureInfo.InvariantCulture)
                    }
                })).ToArray()
            };

            return Json(productsData, JsonRequestBehavior.AllowGet);
        }

توضیحات:
اکثر قسمت‌های این متد با متدی که در مطلب «فعال سازی و پردازش جستجوی پویای jqGrid در ASP.NET MVC» مشاهده کردید یکی است؛ برای مثال order by آن با استفاده از کتابخانه‌ی Dynamic LINQ به صورت پویا عمل می‌کند و متد ApplyFilter، کار تهیه where پویا را انجام می‌دهد.
فقط در اینجا بررسی و پردازش پارامتر oper نیز اضافه شده‌است. اگر این پارامتر مقدار دهی شده باشد، یعنی نیاز است کل اطلاعات را واکشی کرد؛ زیرا می‌خواهیم گزارش گیری کنیم و نه اینکه صرفا اطلاعات یک صفحه را به کاربر بازگشت دهیم. همچنین در اینجا List نهایی فیلتر شده به یک گزارش Pdf Report ارسال می‌شود. این گزارش چون نهایتا اطلاعات را در مرورگر کاربر Flush می‌کند، کار به اجرای سایر قسمت‌ها نخواهد رسید و همینجا گزارش نهایی تهیه می‌شود.



کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید
jqGrid06.7z
 
مطالب
ASP.NET MVC #5

بررسی نحوه انتقال اطلاعات از یک کنترلر به View‌های مرتبط با آن

در ASP.NET Web forms در فایل code behind یک فرم مثلا می‌توان نوشت Label1.Text و سپس مقداری را به آن انتساب داد. اما اینجا به چه ترتیبی می‌توان شبیه به این نوع عملیات را انجام داد؟ با توجه به اینکه در کنترلر‌ها هیچ نوع ارجاع مستقیمی به اشیاء رابط کاربری وجود ندارد و این دو از هم مجزا شده‌اند.
در پاسخ به این سؤال، همان مثال ساده قسمت قبل را ادامه می‌دهیم. یک پروژه جدید خالی ایجاد شده است به همراه HomeController ایی که به آن اضافه کرده‌ایم. همچنین مطابق روشی که ذکر شد، View ایی به نام Index را نیز به آن اضافه کرده‌ایم. سپس برای ارسال اطلاعات از یک کنترلر به View از یکی از روش‌های زیر می‌توان استفاده کرد:

الف) استفاده از اشیاء پویا

ViewBag یک شیء dynamic است که در دات نت 4 امکان تعریف آن میسر شده است. به این معنا که هر نوع خاصیت دلخواهی را می‌توان به این شیء انتساب داد و سپس این اطلاعات در View نیز قابل دسترسی و استخراج خواهد بود. مثلا اگر در اینجا به شیء ViewBag، خاصیت دلخواه Country را اضافه کنیم و سپس مقداری را نیز به آن انتساب دهیم:

using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Country = "Iran";
return View();
}
}
}

این اطلاعات در View مرتبط با اکشنی به نام Index به نحو زیر قابل بازیابی خواهد بود (نحوه اضافه کردن View متناظر با یک اکشن یا متد را هم در قسمت قبل با تصویر مرور کردیم):

@{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
<p>
Country : @ViewBag.Country
</p>

در این مثال، @ در View engine جاری که Razor نام دارد، به این معنا می‌باشد که این مقداری است که می‌خواهم دریافت کنی (ViewBag.Country) و سپس آن‌را در حین پردازش صفحه نمایش دهی.


ب) انتقال اطلاعات یک شیء کامل و غیر پویا به View

هر پروژه جدید MVC به همراه پوشه‌ای به نام Models است که در آن می‌توان تعاریف اشیاء تجاری برنامه را قرار داد. در پروژه جاری، یک کلاس ساده را به نام Employee به این پوشه اضافه می‌کنیم:

namespace MvcApplication1.Models
{
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
}

اکنون برای نمونه یک وهله از این شیء را در متد Index ایجاد کرده و سپس به view متناظر با آن ارسال می‌کنیم (در قسمت return View کد زیر مشخص است). بدیهی است این وهله سازی در عمل می‌تواند از طریق دسترسی به یک بانک اطلاعاتی یا یک وب سرویس و غیره باشد.

using System.Web.Mvc;
using MvcApplication1.Models;

namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Country = "Iran";

var employee = new Employee
{
Email = "name@site.com",
FirstName = "Vahid",
LastName = "N."
};

return View(employee);
}
}
}

امضاهای متفاوت (overloads) متد کمکی View هم به شرح زیر هستند:

ViewResult View(Object model)
ViewResult View(string viewName, Object model)
ViewResult View(string viewName, string masterName, Object model)


اکنون برای دسترسی به اطلاعات این شیء employee در View متناظر با این متد، چندین روش وجود دارد:

@{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
<div>
Country: @ViewBag.Country <‪br />
FirstName: @Model.FirstName
</div>

می‌توان از طریق شیء استاندارد دیگری به نام Model (که این هم یک شیء dynamic است مانند ViewBag قسمت قبل)، به خواص شیء یا مدل ارسالی به View جاری دسترسی پیدا کرد که یک نمونه از آن‌را در اینجا ملاحظه می‌کنید.
روش دوم، بر اساس تعریف صریح نوع مدل است به نحو زیر:

@model MvcApplication1.Models.Employee
@{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
<div>
Country: @ViewBag.Country
<‪br />
FirstName: @Model.FirstName
</div>

در اینجا در مقایسه با قبل، تنها یک سطر به اول فایل View اضافه شده است که در آن نوع شیء Model تعیین می‌گردد (کلمه model هم در اینجا با حروف کوچک شروع شده است). به این ترتیب اینبار اگر سعی کنیم به خواص این شیء دسترسی پیدا کنیم، Intellisense ویژوال استودیو ظاهر می‌شود. به این معنا که شیء Model بکارگرفته شده اینبار دیگر dynamic نیست و دقیقا می‌داند که چه خواصی را باید پیش از اجرای برنامه در اختیار استفاده کننده قرار دهد.
به این روش، روش Strongly typed view هم گفته می‌شود؛ چون View دقیقا می‌داند که چون نوعی را باید انتظار داشته باشد؛ تحت نظر کامپایلر قرار گرفته و همچنین Intellisense نیز برای آن مهیا خواهد بود.
به همین جهت این روش Strongly typed view، در بین تمام روش‌های مهیا، به عنوان روش توصیه شده و مرجح مطرح است.
به علاوه استفاده از Strongly typed views یک مزیت دیگر را هم به همراه دارد: فعال شدن یک code generator توکار در VS.NET به نام scaffolding. یک مثال ساده:
تا اینجا ما اطلاعات یک کارمند را نمایش دادیم. اگر بخواهیم یک لیست از کارمندها را نمایش دهیم چه باید کرد؟
روش کار با قبل تفاوتی نمی‌کند. اینبار در return View ما، یک شیء لیستی ارائه خواهد شد. در سمت View هم با یک حلقه foreach کار نمایش این اطلاعات صورت خواهد گرفت. راه ساده‌تری هم هست. اجازه دهیم تا خود VS.NET، کدهای مرتبط را برای ما تولید کند.
یک کلاس دیگر به پوشه مدل‌های برنامه اضافه کنید به نام Employees با محتوای زیر:

using System.Collections.Generic;

namespace MvcApplication1.Models
{
public class Employees
{
public IList<Employee> CreateEmployees()
{
return new[]
{
new Employee { Email = "name1@site.com", FirstName = "name1", LastName = "LastName1" },
new Employee { Email = "name2@site.com", FirstName = "name2", LastName = "LastName2" },
new Employee { Email = "name3@site.com", FirstName = "name3", LastName = "LastName3" }
};
}
}
}

سپس متد جدید زیر را به کنترلر Home اضافه کنید.

public ActionResult List()
{
var employeesList = new Employees().CreateEmployees();
return View(employeesList);
}

برای اضافه کردن View متناظر با آن، روی نام متد کلیک راست کرده و گزینه Add view را انتخاب کنید. در صفحه ظاهر شده:


تیک مربوط به Create a strongly typed view را قرار دهید. سپس در قسمت Model class، کلاس Employee را انتخاب کنید (نه Employees جدید را، چون از آن می‌خواهیم به عنوان منبع داده لیست تولیدی استفاده کنیم). اگر این کلاس را مشاهده نمی‌کنید، به این معنا است که هنوز برنامه را یکبار کامپایل نکرده‌اید تا VS.NET بتواند با اعمال Reflection بر روی اسمبلی برنامه آن‌را پیدا کند. سپس در قسمت Scaffold template گزینه List را انتخاب کنید تا Code generator توکار VS.NET فعال شود. اکنون بر روی دکمه Add کلیک نمائید تا View نهایی تولید شود. برای مشاهده نتیجه نهایی مسیر http://localhost/Home/List باید بررسی گردد.


ج) استفاده از ViewDataDictionary

ViewDataDictionary از نوع IDictionary با کلیدی رشته‌ای و مقداری از نوع object است. توسط آن شیء‌ایی به نام ViewData در ASP.NET MVC به نحو زیر تعریف شده است:

public ViewDataDictionary ViewData { get; set; }

این روش در نگارش‌های اولیه ASP.NET MVC بیشتر مرسوم بود. برای مثال:

using System;
using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["DateTime"] = "<‪br/>" + DateTime.Now;
return View();
}
}
}

و سپس جهت استفاده از این ViewData تعریف شده با کلید دلخواهی به نام DateTime در View متناظر با اکشن Index خواهیم داشت:

@{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
<div>
DateTime: @ViewData["DateTime"]
</div>

یک نکته امنیتی:
اگر به مقدار انتساب داده شده به شیء ViewDataDictionary دقت کنید، یک تگ br هم به آن اضافه شده است. برنامه را یکبار اجرا کنید. مشاهده خواهید کرد که این تگ به همین نحو نمایش داده می‌شود و نه به صورت یک سطر جدید HTML . چرا؟ چون Razor به صورت پیش فرض اطلاعات را encode شده (فراخوانی متد Html.Encode در پشت صحنه به صورت خودکار) در صفحه نمایش می‌دهد و این مساله از لحاظ امنیتی بسیار عالی است؛ زیرا جلوی بسیاری از حملات cross site scripting یا XSS را خواهد گرفت.
احتمالا الان این سؤال پیش خواهد آمد که اگر «عالمانه» بخواهیم این رفتار نیکوی پیش فرض را غیرفعال کنیم چه باید کرد؟
برای این منظور می‌توان نوشت:
@Html.Raw(myString)

و یا:
<div>@MvcHtmlString.Create("<h1>HTML</h1>")</div>

به این ترتیب خروجی Razor دیگر encode شده نخواهد بود.


د) استفاده از TempData

TempData نیز یک dictionary دیگر برای ذخیره سازی اطلاعات است و به نحو زیر در فریم ورک تعریف شده است:

public TempDataDictionary TempData { get; set; }

TempData در پشت صحنه از سشن‌های ASP.NET جهت ذخیره سازی اطلاعات استفاده می‌کند. بنابراین اطلاعات آن در سایر کنترلرها و View ها نیز در دسترس خواهد بود. البته TempData یک سری تفاوت هم با سشن معمولی ASP.NET دارد:
- بلافاصله پس از خوانده شدن، حذف خواهد شد.
- پس از پایان درخواست از بین خواهد رفت.
هر دو مورد هم به جهت بالابردن کارآیی برنامه‌های ASP.NET MVC و مصرف کمتر حافظه سرور درنظر گرفته‌ شده‌اند.
البته کسانی که برای بار اول هست با ASP.NET مواجه می‌شوند، شاید سؤال بپرسند این مسایل چه اهمیتی دارد؟ پروتکل HTTP، ذاتا یک پروتکل «بدون حالت» است یا Stateless هم به آن گفته می‌شود. به این معنا که پس از ارائه یک صفحه وب توسط سرور، تمام اشیاء مرتبط با آن در سمت سرور تخریب خواهند شد. این مورد متفاوت‌ است با برنامه‌های معمولی دسکتاپ که طول عمر یک شیء معمولی تعریف شده در سطح فرم به صورت یک فیلد، تا زمان باز بودن آن فرم، تعیین می‌گردد و به صورت خودکار از حافظه حذف نمی‌شود. این مساله دقیقا مشکل تمام تازه واردها به دنیای وب است که چرا اشیاء ما نیست و نابود شدند. در اینجا وب سرور قرار است به هزاران درخواست رسیده پاسخ دهد. اگر قرار باشد تمام این اشیاء را در سمت سرور نگهداری کند، خیلی زود با اتمام منابع مواجه می‌گردد. اما واقعیت این است که نیاز است یک سری از اطلاعات را در حافظه نگه داشت. به همین منظور یکی از چندین روش مدیریت حالت در ASP.NET استفاده از سشن‌ها است که در اینجا به نحو بسیار مطلوبی، با سربار حداقل توسط TempData مدیریت شده است.
یک مثال کاربردی در این زمینه:
فرض کنید در متد جاری کنترلر، ابتدا بررسی می‌کنیم که آیا ورودی دریافتی معتبر است یا خیر. در غیراینصورت، کاربر را به یک View دیگر از طریق کنترلری دیگر جهت نمایش خطاها هدایت خواهیم کرد.
همین «هدایت مرورگر به یک View دیگر» یعنی پاک شدن و تخریب اطلاعات کنترلر قبلی به صورت خودکار. بنابراین نیاز است این اطلاعات را در TempData قرار دهیم تا در کنترلری دیگر قابل استفاده باشد:

using System;
using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult InsertData(string name)
{
// Check for input errors.
if (string.IsNullOrWhiteSpace(name))
{
TempData["error"] = "name is required.";
return RedirectToAction("ShowError");
}
// No errors
// ...
return View();
}

public ActionResult ShowError()
{
var error = TempData["error"] as string;
if (!string.IsNullOrWhiteSpace(error))
{
ViewBag.Error = error;
}
return View();
}
}
}

در همان HomeController دو متد جدید به نام‌های InsertData و ShowError اضافه شده‌اند. در متد InsertData ابتدا بررسی می‌شود که آیا نامی وارد شده است یا خیر. اگر خیر توسط متد RedirectToAction، کاربر به اکشن یا متد ShowError هدایت خواهد شد.
برای انتقال اطلاعات خطایی که می‌خواهیم در حین این Redirect نمایش دهیم نیز از TempData استفاده شده است.
بدیهی است برای اجرا این مثال نیاز است دو View جدید برای متدهای InsertData و ShowError ایجاد شوند (کلیک راست روی نام متد و انتخاب گزینه Add view برای اضافه کردن View مرتبط با آن اکشن).
محتوای View مرتبط با متد افزودن اطلاعات فعلا مهم نیست، ولی View نمایش خطاها در ساده‌ترین حالت مثلا می‌تواند به صورت زیر باشد:

@{
ViewBag.Title = "ShowError";
}

<h2>Error</h2>

@ViewBag.Error

برای آزمایش برنامه هم مطابق مسیریابی پیش فرض و با توجه به قرار داشتن در کنترلری به نام Home، مسیر http://localhost/Home/InsertData ابتدا باید بررسی شود. چون آرگومانی وارد نشده، بلافاصله صفحه به آدرس http://localhost/Home/ShowError به صورت خودکار هدایت خواهد شد.


نکته‌ای تکمیلی در مورد Strongly typed viewها:
عنوان شد که Strongly typed view روش مرجح بوده و بهتر است از آن استفاده شود، زیرا اطلاعات اشیاء و خواص تعریف شده در یک View تحت نظر کامپایلر قرار می‌گیرند که بسیار عالی است. یعنی اگر در View بنویسم FirstName: @Model.FirstName1 چون FirstName1 وجود خارجی ندارد، برنامه نباید کامپایل شود. یکبار این را بررسی کنید. برنامه بدون مشکل کامپایل می‌شود! اما تنها در زمان اجرا است که صفحه زرد رنگ معروف خطاهای ASP.NET ظاهر می‌شود که چنین خاصیتی وجود ندارد (این حالت پیش فرض است؛ یعنی کامپایل یک View‌ در زمان اجرا). البته این باز هم خیلی بهتر است از ViewBag، چون اگر مثلا ViewBag.Country1 را وارد کنیم، در زمان اجرا تنها چیزی نمایش داده نخواهد شد؛‌ اما با روش Strongly typed view، حتما خطای Compilation Error به همراه نمایش محل مشکل نهایی، در صفحه ظاهر خواهد شد.
سؤال: آیا می‌شود پیش از اجرای برنامه هم این بررسی را انجام داد؟
پاسخ: بله. باید فایل پروژه را اندکی ویرایش کرده و مقدار MvcBuildViews را که به صورت پیش فرض false هست، true نمود. یا خارج از ویژوال استودیو با یک ادیتور متنی ساده مثلا فایل csproj را گشوده و این تغییر را انجام دهید. یا داخل ویژوال استودیو، بر روی نام پروژه کلیک راست کرده و سپس گزینه Unload Project را انتخاب کنید. مجددا بر روی این پروژه Unload شده کلیک راست نموده و گزینه edit را انتخاب نمائید. در صفحه باز شده، MvcBuildViews را یافته و آن‌را true کنید. سپس پروژه را Reload کنید.
اکنون اگر پروژه را کامپایل کنید، پیغام خطای زیر پیش از اجرای برنامه قابل مشاهده خواهد بود:

'MvcApplication1.Models.Employee' does not contain a definition for 'FirstName1' 
and no extension method 'FirstName1' accepting a first argument of type 'MvcApplication1.Models.Employee'
could be found (are you missing a using directive or an assembly reference?)
d:\Prog\MvcApplication1\MvcApplication1\Views\Home\Index.cshtml 10 MvcApplication1

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

و یک خبر خوب!
مجوز سورس کد ASP.NET MVC از MS-PL به Apache تغییر کرده و همچنین Razor و یک سری موارد دیگر هم سورس باز شده‌اند. این تغییرات به این معنا خواهند بود که پروژه از حالت فقط خواندنی MS-PL به حالت متداول یک پروژه سورس باز که شامل دریافت تغییرات و وصله‌ها از جامعه برنامه نویس‌ها است، تغییر کرده است (^ و ^).