Skip to content

Commit

Permalink
Entity given to Attach/Update does not use key to determine state
Browse files Browse the repository at this point in the history
Issue #3890

When a graph of entities is given to Attach or Update, the state of those entities is set to Unchanged/Modified unless the entity is using key value generation and the key value is not set, in which case the state is set to Added. But for the actual entity passed to these methods (as opposed to additional entities discovered in the graph) we decided that the state should be the requested state regardless of whether or not the key value is set.
  • Loading branch information
ajcvickers committed Jan 20, 2016
1 parent cd54266 commit ad6e0b1
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ private static bool PaintAction(EntityEntryGraphNode n)
return false;
}

if (!internalEntityEntry.IsKeySet)
if (n.InboundNavigation != null
&& !internalEntityEntry.IsKeySet)
{
n.NodeState = EntityState.Added;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public void Attaching_aggregate_with_no_key_set_adds_it_instead()

context.Attach(blog);

Assert.Equal(EntityState.Added, context.Entry(blog).State);
Assert.Equal(EntityState.Unchanged, context.Entry(blog).State); // See Issue #3890
Assert.Equal(EntityState.Added, context.Entry(posts[0]).State);
Assert.Equal(EntityState.Added, context.Entry(posts[1]).State);
Assert.Equal(EntityState.Added, context.Entry(comments0[0]).State);
Expand All @@ -119,7 +119,7 @@ public void Attaching_one_to_one_aggregate_with_no_key_set_adds_it_instead()

context.Attach(category);

Assert.Equal(EntityState.Added, context.Entry(category).State);
Assert.Equal(EntityState.Unchanged, context.Entry(category).State); // See Issue #3890
Assert.Equal(EntityState.Added, context.Entry(category.Statistics).State);
}
}
Expand Down
202 changes: 200 additions & 2 deletions test/EntityFramework.Core.Tests/DbContextTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ private static void TrackEntitiesTest(
Assert.Same(product2, productEntry2.Entity);

Assert.Same(category1, categoryEntry1.Entity);
Assert.Equal(expectedState, categoryEntry2.State);
Assert.Equal(expectedState, categoryEntry1.State);
Assert.Same(category2, categoryEntry2.Entity);
Assert.Equal(expectedState, categoryEntry2.State);

Expand Down Expand Up @@ -480,6 +480,105 @@ private static void TrackMultipleEntitiesTest(
}
}

[Fact]
public void Can_add_existing_entities_with_default_value_to_context_to_be_deleted()
{
TrackEntitiesDefaultValueTest((c, e) => c.Remove(e), (c, e) => c.Remove(e), EntityState.Deleted);
}

[Fact]
public void Can_add_new_entities_with_default_value_to_context_with_graph_method()
{
TrackEntitiesDefaultValueTest((c, e) => c.Add(e), (c, e) => c.Add(e), EntityState.Added);
}

[Fact]
public void Can_add_existing_entities_with_default_value_to_context_to_be_attached_with_graph_method()
{
TrackEntitiesDefaultValueTest((c, e) => c.Attach(e), (c, e) => c.Attach(e), EntityState.Unchanged);
}

[Fact]
public void Can_add_existing_entities_with_default_value_to_context_to_be_updated_with_graph_method()
{
TrackEntitiesDefaultValueTest((c, e) => c.Update(e), (c, e) => c.Update(e), EntityState.Modified);
}

// Issue #3890
private static void TrackEntitiesDefaultValueTest(
Func<DbContext, Category, EntityEntry<Category>> categoryAdder,
Func<DbContext, Product, EntityEntry<Product>> productAdder, EntityState expectedState)
{
using (var context = new EarlyLearningCenter(TestHelpers.Instance.CreateServiceProvider()))
{
var category1 = new Category { Id = 0, Name = "Beverages" };
var product1 = new Product { Id = 0, Name = "Marmite", Price = 7.99m };

var categoryEntry1 = categoryAdder(context, category1);
var productEntry1 = productAdder(context, product1);

Assert.Same(category1, categoryEntry1.Entity);
Assert.Same(product1, productEntry1.Entity);

Assert.Same(category1, categoryEntry1.Entity);
Assert.Equal(expectedState, categoryEntry1.State);

Assert.Same(product1, productEntry1.Entity);
Assert.Equal(expectedState, productEntry1.State);

Assert.Same(categoryEntry1.GetInfrastructure(), context.Entry(category1).GetInfrastructure());
Assert.Same(productEntry1.GetInfrastructure(), context.Entry(product1).GetInfrastructure());
}
}

[Fact]
public void Can_add_multiple_new_entities_with_default_values_to_context()
{
TrackMultipleEntitiesDefaultValuesTest((c, e) => c.AddRange(e[0]), (c, e) => c.AddRange(e[0]), EntityState.Added);
}

[Fact]
public void Can_add_multiple_existing_entities_with_default_values_to_context_to_be_attached()
{
TrackMultipleEntitiesDefaultValuesTest((c, e) => c.AttachRange(e[0]), (c, e) => c.AttachRange(e[0]), EntityState.Unchanged);
}

[Fact]
public void Can_add_multiple_existing_entities_with_default_values_to_context_to_be_updated()
{
TrackMultipleEntitiesDefaultValuesTest((c, e) => c.UpdateRange(e[0]), (c, e) => c.UpdateRange(e[0]), EntityState.Modified);
}

[Fact]
public void Can_add_multiple_existing_entities_with_default_values_to_context_to_be_deleted()
{
TrackMultipleEntitiesDefaultValuesTest((c, e) => c.RemoveRange(e[0]), (c, e) => c.RemoveRange(e[0]), EntityState.Deleted);
}

// Issue #3890
private static void TrackMultipleEntitiesDefaultValuesTest(
Action<DbContext, object[]> categoryAdder,
Action<DbContext, object[]> productAdder, EntityState expectedState)
{
using (var context = new EarlyLearningCenter(TestHelpers.Instance.CreateServiceProvider()))
{
var category1 = new Category { Id = 0, Name = "Beverages" };
var product1 = new Product { Id = 0, Name = "Marmite", Price = 7.99m };

categoryAdder(context, new[] { category1 });
productAdder(context, new[] { product1 });

Assert.Same(category1, context.Entry(category1).Entity);
Assert.Same(product1, context.Entry(product1).Entity);

Assert.Same(category1, context.Entry(category1).Entity);
Assert.Equal(expectedState, context.Entry(category1).State);

Assert.Same(product1, context.Entry(product1).Entity);
Assert.Equal(expectedState, context.Entry(product1).State);
}
}

[Fact]
public void Can_add_no_new_entities_to_context()
{
Expand Down Expand Up @@ -560,7 +659,7 @@ private static void TrackEntitiesTestNonGeneric(
Assert.Same(product2, productEntry2.Entity);

Assert.Same(category1, categoryEntry1.Entity);
Assert.Equal(expectedState, categoryEntry2.State);
Assert.Equal(expectedState, categoryEntry1.State);
Assert.Same(category2, categoryEntry2.Entity);
Assert.Equal(expectedState, categoryEntry2.State);

Expand Down Expand Up @@ -631,6 +730,105 @@ private static void TrackMultipleEntitiesTestEnumerable(
}
}

[Fact]
public void Can_add_existing_entities_with_default_value_to_context_to_be_deleted_non_generic()
{
TrackEntitiesDefaultValuesTestNonGeneric((c, e) => c.Remove(e), (c, e) => c.Remove(e), EntityState.Deleted);
}

[Fact]
public void Can_add_new_entities_with_default_value_to_context_non_generic_graph()
{
TrackEntitiesDefaultValuesTestNonGeneric((c, e) => c.Add(e), (c, e) => c.Add(e), EntityState.Added);
}

[Fact]
public void Can_add_existing_entities_with_default_value_to_context_to_be_attached_non_generic_graph()
{
TrackEntitiesDefaultValuesTestNonGeneric((c, e) => c.Attach(e), (c, e) => c.Attach(e), EntityState.Unchanged);
}

[Fact]
public void Can_add_existing_entities_with_default_value_to_context_to_be_updated_non_generic_graph()
{
TrackEntitiesDefaultValuesTestNonGeneric((c, e) => c.Update(e), (c, e) => c.Update(e), EntityState.Modified);
}

// Issue #3890
private static void TrackEntitiesDefaultValuesTestNonGeneric(
Func<DbContext, object, EntityEntry> categoryAdder,
Func<DbContext, object, EntityEntry> productAdder, EntityState expectedState)
{
using (var context = new EarlyLearningCenter(TestHelpers.Instance.CreateServiceProvider()))
{
var category1 = new Category { Id = 0, Name = "Beverages" };
var product1 = new Product { Id = 0, Name = "Marmite", Price = 7.99m };

var categoryEntry1 = categoryAdder(context, category1);
var productEntry1 = productAdder(context, product1);

Assert.Same(category1, categoryEntry1.Entity);
Assert.Same(product1, productEntry1.Entity);

Assert.Same(category1, categoryEntry1.Entity);
Assert.Equal(expectedState, categoryEntry1.State);

Assert.Same(product1, productEntry1.Entity);
Assert.Equal(expectedState, productEntry1.State);

Assert.Same(categoryEntry1.GetInfrastructure(), context.Entry(category1).GetInfrastructure());
Assert.Same(productEntry1.GetInfrastructure(), context.Entry(product1).GetInfrastructure());
}
}

[Fact]
public void Can_add_multiple_existing_entities_with_default_values_to_context_to_be_deleted_Enumerable()
{
TrackMultipleEntitiesDefaultValueTestEnumerable((c, e) => c.RemoveRange(e), (c, e) => c.RemoveRange(e), EntityState.Deleted);
}

[Fact]
public void Can_add_multiple_new_entities_with_default_values_to_context_Enumerable_graph()
{
TrackMultipleEntitiesDefaultValueTestEnumerable((c, e) => c.AddRange(e), (c, e) => c.AddRange(e), EntityState.Added);
}

[Fact]
public void Can_add_multiple_existing_entities_with_default_values_to_context_to_be_attached_Enumerable_graph()
{
TrackMultipleEntitiesDefaultValueTestEnumerable((c, e) => c.AttachRange(e), (c, e) => c.AttachRange(e), EntityState.Unchanged);
}

[Fact]
public void Can_add_multiple_existing_entities_with_default_values_to_context_to_be_updated_Enumerable_graph()
{
TrackMultipleEntitiesDefaultValueTestEnumerable((c, e) => c.UpdateRange(e), (c, e) => c.UpdateRange(e), EntityState.Modified);
}

// Issue #3890
private static void TrackMultipleEntitiesDefaultValueTestEnumerable(
Action<DbContext, IEnumerable<object>> categoryAdder,
Action<DbContext, IEnumerable<object>> productAdder, EntityState expectedState)
{
using (var context = new EarlyLearningCenter(TestHelpers.Instance.CreateServiceProvider()))
{
var category1 = new Category { Id = 0, Name = "Beverages" };
var product1 = new Product { Id = 0, Name = "Marmite", Price = 7.99m };

categoryAdder(context, new List<Category> { category1 });
productAdder(context, new List<Product> { product1 });

Assert.Same(category1, context.Entry(category1).Entity);
Assert.Same(product1, context.Entry(product1).Entity);

Assert.Same(category1, context.Entry(category1).Entity);
Assert.Equal(expectedState, context.Entry(category1).State);

Assert.Same(product1, context.Entry(product1).Entity);
Assert.Equal(expectedState, context.Entry(product1).State);
}
}

[Fact]
public void Can_add_no_existing_entities_to_context_to_be_deleted_Enumerable()
{
Expand Down

0 comments on commit ad6e0b1

Please sign in to comment.