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

یک تجربه
سالها پیش یکی از همکاران تعریف می‌کردند که یک شرکت نرم افزاری برای مشاوره معماری نرم افزار از ایشان دعوت به همکاری کرده است. پس از مراجعه به شرکت متوجه شدند که تیم اصلی برنامه نویسان درگیر تولید ORM ای برای پروژه جدید شرکت هستند که برای تولید این ابزار بیش از 4 ماه را وقت صرف کرده‌اند؛ اما در مراحل نهایی کار دچار مشکلات زیادی شده اند. به نحوی که از ایشان برای کمک به رفع مشکل ORM ( به جای تولید نرم افزار مشتری) دعوت کرده‌اند.
 
در آن زمان یادم هست که EF 5 (که تقریبا نسخه سوم  بعد از 3.5 و 4 می‌باشد - جزئیات در اینجا) توسط مایکروسافت ارائه شده بود. همچنین NHibernate هم همزمان با EFها (تاریخچه نسخه‌ها در اینجا) قابل دسترسی بوده‌است. با این حال تیم فنی به این دلیل که کوئری‌های تولیدی توسط EF کند هستند، اقدام به ساخت ORM کرده بودند. جالب اینکه با بررسی بیشتر مشخص شده‌است که حجم داده‌های پروژه در بدترین حالت در یک جدول به 5 هزار رکورد می‌رسد.

4 ماه صرف وقت و هزینه تیم 2 نفره برای طراحی و پیاده سازی و تست ORM ای که در نهایت به دلیل مشکلات Performance کنار گذاشته شد و از EF استفاده کردند. شاید در این 4 ماه می‌توانستند 30 درصد پروژه اصلی را پیاده سازی کنند.

شاید بتوان 3 دلیل عمده «فنی» شکست برخی از پروژه‌های نرم افزاری در ایران را به شرح زیر عنوان کرد:
- عدم استفاده مناسب از ابزارها و راهکار‌های موجود و انجام دوباره کاری
- استفاده غیر ضروری و عجولانه از تکنولوژی‌های جدید (بدون داشتن نیروی کار مسلط)
- پایین بودن سطح فنی و به‌روز نبودن برخی از برنامه نویسان ایرانی


متن باز (Open Source)
با پیشرفت توسعه نرم افزار و تمایل شرکت‌های بزرگ دنیا به تولید کامپوننت‌های متن باز (Open Source) ریسک استفاده از این نوع ابزار‌ها نیز کمتر شده است. بطوریکه درصورت نیاز می‌توان کامپوننت را برای پروژه‌ها سفارش سازی کرد.
شاید کمتر کسی باور می‌کرد که روزی شرکت مایکروسافت محصولات خود را Open Source کند. اما امروز، در سال 2017 میلادی، شرکت مایکروسافت اقدامات مهمی را در این زمینه انجام داده است که می‌توانید جزئیات پروژه‌های متن باز این غول کامپیوتری دنیا را در اینجا و همچنین اینجا ملاحظه کنید.

 
یک سناریو
فرض کنید یک پروژه تحت وب را شروع کرده اید. بدون در نظر گرفتن جزئیات پروژه می‌توان گفت به ابزارهای زیر نیاز خواهید داشت:

ابزار
مثال
  ORM   EF , NHibernate , Dapper , LLBLGEN 
 IOC COntainer   Unity , StructureMap , Autofac , Castle.Windsor, LightInject , Ninject 
 Report Tools   CrsytalReport , Stimusoft , DevExpress Report, Telerik Report Tools, EasyReport 
 UI Component   Telerik , JqueryUI , Bootsrap ,CompnentArt, ComponentOne 
 Error Logger   ELMAH , NLog , log4net 
 Mapper Tools   AutoMapper , ValueInjecter 
همانطور که ملاحظه می‌کنید برای همه‌ی موارد فوق ابزارهای مناسبی وجود دارند که برای پیاده سازی هر کدام، سالها وقت و هزینه صرف شده‌است. همچنین قابلیت اطمینان این ابزار‌ها به مراتب بالاتر از ابزارهای دست ساز خواهد بود. شاید برای ساده‌ترین ابزار فوق 3 ماه زمان لازم باشد تا یک نسخه  باگ دار تهیه شود!


ملاحظات استفاده از ابزارها
توجه به چند نکته در استفاده از ابزارها و کتابخانه‌های آماده ضروری می‌باشد، بدین شرح:
- ابزار مورد نیاز را با R&D (تحقیق و توسعه) انتخاب کنید. ابزارهایی که در پروژه‌های واقعی استفاده شده‌اند، بسیار مناسب می‌باشند.
- توجه داشته باشیدکه استفاده از چندین ابزار باعث ایجاد تداخل در پروژه نشود (این مورد معمولا در کامپوننت‌های UI مانند JqueryUI و Bootsrtap اتفاق می‌افتد)
- مستندات مربوط به ابزار‌ها را حتما مطالعه کنید. لطفا بدون تسلط از ابزاری استفاده نکنید.

گاهی پیش می‌آید که یک برنامه نویس بدون مطالعه مستندات مربوط به یک IOC Container از آن ابزار استفاده میکند و در Register اولیه ویژگی LifeCycle مربوط به Context  را با حالت Singleton مقداردهی میکند. بدین ترتیب پس از نیم ساعت، پروژه به دلیل آنچه که می‌توان "چاقی Context" نامید، DONE یا حداقل کند می‌شود که رفع این مشکل ساعت‌ها زمان می‌برد.

درصورت امکان از ابزارها بصورت مستقیم استفاده نکنید. یک لایه واسط مخصوص خودتان را برای تنظیمات کلی ابزار‌ها تهیه کنید که در آینده به دردتان خواهد خورد! (بیشتر در سمت سرور)

فرض کنید در پروژه WPF از کامپوننت‌های زیبای DevExpress استفاده میکنید. به ازای هر کامپوننت یک کلاس به پروژه اضافه کنید که از کلاس اصلی آن کامپوننت Devexspress ارث می‌برد و در لایه UI خود از کلاس جدید خود استفاده کنید. با این کار می‌توانید ویژگی‌های عمومی کامپوننت‌ها را یکبار برای کل پروژه اعمال کنید.


  نتیجه گیری
  اگر بخواهیم چرخ را اختراع نکنیم و از تجربیات موفق موجود استفاده کنیم، می‌توان نتیجه گرفت که استفاده از ابزارهای آماده برای توسعه نرم افزار با رعایت دستورالعمل استفاده امری مفید می‌باشد. اما باید توجه داشته باشیم که استفاده از هر ابزاری به هرقیمتی در هرپروژه‌ای، حرفه ای نیست. همه‌ی راهکارها، ابزراها و تکنولوژی‌های مورد استفاده باید در راستای هدف اصلی «تولید و تحویل به موقع نرم افزار با کیفیت به مشتری» باشد؛ هدفی که در بسیاری از موارد فراموش شده و بیشتر زمان پروژه، صرف کارهای غیر ضروری می‌شود.
مطالب
غیرمعتبر شدن کوکی‌های برنامه‌های ASP.NET Core هاست شده‌ی در IIS پس از ری‌استارت آن
ASP.NET Core از مکانیزم «Data protection» برای تولید کلیدهای رمزنگاری اطلاعات موقتی خود استفاده می‌کند. این روش در دو حالت هاست برنامه‌ها توسط IIS و یا عدم تنظیمات ذخیره سازی آن‌ها به صورت دائمی، اطلاعات خود را در حافظه نگه‌داری می‌کند و با ری‌استارت شدن سرور و یا IIS، این کلیدها از دست رفته و مجددا تولید می‌شوند. به این ترتیب کاربران شاهد این مشکلات خواهند بود:
الف) چون کوکی‌ها و یا توکن‌های آن‌ها دیگر قابل رمزگشایی نیستند (به علت باز تولید کلیدهای رمزنگاری و رمزگشایی اطلاعات)، مجبور به لاگین مجدد خواهند شد (تا کوکی‌های جدیدی برای آن‌ها تولید شوند). همچنین آنتی‌فورجری توکن‌های آن‌ها نیز مجددا باید تولید شوند.
ب) تمام اطلاعات محافظت شده‌ی توسط Data protection API قابل رمزگشایی نخواهند بود.


تنظیم Data protection API مخصوص برنامه‌های هاست شده‌ی توسط IIS

برای اینکه کلیدهای رمزنگاری اطلاعات برنامه‌های وب به صورت دائمی ذخیره شوند و با ری‌استارت سرور از دست نروند، یکی از سه روش ذیل را می‌توان بکار گرفت:

1) اسکریپت پاور شل ذیل را اجرا کنید:
نحوه‌ی اجرای آن نیز به صورت ذیل است و پس از آن، نام Application pool مخصوص برنامه ذکر می‌شود:
 .\Provision-AutoGenKeys.ps1 DefaultAppPool
در این حالت کلیدهای رمزنگاری اطلاعات به صورت دائمی به رجیستری ویندوز اضافه می‌شوند. این کلیدها به صورت خودکار توسط مکانیزم DPAPI ویندوز، رمزنگاری می‌شوند.

2) به تنظیمات پیشرفته‌ی Application pool برنامه در IIS مراجعه کرده و خاصیت Load user profile آن‌را true کنید.


در این حالت کلیدها به صورت دائمی در پوشه‌ی پروفایل کاربر مخصوص Application pool برنامه، به صورت رمزنگاری شده‌ی توسط مکانیزم DPAPI ویندوز، ذخیره خواهند شد.

3) یک SSL Certificate معتبر را تهیه کنید و یا اگر از یک self signed certificate استفاده می‌کنید باید آن‌‌را در Trusted Root store ویندوز قرار دهید. سپس از روش PersistKeysToFileSystem استفاده کنید.
public void ConfigureServices(IServiceCollection services)
{
   services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
    .ProtectKeysWithCertificate("thumbprint");
}

اگر از یک web farm استفاده می‌کنید، روش سوم ذکر شده، تنها روشی است که از آن می‌توانید استفاده کنید. یک پوشه‌ی اشتراکی قابل دسترسی بین سرورها را ایجاد کنید که دربرگیرنده‌ی X509 certificate شما باشد. سپس این پوشه و مجوز موجود در آن‌را توسط روش فوق به برنامه معرفی کنید.
نظرات مطالب
EF Code First #12
لایه‌ی Data Access در این معماری همان ORM مورد استفاده هست.
مطالب
ایجاد بک آپ برای دیتابیس با استفاده از CMD
در پست قبلی مهندس نصیری با استفاده کتابخانه ExpressMaint و Schedule  ویندوز یه سیستم بک آپ گیری را پیاده کرده اند، در این بین با توجه به وجود SMO میتوان از طریق خط فرمان ویندوز و کمک گیری از کتابخانه 7ZIP و نوشتن فرامین مربوط و ایجاد بچ فایل و افزودن به Schedule  ویندوز، نیز میتوان بصورت زیر نیز این پیاده سازی را انجام داد، که بنا بر نیاز اسکریپت بک آپگیری را نوشت:
echo off
cls
rem --------------- Variables --------------- 
set packName=DbName
set connection=-S Server -U User -P Password
set path=PathForSaveBackup
rem --------------- Variables --------------- 

echo create backup
sqlcmd %connection% -Q "BACKUP DATABASE %packName% TO DISK = '%path%\%packName%.bak'"

echo compress backup file
..\tools\7za.exe a %path%\%packName%.7z %path%\%packName%.bak

echo delete backup file
del %path%\%packName%.bak

مطالب
از متد DateTime.ToString بدون پارامتر استفاده نکنید!
در حین تهیه کتابخانه Silverlight DatePicker فارسی، گاها استفاده کنندگان گزارش می‌دادند که برنامه روی سیستم‌های مختلف کرش می‌کند یا تبدیل تاریخ درست انجام نمی‌شود. مشکل هم پس از بررسی طولانی به این ترتیب مشخص شد که استفاده از DateTime.ToString بدون ذکر پارامترهایی که در ادامه توضیح داده خواهند شد، اشتباه است.

متد ToString بر اساس تنظیمات محلی عمل می‌کند

خروجی فراخوانی ذیل
DateTime.Now.ToString()
در یک سیستم می‌تواند
01/11/2012 09:49:08 ق.ظ
و در سیستمی دیگر
11/1/2012 9:49:08 AM
باشد.
این مساله خصوصا برای ذخیره سازی و پردازش اطلاعات به صورت رشته بسیار مهم و مساله ساز است.
فرض کنید در یک شبکه با تنظیمات محلی متفاوت، کاربران اطلاعات تاریخ متفاوتی را به بانک اطلاعاتی ارسال کنند. پردازش صحیح این تاریخ‌ها تقریبا غیرممکن است. حالت اول روز 11 ماه یک را نمایش می‌دهد و حالت دوم روز 1 ماه 11. در حالیکه هر دو تاریخ در یک روز ثبت شده‌اند اما تنظیمات محلی کاربران متناظر یکسان نبوده است.
برای رفع این مشکل نیاز است ToString را مستقل از تنظیمات محلی کاربران کرد:
DateTime.Now.ToString(CultureInfo.InvariantCulture)
این مورد نکته‌ای است که اگر از FxCop برای آنالیز اسمبلی‌های برنامه خود استفاده کنید، حتما گوشزد خواهد شد. همچنین ReSharper نیز رعایت آن‌را در نگارش‌های اخیر خود گنجانده است.

مشکل دیگر مشابه در حین کار با Silverlight و WPF، استفاده و پردازش e.NewValue تغییرات خواص است. این مقدار به صورت object ارسال می‌شود و برای پردازش آن نباید e.NewValue.ToString فراخوانی شود. روش صحیح دریافت تاریخ از آن باید به صورت زیر باشد:
        public static DateTime? DateTimeTryParse(object data)
        {
            if (data == null)
                return null;

            if (data.GetType().Equals(typeof(DateTime)))
                return (DateTime)data;

            DateTime result;
            if (DateTime.TryParse((string)data, CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
                return result;

            return null;
        }

دو نکته در اینجا قابل توجه است:
- در متد DateTime.TryParse بجای data.ToString، به string تبدیل شده است. متد DateTime.TryParse نیز حالت ویژه‌ای پیدا کرده و CultureInfo.InvariantCulture در آن قید شده است.
- همچنین چون نوع e.NewValue تغییرات دریافتی از نوع object می‌باشد، بهتر است ابتدا بررسی شود که آیا DateTime است یا خیر. سپس سایر بررسی‌ها صورت گیرد.
 
مطالب
استفاده از پروایدر SQLite در Entity Framework 7
Entity Framework در نگارش 7 خود از منابع داده‌ایی جدیدی پشتیبانی میکند(+) . یعنی از Windows Phone، Windows Store و همچنین ASP.NET 5 (اپلیکیشن‌هایی که از NET Core. استفاده می‌کنند) پشتیبانی خواهد کرد. در این نسخه از دیتابیس‌های non-relational نیز پشتیبانی می‌شود. پروایدر SQLite به صورت رسمی توسط تیم EF ارائه شده است که در ادامه نحوه‌ی استفاده از آن را در یک برنامه کنسول ساده بررسی خواهیم کرد.
کلاس‌های برنامه:
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Metadata;
using System.Collections.Generic;
using System.Linq;

namespace UsingEF7WithSQLite
{
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }

        public List<Post> Posts { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
}
خب تا اینجا مدل‌های برنامه را تعریف کردیم، قدم بعدی افزودن پکیج مربوط به پروایدر SQLite به پروژه است، با دستور زیر این پکیج را نصب می‌کنیم:
PM> Install-Package EntityFramework.SQLite –Pre
اکنون کلاس کانتکست برنامه را به صورت زیر تعریف می‌کنیم:
namespace UsingEF7SQLiteProvider
{
    public class BloggingContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptions builder)
        {
            builder.UseSQLite(@"Data Source=.\BloggingDatabae.db");
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<Blog>()
                .OneToMany(b => b.Posts, p => p.Blog)
                .ForeignKey(p => p.BlogId);

            // The EF7 SQLite provider currently doesn't support generated values
            // so setting the keys to be generated from developer code
            builder.Entity<Blog>()
                .Property(b => b.BlogId)
                .GenerateValueOnAdd(false);

            builder.Entity<Post>()
                .Property(b => b.BlogId)
                .GenerateValueOnAdd(false);
        }
    }
}

کار را با بازنویسی متد OnConfiguration شروع می‌کنیم، در این قسمت باید به EF بگوئیم که می‌خواهیم از SQLite استفاده کنیم برای اینکار از یک Extension Method با نام UseSQLite و پاس دادن کانکشتن استرینگ به آن استفاده می‌کنیم.
نکته: پروایدر فعلی SQLite در حال حاضر از Generated values پشتیبانی نمی‌کند، برای این منظور باید درون متد OnModelCreating این قابلیت را غیرفعال کنیم.
اکنون می‌توانیم از طریق پاورشل نیوگت دیتابیس را ایجاد کنیم، برای اینکار باید پکیج زیر را به پروژه اضافه کنید:
Install-Package EntityFramework.Commands -Pre
سپس دستورات زیر را اجرا می‌کنیم:
Add-Migration MyFirstMigration
Apply-Migration
توسط دستور Apply-Migrate دیتابیس برای شما ایجاد خواهد شد. البته این دستور زمانی استفاده می‌شود که برنامه شما یک اپلیکیشن دسکتاپ باشد. اگر اپلیکیشن شما یک Windows Phone Application است باید در زمان اجرای برنامه این کد را بنویسید:
using (var db = new BloggingContext())
{
   db.Database.AsMigrationsEnabled().ApplyMigrations();
}
در نهایت می‌توانید با دستور زیر از کانتکست برنامه استفاده کرده و خروجی را مشاهده کنید:
using (var db = new Models.BloggingContext())
{
     db.Blogs.Add(new Models.Blog { Url = "https://www.dntips.ir" });
     db.SaveChanges();

     foreach (var item in db.Blogs)
     {
         Console.WriteLine(item.Url);
     }
}
Console.ReadLine();
نظرات مطالب
استفاده ازExpressionها جهت ایجاد Strongly typed view در ASP.NET MVC
با تشکر از شما. حالت پیشرفته‌تر این مساله، کار با مدل‌های تو در تو هست. برای مثال:
    public class CompanyModel
    {
        public int Id { get; set; }
        public string CompanyName { get; set; }
        public string CompanyAbbr { get; set; }

        public Product Product { set; get; }
    }

    public class Product
    {
        public int Id { set; get; }
    }
در اینجا اگر بخواهیم Product.Id را بررسی کنیم:
var data = PropertyExtensions.PropertyName<CompanyModel>(x => x.Product.Id);
فقط Id آن دریافت می‌شود.
راه حلی که از کدهای EF برای این مساله استخراج شده به صورت زیر است (نمونه‌اش متد Include تو در تو بر روی چند خاصیت):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace PropertyExtensionsApp
{
    public class PropertyHelper : ExpressionVisitor
    {
        private Stack<string> _stack;
        public string GetNestedPropertyPath(Expression expression)
        {
            _stack = new Stack<string>();
            Visit(expression);
            return _stack.Aggregate((s1, s2) => s1 + "." + s2);
        }

        protected override Expression VisitMember(MemberExpression expression)
        {
            if (_stack != null)
                _stack.Push(expression.Member.Name);
            return base.VisitMember(expression);
        }

        public string GetNestedPropertyName<TEntity>(Expression<Func<TEntity, object>> expression)
        {
            return GetNestedPropertyPath(expression);
        }
    }
}
در این حالت خواهیم داشت:
var name = new PropertyHelper().GetNestedPropertyName<CompanyModel>(x => x.Product.Id);
که خروجی Product.Id را بر می‌گرداند.
بازخوردهای دوره
انتقال خودکار Data Annotations از مدل‌ها به ViewModelهای ASP.NET MVC به کمک AutoMapper
روشی دیگر جهت انتقال Data Annotations از Model به ViewModel

در ASP MVC  با MetadataTypeAttribute .
 روش کار به این صورت است که کلاس ViewModel را به Attribute (MetadataType ) مزین می‌کنیم که این Attribute  در سازنده خود تایپ Model را دریافت می‌کند و همچنین  در فضای نامی زیر قرار دارد 
System.ComponentModel.DataAnnotations 
    public class Student
    {
        public int Id { get; set; }

        [Required(ErrorMessage = "نام ضروری است")]
        [Display(Name = "نام")]
        public string Name { get; set; }
    }

ViewModel
    [MetadataType(typeof(Student))]
    public class StudentViewModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

در ASP MVC Core  هم روش کار به همین صورت است بجای MetadataType از ModelMetadataType  استفاده کنید .
نظرات مطالب
وی‍‍ژگی های پیشرفته ی AutoMapper - قسمت دوم
با سلام. لطفا اگر امکانش هست در مورد مپ کردن چند نوع داده‌ای مبدا به یک نوع داده‌ای مقصد هم توضیح دهید. ممنونم.
مثال:
public class SourceType1 //poco class
{
   public string Name
   public string JobTitle
   public string PetsName
}

public class SourceType2
{
  public string BankName 
  public decimal CreditCardBalance
  public DateTime SignupDate
}

public class Destination
{
    public string Job
    public string Balance //thousand seprator
}
مطالب
آشنایی با Refactoring - قسمت 7


یکی دیگر از روش‌های Refactoring ، معرفی کردن یک کلاس بجای پارامترها است. عموما تعریف متدهایی با بیش از 5 پارامتر مزموم است:

using System;
using System.Collections.Generic;

namespace Refactoring.Day7.IntroduceParameterObject.Before
{
public class Registration
{
public void Create(string name, DateTime date, DateTime validUntil,
IEnumerable<string> courses, decimal credits)
{
// do work
}
}
}

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

using System;
using System.Collections.Generic;

namespace Refactoring.Day7.IntroduceParameterObject.After
{
public class RegistrationContext
{
public string Name {set;get;}
public DateTime Date {set;get;}
public DateTime ValidUntil {set;get;}
public IEnumerable<string> Courses {set;get;}
public decimal Credits { set; get; }
}
}

namespace Refactoring.Day7.IntroduceParameterObject.After
{
public class Registration
{
public void Create(RegistrationContext registrationContext)
{
// do work
}
}
}

یکی از مزایای این روش، منعطف شدن معرفی متدها است؛ به این صورت که اگر نیاز به افزودن پارامتر دیگری باشد، تنها کافی است یک خاصیت جدید به کلاس RegistrationContext اضافه شود و امضای متد Create،‌ ثابت باقی خواهد ماند.

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

public int GetIndex(int pageSize, int pageNumber, ...) { ...

همانطور که ملاحظه می‌کنید تعدادی از پارامترها در اینجا با کلمه page شروع شده‌اند. بهتر است این‌ پارامترهای مرتبط را به یک کلاس مجزا به نام Page انتقال داد.