private IReadOnlyList <ColumnModification> GenerateColumnModifications()
        {
            var adding = EntityState == EntityState.Added;
            var columnModifications = new List <ColumnModification>();

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

            var columnMap = _entries.Count == 1
                ? null
                : new Dictionary <string, ColumnModification>();

            Dictionary <IUpdateEntry, List <ColumnModification> > conflictingColumnValues         = null;
            Dictionary <IUpdateEntry, List <ColumnModification> > conflictingOriginalColumnValues = null;

            foreach (var entry in _entries)
            {
                var entityType = entry.EntityType;

                foreach (var property in entityType.GetProperties())
                {
                    var isKey = property.IsPrimaryKey();
                    var isConcurrencyToken = property.IsConcurrencyToken;
                    var isCondition        = !adding && (isKey || isConcurrencyToken);
                    var readValue          = entry.IsStoreGenerated(property);

                    var writeValue = !readValue &&
                                     (adding &&
                                      property.BeforeSaveBehavior == PropertySaveBehavior.Save ||
                                      !adding &&
                                      property.AfterSaveBehavior == PropertySaveBehavior.Save &&
                                      entry.IsModified(property));

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

                        var columnModification = new ColumnModification(
                            entry,
                            property,
                            property.Relational(),
                            _generateParameterName,
                            readValue,
                            writeValue,
                            isKey,
                            isCondition,
                            isConcurrencyToken);

                        if (columnMap != null)
                        {
                            if (columnMap.TryGetValue(columnModification.ColumnName, out var existingColumn))
                            {
                                if (columnModification.UseCurrentValueParameter &&
                                    !Equals(columnModification.Value, existingColumn.Value))
                                {
                                    conflictingColumnValues = AddConflictingColumnValues(
                                        conflictingColumnValues, columnModification, existingColumn);
                                }
                                else if (columnModification.UseOriginalValueParameter &&
                                         !Equals(columnModification.OriginalValue, existingColumn.OriginalValue))
                                {
                                    conflictingOriginalColumnValues = AddConflictingColumnValues(
                                        conflictingOriginalColumnValues, columnModification, existingColumn);
                                }

                                continue;
                            }

                            columnMap.Add(columnModification.ColumnName, columnModification);
                        }

                        columnModifications.Add(columnModification);
                    }
                }
            }

            if (conflictingColumnValues != null)
            {
                var firstPair       = conflictingColumnValues.First();
                var firstEntry      = firstPair.Key;
                var firstProperties = firstPair.Value.Select(c => c.Property).ToList();
                var lastPair        = conflictingColumnValues.Last();
                var lastEntry       = lastPair.Key;
                var lastProperties  = lastPair.Value.Select(c => c.Property);

                if (_sensitiveLoggingEnabled)
                {
                    throw new InvalidOperationException(
                              RelationalStrings.ConflictingRowValuesSensitive(
                                  firstEntry.EntityType.DisplayName(),
                                  lastEntry.EntityType.DisplayName(),
                                  firstEntry.BuildCurrentValuesString(firstEntry.EntityType.FindPrimaryKey().Properties),
                                  firstEntry.BuildCurrentValuesString(firstProperties),
                                  lastEntry.BuildCurrentValuesString(lastProperties),
                                  firstProperties.FormatColumns()));
                }

                throw new InvalidOperationException(
                          RelationalStrings.ConflictingRowValues(
                              firstEntry.EntityType.DisplayName(),
                              lastEntry.EntityType.DisplayName(),
                              Property.Format(firstProperties),
                              Property.Format(lastProperties),
                              firstProperties.FormatColumns()));
            }

            if (conflictingOriginalColumnValues != null)
            {
                var firstPair       = conflictingOriginalColumnValues.First();
                var firstEntry      = firstPair.Key;
                var firstProperties = firstPair.Value.Select(c => c.Property).ToList();
                var lastPair        = conflictingOriginalColumnValues.Last();
                var lastEntry       = lastPair.Key;
                var lastProperties  = lastPair.Value.Select(c => c.Property);

                if (_sensitiveLoggingEnabled)
                {
                    throw new InvalidOperationException(
                              RelationalStrings.ConflictingOriginalRowValuesSensitive(
                                  firstEntry.EntityType.DisplayName(),
                                  lastEntry.EntityType.DisplayName(),
                                  firstEntry.BuildCurrentValuesString(firstEntry.EntityType.FindPrimaryKey().Properties),
                                  firstEntry.BuildOriginalValuesString(firstProperties),
                                  lastEntry.BuildOriginalValuesString(lastProperties),
                                  firstProperties.FormatColumns()));
                }

                throw new InvalidOperationException(
                          RelationalStrings.ConflictingOriginalRowValues(
                              firstEntry.EntityType.DisplayName(),
                              lastEntry.EntityType.DisplayName(),
                              Property.Format(firstProperties),
                              Property.Format(lastProperties),
                              firstProperties.FormatColumns()));
            }

            return(columnModifications);
        }
Exemplo n.º 2
0
        public void BatchCommands_throws_on_conflicting_values_for_shared_table_added_entities(bool useCurrentValues, bool sensitiveLogging)
        {
            var currentDbContext = CreateContextServices(CreateSharedTableModel()).GetRequiredService <ICurrentDbContext>();
            var stateManager     = currentDbContext.GetDependencies().StateManager;

            var first = new FakeEntity {
                Id = 42, Value = "Test"
            };
            var firstEntry = stateManager.GetOrCreateEntry(first);

            firstEntry.SetEntityState(EntityState.Modified);
            firstEntry.SetPropertyModified(firstEntry.EntityType.FindPrimaryKey().Properties.Single(), isModified: false);
            var second = new RelatedFakeEntity {
                Id = 42
            };
            var secondEntry = stateManager.GetOrCreateEntry(second);

            secondEntry.SetEntityState(EntityState.Modified);
            secondEntry.SetPropertyModified(secondEntry.EntityType.FindPrimaryKey().Properties.Single(), isModified: false);

            if (useCurrentValues)
            {
                first.RelatedId  = 1;
                second.RelatedId = 2;
            }
            else
            {
                new EntityEntry <FakeEntity>(firstEntry).Property(e => e.RelatedId).OriginalValue         = 1;
                new EntityEntry <RelatedFakeEntity>(secondEntry).Property(e => e.RelatedId).OriginalValue = 2;
            }

            if (useCurrentValues)
            {
                if (sensitiveLogging)
                {
                    Assert.Equal(
                        RelationalStrings.ConflictingRowValuesSensitive(
                            nameof(RelatedFakeEntity), nameof(FakeEntity), "{Id: 42}",
                            "{RelatedId: 2}", "{RelatedId: 1}", "{'RelatedId'}"),
                        Assert.Throws <InvalidOperationException>(
                            () => CreateCommandBatchPreparer(stateManager: stateManager, sensitiveLogging: true)
                            .BatchCommands(new[] { firstEntry, secondEntry }).ToArray()).Message);
                }
                else
                {
                    Assert.Equal(
                        RelationalStrings.ConflictingRowValues(
                            nameof(RelatedFakeEntity), nameof(FakeEntity),
                            "{'RelatedId'}", "{'RelatedId'}", "{'RelatedId'}"),
                        Assert.Throws <InvalidOperationException>(
                            () => CreateCommandBatchPreparer(stateManager: stateManager, sensitiveLogging: false)
                            .BatchCommands(new[] { firstEntry, secondEntry }).ToArray()).Message);
                }
            }
            else
            {
                if (sensitiveLogging)
                {
                    Assert.Equal(
                        RelationalStrings.ConflictingOriginalRowValuesSensitive(
                            nameof(RelatedFakeEntity), nameof(FakeEntity), "{Id: 42}",
                            "{RelatedId: 2}", "{RelatedId: 1}", "{'RelatedId'}"),
                        Assert.Throws <InvalidOperationException>(
                            () => CreateCommandBatchPreparer(stateManager: stateManager, sensitiveLogging: true)
                            .BatchCommands(new[] { firstEntry, secondEntry }).ToArray()).Message);
                }
                else
                {
                    Assert.Equal(
                        RelationalStrings.ConflictingOriginalRowValues(
                            nameof(RelatedFakeEntity), nameof(FakeEntity),
                            "{'RelatedId'}", "{'RelatedId'}", "{'RelatedId'}"),
                        Assert.Throws <InvalidOperationException>(
                            () => CreateCommandBatchPreparer(stateManager: stateManager, sensitiveLogging: false)
                            .BatchCommands(new[] { firstEntry, secondEntry }).ToArray()).Message);
                }
            }
        }