مطالب
شروع به کار با EF Core 1.0 - قسمت 15 - نوشتن آزمون‌های واحد
یکی از مشخصات آزمون‌های واحد، عدم خروج از مرزهای IO سیستم، در حین اجرای آن‌ها است و چون درهنگام کار با بانک‌های اطلاعاتی حتما از مرزهای IO سیستم رد خواهیم شد (کار با شبکه، کار با فایل سیستم، برای به روز رسانی و درج اطلاعات)، نوشتن آزمون‌های واحد واقعی، برای برنامه‌هایی که از ORMها استفاده می‌کنند مشکل است. به همین جهت مباحث mocking، تقلید قسمت‌های مختلف ORMها و جایگزین کردن آن‌ها با نمونه‌های درون حافظه‌ای بسیار مرسوم است. برای رفع این مشکلات، تیم EF Core، یک تامین کننده‌ی بانک اطلاعاتی ویژه‌ی «درون حافظه‌ای» را به نام «Entity Framework Core InMemory provider» ارائه داده‌است. به این ترتیب، این محل ذخیره سازی اطلاعات درون حافظه‌ای، مشکل رد شدن از مرزهای IO سیستم را برطرف کرده و عملا نیاز به کار کردن با فریم ورک‌های mocking را منتفی می‌کند (حداقل برای تقلید قسمت‌های مختلف EF Core).
در این قسمت ابتدا نحوه‌ی فعال سازی فریم ورک آزمون‌های واحد مایکروسافت و سپس نحوه‌ی فعال سازی این تامین کننده‌ی بانک اطلاعاتی درون حافظه‌ای را بررسی خواهیم کرد. به علاوه برای سرویس بلاگ‌های قسمت قبل نیز آزمون واحد خواهیم نوشت.


نحوه‌ی فعالسازی فریم ورک MSTest در یک پروژه‌ی Class library از نوع NET Core.


تنها نکته‌ی مهم فعالسازی MSTest در یک پروژه‌ی Class library جدید که برای نوشتن آزمون‌های واحد مورد استفاده قرار خواهیم داد، تنظیمات فایل project.json آن است که در ذیل آمده است:
{
    "version": "1.0.0-*",
 
    "testRunner": "mstest",
    "dependencies": {
        "Microsoft.NETCore.App": {
            "type": "platform",
            "version": "1.0.0"
        },
        "dotnet-test-mstest": "1.1.1-preview",
        "MSTest.TestFramework": "1.0.1-preview",
        "NETStandard.Library": "1.6.0",
        "Microsoft.EntityFrameworkCore": "1.0.0",
        "Microsoft.EntityFrameworkCore.InMemory": "1.0.0",
        "Core1RtmEmptyTest.DataLayer": "1.0.0-*",
        "Core1RtmEmptyTest.Entities": "1.0.0-*",
        "Core1RtmEmptyTest.Services": "1.0.0-*",
        "Core1RtmEmptyTest.ViewModels": "1.0.0-*"
    },
 
    "frameworks": {
        "netcoreapp1.0": {
            "imports": [
                "dnxcore50",
                "portable-net45+win8"
            ]
        }
    }
}
- در اینجا قید testRunner الزامی است؛ در غیراینصورت آزمون‌های واحد شما شناسایی نخواهند شد. همچنین بسته‌های dotnet-test-mstest و MSTest.TestFramework نیز باید اضافه شوند.
- به علاوه در اینجا ارجاعاتی را به اسمبلی‌های موجودیت‌ها، Services و DataLayer که در قسمت «شروع به کار با EF Core 1.0 - قسمت 14 - لایه بندی و تزریق وابستگی‌ها» بررسی شدند نیز ملاحظه می‌کنید.
- همچنین وابستگی جدید Microsoft.EntityFrameworkCore.InMemory نیز در اینجا قابل ملاحظه است. این وابستگی را تنها به پروژه‌ی آزمون‌های واحد خود اضافه می‌کنیم. از این جهت که تنظیمات آن صرفا در این قسمت جدید قید می‌شوند و نه در سایر قسمت‌های برنامه.

 پس از آن، کار با این فریم ورک، همانند سایر نگارش‌های دات نت خواهد بود:
using Microsoft.VisualStudio.TestTools.UnitTesting;
 
namespace EFCore.MsTests
{
    [TestClass]
    public class CoreTests
    {
        [TestMethod]
        public void Test1()
        {
            Assert.IsTrue(true);
        }
    }
}
ابتدا کلاس مدنظر، با ویژگی TestClass مزین می‌شود. سپس متد آزمون واحد نوشته شده نیز باید به صورت public void و مزین شده‌ی با ویژگی TestMethod، ارائه شود.
پس از نوشتن اولین آزمون واحد، یکبار پروژه را build کرده و سپس از منوی Test، گزینه‌ی Windows را انتخاب کرده و در اینجا گزینه‌ی Test Explorer را انتخاب کنید. اندکی صبر کنید تا آزمون‌های واحد شما شناسایی شوند و سپس گزینه‌ی Run All را انتخاب کنید:



تغییرات Context برنامه جهت استفاده‌ی از تامین کننده‌ی داخل حافظه‌ای

در مورد نحوه‌ی تعریف و افزودن وابستگی‌های EF Core در مطلب «شروع به کار با EF Core 1.0 - قسمت 1 - برپایی تنظیمات اولیه» پیشتر بحث شد و همچنین در مطلب «شروع به کار با EF Core 1.0 - قسمت 3 - انتقال مهاجرت‌ها به یک اسمبلی دیگر»، اطلاعات Context برنامه را به اسمبلی دیگری منتقل کردیم.
اگر از روش بازنویسی متد OnConfiguring برای تنظیم تامین کننده‌ی بانک اطلاعاتی مورد نظر استفاده می‌کنید، متد OnConfiguring کلاس Context برنامه چنین شکلی را پیدا می‌کند:
public class ApplicationDbContext : DbContext, IUnitOfWork
{
    private readonly IConfigurationRoot _configuration;
 
    public ApplicationDbContext(IConfigurationRoot configuration)
    {
        _configuration = configuration;
    }
 
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
    {
    } 
 
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer(
                _configuration["ConnectionStrings:ApplicationDbContextConnection"]
                , serverDbContextOptionsBuilder =>
                {
                    var minutes = (int)TimeSpan.FromMinutes(3).TotalSeconds;
                    serverDbContextOptionsBuilder.CommandTimeout(minutes);
                });
        }
    }
در اینجا دو تغییر جدید قابل ملاحظه هستند:
الف) اضافه شدن سازنده‌ی دومی که <DbContextOptions<ApplicationDbContext را دریافت می‌کند. از آن در سمت کدهای آزمون واحد برنامه جهت ثبت ()options.UseInMemoryDatabase استفاده می‌شود.
ب) به متد OnConfiguring، بررسی optionsBuilder.IsConfigured هم اضافه شده‌است. چون در سمت کدهای آزمون واحد، تامین کننده‌ی بانک اطلاعاتی درون حافظه‌ای اضافه می‌شود، مقدار optionsBuilder.IsConfigured به true تنظیم خواهد شد و دیگر از تامین کننده‌ی SQL Server استفاده نمی‌شود.

اگر از متد OnConfiguring به این شکل استفاده نمی‌کنید، تنها ذکر سازنده‌ی دوم ضروری است. از این جهت که در آزمون‌های واحد، از تنظیمات متد ConfigureServices کلاس آغازین برنامه استفاده نخواهد شد.


نوشتن آزمون‌های واحد مخصوص EF Core

پس از برپایی پیشنیازهای نوشتن آزمون‌ها واحد، شامل تنظیمات فریم ورک MSTest و همچنین افزودن وابستگی‌های مرتبط با فایل project.json ایی که در ابتدای بحث عنوان شد و اصلاح سازنده و متد OnConfiguring کلاس Context برنامه جهت آماده سازی آن‌ها برای پذیرش تامین کننده‌های دیگر، اکنون یک نمونه از آزمون‌های واحد درون حافظه‌ای EF Core، چنین شکلی را خواهد داشت:
using System;
using System.Linq;
using Core1RtmEmptyTest.DataLayer;
using Core1RtmEmptyTest.Entities;
using Core1RtmEmptyTest.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
 
namespace Core1RtmEmptyTest.MsTests
{
    [TestClass]
    public class CoreTests
    {
        private readonly IServiceProvider _serviceProvider;
 
        public CoreTests()
        {
            var services = new ServiceCollection();
            services.AddEntityFrameworkInMemoryDatabase()
                        .AddDbContext<ApplicationDbContext>(options => options.UseInMemoryDatabase());
 
            services.AddScoped<IUnitOfWork, ApplicationDbContext>();
            services.AddScoped<IBlogService, BlogService>();
 
            _serviceProvider = services.BuildServiceProvider();
        }
 
        [TestMethod]
        public void Find_searches_url()
        {
            // Insert seed data into the database using one instance of the context
            using (var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                using (var context = serviceScope.ServiceProvider.GetRequiredService<IUnitOfWork>())
                {
                    context.Set<Blog>().Add(new Blog { Url = "http://sample.com/cats" });
                    context.Set<Blog>().Add(new Blog { Url = "http://sample.com/catfish" });
                    context.Set<Blog>().Add(new Blog { Url = "http://sample.com/dogs" });
                    context.SaveAllChanges();
                }
            }
 
            // Use a separate instance of the context to verify correct data was saved to database
            using (var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                using (var context = serviceScope.ServiceProvider.GetRequiredService<IUnitOfWork>())
                {
                    Assert.AreEqual(3, context.Set<Blog>().Count());
                    Assert.AreEqual("http://sample.com/cats", context.Set<Blog>().First().Url);
                }
            }
 
            // Use a clean instance of the context to run the test
            using (var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var blogService = serviceScope.ServiceProvider.GetRequiredService<IBlogService>();
                var results = blogService.GetPagedBlogsAsNoTracking(pageNumber: 0, recordsPerPage: 10);
                Assert.AreEqual(3, results.Count);
            }
        }
    }
}
توضیحات:
همانطور که در قسمت «تغییرات Context برنامه جهت استفاده‌ی از تامین کننده‌ی داخل حافظه‌ای» فوق عنوان شد، در حین انجام آزمون‌های واحد، دیگر به کلاس آغازین برنامه و تنظیمات آن مراجعه نمی‌شود. بنابراین باید شبیه به عملکرد متد ConfigureServices آن‌را در اینجا پیاده سازی کرد. نمونه‌ای از انجام اینکار را در سازنده‌ی کلاس انجام آزمون‌های واحد مشاهده می‌کنید:
        private readonly IServiceProvider _serviceProvider;
 
        public CoreTests()
        {
            var services = new ServiceCollection();
            services.AddEntityFrameworkInMemoryDatabase()
                        .AddDbContext<ApplicationDbContext>(options => options.UseInMemoryDatabase());
 
            services.AddScoped<IUnitOfWork, ApplicationDbContext>();
            services.AddScoped<IBlogService, BlogService>();
 
            _serviceProvider = services.BuildServiceProvider();
        }
در اینجا است که توسط متد AddEntityFrameworkInMemoryDatabase، کار افزودن تامین کننده‌ی بانک اطلاعاتی درون حافظه‌ای انجام شده و سپس Context برنامه نیز از آن مطلع می‌شود (علت افزودن سازنده‌ی دومی که <DbContextOptions<ApplicationDbContext را دریافت می‌کند).
سپس همانند قبل، باید تمام سرویس‌های مدنظر تنظیم شوند تا بتوان از آن‌ها استفاده کرد.

نکته‌ی مهم دیگری را که باید به آن دقت داشت، ایجاد scope و سپس دسترسی به سرویس‌ها از طریق این Scope است. از این جهت که چون خارج از طول عمر یک درخواست وب قرار داریم، دیگر Scopeها برای ما به صورت خودکار ایجاد و تخریب نمی‌شوند و باید همان‌کاری را که ASP.NET Core در پشت صحنه انجام می‌دهد، به صورت دستی پیاده سازی کنیم:
            using (var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                using (var context = serviceScope.ServiceProvider.GetRequiredService<IUnitOfWork>())
                {
اگر اینکار صورت نگیرد، چون Scope ایی ایجاد و تخریب نمی‌شود، کار کردن با متد serviceProvider.GetRequiredService_ اشتباه بوده و همیشه یک وهله از Context را باز می‌گرداند که مدنظر ما نیست. شبیه به این نکته را در قسمت «مقدار دهی اولیه‌ی جداول بانک‌های اطلاعاتی در EF Core» پیشتر ملاحظه کرده‌اید.


یک نکته‌ی تکمیلی

EF Core به همراه تامین کننده‌ی بانک اطلاعاتی SQLite نیز هست. یکی از نکات ویژه‌ی بانک اطلاعاتی SQLite، امکان تنظیم پارامتری است در رشته‌ی اتصالی آن، که آن‌را نیز تبدیل به یک «بانک اطلاعاتی درون حافظه‌ای» می‌کند. این روش سال‌ها است که جهت انجام آزمون‌های واحد ORMها مورد استفاده قرار می‌گیرد. بنابراین می‌توان آن‌را به عنوان جایگزینی برای مطلب جاری نیز درنظر گرفت.
 var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" };
var connectionString = connectionStringBuilder.ToString();
var connection = new SqliteConnection(connectionString);
services.AddEntityFrameworkSqlite().AddDbContext<CmsDbContext>(options => options.UseSqlite(connection));
اهمیت آن در اینجا است که تامین کننده‌ی بانک اطلاعاتی درون حافظه‌ای EF، قیود را اعمال نمی‌کند ؛ اما بانک اطلاعاتی درون حافظه‌ای SQLite واقعا همانند یک بانک اطلاعاتی رابطه‌ای کامل عمل می‌کند.
نظرات مطالب
EF Code First #6
در EF 5 جای یک سری از کلاس‌ها تغییر کرده. مثلا ویژگی‌های ForeignKey، ComplexType و ... به فضای نام System.ComponentModel.DataAnnotations.Schema منتقل شده‌اند. در همین حد تغییر جهت کامپایل مجدد کد کفایت می‌کند.
 
دوره‌ها
طراحی یک فریم ورک برای کار با WPF و EF Code First توسط الگوی MVVM
در این دوره، قالب تهیه یک پروژه جدید WPF مبتنی بر EF Code first را دریافت خواهید کرد که دارای این مشخصات است:

1- اعتبارسنجی یکپارچه با EF Code first

2- دارای سیستم راهبری (Navigation) بین صفحات با قابلیت تزریق خودکار وابستگی‌ها توسط کتابخانه StructureMap
3- به همراه مباحثی مانند تعریف کاربران، تعریف سطوح دسترسی و همچنین راهبری بین صفحات برنامه با درنظر گرفتن این مسایل به کمک تنها افزودن یک ویژگی به نام PageAuthorization به ابتدای تعریف کلاس یک صفحه



4- دارای سیستم خودکار پیغام دهی به کاربر در صورتیکه قصد حرکت به صفحه‌ای دیگر را داشته باشد؛ اما تغییرات صفحه جاری ذخیره نشده‌اند.


5- قالب پروژه جدید تدارک دیده شده، به صورت خودکار لایه بندی‌های برنامه را تدارک خواهد دید (شامل DataLayer، DomainClasses، ServiceLayer و غیره)
6- به همراه سیستم DbContext یکپارچه با مباحثی مانند یکسان سازی ی و ک در برنامه به صورت خودکار و نمایش مشکلات اعتبارسنجی داده‌ها به کاربر بدون نیازی به کد نویسی اضافه.
7- این قالب پروژه با کتابخانه‌های زیر یکپارچه است:
Entity Framework Code First
Fody (جهت اعمال مسایل AOP برای کاهش تدارک کدهای INotifyPropertyChanged در برنامه)
MahApps.Metro (برای نمایش قالب مترو سازگار با دات نت 4)
Microsoft.SqlServer.Compact.4 (بانک اطلاعاتی پیش فرض برنامه دسکتاپ تدارک دیده شده)
MvvmLight (پایه مباحث MVVM بکارگرفته شده در برنامه)
StructureMap (جهت پیاده سازی مباحث تزریق وابستگی‌ها در برنامه)
اشتراک‌ها
اختلال در نمایش سایت CodeProject

مدتی هست که به خاطر محدود شدن آدرس  codeproject.cachefly.net نمایش سایت CodeProject با اختلال مواجه شده.آقای نصیری این مشکل رو گزارش  کردن سایر اعضای سایت هم می‌تونن این گزارش رو تائید کنند.

اختلال در نمایش سایت CodeProject
مطالب
Fluent Linq to Sql

نگارش بعدی یا چهارم entity framework چیزی است شبیه به Fluent NHibernate . یعنی اگر مقاله‌ای را در این زمینه مطالعه کنید و عنوان آن حذف شود، نمی‌توان تشخیص داد که این مقاله مربوط به entity framework است یا Fluent NHibernate. هر چند entity framework حداقل دو نگارش دیگر لازم دارد تا NHibernate را کاملا پشت سر بگذارد.
از آن طرف محبوبیت Linq to SQL هم هنوز پابرجا است و برای مثال سایت پر ترافیکی مثل stack overflow از آن استفاده می‌کند و بسیار هم موفق بوده و کارش را به خوبی انجام می‌دهد.
پروژه مکملی به نام Fluent Linq to Sql با الهام گیری از Fluent NHibernate در سایت codeplex موجود است که این نوع نگاشت‌ها را برای Linq to Sql نیز میسر می‌سازد. به این صورت دیگر نیازی به استفاده از attributes و یا فایل‌های xml نگاشت‌های Linq to Sql نخواهد بود. همچنین مدل کاری اول کد بعد دیتابیس نیز به این صورت محقق می‌شود.



مطالب
کار با اسناد در 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 استفاده کنیم. اما این قدرت زیاد، امکان اشتباه در کدهای ما را زیاد میکند چرا که تحت کنترل کامپایلر نیست.
نظرات مطالب
نقدی بر کتاب «مرجع کامل entity framework 4.1»
- نگهداری این‌ها باید در مغز باشد ! :) به علاوه هستند یک سری برنامه مانند این: http://wordsremind.codeplex.com/
- می‌تونید با کمیته‌ی فیلترینگ در این زمینه هماهنگ کنید که چرا تمام آدرس‌های بلاگر را فیلتر کرده. از فید نظرات تا خود وبلاگ‌ها تا همه چیز در همه جا ... چند هزار یا چند صد هزار وبلاگ. مشکل از اینجا است.
- اطلاعی ندارم (کار پخش با انتشارات بود). لطفا با انتشارات ناقوس تماس بگیرید (یا درخواست خرید آنلاین بدید ... در سایت آن‌ها).


درخواست:
لطفا از موضوع بحث خارج نشوید. طبق عادت متداول این سایت کلیه مطالب خارج از عنوان حذف می‌شوند.
بازخوردهای دوره
ارتباطات بلادرنگ و SignalR
من فکر می‌کنم اگر عنوان دوره‌ها - مانند عنوان آخرین مطالب - در صفحه اول سایت نمایش داده شوند بیشتر مورد استقبال قرار خواهد گرفت.
نظرات مطالب
WF:Windows Workflow #۴
ممنون از مطالب مفید و عالی که در سایت قرار میدید.