Skip to content

Commit

Permalink
Merge in 'release/7.0' changes
Browse files Browse the repository at this point in the history
  • Loading branch information
dotnet-bot committed Feb 14, 2023
2 parents d26d4e0 + 77aac27 commit 5d5ae94
Show file tree
Hide file tree
Showing 27 changed files with 1,669 additions and 571 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ namespace Microsoft.EntityFrameworkCore.Migrations.Design;
/// </remarks>
public class CSharpSnapshotGenerator : ICSharpSnapshotGenerator
{
private static readonly bool QuirkEnabled30058
= AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue30058", out var enabled) && enabled;

private static readonly MethodInfo HasAnnotationMethodInfo
= typeof(ModelBuilder).GetRuntimeMethod(nameof(ModelBuilder.HasAnnotation), new[] { typeof(string), typeof(string) })!;

Expand Down Expand Up @@ -861,6 +864,7 @@ private void GenerateTableMapping(

var explicitName = tableNameAnnotation != null
|| entityType.BaseType == null
|| (!QuirkEnabled30058 && (entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy && entityType.IsAbstract()))
|| entityType.BaseType.GetTableName() != tableName
|| hasOverrides;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ namespace Microsoft.EntityFrameworkCore;
/// </remarks>
public static class RelationalEntityTypeExtensions
{
private static readonly bool QuirkEnabled29899
= AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29899", out var enabled) && enabled;

/// <summary>
/// Gets the name used for the <see cref="ISqlQuery" /> mapped using
/// <see cref="O:RelationalEntityTypeBuilderExtensions.ToSqlQuery" />.
Expand Down Expand Up @@ -146,7 +149,7 @@ public static void SetTableName(this IMutableEntityType entityType, string? name
}

return entityType.BaseType != null
? entityType.GetRootType().GetSchema()
? entityType.GetRootType().GetSchema() ?? (QuirkEnabled29899 ? null : GetDefaultSchema(entityType))
: GetDefaultSchema(entityType);
}

Expand Down
7 changes: 6 additions & 1 deletion src/EFCore.Relational/Metadata/Internal/ColumnBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
public class ColumnBase<TColumnMappingBase> : Annotatable, IColumnBase
where TColumnMappingBase : class, IColumnMappingBase
{
private static readonly bool QuirkEnabled29985
= AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29985", out var enabled) && enabled;

private Type? _providerClrType;

/// <summary>
Expand Down Expand Up @@ -84,7 +87,9 @@ public virtual Type ProviderClrType
}

var typeMapping = StoreTypeMapping;
var providerType = typeMapping.Converter?.ProviderClrType ?? typeMapping.ClrType;
var providerType = QuirkEnabled29985
? typeMapping.Converter?.ProviderClrType ?? typeMapping.ClrType
: typeMapping.Converter?.ProviderClrType.UnwrapNullableType() ?? typeMapping.ClrType;

return _providerClrType = providerType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ namespace Microsoft.EntityFrameworkCore.Update.Internal;
/// </summary>
public static class ColumnAccessorsFactory
{
private static readonly bool QuirkEnabled29985
= AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29985", out var enabled) && enabled;

/// <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 Expand Up @@ -51,7 +54,7 @@ private static ColumnAccessors CreateGeneric<TColumn>(IColumn column)

var providerValue = entry.GetCurrentProviderValue(property);
if (providerValue == null
&& !typeof(TColumn).IsNullableType())
&& (!QuirkEnabled29985 || !typeof(TColumn).IsNullableType()))
{
return (value!, valueFound);
}
Expand Down Expand Up @@ -94,7 +97,7 @@ private static ColumnAccessors CreateGeneric<TColumn>(IColumn column)

var providerValue = entry.GetOriginalProviderValue(property);
if (providerValue == null
&& !typeof(TColumn).IsNullableType())
&& (!QuirkEnabled29985 || !typeof(TColumn).IsNullableType()))
{
return (value!, valueFound);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ namespace Microsoft.EntityFrameworkCore.Update.Internal;
/// </summary>
public class RowForeignKeyValueFactoryFactory : IRowForeignKeyValueFactoryFactory
{
private static readonly bool QuirkEnabled29985
= AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29985", out var enabled) && enabled;

private readonly IValueConverterSelector _valueConverterSelector;

/// <summary>
Expand Down Expand Up @@ -53,30 +56,63 @@ private static IRowForeignKeyValueFactory CreateSimple<TKey, TForeignKey>(
IValueConverterSelector valueConverterSelector)
where TKey : notnull
{
var dependentColumn = foreignKey.Columns.Single();
var dependentType = dependentColumn.ProviderClrType;
var principalType = foreignKey.PrincipalColumns.Single().ProviderClrType;
var columnAccessors = ((Column)dependentColumn).Accessors;

if (dependentType.IsNullableType()
&& principalType.IsNullableType())
if (QuirkEnabled29985)
{
return new SimpleFullyNullableRowForeignKeyValueFactory<TKey, TForeignKey>(
foreignKey, dependentColumn, columnAccessors, valueConverterSelector);
}
var dependentColumn = foreignKey.Columns.Single();
var dependentType = dependentColumn.ProviderClrType;
var principalType = foreignKey.PrincipalColumns.Single().ProviderClrType;
var columnAccessors = ((Column)dependentColumn).Accessors;

if (dependentType.IsNullableType())
{
return (IRowForeignKeyValueFactory<TKey>)Activator.CreateInstance(
typeof(SimpleNullableRowForeignKeyValueFactory<,>).MakeGenericType(
typeof(TKey), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors, valueConverterSelector)!;
if (dependentType.IsNullableType()
&& principalType.IsNullableType())
{
return new SimpleFullyNullableRowForeignKeyValueFactory<TKey, TForeignKey>(
foreignKey, dependentColumn, columnAccessors, valueConverterSelector);
}

if (dependentType.IsNullableType())
{
return (IRowForeignKeyValueFactory<TKey>)Activator.CreateInstance(
typeof(SimpleNullableRowForeignKeyValueFactory<,>).MakeGenericType(
typeof(TKey), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors, valueConverterSelector)!;
}

return principalType.IsNullableType()
? (IRowForeignKeyValueFactory<TKey>)Activator.CreateInstance(
typeof(SimpleNullablePrincipalRowForeignKeyValueFactory<,,>).MakeGenericType(
typeof(TKey), typeof(TKey).UnwrapNullableType(), typeof(TForeignKey)), foreignKey, dependentColumn,
columnAccessors)!
: new SimpleNonNullableRowForeignKeyValueFactory<TKey, TForeignKey>(
foreignKey, dependentColumn, columnAccessors, valueConverterSelector);
}
else
{
var dependentColumn = foreignKey.Columns.First();
var principalColumn = foreignKey.PrincipalColumns.First();
var columnAccessors = ((Column)dependentColumn).Accessors;

if (principalColumn.ProviderClrType.IsNullableType()
|| (dependentColumn.IsNullable
&& principalColumn.IsNullable))
{
return new SimpleFullyNullableRowForeignKeyValueFactory<TKey, TForeignKey>(
foreignKey, dependentColumn, columnAccessors, valueConverterSelector);
}

return principalType.IsNullableType()
? (IRowForeignKeyValueFactory<TKey>)Activator.CreateInstance(
typeof(SimpleNullablePrincipalRowForeignKeyValueFactory<,,>).MakeGenericType(
typeof(TKey), typeof(TKey).UnwrapNullableType(), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors)!
: new SimpleNonNullableRowForeignKeyValueFactory<TKey, TForeignKey>(
foreignKey, dependentColumn, columnAccessors, valueConverterSelector);
if (dependentColumn.IsNullable)
{
return (IRowForeignKeyValueFactory<TKey>)Activator.CreateInstance(
typeof(SimpleNullableRowForeignKeyValueFactory<,>).MakeGenericType(
typeof(TKey), typeof(TForeignKey)), foreignKey, dependentColumn, columnAccessors, valueConverterSelector)!;
}

return principalColumn.IsNullable
? (IRowForeignKeyValueFactory<TKey>)Activator.CreateInstance(
typeof(SimpleNullablePrincipalRowForeignKeyValueFactory<,,>).MakeGenericType(
typeof(TKey), typeof(TKey).UnwrapNullableType(), typeof(TKey), typeof(TForeignKey)), foreignKey, dependentColumn,
columnAccessors)!
: new SimpleNonNullableRowForeignKeyValueFactory<TKey, TForeignKey>(
foreignKey, dependentColumn, columnAccessors, valueConverterSelector);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ namespace Microsoft.EntityFrameworkCore.Update.Internal;
/// </summary>
public class SimpleNullablePrincipalRowForeignKeyValueFactory<TKey, TNonNullableKey, TForeignKey>
: RowForeignKeyValueFactory<TKey, TForeignKey>
where TNonNullableKey : struct
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
35 changes: 33 additions & 2 deletions src/EFCore.Relational/Update/Internal/SimpleRowKeyValueFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ namespace Microsoft.EntityFrameworkCore.Update.Internal;
/// </summary>
public class SimpleRowKeyValueFactory<TKey> : IRowKeyValueFactory<TKey>
{
private static readonly bool QuirkEnabled29985
= AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29985", out var enabled) && enabled;

private readonly IUniqueConstraint _constraint;
private readonly IColumn _column;
private readonly ColumnAccessors _columnAccessors;
Expand Down Expand Up @@ -138,8 +141,36 @@ private sealed class NoNullsCustomEqualityComparer : IEqualityComparer<TKey>

public NoNullsCustomEqualityComparer(ValueComparer comparer)
{
_equals = (Func<TKey?, TKey?, bool>)comparer.EqualsExpression.Compile();
_hashCode = (Func<TKey, int>)comparer.HashCodeExpression.Compile();
if (QuirkEnabled29985)
{
_equals = (Func<TKey?, TKey?, bool>)comparer.EqualsExpression.Compile();
_hashCode = (Func<TKey, int>)comparer.HashCodeExpression.Compile();
}
else
{
var equals = comparer.EqualsExpression;
var getHashCode = comparer.HashCodeExpression;
var type = typeof(TKey);
if (type != comparer.Type)
{
var newEqualsParam1 = Expression.Parameter(type, "v1");
var newEqualsParam2 = Expression.Parameter(type, "v2");
equals = Expression.Lambda(
comparer.ExtractEqualsBody(
Expression.Convert(newEqualsParam1, comparer.Type),
Expression.Convert(newEqualsParam2, comparer.Type)),
newEqualsParam1, newEqualsParam2);


var newHashCodeParam = Expression.Parameter(type, "v");
getHashCode = Expression.Lambda(
comparer.ExtractHashCodeBody(
Expression.Convert(newHashCodeParam, comparer.Type)),
newHashCodeParam);
}

_equals = (Func<TKey?, TKey?, bool>)equals.Compile();
_hashCode = (Func<TKey, int>)getHashCode.Compile(); }
}

public bool Equals(TKey? x, TKey? y)
Expand Down
83 changes: 58 additions & 25 deletions src/EFCore/ChangeTracking/Internal/ChangeDetector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
/// </summary>
public class ChangeDetector : IChangeDetector
{
private static readonly bool QuirkEnabled30135
= AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue30135", out var enabled) && enabled;

private readonly IDiagnosticsLogger<DbLoggerCategory.ChangeTracking> _logger;
private readonly ILoggingOptions _loggingOptions;
private bool _inCascadeDelete;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down Expand Up @@ -112,37 +116,51 @@ public virtual void PropertyChanging(InternalEntityEntry entry, IPropertyBase pr
/// </summary>
public virtual void DetectChanges(IStateManager stateManager)
{
OnDetectingAllChanges(stateManager);
var changesFound = false;

_logger.DetectChangesStarting(stateManager.Context);
if (_inCascadeDelete && !QuirkEnabled30135)
{
return;
}

foreach (var entry in stateManager.ToList()) // Might be too big, but usually _all_ entities are using Snapshot tracking
try
{
switch (entry.EntityState)
{
case EntityState.Detached:
break;
case EntityState.Deleted:
if (entry.SharedIdentityEntry != null)
{
continue;
}
_inCascadeDelete = true;

goto default;
default:
if (LocalDetectChanges(entry))
{
changesFound = true;
}
OnDetectingAllChanges(stateManager);
var changesFound = false;

break;
_logger.DetectChangesStarting(stateManager.Context);

foreach (var entry in stateManager.ToList()) // Might be too big, but usually _all_ entities are using Snapshot tracking
{
switch (entry.EntityState)
{
case EntityState.Detached:
break;
case EntityState.Deleted:
if (entry.SharedIdentityEntry != null)
{
continue;
}

goto default;
default:
if (LocalDetectChanges(entry))
{
changesFound = true;
}

break;
}
}
}

_logger.DetectChangesCompleted(stateManager.Context);
_logger.DetectChangesCompleted(stateManager.Context);

OnDetectedAllChanges(stateManager, changesFound);
OnDetectedAllChanges(stateManager, changesFound);
}
finally
{
_inCascadeDelete = false;
}
}

/// <summary>
Expand All @@ -152,7 +170,22 @@ public virtual void DetectChanges(IStateManager stateManager)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual void DetectChanges(InternalEntityEntry entry)
=> DetectChanges(entry, new HashSet<InternalEntityEntry> { entry });
{
if (_inCascadeDelete && !QuirkEnabled30135)
{
return;
}

try
{
_inCascadeDelete = true;
DetectChanges(entry, new HashSet<InternalEntityEntry> { entry });
}
finally
{
_inCascadeDelete = false;
}
}

private bool DetectChanges(InternalEntityEntry entry, HashSet<InternalEntityEntry> visited)
{
Expand Down
28 changes: 22 additions & 6 deletions src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
/// </summary>
public class CurrentValueComparerFactory
{
private static readonly bool QuirkEnabled29985
= AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue29985", out var enabled) && enabled;

/// <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 Expand Up @@ -49,12 +52,25 @@ public virtual IComparer<IUpdateEntry> Create(IPropertyBase propertyBase)
var nonNullableProviderType = providerType.UnwrapNullableType();
if (IsGenericComparable(providerType, nonNullableProviderType))
{
var comparerType = modelType.IsClass
? typeof(NullableClassCurrentProviderValueComparer<,>).MakeGenericType(modelType, converter.ProviderClrType)
: modelType == converter.ModelClrType
? typeof(CurrentProviderValueComparer<,>).MakeGenericType(modelType, converter.ProviderClrType)
: typeof(NullableStructCurrentProviderValueComparer<,>).MakeGenericType(
nonNullableModelType, converter.ProviderClrType);
Type comparerType;
if (QuirkEnabled29985)
{
comparerType = modelType.IsClass
? typeof(NullableClassCurrentProviderValueComparer<,>).MakeGenericType(modelType, converter.ProviderClrType)
: modelType == converter.ModelClrType
? typeof(CurrentProviderValueComparer<,>).MakeGenericType(modelType, converter.ProviderClrType)
: typeof(NullableStructCurrentProviderValueComparer<,>).MakeGenericType(
nonNullableModelType, converter.ProviderClrType);
}
else
{
comparerType = modelType.IsClass
? typeof(NullableClassCurrentProviderValueComparer<,>).MakeGenericType(modelType, providerType)
: modelType == converter.ModelClrType
? typeof(CurrentProviderValueComparer<,>).MakeGenericType(modelType, providerType)
: typeof(NullableStructCurrentProviderValueComparer<,>).MakeGenericType(
nonNullableModelType, providerType);
}

return (IComparer<IUpdateEntry>)Activator.CreateInstance(comparerType, propertyBase, converter)!;
}
Expand Down
Loading

0 comments on commit 5d5ae94

Please sign in to comment.