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

برای تست کدها در ویندوز سرویس، اولین راه پیشنهادی همیشه این بوده که سرویس را موقتا به Console Application تبدیل کنیم و با تهیه یک متد در سرویس و فراخوانی آن در متد Main برنامه کنسولی، بتوانیم به دیباگ برنامه بپردازیم. مثال: 
تغییرات مورد نیاز در سرویس : 
 public Service1(string[] args)
        {
            this.args = args;
        }

        internal void TestStartupAndStop(string[] args)
        {
            this.OnStart(args);
            Console.ReadLine();
            this.OnStop();
        }
        protected override void OnStart(string[] args)
        {
            Console.WriteLine("Service Started");
            //Do Somthing
        }
تغییرات مورد نیاز در متد Main : 
static void Main(string[] args)
        {
            if (Environment.UserInteractive)
            {
                Service1 service1 = new Service1(args);
                service1.TestStartupAndStop(args);
            }
            else
            {
                // Put the body of your old Main method here.
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]
                {
                new Service1()
                };
                ServiceBase.Run(ServicesToRun);
            }
        }
برای کسب اطلاعات بیشتر در مورد این روش برای دیباگ، میتوانید به این لینک مراجعه نمایید.
این راه حل کلی، عموما جوابگوی نیازها هست و تست و دیباگ را میتوانیم از همین طریق انجام دهیم. اما مشکلات زیادی هم دارد. به طور مثال یکی از مشکلات هنگام استفاده از این راه حل، تفاوت دسترسی‌ها و کاربر اجرا کننده برنامه، در حالت کنسول و سرویس ویندوز است. به همین دلیل بسیار با این مشکل مواجه خواهید شد که هنگام دیباگ مشکلی وجود نداشته ولی بعد از نصب سرویس، برنامه بدرستی کار نکند. به طور مثال وقتی شما سرویس را به صورت Network Service ویا Local System نصب کنید، دسترسی برنامه به کلیدهای رجیستری , فایل سیستم و Environment تغییر میکند. در نتیجه اگر مانند روشی که در بالا ذکر شد دیباگ را انجام دهید، نمیتوانید مشکلات را شناسایی و رفع کنید. همینطور روش قبلی صرفا برای دیباگ نیازمند تغییر در سورس کد اصلی برنامه است که خود میتواند مشکلات یا محدودیت‌هایی را هنگام کار به صورت تیمی ایجاد کند.

برای دیباگ سرویس نصب شده چه باید کرد ؟
در این مطلب نمیخواهم وارد جزئیات نحوه نصب ویندوز سرویس‌ها شوم؛ چراکه خودش مبحث گسترده‌ایست. برای کسب اطلاعات بیشتر در مورد نحوه‌ی نصب سرویس‌ها میتوانید به این لینک مراجعه نمایید.
برای اینکه بعد از نصب سرویس بتوانیم به دیباگ آن بپردازیم، مراحل زیر را طی کنید.
1- اول در ابتدای متد OnStart سرویس، تکه کد زیر را وارد نمایید و یک BreakPoint کنارش قرار دهید:
System.Diagnostics.Debugger.Launch(); // Start Debugger
البته پیشنهاد میکنم که این بخش را به صورت زیر وارد کنید تا فقط درصورتیکه سرویس شما در حالت Debug ساخته شده باشد، اجرا شود.
#if DEBUG
      System.Diagnostics.Debugger.Launch(); // Start Debugger
#endif
2- سرویس را با استفاده از installutil نصب کرده و استارت نمایید.
هنگام استارت سرویس، پنجره زیر نمایان میشود.


درصورتی که Yes را انتخاب نمایید، میتوانید در یک Solution جدید به دیباگ برنامه بپردازید و دیباگر به صورت خودکار کدهای مورد نیاز را برای شروع کار، برای شما بارگذاری خواهد کرد. اما در پروژه‌های بزرگ‌تر به دلیل وابستگی‌های سرویس به کتابخانه‌های متعدد، امکان دیباگ کامل را هنوز در اختیار نداریم و دیباگ ما در اینجا، محدود به خود سرویس است. برای اینکه بتوانیم دیباگ را کامل در Solution اصلی برنامه انجام دهید، قبل پاسخ دادن به پنجره بالا یک مرحله دیگر از کار باقی مانده است.
3- در Solution اصلی برنامه خود، کلید‌های Ctrl+Alt+P را فشار دهید؛ یا از طریق منوی Debug > Attach to process، پنجره Attack to Process را باز نمایید.

همانطور که در تصویر مشاهده میکنید، ID پروسه را در هر دو عکس برایتان مشخص کرد‌ه‌ام (هایلایت زرد) که به راحتی میتوانید پروسه سرویستان را از این طریق پیدا کنید.
کافیست روی کلید Attach کلیک نمایید تا در Solution اصلی برنامه بتوانید به صورت کامل به دیباگ سرویس نصب شده بپردازید. پس از اتچ کردن پروسه به Solution خود، پنجره اول (Visual Studio Just-In Time debugger) را ببندید یا گزینه No را انتخاب نمایید.
نکته: برای استفاده از این روش باید Visual Studio به صورت Administrator اجرا شده باشد.
مطالب
راهبری در Silverlight به کمک الگوی MVVM

مقدمات راهبری (Navigation) در سیلورلایت را در اینجا می‌توانید مطالعه نمائید : +
مطلبی را که در فصل فوق نخواهید یافت در مورد نحوه‌ی بکارگیری الگوی MVVM جهت پیاده سازی Navigation در یک برنامه‌ی سیلورلایت است؛ علت آن هم به این بر می‌گردد که این فصل پیش از مباحث Binding مطرح شد.

صورت مساله:
یکی از اصول MVVM این است که در ViewModel‌ نباید ارجاعی از View وجود داشته باشد (ViewModel باید در بی‌خبری کامل از وجود اشیاء UI و ارجاع مستقیم به آن‌ها طراحی شود)، اما برای پیاده سازی مباحث Navigation نیاز است به نحوی به شیء Frame قرار داده شده در صفحه‌ی اصلی یا قالب اصلی برنامه دسترسی یافت تا بتوان درخواست رهنمون شدن به صفحات مختلف را صادر کرد. اکنون چکار باید کرد؟

راه حل:
یکی از راه حل‌های جالبی که برای این منظور وجود دارد استفاده از امکانات کلاس Messenger مجموعه‌ی MVVM Light toolkit است. از طریق ViewModel برنامه، آدرس صفحه‌ی مورد نظر را به صورت یک پیغام به View مورد نظر ارسال می‌کنیم و سپس View برنامه که به این پیغام‌ها گوش فرا می‌دهد، پس از دریافت آدرس مورد نظر، نسبت به فراخوانی تابع Navigate شیء Frame رابط کاربری برنامه اقدام خواهد کرد. به این صورت ViewModel برنامه به View خود جهت اعمال راهبری برنامه، گره نخواهد خورد.

روش پیاده سازی:
ابتدا ساختار پروژه را در نظر بگیرید (این شکل دگرگون شده‌ی Solution explorer مرتبط است با productivity tools نصب شده):



در پوشه‌ی Views ، دو صفحه اضافه شده‌اند که توسط user control ایی به نام menu لیست شده و راهبری خواهند شد. مونتاژ نهایی هم در MainPage.xaml صورت می‌گیرد.
کدهای XAML‌ مرتبط با منوی ساده برنامه به شرح زیر هستند (Menu.xaml) :

<UserControl x:Class="MvvmLight6.Views.Menu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:MvvmLight6.ViewModels" mc:Ignorable="d"
FlowDirection="RightToLeft" d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<vm:MenuViewModel x:Key="vmMenuViewModel" />
</UserControl.Resources>
<StackPanel DataContext="{Binding Source={StaticResource vmMenuViewModel}}">
<HyperlinkButton Content="صفحه یک" Margin="5"
Command="{Binding DoNavigate}"
CommandParameter="/Views/Page1.xaml"
/>
<HyperlinkButton Content="صفحه دو" Margin="5"
Command="{Binding DoNavigate}"
CommandParameter="/Views/Page2.xaml"
/>
</StackPanel>
</UserControl>

کدهای ViewModel مرتبط با این View که کار Command گردانی را انجام خواهد داد به شرح زیر است:
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;

namespace MvvmLight6.ViewModels
{
public class MenuViewModel
{
public RelayCommand<string> DoNavigate { set; get; }

public MenuViewModel()
{
DoNavigate = new RelayCommand<string>(doNavigate);
}

private static void doNavigate(string url)
{
Messenger.Default.Send(url, "MyNavigationService");
}
}
}

تمام آیتم‌های منوی فوق یک روال را صدا خواهند زد : DoNavigate . تنها تفاوت آن‌ها در CommandParameter ارسالی به RelayCommand ما است که حاوی آدرس قرارگیری فایل‌های صفحات تعریف شده است. این آدرس‌ها با کمک امکانات کلاس Messenger مجموعه‌ی MVVM light toolkit به View اصلی برنامه ارسال می‌گردند.
کدهای XAML مرتبط با MainPage.xaml به شرح زیر هستند:

<UserControl x:Class="MvvmLight6.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sdk="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:usr="clr-namespace:MvvmLight6.Views"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="268" />
</Grid.ColumnDefinitions>
<usr:Menu Grid.Column="1" />
<sdk:Frame Margin="5"
Name="frame1"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Grid.Column="0" />
</Grid>
</UserControl>

و کار دریافت پیغام‌ها (یا همان آدرس صفحات جهت انجام راهبری) و عکس العمل نشان دادن به آن‌ها توسط کدهای ذیل صورت خواهد گرفت:
using System;
using GalaSoft.MvvmLight.Messaging;

namespace MvvmLight6
{
public partial class MainPage
{
public MainPage()
{
registerMessenger();
InitializeComponent();
}

private void registerMessenger()
{
Messenger.Default.Register<string>(this, "MyNavigationService", doNavigate);
}

private void doNavigate(string uri)
{
frame1.Navigate(new Uri(uri, UriKind.Relative));
}
}
}

ابتدا یک Messenger در اینجا رجیستر می‌شود و سپس به ازای هر بار دریافت پیغامی با token مساوی MyNavigationService ، متد doNavigate فراخوانی خواهد گردید.
کدهای این مثال را از اینجا می‌توانید دریافت کنید.

اشتراک‌ها
دوره آموزشی چهار ساعته Blazor Hybrid

Learn Blazor Hybrid - Full Course for Beginners | Build cross-platform apps in C#

Let's start our journey together to build beautiful native cross-platform apps for iOS, Android, macOS, and Windows with Blazor Hybrid, .NET MAUI, C#, and Visual Studio! In this full workshop, I will walk you through everything you need to know about .NET MAUI and building your very first app. You will learn the basics including how to build user interfaces with Razor, how to show data from the internet, how to navigate between pages and combine .NET MAUI pages with Razor pages, access platform features like geolocation, and theme your app for light theme and dark theme. This course has everything you need to learn the basics and set you up for success when building apps with Blazor Hybrid!

Chapters:

00:00:00 - Intro to the Blazor Hybrid Workshop

00:04:28 - What is Blazor Hybrid & How to Install

00:06:51 - First Blazor Hybrid App & Architecture

00:21:40 - Get Code to Build Your First Blazor Hybrid App

00:26:38 - Blazor Hybrid Project Walkthrough

00:39:22 - Start to Build First Blazor Hybrid App

01:03:10 - Event Handling, Data Binding and Parameters (Slides)

01:09:00 - Add Monkey Data & Fluent UI Blazor Components

01:32:08 - Navigation, NavigationManager, .NET MAUI Pages (Slides)

01:39:19 - Navigation with NavigationManager

01:52:39 - Navigation with NavLinks

01:57:21 - Add .NET MAUI Pages & Components

02:21:11 - Access Platform Functionality (Slides)

02:27:57 - Check Network Connectivity

02:38:04 - Get User Location with Geolocation

02:49:09 - Integration with Other Apps

02:57:42 - App Theming, Light Theme, Dark Theme (Slides)

03:05:36 - JavaScript Interoperability with IJSRuntime

03:20:48 - Theming FluentUI Blazor Components

03:26:05 - Style Status Bar with .NET MAUI Community Toolkit

03:39:00 - .NET MAUI Light & Dark Theme with AppThemeBinding

03:42:58 - Sharing State & Creating Reusable Components (Slides)

03:47:27 - Implement Shared State Blazor Hybrid & .NET MAUI

04:02:47 - Create Reusable Razor Components

04:08:31 - CONGRATULATIONS!

دوره آموزشی چهار ساعته Blazor Hybrid
مطالب
امن سازی برنامه‌های ASP.NET Core توسط IdentityServer 4x - قسمت دوم - ایجاد ساختار اولیه‌ی مثال این سری
در این قسمت قصد داریم ساختار مقدماتی مثال این سری را که لیستی از تصاویر آپلود شده‌ی توسط کاربران مختلف را نمایش می‌دهد، بدون افزودن مباحث امنیتی و سطوح دسترسی کاربران وارد شده‌ی به سیستم، تشکیل دهیم. در قسمت‌های بعدی، به تدریج آن‌را با قابلیت‌های مختلف IdentityServer 4x یکپارچه خواهیم کرد. در اینجا فرض بر این است که حداقل SDK نگارش 2.1.401 را پیشتر نصب کرده‌اید.


بررسی ساختار WebAPI مقدماتی مثال این سری


این پروژه‌ی مقدماتی که هنوز قسمت‌های اعتبارسنجی به آن اضافه نشده‌اند، از دو قسمت WebApi و MvcClient تشکیل می‌شود.
کار قسمت WebApi، ارائه‌ی یک Restful-API برای کار با گالری تصاویر است. برای اجرای آن وارد پوشه‌ی src\WebApi\ImageGallery.WebApi.WebApp شده و ابتدا فایل restore.bat و سپس dotnet_run.bat را اجرا کنید.
در این حالت برنامه بر روی پورت 7001 در دسترس خواهد بود:


این پورت نیز در فایل Properties\launchSettings.json تنظیم شده‌است تا با پورت کلاینت MVC تهیه شده، تداخل نکند.
کار این سرویس، ارائه‌ی ImagesController است که توسط آن می‌توان لیستی از تصاویر موجود، اطلاعات یک تصویر و همچنین کار ثبت، ویرایش و حذف تصاویر را انجام داد.
در این Solution، رکوردهای تصاویری که در بانک اطلاعاتی ذخیره می‌شوند، یک چنین ساختاری را دارند:
using System;
using System.ComponentModel.DataAnnotations;

namespace ImageGallery.WebApi.DomainClasses
{
    public class Image
    {
        [Key]
        public Guid Id { get; set; }

        [Required]
        [MaxLength(150)]
        public string Title { get; set; }

        [Required]
        [MaxLength(200)]
        public string FileName { get; set; }

        [Required]
        [MaxLength(50)]
        public string OwnerId { get; set; }
    }
}
همانطور که ملاحظه می‌کنید در اینجا OwnerId نیز در نظر گرفته شده‌است تا بتوان پس از اعمال مباحث اعتبارسنجی، تصاویر را از کاربری خاص دریافت و همچنین صرفا تصاویر متعلق به او را به آن کاربر خاص نمایش داد.
در این قسمت توسط کلاس ImageConfiguration کار مقدار دهی اولیه‌ی بانک اطلاعاتی به کمک متد HasData مربوط به EF Core 2.1 صورت گرفته‌است و به این ترتیب می‌توان برنامه را برای نمایش مقدماتی جاری، بدون داشتن سیستم اعتبارسنجی و مفاهیم کاربران، نمایش داد.
این قسمت از Solution، به نحو زیر تشکیل شده‌است:
- ImageGallery.WebApi.DataLayer
در اینجا کار تشکیل DbContext برنامه و همچنین مقدار دهی اولیه‌ی بانک اطلاعاتی و تنظیمات Migrations قرار گرفته‌اند.
- ImageGallery.WebApi.DomainClasses
در این پروژه کلاس‌های موجودیت‌های متناظر با جداول بانک اطلاعاتی قرار می‌گیرند.
- ImageGallery.WebApi.Mappings
این پروژه کار تهیه نگاشت‌های AutoMapper برنامه را انجام می‌دهد؛ نگاشت‌هایی بین Models و DomainClasses که در ImagesController از آن‌ها استفاده می‌شود.
- ImageGallery.WebApi.Models
در این پروژه همان DTO's پروژه قرار گرفته‌اند. جهت رعایت مسایل امنیتی نباید کلاس موجودیت Image فوق را مستقیما در معرض دید API عمومی قرار داد. به همین جهت تعدادی Model در اینجا تعریف شده‌اند که هم برای ثبت، ویرایش و حذف اطلاعات بکار می‌روند و هم جهت گزارشگیری از رکوردهای موجود جدول تصاویر.
- ImageGallery.WebApi.Services
در این پروژه کار با DbContext انجام شده و توسط آن اطلاعات تصاویر به بانک اطلاعاتی اضافه شده و یا واکشی می‌شوند.
- ImageGallery.WebApi.WebApp
این پروژه، همان پروژه‌ی اصلی است که سایر قسمت‌های یاد شده را در کنار هم قرار داده و به صورت یک Restful-API ارائه می‌دهد.



بررسی ساختار MvcClient مقدماتی مثال این سری

پس از اجرای WebAPI و آماده بودن آن جهت استفاده، اکنون یک کلاینت ASP.NET Core MVC را جهت کار با امکانات ImagesController آن، تدارک دیده‌ایم.
برای اجرای آن وارد پوشه‌ی src\MvcClient\ImageGallery.MvcClient.WebApp شده و ابتدا فایل restore.bat و سپس dotnet_run.bat را اجرا کنید.
در این حالت برنامه بر روی پورت 5001 در دسترس خواهد بود:


این پورت نیز در فایل Properties\launchSettings.json تنظیم شده‌است.


در اینجا نمایی از اجرای این برنامه را مشاهده می‌کنید که لیستی از تصاویر را توسط GalleryController، از سرویس ImagesController مربوط به WebAPI، دریافت کرده و سپس نمایش می‌دهد. در این لیست تصاویر تمام کاربران با هم نمایش داده شده‌اند و هنوز امکان فیلتر کردن آن‌ها بر اساس کاربران وارد شده‌ی به سیستم را نداریم که در قسمت‌های بعدی آن‌ها را تکمیل خواهیم کرد.

این قسمت از Solution به نحو زیر تشکیل شده‌است:
- ImageGallery.MvcClient.Services
در اینجا یک Typed HTTP Client مخصوص NET Core 2.1. را تهیه کرده‌ایم. این سرویس جهت دسترسی به آدرس https://localhost:7001 که WebAPI برنامه در آن قرار دارد، تشکیل شده‌است. روش ثبت مخصوص آن‌را نیز در فایل آغازین پروژه‌ی MvcClient.WebApp توسط متد services.AddHttpClient ملاحظه می‌کنید.
- ImageGallery.MvcClient.ViewModels
مدل‌های متناظر با ساختار Viewهای Razor برنامه‌ی وب، در اینجا قرار می‌گیرند.
- ImageGallery.MvcClient.WebApp
این پروژه، همان پروژه‌ی اصلی است که سایر قسمت‌های یاد شده را در کنار هم قرار داده و به صورت یک برنامه‌ی MVC قابل مرور در مرورگر، ارائه می‌دهد.

کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
برای اجرای آن ابتدا باید پروژه‌ی WebApi.WebApp را اجرا کنید و سپس پروژه‌ی MvcClient.WebApp.
مطالب
افزودن یک DataType جدید برای نگه‌داری تاریخ خورشیدی - 1

ثبت و نگه‌داری تاریخ خورشیدی در SQL Server از دیرباز یکی از نگرانی‌های برنامه‌نویسان و طراحان پایگاه داده‌ها بوده است. در این نوشتار، راه‌کار تعریف یک DataType در SQL Server 2012 به روش CLR آموزش داده خواهد شد.

در ویژوال استودیو یک پروژه‌ی جدید از نوع SQL Server Database Project به شکل زیر ایجاد کنید: 

نام پروژه را به یاد تقویم خیام، prgJalaliDate می‌گذارم. در Solution Explorer روی نام پروژه راست‌کلیک کرده، سپس روی Add New Item کلیک کنید. در پنجره‌ی بازشده مطابق شکل SQL CLR C# User Defined Type را برگزینید؛ سپس نام JalaliDateType را برای آن انتخاب کنید.
 

 متن موجود در صفحه‌ی بازشده را کاملاً حذف کرده و با کد زیر جای‌گزین کنید.

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

using System;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

[Serializable()]
[SqlUserDefinedType(Format.Native)]
public struct JalaliDate : INullable
{
    private Int16 m_Year;
    private byte m_Month;
    private byte m_Day;
    private byte m_Hour;
    private byte m_Minute;
    private byte m_Second;
    private bool is_Null;


    public Int16 Year
    {
        get
        {
            return (this.m_Year);
        }
        set
        {
            m_Year = value;
        }
    }

    public byte Month
    {
        get
        {
            return (this.m_Month);
        }
        set
        {
            m_Month = value;
        }
    }

    public byte Day
    {
        get
        {
            return (this.m_Day);
        }
        set
        {
            m_Day = value;
        }
    }

    public byte Hour
    {
        get
        {
            return (this.m_Hour);
        }
        set
        {
            m_Hour = value;
        }
    }

    public byte Minute
    {
        get
        {
            return (this.m_Minute);
        }
        set
        {
            m_Minute = value;
        }
    }

    public byte Second
    {
        get
        {
            return (this.m_Second);
        }
        set
        {
            m_Second = value;
        }
    }

    public bool IsNull
    {
        get
        {
            return is_Null;
        }
    }

    public static JalaliDate Null
    {
        get
        {
            JalaliDate jl = new JalaliDate();
            jl.is_Null = true;
            return (jl);
        }
    }


    public override string ToString()
    {
        if (this.IsNull)
        {
            return "NULL";
        }
        else
        {
            return this.m_Year.ToString("D4") + "/" + this.m_Month.ToString("D2") + "/" + this.m_Day.ToString("D2") + " " + this.Hour.ToString("D2") + ":" + this.Minute.ToString("D2") + ":" + this.Second.ToString("D2");
        }
    }


    public static JalaliDate Parse(SqlString s)
    {
        if (s.IsNull)
        {
            return Null;
        }

        System.Globalization.PersianCalendar pers = new System.Globalization.PersianCalendar();
        string str = Convert.ToString(s);
        string[] JDate = str.Split(' ')[0].Split('/');

        JalaliDate jl = new JalaliDate();

        jl.Year = Convert.ToInt16(JDate[0]);
        byte MonthsInYear = (byte)pers.GetMonthsInYear(jl.Year);
        jl.Month = (byte.Parse(JDate[1]) <= MonthsInYear ? (byte.Parse(JDate[1]) > 0 ? byte.Parse(JDate[1]) : (byte)1) : MonthsInYear);
        byte DaysInMonth = (byte)pers.GetDaysInMonth(jl.Year, jl.Month); ;
        jl.Day = (byte.Parse(JDate[2]) <= DaysInMonth ? (byte.Parse(JDate[2]) > 0 ? byte.Parse(JDate[2]) : (byte)1) : DaysInMonth);
        if (str.Split(' ').Length > 1)
        {
            string[] JTime = str.Split(' ')[1].Split(':');
            jl.Hour = (JTime.Length >= 1 ? (byte.Parse(JTime[0]) < 23 && byte.Parse(JTime[0]) >= (byte)0 ? byte.Parse(JTime[0]) : (byte)0) : (byte)0);
            jl.Minute = (JTime.Length >= 2 ? (byte.Parse(JTime[1]) < 59 && byte.Parse(JTime[1]) >= (byte)0 ? byte.Parse(JTime[1]) : (byte)0) : (byte)0);
            jl.Second = (JTime.Length >= 3 ? (byte.Parse(JTime[2]) < 59 && byte.Parse(JTime[2]) >= (byte)0 ? byte.Parse(JTime[2]) : (byte)0) : (byte)0);
        }
        else { jl.Hour = 0; jl.Minute = 0; jl.Second = 0; }

        return (jl);
    }

    public SqlString GetDate()
    {
        return this.m_Year.ToString("D4") + "/" + this.m_Month.ToString("D2") + "/" + this.m_Day.ToString("D2");
    }

    public SqlString GetTime()
    {
        return this.Hour.ToString("D2") + ":" + this.Minute.ToString("D2") + ":" + this.Second.ToString("D2");
    }

    public SqlDateTime ToGregorianTime()
    {
        System.Globalization.PersianCalendar pers = new System.Globalization.PersianCalendar();
        return SqlDateTime.Parse(pers.ToDateTime(this.Year, this.Month, this.Day, this.Hour, this.Minute, this.Second, 0).ToString());
    }

    public SqlString JalaliDateAdd(SqlString interval, int increment)
    {
        System.Globalization.PersianCalendar pers = new System.Globalization.PersianCalendar();
        DateTime dt = pers.ToDateTime(this.Year, this.Month, this.Day, this.Hour, this.Minute, this.Second, 0);
        string CInterval = interval.ToString();
        bool isConvert = true;
        switch (CInterval)
        {
            case "Year":
                dt = pers.AddYears(dt, increment);
                break;
            case "Month":
                dt = pers.AddMonths(dt, increment);
                break;
            case "Day":
                dt = pers.AddDays(dt, increment);
                break;
            case "Hour":
                dt = pers.AddHours(dt, increment);
                break;
            case "Minute":
                dt = pers.AddMinutes(dt, increment);
                break;
            case "Second":
                dt = pers.AddSeconds(dt, increment);
                break;
            default:
                isConvert = false;
                break;
        }

        if (isConvert == true)
        {
            this.Year = (Int16)pers.GetYear(dt);
            this.Month = (byte)pers.GetMonth(dt);
            this.Day = (byte)pers.GetDayOfMonth(dt);
            this.Hour = (byte)pers.GetHour(dt);
            this.Minute = (byte)pers.GetMinute(dt);
            this.Second = (byte)pers.GetSecond(dt);
        }


        return this.m_Year.ToString("D4") + "/" + this.m_Month.ToString("D2") + "/" + this.m_Day.ToString("D2") + " " + this.Hour.ToString("D2") + ":" + this.Minute.ToString("D2") + ":" + this.Second.ToString("D2");
    }
}

از منوهای بالا روی منوی Bulild و سپس گزینه‌ی Publish prgJalaliDate کلیک کتید:

در پنجره‌ی بازشده روی دکمه‌ی Edit کلیک کنید سپس تنظیمات مربوط به اتصال به پایگاه داده را انجام دهید.

روی دکمه‌ی OK کلیک کنید و سپس در پنجره‌ی اولیه، روی دکمه‌ی Publish کلیک کتید:

به همین سادگی، DataType مربوطه در SQL Server 2012 ساخته می‌شود. خبر خوش این‌که شما می‌توانید با راست‌کلیک روی نام پروژه و انتخاب گزینه‌ی Properties در قسمت Project Setting تنظیمات مربوط به نگارش SQL Server را انجام دهید. (از نگارش 2005 به بعد در VS 2012 پشتیبانی می‌شود.)


اکنون زمان آن رسیده است که DataType ایجادشده را در SQL Server 2012 بیازماییم. SQL Server را باز کنید و دستور زیر را در آن اجرا کتید.

USE Northwind

GO

CREATE TABLE dbo.TestTable
(
Id int NOT NULL IDENTITY (1, 1),
TestDate dbo.JalaliDate NULL
)  ON [PRIMARY]
GO
همین‌طور که مشاهده می‌کنید؛ امکان به‌کارگیری DataType تعریف‌شده وجود دارد. 
اکنون چند رکورد درون این جدول درج می‌کنیم:
Insert into TestTable (TestDate) Values ('1392/02/09'),('1392/02/09 22:40'),('1392/12/30 22:40')
پس از اجرای این دستور خطای زیر در پایین صفحه‌ی SQL Server نمایان می‌شود:

این خطا به این خاطر است که CLR را در SQL Server  فعال نکرده ایم. جهت فعال‌کردن CLR دستور زیر را اجرا کنید:
sp_configure 'clr enabled', 1
Reconfigure
بار دیگر دستور درج را اجرا می‌کنیم:
Insert into TestTable (TestDate) Values ('1392/02/09'),('1392/02/09 22:40'),('1392/12/30 22:40')
ملاحظه می‌کنید که داده‌ها در جدول مربوطه ذخیره شده است. در رکورد نخست چون ساعت، دقیقه و ثانیه تعریف نشده است؛ به طور هوشمند صفر درج شده است. در رکورد دوم، ساعت و دقیقه مقدار دارد ولی ثانیه صفر ثبت شده است. و در رکورد سوم چون سال 1392 کبیسه نیست؛ به صورت هوشمند آخرین روز ماه به جای روز ثبت شده است. هرچند می‌توان با دست‌کاری در توابع سی‌شارپ، این قوانین را عوض کرد.

اکنون زمان آن رسیده است که توسط یک پرس‌وجو، همه‌ی توابعی که در سی‌شارپ برای این نوع داده نوشتیم، بیازماییم. پرس‌وجوی زیر را اجرا کنید:
Select TestDate.ToString() as JalaliDateTime,
          TestDate.GetDate() as JalaliDate, TestDate.GetTime() as JalaliTime,
          TestDate.ToGregorianTime() as GregorianTime,
          TestDate.JalaliDateAdd('Day',1) JalaliTomorrow,
          TestDate.Month as JalaliMonth from TestTable
خروجی این پرس‌وجو به شکل زیر خواهد بود:

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

نیازی به گفتن نیست که می‌توانید به سادگی از توابع مربوط به DateTime در SQL Server بهره ببرید. برای مثال برای به دست آوردن فاصله‌ی میان دو روز از پرس‌وجوی زیر استفاده کنید:
Declare @a JalaliDate  = '1392/02/07 00:00:00'
Declare @b JalaliDate = '1392/02/05 00:00:00'

SELECT DATEDIFF("DAY",@b.ToGregorianTime(),@a.ToGregorianTime()) AS DiffDate

شاد و پیروز باشید.
نظرات مطالب
Lambda Syntax و کارآیی
تعریف متدهای بی نام (چه بصورت delegate{} و چه بصورت Lambda) مستلزم  ایجاد یک کلاس اضافه بهمراه تشکیل متد و متغیر‌های لازمه توسط کامپایلر هستند اما در مورد متدهای بانام (صریح) اغلب نیازی به ایجاد کلاسی مجزا نیست و توسط برنامه نویس معمولا در همان کلاس نوشته میشوند.
مسلماْ کامپایلر خیلی سریع‌تر از برنامه نویس‌ها میتوانند این موارد رو ایجاد کند.
پس قبل از اجرای برنامه متدها و هندلر‌ها وجود دارند و نمیتواند از این بابت در سرعت اجرا تاثیری داشته باشند. بنظر من تنها دلیل کندتر بودن (خیلی کم) متدهای بی نام در زمان اجرا این است که چون متدهای بی نام در کلاسی مجزا تعریف شده است نیاز به نمونه سازی (newobj ) دارند در حالی که متدهای بانام از این مورد مبرا هستند.
با این توصیف و با چشم پوشی نسبت به زمان اندکی که کامپایلر صرف متدهای بی نام میکند استفاده از متدهای بی نام بصرفه‌تر هستند و برنامه رو هم زیباتر و خواناتر میکنند.
نظرات مطالب
تجربه‌ای ناخوشایند از اثر به روز رسانی‌ها بر روی TFS
من با چنین مشکلی مواجه نشدم تا حالا ، البته من از 2012 استفاده میکنم و خیلی هم ازش راضی هستم ولی این مساله اپدیت‌ها و همخوانی اونها چیزیه که تیم توسعه تی اف اس مثل برایان هری هم بهش اشاره داشتن و خواستن که در چنین مواقعی دقت لازم به عمل بیاد تا عدم تداخل در ورژن‌ها رخ نده
نظرات مطالب
6# آموزش سیستم مدیریت کد Git : استفاده به صورت محلی (بخش دوم)
یک gitignore مفید برای VS.NET:
#OS junk files
[Tt]humbs.db
*.DS_Store

#Visual Studio files
*.[Oo]bj
*.user
*.aps
*.pch
*.vspscc
*.vssscc
*_i.c
*_p.c
*.ncb
*.suo
*.tlb
*.tlh
*.bak
*.[Cc]ache
*.ilk
*.log
*.lib
*.sbr
*.sdf
*.opensdf
*.unsuccessfulbuild
ipch/
obj/
[Bb]in
[Dd]ebug*/
[Rr]elease*/
Ankh.NoLoad

#MonoDevelop
*.pidb
*.userprefs

#Tooling
_ReSharper*/
*.resharper
[Tt]est[Rr]esult*
*.sass-cache

#Project files
[Bb]uild/

#Subversion files
.svn

# Office Temp Files
~$*

#NuGet
packages/

#ncrunch
*ncrunch*
*crunch*.local.xml

# visual studio database projects
*.dbmdl

#Test files
*.testsettings