/// <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);
public TestQuerySqlGenerator( QuerySqlGeneratorDependencies dependencies, SelectExpression selectExpression, DiagnosticsLoggers loggers) : base(dependencies, selectExpression, loggers) { }
/// <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)); } } }
/// <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) { }
/// <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); }
/// <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()); } }
/// <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())); } }
/// <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); } }
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); }
/// <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); } }
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())); } }
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)); }
/// <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())); } } } }
/// <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); } } } }