diff --git a/Orm/Xtensive.Orm.Tests/Issues/IssueJira0796_IgnoreHintPathGetsInvalid.cs b/Orm/Xtensive.Orm.Tests/Issues/IssueJira0796_IgnoreHintPathGetsInvalid.cs new file mode 100644 index 0000000000..7397ea19cc --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Issues/IssueJira0796_IgnoreHintPathGetsInvalid.cs @@ -0,0 +1,123 @@ +// Copyright (C) 2020 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kulakov +// Created: 2020.03.04 + +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Xtensive.Orm.Configuration; +using Xtensive.Orm.Tests.Issues.IssueJira0796_IgnoreHintPathGetsInvalidModel; +using Xtensive.Orm.Validation; +using Xtensive.Sql; +using Xtensive.Sql.Dml; +using Xtensive.Caching; +using Xtensive.Orm.Upgrade; +using Xtensive.Modelling.Comparison.Hints; +using Xtensive.Orm.Upgrade.Model; +using Xtensive.Orm.Services; + +namespace Xtensive.Orm.Tests.Issues +{ + [TestFixture] + public class IssueJira0796_IgnoreHintPathGetsInvalid + { + private const string CreateIndexQuery = @" + CREATE NONCLUSTERED INDEX [custom_NonClusteredIndex-20200304-164347] ON [dbo].[SomeEntity2] ( + [FirstName] ASC, + [LastName] ASC) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, + DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)"; + + [Test] + public void MainTest() + { + Require.ProviderIs(StorageProvider.SqlServer); + + var configuration = DomainConfigurationFactory.Create(); + configuration.UpgradeMode = DomainUpgradeMode.Recreate; + configuration.Types.Register(typeof (SomeEntity1)); + configuration.Types.Register(typeof (SomeEntity2)); + + using (var domain = Domain.Build(configuration)) + using (var session = domain.OpenSession()) + using (var transaction = session.OpenTransaction()) { + var accessor = session.Services.Get(); + using (var command = accessor.CreateCommand()) { + command.CommandText = CreateIndexQuery; + _ = command.ExecuteNonQuery(); + } + transaction.Complete(); + } + + configuration = DomainConfigurationFactory.Create(); + configuration.UpgradeMode = DomainUpgradeMode.Perform; + configuration.Types.Register(typeof(SomeEntity1)); + configuration.Types.Register(typeof(CustomUpgradeHandler)); + + Assert.DoesNotThrow(() => Domain.Build(configuration).Dispose()); + } + } +} + +namespace Xtensive.Orm.Tests.Issues.IssueJira0796_IgnoreHintPathGetsInvalidModel +{ + [HierarchyRoot] + public class SomeEntity1 : Entity + { + [Field, Key] + public int Id { get; set; } + + [Field] + public string FirstName { get; set; } + + [Field] + public string LastName { get; set; } + } + + [HierarchyRoot] + public class SomeEntity2 : Entity + { + [Field, Key] + public int Id { get; set; } + + [Field] + public string FirstName { get; set; } + + [Field] + public string LastName { get; set; } + } + + public class CustomUpgradeHandler : UpgradeHandler + { + private class KeepCustomIndicesMarkerHint : Hint + { + public override IEnumerable GetTargets() => Enumerable.Empty(); + } + + private const string CustomIndexPrefix = "custom_"; + + public override void OnSchemaReady() + { + if (UpgradeContext.Stage == UpgradeStage.Upgrading) { + var schemaHints = UpgradeContext.SchemaHints; + var storageModel = (StorageModel) schemaHints.SourceModel; + + foreach (var table in storageModel.Tables) { + foreach (var index in table.SecondaryIndexes) { + var name = index.Name; + if (!name.StartsWith(CustomIndexPrefix)) { + continue; + } + var ignoreHint = new IgnoreHint(index.Path); + schemaHints.Add(ignoreHint); + } + } + } + } + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Modelling/Comparison/Upgrader.cs b/Orm/Xtensive.Orm/Modelling/Comparison/Upgrader.cs index d0b6336c34..2ee5dbe25d 100644 --- a/Orm/Xtensive.Orm/Modelling/Comparison/Upgrader.cs +++ b/Orm/Xtensive.Orm/Modelling/Comparison/Upgrader.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2009-2020 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alex Yakunin // Created: 2009.04.07 @@ -122,20 +122,23 @@ public ReadOnlyList GetUpgradeSequence(Difference difference, HintSe { ArgumentValidator.EnsureArgumentNotNull(hints, "hints"); ArgumentValidator.EnsureArgumentNotNull(comparer, "comparer"); - if (difference == null) + if (difference == null) { return new ReadOnlyList(Enumerable.Empty().ToList()); + } TemporaryRenames = new Dictionary(StringComparer.OrdinalIgnoreCase); SourceModel = (IModel) difference.Source; TargetModel = (IModel) difference.Target; Hints = hints ?? new HintSet(SourceModel, TargetModel); Comparer = comparer; - if (Hints.SourceModel!=SourceModel) + if (Hints.SourceModel != SourceModel) { throw new ArgumentOutOfRangeException("hints.SourceModel"); - if (Hints.TargetModel!=TargetModel) + } + if (Hints.TargetModel != TargetModel) { throw new ArgumentOutOfRangeException("hints.TargetModel"); + } - CurrentModel = (IModel)SourceModel.Clone(null, SourceModel.Name); + CurrentModel = (IModel) SourceModel.Clone(null, SourceModel.Name); Difference = difference; var previous = currentAsync.Value; currentAsync.Value = this; @@ -152,15 +155,17 @@ public ReadOnlyList GetUpgradeSequence(Difference difference, HintSe ProcessStage(UpgradeStage.Cleanup, actions); var validationHints = new HintSet(CurrentModel, TargetModel); - Hints.OfType().ForEach(validationHints.Add); + Hints.OfType() + .Where(h => CurrentModel.Resolve(h.Path, false) != null && SourceModel.Resolve(h.Path, false) != null) + .ForEach(validationHints.Add); var diff = comparer.Compare(CurrentModel, TargetModel, validationHints); - if (diff!=null) { + if (diff != null) { CoreLog.InfoRegion(Strings.LogAutomaticUpgradeSequenceValidation); CoreLog.Info(Strings.LogValidationFailed); CoreLog.Info(Strings.LogItemFormat, Strings.Difference); CoreLog.Info("{0}", diff); - CoreLog.Info(Strings.LogItemFormat+"\r\n{1}", Strings.UpgradeSequence, - new ActionSequence() {actions}); + CoreLog.Info(Strings.LogItemFormat + "\r\n{1}", Strings.UpgradeSequence, + new ActionSequence() { actions }); CoreLog.Info(Strings.LogItemFormat, Strings.ExpectedTargetModel); TargetModel.Dump(); CoreLog.Info(Strings.LogItemFormat, Strings.ActualTargetModel);