مقدمه ای بر RavenDB – قسمت سوم

در دو قسمت اول و دوم “مقدمه ای به RavenDB” به آشنایی با مقدمات RavenDB پرداختیم تو این قسمت میخواهیم با یک مثال در عمل با RavenDB آشنا بشیم.
برای شروع یک پروژه ASP.NET MVC 3 رو ایجاد میکنیم.
اگه از قسمت اول یادتون باشه گفتم یکی از 4 روش راه اندازی (هاست کردن) RavenDB به صورت مدفون شده یا Embed هست برای استفاده از این روش کافیه پکیج RavenDB-Embedded رو با NuGet نصب کنیم.

همونطور که قبلا گفتم RavenDB به صورت schema-less هست ازاینرو برای کار با بانک نیاز نیست شما بانک و جدولی رو از قبل بسازید (کاری که با بانک های Embedded دیگه مثله SQL Server Compact انجام میدیم) تنها پس از اولین فراخوانی متد initialize شی DocumentStore ما فایل های مربوطه (همان بانک RavenDB) ساخته میشه.
برای ذخیره بانک ما از پوشه استاندارد App_Data استفاده میکنیم.
در طول چرخه حیاط یک برنامه بایستی یکه وهله از بانک در دسترس باشه ازاینرو از الگوی Singleton استفاده میکنیم.

public class DataDocumentStore
{
	private static IDocumentStore _instance;
	public static IDocumentStore Instance
	{
		get
		{
			if (_instance == null)
				throw new InvalidOperationException("IDocumentStore has not been initialized.");
			return _instance;
		}
	}

	public static IDocumentStore Initialize()
	{
		_instance = new EmbeddableDocumentStore { ConnectionStringName = "RavenDB" };
		_instance.Conventions.IdentityPartsSeparator = "-";
		_instance.Initialize();
		return _instance;
	}
}

تنها نکته ی که تو کد بالا باید اشاره کرد قسمت ست کردن IdentityPartsSeparator هست همونطور که تو قسمت های قبل اشاره شد RavenDB برای ذخیره کردن سند از فرمت EntityName/ID استفاده میکنه مثلا Albums/20 خب مشکل اینه که / تو سیستم مسیریابی MVC باعث مشکل میشه ازاینرو ما با ست کردن IdentityPartsSeparator به جای /  از – استفاده میکنیم مثلا Albums-20.
برای اینکه در اولین اجرای برنامه بانک ما ساخته بشه فراخوانی متد Initialize رو تو متد Application_Start انجام میدیم.

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            DataAccess.DataDocumentStore.Initialize();
        }

تا اینجای کار ما موفق شدیم شی DocumentStore رو برای کار تو برنامه بسازیم و اطمینان حاصل کنیم تنها یک وهله از شی DocumentStore داخل برنامه استفاده بشه.

کار رو با تعریف کردن کلاس (مدل) Person ادامه میدیم.

 public class Person
    {
        public string Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
	
	

API های RavenDB برای کار با بانک از الگوی Unit of Work استفاده میکنند دقیقا روشی که با data context در Entity Framework انجام میدیم.

using (var session = documentStore.OpenSession())
{
  session.Store(Person);
  session.SaveChanges();
}
	

این روش خوب و بهینه ای هست که یک session رو سراسر یک درخواست HTTP باز نگه داریم ازاینروز میایم یه کنترلر پایه (Base) میسازیم که در شروع یک Action بیاد session رو باز کنه و بعد از اجرا شدن Action تغییرات رو تو بانک اعمال و session رو ببنده و در کنترلرهای دیگه این کنترلر رو به ارث ببریم.

public class BaseDocumentStoreController : Controller
    {
        public IDocumentSession DocumentSession { get; set; }

        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext.IsChildAction)
                return;
            this.DocumentSession = DataDocumentStore.Instance.OpenSession();
            base.OnActionExecuting(filterContext);
        }

        protected override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (filterContext.IsChildAction)
                return;

            if (this.DocumentSession != null && filterContext.Exception == null)
                this.DocumentSession.SaveChanges();

            var documentSession = this.DocumentSession;
            if (documentSession != null) documentSession.Dispose();

            base.OnActionExecuted(filterContext);
        }
    }
	
	

در آخر نحوه انجام چهار عملی اصلی (CRUD) رو در بانک RavenDB میبینیم در کنترلر مربوطه.

public class PersonController : BaseDocumentStoreController
    {
        //
        // GET: /Person/

        public ViewResult Index()
        {
            var model = this.DocumentSession.Query().ToList();
            return View(model);
        }

        public ActionResult Edit(string id)
        {
            var model = DocumentSession.Load(id);
            return View(model);
        }

        [HttpPost]
        public ActionResult Edit(Person person)
        {
            this.DocumentSession.Store(person);
            return RedirectToAction("Index");
        }

        public ActionResult Create()
        {
            var model = new Person();
            return View(model);
        }

        [HttpPost]
        public ActionResult Create(Person person)
        {
            DocumentSession.Store(person);
            return RedirectToAction("Index");
        }

        public ActionResult Delete(string id)
        {
            var model = DocumentSession.Load(id);
            return View(model);
        }

        [HttpPost,ActionName("Delete")]
        public ActionResult DeleteConfirmed(string id)
        {
            DocumentSession.Advanced.DatabaseCommands.Delete(id,null);
            return RedirectToAction("Index");
        }
    }
	

خروجی متد IDocumentSession.Query از نوع IRavenQueryable هست که اینترفیس IEnumerable رو محقق (پیاده سازی) کرده ازاینرو میشه متد ToList رو براش فراخوانی کرد.
متد IDocumentSession.Load براساس پارامتر وردیش (کلید سند) سند مورد نظر رو بازیابی میکنه.
متد IDocumentSession.Store هم کار ایجاد رو انجام میده و هم کار آپدیت و بروز رسانی یک سند موجود رو حالا سوالی که پیش میاد اینه که چجوری تشخیص میده که باید آپدیت کنه یا ایجاد؟
اگه پارامتر ورودی متد Store (در اینجا Person) مقدار Id ش ست شده باشه میره و براساس اون Id سند مورد نظر رو آپدیت میکنه در غیر اینصورت به بانک اضافش میکنه و یک Id بهش اختصاص میده.
برای پاک کردن از 2 راه میشه اینکارو انجام داد

  • فراخوانی متد IDocumentSession.Delete که لازمه قبلش سند مورد نظر رو لود کنیم و بعنوان ورودی این متد بدیم.
  • استفاده از DocumentSession.Advanced.DatabaseCommands.Delete که بدون لود کرد سند مورد نظر و با داشتن Id سند مورد نظر مستقیم اونو پاک کنیم.

نمای از صفحه اصلی برنامه

منابع:+و+و+

نظرات

پست‌های معروف از این وبلاگ

lnav ابزاری بسیار کاربردی برای پیمایش لاگ ها در لینوکس و البته مک

ساختن ایمیج های داکری به کمک BuildKit - بخش دوم

ساختن ایمیج های داکری به کمک BuildKit - بخش اول