مطالب
آموزش زبان Rust - قسمت 8 - Rust-Based CS Masterclass
مدیریت حافظه، نقش مهمی را در برنامه نویسی ایفا می‌کند و بر عملکرد و کارآیی یک برنامه تاثیر می‌گذارد. این مقاله، مروری را بر سه نوع حافظه‌ی اصلی ارائه می‌کند:  static memory, stack memory, heap . درک تفاوت بین این انواع حافظه‌ها می‌تواند به شما در بهینه سازی کد و جلوگیری از مشکلات احتمالی، کمک کند.


Static Memory

حافظه‌ی static برای ذخیره‌ی باینری‌های برنامه، متغیرهای استاتیک و حروف رشته‌ای (در Rust) استفاده می‌شود. اندازه‌ی حافظه استاتیک ثابت است و در زمان کامپایل مشخص می‌شود. حافظه‌ی استاتیک طول عمری برابر با عمر برنامه دارد و مقادیر آن از شروع، تا پایان برنامه، باقی می‌ماند. پاکسازی حافظه‌ی استاتیک به صورت خودکار انجام می‌شود و با پایان برنامه انجام می‌شود.

مواردی که در حافظه استاتیک قرار میگیرند :
  • Program Binary
  • Static variables
  • String Literals (in Rust)

Size :
  Fixed ( محاسبه در زمان کامپایل )
Lifetime : برابر با طول عمر برنامه
پاکسازی : به صورت خودکار ؛ زمانی که برنامه متوقف میشود .


  Stack Memory

حافظه‌ی پشته، مسئول نگهداری آرگومان‌های تابع و متغیرهای محلی است. پشته، شامل stack frames است که برای هر فراخوانی تابع در زنجیره‌ای از فراخوانی‌های تابع، ایجاد می‌شوند (به عنوان مثال، A B را فرا می‌خواند، B C را فرا می‌خواند). حافظه‌ی پشته به اندازه‌ی مشخصی در زمان کامپایل نیاز دارد؛ به این معنا که آرگومان‌ها و متغیرهای درون  stack frames باید اندازه‌های از پیش تعیین شده‌ای داشته باشند. اندازه‌ی پشته، پویا است؛ اما دارای حد بالایی ثابتی است که در هنگام راه اندازی برنامه تعریف شده‌است. حافظه‌ی پشته، دارای طول عمری برابر با طول عمر عملکرد است و هنگامیکه عملکرد، نتیجه‌ای را بر می‌گرداند، پاکسازی آن خودکار است.  

بیایید نگاهی به یک مثال ساده در Rust بیندازیم تا حافظه‌ی پشته را بهتر درک کنیم:
fn add(x: i32, y: i32) -> i32 {
    let sum = x + y;
    sum
}

fn main() {
    let a = 5;
    let b = 3;
    let result = add(a, b);
    println!("The sum is: {}", result);
}
در این برنامه‌ی Rust، دو عملکرد add و main را داریم. هنگامیکه برنامه شروع به اجرا می‌کند، یک stack frames برای تابع اصلی در حافظه‌ی پشته ایجاد می‌شود. این  stack frames شامل متغیرهای محلی a، b و فراخوانی تابع برای add(a, b) است.
هنگامیکه تابع add فراخوانی می‌شود، یک stack frames دیگر در بالای stack frames main موجود ایجاد می‌شود. این stack frames جدید حاوی متغیرهای محلی x، y و sum است. مقادیر a و b به عنوان آرگومان به تابع add ارسال می‌شوند و به ترتیب در x و y ذخیره می‌شوند. پس از محاسبه‌ی مجموع، تابع add، مقداری را بر می‌گرداند و  stack frames آن به طور خودکار از حافظه‌ی پشته حذف می‌شود.
سپس تابع main، مقدار برگشتی را از تابع add دریافت می‌کند و به نتیجه‌ی متغیر اختصاص می‌یابد. از ماکروی println! برای چاپ نتیجه استفاده می‌شود. پس از اتمام اجرای برنامه و بازگشت تابع اصلی، stack frames آن نیز از حافظه‌ی پشته حذف می‌شود و حافظه به‌طور خودکار پاک می‌شود.
در این مثال، می‌توانید ببینید که چگونه از stack frames برای ذخیره‌ی آرگومان‌های تابع و متغیرهای محلی در Rust استفاده می‌شود. اندازه‌ی این متغیرها در زمان کامپایل مشخص می‌شود و طول عمر حافظه‌ی پشته، برابر با طول عمر تابع است. هنگامیکه تابع برمی‌گردد، فرآیند پاکسازی آن خودکار است و قاب پشته‌ی مربوطه را حذف می‌کند.


Heap Memory

حافظه‌ی Heap، مقادیری را ذخیره می‌کند که باید فراتر از طول عمر یک تابع مانند مقادیر بزرگ و مقادیر قابل دسترسی توسط رشته‌های متعدد، زنده بمانند. از آنجائیکه هر رشته دارای پشته‌ی مخصوص به خود است، همه‌ی آنها یک پشته‌ی مشترک دارند. حافظه‌ی Heap می‌تواند مقادیری با اندازه‌ی ناشناخته را در زمان کامپایل، در خود جای دهد؛ مانند رشته‌های ورودی کاربر. اندازه‌ی پشته نیز پویا است؛ با حد بالایی ثابت که در زمان راه اندازی برنامه تعیین می‌شود. حافظه‌ی Heap طول عمری دارد که توسط برنامه نویس تعیین می‌شود و برنامه نویس تصمیم می‌گیرد که چه زمانی باید حافظه تخصیص داده شود. پاکسازی حافظه‌ی هیپ به صورت دستی است و نیاز به مداخله‌ی برنامه نویس دارد.
در این مثال ساده، روش استفاده از حافظه‌ی پشته نشان داده می‌شود:
use std::rc::Rc;

#[derive(Debug)]
struct LargeData {
    data: Vec<i32>,
}

impl LargeData {
    fn new(size: usize) -> LargeData {
        LargeData {
            data: vec![0; size],
        }
    }
}

fn main() {
    let large_data = Rc::new(LargeData::new(1_000_000));
    let shared_data1 = Rc::clone(&large_data);
    let shared_data2 = Rc::clone(&large_data);

    println!("{:?}", shared_data1);
    println!("{:?}", shared_data2);
}
در این برنامه‌ی Rust، یک ساختار LargeData را تعریف می‌کنیم که حاوی <Vec<i32 است. این روش جدید، یک شیء LargeData را به اندازه‌ی مشخصی مقداردهی اولیه می‌کند. در تابع main، یک شیء LargeData را با اندازه (1,000,000 عنصر) ایجاد می‌کنیم و با استفاده از Rc::new روی پشته ذخیره می‌کنیم. Rc یک اشاره‌گر شمارش مرجع است که به چندین متغیر اجازه می‌دهد تا مالکیت داده‌های تخصیص داده شده را به اشتراک بگذارند (در ادامه‌ی دوره توضیح داده خواهد شد).  
سپس دو متغیر دیگر را به نام‌های shared_data1 و shared_data2 ایجاد می‌کنیم که با استفاده از Rc::clone، یک شیء LargeData تخصیص‌یافته‌ی مشابه را به اشتراک می‌گذارند. این نشان می‌دهد که چگونه حافظه‌ی پشته را می‌توان در بین متغیرهای متعددی به اشتراک گذاشت؛ حتی فراتر از طول عمر تابع اصلی که داده را ایجاد کرده است.
در این مثال، پاکسازی حافظه‌ی پشته به طور خودکار توسط مکانیزم شمارش مرجع Rust مدیریت می‌شود (در ادامه‌ی دوره توضیح داده خواهد شد). هنگامیکه تعداد مرجع نشانگر Rc به صفر می‌رسد (یعنی وقتی همه‌ی متغیرهایی که داده‌ها را به اشتراک می‌گذارند از محدوده خارج می‌شوند)، حافظه‌ی تخصیص داده شده، روی پشته تخصیص داده می‌شود.
این مثال نشان می‌دهد که چگونه می‌توان از حافظه‌ی پشته برای ذخیره‌ی ساختارهای داده یا مقادیر بزرگی استفاده کرد که باید بیشتر از طول عمر یک تابع باشند و چگونه می‌توان حافظه‌ی پشته را بین چندین متغیر به اشتراک گذاشت.
نظرات مطالب
هزینه استفاده از دات نت فریم ورک چقدر است؟
البته که در مورد MS-PL و باز بودن آن کاملاً حق با شماست. اما سورس دات نت (حتی BCL) تحت این مجوز ارائه نشده (در مورد تکنولوژی‌هایی مثل LINQ و WCF و ... حتی هنوز استاندارد نشده که بتوان پیاده‌سازی قانونی از آنها داشت).

مجوز دات نت در ویکیپدیا "MS-EULA, BCL under Microsoft Reference Source License" عنوان شده.

 به همین خاطر برنامه‌نویسان مونو حق دیدن سورس‌های مایکروسافت را ندارد.

خلاصه مجوز MS-RSL:
http://en.wikipedia.org/wiki/Shared_source#Microsoft_Reference_Source_License_.28Ms-RSL.29

و توضیحات کاملتر درباره استاندارد شدن دات نت:
http://en.wikipedia.org/wiki/.NET_Framework#Standardization_and_licensing
نظرات مطالب
هزینه استفاده از دات نت فریم ورک چقدر است؟
البته که در مورد MS-PL و باز بودن آن کاملاً حق با شماست. اما سورس دات نت (حتی BCL) تحت این مجوز ارائه نشده (در مورد تکنولوژی‌هایی مثل LINQ و WCF و ... حتی هنوز استاندارد نشده که بتوان پیاده‌سازی قانونی از آنها داشت).

مجوز دات نت در ویکیپدیا "MS-EULA, BCL under Microsoft Reference Source License" عنوان شده.

 به همین خاطر برنامه‌نویسان مونو حق دیدن سورس‌های مایکروسافت را ندارد.

خلاصه مجوز MS-RSL:
http://en.wikipedia.org/wiki/Shared_source#Microsoft_Reference_Source_License_.28Ms-RSL.29

و توضیحات کاملتر درباره استاندارد شدن دات نت:
http://en.wikipedia.org/wiki/.NET_Framework#Standardization_and_licensing
نظرات مطالب
خلاصه‌ای کوتاه در مورد WinRT
جناب نصیری از مطلب کامل، مختصر و مفید شما ممنونم.
منم نظر خودمون رو اینجا عنوان کنم... بسیاری از مسائل از تحلیل و برداشت غلط نشئت می گیرند... تصور اینکه مایکروسافت بخواهد دات نت فریم ورک و یا زبان های دات نتی رو جمع کنه در حالیکه باید ده ها سال ازین تکنولوژی پشتیبانی ارائه کنه خیلی سخت و بعید به نظر می رسد. WinRT همانگونه که عنوان شد فقط بستر طراحی اپلیکیشن های کلاینت مبتنی بر واسط کاربری مترو بوده و هیچ ارتباطی با دات نت فریم ورک ندارد. در بحث سمت سرور دات نت فریم ورک و مباحث مربتط با آن نه تنها تضعیف نشده بلکه حرف اصلی را می زنند و پررنگتر از قبل نیز خواهند بود. این همه ورژن جدید در کنفرانیس build ارائه نشد که با ویندوز 8 جمع شه... مقاله ای در خصوص ویژگی های جدید سی شارپ رو اخیرا منتشر کردم که مطالعه ی آن نیز می تواند برای درک مطالب جدید مفید باشد.

http://www.persiadevelopers.com/articles/cs5-after-build.aspx
نظرات مطالب
بررسی بهبودهای پروسه‌ی Build در دات‌نت 8

یک نکته‌ی تکمیلی: تعدیل خطاهای بررسی امنیتی بسته‌های نیوگت در حالت کار offline در دات‌نت 8

اگر در پروژه‌ی خود، تنظیم گزارش اخطارها را به صورت خطا، فعال کرده باشید:

<PropertyGroup>
   <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

و ... از دات‌نت 8 هم استفاده می‌کنید، هربار با صدور فرمان dotnet build و یا dotnet restore، با خطای زیر مواجه خواهید شد:

warning NU1900: Error occurred while getting package vulnerability data: (more information)

البته یکبار که اطلاعات امنیتی بسته‌ها ذخیره شدند، ممکن است در طول یک روز دیگر شاهد این خطا نباشید، اما ... دوباره فردا تکرار خواهد شد و اگر بخواهید offline کار کنید، این خطا واقعا مشکل ساز می‌شود!

برای کنترل آن یا می‌توان به صورت زیر عمل کرد:

<PropertyGroup>
    <NuGetAudit>false</NuGetAudit>
</PropertyGroup>

که بررسی امنیتی بسته‌های نیوگت را کاملا غیرفعال می‌کند و یا می‌توان به صورت زیر، این بررسی را فقط به حالت Release خلاصه کرد:

<PropertyGroup>
  <NuGetAudit>true</NuGetAudit>
  <NuGetAuditMode>all</NuGetAuditMode>
  <NuGetAuditLevel>low</NuGetAuditLevel>
  <WarningsNotAsErrors Condition="'$(Configuration)' != 'Release'">
    $(WarningsNotAsErrors);NU1900;NU1901;NU1902;NU1903;NU1904
  </WarningsNotAsErrors>
</PropertyGroup>

در این حالت هرچند اخطارهای NU1900 و دردسترس نبودن اینترنت ظاهر می‌شوند، اما دیگر به‌عنوان خطا پردازش نخواهند شد (چون در قسمت WarningsNotAsErrors ذکر شده‌اند) و پروسه‌ی build را متوقف نمی‌کنند.

مطالب
استفاده از نگارش سوم Google Analytics API در سرویس‌های ویندوز یا برنامه‌های وب
در زمان نگارش این مطلب، آخرین نگارش API مخصوص Google Analytics، نگارش سوم آن است و ... کار کردن با آن دارای مراحل خاصی است که حتما باید رعایت شوند. در غیر اینصورت عملا در یک برنامه‌ی وب یا سرویس ویندوز قابل اجرا نخواهند بود. زیرا در حالت متداول کار با API مخصوص Google Analytics، ابتدا یک صفحه‌ی لاگین به Gmail باز می‌شود که باید به صورت اجباری، مراحل آن را انجام داد تا مشخصات تائید شده‌ی اکانت در حال استفاده‌ی از API، در پوشه‌ی AppData ویندوز برای استفاده‌های بعدی ذخیره شود. این مورد برای یک برنامه‌ی دسکتاپ معمولی مشکل ساز نیست؛ زیرا کاربر برنامه، به سادگی می‌تواند صفحه‌ی مرورگری را که باز شده‌است، دنبال کرده و به اکانت گوگل خود وارد شود. اما این مراحل را نمی‌توان در یک برنامه‌ی وب یا سرویس ویندوز پیگیری کرد، زیرا عموما امکان لاگین از راه دور به سرور و مدیریت صفحه‌ی لاگین به Gmail وجود ندارد یا بهتر است عنوان شود، بی‌معنا است. برای حل این مشکل، گوگل راه حل دیگری را تحت عنوان اکانت‌های سرویس، ارائه داده است که پس از ایجاد آن، یک یک فایل X509 Certificate برای اعتبارسنجی سرویس، در اختیار برنامه نویس قرار می‌گیرد تا بدون نیاز به لاگین دستی به Gmail، بتواند از API گوگل استفاده کند. در ادامه نحوه‌ی فعال سازی این قابلیت و استفاده از آن‌را بررسی خواهیم کرد.


ثبت برنامه‌ی خود در گوگل و انجام تنظیمات آن

اولین کاری که برای استفاده از نگارش سوم Google Analytics API باید صورت گیرد، ثبت برنامه‌ی خود در Google Developer Console است. برای این منظور ابتدا به آدرس ذیل وارد شوید:
سپس بر روی دکمه‌ی Create project کلیک کنید. نام دلخواهی را وارد کرده و در ادامه بر روی دکمه‌ی Create کلیک نمائید تا پروفایل این پروژه ایجاد شود.



تنها نکته‌ی مهم این قسمت، بخاطر سپردن نام پروژه است. زیرا از آن جهت اتصال به API گوگل استفاده خواهد شد.
پس از ایجاد پروژه، به صفحه‌ی آن وارد شوید و از منوی سمت چپ صفحه، گزینه‌ی Credentials را انتخاب کنید. در ادامه در صفحه‌ی باز شده، بر روی دکمه‌ی Create new client id کلیک نمائید.


در صفحه‌ی باز شده، گزینه‌ی Service account را انتخاب کنید. اگر سایر گزینه‌ها را انتخاب نمائید، کاربری که قرار است از API استفاده کند، باید بتواند توسط مرورگر نصب شده‌ی بر روی کامپیوتر اجرا کننده‌ی برنامه، یکبار به گوگل لاگین نماید که این مورد مطلوب برنامه‌های وب و همچنین سرویس‌ها نیست.


در اینجا ابتدا یک فایل مجوز p12 را به صورت خودکار دریافت خواهید کرد و همچنین پس از ایجاد client id، نیاز است، ایمیل آن‌را جایی یادداشت نمائید:


از این ایمیل و همچنین فایل p12 ارائه شده، جهت لاگین به سرور استفاده خواهد شد.
همچنین نیاز است تا به برگه‌ی APIs پروژه‌ی ایجاد شده رجوع کرد و گزینه‌ی Analytics API آن‌را فعال نمود:


تا اینجا کار ثبت و فعال سازی برنامه‌ی خود در گوگل به پایان می‌رسد.


دادن دسترسی به Client ID ثبت شده در برنامه‌ی Google Analytics

پس از اینکه Client ID سرویس خود را ثبت کردید، نیاز است به اکانت Google Analytics خود وارد شوید. سپس در منوی آن، گزینه‌ی Admin را پیدا کرده و به آن قسمت، وارد شوید:


در ادامه به گزینه‌ی User management آن وارد شده و به ایمیل Client ID ایجاد شده در قسمت قبل، دسترسی خواندن و آنالیز را اعطاء کنید:


در صورت عدم رعایت این مساله، کلاینت API، قادر به دسترسی به Google Analytics نخواهد بود.


استفاده از نگارش سوم Google Analytics API در دات نت

قسمت مهم کار، تنظیمات فوق است که در صورت عدم رعایت آن‌ها، شاید نصف روزی را مشغول به دیباگ برنامه شوید. در ادامه نیاز است پیشنیازهای دسترسی به نگارش سوم Google Analytics API را نصب کنیم. برای این منظور، سه بسته‌ی نیوگت ذیل را توسط کنسول پاورشل نیوگت، به برنامه اضافه کنید:
 PM> Install-Package Google.Apis
PM> Install-Package Google.Apis.auth
PM> Install-Package Google.Apis.Analytics.v3
پس از نصب، کلاس GoogleAnalyticsApiV3 زیر، جزئیات دسترسی به Google Analytics API را کپسوله می‌کند:
using System;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Analytics.v3;
using Google.Apis.Analytics.v3.Data;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;

namespace GoogleAnalyticsAPIv3Tests
{
    public class AnalyticsQueryParameters
    {
        public DateTime Start { set; get; }
        public DateTime End { set; get; } 
        public string Dimensions { set; get; } 
        public string Filters { set; get; }
        public string Metrics { set; get; }
    }

    public class AnalyticsAuthentication
    {
        public Uri SiteUrl { set; get; }
        public string ApplicationName { set; get; }
        public string ServiceAccountEmail { set; get; }
        public string KeyFilePath { set; get; }
        public string KeyFilePassword { set; get; }

        public AnalyticsAuthentication()
        {
            KeyFilePassword = "notasecret";
        }
    }

    public class GoogleAnalyticsApiV3
    {
        public AnalyticsAuthentication Authentication { set; get; }
        public AnalyticsQueryParameters QueryParameters { set; get; }

        public GaData GetData()
        {
            var service = createAnalyticsService();
            var profile = getProfile(service);
            var query = service.Data.Ga.Get("ga:" + profile.Id,
                                QueryParameters.Start.ToString("yyyy-MM-dd"),
                                QueryParameters.End.ToString("yyyy-MM-dd"),
                                QueryParameters.Metrics);
            query.Dimensions = QueryParameters.Dimensions;
            query.Filters = QueryParameters.Filters;
            query.SamplingLevel = DataResource.GaResource.GetRequest.SamplingLevelEnum.HIGHERPRECISION;
            return query.Execute();
        }

        private AnalyticsService createAnalyticsService()
        {
            var certificate = new X509Certificate2(Authentication.KeyFilePath, Authentication.KeyFilePassword, X509KeyStorageFlags.Exportable);
            var credential = new ServiceAccountCredential(
                new ServiceAccountCredential.Initializer(Authentication.ServiceAccountEmail)
                {
                    Scopes = new[] { AnalyticsService.Scope.AnalyticsReadonly }
                }.FromCertificate(certificate));

            return new AnalyticsService(new BaseClientService.Initializer
            {
                HttpClientInitializer = credential,
                ApplicationName = Authentication.ApplicationName
            });
        }

        private Profile getProfile(AnalyticsService service)
        {
            var accountListRequest = service.Management.Accounts.List();
            var accountList = accountListRequest.Execute();
            var site = Authentication.SiteUrl.Host.ToLowerInvariant();
            var account = accountList.Items.FirstOrDefault(x => x.Name.ToLowerInvariant().Contains(site));
            var webPropertyListRequest = service.Management.Webproperties.List(account.Id);
            var webPropertyList = webPropertyListRequest.Execute();
            var sitePropertyList = webPropertyList.Items.FirstOrDefault(a => a.Name.ToLowerInvariant().Contains(site));
            var profileListRequest = service.Management.Profiles.List(account.Id, sitePropertyList.Id);
            var profileList = profileListRequest.Execute();
            return profileList.Items.FirstOrDefault(a => a.Name.ToLowerInvariant().Contains(site));
        }
    }
}
در اینجا در ابتدا بر اساس فایل p12 ایی که از گوگل دریافت شد، یک X509Certificate2 ایجاد می‌شود. پسورد این فایل مساوی است با ثابت notasecret که در همان زمان تولید اکانت سرویس در گوگل، لحظه‌ای در صفحه نمایش داده خواهد شد. به کمک آن و همچنین ServiceAccountEmail ایمیلی که پیشتر به آن اشاره شد، می‌توان به AnalyticsService لاگین کرد. به این ترتیب به صورت خودکار می‌توان شماره پروفایل اکانت سایت خود را یافت و از آن در حین فراخوانی service.Data.Ga.Get استفاده کرد.


مثالی از نحوه استفاده از کلاس GoogleAnalyticsApiV3

در ادامه یک برنامه‌ی کنسول را ملاحظه می‌کنید که از کلاس GoogleAnalyticsApiV3 استفاده می‌کند:
using System;
using System.Collections.Generic;
using System.Linq;

namespace GoogleAnalyticsAPIv3Tests
{
    class Program
    {

        static void Main(string[] args)
        {
            var statistics = new GoogleAnalyticsApiV3
            {
                Authentication = new AnalyticsAuthentication
                {
                    ApplicationName = "My Project",
                    KeyFilePath = "811e1d9976cd516b55-privatekey.p12",
                    ServiceAccountEmail = "10152bng4j3mq@developer.gserviceaccount.com",
                    SiteUrl = new Uri("https://www.dntips.ir/")
                },
                QueryParameters = new AnalyticsQueryParameters
                {
                    Start = DateTime.Now.AddDays(-7),
                    End = DateTime.Now,
                    Dimensions = "ga:date",
                    Filters = null,
                    Metrics = "ga:users,ga:sessions,ga:pageviews"
                }
            }.GetData();


            foreach (var result in statistics.TotalsForAllResults)
            {
                Console.WriteLine(result.Key + " -> total:" + result.Value);
            }
            Console.WriteLine();

            foreach (var row in statistics.ColumnHeaders)
            {
                Console.Write(row.Name + "\t");
            }
            Console.WriteLine();

            foreach (var row in statistics.Rows)
            {
                var rowItems = (List<string>)row;
                Console.WriteLine(rowItems.Aggregate((s1, s2) => s1 + "\t" + s2));
            }

            Console.ReadLine();
        }
    }
}

چند نکته
ApplicationName همان نام پروژه‌ای است که ابتدای کار، در گوگل ایجاد کردیم.
KeyFilePath مسیر فایل مجوز p12 ایی است که گوگل در حین ایجاد اکانت سرویس، در اختیار ما قرار می‌دهد.
ServiceAccountEmail آدرس ایمیل اکانت سرویس است که در قسمت ادمین Google Analytics به آن دسترسی دادیم.
SiteUrl آدرس سایت شما است که هم اکنون در Google Analytics دارای یک اکانت و پروفایل ثبت شده‌است.
توسط AnalyticsQueryParameters می‌توان نحوه‌ی کوئری گرفتن از Google Analytics را مشخص کرد. تاریخ شروع و پایان گزارش گیری در آن مشخص هستند. در مورد پارامترهایی مانند Dimensions و Metrics بهتر است به مرجع کامل آن در گوگل مراجعه نمائید:
Dimensions & Metrics Reference

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


برای مطالعه بیشتر
Using Google APIs in Windows Store Apps
How To Use Google Analytics From C# With OAuth
Google Analytic’s API v3 with C#
.NET Library for Accessing and Querying Google Analytics V3 via Service Account
Google OAuth2 C#
مطالب دوره‌ها
نگاهی به SignalR Clients
در قسمت قبل موفق به ایجاد اولین Hub خود شدیم. در ادامه، برای تکمیل برنامه نیاز است تا کلاینتی را نیز برای آن تهیه کنیم.
مصرف کنندگان یک Hub می‌توانند انواع و اقسام برنامه‌های کلاینت مانند jQuery Clients و یا حتی یک برنامه کنسول ساده باشند و همچنین Hubهای دیگر نیز قابلیت استفاده از این امکانات Hubهای موجود را دارند. تیم SignalR امکان استفاده از Hubهای آن‌را در برنامه‌های دات نت 4 به بعد، برنامه‌های WinRT، ویندوز فون 8، سیلورلایت 5، jQuery و همچنین برنامه‌های CPP نیز مهیا کرده‌اند. به علاوه گروه‌های مختلف نیز با توجه به سورس باز بودن این مجموعه، کلاینت‌های iOS Native، iOS via Mono و Android via Mono را نیز به این لیست اضافه کرده‌اند.


بررسی کلاینت‌های jQuery

با توجه به پروتکل مبتنی بر JSON سیگنال‌آر، استفاده از آن در کتابخانه‌های جاوا اسکریپتی همانند jQuery نیز به سادگی مهیا است. برای نصب آن نیاز است در کنسول پاور شل نوگت، دستور زیر را صادر کنید:
 PM> Install-Package Microsoft.AspNet.SignalR.JS
برای نمونه به solution پروژه قبل، یک برنامه وب خالی دیگر را اضافه کرده و سپس دستور فوق را بر روی آن اجرا نمائید. در این حالت فقط باید دقت داشت که فرامین بر روی کدام پروژه اجرا می‌شوند:


با استفاده از افزونه SignalR jQuery، به دو طریق می‌توان به یک Hub اتصال برقرار کرد:
الف) استفاده از فایل proxy تولید شده آن (این فایل، در زمان اجرای برنامه تولید می‌شود و یا امکان استفاده از آن به کمک ابزارهای کمکی نیز وجود دارد)
نمونه‌ای از آن‌را در قسمت قبل ملاحظه کردید؛ همان فایل تولید شده در مسیر /signalr/hubs برنامه. به نوعی به آن Service contract نیز گفته می‌شود (ارائه متادیتا و قراردادهای کار با یک سرویس Hub). این فایل همانطور که عنوان شد به صورت پویا در زمان اجرای برنامه ایجاد می‌شود.
امکان تولید آن توسط برنامه کمکی signalr.exe نیز وجود دارد؛ برای دریافت آن می‌توان از طریق NuGet اقدام کرد (بسته Microsoft.AspNet.SignalR.Utils) که نهایتا در پوشه packages قرار خواهد گرفت. نحوه استفاده از آن نیز به صورت زیر است:
 Signalr.exe ghp http://localhost/
در این دستور ghp مخفف generate hub proxy است و نهایتا فایلی را به نام server.js تولید می‌کند.

ب) بدون استفاده از فایل proxy و به کمک روش late binding (انقیاد دیر هنگام)

برای کار با یک Hub از طریق jQuery مراحل ذیل باید طی شوند:
1) ارجاعی به Hub باید مشخص شود.
2) روال‌های رخدادگردان تنظیم گردند.
3) اتصال به Hub برقرار گردد.
4) متدی فراخوانی شود.

در اینجا باید دقت داشت که امکانات Hub به صورت خواص
 $.connection
در سمت کلاینت جی‌کوئری، در دسترس خواهند بود. برای مثال:
 $.connection.chatHub
و نام‌های بکارگرفته شده در اینجا مطابق روش‌های متداول نام گذاری در جاوا اسکریپت، camel case هستند.

خوب، تا اینجا فرض بر این است که یک پروژه خالی ASP.NET را آغاز و سپس فرمان نصب Microsoft.AspNet.SignalR.JS را نیز همانطور که عنوان شد، صادر کرده‌اید. در ادامه یک فایل ساده html را به نام chat.htm، به این پروژه جدید اضافه کنید (برای استفاده از کتابخانه جاوا اسکریپتی SignalR الزامی به استفاده از صفحات کامل پروژه‌های وب نیست).
<!DOCTYPE>
<html>
<head>
    <title></title>
    <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.signalR-1.0.1.min.js" type="text/javascript"></script>
    <script src="http://localhost:1072/signalr/hubs" type="text/javascript"></script>
</head>
<body>
    <div>
        <input id="txtMsg" type="text" /><input id="send" type="button" value="send msg" />
        <ul id="messages">
        </ul>
    </div>
    <script type="text/javascript">
        $(function () {
            var chat;
            $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ می‌کند
            chat = $.connection.chat; //این نام مستعار پیشتر توسط ویژگی نام هاب تنظیم شده است
            chat.client.hello = function (message) {
                //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است
                //به این ترتیب سرور می‌تواند کلاینت را فراخوانی کند
                $("#messages").append("<li>" + message + "</li>");
            };
            $.connection.hub.start(/*{ transport: 'longPolling' }*/); // فاز اولیه ارتباط را آغاز می‌کند

            $("#send").click(function () {
                // Hub's `SendMessage` should be camel case here
                chat.server.sendMessage($("#txtMsg").val());
            });
        });
    </script>
</body>
</html>
کدهای آن‌را به نحو فوق تغییر دهید.
توضیحات:
همانطور که ملاحظه می‌کنید ابتدا ارجاعاتی به jquery و jquery.signalR-1.0.1.min.js اضافه شده‌اند. سپس نیاز است مسیر دقیق فایل پروکسی هاب خود را نیز مشخص کنیم. اینکار با تعریف مسیر signalr/hubs انجام شده است.
<script src="http://localhost:1072/signalr/hubs" type="text/javascript"></script>
در ادامه توسط تنظیم connection.hub.logging سبب خواهیم شد تا اطلاعات بیشتری در javascript console مرورگر لاگ شود.
سپس ارجاعی به هاب تعریف شده، تعریف گردیده است. اگر از قسمت قبل به خاطر داشته باشید، توسط ویژگی HubName، نام chat را برگزیدیم. بنابراین connection.chat ذکر شده دقیقا به این هاب اشاره می‌کند.
سپس سطر chat.client.hello مقدار دهی شده است. متد hello، متدی dynamic و تعریف شده در سمت هاب برنامه است. به این ترتیب می‌توان به پیام‌های رسیده از طرف سرور گوش فرا داد. در اینجا، این پیام‌ها، به li ایی با id مساوی messages اضافه می‌شوند.
سپس توسط فراخوانی متد connection.hub.start، فاز negotiation شروع می‌شود. در اینجا حتی می‌توان نوع transport را نیز صریحا انتخاب کرد که نمونه‌ای از آن را به صورت کامنت شده جهت آشنایی با نحوه تعریف آن مشاهده می‌کنید. مقادیر قابل استفاده در آن به شرح زیر هستند:
 - webSockets
- forverFrame
- serverSentEvents
- longPolling
سپس به رویدادهای کلیک دکمه send گوش فرا داده و در این حین، اطلاعات TextBox ایی با id مساوی txtMsg را به متد SendMessage هاب خود ارسال می‌کنیم. همانطور که پیشتر نیز عنوان شد، در سمت کلاینت، تعریف متد SendMessage باید camel case باشد.

اکنون به صورت جداگانه یکبار برنامه hub را در مرورگر باز کنید. سپس بر روی فایل chat.htm کلیک راست کرده و گزینه مشاهده آن را در مرورگر نیز انتخاب نمائید (گزینه View in browser منوی کلیک راست).

خوب! پروژه کار نمی‌کند! برای اینکه مشکلات را بهتر بتوانید مشاهده کنید نیاز است به JavaScript Console مرورگر خود مراجعه نمائید. برای مثال در مرورگر کروم دکمه F12 را فشرده و برگه Console آن‌را باز کنید. در اینجا اعلام می‌کند که فاز negotiation قابل انجام نیست؛ چون مسیر پیش فرضی را که انتخاب کرده است، همین مسیر پروژه دومی است که اضافه کرده‌ایم (کلاینت ما در پروژه دوم قرار دارد و نه در همان پروژه اول هاب).
برای اینکه مسیر دقیق hub را در این حالت مشخص کنیم، سطر زیر را به ابتدای کدهای جاوا اسکریپتی فوق اضافه نمائید:
 $.connection.hub.url = 'http://localhost:1072/signalr'; //چون در یک پروژه دیگر قرار داریم
اکنون اگر مجدا سعی کنید، باز هم برنامه کار نمی‌کند و پیام می‌دهد که امکان دسترسی به این سرویس از خارج از دومین آن میسر نیست. برای اینکه این مجوز را صادر کنیم نیاز است تنظیمات مسیریابی پروژه هاب را به نحو ذیل ویرایش نمائیم:
using System;
using System.Web;
using System.Web.Routing;
using Microsoft.AspNet.SignalR;

namespace SignalR02
{
    public class Global : HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            // Register the default hubs route: ~/signalr
            RouteTable.Routes.MapHubs(new HubConfiguration
            {
                EnableCrossDomain = true
            });
        }
    }
}
با تنظیم EnableCrossDomain به true اینبار فاز‌های آغاز ارتباط با سرور برقرار می‌شوند:
 SignalR: Auto detected cross domain url. jquery.signalR-1.0.1.min.js:10
SignalR: Negotiating with 'http://localhost:1072/signalr/negotiate'. jquery.signalR-1.0.1.min.js:10
SignalR: SignalR: Initializing long polling connection with server. jquery.signalR-1.0.1.min.js:10
SignalR: Attempting to connect to 'http://localhost:1072/signalr/connect?transport=longPolling&connectionToken…NRh72omzsPkKqhKw2&connectionData=%5B%7B%22name%22%3A%22chat%22%7D%5D&tid=3' using longPolling. jquery.signalR-1.0.1.min.js:10
SignalR: Longpolling connected jquery.signalR-1.0.1.min.js:10
مطابق این لاگ‌ها ابتدا فاز negotiation انجام می‌شود. سپس حالت long polling را به صورت خودکار انتخاب می‌کند.

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

یک نکته:
اگر از ویندوز 8 (یعنی IIS8) و VS 2012 استفاده می‌کنید، برای استفاده از حالت Web socket، ابتدا فایل وب کانفیگ برنامه را باز کرده و در قسمت httpRunTime، مقدار ویژگی targetFramework را بر روی 4.5 تنظیم کنید. اینبار اگر مراحل negotiation را بررسی کنید در همان مرحله اول برقراری اتصال، از روش Web socket استفاده گردیده است.


تمرین 1
به پروژه ساده و ابتدایی فوق یک تکست باکس دیگر به نام Room را اضافه کنید؛ به همراه دکمه join. سپس نکات قسمت قبل را در مورد الحاق به یک گروه و سپس ارسال پیام به اعضای گروه را پیاده سازی نمائید. (تمام نکات آن با مطلب فوق پوشش داده شده است و در اینجا باید صرفا فراخوانی متدهای عمومی دیگری در سمت هاب، صورت گیرد)

تمرین 2
در انتهای قسمت دوم به نحوه ارسال پیام از یک هاب به هابی دیگر اشاره شد. این MonitorHub را ایجاد کرده و همچنین یک کلاینت جاوا اسکریپتی را نیز برای آن تهیه کنید تا بتوان اتصال و قطع اتصال کلیه کاربران سیستم را مانیتور و مشاهده کرد.


پیاده سازی کلاینت jQuery بدون استفاده از کلاس Proxy

در مثال قبل، از پروکسی پویای مهیای در آدرس signalr/hubs استفاده کردیم. در اینجا قصد داریم، بدون استفاده از آن نیز کار برپایی کلاینت را بررسی کنیم.
بنابراین یک فایل جدید html را مثلا به نام chat_np.html به پروژه دوم برنامه اضافه کنید. سپس محتویات آن‌را به نحو زیر تغییر دهید:
<!DOCTYPE>
<html>
<head>
    <title></title>
    <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.signalR-1.0.1.min.js" type="text/javascript"></script>
</head>
<body>
    <div>
        <input id="txtMsg" type="text" /><input id="send" type="button" value="send msg" />
        <ul id="messages">
        </ul>
    </div>
    <script type="text/javascript">
        $(function () {                        
            $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ می‌کند

            var connection = $.hubConnection();
            connection.url = 'http://localhost:1072/signalr'; //چون در یک پروژه دیگر قرار داریم
            var proxy = connection.createHubProxy('chat');

            proxy.on('hello', function (message) {
                //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است
                //به این ترتیب سرور می‌تواند کلاینت را فراخوانی کند
                $("#messages").append("<li>" + message + "</li>");
            });

            $("#send").click(function () {
                // Hub's `SendMessage` should be camel case here
                proxy.invoke('sendMessage', $("#txtMsg").val());
            });

            connection.start();
        });
    </script>
</body>
</html>
در اینجا سطر مرتبط با تعریف مسیر اسکریپت‌های پویای signalr/hubs را دیگر در ابتدای فایل مشاهده نمی‌کنید. کار تشکیل proxy اینبار از طریق کدنویسی صورت گرفته است. پس از ایجاد پروکسی، برای گوش فرا دادن به متدهای فراخوانی شده از طرف سرور از متد proxy.on و نام متد فراخوانی شده سمت سرور استفاده می‌کنیم و یا برای ارسال اطلاعات به سرور از متد proxy.invoke به همراه نام متد سمت سرور استفاده خواهد شد.


کلاینت‌های دات نتی SignalR
تا کنون Solution ما حاوی یک پروژه Hub و یک پروژه وب کلاینت جی‌کوئری است. به همین Solution، یک پروژه کلاینت کنسول ویندوزی را نیز اضافه کنید.
سپس در خط فرمان پاور شل نوگت دستور زیر را صادر نمائید تا فایل‌های مورد نیاز به پروژه کنسول اضافه شوند:
 PM> Install-Package Microsoft.AspNet.SignalR.Client
در اینجا نیز باید دقت داشت تا دستور بر روی default project صحیحی اجرا شود (حالت پیش فرض، اولین پروژه موجود در solution است).
پس از نصب آن اگر به پوشه packages مراجعه کنید، نگارش‌های مختلف آن‌را مخصوص سیلورلایت، دات نت‌های 4 و 4.5، WinRT و ویندوز فون8 نیز می‌توانید در پوشه Microsoft.AspNet.SignalR.Client ملاحظه نمائید. البته در ابتدای نصب، انتخاب نگارش مناسب، بر اساس نوع پروژه جاری به صورت خودکار صورت می‌گیرد.
مدل برنامه نویسی آن نیز بسیار شبیه است به حالت عدم استفاده از پروکسی در حین استفاده از jQuery که در قسمت قبل بررسی گردید و شامل این مراحل است:
1) یک وهله از شیء HubConnection را ایجاد کنید.
2) پروکسی مورد نیاز را جهت اتصال به Hub از طریق متد CreateProxy تهیه کنید.
3) رویدادگردان‌ها را همانند نمونه کدهای جاوا اسکریپتی قسمت قبل، توسط متد On تعریف کنید.
4) به کمک متد Start، اتصال را آغاز نمائید.
5) متدها را به کمک متد Invoke فراخوانی نمائید.

using System;
using Microsoft.AspNet.SignalR.Client.Hubs;

namespace SignalR02.WinClient
{
    class Program
    {
        static void Main(string[] args)
        {
            var hubConnection = new HubConnection(url: "http://localhost:1072/signalr");
            var chat = hubConnection.CreateHubProxy(hubName: "chat");

            chat.On<string>("hello", msg => {
                Console.WriteLine(msg);
            });

            hubConnection.Start().Wait();

            chat.Invoke<string>("sendMessage", "Hello!");

            Console.WriteLine("Press a key to terminate the client...");
            Console.Read();
        }
    }
}
نمونه‌ای از این پیاده سازی را در کدهای فوق ملاحظه می‌کنید که از لحاظ طراحی آنچنان تفاوتی با نمونه ذهنی جاوا اسکریپتی ندارد.

نکته مهم
کلیه فراخوانی‌هایی که در اینجا ملاحظه می‌کنید غیرهمزمان هستند.
به همین جهت پس از متد Start، متد Wait ذکر شده‌است تا در این برنامه ساده، پس از برقراری کامل اتصال، کار invoke صورت گیرد و یا زمانیکه callback تعریف شده توسط متد chat.On فراخوانی می‌شود نیز این فراخوانی غیرهمزمان است و خصوصا اگر نیاز است رابط کاربری برنامه را در این بین به روز کنید باید به نکات به روز رسانی رابط کاربری از طریق یک ترد دیگر دقت داشت.
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 19 - بومی سازی
روش انتقال منابع مرتبط با data annotations و ViewModelها به یک اسمبلی دیگر
- فرض کنید یک class library مخصوص NET Core. را به نام Core1RtmTestResources.ExternalResources جهت درج منابع تهیه کرده‌اید و پوشه‌ی Resources را از پروژه‌ی اصلی به آن انتقال داده‌اید (بدون هیچ تغییر نامی).
- نیازی نیست تا قسمت options.ResourcesPath کلاس آغازین برنامه را تغییر دهید و همان مقدار Resources در این حالت هم کار می‌کند.
- در اینجا دو مورد باید تغییر کنند:
الف) باید مشخص کنید که این اطلاعات قرار است از کدام اسمبلی خوانده شود:
public void ConfigureServices(IServiceCollection services)
{
  services.AddLocalization(options =>
  {
   options.ResourcesPath = "Resources";
  });
 
  services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization(options=>
    {
      options.DataAnnotationLocalizerProvider = (type, factory) =>
      {
       return factory.Create(
        baseName: type.FullName /* بر این اساس نام فایل منبع متناظر باید به همراه ذکر فضای نام پایه آن هم باشد */,
        location: "Core1RtmTestResources.ExternalResources" /*نام اسمبلی ثالث*/);
      };
    });
در اینجا location به نام اسمبلی اشاره می‌کند که حاوی فایل resx مرتبط است. حالت پیش فرض آن (بدون این تنظیمات)، به اسمبلی اشاره می‌کند که کلاس ViewModel در آن قرار گرفته‌است.
ب) چون در این تنظیم baseName به FullName تنظیم شده‌است، نام فایل منبع باید کامل باشد؛ یعنی باید به همراه فضای نام اصلی اسمبلی هم باشد. مثلا اگر قبلا چنین نامی را داشته
ViewModels.Account.RegisterViewModel.fa.resx 
الان باید فضای نام مرتبط را هم داشته باشد (نام کامل نوع آن کلاس):
Core1RtmTestResources.ViewModels.Account.RegisterViewModel.fa.resx 
مطالب
‫دریافت کل یک مخزن SVN به کمک برنامه نویسی

تمام قابلیت‌های موجود در SVN به کمک برنامه نویسی هم قابل دسترسی هستند. برای مثال تهیه خروجی از یک مخزن SVN به همراه تمامی فایل‌ها و ساختار آن. SVN به زبان C نوشته شده است و API آن نیز مبتنی بر همین زبان است اما یک سری محصور کننده برای استفاده از آن در سایر زبان‌های برنامه نویسی هم موجود است. برای مثال معروفترین آن‌ها جهت استفاده به کمک دات نت فریم ورک کتابخانه‌ی SharpSVN است. پس از دریافت و افزودن ارجاعی به اسمبلی آن، چند سطر ذیل کار دریافت یک مخزن SVN را به صورت تمام و کمال انجام خواهد داد:

using SharpSvn;
...
using (var sc = new SvnClient())
{
var target = SvnTarget.FromUri(new Uri("http://someproject.googlecode.com/svn/trunk/"));
var finalSaveToDir = "somepath ..."; //Note: this path should not exist
sc.Export(target, finalSaveToDir);
}

نمونه‌ای از کاربردها:
- راه اندازی یک سایت برای دریافت ساده‌تر مخازن کد برای مثال Google-code یا source forge و امثال آن.