private static bool?GetDefaultIsClustered([NotNull] IKey key, StoreObjectIdentifier storeObject)
        {
            var sharedTableRootKey = key.FindSharedObjectRootKey(storeObject);

            return(sharedTableRootKey?.IsClustered(storeObject));
        }
 /// <summary>
 ///     Gets the foreign keys for the given entity type that point to other entity types
 ///     sharing the same table-like store object.
 /// </summary>
 /// <param name="entityType"> The entity type. </param>
 /// <param name="storeObject"> The identifier of the store object. </param>
 public static IEnumerable <IConventionForeignKey> FindRowInternalForeignKeys(
     [NotNull] this IConventionEntityType entityType, StoreObjectIdentifier storeObject)
 => ((IEntityType)entityType).FindRowInternalForeignKeys(storeObject).Cast <IConventionForeignKey>();
Example #3
0
 public SaveContextQueryBase(DbContext context, string schema, string table, List <IProperty> propertiesToInsert, List <IProperty> propertiesToUpdate, List <List <IProperty> > propertiesForPivotSet, List <IProperty> propertiesToBulkLoad, List <IEntityType> entityTypes, CancellationToken cancellationToken, StoreObjectIdentifier storeObject)
 {
     this.StoreObject           = storeObject;
     this.CancellationToken     = cancellationToken;
     this.PropertiesToInsert    = propertiesToInsert;
     this.PropertiesToUpdate    = propertiesToUpdate;
     this.PropertiesForPivotSet = propertiesForPivotSet;
     this.PropertiesToBulkLoad  = propertiesToBulkLoad;
     this.Schema      = schema;
     this.Table       = table;
     this.Context     = context;
     this.EntityTypes = entityTypes;
 }
Example #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 ValidateSharedTableCompatibility(
        IReadOnlyList <IEntityType> mappedTypes,
        string tableName,
        string?schema,
        IDiagnosticsLogger <DbLoggerCategory.Model.Validation> logger)
    {
        var firstMappedType   = mappedTypes[0];
        var isMemoryOptimized = firstMappedType.IsMemoryOptimized();

        foreach (var otherMappedType in mappedTypes.Skip(1))
        {
            if (isMemoryOptimized != otherMappedType.IsMemoryOptimized())
            {
                throw new InvalidOperationException(
                          SqlServerStrings.IncompatibleTableMemoryOptimizedMismatch(
                              tableName, firstMappedType.DisplayName(), otherMappedType.DisplayName(),
                              isMemoryOptimized ? firstMappedType.DisplayName() : otherMappedType.DisplayName(),
                              !isMemoryOptimized ? firstMappedType.DisplayName() : otherMappedType.DisplayName()));
            }
        }

        if (mappedTypes.Any(t => t.IsTemporal()) &&
            mappedTypes.Select(t => t.GetRootType()).Distinct().Count() > 1)
        {
            // table splitting is only supported when all entites mapped to this table
            // have consistent temporal period mappings also
            var expectedPeriodStartColumnName = default(string);
            var expectedPeriodEndColumnName   = default(string);

            foreach (var mappedType in mappedTypes.Where(t => t.BaseType == null))
            {
                if (!mappedType.IsTemporal())
                {
                    throw new InvalidOperationException(
                              SqlServerStrings.TemporalAllEntitiesMappedToSameTableMustBeTemporal(
                                  mappedType.DisplayName()));
                }

                var periodStartPropertyName = mappedType.GetPeriodStartPropertyName();
                var periodEndPropertyName   = mappedType.GetPeriodEndPropertyName();

                var periodStartProperty = mappedType.GetProperty(periodStartPropertyName !);
                var periodEndProperty   = mappedType.GetProperty(periodEndPropertyName !);

                var storeObjectIdentifier = StoreObjectIdentifier.Table(tableName, mappedType.GetSchema());
                var periodStartColumnName = periodStartProperty.GetColumnName(storeObjectIdentifier);
                var periodEndColumnName   = periodEndProperty.GetColumnName(storeObjectIdentifier);

                if (expectedPeriodStartColumnName == null)
                {
                    expectedPeriodStartColumnName = periodStartColumnName;
                }
                else if (expectedPeriodStartColumnName != periodStartColumnName)
                {
                    throw new InvalidOperationException(
                              SqlServerStrings.TemporalNotSupportedForTableSplittingWithInconsistentPeriodMapping(
                                  "start",
                                  mappedType.DisplayName(),
                                  periodStartPropertyName,
                                  periodStartColumnName,
                                  expectedPeriodStartColumnName));
                }

                if (expectedPeriodEndColumnName == null)
                {
                    expectedPeriodEndColumnName = periodEndColumnName;
                }
                else if (expectedPeriodEndColumnName != periodEndColumnName)
                {
                    throw new InvalidOperationException(
                              SqlServerStrings.TemporalNotSupportedForTableSplittingWithInconsistentPeriodMapping(
                                  "end",
                                  mappedType.DisplayName(),
                                  periodEndPropertyName,
                                  periodEndColumnName,
                                  expectedPeriodEndColumnName));
                }
            }
        }

        base.ValidateSharedTableCompatibility(mappedTypes, tableName, schema, logger);
    }
Example #5
0
        private void LoadData <T>(HookingDbContext context, Type type, IList <T> entities, bool loadOnlyPKColumn)
        {
            LoadOnlyPKColumn = loadOnlyPKColumn;
            var entityType = context.Model.FindEntityType(type);

            if (entityType == null)
            {
                type            = entities[0].GetType();
                entityType      = context.Model.FindEntityType(type);
                HasAbstractList = true;
            }
            if (entityType == null)
            {
                throw new InvalidOperationException($"DbContext does not contain EntitySet for Type: { type.Name }");
            }

            //var relationalData = entityType.Relational(); relationalData.Schema relationalData.TableName // DEPRECATED in Core3.0
            var    storeObjectIdent = StoreObjectIdentifier.Create(entityType, StoreObjectType.Table).Value;
            bool   isSqlServer      = context.DataProvider.ProviderType == DataProviderType.SqlServer;
            string defaultSchema    = isSqlServer ? "dbo" : null;

            Schema    = entityType.GetSchema() ?? defaultSchema;
            TableName = entityType.GetTableName();

            TempTableSufix = "Temp";

            if (!BulkConfig.UseTempDB || BulkConfig.UniqueTableNameTempDb)
            {
                TempTableSufix += Guid.NewGuid().ToString().Substring(0, 8); // 8 chars of Guid as tableNameSufix to avoid same name collision with other tables
            }

            bool areSpecifiedUpdateByProperties = BulkConfig.UpdateByProperties?.Count > 0;
            var  primaryKeys = entityType.FindPrimaryKey()?.Properties?.Select(a => a.Name)?.ToList();

            HasSinglePrimaryKey = primaryKeys?.Count == 1;
            PrimaryKeys         = areSpecifiedUpdateByProperties ? BulkConfig.UpdateByProperties : primaryKeys;

            var allProperties = entityType.GetProperties().AsEnumerable();

            // load all derived type properties
            if (entityType.IsAbstract())
            {
                var extendedAllProperties = allProperties.ToList();
                foreach (var derived in entityType.GetDirectlyDerivedTypes())
                {
                    extendedAllProperties.AddRange(derived.GetProperties());
                }

                allProperties = extendedAllProperties.Distinct();
            }

            var ownedTypes = entityType.GetNavigations().Where(a => a.TargetEntityType.IsOwned());

            HasOwnedTypes  = ownedTypes.Any();
            OwnedTypesDict = ownedTypes.ToDictionary(a => a.Name, a => a);

            IdentityColumnName = allProperties.SingleOrDefault(a => a.IsPrimaryKey() && a.ClrType.Name.StartsWith("Int") && a.ValueGenerated == ValueGenerated.OnAdd)?.GetColumnName(storeObjectIdent); // ValueGenerated equals OnAdd even for nonIdentity column like Guid so we only type int as second condition

            // timestamp/row version properties are only set by the Db, the property has a [Timestamp] Attribute or is configured in FluentAPI with .IsRowVersion()
            // They can be identified by the columne type "timestamp" or .IsConcurrencyToken in combination with .ValueGenerated == ValueGenerated.OnAddOrUpdate
            string timestampDbTypeName = nameof(TimestampAttribute).Replace("Attribute", "").ToLower(); // = "timestamp";
            var    timeStampProperties = allProperties.Where(a => (a.IsConcurrencyToken && a.ValueGenerated == ValueGenerated.OnAddOrUpdate) || a.GetColumnType() == timestampDbTypeName);

            TimeStampColumnName = timeStampProperties.FirstOrDefault()?.GetColumnName(storeObjectIdent); // can be only One
            var allPropertiesExceptTimeStamp = allProperties.Except(timeStampProperties);
            var properties = allPropertiesExceptTimeStamp.Where(a => a.GetComputedColumnSql() == null);

            // TimeStamp prop. is last column in OutputTable since it is added later with varbinary(8) type in which Output can be inserted
            OutputPropertyColumnNamesDict   = allPropertiesExceptTimeStamp.Concat(timeStampProperties).ToDictionary(a => a.Name, b => b.GetColumnName(storeObjectIdent).Replace("]", "]]")); // square brackets have to be escaped
            ColumnNameContainsSquareBracket = allPropertiesExceptTimeStamp.Concat(timeStampProperties).Any(a => a.GetColumnName(storeObjectIdent).Contains("]"));

            bool AreSpecifiedPropertiesToInclude = BulkConfig.PropertiesToInclude?.Count() > 0;
            bool AreSpecifiedPropertiesToExclude = BulkConfig.PropertiesToExclude?.Count() > 0;

            if (AreSpecifiedPropertiesToInclude)
            {
                if (areSpecifiedUpdateByProperties) // Adds UpdateByProperties to PropertyToInclude if they are not already explicitly listed
                {
                    foreach (var updateByProperty in BulkConfig.UpdateByProperties)
                    {
                        if (!BulkConfig.PropertiesToInclude.Contains(updateByProperty))
                        {
                            BulkConfig.PropertiesToInclude.Add(updateByProperty);
                        }
                    }
                }
                else // Adds PrimaryKeys to PropertyToInclude if they are not already explicitly listed
                {
                    foreach (var primaryKey in PrimaryKeys)
                    {
                        if (!BulkConfig.PropertiesToInclude.Contains(primaryKey))
                        {
                            BulkConfig.PropertiesToInclude.Add(primaryKey);
                        }
                    }
                }
            }

            foreach (var property in allProperties)
            {
                if (property.PropertyInfo != null) // skip Shadow Property
                {
                    FastPropertyDict.Add(property.Name, FastProperty.GetProperty(property.PropertyInfo));
                }
            }

            UpdateByPropertiesAreNullable = properties.Any(a => PrimaryKeys != null && PrimaryKeys.Contains(a.Name) && a.IsNullable);

            if (AreSpecifiedPropertiesToInclude || AreSpecifiedPropertiesToExclude)
            {
                if (AreSpecifiedPropertiesToInclude && AreSpecifiedPropertiesToExclude)
                {
                    throw new InvalidOperationException("Only one group of properties, either PropertiesToInclude or PropertiesToExclude can be specified, specifying both not allowed.");
                }
                if (AreSpecifiedPropertiesToInclude)
                {
                    properties = properties.Where(a => BulkConfig.PropertiesToInclude.Contains(a.Name));
                }
                if (AreSpecifiedPropertiesToExclude)
                {
                    properties = properties.Where(a => !BulkConfig.PropertiesToExclude.Contains(a.Name));
                }
            }

            if (loadOnlyPKColumn)
            {
                PropertyColumnNamesDict = properties.Where(a => PrimaryKeys.Contains(a.Name)).ToDictionary(a => a.Name, b => b.GetColumnName(storeObjectIdent).Replace("]", "]]"));
            }
            else
            {
                PropertyColumnNamesDict = properties.ToDictionary(a => a.Name, b => b.GetColumnName(storeObjectIdent).Replace("]", "]]"));
                ShadowProperties        = new HashSet <string>(properties.Where(p => p.IsShadowProperty() && !p.IsForeignKey()).Select(p => p.GetColumnName(storeObjectIdent)));
                foreach (var property in properties.Where(p => p.GetValueConverter() != null))
                {
                    string         columnName = property.GetColumnName(storeObjectIdent);
                    ValueConverter converter  = property.GetValueConverter();
                    ConvertibleProperties.Add(columnName, converter);
                }

                foreach (var navigation in entityType.GetNavigations().Where(x => !x.IsCollection && !x.TargetEntityType.IsOwned()))
                {
                    FastPropertyDict.Add(navigation.Name, FastProperty.GetProperty(navigation.PropertyInfo));
                }

                if (HasOwnedTypes)  // Support owned entity property update. TODO: Optimize
                {
                    foreach (var navgationProperty in ownedTypes)
                    {
                        var property = navgationProperty.PropertyInfo;
                        FastPropertyDict.Add(property.Name, FastProperty.GetProperty(property));

                        Type navOwnedType    = type.Assembly.GetType(property.PropertyType.FullName);
                        var  ownedEntityType = context.Model.FindEntityType(property.PropertyType);
                        if (ownedEntityType == null) // when entity has more then one ownedType (e.g. Address HomeAddress, Address WorkAddress) or one ownedType is in multiple Entities like Audit is usually.
                        {
                            ownedEntityType = context.Model.GetEntityTypes().SingleOrDefault(a => a.DefiningNavigationName == property.Name && a.DefiningEntityType.Name == entityType.Name);
                        }
                        var ownedEntityProperties = ownedEntityType.GetProperties().ToList();
                        var ownedEntityPropertyNameColumnNameDict = new Dictionary <string, string>();
                        var ownedStoreObjectIdent = StoreObjectIdentifier.Create(ownedEntityType, StoreObjectType.Table).Value;

                        foreach (var ownedEntityProperty in ownedEntityProperties)
                        {
                            if (!ownedEntityProperty.IsPrimaryKey())
                            {
                                string columnName = ownedEntityProperty.GetColumnName(ownedStoreObjectIdent);
                                ownedEntityPropertyNameColumnNameDict.Add(ownedEntityProperty.Name, columnName);
                                var ownedEntityPropertyFullName = property.Name + "_" + ownedEntityProperty.Name;
                                if (!FastPropertyDict.ContainsKey(ownedEntityPropertyFullName))
                                {
                                    FastPropertyDict.Add(ownedEntityPropertyFullName, FastProperty.GetProperty(ownedEntityProperty.PropertyInfo));
                                }
                            }

                            var converter = ownedEntityProperty.GetValueConverter();
                            if (converter != null)
                            {
                                ConvertibleProperties.Add($"{navgationProperty.Name}_{ownedEntityProperty.Name}", converter);
                            }
                        }
                        var ownedProperties = property.PropertyType.GetProperties();
                        foreach (var ownedProperty in ownedProperties)
                        {
                            if (ownedEntityPropertyNameColumnNameDict.ContainsKey(ownedProperty.Name))
                            {
                                string columnName        = ownedEntityPropertyNameColumnNameDict[ownedProperty.Name];
                                var    ownedPropertyType = Nullable.GetUnderlyingType(ownedProperty.PropertyType) ?? ownedProperty.PropertyType;

                                bool doAddProperty = true;
                                if (AreSpecifiedPropertiesToInclude && !BulkConfig.PropertiesToInclude.Contains(columnName))
                                {
                                    doAddProperty = false;
                                }
                                if (AreSpecifiedPropertiesToExclude && BulkConfig.PropertiesToExclude.Contains(columnName))
                                {
                                    doAddProperty = false;
                                }

                                if (doAddProperty)
                                {
                                    PropertyColumnNamesDict.Add(property.Name + "." + ownedProperty.Name, columnName);
                                    OutputPropertyColumnNamesDict.Add(property.Name + "." + ownedProperty.Name, columnName);
                                }
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        ///     Returns the foreign key constraint name.
        /// </summary>
        /// <param name="foreignKey"> The foreign key. </param>
        /// <param name="storeObject"> The identifier of the containing store object. </param>
        /// <param name="principalStoreObject"> The identifier of the principal store object. </param>
        /// <returns> The foreign key constraint name. </returns>
        public static string GetConstraintName(
            [NotNull] this IForeignKey foreignKey, StoreObjectIdentifier storeObject, StoreObjectIdentifier principalStoreObject)
        {
            var annotation = foreignKey.FindAnnotation(RelationalAnnotationNames.Name);

            return(annotation != null
                ? (string)annotation.Value
                : foreignKey.GetDefaultName(storeObject, principalStoreObject));
        }
Example #7
0
 /// <summary>
 ///     Returns the name of the index in the database.
 /// </summary>
 /// <param name="index"> The index. </param>
 /// <param name="storeObject"> The identifier of the store object. </param>
 /// <returns> The name of the index in the database. </returns>
 public static string GetDatabaseName([NotNull] this IIndex index, StoreObjectIdentifier storeObject)
 => (string)index[RelationalAnnotationNames.Name]
 ?? index.Name
 ?? index.GetDefaultDatabaseName(storeObject);
Example #8
0
            public virtual void Owned_types_use_table_splitting_by_default()
            {
                var modelBuilder = CreateModelBuilder();
                var model        = modelBuilder.Model;

                modelBuilder.Entity <Book>().OwnsOne(b => b.AlternateLabel)
                .Ignore(l => l.Book)
                .OwnsOne(l => l.AnotherBookLabel)
                .Ignore(l => l.Book)
                .OwnsOne(s => s.SpecialBookLabel)
                .Ignore(l => l.Book)
                .Ignore(l => l.BookLabel);

                modelBuilder.Entity <Book>().OwnsOne(b => b.Label)
                .Ignore(l => l.Book)
                .OwnsOne(l => l.SpecialBookLabel)
                .Ignore(l => l.Book)
                .OwnsOne(a => a.AnotherBookLabel)
                .Ignore(l => l.Book);

                modelBuilder.Entity <Book>().OwnsOne(b => b.Label)
                .OwnsOne(l => l.AnotherBookLabel)
                .Ignore(l => l.Book)
                .OwnsOne(a => a.SpecialBookLabel)
                .Ignore(l => l.Book)
                .Ignore(l => l.BookLabel);

                modelBuilder.Entity <Book>().OwnsOne(b => b.AlternateLabel)
                .OwnsOne(l => l.SpecialBookLabel)
                .Ignore(l => l.Book)
                .OwnsOne(s => s.AnotherBookLabel)
                .Ignore(l => l.Book);

                var book                 = model.FindEntityType(typeof(Book));
                var bookOwnership1       = book.FindNavigation(nameof(Book.Label)).ForeignKey;
                var bookOwnership2       = book.FindNavigation(nameof(Book.AlternateLabel)).ForeignKey;
                var bookLabel1Ownership1 = bookOwnership1.DeclaringEntityType.FindNavigation(nameof(BookLabel.AnotherBookLabel)).ForeignKey;
                var bookLabel1Ownership2 = bookOwnership1.DeclaringEntityType.FindNavigation(nameof(BookLabel.SpecialBookLabel)).ForeignKey;
                var bookLabel2Ownership1 = bookOwnership2.DeclaringEntityType.FindNavigation(nameof(BookLabel.AnotherBookLabel)).ForeignKey;
                var bookLabel2Ownership2 = bookOwnership2.DeclaringEntityType.FindNavigation(nameof(BookLabel.SpecialBookLabel)).ForeignKey;

                Assert.Equal(book.GetTableName(), bookOwnership1.DeclaringEntityType.GetTableName());
                Assert.Equal(book.GetTableName(), bookOwnership2.DeclaringEntityType.GetTableName());
                Assert.Equal(book.GetTableName(), bookLabel1Ownership1.DeclaringEntityType.GetTableName());
                Assert.Equal(book.GetTableName(), bookLabel1Ownership2.DeclaringEntityType.GetTableName());
                Assert.Equal(book.GetTableName(), bookLabel2Ownership1.DeclaringEntityType.GetTableName());
                Assert.Equal(book.GetTableName(), bookLabel2Ownership2.DeclaringEntityType.GetTableName());

                Assert.NotSame(bookOwnership1.DeclaringEntityType, bookOwnership2.DeclaringEntityType);
                Assert.Single(bookOwnership1.DeclaringEntityType.GetForeignKeys());
                Assert.Single(bookOwnership1.DeclaringEntityType.GetForeignKeys());

                Assert.NotSame(bookLabel1Ownership1.DeclaringEntityType, bookLabel2Ownership1.DeclaringEntityType);
                Assert.NotSame(bookLabel1Ownership2.DeclaringEntityType, bookLabel2Ownership2.DeclaringEntityType);
                Assert.Single(bookLabel1Ownership1.DeclaringEntityType.GetForeignKeys());
                Assert.Single(bookLabel1Ownership2.DeclaringEntityType.GetForeignKeys());
                Assert.Single(bookLabel2Ownership1.DeclaringEntityType.GetForeignKeys());
                Assert.Single(bookLabel2Ownership2.DeclaringEntityType.GetForeignKeys());

                Assert.Equal(2, model.GetEntityTypes().Count(e => e.ClrType == typeof(BookLabel)));
                Assert.Equal(4, model.GetEntityTypes().Count(e => e.ClrType == typeof(AnotherBookLabel)));
                Assert.Equal(4, model.GetEntityTypes().Count(e => e.ClrType == typeof(SpecialBookLabel)));

                Assert.Null(
                    bookOwnership1.DeclaringEntityType.FindProperty(nameof(BookLabel.Id))
                    .GetColumnName(StoreObjectIdentifier.Table("Label", null)));
                Assert.Null(
                    bookLabel2Ownership1.DeclaringEntityType.FindProperty(nameof(BookLabel.Id))
                    .GetColumnName(StoreObjectIdentifier.Table("AlternateLabel", null)));

                modelBuilder.Entity <Book>().OwnsOne(b => b.Label).ToTable("Label");
                modelBuilder.Entity <Book>().OwnsOne(b => b.AlternateLabel).ToTable("AlternateLabel");

                modelBuilder.FinalizeModel();

                Assert.Equal(
                    nameof(BookLabel.Id),
                    bookOwnership1.DeclaringEntityType.FindProperty(nameof(BookLabel.Id))
                    .GetColumnName(StoreObjectIdentifier.Table("Label", null)));
                Assert.Equal(
                    nameof(BookLabel.AnotherBookLabel) + "_" + nameof(BookLabel.Id),
                    bookLabel2Ownership1.DeclaringEntityType.FindProperty(nameof(BookLabel.Id))
                    .GetColumnName(StoreObjectIdentifier.Table("AlternateLabel", null)));
            }
Example #9
0
        private IReadOnlyList <ColumnModification> GenerateColumnModifications()
        {
            var state               = EntityState;
            var adding              = state == EntityState.Added;
            var updating            = state == EntityState.Modified;
            var columnModifications = new List <ColumnModification>();
            Dictionary <string, ColumnValuePropagator> sharedColumnMap = null;

            if (_entries.Count > 1 ||
                (_entries.Count == 1 && _entries[0].SharedIdentityEntry != null))
            {
                sharedColumnMap = new Dictionary <string, ColumnValuePropagator>();

                if (_comparer != null)
                {
                    _entries.Sort(_comparer);
                }

                foreach (var entry in _entries)
                {
                    if (entry.SharedIdentityEntry != null)
                    {
                        InitializeSharedColumns(entry.SharedIdentityEntry, updating, sharedColumnMap);
                    }

                    InitializeSharedColumns(entry, updating, sharedColumnMap);
                }
            }

            foreach (var entry in _entries)
            {
                var nonMainEntry = updating &&
                                   (entry.EntityState == EntityState.Deleted ||
                                    entry.EntityState == EntityState.Added);

                foreach (var property in entry.EntityType.GetProperties())
                {
                    var column = (IColumn)property.FindColumn(StoreObjectIdentifier.Table(TableName, Schema));
                    if (column == null)
                    {
                        continue;
                    }

                    var isKey       = property.IsPrimaryKey();
                    var isCondition = !adding && (isKey || property.IsConcurrencyToken);
                    var readValue   = entry.IsStoreGenerated(property);
                    ColumnValuePropagator columnPropagator = null;
                    if (sharedColumnMap != null)
                    {
                        columnPropagator = sharedColumnMap[column.Name];
                    }

                    var writeValue = false;
                    if (!readValue)
                    {
                        if (adding)
                        {
                            writeValue = property.GetBeforeSaveBehavior() == PropertySaveBehavior.Save;
                        }
                        else if ((updating && property.GetAfterSaveBehavior() == PropertySaveBehavior.Save) ||
                                 (!isKey && nonMainEntry))
                        {
                            writeValue = columnPropagator?.TryPropagate(property, entry)
                                         ?? entry.IsModified(property);
                        }
                    }

                    if (readValue ||
                        writeValue ||
                        isCondition)
                    {
                        if (readValue)
                        {
                            _requiresResultPropagation = true;
                        }

                        var columnModification = new ColumnModification(
                            entry,
                            property,
                            column,
                            _generateParameterName,
                            readValue,
                            writeValue,
                            isKey,
                            isCondition,
                            _sensitiveLoggingEnabled);

                        if (columnPropagator != null)
                        {
                            if (columnPropagator.ColumnModification != null)
                            {
                                columnPropagator.ColumnModification.AddSharedColumnModification(columnModification);

                                continue;
                            }

                            columnPropagator.ColumnModification = columnModification;
                        }

                        columnModifications.Add(columnModification);
                    }
                }
            }

            return(columnModifications);
        }
Example #10
0
        /// <summary>
        ///     <para>
        ///         Returns the <see cref="MySqlValueGenerationStrategy" /> to use for the property.
        ///     </para>
        ///     <para>
        ///         If no strategy is set for the property, then the strategy to use will be taken from the <see cref="IModel" />.
        ///     </para>
        /// </summary>
        /// <returns> The strategy, or <see cref="MySqlValueGenerationStrategy.None"/> if none was set. </returns>
        public static MySqlValueGenerationStrategy?GetValueGenerationStrategy([NotNull] this IReadOnlyProperty property, StoreObjectIdentifier storeObject = default)
        {
            var annotation = property[MySqlAnnotationNames.ValueGenerationStrategy];

            if (annotation != null)
            {
                // Allow users to use the underlying type value instead of the enum itself.
                // Workaround for: https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1205
                //return ObjectToEnumConverter.GetEnumValue<MySqlValueGenerationStrategy>(annotation);
                return(ObjectToEnumConverter.GetEnumValue <MySqlValueGenerationStrategy>(annotation));
            }

            if (property.GetContainingForeignKeys().Any(fk => !fk.IsBaseLinking()) ||
                property.TryGetDefaultValue(storeObject, out _) ||
                property.GetDefaultValueSql() != null ||
                property.GetComputedColumnSql() != null)
            {
                return(null);
            }

            if (storeObject != default &&
                property.ValueGenerated == ValueGenerated.Never)
            {
                return(property.FindSharedStoreObjectRootProperty(storeObject)
                       ?.GetValueGenerationStrategy(storeObject));
            }

            if (property.ValueGenerated == ValueGenerated.OnAdd &&
                IsCompatibleIdentityColumn(property))
            {
                return(MySqlValueGenerationStrategy.IdentityColumn);
            }

            if (property.ValueGenerated == ValueGenerated.OnAddOrUpdate &&
                IsCompatibleComputedColumn(property))
            {
                return(MySqlValueGenerationStrategy.ComputedColumn);
            }

            return(null);
        }
        /// <inheritdoc />
        protected override void ValidateCompatible(
            IProperty property,
            IProperty duplicateProperty,
            string columnName,
            StoreObjectIdentifier storeObject,
            IDiagnosticsLogger <DbLoggerCategory.Model.Validation> logger)
        {
            base.ValidateCompatible(property, duplicateProperty, columnName, storeObject, logger);

            var propertyStrategy          = property.GetValueGenerationStrategy(storeObject);
            var duplicatePropertyStrategy = duplicateProperty.GetValueGenerationStrategy(storeObject);

            if (propertyStrategy != duplicatePropertyStrategy)
            {
                throw new InvalidOperationException(
                          SqlServerStrings.DuplicateColumnNameValueGenerationStrategyMismatch(
                              duplicateProperty.DeclaringEntityType.DisplayName(),
                              duplicateProperty.Name,
                              property.DeclaringEntityType.DisplayName(),
                              property.Name,
                              columnName,
                              storeObject.DisplayName()));
            }

            switch (propertyStrategy)
            {
            case SqlServerValueGenerationStrategy.IdentityColumn:
                var increment          = property.GetIdentityIncrement();
                var duplicateIncrement = duplicateProperty.GetIdentityIncrement();
                if (increment != duplicateIncrement)
                {
                    throw new InvalidOperationException(
                              SqlServerStrings.DuplicateColumnIdentityIncrementMismatch(
                                  duplicateProperty.DeclaringEntityType.DisplayName(),
                                  duplicateProperty.Name,
                                  property.DeclaringEntityType.DisplayName(),
                                  property.Name,
                                  columnName,
                                  storeObject.DisplayName()));
                }

                var seed          = property.GetIdentitySeed();
                var duplicateSeed = duplicateProperty.GetIdentitySeed();
                if (seed != duplicateSeed)
                {
                    throw new InvalidOperationException(
                              SqlServerStrings.DuplicateColumnIdentitySeedMismatch(
                                  duplicateProperty.DeclaringEntityType.DisplayName(),
                                  duplicateProperty.Name,
                                  property.DeclaringEntityType.DisplayName(),
                                  property.Name,
                                  columnName,
                                  storeObject.DisplayName()));
                }

                break;

            case SqlServerValueGenerationStrategy.SequenceHiLo:
                if (property.GetHiLoSequenceName() != duplicateProperty.GetHiLoSequenceName() ||
                    property.GetHiLoSequenceSchema() != duplicateProperty.GetHiLoSequenceSchema())
                {
                    throw new InvalidOperationException(
                              SqlServerStrings.DuplicateColumnSequenceMismatch(
                                  duplicateProperty.DeclaringEntityType.DisplayName(),
                                  duplicateProperty.Name,
                                  property.DeclaringEntityType.DisplayName(),
                                  property.Name,
                                  columnName,
                                  storeObject.DisplayName()));
                }

                break;
            }
        }
Example #12
0
        /// <inheritdoc />
        public virtual void ProcessModelFinalizing(
            IConventionModelBuilder modelBuilder,
            IConventionContext <IConventionModelBuilder> context)
        {
            foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
            {
                foreach (var property in entityType.GetDeclaredProperties())
                {
                    SqlServerValueGenerationStrategy?strategy = null;
                    var table = entityType.GetTableName();
                    if (table != null)
                    {
                        var storeObject = StoreObjectIdentifier.Table(table, entityType.GetSchema());
                        strategy = property.GetValueGenerationStrategy(storeObject, Dependencies.TypeMappingSource);
                        if (strategy == SqlServerValueGenerationStrategy.None &&
                            !IsStrategyNoneNeeded(property, storeObject))
                        {
                            strategy = null;
                        }
                    }
                    else
                    {
                        var view = entityType.GetViewName();
                        if (view != null)
                        {
                            var storeObject = StoreObjectIdentifier.View(view, entityType.GetViewSchema());
                            strategy = property.GetValueGenerationStrategy(storeObject, Dependencies.TypeMappingSource);
                            if (strategy == SqlServerValueGenerationStrategy.None &&
                                !IsStrategyNoneNeeded(property, storeObject))
                            {
                                strategy = null;
                            }
                        }
                    }

                    // Needed for the annotation to show up in the model snapshot
                    if (strategy != null)
                    {
                        property.Builder.HasValueGenerationStrategy(strategy);
                    }
                }
            }

            bool IsStrategyNoneNeeded(IReadOnlyProperty property, StoreObjectIdentifier storeObject)
            {
                if (property.ValueGenerated == ValueGenerated.OnAdd &&
                    !property.TryGetDefaultValue(storeObject, out _) &&
                    property.GetDefaultValueSql(storeObject) == null &&
                    property.GetComputedColumnSql(storeObject) == null &&
                    property.DeclaringEntityType.Model.GetValueGenerationStrategy() == SqlServerValueGenerationStrategy.IdentityColumn)
                {
                    var providerClrType = (property.GetValueConverter()
                                           ?? (property.FindRelationalTypeMapping(storeObject)
                                               ?? Dependencies.TypeMappingSource.FindMapping((IProperty)property))?.Converter)
                                          ?.ProviderClrType.UnwrapNullableType();

                    return(providerClrType != null &&
                           (providerClrType.IsInteger() || providerClrType == typeof(decimal)));
                }

                return(false);
            }
        }
 public SqlServerUpdateContextQuery(DbContext context, string schema, string table, List <IProperty> propertiesToUpdate, List <IProperty> propertiesForPivot, List <IProperty> propertiesToBulkLoad, IEntityType baseType, IDictionary <string, MemberInfo> propertiesGetter, StoreObjectIdentifier storeObject)
     : base(context, schema ?? "dbo", table, propertiesToUpdate, propertiesForPivot, propertiesToBulkLoad, baseType, propertiesGetter, storeObject)
 {
 }
        /// <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 IEnumerable <IAnnotation> For(IColumn column, bool designTime)
        {
            if (!designTime)
            {
                yield break;
            }

            var table            = StoreObjectIdentifier.Table(column.Table.Name, column.Table.Schema);
            var identityProperty = column.PropertyMappings.Where(
                m => m.TableMapping.IsSharedTablePrincipal && m.TableMapping.EntityType == m.Property.DeclaringEntityType)
                                   .Select(m => m.Property)
                                   .FirstOrDefault(
                p => p.GetValueGenerationStrategy(table)
                == SqlServerValueGenerationStrategy.IdentityColumn);

            if (identityProperty != null)
            {
                var seed      = identityProperty.GetIdentitySeed(table);
                var increment = identityProperty.GetIdentityIncrement(table);

                yield return(new Annotation(
                                 SqlServerAnnotationNames.Identity,
                                 string.Format(CultureInfo.InvariantCulture, "{0}, {1}", seed ?? 1, increment ?? 1)));
            }

            // Model validation ensures that these facets are the same on all mapped properties
            var property = column.PropertyMappings.First().Property;

            if (property.IsSparse() is bool isSparse)
            {
                yield return(new Annotation(SqlServerAnnotationNames.Sparse, isSparse));
            }

            var entityType = column.Table.EntityTypeMappings.First().EntityType;

            if (entityType.IsTemporal() && designTime)
            {
                var periodStartPropertyName = entityType.GetPeriodStartPropertyName();
                var periodEndPropertyName   = entityType.GetPeriodEndPropertyName();
                var storeObjectIdentifier   = StoreObjectIdentifier.Table(table.Name, table.Schema);

                // for the RevEng path, we avoid adding period properties to the entity
                // because we don't want code for them to be generated - they need to be in shadow state
                // so if we don't find property on the entity, we know it's this scenario
                // and in that case period column name is actually the same as the period property name annotation
                // since in RevEng scenario there can't be custom column mapping
                // see #26007
                var periodStartProperty   = entityType.FindProperty(periodStartPropertyName !);
                var periodStartColumnName = periodStartProperty != null
                    ? periodStartProperty.GetColumnName(storeObjectIdentifier)
                    : periodStartPropertyName;

                var periodEndProperty   = entityType.FindProperty(periodEndPropertyName !);
                var periodEndColumnName = periodEndProperty != null
                    ? periodEndProperty.GetColumnName(storeObjectIdentifier)
                    : periodEndPropertyName;

                if (column.Name == periodStartColumnName ||
                    column.Name == periodEndColumnName)
                {
                    yield return(new Annotation(SqlServerAnnotationNames.IsTemporal, true));

                    yield return(new Annotation(SqlServerAnnotationNames.TemporalPeriodStartColumnName, periodStartColumnName));

                    yield return(new Annotation(SqlServerAnnotationNames.TemporalPeriodEndColumnName, periodEndColumnName));
                }
            }
        }
 /// <summary>
 ///     <para>
 ///         Finds the first <see cref="IConventionForeignKey" /> that is mapped to the same constraint in a shared table-like object.
 ///     </para>
 ///     <para>
 ///         This method is typically used by database providers (and other extensions). It is generally
 ///         not used in application code.
 ///     </para>
 /// </summary>
 /// <param name="foreignKey"> The foreign key. </param>
 /// <param name="storeObject"> The identifier of the containing store object. </param>
 /// <returns> The foreign key if found, or <see langword="null" /> if none was found.</returns>
 public static IConventionForeignKey FindSharedObjectRootForeignKey(
     [NotNull] this IConventionForeignKey foreignKey, StoreObjectIdentifier storeObject)
 => (IConventionForeignKey)((IForeignKey)foreignKey).FindSharedObjectRootForeignKey(storeObject);
        /// <inheritdoc />
        public virtual void ProcessModelFinalizing(
            IConventionModelBuilder modelBuilder,
            IConventionContext <IConventionModelBuilder> context)
        {
            var tableToEntityTypes = new Dictionary <(string Name, string Schema), List <IConventionEntityType> >();

            foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
            {
                var tableName = entityType.GetTableName();
                if (tableName == null)
                {
                    continue;
                }

                var table = (tableName, entityType.GetSchema());
                if (!tableToEntityTypes.TryGetValue(table, out var mappedTypes))
                {
                    mappedTypes = new List <IConventionEntityType>();
                    tableToEntityTypes[table] = mappedTypes;
                }

                mappedTypes.Add(entityType);
            }

            foreach (var tableToEntityType in tableToEntityTypes)
            {
                var table       = tableToEntityType.Key;
                var mappedTypes = tableToEntityType.Value;

                var concurrencyColumns = GetConcurrencyTokensMap(StoreObjectIdentifier.Table(table.Name, table.Schema), mappedTypes);
                if (concurrencyColumns == null)
                {
                    continue;
                }

                foreach (var concurrencyColumn in concurrencyColumns)
                {
                    var concurrencyColumnName = concurrencyColumn.Key;
                    var propertiesMappedToConcurrencyColumn = concurrencyColumn.Value;

                    Dictionary <IConventionEntityType, IProperty> entityTypesMissingConcurrencyColumn = null;
                    foreach (var entityType in mappedTypes)
                    {
                        var foundMappedProperty = !IsConcurrencyTokenMissing(propertiesMappedToConcurrencyColumn, entityType, mappedTypes) ||
                                                  entityType.GetProperties()
                                                  .Any(p => p.GetColumnName(StoreObjectIdentifier.Table(table.Name, table.Schema)) == concurrencyColumnName);

                        if (!foundMappedProperty)
                        {
                            if (entityTypesMissingConcurrencyColumn == null)
                            {
                                entityTypesMissingConcurrencyColumn = new Dictionary <IConventionEntityType, IProperty>();
                            }
                            // store the entity type which is missing the
                            // concurrency token property, mapped to an example
                            // property which _is_ mapped to this concurrency token
                            // column and which will be used later as a template
                            entityTypesMissingConcurrencyColumn.Add(
                                entityType, propertiesMappedToConcurrencyColumn.First());
                        }
                    }

                    if (entityTypesMissingConcurrencyColumn == null)
                    {
                        continue;
                    }

                    RemoveDerivedEntityTypes(entityTypesMissingConcurrencyColumn);

                    foreach (var entityTypeToExampleProperty in entityTypesMissingConcurrencyColumn)
                    {
                        var exampleProperty = entityTypeToExampleProperty.Value;
                        entityTypeToExampleProperty.Key.Builder.CreateUniqueProperty(
                            exampleProperty.ClrType,
                            ConcurrencyPropertyPrefix + exampleProperty.Name,
                            !exampleProperty.IsNullable)
                        .HasColumnName(concurrencyColumnName)
                        .HasColumnType(exampleProperty.GetColumnType())
                        .IsConcurrencyToken(true)
                        .ValueGenerated(exampleProperty.ValueGenerated);
                    }
                }
            }
        }
 /// <summary>
 ///     Returns the foreign key constraint name.
 /// </summary>
 /// <param name="foreignKey"> The foreign key. </param>
 /// <returns> The foreign key constraint name. </returns>
 public static string GetConstraintName([NotNull] this IForeignKey foreignKey)
 => foreignKey.GetConstraintName(
     StoreObjectIdentifier.Table(foreignKey.DeclaringEntityType.GetTableName(), foreignKey.DeclaringEntityType.GetSchema()),
     StoreObjectIdentifier.Table(foreignKey.PrincipalEntityType.GetTableName(), foreignKey.PrincipalEntityType.GetSchema()));
        /// <summary>
        ///     <para>
        ///         Returns the <see cref="MySQLValueGenerationStrategy" /> to use for the property.
        ///     </para>
        ///     <para>
        ///         If no strategy is set for the property, then the strategy to use will be taken from the <see cref="IModel" />.
        ///     </para>
        /// </summary>
        /// <returns> The strategy, or <see cref="SqlServerValueGenerationStrategy.None" /> if none was set. </returns>
        public static MySQLValueGenerationStrategy?GetValueGenerationStrategy([NotNull] this IProperty property, StoreObjectIdentifier storeObject = default)
        {
            var annotation = property[MySQLAnnotationNames.ValueGenerationStrategy];

            if (annotation != null)
            {
                return((MySQLValueGenerationStrategy)annotation);
            }

            if (property.GetDefaultValue() != null ||
                property.GetDefaultValueSql() != null ||
                property.GetComputedColumnSql() != null)
            {
                return(null);
            }

            if (storeObject != default &&
                property.ValueGenerated == ValueGenerated.Never)
            {
                return(property.FindSharedStoreObjectRootProperty(storeObject)?.GetValueGenerationStrategy(storeObject));
            }

            if (IsCompatibleIdentityColumn(property) && property.ValueGenerated == ValueGenerated.OnAdd)
            {
                return(MySQLValueGenerationStrategy.IdentityColumn);
            }

            if (IsCompatibleComputedColumn(property) && property.ValueGenerated == ValueGenerated.OnAddOrUpdate)
            {
                return(MySQLValueGenerationStrategy.ComputedColumn);
            }

            return(null);
        }
Example #19
0
 /// <summary>
 ///     <para>
 ///         Finds the first <see cref="IConventionIndex" /> that is mapped to the same index in a shared table-like object.
 ///     </para>
 ///     <para>
 ///         This method is typically used by database providers (and other extensions). It is generally
 ///         not used in application code.
 ///     </para>
 /// </summary>
 /// <param name="index"> The index. </param>
 /// <param name="storeObject"> The identifier of the containing store object. </param>
 /// <returns> The index found, or <see langword="null" /> if none was found.</returns>
 public static IConventionIndex FindSharedObjectRootIndex(
     [NotNull] this IConventionIndex index, StoreObjectIdentifier storeObject)
 => (IConventionIndex)((IIndex)index).FindSharedObjectRootIndex(storeObject);
 /// <summary>
 ///     Returns the key constraint name for this key.
 /// </summary>
 /// <param name="key"> The key. </param>
 /// <returns> The key constraint name for this key. </returns>
 public static string GetName([NotNull] this IKey key)
 => key.GetName(StoreObjectIdentifier.Table(key.DeclaringEntityType.GetTableName(), key.DeclaringEntityType.GetSchema()));
Example #21
0
    private static void ValidateTemporalPeriodProperty(IEntityType temporalEntityType, bool periodStart)
    {
        var annotationPropertyName = periodStart
            ? temporalEntityType.GetPeriodStartPropertyName()
            : temporalEntityType.GetPeriodEndPropertyName();

        if (annotationPropertyName == null)
        {
            throw new InvalidOperationException(
                      SqlServerStrings.TemporalMustDefinePeriodProperties(
                          temporalEntityType.DisplayName()));
        }

        var periodProperty = temporalEntityType.FindProperty(annotationPropertyName);

        if (periodProperty == null)
        {
            throw new InvalidOperationException(
                      SqlServerStrings.TemporalExpectedPeriodPropertyNotFound(
                          temporalEntityType.DisplayName(), annotationPropertyName));
        }

        if (!periodProperty.IsShadowProperty() && !temporalEntityType.IsPropertyBag)
        {
            throw new InvalidOperationException(
                      SqlServerStrings.TemporalPeriodPropertyMustBeInShadowState(
                          temporalEntityType.DisplayName(), periodProperty.Name));
        }

        if (periodProperty.IsNullable ||
            periodProperty.ClrType != typeof(DateTime))
        {
            throw new InvalidOperationException(
                      SqlServerStrings.TemporalPeriodPropertyMustBeNonNullableDateTime(
                          temporalEntityType.DisplayName(), periodProperty.Name, nameof(DateTime)));
        }

        const string expectedPeriodColumnName = "datetime2";

        if (periodProperty.GetColumnType() != expectedPeriodColumnName)
        {
            throw new InvalidOperationException(
                      SqlServerStrings.TemporalPeriodPropertyMustBeMappedToDatetime2(
                          temporalEntityType.DisplayName(), periodProperty.Name, expectedPeriodColumnName));
        }

        if (periodProperty.TryGetDefaultValue(out var _))
        {
            throw new InvalidOperationException(
                      SqlServerStrings.TemporalPeriodPropertyCantHaveDefaultValue(
                          temporalEntityType.DisplayName(), periodProperty.Name));
        }

        if (temporalEntityType.GetTableName() is string tableName)
        {
            var storeObjectIdentifier = StoreObjectIdentifier.Table(tableName, temporalEntityType.GetSchema());
            var periodColumnName      = periodProperty.GetColumnName(storeObjectIdentifier);

            var propertiesMappedToPeriodColumn = temporalEntityType.GetProperties().Where(
                p => p.Name != periodProperty.Name && p.GetColumnName(storeObjectIdentifier) == periodColumnName).ToList();
            foreach (var propertyMappedToPeriodColumn in propertiesMappedToPeriodColumn)
            {
                if (propertyMappedToPeriodColumn.ValueGenerated != ValueGenerated.OnAddOrUpdate)
                {
                    throw new InvalidOperationException(
                              SqlServerStrings.TemporalPropertyMappedToPeriodColumnMustBeValueGeneratedOnAddOrUpdate(
                                  temporalEntityType.DisplayName(), propertyMappedToPeriodColumn.Name, nameof(ValueGenerated.OnAddOrUpdate)));
                }

                if (propertyMappedToPeriodColumn.TryGetDefaultValue(out var _))
                {
                    throw new InvalidOperationException(
                              SqlServerStrings.TemporalPropertyMappedToPeriodColumnCantHaveDefaultValue(
                                  temporalEntityType.DisplayName(), propertyMappedToPeriodColumn.Name));
                }
            }
        }

        // TODO: check that period property is excluded from query (once the annotation is added)
    }
 /// <summary>
 ///     <para>
 ///         Finds the first <see cref="IConventionKey" /> that is mapped to the same constraint in a shared table-like object.
 ///     </para>
 ///     <para>
 ///         This method is typically used by database providers (and other extensions). It is generally
 ///         not used in application code.
 ///     </para>
 /// </summary>
 /// <param name="key"> The key. </param>
 /// <param name="storeObject"> The identifier of the containing store object. </param>
 /// <returns> The key found, or <see langword="null" /> if none was found.</returns>
 public static IConventionKey FindSharedObjectRootKey(
     [NotNull] this IConventionKey key, StoreObjectIdentifier storeObject)
 => (IConventionKey)((IKey)key).FindSharedObjectRootKey(storeObject);
Example #23
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 virtual ResultSetMapping AppendBulkInsertOperation(
        StringBuilder commandStringBuilder,
        IReadOnlyList <IReadOnlyModificationCommand> modificationCommands,
        int commandPosition)
    {
        var table = StoreObjectIdentifier.Table(modificationCommands[0].TableName, modificationCommands[0].Schema);

        if (modificationCommands.Count == 1)
        {
            return(modificationCommands[0].ColumnModifications.All(
                       o =>
                       !o.IsKey ||
                       !o.IsRead ||
                       o.Property?.GetValueGenerationStrategy(table) == SqlServerValueGenerationStrategy.IdentityColumn)
                ? AppendInsertOperation(commandStringBuilder, modificationCommands[0], commandPosition)
                : AppendInsertOperationWithServerKeys(
                       commandStringBuilder,
                       modificationCommands[0],
                       modificationCommands[0].ColumnModifications.Where(o => o.IsKey).ToList(),
                       modificationCommands[0].ColumnModifications.Where(o => o.IsRead).ToList(),
                       commandPosition));
        }

        var readOperations  = modificationCommands[0].ColumnModifications.Where(o => o.IsRead).ToList();
        var writeOperations = modificationCommands[0].ColumnModifications.Where(o => o.IsWrite).ToList();
        var keyOperations   = modificationCommands[0].ColumnModifications.Where(o => o.IsKey).ToList();

        var defaultValuesOnly     = writeOperations.Count == 0;
        var nonIdentityOperations = modificationCommands[0].ColumnModifications
                                    .Where(o => o.Property?.GetValueGenerationStrategy(table) != SqlServerValueGenerationStrategy.IdentityColumn)
                                    .ToList();

        if (defaultValuesOnly)
        {
            if (nonIdentityOperations.Count == 0 ||
                readOperations.Count == 0)
            {
                foreach (var modification in modificationCommands)
                {
                    AppendInsertOperation(commandStringBuilder, modification, commandPosition);
                }

                return(readOperations.Count == 0
                    ? ResultSetMapping.NoResultSet
                    : ResultSetMapping.LastInResultSet);
            }

            if (nonIdentityOperations.Count > 1)
            {
                nonIdentityOperations.RemoveRange(1, nonIdentityOperations.Count - 1);
            }
        }

        if (readOperations.Count == 0)
        {
            return(AppendBulkInsertWithoutServerValues(commandStringBuilder, modificationCommands, writeOperations));
        }

        if (defaultValuesOnly)
        {
            return(AppendBulkInsertWithServerValuesOnly(
                       commandStringBuilder, modificationCommands, commandPosition, nonIdentityOperations, keyOperations, readOperations));
        }

        if (modificationCommands[0].Entries.SelectMany(e => e.EntityType.GetAllBaseTypesInclusive())
            .Any(e => e.IsMemoryOptimized()))
        {
            if (!nonIdentityOperations.Any(o => o.IsRead && o.IsKey))
            {
                foreach (var modification in modificationCommands)
                {
                    AppendInsertOperation(commandStringBuilder, modification, commandPosition++);
                }
            }
            else
            {
                foreach (var modification in modificationCommands)
                {
                    AppendInsertOperationWithServerKeys(
                        commandStringBuilder, modification, keyOperations, readOperations, commandPosition++);
                }
            }

            return(ResultSetMapping.LastInResultSet);
        }

        return(AppendBulkInsertWithServerValues(
                   commandStringBuilder, modificationCommands, commandPosition, writeOperations, keyOperations, readOperations));
    }
 /// <summary>
 ///     Returns the key constraint name for this key for a particular table.
 /// </summary>
 /// <param name="key"> The key. </param>
 /// <param name="storeObject"> The identifier of the containing store object. </param>
 /// <returns> The key constraint name for this key. </returns>
 public static string GetName([NotNull] this IKey key, StoreObjectIdentifier storeObject)
 => (string)key[RelationalAnnotationNames.Name]
 ?? key.GetDefaultName(storeObject);
Example #25
0
 public static string FormatColumns(
     [NotNull] this IEnumerable <IProperty> properties,
     StoreObjectIdentifier storeObject)
 => "{" + string.Join(", ", properties.Select(p => "'" + p.GetColumnName(storeObject) + "'")) + "}";
        /// <summary>
        ///     Returns the default key constraint name that would be used for this key for a particular table.
        /// </summary>
        /// <param name="key"> The key. </param>
        /// <param name="storeObject"> The identifier of the containing store object. </param>
        /// <returns> The default key constraint name that would be used for this key. </returns>
        public static string GetDefaultName([NotNull] this IKey key, StoreObjectIdentifier storeObject)
        {
            string name = null;

            if (key.IsPrimaryKey())
            {
                var rootKey = key;
                // Limit traversal to avoid getting stuck in a cycle (validation will throw for these later)
                // Using a hashset is detrimental to the perf when there are no cycles
                for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++)
                {
                    var linkingFk = rootKey.DeclaringEntityType.FindRowInternalForeignKeys(storeObject)
                                    .FirstOrDefault();
                    if (linkingFk == null)
                    {
                        break;
                    }

                    rootKey = linkingFk.PrincipalEntityType.FindPrimaryKey();
                }

                if (rootKey != null &&
                    rootKey != key)
                {
                    return(rootKey.GetName(storeObject));
                }

                name = "PK_" + storeObject.Name;
            }
            else
            {
                var propertyNames = key.Properties.Select(p => p.GetColumnName(storeObject)).ToList();
                var rootKey       = key;

                // Limit traversal to avoid getting stuck in a cycle (validation will throw for these later)
                // Using a hashset is detrimental to the perf when there are no cycles
                for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++)
                {
                    var linkedKey = rootKey.DeclaringEntityType
                                    .FindRowInternalForeignKeys(storeObject)
                                    .SelectMany(fk => fk.PrincipalEntityType.GetKeys())
                                    .FirstOrDefault(k => k.Properties.Select(p => p.GetColumnName(storeObject)).SequenceEqual(propertyNames));
                    if (linkedKey == null)
                    {
                        break;
                    }

                    rootKey = linkedKey;
                }

                if (rootKey != key)
                {
                    return(rootKey.GetName(storeObject));
                }

                name = new StringBuilder()
                       .Append("AK_")
                       .Append(storeObject.Name)
                       .Append("_")
                       .AppendJoin(key.Properties.Select(p => p.GetColumnName(storeObject)), "_")
                       .ToString();
            }

            return(Uniquifier.Truncate(name, key.DeclaringEntityType.Model.GetMaxIdentifierLength()));
        }
        private static bool?GetDefaultIsClustered([NotNull] IIndex index, StoreObjectIdentifier storeObject)
        {
            var sharedTableRootIndex = index.FindSharedObjectRootIndex(storeObject);

            return(sharedTableRootIndex?.IsClustered(storeObject));
        }
        /// <summary>
        ///     <para>
        ///         Finds the first <see cref="IForeignKey" /> that is mapped to the same constraint in a shared table-like object.
        ///     </para>
        ///     <para>
        ///         This method is typically used by database providers (and other extensions). It is generally
        ///         not used in application code.
        ///     </para>
        /// </summary>
        /// <param name="foreignKey"> The foreign key. </param>
        /// <param name="storeObject"> The identifier of the containing store object. </param>
        /// <returns> The foreign key if found, or <see langword="null" /> if none was found.</returns>
        public static IForeignKey FindSharedObjectRootForeignKey([NotNull] this IForeignKey foreignKey, StoreObjectIdentifier storeObject)
        {
            Check.NotNull(foreignKey, nameof(foreignKey));

            var foreignKeyName = foreignKey.GetConstraintName(storeObject,
                                                              StoreObjectIdentifier.Table(foreignKey.PrincipalEntityType.GetTableName(), foreignKey.PrincipalEntityType.GetSchema()));
            var rootForeignKey = foreignKey;

            // Limit traversal to avoid getting stuck in a cycle (validation will throw for these later)
            // Using a hashset is detrimental to the perf when there are no cycles
            for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++)
            {
                var linkedKey = rootForeignKey.DeclaringEntityType
                                .FindRowInternalForeignKeys(storeObject)
                                .SelectMany(fk => fk.PrincipalEntityType.GetForeignKeys())
                                .FirstOrDefault(k => k.GetConstraintName(storeObject,
                                                                         StoreObjectIdentifier.Table(k.PrincipalEntityType.GetTableName(), k.PrincipalEntityType.GetSchema()))
                                                == foreignKeyName);
                if (linkedKey == null)
                {
                    break;
                }

                rootForeignKey = linkedKey;
            }

            return(rootForeignKey == foreignKey ? null : rootForeignKey);
        }
Example #29
0
            public virtual void TPT_identifying_FK_are_created_only_on_declaring_type()
            {
                var modelBuilder = CreateModelBuilder();

                modelBuilder.Entity <BigMak>()
                .Ignore(b => b.Bun)
                .Ignore(b => b.Pickles);
                modelBuilder.Entity <Ingredient>(b =>
                {
                    b.ToTable("Ingredients");
                    b.Ignore(i => i.BigMak);
                });
                modelBuilder.Entity <Bun>(b =>
                {
                    b.ToTable("Buns");
                    b.HasOne(i => i.BigMak).WithOne().HasForeignKey <Bun>(i => i.Id);
                });
                modelBuilder.Entity <SesameBun>(b =>
                {
                    b.ToTable("SesameBuns");
                });

                var model = modelBuilder.FinalizeModel();

                var principalType = model.FindEntityType(typeof(BigMak));

                Assert.Empty(principalType.GetForeignKeys());
                Assert.Empty(principalType.GetIndexes());

                var ingredientType = model.FindEntityType(typeof(Ingredient));

                var bunType = model.FindEntityType(typeof(Bun));

                Assert.Empty(bunType.GetIndexes());
                var bunFk = bunType.GetDeclaredForeignKeys().Single(fk => !fk.IsBaseLinking());

                Assert.Equal("FK_Buns_BigMak_Id", bunFk.GetConstraintName());
                Assert.Equal("FK_Buns_BigMak_Id", bunFk.GetConstraintName(
                                 StoreObjectIdentifier.Create(bunType, StoreObjectType.Table).Value,
                                 StoreObjectIdentifier.Create(principalType, StoreObjectType.Table).Value));
                Assert.Single(bunFk.GetMappedConstraints());

                var bunLinkingFk = bunType.GetDeclaredForeignKeys().Single(fk => fk.IsBaseLinking());

                Assert.Equal("FK_Buns_Ingredients_Id", bunLinkingFk.GetConstraintName());
                Assert.Equal("FK_Buns_Ingredients_Id", bunLinkingFk.GetConstraintName(
                                 StoreObjectIdentifier.Create(bunType, StoreObjectType.Table).Value,
                                 StoreObjectIdentifier.Create(ingredientType, StoreObjectType.Table).Value));
                Assert.Single(bunLinkingFk.GetMappedConstraints());

                var sesameBunType = model.FindEntityType(typeof(SesameBun));

                Assert.Empty(sesameBunType.GetIndexes());
                var sesameBunFk = sesameBunType.GetDeclaredForeignKeys().Single();

                Assert.True(sesameBunFk.IsBaseLinking());
                Assert.Equal("FK_SesameBuns_Buns_Id", sesameBunFk.GetConstraintName());
                Assert.Equal("FK_SesameBuns_Buns_Id", sesameBunFk.GetConstraintName(
                                 StoreObjectIdentifier.Create(sesameBunType, StoreObjectType.Table).Value,
                                 StoreObjectIdentifier.Create(bunType, StoreObjectType.Table).Value));
                Assert.Single(sesameBunFk.GetMappedConstraints());
            }
        public static void PostgresModelCreating(this ModelBuilder builder)
        {
            var mapper = new NpgsqlSnakeCaseNameTranslator();

            foreach (var entity in builder.Model.GetEntityTypes())
            {
                // modify column names
                foreach (var property in entity.GetProperties())
                {
                    property.SetColumnName(mapper.TranslateMemberName(property.GetColumnName(StoreObjectIdentifier.Table(entity.GetTableName(), null))));
                }

                // modify table name
                entity.SetTableName(mapper.TranslateMemberName(entity.GetTableName()));

                // modify keys names
                foreach (var key in entity.GetKeys())
                {
                    key.SetName(mapper.TranslateMemberName(key.GetName()));
                }

                // modify foreign keys names
                foreach (var key in entity.GetForeignKeys())
                {
                    key.SetConstraintName(mapper.TranslateMemberName(key.GetConstraintName()));
                }

                // modify indexes names
                foreach (var index in entity.GetIndexes())
                {
                    index.SetDatabaseName(mapper.TranslateMemberName(index.GetDatabaseName()));
                }

                // move asp_net tables into schema 'identity'
                if (entity.GetTableName().StartsWith("asp_net_"))
                {
                    entity.SetTableName(entity.GetTableName().Replace("asp_net_", string.Empty));
                    entity.SetSchema("identity");
                }
            }
        }