-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(Cosmos) Embedded entities (OwnsMany) throws an InvalidOperationException when calling SaveChangesAsync #24828
Comments
I've just realised I originally committed the workaround we need to put in place. I've pushed a commit that fully demonstrates our issue. Sorry about that. |
@AndriySvyryd Looks like this could be a problem with the Cosmos update pipeline. The exception happens when calling SaveChanges twice, where the second call should be a no-op. Minimal repro to be run with emulator: public class CosmosStoreDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseCosmos(
"AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
"ExampleScenarioForEntityWithEmbeddedCollection");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultContainer("CosmosStoreDbContext");
modelBuilder.Entity<Order>().OwnsMany(x => x.Items);
}
}
public record OrderItem
{
public string ItemName { get; set; }
public decimal ItemPrice { get; set; }
}
public record Order
{
public Guid Id { get; set; }
public string CustomerName { get; set; }
public ICollection<OrderItem> Items { get; set; } = new HashSet<OrderItem>();
}
public class Program
{
public static async Task Main()
{
using (var dbContext = new CosmosStoreDbContext())
{
await dbContext.Database.EnsureCreatedAsync();
dbContext.RemoveRange(dbContext.Set<Order>());
}
using (var dbContext = new CosmosStoreDbContext())
{
var bobOrder = dbContext.Add(new Order {CustomerName = "Bob"}).Entity;
await dbContext.SaveChangesAsync();
bobOrder.Items.Add(new OrderItem()
{
ItemName = "Foo",
ItemPrice = 10
});
await dbContext.SaveChangesAsync();
bobOrder.Items.Add(new OrderItem()
{
ItemName = "Bar",
ItemPrice = 20
});
await dbContext.SaveChangesAsync();
await dbContext.SaveChangesAsync();
}
}
} |
Thanks for acknowledging this and adding it to the backlog @ajcvickers In case anyone is looking for a workaround, in the meantime we're manually adding an "Id" (initialised to a new Guid) to the embedded entity ( modelBuilder.Entity<Order>().OwnsMany(x => x.Items).HasKey(item => item.Id);
modelBuilder.Entity<Order>().OwnsMany(x => x.Items).Property(item => item.Id).ValueGeneratedNever(); Our plan is to later remove these IDs as soon as EF supports it.
|
Hello! I'm running into this as well and have an observation that might help. However, if another embedded entity instance is added to the collection, after I modified @ajcvickers example below to demonstrate. public class CosmosStoreDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseCosmos(
"AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
"ExampleScenarioForEntityWithEmbeddedCollection");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultContainer("CosmosStoreDbContext");
modelBuilder.Entity<Order>().OwnsMany(x => x.Items);
}
}
public record OrderItem
{
public string ItemName { get; set; }
public decimal ItemPrice { get; set; }
}
public record Order
{
public Guid Id { get; set; }
public string CustomerName { get; set; }
public ICollection<OrderItem> Items { get; set; } = new HashSet<OrderItem>();
}
public class Program
{
public static async Task Main()
{
using (var dbContext = new CosmosStoreDbContext())
{
await dbContext.Database.EnsureCreatedAsync();
dbContext.RemoveRange(dbContext.Set<Order>());
}
using (var dbContext = new CosmosStoreDbContext())
{
var bobOrder = dbContext.Add(new Order {CustomerName = "Bob"}).Entity;
bobOrder.Items.Add(new OrderItem()
{
ItemName = "Foo",
ItemPrice = 10
});
bobOrder.Items.Add(new OrderItem()
{
ItemName = "Bar",
ItemPrice = 20
});
await dbContext.SaveChangesAsync();
}
using (var dbContext = new CosmosStoreDbContext())
{
var bobOrder = await dbContext.SingleAsync(o => o.CustomerName == "Bob");
bobOrder.Items.Add(new OrderItem()
{
ItemName = "Baz",
ItemPrice = 30
});
// Debugger show's order item id's as expected
await dbContext.SaveChangesAsync();
// Debugger show's order item id's as all zeroes except the latest one.
// Next save on the context throws exception because order item ids have changed.
await dbContext.SaveChangesAsync();
}
}
} |
Hi,
I've created the following repository with a small, runnable project that demonstrates the issue we're seeing, publicly available here: https://github.com/JamesBroadberry/EFCore.Cosmos.EntityWithEmbeddedCollection
The issue could potentially to be with how the ChangeTracker works (although my knowledge of EF isn't that strong).
The README in the repository has comprehensive information about the issue we're seeing but long story short, when adding multiple items to a HashSet on an Entity and calling SaveChangesAsync, we get the following stack trace:
This could be related to (or potentially fixed by the solution to) #24803.
If you need any more information from me, please let me know and I'll do my best to help.
Thanks,
James
The text was updated successfully, but these errors were encountered: