// Copyright (C) 2014-2021 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:    2014.07.18


using System;
using System.Collections.Generic;
using NUnit.Framework;
using System.Linq;
using Xtensive.Orm.Configuration;
using Xtensive.Orm.Providers;
using Xtensive.Orm.Upgrade;
using initialModel = Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1;
using initialModelUpgrader = Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel2;
using onlyCustomsTypeIdsModel = Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel3;
using partialSequencialTypeIdModel = Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel4;
using partialRandomTypeIdModel = Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel5;
using typeNotExistsModel = Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel6;
using typeIdBeyongTheLimitsModel = Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel7;
using conflictModel = Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel8;
using performWithNewType = Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel9;
using performWithNewTypes = Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel10;
using multyDatabaseModel = Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel11;

namespace Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1
{
  [HierarchyRoot]
  public class Author : Entity
  {
    [Field, Key]
    public int Id { get; private set; }

    [Field]
    public string Name { get; set; }

    [Field]
    public string Surname { get; set; }

    [Field]
    public string Somestring { get; set; }

    [Field]
    public EntitySet<Book> Books { get; set; }
  }

  [HierarchyRoot]
  public class Book : Entity
  {
    [Field, Key]
    public int Id { get; set; }

    [Field]
    public string Title { get; set; }

    [Field]
    [Association(PairTo = "Books")]
    public EntitySet<Author> Authors { get; set; }

    [Field]
    [Association(PairTo = "Book")]
    public EntitySet<BookInStore> Stores { get; set; }
  }

  [HierarchyRoot]
  public class Store : Entity
  {
    [Field, Key]
    public int Id { get; set; }

    [Field]
    public string Name { get; set; }

    [Field]
    [Association(PairTo = "Store")]
    public EntitySet<BookInStore> BooksInStore { get; set; }
  }

  [HierarchyRoot]
  public class BookInStore : Entity
  {
    [Field, Key]
    public long Id { get; set; }

    [Field]
    public Book Book { get; set; }

    [Field]
    public Store Store { get; set; }

    [Field]
    public int Count { get; set; }
  }
}

namespace Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel2
{
  public class Upgrader : UpgradeHandler
  {
    public override bool CanUpgradeFrom(string oldVersion) => true;

    public override void OnPrepare()
    {
      base.OnPrepare();
      if (UpgradeContext.Configuration.UpgradeMode != DomainUpgradeMode.Recreate) {
        UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Author).FullName, 200);
        UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Book).FullName, 201);
        UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.BookInStore).FullName, 202);
        UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Store).FullName, 203);
        UpgradeContext.UserDefinedTypeMap.Add("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book", 204);
      }
    }
  }
}

namespace Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel3
{
  public class OnlyCustomIdsUpgrader : UpgradeHandler
  {
    public override bool CanUpgradeFrom(string oldVersion) => true;

    public override void OnPrepare()
    {
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Author).FullName, 200);
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Book).FullName, 201);
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.BookInStore).FullName, 202);
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Store).FullName, 203);
      UpgradeContext.UserDefinedTypeMap.Add("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book", 204);
    }
  }
}

namespace Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel4
{
  public class PartiallySequentialTypeIdsUpgrader : UpgradeHandler
  {
    public override bool CanUpgradeFrom(string oldVersion) => true;

    public override void OnPrepare()
    {
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Author).FullName, 200);
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Book).FullName, 201);
    }
  }
}

namespace Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel5
{
  public class PartiallyRandomTypeIdsUpgrader : UpgradeHandler
  {
    public override bool CanUpgradeFrom(string oldVersion) => true;

    public override void OnPrepare()
    {
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Author).FullName, 150);
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Book).FullName, 149);
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Store).FullName, 250);
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.BookInStore).FullName, 110);
    }
  }
}

namespace Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel6
{
  public class TypeIdForNotExistingTypeUpgrader : UpgradeHandler
  {
    public override bool CanUpgradeFrom(string oldVersion) => true;

    public override void OnPrepare() =>
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Author).FullName + "1", 100);
  }
}

namespace Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel7
{
  public class TypeIdOutOfRangeUpgrader : UpgradeHandler
  {
    public override bool CanUpgradeFrom(string oldVersion) => true;

    public override void OnPrepare()
    {
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.Author).FullName, 99);
      UpgradeContext.UserDefinedTypeMap.Add(typeof(initialModel.BookInStore).FullName, 100);
    }
  }
}

namespace Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel8
{
  [HierarchyRoot]
  public class CommentsOfBook : Entity
  {
    [Field, Key]
    public int Id { get; set; }

    [Field]
    public initialModel.Book Book { get; set; }

    [Field]
    public EntitySet<Comment> Comments { get; set; }
  }

  [HierarchyRoot]
  public class Comment : Entity
  {
    [Field, Key]
    public int Id { get; set; }

    [Field]
    public string Title { get; set; }

    [Field]
    public string Text { get; set; }

    [Field]
    public CommentsOfBook Book { get; set; }
  }

  public class ConflictOfTypeIdUpgrader : UpgradeHandler
  {
    public override bool CanUpgradeFrom(string oldVersion) => true;

    public override void OnPrepare() =>
      UpgradeContext.UserDefinedTypeMap.Add(typeof(Comment).FullName, 100);
  }
}

namespace Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel9
{
  [HierarchyRoot]
  public class CommentsOfBook : Entity
  {
    [Field, Key]
    public int Id { get; set; }

    [Field]
    public initialModel.Book Book { get; set; }

    [Field]
    public EntitySet<Comment> Comments { get; set; }
  }

  [HierarchyRoot]
  public class Comment : Entity
  {
    [Field, Key]
    public int Id { get; private set; }

    [Field]
    public string Title { get; set; }

    [Field]
    public string Text { get; set; }

    [Field]
    public User User { get; set; }
  }

  [HierarchyRoot]
  public class User : Entity
  {
    [Field, Key]
    public int Id { get; private set; }

    [Field]
    public string Login { get; set; }

    [Field]
    public string Password { get; set; }

    [Field]
    [Association(PairTo = "User")]
    public EntitySet<Comment> Comments { get; set; }
  }

  public class UpgradeWithNewTypeUpgrader : UpgradeHandler
  {
    public override bool CanUpgradeFrom(string oldVersion) => true;

    public override void OnPrepare() =>
      UpgradeContext.UserDefinedTypeMap.Add(typeof(Comment).FullName, 110);
  }
}

namespace Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel10
{
  [HierarchyRoot]
  public class CommentsOfBook : Entity
  {
    [Field, Key]
    public int Id { get; set; }

    [Field]
    public initialModel.Book Book { get; set; }

    [Field]
    public EntitySet<Comment> Comments { get; set; }
  }

  [HierarchyRoot]
  public class Comment : Entity
  {
    [Field, Key]
    public int Id { get; private set; }

    [Field]
    public string Title { get; set; }

    [Field]
    public string Text { get; set; }

    [Field]
    public User User { get; set; }
  }

  [HierarchyRoot]
  public class User : Entity
  {
    [Field, Key]
    public int Id { get; private set; }

    [Field]
    public string Login { get; set; }

    [Field]
    public string Password { get; set; }

    [Field]
    [Association(PairTo = "User")]
    public EntitySet<Comment> Comments { get; set; }
  }

  public class UpgradeWithNewTypesUpgrader : UpgradeHandler
  {
    public override bool CanUpgradeFrom(string oldVersion) => true;

    public override void OnPrepare()
    {
      UpgradeContext.UserDefinedTypeMap.Add(typeof(Comment).FullName, 110);
      UpgradeContext.UserDefinedTypeMap.Add(typeof(User).FullName, 112);
    }
  }
}

namespace Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel11
{
  namespace Database1
  {
    [HierarchyRoot]
    public class Book : Entity
    {
      [Field, Key]
      public int Id { get; private set; }
    }

    [HierarchyRoot]
    public class Author : Entity
    {
      [Field, Key]
      public int Id { get; private set; }
    }

    [HierarchyRoot]
    public class Magazine : Entity
    {
      [Field, Key]
      public int Id { get; private set; }
    }

    public class Upgrader : UpgradeHandler
    {
      public override bool CanUpgradeFrom(string oldVersion) => true;

      public override void OnPrepare()
      {
        UpgradeContext.UserDefinedTypeMap.Add(typeof(Book).FullName, 100);
        UpgradeContext.UserDefinedTypeMap.Add(typeof(Author).FullName, 101);
        UpgradeContext.UserDefinedTypeMap.Add(typeof(Magazine).FullName, 102);
        if (UpgradeContext.Configuration.UpgradeMode.In(DomainUpgradeMode.LegacyValidate, DomainUpgradeMode.LegacySkip)) {
          UpgradeContext.UserDefinedTypeMap.Add("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel11.Database2.Parent", 200);
          UpgradeContext.UserDefinedTypeMap.Add("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel11.Database2.Child", 201);

          UpgradeContext.UserDefinedTypeMap.Add("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel11.Database3.Car", 300);
          UpgradeContext.UserDefinedTypeMap.Add("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel11.Database3.Engine", 301);
        }
      }
    }
  }

  namespace Database2
  {
    [HierarchyRoot]
    public class Parent : Entity
    {
      [Field, Key]
      public int Id { get; private set; }
    }

    [HierarchyRoot]
    public class Child : Entity
    {
      [Field, Key]
      public int Id { get; private set; }
    }

    public class Upgrader : UpgradeHandler
    {
      public override bool IsEnabled => UpgradeContext.Configuration.UpgradeMode == DomainUpgradeMode.Recreate;

      public override bool CanUpgradeFrom(string oldVersion) => true;

      public override void OnPrepare()
      {
        UpgradeContext.UserDefinedTypeMap.Add("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel11.Database2.Parent", 200);
        UpgradeContext.UserDefinedTypeMap.Add("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel11.Database2.Child", 201);
      }
    }
  }

  namespace Database3
  {
    [HierarchyRoot]
    public class Car : Entity
    {
      [Field, Key]
      public int Id { get; private set; }
    }

    [HierarchyRoot]
    public class Engine : Entity
    {
      [Field, Key]
      public int Id { get; private set; }
    }

    public class Upgrader : UpgradeHandler
    {
      public override bool IsEnabled => UpgradeContext.Configuration.UpgradeMode == DomainUpgradeMode.Recreate;

      public override bool CanUpgradeFrom(string oldVersion) => true;

      public override void OnPrepare()
      {
        UpgradeContext.UserDefinedTypeMap.Add("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel11.Database3.Car", 300);
        UpgradeContext.UserDefinedTypeMap.Add("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel11.Database3.Engine", 301);
      }
    }
  }
}


namespace Xtensive.Orm.Tests.Upgrade
{
  [TestFixture]
  public class CustomTypeIdMap : AutoBuildTest
  {
    private const string DOTests1Db = WellKnownDatabases.MultiDatabase.AdditionalDb1;
    private const string DOTests2Db = WellKnownDatabases.MultiDatabase.AdditionalDb2;
    private const string DOTests3Db = WellKnownDatabases.MultiDatabase.AdditionalDb3;

    private const string dbo = WellKnownSchemas.SqlServerDefaultSchema;

    [Test]
    public void RecreateTest()
    {
      var configuration = BuildConfiguration(typeof(initialModel.Author), typeof(onlyCustomsTypeIdsModel.OnlyCustomIdsUpgrader), DomainUpgradeMode.Recreate);
      using (var domain = BuildDomain(configuration)) {
        foreach (var type in domain.Model.Types.Where(type => type.IsEntity && !type.IsSystem)) {
          Assert.GreaterOrEqual(type.TypeId, 200);
        }

        Assert.AreEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
      }

      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(partialSequencialTypeIdModel.PartiallySequentialTypeIdsUpgrader), DomainUpgradeMode.Recreate);
      using (var domain = BuildDomain(configuration)) {
        foreach (var type in domain.Model.Types.Where(type => type.IsEntity && !type.IsSystem)) {
          Assert.GreaterOrEqual(type.TypeId, 200);
        }

        Assert.AreEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
      }

      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(partialRandomTypeIdModel.PartiallyRandomTypeIdsUpgrader), DomainUpgradeMode.Recreate);
      using (var domain = BuildDomain(configuration)) {
        foreach (var type in domain.Model.Types.Where(type => type.IsEntity && !type.IsSystem)) {
          Assert.GreaterOrEqual(type.TypeId, 110);
        }

        Assert.AreEqual(150, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreEqual(149, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreEqual(250, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreEqual(110, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreEqual(251, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
      }

      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(typeNotExistsModel.TypeIdForNotExistingTypeUpgrader), DomainUpgradeMode.Recreate);
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(configuration));

      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(typeIdBeyongTheLimitsModel.TypeIdOutOfRangeUpgrader), DomainUpgradeMode.Recreate);
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(configuration));
    }

    [Test]
    public void SkipTest()
    {
      BuildInitialDomain();
      var configuration = BuildConfiguration(typeof(initialModel.Author), DomainUpgradeMode.Skip);
      using (var domain = BuildDomain(configuration)) {
        Assert.AreNotEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreNotEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreNotEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreNotEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreNotEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(typeNotExistsModel.TypeIdForNotExistingTypeUpgrader), DomainUpgradeMode.Skip);
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(configuration));

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(typeIdBeyongTheLimitsModel.TypeIdOutOfRangeUpgrader), DomainUpgradeMode.Skip);
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(configuration));
    }

    [Test]
    public void ValidateTest()
    {
      BuildInitialDomain();
      var configuration = BuildConfiguration(typeof(initialModel.Author), DomainUpgradeMode.Validate);
      using (var domain = BuildDomain(configuration)) {
        Assert.AreNotEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreNotEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreNotEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreNotEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreNotEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(typeNotExistsModel.TypeIdForNotExistingTypeUpgrader), DomainUpgradeMode.Validate);
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(configuration));

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(typeIdBeyongTheLimitsModel.TypeIdOutOfRangeUpgrader), DomainUpgradeMode.Validate);
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(configuration));
    }

    [Test]
    public void PerformAndPerformSafelyTest()
    {
      BuildInitialDomain();
      var configuration = BuildConfiguration(typeof(initialModel.Author), DomainUpgradeMode.Perform);
      using (var domain = BuildDomain(configuration)) {
        Assert.AreNotEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreNotEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreNotEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreNotEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreNotEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), DomainUpgradeMode.PerformSafely);
      using (var domain = BuildDomain(configuration)) {
        Assert.AreNotEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreNotEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreNotEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreNotEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreNotEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(onlyCustomsTypeIdsModel.OnlyCustomIdsUpgrader), DomainUpgradeMode.Perform);
      using (var domain = BuildDomain(configuration)) {
        Assert.AreNotEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreNotEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreNotEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreNotEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreNotEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(onlyCustomsTypeIdsModel.OnlyCustomIdsUpgrader), DomainUpgradeMode.PerformSafely);
      using (var domain = BuildDomain(configuration)) {
        Assert.AreNotEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreNotEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreNotEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreNotEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreNotEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(conflictModel.ConflictOfTypeIdUpgrader), DomainUpgradeMode.Perform);
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(configuration));

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(conflictModel.ConflictOfTypeIdUpgrader), DomainUpgradeMode.PerformSafely);
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(configuration));

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(performWithNewType.UpgradeWithNewTypeUpgrader), DomainUpgradeMode.Perform);
      using (var domain = BuildDomain(configuration)) {
        Assert.AreNotEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreNotEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreNotEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreNotEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreNotEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
        Assert.AreEqual(110, domain.Model.Types[typeof(performWithNewType.Comment)].TypeId);
        Assert.AreEqual(113, domain.Model.Types[typeof(performWithNewType.User)].TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(performWithNewType.UpgradeWithNewTypeUpgrader), DomainUpgradeMode.PerformSafely);
      using (var domain = BuildDomain(configuration)) {
        Assert.AreNotEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreNotEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreNotEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreNotEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreNotEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
        Assert.AreEqual(110, domain.Model.Types[typeof(performWithNewType.Comment)].TypeId);
        Assert.AreEqual(113, domain.Model.Types[typeof(performWithNewType.User)].TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(performWithNewTypes.UpgradeWithNewTypesUpgrader), DomainUpgradeMode.Perform);
      using (var domain = BuildDomain(configuration)) {
        Assert.AreNotEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreNotEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreNotEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreNotEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreNotEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
        Assert.AreEqual(110, domain.Model.Types[typeof(performWithNewTypes.Comment)].TypeId);
        Assert.AreEqual(112, domain.Model.Types[typeof(performWithNewTypes.User)].TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(performWithNewTypes.UpgradeWithNewTypesUpgrader), DomainUpgradeMode.PerformSafely);
      using (var domain = BuildDomain(configuration)) {
        Assert.AreNotEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreNotEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreNotEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreNotEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreNotEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
        Assert.AreEqual(110, domain.Model.Types[typeof(performWithNewTypes.Comment)].TypeId);
        Assert.AreEqual(112, domain.Model.Types[typeof(performWithNewTypes.User)].TypeId);
      }
    }

    [Test]
    public void LegacySkipTest()
    {
      BuildInitialDomain();
      var configuration = BuildConfiguration(typeof(initialModel.Author), typeof(onlyCustomsTypeIdsModel.OnlyCustomIdsUpgrader), DomainUpgradeMode.LegacySkip);
      using (var domain = BuildDomain(configuration)) {
        foreach (var type in domain.Model.Types.Where(type => type.IsEntity && !type.IsSystem)) {
          Assert.GreaterOrEqual(type.TypeId, 200);
        }

        Assert.AreEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(partialSequencialTypeIdModel.PartiallySequentialTypeIdsUpgrader), DomainUpgradeMode.LegacySkip);
      using (var domain = BuildDomain(configuration)) {
        foreach (var type in domain.Model.Types.Where(type => type.IsEntity && !type.IsSystem)) {
          Assert.GreaterOrEqual(type.TypeId, 200);
        }

        Assert.AreEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(partialRandomTypeIdModel.PartiallyRandomTypeIdsUpgrader), DomainUpgradeMode.LegacySkip);
      using (var domain = BuildDomain(configuration)) {
        foreach (var type in domain.Model.Types.Where(type => type.IsEntity && !type.IsSystem)) {
          Assert.GreaterOrEqual(type.TypeId, 110);
        }

        Assert.AreEqual(150, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreEqual(149, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreEqual(250, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreEqual(110, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreEqual(251, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(typeNotExistsModel.TypeIdForNotExistingTypeUpgrader), DomainUpgradeMode.LegacySkip);
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(configuration));

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(typeIdBeyongTheLimitsModel.TypeIdOutOfRangeUpgrader), DomainUpgradeMode.LegacySkip);
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(configuration));
    }

    [Test]
    public void LegacyValidateTest()
    {
      BuildInitialDomain();
      var configuration = BuildConfiguration(typeof(initialModel.Author), typeof(onlyCustomsTypeIdsModel.OnlyCustomIdsUpgrader), DomainUpgradeMode.LegacyValidate);
      using (var domain = BuildDomain(configuration)) {
        foreach (var type in domain.Model.Types.Where(type => type.IsEntity && !type.IsSystem)) {
          Assert.GreaterOrEqual(type.TypeId, 200);
        }

        Assert.AreEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreEqual(203, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreEqual(202, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreEqual(204, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(partialSequencialTypeIdModel.PartiallySequentialTypeIdsUpgrader), DomainUpgradeMode.LegacyValidate);
      using (var domain = BuildDomain(configuration)) {
        foreach (var type in domain.Model.Types.Where(type => type.IsEntity && !type.IsSystem)) {
          Assert.GreaterOrEqual(type.TypeId, 200);
        }

        Assert.AreEqual(200, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreEqual(201, domain.Model.Types[typeof(initialModel.Book)].TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(partialRandomTypeIdModel.PartiallyRandomTypeIdsUpgrader), DomainUpgradeMode.LegacyValidate);
      using (var domain = BuildDomain(configuration)) {
        foreach (var type in domain.Model.Types.Where(type => type.IsEntity && !type.IsSystem)) {
          Assert.GreaterOrEqual(type.TypeId, 110);
        }

        Assert.AreEqual(150, domain.Model.Types[typeof(initialModel.Author)].TypeId);
        Assert.AreEqual(149, domain.Model.Types[typeof(initialModel.Book)].TypeId);
        Assert.AreEqual(250, domain.Model.Types[typeof(initialModel.Store)].TypeId);
        Assert.AreEqual(110, domain.Model.Types[typeof(initialModel.BookInStore)].TypeId);
        Assert.AreEqual(251, domain.Model.Types.Find("Xtensive.Orm.Tests.Upgrade.CustomTypeIdModel1.EntitySetItems.Author-Books-Book").TypeId);
      }

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(typeNotExistsModel.TypeIdForNotExistingTypeUpgrader), DomainUpgradeMode.LegacyValidate);
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(configuration));

      BuildInitialDomain();
      configuration = BuildConfiguration(typeof(initialModel.Author), typeof(typeIdBeyongTheLimitsModel.TypeIdOutOfRangeUpgrader), DomainUpgradeMode.LegacyValidate);
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(configuration));
    }

    [Test]
    public void LegacySkipMultiDatabaseTest()
    {
      Require.AllFeaturesSupported(ProviderFeatures.Multidatabase | ProviderFeatures.Multischema);
      var expectedMap = new Dictionary<string, int>();
      BuildInitialDomains();
      var firstConfiguration = DomainConfigurationFactory.Create();
      firstConfiguration.Types.RegisterCaching(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace);
      firstConfiguration.UpgradeMode = DomainUpgradeMode.PerformSafely;
      firstConfiguration.DefaultDatabase = DOTests2Db;
      firstConfiguration.DefaultSchema = dbo;
      firstConfiguration.Databases.Add(new DatabaseConfiguration(DOTests2Db));
      using (var domain = BuildDomain(firstConfiguration)) {
        expectedMap = domain.Model.Types.ToDictionary(key => key.UnderlyingType.FullName, value => value.TypeId);
      }

      var secondConfiguration = DomainConfigurationFactory.Create();
      secondConfiguration.Types.RegisterCaching(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace);
      secondConfiguration.Types.RegisterCaching(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace);
      secondConfiguration.Types.RegisterCaching(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace);
      secondConfiguration.UpgradeMode = DomainUpgradeMode.LegacySkip;
      secondConfiguration.DefaultDatabase = DOTests2Db;
      secondConfiguration.DefaultSchema = dbo;
      secondConfiguration.Databases.Add(new DatabaseConfiguration(DOTests1Db));
      secondConfiguration.Databases.Add(new DatabaseConfiguration(DOTests2Db));
      secondConfiguration.Databases.Add(new DatabaseConfiguration(DOTests3Db));
      secondConfiguration.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace, DOTests1Db, dbo));
      secondConfiguration.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace, DOTests2Db, dbo));
      secondConfiguration.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace, DOTests3Db, dbo));

      using (var domain = BuildDomain(secondConfiguration)) {
        var currentMap = domain.Model.Types.ToDictionary(key => key.UnderlyingType.FullName, value => value.TypeId);
        foreach (var map in expectedMap) {
          _ = currentMap.TryGetValue(map.Key, out var typeId);
          Assert.AreEqual(map.Value, typeId);
        }
        Assert.AreEqual(200, domain.Model.Types[typeof(multyDatabaseModel.Database2.Parent)].TypeId);
        Assert.AreEqual(201, domain.Model.Types[typeof(multyDatabaseModel.Database2.Child)].TypeId);
        Assert.AreEqual(300, domain.Model.Types[typeof(multyDatabaseModel.Database3.Car)].TypeId);
        Assert.AreEqual(301, domain.Model.Types[typeof(multyDatabaseModel.Database3.Engine)].TypeId);
      }

      using (var domain = BuildDomain(firstConfiguration)) {
        var currentMap = domain.Model.Types.ToDictionary(key => key.UnderlyingType.FullName, value => value.TypeId);
        Assert.AreEqual(expectedMap.Count, currentMap.Count);
        Assert.AreEqual(expectedMap, currentMap);
      }

      using (var domain = BuildDomain(secondConfiguration)) {
        var currentMap = domain.Model.Types.ToDictionary(key => key.UnderlyingType.FullName, value => value.TypeId);
        foreach (var map in expectedMap) {
          _ = currentMap.TryGetValue(map.Key, out var typeId);
          Assert.AreEqual(map.Value, typeId);
        }
        Assert.AreEqual(200, domain.Model.Types[typeof(multyDatabaseModel.Database2.Parent)].TypeId);
        Assert.AreEqual(201, domain.Model.Types[typeof(multyDatabaseModel.Database2.Child)].TypeId);
        Assert.AreEqual(300, domain.Model.Types[typeof(multyDatabaseModel.Database3.Car)].TypeId);
        Assert.AreEqual(301, domain.Model.Types[typeof(multyDatabaseModel.Database3.Engine)].TypeId);
      }

      using (var domain = BuildDomain(secondConfiguration)) {
        var currentMap = domain.Model.Types.ToDictionary(key => key.UnderlyingType.FullName, value => value.TypeId);
        foreach (var map in expectedMap) {
          _ = currentMap.TryGetValue(map.Key, out var typeId);
          Assert.AreEqual(map.Value, typeId);
        }
        Assert.AreEqual(200, domain.Model.Types[typeof(multyDatabaseModel.Database2.Parent)].TypeId);
        Assert.AreEqual(201, domain.Model.Types[typeof(multyDatabaseModel.Database2.Child)].TypeId);
        Assert.AreEqual(300, domain.Model.Types[typeof(multyDatabaseModel.Database3.Car)].TypeId);
        Assert.AreEqual(301, domain.Model.Types[typeof(multyDatabaseModel.Database3.Engine)].TypeId);
      }

      using (var domain = BuildDomain(firstConfiguration)) {
        var currentMap = domain.Model.Types.ToDictionary(key => key.UnderlyingType.FullName, value => value.TypeId);
        Assert.AreEqual(expectedMap.Count, currentMap.Count);
        Assert.AreEqual(expectedMap, currentMap);
      }

      using (var domain = BuildDomain(secondConfiguration)) {
        var currentMap = domain.Model.Types.ToDictionary(key => key.UnderlyingType.FullName, value => value.TypeId);
        foreach (var map in expectedMap) {
          _ = currentMap.TryGetValue(map.Key, out var typeId);
          Assert.AreEqual(map.Value, typeId);
        }
        Assert.AreEqual(200, domain.Model.Types[typeof(multyDatabaseModel.Database2.Parent)].TypeId);
        Assert.AreEqual(201, domain.Model.Types[typeof(multyDatabaseModel.Database2.Child)].TypeId);
        Assert.AreEqual(300, domain.Model.Types[typeof(multyDatabaseModel.Database3.Car)].TypeId);
        Assert.AreEqual(301, domain.Model.Types[typeof(multyDatabaseModel.Database3.Engine)].TypeId);
      }
    }

    [Test]
    public void LegacyValidateMultiDatabaseTest()
    {
      Require.AllFeaturesSupported(ProviderFeatures.Multidatabase | ProviderFeatures.Multischema);
      var expectedMap = new Dictionary<string, int>();
      BuildInitialDomains();
      var firstConfiguration = DomainConfigurationFactory.Create();
      firstConfiguration.Types.RegisterCaching(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace);
      firstConfiguration.UpgradeMode = DomainUpgradeMode.PerformSafely;
      firstConfiguration.DefaultDatabase = DOTests2Db;
      firstConfiguration.DefaultSchema = dbo;
      firstConfiguration.Databases.Add(new DatabaseConfiguration(DOTests2Db));
      using (var domain = BuildDomain(firstConfiguration)) {
        expectedMap = domain.Model.Types.ToDictionary(key => key.UnderlyingType.FullName, value => value.TypeId);
      }

      var secondConfiguration = DomainConfigurationFactory.Create();
      secondConfiguration.Types.RegisterCaching(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace);
      secondConfiguration.Types.RegisterCaching(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace);
      secondConfiguration.Types.RegisterCaching(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace);
      secondConfiguration.UpgradeMode = DomainUpgradeMode.LegacyValidate;
      secondConfiguration.DefaultDatabase = DOTests2Db;
      secondConfiguration.DefaultSchema = dbo;
      secondConfiguration.Databases.Add(new DatabaseConfiguration(DOTests1Db));
      secondConfiguration.Databases.Add(new DatabaseConfiguration(DOTests2Db));
      secondConfiguration.Databases.Add(new DatabaseConfiguration(DOTests3Db));
      secondConfiguration.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace, DOTests1Db, dbo));
      secondConfiguration.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace, DOTests2Db, dbo));
      secondConfiguration.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace, DOTests3Db, dbo));

      using (var domain = BuildDomain(secondConfiguration)) {
        var currentMap = domain.Model.Types.ToDictionary(key => key.UnderlyingType.FullName, value => value.TypeId);
        foreach (var map in expectedMap) {
          _ = currentMap.TryGetValue(map.Key, out var typeId);
          Assert.AreEqual(map.Value, typeId);
        }
        Assert.AreEqual(200, domain.Model.Types[typeof(multyDatabaseModel.Database2.Parent)].TypeId);
        Assert.AreEqual(201, domain.Model.Types[typeof(multyDatabaseModel.Database2.Child)].TypeId);
        Assert.AreEqual(300, domain.Model.Types[typeof(multyDatabaseModel.Database3.Car)].TypeId);
        Assert.AreEqual(301, domain.Model.Types[typeof(multyDatabaseModel.Database3.Engine)].TypeId);
      }
    }

    [Test]
    public void UserTypeIdLimits()
    {
      Require.AllFeaturesSupported(ProviderFeatures.Multidatabase | ProviderFeatures.Multischema);
      var rightConfiguration = DomainConfigurationFactory.Create();
      rightConfiguration.Types.RegisterCaching(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace);
      rightConfiguration.Types.RegisterCaching(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace);
      rightConfiguration.Types.RegisterCaching(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace);
      rightConfiguration.UpgradeMode = DomainUpgradeMode.LegacyValidate;
      rightConfiguration.DefaultDatabase = DOTests2Db;
      rightConfiguration.DefaultSchema = dbo;
      rightConfiguration.Databases.Add(new DatabaseConfiguration(DOTests1Db) { MinTypeId = 200, MaxTypeId = 299 });
      rightConfiguration.Databases.Add(new DatabaseConfiguration(DOTests2Db) { MinTypeId = 100, MaxTypeId = 199 });
      rightConfiguration.Databases.Add(new DatabaseConfiguration(DOTests3Db) { MinTypeId = 300, MaxTypeId = 399 });
      rightConfiguration.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace, DOTests1Db, dbo));
      rightConfiguration.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace, DOTests2Db, dbo));
      rightConfiguration.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace, DOTests3Db, dbo));

      using (var domain = BuildDomain(rightConfiguration)) { }

      var wrongConfiguration1 = DomainConfigurationFactory.Create();
      wrongConfiguration1.Types.RegisterCaching(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace);
      wrongConfiguration1.Types.RegisterCaching(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace);
      wrongConfiguration1.Types.RegisterCaching(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace);
      wrongConfiguration1.UpgradeMode = DomainUpgradeMode.LegacyValidate;
      wrongConfiguration1.DefaultDatabase = DOTests2Db;
      wrongConfiguration1.DefaultSchema = dbo;
      wrongConfiguration1.Databases.Add(new DatabaseConfiguration(DOTests1Db) { MinTypeId = 500, MaxTypeId = 600 });
      wrongConfiguration1.Databases.Add(new DatabaseConfiguration(DOTests2Db) { MinTypeId = 100, MaxTypeId = 199 });
      wrongConfiguration1.Databases.Add(new DatabaseConfiguration(DOTests3Db) { MinTypeId = 300, MaxTypeId = 399 });
      wrongConfiguration1.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace, DOTests1Db, dbo));
      wrongConfiguration1.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace, DOTests2Db, dbo));
      wrongConfiguration1.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace, DOTests3Db, dbo));
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(wrongConfiguration1));

      var wrongConfiguration2 = DomainConfigurationFactory.Create();
      wrongConfiguration2.Types.RegisterCaching(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace);
      wrongConfiguration2.Types.RegisterCaching(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace);
      wrongConfiguration2.Types.RegisterCaching(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace);
      wrongConfiguration2.UpgradeMode = DomainUpgradeMode.LegacyValidate;
      wrongConfiguration2.DefaultDatabase = DOTests2Db;
      wrongConfiguration2.DefaultSchema = dbo;
      wrongConfiguration2.Databases.Add(new DatabaseConfiguration(DOTests1Db) { MinTypeId = 200, MaxTypeId = 299 });
      wrongConfiguration2.Databases.Add(new DatabaseConfiguration(DOTests2Db) { MinTypeId = 500, MaxTypeId = 600 });
      wrongConfiguration2.Databases.Add(new DatabaseConfiguration(DOTests3Db) { MinTypeId = 300, MaxTypeId = 399 });
      wrongConfiguration2.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace, DOTests1Db, dbo));
      wrongConfiguration2.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace, DOTests2Db, dbo));
      wrongConfiguration2.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace, DOTests3Db, dbo));
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(wrongConfiguration2));

      var wrongConfiguration3 = DomainConfigurationFactory.Create();
      wrongConfiguration3.Types.RegisterCaching(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace);
      wrongConfiguration3.Types.RegisterCaching(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace);
      wrongConfiguration3.Types.RegisterCaching(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace);
      wrongConfiguration3.UpgradeMode = DomainUpgradeMode.LegacyValidate;
      wrongConfiguration3.DefaultDatabase = DOTests2Db;
      wrongConfiguration3.DefaultSchema = dbo;
      wrongConfiguration3.Databases.Add(new DatabaseConfiguration(DOTests1Db) { MinTypeId = 200, MaxTypeId = 299 });
      wrongConfiguration3.Databases.Add(new DatabaseConfiguration(DOTests2Db) { MinTypeId = 100, MaxTypeId = 199 });
      wrongConfiguration3.Databases.Add(new DatabaseConfiguration(DOTests3Db) { MinTypeId = 500, MaxTypeId = 600 });
      wrongConfiguration3.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace, DOTests1Db, dbo));
      wrongConfiguration3.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace, DOTests2Db, dbo));
      wrongConfiguration3.MappingRules.Add(
        new MappingRule(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace, DOTests3Db, dbo));
      _ = Assert.Throws<DomainBuilderException>(() => BuildDomain(wrongConfiguration3));
    }

    private void BuildInitialDomain()
    {
      var configuration = DomainConfigurationFactory.Create();
      configuration.Types.RegisterCaching(typeof(initialModel.Author).Assembly, typeof(initialModel.Author).Namespace);
      configuration.Types.Register(typeof(initialModelUpgrader.Upgrader));
      configuration.UpgradeMode = DomainUpgradeMode.Recreate;
      using (var domain = BuildDomain(configuration)) { }
    }

    private void BuildInitialDomains()
    {
      var firstConfiguration = DomainConfigurationFactory.Create();
      firstConfiguration.Types.RegisterCaching(typeof(multyDatabaseModel.Database1.Book).Assembly, typeof(multyDatabaseModel.Database1.Book).Namespace);
      firstConfiguration.UpgradeMode = DomainUpgradeMode.Recreate;
      firstConfiguration.DefaultDatabase = DOTests2Db;
      firstConfiguration.DefaultSchema = dbo;
      firstConfiguration.Databases.Add(new DatabaseConfiguration(DOTests2Db));
      var domain1 = BuildDomain(firstConfiguration);
      domain1.Dispose();

      var secondConfig = DomainConfigurationFactory.Create();
      secondConfig.Types.RegisterCaching(typeof(multyDatabaseModel.Database2.Child).Assembly, typeof(multyDatabaseModel.Database2.Child).Namespace);
      secondConfig.UpgradeMode = DomainUpgradeMode.Recreate;
      secondConfig.DefaultDatabase = DOTests1Db;
      secondConfig.DefaultSchema = dbo;
      secondConfig.Databases.Add(new DatabaseConfiguration(DOTests1Db));
      var domain2 = BuildDomain(secondConfig);
      domain2.Dispose();

      var thirdConfig = DomainConfigurationFactory.Create();
      thirdConfig.Types.RegisterCaching(typeof(multyDatabaseModel.Database3.Car).Assembly, typeof(multyDatabaseModel.Database3.Car).Namespace);
      thirdConfig.UpgradeMode = DomainUpgradeMode.Recreate;
      thirdConfig.DefaultDatabase = DOTests3Db;
      thirdConfig.DefaultSchema = dbo;
      thirdConfig.Databases.Add(new DatabaseConfiguration(DOTests3Db));
      var domain3 = BuildDomain(thirdConfig);
      domain3.Dispose();
    }

    private DomainConfiguration BuildConfiguration(Type baseNamespaceType, DomainUpgradeMode mode) =>
      BuildConfiguration(baseNamespaceType, null, mode);

    private DomainConfiguration BuildConfiguration(Type baseNamespaceType, Type additionalNamespaceType, DomainUpgradeMode mode)
    {
      var configuration = DomainConfigurationFactory.Create();
      configuration.Types.RegisterCaching(baseNamespaceType.Assembly, baseNamespaceType.Namespace);

      if (additionalNamespaceType != null) {
        configuration.Types.RegisterCaching(additionalNamespaceType.Assembly, additionalNamespaceType.Namespace);
      }

      configuration.UpgradeMode = mode;
      return configuration;
    }
  }
}
