Skip to content

Commit db9e7fe

Browse files
authored
Generate type mappings in the compiled model.
All type mapping types now need to be public and have a public static property Default of its own type. Annotate NTS as not supported with NativeAOT. Part of #29761
1 parent f94e979 commit db9e7fe

File tree

105 files changed

+5039
-1055
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+5039
-1055
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
QueryBaseline.txt
2+
RuntimeModelBaseline.txt
23

34
## Ignore Visual Studio temporary files, build results, and
45
## files generated by popular Visual Studio add-ons.
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using static System.Linq.Expressions.Expression;
5+
46
namespace Microsoft.EntityFrameworkCore.Cosmos.ChangeTracking.Internal;
57

68
/// <summary>
@@ -11,6 +13,9 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.ChangeTracking.Internal;
1113
/// </summary>
1214
public sealed class SingleDimensionalArrayComparer<TElement> : ValueComparer<TElement[]>
1315
{
16+
internal static readonly PropertyInfo ArrayLengthProperty
17+
= typeof(Array).GetRuntimeProperty(nameof(Array.Length))!;
18+
1419
/// <summary>
1520
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
1621
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -19,9 +24,9 @@ public sealed class SingleDimensionalArrayComparer<TElement> : ValueComparer<TEl
1924
/// </summary>
2025
public SingleDimensionalArrayComparer(ValueComparer elementComparer)
2126
: base(
22-
(a, b) => Compare(a, b, (ValueComparer<TElement>)elementComparer),
23-
o => GetHashCode(o, (ValueComparer<TElement>)elementComparer),
24-
source => Snapshot(source, (ValueComparer<TElement>)elementComparer))
27+
CreateEqualsExpression(elementComparer),
28+
CreateHashCodeExpression(elementComparer),
29+
CreateSnapshotExpression(elementComparer))
2530
{
2631
}
2732

@@ -34,54 +39,80 @@ public SingleDimensionalArrayComparer(ValueComparer elementComparer)
3439
public override Type Type
3540
=> typeof(TElement[]);
3641

37-
private static bool Compare(TElement[]? a, TElement[]? b, ValueComparer<TElement> elementComparer)
42+
private static Expression<Func<TElement[]?, TElement[]?, bool>> CreateEqualsExpression(ValueComparer elementComparer)
3843
{
39-
if (a is null)
40-
{
41-
return b is null;
42-
}
44+
var type = typeof(TElement[]);
45+
var param1 = Parameter(type, "v1");
46+
var param2 = Parameter(type, "v2");
4347

44-
if (b is null || a.Length != b.Length)
45-
{
46-
return false;
47-
}
48+
return Lambda<Func<TElement[]?, TElement[]?, bool>>(
49+
Condition(
50+
Equal(param1, Constant(null, type)),
51+
Equal(param2, Constant(null, type)),
52+
AndAlso(
53+
NotEqual(param2, Constant(null, type)),
54+
AndAlso(
55+
Equal(MakeMemberAccess(param1, ArrayLengthProperty), MakeMemberAccess(param2, ArrayLengthProperty)),
56+
OrElse(
57+
ReferenceEqual(param1, param2),
58+
Call(EnumerableMethods.All.MakeGenericMethod(typeof(bool)),
59+
Call(EnumerableMethods.ZipWithSelector.MakeGenericMethod(typeof(TElement), typeof(TElement), typeof(bool)),
60+
param1,
61+
param2,
62+
elementComparer.EqualsExpression),
63+
#pragma warning disable EF1001 // Internal EF Core API usage.
64+
BoolIdentity))))),
65+
#pragma warning restore EF1001 // Internal EF Core API usage.
66+
param1, param2);
67+
}
4868

49-
if (ReferenceEquals(a, b))
50-
{
51-
return true;
52-
}
69+
private static Expression<Func<TElement[], int>> CreateHashCodeExpression(ValueComparer elementComparer)
70+
{
71+
var elementType = typeof(TElement);
72+
var param = Parameter(typeof(TElement[]), "v");
5373

54-
for (var i = 0; i < a.Length; i++)
55-
{
56-
if (!elementComparer.Equals(a[i], b[i]))
57-
{
58-
return false;
59-
}
60-
}
74+
var aggregateParam = Parameter(typeof(HashCode), "h");
75+
var aggregateElementParam = Parameter(elementType, "e");
76+
#pragma warning disable EF1001 // Internal EF Core API usage.
77+
var aggregateFunc = Lambda<Func<HashCode, TElement, HashCode>>(
78+
Call(HashCodeAddMethod, aggregateParam, elementComparer.ExtractHashCodeBody(aggregateElementParam)),
79+
aggregateParam, aggregateElementParam);
6180

62-
return true;
81+
var selector = Lambda<Func<HashCode, int>>(
82+
Call(aggregateParam, ToHashCodeMethod),
83+
aggregateParam);
84+
#pragma warning restore EF1001 // Internal EF Core API usage.
85+
86+
return Lambda<Func<TElement[], int>>(
87+
Call(EnumerableMethods.AggregateWithSeedSelector.MakeGenericMethod(elementType, typeof(HashCode), typeof(int)),
88+
param,
89+
New(typeof(HashCode)),
90+
aggregateFunc,
91+
selector),
92+
param);
6393
}
6494

65-
private static int GetHashCode(TElement[] source, ValueComparer<TElement> elementComparer)
95+
private static Expression<Func<TElement[], TElement[]>> CreateSnapshotExpression(ValueComparer elementComparer)
6696
{
67-
var hash = new HashCode();
68-
foreach (var el in source)
69-
{
70-
hash.Add(el, elementComparer);
71-
}
97+
var elementType = typeof(TElement);
98+
var param = Parameter(typeof(TElement[]), "v");
7299

73-
return hash.ToHashCode();
74-
}
100+
var elementParam = Parameter(elementType, "e");
75101

76-
private static TElement[] Snapshot(TElement[] source, ValueComparer<TElement> elementComparer)
77-
{
78-
var snapshot = new TElement[source.Length];
79-
for (var i = 0; i < source.Length; i++)
80-
{
81-
var element = source[i];
82-
snapshot[i] = element is null ? default! : elementComparer.Snapshot(element);
83-
}
102+
var selector = elementType.IsValueType
103+
? elementComparer.SnapshotExpression
104+
: Lambda<Func<TElement, TElement>>(
105+
Condition(
106+
Equal(elementParam, Constant(null, elementType)),
107+
Constant(null, elementType),
108+
elementComparer.ExtractSnapshotBody(elementParam)),
109+
elementParam);
84110

85-
return snapshot;
111+
return Lambda<Func<TElement[], TElement[]>>(
112+
Call(EnumerableMethods.ToArray.MakeGenericMethod(elementType),
113+
Call(EnumerableMethods.Select.MakeGenericMethod(elementType, elementType),
114+
param,
115+
selector)),
116+
param);
86117
}
87118
}

src/EFCore.Cosmos/Storage/Internal/CosmosTypeMapping.cs

+18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using Microsoft.EntityFrameworkCore.Storage.Json;
56

67
namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
@@ -13,6 +14,14 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
1314
/// </summary>
1415
public class CosmosTypeMapping : CoreTypeMapping
1516
{
17+
/// <summary>
18+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
19+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
20+
/// any release. You should only use it directly in your code with extreme caution and knowing that
21+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
22+
/// </summary>
23+
public static CosmosTypeMapping Default { get; } = new(typeof(object));
24+
1625
/// <summary>
1726
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
1827
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -56,4 +65,13 @@ public override CoreTypeMapping Clone(
5665
CoreTypeMapping? elementMapping = null,
5766
JsonValueReaderWriter? jsonValueReaderWriter = null)
5867
=> new CosmosTypeMapping(Parameters.WithComposedConverter(converter, elementMapping, jsonValueReaderWriter));
68+
69+
/// <summary>
70+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
71+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
72+
/// any release. You should only use it directly in your code with extreme caution and knowing that
73+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
74+
/// </summary>
75+
protected override CoreTypeMapping Clone(CoreTypeMappingParameters parameters)
76+
=> new CosmosTypeMapping(parameters);
5977
}

src/EFCore.Design/Design/Internal/CSharpHelper.cs

+47
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
using System.Runtime.CompilerServices;
88
using System.Security;
99
using System.Text;
10+
using Microsoft.CodeAnalysis;
11+
using Microsoft.CodeAnalysis.Editing;
12+
using Microsoft.CodeAnalysis.Simplification;
13+
using Microsoft.CodeAnalysis.Text;
1014
using Microsoft.EntityFrameworkCore.Internal;
15+
using Microsoft.EntityFrameworkCore.Query.Internal;
1116

1217
namespace Microsoft.EntityFrameworkCore.Design.Internal;
1318

@@ -20,6 +25,8 @@ namespace Microsoft.EntityFrameworkCore.Design.Internal;
2025
public class CSharpHelper : ICSharpHelper
2126
{
2227
private readonly ITypeMappingSource _typeMappingSource;
28+
private readonly Project _project;
29+
private readonly LinqToCSharpSyntaxTranslator _translator;
2330

2431
/// <summary>
2532
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -30,6 +37,14 @@ public class CSharpHelper : ICSharpHelper
3037
public CSharpHelper(ITypeMappingSource typeMappingSource)
3138
{
3239
_typeMappingSource = typeMappingSource;
40+
41+
var workspace = new AdhocWorkspace();
42+
var projectId = ProjectId.CreateNewId();
43+
var versionStamp = VersionStamp.Create();
44+
var projectInfo = ProjectInfo.Create(projectId, versionStamp, "Proj", "Proj", LanguageNames.CSharp);
45+
_project = workspace.AddProject(projectInfo);
46+
var syntaxGenerator = SyntaxGenerator.GetGenerator(workspace, LanguageNames.CSharp);
47+
_translator = new LinqToCSharpSyntaxTranslator(syntaxGenerator);
3348
}
3449

3550
private static readonly IReadOnlyCollection<string> Keywords = new[]
@@ -1497,6 +1512,38 @@ public virtual string Arguments(IEnumerable<object> values)
14971512
public virtual IEnumerable<string> GetRequiredUsings(Type type)
14981513
=> type.GetNamespaces();
14991514

1515+
private string ToSourceCode(SyntaxNode node)
1516+
{
1517+
var code = node.NormalizeWhitespace().ToFullString();
1518+
var document = _project.AddDocument("Code.cs", SourceText.From(code));
1519+
1520+
var syntaxRootFoo = document.GetSyntaxRootAsync().Result!;
1521+
var annotatedDocument = document.WithSyntaxRoot(syntaxRootFoo.WithAdditionalAnnotations(Simplifier.Annotation));
1522+
document = Simplifier.ReduceAsync(annotatedDocument).Result;
1523+
1524+
var simplifiedCode = document.GetTextAsync().Result.ToString();
1525+
1526+
return simplifiedCode;
1527+
}
1528+
1529+
/// <summary>
1530+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
1531+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
1532+
/// any release. You should only use it directly in your code with extreme caution and knowing that
1533+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
1534+
/// </summary>
1535+
public virtual string Statement(Expression node, ISet<string> collectedNamespaces)
1536+
=> ToSourceCode(_translator.TranslateStatement(node, collectedNamespaces));
1537+
1538+
/// <summary>
1539+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
1540+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
1541+
/// any release. You should only use it directly in your code with extreme caution and knowing that
1542+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
1543+
/// </summary>
1544+
public virtual string Expression(Expression node, ISet<string> collectedNamespaces)
1545+
=> ToSourceCode(_translator.TranslateExpression(node, collectedNamespaces));
1546+
15001547
private static bool IsIdentifierStartCharacter(char ch)
15011548
{
15021549
if (ch < 'a')

src/EFCore.Design/Extensions/Internal/TypeExtensions.cs

-104
This file was deleted.

src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,7 @@ protected virtual void GeneratePropertyAnnotations(
517517

518518
private ValueConverter? FindValueConverter(IProperty property)
519519
=> property.GetValueConverter()
520-
?? (property.FindTypeMapping()
521-
?? Dependencies.RelationalTypeMappingSource.FindMapping(property))?.Converter;
520+
?? property.GetTypeMapping().Converter;
522521

523522
/// <summary>
524523
/// Generates code for <see cref="IComplexProperty" /> objects.

0 commit comments

Comments
 (0)