Skip to content
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

Add ValueGenerated.OnUpdate #8536

Closed
wants to merge 1 commit into from
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
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,9 @@ protected virtual void GenerateProperty(
.Append(
property.ValueGenerated == ValueGenerated.OnAdd
? ".ValueGeneratedOnAdd()"
: ".ValueGeneratedOnAddOrUpdate()");
: property.ValueGenerated == ValueGenerated.OnUpdate
? ".ValueGeneratedOnUpdate()"
: ".ValueGeneratedOnAddOrUpdate()");
}

GeneratePropertyAnnotations(property, stringBuilder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,10 @@ public virtual void AddValueGeneratedConfiguration(
methodName = nameof(PropertyBuilder.ValueGeneratedOnAdd);
break;

case ValueGenerated.OnUpdate:
methodName = nameof(PropertyBuilder.ValueGeneratedOnUpdate);
break;

case ValueGenerated.OnAddOrUpdate:
methodName = nameof(PropertyBuilder.ValueGeneratedOnAddOrUpdate);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,11 @@ protected virtual PropertyBuilder VisitColumn([NotNull] EntityTypeBuilder builde
property.ValueGeneratedOnAdd();
}

if (column.ValueGenerated == ValueGenerated.OnUpdate)
{
property.ValueGeneratedOnUpdate();
}

if (column.ValueGenerated == ValueGenerated.OnAddOrUpdate)
{
property.ValueGeneratedOnAddOrUpdate();
Expand Down
96 changes: 95 additions & 1 deletion src/EFCore.Specification.Tests/StoreGeneratedTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ protected StoreGeneratedTestBase(TFixture fixture)
[InlineData("OnAddOrUpdateThrowBeforeUseAfter")]
[InlineData("OnAddOrUpdateThrowBeforeIgnoreAfter")]
[InlineData("OnAddOrUpdateThrowBeforeThrowAfter")]
[InlineData("OnUpdateThrowBeforeUseAfter")]
[InlineData("OnUpdateThrowBeforeIgnoreAfter")]
[InlineData("OnUpdateThrowBeforeThrowAfter")]
public virtual void Before_save_throw_always_throws_if_value_set(string propertyName)
{
ExecuteWithStrategyInTransaction(
Expand All @@ -55,6 +58,9 @@ public virtual void Before_save_throw_always_throws_if_value_set(string property
[InlineData("OnAddOrUpdateThrowBeforeUseAfter", "Rabbit")]
[InlineData("OnAddOrUpdateThrowBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnAddOrUpdateThrowBeforeThrowAfter", "Rabbit")]
[InlineData("OnUpdateThrowBeforeUseAfter", "Rabbit")]
[InlineData("OnUpdateThrowBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnUpdateThrowBeforeThrowAfter", "Rabbit")]
public virtual void Before_save_throw_ignores_value_if_not_set(string propertyName, string expectedValue)
{
var id = 0;
Expand All @@ -73,6 +79,7 @@ public virtual void Before_save_throw_ignores_value_if_not_set(string propertyNa
[Theory]
[InlineData("Never")]
[InlineData("OnAdd")]
[InlineData("OnUpdate")]
[InlineData("NeverUseBeforeUseAfter")]
[InlineData("NeverUseBeforeIgnoreAfter")]
[InlineData("NeverUseBeforeThrowAfter")]
Expand All @@ -82,6 +89,9 @@ public virtual void Before_save_throw_ignores_value_if_not_set(string propertyNa
[InlineData("OnAddOrUpdateUseBeforeUseAfter")]
[InlineData("OnAddOrUpdateUseBeforeIgnoreAfter")]
[InlineData("OnAddOrUpdateUseBeforeThrowAfter")]
[InlineData("OnUpdateUseBeforeUseAfter")]
[InlineData("OnUpdateUseBeforeIgnoreAfter")]
[InlineData("OnUpdateUseBeforeThrowAfter")]
public virtual void Before_save_use_always_uses_value_if_set(string propertyName)
{
var id = 0;
Expand All @@ -100,6 +110,7 @@ public virtual void Before_save_use_always_uses_value_if_set(string propertyName
[Theory]
[InlineData("Never", null)]
[InlineData("OnAdd", "Rabbit")]
[InlineData("OnUpdate", null)]
[InlineData("NeverUseBeforeUseAfter", null)]
[InlineData("NeverUseBeforeIgnoreAfter", null)]
[InlineData("NeverUseBeforeThrowAfter", null)]
Expand All @@ -109,6 +120,9 @@ public virtual void Before_save_use_always_uses_value_if_set(string propertyName
[InlineData("OnAddOrUpdateUseBeforeUseAfter", "Rabbit")]
[InlineData("OnAddOrUpdateUseBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnAddOrUpdateUseBeforeThrowAfter", "Rabbit")]
[InlineData("OnUpdateUseBeforeUseAfter", null)]
[InlineData("OnUpdateUseBeforeIgnoreAfter", null)]
[InlineData("OnUpdateUseBeforeThrowAfter", null)]
public virtual void Before_save_use_ignores_value_if_not_set(string propertyName, string expectedValue)
{
var id = 0;
Expand All @@ -135,6 +149,9 @@ public virtual void Before_save_use_ignores_value_if_not_set(string propertyName
[InlineData("OnAddOrUpdateIgnoreBeforeUseAfter", "Rabbit")]
[InlineData("OnAddOrUpdateIgnoreBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnAddOrUpdateIgnoreBeforeThrowAfter", "Rabbit")]
[InlineData("OnUpdateIgnoreBeforeUseAfter", "Rabbit")]
[InlineData("OnUpdateIgnoreBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnUpdateIgnoreBeforeThrowAfter", "Rabbit")]
public virtual void Before_save_ignore_ignores_value_if_not_set(string propertyName, string expectedValue)
{
var id = 0;
Expand All @@ -161,6 +178,9 @@ public virtual void Before_save_ignore_ignores_value_if_not_set(string propertyN
[InlineData("OnAddOrUpdateIgnoreBeforeUseAfter", "Rabbit")]
[InlineData("OnAddOrUpdateIgnoreBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnAddOrUpdateIgnoreBeforeThrowAfter", "Rabbit")]
[InlineData("OnUpdateIgnoreBeforeUseAfter", "Rabbit")]
[InlineData("OnUpdateIgnoreBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnUpdateIgnoreBeforeThrowAfter", "Rabbit")]
public virtual void Before_save_ignore_ignores_value_even_if_set(string propertyName, string expectedValue)
{
var id = 0;
Expand All @@ -186,6 +206,9 @@ public virtual void Before_save_ignore_ignores_value_even_if_set(string property
[InlineData("OnAddOrUpdateUseBeforeThrowAfter")]
[InlineData("OnAddOrUpdateIgnoreBeforeThrowAfter")]
[InlineData("OnAddOrUpdateThrowBeforeThrowAfter")]
[InlineData("OnUpdateUseBeforeThrowAfter")]
[InlineData("OnUpdateIgnoreBeforeThrowAfter")]
[InlineData("OnUpdateThrowBeforeThrowAfter")]
public virtual void After_save_throw_always_throws_if_value_modified(string propertyName)
{
ExecuteWithStrategyInTransaction(
Expand All @@ -209,6 +232,9 @@ public virtual void After_save_throw_always_throws_if_value_modified(string prop
[InlineData("OnAddOrUpdateUseBeforeThrowAfter", "Rabbit")]
[InlineData("OnAddOrUpdateIgnoreBeforeThrowAfter", "Rabbit")]
[InlineData("OnAddOrUpdateThrowBeforeThrowAfter", "Rabbit")]
[InlineData("OnUpdateUseBeforeThrowAfter", null)]
[InlineData("OnUpdateIgnoreBeforeThrowAfter", "Rabbit")]
[InlineData("OnUpdateThrowBeforeThrowAfter", "Rabbit")]
public virtual void After_save_throw_ignores_value_if_not_modified(string propertyName, string expectedValue)
{
var id = 0;
Expand All @@ -230,11 +256,15 @@ public virtual void After_save_throw_ignores_value_if_not_modified(string proper

context.SaveChanges();
},
context => { Assert.Equal(expectedValue, GetValue(context.Anaises.Find(id), propertyName)); });
context =>
{
Assert.Equal(expectedValue, GetValue(context.Anaises.Find(id), propertyName));
});
}

[Theory]
[InlineData("OnAddOrUpdate", "Rabbit")]
[InlineData("OnUpdate", null)]
[InlineData("NeverUseBeforeIgnoreAfter", null)]
[InlineData("NeverIgnoreBeforeIgnoreAfter", null)]
[InlineData("NeverThrowBeforeIgnoreAfter", null)]
Expand All @@ -244,6 +274,9 @@ public virtual void After_save_throw_ignores_value_if_not_modified(string proper
[InlineData("OnAddOrUpdateUseBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnAddOrUpdateIgnoreBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnAddOrUpdateThrowBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnUpdateUseBeforeIgnoreAfter", null)]
[InlineData("OnUpdateIgnoreBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnUpdateThrowBeforeIgnoreAfter", "Rabbit")]
public virtual void After_save_ignore_ignores_value_if_not_modified(string propertyName, string expectedValue)
{
var id = 0;
Expand All @@ -270,6 +303,7 @@ public virtual void After_save_ignore_ignores_value_if_not_modified(string prope

[Theory]
[InlineData("OnAddOrUpdate", "Rabbit")]
[InlineData("OnUpdate", null)]
[InlineData("NeverUseBeforeIgnoreAfter", null)]
[InlineData("NeverIgnoreBeforeIgnoreAfter", null)]
[InlineData("NeverThrowBeforeIgnoreAfter", null)]
Expand All @@ -279,6 +313,9 @@ public virtual void After_save_ignore_ignores_value_if_not_modified(string prope
[InlineData("OnAddOrUpdateUseBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnAddOrUpdateIgnoreBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnAddOrUpdateThrowBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnUpdateUseBeforeIgnoreAfter", null)]
[InlineData("OnUpdateIgnoreBeforeIgnoreAfter", "Rabbit")]
[InlineData("OnUpdateThrowBeforeIgnoreAfter", "Rabbit")]
public virtual void After_save_ignore_ignores_value_even_if_modified(string propertyName, string expectedValue)
{
var id = 0;
Expand Down Expand Up @@ -306,6 +343,8 @@ public virtual void After_save_ignore_ignores_value_even_if_modified(string prop
[Theory]
[InlineData("Never", null)]
[InlineData("OnAdd", "Rabbit")]
[InlineData("OnAddOrUpdate", "Rabbit")]
[InlineData("OnUpdate", null)]
[InlineData("NeverUseBeforeUseAfter", null)]
[InlineData("NeverIgnoreBeforeUseAfter", null)]
[InlineData("NeverThrowBeforeUseAfter", null)]
Expand All @@ -315,6 +354,9 @@ public virtual void After_save_ignore_ignores_value_even_if_modified(string prop
[InlineData("OnAddOrUpdateUseBeforeUseAfter", "Rabbit")]
[InlineData("OnAddOrUpdateIgnoreBeforeUseAfter", "Rabbit")]
[InlineData("OnAddOrUpdateThrowBeforeUseAfter", "Rabbit")]
[InlineData("OnUpdateUseBeforeUseAfter", null)]
[InlineData("OnUpdateIgnoreBeforeUseAfter", "Rabbit")]
[InlineData("OnUpdateThrowBeforeUseAfter", "Rabbit")]
public virtual void After_save_use_ignores_value_if_not_modified(string propertyName, string expectedValue)
{
var id = 0;
Expand Down Expand Up @@ -351,6 +393,9 @@ public virtual void After_save_use_ignores_value_if_not_modified(string property
[InlineData("OnAddOrUpdateUseBeforeUseAfter", "Daisy")]
[InlineData("OnAddOrUpdateIgnoreBeforeUseAfter", "Daisy")]
[InlineData("OnAddOrUpdateThrowBeforeUseAfter", "Daisy")]
[InlineData("OnUpdateUseBeforeUseAfter", "Daisy")]
[InlineData("OnUpdateIgnoreBeforeUseAfter", "Daisy")]
[InlineData("OnUpdateThrowBeforeUseAfter", "Daisy")]
public virtual void After_save_use_uses_value_if_modified(string propertyName, string expectedValue)
{
var id = 0;
Expand Down Expand Up @@ -1094,6 +1139,17 @@ protected class Anais
public string OnAddOrUpdateUseBeforeThrowAfter { get; set; }
public string OnAddOrUpdateIgnoreBeforeThrowAfter { get; set; }
public string OnAddOrUpdateThrowBeforeThrowAfter { get; set; }

public string OnUpdate { get; set; }
public string OnUpdateUseBeforeUseAfter { get; set; }
public string OnUpdateIgnoreBeforeUseAfter { get; set; }
public string OnUpdateThrowBeforeUseAfter { get; set; }
public string OnUpdateUseBeforeIgnoreAfter { get; set; }
public string OnUpdateIgnoreBeforeIgnoreAfter { get; set; }
public string OnUpdateThrowBeforeIgnoreAfter { get; set; }
public string OnUpdateUseBeforeThrowAfter { get; set; }
public string OnUpdateIgnoreBeforeThrowAfter { get; set; }
public string OnUpdateThrowBeforeThrowAfter { get; set; }
}

protected class StoreGeneratedContext : DbContext
Expand Down Expand Up @@ -1319,6 +1375,44 @@ protected virtual void OnModelCreating(ModelBuilder modelBuilder)
property = b.Property(e => e.OnAddOrUpdateThrowBeforeThrowAfter).ValueGeneratedOnAddOrUpdate().Metadata;
property.BeforeSaveBehavior = PropertyValueBehavior.Throw;
property.AfterSaveBehavior = PropertyValueBehavior.Throw;

b.Property(e => e.OnUpdate).ValueGeneratedOnUpdate();

property = b.Property(e => e.OnUpdateUseBeforeUseAfter).ValueGeneratedOnUpdate().Metadata;
property.BeforeSaveBehavior = PropertyValueBehavior.UseValue;
property.AfterSaveBehavior = PropertyValueBehavior.UseValue;

property = b.Property(e => e.OnUpdateIgnoreBeforeUseAfter).ValueGeneratedOnUpdate().Metadata;
property.BeforeSaveBehavior = PropertyValueBehavior.Ignore;
property.AfterSaveBehavior = PropertyValueBehavior.UseValue;

property = b.Property(e => e.OnUpdateThrowBeforeUseAfter).ValueGeneratedOnUpdate().Metadata;
property.BeforeSaveBehavior = PropertyValueBehavior.Throw;
property.AfterSaveBehavior = PropertyValueBehavior.UseValue;

property = b.Property(e => e.OnUpdateUseBeforeIgnoreAfter).ValueGeneratedOnUpdate().Metadata;
property.BeforeSaveBehavior = PropertyValueBehavior.UseValue;
property.AfterSaveBehavior = PropertyValueBehavior.Ignore;

property = b.Property(e => e.OnUpdateIgnoreBeforeIgnoreAfter).ValueGeneratedOnUpdate().Metadata;
property.BeforeSaveBehavior = PropertyValueBehavior.Ignore;
property.AfterSaveBehavior = PropertyValueBehavior.Ignore;

property = b.Property(e => e.OnUpdateThrowBeforeIgnoreAfter).ValueGeneratedOnUpdate().Metadata;
property.BeforeSaveBehavior = PropertyValueBehavior.Throw;
property.AfterSaveBehavior = PropertyValueBehavior.Ignore;

property = b.Property(e => e.OnUpdateUseBeforeThrowAfter).ValueGeneratedOnUpdate().Metadata;
property.BeforeSaveBehavior = PropertyValueBehavior.UseValue;
property.AfterSaveBehavior = PropertyValueBehavior.Throw;

property = b.Property(e => e.OnUpdateIgnoreBeforeThrowAfter).ValueGeneratedOnUpdate().Metadata;
property.BeforeSaveBehavior = PropertyValueBehavior.Ignore;
property.AfterSaveBehavior = PropertyValueBehavior.Throw;

property = b.Property(e => e.OnUpdateThrowBeforeThrowAfter).ValueGeneratedOnUpdate().Metadata;
property.BeforeSaveBehavior = PropertyValueBehavior.Throw;
property.AfterSaveBehavior = PropertyValueBehavior.Throw;
});
}
}
Expand Down
18 changes: 9 additions & 9 deletions src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -955,15 +955,15 @@ public virtual void DiscardStoreGeneratedValues()
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual bool IsStoreGenerated(IProperty property)
=> property.ValueGenerated != ValueGenerated.Never
&& ((EntityState == EntityState.Added
&& (property.BeforeSaveBehavior == PropertyValueBehavior.Ignore
|| HasTemporaryValue(property)
|| HasDefaultValue(property)))
|| (property.ValueGenerated == ValueGenerated.OnAddOrUpdate
&& EntityState == EntityState.Modified
&& (property.AfterSaveBehavior == PropertyValueBehavior.Ignore
|| !IsModified(property))));
=> (property.ValueGenerated.ForAdd() &&
EntityState == EntityState.Added
&& (property.BeforeSaveBehavior == PropertyValueBehavior.Ignore
|| HasTemporaryValue(property)
|| HasDefaultValue(property)))
|| (property.ValueGenerated.ForUpdate()
&& EntityState == EntityState.Modified
&& (property.AfterSaveBehavior == PropertyValueBehavior.Ignore
|| !IsModified(property)));

private bool HasDefaultValue(IProperty property)
=> property.ClrType.IsDefaultValue(this[property]);
Expand Down
15 changes: 12 additions & 3 deletions src/EFCore/Metadata/Builders/PropertyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,7 @@ public virtual PropertyBuilder ValueGeneratedOnAdd()
}

/// <summary>
/// Configures a property to have a value generated when saving a new or existing entity, unless
/// a non-null, non-temporary value has been set for a new entity, or the existing property value has
/// been modified for an existing entity, in which case the set value will be saved instead.
/// Configures a property to have a value generated when saving a new or existing entity.
/// </summary>
/// <returns> The same builder instance so that multiple configuration calls can be chained. </returns>
public virtual PropertyBuilder ValueGeneratedOnAddOrUpdate()
Expand All @@ -269,6 +267,17 @@ public virtual PropertyBuilder ValueGeneratedOnAddOrUpdate()
return this;
}

/// <summary>
/// Configures a property to have a value generated when saving an existing entity.
/// </summary>
/// <returns> The same builder instance so that multiple configuration calls can be chained. </returns>
public virtual PropertyBuilder ValueGeneratedOnUpdate()
{
Builder.ValueGenerated(ValueGenerated.OnUpdate, ConfigurationSource.Explicit);

return this;
}

/// <summary>
/// <para>
/// Sets the backing field to use for this property.
Expand Down
11 changes: 8 additions & 3 deletions src/EFCore/Metadata/Builders/PropertyBuilder`.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,19 @@ public PropertyBuilder([NotNull] InternalPropertyBuilder builder)
=> (PropertyBuilder<TProperty>)base.ValueGeneratedOnAdd();

/// <summary>
/// Configures a property to have a value generated only when saving a new or existing entity, unless
/// a non-null, non-temporary value has been set for a new entity, or the existing property value has
/// been modified for an existing entity, in which case the set value will be saved instead.
/// Configures a property to have a value generated when saving a new or existing entity.
/// </summary>
/// <returns> The same builder instance so that multiple configuration calls can be chained. </returns>
public new virtual PropertyBuilder<TProperty> ValueGeneratedOnAddOrUpdate()
=> (PropertyBuilder<TProperty>)base.ValueGeneratedOnAddOrUpdate();

/// <summary>
/// Configures a property to have a value generated when saving an existing entity.
/// </summary>
/// <returns> The same builder instance so that multiple configuration calls can be chained. </returns>
public new virtual PropertyBuilder<TProperty> ValueGeneratedOnUpdate()
=> (PropertyBuilder<TProperty>)base.ValueGeneratedOnUpdate();

/// <summary>
/// <para>
/// Sets the backing field to use for this property.
Expand Down
Loading