Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion src/EFCore.Cosmos/Update/Internal/DocumentSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,30 @@ private static void SetTemporaryOrdinals(

private static JToken? ConvertPropertyValue(IProperty property, IUpdateEntry entry)
{
var value = entry.GetCurrentProviderValue(property);
object? value;

// For discriminator properties, use the discriminator value from the entity type
// to ensure consistency, rather than relying on the tracked property value
var discriminatorProperty = entry.EntityType.FindDiscriminatorProperty();
if (discriminatorProperty != null && property == discriminatorProperty)
{
var discriminatorValue = entry.EntityType.GetDiscriminatorValue();
if (discriminatorValue != null)
{
// If there's a converter, apply it to convert the discriminator value to the provider value
var converter = property.GetTypeMapping().Converter;
value = converter?.ConvertToProvider(discriminatorValue) ?? discriminatorValue;
}
else
{
value = entry.GetCurrentProviderValue(property);
}
}
else
{
value = entry.GetCurrentProviderValue(property);
}

return value == null
? null
: (value as JToken) ?? JToken.FromObject(value, CosmosClientWrapper.Serializer);
Expand Down
90 changes: 90 additions & 0 deletions test/EFCore.Cosmos.FunctionalTests/EnumDiscriminatorCosmosTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.EntityFrameworkCore.TestUtilities;

namespace Microsoft.EntityFrameworkCore;

#nullable disable

public class EnumDiscriminatorCosmosTest(NonSharedFixture fixture) : NonSharedModelTestBase(fixture), IClassFixture<NonSharedFixture>
{
protected override string StoreName
=> "EnumDiscriminatorCosmosTest";

protected override ITestStoreFactory TestStoreFactory
=> CosmosTestStoreFactory.Instance;
[ConditionalFact]
public async Task Enum_discriminator_saved_as_string_consistently()
{
var contextFactory = await InitializeAsync<MyDbContext>(
shouldLogCategory: _ => true);

var thingy = new DerivedThingy2
{
Id = "A",
Name = "A",
Type = ThingyType.ThingyTwo
};

using (var context = contextFactory.CreateContext())
{
context.Things.Add(thingy);
await context.SaveChangesAsync();
}

using (var context = contextFactory.CreateContext())
{
var entity = context.Things.Single(x => x.Id == "A");
entity.Name = "A updated";
await context.SaveChangesAsync();

// Verify entity can still be found after update
var reloadedEntity = context.Things.Single(x => x.Id == "A");
Assert.Equal("A updated", reloadedEntity.Name);
Assert.Equal(ThingyType.ThingyTwo, reloadedEntity.Type);
}
}

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

public DbSet<Thingy> Things { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Thingy>(b =>
{
b.HasKey(e => e.Id);
b.Property(e => e.Type)
.HasConversion<string>();
b.HasDiscriminator(e => e.Type)
.HasValue<DerivedThingy2>(ThingyType.ThingyTwo);
});

modelBuilder.Entity<DerivedThingy2>();
}
}

public enum ThingyType
{
ThingyOne = 0,
ThingyTwo = 1
}

public class Thingy
{
public string Id { get; set; }
public string Name { get; set; }
public ThingyType Type { get; set; }
}

public class DerivedThingy2 : Thingy
{
public string SomethingForB { get; set; }
}
}