مطالب
تزریق وابستگی (Dependency Injection) و توسعه پذیری
دانستن اینکه چگونه یک نرم افزار با قابلیت نگهداری بالا بنویسیم مهم است ، برای اکثر سیستم‌های سازمانی زمانی که در فاز نگهداری صرف می‌شود بیشتر از زمان فاز توسعه می‌باشد. به عنوان مثال تصور کنید در حال توسعه یک سیستم مالی هستید ، این سیستم احتمالا بین شش ماه تا یک زمان برای توسعه نیاز دارد و بقیه‌ی دوره‌ی پنج ساله صرف نگهداری سیستم خواهد شد. در فاز نگهداری زمان صرف رفع باگ ، افزودن امکانات جدید و یا تغییر عملکرد ویژگی‌های فعلی می‌شود. مهم است که این تغییرات راحت و سریع صورت پذیرد.
 اطمینان از اینکه کدها قابلیت نگهداری دارند به توسعه دهندگان احتمالی که در آینده به پروژه اضافه می‌شوند کمک می‌کند سریع کد‌های فعلی را درک کنند و مشغول کار شوند. روش‌های زیادی برای افزایش قابلیت نگهداری کد‌ها وجود دارد ، مانند نوشتن آزمون‌های واحد ، شکستن قسمت‌های بزرگ سیستم به قسمت‌های کوچک‌تر و ... در این مورد که ما از یکی از زبان‌های شئ گرا مانند C# استفاده می‌کنیم در حالت معمول کلاس‌ها باید با مسئولیت‌های مستقل و منحصر به فرد طراحی شوند به جای آنکه تمام مسئولیت‌ها از قبیل پردازش ورودی‌های کاربر ، رندر کردن HTML و حتی Query زدن به دیتابیس را به یک کلاس سپرد (مثلا Controller در MVC ) باید برای هر مقصود کلاسی مجزا طراحی کرد. با این روش نتیجه اینگونه خواهد بود که می‌توان هر قسمت از عملکرد را بدون نیاز به تغییر بقیه‌ی قسمت‌های Codebase تغییر داد.
در این مطلب قصد داریم به کمک تزریق وابستگی (ِDependency Injection) قسمت‌های مستقلتری توسعه دهیم. تکنیک تزریق وابستگی را نمی‌توان در یک مطلب وبلاگ و حتی یک فصل کامل از یک کتاب کامل تشریح کرد ، اگر جستجو کنید کتاب‌ها و آموزش‌های ویدویی زیادی هستند که فقط روی این تکنیک بحث و آموزش دارند. برای بیان مفهوم DI مثالی از یک سیستم ساده‌ی "چاپ اسناد" ارائه می‌کنیم ، این سیستم ممکن است کار‌های متفاوتی انجام دهد :
 این سیستم ابتدا باید یک سند را تحویل بگیرد ، سپس باید آن را به فرمت قابل چاپ در آورد و در انتها باید عمل اصلی چاپ را انجام دهد. برای اینکه سیستم ما ساختار خوبی داشته باشد می‌توان هر وظیفه را به کلاسی مجزا سپرد :
 کلاس Document : این کلاس اطلاعات سندی که قرار است چاپ شود را نگه می‌دارد.
کلاس DocumentRepository : این کلاس وظیفه‌ی بازیابی سند از فایل سیستم (یا هر منبع دیگری) را دارد.
 کلاس DocumentFormatter : یک وهله از سند را جهت چاپ آماده می‌کند.
کلاس Printer : مسئولیت ارتباط با سخت افزار Printer را دارد.
کلاس DocumentPrinter : مسئولیت سازماندهی اجزا سیستم را بر عهده دارد.
 در این مطلب پیاده سازی بدنه‌ی کلاس‌های بالا اهمیتی ندارد :
public class DocumentPrinter
{
  public void PrintDocument(string documentName)
  {
    var repository = new DocumentRepository();
    var formatter = new DocumentFormatter();         
    var printer = new Printer();              
    var document = repository                        
      .GetDocumentByName(documentName);               
    var formattedDocument = formatter.Format(document);    
    printer.Print(formattedDocument); 
  }
}
همانطور که مشاهده می‌کنید در بدنه‌ی کلاس DocumentPrinter ابتدا وابستگی‌ها نمونه سازی شده اند ، سپس یک سند بر اساس نام دریافت شده و سند پس از آماده شدن به فرمت چاپ به چاپگر ارسال شده است.  کلاس DocumentPrinter به تنهایی قادر به چاپ سند نیست و برای انجام این کار نیاز به نمونه سازی همه‌ی وابستگی‌ها دارد .
 استفاده از این API اینگونه خواهد بود :
var documentPrinter = new DocumentPrinter();
documentPrinter.PrintDocument(@"c:\doc.doc");
در حال حاضر کلاس DocumentPrinter از DI استفاده نمی‌کند این کلاس Loosely coupled نیست. به طور مثال لازم است که API سیستم به گونه ای تغییر پیدا کند که سند به جای فایل سیستم از دیتابیس بازیابی شود ، باید کلاس جدیدی به نام DatabaseDocumentRepository تعریف شود و به جای DocumentRepository اصلی در بدنه‌ی DocumentPrinter استفاده شود ، در نتیجه با تغییر با تغییر دادن یک قسمت از برنامه مجبور به تغییر در قسمت دیگر شده ایم.(tightly coupled است یعنی به دیگر قسمت‌ها چفت شده است.)
  DI به ما کمک می‌کند که این چفت شدگی (coupling) را از بین ببریم.
استفاده از constructor injection:
 اولین قدم برای از بین بردن این چفت شدگی Refactor کردن کلاس DocumentPrinter هست ، پس از این Refactoring وظیفه‌ی وهله سازی مستقیم اشیاء از این کلاس گرفته می‌شود و نیازمندی‌های این کلاس از طریق سازنده به این کلاس تزریق می‌شود و فیلد‌های کلاس نگهداری می‌شود . به کد زیر توجه کنید :
public class DocumentPrinter
{
  private DocumentRepository _repository;
  private DocumentFormatter _formatter;       
  private Printer _printer;              
  public DocumentPrinter(             
    DocumentRepository repository,               
    DocumentFormatter formatter,      
    Printer printer)                  
  {                                   
    _repository = repository;         
    _formatter = formatter;           
    _printer = printer;               
  }
  public void PrintDocument(string documentName)
  {
    var document = _repository.GetDocumentByName(documentName);
    var formattedDocument = _formatter.Format(document);
    _printer.Print(formattedDocument);
  }
}
 اکنون برای استفاده از این کلاس باید نیازمندی هایش را قبل از ارسال به سازنده نمونه سازی کرد :
var repository = new DocumentRepository();
var formatter = new DocumentFormatter();
var printer = new Printer();
var documentPrinter = new DocumentPrinter(repository, formatter, printer);
documentPrinter.PrintDocument(@"c:\doc.doc");
بله هنوز طراحی خوبی نیست اما این یک مثال ساده از DI می‌باشد. هنوز مشکلاتی در این طراحی هست ، به طور مثال کلاس DocumentPrinter به یک پیاده سازی مشخص از وابستگی هایش چفت شده است. (هنوز برای استفاده از  DatabaseDocumentRepository باید DocumentPrinter را تغییر داد) پس این طراحی هنوز انعطاف پذیر نیست و نمی‌توان به سادگی برای آن آزمون واحد نوشت.
برای حل این مشکلات از Interface‌ها کمک می‌گیریم. اگر به مثال قبلی بازگردیم نگرانی هر دو کلاس DocumentRepository و DatabaseDocumentRepository دریافت سند می‌باشد ، تنها پیاده سازی تفاوت دارد ، پس می‌توان یک Interface تعریف کرد
public interface IDocumentRepository
{
  Document GetDocumentByName(string documentName);
}
 حال ما 2 کلاس داریم که هر دو یک Interface را پیاده سازی کرده اند می‌توان این کار را برای بقیه‌ی وابستگی‌های کلاس DocumentPrinter نیز انجام داد ، حالا باید DocumentPrinter را به گونه ای Refactor کنیم که وابستگی‌ها را بر اساس Interface دریافت کند :
public class DocumentPrinter
{
  private IDocumentRepository _repository;                        
  private IDocumentFormatter _formatter;                          
  private IPrinter _printer;                                      
  public DocumentPrinter(
    IDocumentRepository repository,
    IDocumentFormatter formatter,
    IPrinter printer)
  {
    _repository = repository;
    _formatter = formatter;
    _printer = printer;
  }
  public void PrintDocument(string documentName)
  {
    var document = _repository.GetDocumentByName(documentName);
    var formattedDocument = _formatter.Format(document);
    _printer.Print(formattedDocument);
  }
}
حالا به سادگی می‌توان پیاده سازی‌های متفاوتی را از وابستگی‌های DocumentPrinter انجام داد و به آن تزریق کرد. همچنین اکنون نوشتن آزمون واحد هم ممکن شده است ، می‌توان یک پیاده سازی جعلی از هر کدام از Interface‌ها انجام داد و جهت اهداف Unit testing از آن استفاده کرد. به طور مثال می‌توان یک پیاده سازی جعلی از IPrinter انجام داد و بدون نیاز به ارسال صفحه به پرینتر عملکرد سیستم را تست کرد.
با وجودی که موفق شدیم چفت شدگی میان DocumentPrinter و وابستگی هایش را از بین ببریم اما اکنون استفاده از آن پیچیده شده است ، هربار که قصد نمونه سازی شیء را داریم باید به یاد آوریم کدام پیاده سازی از Interface مورد نیاز است ؟ این پروسه را می‌توان به کمک یک DI Container اتوماسیون کرد.
DI Container یک Factory هوشمند است ، مانند بقیه‌ی کلاس‌های Factory وظیفه‌ی نمونه سازی اشیاء را بر عهده دارد. هوشمندی آن در اینجا هست که می‌داند چطور وابستگی‌ها را نمونه سازی کند . DI Container‌های زیادی برای .NET وجود دارند یکی از محبوب‌ترین آنها StructureMap می‌باشد که قبلا در سایت درباره آن صحبت شده است .
برای مثال جاری پس از افزودن StructureMap به پروژه کافی است در ابتدای شروع برنامه به آن بگوییم برای هر Interface کدام شیء را وهله سازی کند : 
ObjectFactory.Configure(cfg =>
{
  cfg.For<IDocumentRepository>().Use<FilesystemDocumentRepository>();
  cfg.For<IDocumentFormatter>().Use<DocumentFormatter>();
  cfg.For<IPrinter>().Use<Printer>();
});
بازخوردهای پروژه‌ها
Fill and FillByObject
متود زیر برای تولید عبارت مبتنی بر الگو زمانی که الگو پیچیده‌تر از این است که بشود با string.Format به راحتی پیاده سازی شود ولی چندان هم پیچیده نیست که ابزارهای حرفه ای‌تری مانند T4 یا Razor به کار بیایند، میتواند مورد استفاده قرار گیرد:
        /// <summary>
        /// Insert values from an object into a string pattern. To specify the object's property you have to use the '{propertyName}'.
        /// </summary>
        /// <remarks>
        /// This method is a replacement to String.Format, but it has two differences:
        /// <list type="simple">
        /// <item>It reverese the order you call the functionality, instead of writing String.Format(pattern, args) you write pattern.FillByObject(args). This makes the code look cleaner.</item>
        /// <item>You supply the pattern with an object and specify insertion points by property names.</item>
        /// </list>
        /// <example>
        /// <![CDATA[
        /// "First name:{firstName}, Sur name:{Surname}".FillByObject(new {firstName = "Sam", lastName="Naseri"});
        /// ]]>
        /// </example>
        /// </remarks>
        /// <seealso cref="Fill"/>
        /// <typeparam name="T">Type of bindingValue.</typeparam>
        /// <param name="bindingPattern">The pattern to fill.</param>
        /// <param name="bindingValue">The object providing values to fill in the pattern.</param>
        /// <returns>The pattern filled with values.</returns>
        public static string FillByObject<T>(this string bindingPattern, T bindingValue)
        {
            var properties = GetProperties(typeof(T)).ToList();
            var values = properties.Select(property => property.GetValue(bindingValue, new object[] { })).ToList();
            var result = bindingPattern;
            for (int index = 0; index < properties.Count; index++)
            {
                var property = properties[index];
                var propPattern = "{" + property.Name + "}";
                var old = result;
                result = result.Replace(propPattern, values[index] != null ? values[index].ToString() : "");
            }
            return result;
        }

پیاده سازی فوق یک پیاده سازی بسیار خام و بسیار کند است و جای بهبود زیادی دارد. من فقط برای سناریوهای ساده که کارایی مطرح نیست استفاده از متود فوق را توصیه میکنم.
متودهای جانبی مورد نیاز:
private static IEnumerable<PropertyInfo> GetProperties(Type t)
{
    return t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
}

همچنین متود زیر هم میتواند نوشتن string.Format را یک خرده ساده‌تر کند:
/// <summary>
/// A simple replacement for String.Format which only makes the codes look nicer.
/// </summary>
/// <param name="pattern">The source string that you want to replace insertion points on it.</param>
/// <param name="args">Values to be replaced in the pattern.</param>
/// <returns></returns>
public static string Fill(this string pattern, params object[] args)
{
    return string.Format(pattern, args);
}

اشتراک‌ها
پکیج Gridify.Elasticsearch به کتابخانه Gridify اضافه شد!

اگر با Elasticsearch کار کرده باشید میدانید که تولید Elasticsearch DSL Query کمی پیچیده است و عموما باید به صورت manual این Query هارو تولید کنید. 

با استفاده از این پکیج برای تولید این Query‌ها میتوانید از syntax پشتیبانی شده در Gridify استفاده کنید که کار ساخت Query برای Elasticsearch را ساده میکند. 

Documentation 

Github Repo


مثال:

await client.SearchAsync<User>(s => s
    .Index("users")
    .ApplyFiltering("emailAddress = John"));

query زیر را تولید میکند:

GET users/_search
{
  "query": {
    "term": {
      "emailAddress.keyword": {
        "value": "test@test.com"
      }
    }
  }
}



پکیج Gridify.Elasticsearch به کتابخانه Gridify اضافه شد!
اشتراک‌ها
چطور برنامه‌نویسی موازی را برای مادربزرگتان توضیح دهید!؟

برنامه نویسی موازی (Parallel Programming) و برنامه نویسی ناهمگام (Asynchronous Programming) مفاهیم نسبتا جدیدی در دنیای برنامه‌نویسی هستند که برای اغلب برنامه‌نویسان جدید است. همه در مورد آن شنیده‌انم ولی اغلب واضح نیست که دقیقا چیست و چرا سخت است. یک مفهوم پایه برای درک این مفاهیم پایه Thread یا نخ است. نخ‌ها مفاهیمی هستند که وظیفه انجام کارها روی CPU را دارند. در دنیای ما انسان‌ها کسانی هستند که کار انجام می‌دهند. مقاله ،فوق مفهوم «نخ» را به «انسان» شبیه دیده‌است و سعی کرده‌است مفاهیم پیچیده دنیای برنامه‌نویسی را با مفاهیم ساده دنیای ما انسان‌ها توضیح دهد. 

چطور برنامه‌نویسی موازی را برای مادربزرگتان توضیح دهید!؟
نظرات مطالب
طبقه بندی Bad Code Smell ها
چرا enum قابلیت refactoring خوبی ندارد؟  با این بسته بندی enum در یک کلاس و بالا بردن درجه‌ی پیچیدگی کد، دقیقا چه ارزش افزوده‌ای را خلق کردید؟ بحث حالت و رفتار باید در کلاس استفاده کننده‌ی از نوع enum مطرح شود یا در کلاسی که یک نوع enum را مجددا بسته بندی کرده؟
با این بسته بندی این مشکلات را ایجاد کرده‌اید:
- یک کد ساده تبدیل به یک کد پیچیده شده‌است.
- عبارات switch با حالت enum-as-class کار نمی‌کند.
- مشکلات serialization و عکس آن‌را سبب خواهید شد.
نظرات مطالب
آشنایی با Window Function ها در SQL Server بخش اول
سلام  
 سرعت و قابلیت اجرایی Over Clause به مراتب از Group by بهتر است. بطوریکه اگر یک عملیات یکسان را،بطور جداگانه،هم با Over Clause و هم با Group By انجام دهید. و در Execution Plan مشاهده نمایید، تفاوت را حس خواهید نمود. سایت زیر یک مثال ساده در این رابطه قرار داده است:
در مورد اینکه برای محیط های Transactional  مناسب است یا نه، عوامل زیادی در آن دخیل است و بسته به حجم داده ای مورد انتظار شما در خروجی دارد،بطور مثال اگر بخواهید یک گزارش 400 صفحه ای ایجاد نمایید، بطور حتم در چنین محیط هایی هیچ Scriptی مناسب، نیست، اما بطور قاطع می‌توان گفت که Window Function‌ها از کارایی بسیار خوبی برخوردار هستند، و انجام عملیات‌های پیچیده محاسباتی را برای ما آسانتر نموده اند.
مطالب
معرفی DNTBreadCrumb
سال نو مبارک! با آرزوی بهترین‌ها برای تمام همراهان سایت.

فرصتی پیدا شد تا قالب سایت، با بوت استرپ 3 انطباق داده شود و در این بین یکی از کمبودهایی که احساس می‌شد، نبود bread crumb و مشخص نبودن عمق صفحه‌ی جاری مورد مطالعه، در قسمت‌های مختلف سایت بود:


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


نصب DNTBreadCrumb

برای نصب این bread crumb مبتنی بر بوت استرپ 3، تنها کافی است دستور ذیل را در کنسول پاورشل نیوگت صادر کنید:
 PM> install-package DNTBreadCrumb


تنظیمات اولیه‌ی DNTBreadCrumb

پس از نصب، علاوه بر فایل اسمبلی DNTBreadCrumb، فایل جدید Views\Shared\_BreadCrumb.cshtml نیز به پروژه‌ی شما اضافه می‌شود. این فایل، لیست نهایی آیتم‌های تنظیم شده‌ی توسط اکشن متدها را به صورت یک bread crumb رندر می‌کند. مزیت کار کردن با فایل‌های cshtml (بجای HTML Helperها)، امکان سفارشی سازی نهایی آن‌ها توسط استفاده کننده‌است.
بنابراین برای نمایش لیست bread crumb تنها کافی است یک سطر ذیل را به فایل layout برنامه اضافه کنید:
 @{Html.RenderPartial("_BreadCrumb");}


طراحی یک bread crumb سه سطحی

اگر به فایل Views\Shared\_BreadCrumb.cshtml مراجعه کنید، مشاهده خواهید کرد که سطح اول bread crumb یا همان نمایش Home، به صورت پیش فرض قرار داده شده‌است و در اینجا اگر می‌خواهید نام دیگری را بجای Home (مثلا خانه) تنظیم کنید، به سادگی قابل انجام است.
دو سطح بعدی یک bread crumb، نام کنترلر و سپس نام اکشن متد خواهند بود:
    [BreadCrumb(Title = "News Root", UseDefaultRouteUrl = true, RemoveAllDefaultRouteValues = true,
        Order = 0, GlyphIcon = "glyphicon glyphicon-link")]
    public class NewsController : Controller
    {
        [BreadCrumb(Title = "Main index", Order = 1)]
        public ActionResult Index(string id)
        {
            if (!string.IsNullOrWhiteSpace(id))
            {
                this.SetCurrentBreadCrumbTitle(id);
            }

            return View();
        }
در این مثال، از ویژگی جدید BreadCrumb بر روی کنترلر و سپس یک اکشن متد مدنظر، استفاده شده‌است.
کار با تنظیم Title یا همان عناوینی که در لینک‌های bread crumb ظاهر می‌شوند، شروع خواهد شد. سپس اگر علاقمند بودید، می‌توانید یک گلیف آیکن را نیز در اینجا مشخص کنید تا در bread crumb نهایی، کنار عنوان مشخص شده، رندر شود.
هر ویژگی BreadCrumb دارای خاصیت Url نیز هست. اما با توجه به اینکه می‌توان از طریق مسیریابی‌های پیش فرض، این آدرس‌ها را پیدا کرد، نیازی به ذکر آن‌ها نیست. برای مثال تنظیم UseDefaultRouteUrl در BreadCrumb یک کنترلر، مقدار Url مرتبط با آن‌را به صورت خودکار از مسیریابی پیش فرض آن دریافت و محاسبه می‌کند. خاصیت RemoveAllDefaultRouteValues به این معنا است که اگر در اکشن متد index، مقدار id تنظیم شده بود، نیازی نیست تا حین تشکیل آدرس ریشه‌ی کنترلر، این مقدار نیز لحاظ شود.

و ... همین مقدار تنظیم، برای کار با این سیستم کافی است.


موارد تکمیلی

- نیاز است عنوان bread crumb به صورت پویا تنظیم شود. چگونه این‌کار را انجام دهیم؟
برای اینکار می‌توانید از متد الحاقی SetCurrentBreadCrumbTitle استفاده کنید. برای نمونه تصویر ابتدای مطلب نیز به همین ترتیب تولید شده‌است. در اینجا عنوان پویای مقاله، توسط متد SetCurrentBreadCrumbTitle بجای Title پیش فرض bread crumb تنظیم شده‌است.

- چگونه می‌توان بیش از سه سطح را تعریف کرد؟
برای تعریف بیش از سه سطح پیش فرض خانه/کنترلر/اکشن متد، می‌توانید از متد الحاقی AddBreadCrumb استفاده کنید:
        [BreadCrumb(Title = "News Archive", Order = 2)]
        public ActionResult Archive(int? id)
        {
            if (id != null)
            {
                this.SetCurrentBreadCrumbTitle(string.Format("News item {0}", id.Value));
                this.AddBreadCrumb(new BreadCrumb
                {
                    Title = "News Archive",
                    Order = 1,
                    Url = Url.Action("Archive", "News", routeValues: new { id = "" })
                });
            }

            return View();
        }
در اینجا به هر تعدادی که نیاز است می‌توانید AddBreadCrumb را انجام دهید. فقط باید دقت داشت که تقدم و تاخر این‌ها بر اساس خاصیت Order انجام می‌شود. بنابراین اگر پس از رندر شدن مشاهده کردید که لینک تولیدی، پس یا پیش از آیتم مدنظر شما است، فقط کافی است Orderها را صحیح مقدار دهی کنید.


سورس کامل مثال‌های مطرح شده‌ی در این مطلب را در پروژه‌ی MVCBreadCrumbTest می‌توانید مشاهده کنید.

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

عموما بر روی سرورهای برنامه‌های وب، نرم افزار خاصی نصب نمی‌شود. برای مثال اگر نیاز به تولید فایل اکسل بر روی سرور باشد، سرور دار بعید است که آفیس را برای شما نصب کند و همچنین مایکروسافت هم این یک مورد را اصلا توصیه و پشتیبانی نمی‌کند (ایجاد چندین وهله از برنامه آفیس (تعامل با اشیاء COM) بر روی سرور توسط یک برنامه‌ی وب چند کاربره).
اگر سایت‌ها را هم جستجو کنید پر است از مقالاتی مانند تبدیل GridView به اکسل ... که تنها هنر آن‌ها انتخاب قسمت table مانند GridView و رندر کردن آن در مرورگر با پسوندی به نام xls یا xlsx است. به عبارتی فایل نهایی تولید شده استاندارد نیست. فقط یک html table است با پسوند xls/xlsx که برنامه‌ی اکسل می‌داند به چه صورتی باید آن‌را باز کند (که گاها در این بین فارسی سازی آن مشکل ساز می‌شود). این فایل نهایی تولیدی عاری است از امکانات پیشرفته‌ و حرفه‌ای اکسل. برای مثال اضافه کردن فرمول به آن، تبدیل اطلاعات به نمودارهای اکسل به صورت خودکار، داشتن فایلی با چندین work sheet‌ مختلف، اعمال قالب‌های مختلف، صفحه بندی بهتر و غیره.
مایکروسافت از سال 2007 تولید فایل‌های آفیس را با معرفی استاندارد OpenXML که توسط مؤسسه ایزو هم پذیرفته شده، بسیار ساده‌تر کرده است. OpenXML SDK‌ در دسترس است و توسط آن می‌توان فایل‌های اکسل حرفه‌ای را بدون نیاز به نصب مجموعه‌ی آفیس تولید کرد. کار کردن با OpenXML SDK هم در نگاه اول شاید ساده به نظر برسد اما آن هم ریزه کاری‌های خاص خودش را دارد که نمونه‌ای از آن‌را در مطلب "تولید فایل Word بدون نصب MS Word بر روی سرور" می‌توانید مشاهده کنید. به عبارتی این مجموعه جهت نوشتن کتابخانه‌های ویژه‌ی شما باز است ...
در این بین یکی از حرفه‌ای‌ترین کتابخانه‌هایی که امکانات تولید فایل‌های اکسل را به کمک OpenXML SDK‌ سهولت می‌بخشد، کتابخانه‌ی سورس باز EPPlus است:


مثالی در مورد نحوه‌ی استفاده از آن:
می‌خواهیم یک DataTable را به یک فایل اکسل واقعی (نه یک html table با پسوند xlsx) تبدیل کنیم با این شرایط که یکی از قالب‌های جدید آفیس به آن اعمال شود؛ جمع کل یکی از ستون‌ها توسط اکسل محاسبه گردیده و همچنین عرض دقیق ستون‌ها نیز در برنامه تنظیم گردد. نموداری نیز به صورت خودکار این اطلاعات را نمایش دهد:




using System.Data;
using System.IO;
using OfficeOpenXml;
using OfficeOpenXml.Drawing.Chart;
using OfficeOpenXml.Style;
using OfficeOpenXml.Table;

namespace EPPlusTest
{
class Program
{
static void Main(string[] args)
{
var newFile = new FileInfo("Test.xlsx");
if (newFile.Exists)
{
newFile.Delete();
}

//ایجاد یک سری اطلاعات دلخواه
var table = createDt();

using (var package = new ExcelPackage(newFile))
{
// اضافه کردن یک ورک شیت جدید
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("مخارج");

//اضافه کردن یک جدول جدید از دیتاتیبل دریافتی
worksheet.Cells["A1"].LoadFromDataTable(table, true, TableStyles.Dark9);

//نمایش جمع ستون هزینه‌های ماه‌ها
var tbl = worksheet.Tables[0];
//زیر آخرین ردیف یک سطر اضافه می‌کند
tbl.ShowTotal = true;
//فرمول نحوه‌ی محاسبه جمع ستون انتساب داده می‌شود
tbl.Columns[1].TotalsRowFunction = RowFunctions.Sum;

//تعیین عرض ستون‌های جدول
worksheet.Column(1).Width = 14;
worksheet.Column(2).Width = 12;

//تنظیم متن هدر
worksheet.HeaderFooter.oddHeader.CenteredText = "مثالی از نحوه‌ی استفاده از ایی پی پلاس";

//می‌خواهیم سرستون‌ها در وسط ستون قرار گیرند
worksheet.Cells["A1"].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
worksheet.Cells["B1"].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;

//افزودن یک نمودار جدید به شیت جاری
var chart = worksheet.Drawings.AddChart("chart1", eChartType.Pie3D);
chart.Title.Text = "نمودار هزینه‌های سال";
chart.SetPosition(Row: 2, RowOffsetPixels: 5, Column: 3, ColumnOffsetPixels: 5);
chart.SetSize(PixelWidth: 320, PixelHeight: 360);
chart.Series.Add("B2:B13", "A2:A13");
chart.Style = eChartStyle.Style26;

//تنظیم یک سری خواص فایل نهایی
package.Workbook.Properties.Title = "مثالی از ایی پی پلاس";
package.Workbook.Properties.Author = "وحید";
package.Workbook.Properties.Subject = "ایجاد فایل اکسل بدون نرم افزار اکسل";

//تنظیم نحوه‌ی نمایش فایل زمانیکه در نرم افزار اکسل گشوده می‌شود
worksheet.View.PageLayoutView = true;
worksheet.View.RightToLeft = true;

// ذخیر سازی کلیه موارد اعمالی در فایل
package.Save();
}
}

private static DataTable createDt()
{
var table = new DataTable("مخارج");
table.Columns.Add("ماه", typeof(string));
table.Columns.Add("هزینه", typeof(decimal));

table.Rows.Add("فروردین", 100);
table.Rows.Add("اردیبهشت", 250);
table.Rows.Add("خرداد", 80);
table.Rows.Add("تیر", 300);
table.Rows.Add("مرداد", 200);
table.Rows.Add("شهریور", 150);
table.Rows.Add("مهر", 250);
table.Rows.Add("آبان", 200);
table.Rows.Add("آذر", 400);
table.Rows.Add("دی", 100);
table.Rows.Add("بهمن", 130);
table.Rows.Add("اسفند", 80);
return table;
}
}
}

اشتراک‌ها
معرفی الگوی استراتژی

چیزی که ما تا الان شنیدم در مورد شی گرایی اینه که خیلی خوبه و همیشه کار راه انداز هستش, ولی واقعیت اینه که همیشه این طوری نیست که بخواد کار راه انداز باشه و باعث کاهش حجم کد بشه اتفاقا بعضی جاها ممکنه استفاده از ویژگی‌های شی گرایی باعث افزایش حجم کد و سخت‌تر شدن نگه داری کد بشه

فرض رو میزاریم که به ما گفتن قراره یه سیستم نرم افزاری جدید طراحی کنیم( اینجا سعی میکنم اول با یه بازی شروع کنیم تا بعدا اگه فرصت شد توی دنیای واقعی این مسائل رو مطرح کنیم) وقتی میریم و در مورد این سیستم صحبت میکنیم متوجه میشیم که قراره یه بازی خیلی ساده طراحی کنیم 

معرفی الگوی استراتژی
نظرات مطالب
الگوی طراحی Builder همراه با اصول Interface Segregation
مطلب آشنایی با Fluent interfaces را مطالعه کنید تا علت وجودی بحث جاری مشخص بشه. در اینجا در مورد نحوه‌ی طراحی یک Fluent API داره بحث میشه با یک مثال ساده. طراحی API یعنی چطور ساختار یک کلاس و اطلاعات اون را در معرض دید عموم قرار بدیم؟ Fluent API همانی هست که مثلا EF Code First ازش برای تنظیم وابستگی‌ها استفاده می‌کنه. همانی هست که ASP.NET Core برای تنظیم وابستگی‌های ابتدای کارش استفاده می‌کنه و یا LINQ ایی که هر روز ازش استفاده می‌کنید هم یک Fluent API هست و ... از این دست زیاد هست.