Entity Framework Core הוא ORM פופולרי (ממופה עצמים-יחסי) עבור יישומים ב-.NET, המאפשר למפתחים לעבוד עם מסדי נתונים באמצעות אובייקטים ב-.NET. ניתן להשתמש בו עם רבים מסוגי מסדי הנתונים, כולל MongoDB.

במאמר זה, תלמד כיצד ניתן להשתמש ב-Entity Framework Core עם MongoDB. המאמר מכסה את היסודות, מסביר את היתרונות ומספק מדריך שלב אחר שלב. בין אם אתה חדש בנושא MongoDB או Entity Framework Core, או רק מחפש לשלב את הכלים הללו בפרויקטים שלך ב-.NET, מדריך זה יעזור לך לחצות את הפער בין מסדי נתונים יחסיים למסדי נתונים מבנה-מסמך.

המאמר מתחיל עם הקדמה קצרה ל-MongoDB וכן הקדמה ל-Entity Framework Core של Microsoft. לאחר מכן, הוא מכסה כיצד להשתמש בספק MongoDB EF Core. לאחר שמתבונן בפרטים הטכניים עם דוגמאות בסיסיות, תיצור פרויקט מלא עם MongoB ו-Entity Framework Core כדי שתוכל לראות כיצד הכל עובד ביחד. הפרויקט ישתמש בנתוני דוגמה מ-MongoDB Atlas כדי ליצור מערכת הזמנות למסעדה.

קיימת גם גרסה וידאו של מאמר זה שאתה יכול לצפות בה בערוץ YouTube של freeCodeCamp.org.

הקדמה ל-MongoDB

MongoDB היא מסד נתונים מבנה-מסמך פופולרי המיועד להתמודד עם כמויות גדולות של נתונים ולספק ביצועים גבוהים, גמישות והתרחבות. להבדיל ממסדי נתונים יחסיים מסורתיים, MongoDB אוחסן נתונים במסמכים גמישים בפורמט JSON-תואם. גישת המסמך הזו מאפשרת אחסון של מבני נתונים מורכבים בדרך טבעית ואינטואיטיבית יותר.

ב-MongoDB, הנתונים מאוחסנים באוספים, שדומים לטבלאות במסדי נתונים רלציוניים אך ללא סכימה קבועה. זה אומר שניתן לצבור מסמכים עם מבנה שונה באותו אוסף. גמישות זו היא אחת מיתרונות המרכזיים בשימוש ב-MongoDB, במיוחד כאשר מתמודדים עם נתונים לא מובנים או חצי מובנים.

בואו נסתכל על דוגמה של מסמך ב-MongoDB. דמיינו שיש לנו אוסף בשם users שמאחסן מידע על משתמשים ביישום. הנה כיצד יכול להיראות מסמך טיפוסי:

{
    "_id": "12345",
    "name": "John Doe",
    "email": "[email protected]",
    "age": 30,
    "address": {
        "street": "123 Main St",
        "city": "Anytown",
        "state": "CA",
        "zip": "12345"
    },
    "hobbies": ["reading", "travelling", "coding"]
}

במסמך זה, יש לנו שדות שונים כמו name, email, age, ו-address. השדה address עצמו הוא מסמך משובץ הכולל מספר שדות משניים כמו street, city, state, ו-zip. בנוסף, השדה hobbies הוא מערך של מחרוזות.

למרות שזה נראה דומה ל-JSON, MongoDB מאחסן נתונים בתבנית בינארית הנקראת BSON (Binary JSON). BSON מרחיב את הדגם של JSON ומספק סוגי נתונים נוספים, כמו מספרים שלמים, מספרים עשרוניים, תאריכים ונתונים בינאריים. פורמט הבינארי הזה מותאם לצורכי ביצוע וגמישות, מאפשר ל-MongoDB לאחסן ולשחזר נתונים ביעילות.

תכונה נוספת חשובה ב-MongoDB היא יכולתה להתרחב באופן אופקי. זה אומר שניתן להפציר את הנתונים על מספר שרתים, מה שהופך את ניהול מערכות נתונים גדולות והבטיחות גבוהה יותר. MongoDB תומכת גם בשאילתות מתקדמות, אינדיקציה וצבירה, ובכך היא מהווה כלי עוצמתי למגוון רחב של יישומים.

לדוגמה, ניתן לבצע שאילתה כדי למצוא את כל המשתמשים שמתגוררים בעיר מסוימת:

db.users.find({ "address.city": "Anytown" })

או תוכל למצוא משתמשים שיש להם תחביב מסוים:

db.users.find({ "hobbies": "coding" })

מונגו די בי משמשת בצורה נרחבת בתעשיות שונות, ממסחר אלקטרוני וניהול תוכן עד אנליטיקה בזמן אמת ויישומי אינטרנט של הדברים (IoT). הגמישות והקיומות שלה הופכים אותה לבחירה מצוינת ליישומים מודרניים שצריכים להתמודד עם נתונים מגוונים ודינמיים.

עכשיו שיש לנו הבנה בסיסית על מהו מונגו די בי ולמה היא פופולרית, בואו נמשיך לכלי עיקרי נוסף בערימת הטכנולוגיה שלנו: Entity Framework Core של מיקרוסופט.

מבוא ל-Entity Framework Core של מיקרוסופט

Entity Framework Core, המקוצרת בדרך כלל ל-EF Core, היא מפתח אובייקט-מסד נתונים מודרנית עבור .NET. היא מאפשרת למפתחים לעבוד עם מסד נתונים באמצעות אובייקטים ב-.NET, ומורידה את הצורך ברוב קוד הגישה לנתונים שמפתחים כמעט תמיד נדרשים לכתוב.

EF Core היא גרסה קלת משקל, ניתנת להרחבה ופעולה במסגרת מגוונת של פלטפורמות של הטכנולוגיה הפופולרית Entity Framework (EF) לגישה לנתונים. היא תומכת במגוון מנועי מסדי נתונים, כולל SQL Server, SQLite ומונגו די בי.

אחד היתרונות העיקריים של שימוש ב-EF Core הוא שהוא מאפשר למפתחים לעבוד עם נתונים בדרך יותר אינטואיטיבית ומבוססת אובייקטים. במקום כתיבת שאילתות SQL גולמיות, ניתן לבצע אינטראקציה עם מסד הנתונים באמצעות LINQ (שאילתה משולבת לשפה) וכיתות מסוגים מוגבלים.

בואו נסתכל על דוגמה בסיסית. נניח שיש לנו כיתה Product:

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

זה פשוט מאוד עם שלושה שדות. באמצעות EF Core, ניתן ליצור כיתת קונטקסט שמייצגת חלקה עם מסד הנתונים וכוללת DbSet עבור כל סוג של ישות שתרצה לשאול או לשמור עליה:

public class AppDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)   
    {
        optionsBuilder.Use<Your_SQL_Database_function>("YourConnectionStringHere");
    }
}

זהו קוד שמגדיר מערכת שם AppDbContext המשמשת להעברת את מערכת הענן Entity Framework Core DbContext מעברת למערכת הענן. המערכת הזו משמשת ליחדוף בעיר המסדרה. בתוך המערכת הזו, ישנו נכל עצם DbSet<Product> בשם Products, שמייצג אוסף של יישומים Product ומתחבר לשולחן בשם Products במסדרה. השיטה OnConfiguring מועברת על מנת להגדיר את החיבור למסדרה, ואתה יכול לציין מספר מסדרות מידע כספקים של המסדרה. השיטה משתמשת בעזרת optionsBuilder כדי להגדיר את החיבור עם מקום תור לשרתי החיבור האמיתיים. שרתי החיבור האמיתיים צריכים להוחלף עם השרתים המלאים עם הפרטים הנחוצים לחיבור למסדרה. כשאתה יוצר מיקסים של AppDbContext ביישום, הוא משתמש בהגדרה זו כדי לבצע פעולות כמו שאילוץ או שמירה על יישומים Product בשולחן Products.

עם ההגדרה הזו, אתה יכול לבצע פעולות CRUD (יצירה, קריאה, עדכון, מחיקה) בעזרת EF Core. לדוגמה, כדי להוסיף מוצר חדש למסדרה, תוכל להשתמש בקוד הזה.

using (var context = new AppDbContext())
{
    var product = new Product { Name = "Laptop", Price = 999.99M };
    context.Products.Add(product);
    context.SaveChanges();
}

הקוד הזה מדגים איך להוסיף מוצר חדש לבסיס הנתונים בשימוש ב- Entity Framework Core. מוצעת מקבץ AppDbContext, ובתוך ההקשר הזה, מוצעת אבן גרסת Product חדשה עם שם "לפטוף" ומחיר 999.99. המוצר החדש הזה אז מוסף לאוסף Products שמנוהל על ידי AppDbContext. לבסוף, נוצר קול לשימוש בשינויים על מנת לשמור על השינויים בבסיס הנתונים, ובאופן משמעותי מוסף המוצר החדש בטבלה Products.

כדי לשאת מוצרים, ניתן להשתמש ב- LINQ:

using (var context = new AppDbContext())
{
    var products = context.Products.Where(p => p.Price > 500).ToList();
    foreach (var product in products)
    {
        Console.WriteLine($"Product: {product.Name}, Price: {product.Price}");  
    }
}

הקוד הזה מדגים איך לשאת את הבסיס הנתונים בשימוש ב- Entity Framework Core. מוצעת מקבץ AppDbContext, ובתוך ההקשר הזה, נעשה שאלה כדי להשיג את כל המוצרים שיש על המחיר הגדול מ-500. התוצאות נאחסות ברשימה שנקראת products. אחר כך, משך לולאה אל אחר מוצר ברשימה, מדפיסים את שמו והמחיר של כל מוצר על המסך הקונסול.

EF Core מטפל בהמרה האותה של השאלות LINQ להצעות SQL מתאימות לבסיס הנתונים שלך, וזה מעצים את הגישה לנתונים ואת השמירה על הם.

EF Core גם תומך בתכונות מתקדמות כמו מעקבת שינויים, טעיית דילוג ומיגרציות, שעוזרים לך לנהל שינויי המבנה של הטבלה הבסיסית לאורך הזמן.

בסיכון, EF Core היא תומכת חזקה בעזרת מבנים מידע סימפלציה שמפשיטים את הגישה לנתונים ביישומים .NET על ידי אפשרו

אחר כך, נראה איך ספק ה-MongoDB EF Core מחבר את הפער בין MongoDB ובין EF Core, מאפשר לנו להשתמש בתבניות המוכרות של EF Core עם מסד נתונים של MongoDB.

איך ספק ה-MongoDB EF Core מחבר את הפער

ספק ה-MongoDB Entity Framework Core הוא כלי שמאפשר למפתחים להשתמש ב-MongoDB עם Entity Framework Core (EF Core), ובכך לשלב את הגמישות של MongoDB עם ה- API ותבניות העיצוב המוכרות של EF Core. ספק זה מאפשר לך לעבוד עם MongoDB באמצעות המתודולוגיות של code-first ו- LINQ query שתשתמש עימם במסדי נתונים רשמיים, דבר המקל על פיתוח ומקטין את הסיכון עבור אלה שכבר מכירים את EF Core.

ספק ה-MongoDB EF Core מחבר את הפער בין MongoDB ובין EF Core על ידי תמיכה בפעולות CRUD בסיסיות, שאילתות LINQ ומסמכים משובצים, בין תכונות אחרות. הנה כמה יכולות מרכזיות:

  1. תהליכי Code-First: ניתן להגדיר את מודלי הנתונים שלך ב-C# ולהשתמש ב-EF Core כדי ליצור את הסכימה של MongoDB, במקום להתחיל עם סכימת מסד הנתונים וליצור קוד ממנה. זה מועיל במיוחד עבור מפתחים שמעדיפים לנהל את מבנה מסד הנתונים שלהם באמצעות קוד.

  2. פעולות CRUD: הספק תומך בפעולות בסיסיות ליצירה, קריאה, עדכון ומחיקה. לדוגמה, אתה יכול להוסיף רשומה חדשה לבסיס נתונים בעזרת אותו קוד שראינו קודם:

     using (var context = new AppDbContext())
     {
         var product = new Product { Name = "Laptop", Price = 999.99M };
         context.Products.Add(product);
         context.SaveChanges();
     }
    
  3. תמיכה בשאילתות LINQ: אפשר להשתמש ב-LINQ כדי לבצע שאילתות נגד מונגו-DB, זה מאפשר לשתמש בידע הקיים שלך ב-C# ו-.NET כדי לתקשר עם בסיס הנתונים.

     בשימוש (גרסה context = חדש AppDbContext())
     {
         גרסה products = context.Products.Where(p => p.Price > 500).ToList();
         לכל (גרסה product בתוך products)
         {
             Console.WriteLine($"Product: {product.Name}, Price: {product.Price}");
         }
     }
    
  4. מעקב אחר שינויים: תמיכת מעקב אחר שינויים ב- EF Core מאפשרת זיהוי ושמירה אוטומטית של השינויים שנעשו בישות הנתונים שלך.

  5. מסמכים מוטבעים: הספק תומך במסמכים מוטבעים, מאפשר לך לאחסן נתונים קשורים במסמך יחיד, שהוא דפוס נפוץ ב-MongoDB.

  6. מיפוי וסיריאליזציה של מחלקות: המחלקות שלך ב-C# מופות לאוספי MongoDB, עם תמיכה בסוגי נתונים שונים והגדרות סיריאליזציה כדי לוודא שהנתונים מאוחסנים כראוי.

עיצוב נתונים ופעולות CRUD באמצעות MongoDB Atlas

עכשיו נעבור על דוגמה מהירה כיצד להשתמש ב-MongoDB EF Core Provider. אך בקרוב, אנחנו ניצור פרויקט מלא ב-Visual Studio Code כדי שתוכל לראות הכל בהקשר.

בחלק זה, נחקור איך להגדיר מודלי מידע ולבצע פעולות CRUD (יצירה, קריאה, עדכון, מחיקה) בעזרת ספקת מונגודב העצמית (EF) עם מונגודב אטלס. השילוב זה מאפשר לך להשתמש בפלטת הגמישות של מונגודב בעזרת תבניות מוכרות של EF Core.

הגדרת הסביבה שלך

כדי להתחיל, צריך להוסיף את החבילות הנחוצות לפרוייקט שלך:

dotnet add package MongoDB.EntityFrameworkCore

חבילת ה- MS EF Core והנתב ה- C# של מונגודב מווסף בתור תלויה בעת שמוסף חבילת הספקת ה- MongoDB EF Core. החבילות אלה מאפשרות ליישום היישומך להתחיל במונגודב דרך EF Core, בהשתמש באותם ההקשרים וההגדרות של האנטימים שהיית משתמש בהם במאגר מידע רלוונטי.

הגדרת מונגודב אטלס

לפני שאתה יכול לבצע פעולות CRUD, צריך להגדיר קבוצת מונגודב אטלס ולהתחבר לה את היישום שלך.

הנה השלבים. שימו לב שאנחנו נעבור על אותם בפירוט בעת שניצור את הפרוייקט.

  1. יצירת חשבון ב- MongoDB Atlas : תרשמו על חשבון חופשי ב MongoDB Atlas.

  2. יצירת קבוצה: קובע קבוצה חדשה. מונגודב אטלס מספקת רמה חינמית שמועדה לפיתוח וליישומים קטנים.
  3. קבלת משמר קישור: קבל את משמר הקישור שלך מלוגין המונגודב אטלס. הוא יראה משהו כמו זה:

     mongodb+srv://<username>:<password>@cluster0.mongodb.net/myFirstDatabase?retryWrites=true&w=majority
    

הגדרת המודל הנתונים

הגדר סוג כדי להשתמש כמודל עבור היישומך. לדוגמה הזו, ניצור מודל Customer:

public class Customer
{
    public ObjectId Id { get; set; }
    public String Name { get; set; }
    public String Order { get; set; }
}

המודל Customer מייצג את מבנה המסמכים שנאחסים באוסף MongoDB.

קיצון ממשק מסמך בסיס

כדי להתחיל להשתמש בEntity Framework Core, יצר קיצון ממשק שנובע מהממשק DBContext. המקבץ DbContext המוגדר בעל DBSet תכונות שמספקות את המודלים שניתן ליצור אינטראקציה אתם משתמשים בקיצון הזה.

מדוגמה זו יוצרת מקבץ מידע DBContext ומספקת את העצם Customer בתור תכונה DBSet:

public class MyDbContext : DbContext
{
    public DbSet<Customer> Customers { get; init; }

    public MyDbContext(DbContextOptions options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Customer>().ToCollection("customers");
    }
}

עבודה ראשונה על פנים

עם ספקת מונגודב עבור מערכת הייצור על פי קוד, ניתן להשתמש בעבודת ראשונה על פנים. זה אומר שאתה מגדיר את הקבצים הראשונים שלך, ומערכת האקספורציה (EF Core) תטפל ביצירת וניהול המבנה הבסיסי של מונגודב. זה מאוד שימושי במונגודב, שאינו מאמץ מבנה מוגבל, מה שמאפשר מבנים מאוד פלטיביים ודינמיים של מידע.

בשימוש במונגודב

אחרי שנוצר את הקבץ הDBContext, עלינו ליצור אובייקט DbContextOptionsBuilder ולקחת אחר את השיטה UseMongoDB(). השיטה מקבלת שני משתמשים: איבקט MongoClient ושם של המסדר שבו מאחסנים את האוספים שאתה עובד איתם.

השיטה UseMongoDB() מחזירה אובייקט DbContextOptions. תעביר את תכונת הOptions של האובייקט הזה לבניין הקבצים הDBContext שלך.

var mongoClient = new MongoClient("<Your MongoDB Connection URI>");

var dbContextOptions =
    new DbContextOptionsBuilder<MyDbContext>().UseMongoDB(mongoClient, "<Database Name");  

var db = new MyDbContext(dbContextOptions.Options);

פעולות CRUD

עכשיו בואו נראה איך לקודד את הפעולות CRUD. אנחנו נראה אותן בניגוד לתפקיד שלהן.

פעולת יצירה

כדי ליצור מסמך חדש במונגודב, משתמשים בשיטה Add על הDbSet ואז מקבלים SaveChanges. הנה דוגמה ליצירת לקוח חדש:

using (var context = new MyDbContext(options))
{
    var customer = new Customer { Name = "Beau Carnes", Order = "Laptop" };
    context.Customers.Add(customer);
    context.SaveChanges();
}

הקוד הזה יוצר מקבץ חדש Customer

פעולת קריאה

כדי לקרוא מסמכים מאוסף MongoDB, ניתן להשתמש בשאילתות LINQ על DbSet. זוהי דוגמה להשלמת כל הלקוחות:

using (var context = new MyDbContext(options))
{
    var customers = context.Customers.ToList();
    foreach (var customer in customers)
    {
        Console.WriteLine($"Customer: {customer.Name}, Order: {customer.Order}"); 
    }
}

קוד זה משיג את כל הלקוחות מהאוסף Customers ומדפיס את פרטיהם.

פעולת עדכון

כדי לעדכן מסמך קיים, ניתן לקלוט את המסמך, לשנות את המאפיינים שלו ולקרוא SaveChanges. זוהי דוגמה לעדכון הזמנת לקוח:

using (var context = new MyDbContext(options))
{
    var customer = context.Customers.FirstOrDefault(c => c.Name == "Beau Carnes"); 
    if (customer != null)
    {
        customer.Order = "Smartphone";
        context.SaveChanges();
    }
}

קוד זה מוצא את הלקוח שנקרא "Beau Carnes" ומעדכן את ההזמנה שלו ל"סמארטפון".

פעולת מחיקה

כדי למחוק מסמך, ניתן לקלוט את המסמך, להסיר אותו מהDbSet, ולקרוא SaveChanges. זוהי דוגמה למחיקת לקוח:

using (var context = new MyDbContext(options))
{
    var customer = context.Customers.FirstOrDefault(c => c.Name == "Beau Carnes"); 
    if (customer != null)
    {
        context.Customers.Remove(customer);
        context.SaveChanges();
    }
}

קוד זה מוצא את הלקוח שנקרא "Beau Carnes" ומוחק אותו מהאוסף Customers.

עקיבת שינויים

יכולות העקיבה של EF Core מותמכות במלואן, מאפשרות עדכון יעיל של מסמכים. כאשר משנה ישות ומקרא SaveChanges, EF Core יייצר את הפקודות הנחוצות ל-MongoDB כדי לעדכן רק את השדות השונים.

באמצעות ספק המתקדם MongoDB, ניתן לשלב באופן מלא תאימות את המודל הגמיש של MongoDB עם היכולות המוצקות של ORM של EF Core, ספק כלי עבודה חזק למפתחי .NET לבניית יישומים מודרניים.

הדרכה

עכשיו נשים את הכל ביחד וניצור מערכת הזמנת מסעדות.

דרישות מתחילת השימוש

כדי לעבוד במדריך הלמידה הזה, תזדקקו למספר דברים:

  • .NET 7.0.

  • ידע בסיסי על ASP.NET MVC ו-C#.

  • חשבון MongoDB Atlas ברמה החינמית וקירבת חשבון חינמית.

יצירת הפרוייקט

ASP.NET Core הוא שרשרת אינטרנט מאוד גמישה, שמאפשרת לך לסדקף סוגים שונים של אפליקציות אינטרנט שיש בהם הבדלים קלים במונחים של המסגרת או מבנה המצח. למדה זו, ניצור פרוייקט MVC שישתמש בקבצים סטטיים ובשליטים. יש סוגים אחרים של חלק קדמי, כמו React, אך MVC עם הצגות .cshtml הוא הכי נפוץ. כדי ליצור את הפרוייקט, נשתמש ב-CLI .NET:

dotnet new mvc -o RestRes

בגלל שהשתמשנו בCLI, למרות שזה קל יותר, זה רק יוצר את הקובץ csproj ולא את הקובץ הפילה שמאפשר לנו לפתוח אותו בVisual Studio, אז נתקן את זה.

cd RestRes
dotnet new sln
dotnet sln .\RestRes.sln add .\RestRes.csproj

הוספת חבילות NuGet

עכשיו שיצרנו את הפרוייקט החדש, נרצה להמשיך ולהוסיף את חבילות הNuGet הדרושות. בעזרת מנהל חבילות NuGet או בעזרת הפקודה הבאה של ה-CLI .NET, הוספו את חבילת MongoDB.EntityFrameworkCore.

dotnet add package MongoDB.EntityFrameworkCore

יצירת המודלים

לפני שנוכל להתחיל ביישום החבילות החדשות שהוספנו, עלינו ליצור את המודלים שייצגו את האנטיטים שברצוננו במערכת ההזמנות למסעדות שתישמר ב-MongoDB Atlas כמסמכים. בחלקים המשניים הבאים, ניצור את המודלים הבאים:

  • מסעדה

  • הזמנה

  • הגדרותMongoDB

מסעדה

ראשית, עלינו ליצור את מודל המסעדה שייצג את המסעדות שזמינות להזמנה במערכת שלנו.

  1. יצירת קובץ חדש בתיקיית המודלים בשם Restaurant.cs.

  2. הוספת הקוד הבא:

using MongoDB.Bson;
using MongoDB.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;


namespace RestRes.Models
{
    [Collection("restaurants")]    
    public class Restaurant
    {

        public ObjectId Id { get; set; }

        [Required(ErrorMessage = "You must provide a name")]
        [Display(Name = "Name")]
        public string? name { get; set; }


        [Required(ErrorMessage = "You must add a cuisine type")]
        [Display(Name = "Cuisine")]
        public string? cuisine { get; set; }


        [Required(ErrorMessage = "You must add the borough of the restaurant")]
        public string? borough { get; set; }

    }
}

התכונה של האוסף לפני הכיתה מספרת ליישום את האוסף בתוך הבסיס הנתונים שאנו משתמשים בו. זה מאפשר לנו להשתמש בשמות שונים או רישיות שונות בין הכיתה והאוסף, אם נרצה לעשות זאת.

הזמנה

עלינו גם ליצור כיתה להזמנות כדי לייצג כל הזמנות שאנחנו קובעים במערכת שלנו.

  1. יצירת קובץ חדש בתוך תיקיית המודלים בשם Reservation.cs.

  2. הוספת הקוד הבא לתוכו:

using MongoDB.Bson;
using MongoDB.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;


namespace RestRes.Models
{
    [Collection("reservations")]
    public class Reservation
    {
        public ObjectId Id { get; set; }


        public ObjectId RestaurantId { get; set; }


        public string? RestaurantName { get; set; }

        [Required(ErrorMessage = "The date and time is required to make this reservation")]
        [Display(Name = "Date")]
        public DateTime date { get; set; }

    }
}

הגדרותMongoDB

למרות שזה לא יהיה מסמך במאגר שלנו, אנחנו זקוקים למחלקת מודל כדי לאחסן הגדרות של MongoDB שיכולות להשתמש ברחבי היישום.

  1. צרו קובץ נוסף במודלים בשם MongoDBSettings.cs.

  2. הוסיפו את הקוד הבא:

namespace RestRes.Models
{
  public class MongoDBSettings
  {
      public string AtlasURI { get; set; }
      public string DatabaseName { get; set; }
  }
}

הגדרת EF Core

זהו החלק המרגש. אנחנו נתחיל ליישם את EF Core וננצל את המספק החדש ל-MongoDB. אם אתה מכיר את עבודתו עם EF Core כבר, חלק מזה יהיה מוכר לך.

RestaurantReservationDbContext

  1. יצירת תיקייה של שרותים, ויצירת קובץ בשם RestaurantReservationDbContext.cs.

  2. החלפו את הקוד בתוך המעגל המקומי עם הבא:

using Microsoft.EntityFrameworkCore;
using RestRes.Models;

namespace RestRes.Services
{
    public class RestaurantReservationDbContext : DbContext
    {
        public DbSet<Restaurant> Restaurants { get; init; }      


        public DbSet<Reservation> Reservations { get; init; }


        public RestaurantReservationDbContext(DbContextOptions options)
        : base(options)
        {
        }


        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);


            modelBuilder.Entity<Restaurant>();
            modelBuilder.Entity<Reservation>();
        }
    }
}

אם אתה מכיר את EF Core, זה ייראה מוכר. המחלקה מרחיבה את DbContext ואנחנו יוצרים תכונות DbSet שמאחסנות את המודלים שיהיו גם בבסיס הנתונים. אנחנו גם משכנעים את השיטה OnModelCreating. ייתכן שתשימו לב שלא כמו כשמשתמשים ב- SQL Server, אנחנו לא קוראים ל .ToTable(). יכולנו לקרוא ל .ToCollection במקום אבל זה לא נדרש כאן כי אנחנו מציין את האוסף באמצעות התכונות על המחלקות.

הוסיפו קו חיבור ופרטי מסד הנתונים ל appsettings

קודם לכן, יצרנו מודל MongoDBSettings, ועכשיו עלינו להוסיף את הערכים שהתכונות מתמapping לתוך ה appsettings.

  1. ב appsettings.json ו appsettings.Development.json, הוסף את החלק החדש הבא:

      "MongoDBSettings": {
        "AtlasURI": "mongodb+srv://<username>:<password>@<url>",
        "DatabaseName": "restaurants"
      }
    
  2. החלף את הכיוון האטלאס עם הקישור המקורי שלך מ Atalas.

עדכון ה program.cs

עכשיו שהגדרנו את המודלים וה DbContext שלנו, הגיע הזמן להוסיף אותם לקובץ ה program.cs שלנו.

אחרי השורה הקיימת builder.Services.AddControllersWithViews();, הוסף את הקוד הבא:

var mongoDBSettings = builder.Configuration.GetSection("MongoDBSettings").Get<MongoDBSettings>();
builder.Services.Configure<MongoDBSettings>(builder.Configuration.GetSection("MongoDBSettings"));

builder.Services.AddDbContext<RestaurantReservationDbContext>(options =>
options.UseMongoDB(mongoDBSettings.AtlasURI ?? "", mongoDBSettings.DatabaseName ?? ""));

יצירת השרותים

עכשיו, זהו הזמן להוסיף את השירותים שניהם נשתמש בהם כדי לתקשר עם הבסיס הנתונים באמצעות ה-RestaurantBookingDbContext שיצרנו. עבור כל שירות, ניצור ממשק וכל המחלקה שמיישמת אותו.

IRestaurantService ו-RestaurantService

הממשק הראשון והשירות שנימplement יהיו לביצוע פעולות CRUD על אוסף המסעדות. זה נקרא דפוס המאגר. יתכן שתראו אנשים מתקשרים ישירות עם ה-DbContext. אך רוב האנשים משתמשים בדפוס זה, לכן כולנו כוללים אותו כאן.

  1. אם לא עשיתם עדיין, יצירת תיקיית Services לאחסן את המחלקות החדשות שלנו.

  2. יצירת ממשק IRestaurantService והוספת הקוד הבא לשיטות שנימplement:

using MongoDB.Bson;
using RestRes.Models;

namespace RestRes.Services
{
    public interface IRestaurantService
    {
        IEnumerable<Restaurant> GetAllRestaurants();
        Restaurant? GetRestaurantById(ObjectId id);

        void AddRestaurant(Restaurant newRestaurant);

        void EditRestaurant(Restaurant updatedRestaurant);

        void DeleteRestaurant(Restaurant restaurantToDelete);
    }
}
  1. יצירת קובץ מחלקת RestaurantService.

  2. עדכון הצהרת המחלקה של RestaurantService כך שתיימplement את ה-IRestaurantService שיצרנו לאחרונה:

using Microsoft.EntityFrameworkCore;
using MongoDB.Bson;
using MongoDB.Driver;
using RestRes.Models;

namespace RestRes.Services
{
  public class RestaurantService : IRestaurantService
  {
    private readonly RestaurantReservationDbContext _restaurantDbContext;
    public RestaurantService(RestaurantReservationDbContext restaurantDbContext)
    {
        _restaurantDbContext = restaurantDbContext;
    }

    public void AddRestaurant(Restaurant restaurant)
    {
      _restaurantDbContext.Restaurants.Add(restaurant);

      _restaurantDbContext.ChangeTracker.DetectChanges();
      Console.WriteLine(_restaurantDbContext.ChangeTracker.DebugView.LongView);

      _restaurantDbContext.SaveChanges();
    }

    public void DeleteRestaurant(Restaurant restaurant)
    {
      var restaurantToDelete = _restaurantDbContext.Restaurants.Where(c => c.Id == restaurant.Id).FirstOrDefault();

      if(restaurantToDelete != null) {
          _restaurantDbContext.Restaurants.Remove(restaurantToDelete);
        _restaurantDbContext.ChangeTracker.DetectChanges();
          Console.WriteLine(_restaurantDbContext.ChangeTracker.DebugView.LongView);
          _restaurantDbContext.SaveChanges();
          }
        else {
            throw new ArgumentException("The restaurant to delete cannot be found.");
        }
    }

    public void EditRestaurant(Restaurant restaurant)
    {
          var restaurantToUpdate = _restaurantDbContext.Restaurants.FirstOrDefault(c => c.Id == restaurant.Id);

        if(restaurantToUpdate != null)
        {                
            restaurantToUpdate.name = restaurant.name;
            restaurantToUpdate.cuisine = restaurant.cuisine;
            restaurantToUpdate.borough = restaurant.borough;

            _restaurantDbContext.Restaurants.Update(restaurantToUpdate);

            _restaurantDbContext.ChangeTracker.DetectChanges();
            Console.WriteLine(_restaurantDbContext.ChangeTracker.DebugView.LongView);

            _restaurantDbContext.SaveChanges();

        }
      else
        {
            throw new ArgumentException("The restaurant to update cannot be found. ");
        }
    }        

    public IEnumerable<Restaurant> GetAllRestaurants()
    {
      return _restaurantDbContext.Restaurants.OrderByDescending(c => c.Id).Take(20).AsNoTracking().AsEnumerable<Restaurant>();
    }

    public Restaurant? GetRestaurantById(ObjectId id)
    {
      return _restaurantDbContext.Restaurants.FirstOrDefault(c  => c.Id == id);
    }
  }

}

IReservationService ו-ReservationService

השירות הבא הוא ה-IReservationService ו-ReservationService.

יצירת ממשק IReservationService והוספת השיטות הבאות:

using MongoDB.Bson;
using RestRes.Models;

namespace RestRes.Services
{
    public interface IReservationService
    {
        IEnumerable<Reservation> GetAllReservations();
        Reservation? GetReservationById(ObjectId id);

        void AddReservation(Reservation newReservation);

        void EditReservation(Reservation updatedReservation);

        void DeleteReservation(Reservation reservationToDelete);
    }
}

יצירת הכלאה ReservationService והחלפת הכלאה שלך עם הקוד הבא שמממש את כל השיטות:

using Microsoft.EntityFrameworkCore;
using MongoDB.Bson;
using RestRes.Models;

namespace RestRes.Services
{
    public class ReservationService : IReservationService
    {
        private readonly RestaurantReservationDbContext _restaurantDbContext;

        public ReservationService(RestaurantReservationDbContext restaurantDbContext)
        {
            _restaurantDbContext = restaurantDbContext;
        }
        public void AddReservation(Reservation newReservation)
        {
            var bookedRestaurant = _restaurantDbContext.Restaurants.FirstOrDefault(c => c.Id == newReservation.RestaurantId);
            if (bookedRestaurant == null)
            {
                throw new ArgumentException("The restaurant to be reserved cannot be found.");
            }

            newReservation.RestaurantName = bookedRestaurant.name;

            _restaurantDbContext.Reservations.Add(newReservation);

            _restaurantDbContext.ChangeTracker.DetectChanges();
            Console.WriteLine(_restaurantDbContext.ChangeTracker.DebugView.LongView);

            _restaurantDbContext.SaveChanges();
        }

        public void DeleteReservation(Reservation reservation)
        {
            var reservationToDelete = _restaurantDbContext.Reservations.FirstOrDefault(b => b.Id == reservation.Id);

            if(reservationToDelete != null)
            {
                _restaurantDbContext.Reservations.Remove(reservationToDelete);

                _restaurantDbContext.ChangeTracker.DetectChanges();
                Console.WriteLine(_restaurantDbContext.ChangeTracker.DebugView.LongView);

                _restaurantDbContext.SaveChanges();
            }
            else
            {
                throw new ArgumentException("The reservation to delete cannot be found.");
            }
        }

        public void EditReservation(Reservation updatedReservation)
        {
           var reservationToUpdate = _restaurantDbContext.Reservations.FirstOrDefault(b => b.Id == updatedReservation.Id);


            if (reservationToUpdate != null)
            {               
                reservationToUpdate.date = updatedReservation.date;

                _restaurantDbContext.Reservations.Update(reservationToUpdate);

                _restaurantDbContext.ChangeTracker.DetectChanges();
                _restaurantDbContext.SaveChanges();

                Console.WriteLine(_restaurantDbContext.ChangeTracker.DebugView.LongView);
            }  
            else 
            { 
                throw new ArgumentException("Reservation to be updated cannot be found");
            }

        }

        public IEnumerable<Reservation> GetAllReservations()
        {
            return _restaurantDbContext.Reservations.OrderBy(b => b.date).Take(20).AsNoTracking().AsEnumerable<Reservation>();
        }

        public Reservation? GetReservationById(ObjectId id)
        {
            return _restaurantDbContext.Reservations.AsNoTracking().FirstOrDefault(b => b.Id == id);
        }

    }
}

זה קוד דומה מאוד לקוד הכלאה RestaurantService אבל להזמנות במקום.

הוספתן להזדמנות התלות

השלב האחרון לשרותים הוא להוסיף אותם למיכל הזדמנות התלות.

בתוך Program.cs, הוסף את הקוד הבא אחרי הקוד שהוספנו שם קודם לכן:

builder.Services.AddScoped<IRestaurantService, RestaurantService>();
builder.Services.AddScoped<IReservationService, ReservationService>();

יצירת מודלי התצוגה

לפני שניממש את הקדמה, עלינו להוסיף את מודלי התצוגה שיתפקדו כמתמצית בין הקדמה והשורה האחורית שלנו, כשנדרש. למרות שהיישום שלנו די פשוט, המימוש של מודל התצוגה הוא עדיין דרך עשייה טובה כיוון שהוא עוזר להפריד בין חלקי היישום.

RestaurantListViewModel

הראשון שנוסיף יהיה RestaurantListViewModel. זה ישמש כמודל בדף ה-Razor שלנו מאוחר יותר עבור רשימת המסעדות בבסיס הנתונים שלנו.

  1. צור תיקייה חדשה בשורש הפרוייקט וקראיו ViewModels.

  2. הוסף קובץ חדש בשם RestaurantListViewModel.cs.

  3. הוסף את הקוד הבא:

using RestRes.Models;

namespace RestRes.ViewModels
{
    public class RestaurantListViewModel
    {        
        public IEnumerable<Restaurant>? Restaurants { get; set; }
    }
}

RestaurantAddViewModel

אנחנו גם רוצים מודל תצוגה שישמש על ידי התצוגה ההוספה שנוסיף מאוחר יותר.

  1. בתוך תיקיית ViewModels, יצירת קובץ חדש בשם RestaurantAddViewMode.cs.

  2. Add:

using RestRes.Models;

namespace RestRes.ViewModels
{
    public class RestaurantAddViewModel
    {
        public Restaurant? Restaurant { get; set; } 
    }
}

ReservationListViewModel

עכשיו, אנחנו רוצים לעשות דבר דומה מאד עבור ההזמנות, התחלה ב ReservationListViewModel.

  1. יצירו קובץ חדש בתיבת ViewModels בשם ReservationListViewModel.cs.

  2. Add:

using RestRes.Models;

namespace RestRes.ViewModels
{
    public class ReservationListViewModel
    {
        public IEnumerable<Reservation>? Reservations { get; set; }
    }
}

ReservationAddViewModel

בסוף, יש לנו את ה ReservationAddViewModel.

יצירו את הקובץ והוסף את הקוד הבא:

using RestRes.Models;

namespace RestRes.ViewModels
{
    public class ReservationAddViewModel
    {
        public Reservation? Reservation { get; set; }
    }
}

Adding to _ViewImports

מאוחר יותר, אנחנו נוסיף הפנים להצגות של המודלים והמנהגים בתצוגות. במטרה שהיישומה תדעה מהם, אנחנו צריכים להוסיף הפנים אליהם בקובץ _ViewImports.cshtml בתיבת התצוגות.

יש כבר הפנים לחלק מהם, כולל התג-עזרים, אז אנחנו רוצים להוסיף הפנים לתיבות .Models ו .ViewModels שלנו. כך שהחל של הקובץ ייראה כך:

@using RestRes
@using RestRes.Models
@using RestRes.ViewModels

Creating the controllers

עכשיו שיש לנו את ההגיעה האחורית ואת המנהגים האלה שנצטרך להתייחס אליהם, אנחנו יכולים להתחיל לעבוד על הקצה הקדמי. אנחנו ניצור שני מבצעים: אחד למסעדה ואחד להזמנות.

RestaurantController

המבצע הראשון שנוסיף הוא למסעדה.

  1. בתוך התיקיית Controllers הקיימת, הוסף קובץ של בקר חדש בשם RestaurantController.cs. אם אתה משתמש בVisual Studio, השתמש בתemplate לבקר ריק – בקר ריק.
  2. הוסף את הקוד הבא:

using Microsoft.AspNetCore.Mvc;
using MongoDB.Bson;
using RestRes.Models;
using RestRes.Services;
using RestRes.ViewModels;

namespace RestRes.Controllers
{
    public class RestaurantController : Controller
    {
        private readonly IRestaurantService _RestaurantService;

        public RestaurantController(IRestaurantService RestaurantService)
        {
            _RestaurantService = RestaurantService;
        }
        public IActionResult Index()
        {
            RestaurantListViewModel viewModel = new()
            {
                Restaurants = _RestaurantService.GetAllRestaurants(),
            };
            return View(viewModel);
        }

        public IActionResult Add()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Add(RestaurantAddViewModel restaurantAddViewModel)
        {
            if(ModelState.IsValid)
            {
                Restaurant newRestaurant = new()
                {
                    name = restaurantAddViewModel.Restaurant.name,
                    borough = restaurantAddViewModel.Restaurant.borough,
                    cuisine = restaurantAddViewModel.Restaurant.cuisine
                };

                _RestaurantService.AddRestaurant(newRestaurant);
                return RedirectToAction("Index");
            }

            return View(restaurantAddViewModel);         
        }

        public IActionResult Edit(ObjectId id)
        {
            if(id == null || id == ObjectId.Empty)
            {
                return NotFound();
            }

            var selectedRestaurant = _RestaurantService.GetRestaurantById(id);
            return View(selectedRestaurant);
        }

        [HttpPost]
        public IActionResult Edit(Restaurant restaurant)
        {
            try
            {
                if(ModelState.IsValid)
                {
                    _RestaurantService.EditRestaurant(restaurant);
                    return RedirectToAction("Index");
                }
                else
                {
                    return BadRequest();
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("", $"Updating the restaurant failed, please try again! Error: {ex.Message}");
            }

            return View(restaurant);
        }

        public IActionResult Delete(ObjectId id) {
            if (id == null || id == ObjectId.Empty)
            {
                return NotFound();
            }

            var selectedRestaurant = _RestaurantService.GetRestaurantById(id);
            return View(selectedRestaurant);
        }

        [HttpPost]
        public IActionResult Delete(Restaurant restaurant)
        {
            if (restaurant.Id == ObjectId.Empty)
            {
                ViewData["ErrorMessage"] = "Deleting the restaurant failed, invalid ID!";
                return View();
            }

            try
            {
                _RestaurantService.DeleteRestaurant(restaurant);
                TempData["RestaurantDeleted"] = "Restaurant deleted successfully!";

                return RedirectToAction("Index");
            }
            catch (Exception ex)
            {
                ViewData["ErrorMessage"] = $"Deleting the restaurant failed, please try again! Error: {ex.Message}";
            }

            var selectedRestaurant = _RestaurantService.GetRestaurantById(restaurant.Id);
            return View(selectedRestaurant);
        }        
    }
}

ReservationController

עכשיו עבור בקר ההזמנות. זה מאוד דומה לRestaurantController, אך יש לו הפנייה לשני השירותים, המסעדה והשירות ההזמנות, כי אנחנו צריכים להקיש מסעדה להזמנה. זה בגלל שעכשיו, ספקת הEF Core אינה מספקת תקשורת בין יישומים, אז אנחנו יכולים לקשר יישומים בדרך אחרת.

  1. יצירה של בקר ריק נוסף של MVC Controller בשם ReservationController.cs.

  2. הדביק את הקוד הבא:

using Microsoft.AspNetCore.Mvc;
using MongoDB.Bson;
using RestRes.Models;
using RestRes.Services;
using RestRes.ViewModels;

namespace RestRes.Controllers
{
    public class ReservationController : Controller
    {
        private readonly IReservationService _ReservationService;
        private readonly IRestaurantService _RestaurantService;        

        public ReservationController(IReservationService ReservationService, IRestaurantService RestaurantService)
        {
            _ReservationService = ReservationService;
            _RestaurantService = RestaurantService;
        }

        public IActionResult Index()
        {
            ReservationListViewModel viewModel = new ReservationListViewModel()
            {
                Reservations = _ReservationService.GetAllReservations()
            };
            return View(viewModel);
        }

        public IActionResult Add(ObjectId restaurantId)
        {
            var selectedRestaurant = _RestaurantService.GetRestaurantById(restaurantId);

            ReservationAddViewModel reservationAddViewModel = new ReservationAddViewModel();

            reservationAddViewModel.Reservation = new Reservation();
            reservationAddViewModel.Reservation.RestaurantId = selectedRestaurant.Id;
            reservationAddViewModel.Reservation.RestaurantName = selectedRestaurant.name;
            reservationAddViewModel.Reservation.date = DateTime.UtcNow;

            return View(reservationAddViewModel);
        }

        [HttpPost]
        public IActionResult Add(ReservationAddViewModel reservationAddViewModel)
        {
                Reservation newReservation = new()
                {
                    RestaurantId = reservationAddViewModel.Reservation.RestaurantId,                   
                    date = reservationAddViewModel.Reservation.date,
                };

                _ReservationService.AddReservation(newReservation);
                return RedirectToAction("Index");   
        }

        public IActionResult Edit(string Id)
        {
            if(Id == null || string.IsNullOrEmpty(Id))
            {
                return NotFound();
            }

            var selectedReservation = _ReservationService.GetReservationById(new ObjectId(Id));
            return View(selectedReservation);
        }

        [HttpPost]
        public IActionResult Edit(Reservation reservation)
        {
            try
            {
                var existingReservation = _ReservationService.GetReservationById(reservation.Id);
                if (existingReservation != null)
                {
                    _ReservationService.EditReservation(reservation);
                    return RedirectToAction("Index");
                }
                else
                {
                    ModelState.AddModelError("", $"Reservation with ID {reservation.Id} does not exist!");
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("", $"Updating the reservation failed, please try again! Error: {ex.Message}");
            }

            return View(reservation);
        }

        public IActionResult Delete(string Id)
        {
            if (Id == null || string.IsNullOrEmpty(Id))
            {
                return NotFound();
            }

            var selectedReservation = _ReservationService.GetReservationById(new ObjectId(Id));
            return View(selectedReservation);
        }

        [HttpPost]
        public IActionResult Delete(Reservation reservation)
        {
            if(reservation.Id == null)
            {
                ViewData["ErrorMessage"] = "Deleting the reservation failed, invalid ID!";
                return View();
            }

            try
            {
                _ReservationService.DeleteReservation(reservation);
                TempData["ReservationDeleted"] = "Reservation deleted successfully";

                return RedirectToAction("Index");
            }
            catch (Exception ex)
            {
                ViewData["ErrorMessage"] = $"Deleting the reservation failed, please try again! Error: {ex.Message}";
            }

            var selectedRestaurant = _ReservationService.GetReservationById(reservation.Id);
            return View(selectedRestaurant);
        }
    }
}

יצירת המסך

עכשיו שיש לנו את הרקע והבקרים עם הנקודות הסוף למערכת ההזמנות של המסעדה, הגיע הזמן ליישם את המסך. זה יעשה בעזרת דפים Razor. תגיעו גם למזרה לקבוצות מאפייני Bootstrap, כי זו הפרימינג הCSS שיוצא עם אפליקציות MVC מהחל מהחיצון. אנחנו נספק מסך לפעולות CRUD לשני רשימות והזמנות.

רשימת המסעדות

תחילה, נספק תצוגה שתמפה לשורש של /Restaurant, שתחפש באופן כללי את השיטה Index שמימשנו.

ASP.NET Core MVC משתמש בתבנית קונבנציה שבה אתה נותן לקובץ .cshtml שם של אמצעי הסיום/שיטה והוא נמצא בתוך תיקייה ששמה נגזר משם הבקר שלו.

  1. בתוך התיקייה Views, צור תיקייה נוספת בשם Restaurant.

  2. בתוך התיקייה Restaurant הזו, הוסף תצוגה חדשה על ידי יצירת קובץ בשם Index.cshtml. אם אתה משתמש בתבניות הזמינות, תרצה תצוגה Razor – ריקה. תקנה לתצוגה שם Index.

  3. הוסף את הקוד הבא:

@model RestaurantListViewModel

@if (TempData["RestaurantDeleted"] != null)
{
    <p class="text-success">@TempData["RestaurantDeleted"]</p>
}


@if (!Model.Restaurants.Any())
{
    <p>No results</p>
}
else
{
    <table class="table table-condensed table-bordered">
        <tr>
            <th>
                Name
            </th>
            <th>
                Cuisine
            </th>
            <th>
                Borough
            </th>            
            <th>
                Actions
            </th>
        </tr>

        @foreach (var restaurant in Model.Restaurants)
        {
            <tr>
                <td>@restaurant.name</td>
                <td>@restaurant.cuisine</td>
                <td>@restaurant.borough</td>                
                <td>
                    <a asp-action="Edit" asp-route-id="@restaurant.Id.ToString()">Edit</a>
                    <a asp-action="Delete" asp-route-id="@restaurant.Id.ToString()">Delete</a>
                    <a asp-controller="Reservation" asp-action="Add" asp-route-restaurantId="@restaurant.Id.ToString()">Reserve</a>
                </td>
            </tr>
        }

    </table>
}

<p>
    <a class="btn btn-primary" asp-action="Add">Add new restaurant</a>
</p>

עכשיו נעדכן את המסלול המוגדר כברירת מחדל מהבית ל- /Restaurant.

בתוך Program.cs, בתוך app.MapControllerRoute, החלף את השורה של התבנית בשורה הבאה:

pattern: "{controller=Restaurant}/{action=Index}/{id?}");

אם נפעיל זאת כעת, הכפתורים יובילו ל-404 כי אנחנו טרם מימשנו אותם. אז נעשה זאת עכשיו.

הוספת מסעדות

נתחיל עם הטופס להוספת מסעדות חדשות.

  1. הוסף תצוגה Razor ריקה חדשה בתוך התיקייה Restaurant בשם Add.cshtml.

  2. הוסף את הקוד הבא:

@model RestaurantAddViewModel

<h2>Create a new restaurant</h2>
<hr />

@if (ViewData["ErrorMessage"] != null)
{
    <p class="text-danger">@ViewData["ErrorMessage"]</p>
}

<form method="post" asp-controller="Restaurant" asp-action="Add">
    <div asp-validation-summary="All" class="text-danger"></div>

    <div class="mb-3">
        <label asp-for="Restaurant.name" class="form-label"></label>
        <input asp-for="Restaurant.name" class="form-control" />
        <span asp-validation-for="Restaurant.name" class="text-danger"></span>
    </div>

    <div class="mb-3">
        <label asp-for="Restaurant.cuisine" class="form-label"></label>
        <input asp-for="Restaurant.cuisine" class="form-control" />
        <span asp-validation-for="Restaurant.cuisine" class="text-danger"></span>
    </div>

      <div class="mb-3">
        <label asp-for="Restaurant.borough" class="form-label">Borough</label>
        <input asp-for="Restaurant.borough" class="form-control" />
        <span asp-validation-for="Restaurant.borough" class="text-danger"></span>
    </div>

    <input type="submit" value="Add restaurant" class="btn btn-primary" />
</form>

<div>
    <a asp-controller="Restaurant" asp-action="Index">Back to list</a>
</div>

עריכת מסעדות

הקוד עבור עמוד העריכה דומה בצורה כמעט זהה לעמוד ההוספה, אך הוא משתמש במודל של מסעדה וישתמש במסעדה שהועברה אליו כדי למלא את הטופס עבור העריכה.

  1. הוסף עוד צפייה בתוך תת תיקיית המסעדה בשם Edit.cshtml.

  2. הוסף את הקוד הבא:

@model Restaurant

<h2>Update @Model.name</h2>
<hr />

<form method="post" asp-controller="Restaurant" asp-action="Edit">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <input type="hidden" asp-for="Id" />

    <div class="mb-3">
        <label asp-for="name" class="form-label">Name</label>
        <input asp-for="name" class="form-control" />
        <span asp-validation-for="name" class="text-danger"/>
    </div>
    <div class="mb-3">
        <label asp-for="cuisine" class="form-label"></label>
        <input asp-for="cuisine" class="form-control" />
        <span asp-validation-for="cuisine" class="text-danger"/>
    </div>
    <div class="mb-3">
        <label asp-for="borough" class="form-label">Borough</label>
        <input asp-for="borough" class="form-control" />
        <span asp-validation-for="borough" class="text-danger"/>
    </div>
    <input type="submit" value="Update restaurant" class="btn btn-primary" />
</form>
<div>
    <a asp-controller="Restaurant" asp-action="Index">Back to list</a>
</div>

מחיקת מסעדות

הדף האחרון שצריך ליישם הוא הדף שיקרא כאשר יתבצע לחיצה על כפתור המחיקה.

  1. צור צפייה ריקה חדשה בשם Delete.cshtml.

  2. הוסף את הקוד הבא:

@model Restaurant

<h2>Deleting @Model.name</h2>
<hr />

@if(ViewData["ErrorMessage"] != null)
{
    <p class="text-danger">@ViewData["ErrorMessage"]</p>
}

<div>
    <dl class="row">
        <dt class="col-sm-4">
            <label asp-for="name">Name</label>
        </dt>
        <dd class="col-sm-10">
            @Model?.name
        </dd>
        <dt class="col-sm-2">
            <label asp-for="cuisine"></label>
        </dt>
        <dd class="col-sm-10">
            @Model?.cuisine
        </dd>
        <dt class="col-sm-2">
            <label asp-for="borough">Borough</label>
        </dt>
        <dd class="col-sm-10">
            @Model?.borough
        </dd>

    </dl>
</div>

<form method="post" asp-action="Delete">
    <input type="hidden" asp-for="Id" />
    <input type="submit" value="Delete restaurant" class="btn btn-danger" onclick="javascript: return confirm('Are you sure you want to delete this restaurant?');" />
</form>

<div>
    <a asp-controller="Restaurant" asp-action="Index">Back to list</a>
</div>

רשימת הזמנות

הוספנו את הצפיות עבור המסעדות וכעת נוסיף את הצפיות עבור הזמנות, התחלה ברשימת כל ההזמנות הקיימות.

  1. צור תיקייה חדשה בתוך תיקיית התצוגה בשם Reservation.

  2. צור קובץ תצוגה ריק בשם Index.cshtml.

  3. הוסף את הקוד הבא כדי להציג את ההזמנות, אם יש כלשהן:

@model ReservationListViewModel

@if (TempData["ReservationDeleted"] != null)
{
    <p class="text-success">@TempData["ReservationDeleted"]</p>
}

@if (!Model.Reservations.Any())
{
    <p>No results</p>
}

else
{    
    <table class="table table-condensed table-bordered">
        <tr>
            <th>
                Booked Restaurant
            </th>
            <th>
                Date and Time
            </th>
            <th>
                Actions
            </th>
        </tr>

        @foreach(var reservation in Model.Reservations)
        {
            <tr>
                <td>@reservation.RestaurantName</td>
                <td>@reservation.date.ToLocalTime()</td>
                <td>
                    <a asp-action="Edit" asp-route-id="@reservation.Id.ToString()">Edit</a>
                    <a asp-action="Delete" asp-route-id="@reservation.Id.ToString()">Delete</a>
                </td>
            </tr>
        }

    </table>   

}

הוספת הזמנות

להוסיף הזמנות היא השלב הבא.

  1. יצירת תצוגה ריקה בשם Add.cshtml.

  2. הוספת הקוד הבא:

@model ReservationAddViewModel


@if (ViewData["ErrorMessage"] != null)
{
    <p class="text-danger">@ViewData["ErrorMessage"]</p>
}

<form method="post" asp-controller="Reservation" asp-action="Add">
    <div asp-validation-summary="All" class="text-danger"></div>
    <input type="hidden" asp-for="Reservation.Id" />
    <input type="hidden" asp-for="Reservation.RestaurantId" />

    <div class="mb-3">
        <label asp-for="Reservation.date" class="form-label"></label>
        <input asp-for="Reservation.date" type="datetime-local" class="form-control" value="@DateTime.Now.ToString("yyyy-MM-ddTHH:mm")" />
        <span asp-validation-for="Reservation.date" class="text-danger"></span>
    </div>

    <input type="submit" value="Reserve table" class="btn btn-primary" />
</form>

עריכת הזמנות

עריכת הזמנות היא השלב הבא.

  1. יצירת תצוגה ריקה בשם Edit.cshtml.

  2. הוספת הקוד הבא:

@model Reservation

<h2>Editing reservation for @Model.RestaurantName on @Model.date.ToLocalTime()</h2>
<hr />

<form method="post" asp-controller="Reservation" asp-action="Edit">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <input type="hidden" asp-for="Id" />

    <div class="mb-3">
        <label asp-for="date" class="form-label"></label>
        <input asp-for="date" value="@Model.date.ToLocalTime().ToString("yyyy-MM-ddTHH:mm")" class="form-control" />
        <span asp-validation-for="date" class="text-danger" />
    </div>
    <input type="submit" value="Update reservation" class="btn btn-primary" />
</form>
<div>
    <a asp-controller="Reservation" asp-action="Index">Back to reservations</a>
</div>

מחיקת הזמנות

מחיקת הזמנות היא השלב הבא.

  1. יצירת תצוגה ריקה בשם Delete.cshtml.

  2. הוספת הקוד הבא:

@model Reservation

<h2>Delete reservation</h2>
<hr />

@if (ViewData["ErrorMessage"] != null)
{
    <p class="text-danger">@ViewData["ErrorMessage"]</p>
}

<div>
    <dl class="row">
        <dt class="col-sm-2">
            <label asp-for="RestaurantName">Name</label>
        </dt>
        <dd class="col-sm-10">
            @Model?.RestaurantName
        </dd>
        <dt class="col-sm-2">
            <label asp-for="date"></label>
        </dt>
        <dd class="col-sm-10">
            @Model?.date.ToLocalTime()
        </dd>
        </dl>
</div>

<form method="post" asp-action="Delete">
    <input type="hidden" asp-for="Id" />
    <input type="hidden" asp-for="RestaurantId" />
    <input type="submit" value="Delete reservation" class="btn btn-danger" onclick="javascript: return confirm('Are you sure you want to delete this reservation?');" />
</form>

<div>
    <a asp-controller="Reservation" asp-action="Index">Back to list</a>
</div>

עדכון NavBar

הדבר האחרון שעלינו להוסיף הוא עדכון שורת הניווט של האפליקציה כך שנוכל בקלות להחליף בין מסעדות והזמנות.

ניווט לקובץ בViews/Shared/_Layout.cshtml. מצאו את div עם הכיתה navbar-collapse. הסרו את החלק המלא הזה והוסיפו את הקוד הבא:

<div class="collapse navbar-collapse justify-content-between">
    <ul class="navbar-nav flex-grow-1">
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-controller="Restaurant" asp-action="Index">Restaurants</a>
        </li>
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-controller="Reservation" asp-action="Index">Reservations</a>
        </li>
    </ul>
</div>

בדיקת האפליקציה שלנו

יש לנו עכשיו אפליקציה פועלת שמשתמשת בספקת MongoDB חדשה עבור EF Core. עכשיו הזמן לבדוק את כל זה ולבקר בנקודות הנגישות שלנו כדי לוודא שהכל עובד.

במסוף, רץ את הפקודה הבאה:

dotnet run

נסה לערוך מסעדות ולהוסיף הזמנות. תוכל לנווט לדף בסיס הנתונים של MongoDB Atlas ולראות שהשינויים שלך מתבטאים במסד הנתונים.

פעולות MongoDB מתקדמות: חיפוש ב-Atlas וחיפוש מוקדם

ספק ה- EF Core בנוי על בסיס נהג ה- MongoDB C#‏. מאחר שכבר יש לנו גישה ל- MongoClient בעת יצירת ה- DbContext, זה מאפשר לנו לבצע פעולות מתקדמות ב- MongoDB כגון חיפוש ב-Atlas וחיפוש מוקדם. תכונות אלה משפרות את יכולות היישום שלך על ידי אפשרויות חיפוש עצומות תוך כדי שימוש במסגרת ה- EF Core המוכרת.

חיפוש ב-Atlas הוא מנוע חיפוש טקסט מלא המסופק על ידי MongoDB Atlas. זה מאפשר לך להריץ שאילתות חיפוש מתוחכמות על נתוני ה- MongoDB שלך. עם חיפוש ב-Atlas, תוכל ליישם תכונות כמו השלמה אוטומטית, חיפוש ממוקד ומיון מבוסס רלוונטיות.

כדי להשתמש בחיפוש ב-Atlas עם ספק ה- EF Core, עקוב אחרי השלבים הללו:

  1. התקן אינדקסים ב- MongoDB Atlas:

    • עבור לאשכול שלך ב- MongoDB Atlas.

    • נווט לכרטיסיית "חיפוש" וצור אינדקס חדש על אוסף הנתונים שלך. הגדר את השדות שברצונך לעשות חיפוש בהם.

  2. הגדרת שדות שנוכל לחפש במודלים שלך: במודלים שלך בC#, ודא שהשדות שבעצם רוצים לחפש מוגדרים באופן מוקף. הנה דוגמה להגדרת מודל של מוצר.

     public class Product
     {
         public ObjectId Id { get; set; }
         public string Name { get; set; }
         public string Description { get; set; }
         public decimal Price { get; set; }
         public string Category { get; set; }
     }
    
  3. ביצוע בקשות חיפוש: השתמשות ביכולתים של נתנית ה.NET של MongoDB כדי לבצע חיפושים טקסטיים. בגלל שעל-עצמו אף קור לא תומך ישירות בסינTAX החיפוש הספציפי לMongoDB, תצטרכו להשתמש בנתנית בשילוב עם אף קור. פה דוגמה אחת:

     using MongoDB.Driver;
     using MongoDB.Driver.Linq;
    
     var client = new MongoClient("your-mongodb-connection-string");
     var database = client.GetDatabase("your-database-name");
     var collection = database.GetCollection<Product>("Products");
    
     var searchResult = collection.Aggregate()
         .Match(Builders<Product>.Filter.Text("search term"))
         .ToList();
    

דוגמה זו מציגה איך לבצע חיפוש טקסט באוסף מוצרים. מסנן ה־טקסט מסייע בחיפוש בכל השדות המוערכים שהוגדרו באינדקס חיפוש Atlas שלך.

חיפוש וקטורי ב־MongoDB משמש לחיפוש מסמכים על סמך דמיון וקטור, שימושי במיוחד ליישומים המעורבים בלמידת מכונה, המלצות ועיבוד שפה טבעית. חיפוש וקטורי מאפשר לך לשאול מסמכים באמצעות וקטורים המייצגים טקסט, תמונות או נתונים גבוהי ממד.

  1. יצירת ואחסון וקטורים: תחילה, וודא שהמסמכים שלך מכילים וקטורים. עשוי להיות עליך לעבד את הנתונים שלך כדי ליצור את הווקטורים הללו באמצעות מודלי למידת מכונה.

  2. אינדוקס וקטורים ב־MongoDB Atlas: צור אינדקס מיוחד על שדה הווקטור ב־MongoDB Atlas כדי לאפשר חיפושים דמיון וקטורים יעילים.

  3. ביצוע חיפושים מונחים בסמלים: השתמש בנתינה .NET של MongoDB על מנת לבדוק על פי דומיין מצורה סמלים.

שילוב עם EF Core

למרות שהספקת המונחה MongoDB ל EF Core מאפשרת את הפעלולים הבסיסיים CRUD, התכונות המתקדמות כמו Atlas Search וחיפושים בסמלים דורשות השתמשות ישירה בנתינה .NET של MongoDB. עם זאת, אתה עדיין יכול לשלב את הפעלולים האלה בתוך היישומך המבוסס על EF Core על ידי השתמשות בנתינה עבור פונקציות החיפוש וEF Core עבור משימוש בפעילויות ניהול מידע אחרות.

על ידי שילוב את התכונות המתקדמות של EF Core וMongoDB, אתה יכול לבנות יישומים חזקים וגמישים שמשתמשים באותה פעם בטבלת הדברים המעניינים של EF Core ובכישורים החיפוש החזקים של MongoDB Atlas.