نظرات نظرسنجی‌ها
چه نوع محیط کاری را بیشتر ترجیح می‌دهید؟
چندتا سوال: دوستان شما اصلا با کاربران برنامه تون به صورت تلفنی صحبت نمی‌کنید؟
بحران‌های برنامه رو چجوری رفع میکنید و یا با این استرس چه جوری مقابل میکنید؟(مثالا برنامه تحت وب درست است اما نیمی از کابران با اپ اندرویدی به مشکل مسخره‌ای بر می‌خورند که به منطق نزدیک نیست"در نگاه اول" و نزدیک تعطیلات عید هم هستید و مراجعه به برنامه بسیار بالاست و کارفرمایان هم تا سر حد مرگ درگیر فراهم کردن اجناس برای شعبه‌ها و...)
من در منزل نمی‌تونم تمرکز کنم و در محیط کاری هم متاسفانه پشتیبانی برنامه با برنامه نویسان است(بابت دریافت مشکلات برنامه) و بعضی روزها بابت فک زدن زیادی خیلی خیلی خسته میشم اما اینکار باعث دلگرمی مشتری میشه تا در موقعیتی که زمان پروژه تمام شده است اما مشکلات هنوز تموم نشد است، دل گرمی برای کارفرماست.
برای همین من در روزهای تعطیل (تعطیلات عید) در دفتر در حال یادگیری مواردی هستم که به آن‌ها علاقه دارم. اما محیط مناسب به نظرم محیطی است که یک تیم در یک اتاق باشند. اما باید قوانینی برای برقراری ارتباط و پرسیدن سوال و راهنمایی و هماهنگی باشه.
مطالب
کار با اسناد در RavenDb 4، ثبت و ویرایش
اگر تا بحال با بانک‌های NoSql کار کرده و لذت برده‌اید، به شما پیشنهاد میکنم حتما RavenDb را هم امتحان کنید، تا لذت استفاده از NoSql را چندین برابر حس کنید! RavenDb یک بانک اطلاعاتی NoSql از نوع DocumentStore است که به‌صورت متن باز توسعه داده می‌شود و مخزن کد آن در Github موجود است. از ویژگی‌های بارز RavenDb نسبت به سایر DocumentStoreها، Transactional بودن میباشد و در نسخه‌ی 4 بصورت کامل از Net Core. پشتیبانی میکند. برای آشنایی بیشتر با NoSql میتوانید از مقالات موجود در گروه NoSql استفاده کنید و برای آشنایی با RavenDb از دوره ای که در سایت وجود دارد استفاده نمایید(دوره مربوط به نسخه‌ی 3.5 می‌باشد).
از بارز‌ترین ویژگی‌های NoSqlها توانایی آن‌ها در ذخیره‌ی اطلاعات، بدون توجه به اسکیمای آن هاست؛ پس هر نوع مدلی که ما برای ذخیره اطلاعات نرم افزار تعریف میکنیم، فقط برای درک بهتر ما هست و بس!

با این مقدمه مدل‌های زیر را برای شروع کار داریم:
Public Class User
{
        public string Id { get; set; }
        public string PhoneNumber { get; set; }
        public Dictionary<string, App> Apps { get; set; }
}
public class App
{
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string UserName { get; set; }
        public List<string> Roles { get; set; }
        public List<String> Messages { get; set; }
        public String AdressId { get; set; }
        public bool IsActive { get; set; } = true;
        [JsonIgnore]
        public string DisplayName => $"{FirstName} {LastName}";
}

در این مدل، هر کاربر با یک شماره تماس میتواند در چندین برنامه ثبت شود و اطلاعات او در هر برنامه هم میتواند متفاوت باشد.
برای اتصال به RavenDb، به DocumentStore و برای ارسال درخواست‌ها به سمت سرور، به DocumentSession نیاز داریم. نمونه سازی DocumentStore هزینه‌بر بوده و باید در طی اجرای نرم افزار فقط یکبار(Singleton) نمونه سازی شود. DocumentSession بسیار سبک بوده و باید به ازای هر درخواست که به سمت سرور RavenDb ارسال میگردد یک بار نمونه سازی شده و بعد از آن نابود شود. پس برای استفاده در ASP.NET Core به این پیاده سازی در Startup میرسیم:
services.AddSingleton<IDocumentStore>(serviceProvider =>
{
      var store = new DocumentStore()
      {
            Urls = new[] { "http://192.168.1.10:8080" },
            Database = "AccountingSystem",
      }.Initialize();
      return store;
});

services.AddScoped<IAsyncDocumentSession>(serviceProvider =>
{
      var store = serviceProvider.GetRequiredService<IDocumentStore>();
      return store.OpenAsyncSession();
});

حال در تمام بخش‌های نرم افزار می‌توانیم DocumentSession استفاده کنیم.
برای ذخیره سازی مدل در RavenDb از کد زیر استفاده می‌کنیم:
var user = new User
{
      PhoneNumber = user.PhoneNumber
};
user.Apps.Add(appCode, new ActiveApp
{
       FirstName = "عبدالصالح",
       LastName = "کاشانی",
       UserName = abdossaleh,
       IsActive = true,
       RolesId = new List<string>{"Admin"}
});
await _documentSession.StoreAsync(user);
await _documentSession.SaveChangesAsync()

این ساده‌ترین کاری هست که میتوانیم انجام دهیم. بلافاصله بعد از استفاده از متد StoreAsync و بدون رفت و برگشتی به سرور، ویژگی Id برای user مقداردهی می‌شود و توضیح این رفتار هم پیشتر گفته شده است. با فراخوانی متد SaveChangesAsync تغییرات اتفاق افتاده در DocumentSession برای ذخیره سازی به سمت سرور ارسال می‌شوند. بله! الگوی Repository و UnitOfWork.
حال برای دریافت همین مدل، در صورتیکه Id آن را در اختیار داشته باشیم، از متد LoadAsync استفاده میکنیم.
var user = await _documentSession.LoadAsync<User>("Users/131-A");
با لود شدن کاربر، این Entity تحت نظر قرار میگیرد و اگر تغییری در هر کدام از ویژگی‌های آن صورت گیرد و متد SaveChangesAsync فراخوانی شود، کل مدل برای به‌روزرسانی به سمت سرور ارسال میشود. کل مدل و این به معنای بار اضافی در شبکه هست. البته در مدل‌های کوچک بهتر است که همین کار را انجام دهیم. ولی در اینجا به عمد مدلی را انتخاب کرده‌ایم که اطلاعات زیادی را در خود نگهداری میکند و ارسال تمام آن به ازای یک تغییر کوچک به صرفه نیست! خوشبختانه RavenDb برای حل این مشکل امکانات جالبی را در اختیار ما قرار داده که در ادامه آن‌ها را بررسی می‌کنیم.

Patching
به معنای تغییر دادن قسمتی از سند که شامل تغییر مقادیر، اضافه یا حذف یک ویژگی، ایجاد تغییرات در لیست و ... می‌باشد. با استفاده از متدهای Patch سند، میتوانیم بدون نیاز به لود سند و تغییر و ذخیره آن، قسمتی از سند را ویرایش کنیم. عملیات Patch، سمت سرور اجرا می‌شوند. برای مثال برای تغییر شماره تماس، از متد زیر استفاده می‌کنیم:
_documentSession.Advanced.Patch<User, string>("Users/131-A",
      u => u.PhoneNumber
      , "09131110000");
که مدلی را که میخواهیم تغییر دهیم، به همراه نوع ویژگی مورد نظر برای تغییر، دریافت میکند و بعد از آن، به ترتیب Id سند مورد نظر، ویژگی مورد نظر برای اعمال تغییر و مقدار را میگیرد و با فراخوانی SaveChangesAsync این تغییرات اعمال می‌شوند. نکته‌ای که باید توجه کنید این است که اگر مدلی را لود کردید و در فیلدهای آن تغییری ایجاد نموده‌اید، دیگر نمیتوانید از Patch یا Defer (توضیح داده میشود) استفاده کنید. به عبارت دیگر در هر درخواست یا باید از سیستم Tracking خود RavenDb استفاده کنید و یا از Patching!
برای اضافه کردن یک آیتم به لیست،  از Patch بصورت زیر استفاده میکنیم:
_documentSession.Advanced.Patch<User, string>("Users/131-A",
      u => u.Apps["59"].RolesId
      , r => r.Add("Admin"));

برای اضافه کردن مقداری به یک مقدار عددی در RavenDb، از متد Increment بصورت زیر استفاده میکنیم:
 _documentSession.Advanced.Increment<User, int>("Users/131-A", x => x.TestProp, 10);
متد Patch برای کارهای ساده‌ی اینچنین بسیار کاربردی می‌باشد؛ ولی برای کارهای پیشرفته‌تر کارآیی ندارد. به همین دلیل متد Defer در کنار آن معرفی شده‌است که فوق العاده کاربردی ولی اصطلاحا non-typed است و تحت نظارت Compiler نیست. برای مثال اضافه کردن یک مقدار به Dictionary ما، از طریق Patch امکان ندارد. اما اینکار با استفاده از متد Defer و کدهای JavaScript به‌سادگی زیر می‌باشد:
_documentSession.Advanced.Defer(new PatchCommandData("Users/131-A", null,
                              new PatchRequest()
                              {
                                    Script = $@"this.Apps[args.appCode] = args.app",
                                    Values =
                                         {
                                              {"appCode", appCode},
                                              {"app", new ActiveApp
                                                   {
                                                        FirstName = "عبدالصالح",
                                                        LastName = "کاشانی",
                                                        UserName = abdossaleh,
                                                        RolesId = new List<string>{"Admin"}
                                                    }
                                              }
                                          }
                              }, null));
متد Defer شناسه‌ی سند مورد نظر را گرفته و اسکریپت ما را با آرگومان‌های ارسالی، بر روی سند اعمال میکند. Defer دسترسی کاملی را به ما برای تغییر در سند میدهد. برای نمونه میتوانیم آیتمی را به مکان خاصی از لیست اضافه کنیم (برای کوتاه‌تر شدن اسکریپت‌ها فقط بخش Script و Value را ذکر میکنم):
Script = "this.Apps[args.app].Roles.splice(args.index,0,args.role)",
Values =
        {
            {
                "index": 1 // مکانی که میخواهیم عملیات انجام شود
                "app", 59
                "role", "User"
            }
        }
this در اینجا به سند جاری اشاره میکند.
از همین روش میتوانیم برای ویرایش کردن یک آیتم هم استفاده کنیم. برای مثال اگر مقدار 0 را در متد splice به یک تغییر دهیم، عملیات ویرایش صورت می‌گیرد (در واقع حذف آیتم در مکان index و درج آیتم جدید در همان مکان):
splice(args.index,1,args.role)
و برای حذف تمام آیتم‌های لیست جز یک آیتم خاص، از کد زیر استفاده میکنیم:
Script = @"this.Roles= this.Apps[args.app].Roles.filter(role=> role != args.role);",
        Values =
        {
            {"app", 59}
            {"role", "User"}
        }
همانطور که مشاهده می‌کنید به راحتی می‌توانیم کدهای جاوا اسکریپتی خود را در Defer استفاده کنیم. اما این قدرت زیاد، امکان اشتباه در کدهای ما را زیاد میکند چرا که تحت کنترل کامپایلر نیست.
نظرات مطالب
استفاده از GitHub Actions برای Build و توزیع خودکار پروژه‌های NET Core.
یک نکته تکمیلی:
برای Build پروژه‌های NET Framework Full. (مانند پروژه‌های ASP.NET MVC قدیم) باید به شکل زیر عمل کنید.
jobs:
  build:

    runs-on: windows-latest
    
    steps:
    - name: Checkout
      uses: actions/checkout@v2
        
    - name: Setup MSBuild
      uses: microsoft/setup-msbuild@v1.0.0  
       
    - name: Setup NuGet
      uses: nuget/setup-nuget@v1.0.2
     
    - name: Restore NuGet Packages
      run: nuget restore src/WebApplication1.sln
 
    - name: Build (Release)
      run: msbuild src/WebApplication1.sln /p:Configuration=Release
  • دستور runs-on: windows-latest این workflow را بر روی ویندوز اجرا میکنه (بدیهیه که پروژه‌های دات نت Full بر روی غیر از ویندوز قابل build شدن نیست)
  • در step دوم MSBuild رو توسط اکشن microsoft/setup-msbuild نصب میکنیم.
  • در step سوم Nuget رو توسطاکشن nuget/setup-nuget نصب میکنیم.
  • در step چهارم وابستگی (پکیج)‌های پروژه رو restore میکنیم. 
  • در step چهارم پروژه رو توسط msbuild و در مود Release بیلد میکنیم.
  • عبارت src/WebApplication1.sln به پوشه و نام فایل سلوشن شما اشاره میکنه.

و برای Test پروژه‌های خود میتونین از اکشن  Malcolmnixon/Setup-VSTest به نحو زیر استفاده کنین
    - name: Setup VSTest
      uses: Malcolmnixon/Setup-VSTest@v2

    - name: VSTest
      run: vstest.console ClassLibrary.Test\bin\Debug\ClassLibrary.Test.dll
عبارت ClassLibrary.Test\bin\Debug\ClassLibrary.Test.dll   به مسیر و نام dll تست شما اشاره میکنه.
مطالب
رفع تداخل jQuery با کتابخانه‌های مشابه

قبل از شروع، یک خبر!
VsDoc for jQuery 1.3.1 (جهت فعال سازی intellisense آخرین نگارش جی کوئری در VS.Net)


اگر سعی کنید jQuery را به همراه سایر کتابخانه‌های جاوا اسکریپتی دیگر به صورت همزمان استفاده کنید (مثلا mootools یا ASP.Net Ajax و امثال آن)، احتمالا قسمتی و یا تمامی کدهای جاوا اسکریپتی شما کار نخواهند کرد. برای مثال update panel شما در ASP.Net Ajax از کار می‌افتد، یا کدهای mootools شما دیگر کار نمی‌کنند. علت اینجا است که تمامی این کتابخانه‌ها از نشانه $ به عنوان متغیری عمومی که بیانگر نام مستعار کتابخانه مربوطه است استفاده می‌کنند و در نهایت تمام این‌ها با هم تداخل خواهند کرد.

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

<script type="text/javascript" language="javascript" src="jquery.min.js"></script>
<script type="text/javascript">
jQuery.noConflict();
jQuery(document).ready(function($) {
//tip-1
$("select > option").each(function() {
var obj = $(this);
obj.attr("title", obj.attr("value"));
});
//tip-1
});
</script>

کد مثال فوق، به تمامی آیتم‌های drop down list‌ های شما در یک صفحه، بر اساس value هر آیتم موجود در آن‌ها، یک tooltip اضافه می‌کند. (با IE7 به بعد و فایرفاکس سازگار است)
در اینجا ابتدا jQuery.noConflict فراخوانی شده و سپس document ready متداول هم باید اندکی مطابق کد فوق تغییر کند. مابقی کدهای شما از این پس نیازی به تغییر نخواهند داشت. (روش‌های دیگری هم برای تغییر نام $ وجود دارند که در مستندات مربوطه قابل مشاهده است)

مطالب
مقدار دهی کلیدهای خارجی در NHibernate و Entity framework

ORM های NHibernate و Entity framework روش‌های متفاوتی را برای به روز رسانی کلید خارجی با حداقل رفت و برگشت به دیتابیس ارائه می‌دهند که در ادامه معرفی خواهند شد.

صورت مساله:
فرض کنید می‌خواهیم برنامه‌ای را بنویسیم که ریز پرداخت‌های روزانه‌ی ما را ثبت کند. برای اینکار حداقل به یک جدول گروه‌های اقلام خریداری شده، یک جدول حساب‌های تامین کننده‌ی مخارج، یک جدول فروشنده‌ها و نهایتا یک جدول صورتحساب‌های پرداختی بر اساس جداول ذکر شده نیاز خواهد بود.

الف) بررسی مدل برنامه



در اینجا جهت تعریف ویژگی‌ها یا Attributes تعریف شده در این کلاس‌ها از NHibernate validator استفاده شده (+). مزیت اینکار هم علاوه بر اعتبارسنجی سمت کلاینت (پیش از تبادل اطلاعات با بانک اطلاعاتی)، تولید جداولی با همین مشخصات است. برای مثال Fluent NHibernate بر اساس ویژگی Length تعریف شده با طول حداکثر 120 ، یک فیلد nvarchar با همین طول را ایجاد می‌کند.

public class Account
{
public virtual int Id { get; set; }

[NotNullNotEmpty]
[Length(Min = 3, Max = 120, Message = "طول نام باید بین 3 و 120 کاراکتر باشد")]
public virtual string Name { get; set; }
}

public class Category
{
public virtual int Id { get; set; }

[NotNullNotEmpty]
[Length(Min = 3, Max = 130, Message = "طول نام باید بین 3 و 130 کاراکتر باشد")]
public virtual string Name { get; set; }
}

public class Payee
{
public virtual int Id { get; set; }

[NotNullNotEmpty]
[Length(Min = 3, Max = 120, Message = "طول نام باید بین 3 و 120 کاراکتر باشد")]
public virtual string Name { get; set; }
}

public class Bill
{
public virtual int Id { get; set; }

[NotNull]
public virtual Account Account { get; set; }

[NotNull]
public virtual Category Category { get; set; }

[NotNull]
public virtual Payee Payee { get; set; }

[NotNull]
public virtual decimal Amount { set; get; }

[NotNull]
public virtual DateTime BillDate { set; get; }

[NotNullNotEmpty]
[Length(Min = 1, Max = 500, Message = "طول توضیحات باید بین 1 و 500 کاراکتر باشد")]
public virtual string Description { get; set; }
}




ب) ساختار جداول متناظر (تولید شده به صورت خودکار توسط Fluent NHibernate در اینجا)


در مورد نحوه‌ی استفاده از ویژگی AutoMapping و همچنین تولید خودکار ساختار بانک اطلاعاتی از روی جداول در NHibernate قبلا توضیح داده شده است. البته بدیهی است که ترکیب مقاله‌ی Validation و آشنایی با AutoMapping در اینجا جهت اعمال ویژگی‌ها باید بکار گرفته شود که در همان مقاله‌ی Validation مفصل توضیح داده شده است.
نکته‌ی مهم database schema تولیدی، کلید‌های خارجی (foreign key) تعریف شده بر روی جدول Bills است (همان AccountId، CategoryId و PayeeId تعریف شده) که به primary key جداول متناظر اشاره می‌کند.
    create table Accounts (
AccountId INT IDENTITY NOT NULL,
Name NVARCHAR(120) not null,
primary key (AccountId)
)

create table Bills (
BillId INT IDENTITY NOT NULL,
Amount DECIMAL(19,5) not null,
BillDate DATETIME not null,
Description NVARCHAR(500) not null,
AccountId INT not null,
CategoryId INT not null,
PayeeId INT not null,
primary key (BillId)
)

create table Categories (
CategoryId INT IDENTITY NOT NULL,
Name NVARCHAR(130) not null,
primary key (CategoryId)
)

create table Payees (
PayeeId INT IDENTITY NOT NULL,
Name NVARCHAR(120) not null,
primary key (PayeeId)
)

alter table Bills
add constraint fk_Account_Bill
foreign key (AccountId)
references Accounts

alter table Bills
add constraint fk_Category_Bill
foreign key (CategoryId)
references Categories

alter table Bills
add constraint fk_Payee_Bill
foreign key (PayeeId)
references Payees

ج) صفحه‌ی ثبت صورتحساب‌ها

صفحات ثبت گروه‌های اقلام، حساب‌ها و فروشنده‌ها، نکته‌ی خاصی ندارند. چون این جداول وابستگی خاصی به جایی نداشته و به سادگی اطلاعات آن‌ها را می‌توان ثبت یا به روز کرد.
صفحه‌ی مشکل در این مثال، همان صفحه‌ی ثبت صورتحساب‌ها است که از سه کلید خارجی به سه جدول دیگر تشکیل شده است.
عموما برای طراحی این نوع صفحات، کلیدهای خارجی را با drop down list نمایش می‌دهند و اگر در جهت سهولت کار کاربر قدم برداشته شود، باید از یک Auto complete drop down list استفاده کرد تا کاربر برنامه جهت یافتن آیتم‌های از پیش تعریف شده کمتر سختی بکشد.



اگر از Silverlight یا WPF استفاده شود، امکان بایند یک لیست کامل از اشیاء با تمام خواص مرتبط به آن‌ها وجود دارد (هر رکورد نمایش داده شده در دراپ داون لیست، دقیقا معادل است با یک شیء متناظر با کلاس‌های تعریف شده است). اگر از ASP.NET استفاده شود (یعنی یک محیط بدون حالت که پس از نمایش یک صفحه دیگر خبری از لیست اشیاء بایند شده وجود نخواهد داشت و همگی توسط وب سرور جهت صرفه جویی در منابع تخریب شده‌اند)، بهتر است datatextfield را با فیلد نام و datavaluefield را با فیلد Id مقدار دهی کرد تا کاربر نهایی، نام را جهت ثبت اطلاعات مشاهده کند و برنامه از Id موجود در لیست جهت ثبت کلیدهای خارجی استفاده نماید.
و نکته‌ی اصلی هم همینجا است که چگونه؟! چون ما زمانیکه با یک ORM سر و کار داریم، برای ثبت یک رکورد در جدول Bills باید یک وهله از کلاس Bill را ایجاد کرده و خواص آن‌را مقدار دهی کنیم. اگر به تعریف کلاس Bill مراجعه کنید، سه خاصیت آن از نوع سه کلاس مجزا تعریف شده است. به به عبارتی با داشتن فقط یک id از رکوردهای این کلاس‌ها باید بتوان سه وهله‌ی متناظر آن‌ها را از بانک اطلاعاتی خواند و سپس به این خواص انتساب داد:

var newBill = new Bill
{
Account = accountRepository.GetByKey(1),
Amount = 1,
BillDate = DateTime.Now,
Category = categoryRepository.GetByKey(1),
Description = "testestest...",
Payee = payeeRepository.GetByKey(1)
};
یعنی برای ثبت یک رکورد در جدول Bills فوق، چهار بار رفت و برگشت به دیتابیس خواهیم داشت:
- یکبار برای دریافت رکورد متناظر با گروه‌ها بر اساس کلید اصلی آن (که از دراپ داون لیست مربوطه دریافت می‌شود)
- یکبار برای دریافت رکورد متناظر با فروشند‌ه‌ها بر اساس کلید اصلی آن (که از دراپ داون لیست مربوطه دریافت می‌شود)
- یکبار برای دریافت رکورد متناظر با حساب‌ها بر اساس کلید اصلی آن (که از دراپ داون لیست مربوطه دریافت می‌شود)
- یکبار هم ثبت نهایی اطلاعات در بانک اطلاعاتی

متد GetByKey فوق همان متد session.Get استاندارد NHibernate است (چون به primary key ها از طریق drop down list دسترسی داریم، به سادگی می‌توان بر اساس متد Get استاندارد ذکر شده عمل کرد).

SQL نهایی تولیدی هم به صورت واضحی این مشکل را نمایش می‌دهد (4 بار رفت و برگشت؛ سه بار select یکبار هم insert نهایی):
SELECT account0_.AccountId as AccountId0_0_, account0_.Name as Name0_0_
FROM Accounts account0_ WHERE account0_.AccountId=@p0;@p0 = 1 [Type: Int32 (0)]

SELECT category0_.CategoryId as CategoryId2_0_, category0_.Name as Name2_0_
FROM Categories category0_ WHERE category0_.CategoryId=@p0;@p0 = 1 [Type: Int32 (0)]

SELECT payee0_.PayeeId as PayeeId3_0_, payee0_.Name as Name3_0_
FROM Payees payee0_ WHERE payee0_.PayeeId=@p0;@p0 = 1 [Type: Int32 (0)]

INSERT INTO Bills (Amount, BillDate, Description, AccountId, CategoryId, PayeeId)
VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
select SCOPE_IDENTITY();
@p0 = 1 [Type: Decimal (0)],
@p1 = 2010/12/27 11:48:33 ق.ظ [Type: DateTime (0)],
@p2 = 'testestest...' [Type: String (500)],
@p3 = 1 [Type: Int32 (0)],
@p4 = 1 [Type: Int32 (0)],
@p5 = 1 [Type: Int32 (0)]

کسانی که قبلا با رویه‌های ذخیره شده کار کرده باشند (stored procedures) احتمالا الان خواهند گفت؛ ما که گفتیم این روش کند است! سربار زیادی دارد! فقط کافی است یک SP بنویسید و کل عملیات را با یک رفت و برگشت انجام دهید.
اما در ORMs نیز برای انجام این مورد در طی یک حرکت یک ضرب راه حل‌هایی وجود دارد که در ادامه بحث خواهد شد:

د) پیاده سازی با NHibernate
برای حل این مشکل در NHibernate با داشتن primary key (برای مثال از طریق datavaluefield ذکر شده)، بجای session.Get از session.Load استفاده کنید.
session.Get یعنی همین الان برو به بانک اطلاعاتی مراجعه کن و رکورد متناظر با کلید اصلی ذکر شده را بازگشت بده و یک شیء از آن را ایجاد کن (حالت‌های دیگر دسترسی به اطلاعات مانند استفاده از LINQ یا Criteria API یا هر روش مشابه دیگری نیز در اینجا به همین معنا خواهد بود).
session.Load یعنی فعلا دست نگه دار! مگر در جدول نهایی نگاشت شده، اصلا چیزی به نام شیء مثلا گروه وجود دارد؟ مگر این مورد واقعا یک فیلد عددی در جدول Bills بیشتر نیست؟ ما هم که الان این عدد را داریم (به کمک عناصر دراپ داون لیست)، پس لطفا در پشت صحنه یک پروکسی برای ایجاد شیء مورد نظر ایجاد کن (uninitialized proxy to the entity) و سپس عملیات مرتبط را در حین تشکیل SQL نهایی بر اساس این عدد موجود انجام بده. یعنی نیازی به رفت و برگشت به بانک اطلاعاتی نیست. در این حالت اگر SQL نهایی را بررسی کنیم فقط یک سطر زیر خواهد بود (سه select ذکر شده حذف خواهند شد):
INSERT INTO Bills (Amount, BillDate, Description, AccountId, CategoryId, PayeeId)
VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
select SCOPE_IDENTITY();
@p0 = 1 [Type: Decimal (0)],
@p1 = 2010/12/27 11:58:22 ق.ظ [Type: DateTime (0)],
@p2 = 'testestest...' [Type: String (500)],
@p3 = 1 [Type: Int32 (0)],
@p4 = 1 [Type: Int32 (0)],
@p5 = 1 [Type: Int32 (0)]

ه) پیاده سازی با Entity framework

Entity framework زمانیکه بانک اطلاعاتی فوق را (به روش database first) به کلاس‌های متناظر تبدیل/نگاشت می‌کند، حاصل نهایی مثلا در مورد کلاس Bill به صورت خلاصه به شکل زیر خواهد بود:
public partial class Bill : EntityObject
{
public global::System.Int32 BillId {set;get;}
public global::System.Decimal Amount {set;get;}
public global::System.DateTime BillDate {set;get;}
public global::System.String Description {set;get;}
public global::System.Int32 AccountId {set;get;}
public global::System.Int32 CategoryId {set;get;}
public global::System.Int32 PayeeId {set;get;}
public Account Account {set;get;}
public Category Category {set;get;}
}
به عبارتی فیلدهای کلیدهای خارجی، در تعریف نهایی این کلاس هم مشاهده می‌شوند. در اینجا فقط کافی است سه کلید خارجی، از نوع int مقدار دهی شوند (و نیازی به مقدار دهی سه شیء متناظر نیست). در این حالت نیز برای ثبت اطلاعات، فقط یکبار رفت و برگشت به بانک اطلاعاتی خواهیم داشت.

مطالب
آموزش فایرباگ - #10 - DOM Panel
در پنل DOM توابع و متغییرهایی که در صفحه وجود دارند بصورت درختی نمایش داده می‌شوند. object‌ها و array‌ها قابل باز شدن هستند و بصورت درختی می‌توانید محتویات آن‌ها را مشاهده کنید. توابع هم بصورت لینک هستند که با کلیک برون آن ها، کد مربوط در پنل Script نمایش داده می‌شود. توجه کنید که محتویاتی که مشاهده می‌کنید برای همان لحظه ای است که پنل را باز کردید و برای مشاهده تغییرات ثانویه باید محتویات پنل را بروزرسانی کرد.

قبل از بررسی این پنل اجازه دهید نگاهی به تعریف DOM بیندازیم.

DOM چیست؟
مدل شیءگرای سند یا دام (DOM - Document Object Model) عنوان یکی از دو ساختوارۀ (architecture) اصلی است (در کنار اس‌اِی‌اکس) که بر اساس آن سندهای اکس‌ام‌ال را به اشیایی که در بردارندهٔ آن است، تجزیه نموده، و آن‌ها را به‌صورت یک ساختار درختی داده‌ها در فضای حافظه اصلی پهن می‌کند. ساختوارۀ دام، نه به زبان برنامه‌نویسی خاصّی وابستگی دارد و نه به سکّوی برنامه‌نویسی ویژه‌ای، بلکه، به منظور اجراء و پیاده‌سازی آن باید از یک زبان برنامه‌نویسی بلندتراز همچون جاوا، سی‌شارپ، جاوااسکریپت یا مشابه آن‌ها سود بجوییم. آنسوی رابط کاربر سند با مدلی شیءگرا نمایانده می‌شود. 


Options Menu
این منو با راست کلیک کردن بروی نام پنل یا کلیک کردن بروی مثلثی که روی پنل قرار دارد، نمایش داده می‌شود.

  • Show User-defined Properties
    در صورت فعال بودن، پراپرتی هایی که توسط کاربر به صفحه اضافه شده اند را نمایش می‌دهد.
  • Show User-defined Functions
    در صورت فعال بودن، توابعی که توسط کاربر به صفحه اضافه شده اند را نمایش می‌دهد.
  • Show DOM Properties
    در صورت فعال بودن، پراپرتی هایی که بصورت پیشفرض در DOM وجود دارند را نمایش می‌دهد.
  • Show DOM Functions
     در صورت فعال بودن، توابعی که بصورت پیشفرض در DOM وجود دارند را نمایش می‌دهد.
  • Show DOM Constants
    در صورت فعال بودن، const هایی که بصورت پیشفرض در DOM وجود دارند را نمایش می‌دهد.
  •  Show Inline Event Handlers
    در صورت فعال بودن، رویدادهایی که بصورت خطی در تگ‌ها تعریف شده اند را نمایش می‌دهد.
  • Show Closures
    در صورت فعال بودن، Closure‌ها را نمایش می‌دهد.
  • Show Own Properties Only
    در صورت فعال بودن، فقط پراپرتی هایی که بروی خود شئ تعریف شده اند را نمایش می‌دهد.
  • Show Enumerable Properties Only
    در صورت فعال بودن، فقط پراپرتی‌های شمارشی را نمایش می‌دهد.
  • Refresh
    محتویات پنل را بروزرسانی می‌کند.


Property Path
این قسمت در بالاترین بخش پنل قرار دارد و وظیفه‌ی آن نمایش مسیر شئ از خود شئ تا window است.
همچنین با راست کلیک کردن بروی این قسمت دو گزینه نمایش داده می‌شود. Refresh برای بروزسازی آدرس نمایش داده شده و Use in Command Line هم برای استفاده از شئ در خط فرمان است. پس از راست کلیک کردن بروی یک شئ و انتخاب گزینه‌ی Use in Command Line فایرباگ تمرکز برنامه را به خط فرمان منتقل می‌کند و شئ را تحت متغییری به نام $p در خط فرمان کپی می‌کند.


رنگ ها
برای مشخص کردن نوع متغییرهای این پنل، فایرباگ برای هر نوع متغییر از یک رنگ استفاده می‌کند.
اشیاء
، اشیاء DOM ، توابع Getter ، توابع تعریفی کاربر ، توابع DOM ، توابع Constructor ، پراپرتی‌های Read-only 


Auto-Completion
مشابه پنل‌های Console, CSS, HTML در این پنل هم امکان اعمال تغییرات همراه با قابلیت تکمیل خودکار وجود دارد.


localStorage
در HTML5 سیستمی برای ذخیره مقادیر در سمت کاربر، به نام localStorage معرفی شد. در این پنل می‌توانید محتویات آن را بررسی/ویرایش کنید. برای کار با توابع آن هم می‌توانید از پنل Console استفاده کنید. ( همچنین می‌توانید از پنل Console بصورت Popup در این پنل و پنل‌های دیگر هم استفاده کنید. به تصویر زیر توجه فرمایید. )
توجه کنید که برای مشاهده‌ی این شئ، باید گزینه‌ی Show DOM Properties فعال باشد و همیچنین در Property Path، شئ window فعال باشد.

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


Context Menu
با راست کلیک کردن در قسمت‌های مختلف پنل، منوهای متفاوتی را خواهید دید. همچنین با راست کلیک کردن بروی مقادیر پراپرتی ها، منوی متناسب با آن مقدار را خواهید دید. مثلا اگر بروی یک تگ HTML در این پنل راست کلیک کنید، منویی که خواهید دید همان منویی است که در پنل HTML مشاهده می‌کردید.

 گزینه Context
توضیحات
Copy Name
Property List نام پراپرتی را در حافظه کپی می‌کند.
Copy Path Property List آدرس پراپرتی را در حافظه کپی می‌کند.
Copy Value
String and Number values محتوای پراپرتی را در حافظه کپی می‌کند.
Edit Property... Property List ( پراپرتی و توابع کاربر )
پراپرتی را به حالت ویرایش می‌آورد.
Delete Property Property List ( پراپرتی و توابع کاربر ) پراپرتی را حذف می‌کند.
Break On Property Change Property List ( پراپرتی و توابع کاربر ) مشابه پاراگرف قبلی.
Refresh Property List, Property Path محتویات پنل را بروزرسانی می‌کند.

برای توابع هم دو منوی اضافی وجود دارد:

    • ‪ Log Calls to "<function name>"
      فراخوانی‌های تابع مورد نظر را Log می‌کند. ( برای توضیحات بیشتر دستور monitor که از توابع خط فرمان است را ملاحظه بفرمایید. )
    • Copy Function
      نام و بنده‌ی تابع را در حافظه کپی می‌کند.
نظرات مطالب
از سرگیری مجدد، لغو درخواست و سعی مجدد دریافت فایل‌های حجیم توسط HttpClient
سلام؛ اگر قصد داشته باشیم از طریق Angular 2  دانلود رو فراخوانی کنیم ، به شکلی که یک فایل حجیم بر روی مرورگر کاربر دانلود بشه ، پیاده سازی به چه صورت خواهد بود ، الان شما در سمت سرور یک حلقه while دارید که محتوی رو می‌خونه ، این یعنی در هر اجرای حلقه من باید محتوی خوانده شده رو return کنم به کلاینت و دوباره با آفست جدید فراخوانی کنم؟
نحوه فراخوانی من به شکل زیر هست که با FileStreamResult برگشتی از سمت سرور به درستی کار میکنه ولی خب همه فایل رو یکجا برمی گردونه :
 downloadOrder(orderId: number , userId : string) {

    this._http.get(this._config.getApiURI() + '/Download/productfile/' + orderId + '/' + userId, { observe: 'blob'})
      .subscribe(
        (data) => {
          if(this._functionService.isNullOrEmpty(data.body)){
            this._snackBarService.error('فایل پیدا نشد');
            return;
          }
          var contentType = data.body.type || "application/octet-stream";

          var fileInfo = JSON.parse(data.headers.get('FileInfo'));          
          var blob = new Blob([data.body], { type: contentType });
          var url = window.URL.createObjectURL(blob);
          var anchor = document.createElement("a");
          anchor.setAttribute("href", url)
          anchor.setAttribute("download", fileInfo.fileName + fileInfo.fileType);
          anchor.setAttribute("target", "_blank");
          document.body.appendChild(anchor);
          anchor.click();
          setTimeout(function () {
            document.body.removeChild(anchor);
            window.URL.revokeObjectURL(url);
          }, 200);
        },
        error => {
          this._snackBarService.error(error);
        });
  }
ولی حالا که قراره فایل رو در چندین مرحله و با حلقه while برگردونیم ، با این سناریو یعنی باید از طرف کلاینت چندین بار فراخوانی باشه ، مگر اینکه کلا یک window جدید باز کنیم  (که احتمالا با مشکل popup blocker مواجه میشیم) و ... آیا باید در سمت سرور بایت‌های خوانده شده رو تو Response بنویسیم ...
نحوه پیاده سازی کد پایین به چه شکل میشه
             while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)  
                    {                        
                        await Response.Body.WriteAsync(buffer);                        
                        //return File(buffer, GetContentType(fullPath), newFileName, true);                        
                        //return new FileStreamResult(stream,"application/octet-stream");
                    }
مطالب
نمایش فرم‌های مودال Ajax ایی در ASP.NET MVC به کمک Twitter Bootstrap
اصول نمایش اطلاعات مودال به کمک bootstrap در مطلب «استفاده از modal dialogs مجموعه Twitter Bootstrap برای گرفتن تائید از کاربر» بررسی شدند.
در این قسمت قصد داریم یک فرم Ajaxایی را در ASP.NET MVC به همراه تمام مسایل اعتبارسنجی، پردازش اطلاعات و غیره را به کمک Twitter Bootstrap و jQuery Ajax پیاده سازی کنیم.


تهیه افزونه jquery.bootstrap-modal-ajax-form.js

از این جهت که مباحث مرتبط با نمایش و پردازش فرم‌های مودال Ajaxایی به کمک Twitter Bootstrap اندکی نکته دار و طولانی هستند، بهتر است این موارد را به شکل یک افزونه، کپسوله کنیم. کدهای کامل افزونه jquery.bootstrap-modal-ajax-form.js را در ادامه ملاحظه می‌کنید:
// <![CDATA[
(function ($) {
    $.bootstrapModalAjaxForm = function (options) {
        var defaults = {
            renderModalPartialViewUrl: null,
            renderModalPartialViewData: null,
            postUrl: '/',
            loginUrl: '/login',
            beforePostHandler: null,
            completeHandler: null,
            errorHandler: null
        };
        var options = $.extend(defaults, options);

        var validateForm = function (form) {
            //فعال سازی دستی اعتبار سنجی جی‌کوئری
            var val = form.validate();
            val.form();
            return val.valid();
        };

        var enableBootstrapStyleValidation = function () {
            $.validator.setDefaults({
                highlight: function (element, errorClass, validClass) {
                    if (element.type === 'radio') {
                        this.findByName(element.name).addClass(errorClass).removeClass(validClass);
                    } else {
                        $(element).addClass(errorClass).removeClass(validClass);
                        $(element).closest('.control-group').removeClass('success').addClass('error');
                    }
                    $(element).trigger('highlited');
                },
                unhighlight: function (element, errorClass, validClass) {
                    if (element.type === 'radio') {
                        this.findByName(element.name).removeClass(errorClass).addClass(validClass);
                    } else {
                        $(element).removeClass(errorClass).addClass(validClass);
                        $(element).closest('.control-group').removeClass('error').addClass('success');
                    }
                    $(element).trigger('unhighlited');
                }
            });
        }

        var enablePostbackValidation = function () {
            $('form').each(function () {
                $(this).find('div.control-group').each(function () {
                    if ($(this).find('span.field-validation-error').length > 0) {
                        $(this).addClass('error');
                    }
                });
            });
        }

        var processAjaxForm = function (dialog) {
            $('form', dialog).submit(function (e) {
                e.preventDefault();

                if (!validateForm($(this))) {
                    //اگر فرم اعتبار سنجی نشده، اطلاعات آن ارسال نشود
                    return false;
                }

                //در اینجا می‌توان مثلا دکمه‌ای را غیرفعال کرد
                if (options.beforePostHandler)
                    options.beforePostHandler();

                //اطلاعات نباید کش شوند
                $.ajaxSetup({ cache: false });
                $.ajax({
                    url: options.postUrl,
                    type: "POST",
                    data: $(this).serialize(),
                    success: function (result) {
                        if (result.success) {
                            $('#dialogDiv').modal('hide');
                            if (options.completeHandler)
                                options.completeHandler();
                        } else {
                            $('#dialogContent').html(result);
                            if (options.errorHandler)
                                options.errorHandler();
                        }
                    }
                });
                return false;
            });
        };

        var mainContainer = "<div id='dialogDiv' class='modal hide fade in'><div id='dialogContent'></div></div>";
        enableBootstrapStyleValidation(); //اعمال نکات خاص بوت استرپ جهت اعتبارسنجی یکپارچه با آن
        $.ajaxSetup({ cache: false });
        $.ajax({
            type: "POST",
            url: options.renderModalPartialViewUrl,
            data: options.renderModalPartialViewData,
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            complete: function (xhr, status) {
                var data = xhr.responseText;
                var data = xhr.responseText;
                if (xhr.status == 403) {
                    window.location = options.loginUrl; //در حالت لاگین نبودن شخص اجرا می‌شود
                }
                else if (status === 'error' || !data) {
                    if (options.errorHandler)
                        options.errorHandler();
                }
                else {
                    var dialogContainer = "#dialogDiv";
                    $(dialogContainer).remove();
                    $(mainContainer).appendTo('body');

                    $('#dialogContent').html(data); // دریافت پویای اطلاعات مودال دیالوگ
                    $.validator.unobtrusive.parse("#dialogContent"); // فعال سازی اعتبارسنجی فرمی که با ایجکس بارگذاری شده                            
                    enablePostbackValidation();
                    // و سپس نمایش آن به صورت مودال
                    $('#dialogDiv').modal({
                        backdrop: 'static', //با کلیک کاربر روی صفحه، صفحه مودال بسته نمی‌شود
                        keyboard: true
                    }, 'show');
                    // تحت نظر قرار دادن این فرم اضافه شده
                    processAjaxForm('#dialogContent');
                }
            }
        });
    };
})(jQuery);
// ]]>
توضیحات:
- توابع enableBootstrapStyleValidation و enablePostbackValidation در مطلب «اعمال کلاس‌های ویژه اعتبارسنجی Twitter bootstrap به فرم‌های ASP.NET MVC» بررسی شدند.
- این افزونه با توجه به مقدار renderModalPartialViewUrl، یک partial view را از برنامه ASP.NET MVC درخواست می‌کند.
- سپس این partial view را به صورت خودکار به صفحه اضافه کرده و آن‌را به صورت modal نمایش می‌دهد.
- پس از افزودن فرم Ajaxایی دریافتی، مسایل اعتبارسنجی را به آن اعمال کرده و سپس دکمه submit آن‌را تحت کنترل قرار می‌دهد.
- در زمان submit، ابتدا بررسی می‌کند که آیا فرم معتبر است و اعتبارسنجی آن بدون مشکل است؟ اگر اینچنین است، اطلاعات فرم را به آدرس postUrl به صورت Ajaxایی ارسال می‌کند.


کدهای مدل برنامه
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace Mvc4TwitterBootStrapTest.Models
{
    public class User
    {
        public int Id { set; get; }

        [DisplayName("نام")]
        [Required(ErrorMessage="لطفا نام را تکمیل کنید")]
        public string Name { set; get; }

        [DisplayName("نام خانوادگی")]
        [Required(ErrorMessage = "لطفا نام خانوادگی را تکمیل کنید")]
        public string LastName { set; get; }
    }
}
در اینجا یک مدل ساده‌را به همراه ویژگی‌های اعتبارسنجی و نام‌های نمایشی خواص ملاحظه می‌کنید.


کدهای کنترلر برنامه

using System.Web.Mvc;
using Mvc4TwitterBootStrapTest.Models;

namespace Mvc4TwitterBootStrapTest.Controllers
{
    public class ModalFormAjaxController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            return View(); //نمایش صفحه اولیه
        }

        [HttpPost] //برای این حالت امن‌تر است
        //[AjaxOnly]
        public ActionResult RenderModalPartialView()
        {
            //رندر پارشال ویوو صفحه مودال به همراه اطلاعات مورد نیاز آن
            return PartialView(viewName: "_ModalPartialView", model: new User { Name = "", LastName = "" });
        }

        [HttpPost]
        //[AjaxOnly]
        public ActionResult Index(User user) //ذخیره سازی اطلاعات
        {
            if (this.ModelState.IsValid)
            {
                //todo: SaveChanges;
                return Json(new { success = true });
            }

            this.ModelState.AddModelError("", "خطایی رخ داده است");
            return PartialView("_ModalPartialView", user);
        }
    }
}
کدهای کنترلر برنامه در این حالت از سه قسمت تشکیل می‌شود:
الف) متد Index حالت HttpGet که صفحه ابتدایی را نمایش خواهد داد.
ب) متد RenderModalPartialView یک partial view اضافه شده به برنامه به نام _ModalPartialView.cshtml را بازگشت می‌دهد. این partial view در حقیقت همان فرمی است که قرار است به صورت مودال نمایش داده شود و پردازش آن نیز Ajaxایی باشد.
ج) متد Index حالت HttpPost که نهایتا اطلاعات فرم مودال را دریافت خواهد کرد. اگر پردازش موفقیت آمیز بود، نیاز است همانند کدهای فوق return Json صورت گیرد. در غیراینصورت مجددا همان partial view را بازگشت دهید.


کدهای Index.cshtml

@{
    ViewBag.Title = "Index";
    var renderModalPartialViewUrl = Url.Action("RenderModalPartialView", "ModalFormAjax");
    var postDataUrl = Url.Action("Index", "ModalFormAjax");
}
<h2>
    Index</h2>
<a href="#" class="btn btn-primary" id="btnCreate">ثبت اطلاعات</a>

@section JavaScript
{
    <script type="text/javascript">
        $(function () {           
            $('#btnCreate').click(function (e) {
                e.preventDefault(); //می‌خواهیم لینک به صورت معمول عمل نکند

                $.bootstrapModalAjaxForm({
                    postUrl: '@postDataUrl',
                    renderModalPartialViewUrl: '@renderModalPartialViewUrl',
                    renderModalPartialViewData: {},
                    loginUrl: '/login',
                    beforePostHandler: function () {                       
                    },
                    completeHandler: function () {
                        // Refresh: برای حالتیکه نیاز به به روز رسانی کامل صفحه زیرین باشد
                        // location.reload();
                    },
                    errorHandler: function () {
                    }
                });
            });
        });             
    </script>
}
این کدها متناظر هستند با کدهای view اکشن متد Index در حالت Get.
- در اینجا یک لینک ساده در صفحه قرار گرفته و به کمک کلاس btn مجموعه bootstrap به شکل یک دکمه مزین شده است.
- در ادامه نحوه استفاده از افزونه‌ای را که در ابتدای بحث طراحی کردیم، ملاحظه می‌کنید. کار با آن بسیار ساده است و تنها باید مسیرهای ارسال اطلاعات نهایی به سرور یا postDataUrl و مسیر دریافت partial view رندر شده یا renderModalPartialViewUrl به آن معرفی شود. سایر مسایل آن خودکار است.


کدهای _ModalPartialView.cshtml یا همان فرم مودال برنامه

@model Mvc4TwitterBootStrapTest.Models.User
<div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">
        &times;</button>
    <h5>
        افزودن کاربر جدید</h5>
</div>
@using (Html.BeginForm("Index", " ModalFormAjax", FormMethod.Post, new { @class = "modal-form" }))
{
    <div class="modal-body">
        @Html.ValidationSummary(true, null, new { @class = "alert alert-error alert-block" })
        <fieldset class="form-horizontal">
            <legend>مشخصات کاربر</legend>
            <div class="control-group">
                @Html.LabelFor(model => model.Name, new { @class = "control-label" })
                <div class="controls">
                    @Html.EditorFor(model => model.Name)
                    @Html.ValidationMessageFor(model => model.Name, null, new { @class = "help-inline" })
                </div>
            </div>
            <div class="control-group">
                @Html.LabelFor(model => model.LastName, new { @class = "control-label" })
                <div class="controls">
                    @Html.EditorFor(model => model.LastName)
                    @Html.ValidationMessageFor(model => model.LastName, null, new { @class = "help-inline" })
                </div>
            </div>
        </fieldset>
    </div>
        
    <div class="modal-footer">
        <button class="btn btn-primary" type="submit">
            ارسال</button>
        <button class="btn" data-dismiss="modal" aria-hidden="true">
            انصراف</button>
    </div>
}
در اینجا اطلاعات فرمی را ملاحظه می‌کنید که قرار است به صورت مودال نمایش داده شود. نحوه طراحی آن بر اساس نکات form-horizontal است. همچنین divهای modal-header، modal-body و modal-footer نیز به این فرم ویژه اضافه شده‌اند تا به خوبی توسط bootstrap پردازش گردد.
حاصل نهایی این مبحث را در دو شکل ذیل ملاحظه می‌کنید. صفحه index نمایش دهنده یک دکمه و در ادامه باز شدن یک فرم مودال، پس از کلیک بر روی دکمه ثبت اطلاعات.


 
نظرات مطالب
DbContext pooling در EF Core 2.0
با سلام و احترام
ظاهرا از این روش فقط زمانی میشه استفاده کرد که کلاس Context برنامه فقط یک سازنده با پارامتر ورودی DbContextOptions داشته باشه.آیا راه حلی برای این موضوع هست ؟
نظرات مطالب
خلاصه‌ای کوتاه در مورد WinRT
- البته «سرعت» در اینجا بیشتر بحث سرعت اتصال به شبکه و مواردی مانند آن است (کار با فایل سیستم، بانک اطلاعاتی، وب کم و ...) و آنچنان ارتباطی به کلاینت مورد استفاده ندارد. یعنی عامل‌های تعیین کننده اصلی سرعت، خارج از مرزهای سیستم قرار دارند.
- هدفم از پاسخ فوق، اشاره به پیش زمینه‌ی ذهنی بود که هم اکنون سبب حذف امکان وجود فراخوانی‌های همزمان شده است؛ مانند: انتخاب فایل ، کار با وب کم
این نوع کدها برای برنامه نویس‌های سیلورلایت بسیار آشنا هستند.