مطالب
ایجاد یک Repository در پروژه برای دستورات EF
ایجاد یک Pattern در پروژتون میتونه نظم, سرعت و زیبایی خاصی به کدتون بده. با وجود frameworkهای  و Patternهایی مسه MVC و MVVM برنامه نویسان را وادار کنه که همه  Actionهای یک پروژه رو به سمت کلاینت ببرن.تو یک فرصت دیگه در مورد فریمورک Knockout حتما تاپیک میزارم.امروز میخوام یک Pattern با استفاده از یک Interface و codefirst model براتون بزارم.
گام اول:ایجاد که class property
Public Class Employee
    Public Property ID As Integer
    Public Property Fname As String
    Public Property Bdate As DateTime
End Class  

گام دوم:ایجاد بانک با استفاده از CodeFirst
Imports System.Data.Entity
Public Class EmployeeDBContext : Inherits DBContext
    Public Property Employees As DbSet(Of Employee)
End Class 
گام سوم: ایجاد repository با استفاده از interface
Interface EmployeeRepository
    ReadOnly Property All As List(Of Employee)
    Function Find(id As Integer) As Employee
    Sub InsertOrUpdate(p As Employee)
    Sub Delete(id As Integer)
    Sub Save()
End Interface 
گام چهارم: تعریف کلاس برای implement کردن از iInterface
Public Class EmployeeClass : Implements EmployeeRepository
    Private DB As New EmployeeDBContext
    Public ReadOnly Property All As List(Of Employee) Implements EmployeeRepository.All
        Get
            Return DB.Employees.ToList()
        End Get
    End Property

    Public Sub Delete(id As Integer) Implements EmployeeRepository.Delete
        Dim query = DB.Employees.Single(Function(q) q.ID = id)
        DB.Employees.Remove(query)
    End Sub

    Public Function Find(id As Integer) As Employee Implements EmployeeRepository.Find
        Return DB.Employees.Where(Function(q) q.ID = id)
    End Function

    Public Sub InsertOrUpdate(p As Employee) Implements EmployeeRepository.InsertOrUpdate
        If p.ID = Nothing Then
            DB.Employees.Add(p)
        Else
            DB.Entry(p).State = Data.EntityState.Modified
        End If
    End Sub

    Public Sub Save() Implements EmployeeRepository.Save
        DB.SaveChanges()
    End Sub
End Class
 برای استفاده تو پروژه براحتی میتونید یک instance  از classتون ایجاد کنید و ..
 Dim cls As New EmployeeClass 
Public Sub BindGrid()
        GridView1.DataSource = cls.All
        GridView1.DataBind()
    End Sub 
 موفق باشید
نظرات مطالب
استفاده از افزونه‌ی jsTree در ASP.NET MVC
با سلام
من دارم از همین jstree مثال شما استفاده میکنم.
و json زیر رو هم به سمت کلاینت برمیگردونم به صورت صحیح :
[{"id":"OrganizationTree","text":"ساختار سازمانی","icon":"/Content/images/tree_icon.png","state":{"opened":true,"disabled":false,"selected":false},"children":[{"id":"2","text":"آنات","icon":"/Content/images/nuclear.png","state":{"opened":true,"disabled":false,"selected":false},"children":[{"id":"4","text":"آموزش","icon":"/Content/images/nuclear.png","state":{"opened":true,"disabled":false,"selected":false},"children":[],"li_attr":{"data":null},"a_attr":{"href":null}},{"id":"5","text":"هیات مدیره","icon":"/Content/images/nuclear.png","state":{"opened":true,"disabled":false,"selected":false},"children":[],"li_attr":{"data":null},"a_attr":{"href":null}}],"li_attr":{"data":null},"a_attr":{"href":null}},{"id":"1","text":"پرنیان","icon":"/Content/images/nuclear.png","state":{"opened":true,"disabled":false,"selected":false},"children":[{"id":"1","text":"BPM","icon":"/Content/images/nuclear.png","state":{"opened":true,"disabled":false,"selected":false},"children":[],"li_attr":{"data":null},"a_attr":{"href":null}},{"id":"3","text":"پشتیبانی","icon":"/Content/images/nuclear.png","state":{"opened":true,"disabled":false,"selected":false},"children":[],"li_attr":{"data":null},"a_attr":{"href":null}},{"id":"2","text":"فروش","icon":"/Content/images/nuclear.png","state":{"opened":true,"disabled":false,"selected":false},"children":[],"li_attr":{"data":null},"a_attr":{"href":null}}],"li_attr":{"data":null},"a_attr":{"href":null}}],"li_attr":{"data":null},"a_attr":{"href":null}}]
منتهی همش خطای too much recursion  میگیرم، در صورتی که حلقه ای رو هم نمی‌بینم که ایجاد شده باشه داخل رشته json تولید شده.

اینهم تنظیمات فراخوانی 

 <script>
            $(function () {
                $('#jstree').jstree({
                    "core": {
                        "multiple": true,
                        "check_callback": true,
                        'data': {
                            'url': '@getTreeJsonUrl',
                            "type": "POST",
                            "dataType": "json",
                            "contentType": "application/json; charset=utf8",
                            'data': function (node) {
                                return { 'id': node.id };
                            }
                        },
                        'themes': {
                            'variant': 'large',
                            'stripes': false
                        }
                    },
                    "types": {
                        "default": {
                            "icon": '@Url.Content("~/Content/images/bookmark_book_open.png")'
                        },
                    },
                    "plugins": ["contextmenu", "dnd", "state", "types", "checkbox", "wholerow", "sort", "unique", "real_checkboxes"],
                    "contextmenu": {
                        "items": function (o, cb) {
                            var items = $.jstree.defaults.contextmenu.items();
                            items["create"].label = "ایجاد زیر شاخه";
                            items["rename"].label = "تغییر نام";
                            items["remove"].label = "حذف";
                            var cpp = items["ccp"];
                            cpp.label = "ویرایش";
                            var subMenu = cpp["submenu"];
                            subMenu["copy"].label = "کپی";
                            subMenu["paste"].label = "پیست";
                            subMenu["cut"].label = "برش";
                            return items;
                        }
                    }
                });
            });
        </script>

فکر میکنین مشکل از کجا باشه؟

جالب اینجاست که اگه فقط یه سطح پایین برم مشکلی نیست!

مطالب
آزمون‌های یکپارچگی در برنامه‌های ASP.NET Core
تا اینجا دو روش را برای آزمایش کلی یک سیستم Web API به همراه تمام زیر ساخت‌های آن، بررسی کردیم:
- استفاده از Postman برای آزمایش یک برنامه‌ی Web API
- استفاده از strest برای آزمایش یک برنامه‌ی Web API

روش سومی هم برای انجام اینکار وجود دارد که به صورت توکار از زمان ارائه‌ی ASP.NET Core 2.1 به همراه TestServer آزمایشی آن میسر شد. این روش در نگارش 3.1، با تغییر روش تعریف فایل program.cs، جهت سازگاری آن با آزمون‌های یکپارچگی/آزمایش کل سیستم، بهبود یافته‌است که خلاصه‌ای از آن را در این مطلب بررسی می‌کنیم.


آزمون‌های یکپارچگی در ASP.NET Core

آزمون‌های یکپارچگی، برخلاف آزمون‌های واحد که عموما از اشیاء تقلیدی استفاده می‌کنند، دقیقا بر روی همان سیستمی که قرار است به کاربر نهایی ارائه شود، اجرا می‌شوند. به همین جهت تنظیمات اولیه‌ی آن‌ها کمی بیشتر است و همچنین زمان اجرای آن‌ها نیز به علت وابستگی به بانک اطلاعاتی واقعی، فایل سیستم، شبکه و غیره، نسبت به آزمون‌های واحد بیشتر است.

برای ایجاد آزمون‌های یکپارچگی در برنامه‌های ASP.NET Core، حداقل سه مرحله باید طی شوند:
الف) ایجاد یک class library که ارجاعی را به پروژه‌ی اصلی دارد. این پروژه حاوی آزمایش‌های ما خواهد بود.
ب) راه اندازی یک هاست وب آزمایشی برای ارسال درخواست‌ها به آن و دریافت پاسخ‌های نهایی.
ج) استفاده از یک test runner (انواع و اقسام فریم ورک‌های unit testing) برای اجرای آزمایش‌ها


ایجاد یک پروژه‌ی کتابخانه برای هاست و اجرای آزمایش‌های یکپارچگی

فرض کنید می‌خواهیم برای همان پروژه‌ی ایجاد JWTها، آزمایش یکپارچگی بنویسیم. پس از ایجاد یک پروژه‌ی کتابخانه‌ی جدید که قرار است هاست آزمایش‌های ما شود، نیاز است محتوای فایل csproj آن‌را به صورت زیر تغییر داد:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <NoWarn>RCS1090</NoWarn>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\ASPNETCore2JwtAuthentication.WebApp\ASPNETCore2JwtAuthentication.WebApp.csproj" />
  </ItemGroup>
  <ItemGroup>
    <None Include="..\ASPNETCore2JwtAuthentication.WebApp\appsettings.json" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>
  <ItemGroup>
    <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c329}" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="fluentassertions" Version="5.10.3" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.8" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
    <PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
    <PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
  </ItemGroup>
</Project>
در اینجا، این نکات قابل مشاهده هستند:
1) TargetFramework آن باید به netcoreapp تنظیم شود.
2) باید ارجاع مستقیمی به کل پروژه‌ی نهایی WebApp در آن وجود داشته باشد. چون در ادامه می‌خواهیم فایل Program.cs آن‌را برای راه اندازی یک هاست وب آزمایشی، فراخوانی کنیم.
3) بسته‌ی نیوگتی که کار راه اندازی هاست وب آزمایشی را انجام می‌دهد، Microsoft.AspNetCore.Mvc.Testing نام دارد. این بسته، کار کپی فایل‌های پروژه‌ی اصلی و همچنین تنظیم مسیر پروژه را به این مسیر جدید نیز انجام می‌دهد.
4) روش افزودن بسته‌های MSTest را مشاهده می‌کنید.
5) همچنین جهت ساده‌تر شدن بررسی نتایج آزمون‌های انجام شده می‌توان از fluentassertions نیز استفاده کرد.


راه اندازی هاست وب آزمایشی جهت انجام آزمون‌های واحد

پس از انجام تنظیمات ابتدایی پروژه‌ی آزمون یکپارچگی، نیاز است یک WebApplicationFactory سفارشی را ایجاد کرد:
using ASPNETCore2JwtAuthentication.WebApp;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;

namespace ASPNETCore2JwtAuthentication.IntegrationTests
{
    public class CustomWebApplicationFactory : WebApplicationFactory<Program>
    {
        protected override IWebHostBuilder CreateWebHostBuilder()
        {
            var builder = base.CreateWebHostBuilder();
            builder.ConfigureLogging(logging =>
            {
                //TODO: ...
            });
            return builder;
        }

        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureTestServices(services =>
            {
                // Don't run `IHostedService`s when running as a test
                services.RemoveAll(typeof(IHostedService));
            });
        }
    }
}
در این تعریف، Program در <WebApplicationFactory<Program، دقیقا به همان کلاس Program برنامه‌ی اصلی وب اشاره می‌کند. به همین جهت امضای این کلاس در نگارش 3.1 تغییر کرده‌است تا با WebApplicationFactory بسته‌ی Microsoft.AspNetCore.Mvc.Testing هماهنگ شود.
در ادامه روش سفارشی سازی WebApplicationFactory  را مشاهده می‌کنید. برای مثال اگر خواستید سرویس‌ها و تنظیمات پیش‌فرض برنامه‌ی اصلی را تغییر دهید می‌توانید متد CreateWebHostBuilder را بازنویسی کنید و یا اگر خواستید سرویس جدیدی را اضافه و یا حذف کنید، می‌توان متد ConfigureWebHost را بازنویسی کرد.


استفاده از WebApplicationFactory سفارشی، جهت ایجاد یک HttpClient

هدف اصلی از ایجاد CustomWebApplicationFactory نه فقط راه اندازی یک هاست وب سفارشی است، بلکه توسط متد CreateClient آن می‌توان به یک HttpClient دسترسی یافت که قابلیت ارسال اطلاعات را به برنامه‌ی وبی که در پشت صحنه راه اندازی می‌شود، دارا است. کار CustomWebApplicationFactory شبیه به راه اندازی dotnet run در پشت صحنه‌است. در اینجا دیگر نیازی نیست تا اینکار را به صورت دستی انجام داد. به همین جهت چون برنامه‌ی وب اصلی به نحو متداولی در پشت صحنه اجرا می‌شود، عموما راه اندازی آن که شامل تنظیمات اولیه و یا حتی ایجاد بانک اطلاعاتی است، کمی کند است و اگر قرار باشد هربار اینکار صورت گیرد، به آزمون‌های بسیار کندی خواهیم رسید. به همین جهت می‌توان یک کلاس singleton را برای مدیریت تک وهله‌ی نهایی HttpClient آن به صورت زیر ایجاد کرد:
using System;
using System.Threading;
using System.Net.Http;

namespace ASPNETCore2JwtAuthentication.IntegrationTests
{
    public static class TestsHttpClient
    {
        private static readonly Lazy<HttpClient> _serviceProviderBuilder =
                new Lazy<HttpClient>(getHttpClient, LazyThreadSafetyMode.ExecutionAndPublication);

        /// <summary>
        /// A lazy loaded thread-safe singleton
        /// </summary>
        public static HttpClient Instance { get; } = _serviceProviderBuilder.Value;

        private static HttpClient getHttpClient()
        {
            var services = new CustomWebApplicationFactory();
            return services.CreateClient(); //NOTE: This action is very time consuming, so it should be defined as a singleton.
        }
    }
}
مزیت کار با این کلاس، عدم راه اندازی مجدد برنامه به ازای هر آزمون انجام شده‌است. چون به ازای هر آزمونی که خواهیم نوشت، نیاز است به HttpClient دسترسی داشته باشیم.


نوشتن اولین آزمون یکپارچگی

پس از تنظیم هاست وب آزمایشی و ایجاد یک HttpClient از پیش تنظیم شده که به آن اشاره می‌کند، اکنون می‌توان اولین آزمون یکپارچگی را به صورت زیر نوشت:
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ASPNETCore2JwtAuthentication.IntegrationTests
{
    [TestClass]
    public class JwtTests
    {
        [TestMethod]
        public async Task TestLoginWorks()
        {
            // Arrange
            var client = TestsHttpClient.Instance;

            // Act
            var token = await doLoginAsync(client);

            // Assert
            token.Should().NotBeNull();
            token.AccessToken.Should().NotBeNullOrEmpty();
            token.RefreshToken.Should().NotBeNullOrEmpty();
        }

        [TestMethod]
        public async Task TestCallProtectedApiWorks()
        {
            // Arrange
            var client = TestsHttpClient.Instance;

            // Act
            var token = await doLoginAsync(client);

            // Assert
            token.Should().NotBeNull();
            token.AccessToken.Should().NotBeNullOrEmpty();
            token.RefreshToken.Should().NotBeNullOrEmpty();

            // Act
            const string protectedApiUrl = "/api/MyProtectedApi";
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
            var response = await client.GetAsync(protectedApiUrl);
            response.EnsureSuccessStatusCode();

            // Assert
            var responseString = await response.Content.ReadAsStringAsync();
            responseString.Should().NotBeNullOrEmpty();
            var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
            var apiResponse = JsonSerializer.Deserialize<MyProtectedApiResponse>(responseString, options);
            apiResponse.Title.Should().NotBeNullOrEmpty();
            apiResponse.Title.Should().Be("Hello from My Protected Controller! [Authorize]");
        }

        private static async Task<Token> doLoginAsync(HttpClient client)
        {
            const string loginUrl = "/api/account/login";
            var user = new { Username = "Vahid", Password = "1234" };
            var response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Post, loginUrl)
            {
                Content = new StringContent(JsonSerializer.Serialize(user), Encoding.UTF8, "application/json")
            });
            response.EnsureSuccessStatusCode();
            var responseString = await response.Content.ReadAsStringAsync();
            responseString.Should().NotBeNullOrEmpty();
            return JsonSerializer.Deserialize<Token>(responseString);
        }
    }
}
توضیحات:
- در هر آزمونی نیاز است در ابتدا به TestsHttpClient.Instance، که همان HttpClient ساخته شده‌ی توسط CustomWebApplicationFactory است، دسترسی یافت و همانطور که عنوان شد، دسترسی به وهله‌ای از HttpClient که به هاست وب آزمایشی برنامه‌ی اصلی اشاره می‌کند، عموما بسیار زمانبراست و برای مثال در دو آزمایش نوشته شده‌ی در اینجا اگر قرا باشد هربار اینکار از صفر انجام شود، زمان به اتمام رسیدن این آزمایش‌ها بسیار طولانی خواهد شد. به همین جهت طول عمر TestsHttpClient را singleton تعریف کردیم تا فقط یکبار کار برپایی وب سرور آزمایشی در پشت صحنه انجام شود.
- سپس مابقی کار، همان روش استاندارد کار با HttpClient است. در ابتدا درخواستی را به سمت سرور آزمایشی که در پشت صحنه در حال اجرا است، ارسال می‌کنیم. چون HttpClient دریافتی توسط  CustomWebApplicationFactory تنظیم شده‌است، دیگر نیازی به ذکر آدرس پایه‌ی وب سایت مانند https://localhost:5001 نیست و آدرس‌های ذکر شده‌ی در اینجا، نسبی هستند. سپس محتوای Response دریافتی از سرور را جهت تکمیل آزمایشات، بررسی خواهیم کرد.


یک نکته: اگر OpenAPI را در برنامه‌های Web API فعال کنید، می‌توان با استفاده از ابزارهای تولید کد، کدهای مرتبط با HttpClient را نیز به صورت خودکار تولید و سپس از آن‌ها در اینجا استفاده کرد.


اجرای آزمون‌های یکپارچگی نوشته شده

چون ظاهر این آزمون‌ها با آزمون‌های واحد MSTest یا هر فریم ورک مشابه دیگری یکسان است، می‌توان از امکانات IDEها برای اجرای آن‌ها استفاده کرد و یا حتی می‌توان دستور dotnet test را نیز در ریشه‌ی این پروژه‌ی جدید برای اجرای تمام آزمون‌های نوشته شده، اجرا کرد:



کدهای کامل این مطلب را در اینجا می‌توانید مشاهده کنید.
مطالب
یک نکته درباره Angular routeProvider
بعد از مطالعه پست‌های ^ و ^    نکته ای به ذهنم رسید که بیان آن از بنده و مطالعه آن توسط شما خالی از لطف نیست. اگر مثال‌های پیاده سازی شده در پست‌های ^ و ^ را با AngularJs نسخه 1.2 اجرا نمایید به طور حتم با خطا روبرو می‌شوید و نتیجه مورد نظر حاصل نمی‌شود. در این پست نیز توسط یکی از دوستان اشاره ای به این مطلب شد.
دلیل خطا این است که از نسخه 1.2 به بعد در Angular سیستم مسیر یابی به این شکل امکان پذیر نیست و بخش مسیریابی به یک فایل دیگر به نام angular-route.js منتقل شده است. در نتیجه اگر به سبک نسخه‌های قبلی Angular از سیستم مسیریابی استفاده نمایید با خطا مواجه خواهید شد و خطای مورد نظر هم مربوط به عدم توانایی در تزریق وابستگی routeProvider$ به ماژول مورد نظر است. حال راه حل چیست؟
کافیست در هنگام تعریف ماژول، ngRoute را به عنوان وابستگی ماژول تعیین نمایید. و از طرفی فایل اسکریپتی angular-route.js را بعد از angular.js فراخوانی کنید.
بررسی مثال:
کد‌های زیر مربوط به مثال‌های پست قبلی می‌باشد که شرح کامل آن در این پست است:
var myFirstRoute = angular.module('myFirstRoute', []);
   
myFirstRoute.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/pageOne', {
        templateUrl: 'templates/page_one.html',
        controller: 'ShowPage1Controller'
      }).
      when('/pageTwo', {
        templateUrl: 'templates/page_two.html',
        controller: 'ShowPage2Controller'
      }).
      otherwise({
        redirectTo: '/pageOne'
      });
}]);
 
 
myFirstRoute.controller('ShowPage1Controller', function($scope) {
    $scope.message = 'Content of page-one.html';
});
  
  
myFirstRoute.controller('ShowPage2Controller', function($scope) {
    $scope.message = 'Content of page-two.html';
});
برای هماهنگ کردن مثال بالا با نسخه 1.2 باید به روش زیر عمل نمود:
var myFirstRoute = angular.module('myFirstRoute',['ngRoute']);
   
myFirstRoute.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/pageOne', {
        templateUrl: 'templates/page_one.html',
        controller: 'ShowPage1Controller'
      }).
      when('/pageTwo', {
        templateUrl: 'templates/page_two.html',
        controller: 'ShowPage2Controller'
      }).
      otherwise({
        redirectTo: '/pageOne'
      });
}]);
تنها تغییر، مشخص کردن ngRoute به عنوان وابستگی ماژول myFirstRoute است(خط اول). نکته دیگر لود فایل angular-route.js قبل از فراخوانی فایل بالا و بعد از فراخوانی فایل angular.js است:
<body ng-app="app">
   
    <div>
        <div>
        <div>
            <ul>
                <li><a href="#pageOne"> Show page one </a></li>
                <li><a href="#pageTwo"> Show page two </a></li>
            </ul>
        </div>
        <div>
            <div ng-view></div>
        </div>
        </div>
    </div>
   
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
     <script src="angular-route.js"></script>
    <script src="app.js"></script>
   
  </body>

مطالب
تاریخ شمسی با Extension Method برای DateTime
برای تبدیل تاریخ میلادی به شمسی راه‌های زیادی وجود داره که خوب این هم یک نوع از اونها هست.
برای این کار با استفاده از Extension Method برای کلاس DateTime تاریخ رو تبدیل می‌کنیم.
برای فرمت‌های‌های نمایشی تاریخ هم می‌بایست به صورت متن به Method بدهید تا تاریخ رو به اون صورتی که می‌خواهید نمایش بدهد، برای نوشتن فرمت‌ها هم می‌توانید از این لینک استفاده کنید.
خوب برای این کار ابتدا یک کلاس با نام PersianDateExtensionMethods  بسازید و تمام کد زیر رو جایگزین کد‌های کلاس کنید:
using System;
using System.Globalization;
using System.Reflection;

namespace System 
{
    public static class PersianDateExtensionMethods
    {
        private static CultureInfo _Culture;
        public static CultureInfo GetPersianCulture()
        {
            if (_Culture == null)
            {
                _Culture = new CultureInfo("fa-IR");
                DateTimeFormatInfo formatInfo = _Culture.DateTimeFormat;
                formatInfo.AbbreviatedDayNames = new[] { "ی", "د", "س", "چ", "پ", "ج", "ش" };
                formatInfo.DayNames = new[] { "یکشنبه", "دوشنبه", "سه شنبه", "چهار شنبه", "پنجشنبه", "جمعه", "شنبه" };
                var monthNames = new[]
                {
                    "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن",
                    "اسفند",
                    ""
                };
                formatInfo.AbbreviatedMonthNames =
                    formatInfo.MonthNames =
                    formatInfo.MonthGenitiveNames = formatInfo.AbbreviatedMonthGenitiveNames = monthNames;
                formatInfo.AMDesignator = "ق.ظ";
                formatInfo.PMDesignator = "ب.ظ";
                formatInfo.ShortDatePattern = "yyyy/MM/dd";
                formatInfo.LongDatePattern = "dddd, dd MMMM,yyyy";
                formatInfo.FirstDayOfWeek = DayOfWeek.Saturday;
                System.Globalization.Calendar cal = new PersianCalendar();

                FieldInfo fieldInfo = _Culture.GetType().GetField("calendar", BindingFlags.NonPublic | BindingFlags.Instance);
                if (fieldInfo != null)
                    fieldInfo.SetValue(_Culture, cal);

                FieldInfo info = formatInfo.GetType().GetField("calendar", BindingFlags.NonPublic | BindingFlags.Instance);
                if (info != null)
                    info.SetValue(formatInfo, cal);

                _Culture.NumberFormat.NumberDecimalSeparator = "/";
                _Culture.NumberFormat.DigitSubstitution = DigitShapes.NativeNational;
                _Culture.NumberFormat.NumberNegativePattern = 0;
            }
            return _Culture;
        }

        public static string ToPeString(this DateTime date,string format = "yyyy/MM/dd")
        {
            return date.ToString(format,GetPersianCulture());
        }
    }
}

در کد بالا ما بااستفاده از متد GetPersianCulture یک کالچر برای تاریخ شمسی و فرمت‌های اون می‌سازیم و در متد ToPeString از اون استفاده و به متد ToString از کلاس DateTime پاس می‌دهیم.
چند مثال هم برای استفاده از متد ToPeString :
var date1 = DateTime.Now.ToPeString("yyyy/MM/dd");
var date2 = DateTime.Now.ToPeString("dddd, dd MMMM,yyyy");

//Output:
//1391/12/13
//یکشنبه, 13 اسفند,1391

نکته : با استفاده از Culture ای که در کلاس بالا در متد GetPersianCulture ساخته می‌شود امکانش هست که خود کلاس DateTime رو به شمسی تبدیل کرد، برای این کار باید به صورت زیر عمل کرد:
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture =  PersianDateExtensionMethods.GetPersianCulture();

var d1 = DateTime.Now.ToString();
//Output : 1391/12/13 11:25:44 ب.ظ

یک نکته‌ی تکمیلی: کتابخانه‌ی «DNTPersianUtils.Core» به همراه نکات این مطلب است؛ مانند:
Thread.CurrentThread.CurrentCulture = PersianCulture.Instance;
مطالب
طراحی یک گرید با Angular و ASP.NET Core - قسمت اول - پیاده سازی سمت سرور
یکی از نیازهای مهم هر برنامه‌ای، امکانات گزارشگیری و نمایش لیستی از اطلاعات است. به همین جهت در طی چند قسمت، قصد داریم یک گرید ساده را به همراه امکانات نمایش، صفحه بندی و مرتب سازی اطلاعات، تنها به کمک امکانات توکار Angular و ASP.NET Core تهیه کنیم.




تهیه مقدمات سمت سرور

مدلی که در تصویر فوق نمایش داده شده‌است، در سمت سرور چنین ساختاری را دارد:
namespace AngularTemplateDrivenFormsLab.Models
{
    public class Product
    {
        public int ProductId { set; get; }
        public string ProductName { set; get; }
        public decimal Price { set; get; }
        public bool IsAvailable { set; get; }
    }
}

همچنین یک منبع ساده درون حافظه‌ای را نیز جهت بازگشت 1500 محصول تهیه کرده‌ایم. علت اینجا است که ساختار نهایی اطلاعات آن شبیه به ساختار اطلاعات حاصل از ORMها باشد و همچنین به سادگی قابلیت اجرا و بررسی را داشته باشد:
using System.Collections.Generic;

namespace AngularTemplateDrivenFormsLab.Models
{
    public static class ProductDataSource
    {
        private static readonly IList<Product> _cachedItems;
        static ProductDataSource()
        {
            _cachedItems = createProductsDataSource();
        }

        public static IList<Product> LatestProducts
        {
            get { return _cachedItems; }
        }

        private static IList<Product> createProductsDataSource()
        {
            var list = new List<Product>();
            for (var i = 0; i < 1500; i++)
            {
                list.Add(new Product
                {
                    ProductId = i + 1,
                    ProductName = "نام " + (i + 1),
                    IsAvailable = (i % 2 == 0),
                    Price = 1000 + i
                });
            }
            return list;
        }
    }
}


مشخص کردن قرارداد اطلاعات دریافتی از سمت کلاینت

زمانیکه کلاینت Angular برنامه، اطلاعاتی را به سمت سرور ارسال می‌کند، یک چنین ساختاری را دریافت خواهیم کرد:
 http://localhost:5000/api/Product/GetPagedProducts?sortBy=productId&isAscending=true&page=2&pageSize=7
درخواست، به یک اکشن متد مشخص ارسال شده‌است و حاوی یک سری کوئری استرینگ مشخص کننده‌ی نام خاصیت یا فیلدی که قرار است مرتب سازی بر اساس آن صورت گیرد، صعودی و نزولی بودن این مرتب سازی، شماره صفحه‌ی درخواستی و تعداد آیتم‌های در هر صفحه، می‌باشد.
بنابراین اینترفیسی را دقیقا بر اساس نام کلیدهای همین کوئری استرینگ‌ها تهیه می‌کنیم:
    public interface IPagedQueryModel
    {
        string SortBy { get; set; }
        bool IsAscending { get; set; }
        int Page { get; set; }
        int PageSize { get; set; }
    }


کاهش کدهای تکراری صفحه بندی اطلاعات در سمت سرور

با تعریف این اینترفیس چند هدف را دنبال خواهیم کرد:
الف) استاندارد سازی نام خواصی که مدنظر هستند و اعمال یک دست آن‌ها به ViewModelهایی که قرار است از سمت کلاینت دریافت شوند:
    public class ProductQueryViewModel : IPagedQueryModel
    {
        // ... other properties ...

        public string SortBy { get; set; }
        public bool IsAscending { get; set; }
        public int Page { get; set; }
        public int PageSize { get; set; }
    }
برای مثال در اینجا یک ViewModel مخصوص Product را ایجاد کرده‌ایم که می‌تواند شامل یک سری فیلد دیگر نیز باشد. اما یک سری خواص مرتب سازی و صفحه بندی آن، یک دست و مشخص هستند.

ب) امکان استفاده‌ی از این قرارداد در متدهای کمکی که نوشته خواهند شد:
    public static class IQueryableExtensions
    {
        public static IQueryable<T> ApplyPaging<T>(
          this IQueryable<T> query, IPagedQueryModel model)
        {
            if (model.Page <= 0)
            {
                model.Page = 1;
            }

            if (model.PageSize <= 0)
            {
                model.PageSize = 10;
            }

            return query.Skip((model.Page - 1) * model.PageSize).Take(model.PageSize);
        }
    }
در حین ارائه‌ی اطلاعات نهایی صفحه بندی شده به کلاینت، همیشه یک قسمت Skip و Take وجود خواهند داشت. این متدها نیز باید بر اساس یک سری خاصیت و مقدار مشخص، مانند صفحه شماره صفحه‌ی جاری و تعداد ردیف‌های در هر صفحه کار کنند. اکنون که قرارداد IPagedQueryModel را تهیه کرده‌ایم و ViewModel ما نیز آن‌را پیاده سازی می‌کند، مطمئن خواهیم بود که می‌توان به سادگی به این خواص دسترسی یافت و همچنین این کد تکراری صفحه بندی را توانسته‌ایم به یک متد الحاقی کمکی منتقل و حجم کدهای نهایی را کاهش دهیم.
همچنین دراینجا بجای صدور استثناء در حین دریافت مقادیر غیرمعتبر شماره صفحه یا تعداد ردیف‌های هر صفحه، از حالت «بخشنده» بجای حالت «تدافعی» استفاده شده‌است. برای مثال در حالت «بخشنده» اگر شماره صفحه منفی بود، همان صفحه‌ی اول اطلاعات نمایش داده می‌شود؛ بجای صدور یک استثناء (یا حالت «تدافعی و defensive programming»).


کاهش کدهای تکراری مرتب سازی اطلاعات در سمت سرور

همانطور که عنوان شد، از سمت کلاینت، چنین لینکی را دریافت خواهیم کرد:
 http://localhost:5000/api/Product/GetPagedProducts?sortBy=productId&isAscending=true&page=2&pageSize=7
در اینجا، هربار sortBy و isAscending می‌توانند متفاوت باشند و در نهایت به یک چنین کدهایی خواهیم رسید:
if(model.SortBy == "f1")
{
   query = !model.IsAscending ? query.OrderByDescending(x => x.F1) : query.OrderBy(x => x.F1);
}
امکان نوشتن این نوع کوئری‌ها توسط قابلیت تعریف زنجیره‌وار کوئری‌های LINQ میسر است و در نهایت زمانیکه ToList نهایی فراخوانی می‌شود، آنگاه است که کوئری SQL معادل این‌ها تولید خواهد شد.
اما در این حالت نیاز است به ازای تک تک فیلدها، یکبار if/else یافتن فیلد و سپس بررسی صعودی و نزولی بودن آن‌ها صورت گیرد که در نهایت ظاهر خوشایندی را نخواهند داشت.

یک نمونه از مزیت‌های تهیه‌ی قرارداد IPagedQueryModel را در حین نوشتن متد ApplyPaging مشاهده کردید. نمونه‌ی دیگر آن کاهش کدهای تکراری مرتب سازی اطلاعات است:
namespace AngularTemplateDrivenFormsLab.Utils
{
    public static class IQueryableExtensions
    {
        public static IQueryable<T> ApplyOrdering<T>(
          this IQueryable<T> query,
          IPagedQueryModel model,
          IDictionary<string, Expression<Func<T, object>>> columnsMap)
        {
            if (string.IsNullOrWhiteSpace(model.SortBy) || !columnsMap.ContainsKey(model.SortBy))
            {
                return query;
            }

            if (model.IsAscending)
            {
                return query.OrderBy(columnsMap[model.SortBy]);
            }
            else
            {
                return query.OrderByDescending(columnsMap[model.SortBy]);
            }
        }
    }
}
در اینجا متد الحاقی ApplyOrdering، کار دریافت و بررسی خواص مدنظر را از طریق یک دیکشنری انجام می‌دهد و مابقی کدهای تکراری نوشته شده، حذف خواهند شد. برای نمونه، مثالی از نحوه‌ی استفاده‌ی از این متد الحاقی را در ذیل مشاهده می‌کنید:
var columnsMap = new Dictionary<string, Expression<Func<Product, object>>>()
            {
                ["productId"] = p => p.ProductId,
                ["productName"] = p => p.ProductName,
                ["isAvailable"] = p => p.IsAvailable,
                ["price"] = p => p.Price
            };
query = query.ApplyOrdering(queryModel, columnsMap);
ابتدا نگاشتی بین خواص رشته‌ای دریافتی از سمت کلاینت، با خواص شیء Product برقرار شده‌است. سپس این نگاشت به متد ApplyOrdering ارسال شده‌است. به این ترتیب از نوشتن تعداد زیادی if/else یا switch بر اساس خاصیت SortBy بی‌نیاز شده‌ایم، حجم کدهای نهایی تولیدی کاهش پیدا می‌کنند و برنامه نیز خواناتر می‌شود.


تهیه قرارداد ساختار اطلاعات بازگشتی از سمت سرور به سمت کلاینت

تا اینجا قرارداد اطلاعات دریافتی از سمت کلاینت را مشخص کردیم. همچنین از آن برای ساده سازی عملیات مرتب سازی و صفحه بندی اطلاعات کمک گرفتیم. در ادامه نیاز است مشخص کنیم چگونه می‌خواهیم این اطلاعات را به سمت کلاینت ارسال کنیم:
using System.Collections.Generic;

namespace AngularTemplateDrivenFormsLab.Models
{
    public class PagedQueryResult<T>
    {
        public int TotalItems { get; set; }
        public IEnumerable<T> Items { get; set; }
    }
}
عموما ساختار اطلاعات صفحه بندی شده، شامل تعداد کل آیتم‌های تمام صفحات (خاصیت TotalItems) و تنها اطلاعات ردیف‌های صفحه‌ی جاری درخواستی (خاصیت Items) است و چون در اینجا این Items از هر نوعی می‌تواند باشد، بهتر است آن‌را جنریک تعریف کنیم.


پایان کار بازگشت اطلاعات سمت سرور با تهیه اکشن متد GetPagedProducts

در اینجا اکشن متدی را مشاهده می‌کنید که اطلاعات نهایی مرتب سازی شده و صفحه بندی شده را بازگشت می‌دهد:
    [Route("api/[controller]")]
    public class ProductController : Controller
    {
        [HttpGet("[action]")]
        public PagedQueryResult<Product> GetPagedProducts(ProductQueryViewModel queryModel)
        {
            var pagedResult = new PagedQueryResult<Product>();

            var query = ProductDataSource.LatestProducts
                                         .AsQueryable();

            //TODO: Apply Filtering ... .where(p => p....) ...

            var columnsMap = new Dictionary<string, Expression<Func<Product, object>>>()
            {
                ["productId"] = p => p.ProductId,
                ["productName"] = p => p.ProductName,
                ["isAvailable"] = p => p.IsAvailable,
                ["price"] = p => p.Price
            };
            query = query.ApplyOrdering(queryModel, columnsMap);

            pagedResult.TotalItems = query.Count();
            query = query.ApplyPaging(queryModel);
            pagedResult.Items = query.ToList();
            return pagedResult;
        }
    }
توضیحات تکمیلی

امضای این اکشن متد، شامل دو مورد مهم است:
 public PagedQueryResult<Product> GetPagedProducts(ProductQueryViewModel queryModel)
الف) ViewModel ایی که پیاده سازی کننده‌ی IPagedQueryModel است. به این ترتیب می‌توان به ساختار استانداردی از مقادیر مورد نیاز برای صفحه بندی و مرتب سازی رسید و همچنین این ViewModel می‌تواند حاوی خواص اضافی ویژه‌ی خود نیز باشد.
ب) خروجی آن از نوع PagedQueryResult است که در مورد آن توضیح داده شد. بنابراین باید به همراه تعداد کل رکوردهای جدول محصولات و همچنین تنها آیتم‌های صفحه‌ی جاری درخواستی باشد.

در ابتدای کار، دسترسی به منبع داده‌ی درون حافظه‌ای ابتدای برنامه را مشاهده می‌کنید. برای اینکه کارکرد آن‌را شبیه به کوئری‌های ORMها کنیم، یک AsQueryable نیز به انتهای آن اضافه شده‌است.
 var query = ProductDataSource.LatestProducts
  .AsQueryable();

//TODO: Apply Filtering ... .where(p => p....) ...
اینجا دقیقا جائی است که در صورت نیاز می‌توان کار فیلتر اطلاعات و اعمال متد where را انجام داد.

پس از مشخص شدن منبع داده و فیلتر آن در صورت نیاز، اکنون نوبت به مرتب سازی اطلاعات است:
var columnsMap = new Dictionary<string, Expression<Func<Product, object>>>()
            {
                ["productId"] = p => p.ProductId,
                ["productName"] = p => p.ProductName,
                ["isAvailable"] = p => p.IsAvailable,
                ["price"] = p => p.Price
            };
query = query.ApplyOrdering(queryModel, columnsMap);
توضیحات این مورد را پیشتر مطالعه کردید و هدف از آن، تهیه یک نگاشت ساده‌ی بین خواص رشته‌ای رسیده‌ی از سمت کلاینت به خواص مدل متناظر با آن است و سپس ارسال آن‌ها به همراه queryModel دریافتی از کاربر، برای اعمال مرتب سازی نهایی.

در آخر مطابق ساختار PagedQueryResult بازگشتی، ابتدا تعداد کل آیتم‌های منبع داده محاسبه شده‌است و سپس صفحه بندی به آن اعمال گردیده‌است. این ترتیب نیز مهم است و گرنه TotalItems دقیقا به همان تعداد ردیف‌های صفحه‌ی جاری محاسبه می‌شود:
var pagedResult = new PagedQueryResult<Product>();
pagedResult.TotalItems = query.Count();
query = query.ApplyPaging(queryModel);
pagedResult.Items = query.ToList();
return pagedResult;


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


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
مطالب
استفاده از thickbox در بلاگر

Thickbox یکی از پلاگین‌های jQuery است که جهت نمایش صفحات modal بکار می‌رود.

روش استفاده بسیار ساده‌ای هم دارد:
الف) jquery.js باید به صفحه معرفی شود.
ب) سپس thickbox.js باید به صفحه الحاق شود.
ج) و فایل css آن یعنی thickbox.css نیز باید به صفحه افزوده شود.

برای استفاده از آن کافی است یک لینک به صفحه اضافه کنید که ویژگی class آن مساوی thickbox باشد. مثلا:
<a href="#TB_inline?height=155&width=300&inlineId=hiddenModalContent&modal=true" class="thickbox">Show hidden modal content.</a>
TB_inline و طول و عرض و غیره، جزو خواص استاندارد این پلاگین است. inlineId جهت معرفی id یک div در صفحه بکار می‌رود که قرار است محتویات آن زمانیکه روی این لینک کلیک شد به صورت modal نمایش داده شود.
<a href="iframeModal.html?placeValuesBeforeTB_=savedValues&TB_iframe=true&height=200&width=300&modal=true" title="add a caption to title attribute / or leave blank" class="thickbox">Open iFrame Modal</a>
و یا مانند مثال فوق می‌تواند یک صفحه خارجی را نیز به صورت modal بارگذاری و نمایش دهد. Title بکارگرفته شده، همان عنوان صفحه Modal ما خواهد بود.
و مثال‌های دیگری از این دست که در صفحه رسمی آن موجود است و نیازی به تکرار آن‌ها نیست.

اما مشکلی که در بلاگر وجود دارد این است که شما نمی‌توانید این ویژگی‌ها را در بسیاری از موارد به صورت دستی تنظیم کنید. یک ویجت ظاهر می‌شود که فقط به شما امکان ثبت یک عنوان و لینک مربوطه را می‌دهد و همین.
برای انتساب ویژگی‌های thickBox به این نوع لینک‌ها که کنترلی روی آن نیست می‌توان از خود jQuery کمک گرفت:
$(document).ready(function(){
$("a[href='http://www.mysite.com/page.htm?TB_iframe=true&height=340&width=530']").each(function(){
var obj = $(this);
obj.attr("title","Contact me");
obj.attr("class","thickbox");
}).bind("contextmenu",function(e){
return false;
});
});
در اینجا http://www.mysite.com/page.htm?TB_iframe=true&height=340&width=530 همان صفحه‌ای که قرار است پس از کلیک بر روی لینکی منتسب به آن در صفحه به صورت modal باز شود. با استفاده از جی‌کوئری آن‌را یافته (لینکی که href آن مساوی مقدار یاد شده است) و سپس ویژگی‌های title و class آن را به صورت پویا تنظیم می‌کنیم تا توسط thickbox قابل شناسایی شود.
همچنین در اینجا کلیک راست نیز بر روی این لینک بسته شده تا کاربر فقط کلیک معمولی را انجام دهد (فقط روی همین یک لینک در صفحه اعمال می‌شود و نه کل صفحه).

نظرات مطالب
صفحه بندی و مرتب سازی خودکار اطلاعات به کمک jqGrid در ASP.NET MVC
columnChooser رویداد done هم دارد که از آن می‌شود برای آنالیز ستون‌ها استفاده کرد:
            .jqGrid('navButtonAdd', '#pager',
            {
                caption: "تنظیم نمایش ستون‌ها", title: "Reorder Columns",
                onClickButton: function () {
                    jQuery("#list").jqGrid('columnChooser', {
                        done: function (perm) {
                            if (perm) {
                                $("#list").jqGrid("remapColumns", perm, true, false);
                            }

                            var colModel = $("#list").jqGrid('getGridParam', 'colModel');
                            var hiddenColumns = new Array();
                            for (var i = 0; i < colModel.length; i++) {
                                if (colModel[i].hidden) {
                                    hiddenColumns.push(colModel[i].name);
                                }
                            }

                            $.ajax({
                                type: "POST",
                                url: "@Url.Action("HiddenColumns","Home")",
                                dataType: "json",
                                traditional: true,
                                data: { hiddenColumns: hiddenColumns }
                            });
                        }
                    });
                }
            })
متد getGridParam با پارامتر colModel، آرایه‌ای از خواص ستون‌ها را بر می‌گرداند.
var colModel = $("#list").jqGrid('getGridParam', 'colModel');
در این خواص اگر hidden مساوی true بود، یعنی مخفی شده‌است. در نهایت از این‌ها می‌شود یک آرایه را تشکیل داد و به سرور ارسال کرد:
        public ActionResult HiddenColumns(string[] hiddenColumns)
        {
            //todo: save it in the DB or cookies or session ....

            return Content("OK");
        }
امضای اکشن متدی است که لیست نام ستون‌های مخفی را دریافت می‌کند.
مطالب
کوئری نویسی در EF Core - قسمت هفتم - کار با رشته‌‌ها
هدف از این سری مثال‌ها، آشنایی با متدها و توابعی است که در حین کار با خواص رشته‌ای در LINQ to Entities، مجاز به استفاده‌ی از آن‌ها هستیم و همچنین اگر تابعی در EF-Core هنوز تعریف نشده بود، راه حل چیست.


مثال 1: نام تمام کاربران را با قالب 'Surname, Firstname'  نمایش دهید.

var members = context.Members
                                    .Select(member => new { Name = member.Surname + ", " + member.FirstName })
                                    .ToList();
متد Select می‌تواند به همراه اعمال محاسباتی ساده‌ای نیز باشد که نمونه‌ای از آن‌را در اینجا مشاهده می‌کنید.
با این خروجی:



مثال 2: تمام امکاناتی را که با Tennis شروع می‌شوند، لیست کنید.
این گزارش به همراه تمام ستون‌های جدول است.

var facilities = context.Facilities
                                        .Where(facility => facility.Name.StartsWith("Tennis"))
                                        .ToList();
متدهای استانداردی مانند StartsWith، EndsWith و Contains را می‌توان بر روی خواص رشته‌ای بکار برد.
با این خروجی:



مثال 3: تمام امکاناتی را که با tennis شروع می‌شوند، لیست کنید. این جستجو باید غیرحساس به بزرگی و کوچکی حروف باشد.
این گزارش به همراه تمام ستون‌های جدول است.

نیازی به انجام مجزای این تمرین نیست؛ چون پاسخ آن همان پاسخ مثال 2 است. Collation پیش‌فرض در SQL Server، غیرحساس به بزرگی و کوچکی حروف است. بنابراین چه tennis را جستجو کنیم و یا TeNnis را، تفاوتی نمی‌کند.


مثال 4: شماره تلفن‌های دارای پرانتز را لیست کنید.
این گزارش باید به همراه ستون‌های memid, telephone باشد.

روش اول: در اینجا دوبار از متد Contains استفاده شده‌است:
var members = context.Members
                                    .Select(member => new { member.MemId, member.Telephone })
                                    .Where(member => member.Telephone.Contains("(")
                                                    && member.Telephone.Contains(")"))
                                    .ToList();
با این خروجی:


روش دوم: اگر می‌خواهیم کنترل بیشتری را بر روی خروجی نهایی LIKE تولیدی داشته باشیم، می‌توان از متد سفارشی استاندارد EF.Functions.Like استفاده کرد که از حروف wild cards نیز پشتیبانی می‌کند:
members = context.Members
                                    .Select(member => new { member.MemId, member.Telephone })
                                    .Where(member => EF.Functions.Like(member.Telephone, "%[()]%"))
                                    .ToList();
با این خروجی:



مثال 5: کد پستی‌ها 5 رقمی هستند. گزارشی را تهیه کنید که در آن اگر کدپستی کمتر از 5 رقم بود، ابتدای آن با صفر شروع شود.
هدف اصلی از این مثال، اعمال متد PadLeft(5, '0') به خاصیت member.ZipCode است.

روش اول: EF-Core فعلا قابلیت ترجمه‌ی PadLeft(5, '0') را به معادل SQL آن‌را ندارد. به همین جهت مجبور هستیم ابتدا ZipCode‌ها را به صورت رشته‌ای بازگشت دهیم که در اینجا استفاده‌ی از Convert.ToString مجاز است.
با این خروجی:
SELECT   CONVERT (NVARCHAR (MAX), [m].[ZipCode]) AS [Zip]
FROM     [Members] AS [m]
ORDER BY CONVERT (NVARCHAR (MAX), [m].[ZipCode]);
 سپس می‌توان بر روی لیست آماده‌ی موجود در حافظه، از LINQ to Objects استفاده کرد و در این حالت دسترسی کاملی به تمام امکانات زبان #C وجود دارد:
var members = context.Members
                                    .Select(member => new { ZipCode = Convert.ToString(member.ZipCode) })
                                    .OrderBy(m => m.ZipCode)
                                    .ToList();
// Now using LINQ to Objects
members = members.Select(member => new { ZipCode = member.ZipCode.PadLeft(5, '0') })
                                                    .OrderBy(m => m.ZipCode)
                                                    .ToList();

روش دوم: SQL Server به همراه تابع استانداردی به نام Replicate است که از آن می‌توان برای شبیه سازی PadLeft، بدون متوسل شدن به LINQ to Objects، استفاده کرد. اما چون این تابع هنوز به EF-Core معرفی نشده‌است، نیاز است خودمان اینکار را انجام دهیم. در این روش، از متد SqlDbFunctionsExtensions.SqlReplicate استفاده می‌شود. روش تعریف این نوع متدها را در مطلب «امکان تعریف توابع خاص بانک‌های اطلاعاتی در EF Core» پیشتر بررسی کرده‌ایم که برای مثال در اینجا چنین شکلی را پیدا می‌کند:
namespace EFCorePgExercises.Utils
{
    public static class SqlDbFunctionsExtensions
    {
        public static string SqlReplicate(string expression, int count)
            => throw new InvalidOperationException($"{nameof(SqlReplicate)} method cannot be called from the client side.");

        private static readonly MethodInfo _sqlReplicateMethodInfo = typeof(SqlDbFunctionsExtensions)
            .GetRuntimeMethod(
                nameof(SqlDbFunctionsExtensions.SqlReplicate),
                new[] { typeof(string), typeof(int) }
            );


        public static void AddCustomSqlFunctions(this ModelBuilder modelBuilder)
        {
            modelBuilder.HasDbFunction(_sqlReplicateMethodInfo)
                .HasTranslation(args =>
                {
                    return SqlFunctionExpression.Create("REPLICATE",
                        args,
                        _sqlReplicateMethodInfo.ReturnType,
                        typeMapping: null);
                });
        }
    }
}
پس از آن فقط کافی است متد AddCustomSqlFunctions را به Context برنامه معرفی کنیم:
namespace EFCorePgExercises.DataLayer
{
    public class ApplicationDbContext : DbContext
    {
         // ...

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
         // ...
            modelBuilder.AddCustomSqlFunctions();
         // ...
        }
    }
}
اکنون می‌توان از تابع SqlDbFunctionsExtensions.SqlReplicate جهت شبیه سازی PadLeft به صورت زیر استفاده کرد:
var newMembers = context.Members
                                        .Select(member => new
                                        {
                                            ZipCode =
                                                SqlDbFunctionsExtensions.SqlReplicate(
                                                    "0", 5 - Convert.ToString(member.ZipCode).Length)
                                                + member.ZipCode
                                        })
                        .OrderBy(m => m.ZipCode)
                        .ToList();
با این خروجی:



مثال 6: اولین حرف نام خانوادگی کاربران در کل ردیف‌های جدول چندبار تکرار شده‌است؟
این گزارش باید به همراه ستون‌های letter,  count باشد.

var members = context.Members
                                    .Select(member => new { Letter = member.Surname.Substring(0, 1) })
                                    .GroupBy(m => m.Letter)
                                    .Select(g => new
                                    {
                                        Letter = g.Key,
                                        Count = g.Count()
                                    })
                                    .OrderBy(r => r.Letter)
                                    .ToList();
هدف از این مثال بیان مجاز بودن استفاده‌ی از متد Substring بر روی خواص رشته‌ای است که EF-Core امکان ترجمه‌ی آن‌ها را به کدهای SQL دارد.
با این خروجی:



مثال 7: حروف '-','(',')', ' ' را از شماره تلفن‌ها حذف کنید.
این گزارش باید به همراه ستون‌های memid, telephone باشد.

بانک اطلاعاتی PostgreSQL به همراه تابع استاندارد regexp_replace است و می‌توان از آن برای حل یک چنین مسایلی استفاده کرد:
select memid, regexp_replace(telephone, '[^0-9]', '', 'g') as telephone
from members
order by memid;
اما SQL Server هنوز هم به همراه یک چنین تابعی نیست. بنابراین از روش زیر نیز می‌توان مثال جاری را حل کرد:
var members = context.Members
                                .Select(member => new
                                {
                                    member.MemId,
                                    Telephone = member.Telephone.Replace("-", "")
                                                        .Replace("(", "")
                                                        .Replace(")", "")
                                                        .Replace(" ", "")
                                })
                                .OrderBy(r => r.MemId)
                                .ToList();
با این خروجی:



کدهای کامل این قسمت را در اینجا می‌توانید مشاهده کنید.