Skip to content
Merged
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
11 changes: 10 additions & 1 deletion src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,16 @@ private IUpdateAdapter AddModelData()
foreach (var targetSeed in entityType.GetSeedData())
{
var runtimeEntityType = updateAdapter.Model.FindEntityType(entityType.Name)!;
var entry = updateAdapter.CreateEntry(targetSeed, runtimeEntityType);
var values = new Dictionary<IProperty, object?>();
foreach (var (name, value) in targetSeed)
{
if (runtimeEntityType.FindProperty(name) is { } property)
{
values[property] = value;
}
}

var entry = updateAdapter.CreateEntry(values, runtimeEntityType);
entry.EntityState = EntityState.Added;
}
}
Expand Down
16 changes: 15 additions & 1 deletion src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public virtual bool EnsureCreated(
foreach (var targetSeed in entityType.GetSeedData())
{
targetEntityType ??= updateAdapter.Model.FindEntityType(entityType.Name)!;
var entry = updateAdapter.CreateEntry(targetSeed, targetEntityType);
var entry = updateAdapter.CreateEntry(GetValues(targetSeed, targetEntityType), targetEntityType);
entry.EntityState = EntityState.Added;
entries.Add(entry);
}
Expand All @@ -87,6 +87,20 @@ public virtual bool EnsureCreated(
}
}

private static Dictionary<IProperty, object?> GetValues(IDictionary<string, object?> seed, IEntityType entityType)
{
var values = new Dictionary<IProperty, object?>();
foreach (var (name, value) in seed)
{
if (entityType.FindProperty(name) is { } property)
{
values[property] = value;
}
}

return values;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
9 changes: 9 additions & 0 deletions src/EFCore/ChangeTracking/Internal/IStateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,17 @@ public interface IStateManager : IResettableService
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[Obsolete("Use the overload that accepts a dictionary keyed by " + nameof(IProperty) + " instead.")]
InternalEntityEntry CreateEntry(IDictionary<string, object?> values, IEntityType entityType);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
InternalEntityEntry CreateEntry(IReadOnlyDictionary<IProperty, object?> values, IEntityType entityType);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
70 changes: 70 additions & 0 deletions src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public InternalEntityEntry(
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[Obsolete("Use the overload that accepts a dictionary keyed by " + nameof(IProperty) + " instead.")]
public InternalEntityEntry(
IStateManager stateManager,
IEntityType entityType,
Expand All @@ -83,6 +84,75 @@ public InternalEntityEntry(
new MaterializationContext(new ValueBuffer(valuesArray), stateManager.Context));
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public InternalEntityEntry(
IStateManager stateManager,
IEntityType entityType,
IReadOnlyDictionary<IProperty, object?> values,
IStructuralTypeMaterializerSource entityMaterializerSource)
: base((IRuntimeTypeBase)entityType, GetShadowValues(entityType, values))
{
StateManager = stateManager;
var valuesArray = new object?[EntityType.PropertyCount];
foreach (var property in entityType.GetFlattenedProperties())
{
var index = property.GetIndex();
if (index >= 0)
{
valuesArray[index] = property.Sentinel;
}
}

foreach (var (property, value) in values)
{
var index = property.GetIndex();
if (index < 0)
{
continue;
}

if (!property.DeclaringType.ContainingEntityType.IsAssignableFrom(entityType))
{
throw new InvalidOperationException(
CoreStrings.PropertyDoesNotBelong(
property.Name,
property.DeclaringType.ContainingEntityType.DisplayName(),
entityType.DisplayName()));
}

valuesArray[index] = value;
}
Comment thread
AndriySvyryd marked this conversation as resolved.
Comment thread
AndriySvyryd marked this conversation as resolved.

Entity = entityType.GetOrCreateMaterializer(entityMaterializerSource)(
new MaterializationContext(new ValueBuffer(valuesArray), stateManager.Context));
}

private static IDictionary<string, object?> GetShadowValues(IEntityType entityType, IReadOnlyDictionary<IProperty, object?> values)
{
var shadowValues = new Dictionary<string, object?>();
if (((IRuntimeTypeBase)entityType).ShadowPropertyCount == 0)
{
return shadowValues;
}

// Shadow values are keyed by name because shadow properties are unique by name on an entity type;
// shadow properties on complex types are not supported (see #35613).
foreach (var (property, value) in values)
{
if (property.IsShadowProperty())
{
shadowValues[property.Name] = value;
}
}

return shadowValues;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
18 changes: 18 additions & 0 deletions src/EFCore/ChangeTracking/Internal/StateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,25 @@ public virtual InternalEntityEntry GetOrCreateEntry(object entity, IEntityType?
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[Obsolete("Use the overload that accepts a dictionary keyed by " + nameof(IProperty) + " instead.")]
public virtual InternalEntityEntry CreateEntry(IDictionary<string, object?> values, IEntityType entityType)
{
#pragma warning disable CS0618 // Type or member is obsolete
var entry = new InternalEntityEntry(this, entityType, values, EntityMaterializerSource);
#pragma warning restore CS0618 // Type or member is obsolete

UpdateReferenceMaps(entry, EntityState.Detached, null);

return entry;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual InternalEntityEntry CreateEntry(IReadOnlyDictionary<IProperty, object?> values, IEntityType entityType)
{
var entry = new InternalEntityEntry(this, entityType, values, EntityMaterializerSource);

Expand Down
6 changes: 5 additions & 1 deletion src/EFCore/EFCore.baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -16507,7 +16507,11 @@
"Member": "void CascadeDelete(Microsoft.EntityFrameworkCore.Update.IUpdateEntry entry, System.Collections.Generic.IEnumerable<Microsoft.EntityFrameworkCore.Metadata.IForeignKey>? foreignKeys = null);"
},
{
"Member": "Microsoft.EntityFrameworkCore.Update.IUpdateEntry CreateEntry(System.Collections.Generic.IDictionary<string, object?> values, Microsoft.EntityFrameworkCore.Metadata.IEntityType entityType);"
"Member": "Microsoft.EntityFrameworkCore.Update.IUpdateEntry CreateEntry(System.Collections.Generic.IDictionary<string, object?> values, Microsoft.EntityFrameworkCore.Metadata.IEntityType entityType);",
"Stage": "Obsolete"
},
{
"Member": "Microsoft.EntityFrameworkCore.Update.IUpdateEntry CreateEntry(System.Collections.Generic.IReadOnlyDictionary<Microsoft.EntityFrameworkCore.Metadata.IProperty, object?> values, Microsoft.EntityFrameworkCore.Metadata.IEntityType entityType);"
},
{
"Member": "void DetectChanges();"
Expand Down
14 changes: 14 additions & 0 deletions src/EFCore/Update/IUpdateAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,22 @@ public interface IUpdateAdapter
/// <param name="values">A dictionary of property names to values.</param>
/// <param name="entityType">The entity type.</param>
/// <returns>The created entry.</returns>
[Obsolete("Use the overload that accepts a dictionary keyed by " + nameof(IProperty) + " instead.")]
IUpdateEntry CreateEntry(IDictionary<string, object?> values, IEntityType entityType);

/// <summary>
/// Creates a new entry with the given property values for the given entity type.
/// </summary>
/// <remarks>
/// Keying the values by <see cref="IProperty" /> rather than by name allows distinct values to be
/// supplied for multiple complex properties that share the same complex type. Complex collections
/// are not supported by this overload.
/// </remarks>
/// <param name="values">A dictionary of properties to values.</param>
/// <param name="entityType">The entity type.</param>
/// <returns>The created entry.</returns>
IUpdateEntry CreateEntry(IReadOnlyDictionary<IProperty, object?> values, IEntityType entityType);

/// <summary>
/// The model with which the data is associated.
/// </summary>
Expand Down
14 changes: 14 additions & 0 deletions src/EFCore/Update/Internal/UpdateAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,23 @@ public virtual IList<IUpdateEntry> GetEntriesToSave()
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[Obsolete("Use the overload that accepts a dictionary keyed by " + nameof(IProperty) + " instead.")]
public virtual IUpdateEntry CreateEntry(
IDictionary<string, object?> values,
IEntityType entityType)
#pragma warning disable CS0618 // Type or member is obsolete
=> _stateManager.CreateEntry(values, entityType);
#pragma warning restore CS0618 // Type or member is obsolete

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual IUpdateEntry CreateEntry(
IReadOnlyDictionary<IProperty, object?> values,
IEntityType entityType)
=> _stateManager.CreateEntry(values, entityType);

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,31 +424,73 @@ public virtual void Can_get_property_value_after_creation_from_value_buffer()
var stateManager = context.GetService<IStateManager>();
var entityType = context.Model.FindEntityType(typeof(TSomeEntity));

var keyProperty = entityType.FindProperty("Id");
var property = entityType.FindProperty("Name");

var entry = stateManager.CreateEntry(
new Dictionary<string, object> { { "Id", 1 }, { "Name", "Kool" } },
new Dictionary<IProperty, object> { { keyProperty, 1 }, { property, "Kool" } },
entityType
);

Assert.Equal(1, entry[keyProperty]);
Assert.Equal("Kool", entry[property]);
}

[Fact]
public virtual void Can_set_property_value_after_creation_from_value_buffer()
{
using var context = new TKContext();
var stateManager = context.GetService<IStateManager>();
var entityType = context.Model.FindEntityType(typeof(TSomeEntity));

var keyProperty = entityType.FindProperty("Id");
var nameProperty = entityType.FindProperty("Name");

var entry = stateManager.CreateEntry(
new Dictionary<IProperty, object> { { keyProperty, 1 }, { nameProperty, "Kool" } },
entityType
);

entry[nameProperty] = "Mule";

Assert.Equal("Mule", entry[nameProperty]);
}

[Fact]
[Obsolete("Tests the obsolete name-keyed CreateEntry overload.")]
public virtual void Can_get_property_value_after_creation_from_value_buffer_using_property_names()
{
using var context = new TKContext();
var stateManager = context.GetService<IStateManager>();
var entityType = context.Model.FindEntityType(typeof(TSomeEntity));

var keyProperty = entityType.FindProperty("Id");
var property = entityType.FindProperty("Name");

var entry = stateManager.CreateEntry(
new Dictionary<string, object> { { "Id", 1 }, { "Name", "Kool" } },
entityType
);

Assert.Equal(1, entry[keyProperty]);
Assert.Equal("Kool", entry[property]);
}

[Fact]
public virtual void Can_set_property_value_after_creation_from_value_buffer()
[Obsolete("Tests the obsolete name-keyed CreateEntry overload.")]
public virtual void Can_set_property_value_after_creation_from_value_buffer_using_property_names()
{
using var context = new TKContext();
var stateManager = context.GetService<IStateManager>();
var entityType = context.Model.FindEntityType(typeof(TSomeEntity));

var nameProperty = entityType.FindProperty("Name");

var entry = stateManager.CreateEntry(
new Dictionary<string, object> { { "Id", 1 }, { "Name", "Kool" } },
entityType
);

var nameProperty = entityType.FindProperty("Name");
entry[nameProperty] = "Mule";

Assert.Equal("Mule", entry[nameProperty]);
Expand Down
Loading
Loading