کلیک بر روی لینک ها
public class CustomerInfo { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime BirthDate { get; set; } }
public interface IModelBinder { object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext); }
using System; using System.Web; using System.Web.Mvc; using ModelBinderExample.Models; using Persia; namespace ModelBinderExample.CustomModelBinder { // Article written for www.dotnettips.info public class CustomerInfoModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { HttpRequestBase request = controllerContext.HttpContext.Request; string firstName = request.Form.Get("FirstName"); string lastName = request.Form.Get("LastName"); DateTime birthDate = this.GetMiladiDate(request); return new CustomerInfo() { FirstName = firstName, LastName = lastName, BirthDate = birthDate }; } private DateTime GetMiladiDate(HttpRequestBase request) { int day = int.Parse(request.Form.Get("Day")); int month = int.Parse(request.Form.Get("Month")); int years = int.Parse(request.Form.Get("Years")); //Convert shamsi to miladi return Persia.Calendar.ConvertToGregorian(years, month, day, DateType.Gerigorian); } } }
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); //Register New ModelBinder ModelBinders.Binders.Add(typeof(CustomerInfo), new CustomerInfoModelBinder()); }
[HttpPost] public ActionResult Create([ModelBinder(typeof (CustomerInfoModelBinder))] CustomerInfo customerInfo) { if (ModelState.IsValid) { ViewBag.FirstName = customerInfo.FirstName; ViewBag.LastName = customerInfo.LastName; ViewBag.BirthDate = customerInfo.BirthDate; } return View(); }
ایجاد کامپوننت جدید BlazorJS
برای بررسی نحوهی تعامل جاوا اسکریپت و Blazor، در ابتدا کامپوننت جدید Pages\LearnBlazor\BlazorJS.razor را ایجاد کرده:
@page "/BlazorJS" <h3>BlazorJS</h3> @code { }
<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.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>
یک نکته: میتوان فایل 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 }); } };
سطر اول آن هم برای رفع عدم تداخل با بوت استرپ 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، متدهای الحاقی زیر را برای سرویس 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); } } }
@using BlazorServerSample.Utils
async Task TestSuccess(string message) { //await JsRuntime.InvokeVoidAsync("ShowToastr", "success", message); await JsRuntime.ToastrSuccess(message); }
فراخوانی یک متد عمومی واقع در کامپوننت فرزند از طریق کامپوننت والد
فرض کنید در کامپوننت فرزند Pages\LearnBlazor\LearnBlazorComponents\ChildComponent.razor که در قسمتهای قبل آنرا تکمیل کردیم، متد عمومی زیر تعریف شدهاست:
@inject IJSRuntime JsRuntime @code { // ... public async Task TestSuccess(string message) { await JsRuntime.ToastrSuccess(message); } }
@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; // ... }
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-11.zip
قبل از استفاده از وب ورکر، بهتر هست مرورگر را بررسی کنیم که آیا از این قابلیت پشتیبانی میکند یا خیر؟ روش بررسی کردن این قابلیت، شیوههای مختلفی دارد که به تعدادی از آنها اشاره میکنیم:
typeof(Worker) !== "undefined"
<script src="/js/modernizr-1.5.min.js"></script> Modernizr.webworkers
نحوه پشتیبانی وب ورکرها در مروگرهای مختلف به شرح زیر است:
برای ایجاد یک وب ورکر ابتدا لازم است تا کدهای پردازشی را داخل یک فایل js جداگانه بنویسیم. در این مثال ما قصد داریم که شمارندهای را بنویسیم:
var i=0; function timedCount() { i=i+1; postMessage(i); setTimeout("timedCount()", 500); } timedCount();
سپس در فایل HTML به شکل زیر وب ورکر را مورد استفاده قرار میدهیم. در سازنده Worker، ما آدرس فایل js را وارد میکنیم و برای توقف آن نیز از متد terminate استفاده میکنیم:
<!DOCTYPE html> <html> <head> <script> var worker; function Start() { worker=new Worker("webwroker-even-numbers.js"); worker.onmessage=(event)=> { document.getElementById("output").value=event.data; } } function Stop() { worker.terminate(); } </script> <meta charset="utf-8"> <title></title> </head> <body> <input type="text" id="output"/> <button onclick="Start();">Start Worker</button> <button onclick="Stop();">Stop Worker</button> </body> </html>
worker.onerror = function (event) { console.log(event.message, event); };
همچنین برای ورکر هم میتوانید پیامی را ارسال کنید، برای همین کد زیر را به کد ورکر اضافه میکنیم:
self.onmessage=(event)=>{ i=event.data; };
worker.onmessage=(event)=> { document.getElementById("output").value=event.data; if(event.data==8) worker.postMessage(100); }
روشهای ارسال پیام
به این نوع ارسال پیام، Structure Cloning گویند و با استفاده از این شیوه، امکان ارسال نوعهای مختلفی امکان پذیر شده است؛ مثل فایلها، Blobها، آرایهها و کلاسها و ... ولی باید دقت داشته باشید که این ارسال پیامها به صورت کپی بوده و آدرسی ارجاع داده نمیشود و باید مدنظر داشته باشید که ارسال یک فایل، به فرض پنجاه مگابایتی، به خوبی قابل تشخیص است. طبق نظر گوگل، از حجم 32 مگابایت به بعد، این گفته به خوبی مشهود بوده و زمانبر میشود. به همین علت فناوری با نام Transferable Objects ایجاد شده است که "کپی صفر" Zero-Copy را پیاده سازی میکند و باعث بهبود عملگر کپی میشود:
worker.postMessage(arrayBuffer, [arrayBuffer]);
var ab = new ArrayBuffer(1); worker.postMessage(ab, [ab]); if (ab.byteLength) { alert('Transferables are not supported in your browser!'); } else { // Transferables are supported. }
worker.postMessage({data: ab1, moreData: ab2}, [ab1, ab2]);
نمودار زیر مقایسهای بین Structure Cloning و Transferable Objects است که توسط گوگل منتشر شده است:
RTT=Round Trip Time
نمودار
بالا برای یک فایل 32 مگابایتی است که زمان رفت به ورکر و پاسخ (برگشت از ورکر)
را اندازه گرفتهاند. در ستونهای اول، این موضوع برای فایرفاکس به روش
Structure Cloning به مدت 302 میلی ثانیه زمان برد که همین موضوع برای
Transferables حدود 6.6 میلی ثانیه زمان برد.
آقای اریک بایدلمن در بخش مهندسی کروم گوگل میگوید: همین سرعت به ما در انتقال تکسچرها و مشها در WebGL کمک میکند.
استفاده از اسکریپت خارجی
در صورتیکه قصد دارید از یک اسکرپیت خارجی، در ورکر استفاده کنید، تابع importScripts برای اینکار ایجاد شده است:
importScripts('script1.js'); importScripts('script2.js');
importScripts('script1.js', 'script2.js');
Inline Worker
اگر بخواهید در همان صفحه اصلی یک ورکر را ایجاد کنید و فایل جاوا اسکریپتی خارجی نداشته باشید، میتوانید از inline worker استفاده کنید. در این روش باید یک نوع blob را ایجاد کنید:
var blob = new Blob([ "onmessage = function(e) { postMessage('msg from worker'); }"]); // یک آدرس همانند آدرس ارجاع به فایل درست میکند var blobURL = window.URL.createObjectURL(blob); var worker = new Worker(blobURL); worker.onmessage = function(e) { // e.data... }; worker.postMessage(); // ورکر آغاز میشود
blob:http://localhost/c745ef73-ece9-46da-8f66-ebes574789b1
window.URL.revokeObjectURL(blobURL);
chrome://blob-internals
HttpContext.Request.IsAjaxRequest();
و ویژگیهای درخواست توسط سرویس http - AngularJs$ :
همینطور که میبینید، در هدر درخواست http$ یک مورد مفقود الاثر شده به نام X-Requested-With داریم و همین مقدار است که مشخص میکند این یک درخواست ایجکسی است یا خیر و اکستنشن ()IsAjaxRequest نیر با همین مقدار عمل تشخیص را انجام میدهد. و به همین خاطر بود که این متد مقدار False را برمیگرداند.
بعد از کمی جستجو در این مورد ، به مخزن git انگیولار رسیدم و به صراحت به این موضوع اشاره شده بود که این هدر به صورت پیشفرض از درخواستهای http$ برداشته شده است.
بنابراین تنها راه حل این بود که خودمان به صورت دستی این هدر خاص رو به ماژول برنامه اضافه کنیم. به صورت زیر :
myAppModule.config(['$httpProvider', function($httpProvider) { $httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest'; }]);
با اضافه کردن این هدر به درخواستهای http$ ، اکستنشن ()IsAjaxRequest مقدار درست را برمیگرداند.
گزارش برای کاغذ های از پیش طراحی شده
در ادامه آموزش Git، به بررسی مفاهیم مورد استفاده در این سیستم مدیریت کد میپردازیم. البته ذکر این نکته ضروری است که ممکن است برخی از تعاریف زیر، برای افرادی که تا کنون با اینگونه سیستمها کار نکردهاند، مبهم باشد. اما مشکلی نیست؛ زیرا در دروس بعدی کار با Git، به صورت عملی، این مفاهیم به شکل دقیقتر و کاربردیتر بیان میشوند. هدف در اینجا تنها ایجاد یک تصویر کلی از نحوه کار سیستمهای مدیریت کد توزیع شده است.
تعاریف زیر هر چند برای Git نوشته شدهاند، اما میتوانند در بقیه DVCSها نیز کاربرد داشته باشند.
Commit:
بعد از آن که برنامه نویسان از صحت کدهای خود مطمئن شدند، برای ثبت وضعیت فعلی باید آنها را commit کنند. با این کار یک نسخه جدید از فایلها ایجاد میشود. به این ترتیب امکان بازگشت به نقطه فعلی درآینده به وجود خواهد آمد.
Pushing:
بعد از انجام عملیات Commit، معمولا برنامه نویسان میخواهند کدهای نوشته شده را با دیگران به اشتراک بگذارند. این کار به وسیله عملیات Pushing صورت میگیرد. بنابراین pushing عبارت است از عملی که با استفاده از آن دادهها از یک Repository به Repository دیگر جهت به اشتراک گذاری انتقال مییابد. معمولا به این مخزن Upstream Repository میگویند. Upstream Repository یک مخزن عمومی برای تمامی برنامه نویسانی است که تغییرات فایلهای خود را در آنجا push میکنند.
Pulling:
عملیات Pushing تنها نیمی از آن چیزی است که برنامه نویسان برای حفظ به روز بودن کدهای خود به آن احتیاج دارند. در بسیاری از موارد آنها نیاز دارند تا تغییرات فایلها و آخرین به روز رسانیها را نیز دریافت کنند. این کار در دو مرحله متفاوت انجام میشود:
1)بازیابی دادهها از مخزن عمومی (fetch)
2)الحاق دادههای دریافت شده با دادههای فعلی
معمولا در بسیاری از سیستمهای مدیریت کد، چون به هر دوی این عملیات توامان نیاز است، با یک دستور هر دو کار انجام میشود. به مجموع عملیات فوق Pulling گویند.
Branchها (شاخهها):
Branch و یا همان شاخه، به ما این امکان را میدهد که بتوانیم برای قسمتهای مختلف یک پروژه که روند تولید آنها با هم ارتباط مستقیمی ندارند، سوابق فایلی متفاوتی را ایجاد کنیم.
به عنوان مثال تصور کنید که در یک پروژه سه تیم متفاوت وجود دارد
1)تیم توسعه برنامه
2)تیم تست و اشکال یابی
3)واحد گرافیکی
در این حالت منطقی است به جای آن که سوابق فایلها برای همه یکسان باشد، هر تیم، شاخه مخصوص به خود را داشته باشد، تا تنها تغییرات فایلهای مربوطه را پیگیری کند و در نهایت بعد از آن که از صحت کار خود مطمئن شد، آن را در یک شاخه اصلی برای استفاده دیگر تیمها قرار دهد.
در Git شاخه اصلی master نام دارد و فایلها به صورت پیش فرض در این شاخه قرار داده میشوند. استاندارد کار بر آن است که در شاخه master تنها فایلهای نهائی قرار گیرند.
Merging:
به عملیات ادغام دو یا چند شاخه با یکدیگر Merging گفته میشود. در بعضی موارد، در روند توسعه یک برنامه نیاز است که شاخههایی جهت مدیریت بهتر کد ایجاد شود. اما بعد از توسعه این قسمت ها، میتوان شاخههای ایجاد شده را با هم ادغام نمود تا تغییرات فایلها در یک شاخه قرار گیرند. مثلا در یک تیم توسعه فرض کنید دو گروه وجود دارند که کدهای مربوط به دسترسی داده را مینویسند و هر دو را در یک شاخه فایلهای خود، نگهداری میکنند. گروه اول بر روی کلاسهای انتزاعی و گروه دوم بر روی کلاسهای عملی کار میکنند. به منظور اینکه گروه دوم به اشتباه کلاسهای انتزاعی را که هنوز کامل نیستند پیاده سازی نکند، دو شاخه از شاخه اصلی ایجاد میشود و هر گروه در شاخهای مجزا قرار میگیرد. گروه اول تنها کلاسهای انتزاعی را در شاخه مشترک قرار میدهد که کار آنها تمام شده باشد و گروه دوم تنها همان کلاسها را پیاده سازی و در شاخه مشترک میگذارد. بعد از آنکه کار این دو بخش پایان گرفت میتوان هر سه شاخه را در یک شاخه مثلا بخش کدهای دسترسی داده قرار داد.
البته عملیات Merging می تواند باعث ایجاد مشکلی به نام Conflict شود که خوشبختانه Git روش هایی را برای مدیریت این مشکل دارد که در مقالات بعد به آن اشاره خواهد شد.
Locking:
با استفاده از این کار میتوان مانع تغییر یک فایل توسط برنامه نویسان دیگر شد. معولا Locking به 2 صورت است
1)Strict Locking
2) Optimistic Locking
در روش اول بعد از آن که فایلی قفل شد همان کسی که فایل را قفل کرده تنها امکان تغییر آن را خواهد داشت؛ که البته این روش مناسب سیستمهای توزیع شده نیست.
در روش دوم فرض بر این است که تغییراتی را که هر کس بر روی فایل میدهد، به گونهای باشد که هنگام ادغام این تغییرات، اختلالی ایجاد نشود. یعنی وظیفه بر عهده مصرف کننده فایل است که آگاهی داشته باشد چگونه فایل را تغییر دهد. هنگامی که فایلی به این روش قفل میشود، اگر در حین تغییر فایل توسط ما، شخص دیگری فایل را تغییر داده باشد و آن را pull کرده باشد ما در زمان push فایل با خطا مواجه میشویم. سیستم از ما میخواهد که ابتدا تغییرات فایل را pull کنیم و سپس فایل را push نمائیم. در هنگام pull اگر برنامه نویسی قوانین تغییرات فایل را رعایت نکرده باشد، ممکن است اعمال تغییرات با خطا همراه گردد.
تعاریف فوق بخشی از مفاهیم اولیه مورد نیاز Git بود. اما ما در ادامه به بررسی objectهای Git و همچنین نحوه ذخیره سازی و مدیریت فایلها در این سیستم مدیریت کد خواهیم پرداخت.
تفاوتی در طراحی گزارش وجود نداره.
شما باید اطلاعات را توسط report.RegBusinessObject برای استیمول بفرستید.
تنها تفاوت طراحی برای این حالت این است که بعد رجیستر کردن اطلاعات برای اولین بار شما باید از متد design استفاده کنید تا پوکو شما به دیکشنری گزارش ساز اضافه بشه تا بتونی طراحی کنی. بقیه کار رو هم که میدونید.
موفق باشید.
dotnet publish -r win10-x64 /p:PublishSingleFile=true
With ReadyToRun images | IL-only Application |
Startup time: 1.3 seconds | Startup time: 1.9 seconds |
Memory usage: 55.7 MB | Memory usage: 69.1 MB |
Application size: 156 MB | Application size: 150 MB |
<PublishReadyToRun>true</PublishReadyToRun>
dotnet publish -r win-x64 -c Release
<PublishTrimmed>true</PublishTrimmed>
dotnet publish -r win-x64 -c Release