نظرات مطالب
EF Code First #3
سلام
آیا روش دیگه برای درج کلید خارجی هست بدون اینکه یک select انجام بدیم و اونو از دیتابیس بخونیم به صورت زیر؟
var user = db.Users.FirstOrDefault(x=>x.UserName == "hamid");

db.Post.Add(new Post
{
      Title = txtTitle.Text,
      Content = txtContent.Text,
      User = user
}
db.SaveChanges();
نظرات مطالب
Implementing second level caching in EF code first
تکرار مجدد:
- هر کلاس لایه سرویس با پیاده سازی یک اینترفیس باید تهیه شود.این مورد به نظر در قسمت 12 سری EF بحث شده با مثال و فایل و همه چیز در برنامه‌های کنسول و MVC و وب فرم‌ها.
- کلاس کمکی فوق نیازی به وب سرور برای اجرا ندارد و باعث fail آزمون‌های واحد شما نمی‌شود چون در صورت نبودن وب سرور از حافظه سیستم استفاده می‌کند نه کش IIS.
- اگر به این نتیجه رسیدید که کش پروایدر بهتری وجود دارد و نیاز به تعویض نمونه مطرح شده در اینجا هست (که من در «مثال» ارائه شده نیازی به آن نداشتم)، لطفا آن‌را معرفی کنید و همچنین پیاده سازی اصلاح شده را به صورت یک وصله ارائه کنید جهت تکمیل بحث.
بازخوردهای دوره
استفاده از AOP Interceptors برای حذف کدهای تکراری کش کردن اطلاعات در لایه سرویس برنامه
سلام
از دیتابیس  redis  بجای کش دات نت استفاده کردم ولی وقتی اطلاعات رو از redis میگیریم بصورت json است ، پیشنهاد میکنید چگونه json را به type خروجی متد cast کنم ؟
نظرات مطالب
شروع به کار با DNTFrameworkCore - قسمت 2 - طراحی موجودیت‌های سیستم
در روابط One to One و One to Zero جهت پیاده سازی مباحث ردیابی تغییرات، ارث بری از TrackableEntity باعث ایجاد یک PrimaryKey جدید میشود. درصورتیکه کلید خارجی و کلید اصلی در جدول دوم بایستی یکی باشند. راه حلی در این مورد دارید؟
مطالب
بررسی میزان پوشش آزمون‌های واحد به کمک برنامه PartCover

همیشه در حین توسعه‌ی یک برنامه این سؤالات وجود دارند:
- چند درصد از برنامه تست شده است؟
- برای چه تعدادی از متدهای موجود آزمون واحد نوشته‌ایم؟
- آیا همین آزمون‌های واحد نوشته شده و موجود، کامل هستند و تمام عملکرد‌های متدهای مرتبط را پوشش می‌دهند؟

این سؤالات به صورت خلاصه مفهوم Code coverage را در بحث Unit testing ارائه می‌دهند: برای چه قسمت‌هایی از برنامه آزمون واحد ننوشته‌ایم و میزان پوشش برنامه توسط آزمون‌های واحد موجود تا چه حدی است؟
بررسی این سؤالات در یک پروژه‌ی کم حجم، ساده بوده و به صورت بازبینی بصری ممکن است. اما در یک پروژه‌ی بزرگ نیاز به ابزار دارد. به همین منظور تعدادی برنامه جهت بررسی code coverage مختص پروژه‌های دات نتی تابحال تولید شده‌اند که در ادامه لیست آن‌ها را مشاهده می‌کنید:
و ...

تمام این‌ها تجاری هستند. اما در این بین برنامه‌ی PartCover سورس باز و رایگان بوده و همچنین مختص به NUnit نیز تهیه شده است. این برنامه را از اینجا می‌توانید دریافت و نصب کنید. در ادامه نحوه‌ی تنظیم آن‌را بررسی خواهیم کرد:

الف) ایجاد یک پروژه آزمون واحد جدید
جهت توضیح بهتر سه سؤال مطرح شده در ابتدای این مطلب، بهتر است یک مثال ساده را در این زمینه مرور نمائیم: (پیشنیاز: (+))
یک Solution جدید در VS.NET آغاز شده و سپس دو پروژه جدید از نوع‌های کنسول و Class library به آن اضافه شده‌اند:



پروژه کنسول، برنامه اصلی است و در پروژه Class library ، آزمون‌های واحد برنامه را خواهیم نوشت.
کلاس اصلی برنامه کنسول به شرح زیر است:
namespace TestPartCover
{
public class Foo
{
public int DoFoo(int x, int y)
{
int z = 0;
if ((x > 0) && (y > 0))
{
z = x;
}
return z;
}

public int DoSum(int x)
{
return ++x;
}
}
}
و کلاس آزمون واحد آن در پروژه class library مثلا به صورت زیر خواهد بود:
using NUnit.Framework;

namespace TestPartCover.Tests
{
[TestFixture]
public class Tests
{
[Test]
public void TestDoFoo()
{
var result = new Foo().DoFoo(-1, 2);
Assert.That(result == 0);
}
}
}
که نتیجه‌ی بررسی آن توسط NUnit test runner به شکل زیر خواهد بود:



به نظر همه چیز خوب است! اما آیا واقعا این آزمون کافی است؟!

ب) در ادامه به کمک برنامه‌ی PartCover می‌خواهیم بررسی کنیم میزان پوشش آزمون‌های واحد نوشته شده تا چه حدی است؟

پس از نصب برنامه، فایل PartCover.Browser.exe را اجرا کرده و سپس از منوی فایل، گزینه‌ی Run Target را انتخاب کنید تا صفحه‌ی زیر ظاهر شود:



توضیحات:
در قسمت executable file آدرس فایل nunit-console.exe را وارد کنید. این برنامه چون در حال حاضر برای دات نت 2 کامپایل شده امکان بارگذاری dll های دات نت 4 را ندارد. به همین منظور فایل nunit-console.exe.config را باز کرده و تنظیمات زیر را به آن اعمال کنید (مهم!):
<configuration>
<startup>
<supportedRuntime version="v4.0.30319" />
</startup>

و همچنین
<runtime>
<loadFromRemoteSources enabled="true" />

در ادامه مقابل working directory‌ ، آدرس پوشه bin پروژه unit test را تنظیم کنید.
در این حالت working arguments به صورت زیر خواهند بود (در غیراینصورت باید مسیر کامل را وارد نمائید):
TestPartCover.Tests.dll /framework=4.0.30319 /noshadow

نام dll‌ وارد شده همان فایل class library تولیدی است. آرگومان بعدی مشخص می‌کند که قصد داریم یک پروژه‌ی دات نت 4 را توسط NUnit بررسی کنیم (اگر ذکر نشود پیش فرض آن دات نت 2 خواهد بود و نمی‌تواند اسمبلی‌های دات نت 4 را بارگذاری کند). منظور از noshadow این است که NUnit‌ مجاز به تولید shadow copies از اسمبلی‌های مورد آزمایش نیست. به این صورت برنامه‌ی PartCover می‌تواند بر اساس StackTrace نهایی، سورس متناظر با قسمت‌های مختلف را نمایش دهد.
اکنون نوبت به تنظیم Rules آن است که یک سری RegEx هستند؛ به عبارتی چه اسمبلی‌هایی آزمایش شوند و کدام‌ها خیر:
+[TestPartCover]*
-[nunit*]*
-[log4net*]*

همانطور که ملاحظه می‌کنید در اینجا از اسمبلی‌های NUnit و log4net صرفنظر شده است و تنها اسمبلی TestPartCover (همان برنامه کنسول، نه اسمبلی برنامه آزمون واحد) بررسی خواهد گردید.
اکنون بر روی دکمه Save در این صفحه کلیک کرده و فایل نهایی را ذخیره کنید (بعدا توسط دکمه Load در همین صفحه قابل بارگذاری خواهد بود). حاصل باید به صورت زیر باشد:
<PartCoverSettings>
<Target>D:\Prog\Libs\NUnit\bin\net-2.0\nunit-console.exe</Target>
<TargetWorkDir>D:\Prog\1390\TestPartCover\TestPartCover.Tests\bin\Debug</TargetWorkDir>
<TargetArgs>TestPartCover.Tests.dll /framework=4.0.30319 /noshadow</TargetArgs>
<Rule>+[TestPartCover]*</Rule>
<Rule>-[nunit*]*</Rule>
<Rule>-[log4net*]*</Rule>
</PartCoverSettings>

برای شروع به بررسی، بر روی دکمه Start کلیک نمائید. پس از مدتی، نتیجه به صورت زیر خواهد بود:



بله! آزمون واحد تهیه شده تنها 39 درصد اسمبلی TestPartCover را پوشش داده است. مواردی که با صفر درصد مشخص شده‌اند، یعنی فاقد آزمون واحد هستند و نکته مهم‌تر پوشش 91 درصدی متد DoFoo است. برای اینکه علت را مشاهده کنید از منوی View ، گزینه‌ی Coverage detail را انتخاب کنید تا تصویر زیر نمایان شود:



قسمت‌ نارنجی در اینجا به معنای عدم پوشش آن در متد TestDoFoo تهیه شده است. تنها قسمت‌های سبز را توانسته‌ایم پوشش دهیم و برای بررسی تمام شرط‌های این متد نیاز به آزمون‌های واحد بیشتری می‌باشد.

روش نهایی کار نیز به همین صورت است. ابتدا آزمون واحد تهیه می‌شود. سپس میزان پوشش آن بررسی شده و در ادامه سعی خواهیم کرد تا این درصد را افزایش دهیم.

مطالب
دیتابیس سایت Stack Overflow

مدتی است که سایت Stack Overflow دیتابیس سؤال و جواب‌های خود را به صورتی سخاوتمندانه در اختیار عموم قرار داده است. مجوز استفاده از این اطلاعات cc-wiki است،‌ به این معنا که مجاز هستید این اطلاعات را به اشتراک بگذارید (کپی، توزیع و امثال آن)، مجاز هستید این اطلاعات را با اطلاعاتی دیگر ترکیب کرده و کار جدیدی را ارائه دهید؛ با این شرط که فراموش نکنید از بانیان اصلی این کار یاد نموده و همچنین کار تولیدی شما نیز باید بر مبنای همین مجوز cc-wiki باقی بماند.



این دیتابیس که با فرمت xml ارائه شده در حقیقت دامپ اطلاعات عمومی سایت بوده که اطلاعات شخصی کاربران از آن حذف شده است و شامل فایل‌های زیر می‌باشد:
1. badges.xml
2. comments.xml
3. posts.xml
4. users.xml
5. votes.xml

این اطلاعات شامل سایت‌های همخانواده‌ی Stack Overflow، Server Fault ، Super Userو Meta Stack Overflow نیز می‌شود.

جهت دریافت آخرین نگارش این مجموعه به آدرس زیر می‌توان مراجعه نمود:

حجم این اطلاعات چند گیگ می‌شود که جهت آشنایی با نحوه‌ی import آن به SQL Server می‌توان به مقالات زیر مراجعه نمود:

مطالب دوره‌ها
تراکنش‌ها در RavenDB
پیش از شروع به بحث در مورد تراکنش‌ها و نحوه مدیریت آن‌ها در RavenDB، نیاز است با مفهوم ACID آشنا شویم.

ACID چیست؟

ACID از 4 قاعده تشکیل شده است (Atomic, Consistent, Isolated, and Durable) که با کنار هم قرار دادن آن‌ها یک تراکنش مفهوم پیدا می‌کند:

الف) Atomic: به معنای همه یا هیچ
اگر تراکنشی از چندین تغییر تشکیل می‌شود، همه‌ی آن‌ها باید با موفقیت انجام شوند، یا اینکه هیچکدام از تغییرات نباید فرصت اعمال نهایی را بیابند.
برای مثال انتقال مبلغ X را از یک حساب، به حسابی دیگر درنظر بگیرید. در این حالت X ریال از حساب شخص کسر و X ریال به حساب شخص دیگری واریز خواهد شد. اگر موجودی حساب شخص، دارای X ریال نباشد، نباید مبلغی از این حساب کسر شود. مرحله اول شکست خورده است؛ بنابراین کل عملیات لغو می‌شود. همچنین اگر حساب دریافت کننده بسته شده باشد نیز نباید مبلغی از حساب اول کسر گردد و در این حالت نیز کل تراکنش باید برگشت بخورد.

ب) Consistent یا یکپارچه
در اینجا consistency علاوه بر اعمال قیود، به معنای اطلاعاتی است که بلافاصله پس از پایان تراکنشی از سیستم قابل دریافت و خواندن است.

ج) Isolated: محصور شده
اگر چندین تراکنش در یک زمان با هم در حال اجرا باشند، نتیجه نهایی با حالتی که تراکنش‌ها یکی پس از دیگری اجرا می‌شوند باید یکی باشد.

د) Durable: ماندگار
اگر سیستم پایان تراکنشی را اعلام می‌کند، این مورد به معنای 100 درصد نوشته شدن اطلاعات در سخت دیسک باید باشد.


مراحل چهارگانه ACID در RavenDB به چه نحوی وجود دارند؟

RavebDB از هر دو نوع تراکنش‌های implicit و explicit پشتیبانی می‌کند. Implicit به این معنا است که در حین استفاده معمول از RavenDB (و بدون انجام تنظیمات خاصی)، به صورت خودکار مفهوم تراکنش‌ها وجود داشته و اعمال می‌شوند. برای نمونه به متد ذیل توجه نمائید:
public void TransferMoney(string fromAccountNumber, string toAccountNumber, decimal amount) 
{
   using(var session = Store.OpenSession()) 
   {
         session.Advanced.UseOptimisticConcurrency = true;

         var fromAccount = session.Load<Account>("Accounts/" + fromAccountNumber);
         var toAccount = session.Load<Account>("Accounts/" + toAccountNumber);

         fromAccount.Balance -= amount;
         toAccount.Balance += amount;

         session.SaveChanges();
   }
}
در این متد مراحل ذیل رخ می‌دهند:
- از document store ایی که پیشتر تدارک دیده شده، جهت بازکردن یک سشن استفاده شده است.
- به سشن صراحتا عنوان شده است که از Optimistic Concurrency استفاده کند. در این حالت RavenDB اطمینان حاصل می‌کند که اکانت‌های بارگذاری شده توسط متدهای Load، تا زمان فراخوانی SaveChanges تغییر پیدا نکرده‌اند (و در غیراینصورت یک استثناء را صادر می‌کند).
- دو اکانت بر اساس Id آن‌ها از بانک اطلاعاتی واکشی می‌شوند.
- موجودی یکی تقلیل یافته و موجودی دیگر، افزایش می‌یابد.
- متد SaveChanges بر روی شی‌ء سشن فراخوانی شده است. تا زمانیکه این متد فراخوانی نشده است، کلیه تغییرات در حافظه نگهداری می‌شوند و به سرور ارسال نخواهند شد. فراخوانی آن سبب کامل شدن تراکنش و ارسال اطلاعات به سرور می‌گردد.
بنابراین شیء سشن بیانگر یک atomic transaction ماندگار و محصور شده است (سه جزء ACID تاکنون محقق شده‌اند). محصور شده بودن آن به این معنا است که:
الف) هر تغییری که در سشن اعمال می‌شود، تا پیش از فراخوانی متد SaveChanges از دید سایر تراکنش‌ها مخفی است.
ب) اگر دو تراکنش همزمان رخ دهند، تغییرات هیچکدام بر روی دیگری اثری ندارد.

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


عاقبت یک دست شدن یا eventual consistency

درک Consistency مفهوم ACID در RavenDB بسیار مهم است و عدم آشنایی با نحوه عملکرد آن می‌تواند مشکل‌ساز شود. در دنیای بانک‌های اطلاعاتی رابطه‌ای، برنامه نویس‌ها به «immediate consistency» عادت دارند (یکپارچگی آنی). به این معنا که هرگونه تغییری در بانک اطلاعاتی، پس از پایان تراکنش، بلافاصله در اختیار کلیه خوانندگان سیستم قرار می‌گیرد. در RavenDB و خصوصا دنیای NoSQL، این یکپارچگی آنی دنیای رابطه‌ای، به «eventual consistency» تبدیل می‌شود (عاقبت یک‌دست شدن). عاقبت یک دست شدن در RavenDB به این معنا است که اگر تغییری به یک سند اعمال گردیده و ذخیره شود؛ کوئری انجام شده بر روی این اطلاعات تغییر یافته ممکن است «stale data» باز گرداند. واژه stale در RavenDB به این معنا است که هنوز اطلاعاتی در دیتابیس موجود هستند که جهت تکمیل ایندکس‌ها پردازش نشده‌اند. به این مورد در قسمت بررسی ایندکس‌ها در RavenDB اشاره شد.
در RavenDB یک سری تردهای پشت صحنه، مدام مشغول به کار هستند و بدون کند کردن عملیات سیستم، کار ایندکس کردن اطلاعات را انجام می‌دهند. هر زمانیکه اطلاعاتی را ذخیره می‌کنیم، بلافاصله این تردها تغییرات را تشخیص داده و ایندکس‌ها را به روز رسانی می‌کنند. همچنین باید درنظر داشت که RavenDB جزو معدود بانک‌های اطلاعاتی است که خودش را بر اساس نحوه استفاده شما ایندکس می‌کند! (نمونه‌ای از آن‌را در قسمت ایندکس‌های پویای حاصل از کوئری‌های LINQ پیشتر مشاهده کرده‌اید)

نکته مهم
در RavenDB اگر از کوئری‌های LINQ استفاده کنیم، ممکن است به علت اینکه هنوز تردهای پشت صحنه‌ی ایندکس سازی اطلاعات، کارشان تمام نشده است، تمام اطلاعات یا آخرین اطلاعات را دریافت نکنیم (که به آن stale data گفته می‌شود). هر آنچه که ایندکس شده است دریافت می‌گردد (مفهوم عاقبت یک دست شدن ایندکس‌ها). اما اگر نیاز به یکپارچگی آنی داشتیم، متد Load یک سشن، مستقیما به بانک اطلاعاتی مراجعه می‌کند و اطلاعات بازگشت داده شده توسط آن هیچگاه احتمال stale بودن را ندارند.
بنابراین برای نمایش اطلاعات یا گزارشگیری، از کوئری‌های LINQ استفاده کنید. RavenDB خودش را بر اساس کوئری شما ایندکس خواهد کرد و نهایتا به کوئری‌هایی فوق العاده سریعی در طول کارکرد سیستم خواهیم رسید. اما در صفحه ویرایش اطلاعات بهتر است از متد Load استفاده گردد تا نیاز به مفهوم immediate consistency یا یکپارچگی آنی برآورده شود.


تنظیمات خاص کار با ایندکس سازها برای انتظار جهت اتمام کار آن‌ها

عنوان شد که اگر ایندکس سازهای پشت صحنه هنوز کارشان تمام نشده است، در حین کوئری گرفتن، هر آنچه که ایندکس شده بازگشت داده می‌شود.
در اینجا می‌توان به RavenDB گفت که تا چه زمانی می‌تواند یک کوئری را جهت دریافت اطلاعات نهایی به تاخیر بیندازد. برای اینکار باید اندکی کوئری‌های LINQ آن‌را سفارشی سازی کنیم:
RavenQueryStatistics stats;
var results = session.Query<Product>()
    .Statistics(out stats)
    .Where(x => x.Price > 10)
    .ToArray();
 
if (stats.IsStale)
{
    // Results are known to be stale
}
توسط امکانات آماری کوئری‌های LINQ در RavenDB مطابق کدهای فوق، می‌توان دریافت که آیا اطلاعات دریافت شده stale است یا خیر.
همچنین زمان انتظار تا پایان کار ایندکس ساز را نیز توسط متد Customize به نحو ذیل می‌توان تنظیم کرد:
RavenQueryStatistics stats;
var results = session.Query<Product>()
    .Statistics(out stats)
    .Where(x => x.Price > 10)
    .Customize(x => x.WaitForNonStaleResults(TimeSpan.FromSeconds(5)))
    .ToArray();
به علاوه می‌توان کلیه کوئری‌های یک documentStore را وارد به صبر کردن تا پایان کار ایندکس سازی کرد (متد Customize پیش فرضی را با WaitForNonStaleResultsAsOfLastWrite مقدار دهی و اعمال می‌کند):
 documentStore.Conventions.DefaultQueryingConsistency = ConsistencyOptions.QueryYourWrites;
این مورد در برنامه‌های وب توصیه نمی‌شود چون کل سیستم در حین آغاز کار با آن بر اساس یک documentStore سینگلتون باید کار کند و همین مساله صبر کردن‌ها، با بالا رفتن حجم اطلاعات و تعداد کاربران، پاسخ دهی سیستم را تحت تاثیر قرار خواهد داد. به علاوه این تنظیم خاص بر روی کوئری‌های پیشرفته Map/Reduce کار نمی‌کند. در این نوع کوئری‌های ویژه، برای صبر کردن تا پایان کار ایندکس شدن، می‌توان از روش زیر استفاده کرد:
while (documentStore.DatabaseCommands.GetStatistics().StaleIndexes.Length != 0)
{
    Thread.Sleep(10);
}

مقابله با تداخلات همزمانی

با تنظیم session.Advanced.UseOptimisticConcurrency = true، اگر سندی که در حال ویرایش است، در این حین توسط کاربر دیگری تغییر کرده باشد، استثنای ConcurrencyException صادر خواهد شد. همچنین این استثناء در صورتیکه شخصی قصد بازنویسی سند موجودی را داشته باشد نیز صادر خواهد شد (شخصی بخواهد سندی را با ID سند موجودی ذخیره کند). اگر از optimistic concurrency استفاده نشود، آخرین ترد نویسنده یا به روز کننده اطلاعات، برنده خواهد شد و اطلاعات نهایی موجود در بانک اطلاعاتی متعلق به او و حاصل بازنویسی آن ترد است.
 optimistic concurrency به زبان ساده به معنای به خاطر سپردن شماره نگارش یک سند است، زمانیکه آن‌را بارگذاری می‌کنیم و سپس ارسال آن به سرور، زمانیکه قصد ذخیره آن‌را داریم. در SQL Server اینکار توسط RowVersion انجام می‌شود. در بانک‌های اطلاعاتی سندگرا چون تمایل به استفاده از HTTP در آن‌ها زیاد است (مانند RavenDB) از مکانیزمی به نام E-Tag برای این منظور کمک گرفته می‌شود. هر زمانیکه تغییری به یک سند اعمال می‌شود، E-Tag آن  به صورت خودکار افزایش خواهد یافت.
برای مثال فرض کنید کاربری سندی را با E-Tag مساوی 2 بارگذاری کرده است. قبل از اینکه این کاربر در صفحه ویرایش اطلاعات کارش با این سند خاتمه یابد، کاربر دیگری در شبکه، این سند را ویرایش کرده است و اکنون E-Tag آن مثلا مساوی 6 است. در این زمان اگر کاربر یک سعی به ذخیره سازی اطلاعات نماید، چون E-Tag سند او با E-Tag سند موجود در سرور دیگر یکی نیست، با استثنای ConcurrencyException متوقف خواهد شد.



مشکل! در برنامه‌های بدون حالت وب، چون پس از نمایش صفحه ویرایش اطلاعات، سشن RavenDB نیز بلافاصله Dispose خواهد شد، این E-Tag را از دست خواهیم داد. همچنین باید دقت داشت که سشن RavenDB به هیچ عنوان نباید در طول عمر یک برنامه باز نگهداشته شود و برای طول عمری کوتاه طراحی شده است. راه حلی که برای آن درنظر گرفته شده است، ذخیره سازی این E-Tag در بار اول دریافت آن از سشن می‌باشد. برای این منظور تنها کافی است خاصیتی را به نام Etag با ویژگی JsonIgnore (که سبب عدم ذخیره سازی آن در بانک اطلاعاتی خواهد شد) تعریف کنیم:
public class Person
{
    public string Id { get; set; }

    [JsonIgnore]
    public Guid? Etag { get; set; }

    public string Name { get; set; }
}
اکنون زمانیکه سندی را از بانک اطلاعاتی دریافت می‌کنیم، با استفاده از متد session.Advanced.GetEtagFor، می‌توان این Etag واقعی را دریافت کرد و ذخیره نمود:
public Person Get(string id)
{
    var person = session.Load<Person>(id);
    person.Etag = session.Advanced.GetEtagFor(person);
    return person;
}
و برای استفاده از آن ابتدا باید UseOptimisticConcurrency به true تنظیم شده و سپس در متد Store این Etag دریافتی از سرور را مشخص نمائیم:
public void Update(Person person)
{
    session.Advanced.UseOptimisticConcurrency = true;
    session.Store(person, person.Etag, person.Id);
    session.SaveChanges();
    person.Etag = session.Advanced.GetEtagFor(person);
}


تراکنش‌های صریح

همانطور که عنوان شد، به صورت ضمنی کلیه سشن‌ها، یک واحد کار را تشکیل داده و با پایان آن‌ها، تراکنش خاتمه می‌یابد. اگر به هر علتی قصد تغییر این رفتار ضمنی پیش فرض را دارید، امکان تعریف صریح تراکنش‌های نیز وجود دارد:
using (var transaction = new TransactionScope())
{
   using (var session1 = store.OpenSession())
   {
     session1.Store(new Account());
     session1.SaveChanges();
   }

   using (var session2 = store.OpenSession())
   {
     session2.Store(new Account());
     session2.SaveChanges();
   }

   transaction.Complete();
}
باید دقت داشت که پایان یک تراکنش، یک non-blocking asynchronous call است و مباحث stale data که پیشتر در مورد آن بحث شد، برقرار هستند.
مطالب
نحوه‌ی نگاشت فیلدهای فرمول در Fluent NHibernate

اگر با SQL Server کار کرده باشید حتما با مفهوم و امکان Computed columns (فیلدهای محاسبه شده) آن آشنایی دارید. چقدر خوب می‌شد اگر این امکان برای سایر بانک‌های اطلاعاتی که از تعریف فیلدهای محاسبه شده پشتیبانی نمی‌کنند، نیز مهیا می‌شد. زیرا یکی از اهداف مهم استفاده‌ی صحیح از ORMs ، مستقل شدن برنامه از نوع بانک اطلاعاتی است. برای مثال امروز می‌خواهیم با MySQL‌ کار کنیم، ماه بعد شاید بخواهیم یک نسخه‌ی سبک‌تر مخصوص کار با SQLite را ارائه دهیم. آیا باید قسمت دسترسی به داده برنامه را از نو بازنویسی کرد؟ اینکار در NHibernate فقط با تغییر نحوه‌ی اتصال به بانک اطلاعاتی میسر است و نه بازنویسی کل برنامه (و صد البته شرط مهم و اصلی آن هم این است که از امکانات ذاتی خود NHibernate استفاده کرده باشید. برای مثال وسوسه‌ی استفاده از رویه‌های ذخیره شده را فراموش کرده و به عبارتی ORM مورد استفاده را به امکانات ویژه‌ی یک بانک اطلاعاتی گره نزده باشید).
خوشبختانه در NHibernate امکان تعریف فیلدهای محاسباتی با کمک تعریف نگاشت خواص به صورت فرمول مهیا است. برای توضیحات بیشتر لطفا به مثال ذیل دقت بفرمائید:
در ابتدا کلاس کاربر تعریف می‌شود:

using System;
using NHibernate.Validator.Constraints;

namespace FormulaTests.Domain
{
public class User
{
public virtual int Id { get; set; }

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

[NotNullNotEmpty]
[Length(450)]
public virtual string FirstName { get; set; }

[NotNullNotEmpty]
[Length(450)]
public virtual string LastName { get; set; }

[Length(900)]
public virtual string FullName { get; private set; } //از طریق تعریف فرمول مقدار دهی می‌گردد

public virtual int DayOfWeek { get; private set; }//از طریق تعریف فرمول مقدار دهی می‌گردد
}
}
در این کلاس دو خاصیت FullName و DayOfWeek به صورت فقط خواندنی به کمک private set ذکر شده، تعریف گردیده‌اند. قصد داریم روی این دو خاصیت فرمول تعریف کنیم:

using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;

namespace FormulaTests.Domain
{
public class UserCustomMappings : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
mapping.Id(u => u.Id).GeneratedBy.Identity(); //ضروری است
mapping.Map(x => x.DayOfWeek).Formula("DATEPART(dw, JoinDate) - 1");
mapping.Map(x => x.FullName).Formula("FirstName + ' ' + LastName");
}
}
}
نحوه‌ی انتساب فرمول‌های مبتنی بر SQL را در نگاشت فوق ملاحظه می‌نمائید. برای مثال FullName از جمع دو فیلد نام و نام خانوادگی حاصل خواهد شد و DayOfWeek از طریق فرمول SQL دیگری که ملاحظه می‌نمائید (یا هر فرمول SQL دلخواه دیگری که صلاح می‌دانید).
اکنون اگر Fluent NHibernate را وادار به تولید اسکریپت متناظر با این دو کلاس کنیم حاصل به صورت زیر خواهد بود:
    create table Users (
UserId INT IDENTITY NOT NULL,
JoinDate DATETIME not null,
FirstName NVARCHAR(450) not null,
LastName NVARCHAR(450) not null,
primary key (UserId)
)
همانطور که ملاحظه می‌کنید در اینجا خبری از دو فیلد محاسباتی تعریف شده نیست. این فیلدها در تعاریف نگاشت‌ها به صورت خودکار ظاهر می‌شوند:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" mutable="true"
name="FormulaTests.Domain.User, FormulaTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Users">
<id name="Id" type="System.Int32" unsaved-value="0">
<column name="UserId" />
<generator class="identity" />
</id>
<property name="DayOfWeek" formula="DATEPART(dw, JoinDate) - 1" type="System.Int32" />
<property name="FullName" formula="FirstName + ' ' + LastName" type="System.String" />
<property name="JoinDate" type="System.DateTime">
<column name="JoinDate" />
</property>
<property name="FirstName" type="System.String">
<column name="FirstName" />
</property>
<property name="LastName" type="System.String">
<column name="LastName" />
</property>
</class>
</hibernate-mapping>
اکنون اگر کوئری زیر را در برنامه اجرا نمائیم:
var list = session.Query<User>.ToList();
foreach (var item in list)
{
Console.WriteLine("{0}:{1}", item.FullName, item.DayOfWeek);
}
به صورت خودکار به SQL ذیل ترجمه خواهد شد و اکنون نحوه‌ی بکارگیری فیلدهای فرمول، بهتر مشخص می‌گردد:
select
user0_.UserId as UserId0_,
user0_.JoinDate as JoinDate0_,
user0_.FirstName as FirstName0_,
user0_.LastName as LastName0_,
DATEPART(user0_.dw, user0_.JoinDate) - 1 as formula0_, --- همان فرمول تعریف شده است
user0_.FirstName + ' ' + user0_.LastName as formula1_ ---از طریق فرمول تعریف شده حاصل گردیده است
from
Users user0_

نظرات مطالب
ساخت دیتابیس sqlite با EF6 Code First
- با به روز رسانی بسته‌های نیوگت مثال پیوستی EF 6x، مشکلی مشاهده نشد و مثال قابل اجرا است. نگارش ویژوال استودیو در اینجا مهم نیست و تنها اجرای دستور ذیل مهم است:
PM> update-package
الف) فایل کانفیگ پروژه خودتان را بررسی کنید و با محتوای فایل App.config پیوستی مطابقت دهید.
ب) همچنین رشته‌ی اتصالی آن باید به محل دقیق قرارگیری فایل phonebook.sqlite اشاره کند.
ج) نام بسته‌های موجود در فایل packages.config خودتان را با نمونه‌ی پروژه‌ی پیوستی مطابقت دهید.
این موارد را بررسی کنید؛ وگرنه EF 6x در حال توسعه‌ی پیوسته نیست و تغییر خاصی از زمان نگارش این مطلب نداشته‌است. 

+ اگر می‌خواهید نسخه‌ی EF Core آن‌را بررسی کنید:
- نیاز است سری EF Core را در سایت از ابتدا مطالعه کنید. 
- VS 2015 دیگر برای کار با NET Core‌. مناسب نیست. حتما نیاز است VS 2017 را نصب کنید. اطلاعات بیشتر
- پیشنیازهای جدید کار با SQLite در فایل csproj آن برای VS 2017 و EF Core 1.1.1 به صورت ذیل هستند:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" PrivateAssets="All" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.0" PrivateAssets="All" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.*" />
  </ItemGroup>
</Project>
- قسمت‌های تنظیم Context آن‌را در مطالب «استفاده از EF7 با پایگاه داده SQLite تحت NET Core. به کمک Visual Studio Code» و «استفاده از پروایدر SQLite در Entity Framework 7» پیگیری کنید.
دوره‌ها
RavenDB
RavenDB یک بانک اطلاعاتی سورس باز NoSQL سندگرای تهیه شده با دات نت است. در این دوره به بررسی جزئیات نحوه کار با آن خواهیم پرداخت.