An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
EF Code First #8
ادامه بحث بررسی جزئیات نحوه نگاشت کلاسها به جداول، توسط EF Code first
استفاده از Viewهای SQL Server در EF Code first
از Viewها عموما همانند یک جدول فقط خواندنی استفاده میشود. بنابراین نحوه نگاشت اطلاعات یک کلاس به یک View دقیقا همانند نحوه نگاشت اطلاعات یک کلاس به یک جدول است و تمام نکاتی که تا کنون بررسی شدند، در اینجا نیز صادق است. اما ...
الف) بر اساس تنظیمات توکار EF Code first، نام مفرد کلاسها، حین نگاشت به جداول، تبدیل به اسم جمع میشوند. بنابراین اگر View ما در سمت بانک اطلاعاتی چنین تعریفی دارد:
Create VIEW EmployeesView
AS
SELECT id,
FirstName
FROM Employees
در سمت کدهای برنامه نیاز است به این شکل تعریف شود:
using System.ComponentModel.DataAnnotations;
namespace EF_Sample04.Models
{
[Table("EmployeesView")]
public class EmployeesView
{
public int Id { set; get; }
public string FirstName { set; get; }
}
}
در اینجا به کمک ویژگی Table، نام دقیق این View را در بانک اطلاعاتی مشخص کردهایم. به این ترتیب تنظیمات توکار EF بازنویسی خواهد شد و دیگر به دنبال EmployeesViews نخواهد گشت؛ یا جدول متناظر با آنرا به صورت خودکار ایجاد نخواهد کرد.
ب) View شما نیاز است دارای یک فیلد Primary key نیز باشد.
ج) اگر از مهاجرت خودکار توسط MigrateDatabaseToLatestVersion استفاده کنیم، پیغام خطای زیر را دریافت خواهیم کرد:
There is already an object named 'EmployeesView' in the database.
علت این است که هنوز جدول dbo.__MigrationHistory از وجود آن مطلع نشده است، زیرا یک View، خارج از برنامه و در سمت بانک اطلاعاتی اضافه میشود.
برای حل این مشکل میتوان همانطور که در قسمتهای قبل نیز عنوان شد، EF را طوری تنظیم کرد تا کاری با بانک اطلاعاتی نداشته باشد:
Database.SetInitializer<Sample04Context>(null);
به این ترتیب EmployeesView در همین لحظه قابل استفاده است.
و یا به حالت امن مهاجرت دستی سوئیچ کنید:
Add-Migration Init -IgnoreChanges
Update-Database
پارامتر IgnoreChanges سبب میشود تا متدهای Up و Down کلاس مهاجرت تولید شده، خالی باشد. یعنی زمانیکه دستور Update-Database انجام میشود، نه Viewایی دراپ خواهد شد و نه جدول اضافهای ایجاد میگردد. فقط جدول dbo.__MigrationHistory به روز میشود که هدف اصلی ما نیز همین است.
همچنین در این حالت کنترل کاملی بر روی کلاسهای Up و Down وجود دارد. میتوان CreateTable اضافی را به سادگی از این کلاسها حذف کرد.
ضمن اینکه باید دقت داشت یکی از اهداف کار با ORMs، فراهم شدن امکان استفاده از بانکهای اطلاعاتی مختلف، بدون اعمال تغییری در کدهای برنامه میباشد (فقط تغییر کانکشن استرینگ، به علاوه تعیین Provider جدید، باید جهت این مهاجرت کفایت کند). بنابراین اگر از View استفاده میکنید، این برنامه به SQL Server گره خواهد خورد و دیگر از سایر بانکهای اطلاعاتی که از این مفهوم پشتیبانی نمیکنند، نمیتوان به سادگی استفاده کرد.
استفاده از فیلدهای XML اس کیوال سرور
در حال حاضر پشتیبانی توکاری توسط EF Code first از فیلدهای ویژه XML اس کیوال سرور وجود ندارد؛ اما استفاده از آنها با رعایت چند نکته ساده، به نحو زیر است:
using System.ComponentModel.DataAnnotations;
using System.Xml.Linq;
namespace EF_Sample04.Models
{
public class MyXMLTable
{
public int Id { get; set; }
[Column(TypeName = "xml")]
public string XmlValue { get; set; }
[NotMapped]
public XElement XmlValueWrapper
{
get { return XElement.Parse(XmlValue); }
set { XmlValue = value.ToString(); }
}
}
}
در اینجا توسط TypeName ویژگی Column، نوع توکار xml مشخص شده است. این فیلد در طرف کدهای کلاسهای برنامه، به صورت string تعریف میشود. سپس اگر نیاز بود به این خاصیت توسط LINQ to XML دسترسی یافت، میتوان یک فیلد محاسباتی را همانند خاصیت XmlValueWrapper فوق تعریف کرد. نکته دیگری را که باید به آن دقت داشت، استفاده از ویژگی NotMapped میباشد، تا EF سعی نکند خاصیتی از نوع XElement را (یک CLR Property) به بانک اطلاعاتی نگاشت کند.
و همچنین اگر علاقمند هستید که این قابلیت به صورت توکار اضافه شود، میتوانید اینجا رای دهید!
نحوه تعریف Composite keys در EF Code first
کلاس نوع فعالیت زیر را درنظر بگیرید:
namespace EF_Sample04.Models
{
public class ActivityType
{
public int UserId { set; get; }
public int ActivityID { get; set; }
}
}
در جدول متناظر با این کلاس، نباید دو رکورد تکراری حاوی شماره کاربری و شماره فعالیت یکسانی باهم وجود داشته باشند. بنابراین بهتر است بر روی این دو فیلد، یک کلید ترکیبی تعریف کرد:
using System.Data.Entity.ModelConfiguration;
using EF_Sample04.Models;
namespace EF_Sample04.Mappings
{
public class ActivityTypeConfig : EntityTypeConfiguration<ActivityType>
{
public ActivityTypeConfig()
{
this.HasKey(x => new { x.ActivityID, x.UserId });
}
}
}
در اینجا نحوه معرفی بیش از یک کلید را در متد HasKey ملاحظه میکنید.
یک نکته:
اینبار اگر سعی کنیم مثلا از متد db.ActivityTypes.Find با یک پارامتر استفاده کنیم، پیغام خطای «The number of primary key values passed must match number of primary key values defined on the entity» را دریافت خواهیم کرد. برای رفع آن باید هر دو کلید، در این متد قید شوند:
var activity1 = db.ActivityTypes.Find(4, 1);
ترتیب آنها هم بر اساس ترتیبی که در کلاس ActivityTypeConfig، ذکر شده است، مشخص میگردد. بنابراین در این مثال، اولین پارامتر متد Find، به ActivityID اشاره میکند و دومین پارامتر به UserId.
بررسی نحوه تعریف نگاشت جداول خود ارجاع دهنده (Self Referencing Entity)
سناریوهای کاربردی بسیاری را جهت جداول خود ارجاع دهنده میتوان متصور شد و عموما تمام آنها برای مدل سازی اطلاعات چند سطحی کاربرد دارند. برای مثال یک کارمند را درنظر بگیرید. مدیر این شخص هم یک کارمند است. مسئول این مدیر هم یک کارمند است و الی آخر. نمونه دیگر آن، طراحی منوهای چند سطحی هستند و یا یک مشتری را درنظر بگیرید. مشتری دیگری که توسط این مشتری معرفی شده است نیز یک مشتری است. این مشتری نیز میتواند یک مشتری دیگر را به شما معرفی کند و این سلسله مراتب به همین ترتیب میتواند ادامه پیدا کند.
در طراحی بانکهای اطلاعاتی، برای ایجاد یک چنین جداولی، یک کلید خارجی را که به کلید اصلی همان جدول اشاره میکند، ایجاد خواهند کرد؛ اما در EF Code first چطور؟
using System.Collections.Generic;
namespace EF_Sample04.Models
{
public class Employee
{
public int Id { set; get; }
public string FirstName { get; set; }
public string LastName { get; set; }
//public int? ManagerID { get; set; }
public virtual Employee Manager { get; set; }
}
}
در این کلاس، خاصیت Manager دارای ارجاعی است به همان کلاس؛ یعنی یک کارمند میتواند مسئول کارمند دیگری باشد. برای تعریف نگاشت این کلاس به بانک اطلاعاتی میتوان از روش زیر استفاده کرد:
using System.Data.Entity.ModelConfiguration;
using EF_Sample04.Models;
namespace EF_Sample04.Mappings
{
public class EmployeeConfig : EntityTypeConfiguration<Employee>
{
public EmployeeConfig()
{
this.HasOptional(x => x.Manager)
.WithMany()
//.HasForeignKey(x => x.ManagerID)
.WillCascadeOnDelete(false);
}
}
}
با توجه به اینکه یک کارمند میتواند مسئولی نداشته باشد (خودش مدیر ارشد است)، به کمک متد HasOptional مشخص کردهایم که فیلد Manager_Id را که میخواهی به این کلاس اضافه کنی باید نال پذیر باشد. توسط متد WithMany طرف دیگر رابطه مشخص شده است.
اگر نیاز بود فیلد Manager_Id اضافه شده نام دیگری داشته باشد، یک خاصیت nullable مانند ManagerID را که در کلاس Employee مشاهده میکنید، اضافه نمائید. سپس در طرف تعاریف نگاشتها به کمک متد HasForeignKey، باید صریحا عنوان کرد که این خاصیت، همان کلید خارجی است. از این نکته در سایر حالات تعاریف نگاشتها نیز میتوان استفاده کرد، خصوصا اگر از یک بانک اطلاعاتی موجود قرار است استفاده شود و از نامهای دیگری بجز نامهای پیش فرض EF استفاده کرده است.
مثالهای این سری رو از این آدرس هم میتونید دریافت کنید: (^)
مدیریت رجیستری در #C
رجیستری یک پایگاه دادهی سیستمی است که برنامهها، اجزای سیستم و اطلاعات پیکربندی در آن ذخیره و بازیابی میشود. دادههای ذخیره شده در رجیستری مطابق با نسخه ویندوز فرق میکنند. نرمافزارها برای بازیابی، تغییر و پاک کردن رجیستری از API های مختلفی استفاده میکنند. خوشبختانه .NET نیز امکانات لازم برای مدیریت رجیستری را فراهم کرده است.
در صورت رخداد خطا در رجیستری، امکان خراب شدن ویندوز وجود دارد در نتیجه با احتیاط عمل کنید و قبل از هر کاری از رجیستری پشتیبان تهیه نمایید. قبل از شروع به کدنویسی قدری با ساختار رجیستری آشنا شویم تا در ادامه قادر به درک مفاهیم باشیم.
ساختار رجیستری
رجیستری اطلاعات را در ساختار درختی نگاه میدارد. هر گره در درخت، یک کلید ( key ) نامیده میشود. هر کلید میتواند شامل چندین زیرکلید ( subkey ) و چندین مقدار ( value ) باشد. در برخی موارد، وجود یک کلید تمام اطلاعاتی است که نرم افزار بدان نیاز دارد و در برخی موارد، برنامه کلید را باز کرده و مقادیر مربوط به آن کلید را میخواند. یک کلید میتواند هر تعداد مقدار داشته باشد و مقادیر به هر شکلی میتوانند باشند. هر کلید شامل یک یا چند کاراکتر است. نام کلیدها نمیتوانند کاراکتر “\” را داشته باشند. نام هر زیرکلید یکتاست و وابسته به کلیدی است که در سلسله مراتب، بلافاصله بالای آن میآید. نام کلیدها باید انگلیسی باشند اما مقادیر را به هر زبانی میتوان نوشت. در زیر یک نمونه از ساختار رجیستری را مشاهده میکنید که در نرمافزار registry editor به نمایش در آمده است.
هر کدام از درختهای زیر my computer یک کلید است. HKEY_LOCAL_MACHINE دارای زیرکلیدهایی مثل HARDWARE ، SAM و SECURITY است. هر مقدار شامل یک اسم، نوع و دادههای درون آن است. برای مثال MaxObjectNumber از مقادیر زیرکلید HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\VIDEO است. دادههای درون هر مقدار میتواند از انواع باینری، رشتهای و عددی باشد؛ برای مثال MaxObjectNumber یک عدد ۳۲ بیتی است.
محدودیتهای فنی برای نوع و اندازهی اطلاعاتی که در رجیستری ذخیره میگردد، وجود دارد. برنامهها باید اطلاعات اولیه و پیکربندی را در رجیستری نگه دارند وسایر دادهها را در جای دیگر ذخیره کنند. معمولا دادههای بیشتر از یک یا دو کیلوبایت باید در یک فایل ذخیره شوند و با استفاده از یک کلید در رجیستری به آن فایل رجوع کرد. برای حفظ فضای ذخیره سازی باید دادههای شبیه به هم در یک ساختار جمع آوری گردند و ساختار را به عنوان یک مقدار ذخیره کرد؛ به جای آن که هر عضو ساختار را به عنوان یک کلید ذخیره کرد. ذخیره سازی اطلاعات به صورت باینری این امکان را میدهد که اطلاعات را در یک مقدار ذخیره کنید.
اطلاعات رجیستری در پیج فایل ( Page File ) ذخیره میشوند. پیج فایل ناحیهای از حافظه RAM است که میتواند در زمانی که استفاده نمیشود به Hard منتقل شود. اندازهی پیج فایل به وسیلهی مقدار PagedPoolSize در کلید HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management مطابق با جدول زیر تنظیم میگردد.
مقدار | توضیحات |
0×00000000 | سیستم یک مقدار بهینه را تعیین میکند |
0x1–0x20000000 | یک اندازه مشخص برحسب بایت که در این بازه باشد |
0xFFFFFFFF | سیستم بیشترین مقدار ممکن را تشخیص میدهد |
کلیدهای از پیش تعریف شده
یک برنامه قبل از آن که اطلاعاتی را در رجیستری درج کند باید یک کلید را باز کند. برای باز کردن یک کلید میتوان از سایر کلیدهایی که باز هستند، استفاده کرد. سیستم کلیدهایی را از پیش تعریف کرده که همیشه باز هستند. در ادامه کلیدهای از پیش تعریف شده را قدری بررسی میکنیم.
HKEY_CLASSES_ROOT
زیرشاخههای این کلید، انواع اسناد و خصوصیات مربوط به آنها را مشخص میکنند. این شاخه نباید در یک سرویس یا برنامهای که کاربران متعدد دارد، مورد استفاده قرار گیرد.
HKEY_CURRENT_USER
زیرشاخههای این کلید، تنظیمات مربوط به کاربر جاری را مشخص میکنند. این تنظیمات شامل متغیرهای محیطی، اطلاعات دربارهی برنامهها، رنگها، پرینترها، ارتباطات شبکه و تنظیمات برنامههاست. به طور مثال مایکروسافت اطلاعات مربوط به برنامههای خود را در کلید HKEY_CURRENT_USER\Software\Microsoft ذخیره میکند. هر کدام از برنامهها یک زیرکلید در کلید مزبور را به خود اختصاص دادهاند. این شاخه نیز نباید در یک سرویس یا برنامهای که کاربران متعدد دارد، مورد استفاده قرار گیرد.
HKEY_LOCAL_MACHINE
زیرشاخههای این کلید، وضعیت فیزیکی کامپیوتر را مشخص میکنند که شامل حافظهی سیستم، سختافزار و نرمافزارهای نصب شده بر روی سیستم، اطلاعات پیکربندی، تنظیمات ورود به سیستم، اطلاعات امنیتی شبکه و اطلاعات دیگر سیستم است.
HKEY_USERS
زیرشاخههای این کلید، پیکربندی کاربران پیش فرض، جدید، جاری سیستم و به طور کلی همهی کاربران را مشخص میکند.
HKEY_CURRENT_CONFIG
زیرشاخههای این کلید، اطلاعاتی درباره وضعیت سختافزار کامپیوتر در اختیار ما میگذارند. در واقع این کلید نام مستعاری برای کلید HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Hardware Profiles\Current است که در ویندوزهای قبل از ۳.۵۱ NT وجود نداشته است.
کندوهای رجیستری
یک کندو ( Hive ) یک گروه از کلیدها، زیرکلیدها و مقادیر در رجیستری است که یک مجموعه از فایلهای پشتیبان را به همراه دارد. در هنگام بوت ویندوز، اطلاعات از این فایلها استخراج میشوند. شما هم چنین میتوانید با استفاده از Import در منوی فایل registry editor به صورت دستی این کار را انجام دهید. زمانی که ویندوز را خاموش میکنید، اطلاعات کندوها در فایلهای پشتیبان نوشته میشوند. شما میتوانید این کار را به طور دستی با Export در منوی فایل registry editor نیز انجام دهید.
فایلهای پشتیبان همه کندوها به جز HKEY_CURRENT_USER در شاخهی Windows Root\System32\config قرار دارند. فایلهای پشتیبان HKEY_CURRENT_USER در شاخهی System Root\Documents and Settings\Username قرار دارند. پسوند فایلها در این شاخهها، نوع دادههایی که در بر دارند را نشان میدهند. در جدول زیر برخی کندوها و فایلهای پشتیبانشان آمده است.
کندوی رجیستری | فایلهای پشتیبان |
HKEY_CURRENT_CONFIG | System, System.alt, System.log, System.sav |
HKEY_CURRENT_USER | Ntuser.dat, Ntuser.dat.log |
HKEY_LOCAL_MACHINE\SAM | Sam, Sam.log, Sam.sav |
HKEY_LOCAL_MACHINE\Security | Security, Security.log, Security.sav |
HKEY_LOCAL_MACHINE\Software | Software, Software.log, Software.sav |
HKEY_LOCAL_MACHINE\System | System, System.alt, System.log, System.sav |
HKEY_USERS\.DEFAULT | Default, Default.log, Default.sav |
دسته بندی اطلاعات
قبل از قرار دادن اطلاعات در رجیستری باید آنها را به دو دسته اطلاعات کامپیوتر و اطلاعات کاربر تقسیم کرد. با این تقسیم بندی، چندین کاربر میتوانند از یک برنامه استفاده کنند و یا اطلاعات را بر روی شبکه قرار دهند. زمانی که یک برنامه نصب میشود، باید اطلاعات کامپیوتری خود را در شاخه فرضی HKEY_LOCAL_MACHINE\Software\MyCompany\MyProduct\1.0 به گونهای تعریف کند که نام شرکت، نام محصول و نسخه برنامه به خوبی مشخص گردند و هم چنین اطلاعات مربوط به کاربران را در شاخه فرضی HKEY_CURRENT_USER\Software\MyCompany\MyProduct\1.0 نگاه دارد.
باز کردن، ساختن و بستن کلیدها
قبل از آن که بتوانیم یک اطلاعات را در رجیستری درج کنیم، باید یک کلید بسازیم و یا یک کلید موجود را باز کنیم. یک برنامه همیشه به یک کلید به عنوان زیرکلیدی از یک کلید باز رجوع میکند. کلیدهای از پیش تعریف شده همیشه باز هستند.
کلاسهای تعریف شده برای کار با رجیستری در فضانام Microsoft.Win32 قرار دارند. کلاس Microsoft.Win32.Registry مربوط به کلاسهای از پیش تعریف شده و کلاس Microsoft.Win32.RegistryKey برای کار با رجیستری است. برای باز کردن یک کلید از متد RegistryKey.OpenSubKey استفاده میکنیم. به یاد داشته باشید که کلیدهای از پیش تعریف شده همیشه باز هستند و نیازی به باز کردن ندارند. برای ساختن یک کلید از متد RegistryKey.CreateSubKey استفاده میکنیم. دقت کنید زیرکلیدی که میخواهید بسازید، باید به یک کلید باز رجوع کند. برای خاتمه دسترسی به یک کلید، باید آن را ببندیم. برای بستن یک کلید از متد RegistryKey.Close استفاده میکنیم.
اکنون که با ساختار رجیستری و کلاسهای مربوطه در .NET برای کار با رجیستری آشنا شدیم، به کدنویسی میپردازیم.
ساختن یک زیرکلید جدید
برای ساختن یک زیرکلید جدید از متد RegistryKey.CreateSubKey به صورت زیر استفاده میکنیم.
public RegistryKey CreateSubKey( string subkey);
subkey نام و مسیر کلیدی که میخواهید بسازید را مشخص میکند که معمولا به فرم فرضی key name\Company Name\Application Name\version است. این متد یک زیرکلید را برمیگرداند و در صورت بروز خطا مقدار null را برمیگرداند و یک exception را فرا میخواند. خطا به دلایلی چون عدم داشتن مجوز، وجود نداشتن مسیر درخواستی و غیره رخ میدهد. برای بررسی exception ها میتوانید از بلوک try-catch استفاده کنید.
RegistryKey MyReg = Registry .CurrentUser.CreateSubKey( "SOFTWARE\\SomeCompany\\SomeApp\\SomeVer" );
برای دست یابی به کلیدهای از پیش تعریف شده از کلاس Registry مطابق جدول زیر استفاده میکنیم.
فیلد | کلید |
ClassesRoot | HKEY_CLASSES_ROOT |
CurrentUser | HKEY_CURRENT_USER |
LocalMachine | HKEY_LOCAL_MACHINE |
Users | HKEY_USERS |
CurrentConfig | HKEY_CURRENT_CONFIG |
باز کردن زیرکلید موجود
برای باز کردن یک زیرکلید موجود از متد RegistryKey.OpenSubKey به دو صورت استفاده میکنیم.
public RegistryKey OpenSubKey( string name); public RegistryKey OpenSubKey( string name, bool writable);
RegistryKey MyReg = Registry .CurrentUser.OpenSubKey( "SOFTWARE\\SomeCompany\\SomeApp\\SomeVer" , true );
مثال فوق کلید مشخص شده را در شاخهی HKEY_CURRENT_USER و در حالت ویرایش باز میکند.
خواندن اطلاعات از رجیستری
اگر یک شیء RegistryKey سالم داشته باشید میتوانید به مقادیر و اطلاعات درون مقادیر آن دسترسی داشته باشید. برای دست یابی به اطلاعات درون یک مقدار مشخص در کلید از متد RegistryKey.GetValue به دو صورت استفاده کنیم.
public object GetValue( string name); public object GetValue( string name, object defaultValue);
نوشتن اطلاعات در رجیستری
برای نوشتن اطلاعات در یک مقدار از متد RegistryKey.SetValue به صورت زیر استفاده میکنیم.
public void SetValue( string name, object value);
بستن یک کلید
زمانی که دیگر با کلید کاری ندارید و میخواهید تغییرات در رجیستری ثبت گردد باید فرآیندی به نام flushing را انجام دهید. برای انجام این کار به راحتی از متد RegistryKey.Close استفاده کنید.
RegistryKey MyReg = Registry .CurrentUser.CreateSubKey( "SOFTWARE\\SomeCompany\\SomeApp\\SomeVer" ); int nSomeVal = ( int )MyReg.GetValue( "SomeVal" , 0); MyReg.SetValue( "SomeValue" , nSomeVal + 1); MyReg.Close();
پاک کردن یک کلید
برای پاک کردن یک زیرکلید از متد RegistryKey.DeleteSubKey به دو صورت استفاده میکنیم.
public void DeleteSubKey( string subkey); public void DeleteSubKey( string subkey, bool throwOnMissingSubKey);
پاک کردن کل یک درخت
برای پاک کردن کل یک درخت با همهی کلیدهای فرزند و مقادیر آنها از متد RegistryKey.DeleteSubKeyTree به دو صورت استفاده میکنیم.
public void DeleteSubKeyTree( string subkey); public void DeleteSubKeyTree( string subkey, bool throwOnMissingSubKey);
پاک کردن یک مقدار
برای پاک کردن یک مقدار از متد RegistryKey.DeleteValue به دو صورت زیر استفاده میکنیم.
public void DeleteValue( string name); public void DeleteValue( string name, bool throwOnMissingValue);
لیست کردن زیرکلیدها
برای به دست آوردن یک لیست از همه زیرکلیدهای یک شیء RegistryKey از متد RegistryKey.GetSubKeyNames به صورت زیر استفاده میکنیم که یک آرایه رشتهای از نام زیرکلیدها را برمیگرداند.
public string [] GetSubKeyNames();
لیست کردن نام مقادیر
برای به دست آوردن یک لیست از همه مقادیری که در یک شیء RegistryKey وجود دارند از متد RegistryKey.GetValueNames به صورت زیر استفاده میکنیم که یک آرایه رشتهای از نام مقادیر را برمیگرداند.
public string [] GetSubKeyNames();
ثبت تغییرات به صورت دستی
برای ثبت تغییرات یا به اصطلاح فلاش کردن به صورت دستی میتوانید از متد RegistryKey.Flush به صورت زیر استفاده نمایید. زمانی که از RegistryKey.Close استفاده میکنید فرآیند فلاش کردن به طور اتوماتیک انجام میگیرد.
public void Flush();
MongoDB #7
- String: این نوع پرکاربردترین نوع داده برای ذخیره اطلاعات است. رشته در MongoDB باید بصورت یونیکد (utf-8) معتبر باشد.
- Integer: این نوع برای ذخیره کردن یک مقدار عددی استفاده میشود. Integer بسته به نوع سرور میتواند 32 یا 64 بیت باشد.
- Boolean: این نوع برای ذخیره کردن یک مقدار بولی (true/false) استفاده میشود.
- Double: این نوع برای مقادیر با ممیز شناور استفاده میشود.
- کلیدهای Min/Max: این نوع برای مقایسه یک مقدار با کمترین یا بیشترین عناصر BSON استفاده میشود.
- Array: این نوع برای ذخیره آرایهها یا لیست یا چندین مقدار در یک کلید استفاده میشود.
- Timestamp: این نوع میتواند برای ضبط زمان تغییرات (مثلا وقتی یک سند درج میشود یا تغییر میکند) مفید باشد.
- Object: این نوع برای سندهای توکار استفاده میشود.
- Null: این نوع برای ذخیره مقدار تهی (Null) استفاده میشود.
- Symbol: این نوع بطور یکسان برای ذخیره رشته استفاده میشود، اما عموما برای زبانهایی که از یک نوع نماد (Symbol) مشخص استفاده میکنند تعبیه شده است.
- Date: این نوع برای ذخیره تاریخ یا زمان جاری به فرمت زمان در یونیکس (UNIX) استفاده میشود. با ساخت یک شی از نوع Date و ارسال روز، ماه و سال به آن میتوانید تاریخ مشخص خود را داشته باشید.
- Object ID: ای نوع برای ذخیره سازی شناسه سند استفاه میشود.
- Binary Data: این نوع برای ذخیره سازی داده باینری استفاده میشود.
- Code: این نوع برای ذخیره سازی کد جاوا اسکریپت داخل سند استفاده میشود.
- Regular Expression: این نوع برای ذخیره سازی عبارت باقاعده استفاده میشود.
درج سند در MongoDB
>db.COLLECTION_NAME.insert(document)
>db.mycol.insert({ _id: ObjectId(7df78ad8902c), title: 'MongoDB Overview', description: 'MongoDB is no sql database', by: 'tutorials point', url: 'http://www.tutorialspoint.com', tags: ['mongodb', 'database', 'NoSQL'], likes: 100 })
>db.post.insert([ { title: 'MongoDB Overview', description: 'MongoDB is no sql database', by: 'tutorials point', url: 'http://www.tutorialspoint.com', tags: ['mongodb', 'database', 'NoSQL'], likes: 100 }, { title: 'NoSQL Database', description: 'NoSQL database doesn't have tables', by: 'tutorials point', url: 'http://www.tutorialspoint.com', tags: ['mongodb', 'database', 'NoSQL'], likes: 20, comments: [ { user:'user1', message: 'My first comment', dateCreated: new Date(2013,11,10,2,35), like: 0 } ] } ])
public class CustomNormalizer : ILookupNormalizer { public string NormalizeName(string key) { key = Normalize(key); key = key.ApplyCorrectYeKe() .RemoveDiacritics() .CleanUnderLines() .RemovePunctuation(); key = key.Trim().Replace(" ", ""); return key; } public string NormalizeEmail(string key) { key = Normalize(key); key = fixGmailDots(key); return key; } public string Normalize(string key) { if (string.IsNullOrWhiteSpace(key)) { return null; } key = key.Trim(); key = key.ToUpperInvariant(); return key; } private static string fixGmailDots(string email) { email = email.ToLowerInvariant().Trim(); var emailParts = email.Split('@'); var name = emailParts[0].Replace(".", string.Empty); var plusIndex = name.IndexOf("+", StringComparison.OrdinalIgnoreCase); if (plusIndex != -1) { name = name.Substring(0, plusIndex); } var emailDomain = emailParts[1]; emailDomain = emailDomain.Replace("googlemail.com", "gmail.com"); string[] domainsAllowedDots = { "gmail.com", "facebook.com" }; var isFromDomainsAllowedDots = domainsAllowedDots.Any(domain => emailDomain.Equals(domain)); return !isFromDomainsAllowedDots ? email : string.Format("{0}@{1}", name, emailDomain); } }
#region Assembly Microsoft.Extensions.Identity.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 // C:\Users\kazemi\.nuget\packages\microsoft.extensions.identity.core\3.0.0-preview-18579-0056\lib\netstandard2.0\Microsoft.Extensions.Identity.Core.dll #endregion namespace Microsoft.AspNetCore.Identity { // // Summary: // Provides an abstraction for normalizing keys for lookup purposes. public interface ILookupNormalizer { // // Summary: // Returns a normalized representation of the specified key. // // Parameters: // key: // The key to normalize. // // Returns: // A normalized representation of the specified key. string Normalize(string key); } }
چون سیستم کش لایه دوم بر اساس پارامتر آخر عمل میکنه باعث میشه که دیتا به ازای پارامترهای مختلف یک داده خاص فقط از کش بازیابی کنه چون باعث میشه کلید یکسان برای همه ثبت بشه مثلا در دستور زیر
public Article GetById(int id) { return articles .Where(e => e.Id == id) .Include(x => x.Category) .Include(x => x.Tags) //پارامتر در نظر گرفته شده برای تولید کلید کش .Cacheable(x => x.FirstOrDefault()); }
در صورتی که دستور به شکل زیر اصلاح بشه این مشکل رفع میشه چون در اون صورت Id مقاله به عنوان پارامتر در نظر گرفته میشه
public Article GetById(int id) { return articles .Include(x => x.Category) .Include(x => x.Tags) .Where(e => e.Id == id) //پارامتر در نظر گرفته شده برای تولید کلید کش .Cacheable(x => x.FirstOrDefault()); }
var collection = ['a', 1, /3/, {}];
collection[0];
collection.length
var numbers = [1, 2, 3]; _.each(numbers, function (num) { write(num); });
var ary = [1, 5, 10]; var match = ary.find(item => item > 8);
var match = ary.findIndex(item => item > 8);
ary.find('a'); // خروجی ["a", "a", "a"]
var ary = [1, 5, 10, 5, 6]; ary.fill('a', 2, 3)
// خروجی [ 1, 5, "a", 5, 6 ]
[1, 2, 3, 4, 5].copyWithin(0, 3);
// [4, 5, 3, 4, 5]
[1, 2, 3, 4, 5].copyWithin(0, 3, 4); // خروجی // [4, 2, 3, 4, 5]
var ary = new Array(1, 2);
var ary = new Array(1, 2, 3);
var ary = new Array(3);
var Ofary = Array.of(3);
var set = new Set(); set.add(1); set.add(2); set.add(3); console.log(set.size); // logs 3
var set = new Set([1, 2, 3]); console.log(set.size); // logs 3
var set = new Set(); set.has(1); // false set.add(1); set.has(1); // true set.clear(); set.has(1); // false set.add(1); set.add(2); set.size; // 2 set.delete(2); set.size; // 1
var set = new Set(); set.add('Vahid'); set.add('Sirwan'); var i = 0; set.forEach(item => i++); console.log(i);
var set = new Set(); set.add('Vahid'); set.add('Sirwan'); var i = 0; for(let item of set) { i++; } console.log(i);
var set = new Set(); set.add("Sirwan"); set.add(1); set.add("Afifi"); var setIter = set.entries(); console.log(setIter.next().value); // ["Sirwan", "Sirwan"] console.log(setIter.next().value); // [1, 1] console.log(setIter.next().value); // ["Afifi", "Afifi"]
var map = new Map(); map.set('name', 'Sirwan'); map.get('name'); // Sirwan
var map = new Map([['name', 'Sirwan'], ['age', 27]]); map.has('age'); // true map.get('age'); // 27 map.get('name'); // Sirwan
var map = new Map(); map.set(1, true); map.has("1"); // false map.set("1", true); map.has("1"); // true
var map = new Map([['name', 'Sirwan'], ['age', 27]]); var i = 0; map.forEach(function (value, key) { i++; }); console.log(i); // log 2
for (var [key, value] of map) { i++; }
var newArray = new Array(); newArray["name"] = "Sirwan"; newArray["lastName"] = "Afifi";
let user1 = { name: "Vahid" }; let user2 = { name: "Sirwan" }; let result = {}; result[user1] = 10; result[user2] = 20; console.log(result[user1]); // logs 20 console.log(result[user2]); // logs 20
result["[object object]"] = 10; result["[object object]"] = 20; console.log(result["[object object]"]); // logs 20 console.log(result["[object object]"]); // logs 20
- WeakMap و WeakSet فاقد پراپرتیهای size, entries, values و متد foreach هستند.
- WeakMap همچنین فاقد keys است.
یک کاراکتر کنترلی، یک نقطه کدی است که به وسیله علائم نوشتاری قابل نمایش نباشد. مانند Backspace
تعریف بالا به ما میگوید که در یک متن نوشتاری، به غیر از کد حروفی که مشاهده میکنیم، کدهای دیگری هم هستن که قابل نمایش نیستند ولی بین متون وجود دارند. شاید شما تعدادی از آنها را بشناسید مثل کدهای 10 و 13 برای خط بعد و اول سطر که به line feed و carriage return معروف هستند. در این نوشتار قصد داریم با تعدادی از آنها آشنا شویم.
قبل از آغاز این نوشتار به شما توصیه میکنم یک نگاه اجمالی هم که شده بر نوشتار «داستانی از unicode» داشته باشید تا اطلاعات تکمیلیتری از این نوشتار به دست آورید. مبحث کلیدهای کنترلی از زمانی آغاز شد که کدهای اسکی ایجاد شدند و به دو دستهی c0 و c1 تقسیم شدند. خود کدهای اسکی هم بر اساس کدهای تلگراف ایجاد شدند و بسیاری از کلیدهای کنترلی هم از آنجا به استاندارد اسکی پیوستند و برای ارتباط و کنترل دستگاههایی چون چاپگرها و تهیه اطلاعات متا در مورد طلاعاتی که قرار بود در نوار مغناطیسی ذخیره شوند به کار رفتند. به عنوان نمونه کد 10 به عنوان line feed در چاپگر، یک خط کاغذ را به سمت داخل میکشید و کد 13 هم باعث میشد چاپگر به ابتدای سطر بازگردد. البته بیشتر این کاراکترها در پردازش متون به خصوص امروزه استفاده نمیشوند و فقط یک سری از آنها رایج هستند؛ مثل دو موردی که در بالا و در همین خط به آنها اشاره شد.
دستهی c0 از کد 0 آغاز شده و تا کد 31 ادامه مییابد. دو کد بعدی که کدهای Space و DEL هستند در هیچ گروهی قرار نمیگیرند. گروه c1 از کدهای 128 آغاز شده و تا 159 ادامه مییابند که جدول این گروهها و کلیدها کنترلی را میتوانید مشاهده کنید. برای مثال اولین کلید کنترلی که کد آن 0 است به نام نال است که در قدیم هم برای بستن رشتهها در زبان سی از آن استفاده میکردیم.
هر چند به مرور زمان هم تعدادی از همین کلیدهای کنترلی کاربرد خود را از دست دادند و برای آنها شکلک هایی چون خنده، قلب، نت موسیقی و ... را قرار دادند ولی گاهی اوقات برنامه نویسها هنوز در برنامههای خود از کد آنها برای کارهایی چون انجام عملیات بیتی استفاده میکنند.
استفادههای C0
کلیدهای کنترلی این دسته بیشتر برای منظم ساختن متنهای ساده و همچنین ایجاد ارتباط در پروتکل ارتباطی و دستگاههای مختلف به کار میرفت؛ ارسال فرمانهایی چون آغاز و توقف کار یا انجام عملی خاص توسط هر یک از این کلیدها صورت میگرفت. دستگاههایی چون کارت پانچها، ماشین تایپ و موارد مشابه، از این نوع هستند. با اینکه عمر این دستگاه به سر آمد ولی کلیدهای کنترلی جان سالم به در بردند.
استفادههای C1
این دسته در اواخر سال 1970 اضافه شدند و بیشتر برای ارتباط با چاپگر و صفحهی نمایش به کار میرفتند؛ مثل پیمایشهای افقی و عمودی، تعریف ناحیهای برای پر کردن فرم و Line-Break و کلیدهای انتقالی (شیفت) برای پشتیبانی از کلیدهای کنترلی و قابل چاپ بیشتر. 2 تا از کلیدها هم برای استفادهی خصوصی برنامه نویس کنار گذاشته شدند و 4 تا هم رزرو شده برای استفادهی آینده، تا بعدا استانداردسازی شوند.
کلیدهای کنترلی در سی شارپ
بسیاری از ما از علامت \ در کدهایمان برای قرار دادن کلیدهای کنترلی استفاده میکنیم مثل r\n\ که ترکیب دو کد CR و LF است.
برای شناسایی یک کلید کنترلی در سی شارپ از متد ایستای Char.IsControl استفاده مینماییم. کد زیر در مجموعهی MSDN برای نشان دادن قابلیت این متد نوشته شده است که در طی یک حلقه رنجی از کد پوینتها را بررسی کرده و نتیجه را به صورت شش ستونی در کنسول نمایش میدهد. یا کد مشابه دیگر که بر اساس دسیمال نمایش میدهد.
using System; public class ControlChars { public static void Main() { int charsWritten = 0; for (int ctr = 0x00; ctr <= 0xFFFF; ctr++) { char ch = Convert.ToChar(ctr); if (char.IsControl(ch)) { Console.Write(@"\U{0:X4} ", ctr); charsWritten++; if (charsWritten % 6 == 0) Console.WriteLine(); } } } } // The example displays the following output to the console: // \U0000 \U0001 \U0002 \U0003 \U0004 \U0005 // \U0006 \U0007 \U0008 \U0009 \U000A \U000B // \U000C \U000D \U000E \U000F \U0010 \U0011 // \U0012 \U0013 \U0014 \U0015 \U0016 \U0017 // \U0018 \U0019 \U001A \U001B \U001C \U001D // \U001E \U001F \U007F \U0080 \U0081 \U0082 // \U0083 \U0084 \U0085 \U0086 \U0087 \U0088 // \U0089 \U008A \U008B \U008C \U008D \U008E // \U008F \U0090 \U0091 \U0092 \U0093 \U0094 // \U0095 \U0096 \U0097 \U0098 \U0099 \U009A // \U009B \U009C \U009D \U009E \U009F
آیا هنوز برنامه نویسها از کلیدهای کنترلی استفاده میکنند؟
این سوال بستگی به برنامهای دارد که شما مینویسید. باید گفت هنوز بسیاری از آنها در بسیاری از برنامهها استفاده میشوند. مانند بعضی از درایورها برای ارسال اطلاعات به سمت یک قطعه یا دستگاه یا حتی از شما میخواهند برنامهای بنویسید که با دستگاههای قدیمی ارتباط برقرار کند. برنامههایی که نیاز به کار با رشتهها دارند و ...
لیست زیر مشخص میکند که کدامیک از کلیدهای کنترلی تا چه اندازه امروزه توسط برنامه نویسان استفاده میشوند.
Null | استفاده روزمرهای از آن در همهی برنامهها وجود دارد و نیاز به معرفی ندارد. |
Transmission Control | این کلیدها که 10 عدد هستند شامل SOH , ACK , DLE , ENQ , EOT , ETB , ETX , Nak , STX , SYN هستند. کاربردشان در انتقال اطلاعات بود ولی امروزه استفاده از آنها به شدت کم شده است و انتقال دادهها با سوکت TCP/IP و HTTP و FTP و دیگر پروتکولها به سرانجام رسید و گاها برای بعضی کاربردهای ویژه استفاده میشوند. |
BEL | این مورد واقعا کاربردش را از دست داده است. وظیفه قبلیاش ارسال یه هشدار یا یک زنگ اخطار به کاربر بود. مثلا برای اینکه ماشین تایپ به کاربر هشدار بدهد به آخر خط رسیده است، یک کد BELL به سمت آن ارسال میکرد. |
Format Effectors | کدهای این دسته عبارتند از BS , CR , FF , HT , HTJ , HTS , IND , LF , NEL , PLD , PLU , RI , VT , VTS هستند که احتمالا مهمترین کدهایی هستند که امروزه از آنها استفاده میشود. کاربردشان در فرمت بندی یا قالب بندی متون نوشته شده یا همان کلیدهای قابل چاپ میباشد. CR و LF که همیشه معرف حضور ما هستند و بودنشان در سیستم یک امر حیاتی است. HT که همان tab است. BS که همان Backspace است. FF و VT هم که امروزه به ندرت استفاده میشوند. |
Device Control | هنوز برای ارتباط با دستگاههای مختلف مثل کار با پورتها استفاده میشوند. کلیدهای معروف آن DC1 و DC3 هستند که به XON و XOFF هم شناخته میشوند. یکی از کاربردهای آن. |
SUB | یک نماد جایگزین که استفادهی خود را از دست داده است. موقعیکه نمادی نامعتبر بود یا خطایی رخ میداد، این نماد جایگزین آن میشد. امروزه بیشتر از علامت ؟ در متون استفاده میشود. در یک صفحه کلید استاندارد این کد توسط فشرده شدن Ctrl+Z ارسال میشود. |
CAN , EM | کاربردی امروزه ندارد. CAN برای کنترل خطا به کار میرفت و EM در نوارهای مغناطیسی. |
Information Separators | شامل 4 کلید FS ,GS , RS و US میشود که برای جداسازی دادهها از یکدیگر به کار میروند؛ ولی بهخاطر جایگزینی آنها با اسنادی مثل XML یا دیتابیسها، استفاده از آنها تا حدودی به پایان رسیده است. |
SP | همان کلید space است که نیاز به معرفی ندارد و کارش گویای همه چیز هست. |
DELL | همان کلید Delete است. |
NBSP | این کلید همان کاراکتر ;nbsp& است که در کدهای HTML استفاده میشود. |
SHY | علامت - یا Hyphen است که به شدت استفاده از آن کم شده است. |
- نصب پایتون 2.5 یا 2.6 یا 2.7 که فعلا در سایت آن، نسخهی 2.7 در دسترس هست. توجه داشته باشید که هنوز برای نسخهی 3 پایتون پشتیبانی صورت نگرفته است.
- آخرین نسخهی sdk را هم میتوانید از این آدرس به صورت zip و یا از این آدرس به صورت tar دانلود کنید و در صورتیکه دوست دارید به سورس آن دسترسی داشته باشید یا اینکه از سورسهای مشارکت شده یا غیر رسمی استفاده کنید، از این صفحه آن را دریافت کنید.
(C:\Users\aym\Downloads\addon-sdk-1.17) C:\Users\aym\Downloads\addon-sdk-1.17\bin>
source bin/activate
bash bin/activate
(addon-sdk)~/mozilla/addon-sdk >
آغاز به کار
mkdir fxaddon cd fxaddon cfx init
* lib directory created * data directory created * test directory created * doc directory created * README.md written * package.json written * test/test-main.js written * lib/main.js written * doc/main.md written Your sample add-on is now ready for testing: try "cfx test" and then "cfx run". Have fun!"
{ "name": "fxaddon", "title": "fxaddon", "id": "jid1-QfyqpNby9lTlcQ", "description": "a basic add-on", "author": "", "license": "MPL 2.0", "version": "0.1" }
{ "name": "dotnettips", "title": ".net Tips Updater", "id": "jid1-QfyqpNby9lTlcQ", "description": "This extension keeps you updated on current activities on dotnettips.info", "author": "yeganehaym@gmail.com", "license": "MPL 2.0", "version": "0.1" }
var button= require('sdk/ui/button/action');
buttons.ActionButton({...});
var tgbutton = require('sdk/ui/button/toggle'); var panels = require("sdk/panel"); var self = require("sdk/self"); var button = tgbutton.ToggleButton({ id: "updaterui", label: ".Net Updater", icon: { "16": "./icon-16.png", "32": "./icon-32.png", "64": "./icon-64.png" }, onChange: handleChange }); var panel = panels.Panel({ contentURL: self.data.url("./popup.html"), onHide: handleHide }); function handleChange(state) { if (state.checked) { panel.show({ position: button }); } } function handleHide() { button.state('window', {checked: false}); }
tgbutton.ToggleButton
require('sdk/ui/button/toggle').ToggleButton
Context Menus
var contextMenu = require("sdk/context-menu"); var home = contextMenu.Item({ label: "صفحه اصلی", data: "https://www.dntips.ir/" }); var postsarchive = contextMenu.Item({ label: "مطالب سایت", data: "https://www.dntips.ir/postsarchive" }); var menuItem = contextMenu.Menu({ label: "Open .Net Tips", context: contextMenu.PageContext(), items: [home, postsarchive], image: self.data.url("icon-16.png"), contentScript: 'self.on("click", function (node, data) {' + ' window.location.href = data;' + '});' });
SelectorContext("img")
SelectorContext("img,a[href]")
var tabs = require("sdk/tabs"); var menuItem = contextMenu.Menu({ label: "Open .Net Tips", context: contextMenu.PageContext(), items: [home, postsarchive], image: self.data.url("icon-16.png"), contentScript: 'self.on("click", function (node, data) {' + ' self.postMessage(data);' + '});', onMessage: function (data) { tabs.open(data); } });
var Url="https://www.dntips.ir/search?term="; var searchMenu = contextMenu.Item({ label: "search for", context: [contextMenu.PredicateContext(checkText),contextMenu.SelectionContext()], image: self.data.url("icon-16.png"), contentScript: 'self.on("click", function () {' + ' var text = window.getSelection().toString();' + ' if (text.length > 20)' + ' text = text.substr(0, 20);' + ' self.postMessage(text);'+ '})', onMessage: function (data) { tabs.open(Url+data); } }); function checkText(data) { if(data.selectionText === null) return false; console.log('selectionText: ' + data.selectionText); //handle showing or hiding of menu items based on the text content. menuItemToggle(data.selectionText); return true; }; function menuItemToggle(text){ var searchText="جست و جو برای "; searchMenu.label=searchText+text; };
در قسمت آینده موارد بیشتری را در مورد افزونه نویسی در فایرفاکس بررسی خواهیم کرد و افزونه را تکمیل خواهیم کرد
MongoDB #8
>db.COLLECTION_NAME.find()
>db.mycol.find().pretty()
>db.mycol.find().pretty() { "_id": ObjectId(7df78ad8902c), "title": "MongoDB Overview", "description": "MongoDB is no sql database", "by": "tutorials point", "url": "http://www.tutorialspoint.com", "tags": ["mongodb", "database", "NoSQL"], "likes": "100" } >
عملگر | گرامر | مثال | مشابه در پایگاه داده رابطه ای |
Equality | {<key>:<value>} | ()db.mycol.find({"by":"tutorialspoint"}).pretty | ' where by = 'tutorials point |
Less Than | {<key>:{$lt:<value>}} | ()db.mycol.find({"likes":{$lt:50}}).pretty | where likes < 50 |
Less Than Equals | {<key>:{$lte:<value>}} | ()db.mycol.find({"likes":{$lte:50}}).pretty | where likes <= 50 |
Greater Than | {<key>:{$gt:<value>}} | ()db.mycol.find({"likes":{$gt:50}}).pretty | where likes > 50 |
Greater Than Equals | {<key>:{$gte:<value>}} | ()db.mycol.find({"likes":{$gte:50}}).pretty | where likes >= 5 0 |
Not Equals | {<key>:{$ne:<value>}} | ()db.mycol.find({"likes":{$ne:50}}).pretty | where likes != 50 |
>db.mycol.find({key1:value1, key2:value2}).pretty()
>db.mycol.find({"by":"tutorials point","title": "MongoDB Overview"}).pretty() { "_id": ObjectId(7df78ad8902c), "title": "MongoDB Overview", "description": "MongoDB is no sql database", "by": "tutorials point", "url": "http://www.tutorialspoint.com", "tags": ["mongodb", "database", "NoSQL"], "likes": "100" } >
>db.mycol.find( { $or: [ {key1: value1}, {key2:value2} ] } ).pretty()
>db.mycol.find({$or:[{"by":"tutorials point"},{"title": "MongoDB Overview"}]}).pretty() { "_id": ObjectId(7df78ad8902c), "title": "MongoDB Overview", "description": "MongoDB is no sql database", "by": "tutorials point", "url": "http://www.tutorialspoint.com", "tags": ["mongodb", "database", "NoSQL"], "likes": "100" } >
>db.mycol.find("likes": {$gt:10}, $or: [{"by": "tutorials point"}, {"title": "MongoDB Overview"}] }).pretty() { "_id": ObjectId(7df78ad8902c), "title": "MongoDB Overview", "description": "MongoDB is no sql database", "by": "tutorials point", "url": "http://www.tutorialspoint.com", "tags": ["mongodb", "database", "NoSQL"], "likes": "100" } >