EF Core DbContext not using global GuidRepresentation

Hello,

I am trying to use the DbContext extension from MongoDb. I followed their documentation, but I am running into an issue and cannot find a good solution.

I am setting the global values for the GuidRepresentation to Standard at an early point during DI registration, and I am using the V3 Mode.

I debugged the code and found that the database created in the DbContext code doesn’t update the GuidRepresentation to Standard and keeps using the Unspecified.

Here is the actual exception:

An exception occurred while iterating over the results of a query for context type 'MongoDbContext'. MongoDB.Bson.BsonSerializationException: GuidSerializer cannot serialize a Guid when GuidRepresentation is Unspecified.
   at MongoDB.Bson.Serialization.Serializers.GuidSerializer.Serialize(BsonSerializationContext context, BsonSerializationArgs args, Guid value)
   at MongoDB.Bson.Serialization.Serializers.SerializerBase`1.MongoDB.Bson.Serialization.IBsonSerializer.Serialize(BsonSerializationContext context, BsonSerializationArgs args, Object value)

When I use the database/collection directly, I can insert and read without issues.

Thanks,
JF

1 Like

Hi there!

Since there wasn’t a reply to your message, I tried to run it by our documentation bot. Here is the response I got :slight_smile:


The error message you encountered indicates that the GuidSerializer is unable to serialize a Guid when the GuidRepresentation is unspecified. To fix this error in your DbContext, you need to specify the GuidRepresentation for the Guid property.

In the case of the MongoDB C# Driver, you can use the BsonGuidRepresentation attribute to specify the representation for the Guid property. Here’s an example of how to use it:

[BsonGuidRepresentation(GuidRepresentation.Standard)]public Guid MyGuid { get; set; }

In this example, the GuidRepresentation.Standard is used, which is equivalent to BsonBinaryData subtype 4. You can choose other GuidRepresentation values depending on your requirements.

Make sure to add the BsonGuidRepresentation attribute to all Guid properties in your DbContext that need to be serialized.

If you’re using an older version of the MongoDB C# Driver (version 2.x), you can set the GuidRepresentationMode to V3 before creating the client. This can be done using the following code:

BsonDefaults.GuidRepresentationMode = GuidRepresentationMode.V3;

Remember to specify the GuidRepresentation for each Guid property to avoid the BsonSerializationException.

I hope this helps! Let me know if you have any further questions.

Related resources:

Hi Joel,

Thank you for your response.

Since I am using DDD for my services, I can’t add an attribute to our Aggregate Roots, Entities, and Value Objects. That would pollute the Aggregates with concerns that are beyond the Domain.

Is there a way to set the GuidRepresentation directly (it seems like there was something like that, but it is now deprecated) to the Client/Database/Collection?

I hoped the global setting for the GuidRepresentation and GuidRepresentationMode would work with the DbContext.

Even though I am passing the explicitly instantiated Mongo client during DI, which should assume the global settings, the GuidRepresentation remains Unspecified.

I am unsure whether I can assign an attribute to the properties during the DbContext model configuration (DbContext mapping).

Thanks,
Jose

Hi Jose,

Although the EF Provider supports GuidRepresentation, it doesn’t at this moment in time support changing the representation mode.

We are actively working on expanding the features and capabilities of the provider though, and our engineers have confirmed that they plan to add support for changing representation modes in mid-September.

Keep an eye on our Docs and the GitHub repo for updates on this and new features though! We love hearing community feedback and what features you would like to see.

I’ve raised ticket EF-154 in our JIRA for this issue and it’s now under active investigation.

Thank you for your attention to this issue.

One suggestion to the team is to be able to set the GuidRepresentation and RepresentationMode during model configuration.

I see that you have .HasBsonRepresentation and BinaryData are one of them. Maybe you could create one for GuidRepresentation (.HasGuidRepresentation) or extend the one for BinaryData.

This addition will allow us to set the representation at the Property level without using Attributes. This feature will be handy (and honestly, the only way) for people who build software following the DDD approach where Persistence concerns are not part of the Domain.

How confident are you that we will have that feature released in mid-September? I ask because, as of now, every day we use CosmosDb, we have to pay a hefty sum per Container, so planning our migration to MongoDb and whether we use EF or plain old MongoDb client is vital.

Thank you all for the effort.

I think given that the old representations are somewhat deprecated the best thing here would probably be to switch to using V3/Standard by default and revisit whether we need to support any older formats again in the future.

1 Like

8.1.0 is now out which changes the format used by Guids to the standard binary format. As this is a breaking change you might need to write a small console app to convert from one format to the other if you need to preserve existing data.

The new update works perfectly fine. I was able to have consistent behavior with my GUIDs.

Thanks so much for making this fix and releasing it so quickly.

1 Like

I’m trying this out on a brand new project and i have this error on read:

System.FormatException: GuidSerializer cannot deserialize a Guid when GuidRepresentation is Standard and binary sub type is UuidLegacy.
   at MongoDB.Bson.Serialization.Serializers.GuidSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
   at MongoDB.Bson.Serialization.Serializers.SerializerBase`1.MongoDB.Bson.Serialization.IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
   at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize(IBsonSerializer serializer, BsonDeserializationContext context)
   at MongoDB.Bson.Serialization.BsonSerializationInfo.DeserializeValue(BsonValue value)
   at MongoDB.EntityFrameworkCore.Serializers.SerializationHelper.TryReadElementValue[T](BsonDocument document, BsonSerializationInfo elementSerializationInfo, T& value)
   at MongoDB.EntityFrameworkCore.Serializers.SerializationHelper.GetPropertyValue[T](BsonDocument document, IReadOnlyProperty property)
   at lambda_method41(Closure, QueryContext, BsonDocument)
   at MongoDB.EntityFrameworkCore.Query.QueryingEnumerable`2.Enumerator.MoveNextHelper()
   at MongoDB.EntityFrameworkCore.Query.QueryingEnumerable`2.Enumerator.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Program.<>c.<<Main>$>b__0_2(NotificationContext dbContext) in C:\DevProjects\TestProjects\MongoDbGuidProblems\TestApplication\Program.cs:line 45
   at lambda_method3(Closure, Object, HttpContext)
   at Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

My repro is very simple:

`
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var mongodbConnectionString = builder.Configuration.GetConnectionString(“testdb”)!;

builder.Services.AddDbContext(options =>
{
options.UseMongoDB(mongodbConnectionString, “testdb”);
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost(“/”, (Notification notification, NotificationContext dbContext) =>
{
notification.Id = Guid.NewGuid();
dbContext.Notifications.Add(notification);
dbContext.SaveChanges();
return Results.Created();
})
.WithName(“CreateNotification”)
.WithOpenApi();

app.MapGet(“/”, (NotificationContext dbContext) =>
{
var notifications = dbContext.Notifications.ToList();
return notifications;
})
.WithName(“GetNotificationList”)
.WithOpenApi();

var scope = app.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService();
dbContext.Database.EnsureCreated();

app.Run();

public class NotificationContext(DbContextOptions options) : DbContext(options)
{
public DbSet Notifications => Set();
}

public class Notification
{
public Guid Id { get; set; }
public required string Message { get; set; }
public bool IsRead { get; set; }
}
`

What do i do wrong here? It works perfectly on 8.0.x. The EF Add methods saves the guid using bin format but it’s trying to read standard i think?

Hi Jakub.

The path that sets up the V3 Guid mode is unfortunately on the query path and so what’s happening here is data is being inserted before it can be reconfigured. I have a fix for this in the next version but in the mean time you can work around this issue by adding the line:

var _ = db.Notifications.FirstOrDefault();

Into your app initialization once the DbContext is ready.

Apologies for the inconvenience and you’ll be able to remove this line when the next update ships.

Update: A PR to fix this is now up for review. https://jira.mongodb.org/browse/EF-163

Damien

No worries, do i have to do that for every entity or just on random one? And do you have some ETA on the new version?

Just one. No official ETA on the new version but I’m personally hoping for EOD tomorrow as there’s one more issue I want to see resolved.

1 Like

8.1.1 is now out which removes the need for that workaround.

Works perfectly, thanks!

1 Like