مطالب
ایجاد 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 استفاده شده است.
مطالب
تولید فایل Word بدون نصب MS Word بر روی سرور

یکی از مواردی که ممکن است در محیط کاری با آن برخورد داشت، تقاضای تولید فایل word یک گزارش با فرمتی مشخص از یک برنامه ASP.Net است. برای مثال یک قالب درست کرده‌اند که header‌ و footer و کلا یک فرمت رسمی دارد. الان برنامه شما باید این فایل word رسمی را با گزارشی که تولید می‌کند پر کند. حالا اینجاست که گرفتاری برنامه نویس شروع می‌شود! روی سرور باید word نصب باشد تا توسط اشیاء COM آن بتوان یک چنین کارهایی را آن‌هم با ASP.Net که به صورت پیش فرض کمترین سطح دسترسی را روی سیستم دارد انجام داد. یا اینکه باید به سراغ کامپوننت‌های تجاری رفت و حالا اینجا با این وضع تحریم و غیره چگونه بتوان آنها را خریداری کرد یا شاید احتمالا در سایت‌های وارز بتوان نسخه تکه پاره شده آنها را یافت. مشکلی هم که این نوع کامپوننت‌ها دارند این است که ممکن است سال دیگر اصلا ساپورت نشوند. محصولات مایکروسافت هم که مرتبا در حال به روز رسانی هستند. در این حالت برنامه متکی به این نوع کامپوننت‌های تجاری سورس بسته در همان نگارش قبلی خود مجبور است باقی بماند.
خوشبختانه با ارائه آفیس 2007 و فرمت OpenXML فایلهای آن، این مشکل تقریبا مرتفع شده است. مایکروسافت نیز برای سهولت تولید این نوع اسناد، OpenXML SDK را ارائه داده است که از آدرس زیر قابل دریافت است:
Open XML Format SDK 1.0

البته پیش نمایش نگارش دو SDK آن نیز موجود است که در مطلب جاری به آن پرداخته نخواهد شد.

فایل‌های office 2007 از یک فایلzip تشکیل شده از چند فایل xml داخل آن، ایجاد شده‌اند. برای مثال یک فایل docx را با winrar یا امثال آن باز کنید (تصویر زیر):



برای کار با اینگونه اسناد باید با اصطلاحات زیر آشنا شد:
Package : فایل zip شما (همان فایل برای مثال docx) اینجا یک بسته نام دارد.
Parts : اجزای این بسته که همان فایل‌های آن هستند، parts نامیده شده اند.
Relations : اگر به فایل‌های موجود در یک بسته دقت کنید، فایلهایی با پسوند rels را خواهید دید که بیانگر نحوه ارتباط Parts با یکدیگر هستند.
Relations Ids: هر ارتباط با یک ID منحصربفرد تعریف می‌گردد.

اگر علاقمند باشید که پوستری را در این رابطه مشاهده نمائید می‌توان به آدرس زیر مراجعه نمود.
Open XML Developer Map

نحوه استفاده از OpenXML SDK در دات نت:
ابتدا باید ارجاعی را به فایل DocumentFormat.OpenXml.dll که پس از نصب در مسیر OpenXMLSDK\1.0.1825\lib قرار گرفته است به پروژه افزود. سپس نیاز است تا ارجاعی به کتابخانه WindowsBase نیز به برنامه افزوده شود (تصویر زیر). افزودن ارجاعی به این کتابخانه جهت کامپایل برنامه ضروری است (شکل زیر).


تا اینجا ارجاعات برنامه به صورت زیر خواهند بود:



یک مثال ساده:
قصد داریم یک فایل docx ساده را با استفاده از OpenXML SDK ایجاد کنیم. در مثال زیر فرمت متغیر docXml را می‌توان با ایجاد یک فایل docx ساده در word و سپس باز کردن بسته فشرده شده آن و مشاهده محتوای فایل word\document.xml بدست آورد.
using System.IO;
using System.Text;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;

namespace OpenXMLTestApp
{
class CWord
{

public static void CreateDocument(string documentFileName, string text)
{
using (WordprocessingDocument wordDoc =
WordprocessingDocument.Create(documentFileName, WordprocessingDocumentType.Document))
{
MainDocumentPart mainPart = wordDoc.AddMainDocumentPart();

string docXml =
@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
<w:document xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"">
<w:body><w:p><w:r><w:t>#REPLACE#</w:t></w:r></w:p></w:body>
</w:document>";

docXml = docXml.Replace("#REPLACE#", text);

using (Stream stream = mainPart.GetStream())
{
byte[] buf = (new UTF8Encoding()).GetBytes(docXml);
stream.Write(buf, 0, buf.Length);
}
}
}
}
}

و نحوه استفاده از آن می‌تواند به صورت زیر باشد:

CWord.CreateDocument("test.docx", "سلام دنیا");

این کتابخانه کار ایجاد فایل‌های xml ، تولید روابط بین‌ آنها و همچنین بسته بندی و zip کردن نهایی را به صورت خودکار انجام می‌دهد.

برای مطالعه بیشتر می‌توان به منابع زیر مراجعه نمود:

یک ویدیوی آموزشی رایگان از مایکروسافت
دریافت

سؤالات متداول در MSDN
http://msdn.microsoft.com/en-us/library/bb491088.aspx
البته اگر پس از نصب SDK به پوشه doc آن مراجعه نمائید، این سؤال و جواب‌ها را در فایل راهنمای chm آن نیز می‌توان پیدا کرد.

مثال دیگری در مورد ایجاد یک گزارش از بانک اطلاعاتی و گرفتن خروجی docx از آن
http://openxmldeveloper.org/articles/GenerateWordTable.aspx
البته این مثال خیلی قدیمی است و قسمت‌های کار با پکیج را با SDK‌ ارائه شده می‌توان به صورت خودکار انجام داد. اما حداقل نحوه تولید جداول استاندارد OpenXML را می‌توان از آن ایده گرفت.

مثالی در مورد نحوه قرار دادن عکس در فایل docx تولیدی

همچنین مثال‌های بیشتری را در وبلاگ‌های مربوطه می‌توان یافت:
http://blogs.msdn.com/brian_jones/
http://blogs.msdn.com/ericwhite/default.aspx


مطالب
چگونه تشخیص دهیم اسمبلی دات نت ما وصله شده است؟

یکی از روش‌هایی که برای بررسی یکپارچگی فایل‌ها مورد استفاده قرار می‌گیرد و عموما در دنیای سخت افزار و firmware های نوشته شده برای آن‌ها مرسوم است، قرار دادن CRC32 فایل در قسمتی از فایل و بررسی آن حین Boot سیستم است. اگر CRC32 جدید با CRC32 اصلی یکسان نباشد به این معنا است که فایل در حال اجرا پیش تر دستکاری شده است.
اما در دات نت فریم ورک روش متداول اینکار چیست؟ برای این منظور اضافه کردن امضای دیجیتال به فایل و اسمبلی نهایی تولیدی (فایل exe یا dll تولیدی) توصیه می‌شود (مراجعه به قسمت خواص پروژه و افزودن امضای دیجیتال جدید فقط با چند کلیک، +).
این مورد خوب است (با توجه به اینکه از الگوریتم‌های RSA و SHA1 استفاده می‌کند)، لازم است، اما کافی نیست زیرا ابزارهای حذف آن وجود دارند. به عبارتی برای وصله کردن این فایل‌ها فقط کافی است این امضای دیجیتال حذف شود و زمانی هم که نباشد، بررسی خاصی در مورد یکپارچگی فایل صورت نخواهد گرفت.
اما اگر باز هم نگران patch یا وصله شدن اسمبلی دات نت خود هستید این مورد افزودن امضای دیجیتال را حتما انجام دهید. مهم‌ترین خاصیت آن این است که یک سری تابع native در دات نت فریم ورک برای بررسی نبود آن وجود دارند (+):
[DllImport("mscoree.dll", CharSet=CharSet.Unicode)]
public static extern bool StrongNameSignatureVerificationEx(string wszFilePath, bool fForceVerification, ref bool pfWasVerified);

wszFilePath مسیر فایلی است که باید بررسی شود.
fForceVerification آیا متغیر pfWasVerified نیز مقدار دهی گردد؟
خروجی تابع مشخص می‌سازد که آیا strong name موجود و معتبر است یا خیر؟

و مثالی از استفاده‌ی آن (که بهتر است در یک تایمر نیم ساعت پس از اجرای برنامه رخ دهد):
using System;
using System.Reflection;
using System.Runtime.InteropServices;

namespace SigCheck
{
public class Validation
{
[DllImport("mscoree.dll", CharSet = CharSet.Unicode)]
public static extern bool StrongNameSignatureVerificationEx(
string wszFilePath, bool fForceVerification, ref bool pfWasVerified);

public static void SigCheck()
{
var assembly = Assembly.GetExecutingAssembly();
bool pfWasVerified = false;
if (!StrongNameSignatureVerificationEx(assembly.Location, true, ref pfWasVerified))
{
//خاتمه برنامه در صورت عدم وجود امضای دیجیتال معتبر
throw new Exception();
}
}
}

class Program
{
static void Main(string[] args)
{
Validation.SigCheck();
}
}
}
خوب، شاید پس از حذف و وصله شدن اسمبلی، مجددا strong name به آن اضافه شود! ، آن وقت چه باید کرد؟
زمانیکه به اسمبلی خود امضای دیجیتال اضافه می‌کنید، هش رمزنگاری شده فایل با الگوریتم RSA ، به همراه public key مورد نیاز در اسمبلی ذخیره می‌شوند. از آنجائیکه private key الگوریتم RSA را منتشر نکرده‌اید، شکستن الگوریتم RSA کار ساده‌ای نیست، مگر اینکه جفت کلید خودشان را تولید کنند و public key جدید را در فایل نهایی قرار دهند. بدیهی است این public key جدید با کلید عمومی ما که متناظر است با کلید خصوصی منتشر نشده‌ی اصلی، تطابق نخواهد داشد. برای آشنایی با تابعی که این بررسی را انجام می‌دهد به مقاله ذکر شده رجوع کنید:



نظرات مطالب
اعمال تزریق وابستگی‌ها به مثال رسمی ASP.NET Identity
سلام با کد ذیل _userStore واسه من نال بر میگردونه
using System.Data.Entity;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity.EntityFramework;
using SmartMarket.Core.Domain.Members;
using SmartMarket.Data;

namespace SmartMarket.Services.Members
{
    /// <summary>
    /// The ApplicationUserStore Class 
    /// </summary>
    public class ApplicationUserStore : UserStore<User, Role, int, UserLogin, UserRole, UserClaim>, IApplicationUserStore
    {
#region Fields (1) 

        private readonly IDbSet<User> _userStore;

#endregion Fields 

#region Constructors (2) 

        /// <summary>
        /// Initializes a new instance of the <see cref="ApplicationUserStore" /> class.
        /// </summary>
        /// <param name="dbContext">The database context.</param>
        public ApplicationUserStore(DbContext dbContext) : base(dbContext) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="ApplicationUserStore"/> class.
        /// </summary>
        /// <param name="context">The context.</param>
        public ApplicationUserStore(IdentityDbContext context)
            : base(context)
        {
            _userStore = context.Set<User>();
 
        }

#endregion Constructors 

#region Methods (2) 

// Public Methods (2) 

        /// <summary>
        /// Adds to previous passwords asynchronous.
        /// </summary>
        /// <param name="user">The user.</param>
        /// <param name="password">The password.</param>
        /// <returns></returns>
        public Task AddToPreviousPasswordsAsync(User user, string password)
        {
            user.PreviousUserPasswords.Add(new PreviousPassword { UserId = user.Id, PasswordHash = password });
            return UpdateAsync(user);
        }

        /// <summary>
        /// Finds the by identifier asynchronous.
        /// </summary>
        /// <param name="userId">The user identifier.</param>
        /// <returns></returns>
        public override Task<User> FindByIdAsync(int userId)
        {
            return Task.FromResult(_userStore.Find(userId));
        }

#endregion Methods 

        /// <summary>
        /// Creates the asynchronous.
        /// </summary>
        /// <param name="user">The user.</param>
        /// <returns></returns>
        public override async Task CreateAsync(User user)
        {
            await base.CreateAsync(user);
            await AddToPreviousPasswordsAsync(user, user.PasswordHash);
        }
    }
}
مطالب
افزودن یک DataType جدید برای نگه‌داری تاریخ خورشیدی - 3
در دو درس گذشته با DataType ای که با هم نوشتیم؛ می‌توانستیم این پرس‌وجوها را داشته باشیم:
Declare @MyDate JalaliDate = '1392/02/11 21:38:24'
Select @MyDate.ToString() as MyDateTime
           , @MyDate.GetDate() as MyDate
           , @MyDate.GetTime() as MyTime
           , @MyDate.Year as MyYear
           , @MyDate.Month as MyMonth
          , @MyDate.Day as MyDay
          , @MyDate.Hour as MyHour
          , @MyDate.Minute as MyMinute
          , @MyDate.Second as MySecond
          , @MyDate.JalaliDateAdd('Year',1) as NextYear
          , @MyDate.JalaliDateAdd('Month',1) as NextMonth
          , @MyDate.JalaliDateAdd('Day',1) as NextDay
          , @MyDate.JalaliDateAdd('Hour',1) as NextHour
          , @MyDate.JalaliDateAdd('Minute',1) as NextMinute
          , @MyDate.JalaliDateAdd('Second',1) as NextSecond
که در نهایت خروجی کار به شکل زیر خواهد بود:

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

برای این‌کار پروژه‌ای را که در دو درس 1 ساختیم باز کنید و سپس روی نام پروژه در Solution Explorer راست‌کلیک کرده و Add New Item را انتخاب کنید.

 
محتویات فایل بازشده را حذف کنید و دستورهای زیر را جای‌گزین کنید:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction]
    public static JalaliDate GetCurrentDateTime()
    {
        System.Globalization.PersianCalendar pers = new System.Globalization.PersianCalendar();
        DateTime CurrentDate = DateTime.Now;
        JalaliDate jl;
        jl.Year = (Int16)pers.GetYear(CurrentDate);
        jl.Month = (byte)pers.GetMonth(CurrentDate);
        jl.Day = (byte)pers.GetDayOfMonth(CurrentDate);
        jl.Hour = (byte)pers.GetHour(CurrentDate);
        jl.Minute = (byte)pers.GetMinute(CurrentDate);
        jl.Second = (byte)pers.GetSecond(CurrentDate);
        return jl;
    }
}
بار دیگر پروژه را به روشی که در درس یک آموزش داده شد؛ Publish کنید. اکنون می‌توانیم تاریخ و زمان کنونی را در متغیری از نوع JalaliDate وارد کنیم.
Declare @MyDate JalaliDate = dbo.GetCurrentDateTime()
یا به این شکل تاریخ و زمان جاری را مشاهده کنیم:
Select dbo.GetCurrentDateTime().ToString()

دنباله دارد...
نظرات مطالب
تاریخچه‌ی نگارش‌های مختلف دات نت فریم ورک
ممنون. دات نت فریم ورک پایه و اساس محصولات جدید مایکروسافت است. به همین جهت توسعه‌ی آن برای آن‌ها خیلی اهمیت دارد.
WF برای مثال در شیرپوینت و بیزتاک سرور داره استفاده میشه. یا خیلی از قسمت‌هایی که به ASP.Net‌ داره اضافه میشه به نظر من با هدفگیری این محصولات است که خیرش داره به بقیه هم میرسه.
management studio اس کیوال سرورهای 2005 به بعد با دات نت نوشته شده.
و خیلی از مواردی که مایکروسافت از لحاظ امنیتی با آن‌ها مشکل داشت با کوچ به دات نت فریم ورک به شدت امن شده‌اند چون برای مثال buffer overflow attacks در دات نت معنی ندارد و اگر کد شما فقط از managed code تشکیل شده باشد، کاملا کنترل شده است. به همین جهت اگر به لیست تعداد حملات صورت گرفته به اس کیوال سرورهای 2005 به بعد مراجعه کنید، کاهش محسوسی رو می‌تونید ملاحظه کنید.
مطالب دوره‌ها
بررسی جزئیات تزریق وابستگی‌ها در قالب پروژه WPF Framework
در قالب طراحی شده، نه در کدهای Viewهای اضافه شده و نه در ViewModelها، اثری از کدهای مرتبط با تزریق وابستگی‌ها و یا حتی وهله سازی ViewModel مرتبط با یک View مشاهده نمی‌شود. در ادامه قصد داریم جزئیات پیاده سازی آن‌را مرور کنیم.

مدیریت خودکار وهله سازی ViewModelها

اگر به فایل MVVM\ViewModelFactory.cs قرار گرفته در پروژه Common مراجعه کنید، کدهای کلاسی که کار وهله سازی ViewModelها را انجام می‌دهد، مشاهده خواهید کرد:
using System.Windows;
using StructureMap;

namespace WpfFramework1999.Common.MVVM
{
    /// <summary>
    /// Stitches together a view and its view-model
    /// </summary>
    public class ViewModelFactory
    {
        private readonly FrameworkElement _control;

        /// <summary>
        /// سازنده کلاس تزریق وابستگی‌ها به ویوو مدل و وهله سازی آن
        /// </summary>
        /// <param name="control">وهله‌ای از شیءایی که باید کار تزریق وابستگی‌ها در آن انجام شود</param>
        public ViewModelFactory(FrameworkElement control)
        {
            _control = control;
        }

        /// <summary>
        /// وهله متناظر با ویوو مدل
        /// </summary>
        public IViewModel ViewModelInstance { get; private set; }

        /// <summary>
        /// کار تزریق خودکار وابستگی‌ها و وهله سازی ویوو مدل مرتبط انجام خواهد شد
        /// </summary>        
        public void WireUp()
        {
            var viewName = _control.GetType().Name;
            var viewModelName = string.Concat(viewName, "ViewModel"); //قرار داد نامگذاری ما است

            if (!_control.IsLoaded)
            {
                _control.Loaded += (s, e) =>
                {
                    setDataContext(viewModelName);
                };
            }
            else
            {
                setDataContext(viewModelName);
            }
        }

        private void setDataContext(string viewModelName)
        {
            //کار تزریق خودکار وابستگی‌ها و وهله سازی ویوو مدل مرتبط انجام خواهد شد
            ViewModelInstance = ObjectFactory.TryGetInstance<IViewModel>(viewModelName);
            if (ViewModelInstance == null) // این صفحه ویوو مدل ندارد
                return;

            _control.DataContext = ViewModelInstance;
        }
    }
}
در این کلاس، یک وهله از صفحه‌ای که توسط کاربر درخواست شده‌است، در سازنده کلاس دریافت گردیده و سپس در متد WireUp، بر اساس قرارداد نامگذاری که پیشتر نیز عنوان شد، ViewModel متناظر با نام View از IoC Container استخراج و وهله سازی می‌گردد. سپس این وهله به DataContext صفحه انتساب داده می‌شود.
چند سؤال مهم:
- IoC Container از کجا می‌داند که ViewModelها در کجا قرار دارند؟
- این کلاس ViewModelFactory چگونه به وهله‌ای از یک صفحه درخواستی توسط کاربر دسترسی پیدا می‌کند و در کجا؟


IoC Container از کجا می‌داند که ViewModelها در کجا قرار دارند؟

اگر بحث سری جاری را از ابتدا دنبال کرده باشید، عنوان شد که ViewModelها را در این قالب، باید مشتق شده از کلاس پایه‌ای به نام BaseViewModel تهیه کنیم. برای مثال:
/// <summary>
/// ویوو مدل افزودن و مدیریت کاربران
/// </summary>
public class AddNewUserViewModel : BaseViewModel
این کلاس پایه که در فایل MVVM\BaseViewModel.cs پروژه Common قرار دارد، به نحو زیر آغاز شده است:
/// <summary>
/// کلاس پایه ویوو مدل‌های برنامه که جهت علامتگذاری آن‌ها برای سیم کشی‌های تزریق وابستگی‌های برنامه نیز استفاده می‌شود
/// </summary>
public abstract class BaseViewModel : DataErrorInfoBase, INotifyPropertyChanged, IViewModel
اگر دقت کنید در اینجا اینترفیس IViewModel نیز ذکر شده است. این اینترفیس برای علامتگذاری ViewModelها و یافتن خودکار آن‌ها توسط IoC Container مورد استفاده درنظر گرفته شده است. اگر به فایل Core\IocConfig.cs پروژه Infrastructure مراجعه کنید، چنین تنظیمی را در آن مشاهده خواهید نمود:
// Add all types that implement IView into the container,
// and name each specific type by the short type name.
scan.AddAllTypesOf<IViewModel>().NameBy(type => type.Name);
به این ترتیب StructureMap با اسکن اسمبلی Infrastructure کلیه کلاس‌های پیاده سازی کننده IViewModel را یافته و سپس آن‌ها را بر اساس نام متناظری که دارند، ذخیره می‌کند. با این تنظیم، اکنون در کلاس ViewModelFactory یک چنین کدی کار خواهد کرد:
 //کار تزریق خودکار وابستگی‌ها و وهله سازی ویوو مدل مرتبط انجام خواهد شد
ViewModelInstance = ObjectFactory.TryGetInstance<IViewModel>(viewModelName);


کلاس ViewModelFactory چگونه به وهله‌ای از یک صفحه درخواستی توسط کاربر دسترسی پیدا می‌کند و در کجا؟

در اینجا قسمتی از کدهای فایل Core\FrameFactory.cs قرار گرفته در پروژه Infrastructure را ملاحظه می‌کنید:
namespace WpfFramework.Infrastructure.Core
{
    /// <summary>
    /// ایجاد یک کنترل فریم سفارشی که قابلیت تزریق وابستگی‌ها را به صورت خودکار دارد
    /// به همراه اعمال مسایل راهبری برنامه که از منوی اصلی دریافت می‌شوند
    /// </summary>
    public class FrameFactory : Frame
    {
        /// <summary>
        /// در اینجا می‌شود به وهله‌ای از صفحه‌ای که قرار است اضافه گردد دسترسی یافت
        /// </summary>
        protected override void OnContentChanged(object oldContent, object newContent)
        {
            base.OnContentChanged(oldContent, newContent);

            var newPage = newContent as FrameworkElement;
            if (newPage == null)
                return;

            _currentViewModelFactory = new ViewModelFactory(newPage);
            _currentViewModelFactory.WireUp(); //کار تزریق وابستگی‌ها و وهله سازی ویوو مدل مرتبط انجام خواهد شد
        }
    }
}
در این کلاس، یک Frame سفارشی را طراحی کرده‌ایم؛ از این جهت که بتوان متد OnContentChanged آن‌را تحریف کرد. در این متد، newContent دقیقا وهله‌ای از صفحه جدیدی است که توسط کاربر درخواست شده‌است. خوب ... این وهله را داریم، بنابراین تنها کافی است آن‌را به کلاس ViewModelFactory ارسال کنیم و متد WireUp آن‌را بر روی وهله کلاس صفحه درخواستی فراخوانی نمائیم. به این ترتیب، صفحه‌ای نمایش داده خواهد شد که DataContext آن با وهله‌ای از ViewModel متناظر مقدار دهی شده‌است. از این جهت که این وهله سازی توسط IoC Container صورت می‌گیرد، کلیه وابستگی‌های تعریف شده در سازنده کلاس ViewModel نیز به صورت خودکار وهله سازی و مقدار دهی خواهند شد.

نهایتا فراخوانی متد IocConfig.Init، در فایل App.xaml.cs پروژه ریشه، در آغاز برنامه قرار گرفته است.
مطالب دوره‌ها
ایجاد یک اسمبلی جدید توسط Reflection.Emit
مطابق استاندارد ECMA-335 قسمت دوم آن، یک اسمبلی از یک یا چند ماژول تشکیل می‌شود. هر ماژول از تعدادی نوع، enum و delegate تشکیل خواهد شد و هر نوع دارای تعدادی متد، فیلد، خاصیت و غیره می‌باشد. به همین جهت در حین کار با Reflection.Emit نیز این مراحل رعایت می‌شوند. ابتدا یک اسمبلی (AppDomain.DefineDynamicAssembly) ایجاد خواهد شد (یا از اسمبلی موجود استفاده می‌شود). سپس یک ماژول (AssemblyBuilder.DefineDynamicModule) را باید به آن اضافه کنیم (یا از ماژول اسمبلی جاری استفاده نمائیم). در ادامه یک Type باید به این ماژول اضافه شود (ModuleBuilder.DefineType) و اکنون می‌توان به این نوع جدید، سازنده (TypeBuilder.DefineConstructor)، متد (TypeBuilder.DefineMethod)، فیلد (TypeBuilder.DefineField)، خاصیت (TypeBuilder.DefineProperty) و رخداد (TypeBuilder.DefineEvent) اضافه کرد.


using System;
using System.Reflection;
using System.Reflection.Emit;

namespace FastReflectionTests
{
    class Program
    {
        static void Main(string[] args)
        {
            var name = "HelloWorld.exe";
            var assemblyName = new AssemblyName(name);
            // ایجاد یک اسمبلی جدید با قابلیت ذخیره سازی آن
            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
                                            name: assemblyName,
                                            access: AssemblyBuilderAccess.RunAndSave);
            // افزودن یک ماژول به اسمبلی
            var moduleBuilder = assemblyBuilder.DefineDynamicModule(name);
            // تعریف یک کلاس در این ماژول
            var programmClass = moduleBuilder.DefineType("Program", TypeAttributes.Public);
            // افزودن یک متد به این کلاس
            // این متد خروجی ندارد اما ورودی آن شبیه به متد اصلی یک برنامه کنسول است
            var mainMethod = programmClass.DefineMethod(name: "Main",
                                            attributes: MethodAttributes.Public | MethodAttributes.Static,
                                            returnType: null,
                                            parameterTypes: new Type[] { typeof(string[]) });
            // تعیین بدنه متد اصلی برنامه
            var il = mainMethod.GetILGenerator();
            il.Emit(OpCodes.Ldstr, "Hello World!");
            il.Emit(OpCodes.Call, (typeof(Console)).GetMethod("WriteLine", new Type[] { typeof(string) }));
            il.Emit(OpCodes.Call, (typeof(Console)).GetMethod("ReadKey", new Type[0]));
            il.Emit(OpCodes.Pop);
            il.Emit(OpCodes.Ret);

            // تکمیل کار ایجاد نوع جدید
            programmClass.CreateType();

            // تعیین نقطه شروع فایل اجرایی برنامه کنسول تهیه شده
            assemblyBuilder.SetEntryPoint(((Type)programmClass).GetMethod("Main"));

            // ذخیره سازی این اسمبلی بر روی دیسک سخت
            assemblyBuilder.Save(name);
        }
    }
}
مراحلی را که توضیح داده شد، در کدهای فوق ملاحظه می‌کنید. انتخاب حالت دسترسی AssemblyBuilderAccess.RunAndSave سبب می‌شود تا بتوان نتیجه حاصل را ذخیره کرد. فایل Exe نهایی را اگر در برنامه ILSpy باز کنیم چنین شکلی دارد:
using System;
public class Program
{
    public static void Main(string[] array)
    {
       Console.WriteLine("Hello World!");
       Console.ReadKey();
    }
}
مطالب
پیاده سازی Unobtrusive Ajax در ASP.NET Core 1.0
پیاده سازی Unobtrusive Ajax را در ASP.NET MVC 5.x، می‌توانید در مطلب «ASP.NET MVC #21» مطالعه کنید. HTML Helpers مرتبط با Ajax، به طور کامل از ASP.NET Core 1.0 حذف شده‌اند. اما این مورد به این معنا نیست که نمی‌توان Unobtrusive Ajax را در ASP.NET Core که تمرکزش بیشتر بر روی Tag Helpers جدید هست تا HTML Helpers قدیمی، پیاده سازی کرد.


Unobtrusive Ajax چیست؟

در حالت معمولی، با استفاده از متد ajax جی‌کوئری، کار ارسال غیرهمزمان اطلاعات، به سمت سرور صورت می‌گیرد. چون در این روش کدهای جی‌کوئری داخل صفحات برنامه‌های ما قرار می‌گیرند، به این روش، «روش چسبنده» می‌گویند. اما با استفاده از افزونه‌ی «jquery.unobtrusive-ajax.min.js» مایکروسافت، می‌توان این کدهای چسبنده را تبدیل به کدهای غیرچسنبده یا Unobtrusive کرد. در این حالت، پارامترهای متد ajax، به صورت ویژگی‌ها (attributes) به شکل data-ajax به المان‌های مختلف صفحه اضافه می‌شوند و به این ترتیب، افزونه‌ی یاد شده به صورت خودکار با یافتن مقادیر ویژگی‌های data-ajax، این المان‌ها را تبدیل به المان‌های ای‌جکسی می‌کند. در این حالت به کدهایی تمیزتر و عاری از متدهای چسبنده‌ی ajax قرار گرفته‌ی در داخل صفحات وب خواهیم رسید.
روش طراحی Unobtrusive را در کتابخانه‌های معروفی مانند بوت استرپ هم می‌توان مشاهده کرد.


پیشنیازهای فعال سازی Unobtrusive Ajax در ASP.NET Core 1.0

توزیع افزونه‌ی «jquery.unobtrusive-ajax.min.js» مایکروسافت، از طریق bower صورت می‌گیرد که پیشتر در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 14 - فعال سازی اعتبارسنجی ورودی‌های کاربران» با آن آشنا شدیم. در اینجا نیز برای دریافت آن، تنها کافی است فایل bower.json را به نحو ذیل تکمیل کرد:
{
  "name": "asp.net",
  "private": true,
  "dependencies": {
   "bootstrap": "3.3.6",
   "jquery": "2.2.0",
   "jquery-validation": "1.14.0",
   "jquery-validation-unobtrusive": "3.2.6",
   "jquery-ajax-unobtrusive": "3.2.4"
  }
}
و پس از آن فایل bundleconfig.json مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 21 - بررسی تغییرات Bundling و Minification» یک چنین شکلی را پیدا می‌کند:
[
  {
   "outputFileName": "wwwroot/css/site.min.css",
   "inputFiles": [
    "bower_components/bootstrap/dist/css/bootstrap.min.css",
    "content/site.css"
   ]
  },
  {
   "outputFileName": "wwwroot/js/site.min.js",
   "inputFiles": [
    "bower_components/jquery/dist/jquery.min.js",
    "bower_components/jquery-validation/dist/jquery.validate.min.js",
    "bower_components/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js",
    "bower_components/jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.min.js",
    "bower_components/bootstrap/dist/js/bootstrap.min.js"
   ],
   "minify": {
    "enabled": true,
    "renameLocals": true
   },
   "sourceMap": false
  }
]
در اینجا فایل‌های css و اسکریپت مورد نیاز برنامه، به ترتیب اضافه شده و یکی خواهند شد. خروجی نهایی آن‌ها به شکل زیر در صفحات وب مورد استفاده قرار می‌گیرند:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
    <link href="~/css/site.min.css" rel="stylesheet" />
</head>
<body>
    <div>
        <div>
                @RenderBody()
        </div>
    </div>

    <script src="~/js/site.min.js" type="text/javascript" asp-append-version="true"></script>
    @RenderSection("Scripts", required: false)
</body>
</html>
در اینجا تنها دو فایل نهایی این عملیات، یعنی css/site.min.css و js/site.min.js به صفحه الحاق شده‌اند که حاوی تمام پیشنیازهای اسکریپتی و شیوه‌نامه‌های برنامه هستند و در این حالت دیگر نیاز به افزودن آن‌ها به دیگر صفحات سایت نیست.


استفاده از معادل‌های واقعی Unobtrusive Ajax در ASP.NET Core 1.0

واقعیت این است که HTML Helper ای‌جکسی حذف شده‌ی از ASP.NET Core 1.0، کاری بجز افزودن ویژگی‌های data-ajax را که توسط افزونه‌ی jquery.unobtrusive-ajax.min.js پردازش می‌شوند، انجام نمی‌دهد و این افزونه مستقل است از مباحث سمت سرور و به نگارش خاصی از ASP.NET گره نخورده است. بنابراین در اینجا تنها کاری را که باید انجام داد، استفاده از همان ویژگی‌های اصلی است که این افزونه قادر به شناسایی آن‌ها است.
خلاصه‌ی آن‌ها را جهت انتقال کدهای قدیمی و یا تهیه‌ی کدهای جدید، در جدول ذیل می‌توانید مشاهده کنید:

 HTML attribute   AjaxOptions 
 data-ajax-confirm   Confirm 
 data-ajax-method   HttpMethod 
 data-ajax-mode   InsertionMode 
 data-ajax-loading-duration   LoadingElementDuration 
 data-ajax-loading   LoadingElementId 
 data-ajax-begin   OnBegin 
 data-ajax-complete   OnComplete 
 data-ajax-failure   OnFailure 
 data-ajax-success   OnSuccess 
 data-ajax-update   UpdateTargetId 
 data-ajax-url   Url 
   
در ASP.NET Core 1.0، به علت حذف متدهای کمکی Ajax دیگر خبری از AjaxOptions نیست. اما اگر علاقمند به انتقال کدهای قدیمی به ASP.NET Core 1.0 هستید، معادل‌های اصلی این پارامترها را می‌توانید در ستون HTML attribute مشاهده کنید.

چند نکته:
- اگر قصد استفاده‌ی از این ویژگی‌ها را دارید، باید ویژگی "data-ajax="true را نیز حتما قید کنید تا سیستم Unobtrusive Ajax فعال شود.
- ویژگی data-ajax-mode تنها با ذکر data-ajax-update (و یا همان UpdateTargetId پیشین) معنا پیدا می‌کند.
- ویژگی data-ajax-loading-duration نیاز به ذکر data-ajax-loading (و یا همان LoadingElementId پیشین) را دارد.
- ویژگی data-ajax-mode مقادیر before، after و replace-with را می‌پذیرد. اگر قید نشود، کل المان با data دریافتی جایگزین می‌شود.
- سه callback قابل تعریف data-ajax-complete، data-ajax-failure و data-ajax-success، یک چنین پارامترهایی را از سمت سرور در اختیار کلاینت قرار می‌دهند:

parameters  
 Callback  
 xhr, status   data-ajax-complete 
 data, status, xhr   data-ajax-success 
 xhr, status, error   data-ajax-failure 

برای مثال می‌توان ویژگی data-ajax-success را به نحو ذیل در سمت کلاینت مقدار دهی کرد:
 data-ajax-success = "myJsMethod"
این متد جاوا اسکریپتی یک چنین امضایی را دارد:
  function myJsMethod(data, status, xhr) {
}
در این حالت در سمت سرور، پارامتر data در یک اکشن متد، به صورت ذیل مقدار دهی می‌شود:
 return Json(new { param1 = 1, param2 = 2, ... });
و در سمت کلاینت در متد myJsMethod این پارامترها را به صورت data.param1 می‌توان دریافت کرد.


مثال‌هایی از افزودن ویژگی‌های data-ajax به المان‌های مختلف

 در حالت استفاده از Form Tag Helpers که در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 12 - معرفی Tag Helpers» بررسی شدند، یک فرم ای‌جکسی، چنین تعاریفی را پیدا خواهد کرد:
با این ViewModel فرضی
using System.ComponentModel.DataAnnotations;
 
namespace Core1RtmEmptyTest.ViewModels.Account
{
    public class RegisterViewModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; }
    }
}
که در View متناظر Ajax ایی ذیل استفاده شده‌است:
@using Core1RtmEmptyTest.ViewModels.Account
@model RegisterViewModel
@{
}
 
<form method="post"
      asp-controller="TestAjax"
      asp-action="Index"
      asp-route-returnurl="@ViewBag.ReturnUrl"
      class="form-horizontal"
      role="form"
      data-ajax="true"
      data-ajax-loading="#Progress"
      data-ajax-success="myJsMethod">
 
    <input asp-for="Email" class="form-control" />
    <span asp-validation-for="Email" class="text-danger"></span>
 
    <button type="submit">ارسال</button>
 
    <div id="Progress" style="display: none">
        <img src="images/loading.gif" alt="loading..." />
    </div>
</form>
 
@section scripts{
    <script type="text/javascript">
        function myJsMethod(data, status, xhr) {
            alert(data.param1);
        }
    </script>
}
در اینجا تمام تعاریف مانند قبل است؛ تنها سه ویژگی data-ajax جهت فعال سازی jquery-ajax-unobtrusive به فرم اضافه شده‌اند. همچنین یک callback دریافت پیام موفقیت آمیز بودن عملیات Ajax ایی نیز تعریف شده‌است.

این View از کنترلر ذیل استفاده می‌کند:
using Core1RtmEmptyTest.ViewModels.Account;
using Microsoft.AspNetCore.Mvc;
 
namespace Core1RtmEmptyTest.Controllers
{
    public class TestAjaxController : Controller
    {
 
        public IActionResult Index()
        {
            return View();
        }
 
        [HttpPost]
        public IActionResult Index([FromForm]RegisterViewModel vm)
        {
            var ajax = isAjax();
            if (ajax)
            {
                // it's an ajax post
            }
 
 
            if (ModelState.IsValid)
            {
                //todo: save data
 
                return Json(new { param1 = 1, param2 = 2 });
            }
            return View();
        }
 
        private bool isAjax()
        {
            return Request?.Headers != null && Request.Headers["X-Requested-With"] == "XMLHttpRequest";
        }
    }
}
به ASP.NET Core 1.0، متد کمکی IsAjax اضافه نشده‌است؛ اما تعریف آن‌را در این کنترلر مشاهده می‌کنید. در مورد قید FromForm در ادامه توضیح داده خواهد شد (هرچند در این مورد خاص، حالت پیش فرض است و الزامی به قید آن نیست).

و یا Action Link ای‌جکسی نیز به صورت خلاصه به این نحو قابل تعریف است:
<div id="EmployeeInfo">
<a 
 asp-controller="MyController" asp-action="MyAction"
 data-ajax="true" 
 data-ajax-loading="#Progress" 
 data-ajax-method="POST" 
 data-ajax-mode="replace" 
 data-ajax-update="#EmployeeInfo">
 Get Employee-1 info
</a>

  <div id="Progress" style="display: none">
    <img src="images/loading.gif" alt="loading..."  />
  </div>
</div>


نکته‌ای در مورد اکشن متدهای ای‌جکسی در ASP.NET Core 1.0

همانطور که در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 18 - کار با ASP.NET Web API»، قسمت «تغییرات Model binding پیش فرض، برای پشتیبانی از ASP.NET MVC و ASP.NET Web API» نیز ذکر شد:
public IActionResult Index([FromBody] MyViewModel vm)
{
   return View();
}
ذکر ویژگی FromBody در اینجا الزامی است. از این جهت که اطلاعات با فرمت JSON، از قسمت body درخواست استخراج و به MyViewModel بایند خواهند شد (در حالت dataType: json). و اگر dataType : application/x-www-form-urlencoded; charset=utf-8 بود (مانند حالت پیش فرض Unobtrusive Ajax)، باید از ویژگی FromForm استفاده شود. در غیر اینصورت در سمت سرور نال دریافت خواهیم کرد.
مطالب
نحوه نمایش فیلدهای تصویری و همچنین بارگذاری تصاویر از مسیری مشخص در PdfReport
عموما برای نمایش تصاویر در گزارشات، یکی از دو حالت زیر وجود دارد:
الف) مسیر تصاویر موجود در فایل سیستم، در بانک اطلاعاتی ذخیره شده‌اند و قرار است گزارش نهایی در ستونی مشخص شامل این تصاویر باشد.
ب) محتوای بایناری تصاویر در خود بانک اطلاعاتی ذخیره شده‌اند و نیاز است آن‌ها را در گزارشات نمایش دهیم.

هر دو حالت فوق در PdfReport پشتیبانی می‌شوند و در ادامه نحوه انجام این موارد را بررسی خواهیم کرد.

الف) بارگذاری تصاویر از فایل سیستم

ابتدا مدل زیر را در نظر بگیرید:
namespace PdfReportSamples.Models
{
    public class ImageRecord
    {
        public int Id { set; get; }
        public string ImagePath { set; get; }
        public string Name { set; get; }
    }
}
توسط آن تعدادی رکورد را که ImagePath آن‌ها فایل‌هایی بر روی سیستم هستند، خواهیم ساخت:
using System;
using System.Collections.Generic;
using iTextSharp.text.pdf;
using PdfReportSamples.Models;
using PdfRpt.Core.Contracts;
using PdfRpt.FluentInterface;

namespace PdfReportSamples.ImageFilePath
{
    public class ImageFilePathPdfReport
    {
        public IPdfReportData CreatePdfReport()
        {
            return new PdfReport().DocumentPreferences(doc =>
            {
                doc.RunDirection(PdfRunDirection.RightToLeft);
                doc.Orientation(PageOrientation.Portrait);
                doc.PageSize(PdfPageSize.A4);
                doc.DocumentMetadata(new DocumentMetadata { Author = "Vahid", Application = "PdfRpt", Keywords = "Test", Subject = "Test Rpt", Title = "Test" });
            })
            .DefaultFonts(fonts =>
            {
                fonts.Path(AppPath.ApplicationPath + "\\fonts\\irsans.ttf",
                                  Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\verdana.ttf");
            })
            .PagesFooter(footer =>
            {
                footer.DefaultFooter(DateTime.Now.ToString("MM/dd/yyyy"));
            })
            .PagesHeader(header =>
            {
                header.DefaultHeader(defaultHeader =>
                {
                    defaultHeader.ImagePath(AppPath.ApplicationPath + "\\Images\\01.png");
                    defaultHeader.Message("گزارش جدید ما");
                });
            })
            .MainTableTemplate(template =>
            {
                template.BasicTemplate(BasicTemplate.SnowyPineTemplate);
            })
            .MainTablePreferences(table =>
            {
                table.ColumnsWidthsType(TableColumnWidthType.Relative);
            })
            .MainTableDataSource(dataSource =>
            {
                var listOfRows = new List<ImageRecord>
                                             {
                                                 new ImageRecord
                                                     {
                                                         Id=1,
                                                         ImagePath =  AppPath.ApplicationPath + "\\Images\\01.png",
                                                         Name = "Rnd"
                                                     },
                                                 new ImageRecord
                                                     {
                                                         Id=2,
                                                         ImagePath =  AppPath.ApplicationPath + "\\Images\\02.png",
                                                         Name = "Bug"
                                                     },
                                                 new ImageRecord
                                                     {
                                                         Id=3,
                                                         ImagePath =  AppPath.ApplicationPath + "\\Images\\03.png",
                                                         Name = "Stuff"
                                                     },
                                                 new ImageRecord
                                                     {
                                                         Id=4,
                                                         ImagePath =  AppPath.ApplicationPath + "\\Images\\04.png",
                                                         Name = "Sun"
                                                     }
                                             };
                dataSource.StronglyTypedList(listOfRows);
            })
            .MainTableColumns(columns =>
            {
                columns.AddColumn(column =>
                {
                    column.PropertyName("rowNo");
                    column.IsRowNumber(true);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(0);
                    column.Width(1);
                    column.HeaderCell("ردیف");
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName<ImageRecord>(x => x.Id);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(1);
                    column.Width(3);
                    column.HeaderCell("شماره");
                    column.ColumnItemsTemplate(t => t.Barcode(new Barcode39()));
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName<ImageRecord>(x => x.ImagePath);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(2);
                    column.Width(3);
                    column.HeaderCell("تصویر");
                    column.ColumnItemsTemplate(t => t.ImageFilePath(defaultImageFilePath: string.Empty, fitImages: false));
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName<ImageRecord>(x => x.Name);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(3);
                    column.Width(2);
                    column.HeaderCell("نام");
                });
            })
            .MainTableEvents(events =>
            {
                events.DataSourceIsEmpty(message: "There is no data available to display.");
            })
            .Export(export =>
            {
                export.ToExcel();
                export.ToXml();
            })
            .Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\RptImageFilePathSample.pdf"));
        }
    }
}

توضیحات:
- در متد MainTableDataSource، یک سری رکورد دلخواه را بر اساس تعدادی تصویر مشخص ایجاد کرده‌ایم. این تصاویر در پوشه bin\images مثال‌های PdfReport قرار دارند. سپس آن‌ها را توسط متد dataSource.StronglyTypedList، در اختیار PdfReport قرار داده‌ایم.
- در مرحله بعد، توسط متد MainTableColumns، ستون‌های دلخواه گزارش را ایجاد کرده‌ایم.
دو نکته در اینجا مهم هستند:
الف) برای نمایش یک تصویر از فایل سیستم، فقط کافی است از ColumnItemsTemplate متناظر با آن استفاده کرد (حالت پیش فرض، نمایش متنی اطلاعات است). برای نمونه قالب پیش فرض ImageFilePath به نحو زیر قابل استفاده است:
 column.ColumnItemsTemplate(t => t.ImageFilePath(defaultImageFilePath: string.Empty, fitImages: false));
در اینجا در آرگومان اول آن می‌توان مسیر تصویری را مشخص کرد که در صورت موجود نبودن تصویر مشخص شده در منبع داده، بهتر است نمایش داده شود. اگر string.Empty وارد شد، از این مورد صرفنظر خواهد شد. آرگومان دوم آن مشخص می‌کند که آیا تصویر نمایش داده شده باید با ابعاد سلول متناظر با آن هماهنگ شده و نمایش داده شود یا خیر.
ب) در کتابخانه iTextSharp که پایه PdfReport است، امکان تهیه بارکد هم وجود دارد. برای مثال این بارکدها توسط قالب زیر، قابل استفاده خواهند شد:
 column.ColumnItemsTemplate(t => t.Barcode(new Barcode39()));
تصویری از حاصل این گزارش را در ذیل مشاهده می‌کنید:


ب) بارگذاری محتوای تصاویر از بانک اطلاعاتی

ابتدا کدهای کامل این مثال را در نظر بگیرید:
using System;
using PdfRpt.Core.Contracts;
using PdfRpt.FluentInterface;

namespace PdfReportSamples.DbImage
{
    public class DbImagePdfReport
    {
        public IPdfReportData CreatePdfReport()
        {
            return new PdfReport().DocumentPreferences(doc =>
            {
                doc.RunDirection(PdfRunDirection.RightToLeft);
                doc.Orientation(PageOrientation.Portrait);
                doc.PageSize(PdfPageSize.A4);
                doc.DocumentMetadata(new DocumentMetadata { Author = "Vahid", Application = "PdfRpt", Keywords = "Test", Subject = "Test Rpt", Title = "Test" });
            })
             .DefaultFonts(fonts =>
             {
                 fonts.Path(AppPath.ApplicationPath + "\\fonts\\irsans.ttf",
                            Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\verdana.ttf");
             })
             .PagesFooter(footer =>
             {
                 footer.DefaultFooter(DateTime.Now.ToString("MM/dd/yyyy"));
             })
             .PagesHeader(header =>
             {
                 header.DefaultHeader(defaultHeader =>
                 {
                     defaultHeader.ImagePath(AppPath.ApplicationPath + "\\Images\\01.png");
                     defaultHeader.Message("گزارش جدید ما");
                 });
             })
             .MainTableTemplate(template =>
             {
                 template.BasicTemplate(BasicTemplate.AutumnTemplate);
             })
             .MainTablePreferences(table =>
             {
                 table.ColumnsWidthsType(TableColumnWidthType.FitToContent);
             })
             .MainTableDataSource(dataSource =>
             {
                 dataSource.GenericDataReader(
                   providerName: "System.Data.SQLite",
                   connectionString: "Data Source=" + AppPath.ApplicationPath + "\\data\\blogs.sqlite",
                   sql: @"SELECT [url], [name], [NumberOfPosts], [AddDate], [thumbnail]
                               FROM [tblBlogs]
                               WHERE [NumberOfPosts]>=@p1",
                   parametersValues: new object[] { 10 }
               );
             })
             .MainTableEvents(events =>
             {
                 events.DataSourceIsEmpty(message: "There is no data available to display.");
             })
             .MainTableSummarySettings(summary =>
             {
                 summary.OverallSummarySettings("جمع کل");
                 summary.PreviousPageSummarySettings("نقل از صفحه قبل");
             })
             .MainTableColumns(columns =>
             {
                 columns.AddColumn(column =>
                 {
                     column.PropertyName("rowNo");
                     column.IsRowNumber(true);
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(0);
                     column.HeaderCell("ردیف");
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName("url");
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(1);
                     column.HeaderCell("آدرس");
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName("name");
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(2);
                     column.HeaderCell("نام");
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName("NumberOfPosts");
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(3);
                     column.HeaderCell("تعداد پست");
                     column.ColumnItemsTemplate(template =>
                     {
                         template.TextBlock();
                         template.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                     });
                     column.AggregateFunction(aggregateFunction =>
                     {
                         aggregateFunction.NumericAggregateFunction(AggregateFunction.Sum);
                         aggregateFunction.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                     });
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName("AddDate");
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(4);
                     column.HeaderCell("تاریخ ثبت");
                     column.ColumnItemsTemplate(template =>
                     {
                         template.TextBlock();
                         template.DisplayFormatFormula(obj => obj == null ? string.Empty : ((DateTime)obj).ToString("dd/MM/yyyy HH:mm"));
                     });
                 });

                 columns.AddColumn(column =>
                 {
                     column.PropertyName("thumbnail");
                     column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                     column.IsVisible(true);
                     column.Order(5);
                     column.HeaderCell("تصویر");
                     column.ColumnItemsTemplate(t => t.ByteArrayImage(defaultImageFilePath: string.Empty, fitImages: false));
                 });
             })
             .Export(export =>
             {
                 export.ToXml();
                 export.ToExcel();
             })
             .Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\RptDbImageSample.pdf"));
        }
    }
}

توضیحات:
بانک اطلاعاتی SQLite موجود در پوشه bin\data سورس PdfReport، حاوی فیلدی است به نام thumbnail که در آن محتوای یک سری تصویر ذخیره شده‌اند.
در اینجا توسط کوئری زیر قصد داریم این اطلاعات را خوانده و نمایش دهیم:
SELECT [url], [name], [NumberOfPosts], [AddDate], [thumbnail]
FROM [tblBlogs]
WHERE [NumberOfPosts]>=@p1
همانطور که ملاحظه می‌کنید، در اینجا نیز تنها کافی است قالب ستون مرتبطی را انتخاب کنیم:
column.ColumnItemsTemplate(t => t.ByteArrayImage(defaultImageFilePath: string.Empty, fitImages: false));
قالب ByteArrayImage، اطلاعات باینری فیلد thumbnail را خوانده و تبدیل به تصاویر قرار داده شده در فایل گزارش نهایی می‌کند. پارامترهای آن، با پارامترهای قالب ImageFilePath که پیشتر توضیح داده شد، یکی هستند.