‫رکورد اضافی در NHibernate

من مشکلی با NHibernate دارم که نمی‌دانم آیا بقیه هم این مشکل را با NHibernate یا دیگر ORMها یا حتی ADO دارند یا نه. البته اصل این مشکل در صفحات ASP.NET Webform وجود دارد. مشکل این است که وقتی می‌خواهم یک آیتم را در دیتابیس ذخیره کنم یا حتی وقتی می‌خواهم یک رکورد را روی صفحه نمایش دهم مجبور می‌شوم یک یا چند رکورد اضافی را هم از دیتابیس بخوانم.

به عنوان مثال یک صفحه ASP.NET را به شکل زیر تصور کنید:





protected void btnSave_Click(object sender, EventArgs e)
{
Company company = new Company()
{
City = City.Find(drpCity.SelectedValue),
Address = txtAddress.Text
};

company.Save();
}


protected void Search()
{
IList result = MyCustomSearch.SearchCompanies(
City.Find(drpCity.SelectedValue), txtAddress.Text);

//display results in GridView
}

در صفحه بالا، همه Cityها به طور خودکار به drpCity بایند می‌شوند. سپس با کلیک روی دکمه btnSave یک Company با کمک اطلاعات txtAddress و drpCity ذخیره می‌شود. البته کد بالا با Castle ActiveRecord نوشته شده ولی اصل مشکل با دیگر ORMها همان است. همان طور در متود btnSave_Click مشاهده می‌کنید من مجبور هستم برای ذخیره آبجکت Company یک بار به دیتابیس مراجعه کرده و City مورد نظر را از آن فراخوانی نمایم. در متود Search هم همین اتفاق می‌افتد چون متود SearchCompanies پارامتری از نوع City نیاز دارد.

مدتی است که با خودم فکر می‌کنم اگر بتوان این مراجعات بی‌مورد به دیتابیس را حذف کرد، به performance بسیار بهتری می‌رسیم. به همین دلیل به دنبال راه‌هایی هستم که نیاز به load مجدد object را از بین ببرد. در اولین قدم می‌توان همه‌ی متودهایی را که به عنوان ورودی به کل object نیاز دارند را پیدا کرده و آنها را طوری تغییر دهیم که صرفاً بتوانند با ID آبجکت مورد نظر کار کنند. مثلاً متود زیر را در نظر بگیرید:

IList SearchCompanies(City city, string address)
{
var query = from company in ActiveRecordLinq.AsQueryable()
where City == city
select company;

//...
}

این متود را به راحتی می‌توان طوری تغییر داد که به جای City از CityID استفاده کند:

IList SearchCompanies(long cityID, string address)
{
var query = from company in ActiveRecordLinq.AsQueryable()
where City.ID == cityID
select company;

//...
}

Comments

  1. ghafoori

    من دو راه حل به ذهنم می خوره
    یکی اینکه اگر مثلا جداولی مثل city خیلی کاربرد داره برداریم اون را کش کنیم

    راه حل دوم نمی دانم درسته یا نه این تازه به ذهنم خورده بیایم ارتباط company و city را ناقص کنیم چیزی شبیه این

    [Property(NotNull=true)]
    public virtual long cityID { get; set; }

    public City GetCity(long cityID)
    {
    return City.Find(drpCity.SelectedValue);
    }

    IList SearchCompanies(long cityID, string address)
    {
    var query = from company in ActiveRecordLinq.AsQueryable()
    where CityID == cityID
    select company;
    }

    public City GetCityOfCompany(company Company)
    {
    return GetCity(Company.CityID);
    }

    دراین حالت ما ارتباط
    company و city
    را قطع می کنیم این کار برنامه نویسی را زیاد می کنه و امکان اشتباه را به علت نبود رابطه بیشتر می کنه اما در عوض مثلا برای ذخیره company ما فقط یک بار به بانک مراجعه می کنیم

    اما باید داخل
    nhibernate
    یک چیزی مثل
    subquery
    در بانک های اطلاعاتی را ایجاد کنند طوری که مثلا ما به
    nhibernate
    بگیم این پیدا کردن
    city
    جزئی از متد
    شرکت است این دو تا باید داخل هم
    save
    اجرا بشه

    البته من تازه کارم یک چیزی هایی مانند این دو لینک پیدا کردم
    http://devlicio.us/blogs/derik_whittaker/archive/2009/04/06/simple-example-of-using-a-subquery-in-nhibernate-when-using-icriteria.aspx

    http://bartreyserhove.blogspot.com/2008/08/nhibernate-subqueries.html

    این لینک فکر من را نمی رسونه ولی خیلی شبیه

    یک فکری به ذهنم خورده
    ببینید
    Company company = new Company()
    {
    City = new City(drpCity.SelectedValue),
    Address = txtAddress.Text
    };
    company.Save();
    شاید کلاس شهر شما 10 تا خصوصیت داشته باشه اما برای ذخیره داخل دتابیس
    nhibernate
    فقط ای دی را می خواهد که با یک ایجاد یک نمونه از کلاس شهر و مقدار دادن آی دی مشکل قاعدتا باید حل بشه

    ببخشید که نظر طولانی شد اگر نظر من خیلی آبکی بود من هنوز داخل
    nhibernate
    وارد نشدم

  2. Afshar Mohebbi

    ‫سلام غفوری عزیز،

    ممنون از این که راجع به این موضوع کاملاً فکر کردی و کلی راه حلش بهش دادی. و خوشحالم از این که یک نفر دیگه هم وجود داره که به این مشکل فکر کرده. باید یک وقت کاملی بگذارم برای امتحان و استفاده از راه حلی که دادی.

    خیلی دوست دارم بیشتر با هم آشنا شیم. توی وبلاگت چیز خاصی نبود. فیس بوک داری؟ فیس بوک من اینه: http://facebook.com/afsharm

  3. ghafoori

    من کلا داخل وب زیاد تولید کننده نیستم بیشتر مصرف کننده هستم برای همین نه وبلاگم نه فیس بوکم و نه گودرم زیاد خبریش نیست

    یک شش ماهی هست برای یکی از پروژه هایم برداشتم از هایبرنیت استفاده کردم
    بااینکه فناوری جالبیه ولی خیلی دردسرسازه یکی از دردسرهایی که برای من درسته داخل این نظرهای این پست اقای نصیری قرار دادم
    http://vahidnasiri.blogspot.com/2010/12/nhibernate-30-icriteria-api.html#comments

    فکر کنم بخاطر پیداسازی از سمت جاوا شده کلا هم راه حلاش جاوایی و پیچیده است و پر از خطا عجیب غریب اما خوبیش این بوده که بواسطه اون خیلی از مفاهیم برنامه نویسی را ادم درک می کنه

  4. ناشناس

    دوست خوبم سلام
    با تشکر از مطالب وبلاگ شما
    لطفا به این وب سایت برور و عضو بشو ، اینجا می تونی اطلاعات خودت رو به کلی شرکت نرم افزاری و طراحی معرفی کنی بدون هیچگونه هزینه ، فقط اطلاعات رو تکمیل وارد کن. درحال حاضر روی برنامه نویسی و طراحی فوکوس کرده ، چون که شرکت ها نیاز به این نیرو ها دارن
    http://www.greenjob.ir
    باتشکر میثم قاسمی،مدیر روابط عمومی

  5. Afshar Mohebbi

    ‫@غفوری:

    استفاده از cache که به نظرم بتونه راه حل خوبی باشه. اما به نظرم اگر بخواهیم performance را به خوبی افزایش دهیم باید بتوانیم از روشی غیر از cache هم استفاده کنیم. من تا حالا از NHibernate cache استفاده نکردم ولی حدس می‌زنم محدودیت‌های دردسر سازی هم داشته باشه.

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

    البته در مورد update کردن اطلاعات اخیراً مطلبی که در وبلاگ وحید منتشر شده که به عنوان یک راه حل پیشنهاد کرده به جای ISession.Get از ISession.Load استفاده شود. خود من هم دو تا راه به ذهنم رسیده یکی استفاده از HQL برای update کردن و دیگری استفاده از امکان جدید NHibernate یعنی Future.

    در مورد استفاده از sub-query متوجه نشدم منظور چی بود خصوصاً آن قسمت که از کد City = new City(drpCity.SelectedValue) استفاده شده بود.

  6. ghafoori

    در مورد ساب کوئری این بود مثلا این کد sql را در نظر بگیر
    Insert into company(name,cityID) select “company1”, cityID from City where City.name=’Tehran’
    چجوری میشه همچین چیزی را داخل هابرنیت فقط با یک بار اتصال به دیتابیس انجام داد

    در مورد City = new City(drpCity.SelectedValue)
    موضوع فرق داره نظر من این بود که شما ای دی شهرها را داخل منوی کشویی خودتان دارید حالا که می خواهید خصوصیت سیتی کلاس کمپانی را پرکنید یک شی از کلاس سیتی درست کنید که ای دی ان پر شده باشه با این کد منظور
    شما تابع سازنده سیتی را
    overload
    کنید که در بعضی مواقع ای دی بگیره حالا یک شی از نوع سیتی دارید که ای دی ان صحیحه ولی خصوصیت های دیگر ان پر نشده اما برای هایبرنیت قاعدتا فقط ای دی مهمه با همان ای دی اطلاعات را پر می کنه

  7. ایمان نعمتی

    همونطور که وحید نصیری هم اشاره کرده بود، اگر یک SessionScope وجود داشته باشه برای ست کردن کلید خارجی دیگه مراجعتی به دیتابیس نخواهید داشت و NH فقط ID کلید خارجی را در کوئری استفاده خواهد کرد. نکته ای که وجود داره اینه که حتماً باید کلاس شما از نوع Lazy تعریف شده باشه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *