مطالب
مشکل اعتبار سنجی jQuery validator در Bootstrap tabs
با استفاده از چهارچوب بوت استرپ می‌توان رابط‌های کاربر استانداردی ساخت که قبلا دوستان در این سایت مطالبی را در این باب نوشته اند.
در مطلب  صفحات مودال در بوت استرپ 3  در مورد  ترکیب قالب بوت استرپ با سیستم اعتبارسنجی MVC  و jQuery validation  و نمایش فرم‌های مودال بوت استرپ صحبت شده و بسیار کامل هست.

مشکل: 
هنگام کار با بوت استرپ اگر از tab‌های آن در فرم برنامه استفاده کنید، هنگام validate کردن فرم متوجه می‌شوید که فقط کنترل‌های تب جاری اعتبارسنجی میشوند و بدون توجه به سایر کنترل هایی که در تب‌های دیگر هستند و احیانا در وضعیت عدم اعتبار هستند، فرم اعتبارسنجی و کامل اعلام شده و اطلاعات ارسال می‌شود. 

دلیل:
دلیل این اتفاق این هست که jQuery validation به طور پیش فرض کنترل‌هایی که در صفحه مخفی هستند را اعتبارسنجی نمی‌کند و نادیده می‌گیرد.

راه حل:

با تغییر رفتار پیش فرض سیستم اعتبارسنجی می‌شود این مساله را حل کرد. با اضافه کردن  ignore: ""  اعلام می‌شود که کنترل‌های مخفی هم اعتبارسنجی شوند.
در نهایت کد کامل ترکیب قالب بوت استرپ با سیستم اعتبارسنجی جی کوئری به صورت زیر می‌شود. (فقط همان  ignore: "" اضافه شده است).
function enableBootstrapStyleValidation() {
    $.validator.setDefaults({
        ignore: "",
        highlight: function (element, errorClass, validClass) {
            if (element.type === 'radio') {
                this.findByName(element.name).addClass(errorClass).removeClass(validClass);
            } else {
                $(element).addClass(errorClass).removeClass(validClass);
                $(element).closest('.form-group').removeClass('has-success').addClass('has-error');
            }
            $(element).trigger('highlited');
        },
        unhighlight: function (element, errorClass, validClass) {
            if (element.type === 'radio') {
                this.findByName(element.name).removeClass(errorClass).addClass(validClass);
            } else {
                $(element).removeClass(errorClass).addClass(validClass);
                $(element).closest('.form-group').removeClass('has-error').addClass('has-success');
            }
            $(element).trigger('unhighlited');
        }
    });
}
البته می‌توان این تغییر رفتار پیش فرض را فقط به فرم خاصی هم اعمال کرد. به این صورت
$("#form").validate({
...
rules: {...},
messages: {...},
ignore: "",
...
});
نظرات مطالب
Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت
اضافه شدن JavaScript initializers   به Blazor 6x

در اینجا می‌توان فایل ویژه‌ای به نام NAME.lib.module.js را به پوشه‌ی wwwroot پروژه اضافه کرد که name آن، همان نام اسمبلی، کتابخانه و در اصل package identifier پروژه‌است؛ با این محتوا:
export function beforeStart(options, extensions) {
    console.log("beforeStart");
}

export function afterStarted(blazor) {
    console.log("afterStarted");
}
قالب این محتوا باید به همین نحو باشد و معرف اجرای کدهایی پیش از و پس از load برنامه است. به این ترتیب می‌توان به این مزایا دست یافت:
- سفارشی سازی نحوه‌ی بارگذاری یک برنامه‌ی Blazor
- اجرای کدهای سفارشی، پیش و پس از بارگذاری برنامه
- امکان تنظیم ویژگی‌های Blazor

یک مثال: بارگذاری یک اسکریپت پس از کامل شدن بارگذاری Blazor
<body>
    ...

    <script src="_framework/blazor.{webassembly|server}.js" 
        autostart="false"></script>
    <script>
      Blazor.start().then(function () {
        var customScript = document.createElement('script');
        customScript.setAttribute('src', 'scripts.js');
        document.head.appendChild(customScript);
      });
    </script>
</body>
مطالب
راه‌اندازی Http Interceptor در Angular
ماژول Http در Angular، برای برقراری ارتباط بین کلاینت و سمت سرور، مورد استفاده قرار می‌گیرد. معمولا هنگام ساخت درخواست‌های Http، یکسری کدهای تکراری برای تنظیم هدر (برای اعتبارسنجی و همچنین تنظیمات دیگر) نوشته می‌شوند که در هر درخواست یکسان هستند. همچنین بعد از آمدن جواب (Response) از سمت سرور نیز یکسری کدهای تکراری جهت برسی کد response و یا تغییر فرمت اطلاعات رسیده، به ساختار مورد توافق نوشته خواهند شد.

برای مثال در صورتیکه در برنامه خود از اعتبار سنجی مبتنی بر توکن (Token Base Authentication) استفاده می‌کنید، قبل از ارسال هر درخواست (request)، کدهایی مشابه کد زیر باید نوشته شوند:
let headers = new Headers();
let token = localStorage.getItem('token');
headers.append('Authorization', 'bearer ' + token);
this.http.get('/api/controller/action', { headers: headers })

همچنین فرض کنید بعد از رسیدن جواب هر درخواست، می‌خواهید response code را چک کنید و خطاهای احتمالی را مدیریت کنید. مثلا درصورت دریافت کد 401، کاربر را به صفحه «ورود» و با دریافت کد 404 آنرا را به صفحه «یافت نشد» هدایت کنید و یا با دریافت کد 403 یا 500 پیغام مناسبی را نمایش دهید. بدیهی است در این صورت بعد از هر آمدن پاسخ از سمت سرور (response)، این کدها بایستی تکرار شوند.

جهت پرهیز از این کدهای تکراری، می‌توان برای ماژول Http، یک interceptor واحد درنظر گرفت که تمامی کدهای تکراری را تنها یکبار داخل آن پیاده سازی کرد. مزیت این روش، مدیریت راحت کد، کاهش پیچیدگی‌ها و همچنین حذف کدهای تکراری و یکسان سازی آنها است.
هرچند در Angular دیگر به مانند Angular 1.x مفهوم intercept بر روی Http را به صورت توکار نداریم، ولی به دلایل زیر ما نیاز به پیاده سازی interceptor برای ماژول Http را داریم:
- تنظیم هدرهای سفارشی و اصلاح آدرس، قبل از ارسال درخواست به سمت سرور
- تنظیم token در هدر درخواست، جهت اعتبار سنجی
- مدیریت سراسری خطاهای Http
- انجام هرگونه عملیات crosscutting

حالا که Angular مفهموم intercept را برای ماژول Http خود به صورت توکار درنظر نگرفته است، راه‌حل چیست؟ بهترین راه‌حل برای پیاده سازی موارد مطرح شده در بالا، ارث بری و یا گسترش (extend) مستقیم ماژول Http است:
import { Injectable } from "@angular/core";
import { ConnectionBackend, RequestOptions, Request, RequestOptionsArgs, Response, Http, Headers } from "@angular/http";
import { Observable } from "rxjs/Rx";
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

@Injectable()
export class InterceptedHttp extends Http {
    constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
        super(backend, defaultOptions);
    }

    request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
        // اصلاح url
        if (typeof (url) === 'string')
            url = this.updateUrl((url as string));
        else
            url.url = this.updateUrl((url as Request).url);

        return super.request(url, this.getRequestOptionArgs(options)).catch((err: Response) => {
            // Exception handling
            switch (err.status) {
                case 400:
                    console.log('interceptor: 400');
                    console.log(err);
                    break;
                case 404:
                    console.log('interceptor: 404');
                    console.log(err);
                    break;
                case 500:
                    console.log('interceptor: 500');
                    console.log(err);
                    break;
                case 401:
                    console.log('interceptor: 401');
                    console.log(err);
                    break;
                case 403:
                    console.log('interceptor: 403');
                    console.log(err);
                    break;
                default:
                    console.log('interceptor: ' + err.status);
                    console.log(err);
            }
            return Observable.throw(err);

        });
    }

    private updateUrl(req: string) {
        return `http://localhost:61366/api/${req}`
    }

    private getRequestOptionArgs(options?: RequestOptionsArgs): RequestOptionsArgs {
        if (options == null) {
            options = new RequestOptions();
        }
        if (options.headers == null) {
            options.headers = new Headers();
        }
        // هدر درخواست تنظیم
        let token = localStorage.getItem('token');
        options.headers.append('Authorization', 'bearer ' + token);


        return options;
    }
}
تمامی افعال Http، از جمله get ،post ،put ،delete ،patch ،head و options در نهایت از متد request موجود در ماژول http برای ارسال درخواست خود استفاده می‌کنند. به همین جهت تمامی عملیات crosscutting را در این متد پیاده سازی کرده‌ایم. به علاوه قبل از ارسال درخواست، توسط متد updateUrl آدرس url خود را اصلاح کرده‌ایم. همچنین توسط متد getRequestOptionArgs هدر درخواست را جهت اعتبار سنجی مقداردهی کرده‌ایم. در اینجا بعد از ارسال درخواست و آمدن response از سمت سرور، توسط catch خطاهای احتمالی را برسی کرده‌ایم.

نکته: به عنوان مثال، در صورتیکه قصد دارید، برای درخواست‌هایی از جنس get، هدر متفاوتی نسبت به دیگر درخواست‌ها داشته باشید، آنگاه پیاده سازی عملیات اصلاح هدر در متد request جواب کار را نخواهد داد. برای حل این موضوع می‌توانید به جای اصلاح header در متد request، تمامی متدهای get ،post، put ،delete ،patch ،head و options را باز نویسی کرده و در هرکدام از این متدها اینکار را انجام دهید.

حالا با تغییر قسمت providers در ماژول اصلی برنامه به شکل زیر، Angular را مجبور می‌کنیم بجای استفاده از ماژول Http توکار خود، از ماژول جدید InterceptedHttp استفاده کند:
//…
providers: [{
        provide: Http,
        useFactory: (backend: XHRBackend, options: RequestOptions) => {
            return new InterceptedHttp(backend, options);
        },
        deps: [XHRBackend, RequestOptions],
    }],
//…

همه چیز آماده است. اکنون کافی است ماژول Http را در سرویس یا کامپوننت‌های خود تزریق کرده و درخواست‌های Http را بدون هیچگونه نوشتن کد اضافی برای تنظیم هدر و غیره (با فرض اینکه تمامی آنها در متد request از ماژول http نوشته شده‌اند)، به مانند قبل صادر کنید. برای نمونه کد زیر را ببینید.
import { Http, URLSearchParams } from '@angular/http';

//…
constructor(private _http: Http) { }

ngOnInit() {
    let urlSearchParams: URLSearchParams = new URLSearchParams();
    urlSearchParams.append('page', page.toString());
    urlSearchParams.append('count', count.toString());
    let params = urlSearchParams.toString();
    this._http.get(`/cars`, { params: params })
        .subscribe(result => {
            console.log('service: Succ');
            this.cars = result.json();
        }, err => {
            console.log('service: error');
        });
}
//…
با اینکه Angular از interceptor پشتیبانی نمی‌کند، ولی کتابخانه‌هایی برای ایجاد قابلیت مشابه interceptor به وجود آمده‌اند که برخی از آنها عبارتند از:  angular2-cool-http ، ng2-http-interceptor ، ng2-interceptors . به جای extend مستقیم ماژول Http توسط خودتان، اینکار را می‌توانید به این کتابخانه‌ها بسپارید.
مطالب
مشکل فایل‌های ANSI-Windows-1256 با VS.Net در ویندوز 7

در ویندوز XP زمانیکه زبان سیستم و همچنین کشور جاری به ایران تنظیم شود، VS.Net فایل‌های ANSI را از نوع ANSI-Windows-1256 (یا همان ANSI-Arabic) در نظر می‌گیرد و مشکلی هم برای ذخیره داده‌های یونیکد در این نوع فایل‌های ANSI ویژه نخواهد بود (الزامی وجود ندارد که این فایل‌ها حتما به فرمت UTF8 ذخیره شوند). اما در ویندوز 7 با همان تنظیمات، VS.Net این فایل‌ها را با encoding از نوع windows-1252 تشخیص می‌دهد و پس از کامپایل برنامه‌ای که قبلا مشکل نداشت، این‌بار همه چیز به همه ریخته خواهد بود. شاید اینطور به نظر برسد که این فایل‌ها خراب شده‌اند، ولی خیر. مشکلی وجود ندارد؛ فقط فرمت encoding خواندن آن‌ها باید windows-1256 باشد (و نه 1252) و گرنه تخریب شده به نظر می‌رسند.

تعداد فایل‌ها هم زیاد است و نیاز به یک روش سریع برای رفع این مشکل وجود داشت.
بنابراین سه عملیات باید صورت گیرد:
لیست کردن تمام فایل‌های مورد نظر (فایل‌های cs و aspx و امثال آن فقط)
پیدا کردن encoding جاری فایل‌ها : کدامیک از آن‌ها با فرمت UTF-8 ذخیره نشده‌اند؟
تبدیل به یونیکد: خواندن این فایل‌های غیر یونیکد یافت شده با فرمت windows-1256 و سپس ذخیره مجدد با فرمت UTF-8

که خلاصه روش انجام کار به صورت زیر است:

الف) آیا فایل جاری مورد نظر با فرمت UTF-8 with signature ذخیره شده است؟
این signature در مورد فایل‌های UTF-8 به سه بایت اول فایل بر می‌گردد که اصطلاحا byte-order mark یا BOM گفته می‌شود و باید مساوی EFBBBF باشد. چون فایل‌های ANSI این امضا را ندارند، در یک سیستم ممکن است 1256 خوانده شوند و در یک سیستم دیگر 1252 یا نوع‌های ANSI دیگر بسته به تنظیمات جاری سیستم و مشکل اصلی از VS.Net نیست.

/// <summary>
/// آیا فایل مورد نظر با فرمت یونیکد دارای امضا ذخیره شده است؟
/// </summary>
/// <param name="filePath">فایل ورودی</param>
/// <returns>بله یا خیر</returns>
public static bool IsUTF8(string filePath)
{
using (FileStream file = new FileStream(filePath,
FileMode.Open, FileAccess.Read, FileShare.Read))
{
if (file.CanSeek)
{
byte[] bom = new byte[4]; // Get the byte-order mark, if there is one
file.Read(bom, 0, 4);
if ((bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)) // utf-8
{
return true;
}
else
{
return false;
}
}
else
{
//احتمالا فایل بایناری است
return false;
}
}
}

ب) خواندن یک فایل ANSI عربی با فرمت windows-1256 بدون تخریب اطلاعات آن و سپس ذخیره سازی با فرمت UTF-8

/// <summary>
/// تبدیل یک فایل انسی عربی به یونیکد دارای امضاء
/// </summary>
/// <param name="path">مسیر ورودی</param>
public static void FixWindows1256(string path)
{
try
{
//باز کردن فایل با فرمت انسی عربی و تبدیل به یونیکد
string data = File.ReadAllText(path, Encoding.GetEncoding("windows-1256"));
//نوشتن حاصل یونیکد در جای قبلی با فرمت مربوطه
File.WriteAllText(path, data, Encoding.UTF8);
}
catch (Exception ex)
{
//دسترسی وجود ندارد یا امثال آن
Console.WriteLine(ex.ToString());
}
}


پ.ن.
جالب اینجا است که این نوع فایل‌های ANSI عربی در وب زیاد پیدا می‌شوند. برای مثال اینجا کلیک کنید. تمام این نوع فایل‌ها را با متد فوق می‌توان بدون تخریب اطلاعات به فرمت UTF-8 دارای امضاء اصلاح کرد.

مطالب
Functional Programming - قسمت پنجم - وسواس استفاده از نوع های اولیه
در ادامه سری مقالات مرتبط با برنامه نویسی تابعی ، قصد دارم به استفاده کردن یا نکردن از نوع‌های داده اولیه (Primitive Types) را بررسی کنیم. پیشنهاد میکنم در صورتی که قسمت‌های قبلی را مطالعه نکرده اید ابتدا قسمت‌های قبل را بخوانید.

در طراحی مدل دامین، بیشتر مواقع از نوع‌های اولیه مانند int , string,… استفاده میکنیم و به عبارتی میتوانیم بگوییم در استفاده از این نوع داده وسواس داریم. قطعه کد زیر را در نظر بگیرید:
public class UserFactory
{
    public User CreateUser(string email) {
        return new User(email);
    }
}
کلاس UserFactory، یک متد به نام CreateUser دارد که یک رشته را به عنوان ورودی میگیرد و یک شیء از کلاس User را بر می‌گرداند. خوب مشکل این متد کجاست؟
اگر به خاطر داشته باشید، در قسمت‌های قبلی در مورد مفهومی به نام Honesty صحبت کردیم. به طور ساده باید بتوانیم از روی امضای تابع، کاری را که تابع انجام میدهد و خروجی آن را ببینیم. این تابع Honest نیست؛ شرایطی که string می‌تواند درست نباشد، خالی باشد، طول غیر مجاز داشته باشد و ... را نمیتوانیم از امضای تابع حدس بزنیم.

برای روشن‌تر شدن بحث، مثال بالا را همیشه در ذهن خود داشته باشید. در این مثال، در تابع Divide که عمل تقسیم را انجام می‌دهد، پارامتر y که یک عدد از نوع int است، میتواند مقدار صفر را داشته باشد و باعث یک exception شود.و از آنجائیکه نوع خروجی این متد هم int است، انتظار دریافت یک exception را نداریم. در مورد exception‌ها به طول مفصل در قسمت قبلی صحبت کردیم. در مثال بالا تصور کنید که بجای یک ایمیل، از چند ایمیل به عنوان ورودی می‌خواهید استفاده کنید. آیا منطق Validation را به ازای هر پارامتر ورودی باید تکرار کنید؟

به طور کلی استفاده‌ی نابجا و بیش از حد از نوع‌های داده‌ی اولیه، باعث می‌شود تا Honesty متد‌ها را از دست بدهیم و قاعده‌ی DRY را نقض کنیم.

صحبت در مورد استفاده کردن یا نکردن، جنبه‌های زیادی دارد و یکی از مواردی است که در معماری DDD تحت عنوان Value Object به آن پرداخته شده. هدف ما در این قسمت از مقاله، صرفا پرداختن به گوشه‌ای از این مورد هست. ولی شما میتوانید برای مطالعه بیشتر و اطلاعات تکمیلی کتاب Domain-Driven Design: Tackling Complexity in the Heart of Software نوشته Eric Evans را مطالعه کنید.


به جای نوع‌های اولیه از چی استفاده کنیم؟

جواب خیلی ساده‌است؛ شما نیاز دارید تا یک Type اختصاصی را ایجاد کنید. برای مثال بجای استفاده از نوع string برای یک ایمیل، می‌توانید یک کلاس را به عنوان Email ایجاد کنید که مشخصه‌ای به نام Value دارد. این کار به روش‌های مختلفی قابل انجام است؛ اما پیشنهاد من استفاده از این روش هست:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ValueOf
{
    public class ValueOf<TValue, TThis> where TThis : ValueOf<TValue, TThis>, new()
    {
        private static readonly Func<TThis> Factory;

        /// <summary>
        /// WARNING - THIS FEATURE IS EXPERIMENTAL. I may change it to do
        /// validation in a different way.
        /// Right now, override this method, and throw any exceptions you need to.
        /// Access this.Value to check the value
        /// </summary>
        protected virtual void Validate()
        {
        }

        static ValueOf()
        {
            ConstructorInfo ctor = typeof(TThis)
                .GetTypeInfo()
                .DeclaredConstructors
                .First();

            var argsExp = new Expression[0];
            NewExpression newExp = Expression.New(ctor, argsExp);
            LambdaExpression lambda = Expression.Lambda(typeof(Func<TThis>), newExp);

            Factory = (Func<TThis>)lambda.Compile();
        }

        public TValue Value { get; protected set; }

        public static TThis From(TValue item)
        {
            TThis x = Factory();
            x.Value = item;
            x.Validate();

            return x;
        }

        protected virtual bool Equals(ValueOf<TValue, TThis> other)
        {
            return EqualityComparer<TValue>.Default.Equals(Value, other.Value);
        }

        public override bool Equals(object obj)
        {
            if (obj is null)
                return false;

            if (ReferenceEquals(this, obj))
                return true;

            return obj.GetType() == GetType() && Equals((ValueOf<TValue, TThis>)obj);
        }

        public override int GetHashCode()
        {
            return EqualityComparer<TValue>.Default.GetHashCode(Value);
        }

        public static bool operator ==(ValueOf<TValue, TThis> a, ValueOf<TValue, TThis> b)
        {
            if (a is null && b is null)
                return true;

            if (a is null || b is null)
                return false;

            return a.Equals(b);
        }

        public static bool operator !=(ValueOf<TValue, TThis> a, ValueOf<TValue, TThis> b)
        {
            return !(a == b);
        }

        public override string ToString()
        {
            return Value.ToString();
        }
    }
}
در این روش، یک کلاس را به عنوان Value Object ایجاد کرده‌ایم. این کلاس، نوع اولیه‌ای را که با آن سر و کار داریم، در بر خواهد گرفت و منطق مربوط به مقایسه، همچنین عملگرهای == و != را هم از طریق Equals و GetHashCode، پیاده سازی کرده. برای مثال جهت کلاس ایمیل می‌توانیم به صورت زیر عمل کنیم:
public class EmailAddress : ValueOf<string, EmailAddress> { }
همچنین برای مقدار دهی این کلاس میتوانید به صورت زیر عمل کنید:
EmailAddress emailAddress = EmailAddress.From("foo@bar.com");
برای مثال‌های پیچیده‌تر مانند آدرس، که شامل آدرس، کد پستی و … می‌باشد، میتوانید با استفاده از امکان Tuple‌ها که از سی شارپ 7 به بعد معرفی شده، مانند مثال زیر عمل کنید:
public class Address : ValueOf<(string firstLine, string secondLine, Postcode postcode), Address> { }
و در نهایت برای نوشتن منطق مربوط به validation می‌توانید متد Validate را Override کنید و قاعده‌ی DRY را هم نقض نکنید.

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

در صورتیکه مشکلی در پیاده سازی داشتید، می‌توانید مشکل خود را زیر همین مطلب و یا بر روی gist آن کامنت کنید.
مطالب
توسعه سیستم مدیریت محتوای DNTCms - قسمت اول
قصد داریم طی یک سری مقالات به توسعه یک سیستم مدیریت محتوا بپردازیم. مسلما فاصله‌ی زمانی بین انتشار مقالات این سری، کمی زیاد خواهد بود. ولی سعی خواهیم کرد تا قدم به قدم و با تحلیل و توضیح کافی هر بخش به این هدف برسیم.
همکاران این قسمت:
پیشنیاز‌ها:
در زیر بخش هایی که برای این سیستم تا به امروز در نظر گرفتیم (مسلما با ارائه ایده‌ها و بازخورد‌های دوستان این امکانات دستخوش تغییر خواهند شد) ، به شرح زیر میباشد:
  • انجمن
  • ارتباط دوستی
  • سیستم ترفیع رتبه
  • Themeable
  • سیستم Following
  • صفحات داینامیک
  • سیستم پیام رسانی
  • امکان ساخت گروه‌های شخصی برای انتشار مطالب خود (توسط کاربران) با اعمال دسترسی مختلف
  • پیغام خصوصی
  • وبلاگ
  • نظرسنجی ها
  • مدیریت کاربران با دسترسی‌ها داینامیک
  • اخبار
  • آگهی ها
در این قسمت مدل‌های مربوط به بخش وبلاگ را بررسی میکنیم. ابتدا قصد داشتیم تا امکان نگارش یک پست توسط چندین کاربر را به طور همزمان، در سیستم قرار دهیم و ایده کار هم که توسط یکی دوستان (محمد شریفی) پیشنهاد شد به صورت زیر بود:
ابتدا کاربری به عنوان ایجاد کننده اصلی پست، بخش‌ها مختلفی را برای یک پست در نظر بگیرد و هر قسمت را به یک همکار نسبت دهد و با اتمام تمام بخش‌ها و اعلام آن توسط همکاران، کاربر اصلی ایجاد کننده پست بتواند بخش‌ها را باهم ادغام کند و برای آماده انتشار نهایی باشد.
ولی خب به نظر بنده الان سیستم‌های ارتباطی قدرتمندتری هم هست که همکاران در مورد نگارش یک پست به صورت مشارکتی بپردازند و صرفا در مرحله‌ی انتشار، این کاربران به عنوان کاربران همکار به پست منتشر شده نسبت داده شوند تا در زیر پست مشخصات کامل آنها قابل مشاهد باشد (راه حلی که پیاده سازی خواهیم کرد).
مدل‌های در نظر گرفته شده برای سیستم وبلاگ به صورت زیر می‌باشد:

مخزن برچسب ها
/// <summary>
    /// Represents the lable 
    /// </summary>
    public class Tag
    {
        #region Ctor
        /// <summary>
        /// Create one instance of <see cref="Tag"/>
        /// </summary>
        public Tag()
        {
            Id = SequentialGuidGenerator.NewSequentialGuid();
        }
        #endregion

        #region Properties
        /// <summary>
        /// sets or gets Tag's identifier
        /// </summary>
        public virtual Guid  Id { get; set; }
        /// <summary>
        /// sets or gets Tag's name
        /// </summary>
        public virtual string Name { get; set; }
        #endregion

        #region NavigationProperties
        /// <summary>
        /// sets or gets Tag's posts
        /// </summary>
        public virtual ICollection<BlogPost> BlogPosts { get; set; }
        #endregion
    }
کلاس بالا مشخص کننده‌ی مخزن برچسب‌های سیستم ما خواهد بود. جدول حاصل از این مدل با جداول سایر بخش‌ها که نیاز به داشتن برچسب خواهند داشت، ارتباط چند به چند خواهد داشت. برای این منظور همانطور که مشخص است در قسمت NavigationProperties یک لیست از BlogPost معرفی شده است و در ادامه خواهیم دید که لیستی از کلاس Tag هم در کلاس BlogPost معرفی خواهد شد.

مدل پیش نویس ها
  /// <summary>
    /// Represents the Post's Draft
    /// </summary>
    public  class BlogDraft
    {
        #region Ctor
        /// <summary>
        /// create one instance of <see cref="BlogDraft"/>
        /// </summary>
        public  BlogDraft()
        {
            Id = SequentialGuidGenerator.NewSequentialGuid();
        }
        #endregion

        #region Properties
        /// <summary>
        /// gets or sets Id of post's draft
        /// </summary>
        public virtual  Guid Id { get; set; }
        /// <summary>
        /// gets or sets body of post's draft
        /// </summary>
        public virtual  string Body { get; set; }
        /// <summary>
        /// gets or set title of post's draft
        /// </summary>
        public virtual  string Title { get; set; }
        /// <summary>
        /// gets or sets tags of post's draft that seperated using ','
        /// </summary>
        public virtual  string TagNames { get; set; }
        /// <summary>
        /// gets or sets value indicating whether this draft is ready to publish
        /// </summary>
        public virtual  bool IsReadyForPublish { get; set; }
        /// <summary>
        /// ges ro sets DateTime that this draft added
        /// </summary>
        public virtual  DateTime CreatedOn { get; set; }
        /// <summary>
        /// gets or sets information of User-Agent
        /// </summary>
        public virtual string Agent { get; set; }
        /// <summary>
        /// gets or sets date that this draft publish as ready
        /// </summary>
        public virtual DateTime? ReadyForPublishOn { get; set; }
        #endregion

        #region NavigationProperties
        /// <summary>
        /// gets or sets Id of user that he is owner of this draft
        /// </summary>
        public virtual  long OwnerId { get; set; }
        /// <summary>
        /// gets or sets user that he is owner of this draft
        /// </summary>
        public virtual  User Owner { get; set; }
        #endregion
    }
کلاس فوق برای ذخیره سازی پست‌های پیشنویس کاربران در نظر گرفته شده است. شاید بهتر بود این پیش نویس‌ها هم در همان مدل پستی که در ادامه مشاهده می‌کنید ادغام شوند. ولی این یک تصمیم شخصی برای این کار بوده و برای سیستی بزرگی که امکان دارد حذف و درج پیشنویس‌ها زیاد باشد و فرض بر اینکه long هم برای آی دی جواب گو نخواهد بود و نیاز است از Guid ای که به صورت متوالی افزایش میابد به منظور جلوگیری از Fragmentation، استفاده کرد. بقیه فیلد‌ها هم مشخص هستند و نیازی به توضیح اضافی نخواهد بود. مسلما هر کاربر می‌تواند چندین پیشنویس داشته باشد. لذا ارتباط یک به چند مابین کاربر و پیشنویس پست، خواهیم داشت.
مدل امتیاز دهی کاربران
/// <summary>
    /// Section of Rating
    /// </summary>
    public enum RatingSection
    {
        News,
        Announcement,
        ForumTopic,
        BlogComment,
        NewsComment,
        PollComment,
        AnnouncementComment,
        ForumPost,
        ...
    }


    /// <summary>
    /// Represents Rating Record regard by section type for Rating System
    /// </summary>
    public class UserRating
    {
        #region Ctor
        /// <summary>
        /// Create one instance of <see cref="UserRating"/>
        /// </summary>
        public UserRating()
        {
            Id = SequentialGuidGenerator.NewSequentialGuid();
        }
        #endregion

        #region Properties
        /// <summary>
        /// gets or sets Id of Rating Record
        /// </summary>
        public virtual Guid Id { get; set; }
        /// <summary>
        /// gets or sets value of rate
        /// </summary>
        public virtual double RatingValue { get; set; }
        /// <summary>
        /// gets or sets Section's Id 
        /// </summary>
        public virtual long SectionId { get; set; }
        /// <summary>
        /// gets or sets Section 
        /// </summary>
        public virtual RatingSection Section { get; set; }
        #endregion

        #region Navigation Properties
        /// <summary>
        /// gets or sets user that rate one section
        /// </summary>
        public virtual User Rater { get; set; }
        /// <summary>
        /// gets or sets Rater Id that rate one section
        /// </summary>
        public virtual long RaterId { get; set; }

        #endregion
    }
نوع داده‌ی شمارشی RatingSection مشخص کننده‌ی بخش‌های مختلف سیستم می‌باشد که نیاز به امتیاز دهی دارند. چون سیستم ما یک سیستم بسته می‌باشد و برای امتیاز دهی نیاز به احراز هویت خواهد بود، لذا باید از امتیاز دادن بیش از یک بار برای هر بخش جلوگیری کنیم. برای این کار می‌توان بین تمام بخش‌های موجود ارتباط‌های چند به چند در نظر گرفت. برای مثال یک ارتباط چند به چند بین کاربر و نظرات که مشخص کننده‌ی این است که یک کاربر می‌تواند به چند نظر امتیاز دهد و هر نظر هم میتواند چندین امتیاز دهنده داشته باشد ولی تعداد جداول بالا خواهد رفت و کار آن هم زیاد خواهد شد. برای این منظور میتوان یک جدول به مانند UserRating در نظر گرفت که صرفا با جدول کاربر ما یک ارتباط یک به چند دارد و با استفاده از خصوصیت RatingSection موجود در این کلاس وخصوصیت SectionId و همچنین در نظر گرفتن یک کلید منحصر به فرد بر روی خصوصیت‌های RatingSection ، RaterId و SectionId، از امتیاز دادن چند باره‌ی کاربر به یک بخش جلوگیری کرد.
/// <summary>
    /// Represent the rating as ComplexType
    /// </summary>
    [ComplexType]
    public class Rating
    {
        /// <summary>
        /// sets or gets total of rating
        /// </summary>
        public virtual double? TotalRating { get; set; }
        /// <summary>
        /// sets or gets rater's count
        /// </summary>
        public virtual long? RatersCount { get; set; }
        /// <summary>
        /// sets or gets average of rating
        /// </summary>
        public virtual double? AverageRating { get; set; }
    }
کلاس بالا یک ComplexType می‌باشد که در نهایت به هیچ جدولی مپ نخواهد شد و صرفا به منظور کپسوله کردن یکسری فیلد مورد استفاده قرار گرفته است و از این کلاس می‌توان در هر بخش که لازم است امتیاز دهی داشته باشیم، به عنوان یک خصوصیت، استفاده کرد.

کلاس پایه‌ی محتوا
/// <summary>
    /// Represents a base class for every content in system
    /// </summary>
    public abstract class BaseContent
    {
        #region Properties

        /// <summary>
        /// get or set identifier of record
        /// </summary>
        public virtual long Id { get; set; }
        /// <summary>
        /// gets or sets date of publishing content
        /// </summary>
        public virtual DateTime PublishedOn { get; set; }
        /// <summary>
        /// gets or sets Last Update's Date
        /// </summary>
        public virtual DateTime ModifiedOn { get; set; }
        /// <summary>
        /// gets or sets the blog pot body
        /// </summary>
        public virtual string Body { get; set; }
        /// <summary>
        /// gets or sets the content title
        /// </summary>
        public virtual string Title { get; set; }
        /// <summary>
        /// gets or sets value  indicating Custom Slug
        /// </summary>
        public virtual string SlugUrl { get; set; }
        /// <summary>
        /// gets or sets meta title for seo
        /// </summary>
        public virtual string MetaTitle { get; set; }
        /// <summary>
        /// gets or sets meta keywords for seo
        /// </summary>
        public virtual string MetaKeywords { get; set; }
        /// <summary>
        /// gets or sets meta description of the content
        /// </summary>
        public virtual string MetaDescription { get; set; }
        /// <summary>
        /// gets or sets 
        /// </summary>
        public virtual string FocusKeyword { get; set; }
        /// <summary>
        /// gets or sets value indicating whether the content use CanonicalUrl
        /// </summary>
        public virtual bool UseCanonicalUrl { get; set; }
        /// <summary>
        /// gets or sets CanonicalUrl That the Post Point to it
        /// </summary>
        public virtual string CanonicalUrl { get; set; }
        /// <summary>
        /// gets or sets value indicating whether the content user no Follow for Seo
        /// </summary>
        public virtual bool UseNoFollow { get; set; }
        /// <summary>
        /// gets or sets value indicating whether the content user no Index for Seo
        /// </summary>
        public virtual bool UseNoIndex { get; set; }
        /// <summary>
        /// gets or sets value indicating whether the content in sitemap
        /// </summary>
        public virtual bool IsInSitemap { get; set; }
        /// <summary>
        /// gets or sets a value indicating whether the content comments are allowed 
        /// </summary>
        public virtual bool AllowComments { get; set; }
        /// <summary>
        /// gets or sets a value indicating whether the content comments are allowed for anonymouses 
        /// </summary>
        public virtual bool AllowCommentForAnonymous { get; set; }
        /// <summary>
        /// gets or sets  viewed count by rss
        /// </summary>
        public virtual long ViewCountByRss { get; set; }
        /// <summary>
        /// gets or sets viewed count 
        /// </summary>
        public virtual long ViewCount { get; set; }
        /// <summary>
        /// Gets or sets the total number of  comments
        /// <remarks>The same as if we run Item.Comments.where(a=>a.Status==Status.Approved).Count()
        /// We use this property for performance optimization (no SQL command executed)
        /// </remarks>
        /// </summary>
        public virtual int ApprovedCommentsCount { get; set; }
        /// <summary>
        /// Gets or sets the total number of  comments
        /// <remarks>The same as if we run Item.Comments.where(a=>a.Status==Status.UnApproved).Count()
        /// We use this property for performance optimization (no SQL command executed)</remarks></summary>
        public virtual int UnApprovedCommentsCount { get; set; }
        /// <summary>
        /// gets or sets value  indicating whether the content is logical deleted or hidden
        /// </summary>
        public virtual bool IsDeleted { get; set; }
        /// <summary>
        /// gets or sets rating complex instance
        /// </summary>
        public virtual Rating Rating { get; set; }
        /// <summary>
        /// gets or sets value indicating whether the content show with rssFeed
        /// </summary>
        public virtual bool ShowWithRss { get; set; }
        /// <summary>
        /// gets or sets value indicating maximum days count that users can send comment
        /// </summary>
        public virtual int DaysCountForSupportComment { get; set; }
        /// <summary>
        /// gets or sets information of User-Agent
        /// </summary>
        public virtual string Agent { get; set; }
        /// <summary>
        /// gets or sets icon name with size 200*200 px for snippet 
        /// </summary>
        public virtual string SocialSnippetIconName { get; set; }
        /// <summary>
        /// gets or sets title for snippet
        /// </summary>
        public virtual string SocialSnippetTitle { get; set; }
        /// <summary>
        /// gets or sets description for snippet
        /// </summary>
        public virtual string SocialSnippetDescription { get; set; }
        /// <summary>
        /// gets or sets body of content's comment
        /// </summary>
        public virtual byte[] RowVersion { get; set; }
        /// <summary>
        /// gets or sets name of tags seperated by comma that assosiated with this content fo increase performance
        /// </summary>
        public virtual string TagNames { get; set; }
        /// <summary>
        /// gets or sets counter for Content's report
        /// </summary>
        public virtual int ReportsCount { get; set; }
        #endregion

        #region NavigationProperties

        /// <summary>
        /// get or set user that create this record
        /// </summary>
        public virtual User Author { get; set; }

        /// <summary>
        /// gets or sets Id of user that create this record
        /// </summary>
        public virtual long AuthorId { get; set; }
        /// <summary>
        /// get or set  the tags integrated with content
        /// </summary>
        public virtual ICollection<Tag> Tags { get; set; }
        #endregion

    }

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

نکته‌ای که وجود دارد فیلد‌های ApprovedCommentsCount  UnApprovedCommentsCount و TagNames می‌باشند که هنگام درج نظر جدید باید تعداد نظرات ذخیره شده را  ویرایش کنیم و هنگام ویرایش خود پست یا خبر با ... و یا حتی ویرایش خود تگ یا حذف آن تگ باید TagNames که لیست برچسب‌های محتوا را به صورت جدا شده با (,) از هم دیگر می‌باشد، ویرایش کنیم (جای بحث دارد).

مشخص است که هر یک از مطالب منتشر شده در بخش‌های وبلاگ، اخبار، نظرسنجی و آگهی‌ها، یک کابر ایجاد کننده (Author نامیده‌ایم) خواهد داشت و هر کاربر هم می‌تواند چندین مطلب را ایجاد کند. لذا رابطه‌ی یک به چند بین تمام این بخش‌ها مذکور و کاربر ایجاد خواهد شد.

مدل  LinkBack

 /// <summary>
    /// Represents link for implemention linkback
    /// </summary>
    public class LinkBack
    {

        #region Ctor
        /// <summary>
        /// create one instance of <see cref="LinkBack"/>
        /// </summary>
        public LinkBack()
        {
            CreatedOn = DateTime.Now;
            Id = SequentialGuidGenerator.NewSequentialGuid();
        }
        #endregion

        #region Properties
        /// <summary>
        /// gets or sets link's Id
        /// </summary>
        public virtual Guid Id { get; set; }
        /// <summary>
        /// gets or sets text for show Link
        /// </summary>
        public virtual string Title { get; set; }
        /// <summary>
        /// gets or sets link's address 
        /// </summary>
        public virtual string Url { get; set; }
        /// <summary>
        /// gets or set value indicating whether this link is internal o external
        /// </summary>
        public virtual LinkBackType Type { get; set; }
        /// <summary>
        /// gets or sets date that this record is added
        /// </summary>
        public virtual DateTime CreatedOn { get; set; }
        #endregion

        #region NavigationProperties
        /// <summary>
        /// gets or sets Post that associated
        /// </summary>
        public virtual BlogPost Post { get; set; }
        /// <summary>
        /// gets or sets id of Post that associated
        /// </summary>
        public virtual long PostId { get; set; }
        #endregion
    }


 /// <summary>
    /// represents Type of ReferrerLinks
    /// </summary>
    public enum LinkBackType
    {
        /// <summary>
        /// Internal link
        /// </summary>
        Internal,
        /// <summary>
        /// External Link
        /// </summary>
        External
    }

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

مدل پست ها

  /// <summary> 
    /// Represents a blog post
    /// </summary>
    public  class BlogPost : BaseContent
    {
        #region Ctor
        /// <summary>
        /// Create one Instance of <see cref="BlogPost"/>
        /// </summary>
        public BlogPost()
        {
            Rating = new Rating();
            PublishedOn = DateTime.Now;
        }
        #endregion

       #region Properties
        /// <summary>
        /// gets or sets Status of LinkBack Notifications
        /// </summary>
        public virtual LinkBackStatus LinkBackStatus { get; set; }
        #endregion

        #region NavigationProperties

        /// <summary>
        /// get or set  blog post's Reviews
        /// </summary>
        public virtual ICollection<BlogComment> Comments { get; set; }
        /// <summary>
        /// get or set collection of links that reference to this blog post
        /// </summary>
        public virtual ICollection<LinkBack> LinkBacks { get; set; }
        /// <summary>
        /// get or set Collection of Users that Contribute on this post
        /// </summary>
        public virtual ICollection<User> Contributors  { get; set; }
        
        #endregion
    }


  /// <summary>
    /// Represents Status for ReferrerLinks
    /// </summary>
    public enum  LinkBackStatus
    {
        [Display(Name ="غیرفعال")]
        Disable,
        [Display(Name = "فعال")]
        Enable,
        [Display(Name = "لینک‌ها داخلی")]
        JustInternal,
        [Display(Name = "لینک‌ها خارجی")]
        JustExternal
    }
 کلاس بالا نشان دهنده‌ی مدل پست‌های ما در سیستم می‌باشد که بیشتر خصوصیات خود را از کلاس پایه‌ی BaseContent به ارث برده و لازم به تکرار آنها نیست. به علاوه یکسری خصوصیات دیگر، از جلمه‌ی آنها یک لیست از کلاس LinkBack، لیستی از کاربران مشارکت کننده به منظور نمایش مشخصات آنها در زیر پست و لیستی از کلاس BlogComment که نشان دهنده‌ی نظرات پست می‌باشد، هم خواهد داشت. خصوصیتی از نوع داده‌ی LinkBackStatus هم صرفا به منظور تنظیمات مدیریتی در نظر گرفته شده است. 

کلاس پایه نظرات
  /// <summary>
    /// Represents a base class for every comment in system 
    /// </summary>

    public abstract class BaseComment
    {
        #region Properties
        /// <summary>
        /// get or set identifier of record
        /// </summary>
        public virtual long Id { get; set; }
        /// <summary>
        /// gets or sets date of creation 
        /// </summary>
        public virtual DateTime CreatedOn { get; set; }
        /// <summary>
        /// gets or sets displayName of this comment's Creator if  he/she is Anonymous
        /// </summary>
        public virtual string CreatorDisplayName { get; set; }
        /// <summary>
        /// gets or sets body of blog post's comment
        /// </summary>
        public virtual string Body { get; set; }
        /// <summary>
        /// gets or sets body of blog post's comment
        /// </summary>
        public virtual Rating Rating { get; set; }
        /// <summary>
        /// gets or sets informations of agent
        /// </summary>
        public virtual string UserAgent { get; set; }
        /// <summary>
        /// gets or sets siteUrl of Creator if he/she is Anonymous
        /// </summary>
        public virtual string SiteUrl { get; set; }
        /// <summary>
        /// gets or sets Email of Creator if he/she is anonymous
        /// </summary>
        public virtual string Email { get; set; }
        /// <summary>
        /// gets or sets status of comment
        /// </summary>
        public virtual CommentStatus Status { get; set; }
        /// <summary>
        /// gets or sets Ip Address of Creator
        /// </summary>
        public virtual string CreatorIp { get; set; }
        /// <summary>
        /// gets or sets datetime that is modified
        /// </summary>
        public virtual DateTime? ModifiedOn { get; set; }
        /// <summary>
        /// gets or sets counter for report this comment
        /// </summary>
        public virtual int ReportsCount { get; set; }
        #endregion

        #region NavigationProperties

        /// <summary>
        /// get or set user that create this record
        /// </summary>
        public virtual User Creator { get; set; }

        /// <summary>
        /// get or set Id of user that create this record
        /// </summary>
        public virtual long? CreatorId { get; set; }
        #endregion
    }

public enum CommentStatus
    {
        /* 0 - approved, 1 - pending, 2 - spam, -1 - trash */
        [Display(Name = "تأیید شده")]
        Approved = 0,
        [Display(Name = "در انتظار بررسی")]
        Pending = 1,
        [Display(Name = "جفنگ")]
        Spam = 2,
        [Display(Name = "زباله دان")]
        Trash = -1
    }
به مانند BaseContent که برای کپسوله کردن خصوصیات تکراری و نه برای اعمال ارث بری TPH یا TPT، کلاس BaseComment را در نظر گرفته‌ایم. نکته‌ی مهم این است که هر نظری یک درج کننده دارد. ولی اگر فیلد AllowCommentForAnonymous مربوط به مطلب True باشد، این امکان وجود خواهد داشت تا کاربران احراز هویت نشده نیز نظر ارسال کنند. لذا CreatorId در کلاس BaseComment به صورت Nullable در نظر گرفته شده است و یک سری خصوصیت‌ها از قبیل SiteUrl ، CreatorDisplayName ، Email برای کاربران احراز هویت نشده در نظر گرفته شده است. نوع شمارشی CommentStatus نیز برای اعمال مدیریتی در نظر گرفته شده است.
مدل نظرات پست ها
/// <summary>
    /// Represents a blog post's comment
    /// </summary>
    public class BlogComment : BaseComment
    {
        #region Ctor
        /// <summary>
        /// Create One Instance for <see cref="BlogComment"/>
        /// </summary>
        public BlogComment()
        {
            Rating = new Rating();
            CreatedOn = DateTime.Now;
        }
        #endregion

        #region NavigationProperties
        /// <summary>
        /// gets or sets BlogComment's identifier for Replying and impelemention self referencing
        /// </summary>
        public virtual long? ReplyId { get; set; }
        /// <summary>
        /// gets or sets blog's comment for Replying and impelemention self referencing
        /// </summary>
        public virtual BlogComment Reply { get; set; }
        /// <summary>
        /// get or set collection of blog's comment for Replying and impelemention self referencing
        /// </summary>
        public virtual ICollection<BlogComment> Children { get; set; }
        /// <summary>
        /// gets or sets post that this comment sent to it
        /// </summary>
        public virtual BlogPost Post { get; set; }
        /// <summary>
        /// gets or sets post'Id that this comment sent to it
        /// </summary>
        public virtual long PostId { get; set; }
        #endregion
    }
کلاس بالا مشخص کننده‌ی نظرات پست‌های وبلاگ می‌باشند که بیشتر خصوصیت‌های خود را از کلاس پایه‌ی BaseComment به ارث برده است و همچنین ساختار سلسله مراتبی آن نیز  قابل مشاهده است. 
برای سیستم Report یا همان گزارش خطا هم سیستمی شبیه به امتیاز دهی ستاره‌ای در نظر گرفته ایم. برای اینکه مقاله خیلی طولانی شد، بهتر است در مقاله‌ای جدا کار را ادامه داد.
نتیجه این قسمت

نظرات مطالب
ایجاد alert,confirm,prompt هایی متفاوت با jQuery Impromptu
سلام؛

طبق مطلب ارائه شده در مقاله:

تابع submit :
 function(event, value, message, formVals){}
value مقدار دکمه ای است که بروی آن کلیک شده ، message مقدار html تعریف شده برای state است ، formVals هم در صورتی که در html تعریف شده برای state ، المنت‌های فرم وجود داشته باشد ، شامل نام/مقادیر آنها می‌باشد . ( برای دریافت مقادیر فرم ، باید از نام المنت استفاده نمایید . )


خب حالا مشابه مثال زیر عمل کنید:
<div class="prompt-content" style="display: none;">
    <span>ایمیل خود را وارد نمایید : </span>
    <span>
        <input type="text" name="user_email" />
    </span>
</div>

$.prompt(
    $(".prompt-content").html(),
    {
        submit: function (e, v, m, f) {
            var userEmail = f["user_email"];
            console.log(userEmail);
        }
    });


پرسش‌ها
تا چه میزان کلیدهای خارجی باید در لایه Application بررسی شوند؟

سلام

زمانی که کاربر یک درخواست از نوع POST را به API ارسال میکنه، نتیجه انجام عملیات در متغیری با نام IsSuccess برگردانده میشه. اگر مقدار این متغیر برابر false باشه یعنی متغیر ErrorList حاوی پیغام است.

سناریو:

  • نام Entity در این مثال Product هست که پنج Navigation Property داره که با Entityهای دیگر دارای Relation هستند.
  • لایه Application قبل از اینکه در Entity یک ردیف جدید ثبت کنه ابتدا با دستور Any مقدار یکی از Propertyها را بررسی میکنه و اگر وجود نداشت، یک Error در ErrorList اضافه میکنه و نتیجه را بصورت IsSeccess=false بر میگردونه.
  • در واقع حالا کاربر مطلع هست که مثلا ProductTypeId اشتباه است و اصلا ProductTypeی با این شناسه وجود نداره.

سوال:

  1. آیا ضروریه تا به ازای هر کلید خارجی که در Entity تعریف شده بدین صورت اعتبار تمام آنها بررسی و به کلاینت اعلام شود؟
  2. اگر تعداد کلیدهای خارجی در یک Entity زیاد باشد راه حل چیست؟
  3. آیا اگر اطلاعات را مستقیما به بانک اطلاعاتی ارسال کنیم تا خود بانک اطلاعاتی صحت وجود این کلیدها را بررسی کند بهتر است یا خیر؟
  4. اگر برعهده بانک اطلاعاتی بگذاریم چطور باید به کلاینت و به ازای هر کلید خارجی که باعث بروز Exception شده اطلاع رسانی کنیم؟
  5. در مجموع روش مناسب کدام است؟

تشکر

نظرات مطالب
تبدیل HTML به PDF با استفاده از کتابخانه‌ی iTextSharp
من همین روش رو برای گریدویو تودرتو که داخل یه div هستند و آن div بصورت runat=server میباشد و آن را با استفاده از متد RenderControl داخل یه String Writer ریختم و اونو بعنوان پارامتر  StreamReader پاس دادم.نتیجه این شد که فیلدهای گرید بیرونی درست نمایش داده شدند ولی گریدویو داخلی متنهای آن بصورت برعکس و نچسبیده نمایان شدندممنون یشم راهنمایی کنید.
نظرات مطالب
اعتبارسنجی درخواست های http$ با استفاده از یک Interceptor

با سلام و احترام

ممنون بابت مقاله خوبتون

ولی این شرط

filterContext.HttpContext.Request.IsAjaxRequest

معنی

یعنی اعتبارسنجی نشده است

را نمی‌دهد.

شما در متد HandleUnauthorizedRequest هستید و بالطبع اعتبار درخواست رد شده است، کاری که می‌خواهید انجام دهید، اعتبارسنجی نیست، بلکه اصلاح Response درخواست‌های Ajax ای است که Status Code آنها ۲۰۰ است که نباید باشد.

این نوع دقت داشتن و شفافیت لازمه ایجاد یک کد صحیح است، چون الآن و در مرحله ای بالاتر، می‌توانیم بگویم که هر Response ای که وارد متد HandleUnauthorizedRequest شده است، اگر Status Code 401 ندارد، Status Code آن برابر ۴۰۱ قرار داده شود. فارغ از Ajax بودن یا نبودن.

و باز در مرحله ای بالاتر اگر در پروژه از Owin استفاده شده باشد، یک Owin Middleware و اگر نه یک ASP.NET Module جای مناسب‌تری برای نوشتن این چنین کدی است.

از این که IsAjaxRequest همیشه درست کار نمی‌کند و فقط وقتی درخواست Ajax ای بگوید که Ajax ای است، کار خواهد کرد نیز بگذریم.

موفق و پایدار باشید.