Skip to content

Commit

Permalink
Add view support to the relational model API
Browse files Browse the repository at this point in the history
Use the new model in the query pipeline

Fixes #12846
  • Loading branch information
AndriySvyryd committed Feb 20, 2020
1 parent 1a5c4fb commit 8ea444b
Show file tree
Hide file tree
Showing 41 changed files with 1,475 additions and 279 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ public virtual void Generate(string builderName, IModel model, IndentedStringBui
CoreAnnotationNames.ChangeTrackingStrategy,
CoreAnnotationNames.OwnedTypes,
RelationalAnnotationNames.CheckConstraints,
RelationalAnnotationNames.Tables);
RelationalAnnotationNames.Tables,
RelationalAnnotationNames.Views);

if (annotations.Count > 0)
{
Expand Down Expand Up @@ -497,6 +498,7 @@ protected virtual void GeneratePropertyAnnotations([NotNull] IProperty property,
annotations,
RelationalAnnotationNames.ColumnType,
RelationalAnnotationNames.TableColumnMappings,
RelationalAnnotationNames.ViewColumnMappings,
CoreAnnotationNames.ValueGeneratorFactory,
CoreAnnotationNames.PropertyAccessMode,
CoreAnnotationNames.ChangeTrackingStrategy,
Expand Down Expand Up @@ -787,7 +789,8 @@ protected virtual void GenerateEntityTypeAnnotations(
CoreAnnotationNames.DefiningQuery,
CoreAnnotationNames.QueryFilter,
RelationalAnnotationNames.CheckConstraints,
RelationalAnnotationNames.TableMappings);
RelationalAnnotationNames.TableMappings,
RelationalAnnotationNames.ViewMappings);

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

var ignoredAnnotationTypes = new List<string>
Expand Down
11 changes: 2 additions & 9 deletions src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ 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 @@ -35,11 +34,9 @@ public class SnapshotModelProcessor : ISnapshotModelProcessor
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public SnapshotModelProcessor(
[NotNull] IOperationReporter operationReporter,
[NotNull] ProviderConventionSetBuilderDependencies conventionDependencies)
[NotNull] IOperationReporter operationReporter)
{
_operationReporter = operationReporter;
_conventionDependencies = conventionDependencies;
_relationalNames = new HashSet<string>(
typeof(RelationalAnnotationNames)
.GetRuntimeFields()
Expand Down Expand Up @@ -81,12 +78,8 @@ public virtual IModel Process(IModel model)
}
}

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ protected virtual void GenerateOnModelCreating(
RemoveAnnotation(ref annotations, RelationalAnnotationNames.MaxIdentifierLength);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.CheckConstraints);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.Tables);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.Views);
RemoveAnnotation(ref annotations, ScaffoldingAnnotationNames.DatabaseName);
RemoveAnnotation(ref annotations, ScaffoldingAnnotationNames.EntityTypeErrors);

Expand Down Expand Up @@ -366,6 +367,7 @@ private void GenerateEntityType(IEntityType entityType, bool useDataAnnotations)
RemoveAnnotation(ref annotations, RelationalAnnotationNames.Comment);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.Schema);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.TableMappings);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.ViewMappings);
RemoveAnnotation(ref annotations, ScaffoldingAnnotationNames.DbSetName);

RemoveAnnotation(ref annotations, RelationalAnnotationNames.ViewDefinition);
Expand Down Expand Up @@ -616,6 +618,7 @@ private void GenerateProperty(IProperty property, bool useDataAnnotations)
RemoveAnnotation(ref annotations, RelationalAnnotationNames.ComputedColumnSql);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.IsFixedLength);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.TableColumnMappings);
RemoveAnnotation(ref annotations, RelationalAnnotationNames.ViewColumnMappings);
RemoveAnnotation(ref annotations, ScaffoldingAnnotationNames.ColumnOrdinal);

if (!useDataAnnotations)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,29 @@ public static void SetSchema(
?.GetConfigurationSource();

/// <summary>
/// Returns the name of the table to which the entity type is mapped.
/// Returns the tables 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 views or tables 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<ITableMappingBase> GetViewOrTableMappings([NotNull] this IEntityType entityType) =>
(IEnumerable<ITableMappingBase>)GetViewMappings(entityType) ?? GetTableMappings(entityType);

/// <summary>
/// Returns the views 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<IViewMapping> GetViewMappings([NotNull] this IEntityType entityType) =>
(IEnumerable<IViewMapping>)entityType[RelationalAnnotationNames.ViewMappings];

/// <summary>
/// Returns the name of the view to which the entity type is mapped.
/// </summary>
Expand Down
36 changes: 33 additions & 3 deletions src/EFCore.Relational/Extensions/RelationalModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public static void SetDefaultSchema(
/// <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;
((IDictionary<(string, string), Table>)model[RelationalAnnotationNames.Tables])?.Values
?? Enumerable.Empty<ITable>();

/// <summary>
/// Gets the table with a given name. Returns <c>null</c> if no table with the given name is defined.
Expand All @@ -70,10 +71,39 @@ public static IEnumerable<ITable> GetTables([NotNull] this IModel model) =>
/// <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)
public static ITable FindTable([NotNull] this IModel model, [NotNull] string name, [CanBeNull] string schema)
{
Table table = null;
return ((IDictionary<(string, string), Table>)model[RelationalAnnotationNames.Tables])
?.TryGetValue((name, schema), out table) == true
? table
: null;
}

/// <summary>
/// Returns all the views 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<IView> GetViews([NotNull] this IModel model) =>
((IDictionary<(string, string), View>)model[RelationalAnnotationNames.Views])?.Values
?? Enumerable.Empty<IView>();

/// <summary>
/// Gets the view with a given name. Returns <c>null</c> if no view with the given name is defined.
/// </summary>
/// <param name="model"> The model to get the view for. </param>
/// <param name="name"> The name of the view. </param>
/// <param name="schema"> The schema of the view. </param>
/// <returns> The view with a given name or <c>null</c> if no view with the given name is defined. </returns>
public static IView FindView([NotNull] this IModel model, [NotNull] string name, [CanBeNull] string schema)
{
View view = null;
return ((IDictionary<(string, string), View>)model[RelationalAnnotationNames.Views])
?.TryGetValue((name, schema), out view) == true
? view
: null;
}

/// <summary>
/// Returns the maximum length allowed for store identifiers.
Expand Down
32 changes: 29 additions & 3 deletions src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ private static string GetDefaultColumnType(IProperty property)
var sharedTablePrincipalPrimaryKeyProperty = property.FindSharedTableRootPrimaryKeyProperty();
return sharedTablePrincipalPrimaryKeyProperty != null
? sharedTablePrincipalPrimaryKeyProperty.GetColumnType()
: property.FindRelationalMapping()?.StoreType;
: property.FindRelationalTypeMapping()?.StoreType;
}

/// <summary>
Expand Down Expand Up @@ -175,13 +175,29 @@ public static void SetColumnType(
=> property.FindAnnotation(RelationalAnnotationNames.ColumnType)?.GetConfigurationSource();

/// <summary>
/// Returns the columns to which the property is mapped.
/// Returns the table 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>
/// <returns> The table columns to which the property is mapped. </returns>
public static IEnumerable<IColumnMapping> GetTableColumnMappings([NotNull] this IProperty property) =>
(IEnumerable<IColumnMapping>)property[RelationalAnnotationNames.TableColumnMappings];

/// <summary>
/// Returns the view or table columns to which the property is mapped.
/// </summary>
/// <param name="property"> The property. </param>
/// <returns> The view or table columns to which the property is mapped. </returns>
public static IEnumerable<IColumnMappingBase> GetViewOrTableColumnMappings([NotNull] this IProperty property) =>
(IEnumerable<IColumnMappingBase>)GetViewColumnMappings(property) ?? GetTableColumnMappings(property);

/// <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([NotNull] this IProperty property) =>
(IEnumerable<IViewColumnMapping>)property[RelationalAnnotationNames.ViewColumnMappings];

/// <summary>
/// Returns the SQL expression that is used as the default value for the column this property is mapped to.
/// </summary>
Expand Down Expand Up @@ -411,7 +427,17 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IPro
/// <param name="property"> The property. </param>
/// <returns> The type mapping, or null if none was found. </returns>
[DebuggerStepThrough]
[Obsolete("Use FindRelationalTypeMapping")]
public static RelationalTypeMapping FindRelationalMapping([NotNull] this IProperty property)
=> property.FindRelationalTypeMapping();

/// <summary>
/// Returns the <see cref="RelationalTypeMapping" /> for the given property on a finalized model.
/// </summary>
/// <param name="property"> The property. </param>
/// <returns> The type mapping, or null if none was found. </returns>
[DebuggerStepThrough]
public static RelationalTypeMapping FindRelationalTypeMapping([NotNull] this IProperty property)
=> (RelationalTypeMapping)property.FindTypeMapping();

/// <summary>
Expand Down
33 changes: 9 additions & 24 deletions src/EFCore.Relational/Metadata/ITable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,36 +30,21 @@ public interface ITable : ITableBase
bool IsMigratable { get; }

/// <summary>
/// Returns a value indicating whether multiple entity types are sharing the rows in the table.
/// The check constraints for this table.
/// </summary>
bool IsSplit { get; }

/// <summary>
/// Returns the column with a given name. Returns <c>null</c> if no column with the given name is defined.
/// </summary>
IColumn FindColumn([NotNull] string name);

/// <summary>
/// Returns the foreign keys for the given entity type that point to other entity types sharing this table.
/// </summary>
IEnumerable<IForeignKey> GetInternalForeignKeys([NotNull] IEntityType entityType);

/// <summary>
/// Returns the foreign keys referencing the given entity type from other entity types sharing this table.
/// </summary>
IEnumerable<IForeignKey> GetReferencingInternalForeignKeys([NotNull] IEntityType entityType);

/// <summary>
/// Returns the check constraints for this table.
/// </summary>
IEnumerable<ICheckConstraint> GetCheckConstraints()
IEnumerable<ICheckConstraint> CheckConstraints
=> EntityTypeMappings.SelectMany(m => CheckConstraint.GetCheckConstraints(m.EntityType))
.Distinct((x, y) => x.Name == y.Name);

/// <summary>
/// Returns the comment for this table.
/// The comment for this table.
/// </summary>
public virtual string GetComment()
public virtual string Comment
=> EntityTypeMappings.Select(e => e.EntityType.GetComment()).FirstOrDefault(c => c != null);

/// <summary>
/// Returns the column with a given name. Returns <c>null</c> if no column with the given name is defined.
/// </summary>
new IColumn FindColumn([NotNull] string name);
}
}
21 changes: 21 additions & 0 deletions src/EFCore.Relational/Metadata/ITableBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

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

namespace Microsoft.EntityFrameworkCore.Metadata
Expand All @@ -21,6 +22,11 @@ public interface ITableBase : IAnnotatable
/// </summary>
string Schema { get; }

/// <summary>
/// Returns a value indicating whether multiple entity types are sharing the rows in the table.
/// </summary>
bool IsSplit { get; }

/// <summary>
/// The entity type mappings.
/// </summary>
Expand All @@ -30,5 +36,20 @@ public interface ITableBase : IAnnotatable
/// The columns defined for this table.
/// </summary>
IEnumerable<IColumnBase> Columns { get; }

/// <summary>
/// Returns the column with a given name. Returns <c>null</c> if no column with the given name is defined.
/// </summary>
IColumnBase FindColumn([NotNull] string name);

/// <summary>
/// Returns the foreign keys for the given entity type that point to other entity types sharing this table.
/// </summary>
IEnumerable<IForeignKey> GetInternalForeignKeys([NotNull] IEntityType entityType);

/// <summary>
/// Returns the foreign keys referencing the given entity type from other entity types sharing this table.
/// </summary>
IEnumerable<IForeignKey> GetReferencingInternalForeignKeys([NotNull] IEntityType entityType);
}
}
34 changes: 34 additions & 0 deletions src/EFCore.Relational/Metadata/IView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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;
using JetBrains.Annotations;

namespace Microsoft.EntityFrameworkCore.Metadata
{
/// <summary>
/// Represents a view in the database.
/// </summary>
public interface IView : ITableBase
{
/// <summary>
/// The entity type mappings.
/// </summary>
new IEnumerable<IViewMapping> EntityTypeMappings { get; }

/// <summary>
/// The columns defined for this view.
/// </summary>
new IEnumerable<IViewColumn> Columns { get; }

/// <summary>
/// Returns the column with a given name. Returns <c>null</c> if no column with the given name is defined.
/// </summary>
new IViewColumn FindColumn([NotNull] string name);

/// <summary>
/// The view definition or <c>null</c> if this view is not managed by migrations.
/// </summary>
public string ViewDefinition { get; }
}
}
23 changes: 23 additions & 0 deletions src/EFCore.Relational/Metadata/IViewColumn.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 view.
/// </summary>
public interface IViewColumn : IColumnBase
{
/// <summary>
/// The containing view.
/// </summary>
IView View { get; }

/// <summary>
/// The property mappings.
/// </summary>
new IEnumerable<IViewColumnMapping> PropertyMappings { get; }
}
}
Loading

0 comments on commit 8ea444b

Please sign in to comment.