نظرات مطالب
ASP.NET MVC #12
با تشکر از مطلب خوبتون 
امکان استفاده از RenderAction در Shared/_Layout  وجود نداره ؟ وقتی اینکارو انجام میدم با ارور StackOverflowException  برخورد میکنم تکه کد اجرای   RenderAction  انقدر تکرار میشه تا به این ارور برخورد مبکنه!

میخوام لیست گروهبندی مطالبمو تو _Layout.cshtml  ( نمایش در تمام صفحات مثل MasterPage در WebForm) نشون بدم چه راهی رو پیشنهاد میدید ؟ خیلی ممنون

نظرات مطالب
مروری سریع بر اصول مقدماتی MVVM
سلام
از مطالب خوبتون تشکر می کنم. دیگه تقریبا مشتری ثابت و ساعتی سایتتون شدم.
wpf & mvvm هنوز تبدیل به یه ابزار کامل برای تولید یه برنامه کاربردی حرفه ای نشده. من پوستم کنده شد تا تونستم یه برنامه کامل باهاش بنویسم. واسه راتباطش با ssrs چه مشقاتی که نکشیدم.
ناچار شدم یه فرم ویندوزی به برنامه اضافه کنم و گزارش را توی اون نمایش بدم.
 راهم درسته فعلا؟ در نسخه 2011 فکری به حال این مشکل نشده؟
راستی چطور میشه توسط یه کلید و بدون نوشتم کد یه ویو جدید را نمایش داد. راهی برای بایند کردن کلید به ویو وجود داره. بدون نوشتن Command  ?
اشتراک‌ها
استفاده از React و TypeScript
An introduction to the development of React applications with Atom and TypeScript.
We are about to develop the famous TODO App from the TodoMVC project using React and TypeScript.
استفاده از React و TypeScript
نظرات مطالب
ASP.NET MVC #18
یک HTTP Module درست کنید تا کوکی‌های Response را لاگ کند:
public void Init(HttpApplication application)
{
    application.EndRequest += onEndRequest;
}

public void onEndRequest(Object sender, EventArgs e)
{
   HttpCookieCollection cookies = (((HttpApplication)sender).Response).Cookies;
   //todo: check Cookie.Value.Length
طول (حجم) و نام این کوکی‌ها را بررسی کنید.
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 18 - کار با ASP.NET Web API
در مثال ProductModel شیوه دریافت متغیر productId ذکر نشده، که به عنوان مثال به روش زیر می‌توان مقدار متغیر  productId را دریافت کرد:
public class ProductModel
{
    public ProductModel(IContextAccessor<ActionContext> action, IProductService prodService)
    {
        //TODO: Do some error checking...
        var productId = action.Value.RouteData.Values["product"];
        Value = prodService.Get(productId);
    }

    public IProduct Value { get; private set; }
}
نظرات مطالب
ارسال فایل و تصویر به همراه داده‌های دیگر از طریق jQuery Ajax
اگر نام کنترل‌های ارسال فایل سمت کلاینت یکسان نباشند، هر کدام را باید به عنوان یک پارامتر جدید اکشن متد تعریف کرد و یا می‌توان از طریق آرایه‌ی Request.Files، بدون ذکر پارامتری در اکشن متد، به تمام آن‌ها دسترسی پیدا کرد:
foreach (string file in Request.Files)
{
   var hpf = Request.Files[file] as HttpPostedFileBase;
  // todo: save it
}
پاسخ به بازخورد‌های پروژه‌ها
عدم authorization بر اساس Permissions
خیلی ممنون بابت راهنمایی. در پروژه خودم متد GetRolesAsync را به کلاس ApplicationUserManager.cs اضافه نکرده بودم. 
        public async override Task<IList<string>> GetRolesAsync(Guid userId)
        {
            var userPermissions = await _roleManager.FindUserPermissions(userId);
            ////todo: any permission form other sections
            return userPermissions;
        }
بازخوردهای پروژه‌ها
AuditLogs
سلام،
وقتی تابع OnActionExecuted در کلاس Decision.Web.Filters.AuditAttribute رو از حالت کامنت خارج می‌کنم 
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (LogType == AuditLogType.Login)
            {
                AuditLogService.Create(Description, LogType);
            }
            if (LogType != AuditLogType.Serialize) return;
            if (filterContext.Controller.ViewData.ModelState.IsValid)
            {
                AuditLogService.Create(Description, LogType);
            }

            base.OnActionExecuted(filterContext);
        }

موقع login با خطای زیر مواجه می‌شوم؛
البته مشخص است که وقتی هنوز login نشده ایم پس current user هم نداریم! 
 
- این بخش به طور کامل پیاده سازی نشده است؛
این فیلتر (Decision.Web.Filters.AuditAttribute ) برای اکشن‌های زیادی استفاده شده است البته فقط  Description  یا عنوان لاگ در database ذخیره می‌شود و oldvalue و NewValue مقداری ندارند.
این بخش را چطور می‌توان کامل کرد؟  

مطالب
Soft Delete در Entity Framework 6
برای حذف نمودن یک رکورد از دیتابیس 2 راه وجود دارد : 1- حذف به صورت فیزیکی 2- حذف به صورت منطقی ( مورد بحث این مطلب )
در حذف رکورد به صورت منطقی، طراحان دیتابیس، فیلدی را با نام‌های متفاوتی همچون Flag , IsDeleted , IsActive , و غیره، در جداول ایجاد می‌نمایند. خوب، این روش مزایا و معایب خاص خودش را دارد. مثلا شما در هر پرس و جویی که ایجاد می‌نمایید، بایستی این مورد را چک نموده و رکوردهایی را فراخوانی نمایید که فیلد IsDeleted آن برابر با false باشد. و همچنین در زمان حذف رکورد، برنامه نویس بایستی از متد Update به جای حذف فیزیکی استفاده نماید که تمام این موارد حاکی از مشکلات خاص این روش است. 
در این مقاله سعی داریم که مشکلات ذکر شده در بالا را با ایجاد SoftDelete در EF 6 برطرف نماییم .*یکی از پیش نیاز‌های این پست مطالعه ( سری آموزشی EF CodeFirst ) در سایت جاری می‌باشد.
برای شروع، ما نیاز به داشتن یک Attribute برای مشخص ساختن موجودیت هایی داریم که بایستی بر روی آنها SoftDelete فعال گردد. پس برای اینکار کلاسی را به شکل زیر طراحی مینماییم:
using System.Data.Entity.Core.Metadata.Edm;
public class SoftDeleteAttribute : Attribute
    {
        public string ColumnName { get; set; }
        public SoftDeleteAttribute(string column)
        {
            ColumnName = column;
        }
        public static string GetSoftDeleteColumnName(EdmType type)
        {
            MetadataProperty column = type.MetadataProperties.Where(x => x.Name.EndsWith("customannotation:SoftDeleteColumnName")).SingleOrDefault();
            return column == null ? null : (string)column.Value;
        }
    }
توضیحات کد بالا: در متد سازنده، نام فیلدی را که قرار است بر روی آن SoftDelete به صورت اتوماتیک ایجاد شود، دریافت می‌نماییم و متد GetSoftDeleteColumnName در واقع با استفاده از متادیتاهایی که بر روی فیلد‌ها وجود دارد، فیلدی که انتهای نام آن متادیتای "customannotation:SoftDeleteColumnName" را دارد، انتخاب نموده و برگشت می‌دهد.
سؤال: متادیتای  "customannotation:SoftDeleteColumnName"  از کجا آمد؟ برای پاسخ به این سوال کافیست ادامه‌ی مطلب را کامل مطالعه نمایید.
حال این Attribute برای استفاده در موجودیت‌های ما آمده است. برای استفاده کافیست به روش زیر عمل نمایید .
    [SoftDelete("IsDeleted")]
    public class TblUser 
    {        
        [Key]
        public int TblUserID { get; set; }

        [MaxLength(30)]
        public string Name { get; set; }

        public bool IsDeleted { get; set; }
    }
برای معرفی این قابلیت جدید به EF 6 کافیست در DbContext برنامه در متد OnModelCreating به نحو زیر عمل نماییم.
 protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            var Conv = new AttributeToTableAnnotationConvention<SoftDeleteAttribute, string>(
                "SoftDeleteColumnName",
                (type, attribute) => attribute.Single().ColumnName);
            modelBuilder.Conventions.Add(Conv);

        }
در واقع ما در اینجا به Ef می‌گوییم که یک Annotation جدید، با نام SoftDeleteColumnName به Entity که توسط این Attribute مزین شده است، اضافه نماید و همچنین مقدار این Annotation را نام فیلدی که در متد سازنده SoftDeleteAttribute معرفی گردیده است قرار دهد.
برای اطمینان حاصل کردن از اینکه آیا Annotation جدید به مدل برنامه اضافه شده است یا نه کافیست بر روی فایل cs کانتکست DbContext، کلیک راست نموده و در منوی نمایش داده شده گزینه‌ی EntityFramework و سپس گزینه View Entity Data Model را انتخاب نمایید . مانند تصویر زیر:

در پنجره باز شده به قسمت سوم یعنی <StorageModels> مراجعه نمایید و بایستی گزینه زیر را مشاهده نمایید .

 <EntityType Name="TblUser" customannotation:SoftDeleteColumnName="IsDeleted">

تا اینجای کار ما توانستیم یک Annotation جدید را به Ef اضافه نماییم .

در مرحله بعد بایستی به Ef دستور دهیم که در تولید Query بر روی این Entity، این مورد را نیز لحاظ کند.

برای این کار کلاسی را ایجاد می‌نماییم که از اینترفیس IDbCommandTreeInterceptor ارث بری می‌نماید. مانند کد زیر :

public class SoftDeleteInterceptor : IDbCommandTreeInterceptor
    {
        public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
        {
            if (interceptionContext.OriginalResult.DataSpace == System.Data.Entity.Core.Metadata.Edm.DataSpace.SSpace)
            {
                var QueryCommand = interceptionContext.Result as DbQueryCommandTree;
                if (QueryCommand != null)
                {
                    var newQuery = QueryCommand.Query.Accept(new SoftDeleteQueryVisitor());
                    interceptionContext.Result = new DbQueryCommandTree(QueryCommand.MetadataWorkspace, QueryCommand.DataSpace, newQuery);
                }
            }
       }
}

در ابتدا تشخیص داده می‌شود که نوع خروجی Query آیا از نوع Storage Model است . ( برای توضیحات بیشتر ) سپس پرس و جوی تولید شده را با استفاده از الگوی visitor تغییر داده و Query جدید را تولید نموده و در انتها Query جدیدی را به جای Query قبلی جایگزین می‌نماییم.

در اینجا ما نیاز به داشتن کلاس  SoftDeleteQueryVisitor  برای تغییر دادن Query و اضافه نمودن IsDeleted <>1 به Query می‌باشیم.

یک کلاس دیگری با نام  SoftDeleteQueryVisitor  به شکل زیر  به برنامه اضافه می‌نماییم.

  public class SoftDeleteQueryVisitor : DefaultExpressionVisitor
    {
        public override DbExpression Visit(DbScanExpression expression)
        {
            var column = SoftDeleteAttribute.GetSoftDeleteColumnName(expression.Target.ElementType);
            if (column!=null)
            {
                var Binding = DbExpressionBuilder.Bind(expression);
                return DbExpressionBuilder.Filter(Binding, DbExpressionBuilder.NotEqual(DbExpressionBuilder.Property(DbExpressionBuilder.Variable(Binding.VariableType, Binding.VariableName), column), DbExpression.FromBoolean(true)));
            }
            else
            {
                return base.Visit(expression);
            }
        }
    }
در متد Visit تشخیص داده می‌شود که آیا Query ساخته شده دارای customannotation:SoftDeleteColumnName است؟ چنانچه این Annotation را دارا باشد، نام فیلدی را که بالای Entity ذکر شده است، بازگشت می‌دهد و در خط بعدی، نام این فیلد را با مقدار مخالف True به Query تولید شده اضافه می‌نماید.

در نهایت برای اینکه EF تشخیص دهد که یک‌چنین Interceptor ایی وجود دارد، بایستی در کلاس DbContextConfig، کلاس SoftDeleteInterceptor را اضافه نماییم؛ همانند کد زیر:

 public class DbContextConfig : DbConfiguration
    {
        public DbContextConfig()
        {
             AddInterceptor(new SoftDeleteInterceptor());
        }
    }

تا اینجا در تمام Query‌های تولید شده بر روی Entity که با خاصیت SoftDelete مزین شده است، مقدار IsDeleted <> 1 را به صورت اتوماتیک اعمال می‌نماید. حتی به صورت هوشمند چنانچه این موجودیت در یک Join استفاده شده باشد این شرط را قبل از Join به Query تولید شده اضافه می‌نماید.

در مقاله بعدی در مورد تغییر کد Remove به کد Update توضیح داده خواهد شد.


برای مطالعه بیشتر

Entity Framework: Building Applications with Entity Framework 6