۱۳۹۰/۱۲/۲۵

استفاده از SQL-CE به كمك NHibernate


خلاصه‌اي را در مورد SQL Server CE قبلا در اين سايت مطالعه‌ كرده‌ايد. در ادامه خلاصه‌اي كاربردي را از تنظيمات و نكات مرتبط به كار با SQL-CE به كمك NHibernate ملاحظه خواهيد نمود:

1) دريافت SQL-CE 4.0


همين مقدار براي استفاده از SQL-CE 4.0 به كمك NHibernate كفايت مي‌كند و حتي نيازي به نصب سرويس پك يك VS 2010 هم نيست.

2) ابزار سازي جهت ايجاد يك بانك اطلاعاتي خالي SQL-CE

using System;
using System.IO;

namespace NHibernate.Helper.DbSpecific
{
    public class SqlCEDbHelper
    {
        const string engineTypeName = "System.Data.SqlServerCe.SqlCeEngine, System.Data.SqlServerCe";

        /// <summary>
        /// note: this method will delete existing db and then creates a new one.
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="password"></param>
        public static void CreateEmptyDatabaseFile(string filename, string password = "")
        {
            if (File.Exists(filename))
                File.Delete(filename);

            var type = System.Type.GetType(engineTypeName);
            var localConnectionString = type.GetProperty("LocalConnectionString");
            var createDatabase = type.GetMethod("CreateDatabase");

            var engine = Activator.CreateInstance(type);

            string connectionStr = string.Format("Data Source='{0}';Password={1};Encrypt Database=True", filename, password);
            if (string.IsNullOrWhiteSpace(password))
                connectionStr = string.Format("Data Source='{0}'", filename);

            localConnectionString.SetValue(
                obj: engine,
                value: connectionStr,
                index: null);
            createDatabase.Invoke(engine, new object[0]);
        }

        /// <summary>
        /// use this method to compact or encrypt existing db or decrypt it to a new db with all records
        /// </summary>
        /// <param name="sourceConnection"></param>
        /// <param name="destConnection"></param>
        public static void CompactDatabase(string sourceConnection, string destConnection)
        {
            var type = System.Type.GetType(engineTypeName);
            var engine = Activator.CreateInstance(type);

            var localConnectionString = type.GetProperty("LocalConnectionString");
            localConnectionString.SetValue(
                obj: engine,
                value: sourceConnection,
                index: null);

            var compactDatabase = type.GetMethod("Compact");
            compactDatabase.Invoke(engine, new object[] { destConnection });
        }
    }
}

كلاس فوق، يك كلاس عمومي است و مرتبط به NHibernate نيست و در همه جا قابل استفاده است.
متد CreateEmptyDatabaseFile يك فايل بانك اطلاعاتي خالي با فرمت مخصوص SQL-CE را براي شما توليد خواهد كرد. به اين ترتيب مي‌توان بدون نياز به ابزار خاصي، سريعا يك بانك خالي را توليد و شروع به كار كرد. در اين متد اگر كلمه عبوري را وارد نكنيد، بانك اطلاعاتي رمزنگاري شده نخواهد بود و اگر كلمه عبور را وارد كنيد، ديتابيس اوليه به همراه كليه اعمال انجام شده بر روي آن در طول زمان، با كمك الگوريتم AES به صورت خودكار رمزنگاري خواهند شد. كل كاري را هم كه بايد انجام دهيد ذكر اين كلمه عبور در كانكشن استرينگ است.
متد CompactDatabase، يك متد چند منظوره است. اگر بانك اطلاعاتي SQL-CE رمزنگاري نشده‌اي داريد و مي‌خواهيد كل آن‌را به همراه تمام اطلاعات درون آن رمزنگاري كنيد، مي‌توانيد جهت سهولت كار از اين متد استفاده نمائيد. آرگومان اول آن به كانكشن استرينگ بانكي موجود و آرگومان دوم به كانكشن استرينگ بانك جديدي كه توليد خواهد شد، اشاره مي‌كند.
همچنين اگر يك بانك اطلاعاتي SQL-CE رمزنگاري شده داريد و مي‌خواهيد آن‌را به صورت يك بانك اطلاعاتي جديد به همراه تمام ركوردهاي آن رمزگشايي كنيد، باز هم مي‌توان از اين متد استفاده كرد. البته بديهي است كه كلمه عبور را بايد داشته باشيد و اين كلمه عبور جايي درون فايل بانك اطلاعاتي ذخيره نمي‌شود. در اين حالت در كانكشن استرينگ اول بايد كلمه عبور ذكر شود و كانكشن استرينگ دوم نيازي به كلمه عبور نخواهد داشت.

فرمت كلي كانكشن استرينگ SQL-CE هم به شكل زير است:

Data Source=c:\path\db.sdf;Password=1234;Encrypt Database=True

البته اين براي حالتي است كه قصد داشته باشيد بانك اطلاعاتي مورد استفاده را رمزنگاري كنيد يا از يك بانك اطلاعاتي رمزنگاري شده استفاده نمائيد. اگر بانك اطلاعاتي شما كلمه عبوري ندارد، ذكر Data Source=c:\path\db.sdf كفايت مي‌كند.

اين كلاس هم از اين جهت مطرح شد كه NHibernate مي‌تواند ساختار بانك اطلاعاتي را بر اساس تعاريف نگاشت‌ها به صورت خودكار توليد و اعمال كند، «اما» بر روي يك بانك اطلاعاتي خالي SQL-CE از قبل تهيه شده (در غيراينصورت خطاي The database file cannot be found. Check the path to the database را دريافت خواهيد كرد).

نكته:
اگر دقت كرده باشيد در اين كلاس engineTypeName به صورت رشته ذكر شده است. چرا؟
علت اين است كه با ذكر engineTypeName به صورت رشته، مي‌توان از اين كلاس در يك كتابخانه عمومي هم استفاده كرد، بدون اينكه مصرف كننده نيازي داشته باشد تا ارجاع مستقيمي را به اسمبلي SQL-CE به برنامه خود اضافه كند. اگر اين ارجاع وجود داشت، متدهاي ياد شده كار مي‌كنند، در غيراينصورت در گوشه‌اي ساكت و بدون دردسر و بدون نياز به اسمبلي خاصي براي روز مبادا قرار خواهند گرفت.


3) ابزار مرور اطلاعات بانك اطلاعاتي SQL-CE

با استفاده از management studio خود SQL Server هم مي‌شود با بانك‌هاي اطلاعاتي SQL-CE كار كرد، اما ... اينبار برخلاف نگارش كامل اس كيوال سرور، با يك نسخه‌ي بسيار بدوي، كه حتي امكان rename فيلدها را هم ندارد مواجه خواهيد شد. به همين جهت به شخصه برنامه SqlCe40Toolbox را ترجيح مي‌دهم و اطمينان داشته باشيد كه امكانات آن براي كار با SQL-CE از امكانات ارائه شده توسط management studio مايكروسافت، بيشتر و پيشرفته‌تر است!



4) تنظيمات NHibernate جهت كار با SQL-CE

الف) پس از نصب SQL-CE ، فايل‌هاي آن‌را در مسير C:\Program Files\Microsoft SQL Server Compact Edition\v4.0 مي‌توان يافت. درايور ADO.NET آن هم در مسير C:\Program Files\Microsoft SQL Server Compact Edition\v4.0\Desktop قرار دارد. بنابراين در ابتدا نياز است تا ارجاعي را به اسمبلي System.Data.SqlServerCe.dll به برنامه خود اضافه كنيد (نام پوشه desktop آن هم غلط انداز است. از اين جهت كه نگارش 4 آن، به راحتي در برنامه‌هاي ذاتا چند ريسماني ASP.Net بدون مشكل قابل استفاده است).
نكته مهم: در اين حالت NHibernate قادر به يافتن فايل درايور ياد شده نخواهد بود و پيغام خطاي «Could not create the driver from NHibernate.Driver.SqlServerCeDriver» را دريافت خواهيد كرد. براي رفع آن، اسمبلي System.Data.SqlServerCe.dll را در ليست ارجاعات برنامه يافته و در برگه خواص آن، خاصيت «Copy Local» را true كنيد. به اين معنا كه NHibernate اين اسمبلي را در كنار فايل اجرايي برنامه شما جستجو خواهد كرد.

ب) مطلب بعد، تنظيمات ابتدايي NHibernate‌ است جهت شناساندن SQL-CE . مابقي مسايل (نكات mapping، كوئري‌ها و غيره) هيچ تفاوتي با ساير بانك‌هاي اطلاعاتي نخواهد داشت و يكي است. به اين معنا كه اگر برنامه شما از ويژگي‌هاي خاص بانك‌هاي اطلاعاتي استفاده نكند (مثلا اگر از رويه‌هاي ذخيره شده اس كيوال سرور استفاده نكرده باشد)، فقط با تغيير كانكشن استرينگ و معرفي dialect و driver جديد، به سادگي مي‌تواند به يك بانك اطلاعاتي ديگر سوئيچ كند؛ بدون اينكه حتي بخواهيد يك سطر از كدهاي اصلي برنامه خود را تغيير دهيد.



تنها نكته جديد آن اين متد است:

private Configuration getConfig()
{
            var configure = new Configuration();
            configure.SessionFactoryName("BuildIt");

            configure.DataBaseIntegration(db =>
            {
                db.ConnectionProvider<DriverConnectionProvider>();
                db.Dialect<MsSqlCe40Dialect>();
                db.Driver<SqlServerCeDriver>();
                db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
                db.IsolationLevel = IsolationLevel.ReadCommitted;
                db.ConnectionString = ConnectionString;
                db.Timeout = 10;

                //for testing ...
                db.LogFormattedSql = true;
                db.LogSqlInConsole = true;
            });

            return configure;
}

كه در آن نحوه تعريف MsSqlCe40Dialect و SqlServerCeDriver مشخص شده است.

نكته حاشيه‌اي!
در اين مثال primary key از نوع identity تعريف شده و بدون مشكل كار كرد. همين را اگر با EF تست كنيد، اين خطا را دريافت مي‌كنيد: «Server-generated keys and server-generated values are not supported by SQL Server Compact». بله، EF نمي‌تواند با primary key از نوع identity حين كار با SQL-CE كار كند. براي رفع آن توصيه شده است كه از Guid استفاده كنيد!

نكته تكميلي:
استفاده از Dialect سفارشي در NHibernate


نكته پاياني!
و در پايان بايد اشاره كرد كه SQL-CE يك بانك اطلاعاتي نوشته شده با دات نت نيست (با CPP نوشته شده است و نصب آن هم نياز به ران تايم به روز VC را دارد). به اين معنا كه جهت سيستم‌هاي 64 بيتي و 32 بيتي بايد نسخه مناسب آن‌را توزيع كنيد. يا اينكه Target platform پروژه جاري دات نت خود را بر روي X86 قرار دهيد (نه بر روي Any CPU پيش فرض) و در اين حالت تنها يك نسخه X86 بانك اطلاعاتي SQL-CE و همچنين برنامه خود را براي تمام سيستم‌ها توزيع كنيد.