Skip to content

Commit

Permalink
Generate type mappings in the compiled model.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
AndriySvyryd authored Aug 7, 2023
1 parent f94e979 commit db9e7fe
Show file tree
Hide file tree
Showing 105 changed files with 5,039 additions and 1,055 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
QueryBaseline.txt
RuntimeModelBaseline.txt

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

using static System.Linq.Expressions.Expression;

namespace Microsoft.EntityFrameworkCore.Cosmos.ChangeTracking.Internal;

/// <summary>
Expand All @@ -11,6 +13,9 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.ChangeTracking.Internal;
/// </summary>
public sealed class SingleDimensionalArrayComparer<TElement> : ValueComparer<TElement[]>
{
internal static readonly PropertyInfo ArrayLengthProperty
= typeof(Array).GetRuntimeProperty(nameof(Array.Length))!;

/// <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 All @@ -19,9 +24,9 @@ public sealed class SingleDimensionalArrayComparer<TElement> : ValueComparer<TEl
/// </summary>
public SingleDimensionalArrayComparer(ValueComparer elementComparer)
: base(
(a, b) => Compare(a, b, (ValueComparer<TElement>)elementComparer),
o => GetHashCode(o, (ValueComparer<TElement>)elementComparer),
source => Snapshot(source, (ValueComparer<TElement>)elementComparer))
CreateEqualsExpression(elementComparer),
CreateHashCodeExpression(elementComparer),
CreateSnapshotExpression(elementComparer))
{
}

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

private static bool Compare(TElement[]? a, TElement[]? b, ValueComparer<TElement> elementComparer)
private static Expression<Func<TElement[]?, TElement[]?, bool>> CreateEqualsExpression(ValueComparer elementComparer)
{
if (a is null)
{
return b is null;
}
var type = typeof(TElement[]);
var param1 = Parameter(type, "v1");
var param2 = Parameter(type, "v2");

if (b is null || a.Length != b.Length)
{
return false;
}
return Lambda<Func<TElement[]?, TElement[]?, bool>>(
Condition(
Equal(param1, Constant(null, type)),
Equal(param2, Constant(null, type)),
AndAlso(
NotEqual(param2, Constant(null, type)),
AndAlso(
Equal(MakeMemberAccess(param1, ArrayLengthProperty), MakeMemberAccess(param2, ArrayLengthProperty)),
OrElse(
ReferenceEqual(param1, param2),
Call(EnumerableMethods.All.MakeGenericMethod(typeof(bool)),
Call(EnumerableMethods.ZipWithSelector.MakeGenericMethod(typeof(TElement), typeof(TElement), typeof(bool)),
param1,
param2,
elementComparer.EqualsExpression),
#pragma warning disable EF1001 // Internal EF Core API usage.
BoolIdentity))))),
#pragma warning restore EF1001 // Internal EF Core API usage.
param1, param2);
}

if (ReferenceEquals(a, b))
{
return true;
}
private static Expression<Func<TElement[], int>> CreateHashCodeExpression(ValueComparer elementComparer)
{
var elementType = typeof(TElement);
var param = Parameter(typeof(TElement[]), "v");

for (var i = 0; i < a.Length; i++)
{
if (!elementComparer.Equals(a[i], b[i]))
{
return false;
}
}
var aggregateParam = Parameter(typeof(HashCode), "h");
var aggregateElementParam = Parameter(elementType, "e");
#pragma warning disable EF1001 // Internal EF Core API usage.
var aggregateFunc = Lambda<Func<HashCode, TElement, HashCode>>(
Call(HashCodeAddMethod, aggregateParam, elementComparer.ExtractHashCodeBody(aggregateElementParam)),
aggregateParam, aggregateElementParam);

return true;
var selector = Lambda<Func<HashCode, int>>(
Call(aggregateParam, ToHashCodeMethod),
aggregateParam);
#pragma warning restore EF1001 // Internal EF Core API usage.

return Lambda<Func<TElement[], int>>(
Call(EnumerableMethods.AggregateWithSeedSelector.MakeGenericMethod(elementType, typeof(HashCode), typeof(int)),
param,
New(typeof(HashCode)),
aggregateFunc,
selector),
param);
}

private static int GetHashCode(TElement[] source, ValueComparer<TElement> elementComparer)
private static Expression<Func<TElement[], TElement[]>> CreateSnapshotExpression(ValueComparer elementComparer)
{
var hash = new HashCode();
foreach (var el in source)
{
hash.Add(el, elementComparer);
}
var elementType = typeof(TElement);
var param = Parameter(typeof(TElement[]), "v");

return hash.ToHashCode();
}
var elementParam = Parameter(elementType, "e");

private static TElement[] Snapshot(TElement[] source, ValueComparer<TElement> elementComparer)
{
var snapshot = new TElement[source.Length];
for (var i = 0; i < source.Length; i++)
{
var element = source[i];
snapshot[i] = element is null ? default! : elementComparer.Snapshot(element);
}
var selector = elementType.IsValueType
? elementComparer.SnapshotExpression
: Lambda<Func<TElement, TElement>>(
Condition(
Equal(elementParam, Constant(null, elementType)),
Constant(null, elementType),
elementComparer.ExtractSnapshotBody(elementParam)),
elementParam);

return snapshot;
return Lambda<Func<TElement[], TElement[]>>(
Call(EnumerableMethods.ToArray.MakeGenericMethod(elementType),
Call(EnumerableMethods.Select.MakeGenericMethod(elementType, elementType),
param,
selector)),
param);
}
}
18 changes: 18 additions & 0 deletions src/EFCore.Cosmos/Storage/Internal/CosmosTypeMapping.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
Expand All @@ -13,6 +14,14 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
/// </summary>
public class CosmosTypeMapping : CoreTypeMapping
{
/// <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 CosmosTypeMapping Default { get; } = new(typeof(object));

/// <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 @@ -56,4 +65,13 @@ public override CoreTypeMapping Clone(
CoreTypeMapping? elementMapping = null,
JsonValueReaderWriter? jsonValueReaderWriter = null)
=> new CosmosTypeMapping(Parameters.WithComposedConverter(converter, 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(parameters);
}
47 changes: 47 additions & 0 deletions src/EFCore.Design/Design/Internal/CSharpHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Text;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace Microsoft.EntityFrameworkCore.Design.Internal;

Expand All @@ -20,6 +25,8 @@ namespace Microsoft.EntityFrameworkCore.Design.Internal;
public class CSharpHelper : ICSharpHelper
{
private readonly ITypeMappingSource _typeMappingSource;
private readonly Project _project;
private readonly LinqToCSharpSyntaxTranslator _translator;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -30,6 +37,14 @@ public class CSharpHelper : ICSharpHelper
public CSharpHelper(ITypeMappingSource typeMappingSource)
{
_typeMappingSource = typeMappingSource;

var workspace = new AdhocWorkspace();
var projectId = ProjectId.CreateNewId();
var versionStamp = VersionStamp.Create();
var projectInfo = ProjectInfo.Create(projectId, versionStamp, "Proj", "Proj", LanguageNames.CSharp);
_project = workspace.AddProject(projectInfo);
var syntaxGenerator = SyntaxGenerator.GetGenerator(workspace, LanguageNames.CSharp);
_translator = new LinqToCSharpSyntaxTranslator(syntaxGenerator);
}

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

private string ToSourceCode(SyntaxNode node)
{
var code = node.NormalizeWhitespace().ToFullString();
var document = _project.AddDocument("Code.cs", SourceText.From(code));

var syntaxRootFoo = document.GetSyntaxRootAsync().Result!;
var annotatedDocument = document.WithSyntaxRoot(syntaxRootFoo.WithAdditionalAnnotations(Simplifier.Annotation));
document = Simplifier.ReduceAsync(annotatedDocument).Result;

var simplifiedCode = document.GetTextAsync().Result.ToString();

return simplifiedCode;
}

/// <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 string Statement(Expression node, ISet<string> collectedNamespaces)
=> ToSourceCode(_translator.TranslateStatement(node, collectedNamespaces));

/// <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 string Expression(Expression node, ISet<string> collectedNamespaces)
=> ToSourceCode(_translator.TranslateExpression(node, collectedNamespaces));

private static bool IsIdentifierStartCharacter(char ch)
{
if (ch < 'a')
Expand Down
104 changes: 0 additions & 104 deletions src/EFCore.Design/Extensions/Internal/TypeExtensions.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -517,8 +517,7 @@ protected virtual void GeneratePropertyAnnotations(

private ValueConverter? FindValueConverter(IProperty property)
=> property.GetValueConverter()
?? (property.FindTypeMapping()
?? Dependencies.RelationalTypeMappingSource.FindMapping(property))?.Converter;
?? property.GetTypeMapping().Converter;

/// <summary>
/// Generates code for <see cref="IComplexProperty" /> objects.
Expand Down
Loading

0 comments on commit db9e7fe

Please sign in to comment.