Skip to content

Add a relational model API #19896

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

Merged
merged 1 commit into from
Feb 13, 2020
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
21 changes: 12 additions & 9 deletions src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ public virtual void Generate(string builderName, IModel model, IndentedStringBui

var annotations = model.GetAnnotations().ToList();

IgnoreAnnotationTypes(annotations, RelationalAnnotationNames.DbFunction);
IgnoreAnnotations(
annotations,
ChangeDetector.SkipDetectChangesAnnotation,
CoreAnnotationNames.ChangeTrackingStrategy,
CoreAnnotationNames.OwnedTypes,
RelationalAnnotationNames.CheckConstraints,
RelationalAnnotationNames.Tables);

if (annotations.Count > 0)
{
stringBuilder.Append(builderName);
Expand All @@ -65,14 +74,6 @@ public virtual void Generate(string builderName, IModel model, IndentedStringBui
ref annotations, RelationalAnnotationNames.DefaultSchema, nameof(RelationalModelBuilderExtensions.HasDefaultSchema),
stringBuilder);

IgnoreAnnotationTypes(annotations, RelationalAnnotationNames.DbFunction);
IgnoreAnnotations(
annotations,
ChangeDetector.SkipDetectChangesAnnotation,
CoreAnnotationNames.ChangeTrackingStrategy,
CoreAnnotationNames.OwnedTypes,
RelationalAnnotationNames.CheckConstraints);

GenerateAnnotations(annotations, stringBuilder);
}

Expand Down Expand Up @@ -495,6 +496,7 @@ protected virtual void GeneratePropertyAnnotations([NotNull] IProperty property,
IgnoreAnnotations(
annotations,
RelationalAnnotationNames.ColumnType,
RelationalAnnotationNames.TableColumnMappings,
CoreAnnotationNames.ValueGeneratorFactory,
CoreAnnotationNames.PropertyAccessMode,
CoreAnnotationNames.ChangeTrackingStrategy,
Expand Down Expand Up @@ -784,7 +786,8 @@ protected virtual void GenerateEntityTypeAnnotations(
CoreAnnotationNames.ConstructorBinding,
CoreAnnotationNames.DefiningQuery,
CoreAnnotationNames.QueryFilter,
RelationalAnnotationNames.CheckConstraints);
RelationalAnnotationNames.CheckConstraints,
RelationalAnnotationNames.TableMappings);

if (annotations.Count > 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,10 @@ private IEnumerable<string> GetAnnotationNamespaces(IEnumerable<IAnnotatable> it
CoreAnnotationNames.ValueGeneratorFactory,
CoreAnnotationNames.DefiningQuery,
CoreAnnotationNames.QueryFilter,
RelationalAnnotationNames.CheckConstraints
RelationalAnnotationNames.CheckConstraints,
RelationalAnnotationNames.Tables,
RelationalAnnotationNames.TableMappings,
RelationalAnnotationNames.TableColumnMappings
};

var ignoredAnnotationTypes = new List<string>
Expand Down
17 changes: 16 additions & 1 deletion src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

namespace Microsoft.EntityFrameworkCore.Migrations.Internal
Expand All @@ -23,6 +25,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations.Internal
public class SnapshotModelProcessor : ISnapshotModelProcessor
{
private readonly IOperationReporter _operationReporter;
private readonly ProviderConventionSetBuilderDependencies _conventionDependencies;
private readonly HashSet<string> _relationalNames;

/// <summary>
Expand All @@ -31,9 +34,12 @@ public class SnapshotModelProcessor : ISnapshotModelProcessor
/// 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 SnapshotModelProcessor([NotNull] IOperationReporter operationReporter)
public SnapshotModelProcessor(
[NotNull] IOperationReporter operationReporter,
[NotNull] ProviderConventionSetBuilderDependencies conventionDependencies)
{
_operationReporter = operationReporter;
_conventionDependencies = conventionDependencies;
_relationalNames = new HashSet<string>(
typeof(RelationalAnnotationNames)
.GetRuntimeFields()
Expand Down Expand Up @@ -75,6 +81,15 @@ public virtual IModel Process(IModel model)
}
}

if (model is IConventionModel conventionModel
&& _conventionDependencies != null)
{
var typeMappingConvention = new TypeMappingConvention(_conventionDependencies);
typeMappingConvention.ProcessModelFinalizing(conventionModel.Builder, null);

model = new RelationalModelConvention().ProcessModelFinalized(conventionModel);
}

return model is IMutableModel mutableModel
? mutableModel.FinalizeModel()
: model;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ protected virtual void GenerateOnModelCreating(
RemoveAnnotation(ref annotations, ChangeDetector.SkipDetectChangesAnnotation);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.MaxIdentifierLength);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.CheckConstraints);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.Tables);
RemoveAnnotation(ref annotations, ScaffoldingAnnotationNames.DatabaseName);
RemoveAnnotation(ref annotations, ScaffoldingAnnotationNames.EntityTypeErrors);

Expand Down Expand Up @@ -364,9 +365,10 @@ private void GenerateEntityType(IEntityType entityType, bool useDataAnnotations)
RemoveAnnotation(ref annotations, RelationalAnnotationNames.TableName);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.Comment);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.Schema);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.TableMappings);
RemoveAnnotation(ref annotations, ScaffoldingAnnotationNames.DbSetName);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.ViewDefinition);

RemoveAnnotation(ref annotations, RelationalAnnotationNames.ViewDefinition);
var isView = entityType.FindAnnotation(RelationalAnnotationNames.ViewDefinition) != null;
if (!useDataAnnotations || isView)
{
Expand Down Expand Up @@ -603,16 +605,17 @@ private void GenerateProperty(IProperty property, bool useDataAnnotations)

var annotations = property.GetAnnotations().ToList();

RemoveAnnotation(ref annotations, RelationalAnnotationNames.ColumnName);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.ColumnType);
RemoveAnnotation(ref annotations, CoreAnnotationNames.MaxLength);
RemoveAnnotation(ref annotations, CoreAnnotationNames.TypeMapping);
RemoveAnnotation(ref annotations, CoreAnnotationNames.Unicode);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.ColumnName);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.ColumnType);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.DefaultValue);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.DefaultValueSql);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.Comment);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.ComputedColumnSql);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.IsFixedLength);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.TableColumnMappings);
RemoveAnnotation(ref annotations, ScaffoldingAnnotationNames.ColumnOrdinal);

if (!useDataAnnotations)
Expand Down
40 changes: 40 additions & 0 deletions src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public static string GetTableName([NotNull] this IEntityType entityType) =>
/// <returns> The default name of the table to which the entity type would be mapped. </returns>
public static string GetDefaultTableName([NotNull] this IEntityType entityType)
{
if (entityType.GetDefiningQuery() != null)
{
return null;
}

var ownership = entityType.FindOwnership();
if (ownership != null
&& ownership.IsUnique)
Expand Down Expand Up @@ -140,6 +145,41 @@ public static void SetSchema(
=> entityType.FindAnnotation(RelationalAnnotationNames.Schema)
?.GetConfigurationSource();

/// <summary>
/// Returns the name of the table to which the entity type is mapped.
/// </summary>
/// <param name="entityType"> The entity type to get the table name for. </param>
/// <returns> The name of the table to which the entity type is mapped. </returns>
public static IEnumerable<ITableMapping> GetTableMappings([NotNull] this IEntityType entityType) =>
(IEnumerable<ITableMapping>)entityType[RelationalAnnotationNames.TableMappings];

/// <summary>
/// Returns the name of the view to which the entity type is mapped.
/// </summary>
/// <param name="entityType"> The entity type to get the view name for. </param>
/// <returns> The name of the view to which the entity type is mapped. </returns>
public static string GetViewName([NotNull] this IEntityType entityType)
{
if (entityType.BaseType != null)
{
return entityType.GetRootType().GetViewName();
}

if (entityType.FindAnnotation(RelationalAnnotationNames.ViewDefinition) != null)
{
return entityType.GetTableName();
}

var ownership = entityType.FindOwnership();
if (ownership != null
&& ownership.IsUnique)
{
return ownership.PrincipalEntityType.GetViewName();
}

return null;
}

/// <summary>
/// Finds an <see cref="ICheckConstraint" /> with the given name.
/// </summary>
Expand Down
20 changes: 20 additions & 0 deletions src/EFCore.Relational/Extensions/RelationalModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@ public static void SetDefaultSchema(
public static ConfigurationSource? GetDefaultSchemaConfigurationSource([NotNull] this IConventionModel model)
=> model.FindAnnotation(RelationalAnnotationNames.DefaultSchema)?.GetConfigurationSource();

/// <summary>
/// Returns all the tables mapped in the model.
/// </summary>
/// <param name="model"> The model to get the tables for. </param>
/// <returns> All the tables mapped in the model. </returns>
public static IEnumerable<ITable> GetTables([NotNull] this IModel model) =>
((IDictionary<(string, string), Table>)model[RelationalAnnotationNames.Tables]).Values;

/// <summary>
/// Gets the table with a given name. Returns <c>null</c> if no table with the given name is defined.
/// </summary>
/// <param name="model"> The model to get the table for. </param>
/// <param name="name"> The name of the table. </param>
/// <param name="schema"> The schema of the table. </param>
/// <returns> The table with a given name or <c>null</c> if no table with the given name is defined. </returns>
public static ITable FindTable([NotNull] this IModel model, [NotNull] string name, [CanBeNull] string schema) =>
((IDictionary<(string, string), Table>)model[RelationalAnnotationNames.Tables]).TryGetValue((name, schema), out var table)
? table
: null;

/// <summary>
/// Returns the maximum length allowed for store identifiers.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ public static void SetColumnType(
public static ConfigurationSource? GetColumnTypeConfigurationSource([NotNull] this IConventionProperty property)
=> property.FindAnnotation(RelationalAnnotationNames.ColumnType)?.GetConfigurationSource();

/// <summary>
/// Returns the columns to which the property is mapped.
/// </summary>
/// <param name="property"> The property. </param>
/// <returns> The name of the table to which the entity type is mapped. </returns>
public static IEnumerable<IColumnMapping> GetTableColumnMappings([NotNull] this IProperty property) =>
(IEnumerable<IColumnMapping>)property[RelationalAnnotationNames.TableColumnMappings];

/// <summary>
/// Returns the SQL expression that is used as the default value for the column this property is mapped to.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,13 @@ protected virtual void ValidateSharedTableCompatibility(
var tables = new Dictionary<string, List<IEntityType>>();
foreach (var entityType in model.GetEntityTypes())
{
var tableName = Format(entityType.GetSchema(), entityType.GetTableName());
var name = entityType.GetTableName();
if (name == null)
{
continue;
}

var tableName = Format(entityType.GetSchema(), name);

if (!tables.TryGetValue(tableName, out var mappedTypes))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ public override ConventionSet CreateConventionSet()
(QueryFilterDefiningQueryRewritingConvention)new RelationalQueryFilterDefiningQueryRewritingConvention(
Dependencies, RelationalDependencies));

ConventionSet.AddAfter(
conventionSet.ModelFinalizedConventions,
new RelationalModelConvention(),
typeof(ValidatingConvention));

return conventionSet;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.EntityFrameworkCore.Metadata.Internal;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
/// <summary>
/// A convention that precomputes a relational model.
/// </summary>
public class RelationalModelConvention : IModelFinalizedConvention
{
/// <inheritdoc />
public virtual IModel ProcessModelFinalized(IModel model)
=> model is IConventionModel conventionModel ? RelationalModel.AddRelationalModel(conventionModel) : model;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ private static void TryUniquifyTableNames(
foreach (var entityType in model.GetEntityTypes())
{
var tableName = (Schema: entityType.GetSchema(), TableName: entityType.GetTableName());
if (tableName.TableName == null)
{
continue;
}

if (!tables.TryGetValue(tableName, out var entityTypes))
{
Expand Down
23 changes: 23 additions & 0 deletions src/EFCore.Relational/Metadata/IColumn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;

namespace Microsoft.EntityFrameworkCore.Metadata
{
/// <summary>
/// Represents a column in a table.
/// </summary>
public interface IColumn : IColumnBase
{
/// <summary>
/// The containing table.
/// </summary>
new ITable Table { get; }

/// <summary>
/// The property mappings.
/// </summary>
new IEnumerable<IColumnMapping> PropertyMappings { get; }
}
}
40 changes: 40 additions & 0 deletions src/EFCore.Relational/Metadata/IColumnBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;

namespace Microsoft.EntityFrameworkCore.Metadata
{
/// <summary>
/// Represents a column-like object in a table-like object.
/// </summary>
public interface IColumnBase : IAnnotatable
{
/// <summary>
/// The column name.
/// </summary>
string Name { get; }

/// <summary>
/// The column type.
/// </summary>
string Type { get; }

/// <summary>
/// Whether the column can contain NULL.
/// </summary>
bool IsNullable { get; }

/// <summary>
/// The containing table-like object.
/// </summary>
ITableBase Table { get; }

/// <summary>
/// The property mappings.
/// </summary>
IEnumerable<IColumnMappingBase> PropertyMappings { get; }
}
}
21 changes: 21 additions & 0 deletions src/EFCore.Relational/Metadata/IColumnMapping.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.EntityFrameworkCore.Metadata
{
/// <summary>
/// Represents property mapping to a column.
/// </summary>
public interface IColumnMapping : IColumnMappingBase
{
/// <summary>
/// The target column.
/// </summary>
new IColumn Column { get; }

/// <summary>
/// The containing table mapping.
/// </summary>
new ITableMapping TableMapping { get; }
}
}
Loading