شروع کار با مانگو در سی شارپ
روشهای جایگزین پرسوجو
قطعه کدی که در قسمت قبل نمایش داده شد، معادل دستورات زیر در پوسته دستوری MongoDB است:
use retrogames db.games.findOne({ name: "Invaders 2013"})
اما شاید بد نباشد تا کمی به توضیح کد اخیر که بدنه Program.cs را تشکیل میدهد، بپردازیم. این کد همانطور که از خوانش آن نیز مشخص است، یک شئ thread-safe را بههمراه یک رشته اتصال ساده دریافت میکند. از آنجا که بهطور پیشفرض سرویس در حال اجرا روی localhost در انتظار درخواست روی پورت ۲۷۰۱۷ است، این رشته اتصال نیازی به مشخص کردن پورت مذکور ندارد. نکته دیگر، نبود هیچ تابع فراخوانی برقراری اتصال (connect) یا قطع اتصال (disconnect) است.
درواقع تمامی این موارد بهصورت خودکار و در زمان نیاز توسط راهانداز MongoDB فراخوانی و مدیریت میشوند. بهعبارت سادهتر، قطعه کد مورد بحث، تنها یک اتصال به شئ سرویسدهنده پایگاه داده برقرا کرده و از آن برای متصل شدن به پایگاه داده retrogames استفاده میکند.
پس از آنکه اتصال به پایگاهداده retrogames برقرار شد، برنامه با استفاده از سه خط کد زیر، به شی مجموعه games متصل شده، با استفاده از سازنده پرسوجوی نوعدار (Typed Query) یعنی Query<Game>، پرسوجویی را میسازد که در زمان اجرا توسط راهانداز MongoDB به پرسوجوی قابل اجرا توسط مدیر پایگاه داده ترجمه میشود و در نهایت فراخوانی تابع FindOne با پارامتر نوعدار gameQuery اطلاعات مورد نظر را از پایگاه داده retrogames و مجموعه games باز میگرداند.
var games = db.GetCollection<Game>("games"); var gameQuery = Query<Game>.EQ(g => g.Name, "Invaders 2013"); var foundGame = games.FindOne(gameQuery);
از ویژگیهای پرسوجوساز نوعدار (Typed Query Builder) میتوان به نوع«امن بودن» و «نوع آگاه بودن» همزمان آن اشاره داشت که این امکان را فراهم میآورد تا از صفات تعریف شده در کلاسهای دامنه برای ساخت پرسوجو استفاده کنیم. البته این امکان وجود دارد که از پرسوجو ساز بینوع نیز استفاده کرد که در آن ناچار به استفاده از نام حقیقی فیلدهای موجود در مجموعهها خواهیم بود.
برای مثال، خطوط زیر از پرسوجو ساز بینوع برای دستیابی به دادههای مورد نیاز بهره بردهاند و نتیجه یکسانی با پرسوجوی پیشین بهدست میدهند:
var games = db.GetCollection<Game>("games"); var gameQuery = Query.EQ("name", "Invaders 2013"); var foundGame = games.FindOne(gameQuery);
با توجه به نوع کارکرد پرسوجو ساز بینوع، طبیعی است که احتمال بروز خطا در استفاده از آن بیشتری باشد.
اعمال پایه در راهانداز سي شارپ پایگاه داده MongoDB
چه از پرسوجو ساز با نوع استفاده کنید و چه از گونه بینوع آن، در هر دو حالت، به ازای تمامی دستوراتی که در زبان پرسوجوی MongoDB تعریف شده است، عملگر و تابعی در راهانداز ارائه شده برای کار با این پایگاه داده در سي شارپ تعبیه شده است. میتوانید بهسادگی و با مراجعه به Object Browser موجود در محیط ویژوال استودیو (قابل دسترسی از منوی VIEW یا کلید میانبر Ctrl+W,J -) و انتخاب MongoDB.Driver.Builders.Query<TDocument>، لیست تمامی عملگرهای موجود را مشاهده کرده و با انتخاب هر یک، کارکرد و نحوه استفاده از آن را مشاهده کنید.
برای مثال تابع GTE<TMember> معادل دستور $gte در پوسته دستوری MongoDB بوده و بررسی میکند که آیا مقدار یک عنصر نامدار، بزرگتر از، یا برابر با یک مقدار مشخص است یا خیر(شکل۱۱).
شکل ۱۱
بهعنوان مثالی دیگر، خطوط کد زیر را در نظر بگیرید. این خطوط که قابل اجرا در پوسته دستوری پایگاه داده هستند، ۵۰ بازی اول بازی شده که زمان عرضه آنها برابر با، یا بزرگتر از ۱ ژانویه ۲۰۱۳ را بهعنوان نتیجه (مرتب شده بر اساس تاریخ عرضه نزولی) باز میگردانند.
db.games.find( { "release_date" : { "$gte" : ISODate("2013-01-01T00:00:00Z") }, "played" : true } ) .limit(50).sort( { "release_date" : -1 } );
حال اگر بخواهیم همین پرسوجو را با استفاده از پرسوجو ساز نوعدار سي شارپ تهیه کنیم، به خطوط کد زیر خواهیم رسید. توجه کنید که توابع SetSortOrder و SetLimit در واقع دستورات sort و limit موجود در زبان پرسوجوی MongoDB هستند که پس از FindAs و برای ترتیب بخشیدن به نتیجه مورد نیاز استفاده شدهاند. در نهایت، یک حلقه foreach مقادیر برخی صفات موجود در نمونه Game را در کنسول برنامه بهنمایش در میآورد.
var playedGamesQuery = Query.And( Query<Game>.GTE(g => g.ReleaseDate, new DateTime(2013, 01, 01)), Query<Game>.EQ(g => g.Played, true) ); var playedGames = games .FindAs<Game>(playedGamesQuery) .SetSortOrder(SortBy<Game>.Descending(g => g.ReleaseDate)) .SetLimit(50); foreach (var playedGame in playedGames) { //// Do something with each played game Console.WriteLine("Release date: {0}. Name: '{1}'", playedGame.ReleaseDate, playedGame.Name); }
از نسخه ۱٫۴ راهانداز سي شارپ برای تعامل با پایگاه دادههای MongoDB شرکت ۱۰Gen، پشتیبانی از LINQ را اعلام و از آن زمان تاکنون در هر نسخه نسبت به ارتقای آن تلاش قابل ارجی کرده است. درواقع میتوان LINQ را راه نجاتی برای کسانی دانست که نمیخواهند زمان زیادی را برای یادگیری زبان پرسوجو نویسی MongoDB کنند.
البته ذکر این نکته از اهمیت بسیار بالایی برخوردار است که در حال حاضر تمامی پرسوجوهای نوشته شده به زبان LINQ قابل ترجمه به پرسوجوهای بومی MongoDB نیستند و در نتیجه چنانچه توسعهدهندهای قصد بهرهبرداری حداکثری از توانمندیهای MongoDB را داشته باشد، چارهای جز کار با زبان بومی و خاص این پایگاه داده را ندارد. بهعبارت دقیقتر، ما میتوانیم در زمان نیاز، پرسوجوهای MongoDB را به یک پرسوجوی LINQ تزریق کنیم.
برای مثال، خطوط کد زیر را در نظر بگیرید. این خطوط یک اتصال به شئ مجموعه games برقرار کرده و سپس با فراخوانی متد AsQueryable، یک پرسوجوی LINQ را راه میسازند که نخستین نمونه از مستند Game را که فیلد اطلاعاتی name آن برابر با “Invader 2013” باشد، باز میگرداند. برای دستیابی به این نتیجه، پرسوجوی LINQ یک درخت عبارت (Expression Tree) تشکیل میدهد و راهانداز MongoDB در زمان اجرای برنامه، این درخت عبارت را به پرسوجوی قابل اجرا توسط مدیر پایگاه داده تبدیل میکند.
var games = db.GetCollection<Game>("games"); var foundGame = games.AsQueryable().FirstOrDefault(g => g.Name == "Invaders 2013");
قطعه کد بالا، معادل کد استفاده شده در پرسوجو ساز نوعدار ذیل است که پیشتر برای دریافت نتیجهای مشابه مورد استفاده قرار گرفت. توجه کنید که در این پرسوجو از متد FindOne استفاده شده است، حال آنکه در LINQ این تابع جای خود را به FirstOrDefault داده است که در صورت نتیجه نیافتن مورد نظر مقدار null را بهعنوان نتیجه باز میگرداند:
var games = db.GetCollection<Game>("games"); var gameQuery = Query<Game>.EQ(g => g.Name, "Invaders 2013"); var foundGame = games.FindOne(gameQuery);
همچنین برای دریافت ۵۰ بازی با تاریخ عرضه برابر با، یا بزرگتر از یکم ژانویه ۲۰۱۳ میتوان از پرسوجوی LINQ زیر بهره برد.
var dateTimeFrom = new DateTime(2013, 01, 01); var playedGames = games.AsQueryable() .Where(g => (g.ReleaseDate >= dateTimeFrom) && g.Played) .OrderByDescending(g => g.ReleaseDate) .Take(50);
این قطعه کد را همچنین میتوان به شکل زیر مورد استفاده قرار داد:
var playedGames = (from game in games.AsQueryable() where (game.ReleaseDate >= dateTimeFrom) && game.Played orderby game.ReleaseDate descending select game).Take(50);
توجه کنید که اگر علاقهای به کار با کلاسهای دامنه که در بخشهای پیشین ایجاد شد ندارید، میتوانید از نمونههای BsonDocument استفاده کرده و از رشتهها برای مشخص کردن نام فیلدها در پرسوجوهای خود بهره ببرید.
برای این منظور و برای دریافت مقادیر یک فیلد اطلاعاتی متد GetElement را با نام فیلد اطلاعاتی مورد نظر بهعنوان پارامتر فراخوانی کنید. هرگز فراموش نکنید که یک BsonDocument متشکل از جفتهای فیلد«مقدار» است و مقدار یک فیلد ممکن است خود یک مستند دیگر باشد. قطعه کد زیر نحوه استفاده از BsonDocument را نشان میدهد:
var dateTimeFrom = new DateTime(2013, 01, 01); var gamesBson = db.GetCollection("games"); var playedGamesQueryBson = Query.And( Query.GTE("release_date", dateTimeFrom), Query.EQ("played", true) ); var playedGamesBson = gamesBson .Find(playedGamesQueryBson) .SetSortOrder(SortBy.Descending("release_date")) .SetLimit(50); foreach (var playedGameBson in playedGamesBson) { //// Do something with each played game Console.WriteLine("Release date: {0}. Name: '{1}'", playedGameBson.GetElement("release_date").Value, playedGameBson.GetElement("name").Value); }
جمعبندی
ما در این مقاله ضمن معرفی یک ابزار گرافیکی موسوم به MongoVUE برای تعامل با پایگاه دادههای MongoDB، نحوه کارکرد آن را بهصورت مختصر توضیح داده و کارکردهای مورد نظر در این سری مقالهها را مورد ارزیابی قرار دادیم. همچنین با ساخت یک پروژه کنسول، نحوه استفاده از راهانداز سي شارپ را مورد کنکاش قرار داده و انواع روشهای موجود برای واکشی اطلاعات از پایگاه دادههای MongoDB را معرفی کردیم. با دراختیار داشتن این اطلاعات این توانایی در شما بهوجود آمده است تا اعمال بیشتری در MongoDB را در قالب پروژهای پیچیدهتر بیاموزید.