Skip to content

Commit

Permalink
Store check constraints in the snapshot as Fluent API
Browse files Browse the repository at this point in the history
Moved CheckConstraints from IModel to IEntityType to store annotations on each entity
  • Loading branch information
Muppets committed May 14, 2019
1 parent 3637d8a commit dbb205c
Show file tree
Hide file tree
Showing 13 changed files with 348 additions and 353 deletions.
54 changes: 53 additions & 1 deletion src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ protected virtual void GenerateEntityType(

GenerateEntityTypeAnnotations(builderName, entityType, stringBuilder);

GenerateCheckConstraints(builderName, entityType, stringBuilder);

if (ownerNavigation != null)
{
GenerateRelationships(builderName, entityType, stringBuilder);
Expand Down Expand Up @@ -767,7 +769,8 @@ protected virtual void GenerateEntityTypeAnnotations(
CoreAnnotationNames.ChangeTrackingStrategy,
CoreAnnotationNames.ConstructorBinding,
CoreAnnotationNames.DefiningQuery,
CoreAnnotationNames.QueryFilter);
CoreAnnotationNames.QueryFilter,
RelationalAnnotationNames.CheckConstraints);

if (annotations.Count > 0)
{
Expand All @@ -790,6 +793,55 @@ protected virtual void GenerateEntityTypeAnnotations(
}
}

/// <summary>
/// Generates code for <see cref="ICheckConstraint" /> objects.
/// </summary>
/// <param name="builderName"> The name of the builder variable. </param>
/// <param name="entityType"> The entity type. </param>
/// <param name="stringBuilder"> The builder code is added to. </param>
protected virtual void GenerateCheckConstraints(
[NotNull] string builderName,
[NotNull] IEntityType entityType,
[NotNull] IndentedStringBuilder stringBuilder)
{
Check.NotNull(builderName, nameof(builderName));
Check.NotNull(entityType, nameof(entityType));
Check.NotNull(stringBuilder, nameof(stringBuilder));

var constraintsForEntity = entityType.GetCheckConstraints();

foreach (var checkConstraint in constraintsForEntity)
{
stringBuilder.AppendLine();

GenerateCheckConstraint(builderName, checkConstraint, stringBuilder);
}
}

/// <summary>
/// Generates code for an <see cref="ICheckConstraint" />.
/// </summary>
/// <param name="builderName"> The name of the builder variable. </param>
/// <param name="checkConstraint"> The check constraint. </param>
/// <param name="stringBuilder"> The builder code is added to. </param>
protected virtual void GenerateCheckConstraint(
[NotNull] string builderName,
[NotNull] ICheckConstraint checkConstraint,
[NotNull] IndentedStringBuilder stringBuilder)
{
Check.NotNull(builderName, nameof(builderName));
Check.NotNull(checkConstraint, nameof(checkConstraint));
Check.NotNull(stringBuilder, nameof(stringBuilder));

stringBuilder
.Append(builderName)
.Append(".HasCheckConstraint(")
.Append(Code.Literal(checkConstraint.Name))
.Append(", ")
.Append(Code.Literal(checkConstraint.Sql))
.AppendLine(");");
}

/// <summary>
/// Generates code for <see cref="IForeignKey" /> objects.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,24 +353,20 @@ public static EntityTypeBuilder HasCheckConstraint(

var entityType = entityTypeBuilder.Metadata;

var tableName = entityType.GetTableName();
var schema = entityType.GetSchema();

var constraint = entityType.Model.FindCheckConstraint(name, tableName, schema);
var constraint = entityType.FindCheckConstraint(name);
if (constraint != null)
{
if (constraint.Sql == sql)
{
((CheckConstraint)constraint).UpdateConfigurationSource(ConfigurationSource.Explicit);
return entityTypeBuilder;
}

entityType.Model.RemoveCheckConstraint(name, tableName, schema);
entityType.RemoveCheckConstraint(name);
}

if (sql != null)
{
entityType.Model.AddCheckConstraint(sql, name, tableName, schema);
entityType.AddCheckConstraint(name, sql);
}

return entityTypeBuilder;
Expand Down Expand Up @@ -414,15 +410,12 @@ public static IConventionEntityTypeBuilder HasCheckConstraint(

var entityType = entityTypeBuilder.Metadata;

var tableName = entityType.GetTableName();
var schema = entityType.GetSchema();

var constraint = entityType.Model.FindCheckConstraint(name, tableName, schema);
var constraint = entityType.FindCheckConstraint(name);
if (constraint != null)
{
if (constraint.Sql == sql)
{
((CheckConstraint)constraint).UpdateConfigurationSource(
((CheckConstraint)constraint).UpdateConfigurationSource(
fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
return entityTypeBuilder;
}
Expand All @@ -433,19 +426,19 @@ public static IConventionEntityTypeBuilder HasCheckConstraint(
return null;
}

entityType.Model.RemoveCheckConstraint(name, tableName, schema);
entityType.RemoveCheckConstraint(name);
}

if (sql != null)
{
entityType.Model.AddCheckConstraint(sql, name, tableName, schema, fromDataAnnotation);
entityType.AddCheckConstraint(name, sql, fromDataAnnotation);
}

return entityTypeBuilder;
}

/// <summary>
/// Returns a value indicating whether the discriminator column can be configured.
/// Returns a value indicating whether the check constraint can be configured.
/// </summary>
/// <param name="entityTypeBuilder"> The builder for the entity type being configured. </param>
/// <param name="name"> The name of the check constraint. </param>
Expand All @@ -462,16 +455,12 @@ public static bool CanSetCheckConstraint(
Check.NotEmpty(name, nameof(name));
Check.NullButNotEmpty(sql, nameof(sql));

var entityType = entityTypeBuilder.Metadata;
var tableName = entityType.GetTableName();
var schema = entityType.GetSchema();

var constraint = entityType.Model.FindCheckConstraint(name, tableName, schema);
var constraint = entityTypeBuilder.Metadata.FindCheckConstraint(name);

return constraint == null
|| constraint.Sql == sql
|| (fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention)
.Overrides(entityTypeBuilder.Metadata.GetDiscriminatorPropertyConfigurationSource());
.Overrides(constraint.GetConfigurationSource());
}
}
}
111 changes: 111 additions & 0 deletions src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;

// ReSharper disable once CheckNamespace
Expand Down Expand Up @@ -142,5 +144,114 @@ public static void SetSchema(
public static ConfigurationSource? GetSchemaConfigurationSource([NotNull] this IConventionEntityType entityType)
=> entityType.FindAnnotation(RelationalAnnotationNames.Schema)
?.GetConfigurationSource();

/// <summary>
/// Finds an <see cref="ICheckConstraint" /> with the given name.
/// </summary>
/// <param name="entityType"> The entity type to find the check constraint for. </param>
/// <param name="name"> The check constraint name. </param>
/// <returns>
/// The <see cref="ICheckConstraint" /> or <c>null</c> if no check constraint with the
/// given name in the given entity type was found.
/// </returns>
public static ICheckConstraint FindCheckConstraint(
[NotNull] this IEntityType entityType, [NotNull] string name)
{
Check.NotEmpty(name, nameof(name));

return CheckConstraint.FindCheckConstraint(entityType, name);
}

/// <summary>
/// Finds an <see cref="IConventionCheckConstraint" /> with the given name.
/// </summary>
/// <param name="entityType"> The entity type to find the check constraint for. </param>
/// <param name="name"> The check constraint name. </param>
/// <returns>
/// The <see cref="IConventionCheckConstraint" /> or <c>null</c> if no check constraint with the
/// given name in the given entity type was found.
/// </returns>
public static IConventionCheckConstraint FindCheckConstraint(
[NotNull] this IConventionEntityType entityType, [NotNull] string name)
=> (IConventionCheckConstraint)((IEntityType)entityType).FindCheckConstraint(name);

/// <summary>
/// Creates a new check constraint with the given name on entity type. Throws an exception
/// if a check constraint with the same name exists on the same entity type.
/// </summary>
/// <param name="entityType"> The entity type to add the check constraint to. </param>
/// <param name="name"> The check constraint name. </param>
/// <param name="sql"> The logical constraint sql used in the check constraint. </param>
/// <returns> The new check constraint. </returns>
public static ICheckConstraint AddCheckConstraint(
[NotNull] this IMutableEntityType entityType,
[NotNull] string name,
[NotNull] string sql)
{
Check.NotEmpty(name, nameof(name));
Check.NotEmpty(sql, nameof(sql));

return new CheckConstraint(entityType, name, sql, ConfigurationSource.Explicit);
}

/// <summary>
/// Creates a new check constraint with the given name on entity type. Throws an exception
/// if a check constraint with the same name exists on the same entity type.
/// </summary>
/// <param name="entityType"> The entity type to add the check constraint to. </param>
/// <param name="name"> The check constraint name. </param>
/// <param name="sql"> The logical constraint sql used in the check constraint. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> The new check constraint. </returns>
public static IConventionCheckConstraint AddCheckConstraint(
[NotNull] this IConventionEntityType entityType,
[NotNull] string sql,
[NotNull] string name,
bool fromDataAnnotation = false)
{
Check.NotEmpty(name, nameof(name));
Check.NotEmpty(sql, nameof(sql));

return new CheckConstraint(
(IMutableEntityType)entityType, name, sql,
fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
}

/// <summary>
/// Removes the <see cref="ICheckConstraint" /> with the given name.
/// </summary>
/// <param name="entityType"> The entity type to remove the check constraint from. </param>
/// <param name="name"> The check constraint name to be removed. </param>
/// <returns>
/// True if the <see cref="ICheckConstraint" /> is successfully found and removed; otherwise, false.
/// </returns>
public static bool RemoveCheckConstraint(
[NotNull] this IMutableEntityType entityType,
[NotNull] string name)
{
Check.NotEmpty(name, nameof(name));

return CheckConstraint.RemoveCheckConstraint(entityType, name);
}

/// <summary>
/// Removes the <see cref="IConventionCheckConstraint" /> with the given name.
/// </summary>
/// <param name="entityType"> The entity type to remove the check constraint from. </param>
/// <param name="name"> The check constraint name. </param>
/// <returns>
/// True if the <see cref="IConventionCheckConstraint" /> is successfully found and removed; otherwise, false.
/// </returns>
public static bool RemoveCheckConstraint(
[NotNull] this IConventionEntityType entityType,
[NotNull] string name)
=> RemoveCheckConstraint((IMutableEntityType)entityType, name);

/// <summary>
/// Returns all <see cref="ICheckConstraint" /> contained in the entity type.
/// </summary>
/// <param name="entityType"> The entity type to get the check constraints for. </param>
public static IEnumerable<ICheckConstraint> GetCheckConstraints([NotNull] this IEntityType entityType)
=> CheckConstraint.GetCheckConstraints(entityType);
}
}
Loading

0 comments on commit dbb205c

Please sign in to comment.