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

Remove ValueBuffer from update pipeline #27898

Merged
1 commit merged into from
May 2, 2022
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
168 changes: 168 additions & 0 deletions src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

Expand All @@ -16,6 +17,15 @@ namespace Microsoft.EntityFrameworkCore;
/// </remarks>
public static class RelationalPropertyExtensions
{
private static readonly MethodInfo GetFieldValueMethod =
typeof(DbDataReader).GetRuntimeMethod(nameof(DbDataReader.GetFieldValue), new[] { typeof(int) })!;

private static readonly MethodInfo IsDbNullMethod =
typeof(DbDataReader).GetRuntimeMethod(nameof(DbDataReader.IsDBNull), new[] { typeof(int) })!;

private static readonly MethodInfo ThrowReadValueExceptionMethod
= typeof(RelationalPropertyExtensions).GetTypeInfo().GetDeclaredMethod(nameof(ThrowReadValueException))!;

/// <summary>
/// Returns the base name of the column to which the property would be mapped.
/// </summary>
Expand Down Expand Up @@ -1500,4 +1510,162 @@ public static IConventionAnnotatable GetOrCreateOverrides(
this IConventionProperty property,
in StoreObjectIdentifier storeObject)
=> RelationalPropertyOverrides.GetOrCreate(property, storeObject);

/// <summary>
/// Reads a value for this property from the given <paramref name="relationalReader" />.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="relationalReader">The read from which to read the property's value.</param>
/// <param name="ordinal">The ordinal to read in the <paramref name="relationalReader" />.</param>
/// <param name="detailedErrorsEnabled">Whether detailed errors should be logged.</param>
public static object? GetReaderFieldValue(
this IProperty property, RelationalDataReader relationalReader, int ordinal, bool detailedErrorsEnabled)
{
#if DEBUG
// DetailedErrorsEnabled is a singleton option, meaning that we should never get differing values for the same model.
var previousDetailedErrorsEnabled = property.GetOrAddRuntimeAnnotationValue(
"DebugDetailedErrorsEnabled", static x => x, detailedErrorsEnabled);
Check.DebugAssert(previousDetailedErrorsEnabled == detailedErrorsEnabled, "Differing values of DetailedErrorsEnabled");
#endif

var getReadFieldValue = property.GetOrAddRuntimeAnnotationValue(
RelationalAnnotationNames.GetReaderFieldValue,
static x => CreateGetValueDelegate(x.property, x.detailedErrorsEnabled),
(property, detailedErrorsEnabled));

return getReadFieldValue(relationalReader.DbDataReader, ordinal);
}

private static Func<DbDataReader, int, object?> CreateGetValueDelegate(IProperty property, bool detailedErrorsEnabled)
{
var readerParameter = Expression.Parameter(typeof(DbDataReader), "reader");
var indexParameter = Expression.Parameter(typeof(int), "index");

var typeMapping = (RelationalTypeMapping)property.GetTypeMapping();
var getMethod = typeMapping.GetDataReaderMethod();

Expression valueExpression
= Expression.Call(
getMethod.DeclaringType != typeof(DbDataReader)
? Expression.Convert(readerParameter, getMethod.DeclaringType!)
: readerParameter,
getMethod,
indexParameter);

valueExpression = typeMapping.CustomizeDataReaderExpression(valueExpression);

var converter = typeMapping.Converter;

if (converter != null)
{
if (valueExpression.Type != converter.ProviderClrType)
{
valueExpression = Expression.Convert(valueExpression, converter.ProviderClrType);
}

valueExpression = ReplacingExpressionVisitor.Replace(
converter.ConvertFromProviderExpression.Parameters.Single(),
valueExpression,
converter.ConvertFromProviderExpression.Body);
}

if (valueExpression.Type != property.ClrType)
{
valueExpression = Expression.Convert(valueExpression, property.ClrType);
}

var exceptionParameter = Expression.Parameter(typeof(Exception), name: "e");

if (detailedErrorsEnabled)
{
var catchBlock
= Expression
.Catch(
exceptionParameter,
Expression.Call(
ThrowReadValueExceptionMethod
.MakeGenericMethod(valueExpression.Type),
exceptionParameter,
Expression.Call(
readerParameter,
GetFieldValueMethod.MakeGenericMethod(typeof(object)),
indexParameter),
Expression.Constant(property, typeof(IPropertyBase))));

valueExpression = Expression.TryCatch(valueExpression, catchBlock);
}

if (valueExpression.Type.IsValueType)
{
valueExpression = Expression.Convert(valueExpression, typeof(object));
}

if (property.IsNullable)
{
Expression replaceExpression;
if (converter?.ConvertsNulls == true)
{
replaceExpression = ReplacingExpressionVisitor.Replace(
converter.ConvertFromProviderExpression.Parameters.Single(),
Expression.Default(converter.ProviderClrType),
converter.ConvertFromProviderExpression.Body);

if (replaceExpression.Type != valueExpression.Type)
{
replaceExpression = Expression.Convert(replaceExpression, valueExpression.Type);
}
}
else
{
replaceExpression = Expression.Default(valueExpression.Type);
}

valueExpression
= Expression.Condition(
Expression.Call(readerParameter, IsDbNullMethod, indexParameter),
replaceExpression,
valueExpression);
}

var lambdaExpression = Expression.Lambda<Func<DbDataReader, int, object?>>(valueExpression, readerParameter, indexParameter);

return lambdaExpression.Compile();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static TValue ThrowReadValueException<TValue>(
Exception exception,
object? value,
IPropertyBase? property = null)
{
var expectedType = typeof(TValue);
var actualType = value?.GetType();

string message;

if (property != null)
{
var entityType = property.DeclaringType.DisplayName();
var propertyName = property.Name;

message
= exception is NullReferenceException
|| Equals(value, DBNull.Value)
? RelationalStrings.ErrorMaterializingPropertyNullReference(entityType, propertyName, expectedType)
: exception is InvalidCastException
? CoreStrings.ErrorMaterializingPropertyInvalidCast(entityType, propertyName, expectedType, actualType)
: RelationalStrings.ErrorMaterializingProperty(entityType, propertyName);
}
else
{
message
= exception is NullReferenceException
? RelationalStrings.ErrorMaterializingValueNullReference(expectedType)
: exception is InvalidCastException
? RelationalStrings.ErrorMaterializingValueInvalidCast(expectedType, actualType)
: RelationalStrings.ErrorMaterializingValue;
}

throw new InvalidOperationException(message, exception);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ public static readonly IDictionary<Type, ServiceCharacteristics> RelationalServi
{ typeof(IMigrationsAnnotationProvider), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IMigrationCommandExecutor), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRelationalTypeMappingSource), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRelationalValueBufferFactoryFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IUpdateSqlGenerator), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRelationalTransactionFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRelationalCommandBuilderFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
Expand Down Expand Up @@ -151,7 +150,6 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd<IMigrationsSqlGenerator, MigrationsSqlGenerator>();
TryAdd<IExecutionStrategyFactory, RelationalExecutionStrategyFactory>();
TryAdd<ITypeMappingSource>(p => p.GetRequiredService<IRelationalTypeMappingSource>());
TryAdd<IRelationalValueBufferFactoryFactory, TypedRelationalValueBufferFactoryFactory>();
TryAdd<IDatabaseCreator>(p => p.GetRequiredService<IRelationalDatabaseCreator>());
TryAdd<IDbContextTransactionManager>(p => p.GetRequiredService<IRelationalConnection>());
TryAdd<IQueryContextFactory, RelationalQueryContextFactory>();
Expand Down Expand Up @@ -190,7 +188,6 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
.AddDependencySingleton<RelationalAnnotationProviderDependencies>()
.AddDependencySingleton<MigrationsAnnotationProviderDependencies>()
.AddDependencySingleton<ParameterNameGeneratorDependencies>()
.AddDependencySingleton<RelationalValueBufferFactoryDependencies>()
.AddDependencySingleton<RelationalTransactionFactoryDependencies>()
.AddDependencySingleton<RelationalCommandBuilderDependencies>()
.AddDependencySingleton<QuerySqlGeneratorDependencies>()
Expand Down
5 changes: 5 additions & 0 deletions src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,9 @@ public static class RelationalAnnotationNames
/// The name for relational model dependencies annotation.
/// </summary>
public const string ModelDependencies = Prefix + "ModelDependencies";

/// <summary>
/// The name for the reader get value delegate annotations.
/// </summary>
public const string GetReaderFieldValue = Prefix + "GetReaderFieldValue";
}
1 change: 0 additions & 1 deletion src/EFCore.Relational/Query/QuerySqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ namespace Microsoft.EntityFrameworkCore.Query;
/// </summary>
public class QuerySqlGenerator : SqlExpressionVisitor
{

private static readonly Dictionary<ExpressionType, string> OperatorMap = new()
{
{ ExpressionType.Equal, " = " },
Expand Down
29 changes: 0 additions & 29 deletions src/EFCore.Relational/Storage/IRelationalValueBufferFactory.cs

This file was deleted.

This file was deleted.

This file was deleted.

Loading