Skip to content

Commit

Permalink
Make RelationalModel lazy (#33650)
Browse files Browse the repository at this point in the history
Part of #33483
  • Loading branch information
AndriySvyryd committed May 1, 2024
1 parent 1e16f86 commit 9527e8a
Show file tree
Hide file tree
Showing 44 changed files with 712 additions and 353 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,29 +36,26 @@ public override void Generate(IModel model, CSharpRuntimeAnnotationCodeGenerator
if (parameters.IsRuntime)
{
annotations.Remove(RelationalAnnotationNames.ModelDependencies);
annotations.Remove(RelationalAnnotationNames.RelationalModel);
annotations.Remove(RelationalAnnotationNames.RelationalModelFactory);

if (annotations.TryGetAndRemove(
RelationalAnnotationNames.RelationalModel,
out RelationalModel relationalModel))
{
GenerateSimpleAnnotation(RelationalAnnotationNames.RelationalModel, "CreateRelationalModel()", parameters);

var methodBuilder = new IndentedStringBuilder();
var newScope = new BidirectionalDictionary<string, object>();
Create(
relationalModel, parameters with
{
MainBuilder = parameters.MethodBuilder,
MethodBuilder = methodBuilder,
ScopeObjects = newScope,
ScopeVariables = newScope.Inverse
});

var methods = methodBuilder.ToString();
if (!string.IsNullOrEmpty(methods))
GenerateSimpleAnnotation(RelationalAnnotationNames.RelationalModelFactory, "() => CreateRelationalModel()", parameters);

var methodBuilder = new IndentedStringBuilder();
var newScope = new BidirectionalDictionary<string, object>();
Create(
model.GetRelationalModel(), parameters with
{
parameters.MethodBuilder.AppendLines(methods);
}
MainBuilder = parameters.MethodBuilder,
MethodBuilder = methodBuilder,
ScopeObjects = newScope,
ScopeVariables = newScope.Inverse
});

var methods = methodBuilder.ToString();
if (!string.IsNullOrEmpty(methods))
{
parameters.MethodBuilder.AppendLines(methods);
}
}
else
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore.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 static class RelationalModelExtensions
{
/// <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 void EnsureRelationalModel(this IModel model) => model.GetRelationalModel();
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// 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 System.Text;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

// ReSharper disable once CheckNamespace
Expand Down Expand Up @@ -134,9 +136,12 @@ public static void SetConstraintName(this IMutableForeignKey foreignKey, string?
/// <param name="foreignKey">The foreign key.</param>
/// <returns>The foreign key constraints to which the foreign key is mapped.</returns>
public static IEnumerable<IForeignKeyConstraint> GetMappedConstraints(this IForeignKey foreignKey)
=> (IEnumerable<IForeignKeyConstraint>?)foreignKey.FindRuntimeAnnotationValue(
RelationalAnnotationNames.ForeignKeyMappings)
?? Enumerable.Empty<IForeignKeyConstraint>();
{
foreignKey.DeclaringEntityType.Model.EnsureRelationalModel();
return (IEnumerable<IForeignKeyConstraint>?)foreignKey.FindRuntimeAnnotationValue(
RelationalAnnotationNames.ForeignKeyMappings)
?? Enumerable.Empty<IForeignKeyConstraint>();
}

/// <summary>
/// <para>
Expand Down
11 changes: 8 additions & 3 deletions src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

// ReSharper disable once CheckNamespace
Expand Down Expand Up @@ -165,9 +167,12 @@ public static void SetFilter(this IMutableIndex index, string? value)
/// <param name="index">The index.</param>
/// <returns>The table indexes to which the index is mapped.</returns>
public static IEnumerable<ITableIndex> GetMappedTableIndexes(this IIndex index)
=> (IEnumerable<ITableIndex>?)index.FindRuntimeAnnotationValue(
RelationalAnnotationNames.TableIndexMappings)
?? Enumerable.Empty<ITableIndex>();
{
index.DeclaringEntityType.Model.EnsureRelationalModel();
return (IEnumerable<ITableIndex>?)index.FindRuntimeAnnotationValue(
RelationalAnnotationNames.TableIndexMappings)
?? Enumerable.Empty<ITableIndex>();
}

/// <summary>
/// <para>
Expand Down
11 changes: 8 additions & 3 deletions src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

// ReSharper disable once CheckNamespace
Expand Down Expand Up @@ -106,9 +108,12 @@ public static void SetName(this IMutableKey key, string? name)
/// <param name="key">The key.</param>
/// <returns>The unique constraints to which the key is mapped.</returns>
public static IEnumerable<IUniqueConstraint> GetMappedConstraints(this IKey key)
=> (IEnumerable<IUniqueConstraint>?)key.FindRuntimeAnnotationValue(
RelationalAnnotationNames.UniqueConstraintMappings)
?? Enumerable.Empty<IUniqueConstraint>();
{
key.DeclaringEntityType.Model.EnsureRelationalModel();
return (IEnumerable<IUniqueConstraint>?)key.FindRuntimeAnnotationValue(
RelationalAnnotationNames.UniqueConstraintMappings)
?? Enumerable.Empty<IUniqueConstraint>();
}

/// <summary>
/// <para>
Expand Down
16 changes: 12 additions & 4 deletions src/EFCore.Relational/Extensions/RelationalModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,21 @@ public static void SetDefaultSchema(this IMutableModel model, string? value)
/// <returns>The database model.</returns>
public static IRelationalModel GetRelationalModel(this IModel model)
{
var databaseModel = (IRelationalModel?)model.FindRuntimeAnnotationValue(RelationalAnnotationNames.RelationalModel);
if (databaseModel == null)
var relationalModel = (IRelationalModel?)model.FindRuntimeAnnotationValue(RelationalAnnotationNames.RelationalModel);
if (relationalModel == null)
{
throw new InvalidOperationException(CoreStrings.ModelNotFinalized(nameof(GetRelationalModel)));
var relationalModelFactory = (Func<IRelationalModel>?)model.FindRuntimeAnnotationValue(
RelationalAnnotationNames.RelationalModelFactory)
?? throw new InvalidOperationException(CoreStrings.ModelNotFinalized(nameof(GetRelationalModel)));
lock (relationalModelFactory)
{
relationalModel = model.GetOrAddRuntimeAnnotationValue(
RelationalAnnotationNames.RelationalModel, f => f!(), relationalModelFactory);
model.RemoveRuntimeAnnotation(RelationalAnnotationNames.RelationalModelFactory);
}
}

return databaseModel;
return relationalModel;
}

#region Max identifier length
Expand Down
91 changes: 61 additions & 30 deletions src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

// ReSharper disable once CheckNamespace
Expand Down Expand Up @@ -517,99 +518,129 @@ public static void SetColumnType(this IMutableProperty property, string? value)
/// <param name="property">The property.</param>
/// <returns>The default columns to which the property would be mapped.</returns>
public static IEnumerable<IColumnMappingBase> GetDefaultColumnMappings(this IProperty property)
=> (IEnumerable<IColumnMappingBase>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.DefaultColumnMappings)
?? Enumerable.Empty<IColumnMappingBase>();
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable<IColumnMappingBase>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.DefaultColumnMappings)
?? Enumerable.Empty<IColumnMappingBase>();
}

/// <summary>
/// Returns the table columns to which the property is mapped.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The table columns to which the property is mapped.</returns>
public static IEnumerable<IColumnMapping> GetTableColumnMappings(this IProperty property)
=> (IEnumerable<IColumnMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.TableColumnMappings)
?? Enumerable.Empty<IColumnMapping>();
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable<IColumnMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.TableColumnMappings)
?? Enumerable.Empty<IColumnMapping>();
}

/// <summary>
/// Returns the view columns to which the property is mapped.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The view columns to which the property is mapped.</returns>
public static IEnumerable<IViewColumnMapping> GetViewColumnMappings(this IProperty property)
=> (IEnumerable<IViewColumnMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.ViewColumnMappings)
?? Enumerable.Empty<IViewColumnMapping>();
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable<IViewColumnMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.ViewColumnMappings)
?? Enumerable.Empty<IViewColumnMapping>();
}

/// <summary>
/// Returns the SQL query columns to which the property is mapped.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The SQL query columns to which the property is mapped.</returns>
public static IEnumerable<ISqlQueryColumnMapping> GetSqlQueryColumnMappings(this IProperty property)
=> (IEnumerable<ISqlQueryColumnMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.SqlQueryColumnMappings)
?? Enumerable.Empty<ISqlQueryColumnMapping>();
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable<ISqlQueryColumnMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.SqlQueryColumnMappings)
?? Enumerable.Empty<ISqlQueryColumnMapping>();
}

/// <summary>
/// Returns the function columns to which the property is mapped.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The function columns to which the property is mapped.</returns>
public static IEnumerable<IFunctionColumnMapping> GetFunctionColumnMappings(this IProperty property)
=> (IEnumerable<IFunctionColumnMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.FunctionColumnMappings)
?? Enumerable.Empty<IFunctionColumnMapping>();
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable<IFunctionColumnMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.FunctionColumnMappings)
?? Enumerable.Empty<IFunctionColumnMapping>();
}

/// <summary>
/// Returns the insert stored procedure result columns to which the property is mapped.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The insert stored procedure result columns to which the property is mapped.</returns>
public static IEnumerable<IStoredProcedureResultColumnMapping> GetInsertStoredProcedureResultColumnMappings(this IProperty property)
=> (IEnumerable<IStoredProcedureResultColumnMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.InsertStoredProcedureResultColumnMappings)
?? Enumerable.Empty<IStoredProcedureResultColumnMapping>();
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable<IStoredProcedureResultColumnMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.InsertStoredProcedureResultColumnMappings)
?? Enumerable.Empty<IStoredProcedureResultColumnMapping>();
}

/// <summary>
/// Returns the insert stored procedure parameters to which the property is mapped.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The insert stored procedure parameters to which the property is mapped.</returns>
public static IEnumerable<IStoredProcedureParameterMapping> GetInsertStoredProcedureParameterMappings(this IProperty property)
=> (IEnumerable<IStoredProcedureParameterMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.InsertStoredProcedureParameterMappings)
?? Enumerable.Empty<IStoredProcedureParameterMapping>();
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable<IStoredProcedureParameterMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.InsertStoredProcedureParameterMappings)
?? Enumerable.Empty<IStoredProcedureParameterMapping>();
}

/// <summary>
/// Returns the delete stored procedure parameters to which the property is mapped.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The delete stored procedure parameters to which the property is mapped.</returns>
public static IEnumerable<IStoredProcedureParameterMapping> GetDeleteStoredProcedureParameterMappings(this IProperty property)
=> (IEnumerable<IStoredProcedureParameterMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.DeleteStoredProcedureParameterMappings)
?? Enumerable.Empty<IStoredProcedureParameterMapping>();
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable<IStoredProcedureParameterMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.DeleteStoredProcedureParameterMappings)
?? Enumerable.Empty<IStoredProcedureParameterMapping>();
}

/// <summary>
/// Returns the update stored procedure result columns to which the property is mapped.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The update stored procedure result columns to which the property is mapped.</returns>
public static IEnumerable<IStoredProcedureResultColumnMapping> GetUpdateStoredProcedureResultColumnMappings(this IProperty property)
=> (IEnumerable<IStoredProcedureResultColumnMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.UpdateStoredProcedureResultColumnMappings)
?? Enumerable.Empty<IStoredProcedureResultColumnMapping>();
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable<IStoredProcedureResultColumnMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.UpdateStoredProcedureResultColumnMappings)
?? Enumerable.Empty<IStoredProcedureResultColumnMapping>();
}

/// <summary>
/// Returns the update stored procedure parameters to which the property is mapped.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The update stored procedure parameters to which the property is mapped.</returns>
public static IEnumerable<IStoredProcedureParameterMapping> GetUpdateStoredProcedureParameterMappings(this IProperty property)
=> (IEnumerable<IStoredProcedureParameterMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.UpdateStoredProcedureParameterMappings)
?? Enumerable.Empty<IStoredProcedureParameterMapping>();
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable<IStoredProcedureParameterMapping>?)property.FindRuntimeAnnotationValue(
RelationalAnnotationNames.UpdateStoredProcedureParameterMappings)
?? Enumerable.Empty<IStoredProcedureParameterMapping>();
}

/// <summary>
/// Returns the column corresponding to this property if it's mapped to the given table-like store object.
Expand Down

0 comments on commit 9527e8a

Please sign in to comment.