Skip to content
Open
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 @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
/// 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 class CosmosTimeOnlyTypeMapping : CosmosTypeMapping
public class CosmosTimeOnlyTypeMapping : CosmosTypeMapping<TimeOnly>
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -25,7 +25,7 @@ public class CosmosTimeOnlyTypeMapping : CosmosTypeMapping
/// 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 CosmosTimeOnlyTypeMapping() : base(typeof(TimeOnly), null, null, null, CosmosJsonTimeOnlyReaderWriter.Instance)
public CosmosTimeOnlyTypeMapping() : base(jsonValueReaderWriter: CosmosJsonTimeOnlyReaderWriter.Instance)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
/// 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 class CosmosTimeSpanTypeMapping : CosmosTypeMapping
public class CosmosTimeSpanTypeMapping : CosmosTypeMapping<TimeSpan>
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -25,7 +25,7 @@ public class CosmosTimeSpanTypeMapping : CosmosTypeMapping
/// 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 CosmosTimeSpanTypeMapping() : base(typeof(TimeSpan), null, null, null, CosmosJsonTimeSpanReaderWriter.Instance)
public CosmosTimeSpanTypeMapping() : base(jsonValueReaderWriter: CosmosJsonTimeSpanReaderWriter.Instance)
{
}

Expand Down
74 changes: 64 additions & 10 deletions src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.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.Collections.Frozen;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.ChangeTracking.Internal;
Expand Down Expand Up @@ -36,7 +37,7 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
{ typeof(TimeOnly), CosmosTimeOnlyTypeMapping.Default },
{ typeof(TimeSpan), CosmosTimeSpanTypeMapping.Default },
{
typeof(JObject), new CosmosTypeMapping(
typeof(JObject), CreateMapping(
typeof(JObject), jsonValueReaderWriter: dependencies.JsonValueReaderWriterSource.FindReaderWriter(typeof(JObject)))
}
}.ToFrozenDictionary();
Expand Down Expand Up @@ -115,10 +116,10 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
CoreTypeMapping? typeMapping = null;
return !TryFindJsonCollectionMapping(elementMappingInfo, memoryType.MakeArrayType(), null, ref typeMapping, out var _, out var readerWriter)
? null
: new CosmosTypeMapping(clrType, jsonValueReaderWriter: readerWriter)
.WithComposedConverter(
(ValueConverter)Activator.CreateInstance(typeof(ReadOnlyMemoryConverter<>).MakeGenericType(memoryType))!,
(ValueComparer)Activator.CreateInstance(typeof(ReadOnlyMemoryComparer<>).MakeGenericType(memoryType))!);
: CreateMapping(clrType, jsonValueReaderWriter: readerWriter)
.WithComposedConverter(
(ValueConverter)Activator.CreateInstance(typeof(ReadOnlyMemoryConverter<>).MakeGenericType(memoryType))!,
(ValueComparer)Activator.CreateInstance(typeof(ReadOnlyMemoryComparer<>).MakeGenericType(memoryType))!);
}

return clrType.IsNumeric()
Expand All @@ -129,7 +130,7 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
|| clrType == typeof(DateTimeOffset)
|| clrType == typeof(TimeSpan)
|| clrType == typeof(string)
? new CosmosTypeMapping(
? CreateMapping(
clrType, jsonValueReaderWriter: Dependencies.JsonValueReaderWriterSource.FindReaderWriter(clrType))
: null;
}
Expand All @@ -155,7 +156,7 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
out var collectionReaderWriter)
&& elementMapping is not null)
{
return new CosmosTypeMapping(
return CreateMapping(
clrType,
elementComparer,
elementMapping: elementMapping,
Expand Down Expand Up @@ -234,7 +235,7 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
}
}

return new CosmosTypeMapping(
return CreateMapping(
clrType,
CreateStringDictionaryComparer(elementMapping, elementType, clrType),
jsonValueReaderWriter: jsonValueReaderWriter);
Expand All @@ -245,14 +246,67 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
return null;
}

private static CosmosTypeMapping CreateMapping(
Type clrType,
ValueComparer? comparer = null,
ValueComparer? keyComparer = null,
CoreTypeMapping? elementMapping = null,
JsonValueReaderWriter? jsonValueReaderWriter = null)
=> clrType switch
{
_ when clrType == typeof(bool) => Create<bool>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(byte) => Create<byte>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(sbyte) => Create<sbyte>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(char) => Create<char>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(short) => Create<short>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(ushort) => Create<ushort>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(int) => Create<int>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(uint) => Create<uint>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(long) => Create<long>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(ulong) => Create<ulong>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(float) => Create<float>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(double) => Create<double>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(decimal) => Create<decimal>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(string) => Create<string>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(Guid) => Create<Guid>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(DateTime) => Create<DateTime>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(DateTimeOffset) => Create<DateTimeOffset>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ when clrType == typeof(DateOnly) => Create<DateOnly>(comparer, keyComparer, elementMapping, jsonValueReaderWriter),
_ => CreateMappingWithReflection(clrType, comparer, keyComparer, elementMapping, jsonValueReaderWriter)
};
Comment on lines +255 to +276

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@roji loves it!


private static CosmosTypeMapping Create<
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicProperties)] T>(
ValueComparer? comparer,
ValueComparer? keyComparer,
CoreTypeMapping? elementMapping,
JsonValueReaderWriter? jsonValueReaderWriter)
=> comparer is null && keyComparer is null && elementMapping is null && jsonValueReaderWriter is null
? CosmosTypeMapping<T>.Default
: new CosmosTypeMapping<T>(comparer, keyComparer, elementMapping, jsonValueReaderWriter);

[UnconditionalSuppressMessage(
"AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.",
Justification = "The type mapping source is not used at runtime by NativeAOT applications, which use a compiled model instead.")]
private static CosmosTypeMapping CreateMappingWithReflection(
Type clrType,
ValueComparer? comparer,
ValueComparer? keyComparer,
CoreTypeMapping? elementMapping,
JsonValueReaderWriter? jsonValueReaderWriter)
{
var genericType = typeof(CosmosTypeMapping<>).MakeGenericType(clrType);
return comparer is null && keyComparer is null && elementMapping is null && jsonValueReaderWriter is null
? (CosmosTypeMapping)genericType.GetAnyProperty(nameof(CosmosTypeMapping<object>.Default))!.GetValue(null)!
: (CosmosTypeMapping)Activator.CreateInstance(genericType, comparer, keyComparer, elementMapping, jsonValueReaderWriter)!;
}

private static ValueComparer CreateStringDictionaryComparer(
CoreTypeMapping elementMapping,
Type elementType,
Comment thread
AndriySvyryd marked this conversation as resolved.
Type dictType,
bool readOnly = false)
{
var unwrappedType = elementType.UnwrapNullableType();

return (ValueComparer)Activator.CreateInstance(
typeof(StringDictionaryComparer<,>).MakeGenericType(dictType, elementType),
#pragma warning disable EF1001 // Internal EF Core API usage.
Expand Down
89 changes: 89 additions & 0 deletions src/EFCore.Cosmos/Storage/Internal/CosmosTypeMapping`1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore.Storage.Json;

namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;

/// <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 class CosmosTypeMapping<
[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.PublicMethods
| DynamicallyAccessedMemberTypes.PublicProperties)]
T> : CosmosTypeMapping
{
/// <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 static new CosmosTypeMapping<T> Default { get; } = new();

/// <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 CosmosTypeMapping(
ValueComparer? comparer = null,
ValueComparer? keyComparer = null,
CoreTypeMapping? elementMapping = null,
JsonValueReaderWriter? jsonValueReaderWriter = null)
: base(typeof(T), comparer, keyComparer, elementMapping, jsonValueReaderWriter)
{
}

/// <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>
protected CosmosTypeMapping(CoreTypeMappingParameters parameters)
: base(parameters)
{
}

/// <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>
protected override ValueComparer CreateDefaultComparer(bool favorStructuralComparisons)
=> ClrType == typeof(T)
? ValueComparer.CreateDefault<T>(favorStructuralComparisons)
: base.CreateDefaultComparer(favorStructuralComparisons);

/// <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 override CoreTypeMapping WithComposedConverter(
ValueConverter? converter,
ValueComparer? comparer = null,
ValueComparer? keyComparer = null,
CoreTypeMapping? elementMapping = null,
JsonValueReaderWriter? jsonValueReaderWriter = null)
=> new CosmosTypeMapping<T>(
Parameters.WithComposedConverter(converter, comparer, keyComparer, elementMapping, jsonValueReaderWriter));

/// <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>
protected override CoreTypeMapping Clone(CoreTypeMappingParameters parameters)
=> new CosmosTypeMapping<T>(parameters);
}
12 changes: 11 additions & 1 deletion src/EFCore.Cosmos/Storage/Internal/ReadOnlyMemoryConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,22 @@ public static ReadOnlyMemory<T> ToMemory(T[] array)
// ReadOnlyMemory instances, while the instance created with an empty array is considered not equal to the default.
=> array.Length == 0 ? default : new ReadOnlyMemory<T>(array);

/// <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 static ReadOnlyMemoryConverter<T> Instance { get; } = new();

/// <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 static ValueConverterInfo DefaultInfo { get; }
= new(typeof(ReadOnlyMemory<T>), typeof(T[]), i => new ReadOnlyMemoryConverter<T>(i.MappingHints), DefaultHints);
= new(typeof(ReadOnlyMemory<T>), typeof(T[]),
i => ReferenceEquals(i.MappingHints, Instance.MappingHints) ? Instance : new ReadOnlyMemoryConverter<T>(i.MappingHints),
DefaultHints);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public InMemoryTypeMapping(
{
}

private InMemoryTypeMapping(CoreTypeMappingParameters parameters)
private protected InMemoryTypeMapping(CoreTypeMappingParameters parameters)
: base(parameters)
{
}
Expand Down
Loading
Loading