مطالب
خواندن اطلاعات از فایل اکسل با استفاده از LinqToExcel
در این مقاله مروری سریع و کاربردی خواهیم داشت بر توانایی‌های مقدماتی LinqToExcel
در ابتدا می‌بایست LinqToExcel را از طریق NuGet به پروژه افزود.
PM> Install-Package LinqToExcel
و یا از طریق solution Explorer گزینه Manage NuGet Packages 

اکنون فایل اکسل ذیل را در نظر بگیرید.

روش خواندن اطلاعات از فایل اکسل فوق تحت فرامین Linq و با مشخص کردن نام sheet مورد نظر  توسط شئ ExcelQueryFactory  بصورت زیر است.

 string pathToExcelFile = @"C:\Users\MASOUD\Desktop\ExcelFile.xlsx";
var excel = new ExcelQueryFactory(pathToExcelFile);
            string sheetName = "Sheet1";
            var persons = from a in excel.Worksheet(sheetName) select a;
            foreach (var a in persons)
            {
                MessageBox.Show(a["Name"]+" "+a["Family"]);
            }


در صورتیکه بخواهیم انتقال اطلاعات فایل اکسل به جداول بانک اطلاعاتی مانند Sql Server بطور مثال با روش EF Entity Framework را انجام دهیم کلاس زیر با نام person را فرض نمایید.

 public class Person
        {
            public string Name { get; set; }
            public string Family { get; set; }
        }
باید بدانید که بصورت پیشفرض سطر اول از فایل اکسل به عنوان نام ستون انتخاب می‌شود و می‌بایست جهت نگاشت با نام property‌های کلاس ما دقیقاً همنام باشد.

 string pathToExcelFile = @"C:\Users\MASOUD\Desktop\ExcelFile.xlsx";
            var excel = new ExcelQueryFactory(pathToExcelFile);
            string sheetName = "Sheet1";
            var persons = from a in excel.Worksheet<Person>(sheetName) select a;
            foreach (var a in persons)
            {
                MessageBox.Show(a.Name+" "+a.Family);
            }
  اگر فایل اکسل ما ستون‌های بیشتری داشته باشد تنها ستونهای همنام با propertyهای کلاس ما به کلاس نگاشت پیدا می‌کند و سایر ستونها نادیده گرفته می‌شود.
در صورتیکه نام ستونهای فایل اکسل(سطر اول) با نام property‌های کلاس یکسان نباشد جهت نگاشت آنها در کلاس می‌توان از متد AddMapping استفاده نمود.
 

 string pathToExcelFile = @"C:\Users\MASOUD\Desktop\ExcelFile.xlsx";
            var excel = new ExcelQueryFactory(pathToExcelFile);
            string sheetName = "Sheet1";
            excel.AddMapping("Name","نام");
            excel.AddMapping("Family", "نام خانوادگی");
            var persons = from a in excel.Worksheet<Person>(sheetName) select a;
            foreach (var a in persons)
            {
                MessageBox.Show(a.Name+" "+a.Family);
            }

در کدهای بالا در صورتی که sheetName قید نشود بصورت پیشفرض Sheet1 از فایل اکسل  انتخاب می‌شود.

var persons = from a in excel.Worksheet<Person>() select a;
همچنین می‌توان از اندیس جهت مشخص نمودن Sheet مورد نظر استفاده نمود که اندیس‌ها از صفر شروع می‌شوند.

var persons = from a in excel.Worksheet<Person>(0) select a;
توسط متد GetWorksheetNames می توان نام sheet‌ها را بدست آورد.

public IEnumerable<string> getWorkSheets()
{
string pathToExcelFile = @"C:\Users\MASOUD\Desktop\ExcelFile.xlsx";
    
    var excel = new ExcelQueryFactory(pathToExcelFile);

    return excel.GetWorksheetNames();
}
و توسط متد GetColumnNames   می توان نام ستونها را بدست آورد.  

var SheetColumnNames = excel.GetColumnNames(sheetName);
همانطور که می‌بینید با روش توضیح داده شده در این مقاله به راحتی از فرامین Linq مانند where می‌توان در انتخاب اطلاعات از فایل اکسل استفاده نمود و سپس نتیجه را به جداول مورد نظر انتقال داد.
مطالب
INPC استاندارد با بهره گیری از صفت CallerMemberName
یکی از Attribute‌های بسیار کاربردی که در سی شارپ 5 اضافه شد CallerMemberNameAttribute بود. این صفت به یک متد اجازه میدهد که از فراخواننده‌ی خود مطلع شود. این صفت را می‌توان بر روی یک پارامتر انتخابی که مقدار پیش‌فرضی دارد اعمال نمود.

استفاده از این صفت هم بسیار ساده است:

private void A ( [CallerMemberName] string callerName = "") 
{
  Console.WriteLine("Caller is " + callerName);
}

private static void B()
{
        // let's call A
        A();
}
در کد فوق، متد A به راحتی می‌تواند بفهمد چه کسی آن را فراخوانی کرده است. از جمله کاربردهای این صفت در ردیابی و خطایابی است.

ولی یک استفاده‌ی بسیار کاربردی از این صفت، در پیاده سازی رابط INotifyPropertyChanged می‌باشد.

معمولا هنگام پیاده سازی INotifyPropertyChanged کدی شبیه به این را می‌نویسیم:

    public class PersonViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private string name;
        public string Name
        {
            get { return name; }
            set
            {
                this.name = value;
                OnPropertyChanged("Name");
            }
        }
    }

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

برای مثال فرض کنید پس از نوشتن کلاس PersonViewModel تصمیم می‌گیرد نام ویژگی Name را به FirstName تغییر دهید؛ چرا که می‌خواهید اجزای نام یک شخص را به صورت مجزا نگهداری و پردازش کنید. پس احتمالا با زدن کلید F2 روی فیلد name آن را به firstName و ویژگی Name را به FirstName تغییر نام می‌دهید. همانند کد زیر:

private string firstName;
public string FirstName
{
            get { return firstName; }
            set
            {
                this.firstName = value;
                OnPropertyChanged("Name");
            }
}

برنامه را کامپایل کرده و در کمال تعجب می‌بینید که بخشی از برنامه درست رفتار نمی‌کند و تغییراتی که در نام کوچک شخص توسط کاربر ایجاد می‌شود به درستی بروزرسانی نمی‌شوند. علت ساده است: ما کد را به صورت اتوماتیک Refactor کرده ایم و گزینه‌ی Include String را در حین Refactor، در حالت پیشفرض غیرفعال رها کرده‌ایم. پس جای تعجبی ندارد که در هر جای کد که رشته‌ای به نام "Name" با ماهیت نام شخص داشته ایم، دست نخورده باقی مانده است. در واقع در کد تغییر یافته، هنگام تغییر FirstName، ما به سیستم گزارش می‌کنیم که ویژگی Name (که اصلا وجود ندارد) تغییر یافته است و این یعنی خطا.

حال احتمال بروز این خطا را در ViewModel هایی با ده‌ها ویژگی و ترکیب‌های مختلف در نظر بگیرید. پس کاملا محتمل است و برای خیلی از دوستان این اتفاق رخ داده است.

و اما راه حل چیست؟ به کارگیری صفت CallerMemberName

بهتر است که یک کلاس انتزاعی برای تمام ViewModel‌های خود داشته باشیم و پیاده سازی جدید INPC را در درون آن قرار دهیم تا براحتی VM‌های ما از آن مشتق شوند:

public abstract class ViewModelBase : INotifyPropertyChanged
{
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            OnPropertyChangedExplicit(propertyName);
        }

        protected void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> projection)
        {
            var memberExpression = (MemberExpression)projection.Body;
            OnPropertyChangedExplicit(memberExpression.Member.Name);
        }

        void OnPropertyChangedExplicit(string propertyName)
        {
            this.CheckPropertyName(propertyName);

            PropertyChangedEventHandler handler = this.PropertyChanged;

            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        #region Check property name

        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public void CheckPropertyName(string propertyName)
        {
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
                throw new Exception(String.Format("Could not find property \"{0}\"", propertyName));
        }

        #endregion // Check property name
}

در این کلاس، ما پارامتر propertyName را از متد OnPropertyChanged، توسط صفت CallerMemberName حاشیه نویسی کرده‌ایم. این کار باعث می‌شود در Setter‌های ویژگی‌ها، به راحتی بدون نوشتن نام ویژگی، عملیات اطلاع رسانی تغییرات را انجام دهیم. بدین صورت که کافیست متد OnPropertyChanged بدون هیچ آرگومانی در Setter فراخوانی شود و صفت CallerMemberName به صورت اتوماتیک نام ویژگی ای که فراخوانی از درون آن انجام شده است را درون پارامتر propertyName قرار می‌دهد.

پس کلاس PersonViewModel را به صورت زیر می‌توانیم اصلاح و تکمیل کنیم:

public class PersonViewModel : ViewModelBase
{
        private string firstName;
        public string FirstName
        {
            get { return firstName; }
            set
            {
                this.firstName = value;

                OnPropertyChanged();
                OnPropertyChanged(() => this.FullName);
            }
        }

        private string lastName;
        public string LastName
        {
            get { return lastName; }
            set
            {
                this.lastName = value;

                OnPropertyChanged();
                OnPropertyChanged(() => this.FullName);
            }
        }

        public string FullName
        {
            get { return string.Format("{0} {1}", FirstName, LastName); }
        }
}
همانطور که می‌بینید متد OnPropertyChanged بدون آرگومان فراخوانی میشود. اکنون اگر شما اقدام به Refactor کردن کد خود بکنید دیگر نگرانی از بابت تغییر نکردن رشته‌ها و کامنت‌ها نخواهید داشت و مطمئن هستید، نام ویژگی هر چیزی که باشد، به صورت خودکار به متد ارسال خواهد شد.

کلاس ViewModelBase یک پیاده سازی دیگر از OnPropetyChanged هم دارد که به شما اجازه می‌دهد با استفاده دستورات لامبدا، OnPropertyChanged را برای هر یک از اعضای دلخواه کلاس نیز فراخوانی کنید. همانطور که در مثال فوق می‌بینید، تغییرات نام خانوادگی در نام کامل شخص نیز اثرگذار است. در نتیجه به وسیله‌ی یک Func به راحتی بیان می‌کنیم که FullName هم تغییر کرده است و اطلاع رسانی برای آن نیز باید صورت پذیرد.

برای استفاده از صفت CallerMemberName باید دات نت هدف خود را 4.5 یا 4.6 قرار دهید.

ارجاع:
Raise INPC witout string name
مطالب دوره‌ها
خلاصه‌ای از اعمال متداول با AutoMapper و Entity Framework
فرض کنید کلاس‌های مدل برنامه از سه کلاس مشتری، سفارشات مشتری‌ها و اقلام هر سفارش تشکیل شده‌است:
public class Customer
{
    public int Id { set; get; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Bio { get; set; }
 
    public virtual ICollection<Order> Orders { get; set; }
 
    [Computed]
    [NotMapped]
    public string FullName
    {
        get { return FirstName + ' ' + LastName; }
    }
}

public class Order
{
    public int Id { set; get; }
    public string OrderNo { get; set; }
    public DateTime PurchaseDate { get; set; }
    public bool ShipToHomeAddress { get; set; }
 
    public virtual ICollection<OrderItem> OrderItems { get; set; }
 
    [ForeignKey("CustomerId")]
    public virtual Customer Customer { get; set; }
    public int CustomerId { get; set; }
 
    [Computed]
    [NotMapped]
    public decimal Total
    {
        get { return OrderItems.Sum(x => x.TotalPrice); }
    }
}

public class OrderItem
{
    public int Id { get; set; }
    public decimal Price { get; set; }
    public string Name { get; set; }
    public int Quantity { get; set; }
 
    [ForeignKey("OrderId")]
    public virtual Order Order { get; set; }
    public int OrderId { get; set; }
 
    [Computed]
    [NotMapped]
    public decimal TotalPrice
    {
        get { return Price * Quantity; }
    }
}
در اینجا برای پیاده سازی خواص محاسباتی، از نکته‌ی مطرح شده‌ی در مطلب «نگاشت خواص محاسبه شده به کمک AutoMapper و DelegateDecompiler» استفاده شده‌است.
در ادامه می‌خواهیم اطلاعات حاصل از این کلاس‌ها را با شرایط خاصی به ViewModelهای مشخصی جهت نمایش در برنامه نگاشت کنیم.


نمایش اطلاعات مشتری‌ها

می‌خواهیم اطلاعات مشتری‌ها را مطابق فرمت کلاس ذیل بازگشت دهیم:
public class CustomerViewModel
{
    public string Bio { get; set; }
    public string CustomerName { get; set; }
}
با این شرایط که
- اگر Bio نال بود، بجای آن N/A نمایش داده شود.
- CustomerName از خاصیت محاسباتی FullName کلاس مشتری تامین گردد.

برای حل این مساله، نیاز است نگاشت زیر را تهیه کنیم:
this.CreateMap<Customer, CustomerViewModel>()
   .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(entity => entity.FullName))
   .ForMember(dest => dest.Bio, opt => opt.MapFrom(entity => entity.Bio ?? "N/A"));
AutoMapper برای جایگزین کردن خواص با مقدار نال، با یک مقدار مشخص، از متدی به نام NullSubstitute استفاده می‌کند. اما در این حالت خاص که قصد داریم از Project To استفاده کنیم، این روش پاسخ نمی‌دهد و محدودیت‌هایی دارد. به همین جهت از روش map from و بررسی مقدار خاصیت، استفاده شده‌است.
همچنین در اینجا مطابق نگاشت فوق، خاصیت CustomerName از خاصیت FullName کلاس مشتری دریافت می‌شود.

کوئری نهایی استفاده کننده‌ی از این اطلاعات به شکل زیر خواهد بود:
using (var context = new MyContext())
{
    var viewCustomers = context.Customers
        .Project()
        .To<CustomerViewModel>()
        .Decompile()
        .ToList();
    // don't use
    // var viewCustomers = Mapper.Map<IEnumerable<Customer>, IEnumerable<CustomerViewModel>>(dbCustomers);
    foreach (var customer in viewCustomers)
    {
        Console.WriteLine("{0} - {1}", customer.CustomerName, customer.Bio);
    }
}
در اینجا از متدهای Project To و همچنین Decompile استفاده شده‌است (جهت پردازش خاصیت محاسباتی).


نمایش اطلاعات سفارش‌های مشتری‌ها

در ادامه قصد داریم اطلاعات سفارش‌ها را با فرمت ViewModel ذیل نمایش دهیم:
public class OrderViewModel
{
    public string CustomerName { get; set; }
    public decimal Total { get; set; }
    public string OrderNumber { get; set; }
    public IEnumerable<OrderItemsViewModel> OrderItems { get; set; }
}

public class OrderItemsViewModel
{
    public string Name { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }
}
با این شرایط که
- CustomerName از خاصیت محاسباتی FullName کلاس مشتری تامین گردد.
- خاصیت OrderNumber آن از خاصیت OrderNo تهیه گردد.

به همین جهت کار را با تهیه‌ی نگاشت ذیل ادامه می‌دهیم:
this.CreateMap<Order, OrderViewModel>()
  .ForMember(dest => dest.OrderNumber, opt => opt.MapFrom(src => src.OrderNo))
  .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.FullName));
بر این اساس کوئری مورد استفاده نیز به نحو ذیل تشکیل می‌شود:
using (var context = new MyContext())
{
    var viewOrders = context.Orders
        .Project()
        .To<OrderViewModel>()
        .Decompile()
        .ToList();
    // don't use
    // var viewOrders = Mapper.Map<IEnumerable<Order>, IEnumerable<OrderViewModel>>(dbOrders);
    foreach (var order in viewOrders)
    {
        Console.WriteLine("{0} - {1} - {2}", order.OrderNumber, order.CustomerName, order.Total);
    }
}
در اینجا چون از خاصیت OrderItems کلاس ViewModel صرفنظر نشده‌است، اطلاعات آن نیز به همراه viewOrders موجود است. یعنی می‌توان چنین کوئری را نیز جهت نمایش اطلاعات تو در توی اقلام هر سفارش نیز نوشت:
using (var context = new MyContext())
{
    var viewOrders = context.Orders
        .Project()
        .To<OrderViewModel>()
        .Decompile()
        .ToList();
    // don't use
    // var viewOrders = Mapper.Map<IEnumerable<Order>, IEnumerable<OrderViewModel>>(dbOrders);
    foreach (var order in viewOrders)
    {
        Console.WriteLine("{0} - {1} - {2}", order.OrderNumber, order.CustomerName, order.Total);
        foreach (var item in order.OrderItems)
        {
            Console.WriteLine("({0}) {1} - {2}", item.Quantity, item.Name, item.Price);
        }
    }
}
اگر می‌خواهید OrderItems به صورت خودکار واکشی نشود، نیاز است در نگاشت تهیه شده، توسط متد Ignore از آن صرفنظر کنید:
this.CreateMap<Order, OrderViewModel>()
  .ForMember(dest => dest.OrderNumber, opt => opt.MapFrom(src => src.OrderNo))
  .ForMember(dest => dest.OrderItems, opt => opt.Ignore())
  .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.FullName));


نمایش اطلاعات یک سفارش، با فرمتی خاص

تا اینجا نگاشت‌های انجام شده بر روی لیستی از اشیاء صورت گرفتند. در ادامه می‌خواهیم اولین سفارش ثبت شده را با فرمت ذیل نمایش دهیم:
public class OrderDateViewModel
{
    public int PurchaseHour { get; set; }
    public int PurchaseMinute { get; set; }
    public string CustomerName { get; set; }
}
به همین منظور ابتدا نگاشت ذیل را تهیه می‌کنیم:
this.CreateMap<Order, OrderDateViewModel>()
  .ForMember(dest => dest.PurchaseHour, opt => opt.MapFrom(src => src.PurchaseDate.Hour))
  .ForMember(dest => dest.PurchaseMinute, opt => opt.MapFrom(src => src.PurchaseDate.Minute))
  .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.FullName));
در اینجا ساعت و دقیقه‌ی خرید، از خاصیت PurchaseDate استخراج شده‌اند. همچنین CustomerName نیز از خاصیت FullName کلاس مشتری دریافت گردیده‌است.
پس از این تنظیمات، کوئری نهایی به شکل ذیل خواهد بود:
using (var context = new MyContext())
{
    var viewOrder = context.Orders
        .Project()
        .To<OrderDateViewModel>()
        .Decompile()
        .FirstOrDefault();
    // don't use
    // var viewOrder = Mapper.Map<Order, OrderDateViewModel>(dbOrder);
 
    if (viewOrder != null)
    {
        Console.WriteLine("{0}, {1}:{2}", viewOrder.CustomerName, viewOrder.PurchaseHour, viewOrder.PurchaseMinute);
    }
}


فرمت کردن سفارشی اطلاعات در حین نگاشت‌ها

در مورد فرمت کننده‌های سفارشی و تبدیلگرها پیشتر بحث کرده‌ایم. اما اغلب آن‌ها را در حالت خاص LINQ to Entities نمی‌توان بکار برد، زیرا قابلیت تبدیل به SQL را ندارند. برای مثال فرض کنید می‌خواهیم خاصیت ShipToHomeAddress کلاس Order را به خاصیت ShipHome کلاس ذیل نگاشت کنیم:
public class OrderShipViewModel
{
    public string ShipHome { get; set; }
    public string CustomerName { get; set; }
}
با این شرط که اگر مقدار آن True بود، Yes را نمایش دهد. با توجه به ساختار مدنظر، نگاشت ذیل را می‌توان تهیه کرد که در آن فرمت کردن سفارشی، به متد MapFrom واگذار شده‌است:
this.CreateMap<Order, OrderShipViewModel>()
   .ForMember(dest => dest.ShipHome, opt => opt.MapFrom(src=>src.ShipToHomeAddress? "Yes": "No"))
   .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.FullName));
با این کوئری جهت استفاده‌ی از این تنظیمات:
using (var context = new MyContext())
{
    var viewOrders = context.Orders
        .Project()
        .To<OrderShipViewModel>()
        .Decompile()
        .ToList();
    // don't use
    // var viewOrders = Mapper.Map<IEnumerable<Order>, IEnumerable<OrderShipViewModel>>(dbOrders);
    foreach (var order in viewOrders)
    {
        Console.WriteLine("{0} - {1}", order.CustomerName, order.ShipHome);
    }
}

کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید.
نظرات مطالب
مقابله با XSS ؛ یکبار برای همیشه!
سلام 
من یک سوال داشتم . من از کلاس‌های شما در برنامه ام استفاده کردم مدل من به شرح زیر است :
  public class Contact
    {
        public int Id { get; set; }
        [AllowHtml]
        public string Name { get; set; }
        public string Address { get; set; }
    }
و برای تست اکشن‌های زیر رو داخل کنترلر نوشتم :
 [HttpGet]
        public ActionResult ContactUs()
        {
            return View();
        }
        [HttpPost]
        public ActionResult ContactUs(Contact con)
        {

            ViewBag.Name = con.Name.ToSafeHtml();
            ViewBag.MyAddress = con.Address;
            return View();
        }

خوب همه چیز به درستی کار می‌کنه . اما سوالات من به شرح زیر است :
۱ - آیا واقعا نیاز نیست پروپرتی هایی رو که AllowHTML نیستند رو کنترل کنیم ؟ خود Asp.net MVC جلوی این قضیه رو می‌گیره ؟
۲ - اگر ما یک پروپرتی رو AllowHtml نگذاریم و قصد وارد کردن یک تگ Script داشته باشیم با خطای زیر مواجه می‌شویم :
A potentially dangerous Request.Form value was detected from the client (Name="<script>alert("aaa")...").
چطور این پیام رو سیستم نشون نده و یک پیام سفارشی نشون بده ؟
۳ - از متد‌های دیگه ای که در کلاس‌های شما است در کجا‌ها با مثال عملیاتی می‌شه استفاده کرد ؟
تشکر از لطفتون
مطالب
ایجاد Helper سفارشی جهت نمایش ویدئو در ASP.NET MVC
معرفی HTML Helpers
در صورتی که در مورد Helper‌ها در ASP.NET MVC اطلاعات بیشتری نیاز دارید پیشنهاد می‌کنم ابتدا این مطلب + را مطالعه کنید.
 
ایجاد یک HTML Helper سفارشی برای نمایش ویدئو‌های سایت آپارات
قبل از ایجاد هر Helper ی باید با خروجی نهایی آن آشنا بود. پس از بررسی خروجی نهایی کافیست بخش هایی از آن را Optional کنیم تا در زمان استفاده از آن، نسبت به مقادیر این دسته از پارامترها تصمیم گیری کنیم.
سایت آپارات نمونه‌ی فارسی سایت YouTube است و امکان اشتراک گذاری فایل‌های ویدئویی را برای کاربران مهیا کرده است. در این سایت در صفحه نمایش هر ویدئو، بخشی با عنوان "دریافت کد ویدئو" وجود دارد. با کمی بررسی در کدهای نهایی ایجاد شده توسط دستورات ذکر شده در این بخش، کد نهایی برای نمایش یک ویدئو به صورت زیر خواهد بود:

<embed height="400" width="500" 
flashvars="config=http://www.aparat.com//video/video/config/videohash/BA9Md/watchtype/embed" 
allowfullscreen="true" 
quality="high" name="aparattv_BA9Md" id="aparattv_BA9Md" 
src="http://host10.aparat.com/public/player/aparattv" type="application/x-shockwave-flash">
برای تولید پویای این کد Helper زیر می‌تواند مفید باشد:
using System.Web.Mvc;

namespace MvcApplication1
{
    public static class AparatPlayerHelper
    {
        public static MvcHtmlString AparatPlayer(this HtmlHelper helper, string mediafile, int height, int width)
        {
            var player = @"<embed height=""{0}"" width=""{1}"" flashvars=""config=http://www.aparat.com//video/video/config/videohash/{2}/watchtype/embed"" 
                                allowfullscreen=""true"" 
                                quality=""high"" 
                                name=""aparattv_{2}"" id=""aparattv_{2}"""" src=""http://host10.aparat.com/public/player/aparattv"" 
                                type=""application/x-shockwave-flash"">";

            player = string.Format(player, height, width, mediafile);
            return new MvcHtmlString(player);
        }
    }
}
نحوه استفاده از این Helper:
@Html.AparatPlayer("BA9Md", 400, 500)
اگر به آدرس صفحات در سایت آپارات دقت کنید URL جاری به یک عبارت چند حرقی ختم می‌شود که از این عبارت به عنوان مقدار پارامتر mediafile و شناسه منحصر بفرد فایل ویدئو استفاده شده است.
ایجاد Helper سفارشی جهت نمایش ویدئو‌های YouTube:
با توجه به توضیحات فوق یک Helper سفارشی برای نمایش ویدئو‌های YouTube بصورت زیر است:
using System;
using System.Drawing;
using System.Web.Mvc;

namespace MvcApplication1
{

    public static class YouTubePlayerHelper
    {
        public static MvcHtmlString YouTubePlayer(this HtmlHelper helper, string playerId, string mediaFile, YouTubePlayerOption youtubePlayerOption)
        {

            const string baseURL = "http://www.youtube.com/v/";

            // YouTube Embedded Code
            var player = @"<div id=""YouTubePlayer_{7}""width:{1}px; height:{2}px;"">
                                 <object width=""{1}"" height=""{2}"">
                                 <param name=""movie"" value=""{6}{0}&fs=1&border={3}&color1={4}&color2={5}""></param>
                                 <param name=""allowFullScreen"" value=""true""></param>
                                 <embed src=""{6}{0}&fs=1&border={3}&color1={4}&color2={5}""
                                 type = ""application/x-shockwave-flash""
                                 width=""{1}"" height=""{2}"" allowfullscreen=""true""></embed>
                                 </object>
                             </div>";

            // Replace All The Value
            player = String.Format(player, mediaFile, youtubePlayerOption.Width, youtubePlayerOption.Height, (youtubePlayerOption.Border ? "1" : "0"), ConvertColorToHexa.ConvertColorToHexaString(youtubePlayerOption.PrimaryColor), ConvertColorToHexa.ConvertColorToHexaString(youtubePlayerOption.SecondaryColor), baseURL, playerId);

            //Retrun Embedded Code
            return new MvcHtmlString(player);
        }
    }

    public class YouTubePlayerOption
    {
        int _width = 425;
        int _height = 355;
        Color _color1 = Color.Black;
        Color _color2 = Color.Aqua;

        public YouTubePlayerOption()
        {
            Border = false;
        }

        public int Width { get { return _width; } set { _width = value; } }
        public int Height { get { return _height; } set { _height = value; } }
        public Color PrimaryColor { get { return _color1; } set { _color1 = value; } }
        public Color SecondaryColor { get { return _color2; } set { _color2 = value; } }
        public bool Border { get; set; }
    }

    public class ConvertColorToHexa
    {
        private static readonly char[] HexDigits =
            {
                '0', '1', '2', '3', '4', '5', '6', '7',
                '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
            };

        public static string ConvertColorToHexaString(Color color)
        {
            var bytes = new byte[3];
            bytes[0] = color.R;
            bytes[1] = color.G;
            bytes[2] = color.B;
            var chars = new char[bytes.Length * 2];
            for (int i = 0; i < bytes.Length; i++)
            {
                int b = bytes[i];
                chars[i * 2] = HexDigits[b >> 4];
                chars[i * 2 + 1] = HexDigits[b & 0xF];
            }
            return new string(chars);
        }


    }        

}
نحوه استفاده:
@Html.YouTubePlayer("Casablanca", "iLdqKUkkM6w", new YouTubePlayerOption()
                                {
                                    Border = true
                                })
در اینجا جهت مقداردهی پارمتر‌های پخش کننده ویدئو، از کلاس YouTubePlayerOption استفاده شده است.
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 13 - معرفی View Components
روش رندر یک View در ASP.NET MVC، بر مبنای اطلاعاتی است که از کنترلر، در اختیار View آن قرار می‌گیرد. اما گاهی از اوقات نیاز است بعضی از قسمت‌های صفحه همواره نمایش داده شوند (مانند نمایش تعداد کاربران آنلاین، سخن روز، منوهای کنار صفحه و امثال آن). یک راه حل برای این مساله، اضافه کردن اطلاعات مورد نیاز View در ViewModel ارائه شده‌ی توسط کنترلر است. هرچند این روش کار می‌کند اما پس از مدتی به ViewModel هایی خواهیم رسید که تشکیل شده‌اند از چندین و چند خاصیت اضافی که الزاما مرتبط با تعریف آن ViewModel نیستند. راه حل بهتر، قرار دادن قسمت‌های مشترک صفحات در فایل layout برنامه است؛ اما فایل layout، به سادگی نمی‌تواند از دایرکتیو model@ برای مشخص سازی مدل و یا مدل‌های مورد نیاز خود استفاده کند (هر چند ممکن است؛ اما بیش از اندازه پیچیده خواهد شد).
در نگارش‌های پیشین ASP.NET MVC، یک چنین مسائلی را با معرفی Child Actionها
    public partial class SidebarMenuController : Controller
    {
        const int Min15 = 900;

        [ChildActionOnly]
        [OutputCache(Duration = Min15)]
        public virtual ActionResult Index()
        {
            return PartialView("_SidebarMenu");
        }
    }
و سپس نمایش آن‌ها توسط Html.RenderAction در فایل layout برنامه، حل می‌کنند. در ASP.NET Core، جایگزین Child Actionها، مفهوم جدیدی است به نام View Components.


یک مثال: تهیه‌ی اولین View Component

ساختار یک View Component، بسیار شبیه است به ساختار یک Controller، اما با عملکردی محدود. به همین جهت کار تعریف آن با افزودن یک کلاس سی‌شارپ شروع می‌شود و این کلاس را می‌توان در پوشه‌ای به نام ViewComponents در ریشه‌ی پروژه قرار داد (اختیاری).


سپس برای نمونه، کلاس ذیل را به این پوشه اضافه کنید:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Core1RtmEmptyTest.Services;
 
namespace Core1RtmEmptyTest.ViewComponents
{
    public class SiteCopyright : ViewComponent
    {
        private readonly IMessagesService _messagesService;
 
        public SiteCopyright(IMessagesService messagesService)
        {
            _messagesService = messagesService;
        }
 
        public IViewComponentResult Invoke(int numberToTake)
        {
            var name = _messagesService.GetSiteName();
            return View(viewName: "Default", model: name);
        }
 
        //public async Task<IViewComponentResult> InvokeAsync(int numberToTake)
        //{
        //    return View();
        //}
    }
}
همانطور که پیشتر نیز عنوان شد، تزریق وابستگی‌ها در تمام قسمت‌های ASP.NET Core در دسترس هستند. در اینجا نیز از سرویس MessagesService بررسی شده‌ی در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 6 - سرویس‌ها و تزریق وابستگی‌ها» برای نمایش نام سایت استفاده می‌کنیم.

ساختار کلی یک کلاس ViewComponent شامل دو جزء اصلی است:
الف) از کلاس پایه ViewComponent مشتق می‌شود. به این ترتیب توسط ASP.NET Core قابل شناسایی خواهد شد.
ب) دارای متد Invoke ایی است که بجای Html.RenderAction در نگارش‌های پیشین ASP.NET MVC، قابل فراخوانی است. این متد یک View را باز می‌گرداند.
ج) در اینجا امکان تعریف نمونه‌ی Async متد Invoke نیز وجود دارد (برای مثال جهت کار با متدهای Async بانک اطلاعاتی).
روش فراخوانی این متدها نیز به این صورت است: ابتدا به دنبال نمونه‌ی async می‌گردد. اگر یافت شد، همینجا کار خاتمه می‌یابد. اگر یافت نشد، نمونه‌ی sync یا معمولی آن فراخوانی می‌شود و اگر این هم یافت نشد، یک استثناء صادر خواهد شد.
د) متد Invoke می‌تواند دارای پارامترهای دلخواهی نیز باشد و حالت پیش فرض آن بدون پارامتر است.

روش یافتن یک view component توسط ASP.NET Core به این صورت است:
الف) این کلاس باید عمومی بوده و همچنین abstract نباشد.
ب) «یکی» از مشخصه‌های ذیل را داشته باشد:
1) نامش به ViewComponent ختم شده باشد.
2) از کلاس ViewComponent ارث بری کرده باشد.
3) با ویژگی ViewComponent مزین شده باشد.


نحوه و محل تعریف View یک View Component

پس از تعریف کلاس ViewComponent مورد نظر، اکنون نیاز است View آن‌را اضافه کرد. روش یافتن این Viewها توسط ASP.NET Core نیز بر این مبنا است که
الف) اگر این View Component عمومی و سراسری است، باید درون پوشه‌ی shared، پوشه‌ی جدیدی را به نام Components ایجاد کرده و سپس ذیل این پوشه، بر اساس نام کلاس ViewComponent، یک زیر پوشه‌ی دیگر را ایجاد و داخل آن، View مدنظر را اضافه کرد (تصویر ذیل).
 /Views/Shared/Components/[NameOfComponent]/Default.cshtml
ب) اگر این View Component تنها باید از طریق Viewهای یک کنترلر خاص قابل دسترسی باشند، زیر پوشه‌ی Component یاد شده را ذیل پوشه‌ی View همان کنترلر قرار دهید (و آن‌را از قسمت Shared خارج کنید).
 /Views/[CurrentController]/Components/[NameOfComponent]/Default.cshtml


یک نکته: اگر نام کلاسی به ViewComponent  ختم شده بود، نیازی نیست تا ViewComponent  را هم در حین ساخت پوشه‌ی آن ذکر کرد.


نحوه‌ی استفاده‌ی از View Component تعریف شده و ارسال پارامتر به آن

و در آخر برای استفاده‌ی از این View Component تعریف شده، به فایل layout برنامه مراجعه کرده و آن‌را به نحو ذیل فراخوانی کنید:
 <footer>
    <p>@await Component.InvokeAsync("SiteCopyright", new { numberToTake = 5 })</p>
</footer>
اولین پارامتر متد InvokeAsync، همان نام کلاس View Component است. اگر خواستید پارامتر(های) دلخواهی را به متد Invoke کلاس View Component ارسال کنید (مانند پارامتر int numberToTake در مثال فوق)، آن‌را در همینجا می‌توان ذکر کرد (با فرمت dictionary و یا  anonymous type).

یک نکته: متدهای قدیمی Component.Invoke و Component.Renderدر اینجا حذف شده‌اند (اگر مقالات پیش از RTM را مطالعه کردید) و روش توصیه شده‌ی در اینجا، کار با متدهای async است.


تفاوت‌های View Components با Child Actions نگارش‌های پیشین ASP.NET MVC

پارامترهای یک View Component از طریق یک HTTP Request تامین نمی‌شوند و همانطور که ملاحظه کردید در همان زمان فراخوانی آن‌ها به صورت مستقیم فراهم خواهند شد. بنابراین مباحث model binding در اینجا دیگر وجود خارجی ندارند. همچنین View Components جزئی از طول عمر یک کنترلر نیستند. بنابراین اکشن فیلترهای مختلف تعریف شده، تاثیری را بر روی آن‌ها نخواهند داشت (این مشکلی بود که با Child Actions در نگارش‌های قبلی مشاهده می‌شد). همچنین View Components به صورت مستقیم از طریق درخواست‌های HTTP قابل دسترسی نیستند. به علاوه Child actions قدیمی، از فراخوانی‌های async پشتیبانی نمی‌کنند.
زمانیکه کلاسی از کلاس پایه ViewComponent ارث بری می‌کند، تنها به این خواص عمومی از درخواست HTTP جاری دسترسی خواهد داشت:
[ViewComponent]
public abstract class ViewComponent
{
   protected ViewComponent();
   public HttpContext HttpContext { get; }
   public ModelStateDictionary ModelState { get; }
   public HttpRequest Request { get; }
   public RouteData RouteData { get; }
   public IUrlHelper Url { get; set; }
   public IPrincipal User { get; }

   [Dynamic]  
   public dynamic ViewBag { get; }
   [ViewComponentContext]
   public ViewComponentContext ViewComponentContext { get; set; }
   public ViewContext ViewContext { get; }
   public ViewDataDictionary ViewData { get; }
   public ICompositeViewEngine ViewEngine { get; set; }

   //...
}


فراخوانی Ajax ایی یک View Component

در ASP.NET Core، یک اکشن متد می‌تواند خروجی ViewComponent نیز داشته باشد و این تنها روشی است که می‌توان یک View Component را از طریق درخواست‌های HTTP، مستقیما قابل دسترسی کرد:
public IActionResult AddURLTest()
{
   return ViewComponent("AddURL");
}
در این حالت می‌توان این اکشن متد را به صورت Ajax ایی نیز بارگذاری و به صفحه اضافه کرد:
$(document).ready (function(){
    $("#LoadSignIn").click(function(){
         $('#UserControl').load("/Home/AddURLTest");
    });
});


امکان بارگذاری View Components از اسمبلی‌های دیگر نیز وجود دارد

در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 10 - بررسی تغییرات Viewها» روش دسترسی به Viewهای برنامه را که در اسمبلی آن قرار گرفته بودند، بررسی کردیم. دقیقا همان روش در مورد view components نیز صادق است و کاربرد دارد. جهت یادآوری، این مراحل باید طی شوند:
الف) اسمبلی ثالث حاوی View Component‌های برنامه باید ارجاعاتی را به ASP.NET Core و قابلیت‌های Razor آن داشته باشد:
"dependencies": {
   "NETStandard.Library": "1.6.0",
   "Microsoft.AspNetCore.Mvc": "1.0.0",
   "Microsoft.AspNetCore.Razor.Tools": {
   "version": "1.0.0-preview2-final",
   "type": "build"
  }
},
"tools": {
   "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final"
}
ب) محل قرارگیری viewهای این اسمبلی ثالث نیز همانند قسمت «نحوه و محل تعریف View یک View Component» مطلب جاری است و تفاوتی نمی‌کند. فقط برای  قرار دادن این Viewها در اسمبلی برنامه باید گزینه‌ی embed را مقدار دهی کرد:
"buildOptions": {
   "embed": "Views/**/*.cshtml"
}
ج) مرحله‌ی آخر هم معرفی این اسمبلی ثالث، به RazorViewEngineOptions به صورت یک EmbeddedFileProvider جدید است. در این مثال، ViewComponentLibrary نام فضای نام این اسمبلی است.
public void ConfigureServices(IServiceCollection services)
{
   services.AddMvc();
   //Get a reference to the assembly that contains the view components
   var assembly = typeof(ViewComponentLibrary.ViewComponents.SimpleViewComponent).GetTypeInfo().Assembly;
   //Create an EmbeddedFileProvider for that assembly
   var embeddedFileProvider = new EmbeddedFileProvider(assembly,"ViewComponentLibrary");
   //Add the file provider to the Razor view engine
   services.Configure<RazorViewEngineOptions>(options =>
   {
      options.FileProviders.Add(embeddedFileProvider);
   });
د) جهت رفع تداخلات احتمالی این اسمبلی با سایر اسمبلی‌ها بهتر است ویژگی ViewComponent را به همراه نامی مشخص ذکر کرد (در حین تعریف کلاس View Component):
 [ViewComponent(Name = "ViewComponentLibrary.Simple")]
public class SimpleViewComponent : ViewComponent
و در آخر فراخوانی این View Component بر اساس این نام صورت خواهد گرفت:
 @await Component.InvokeAsync("ViewComponentLibrary.Simple", new { number = 5 })
نظرات مطالب
چگونگی رسیدگی به Null property در AutoMapper
public class TestModel
    {
        public byte[] MProperty { get; set; }
    }

 public class TestViewModel
    {
       public byte[] VMProperty { get; set; }
    }

class Program
    {
        static void Main(string[] args)
        {
            Mapper.CreateMap<TestModel,TestViewModel>().ForMember(tv=>tv.VMProperty,m=>m.MapFrom(t=>t.MProperty));
            TestModel tm = new TestModel();
            tm.MProperty = new byte[]  { 1, 2, 3 ,4};

            TestViewModel tvm = Mapper.Map<TestModel, TestViewModel>(tm);
            foreach (var item in tvm.VMProperty)
            {
                Console.WriteLine(item.ToString());
            }
            Console.ReadKey();
        }
    }
در صورتی که MProperty، مقدار داشته باشه مشکلی پیش نمیاد اگر هم null باشه بازم مشکلی پیش نمیاد و VMProperty برابر با null هست.
مسیرراه‌ها
SQL Server
آخرین تاریخ بروزرسانی 93/10/21


SQL Server 2005

SQL Server 2008

SQL Server 2012

SQL Serve 2014


نظرات مطالب
EF Code First #7
یعنی میفرمایید من چه رابطه ای بین این دو کلاس تعریف کنم تا رابطه 0..1-0..1 داشته باشم؟
    public class Order
    {
        public int OrderId { get; set; }
        public virtual Quotation Quotation { get; set; }
    }
    public class Quotation
    {
        public int QuotationId { get; set; }
        public virtual Order Order { get; set; }
    }