/// <summary>
        ///     Validates the mapping/configuration of shadow keys in the model.
        /// </summary>
        /// <param name="model"> The model to validate. </param>
        /// <param name="loggers"> Loggers to use if needed. </param>
        protected virtual void ValidateNoShadowKeys([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            Check.NotNull(model, nameof(model));

            foreach (var entityType in model.GetEntityTypes().Where(t => t.ClrType != null))
            {
                foreach (var key in entityType.GetDeclaredKeys())
                {
                    if (key.Properties.Any(p => p.IsShadowProperty()) &&
                        key is Key concreteKey &&
                        ConfigurationSource.Convention.Overrides(concreteKey.GetConfigurationSource()) &&
                        !key.IsPrimaryKey())
                    {
                        var referencingFk = key.GetReferencingForeignKeys().FirstOrDefault();

                        if (referencingFk != null)
                        {
                            throw new InvalidOperationException(
                                      CoreStrings.ReferencedShadowKey(
                                          referencingFk.DeclaringEntityType.DisplayName() +
                                          (referencingFk.DependentToPrincipal == null
                                        ? ""
                                        : "." + referencingFk.DependentToPrincipal.Name),
                                          entityType.DisplayName() +
                                          (referencingFk.PrincipalToDependent == null
                                        ? ""
                                        : "." + referencingFk.PrincipalToDependent.Name),
                                          referencingFk.Properties.Format(includeTypes: true),
                                          entityType.FindPrimaryKey().Properties.Format(includeTypes: true)));
                        }
                    }
                }
            }
        }
 /// <summary>
 ///     Creates a query SQL generator for a FromSql query.
 /// </summary>
 /// <param name="selectExpression"> The select expression. </param>
 /// <param name="sql"> The SQL. </param>
 /// <param name="arguments"> The arguments. </param>
 /// <param name="loggers"> Some loggers. </param>
 /// <returns>
 ///     The query SQL generator.
 /// </returns>
 public virtual IQuerySqlGenerator CreateFromSql(
     SelectExpression selectExpression,
     string sql,
     Expression arguments,
     DiagnosticsLoggers loggers)
 => new FromSqlNonComposedQuerySqlGenerator(
     Dependencies, selectExpression, sql, arguments, loggers);
Beispiel #3
0
 public TestQuerySqlGenerator(
     QuerySqlGeneratorDependencies dependencies,
     SelectExpression selectExpression,
     DiagnosticsLoggers loggers)
     : base(dependencies, selectExpression, loggers)
 {
 }
Beispiel #4
0
        /// <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 void ValidateSharedKeysCompatibility(
            IReadOnlyList <IEntityType> mappedTypes, string tableName, DiagnosticsLoggers loggers)
        {
            base.ValidateSharedKeysCompatibility(mappedTypes, tableName, loggers);

            var keyMappings = new Dictionary <string, IKey>();

            foreach (var key in mappedTypes.SelectMany(et => et.GetDeclaredKeys()))
            {
                var keyName = key.GetName();

                if (!keyMappings.TryGetValue(keyName, out var duplicateKey))
                {
                    keyMappings[keyName] = key;
                    continue;
                }

                if (key.GetSqlServerIsClustered()
                    != duplicateKey.GetSqlServerIsClustered())
                {
                    throw new InvalidOperationException(
                              SqlServerStrings.DuplicateKeyMismatchedClustering(
                                  key.Properties.Format(),
                                  key.DeclaringEntityType.DisplayName(),
                                  duplicateKey.Properties.Format(),
                                  duplicateKey.DeclaringEntityType.DisplayName(),
                                  tableName,
                                  keyName));
                }
            }
        }
Beispiel #5
0
        /// <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 override void Validate(IModel model, DiagnosticsLoggers loggers)
        {
            base.Validate(model, loggers);

            ValidateNoSchemas(model, loggers);
            ValidateNoSequences(model, loggers);
        }
 /// <summary>
 ///     This API supports the Entity Framework Core infrastructure and is not intended to be used
 ///     directly from your code. This API may change or be removed in future releases.
 /// </summary>
 public SqliteQuerySqlGenerator(
     [NotNull] QuerySqlGeneratorDependencies dependencies,
     [NotNull] SelectExpression selectExpression,
     DiagnosticsLoggers loggers)
     : base(dependencies, selectExpression, loggers)
 {
 }
Beispiel #7
0
        /// <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 override void Validate(IModel model, DiagnosticsLoggers loggers)
        {
            base.Validate(model, loggers);

            ValidateDefaultDecimalMapping(model, loggers);
            ValidateByteIdentityMapping(model, loggers);
            ValidateNonKeyValueGeneration(model, loggers);
            ValidateIndexIncludeProperties(model, loggers);
        }
Beispiel #8
0
        /// <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 virtual void ValidateNoSchemas([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            var logger = loggers.GetLogger <DbLoggerCategory.Model.Validation>();

            foreach (var entityType in model.GetEntityTypes().Where(e => e.GetSchema() != null))
            {
                logger.SchemaConfiguredWarning(entityType, entityType.GetSchema());
            }
        }
Beispiel #9
0
        /// <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 virtual void ValidateNoSequences([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            var logger = loggers.GetLogger <DbLoggerCategory.Model.Validation>();

            foreach (var sequence in model.GetSequences())
            {
                logger.SequenceConfiguredWarning(sequence);
            }
        }
        /// <summary>
        ///     Validates the mapping/configuration of ownership in the model.
        /// </summary>
        /// <param name="model"> The model to validate. </param>
        /// <param name="loggers"> Loggers to use if needed. </param>
        protected virtual void ValidateOwnership([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            Check.NotNull(model, nameof(model));

            foreach (var entityType in model.GetEntityTypes())
            {
                var ownerships = entityType.GetForeignKeys().Where(fk => fk.IsOwnership).ToList();
                if (ownerships.Count > 1)
                {
                    throw new InvalidOperationException(CoreStrings.MultipleOwnerships(entityType.DisplayName()));
                }

                if (ownerships.Count == 1)
                {
                    var ownership = ownerships[0];
                    if (entityType.BaseType != null &&
                        ownership.DeclaringEntityType == entityType)
                    {
                        throw new InvalidOperationException(CoreStrings.OwnedDerivedType(entityType.DisplayName()));
                    }

                    foreach (var referencingFk in entityType.GetReferencingForeignKeys().Where(
                                 fk => !fk.IsOwnership &&
                                 !Contains(fk.DeclaringEntityType.FindOwnership(), fk)))
                    {
                        throw new InvalidOperationException(
                                  CoreStrings.PrincipalOwnedType(
                                      referencingFk.DeclaringEntityType.DisplayName() +
                                      (referencingFk.DependentToPrincipal == null
                                    ? ""
                                    : "." + referencingFk.DependentToPrincipal.Name),
                                      referencingFk.PrincipalEntityType.DisplayName() +
                                      (referencingFk.PrincipalToDependent == null
                                    ? ""
                                    : "." + referencingFk.PrincipalToDependent.Name),
                                      entityType.DisplayName()));
                    }

                    foreach (var fk in entityType.GetDeclaredForeignKeys().Where(
                                 fk => !fk.IsOwnership && fk.PrincipalToDependent != null &&
                                 !Contains(fk.DeclaringEntityType.FindOwnership(), fk)))
                    {
                        throw new InvalidOperationException(
                                  CoreStrings.InverseToOwnedType(
                                      fk.PrincipalEntityType.DisplayName(),
                                      fk.PrincipalToDependent.Name,
                                      entityType.DisplayName(),
                                      ownership.PrincipalEntityType.DisplayName()));
                    }
                }
                else if (entityType.HasClrType() && ((IMutableModel)model).IsOwned(entityType.ClrType))
                {
                    throw new InvalidOperationException(CoreStrings.OwnerlessOwnedType(entityType.DisplayName()));
                }
            }
        }
        /// <summary>
        ///     Validates a model, throwing an exception if any errors are found.
        /// </summary>
        /// <param name="model"> The model to validate. </param>
        /// <param name="loggers"> Loggers to use. </param>
        public override void Validate(IModel model, DiagnosticsLoggers loggers)
        {
            base.Validate(model, loggers);

            ValidateSharedTableCompatibility(model, loggers);
            ValidateInheritanceMapping(model, loggers);
            ValidateDefaultValuesOnKeys(model, loggers);
            ValidateBoolsWithDefaults(model, loggers);
            ValidateDbFunctions(model, loggers);
        }
        /// <summary>
        ///     Validates the mapping/configuration of properties mapped to fields in the model.
        /// </summary>
        /// <param name="model"> The model to validate. </param>
        /// <param name="loggers"> Loggers to use if needed. </param>
        protected virtual void ValidateFieldMapping([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            Check.NotNull(model, nameof(model));

            foreach (var entityType in model.GetEntityTypes())
            {
                var properties = new HashSet <IPropertyBase>(
                    entityType
                    .GetDeclaredProperties()
                    .Cast <IPropertyBase>()
                    .Concat(entityType.GetDeclaredNavigations())
                    .Where(p => !p.IsShadowProperty()));

                var constructorBinding = (ConstructorBinding)entityType[CoreAnnotationNames.ConstructorBinding];

                if (constructorBinding != null)
                {
                    foreach (var consumedProperty in constructorBinding.ParameterBindings.SelectMany(p => p.ConsumedProperties))
                    {
                        properties.Remove(consumedProperty);
                    }
                }

                foreach (var propertyBase in properties)
                {
                    if (!propertyBase.TryGetMemberInfo(
                            forConstruction: true,
                            forSet: true,
                            memberInfo: out _,
                            errorMessage: out var errorMessage))
                    {
                        throw new InvalidOperationException(errorMessage);
                    }

                    if (!propertyBase.TryGetMemberInfo(
                            forConstruction: false,
                            forSet: true,
                            memberInfo: out _,
                            errorMessage: out errorMessage))
                    {
                        throw new InvalidOperationException(errorMessage);
                    }

                    if (!propertyBase.TryGetMemberInfo(
                            forConstruction: false,
                            forSet: false,
                            memberInfo: out _,
                            errorMessage: out errorMessage))
                    {
                        throw new InvalidOperationException(errorMessage);
                    }
                }
            }
        }
        /// <summary>
        ///     Validates the mapping/configuration of inheritance in the model.
        /// </summary>
        /// <param name="model"> The model to validate. </param>
        /// <param name="loggers"> Loggers to use if needed. </param>
        protected virtual void ValidateClrInheritance([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            Check.NotNull(model, nameof(model));

            var validEntityTypes = new HashSet <IEntityType>();

            foreach (var entityType in model.GetEntityTypes())
            {
                ValidateClrInheritance(model, entityType, validEntityTypes, loggers);
            }
        }
 /// <summary>
 ///     Returns the model from the cache, or creates a model if it is not present in the cache.
 /// </summary>
 /// <param name="context"> The context the model is being produced for. </param>
 /// <param name="conventionSetBuilder"> The convention set to use when creating the model. </param>
 /// <param name="validator"> The validator to verify the model can be successfully used with the context. </param>
 /// <param name="loggers"> The loggers to use. </param>
 /// <returns> The model to be used. </returns>
 public virtual IModel GetModel(
     DbContext context,
     IConventionSetBuilder conventionSetBuilder,
     IModelValidator validator,
     DiagnosticsLoggers loggers)
 => _models.GetOrAdd(
     Dependencies.ModelCacheKeyFactory.Create(context),
     // Using a Lazy here so that OnModelCreating, etc. really only gets called once, since it may not be thread safe.
     k => new Lazy <IModel>(
         () => CreateModel(context, conventionSetBuilder, validator, loggers),
         LazyThreadSafetyMode.ExecutionAndPublication)).Value;
        /// <summary>
        ///     Validates that the model does not contain any entity types without a corresponding CLR type.
        /// </summary>
        /// <param name="model"> The model to validate. </param>
        /// <param name="loggers"> Loggers to use if needed. </param>
        protected virtual void ValidateNoShadowEntities([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            Check.NotNull(model, nameof(model));

            var firstShadowEntity = model.GetEntityTypes().FirstOrDefault(entityType => !entityType.HasClrType());

            if (firstShadowEntity != null)
            {
                throw new InvalidOperationException(
                          CoreStrings.ShadowEntity(firstShadowEntity.DisplayName()));
            }
        }
Beispiel #16
0
 /// <summary>
 ///     This API supports the Entity Framework Core infrastructure and is not intended to be used
 ///     directly from your code. This API may change or be removed in future releases.
 /// </summary>
 public SqlServerQuerySqlGenerator(
     [NotNull] QuerySqlGeneratorDependencies dependencies,
     [NotNull] SelectExpression selectExpression,
     bool rowNumberPagingEnabled,
     DiagnosticsLoggers loggers)
     : base(dependencies, selectExpression, loggers)
 {
     if (rowNumberPagingEnabled)
     {
         var rowNumberPagingExpressionVisitor = new RowNumberPagingExpressionVisitor();
         rowNumberPagingExpressionVisitor.Visit(selectExpression);
     }
 }
Beispiel #17
0
        public void Stores_model_version_information_as_annotation_on_model()
        {
            var modelSource = CreateDefaultModelSource(new DbSetFinder());
            var loggers     = new DiagnosticsLoggers(
                new TestLogger <DbLoggerCategory.Model, TestLoggingDefinitions>(),
                new TestLogger <DbLoggerCategory.Model.Validation, TestLoggingDefinitions>());

            var model          = modelSource.GetModel(new Context1(), _nullConventionSetBuilder, _coreModelValidator, loggers);
            var packageVersion = typeof(Context1).Assembly.GetCustomAttributes <AssemblyMetadataAttribute>()
                                 .Single(m => m.Key == "PackageVersion").Value;

            Assert.StartsWith(packageVersion, model.GetProductVersion(), StringComparison.OrdinalIgnoreCase);
        }
        public static ConventionSet CreateConventionSet([NotNull] DbContext context)
        {
            var loggers = new DiagnosticsLoggers(
                context.GetService <IDiagnosticsLogger <DbLoggerCategory.Model> >(),
                context.GetService <IDiagnosticsLogger <DbLoggerCategory.Model.Validation> >());

            var conventionSet = context.GetService <IConventionSetBuilder>().CreateConventionSet();

            conventionSet.ModelBuiltConventions.Add(
                new ValidatingConvention(context.GetService <IModelValidator>(), loggers));

            return(conventionSet);
        }
Beispiel #19
0
        /// <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 virtual void ValidateByteIdentityMapping([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            var logger = loggers.GetLogger <DbLoggerCategory.Model.Validation>();

            foreach (var property in model.GetEntityTypes()
                     .SelectMany(t => t.GetDeclaredProperties())
                     .Where(
                         p => p.ClrType.UnwrapNullableType() == typeof(byte) &&
                         p.GetSqlServerValueGenerationStrategy() == SqlServerValueGenerationStrategy.IdentityColumn))
            {
                logger.ByteIdentityColumnWarning(property);
            }
        }
Beispiel #20
0
        public void Adds_all_entities_based_on_all_distinct_entity_types_found()
        {
            var setFinder = new FakeSetFinder();
            var loggers   = new DiagnosticsLoggers(
                new TestLogger <DbLoggerCategory.Model, TestLoggingDefinitions>(),
                new TestLogger <DbLoggerCategory.Model.Validation, TestLoggingDefinitions>());

            var model = CreateDefaultModelSource(setFinder)
                        .GetModel(InMemoryTestHelpers.Instance.CreateContext(), _nullConventionSetBuilder, new FakeModelValidator(), loggers);

            Assert.Equal(
                new[] { typeof(SetA).DisplayName(), typeof(SetB).DisplayName() },
                model.GetEntityTypes().Select(e => e.Name).ToArray());
        }
        /// <summary>
        ///     Validates the mapping/configuration of primary key nullability in the model.
        /// </summary>
        /// <param name="model"> The model to validate. </param>
        /// <param name="loggers"> Loggers to use if needed. </param>
        protected virtual void ValidateNonNullPrimaryKeys([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            Check.NotNull(model, nameof(model));

            var entityTypeWithNullPk
                = model.GetEntityTypes()
                  .FirstOrDefault(et => !((EntityType)et).IsKeyless && et.BaseType == null && et.FindPrimaryKey() == null);

            if (entityTypeWithNullPk != null)
            {
                throw new InvalidOperationException(
                          CoreStrings.EntityRequiresKey(entityTypeWithNullPk.DisplayName()));
            }
        }
Beispiel #22
0
        public void Caches_model_by_context_type()
        {
            var modelSource = CreateDefaultModelSource(new DbSetFinder());
            var loggers     = new DiagnosticsLoggers(
                new TestLogger <DbLoggerCategory.Model, TestLoggingDefinitions>(),
                new TestLogger <DbLoggerCategory.Model.Validation, TestLoggingDefinitions>());

            var model1 = modelSource.GetModel(new Context1(), _nullConventionSetBuilder, _coreModelValidator, loggers);
            var model2 = modelSource.GetModel(new Context2(), _nullConventionSetBuilder, _coreModelValidator, loggers);

            Assert.NotSame(model1, model2);
            Assert.Same(model1, modelSource.GetModel(new Context1(), _nullConventionSetBuilder, _coreModelValidator, loggers));
            Assert.Same(model2, modelSource.GetModel(new Context2(), _nullConventionSetBuilder, _coreModelValidator, loggers));
        }
Beispiel #23
0
        /// <summary>
        ///     This API supports the Entity Framework Core infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public FromSqlNonComposedQuerySqlGenerator(
            [NotNull] QuerySqlGeneratorDependencies dependencies,
            [NotNull] SelectExpression selectExpression,
            [NotNull] string sql,
            [NotNull] Expression arguments,
            DiagnosticsLoggers loggers)
            : base(dependencies, selectExpression, loggers)
        {
            Check.NotEmpty(sql, nameof(sql));
            Check.NotNull(arguments, nameof(arguments));

            _sql       = sql;
            _arguments = arguments;
        }
        /// <summary>
        ///     Validates the mapping/configuration of mutable in the model.
        /// </summary>
        /// <param name="model"> The model to validate. </param>
        /// <param name="loggers"> Loggers to use if needed. </param>
        protected virtual void ValidateNoMutableKeys([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            Check.NotNull(model, nameof(model));

            foreach (var entityType in model.GetEntityTypes())
            {
                foreach (var key in entityType.GetDeclaredKeys())
                {
                    var mutableProperty = key.Properties.FirstOrDefault(p => p.ValueGenerated.HasFlag(ValueGenerated.OnUpdate));
                    if (mutableProperty != null)
                    {
                        throw new InvalidOperationException(CoreStrings.MutableKeyProperty(mutableProperty.Name));
                    }
                }
            }
        }
        /// <summary>
        ///     Validates the mapping/configuration of query filters in the model.
        /// </summary>
        /// <param name="model"> The model to validate. </param>
        /// <param name="loggers"> Loggers to use if needed. </param>
        protected virtual void ValidateQueryFilters([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            Check.NotNull(model, nameof(model));

            foreach (var entityType in model.GetEntityTypes())
            {
                if (entityType.GetQueryFilter() != null)
                {
                    if (entityType.BaseType != null)
                    {
                        throw new InvalidOperationException(
                                  CoreStrings.BadFilterDerivedType(entityType.GetQueryFilter(), entityType.DisplayName()));
                    }
                }
            }
        }
Beispiel #26
0
 /// <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 virtual void ValidateNonKeyValueGeneration([NotNull] IModel model, DiagnosticsLoggers loggers)
 {
     foreach (var property in model.GetEntityTypes()
              .SelectMany(t => t.GetDeclaredProperties())
              .Where(
                  p => p.GetSqlServerValueGenerationStrategy() == SqlServerValueGenerationStrategy.SequenceHiLo &&
                  ((IConventionProperty)p).GetSqlServerValueGenerationStrategyConfigurationSource() != null &&
                  !p.IsKey() &&
                  p.ValueGenerated != ValueGenerated.Never &&
                  (!(p.FindAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy) is ConventionAnnotation strategy) ||
                   !ConfigurationSource.Convention.Overrides(strategy.GetConfigurationSource()))))
     {
         throw new InvalidOperationException(
                   SqlServerStrings.NonKeyValueGeneration(property.Name, property.DeclaringEntityType.DisplayName()));
     }
 }
        private void ValidateClrInheritance(
            [NotNull] IModel model,
            [NotNull] IEntityType entityType,
            [NotNull] HashSet <IEntityType> validEntityTypes,
            DiagnosticsLoggers loggers)
        {
            Check.NotNull(model, nameof(model));
            Check.NotNull(entityType, nameof(entityType));
            Check.NotNull(validEntityTypes, nameof(validEntityTypes));

            if (validEntityTypes.Contains(entityType))
            {
                return;
            }

            if (!entityType.HasDefiningNavigation() &&
                entityType.FindDeclaredOwnership() == null &&
                entityType.BaseType != null)
            {
                var baseClrType = entityType.ClrType?.GetTypeInfo().BaseType;
                while (baseClrType != null)
                {
                    var baseEntityType = model.FindEntityType(baseClrType);
                    if (baseEntityType != null)
                    {
                        if (!baseEntityType.IsAssignableFrom(entityType))
                        {
                            throw new InvalidOperationException(
                                      CoreStrings.InconsistentInheritance(entityType.DisplayName(), baseEntityType.DisplayName()));
                        }

                        break;
                    }

                    baseClrType = baseClrType.GetTypeInfo().BaseType;
                }
            }

            if (entityType.ClrType?.IsInstantiable() == false &&
                !entityType.GetDerivedTypes().Any())
            {
                throw new InvalidOperationException(
                          CoreStrings.AbstractLeafEntityType(entityType.DisplayName()));
            }

            validEntityTypes.Add(entityType);
        }
        /// <summary>
        ///     This API supports the Entity Framework Core infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        protected override void ValidateSharedColumnsCompatibility(
            IReadOnlyList <IEntityType> mappedTypes, string tableName, DiagnosticsLoggers loggers)
        {
            base.ValidateSharedColumnsCompatibility(mappedTypes, tableName, loggers);

            var identityColumns  = new List <IProperty>();
            var propertyMappings = new Dictionary <string, IProperty>();

            foreach (var property in mappedTypes.SelectMany(et => et.GetDeclaredProperties()))
            {
                var propertyAnnotations = property.Relational();
                var columnName          = propertyAnnotations.ColumnName;
                if (propertyMappings.TryGetValue(columnName, out var duplicateProperty))
                {
                    var propertyStrategy          = property.SqlServer().ValueGenerationStrategy;
                    var duplicatePropertyStrategy = duplicateProperty.SqlServer().ValueGenerationStrategy;
                    if (propertyStrategy != duplicatePropertyStrategy &&
                        (propertyStrategy == SqlServerValueGenerationStrategy.IdentityColumn ||
                         duplicatePropertyStrategy == SqlServerValueGenerationStrategy.IdentityColumn))
                    {
                        throw new InvalidOperationException(
                                  SqlServerStrings.DuplicateColumnNameValueGenerationStrategyMismatch(
                                      duplicateProperty.DeclaringEntityType.DisplayName(),
                                      duplicateProperty.Name,
                                      property.DeclaringEntityType.DisplayName(),
                                      property.Name,
                                      columnName,
                                      tableName));
                    }
                }
                else
                {
                    propertyMappings[columnName] = property;
                    if (property.SqlServer().ValueGenerationStrategy == SqlServerValueGenerationStrategy.IdentityColumn)
                    {
                        identityColumns.Add(property);
                    }
                }
            }

            if (identityColumns.Count > 1)
            {
                var sb = new StringBuilder()
                         .AppendJoin(identityColumns.Select(p => "'" + p.DeclaringEntityType.DisplayName() + "." + p.Name + "'"));
                throw new InvalidOperationException(SqlServerStrings.MultipleIdentityColumns(sb, tableName));
            }
        }
        /// <summary>
        ///     Validates the mapping/configuration of <see cref="bool"/> properties in the model.
        /// </summary>
        /// <param name="model"> The model to validate. </param>
        /// <param name="loggers"> Loggers to use if needed. </param>
        protected virtual void ValidateBoolsWithDefaults([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            Check.NotNull(model, nameof(model));

            var logger = loggers.GetLogger <DbLoggerCategory.Model.Validation>();

            foreach (var property in model.GetEntityTypes().SelectMany(e => e.GetDeclaredProperties()))
            {
                if (property.ClrType == typeof(bool) &&
                    property.ValueGenerated != ValueGenerated.Never &&
                    (IsNotNullAndFalse(property.Relational().DefaultValue) ||
                     property.Relational().DefaultValueSql != null))
                {
                    logger.BoolWithDefaultWarning(property);
                }
            }
        }
        /// <summary>
        ///     Logs all shadow properties that were created because there was no matching CLR member.
        /// </summary>
        /// <param name="model"> The model to validate. </param>
        /// <param name="loggers"> Loggers to use if needed. </param>
        protected virtual void LogShadowProperties([NotNull] IModel model, DiagnosticsLoggers loggers)
        {
            Check.NotNull(model, nameof(model));

            var modelLogger = loggers.GetLogger <DbLoggerCategory.Model>();

            foreach (var entityType in model.GetEntityTypes().Where(t => t.ClrType != null))
            {
                foreach (var property in entityType.GetDeclaredProperties())
                {
                    if (property.IsShadowProperty())
                    {
                        modelLogger.ShadowPropertyCreated(property);
                    }
                }
            }
        }