MongoDB Provider for EF Core: The Latest Updates
Rate this article
Exciting news! As announced at .local NYC, the MongoDB Provider for Entity Framework (EF) has gone into General Availability (GA) with the release of version 8.0 in NuGet. The major version numbers are set to align with the version number of .NET and EF so the release of 8.0 means the provider now officially supports .NET 8 and EF 8! 🎉
In this article, we will take a look at four highlights of what’s new and how you can add the features to your EF projects today.
This will be an article with code snippets, so it is assumed you have some knowledge of not just MongoDB but also EF. If you want to see an example application in Blazor that uses the new provider and how to get started implementing the CRUD operations, there is a previous tutorial I wrote on getting started that will take you from a blank application all the way to a working system.
Should you wish to see the application from which the code snippets in this article are taken, you can find it on GitHub.
While the provider was in preview, it wasn’t possible to handle lists or embedded documents. But this has changed in GA. It is now just as easy as with the MongoDB C# driver to handle embedded documents and arrays in your application.
In MongoDB’s sample restaurants collection from the sample dataset, the documents have an address field, which is an embedded document, and a grades field which contains an array of embedded documents representing each time the restaurant was graded.
Just like before, you can have a C# model class that represents your restaurant documents and classes for each embedded document, and the provider will take care of mapping to and from the documents from the database to those classes and making those available inside your DbSet.
We can then access the properties on that class to display data retrieved from MongoDB using the provider.
1 var restaurants = dbContext.Restaurants.AsNoTracking().Take(numOfDocsToReturn).AsEnumerable<Restaurant>(); 2 3 foreach (var restaurant in restaurants) 4 { 5 Console.WriteLine($"{restaurant.Id.ToLower()}: {restaurant.Name} - {restaurant.Borough}, {restaurant.Address.Zipcode}"); 6 foreach (var grade in restaurant.Grades) 7 { 8 Console.WriteLine($"Grade: {grade.GradeLetter}, Score: {grade.Score}"); 9 } 10 Console.WriteLine("--------------------"); 11 }
This code is pretty straightforward. It creates an IEnumerable of restaurants from querying the Dbset, only selecting (using the Take method) the number of requested restaurant documents. It then loops through each returned restaurant and prints out data from it, including the zip code from the embedded address document.
Because grades is an array of grade documents, there is also an additional loop to access data from each document in the array.
Creating documents is also able to support embedded documents. As expected, you can create a new Restaurant object and new versions of both the Address and Grade objects to populate those fields too.
1 new Restaurant() 2 { 3 Id = "5678", 4 Name = "My Awesome Restaurant", 5 Borough = "Brooklyn", 6 Cuisine = "American", 7 Address = new Address() 8 { 9 Building = "123", 10 Coord = new double[] { 0, 0 }, 11 Street = "Main St", 12 Zipcode = "11201" 13 }, 14 Grades = new List<Grade>() 15 { 16 new Grade() 17 { 18 Date = DateTime.Now, 19 GradeLetter = "A", 20 Score = 100 21 } 22 }, 23 IsTestData = true, 24 RestaurantId = "123456"
Then, just like with any EF code, you can call Add on the db context, passing in the object to insert and call save changes to sync the db context with your chosen storage — in this case, MongoDB.
1 dbContext.Add(newResturant); 2 3 await dbContext.SaveChangesAsync();
Another exciting new feature available in the GA is the ability to get more detailed information, to your logging provider of choice, about what is going on under the hood.
You can achieve this using the LogTo and EnableSensitiveLogging methods, available from the DbContextOptionsBuilder in EF. For example, you can log to your own logger, logging factory, or even Console.Write.
1 public static RestaurantDbContext Create(IMongoDatabase database) => 2 new(new DbContextOptionsBuilder<RestaurantDbContext>() 3 .LogTo(Console.WriteLine) 4 .EnableSensitiveDataLogging() 5 .UseMongoDB(database.Client, database.DatabaseNamespace.DatabaseName) 6 .Options);
One of the reasons you might choose to do this, and the reason why it is so powerful, is that it will show you what the underlying query was that was used to carry out your requested LINQ.
This can be helpful for debugging purposes, but also for learning more about MongoDB as well as seeing what fields are used most in queries and might benefit from being indexed, if not already an index.
Another feature that has been added that is really useful is support for the BSON attributes. One of the most common use cases for these is to allow for the use of different field names in your document versus the property name in your class.
One of the most often seen differences between MongoDB documents and C# properties is in the capitalization. MongoDB documents, including fields in the restaurant documents, use lowercase. But in C#, it is common to use camel casing. We have a set of naming convention packs you can use in your code to apply class-wide handling of that, so you can specify once that you will be using that convention, such as camel case in your code, and it will automatically handle the conversion. But sometimes, that alone isn’t enough.
For example, in the restaurant data, there is a field called “restaurant_id” and the most common naming convention in C# would be to call the class property “RestaurantId.” As you can see, the difference is more than just the capitalization. In these instances, you can use attributes from the underlying MongoDB driver to specify what the element in the document would be.
1 [ ]2 public string RestaurantId { get; set; }
Other useful attributes include the
[BsonId]
attribute, to specify which property is to be used to represent your _id field, and [BsonRequired]
, which states that a field is required.There are other BSON attributes as well, already in the C# driver, that will be available in the provider in future releases, such as
[BsonDiscriminator]
and [BsonGuideRepresentation]
.Lastly, we have value converters. These allow you to convert the type of data as it goes to/from storage.
The one I use the most is a string as the type for the Id property instead of the ObjectId data type, as this can be more beneficial when using web frameworks such as Blazor, where the front end will utilize that property. Before GA, you would have to set your Id property to ObjectId, such as:
1 public ObjectId Id { get; set; }
However, you might prefer to use string because of the string-related methods available or for other reasons, so now you can use:
1 public string Id { get; set; }
To enable the provider to handle mapping an incoming _id value to the string type, you use HasConversion on the entity type.
1 modelBuilder.Entity <Restaurant>() 2 .Property(r => r.Id) 3 .HasConversion<ObjectId>();
It means if you want to, you can then manipulate the value, such as converting it to lowercase more easily.
1 Console.WriteLine(restaurant.Id.ToLower());
There is one thing, though, to take note of and that is when creating documents/entities. Although MongoDB can support not specifying an _id — because if it is missing, one will be automatically generated — EF requires that a key not be null. Since the _id field is the primary key in MongoDB documents, EF will error when creating a document if you don’t provide an id.
This can easily be solved by creating a new ObjectId and casting to a string when creating a new document, such as a new restaurant.
1 Id = new ObjectId().ToString()
Today is a big milestone in the journey for the official MongoDB Provider for EF Core, but it is by no means the end of the journey. Work is only just beginning!
You have read today about some of the highlights of the release, including value converters, support for embedded documents, and detailed logging to see how a query was generated and used under the hood. But there is not only more in this release, thanks to the hard work of engineers in both MongoDB and Microsoft, but more to come.
The code for the provider is all open source so you can see how it works. But even better, the Readme contains the roadmap, showing you what is available now, what is to come, and what is out of scope.
Plus, it has a link to where you can submit issues or more excitingly, feature requests!
So get started today, taking advantage of your existing EF knowledge and application code, while enjoying the benefits of MongoDB!
Top Comments in Forums
There are no comments on this article yet.