نظرات مطالب
جایگزین کردن StructureMap با سیستم توکار تزریق وابستگی‌ها در ASP.NET Core 1.0
یک نکته‌ی تکمیلی
شاید جذاب‌ترین قسمت کار با استراکچرمپ، قسمت scan آن باشد که می‌تواند اتصالات را به صورت خودکار از اسمبلی‌های مختلفی تشخیص داده و قسمت تعاریف مقدماتی تزریق وابستگی‌های سیستم را کاهش دهد.کتابخانه‌ی Scrutor ، دقیقا شبیه به همین قابلیت را آن‌هم به صورت افزونه‌ای برای سیستم تزریق وابستگی‌های استاندارد NET Core. فراهم می‌کند.
مطالب
Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت
در حین کار با برنامه‌های وب، چشم‌پوشی از جاوا اسکریپت عملا ممکن نیست؛ هرچند با Blazor، امکان انجام کارهایی را یافته‌ایم که پیشتر با MVC و یا Razor pages میسر نبودند، اما هیچگاه به تنهایی نمی‌تواند جایگزین کامل جاوا اسکریپت، در تولید برنامه‌های وب باشد. بنابراین ضروری است که نحوه‌ی یکپارچگی جاوا اسکریپت را با برنامه‌های مبتنی بر Blazor، بررسی کنیم.


ایجاد کامپوننت جدید BlazorJS

برای بررسی نحوه‌ی تعامل جاوا اسکریپت و Blazor، در ابتدا کامپوننت جدید Pages\LearnBlazor\BlazorJS.razor را ایجاد کرده:
@page "/BlazorJS"

<h3>BlazorJS</h3>

@code
{
}
و همچنین مدخل منوی آن‌را نیز بر اساس مسیریابی ابتدای فایل این کامپوننت، به فایل Shared\NavMenu.razor اضافه می‌کنیم:
<li class="nav-item px-3">
    <NavLink class="nav-link" href="BlazorJS">
        <span class="oi oi-list-rich" aria-hidden="true"></span> BlazorJS
    </NavLink>
</li>


روش فراخوانی کدهای جاوا اسکریپتی از طریق کدهای سی‌شارپ Blazor

فرض کنید می‌خواهیم در حین کلیک بر روی دکمه‌ای مانند دکمه‌ی حذف، ابتدا تائیدیه‌ای را توسط تابع confirm جاوا اسکریپتی، از کاربر اخذ کنیم. روش انجام چنین کاری در برنامه‌های مبتنی بر Blazor به صورت زیر است:
@page "/BlazorJS"

@inject IJSRuntime JsRuntime

<h3>BlazorJS</h3>

<div class="row">
    <button class="btn btn-secondary" @onclick="TestConfirmBox">Test Confirm Button</button>
</div>
<div class="row">
    @if (ConfirmResult)
    {
        <p>Confirmation has been made!</p>
    }
    else
    {
        <p>Confirmation Pending!</p>
    }
</div>

@code {
    string ConfirmMessage = "Are you sure you want to click?";
    bool ConfirmResult;

    async Task TestConfirmBox()
    {
        ConfirmResult = await JsRuntime.InvokeAsync<bool>("confirm", ConfirmMessage);
    }
}
توضیحات:
- در اینجا می‌خواهیم تابع استاندارد confirm جاوا اسکریپتی را از طریق کدهای سی‌شارپ، با کلیک بر روی دکمه‌ی Test Confirm Button، فراخوانی کنیم. به همین جهت onclick@ این دکمه، به متد TestConfirmBox کدهای UI سی‌شارپ این کامپوننت، متصل شده‌است.
- برای دسترسی به توابع جاوا اسکریپتی، نیاز است سرویس توکار IJSRuntime را به کدهای کامپوننت تزریق کنیم که روش انجام آن‌را توسط دایرکتیو inject@ مشاهده می‌کنید. برای دسترسی به این سرویس توکار، نیاز به تنظیمات ابتدایی خاصی نیست و اینکار پیشتر انجام شده‌است.
- سرویس JsRuntime تزریق شده، دو متد مهم InvokeVoidAsync و InvokeAsync را جهت فراخوانی توابع جاوا اسکریپتی به همراه دارد. اگر تابعی، خروجی غیر void داشته باشد، باید از متد InvokeAsync استفاده کرد. برای مثال خروجی تابع استاندارد confirm، از نوع boolean است. بنابراین نوع این خروجی را به صورت یک آرگومان جنریک متد InvokeAsync مشخص کرده‌ایم.
- اولین پارامتر متد InvokeAsync، نام رشته‌ای تابع جاوا اسکریپتی است که قرار است صدا زده شود. پارامترهای اختیاری بعدی که به صورت params object?[]? args تعریف شده‌اند، لیست نامحدود آرگومان‌های ورودی این متد هستند.
- فیلد ConfirmMessage، پیامی را جهت اخذ تائید، تعریف می‌کند که به عنوان پارامتر متد confirm، توسط JsRuntime.InvokeAsync فراخوانی خواهد شد.
- فیلد ConfirmResult، نتیجه‌ی فراخوانی متد confirm جاوا اسکریپتی را به همراه دارد.
- در اینجا روش عکس العمل نشان دادن به خروجی دریافتی از متد جاوااسکریپتی را نیز مشاهده می‌کنید. پس از پایان متد TestConfirmBox که یک متد رویدادگران است، همانطور که در مطلب بررسی «چرخه‌ی حیات کامپوننت‌ها» نیز بررسی کردیم، متد StateHasChanged، در پشت صحنه فراخوانی می‌شود که سبب رندر مجدد UI خواهد شد. بنابراین در حین رندر مجدد UI، بر اساس مقدار جدید ConfirmResult دریافت شده‌ی از کاربر، با تشکیل یک if/else@، می‌توان به نتیجه‌ی تائید یا عدم تائید کاربر، واکنش نشان داد. با این توضیحات در اولین بار نمایش کامپوننت جاری چون مقدار ConfirmResult مساوی false است، پیام زیر را مشاهده می‌کنیم:


اما در ادامه با کلیک بر روی دکمه و تائید پیام ظاهر شده، عبارت زیر ظاهر می‌شود:



روش افزودن یک کتابخانه‌ی خارجی جاوا اسکریپتی به پروژه‌های Blazor

فرض کنید می‌خواهیم پیام‌های برنامه را توسط کتابخانه‌ی معروف جاوا اسکریپتی Toastr نمایش دهیم؛ با این دمو.
مرحله‌ی اول کار با این کتابخانه، دریافت فایل‌های CSS و JS آن است. برای این منظور قصد داریم از برنامه‌ی مدیریت بسته‌های LibMan استفاده کنیم:
dotnet tool install -g Microsoft.Web.LibraryManager.Cli
libman init
libman install bootstrap --provider unpkg --destination wwwroot/lib/bootstrap
libman install jquery --provider unpkg --destination wwwroot/lib/jquery
libman install toastr --provider unpkg --destination wwwroot/lib/toastr
بنابراین خط فرمان را در ریشه‌ی پروژه گشوده و پنج دستور فوق را اجرا می‌کنیم. دستور اول، ابزار خط فرمان LibMan را نصب می‌کند. دستور دوم، یک فایل libman.json خالی را در این پوشه ایجاد می‌کند و سه دستور بعدی، جی‌کوئری، بوت استرپ و toastr را دریافت و در پوشه‌ی wwwroot/lib قرار می‌دهند. Toastr برای اجرا، نیاز به jQuery نیز دارد.
البته تعاریف مداخل آن‌ها به فایل libman.json نیز اضافه می‌شوند. مزیت آن، اجرای دستور libman restore برای بازیابی و نصب مجدد تمام بسته‌های ذکر شده‌ی در فایل libman.json است.

پس از دریافت بسته‌های سمت کلاینت آن، مداخل مرتبط را به فایل Pages\_Host.cshtml برنامه‌ی Blazor Server اضافه خواهیم کرد (و یا در فایل wwwroot/index.html برنامه‌های Blazor WASM).
<head>
    <base href="~/" />
    <link rel="stylesheet" href="lib/toastr/build/toastr.min.css" />

</head>
<body>
 
    <script src="lib/jquery/dist/jquery.min.js"></script>
    <script src="lib/toastr/build/toastr.min.js"></script>
    <script src="_framework/blazor.server.js"></script>
</body>
مدخل فایل css آن‌را در قسمت head و فایل js آن‌را پیش از بسته شدن تگ body تعریف می‌کنیم. در اینجا نیازی به ذکر پوشه‌ی آغازین wwwroot نیست؛ چون base href تعریف شده، به این پوشه اشاره می‌کند.

یک نکته: می‌توان فایل csproj برنامه را به صورت زیر تغییر داد تا کار اجرای دستور libman restore را قبل از build، به صورت خودکار انجام دهد:
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <Target Name="DebugEnsureLibManEnv" BeforeTargets="BeforeBuild" Condition=" '$(Configuration)' == 'Debug' ">
    <!-- Ensure libman is installed -->
    <Exec Command="libman --version" ContinueOnError="true">
      <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
    </Exec>
    <Error Condition="'$(ErrorCode)' != '0'" Text="libman is required to build and run this project. To continue, please run `dotnet tool install -g Microsoft.Web.LibraryManager.Cli`, and then restart your command prompt or IDE." />
    <Message Importance="high" Text="Restoring dependencies using 'libman'. This may take several minutes..." />
    <Exec WorkingDirectory="$(MSBuildProjectDirectory)" Command="libman restore" />
  </Target>
</Project>


روش فراخوانی یک کتابخانه‌ی خارجی جاوا اسکریپتی در پروژه‌های Blazor

پس از افزودن فایل‌های سمت کلاینت toastr و تعریف مداخل آن در فایل Pages\_Host.cshtml برنامه‌ی Blazor Server جاری، اکنون می‌خواهیم از این کتابخانه استفاده کنیم. یک روش کار با این نوع کتابخانه‌های عمومی و سراسری به صورت زیر است:
- ابتدا فایل خالی جدید wwwroot\js\common.js را ایجاد می‌کنیم.
- سپس تابع عمومی و سراسری ShowToastr را بر اساس امکانات کتابخانه‌ی toastr و مستندات آن، به صورت زیر ایجاد می‌کنیم:
window.ShowToastr = (type, message) => {
  // Toastr don't work with Bootstrap 4.2
  toastr.options.toastClass = "toastr"; // https://github.com/CodeSeven/toastr/issues/599

  if (type === "success") {
    toastr.success(message, "Operation Successful", { timeOut: 20000 });
  }
  if (type === "error") {
    toastr.error(message, "Operation Failed", { timeOut: 20000 });
  }
};
چون تابع ShowToastr به شیء window انتساب داده شده‌است، در سراسر برنامه‌ی جاری قابل دسترسی است.
سطر اول آن هم برای رفع عدم تداخل با بوت استرپ 4x اضافه شده‌است. بوت استرپ 4x به همراه کلاس‌های CSS مشابهی است که نیاز است با تنظیم toastClass به مقداری دیگر، این تداخل را برطرف کرد.

- در ادامه مدخل تعریف فایل wwwroot\js\common.js را به انتهای تگ body فایل Pages\_Host.cshtml اضافه می‌کنیم:
    <script src="lib/jquery/dist/jquery.min.js"></script>
    <script src="lib/toastr/build/toastr.min.js"></script>
    <script src="js/common.js"></script>
    <script src="_framework/blazor.server.js"></script>
</body>

در آخر برای آزمایش آن به کامپوننت Pages\LearnBlazor\BlazorJS.razor مراجعه کرده و تابع سراسری ShowToastr را دقیقا مانند روشی که در مورد تابع confirm بکار بردیم، توسط سرویس JsRuntime، فراخوانی می‌کنیم:
@page "/BlazorJS"

@inject IJSRuntime JsRuntime


<div class="row">
    <button class="btn btn-success" @onclick="@(()=>TestSuccess("Success Message"))">Test Toastr Success</button>
    <button class="btn btn-danger" @onclick="@(()=>TestFailure("Error Message"))">Test Toastr Failure</button>
</div>

@code {
    async Task TestSuccess(string message)
    {
        await JsRuntime.InvokeVoidAsync("ShowToastr", "success", message);
    }

    async Task TestFailure(string message)
    {
        await JsRuntime.InvokeVoidAsync("ShowToastr", "error", message);
    }
}
در اینجا دو دکمه، جهت فراخوانی متد ShowToastr با پارامترهای مختلفی تعریف شده‌اند. چون تابع ShowToastr خروجی ندارد، به همین جهت اینبار از متد InvokeVoidAsync استفاده کرده‌ایم. پارامتر اول آن، نام متد ShowToastr است. پارامتر‌های دوم و سوم آن با آرگومان‌های (type, message) تعریف شده‌ی تابع ShowToastr تطابق دارند. به علاوه در این مثال، روش ارسال پارامترها را نیز در onlick@ توسط arrow functions مشاهده می‌کنید.



کاهش کدهای تکراری فراخوانی متدهای جاوا اسکریپتی با تعریف متدهای الحاقی

می‌توان جهت کاهش تکرار کدهای استفاده از تابع ShowToastr، متدهای الحاقی زیر را برای سرویس IJSRuntime تهیه کرد:
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace BlazorServerSample.Utils
{
    public static class JSRuntimeExtensions
    {
        public static ValueTask ToastrSuccess(this IJSRuntime JSRuntime, string message)
        {
            return JSRuntime.InvokeVoidAsync("ShowToastr", "success", message);
        }

        public static ValueTask ToastrError(this IJSRuntime JSRuntime, string message)
        {
            return JSRuntime.InvokeVoidAsync("ShowToastr", "error", message);
        }
    }
}
و سپس فضای نام آن‌را به فایل Imports.razor_ معرفی نمود تا در تمام کامپوننت‌های برنامه قابل استفاده شوند.
@using BlazorServerSample.Utils
به این ترتیب به فراخوانی‌های ساده شده‌ی زیر خواهیم رسید:
async Task TestSuccess(string message)
{
   //await JsRuntime.InvokeVoidAsync("ShowToastr", "success", message);
   await JsRuntime.ToastrSuccess(message);
}


فراخوانی یک متد عمومی واقع در کامپوننت فرزند از طریق کامپوننت والد

فرض کنید در کامپوننت فرزند Pages\LearnBlazor\LearnBlazor‍Components\ChildComponent.razor که در قسمت‌های قبل آن‌را تکمیل کردیم، متد عمومی زیر تعریف شده‌است:
@inject IJSRuntime JsRuntime


@code {
    // ...

    public async Task TestSuccess(string message)
    {
        await JsRuntime.ToastrSuccess(message);
    }
}
اکنون اگر بخواهیم این متد عمومی را از طریق کامپوننت والد یا دربرگیرنده‌ی آن فراخوانی کنیم، نیاز است از مفهوم جدیدی به نام ref استفاده کرد. برای این منظور به کامپوننت Pages\LearnBlazor\ParentComponent.razor مراجعه کرده و تغییرات زیر را اعمال می‌کنیم:
@page "/ParentComponent"

<ChildComponent
    OnClickBtnMethod="ShowMessage"
    @ref="ChildComp"
    Title="This title is passed as a parameter from the Parent Component">
    <ChildContent>
        A `Render Fragment` from the parent!
    </ChildContent>
    <DangerChildContent>
        A danger content from the parent!
    </DangerChildContent>
</ChildComponent>

<div class="row">
    <button class="btn btn-success" @onclick="@(()=>ChildComp.TestSuccess("Done!"))">Show Alert</button>
</div>


@code {
    ChildComponent ChildComp;
    // ...
}
با استفاده از ref@ که به فیلد ChildComp انتساب داده شده‌است، می‌توان ارجاعی از کامپوننت فرزند را (وهله‌ای از کلاس مرتبط با آن‌را) در کامپوننت جاری بدست آورد و سپس از آن جهت فراخوانی متدهای عمومی کامپوننت فرزند استفاده کرد.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-11.zip
مطالب
9# آموزش سیستم مدیریت کد Git : کار به صورت remote
تا اینجا هر آنچه درباره git آموختیم در رابطه با عملکرد git به صورت محلی بود. اما یکی از ویژگی‌های سیستم‌های توزیع شده، امکان استفاده از آن‌ها به صورت remote می‌باشد.
در مورد git تفاوت چندانی بین سرور‌ها و کلاینت‌ها وجود ندارد. تنها تفاوت، نحوه‌ی پیکربندی سرور است که این امکان را می‌دهد تا چندین کلاینت به صورت همزمان به آن متصل شده و با repository آن کار کنند. اما عملا تفاوتی بین repository موجود در کلاینت و سرور نیست.
تذکر ۱: در این مقاله از وب سایت github برای توضیح مثال‌ها استفاده شده است. github قدیمی‌ترین و قدرتمندترین وب سایت برای مدیریت repository‌های git است. اما اجباری در انتخاب آن نیست؛ زیرا انتخاب‌های فراوانی از جمله bitbucket   نیز وجود دارد.
تذکر ۲: نام مستعار origin اجباری نیست؛ اما از آن جهت که نام پیش فرض است، در اکثر مثال‌ها و توضیحات استفاده شده است.
قبل از شروع مبحث بهتر است کمی درباره‌ی پروتکل‌های ارتباطی پشتیبانی شده توسط git صحبت کنیم:
git از ۴ نوع پروتکل پشتیبانی می‌کند:
۱) (http(s: پروتکل http با پورت ۸۰ و https با پورت ۴۴۳ کار می‌کند و معمولا فایروال‌ها مشکلی با این پروتکل‌ها ندارند. از هر دوی آن‌ها می‌توان برای عملیات نوشتن و یا خواندن استفاده نمود و می‌توان آن‌ها را به گونه‌ای تنظیم کرد که برای برقراری ارتباط نیاز به تائید هویت داشته باشند.
۲) git: پروتکلی فقط خواندنی است که به صورت  anonymous و بر روی پورت ۹۴۱۸ کار می‌کند. شکل استفاده از آن به صورت زیر است و معمولا در github کاربرد فراوانی دارد:
git://github.com/[username]/[repositoryname].git
۳) ssh: همان پروتکل استفاده شده در یونیکس است که بر اساس مقادیر کلیدهای عمومی و خصوصی تعیین هویت را انجام می‌دهد. شکل استفاده از آن به صورت زیر است و بر روی پورت ۲۲ کار می‌کند و امکان نوشتن و خواندن را می‌دهد:
git@github.com:[username]/[repositoryname].git
۴) file: تنها استفاده محلی دارد و امکان نوشتن و خواندن را می‌دهد.

نحوه‌ی عملکرد git به صورت remote:
به طور کلی هر برنامه‌نویس نیاز به دو نوع از دستورات دارد تا همواره repository محلی با remote هماهنگ باشد:
۱) بتواند به طریقی داده‌های موجود در repository محلی خود را به سمت سرور بفرستد.
۲) این امکان را داشته باشد تا repository محلی خود را با استفاده از repository در سمت سرور آپدیت نماید تا از آخرین تغییراتی که توسط بقیه اعضای گروه صورت گرفته است آگاهی یابد.

طریقه رفتار git برای کار با repository‌های remote به صورت زیر است:
هنگامی‌که کاربر قصد دارد تا repository یا شاخه‌ای از آن را به سمت سرور بفرستد، git ابتدا یک شاخه با نام همان شاخه به اضافه /origin ایجاد می‌کند. مثلا برای شاخه master، آن نام به صورت زیر می‌شود:
origin/master
عملکرد این شاخه دقیقا مانند دیگر شاخه‌های git است؛ با این تفاوت که امکان check-in یا out برای این نوع شاخه‌ها وجود ندارد. زیرا git باید این شاخه‌ها را با شاخه‌ها متناظرشان در remote هماهنگ نگه دارد.
از این پس این شاخه‌ی ایجاد شده، به عنوان واسطی بین شاخه محلی و شاخه راه دور عمل می‌کند.

cloning:
با استفاده از دستور clone می‌توان یک repository در سمت سرور را به طور کامل در سمت کلاینت کپی کرد. به عنوان مثال repository مربوط به کتابخانه jquery از وب سایت github به صورت زیر است:
git clone https://github.com/jquery/jquery.git
همچنین می‌توان با استفاده از دستور زیر پوشه‌ای با نامی متفاوت را برای repository محلی انتخاب نمود:
git clone [URL][directory name]

اضافه کردن یک remote repository:
برای آن‌که بتوان تغییرات یک remote repository را به repository محلی منتقل نمود، ابتدا باید آن را به لیست repository‌های ریموت که در فایل config. ذخیره می‌شود به شکل زیر اضافه نمود:
git remote add [alias][URL]
در دستور فوق، برای repository باید یک نام مستعار تعریف کرد و در بخش URL باید آدرسی که سرور به وسیله آن امکان دریافت اطلاعات را به ما می‌دهد، نوشت. البته این بستگی به نوع پروتکل انتخابی دارد. به عنوان مثال:
git remote add origin https://github.com/jquery/jquery.git
اگر بخواهیم لیست repository‌هایی که به صورت remote اضافه شده‌اند را مشاهده کنیم، از دستور زیر استفاده می‌کنیم:
git remote
در صورتی‌که دستور فوق را با v- تایپ کنید اطلاعات کامل‌تری در رابطه با repository‌ها مشاهده خواهید کرد.
همچنین برای حذف یک remote repository از دستور زیر استفاده می‌‌کنیم:
git remote [alias] -rm
در صورتی‌که بخواهید لیستی از شاخه‌های remote را مشاهده کنید کافیست از دستور زیر استفاده کنید:
git branch -r
همچنین می‌توان از دستور زیر برای نمایش تمامی شاخه‌ها استفاده کرد:
git branch -a

fetch:
برای دریافت اطلاعات از دستور زیر استفاده می‌کنیم:
git fetch [alias][alias/branch name]
در صورتی‌که تنها یک repository باشد می‌توان از نوشتن نام مستعار صرفنظر نمود. همچنین اگر شاخه یا شاخه‌های مورد نظر به صورت track شده باشند، می‌توان قسمت دوم دستور فوق را نیز ننوشت.
اگر بعد از اجرای دستور فوق، بر روی یک شاخه log بگیرید، خواهید دید که تغییرات در شاخه محلی اعمال نشده است زیرا دستور فوق تنها داده‌ها را بر روی شاخه [origin/[branchname ذخیره کرده است. برای آپدیت شدن شاخه اصلی باید با استفاده از دستور merge آن را در شاخه مورد نظر ادغام کرد.

pulling:
چون کاربرد دو دستور fetch و merge به صورت پشت سر هم زیاد است git دو دستور فوق را با استفاده از pull انجام می‌دهد:
pull [alias][remote branch name ]
اگر دو مقدار فوق را برای دستور pull تعیین نکنید، ممکن است در هنگام اجرای دستور فوق با خطایی مواجه شوید مبنی بر اینکه git نمی‌داند دقیقا شاخه ریموت را با کدام شاخه محلی باید ادغام کند. این مشکل زمانی پیش می‌آید که برای شاخه ریموت یک شاخه محلی متناظر وجود نداشته باشد. برای ایجاد تناظر بین دو شاخه ریموت و لوکال درگذشته باید فایل config. را تغییر می‌دادیم، اما نسخه جدید git دستوری را برای آن دارد:
git branch --set-upstream [local brnach][alias/branch name]
با اجرای این دستور از این پس شاخه محلی تغییرات شاخه remote را دنبال می‌کند.

pushing:
با استفاده از push می‌توان تغییرات ایجاد شده را به remote repository انتقال داد:
git push -u [alias][branch name ]
وجود u- در اینجا بدین معنا است که ما میخواهیم تغییرات repository در سمت سرور دنبال شود. در صورت استفاده نکردن از u- بایستی برای push هر بار مقادیر داخل کروشه‌ها را بنویسیم. در صورتی‌که بعدا بخواهیم، می‌توان توسط همان دستوری که در قسمت pull گفته شد دو شاخه را به هم وابسته کنیم.
tag:
همانطور که قبلا گفته شد تگ‌ها برای نشانه‌گذاری و دسترسی راحت‌تر به به commitها هستند. برای ایجاد یک تگ از دستور زیر استفاده می‌شود:
git tag [tag name]
همچنین می‌توان با a- برای تگ پیامی نوشت و یا با s- آن را امضا کرد. برای مشاهده تگ‌ها از دستور زیر استفاده می‌شود:
git tag
در حالت پیشفرض git تگ‌ها را push نمی‌کند. برای push کردن تگ‌ها باید دستور push را با اصلاح‌کننده tages-- به کارببرید.
مطالب
قابلیت های جدید VisualStudio.NET 2012 - قسمت سوم
اگر شما در زمینه طراحی وب سایت و برنامه‌های کاربردی تحت وب فعالیت دارید حتماً با ابزارهایی مانند Firebug آشنا هستید. معمولاً فرآیند بررسی مشکلات رابط کاربری و موضوعات مشابه آن بصورت زیر بوده است:
 
 

توجه داشته باشید که یک صفحه وب که در مرورگر به نمایش در می‌آید، برآیند فایل‌های جاوا اسکریپت، شیوه نامه ها، User control ها، صفحات ASPX و Master Page‌ها و ... است. افزون بر این آنچه در مرورگر نمایش داده می‌شود با چیزی که ما در محیط طراحی (Visual Studio.NET (Design View  می‌بینیم متفاوت است.
تمام مشکلات و سختی‌های بالا دست به دست هم دادند تا در نسخه جدید نرم افزار Visual Studio.NET شاهد ابزار جادویی با عنوان Page Inspector باشیم.
این ابزار بصورت Real-time امکان نگاشت (mapping) عناصر موجود در نتیجه نهایی برنامه وب را با سورس کد مهیا می‌کند. بدین معنا توسط Page Inspector با حرکت ماوس روی عناصر صفحه در مرورگر، Visual Studio.NET بخشی را که آن عنصر را تولید کرده است (User Control, Master Page, View, و ...) نمایش می‌دهد و شما می‌توانید بلافاصله پس از اعمال تغییرات جدید بر روی سورس کد، نتیجه را روی مرورگر ببنید. البته عکس این موضوع نیز صادق است و شما می‌توانید با حرکت در سورس کد، نتیجه بصری و عناصر HTML ی که در نتیجه تولید می‌شوند را مشاهده کنید. (عناصر متناظر به حالت Select در می‌آیند.)
 

 
از دیگر قابلیت‌های این ابزار نمایش CSS‌های متناظر هر عنصر است. شما می‌توانید هر یک از قوانینی که در Style هر عنصر تعریف کرده اید را فعال و یا غیر فعال کنید. همچنین امکان ویرایش آن‌ها وحود دارد.
همچنین از طریق گزینه File می‌توان لیست تمام فایل‌های سورس صفحه را مشاهده کرد.
 

 
با وجود چنین ابزاری یقیناً داشتن دو مانیتور برای برنامه نویسان و توسعه دهندگان وب کاملاً حیاتی است. چراکه Visual Studio.NET به شما این امکان را می‌دهد تا Page Inspector را در یک مانیتور و نمای سورس را در مانیتور دیگر به نمایش در آورید.
 
نکته:
جهت استفاده از تمام امکانات این ابزار باید دستور زیر را در تگ appsettings فایل web.config اضافه کنید:
 <add key="VisualStudioDesignTime:Enabled" value="true" />
پیشنهاد می‌کنم برای درک بهتر این ابزار و آشنایی با آن ویدئو مربوطه در کانال 9  را از دست ندهید.
 
راهنماهای پروژه‌ها
سؤالات متداول
  • می‌خواهم برنامه‌ام را بر روی کامپیوتر مشتری بدون دردسر نصب کنم؛ با حداقل حجم و دردسر توزیع. به علاوه بدون نیاز به وصله پینه کردن اسمبلی‌های تجاری دریافت شده.

پاسخ: PdfReport از چند اسمبلی دات نتی تشکیل شده است که نیاز به نصب خاصی ندارد. همچنین حجم فشرده شده آن نیز زیر 2 مگابایت است. بنابراین از جهت توزیع مشکل خاصی نخواهید داشت. همچنین کل این مجموعه سورس باز است و مشکلات متداول همراه با گزارش سازهای تجاری را به همراه ندارد.

  • می‌خواهم فایل گزارش، به همراه برنامه و فایل exe آن و نه جدای از آن توزیع شود.

پاسخ: روش کار با PdfReport اصطلاحا code-first است. یعنی یک یا چند کلاس تهیه شده توسط شما، کار تهیه گزارش را انجام می‌دهند و تمام این‌ها کامپایل شده و به همراه فایل اجرایی یا اسمبلی‌های خاصی که برای آن درنظر می‌گیرید، کامپایل و توزیع خواهند شد. همچنین با توجه به code-first بودن آن و عدم وابستگی به فناوری خاصی، این گزارشات در برنامه‌های وب و ویندوز نیز می‌توانند بدون نیاز به تغییری در کدهای شما مورد استفاده قرار گیرند.

  • برای کار با PdfReport از کجا باید شروع کرد؟

پاسخ: لطفا برچسب PdfReport را در سایت جاری دنبال نمائید. نحوه استفاده از قابلیت‌های آن قدم به قدم توضیح داده شده‌اند.

  • می‌خواهم برای صرفه جویی در کاغذ چاپی، گزارش چند ستونه‌ای را تهیه کنم.

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

  • می‌خواهم گزارشی را تولید کنم که حجم متن فیلدهای آن مشخص نیست. یکی ممکن است نصف صفحه باشد دیگری دو صفحه و یا بعضی تنها یک سطر.

پاسخ: در PdfReport ارتفاع هر سطر به صورت خودکار بر مبنای حجم وارد شده محاسبه و تنظیم می‌گردد. به عبارتی این تنظیم ثابت نیست و سبب حذف محتوای ارائه شده در آن‌ها یا محو شدن ردیف‌های دیگر نمی‌گردد.

  • نیاز به گزارش سازی دارم که بدون مشکل با ORMها کار کند. برای مثال در حین کار با Entity framework مستقیما بتواند با اشیاء و لیست‌های حاصل از آن کار کند.

پاسخ: یکی از انواع منابع داده تعریف شده در PdfReport جهت کار با ORMها طراحی شده است که مثالی از نحوه استفاده از آن‌را در مثال EF Code first همراه با مجموعه مثال‌های این کتابخانه می‌توانید ملاحظه نمائید.

  • آیا این کتابخانه می‌تواند از فایل سیستم (و نه صرفا بانک اطلاعاتی) هم تصاویر را دریافت و در گزارشات قرار دهد؟

پاسخ: بلی. لطفا به مثال ImageFilePath مراجعه نمائید.

  • می‌خواهم یک گزارش ساز پویا در برنامه داشته باشم. فقط کوئری غیر مشخصی را به آن بدهم و حاصل آن یک گزارش باشد.

پاسخ: امکان تهیه گزارش‌های پویا نیز در PdfReport پیش بینی شده است. توضیحات بیشتر همچنین این امکان در حین کار با ORMها نیز وجود دارد.

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

پاسخ: برای این منظور تنها کافی است از منبع داده صحیحی استفاده نمائید. برای اطلاعات بیشتر به مثال IList مراجعه کنید.

  • می‌خواهم از بانک‌های اطلاعاتی دیگری بجز SQL Server استفاده کنم.

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

مطالب
توسعه برنامه‌های Cross Platform با Xamarin Forms & Bit Framework - قسمت دوازدهم
در این قسمت قصد داریم به بررسی چند زبانه سازی برنامه‌ها بپردازیم. برای چند زبانه کردن یک برنامه باید حداقل به موارد زیر توجه شود:
1- بحث Right to left و Left to right در صورتیکه زبان هایی که قصد پشتیبانی از آن‌ها را داریم، از هر دو مدل باشند.
2- بحث string‌های استفاده شده در View (مثلا Text یک Button) و View Model (مثل متن هشدار Alert Dialog)
3- بحث تقویم شمسی، قمری و میلادی در صورت لزوم.

همه کنترل‌ها در Xamarin Forms دارای Property ای با نام FlowDirection هستند که مقادیر RightToLeft، LeftToRight و MatchParent را می‌پذیرد. MatchParent که مقدار پیش فرض است، به این معنی است که مثلا اگر در ContentPage، مقدار FlowDirection را RightToLeft دهیم، تمامی کنترل‌های داخل آن صفحه RightToLeft باشند و بالعکس.
برای اطلاعات بیشتر، به مستندات مربوطه مراجعه کنید.

برای string هایی که ممکن است در View یا View Model استفاده شوند، از فایل‌های resx استفاده می‌کنیم. در پروژه XamApp، یک فولدر بسازید با نام Resources و در آن فولدر، یک فایل از نوع Resources file را اضافه کنید با نام Strings.resx

مجددا یک Resources file را با نام Strings.fa.resx و یکی دیگر را با نام Strings.en.resx اضافه کنید. برای درک بهتر وضعیت نهایی، پروژه XamApp را Clone/Pull کنید و آن را بررسی کنید.

در فایل Strings.resx یک ردیف جدید اضافه کنید که Name آن برابر با HelloWorld باشد و Value آن خالی است. این نام، در کد نویسی ما استفاده می‌شود و مثلا نباید شامل Space، علامت ! و ... باشد. در فایل Strings.fa.resx یک ردیف جدید اضافه کنید که Name آن برابر با همان HelloWorld باشد و Value آن برابر با سلام دنیا! در نهایت در فایل Strings.en.resx یک ردیف جدید را اضافه کنید که Name آن HelloWorld بوده و Value آن ! Hello world باشد.

سپس در فایل App.xaml.cs می‌توانید قبل از اولین NavigationService.NavigateAsync، از کد زیر را استفاده کنید:

 
Strings.Culture = CultureInfo.CurrentUICulture = new CultureInfo("en"); // or new CultureInfo("fa");

برای نمایش پیام در View Model با استفاده از IUserDialogs نیز می‌توانید به شکل زیر عمل کنید:

await UserDialogs.AlertAsync(message: Strings.HelloWorld);

در صورتیکه بخواهید پارامتری را در string‌های چند زبانه خود داشته باشید نیز می‌توانید به شکل زیر عمل کنید:

 Name  En Value  Fa Value
 ButtonTappedCount   Button tapped {0} times!   دکمه {0} کلیک شده است 
سپس در Xaml داریم: (مثال در فایل HelloWorldMultiLanguageView.xaml قرار دارد)
<Label Text="{Binding StepsCount, StringFormat={x:Static resx:Strings.ButtonTappedCount}}" />
که باعث نمایش چنین چیزی می‌شود: "دکمه 7 بار کلیک شده است!" 
namespace مربوطه یعنی resx هم در بالای فایل Xaml باید قرار داده شود، که می‌شود:
xmlns:resx="clr-namespace:XamApp.Resources"
و در CSharp داریم:
await UserDialogs.AlertAsync(string.Format(Strings.ButtonTappedCount, StepsCount));
یکی از کتابخانه‌های خوب در این زمینه، Humanizer است. فرض کنید می‌خواهید string هایی چون "آخرین ورود: بیست ساعت پیش" و ... را بسازید. این کتابخانه فوق العاده در این زمینه به شما کمک می‌کند که استفاده از آن را شدیدا توصیه می‌کنم.
برای داشتن تقویم شمسی، میلادی و هجری نیز می‌توانید از Bit Date Picker استفاده کنید که توضیح نحوه استفاده از آن در این پست آورده شده است.
مطالب
چگونه تشخیص دهیم اسمبلی دات نت ما وصله شده است؟

یکی از روش‌هایی که برای بررسی یکپارچگی فایل‌ها مورد استفاده قرار می‌گیرد و عموما در دنیای سخت افزار و firmware های نوشته شده برای آن‌ها مرسوم است، قرار دادن CRC32 فایل در قسمتی از فایل و بررسی آن حین Boot سیستم است. اگر CRC32 جدید با CRC32 اصلی یکسان نباشد به این معنا است که فایل در حال اجرا پیش تر دستکاری شده است.
اما در دات نت فریم ورک روش متداول اینکار چیست؟ برای این منظور اضافه کردن امضای دیجیتال به فایل و اسمبلی نهایی تولیدی (فایل exe یا dll تولیدی) توصیه می‌شود (مراجعه به قسمت خواص پروژه و افزودن امضای دیجیتال جدید فقط با چند کلیک، +).
این مورد خوب است (با توجه به اینکه از الگوریتم‌های RSA و SHA1 استفاده می‌کند)، لازم است، اما کافی نیست زیرا ابزارهای حذف آن وجود دارند. به عبارتی برای وصله کردن این فایل‌ها فقط کافی است این امضای دیجیتال حذف شود و زمانی هم که نباشد، بررسی خاصی در مورد یکپارچگی فایل صورت نخواهد گرفت.
اما اگر باز هم نگران patch یا وصله شدن اسمبلی دات نت خود هستید این مورد افزودن امضای دیجیتال را حتما انجام دهید. مهم‌ترین خاصیت آن این است که یک سری تابع native در دات نت فریم ورک برای بررسی نبود آن وجود دارند (+):
[DllImport("mscoree.dll", CharSet=CharSet.Unicode)]
public static extern bool StrongNameSignatureVerificationEx(string wszFilePath, bool fForceVerification, ref bool pfWasVerified);

wszFilePath مسیر فایلی است که باید بررسی شود.
fForceVerification آیا متغیر pfWasVerified نیز مقدار دهی گردد؟
خروجی تابع مشخص می‌سازد که آیا strong name موجود و معتبر است یا خیر؟

و مثالی از استفاده‌ی آن (که بهتر است در یک تایمر نیم ساعت پس از اجرای برنامه رخ دهد):
using System;
using System.Reflection;
using System.Runtime.InteropServices;

namespace SigCheck
{
public class Validation
{
[DllImport("mscoree.dll", CharSet = CharSet.Unicode)]
public static extern bool StrongNameSignatureVerificationEx(
string wszFilePath, bool fForceVerification, ref bool pfWasVerified);

public static void SigCheck()
{
var assembly = Assembly.GetExecutingAssembly();
bool pfWasVerified = false;
if (!StrongNameSignatureVerificationEx(assembly.Location, true, ref pfWasVerified))
{
//خاتمه برنامه در صورت عدم وجود امضای دیجیتال معتبر
throw new Exception();
}
}
}

class Program
{
static void Main(string[] args)
{
Validation.SigCheck();
}
}
}
خوب، شاید پس از حذف و وصله شدن اسمبلی، مجددا strong name به آن اضافه شود! ، آن وقت چه باید کرد؟
زمانیکه به اسمبلی خود امضای دیجیتال اضافه می‌کنید، هش رمزنگاری شده فایل با الگوریتم RSA ، به همراه public key مورد نیاز در اسمبلی ذخیره می‌شوند. از آنجائیکه private key الگوریتم RSA را منتشر نکرده‌اید، شکستن الگوریتم RSA کار ساده‌ای نیست، مگر اینکه جفت کلید خودشان را تولید کنند و public key جدید را در فایل نهایی قرار دهند. بدیهی است این public key جدید با کلید عمومی ما که متناظر است با کلید خصوصی منتشر نشده‌ی اصلی، تطابق نخواهد داشد. برای آشنایی با تابعی که این بررسی را انجام می‌دهد به مقاله ذکر شده رجوع کنید:



مطالب
خواندنی‌های 12 تیر

اس کیوال سرور

امنیت

توسعه وب

دات نت فریم ورک

دبلیو سی اف

دبلیو پی اف و سیلور لایت

سایت‌های ایرانی

شیرپوینت

لینوکس

متفرقه

محیط‌های مجتمع توسعه

مرورگرها

مسایل انسانی، اجتماعی و مدیریتی برنامه نویسی

پی اچ پی

مطالب
افزودن یک DataType جدید برای نگه‌داری تاریخ خورشیدی - 2
پیش از هرچیز به شما پیش‌نهاد می‌کنم؛ بار دیگر کد سی‌شارپ درس نخست را در پروژه‌ی خود کپی کنید و سپس Publish را بزنید. پس از ارسال آن مطلب، تغییراتی در جهت بهینه‌سازی کد دادم که به نظرم بهتر است شما نیز در پروژه‌ی خود به کار برید.

چرا از این نوع داده استفاده کنیم؟
نخستین پرسشی که ممکن است برای شما پیش بیاید این است که چرا بهتر است از این نوع داده استفاده کنیم. برای پاسخ به این پرسش باید راه‌کارهای گذشته را بررسی کنیم. معمولاً طراحان پایگاه داده‌ها برای استفاده از تاریخ خورشیدی، زمان را به صورت میلادی ثبت می‌کنند؛ سپس با یک scalar-valued function زمان درج شده را به خورشیدی تبدیل می‌کنند. در این صورت می‌توان با یک تابع کوچک دیگر بخش مربوط به ساعت را نیز از همان ستون به دست آورد. در این صورت می‌توانیم از کلیه‌ی متدهای مربوط به DateTime در SQL از جمله افزایش و کاهش و تفاضل دو تاریخ بهره برد. برخی دیگر از طراحان، ستونی از نوع (char(10 در نظر می‌گیرند و تاریخ خورشیدی را به صورت ده‌کاراکتری در آن ذخیره می‌کنند. این روش هرچند نیاز به تبدیل به خورشیدی را ندارد ولی کلیه‌ی مزایایی که در استفاده از DateTime به آن‌ها دسترسی داریم از دست می‌دهیم. افزون بر این جهت نگه‌داری زمان باید یک فیلد دیگر از نوع کاراکتری و یا در نگارش‌های نوین‌تر از نوع time تعریف کنیم. برخی دیگر از هر دو را در کنار هم استفاده می‌کنند و در واقع جهت سرعت بالاتر نمایش و بررسی داده‌ها از طریق محیط SQL Server از فیلد کاراکتری تاریخ خورشیدی و برای مقایسه و بدست آوردن ساعت از فیلد نوع DateTime استفاده می‌کنند.

از نظر فضای اشغال‌شده نوع DataTime، هشت بایت، smalldatetime (در صورت استفاده) 4 بایت و فیلد 10 کاراکتری تاریخ 10 بایت فضا اشغال می‌کند در صورتی که نوع JalaliDate با درنظر گرفتن همه‌ی مزایای انواع داده‌ی استفاده‌شده برای تاریخ، فقط 8 بایت فضا اشغال می‌کند. با استفاده از این نوع به راحتی داده‌ی تاریخ را بر اساس تقویم ایرانی اعتبارسنجی می‌کنید و بخش‌های مختلف زمان از سال تا ثانیه را با یک متد به دست می‌آورید. می‌توانید به راحتی به تاریخ خود زمانی را بیفزایید یا بکاهید و در گزارش‌ها بدون نگرانی از تبدیل درست استفاده کنید. چون کدباز است می‌توانید با کمی حوصله امکانات دیگر مد نظر خود را به آن بیفزایید و از آن در SQL بهره ببرید.

چگونه این نوع داده را حذف کنم!؟
شما می‌توانید به سادگی نوع داده‌ی ایجادشده توسط CLR را در مسیر زیر بیابید و اقدام به حذف آن نمایید:

همان‌طور که مشاهده می‌شود؛ حتی نوع داده‌ی سیستمی hierarchyid که جهت ساختار سلسله‌مراتبی مانند چارت سازمانی یا درخت تجهیزات استفاده می‌شود؛ نیز یک نوع داده‌ی CLR است.

آیا راه دیگری نیز برای افزودن این نوع داده به SQL به جز Publish کردن وجود دارد؟
مانند بسیاری دیگر از گونه‌های پروژه، در اینجا نیز شما یک فایل DLL خواهید داشت. این فایل برپایه‌ی تنظیماتی که شما در قسمت Properties پروژه‌ی خود انجام می‌دهید ساخته می‌شود. پس از تغییر مسیر فایل DLL در دستور زیر توسط یک New Query از Database خود، آن را اجرا کنید:

CREATE ASSEMBLY JalaliDate
FROM 'F:\prgJalaliDate.dll' 
WITH PERMISSION_SET = SAFE;
هم‌چنین در صورت ویرایش‌های دوباره پروژه از دستور زیر استفاده کنید:
ALTER ASSEMBLY JalaliDate
FROM 'F:\prgJalaliDate.dll'
با استفاده از دستورهای زیر می‌توانید از چگونگی درج فایل‌های افزوده شده آگاه شوید:
select * from sys.assemblies
select * from sys.assembly_files
تا اینجا SQL Server، دی‌ال‌ال مربوط به پروژه را شناخته است. برای تعریف نوع داده از دستور زیر بهره ببرید:
CREATE TYPE dbo.JalaliDate 
EXTERNAL NAME JalaliDate.[JalaliDate];
این کار همانند استفاده از گزینه‌ی Publish در Visual Studio است.
هم‌چنین چنان‌چه در SQL Server 2012 از منوی راست‌کلیک پایگاه داده‌ها روی گزینه Tasks و سپس Generate Scripts را انتخاب کنیم، از مشاهده‌ی سند ساخته شده، درخواهیم یافت که حتی دستورهای مربوط به ساخت اسمبلی CLR با تبدیل فایل به کد در Scripts وجود دارد و با اجرای آن در سروری دیگر، انتقال می‌یابد.

GO
/****** Object:  SqlAssembly [prgJalaliDate]    Script Date: 2013/04/30 08:27:00 ب.ظ ******/
CREATE ASSEMBLY [prgJalaliDate]
FROM 0x4D5A90000300000004000000FFFF0000B8000000000000 ..... بقیه‌ی کدها حذف شده
WITH PERMISSION_SET = SAFE

GO
ALTER ASSEMBLY [prgJalaliDate]
ADD FILE FROM 0x4D6963726F736F667420432F432B2B204D534620372E30300D0A1A44530..... بقیه‌ی کدها حذف شده
AS N'prgJalaliDate.pdb'

GO
/****** Object:  UserDefinedType [dbo].[JalaliDate]    Script Date: 2013/04/30 08:27:00 ب.ظ ******/
CREATE TYPE [dbo].[JalaliDate]
EXTERNAL NAME [prgJalaliDate].[JalaliDate]

GO

دنباله دارد ...