{"id":54127,"date":"2024-10-21T10:05:00","date_gmt":"2024-10-21T17:05:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=54127"},"modified":"2024-10-21T10:05:00","modified_gmt":"2024-10-21T17:05:00","slug":"mongodb-ef-core-provider-whats-new","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/mongodb-ef-core-provider-whats-new\/","title":{"rendered":"MongoDB EF Core Provider: What&#8217;s New?"},"content":{"rendered":"<blockquote>\n<p>This is a guest post by Rishit Bhatia and Luce Carter. Rishit is a Senior Product Manager at MongoDB focusing on the .NET Developer Experience and has been working with C# since many years hands on before moving into Product Management. Luce is a Developer Advocate at MongoDB, Microsoft MVP and lover of code, sunshine and learning. This blog was reviewed by the Microsoft .NET team for EF Core.<\/p>\n<\/blockquote>\n<p>The EF Core provider for <a href=\"https:\/\/www.mongodb.com\/\">MongoDB<\/a> went <a href=\"https:\/\/www.mongodb.com\/blog\/post\/mongodb-provider-entity-framework-core-now-generally-available\">GA<\/a> in May 2024. We\u2019ve come a long way since we initially released this package in preview six months ago. We wanted to share some interesting features that we\u2019ve been working on which would not have been possible without the support of and collaboration with Microsoft\u2019s .NET Data and Entity Framework team.<\/p>\n<p>In this post, we will be using the <a href=\"https:\/\/www.mongodb.com\/docs\/drivers\/csharp\/current\/\">MongoDB EF Core provider<\/a> with <a href=\"https:\/\/www.mongodb.com\/products\/platform\/atlas-database\">MongoDB Atlas<\/a> to showcase the following:<\/p>\n<ul>\n<li>Adding a property to an entity and change tracking<\/li>\n<li>Leveraging the escape hatch to create an index<\/li>\n<li>Performing complex queries<\/li>\n<li>Transactions and optimistic concurrency<\/li>\n<\/ul>\n<p>The code related to this blog can be found on <a href=\"https:\/\/github.com\/mongodb-developer\/efcore_highlights\">Github<\/a>. The boilerplate code to get started is in the \u201cstart\u201d branch. The full code with all the feature highlights mentioned below is in the \u201cmain\u201d branch.  <\/p>\n<h2>Prerequisites<\/h2>\n<p>We will be using a <a href=\"https:\/\/www.mongodb.com\/docs\/atlas\/sample-data\/\">sample dataset<\/a> \u2014 specifically, the movies collection from the <em>sample_mflix<\/em> database available for MongoDB Atlas in this example. To set up an Atlas cluster with sample data, you can follow the steps <a href=\"https:\/\/www.mongodb.com\/docs\/atlas\/getting-started\/\">in the docs<\/a>. We\u2019ll create a simple .NET Console App to get started with the MongoDB EF Core provider. For more details on how to do that, you can check the <a href=\"https:\/\/www.mongodb.com\/docs\/entity-framework\/current\/quick-start\/\">quickstart guide<\/a>.<\/p>\n<p>At this point, you should be connected to Atlas and able to output the movie plot from the movie being read in the quickstart guide.<\/p>\n<h2>Features highlight<\/h2>\n<h3>Adding properties and change tracking<\/h3>\n<p>One of the advantages of MongoDB\u2019s document model is that it supports a flexible schema. This, coupled with EF Core\u2019s ability to support a Code First approach, lets you add properties to your entities on the fly. To show this, we are going to add a new nullable boolean property called <code>adapted_from_book<\/code> to our model class. This will make our model class as seen below:<\/p>\n<pre><code class=\"language-csharp\">public class Movie\n{\n    public ObjectId Id { get; set; }\n\n    [BsonElement(\"title\")]\n    public string Title { get; set; }\n\n    [BsonElement(\"rated\")]\n    public string Rated { get; set; }\n\n    [BsonElement(\"plot\")]\n    public string Plot { get; set; }\n\n    [BsonElement(\"adaptedFromBook\")]\n    public bool? AdaptedFromBook { get; set; }\n}<\/code><\/pre>\n<p>Now, we are going to set this newly added property for the movie entity we found and see <a href=\"https:\/\/learn.microsoft.com\/ef\/core\/change-tracking\/\">EF Core\u2019s Change Tracking<\/a> in action after we save our changes. To do so, we\u2019ll add the following lines of code after printing the movie plot:<\/p>\n<pre><code class=\"language-csharp\">movie.AdaptedFromBook = false;\nawait db.SaveChangesAsync();<\/code><\/pre>\n<p>Before we run our program, let\u2019s go to our collection in Atlas and find this movie to make sure that this newly created field <code>adapted_from_book<\/code> does not exist in our database. To do so, simply go to your cluster in the Atlas Web UI and select Browse Collections.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/10\/mongodb-atlas-browse-collections-ui.png\" alt=\"Browse Collections button showing in Atlas UI\" \/><\/p>\n<p>Then, choose the movies collection from the <em>sample_mflix<\/em> database. In the filter tab, we can find our movie using the query below:<\/p>\n<pre><code class=\"language-javascript\">{title: \"Back to the Future\"}<\/code><\/pre>\n<p>This should find our movie and we can confirm that the new field we intend to add is indeed not seen.  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/10\/mongodb-sample-movie-document.png\" alt=\"Example movie document\" \/><\/p>\n<p>Next, let\u2019s add a breakpoint to the two new lines we just added to make sure that we can track the changes live as we proceed. Select the Start Debugging button to run the app. When the first breakpoint is hit, we can see that the local field value has been assigned.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/10\/mongodb-contents-movie-field-view-debugger.png\" alt=\"Contents of local movie field viewed from debugger\" \/><\/p>\n<p>Let\u2019s hit Continue and check the document in the database. We can see that the new field has not yet been added. Let\u2019s step over the save changes call which will end the program. At this point, if we check our document in the database, we\u2019ll notice that the new field has been added as seen below!<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/10\/mongodb-new-document-field-adapted.png\" alt=\"Previous document example with new field adapted from book added\" \/><\/p>\n<h3>Index management<\/h3>\n<p>The MongoDB EF Core provider is built on top of the existing <a href=\"https:\/\/www.mongodb.com\/docs\/drivers\/csharp\/current\/\">.NET\/C# driver<\/a>. One advantage of this architecture is that we can reuse the <code>MongoClient<\/code> already created for the <code>DbContext<\/code> to leverage other capabilities exposed by MongoDB\u2019s developer data platform. This includes but is not limited to features such as <a href=\"https:\/\/www.mongodb.com\/docs\/drivers\/csharp\/upcoming\/fundamentals\/indexes\/#list-indexes\">Index Management<\/a>, <a href=\"https:\/\/www.mongodb.com\/docs\/drivers\/csharp\/upcoming\/fundamentals\/atlas-search\/\">Atlas Search<\/a>, and <a href=\"https:\/\/www.mongodb.com\/docs\/atlas\/atlas-vector-search\/tutorials\/vector-search-quick-start\/\">Vector Search<\/a>.<\/p>\n<p>We\u2019ll see how we can create a new index using the driver in this same application. First, we\u2019ll list the indexes in our collection to see which indexes already exist. MongoDB creates an index on the <code>_id<\/code> field by default. We\u2019re going to create a helper function to print the indexes:<\/p>\n<pre><code class=\"language-csharp\">var moviesCollection = client.GetDatabase(\"sample_mflix\").GetCollection&lt;Movie&gt;(\"movies\");\nConsole.WriteLine(\"Before creating a new Index:\");\nPrintIndexes();\n\nvoid PrintIndexes()\n{\n    var indexes = moviesCollection.Indexes.List();\n    foreach (var index in indexes.ToList())\n    {\n        Console.WriteLine(index);\n    }\n}<\/code><\/pre>\n<p>The expected output is as seen below:<\/p>\n<pre><code class=\"language-javascript\">{ \"v\" : 2, \"key\" : { \"_id\" : 1 }, \"name\" : \"_id_\" }<\/code><\/pre>\n<p>Now, we\u2019ll create a <a href=\"https:\/\/www.mongodb.com\/docs\/manual\/core\/indexes\/index-types\/index-compound\/\">compound index<\/a> on the title and rated fields in our collection and print the indexes again.<\/p>\n<pre><code class=\"language-csharp\">var moviesIndex = new CreateIndexModel&lt;Movie&gt;(Builders&lt;Movie&gt;.IndexKeys\n    .Ascending(m =&gt; m.Title)\n    .Ascending(x =&gt; x.Rated));\nawait moviesCollection.Indexes.CreateOneAsync(moviesIndex);\n\nConsole.WriteLine(\"After creating a new Index:\");\nPrintIndexes();<\/code><\/pre>\n<p>We can see that a new index with the name <code>title_1_rated_1<\/code> has been created.<\/p>\n<pre><code class=\"language-javascript\">After creating a new Index:\n{ \"v\" : 2, \"key\" : { \"_id\" : 1 }, \"name\" : \"_id_\" }\n{ \"v\" : 2, \"key\" : { \"title\" : 1, \"rated\" : 1 }, \"name\" : \"title_1_rated_1\" }<\/code><\/pre>\n<h3>Querying data<\/h3>\n<p>Since EF Core already supports Language Integrated Query (LINQ) Syntax, it becomes easy to write strongly typed queries in C#. Based on the fields available in our model classes, we can try to find some interesting movies from our collection. Let&#8217;s say I wanted to find all movies that are rated \u201cPG-13\u201d with their plot containing the word \u201cshark\u201d but I wanted them ordered by their title field. I can do so easily with the following query:<\/p>\n<pre><code class=\"language-csharp\">var myMovies = await db.Movies\n    .Where(m =&gt; m.Rated == \"PG-13\" &amp;&amp; m.Plot.Contains(\"shark\"))\n    .OrderBy(m =&gt; m.Title)\n    .ToListAsync();\n\nforeach (var m in myMovies)\n{\n    Console.WriteLine(m.Title);\n}<\/code><\/pre>\n<p>We can then print out the queries using the code above and run the program using <code>dotnet run<\/code> to see the results. We should be able to see two movie names from the 20K+ movies in our collection printed in the console as seen below.<\/p>\n<pre><code class=\"language-text\">Jaws: The Revenge\nShark Night 3D<\/code><\/pre>\n<p>If you would like to see the query that is sent to the server, which in this case is the MQL, then you can enable logging in the <code>Create<\/code> function on the DbContext as seen below:<\/p>\n<pre><code class=\"language-csharp\">   public static MflixDbContext Create(IMongoDatabase database) =&gt;\n       new(new DbContextOptionsBuilder&lt;MflixDbContext&gt;()\n           .UseMongoDB(database.Client, database.DatabaseNamespace.DatabaseName)\n           .LogTo(Console.WriteLine)\n           .EnableSensitiveDataLogging()\n           .Options);<\/code><\/pre>\n<p>This way we can see the following as a part of our detailed logs when we run the program again:<\/p>\n<pre><code class=\"language-text\">Executed MQL query\nsample_mflix.movies.aggregate([{ \"$match\" : { \"rated\" : \"PG-13\", \"plot\" : \/shark\/s } }, { \"$sort\" : { \"title\" : 1 } }])<\/code><\/pre>\n<h3>Autotransactions and optimistic concurrency<\/h3>\n<p>Yes, you read that right! The MongoDB EF Core provider from its 8.1.0 release supports transactions and optimistic concurrency. What this means is that by default, <code>SaveChanges<\/code> and <code>SaveChangesAsync<\/code> are transactional. This will empower automatic rollback of operations in production grade workloads in case of any failures and ensure that all operations are fulfilled with <a href=\"https:\/\/en.wikipedia.org\/wiki\/Optimistic_concurrency_control\">optimistic concurrency<\/a>.<\/p>\n<p>If you want to turn off transactions, you can do so during the initialization phase before calling any <code>SaveChanges<\/code> operation.<\/p>\n<pre><code class=\"language-csharp\">db.Database.AutoTransactionBehavior = AutoTransactionBehavior.Never;<\/code><\/pre>\n<p>The provider supports two methods of optimistic concurrency depending on your requirements which are through a concurrency check or row versions. You can read more about it <a href=\"https:\/\/www.mongodb.com\/docs\/entity-framework\/current\/fundamentals\/optimistic-concurrency\/\">in the docs<\/a>. We\u2019ll be using the RowVersion to demonstrate this use case. This leverages the <code>Version<\/code> field in our model class which will be updated automatically by the MongoDB EF Provider. To add the version, we add the following to our model class.<\/p>\n<pre><code class=\"language-csharp\"> [Timestamp]\n public long? Version { get; set; }<\/code><\/pre>\n<p>First, let\u2019s create a new movie entity called <code>myMovie<\/code> as seen below and add it to the <code>DbSet<\/code>, followed by <code>SaveChangesAsync<\/code>.<\/p>\n<pre><code class=\"language-csharp\">Movie myMovie1= new Movie {\n    Title = \"The Rise of EF Core 1\",\n    Plot = \"Entity Framework (EF) Core is a lightweight, extensible, open source and cross-platform version of the popular Entity Framework data access technology.\",\n    Rated = \"G\"\n};\n\ndb.Movies.Add(myMovie1);\nawait db.SaveChangesAsync();<\/code><\/pre>\n<p>Now, let\u2019s create a new <code>DbContext<\/code> similar to the one we created above. We can move the database creation into a variable so we don\u2019t have to define the name of the database again. With this new context, let\u2019s add a sequel for our movie and add it to the DbSet. We\u2019ll also add a third part (yes, it&#8217;s a trilogy) but use the same ID as our second movie entity to this new context and then save our changes.<\/p>\n<pre><code class=\"language-csharp\">var dbContext2 = MflixDbContext.Create(database);\ndbContext2.Database.AutoTransactionBehavior = AutoTransactionBehavior.Never;\nvar myMovie2 = new Movie { title = \"The Rise of EF Core 2\" };\ndbContext2.Movies.Add(myMovie2);\n\nvar myMovie3 = new Movie { Id = myMovie2.Id,Title = \"The Rise of EF Core 3\" };\ndbContext2.Movies.Add(myMovie3);\nawait dbContext2.SaveChangesAsync();<\/code><\/pre>\n<p>With transactions now being supported, the second set of operations for our latter two movie entities should not go through since we are trying to add them with an already existing <code>_id<\/code>. We should see an exception and the transaction should be rolled with only one movie being seen in our database. Let\u2019s run and see if that is true.<\/p>\n<p>We rightfully see an exception and we can confirm that we have only one movie (the first part) inserted into the database.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/10\/mongodb-transaction-exception.png\" alt=\"Exception being thrown by the transaction as the document being added has the same id as an existing one\" \/><\/p>\n<p>The following shows only a single document in the database as the transaction was rolled back.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/10\/mongodb-transaction-rollback.png\" alt=\"Only one document in the database as the transaction was rolled back\" \/><\/p>\n<p>Don\u2019t worry, we will correctly add our trilogy in the database. Let\u2019s remove the <code>_id<\/code> assignment on our third entity and let MongoDB automatically insert it for us.<\/p>\n<pre><code class=\"language-csharp\">var myMovie3 = new Movie { Title = \"The Rise of EF Core 3\" };<\/code><\/pre>\n<p>Once we re-run the program, we can see that all our entities have been added to the database.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/10\/mongodb-duplicate-exception-fix.png\" alt=\"All three movies in the trilogy in the database due to fixing the duplicate id issue\" \/><\/p>\n<h2>Summary<\/h2>\n<p>We were able to use the <a href=\"https:\/\/www.mongodb.com\/docs\/drivers\/csharp\/current\/\">MongoDB EF Core provider<\/a> with <a href=\"https:\/\/www.mongodb.com\/products\/platform\/atlas-database\">MongoDB Atlas<\/a> to showcase different capabilities like adding a property to an entity on the fly, leveraging the Escape Hatch to create an index, performing complex queries via LINQ, and demonstrating the newly added transactions and optimistic concurrency support.<\/p>\n<h2>Learn more<\/h2>\n<p>To learn more about EF Core and MongoDB:<\/p>\n<ul>\n<li>See the <a href=\"https:\/\/learn.microsoft.com\/ef\/core\/\">EF Core documentation<\/a> to learn more about using EF Core to access all kinds of databases.<\/li>\n<li>See the <a href=\"https:\/\/www.mongodb.com\/docs\/\">MongoDB documentation<\/a> to learn more about using MongoDB from any platform.<\/li>\n<li>See the <a href=\"https:\/\/www.mongodb.com\/docs\/entity-framework\/current\/quick-start\/\">MongoDB EF Core provider documentation<\/a> for more information on how to get started.<\/li>\n<li>Watch the talk about <a href=\"https:\/\/www.youtube.com\/watch?v=LuvdiUggQrU&amp;list=PLdo4fOcmZ0oUZz7p8H1HsQjgv5tRRIvAS&amp;index=19\">EF Core 9: Evolving Data Access in .NET<\/a> on the Microsoft Youtube channel.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>The latest updates to the MongoDB EF Core Provider brings updates to change tracking, index creation, complex queries, and transactions.<\/p>\n","protected":false},"author":172342,"featured_media":54128,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,859],"tags":[4,437,7694,7760],"class_list":["post-54127","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-entity-framework","tag-net","tag-data","tag-efcore","tag-mongodb"],"acf":[],"blog_post_summary":"<p>The latest updates to the MongoDB EF Core Provider brings updates to change tracking, index creation, complex queries, and transactions.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/54127","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/172342"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=54127"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/54127\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/54128"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=54127"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=54127"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=54127"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}