نظرات مطالب
غیرمعتبر کردن توکن و یا کوکی سرقت شده در برنامه‌های مبتنی بر ASP.NET Core
استاندارد جدیدی در مرورگرها در حال پیاده سازی است، جهت محدود کردن استفاده از یک توکن یک کلاینت، فقط در همان مرورگر اولیه‌ای که آن‌را دریافت کرده و نه در سایر مرورگرها. در این لحظه فقط Microsoft edge این استاندارد را پیاده سازی کرده‌است.
نظرات مطالب
تنظیمات CORS در ASP.NET Core
- مطلب جاری هیچ ارتباطی به درخواست‌های درون برنامه‌ای ندارد و فقط مرتبط با تنظیمات امنیتی مرورگرها است. این مرورگرها هستند که عنوان می‌کنند، من اینکار خاص را تحت این شرایط ویژه انجام نمی‌دهم؛ اما برنامه‌ی شما در پشت صحنه و توسط HttpClient یا هر روش مشابه دیگری، چنین محدودیتی ندارد.
- موردی که عنوان کردید فقط مرتبط با تنظیمات HttpClient مورد استفاده‌است که چنین گزینه‌ای را تنظیم نکرده.
مطالب
مقایسه کارآیی روش‌های مختلف جایگزین کردن حروف در یک رشته در برنامه‌های NET.
فرض کنید قصد دارید عملیات نرمال سازی اطلاعات را بر روی یک رشته انجام داده و برای مثال اعداد فارسی و انگلیسی موجود در یک رشته را یک‌دست کنید. اولین روشی که برای اینکار به ذهن می‌رسد، استفاده از متد Replace است:
private static string toPersianNumbersUsingReplace(string data)
{
    if (string.IsNullOrWhiteSpace(data)) return string.Empty;
    return
      data
        .Replace("0", "\u06F0")
        .Replace("1", "\u06F1")
        .Replace("2", "\u06F2")
        .Replace("3", "\u06F3")
        .Replace("4", "\u06F4")
        .Replace("5", "\u06F5")
        .Replace("6", "\u06F6")
        .Replace("7", "\u06F7")
        .Replace("8", "\u06F8")
        .Replace("9", "\u06F9");
}
اما آیا این روش، کارآیی مناسبی را به همراه دارد؟ در ادامه چند روش دیگر را نیز جهت جایگزین کردن حروف، معرفی کرده و کارآیی آن‌ها را با هم مقایسه می‌کنیم.


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

نگارش اصلی تبدیل تمام اعداد موجود در یک رشته به اعداد فارسی، به صورت زیر است که در آن یک دست سازی اعداد عربی هم درنظر گرفته شده‌اند (برای مثال طرز نگارش عدد 4 فارسی و عربی متفاوت است):
        private static string toPersianNumbersUsingReplace(string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return string.Empty;
            return
                toEnglishNumbers(data)
                .Replace("0", "\u06F0")
                .Replace("1", "\u06F1")
                .Replace("2", "\u06F2")
                .Replace("3", "\u06F3")
                .Replace("4", "\u06F4")
                .Replace("5", "\u06F5")
                .Replace("6", "\u06F6")
                .Replace("7", "\u06F7")
                .Replace("8", "\u06F8")
                .Replace("9", "\u06F9");
        }

        private static string toEnglishNumbers(string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return string.Empty;
            return
               data.Replace("\u0660", "0") //٠
                   .Replace("\u06F0", "0") //۰
                   .Replace("\u0661", "1") //١
                   .Replace("\u06F1", "1") //۱
                   .Replace("\u0662", "2") //٢
                   .Replace("\u06F2", "2") //۲
                   .Replace("\u0663", "3") //٣
                   .Replace("\u06F3", "3") //۳
                   .Replace("\u0664", "4") //٤
                   .Replace("\u06F4", "4") //۴
                   .Replace("\u0665", "5") //٥
                   .Replace("\u06F5", "5") //۵
                   .Replace("\u0666", "6") //٦
                   .Replace("\u06F6", "6") //۶
                   .Replace("\u0667", "7") //٧
                   .Replace("\u06F7", "7") //۷
                   .Replace("\u0668", "8") //٨
                   .Replace("\u06F8", "8") //۸
                   .Replace("\u0669", "9") //٩
                   .Replace("\u06F9", "9"); //۹
        }


جایگزین کردن حروف با استفاده از Replace معمولی توسط کاراکترها

اینبار همان حالت قبل را درنظر بگیرید؛ با این تفاوت که بجای رشته‌ها از کاراکترها استفاده شود. برای مثال بجای:
  .Replace("\u0669", "9") //٩
خواهیم داشت:
  .Replace('\u0669', '9') //٩


جایگزین کردن حروف با استفاده از String Builder

در ادامه بجای استفاده از متد Replace متداول، آرایه‌ای از حروف قابل جایگزینی را توسط یک StringBuilder ایجاد کرده و حروف را یکی یکی تبدیل می‌کنیم و به این ترتیب برخلاف متد Replace، هربار برای جایگزینی یک مورد خاص، مجددا از ابتدای رشته شروع به جستجو نمی‌شود:
        private static string toPersianNumbersUsingStringBuilder(string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return string.Empty;

            var strBuilder = new StringBuilder(data);
            for (var i = 0; i < strBuilder.Length; i++)
            {
                switch (strBuilder[i])
                {
                    case '0':
                    case '\u0660':
                        strBuilder[i] = '\u06F0';
                        break;

                    case '1':
                    case '\u0661':
                        strBuilder[i] = '\u06F1';
                        break;

                    case '2':
                    case '\u0662':
                        strBuilder[i] = '\u06F2';
                        break;

                    case '3':
                    case '\u0663':
                        strBuilder[i] = '\u06F3';
                        break;

                    case '4':
                    case '\u0664':
                        strBuilder[i] = '\u06F4';
                        break;

                    case '5':
                    case '\u0665':
                        strBuilder[i] = '\u06F5';
                        break;

                    case '6':
                    case '\u0666':
                        strBuilder[i] = '\u06F6';
                        break;

                    case '7':
                    case '\u0667':
                        strBuilder[i] = '\u06F7';
                        break;

                    case '8':
                    case '\u0668':
                        strBuilder[i] = '\u06F8';
                        break;

                    case '9':
                    case '\u0669':
                        strBuilder[i] = '\u06F9';
                        break;

                    default:
                        strBuilder[i] = strBuilder[i];
                        break;
                }
            }

            return strBuilder.ToString();
        }


جایگزین کردن حروف با استفاده از ToCharArray

متد زیر دقیقا شبیه به حالت استفاده از String Builder است؛ با یک تفاوت مهم: بجای استفاده از String Builder برای تهیه‌ی آرایه‌ای از حروف قابل تغییر، از متد ToCharArray استفاده شده‌است:
        private static string toPersianNumbersUsingToCharArray(string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return string.Empty;

            var letters = data.ToCharArray();
            for (var i = 0; i < letters.Length; i++)
            {
                switch (letters[i])
                {
                    case '0':
                    case '\u0660':
                        letters[i] = '\u06F0';
                        break;

                    // مانند قبل

                }
            }

            return new string(letters);
        }


جایگزین کردن حروف با استفاده از string.Create

string.Create یکی از تازه‌های NET Core. است که امکان تغییر مستقیم یک قطعه string را میسر می‌کند:
        private static string toPersianNumbersUsingStringCreate(string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return string.Empty;

            return string.Create(data.Length, data, (chars, context) =>
            {
                for (var i = 0; i < data.Length; i++)
                {
                    switch (context[i])
                    {
                        case '0':
                        case '\u0660':
                            chars[i] = '\u06F0';
                            break;

                    // مانند قبل

                    }
                }
            });
        }
در کدهای فوق، ابتدا طول رشته‌ی نهایی بازگشتی از string.Create مشخص می‌شود. سپس توسط پارامتر دوم، داده‌هایی که قرار است بر روی آن‌ها کاری صورت گیرد به متد string.Create ارسال می‌شوند. در آخر عملیات نهایی در action delegate تعریف شده رخ می‌دهد. در اینجا chars، به بافر درونی رشته‌ای که بازگشت داده می‌شود، اشاره می‌کند و باید پر شود (این بافر مستقیما در دسترس است). context همان پارامتر دوم متد string.Create است.

توضیحات بیشتر:
در دات نت، رشته‌ها نوع‌های ارجاعی (reference type) غیرقابل تغییر (immutable) هستند. به این معنا که هر زمانیکه ایجاد شدند، دیگر نمی‌توان محتوای آن‌ها را تغییر داد. به همین جهت است که مجبور هستیم آن‌ها را برای مثال توسط ToCharArray به یک آرایه تبدیل کنیم و سپس این آرایه‌ی قابل تغییر را ویرایش کنیم. در حین کار با رشته‌ها، این غیرقابل تغییر بودن، سبب تخصیص حافظه‌های بیش از حدی می‌شوند. اگر بخواهیم قسمتی از یک رشته را جدا و یا جایگزین کنیم و یا تعدادی رشته را با هم جمع بزنیم، نتیجه‌ی آن نیاز به یک تخصیص حافظه‌ی جدید را دارد. راه حل استاندارد مواجه شدن با این مشکل، استفاده از StringBuilder است که از یک بافر داخلی برای انجام کارهای خودش استفاده می‌کند و زمانیکه نتیجه‌ی نهایی را از آن درخواست می‌کنیم، تخصیص حافظه‌ای را برای تولید رشته‌ی حاصل انجام می‌دهد. البته این مورد نیاز به اندازه گیری دارد و ارزش StringBuilder با حجم بالایی از اطلاعات متنی مشخص می‌شود؛ وگرنه همانطور که مشاهده می‌کنید (در نتیجه‌ی نهایی بحث در ادامه)، الزاما کدهای سریعتری را به همراه نخواهد داشت.
هدف از string.Create، ایجاد رشته‌ها از داده‌های موجود است. هدف اصلی آن کاهش تخصیص‌های حافظه و کپی کردن اطلاعات است و امضای آن به صورت زیر می‌باشد:
public static string Create<TState> (int length, TState state, System.Buffers.SpanAction<char,TState> action);
مزیت این متد، عدم نیاز به یک پیش‌بافر است؛ به این معنا که مستقیما بر روی قسمتی از حافظه کار می‌کند که ارجاعی را به رشته‌ی «بازگشتی» دارد. یعنی در حالت کار با string.Create، غیرقابل تغییر بودن رشته‌ها در دات نت دیگر صادق نخواهد بود و برای تغییر آن نیازی به تخصیص بافر، کپی کردن و تخصیص حافظه‌ی نهایی برای بازگشت نتیجه نیست. پارامتر SpanAction آن، امکان دسترسی مستقیم به این ناحیه‌ی از حافظه را میسر می‌کند.
هنگام کار با این متد، chars ای که در اختیار ما قرار می‌گیرد، یک <Span<char اشاره کننده به رشته‌ی نهایی است که قرار است بازگشت داده شود (در ابتدای کار بر اساس اندازه‌ای که مشخص می‌شود، یک رشته‌ی خالی تخصیص داده می‌شود، اما بافر پر کردن آن اینبار در دسترس است و نیازی به تخصیص و کپی جداگانه‌ای را ندارد). بنابراین روش کار با این متد، پر کردن بافر درونی رشته‌ی بازگشتی (همان chars در اینجا) به صورت مستقیم است؛ کاری که با یک رشته‌ی معمولی نمی‌توان انجام داد.
State یا همان پارامتر دوم این متد، هر چیزی می‌تواند باشد. اگر نیاز است چندین رشته را در اینجا دریافت کنید تا بتوان بر اساس آن رشته‌ی نهایی را تشکیل داد، یک struct را تعریف کرده و بجای state به آن ارسال کنید. سپس این state توسط پارامتر context مربوط به SpanAction<char, string> action قابل دریافت و استفاده‌است که در این مثال، context همان data ارسالی به این متد است.

سؤال: در حین کار با string.Create، باید از پارامتر data استفاده کنیم و یا از context دریافتی؟ به نظر در مثال فوق، data و context یکی هستند. اکنون داخل action delegate مهیا که جهت ساخت رشته‌ی نهایی بکار می‌رود، باید از data استفاده کرد و یا از context؟
 return string.Create(data.Length, data, (chars, context) => {});
در اینجا اگر در داخل action delegate، ارجاعی را به data داشته باشیم، یک closure تشکیل می‌شود و در این حالت کامپایلر برای مدیریت آن، نیاز به تولید یک کلاس را در پشت صحنه خواهد داشت که خودش سبب کاهش کارآیی می‌گردد. به همین جهت متد Create، پارامتر state را به صورت معمولی دریافت می‌کند و آن‌را توسط context در اختیار delegate قرار می‌دهد تا نیازی نباشد delegate تعریف شده، یک closure را تشکیل دهد.


نتیجه‌ی نهایی بررسی کارآیی روش‌های مختلف جایگزین کردن حروف در یک رشته

کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: ReplacePerformanceTests.zip


ستون op/s در اینجا، مهم‌ترین ستون گزارش است و به معنای تعداد عملیات قابل انجام در یک ثانیه است. از 670 هزار عملیات در ثانیه با Replace معمولی، به 5 میلیون عملیات در ثانیه رسیده‌ایم که بسیار قابل توجه‌است.
همانطور که مشاهده می‌کنید، string.Create، سریعترین نگارش موجود است. در این بین نگارشی که از ToCharArray استفاده می‌کند، قابلیت انتقال بیشتری را دارد؛ از این جهت که نگارش‌های دیگر NET. هنوز دسترسی به string.Create را ندارند. همچنین نگارش کاراکتری متد Replace، از متد رشته‌ای آن سریعتر عمل کرده‌است.
مطالب
ساخت یک بلاگ ساده با Ember.js، قسمت سوم
پس از ایجاد کنترلرها، در این قسمت سعی خواهیم کرد تا آرایه‌ای ثابت از مطالب و نظرات را در سایت نمایش دهیم. همچنین امکان ویرایش اطلاعات را نیز به این آرایه‌های جاوا اسکریپتی مدل، اضافه خواهیم کرد.


تعریف مدل سمت کاربر برنامه

فایل جدید Scripts\App\store.js را اضافه کرده و محتوای آن‌را به نحو ذیل تغییر دهید:
var posts = [
  {
      id: '1',
      title: "Getting Started with Ember.js",
      body: "Bla bla bla 1."
  },
  {
      id: '2',
      title: "Routes and Templates",
      body: "Bla bla bla 2."
  },
  {
      id: '3',
      title: "Controllers",
      body: "Bla bla bla 3."
  }
];
 
var comments = [
    {
        id: '1',
        postId: '3',
        text: 'Thanks!'
    },
    {
        id: '2',
        postId: '3',
        text: 'Good to know that!'
    },
    {
        id: '3',
        postId: '1',
        text: 'Great!'
    }
];
در اینجا دو آرایه ثابت از اشیاء مطالب و نظرات را مشاهده می‌کنید.
سپس جهت استفاده از آن، تعریف مدخل آن‌را به فایل index.html، پیش از تعاریف کنترلرها اضافه خواهیم کرد:
 <script src="Scripts/App/store.js" type="text/javascript"></script>


ویرایش قالب مطالب برای نمایش لیستی از عناوین ارسالی

قالب فعلی Scripts\Templates\posts.hbs صرفا دارای یک سری عنوان درج شده به صورت مستقیم در صفحه است. اکنون قصد داریم آن‌را جهت نمایش لیستی از آرایه مطالب تغییر دهیم.


همانطور که در تصویر ملاحظه می‌کنید، با درخواست آدرس صفحه‌ی مطالب، router آن مسیریابی متناظری را یافته و سپس بر این اساس، template، کنترلر و مدلی را انتخاب می‌کند. به صورت پیش فرض، قالب و کنترلر انتخاب شده، مواردی هستند همنام با مسیریابی جاری. اما مقدار پیش فرضی برای model وجود ندارد و باید آن‌را به صورت دستی مشخص کرد.
برای این منظور فایل Scripts\Routes\posts.js را به پوشه‌ی routes با محتوای ذیل اضافه کنید:
Blogger.PostsRoute = Ember.Route.extend({
    controllerName: 'posts',
    renderTemplare: function () {
        this.render('posts');
    },
    model: function () {
        return posts;
    }
});
در اینجا صرفا جهت نمایش پیش فرض‌ها و نحوه‌ی کار یک route، دو خاصیت controllerName و renderTemplare آن نیز مقدار دهی شده‌اند. این دو خاصیت به صورت پیش فرض، همنام مسیریابی جاری مقدار دهی می‌شوند و نیازی به ذکر صریح آن‌ها نیست. اما خاصیت model یک مسیریابی است که باید دقیقا مشخص شود. در اینجا مقدار آن‌را به آرایه posts تعریف شده در فایل Scripts\App\store.js تنظیم کرده‌ایم. به این ترتیب مدل تعریف شده در اینجا، به صورت خودکار در کنترلر posts و قالب متناظر با آن، قابل استفاده خواهد بود.
همچنین اگر به خاطر داشته باشید، در پوشه‌ی کنترلرها فایل posts.js تعریف نشده‌است. اگر اینکار صورت نگیرد، ember.js به صورت خودکار کنترلر پیش فرضی را ایجاد خواهد کرد. در کل، یک قالب هیچگاه به صورت مستقیم با مدل کار نمی‌کند. این کنترلر است که مدل را در اختیار یک قالب قرار می‌دهد.
سپس مدخل تعریف این فایل را به فایل index.html، پس از تعاریف کنترلرها اضافه نمائید:
 <script src="Scripts/Routes/posts.js" type="text/javascript"></script>

اکنون فایل Scripts\Templates\posts.hbs را گشوده و به نحو ذیل، جهت نمایش عناوین مطالب، ویرایش کنید:
<h2>Emeber.js blog</h2>
<ul>
    {{#each post in model}}
    <li>{{post.title}}</li>
    {{/each}}
</ul>
در این قالب، حلقه‌ای بر روی عناصر model تشکیل شده و سپس خاصیت title هر عضو نمایش داده می‌شود.




نمایش لیست آخرین نظرات ارسالی

در ادامه قصد داریم تا آرایه comments ابتدای بحث را در صفحه‌ای جدید نمایش دهیم. بنابراین نیاز است تا ابتدا مسیریابی آن تعریف شود. بنابراین فایل Scripts\App\router.js را گشوده و مسیریابی جدید recent-comments را به آن اضافه کنید:
Blogger.Router.map(function () {
    this.resource('posts', { path: '/' });
    this.resource('about');
    this.resource('contact', function () {
        this.resource('email');
        this.resource('phone');
    });
    this.resource('recent-comments');
});
سپس جهت تعیین مدل این مسیریابی جدید نیاز است تا فایل Scripts\Routes\recent-comments.js را در پوشه‌ی routes با محتوای ذیل اضافه کرد:
Blogger.RecentCommentsRoute = Ember.Route.extend({
    model: function () {
        return comments;
    }
});
در اینجا آرایه comments بازگشتی، همان آرایه‌ای است که در ابتدای بحث در فایل Scripts\App\store.js تعریف کردیم.
همچنین نیاز است تا تعریف مدخل این فایل جدید را نیز به انتهای تعاریف مداخل فایل index.html اضافه کنیم:
 <script src="Scripts/Routes/recent-comments.js" type="text/javascript"></script>

اکنون قالب application واقع در فایل Scripts\Templates\application.hbs را جهت افزودن منوی مرتبط با این مسیریابی جدید، به نحو ذیل ویرایش خواهیم کرد:
<div class='container'>
    <nav class='navbar navbar-default' role='navigation'>
        <ul class='nav navbar-nav'>
            <li>{{#link-to 'posts'}}Posts{{/link-to}}</li>
            <li>{{#link-to 'recent-comments'}}Recent comments{{/link-to}}</li>
            <li>{{#link-to 'about'}}About{{/link-to}}</li>
            <li>{{#link-to 'contact'}}Contact{{/link-to}}</li>
        </ul>
    </nav>
    {{outlet}}
</div>
و در آخر قالب جدید Scripts\Templates\recent-comments.hbs را برای نمایش لیست آخرین نظرات، با محتوای زیر اضافه می‌کنیم:
<h1>Recent comments</h1>
<ul>
  {{#each comment in model}}
    <li>{{comment.text}}</li>
  {{/each}}
</ul>
برای فعال شدن آن نیاز است تا تعریف این قالب جدید را به template loader برنامه، در فایل index.html اضافه کنیم:
<script type="text/javascript">
    EmberHandlebarsLoader.loadTemplates([
       'posts', 'about', 'application', 'contact', 'email', 'phone',
       'recent-comments'
    ]);
</script>



نمایش مجزای هر مطلب در یک صفحه‌ی جدید

تا اینجا در صفحه‌ی اول سایت، لیست عناوین مطالب را نمایش دادیم. در ادامه نیاز است تا بتوان هر عنوان را به صفحه‌ی متناظر و اختصاصی آن لینک کرد؛ برای مثال لینکی مانند http://localhost:25918/#/posts/3 به سومین مطلب ارسالی اشاره می‌کند. Ember.js به عدد 3 در اینجا، یک dynamic segment می‌گوید. از این جهت که مقدار آن بر اساس شماره مطلب درخواستی، متفاوت خواهد بود. برای پردازش این نوع آدرس‌ها نیاز است مسیریابی ویژه‌ای را تعریف کرد. فایل Scripts\App\router.js را گشوده و سپس مسیریابی post را به نحو ذیل به آن اضافه نمائید:
Blogger.Router.map(function () {
    this.resource('posts', { path: '/' });
    this.resource('about');
    this.resource('contact', function () {
        this.resource('email');
        this.resource('phone');
    });
    this.resource('recent-comments');
    this.resource('post', { path: 'posts/:post_id' });
});
قسمت پویای مسیریابی با یک : مشخص می‌شود.
با توجه به اینکه این مسیریابی جدید post نام گرفت (جهت نمایش یک مطلب)، به صورت خودکار، کنترلر و قالبی به همین نام را بارگذاری می‌کند. همچنین مدل خود را نیز باید از مسیریابی خاص خود دریافت کند. بنابراین فایل جدید Scripts\Routes\post.js را در پوشه‌ی routes با محتوای ذیل اضافه کنید:
Blogger.PostRoute = Ember.Route.extend({
    model: function (params) {
        return posts.findBy('id', params.post_id);
    }
});
در اینجا مدل مسیریابی post بر اساس پارامتری به نام params تعیین می‌شود. این پارامتر حاوی مقدار متغیر پویای post_id که در مسیریابی جدید post مشخص کردیم می‌باشد. در ادامه از آرایه posts تعریف شده در ابتدای بحث، توسط متد findBy که توسط Ember.js اضافه شده‌است، عنصری را که خاصیت id آن مساوی post_id دریافتی است، انتخاب کرده و به عنوان مقدار مدل بازگشت می‌دهیم.
برای مثال، جهت آدرس http://localhost:25918/#/posts/3، مقدار post_id به صورت خودکار به عدد 3 تنظیم می‌شود.

پس از آن نیاز است مدخل این فایل جدید را در صفحه‌ی index.html نیز اضافه کنیم:
 <script src="Scripts/Routes/post.js" type="text/javascript"></script>

در ادامه برای نمایش اطلاعات مدل نیاز است قالب جدید Scripts\Templates\post.hbs را با محتوای زیر اضافه کنیم:
 <h1>{{title}}</h1>
<p>{{body}}</p>
و template loader صفحه‌ی index.html را نیز باید از وجود آن باخبر کرد:
<script type="text/javascript">
    EmberHandlebarsLoader.loadTemplates([
       'posts', 'about', 'application', 'contact', 'email', 'phone',
       'recent-comments', 'post'
    ]);
</script>

اکنون به قالب Scripts\Templates\posts.hbs مراجعه کرده و هر عنوان را به مطلب متناظر با آن لینک می‌کنیم:
<h2>Emeber.js blog</h2>
<ul>
    {{#each post in model}}
    <li>{{#link-to 'post' post.id}}{{post.title}}{{/link-to}}</li>
    {{/each}}
</ul>
همانطور که ملاحظه می‌کنید، link-to امکان پذیرش id یک مطلب را به صورت متغیر نیز دارا است که سبب خواهد شد تا عناوین، به مطالب متناظر لینک شوند:


همچنین با کلیک بر روی هر عنوان نیز مطلب مرتبط نمایش داده خواهد شد:



افزودن امکان ویرایش مطالب

می‌خواهیم در صفحه‌ی نمایش جزئیات یک مطلب، امکان ویرایش آن‌را نیز فراهم کنیم. بنابراین فایل Scripts\Templates\post.hbs را گشوده و محتوای آن‌را به نحو ذیل ویرایش کنید:
<h2>{{title}}</h2>
{{#if isEditing}}
<form>
    <div class="form-group">
        <label for="title">Title</label>
        {{input value=title id="title" class="form-control"}}
    </div>
    <div class="form-group">
        <label for="body">Body</label>
        {{textarea value=body id="body" class="form-control" rows="5"}}
    </div>
    <button class="btn btn-primary" {{action 'save' }}>Save</button>
</form>
{{else}}
<p>{{body}}</p>
<button class="btn btn-primary" {{action 'edit' }}>Edit</button>
{{/if}}
شبیه به این if و else را در قسمت قبل حین ایجاد صفحات about و یا contact نیز مشاهده کرده‌اید. در اینجا اگر خاصیت isEditing مساوی true باشد، فرم ویرایش اطلاعات ظاهر می‌شود و اگر خیر، محتوای مطلب جاری نمایش داده خواهد شد.
در فرم تعریف شده، المان‌های ورودی اطلاعات از handlebar helper‌های ویژه‌ی input و textarea استفاده می‌کنند؛ بجای المان‌های متداول HTML. همچنین value یکی به title و دیگری به body تنظیم شده‌است (خواص مدل ارائه شده توسط کنترلر متصل به قالب). این مقادیر نیز داخل '' قرار ندارند؛ به عبارتی در یک handlebar helper به عنوان متغیر در نظر گرفته می‌شوند. به این ترتیب اطلاعات کنترلر جاری، به این المان‌های ورودی اطلاعات به صورت خودکار bind می‌شوند و برعکس. اگر کاربر مقادیر آن‌ها را تغییر دهد، تغییرات نهایی به صورت خودکار به خواص متناظری در کنترلر جاری منعکس خواهند شد (two-way data binding).
دو دکمه نیز تعریف شده‌اند که به اکشن‌های save و edit متصل هستند.
بنابراین نیاز به یک کنترلر جدید، به نام post داریم تا بتوان رفتار قالب post را کنترل کرد. برای این منظور فایل جدید Scripts\Controllers\post.js را با محتوای ذیل ایجاد کنید:
Blogger.PostController = Ember.ObjectController.extend({
    isEditing: false,
    actions: {
        edit: function () {
            this.set('isEditing', true);
        },
        save: function () {
            this.set('isEditing', false);
        }
    }
});
همچنین مدخل تعریف آن‌را نیز به فایل index.html اضافه نمائید (پس از تعاریف کنترلرهای موجود):
 <script src="Scripts/Controllers/post.js" type="text/javascript"></script>

اگر به کدهای این کنترلر دقت کرده باشید، اینبار زیرکلاسی از ObjectController ایجاد شده‌است و نه Controller، مانند مثال‌های قبل. ObjectController تغییرات رخ داده بر روی خواص مدل را که توسط کنترلر در معرض دید قالب قرار داده‌است، به صورت خودکار به مدل مرتبط نیز منعکس می‌کند (Ember.ObjectController.extend)؛ اما Controller خیر (Ember.Controller.extend). در اینجا مدل کنترلر، تنها «یک» شیء است که بر اساس id آن انتخاب شده‌است. به همین جهت از ObjectController برای ارائه two-way data binding کمک گرفته شد.
در ember.js، یک قالب تنها با کنترلر خودش دارای تبادل اطلاعات است. اگر این کنترلر از نوع ObjectController باشد، تغییرات خاصیتی در یک قالب، ابتدا به کنترلر آن منعکس می‌شود و سپس این کنترلر، در صورت یافتن معادلی از این خاصیت در مدل، آن‌را به روز خواهد کرد. در حالت استفاده از Controller معمولی، صرفا تبادل اطلاعات بین قالب و کنترلر را شاهد خواهیم بود و نه بیشتر.

در ابتدای کار مقدار خاصیت isEditing مساوی false است. این مورد سبب می‌شود تا در بار اول بارگذاری اطلاعات یک مطلب انتخابی، صرفا عنوان و محتوای مطلب نمایش داده شوند؛ به همراه یک دکمه‌ی edit. با کلیک بر روی دکمه‌ی edit، مطابق کدهای کنترلر فوق، تنها خاصیت isEditing به true تنظیم می‌شود و در این حالت، بدنه‌ی اصلی شرط if isEditing در قالب post، رندر خواهد شد.

برای مثال در ابتدا مطلب شماره یک را انتخاب می‌کنیم:


با کلیک بر روی دکمه‌ی edit، فرم ویرایش ظاهر خواهد شد:


نکته‌ی جالب آن، مقدار دهی خودکار المان‌های ویرایش اطلاعات است. در این حالت سعی کنید، عنوان مطلب جاری را اندکی ویرایش کنید:


با ویرایش عنوان، می‌توان بلافاصله مقدار تغییر یافته را در برچسب عنوان مطلب نیز مشاهده کرد. این مورد دقیقا مفهوم two-way data binding و اتصال مقادیر value هر کدام از handlebar helper‌های ویژه‌ی input و textarea را به عناصر مدل ارائه شده توسط کنترلر post، بیان می‌کند.
در این حالت در کدهای متد save، تنها کافی است که خاصیت isEditing را به false تنظیم کنیم. زیرا کلیه مقادیر ویرایش شده توسط کاربر، در همان لحظه در برنامه منتشر شده‌اند و نیاز به کار بیشتری برای اعمال تغییرات نیست.


اضافه کردن دکمه‌ی مرتب سازی بر اساس عناوین، در صفحه‌ی اول سایت

Ember.ObjectController.extend برای data bindg یک شیء کاربرد دارد. اگر قصد داشته باشیم با آرایه‌ای از اشیاء کار کنیم می‌توان از ArrayController استفاده کرد. فرض کنید در صفحه‌ی اول سایت می‌خواهیم امکان مرتب سازی مطالب را بر اساس عنوان آن‌ها اضافه کنیم. فایل Scripts\Templates\posts.hbs را گشوده و لینک Sort by title را به انتهای آن اضافه کنید:
<h2>Emeber.js blog</h2>
<ul>
    {{#each post in model}}
    <li>{{#link-to 'post' post.id}}{{post.title}}{{/link-to}}</li>
    {{/each}}
</ul>
 
<a href="#" class="btn btn-primary" {{action 'sortByTitle'}}>Sort by title</a>
در اینجا چون قصد تغییر رفتار قالب posts را توسط اکشن جدید sortByTitle داریم، نیاز است کنترلر متناظر با آن‌را نیز اضافه کنیم. برای این منظور فایل جدید Scripts\Controllers\posts.js را به پوشه‌ی کنترلرها اضافه کنید؛ با محتوای ذیل:
Blogger.PostsController = Ember.ArrayController.extend({
    sortProperties: ['id'],// مقادیر پیش فرض مرتب سازی
    sortAscending: false,
    actions: {
        sortByTitle: function () {
            this.set('sortProperties', ['title']);
            this.set('sortAscending', !this.get('sortAscending'));
        }
    }
});
sortProperties جزو خواص کلاس پایه ArrayController است. اگر مانند سطر اول به صورت مستقیم مقدار دهی شود، خاصیت یا خواص پیش فرض مرتب سازی را مشخص می‌کند. اگر مانند اکشن sortByTitle توسط متد set مقدار دهی شود، امکان مرتب سازی تعاملی و با فرمان کاربر را فراهم خواهد کرد.

در ادامه، تعریف مدخل این کنترلر جدید را نیز باید به فایل index.html، اضافه کرد:
 <script src="Scripts/Controllers/posts.js" type="text/javascript"></script>

اگر برنامه را در این حالت اجرا کرده و بر روی دکمه‌ی Sort by title کلیک کنید، اتفاقی رخ نمی‌دهد. علت اینجا است که ArrayController خروجی تغییر یافته خودش را توسط خاصیتی به نام arrangedContent در اختیار قالب خود قرار می‌دهد. بنابراین نیاز است فایل قالب Scripts\Templates\posts.hbs را به نحو ذیل ویرایش کرد:
<h2>Emeber.js blog</h2>
<ul>
    {{#each post in arrangedContent}}
    <li>{{#link-to 'post' post.id}}{{post.title}}{{/link-to}}</li>
    {{/each}}
</ul>
 
<a href="#" class="btn btn-primary" {{action 'sortByTitle'}}>Sort by title</a>
اینبار کلیک بر روی دکمه‌ی مرتب سازی بر اساس عناوین، هربار لیست موجود را به صورت صعودی و یا نزولی مرتب می‌کند.



یک نکته: حلقه‌ی ویژه‌ای به نام each

اگر قالب Scripts\Templates\posts.hbs را به نحو ذیل، با یک حلقه‌ی each ساده بازنویسی کنید:
<h2>Ember.js blog</h2>
<ul>
    {{#each}}
    <li>{{#link-to 'post' id}}{{title}}{{/link-to}}</li>
    {{/each}}
</ul>
 
<a href="#" class="btn btn-primary" {{action 'sortByTitle'}}>Sort by title</a>
هم در حالت نمایش معمولی و هم در حالت استفاده از ArrayController برای نمایش اطلاعات مرتب شده، بدون مشکل کار می‌کند و نیازی به تغییر نخواهد داشت.



کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید:
EmberJS03_03.zip
نظرات اشتراک‌ها
بحثی در مورد تغییرات اخیر سایت Medium
اگر قصد نصب افزونه‌ای را ندارید، برای مشاهده‌ی یک صفحه در کش گوگل، بر روی نوار ابزار bookmarks مرورگر خود، راست کلیک کرده و یک bookmark جدید را با URL زیر به آن اضافه کنید:
javascript:location.href='https://webcache.googleusercontent.com/search?q=cache:'+encodeURIComponent(location.href)
پس از این اینکار، با کلیک بر روی این دکمه‌ی جدید، صفحه‌ی جاری در حال مرور را در کش گوگل مشاهده خواهید کرد و اگر محتوای صفحه‌ی باز شده قابل مشاهده نبود، حالت Text version آن‌را آزمایش کنید.
نظرات اشتراک‌ها
راه اندازی IdentityServer 4 و اتصال از طریق یک webApp Client
جهت اجرای همزمان چندین پروژه دو روش پیش رو دارید :
روش اول : پروژه ای که پیش فرض انتخاب شده است را اجرا کنید (run or ctrl+f5) و بعد برای اجرای سایر پروژه‌ها ، روی آنها کلیک راست کرده و گزینه Debug و در نهایت انتخاب گزینه start new instance . شکل زیر

روش دوم :  بر روی solution  کلیک راست کرده و propperties را انتخاب نمایید و در پنجره ظاهر شده multiple startup project رو انتخاب نمایید  و پروژه‌های مورد نظر رو انتخاب نمایید و تغییر وضعیت None به start


اشتراک‌ها
باز کردن cmd در هر جایی از ویندوز 7!
در بسیاری از موارد نیازمند باز شدن  cmd (پنچره کامند) در مسیر خاصی از ویندوز هستیم. ترفند بسیار خوبی در ویندوز 7  و ویندوز سرور 2008 (و احتمالا ویندوز ویستا) برای این منظور تعبیه شده است. چنانچه قبل از کلیک راست بر روی فایل یا پوشه مورد نظر کلید شیفت را نگه دارین، گزینه 'Open command window here' در بین گزینه‌های منوی کلیک راست ظاهر خود شد.



باز کردن cmd در هر جایی از ویندوز 7!
نظرات مطالب
Ankh-SVN-2.0.6347
Ankh هم مثل TortoiseSVN زمانیکه گزینه Ankh –> Add solution to Subversion repository را انتخاب می‌کنید (با کلیک راست روی پروژه موجود در VS.Net) و مثلا آدرس svn://localhost/trunk را ارائه می‌دهید، پس از کلیک بر روی OK ، به صورت خودکار صفحه وارد کردن نام کاربری و کلمه عبور را نمایش می‌دهد.
مطالب
ایجاد رنگ و روی سفارشی برای ادیتور Visual Studio

برنامه‌ی آنلاینی جهت تولید یک theme سفارشی جدید برای ویژوال استودیو ایجاد شده است که در آدرس زیر قابل استفاده است:


ابتدا تنظیمات رنگ خود را انتخاب کنید، سپس بر روی دکمه refresh کلیک نمائید تا نمونه‌ای از نتیجه‌ی کار را بتوان مشاهده کرد و سپس بر روی دکمه create کلیک کنید.


و برای استفاده از فایل تولید شده از طریق منوی tools گزینه‌ی Import and Export Settings اقدام کنید.


مطالب
آشنایی با NuGet - قسمت اول

NuGet چیست؟

روش متداول استفاده از کتابخانه‌های موجود دات نتی در Visual studio‌ عموما به این صورت است: مراجعه به سایت مربوطه، دریافت بسته مورد نظر، باز کردن آن و سپس افزودن ارجاعی به اسمبلی‌های آن کتابخانه. در این حالت زمانیکه نسخه‌ی جدیدی از کتابخانه‌ی مورد استفاده ارائه ‌شود (و عموما تا مدت‌ها شاید از آن بی‌اطلاع باشیم) تمام این مراحل باید از ابتدا تکرار شوند و همینطور الی آخر.
برای رفع این نقیصه، تیم ASP.NET، افزونه‌ای سورس باز و رایگان را به نام NuGet جهت VS.Net 2010 طراحی کرده‌اند که کار مدیریت بسته‌های کتابخانه‌های مورد استفاده را بسیار ساده کرده است. امکانات این افزونه پس از نصب، در دو حالت استفاده از رابط گرافیکی کاربری آن و یا با استفاده از خط فرمان PowerShell ویندوز در دسترس خواهد بود. این افزونه در زمان بارگذاری، با مراجعه به فید سایت مرکزی خود، لیست بسته‌های مهیا را در اختیار علاقمندان قرار می‌دهد. درب این سایت مرکزی به روی تمام توسعه‌ دهنده‌ها جهت افزودن بسته‌های خود باز است.
و ... فراگیری کار با NuGet برای تمام برنامه نویسان دات نت لازم و ضروری است! از این جهت که پیغام "این بسته تنها برای NuGet عرضه شده است" کم کم در حال متداول شدن می‌باشد و دیگر سایت‌های مرتبط، لینک مستقیمی را جهت دریافت کتابخانه‌های خود ارائه نمی‌دهند. حتی خبر به روز شدن محصولات خود را هم شاید دیگر به صورت منظم ارائه ندهند؛ زیرا NuGet کار مدیریت آن‌ها را به عهده خواهد داشت.


دریافت و نصب NuGet

NuGet را حداقل به سه طریق می‌توان دریافت و نصب کرد:
الف) با مراجعه به سایت CodePlex : (+)
ب) دریافت آن از سایت گالری‌های آن : (+)


ج) با استفاده از امکانات VS.NET

هر سه روش فوق به دریافت و نصب فایل NuGet.Tools.vsix منتهی می‌شوند. برای مثال در روش (ج) باید به منوی Tools و گزینه‌ی Extension Manager مراجعه کنید. سپس برگه‌ی Online Gallery را گشوده و اندکی صبر کنید تا اطلاعات آن دریافت و نمایش داده شود. سپس NuGet را در Search box بالای صفحه نوشته و NuGet Package manager ظاهر شده را انتخاب و نصب کنید.



نحوه استفاده از NuGet

فرض کنید یک پروژه جدید ASP.NET را ایجاد کرده‌اید و نیاز است تا کتابخانه‌ی ELMAH به آن اضافه شود. روش انجام اینکار را به کمک NuGet در ادامه بررسی خواهیم کرد (کمتر از یک دقیقه زمان خواهد برد):

الف) با کمک امکانات رابط گرافیکی کاربر آن
ساده‌ترین روش استفاده از NuGet ، کلیک راست بر روی پوشه References در Solution explorer و سپس انتخاب گزینه‌ی Add Library Package Reference می‌باشد:



در صفحه‌ی باز شده، برگه‌ی Online را باز کنید و مدتی صبر نمائید تا اطلاعات لازم دریافت گردد (در زمان نگارش این مطلب، 1135 بسته در این مخزن موجود است):



سپس در جعبه‌ی جستجوی سمت راست بالای صفحه، نام کتابخانه‌ی مورد نظر را نوشته و اندکی صبر کنید تا اطلاعات آن نمایش داده شود:



اکنون با کلیک بر روی دکمه Install ، بسته مرتبط با این کتابخانه دریافت شده و سپس به صورت خودکار ارجاعی به آن نیز افزوده خواهد شد. همچنین تنظیمات مرتبط با فایل Config برنامه هم اضافه می‌شوند.

روش دیگر ظاهر کردن این صفحه، مراجعه به منوی Tools و گزینه‌ی Library Package Manager می‌باشد:



جهت دریافت به روز رسانی‌های بسته‌های نصب شده تنها کافی است به برگه‌ی Updates این صفحه مراجعه کرده و موارد لیست شده را نصب نمائیم:



نکته: NuGet در SharpDevelop 4.1 به بعد هم پشتیبانی می‌شود:




ب) با استفاده از امکانات خط فرمان PowerShell ویندوز
برای استفاده از امکانات پاورشل ویندوز نیاز است تا پاورشل نگارش 2 بر روی سیستم شما نصب باشد (نیاز به Windows XP with Service Pack 3 به بعد دارد). سپس به منوی Tools ، قسمت Library Package Manager ، گزینه‌ی Package Manager Console آن جهت فعال سازی کنسول پاور شل در VS.NET مراجعه نمائید:


نکته: در تصویر فوق پس از نوشتن el ، دکمه tab فشرده شده است. در این حالت منوی پکیج‌های مهیای شروع شده با el، از سایت مرکزی NuGet ظاهر گردیده است.

فرامین مهمی که در اینجا در دسترس هستند شامل: List-Package ، Uninstall-Package ، Update-Package و Get-Package می‌باشند. برای مثال اگر قصد جستجو در بین بسته‌های موجود را داشته باشید Get-Package بسیار مفید است:



برای مثال جهت یافتن بسته‌های مرتبط با wpf و silverlight به صورت زیر می‌توان عمل کرد:
PM> get-package -remote -filter wpf
PM> get-package -remote -filter silverlight

نکته: روش دیگر جستجو در بین بسته‌های مهیا، مراجعه به سایت گالری آن است : (+) . در اینجا دستور پاورشل نصب هر بسته‌ی یافت شده نیز نمایش داده می‌شود.


ج) استفاده از برنامه NuGet.exe
برنامه NuGet.exe از سایت CodePlex قابل دریافت است. این روش هم جهت علاقمندان به خط فرمان تدارک دیده شده است!
پس از دریافت آن فرض کنید می‌خواهیم تمام بسته‌های شروع شده با nhi را لیست کنیم. برای این منظور دستور خط فرمان ذیل را صادر کنید:
D:\Test>nuget list nhi
سپس برای دریافت مثلا NHibernate تنها کافی است دستور زیر اجرا شود:
D:\Test>nuget install NHibernate

به این صورت کتابخانه NHibernate ‌به همراه تمام وابستگی‌های آن دریافت خواهد شد.

به روز رسانی خودکار NuGet
برای به روز رسانی برنامه nuget.exe دستور زیر را می‌توان صادر نمود:
D:\Test>NuGet.exe u
و یا جهت فعال سازی به روز رسانی‌های خودکار افزونه‌ها در VS.NET به منوی زیر مراجعه کنید:
Tools | Options, then Environment | Extension Manager and click "Automatically check for updates to installed extensions."





ادامه دارد ...