نظرات مطالب
غیرمعتبر شدن کوکی‌های برنامه‌های ASP.NET Core هاست شده‌ی در IIS پس از ری‌استارت آن
بعد از Expire شدن کلید چه اتفاقی برای اطلاعاتی که قبلا با کلید قبلی Encrypt شدن میافته؟
برای بخشی از اطلاعات از این روش برای رمزنگاری استفاده کردم آیا اون اطلاعات دیگر قابل خواندن نیست؟
نظرات مطالب
توسعه سیستم مدیریت محتوای DNTCms - قسمت ششم
خواهش میکنم.
فرض کنید در ادامه مدلی داشته باشیم که کلید آن ترکیبی است ؛ در آن صورت استفاده از BaseEntity که صراحتا کلید آن یک خصوصیت مشخص است ، قابل استفاده نخواهد بود.
البته شاید در ادامه همچین سناریویی وجود نداشته باشد.
نظرات مطالب
Persist ، Load و Bookmark در Workflow
سلام .ممنون از زحمتی که کشیدید.یک سوال کلید WorkFlowID در جدول User کلید خارجی از کدوم یک از جداول WF است؟ توی هیچ کدوم از جدول‌ها نیست!
نظرات مطالب
کار با کلیدهای اصلی و خارجی در EF Code first
درسته،ولی تو رابطه‌های one-to-one(و one-to-zero) هم این روش کاربرد نداره،چون کلید Dependent همین کلید خارچی میشه دیگه،اینم درسته دیگه؟
مطالب
آموزش Linq - بخش ششم: عملگرهای پرس و جو قسمت پنجم (پایانی)
عملگرهای اتصال (Join Operators)
• Join
• GroupJoin
• Zip

عملگر Join

این عملگر همانند inner join در SQL، دو مجموعه را بر اساس کلید‌های مرتبط که از طریق پارامترها  به آن ارسال می‌شوند، با یکدیگر ترکیب می‌کند.
در عملیات Join، یک توالی ورودی که به آن توالی خارجی (Outer Sequence) گفته می‌شود با یک توالی دیگر که به آن توالی داخلی (Inner Sequence) می‌گوییم، بر اساس کلید‌های مشخص شده، ترکیب شده و یک توالی خروجی تولید می‌شود.

بررسی پارامتر‌های عملگر Join:
public static IEnumerable<TResult> Join<TOuter,TInner,TKey,TResult>
(this IEnumerable<TOuter> outer,
IEnumerable<TInner> inner,
Func<TOuter,TKey> outerKeySelector,
Func<TInner,TKey> innerKeySelector,
Func<TOuter,TInner,TResult> resultSelector)
 • <Inner IEnumerable<TInner: نشان دهنده توالی داخلی می‌باشد.
 • Func<Touter,Tkey> outerKeySelector : عنصر کلید، در توالی خارجی
 • Func<Tinner,Tkey> innerKeySelector : عنصر کلید، در توالی داخلی
 • Func<Touter,Tinner,Tresult> resultSelector : یک عبارت Lambda است که ظاهر عناصر خروجی را مشخص می‌کند.

نکته
: بطور کلی T در پارامتر‌های بالا معرف Generic Type Parameter می‌باشد؛ T==>Type  (هر نوع داده‌ای که ما مشخص کنیم).
نکته : عملگر Join یک امضاء دیگر نیز دارد که اجازه مشخص کردن IEqualityComparer را می‌دهد.

کد زیر استفاده از عملگر Join را نشان می‌دهد. توجه داشته باشید که اعلان صریح نوع داده‌ها در عبارات Lambda نوشته شده، فقط برای روشن‌تر شدن فرآیند عملیات می‌باشد.
تعریف دو آرایه از کلاس‌های Recipe و  Review:
Recipe[] recipes =
{
   new Recipe {Id = 1, Name = "Mashed Potato"},
   new Recipe {Id = 2, Name = "Crispy Duck"},
   new Recipe {Id = 3, Name = "Sachertorte"}
};

// inner sequence
Review[] reviews =
{
   new Review {RecipeId = 1, ReviewText = "Tasty!"},
   new Review {RecipeId = 1, ReviewText = "Not nice :("},
   new Review {RecipeId = 1, ReviewText = "Pretty good"},
   new Review {RecipeId = 2, ReviewText = "Too hard"},
   new Review {RecipeId = 2, ReviewText = "Loved it"}
};

  var query = recipes // recipes توالی خارجی
.Join(reviews, // reviewsتوالی داخلی
  (Recipe outerKey) => outerKey.Id, // کلید انخاب شده از توالی خارجی
  (Review innerKey) => innerKey.RecipeId, // کلید انتخاب شده از توالی داخلی
  // نحوه قالب بندی خروجی
  (recipe, review) => recipe.Name + " - " + review.ReviewText);

foreach (string item in query)
{
   Console.WriteLine(item);
}
خروجی مثال بالا:
Mashed Potato - Tasty!
Mashed Potato - Not nice :(
Mashed Potato - Pretty good
Crispy Duck - Too hard
Crispy Duck - Loved it
ساده‌تر شده‌ی کد بالا:
var query =
recipes.Join
(reviews,
outerKey => outerKey.Id,
innerKey => innerKey.RecipeId,
(recipe, review) => recipe.Name + " - " + review.ReviewText);
همانطور که مشاهده می‌کنید در خروجی مثال بالا، عبارت Sachertorte مشاهده نمی‌شود. علت آن است که عملیات انجام شده، عملیات Left Join می‌باشد. بدین معنا که عناصری که در توالی خارجی هیچ عنصر متناظری در توالی داخلی ندارند، در توالی خروجی ظاهر نخواهند شد.

پیاده سازی توسط عبارت‌های جستجو
کلمه کلیدی Join، در زمان استفاده از روش عبارت‌های پرس و جو، مورد استفاده قرار گرفت. دستور Join در قسمت چهارم از این سری آموزشی بطور کامل بررسی شده است. کد زیر نحوه اجرای دستور Join را به روش عبارت‌های پرس و جو، نشان می‌دهد.
var query = from recipe in recipes
join review in reviews
on
recipe.Id equals review.RecipeId
select new //انواع بی نام
{
    RecipeName = recipe.Name,
    RecipeReview = review.ReviewText
};

foreach (var item in query)
{
    Console.WriteLine(item.RecipeName + " - " + item.RecipeReview);
}
نکته : بررسی‌ها نشان داده است که استفاده از دستور Join، به روش عبارت‌های پرس و جو نسبت به عملگر‌های پرس و جو، خوانایی بیشتری دارد.

عملگر GroupJoin
نحوه عملکرد عملگر GroupJoin، شبیه عملگر Join می‌باشد؛ با این تفاوت که خروجی حاصل از دستور GroupJoin، یک ساختار سلسله مراتبی می‌باشد. توالی خروجی، مجموعه‌ای  از گروه‌ها می‌باشد که هر گروه، تشکیل شده‌است از عناصر توالی درونی.

بررسی پارامتر‌های عملگر GroupJoin
• <Inner IEnumerable<TInner : نشان دهنده توالی داخلی
• Func<Touter,Tkey> outerKeySelector : عنصر کلید، در توالی خارجی
• Func<Tinner,Tkey> innerKeySelector : عنصر کلید، در توالی داخلی
• Func<Touter,Ienumerable<Tinner>,Tresult> resultSelector : قالب بندی گروه‌های تولید شده خروجی را مشخص می‌کند

کد زیر استفاده از عملگر GroupJoin را نشان می‌دهد :
// outer sequence
Recipe[] recipes =
{
   new Recipe {Id = 1, Name = "Mashed Potato"},
   new Recipe {Id = 2, Name = "Crispy Duck"},
   new Recipe {Id = 3, Name = "Sachertorte"}
};

// inner sequence
Review[] reviews =
{
   new Review {RecipeId = 1, ReviewText = "Tasty!"},
   new Review {RecipeId = 1, ReviewText = "Not nice :("},
   new Review {RecipeId = 1, ReviewText = "Pretty good"},
   new Review {RecipeId = 2, ReviewText = "Too hard"},
   new Review {RecipeId = 2, ReviewText = "Loved it"}
};

var query = recipes
.GroupJoin(
reviews,
(Recipe outerKey) => outerKey.Id,//outer key
(Review innerKey) => innerKey.RecipeId,//inner key
(Recipe recipe, IEnumerable<Review> rev )=>تعریف ساختار گروه‌ها new
{
  RecipeName = recipe.Name,
  Reviews = rev
}
);

foreach (var item in query)
{
   Console.WriteLine($"Reviews for {item.RecipeName}");
   foreach (var review in item.Reviews)
   {
      Console.WriteLine($" - {review.ReviewText}");
   }
}
خروجی مثال فوق:
Reviews for Mashed Potato
 - Tasty!
 - Not nice :(
 - Pretty good
Reviews for Crispy Duck
 - Too hard
 - Loved it
Reviews for Sachertorte
همانطور که مشاهده می‌کنید گروه "Sachertorte" در خروجی اضافه شده است؛ در صورتی که هیچ عضوی ندارد.

پیاده سازی توسط عبارت‌های جستجو
var query =
from recipe in recipes
join review in reviews on recipe.Id equals review.RecipeId
into reviewGroup
select new //انواع بی نام
{
  RecipeName = recipe.Name,
  Reviews = reviewGroup//کلیه بازخورد‌ها مرتبط با یک دستور غذایی
};
خروجی مثال فوق:
Reviews for Mashed Potato
 - Tasty!
 - Not nice :(
 - Pretty good
Reviews for Crispy Duck
 - Too hard
 - Loved it
Reviews for Sachertorte

عملگر Zip

عملگر Zip، رفتاری متفاوت نسبت به عملگر GroupJoin و Join دارد و هیچ آیتمی را به عنوان کلید، از دو توالی دریافت نمی‌کند. عملگر Zip همه عناصر دو توالی را یک به یک، به ترتیب کنار هم قرار می‌دهد. مثل زیپ در دنیای واقعی که لبه‌های دو طرف زیپ را به هم می‌رساند.
 public class Ingredient
{
  public string Name { get; set; }
  public int Calories { get; set; }
}

string[] names = { "Flour", "Butter", "Sugar" };
int[] calories = { 100, 400, 500 };
IEnumerable<Ingredient> ingredients =
names.Zip(calories, (name, calorie) =>
new Ingredient
{
  Name = name,
  Calories = calorie
});
foreach (var item in ingredients)
{
Console.WriteLine($"{item.Name} has {item.Calories} calories");
}
خروجی مثال بالا :
Flour has 100 calories
Butter has 400 calories
Sugar has 500 calories
نکته: اگر تعداد اعضای مجموعه‌ها برابر نباشد، اعضای اضافی نادیده گرفته می‌شوند.

پیاده سازی توسط عبارت‌های جستجو
معادل عملگر Zip، کلمه کلیدی در عبارت‌های جستجو وجود ندارد. ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.
اشتراک‌ها
JSON Web Token چیست؟

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with HMAC algorithm) or a public/private key pair using RSA. 

JSON Web Token چیست؟
مطالب
NOSQL قسمت دوم
در مطلب قبلی با تعاریف  سیستم‌های NoSQL آشنا شدیم و به طور کلی ویژگی‌های یک سیستم NoSQL را بررسی کردیم.

در این مطلب دسته‌بندی کلی و  نوع ساختار داده‌ای این سیستم‌ها و بررسی ساده‌ترین آنها را مرور می‌کنیم.

در حالت کلی پایگا‌های داده NoSQL به ۴ دسته تقسیم می‌شوند که به ترتیب پیچیدگی ذخیره‌سازی داده‌ها عبارتند از:
  • Key/Value Store Databases
  • Document Databases
  • Graph Databases
  • Column Family Databases
  در حالت کلی در  پایگاه‌‌های‌داده NoSQL داده‌ها در قالب KEY/VALUE (کلید/مقدار) نگه‌داری می‌شوند ، به این صورت که مقادیر توسط کلید یکتایی نگاشت شده و ذخیره می‌شوند، هر مقدار صرفا توسط همان کلید نگاشت شده قابل بازگردانی می‌باشد و راهی جهت دریافت مقدار بدون دانستن کلید وجود ندارد . در این ساختار‌داده منظور از مقادیر، داده‌های اصلی برنامه‌  هستند که نیاز به نگه‌داری دارند و کلید‌ها نیز رشته‌هایی هستند که توسط برنامه‌نویس ایجاد می‌شوند.
  به دلیل موجود بودن این نوع ساختار داده‌ای در اکثر کتابخانه‌های زبان‌های برنامه‌نویسی ( به عنوان مثال پیاده‌سازی‌های مختلف اینترفیس Map شامل HashTable ، HashMap و موارد دیگر در کتابخانه‌های JDK ) این نوع ساختار برای اکثر برنامه‌نویسان آشنا بوده و فراگیری آن نیز ساده می‌باشد.
  بدیهی است که اعمال فرهنگ داده‌ای ( درج ، حذف ، جستجو ) در این سیستم به دلیل اینکه داده‌ها به صورت کلید/مقدار ذخیره می‌شوند دارای پیچیدگی زمانی (1)O می‌باشد که بهینه‌ترین حالت ممکن به لحاظ طراحی می‌باشد. همان‌گونه که مستحضرید در الگوریتم‌هایی که دارای پیچیدگی زمانی با مقدار ثابت دارند کم یا زیاد بودن داده‌ها تاثیری در کارایی الگوریتم نداشته و همواره با هر حجم داده‌ای زمان ثابتی جهت پردازش نیاز می‌باشد.
 

Key/Value Store Databases:
این سیستم  ساده‌ترین حالت از دسته‌بندی‌های NoSQL می‌باشد ، به طور کلی جهت استفاده در سیستم‌هایی است که داده‌ها متمایز از یکدیگر هستند و اصولا Availability و یا در دسترس بودن داده‌ها نسبت به سایر موارد نظیر پایائی اهمیت بالاتری دارد.

از موارد استفاده این گونه سیستم‌ها به موارد زیر می‌توان اشاره کرد:
  • در پلتفرم‌های اشتراک گذاری داده‌ها . هدف کلی صرفا هندل کردن آپلود محتوی (باینری) و به صورت همزمان بروز کردن در سمت دیگر می‌باشد.( اپلیکیشنی مانند اینستاگرام را تصور کنید) در اینگونه نرم‌افزار‌ها با تعداد بسیار زیاد کاربر و تقاضا، استفاده از این نوع پایگاه داده به مراتب کارایی و سرعت را بالاتر می‌برد. و با توجه به عدم پیش‌بینی حجم داده‌ها یکی از ویژگی‌های این نوع پایگاه داده تحت عنوان Horizontal Scaling مطرح می‌شود که در صورت Overflow شدن سرور، داده‌ها را به سمت سرور دیگری می‌توان هدایت کرد وبدون مشکل پردازش را  ادامه داد ، این ویژگی یک وجه تمایز کارایی این سیستم با سیستم‌های RDBMS می‌باشد که جهت مقابله با چنین وضعیتی تنها راه پیش‌رو بالا بردن امکانات سرور می‌باشد و به طور کلی داده‌ها را در یک سرور می‌توان نگه‌داری کرد ( البته راه‌حل‌هایی نظیر پارتیشن کردن و غیره وجود دارد که به مراتب پیچیدگی و کارایی کمتری نسبت به Horizontal Scaling در پایگاه‌های داده NoSQL دارد.)
  • برای Cache کردن صقحات بسیار کارا می‌باشد ، به عنوان مثال می‌توان آدرس درخواست را به عنوان Key در نظر گرفت و مقدار آن را نیز معادل JSON نتیجه که توسط کلاینت پردازش خواهد شد قرار داد.
  • ‌یک نسخه کپی شده از توئیتر که کاملا توسط این نوع پایگاه داده پیاده شده است نیز از این آدرس قابل مشاهده است. این برنامه به زبان‌های php , ruby و java  نوشته شده است و سورس نیز در مخارن github می‌جود می‌باشد. (یک نمونه پیاده سازی اید‌ه‌آل جهت آشنایی با  نحوه‌ی مدیریت داده‌ها در این نوع پایگاه داده)
از پیاده‌سازی‌های این نوع پایگاه داده به موارد زیر می‌توان اشاره کرد:
  هر یک از پیاده‌سازی‌ها دارای ویژگی‌های مربوط به خود هستند به عنوان مثال Memcached داده‌ها را صرفا در DRAM ذخیره می‌کند که نتیجه‌ی آن Volatile بودن داده‌ها می‌باشد و به هیچ وجه از این سیستم جهت نگهداری دائمی داده‌ها نباید استفاده شود. از طرف دیگر Redis داده‌ها را علاوه بر حافظه اصلی در حافظه جانبی نیز ذخیره می‌کند که نتیجه‌ی آن سرعت بالا در کنار پایائی می‌باشد.
همان‌گونه که در تعریف کلی عنوان شد یکی از ویژگی‌های این سیستم‌ها متن‌باز بودن انها می‌باشد که نتیجه‌ی آن وجود پیاده‌سازی‌های متنوع از هر کدام می‌باشد ، لازم است قبل از انتخاب هر سیستم به خوبی با ویژگی‌های اکثر سیستم‌های محبوب و پراستفاده آشنا شویم و با توجه به نیاز سیستم را انتخاب کنیم.
در مطلب بعدی با نوع دوم یعنی Document Databases آشنا خواهیم شد.
 
مطالب
معرفی Local Storage و چند کتابخانه مرتبط
یکی از راه‌های ذخیره داده‌ها در سمت کلاینت، استفاده از کوکی‌ها می‌باشد که تقریبا همه با آن کم و بیش آشنایی داریم و هر کدام بسته به نیاز، تا حدودی از آن استفاده کرده‌ایم. اما می‌توان local storage را در یکی دو مورد نسبت به کوکی برتر شمرد؛ مواردی مثل حجم داده‌های ذخیره شده که نسبت به کوکی بیشتر می‌باشد و عدم ارسال با درخواست‌های ارسالی سمت سرور.

Local Storage
با استفاده از Local Storage قادر خواهیم بود تا داده‌هایی را در سمت کلاینت ذخیره کنیم؛ بدون اینکه بر عملکرد سایت تاثیر بگذارد. منظور از تاثیر بر روی عملکرد این است که در هر رفت به سمت سرور لازم نیست با درخواست ارسالی، داده‌های اضافه‌ای ارسال شوند.
با استفاده از Local Storage قادر خواهیم بود حداقل 5 مگابایت داده را ذخیره کنیم.

  استفاده از Local Storage با استفاده از دو شیء زیر امکان پذیر می‌باشد:
  • window.localStorage  : اطلاعات ذخیره شده در آن فاقد تاریخ انقضاء می‌باشند و تا زمانی که اقدام به حذف آن ننمایید، داده‌های آن معتبر می‌باشند.
  • window.sessionStorage : اطلاعات ذخیره شده در آن تا زمان بسته شدن تب مرورگر، معتبر می‌باشند. با بسته شدن تب فعلی مرورگر، تمامی اطلاعات ذخیره شده از بین خواهند رفت.

نحوه استفاده:

- نحوه افزودن داده در Local Storage به صورت کلید/ مقدار می‌باشد.

ابتدا نگاهی داشته باشیم به اینترفیس Storage:

interface Storage {
  readonly attribute unsigned long length;
  DOMString? key(unsigned long index);
  getter DOMString? getItem(DOMString key);
  setter void setItem(DOMString key, DOMString value);
  deleter void removeItem(DOMString key);
  void clear();
}
برای مطمئن شدن از پشتیبانی مرورگر از Local storage، بهتر است ابتدا اقدام به بررسی پشتیبانی مرورگر مربوطه کرده و در صورت پشتیبانی، اقدام به ذخیره سازی داده کنیم. نحوه بررسی پشتیبانی مرورگر به صورت زیر می‌باشد:
if (typeof(Storage) !== "undefined") {
    // do ...
}

شیء window.localStorage
همانطور که بیان کردیم، در این روش داده دارای تاریخ انقضاء نمی‌باشد.
() SetItem :
متد setItem برای ذخیره اطلاعات است و نحوه استفاده از آن به صورت زیر می‌باشد:
localStorage.setItem("lastpost", "localstorage");
setItem ابتدا مقدار کلید را بررسی کرده و در صورت عدم موجود بودن، اقدام به ذخیره مقدار با کلید مورد نظر خواهد کرد.
localStorage.setItem("visitorCount",15 );
localStorage.setItem("visitorCount", 16);
در کد فوق مقدار visitorcount برابر با 16 خواهد بود.

() getItem  :
برای دسترسی به مقدار ذخیره شده می‌توانیم از متد getItem به همراه مقدار کلید استفاده کنیم. در صورت موجود نبودن مقدار، null برگردانده خواهد شد.
 localStorage.getItem("lastpost");
لازم به ذکر است، میتوانیم به صورت زیر هم اقدام به ذخیره و بازیابی داده‌ها کنیم:
localStorage.lastpost = "localstorage";
document.getElementById("result").innerHTML = localStorage.lastpost;
در این حالت شبیه به استفاده از ViewBag  می‌باشد.

و برای حذف اطلاعات ذخیره شده:
localStorage.removeItem("lastpost");
- کلید و مقدار به صورت رشته‌ای ذخیره خواهند شد. در صورتی که قصد ذخیره سازی با فرمت‌های دیگری را داشته باشید، باید ابتدا اقدام به convert نمایید. البته در ادامه کتابخانه‌هایی را برای این مقصود معرفی خواهیم کرد.

خصوصیت length :
تعداد جفت کلید / مقدار‌های جاری را بر میگرداند.

متد Key :
این متد مقداری را از ورودی دریافت کرده و اسم کلید مورد نظر را بر میگرداند. ایندکس آن از صفر شروع خواهد شد. قطعه کد زیر باعث برگرداندن مقدار lastname می‌شود:
 localStorage.setItem("name","uthman" );
   localStorage.setItem("lastname","24" );

   alert(localStorage.key(1));
متد () clear :
این متد باعث پاک شدن تمامی داده‌های ذخیره شده خواهد شد.

شی sessionStorage :
از نظر syntax دستوری همانند localStorage می‌باشد و تفاوت آن فقط در زمان ذخیره سازی است؛ با بسته شدن تب مرورگر، تمامی داده‌های ذخیره شده پاک خواهند شد.

کتابخانه‌های مرتبط :
Locker :
با استفاده از این کتابخانه ، قادر خواهید بود تا اطلاعات را با فرمت‌های مورد نظر، بدون convert آنها ذخیره نمایید. نمونه‌ای از آن به صورت زیر خواهد بود:
Lockr.set('website', 'SitePoint'); // string
Lockr.set('categories', 8); // number
Lockr.set('users', [{ name: 'John Doe', age: 18 }, { name: 'Jane Doe', age: 19 }]);
کتابخانه فوق دارای امکاناتی بیشتری می‌باشد که در صورت تمایل می‌توانید به گیت هاب آن مراجعه نمایید.

secStore.js  :
با استفاده از کتابخانه SJCL امنیت داده‌های ذخیره شده، بالا می‌روند.
var storage = new secStore
  , options = {
    encrypt: true,
    data: {
      key: 'some data that is somewhat private'
    }
  };


storage.set(options, function(err, results){
  if (err) throw err;
  console.log(results);
});

و سایر کتابخانه‌های مرتبط :

//lz-string example

var string = 'A string to test our compression.';
console.log(string.length); // 33 
var compressed = LZString.compress(string);
console.log(compressed.length); // 18
string = LZString.decompress(compressed);
 
نظرات مطالب
بالا بردن سرعت DbContext هنگام ثبت داده های زیاد
ممنون.
- نکته دیگری که به شدت روی سرعت bulk insert حین کار با ORMها (فرقی نمی‌کنه؛ تمامشون) تاثیر داره، استراتژی انتخاب نوع primary key است. زمانیکه کلید اصلی از نوع auto generated توسط بانک اطلاعاتی باشه، ORM بعد از insert سعی می‌کنه این Id رو به مصرف کننده برگردونه (چون برنامه نقشی در تعیین اون نداره). یعنی عملا با یک insert و بلافاصله با یک select مواجه خواهیم بود. البته این مورد هم باید اضافه بشه که ORMها برای فرآیندها و تراکنش‌هایی کوتاه طراحی شدن و این مساله در حالت متداول کار با ORMها اهمیتی نداره و اصلا به چشم نمیاد.
همین مساله سرعت insert رو «فقط» در حالت فراخوانی با تعداد بالا در یک حلقه برای مثال به شدت پایین میاره و اگر مثلا کلید اصلی توسط خود برنامه مدیریت بشه (مثلا از نوع Guid تولید شده در برنامه باشه)، سرعت bulk insert به شدت بالا میره چون به select بعد از insert نیازی نخواهد بود.
- در حالت bulk insert اگر شخص مطمئن هست که اطلاعات ارسالی توسط او اعتبارسنجی شدن، بهتره تنظیم context.Configuration.ValidateOnSaveEnabled = false رو هم انجام بده. این مورد اعتبارسنجی در حین ذخیره سازی رو غیرفعال می‌کنه.
- همچنین شخصی در اینجا در مورد تعداد بار فراخوانی متد SaveChanges در یک حلقه، تحقیقی رو انجام داده که جالب است.
مطالب
ذخیره سازی SQL تولیدی در NH3

همانطور که در مطلب "NHibernate 3.0 و عدم وابستگی مستقیم به Log4Net" عنوان شد، از اینترفیس جدید IInternalLogger آن می‌توان جهت ثبت وقایع داخلی NHibernate استفاده کرد. اگر در این بین صرفا بخواهیم SQL های تولیدی را لاگ کنیم، خلاصه‌ی آن به صورت زیر خواهد بود:
public class LoggerFactory : ILoggerFactory
{
public IInternalLogger LoggerFor(System.Type type)
{
if (type == typeof(NHibernate.Tool.hbm2ddl.SchemaExport))
//log it
}

public IInternalLogger LoggerFor(string keyName)
{
if (keyName == "NHibernate.SQL")
//log it
}
}
یا کلید NHibernate.SQL باید پردازش شود (جهت ثبت SQL های کوئری‌ها) یا نوع NHibernate.Tool.hbm2ddl.SchemaExport جهت ثبت SQL ساخت ساختار جداول بانک اطلاعاتی باید بررسی گردد.
سورس کامل این کتابخانه‌ی کوچک را از اینجا می‌توانید دریافت کنید. جهت استفاده از آن تنها کافی است چند سطر زیر به فایل app.config یا web.config برنامه‌ی شما اضافه شوند:

<appSettings>
<add key="nhibernate-logger" value="NH3SQLLogger.LoggerFactory, NH3SQLLogger" />
</appSettings>

کلید nhibernate-logger ، به صورت مستقیم توسط NHibernate بررسی می‌شود و صرف نظر از اینکه از کدامیک از مشتقات NHibernate استفاده می‌کنید، با تمام آن‌ها کار خواهد کرد.
لازم به ذکر است که اگر برنامه‌ی شما از نوع ASP.NET است، این کتابخانه اطلاعات را در پوشه‌ی استاندارد App_Data ثبت خواهد کرد؛ در غیراینصورت فایل‌ها در کنار فایل اجرایی برنامه تشکیل خواهند شد.