diff --git a/ChangeLog/7.1.2_dev.txt b/ChangeLog/7.1.2_dev.txt
index 78b15d4fc6..68137d651a 100644
--- a/ChangeLog/7.1.2_dev.txt
+++ b/ChangeLog/7.1.2_dev.txt
@@ -1 +1,9 @@
-[main] Addressed issue when cycles in Entity dependency graph were not detected
\ No newline at end of file
+[main] Addressed issue when cycles in Entity dependency graph were not detected
+[main] Intoduced DomainConfuguration.ExtensionConfigurations as unified configuration storage for extensions' configurations
+[main] DomainConfiguration.Load() method now has overloads which gets abstractions from Microsoft.Extensions.Configuration API
+[localization] LocalizationConfiguration.Load() method now has overloads which gets abstractions from Microsoft.Extensions.Configuration API
+[localization] ConfigureLocalizationExtension() extensions for DomainConfiguration are added, check ReadMe/documentation for examples of usage
+[reprocessing] ReprocessingConfiguration.Load() method now has overloads which gets abstractions from Microsoft.Extensions.Configuration API
+[reprocessing] ConfigureReprocessingExtension() extensions for DomainConfiguration are added, check ReadMe/documentation for examples of usage
+[security] SecurityConfiguration.Load() method now has overloads which gets abstractions from Microsoft.Extensions.Configuration API
+[security] ConfigureSecurityExtension() extensions for DomainConfiguration are added, check ReadMe/documentation for examples of usage
\ No newline at end of file
diff --git a/Extensions/TestCommon/ModernConfigurationTestBase.cs b/Extensions/TestCommon/ModernConfigurationTestBase.cs
new file mode 100644
index 0000000000..a0faf76c24
--- /dev/null
+++ b/Extensions/TestCommon/ModernConfigurationTestBase.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Configuration;
+using NUnit.Framework;
+
+namespace TestCommon
+{
+ public abstract class MicrosoftConfigurationTestBase
+ {
+ protected enum ConfigTypes
+ {
+ Json,
+ Xml,
+ }
+
+ protected IConfigurationRoot configurationRoot;
+
+ protected abstract ConfigTypes ConfigFormat { get; }
+
+ protected abstract void AddConfigurationFile(IConfigurationBuilder configurationBuilder);
+
+ [OneTimeSetUp]
+ public virtual void BeforeAllTestsSetUp()
+ {
+ var configurationBuilder = new ConfigurationBuilder();
+ configurationBuilder.SetBasePath(Directory.GetCurrentDirectory());
+ AddConfigurationFile(configurationBuilder);
+ configurationRoot = (ConfigurationRoot) configurationBuilder.Build();
+ }
+
+ protected void IgnoreIfXml()
+ {
+ if (ConfigFormat == ConfigTypes.Xml)
+ throw new IgnoreException("Not valid for Xml format");
+ }
+ protected void IgnoreIfJson()
+ {
+ if (ConfigFormat == ConfigTypes.Json)
+ throw new IgnoreException("Not valid for JSON format");
+ }
+
+ protected IConfigurationSection GetAndCheckConfigurationSection(string sectionName)
+ {
+ var section = configurationRoot.GetSection(sectionName);
+ Assert.That(section, Is.Not.Null);
+ return section;
+ }
+ }
+}
diff --git a/Extensions/TestCommon/TestCommon.csproj b/Extensions/TestCommon/TestCommon.csproj
index e23f0c4aef..16a3f08891 100644
--- a/Extensions/TestCommon/TestCommon.csproj
+++ b/Extensions/TestCommon/TestCommon.csproj
@@ -8,16 +8,13 @@
$(ExtensionsKeyFile)
-
-
-
-
-
-
+
+
+
diff --git a/Extensions/Xtensive.Orm.BulkOperations/NugetContent/ReadMe.md b/Extensions/Xtensive.Orm.BulkOperations/NugetContent/ReadMe.md
index 9fccda4329..2500340342 100644
--- a/Extensions/Xtensive.Orm.BulkOperations/NugetContent/ReadMe.md
+++ b/Extensions/Xtensive.Orm.BulkOperations/NugetContent/ReadMe.md
@@ -8,7 +8,7 @@ to server-side UPDATE or DELETE commands.
Prerequisites
-------------
-DataObjects.Net 7.0.x (http://dataobjects.net)
+DataObjects.Net 7.1.x (http://dataobjects.net)
Examples of usage
diff --git a/Extensions/Xtensive.Orm.Localization.Tests/LocalizationSettings.config b/Extensions/Xtensive.Orm.Localization.Tests/LocalizationSettings.config
new file mode 100644
index 0000000000..8f439a9e13
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Localization.Tests/LocalizationSettings.config
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ es-ES
+
+
+
+ ololo
+
+
+
+
+
+
+
+
+ es-ES
+
+
+
+ es-ES
+
+
+
+ es-ES
+
+
+
+ es-ES
+
+
+
+
+
+
+
+
+ es-ES
+
+
+
+ es-ES
+
+
+
+ es-ES
+
+
+
+ es-ES
+
+
+
+
+
+
+
+
+
+
+
+ es-ES
+
+
+
+
+
+ ololo
+
+
+
+
+
+ es-ES
+
+
+
+
+
+ es-ES
+
+
+
+
+
+ es-ES
+
+
+
+
+
+ es-ES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Localization.Tests/MicrosoftConfigurationTests.cs b/Extensions/Xtensive.Orm.Localization.Tests/MicrosoftConfigurationTests.cs
new file mode 100644
index 0000000000..9bb8d2b77c
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Localization.Tests/MicrosoftConfigurationTests.cs
@@ -0,0 +1,347 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using System.Globalization;
+using System.Threading;
+using Microsoft.Extensions.Configuration;
+using NUnit.Framework;
+using Xtensive.Orm.Localization.Configuration;
+
+namespace Xtensive.Orm.Localization.Tests.Configuration
+{
+ public sealed class JsonConfigurationTest : MicrosoftConfigurationTest
+ {
+ protected override ConfigTypes ConfigFormat => ConfigTypes.Json;
+
+ protected override void AddConfigurationFile(IConfigurationBuilder configurationBuilder)
+ {
+ _ = configurationBuilder.AddJsonFile("localizationsettings.json");
+ }
+ }
+
+ public sealed class XmlConfigurationTest : MicrosoftConfigurationTest
+ {
+ protected override ConfigTypes ConfigFormat => ConfigTypes.Xml;
+
+ protected override void AddConfigurationFile(IConfigurationBuilder configurationBuilder)
+ {
+ _ = configurationBuilder.AddXmlFile("LocalizationSettings.config");
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeEmptyNameTest(bool useRoot)
+ {
+ var locConfig = LoadConfiguration("Xtensive.Orm.Localization.NameAttribute.NameEmpty", useRoot);
+ CheckConfigurationIsDefault(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeAbsentTest(bool useRoot)
+ {
+ var locConfig = LoadConfiguration("Xtensive.Orm.Localization.NameAttribute.None", useRoot);
+ CheckConfigurationIsDefault(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeDefinedTest(bool useRoot)
+ {
+ var locConfig = LoadConfiguration("Xtensive.Orm.Localization.NameAttribute.Defined", useRoot);
+ Assert.That(locConfig, Is.Not.Null);
+ Assert.That(locConfig.DefaultCulture, Is.EqualTo(expectedCulture));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeFaultyValueTest(bool useRoot)
+ {
+ var locConfig = LoadConfiguration("Xtensive.Orm.Localization.NameAttribute.FaultyValue", useRoot);
+ CheckConfigurationIsDefault(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeLowCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration("Xtensive.Orm.Localization.NameAttribute.LC", useRoot);
+ Assert.That(locConfig, Is.Not.Null);
+ Assert.That(locConfig.DefaultCulture, Is.EqualTo(expectedCulture));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeUpperCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration("Xtensive.Orm.Localization.NameAttribute.UC", useRoot);
+ Assert.That(locConfig, Is.Not.Null);
+ Assert.That(locConfig.DefaultCulture, Is.EqualTo(expectedCulture));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributePascalCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration("Xtensive.Orm.Localization.NameAttribute.PC", useRoot);
+ Assert.That(locConfig, Is.Not.Null);
+ Assert.That(locConfig.DefaultCulture, Is.EqualTo(expectedCulture));
+ }
+ }
+
+ [TestFixture]
+ public abstract class MicrosoftConfigurationTest : TestCommon.MicrosoftConfigurationTestBase
+ {
+ protected readonly CultureInfo defaultCulture = new CultureInfo("en-US");
+ protected readonly CultureInfo expectedCulture = new CultureInfo("es-ES");
+
+ private CultureInfo resetCulture;
+
+ public override void BeforeAllTestsSetUp()
+ {
+ base.BeforeAllTestsSetUp();
+
+ }
+
+ protected LocalizationConfiguration LoadConfiguration(string sectionName, bool useRoot)
+ {
+ return useRoot
+ ? LocalizationConfiguration.Load(configurationRoot, sectionName)
+ : LocalizationConfiguration.Load(configurationRoot.GetSection(sectionName));
+ }
+
+ [SetUp]
+ public void SetUp()
+ {
+ resetCulture = Thread.CurrentThread.CurrentCulture;
+ Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ Thread.CurrentThread.CurrentCulture = resetCulture;
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void EmptySectionCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.Empty", useRoot);
+ CheckConfigurationIsDefault(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void EmptyName(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.NameEmpty", useRoot);
+ CheckConfigurationIsDefault(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameDefined(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.NameDefined", useRoot);
+ Assert.That(locConfig, Is.Not.Null);
+ Assert.That(locConfig.DefaultCulture, Is.EqualTo(expectedCulture));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void FaultyValue(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.FaultyValue", useRoot);
+ CheckConfigurationIsDefault(locConfig);
+ }
+
+ #region Naming
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingInLowCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.Naming.LC", useRoot);
+ ValidateNamingConfigurationResults(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingInUpperCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.Naming.UC", useRoot);
+ ValidateNamingConfigurationResults(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingInCamelCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.Naming.CC", useRoot);
+ ValidateNamingConfigurationResults(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingInPascalCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.Naming.PC", useRoot);
+ ValidateNamingConfigurationResults(locConfig);
+ }
+
+ private void ValidateNamingConfigurationResults(LocalizationConfiguration locConfig)
+ {
+ Assert.That(locConfig, Is.Not.Null);
+ Assert.That(locConfig.DefaultCulture, Is.EqualTo(expectedCulture));
+ }
+
+ #endregion
+
+ #region mistype cases
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MistypeInLowCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.Mistype.LC", useRoot);
+ ValidateMistypeConfigurationResults(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MistypeInUpperCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.Mistype.UC", useRoot);
+ ValidateMistypeConfigurationResults(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MistypeInCamelCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.Mistype.CC", useRoot);
+ ValidateMistypeConfigurationResults(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MistypeInPascalCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.Mistype.PC", useRoot);
+ ValidateMistypeConfigurationResults(locConfig);
+ }
+
+ private void ValidateMistypeConfigurationResults(LocalizationConfiguration locConfig)
+ {
+ CheckConfigurationIsDefault(locConfig);
+ }
+
+ #endregion
+
+ #region Name as node
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NoNameNodes(bool useRoot)
+ {
+ IgnoreIfXml();
+
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.NameNode.Empty", useRoot);
+ CheckConfigurationIsDefault(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameNodeIsEmpty(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.NameNode.NameEmpty", useRoot);
+ CheckConfigurationIsDefault(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DefinedNameNode(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.NameNode.Espaniol", useRoot);
+ Assert.That(locConfig, Is.Not.Null);
+ Assert.That(locConfig.DefaultCulture, Is.EqualTo(expectedCulture));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void FaultyNameNodeValue(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.NameNode.FaultyValue", useRoot);
+ CheckConfigurationIsDefault(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameNodeInLowCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.NameNode.Naming.LC", useRoot);
+ ValidateNamingConfigurationResults(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameNodeInUpperCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.NameNode.Naming.UC", useRoot);
+ ValidateNamingConfigurationResults(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameNodeInCamelCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.NameNode.Naming.CC", useRoot);
+ ValidateNamingConfigurationResults(locConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameNodeInPascalCase(bool useRoot)
+ {
+ var locConfig = LoadConfiguration($"Xtensive.Orm.Localization.{ConfigFormat}.NameNode.Naming.PC", useRoot);
+ ValidateNamingConfigurationResults(locConfig);
+ }
+
+ #endregion
+
+ protected void CheckConfigurationIsDefault(LocalizationConfiguration locConfig)
+ {
+ Assert.That(locConfig, Is.Not.Null);
+ Assert.That(locConfig.DefaultCulture, Is.EqualTo(defaultCulture));
+ }
+ }
+}
diff --git a/Extensions/Xtensive.Orm.Localization.Tests/Xtensive.Orm.Localization.Tests.csproj b/Extensions/Xtensive.Orm.Localization.Tests/Xtensive.Orm.Localization.Tests.csproj
index e0eba70610..79c0f9f637 100644
--- a/Extensions/Xtensive.Orm.Localization.Tests/Xtensive.Orm.Localization.Tests.csproj
+++ b/Extensions/Xtensive.Orm.Localization.Tests/Xtensive.Orm.Localization.Tests.csproj
@@ -8,6 +8,9 @@
+
+
+
@@ -20,4 +23,16 @@
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Localization.Tests/localizationsettings.json b/Extensions/Xtensive.Orm.Localization.Tests/localizationsettings.json
new file mode 100644
index 0000000000..9fe0bc835b
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Localization.Tests/localizationsettings.json
@@ -0,0 +1,80 @@
+{
+ "Xtensive.Orm.Localization.Json.Empty": {
+
+ },
+
+ "Xtensive.Orm.Localization.Json.NameEmpty": {
+ "DefaultCulture": ""
+ },
+
+ "Xtensive.Orm.Localization.Json.NameDefined": {
+ "DefaultCulture": "es-ES"
+ },
+
+ "Xtensive.Orm.Localization.Json.FaultyValue": {
+ "DefaultCulture": "ololo"
+ },
+
+ "Xtensive.Orm.Localization.Json.Naming.LC": {
+ "defaultculture": "es-ES"
+ },
+ "Xtensive.Orm.Localization.Json.Naming.UC": {
+ "DEFAULTCULTURE": "es-ES"
+ },
+ "Xtensive.Orm.Localization.Json.Naming.CC": {
+ "defaultCulture": "es-ES"
+ },
+ "Xtensive.Orm.Localization.Json.Naming.PC": {
+ "DefaultCulture": "es-ES"
+ },
+
+ "Xtensive.Orm.Localization.Json.Mistype.LC": {
+ "defailtculture": "es-ES"
+ },
+ "Xtensive.Orm.Localization.Json.Mistype.UC": {
+ "DEFAILTCULTURE": "es-ES"
+ },
+ "Xtensive.Orm.Localization.Json.Mistype.CC": {
+ "defailtCulture": "es-ES"
+ },
+ "Xtensive.Orm.Localization.Json.Mistype.PC": {
+ "DefailtCulture": "es-ES"
+ },
+
+
+ "Xtensive.Orm.Localization.Json.NameNode.Empty": {
+ "DefaultCulture": {
+ }
+ },
+
+ "Xtensive.Orm.Localization.Json.NameNode.NameEmpty": {
+ "DefaultCulture": {
+ "Name": ""
+ }
+ },
+
+ "Xtensive.Orm.Localization.Json.NameNode.Espaniol": {
+ "DefaultCulture": {
+ "Name": "es-ES"
+ }
+ },
+
+ "Xtensive.Orm.Localization.Json.NameNode.FaultyValue": {
+ "DefaultCulture": {
+ "Name": "ololo"
+ }
+ },
+
+ "Xtensive.Orm.Localization.Json.NameNode.Naming.LC": {
+ "DefaultCulture": { "name": "es-ES" }
+ },
+ "Xtensive.Orm.Localization.Json.NameNode.Naming.UC": {
+ "DefaultCulture": { "NAME": "es-ES" }
+ },
+ "Xtensive.Orm.Localization.Json.NameNode.Naming.CC": {
+ "DefaultCulture": { "naMe": "es-ES" }
+ },
+ "Xtensive.Orm.Localization.Json.NameNode.Naming.PC": {
+ "DefaultCulture": { "NaMe": "es-ES" }
+ }
+}
diff --git a/Extensions/Xtensive.Orm.Localization/Configuration/Elements/ConfigurationSection.cs b/Extensions/Xtensive.Orm.Localization/Configuration/Elements/ConfigurationSection.cs
index c13ec91716..8349378aed 100644
--- a/Extensions/Xtensive.Orm.Localization/Configuration/Elements/ConfigurationSection.cs
+++ b/Extensions/Xtensive.Orm.Localization/Configuration/Elements/ConfigurationSection.cs
@@ -18,6 +18,7 @@ public class ConfigurationSection : System.Configuration.ConfigurationSection
/// Gets default section name for security configuration.
/// Value is "Xtensive.Orm.Localization".
///
+ [Obsolete("Use Localization.DefaultSectionName instead.")]
public static readonly string DefaultSectionName = "Xtensive.Orm.Localization";
private const string DefaultCultureElementName = "defaultCulture";
diff --git a/Extensions/Xtensive.Orm.Localization/Configuration/LocalizationConfiguration.cs b/Extensions/Xtensive.Orm.Localization/Configuration/LocalizationConfiguration.cs
index aab701d997..6b84a40dd2 100644
--- a/Extensions/Xtensive.Orm.Localization/Configuration/LocalizationConfiguration.cs
+++ b/Extensions/Xtensive.Orm.Localization/Configuration/LocalizationConfiguration.cs
@@ -1,13 +1,17 @@
-// Copyright (C) 2011 Xtensive LLC.
-// All rights reserved.
-// For conditions of distribution and use, see license.
+// Copyright (C) 2012-2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
// Created by: Dmitri Maximov
// Created: 2012.07.06
using System;
using System.Configuration;
using System.Globalization;
+using System.Linq;
using System.Threading;
+using Microsoft.Extensions.Configuration;
+using Xtensive.Core;
+using Xtensive.Orm.Configuration;
namespace Xtensive.Orm.Localization.Configuration
{
@@ -15,7 +19,7 @@ namespace Xtensive.Orm.Localization.Configuration
/// The configuration of the localization extension.
///
[Serializable]
- public class LocalizationConfiguration
+ public class LocalizationConfiguration : ConfigurationBase
{
///
/// Default SectionName value:
@@ -23,11 +27,35 @@ public class LocalizationConfiguration
///
public const string DefaultSectionName = "Xtensive.Orm.Localization";
+ private CultureInfo culture;
+
///
/// Gets or sets the default culture.
///
/// The default culture.
- public CultureInfo DefaultCulture { get; private set; }
+ public CultureInfo DefaultCulture {
+ get => culture;
+ internal set {
+ EnsureNotLocked();
+ culture = value;
+ }
+ }
+
+ ///
+ protected override LocalizationConfiguration CreateClone() => new LocalizationConfiguration();
+
+ ///
+ protected override void CopyFrom(ConfigurationBase source)
+ {
+ base.CopyFrom(source);
+
+ var nativeConfig = source as LocalizationConfiguration;
+ nativeConfig.DefaultCulture = DefaultCulture;
+ }
+
+ ///
+ public override LocalizationConfiguration Clone() => (LocalizationConfiguration) base.Clone();
+
///
/// Loads the
@@ -88,8 +116,8 @@ private static LocalizationConfiguration GetConfigurationFromSection(Configurati
var result = new LocalizationConfiguration();
result.DefaultCulture = Thread.CurrentThread.CurrentCulture;
- string cultureName = configurationSection==null
- ? string.Empty
+ string cultureName = configurationSection == null
+ ? string.Empty
: configurationSection.DefaultCulture.Name;
if (string.IsNullOrEmpty(cultureName))
return result;
@@ -98,10 +126,58 @@ private static LocalizationConfiguration GetConfigurationFromSection(Configurati
var culture = new CultureInfo(cultureName);
result.DefaultCulture = culture;
}
- catch (CultureNotFoundException)
- {
+ catch (CultureNotFoundException) {
}
return result;
}
+
+ ///
+ /// Loads from given configuration section of .
+ /// If section name is not provided is used.
+ ///
+ /// of sections.
+ /// Custom section name to load from.
+ /// Loaded configuration or default configuration if loading failed for some reason.
+ public static LocalizationConfiguration Load(IConfiguration configuration, string sectionName = null)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configuration, nameof(configuration));
+
+ if (configuration is IConfigurationRoot configurationRoot) {
+ return new LocalizationConfigurationReader().Read(configurationRoot, sectionName ?? DefaultSectionName);
+ }
+ else if (configuration is IConfigurationSection configurationSection) {
+ return sectionName.IsNullOrEmpty()
+ ? new LocalizationConfigurationReader().Read(configurationSection)
+ : new LocalizationConfigurationReader().Read(configurationSection.GetSection(sectionName));
+ }
+
+ throw new NotSupportedException("Type of configuration is not supported.");
+ }
+
+ ///
+ /// Loads from given configuration section.
+ ///
+ /// to load from.
+ /// Loaded configuration or default configuration if loading failed for some reason.
+ public static LocalizationConfiguration Load(IConfigurationSection configurationSection)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationSection, nameof(configurationSection));
+
+ return new LocalizationConfigurationReader().Read(configurationSection);
+ }
+
+ ///
+ /// Loads from given configuration section of .
+ /// If section name is not provided is used.
+ ///
+ /// of sections.
+ /// Custom section name to load from.
+ /// Loaded configuration or default configuration if loading failed for some reason.
+ public static LocalizationConfiguration Load(IConfigurationRoot configurationRoot, string sectionName = null)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationRoot, nameof(configurationRoot));
+
+ return new LocalizationConfigurationReader().Read(configurationRoot, sectionName ?? DefaultSectionName);
+ }
}
}
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Localization/Configuration/LocalizationConfigurationReader.cs b/Extensions/Xtensive.Orm.Localization/Configuration/LocalizationConfigurationReader.cs
new file mode 100644
index 0000000000..0b7fce4531
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Localization/Configuration/LocalizationConfigurationReader.cs
@@ -0,0 +1,67 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using Microsoft.Extensions.Configuration;
+using Xtensive.Core;
+using Xtensive.Orm.Configuration;
+
+namespace Xtensive.Orm.Localization.Configuration
+{
+ internal sealed class LocalizationConfigurationReader : IConfigurationSectionReader
+ {
+ private const string DefaultCultureElementName = "DefaultCulture";
+ private const string CultureNameAttributeName = "name";
+
+ public LocalizationConfiguration Read(IConfigurationSection configurationSection) => ReadInternal(configurationSection);
+
+ public LocalizationConfiguration Read(IConfigurationSection configurationSection, string nameOfConfiguration) =>
+ throw new NotSupportedException();
+
+ public LocalizationConfiguration Read(IConfigurationRoot configurationRoot) =>
+ Read(configurationRoot, LocalizationConfiguration.DefaultSectionName);
+
+ public LocalizationConfiguration Read(IConfigurationRoot configurationRoot, string sectionName)
+ {
+ var section = configurationRoot.GetSection(sectionName);
+ return ReadInternal(section);
+ }
+
+ public LocalizationConfiguration Read(IConfigurationRoot configurationRoot, string sectionName, string nameOfConfiguration) =>
+ throw new NotSupportedException();
+
+ private LocalizationConfiguration ReadInternal(IConfigurationSection configurationSection)
+ {
+ var defaultCultureSection = configurationSection.GetSection(DefaultCultureElementName);
+ var defaultCulture = Thread.CurrentThread.CurrentCulture;
+ if (defaultCultureSection == null) {
+ return new LocalizationConfiguration() { DefaultCulture = defaultCulture };
+ }
+
+ var cultureName = defaultCultureSection.Value;
+ if (cultureName == null) {
+ cultureName = defaultCultureSection.GetSection(CultureNameAttributeName)?.Value;
+ if (cultureName == null) {
+ var children = defaultCultureSection.GetChildren().ToList();
+ if (children.Count > 0) {
+ cultureName = children[0].GetSection(CultureNameAttributeName).Value;
+ }
+ }
+ }
+ if(!cultureName.IsNullOrEmpty()) {
+ try {
+ defaultCulture = CultureInfo.GetCultureInfo(cultureName);
+ }
+ catch(CultureNotFoundException) {
+ // swallow it, this is mark wrong culture name;
+ }
+ }
+
+ return new LocalizationConfiguration() { DefaultCulture = defaultCulture };
+ }
+ }
+}
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Localization/DomainConfugurationExtensions.cs b/Extensions/Xtensive.Orm.Localization/DomainConfugurationExtensions.cs
new file mode 100644
index 0000000000..e63b2b0c74
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Localization/DomainConfugurationExtensions.cs
@@ -0,0 +1,130 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using Microsoft.Extensions.Configuration;
+using Xtensive.Orm.Configuration;
+using Xtensive.Orm.Localization.Configuration;
+
+namespace Xtensive.Orm.Localization
+{
+ ///
+ /// Contains extensions for DomainConfiguration that help to configure the extension.
+ ///
+ public static class DomainConfugurationLocalizationExtensions
+ {
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureLocalizationExtension(this DomainConfiguration domainConfiguration)
+ => ConfigureLocalizationExtension(domainConfiguration, LocalizationConfiguration.Load());
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Section name.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureLocalizationExtension(this DomainConfiguration domainConfiguration,
+ string configurationSectionName) =>
+ ConfigureLocalizationExtension(domainConfiguration, LocalizationConfiguration.Load(configurationSectionName));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration to load from.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureLocalizationExtension(this DomainConfiguration domainConfiguration,
+ System.Configuration.Configuration configuration) =>
+ ConfigureLocalizationExtension(domainConfiguration, LocalizationConfiguration.Load(configuration));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration to load from.
+ /// Section in
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureLocalizationExtension(this DomainConfiguration domainConfiguration,
+ System.Configuration.Configuration configuration, string sectionName) =>
+ ConfigureLocalizationExtension(domainConfiguration, LocalizationConfiguration.Load(configuration, sectionName));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration to load from.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureLocalizationExtension(this DomainConfiguration domainConfiguration,
+ IConfiguration configuration) =>
+ ConfigureLocalizationExtension(domainConfiguration, LocalizationConfiguration.Load(configuration));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration to load from.
+ /// Section in
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureLocalizationExtension(this DomainConfiguration domainConfiguration,
+ IConfiguration configuration, string sectionName = null) =>
+ ConfigureLocalizationExtension(domainConfiguration, LocalizationConfiguration.Load(configuration, sectionName));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration to load from.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureLocalizationExtension(this DomainConfiguration domainConfiguration,
+ IConfigurationRoot configurationRoot) =>
+ ConfigureLocalizationExtension(domainConfiguration, LocalizationConfiguration.Load(configurationRoot));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration to load from.
+ /// Section in
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureLocalizationExtension(this DomainConfiguration domainConfiguration,
+ IConfigurationRoot configurationRoot, string sectionName) =>
+ ConfigureLocalizationExtension(domainConfiguration, LocalizationConfiguration.Load(configurationRoot, sectionName));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration section to load from.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureLocalizationExtension(this DomainConfiguration domainConfiguration,
+ IConfigurationSection configurationSection) =>
+ ConfigureLocalizationExtension(domainConfiguration, LocalizationConfiguration.Load(configurationSection));
+
+ ///
+ /// Configures the extension with given localization configuration instance.
+ ///
+ /// Domain configuration.
+ /// Localization configuration instance.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureLocalizationExtension(this DomainConfiguration domainConfiguration,
+ LocalizationConfiguration localizationConfiguration)
+ {
+ domainConfiguration.ExtensionConfigurations.Set(localizationConfiguration);
+ domainConfiguration.Types.Register(typeof(DomainConfugurationLocalizationExtensions).Assembly);
+ return domainConfiguration;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Localization/Internals/TypeLocalizationMap.cs b/Extensions/Xtensive.Orm.Localization/Internals/TypeLocalizationMap.cs
index 51e17ff140..2ac7db18cd 100644
--- a/Extensions/Xtensive.Orm.Localization/Internals/TypeLocalizationMap.cs
+++ b/Extensions/Xtensive.Orm.Localization/Internals/TypeLocalizationMap.cs
@@ -10,6 +10,7 @@
using Xtensive.Orm.Model;
using Xtensive.Orm;
using Xtensive.Reflection;
+using Xtensive.Core;
namespace Xtensive.Orm.Localization
{
@@ -21,13 +22,19 @@ internal class TypeLocalizationMap
public static void Initialize(Domain domain)
{
- if (domain == null)
- throw new ArgumentNullException("domain");
- if (domain.Extensions.Get() != null)
+ ArgumentValidator.EnsureArgumentNotNull(domain, nameof(domain));
+
+ var existing = domain.Extensions.Get();
+ if (existing != null) {
return;
+ }
+
+ var configFromNewSource = domain.Configuration.ExtensionConfigurations.Get();
var map = new TypeLocalizationMap() {
- Configuration = LocalizationConfiguration.Load()
+ Configuration = (configFromNewSource != null)
+ ? configFromNewSource
+ : LocalizationConfiguration.Load()// config from old source.
};
foreach (var localizableTypeInfo in domain.Model.Types.Entities) {
var type = localizableTypeInfo.UnderlyingType;
diff --git a/Extensions/Xtensive.Orm.Localization/NugetContent/ReadMe.md b/Extensions/Xtensive.Orm.Localization/NugetContent/ReadMe.md
index 87434ac868..58ec4541c9 100644
--- a/Extensions/Xtensive.Orm.Localization/NugetContent/ReadMe.md
+++ b/Extensions/Xtensive.Orm.Localization/NugetContent/ReadMe.md
@@ -8,7 +8,7 @@ This implies that localizable resources are a part of domain model so they are s
Prerequisites
-------------
-DataObjects.Net 7.0.x or later (http://dataobjects.net)
+DataObjects.Net 7.1.x or later (http://dataobjects.net)
Implementation
--------------
@@ -50,6 +50,7 @@ Define corresponding localizations, e.g.:
}
```
+
Examples of usage
-----------------
@@ -106,4 +107,267 @@ Examples of usage
where p.Title=="Bienvenido"
select p;
Assert.AreEqual(1, query.Count());
+```
+
+
+Examples of how to configure extension
+--------------------------------------
+
+Following examples show different ways to configure extension in configuration files of various types.
+
+**Example #1** Confugure default culture in App.config/Web.config
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Such configuration is usually read with ```System.Configuration.ConfigurationManager```.
+If project still supports such configurations then Localization configuration will be read automatically when it needs to be read.
+Sometimes a work-around is needed to read such configuration, for more read Example #2 and Example #3
+
+
+**Example #2** Reading old-style configuration of an assembly in NET 5 and newer.
+
+Due to new architecture without AppDomain (which among the other things was in charge of gathering configuration files of loaded assemblies
+as it would be one configuration file) ```System.Configuration.ConfigurationManager``` now reads only configuration file of actual executable, loaded
+assemblies' configuration files stay unreachable by default, though there is need to read some data from them.
+A great example is test projects which are usually get loaded by test runner executable, and the only configuration accessible in this case
+is test runner one.
+
+Extra step is required to read configuration files in such cases. Thankfully, ```ConfigurationManager``` has methods to get access to assemblies' configuration files.
+
+To get access to an assembly configuration file it should be opened explicitly by
+
+```csharp
+ var configuration = ConfigurationManager.OpenExeConfiguration(typeof(SomeTypeInConfigOwnerAssembly).Assembly.Location);
+```
+
+The instance returned from ```OpenExeConfiguration``` provides access to sections of the assembly configuration. DataObjects.Net configurations
+(```DomainConfiguration```, ```LocalizationConfiguration```, etc.) have ```Load()``` methods that can recieve this instance.
+```LocalizationConfiguration``` can be read like so
+
+```csharp
+ var configuration = ConfigurationManager.OpenExeConfiguration(typeof(SomeTypeInConfigOwnerAssembly).Assembly.Location);
+ var localizationConfig = LocalizationConfiguration.Load(configuration);
+
+ // loaded configuration should be manually placed to
+ domainConfiguration.ExtensionConfigurations.Set(localizationConfig);
+```
+
+The ```domainConfiguration.ExtensionConfigurations``` is a new unified place from which the extension will try to get its configuration
+instead of calling default parameterless ```Load()``` method, which has not a lot of sense now, though the method is kept as a second source
+for backwards compatibility.
+
+For more convenience, ```DomainConfiguration``` extensions are provided, which make code neater.
+For instance,
+
+```csharp
+ var configuration = ConfigurationManager.OpenExeConfiguration(typeof(SomeTypeInConfigOwnerAssembly).Assembly.Location);
+
+ // the extension hides getting configuration with LocalizationConfiguration.Load(configuration)
+ // and also putting it to ExtensionConfigurations collection.
+ domainConfiguration.ConfigureLocalizationExtension(configuration);
+```
+
+Custom section names are also supported if for some reason default section name is not used.
+
+
+**Example #3** Reading old-style configuration of an assembly in a project that uses appsettings.json file.
+
+If for some reason there is need to keep the old-style configuration then there is a work-around as well.
+Static configuration manager provides method ```OpenMappedExeConfiguration()``` which allows to get
+any *.config file as ```System.Configuration.Configuration``` instance. For example,
+
+```csharp
+ ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
+ configFileMap.ExeConfigFilename = "Orm.config"; //or other file name, the file should exist bin folder
+ var configuration = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
+```
+
+After that, as in previous example, the instance can be passed to ```Load``` method of ```LocalizationConfiguration``` to read extension configuration
+and later put it to ```DomainConfiguration.ExtensionConfigurations```
+
+```csharp
+ var localizationConfiguration = LocalizationConfiguration.Load(configuration);
+
+ domainConfiguration.ExtensionConfigurations.Set(localizationConfiguration);
+```
+
+Extension usage will look like
+
+```csharp
+ domainConfiguration.ConfigureLocalizationExtension(configuration);
+```
+
+
+**Example #4** Configure using Microsoft.Extensions.Configuration API.
+
+This API allows to have configurations in various forms including JSON and XML formats.
+Loading of such files may differ depending on .NET version, check Microsoft manuals for instructions.
+
+Allowed JSON and XML configuration definition look like below
+
+```xml
+
+
+ es-ES
+
+
+```
+
+```json
+{
+ "Xtensive.Orm.Localization": {
+ "DefaultCulture": "es-ES"
+ }
+}
+```
+
+The API has certain issues with XML elements with attributes so it is recommended to use
+more up-to-date attributeless nodes.
+For JSON it is pretty clear, almost averyone knows its format.
+
+```LocalizationConfiguration.Load``` method can accept different types of abstractions from the API, including
+- ```Microsoft.Extensions.Configuration.IConfiguration```;
+- ```Microsoft.Extensions.Configuration.IConfigurationRoot```;
+- ```Microsoft.Extensions.Configuration.IConfigurationSection```.
+
+Loading of configuration may look like
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ // tries to load from default section "Xtensive.Orm.Localization"
+ var localizationConfig = LocalizationConfiguration.Load(app.Configuration);
+
+ domainConfiguration.ExtensionConfigurations.Set(localizationConfig);
+```
+
+or, with use of extension, like
+
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ // tries to load from default section "Xtensive.Orm.Localization"
+ // and additionally adds Xtensive.Orm.Localization assembly to domain types.
+
+ domainConfiguration.ConfigureLocalizationExtension(app.Configuration);
+```
+
+
+
+**Example #5** Configure using Microsoft.Extensions.Configuration API from section with non-default name.
+
+For configurations like
+
+```xml
+
+
+ es-ES
+
+
+```
+
+```json
+{
+ "Orm.Localization": {
+ "DefaultCulture": "es-ES"
+ }
+}
+```
+
+Loading of configuration may look like
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ var localizationConfig = LocalizationConfiguration.Load(app.Configuration, "Orm.Localization");
+
+ domainConfiguration.ExtensionConfigurations.Set(localizationConfig);
+```
+
+or, with use of extension, like
+
+```csharp
+ var app = builder.Build();
+ domainConfiguration.ConfigureLocalizationExtension(app.Configuration, "Orm.Localization");
+```
+
+
+**Example #6** Configure using Microsoft.Extensions.Configuration API from sub-section deeper in section tree.
+
+If for some reason extension configuration should be moved deeper in section tree like something below
+
+```xml
+
+
+
+ es-ES
+
+
+
+```
+
+or in JSON
+
+```json
+{
+ "Orm.Extensions": {
+ "Xtensive.Orm.Localization": {
+ "DefaultCulture": "es-ES"
+ }
+ }
+}
+```
+
+Then section must be provided manually, code may look like
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ var configurationRoot = app.Configuration;
+ var extensionsGroupSection = configurationRoot.GetSection("Orm.Extensions");
+ var localizationSection = extensionsGroupSection.GetSection("Xtensive.Orm.Localization");
+ var localizationConfig = LocalizationConfiguration.Load(localizationSection);
+
+ domainConfiguration.ExtensionConfigurations.Set(localizationConfig);
+```
+
+or, with use of extension method, like
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ var configurationRoot = app.Configuration;
+ var extensionsGroupSection = configurationRoot.GetSection("Orm.Extensions");
+ var localizationSection = extensionsGroupSection.GetSection("Xtensive.Orm.Localization");
+
+ domainConfiguration.ConfigureLocalizationExtension(localizationSection);
```
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Localization/Xtensive.Orm.Localization.csproj b/Extensions/Xtensive.Orm.Localization/Xtensive.Orm.Localization.csproj
index 404de113d2..318bb83b12 100644
--- a/Extensions/Xtensive.Orm.Localization/Xtensive.Orm.Localization.csproj
+++ b/Extensions/Xtensive.Orm.Localization/Xtensive.Orm.Localization.csproj
@@ -27,4 +27,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Logging.NLog/NugetContent/ReadMe.md b/Extensions/Xtensive.Orm.Logging.NLog/NugetContent/ReadMe.md
index 3fb46089a9..450daa9b68 100644
--- a/Extensions/Xtensive.Orm.Logging.NLog/NugetContent/ReadMe.md
+++ b/Extensions/Xtensive.Orm.Logging.NLog/NugetContent/ReadMe.md
@@ -1,4 +1,4 @@
-Xtensive.Orm.Logging.NLog
+Xtensive.Orm.Logging.NLog
=========================
Summary
@@ -8,7 +8,7 @@ The extension provides integration points between DataObjects.Net internal loggi
Prerequisites
-------------
-DataObjects.Net 7.0.x (http://dataobjects.net)
+DataObjects.Net 7.1.x (http://dataobjects.net)
NLog 4.5 or later (http://nlog-project.org)
Implementation
diff --git a/Extensions/Xtensive.Orm.Logging.log4net/NugetContent/ReadMe.md b/Extensions/Xtensive.Orm.Logging.log4net/NugetContent/ReadMe.md
index d5169400ed..664c522177 100644
--- a/Extensions/Xtensive.Orm.Logging.log4net/NugetContent/ReadMe.md
+++ b/Extensions/Xtensive.Orm.Logging.log4net/NugetContent/ReadMe.md
@@ -1,4 +1,4 @@
-Xtensive.Orm.Logging.log4net
+Xtensive.Orm.Logging.log4net
============================
Summary
@@ -7,7 +7,7 @@ The extension provides integration points between DataObjects.Net internal loggi
Prerequisites
-------------
-DataObjects.Net 7.0.x (http://dataobjects.net)
+DataObjects.Net 7.1.x (http://dataobjects.net)
log4net 2.0.10 or later (http://logging.apache.org/log4net/)
Implementation
diff --git a/Extensions/Xtensive.Orm.Reprocessing.Tests/App.config b/Extensions/Xtensive.Orm.Reprocessing.Tests/App.config
index cfe235c4cc..b088293b47 100644
--- a/Extensions/Xtensive.Orm.Reprocessing.Tests/App.config
+++ b/Extensions/Xtensive.Orm.Reprocessing.Tests/App.config
@@ -1,4 +1,4 @@
-
+
diff --git a/Extensions/Xtensive.Orm.Reprocessing.Tests/ReprocessingSettings.config b/Extensions/Xtensive.Orm.Reprocessing.Tests/ReprocessingSettings.config
new file mode 100644
index 0000000000..25567e878d
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Reprocessing.Tests/ReprocessingSettings.config
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Auto
+
+
+ New
+
+
+ Default
+
+
+
+
+
+
+
+ Xtensive.Orm.Reprocessing.HandleReprocessableExceptionStrategy, Xtensive.Orm.Reprocessing
+
+
+
+ Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing
+
+
+
+ Xtensive.Orm.Reprocessing.DummyStrategy, Xtensive.Orm.Reprocessing
+
+
+
+
+
+
+
+
+
+ Auto
+ Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing
+
+
+
+ Auto
+ Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing
+
+
+
+ Auto
+ Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing
+
+
+
+ Auto
+ Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing
+
+
+
+
+
+
+
+
+ Auto
+ Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing
+
+
+
+ Auto
+ Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing
+
+
+
+ Auto
+ Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing
+
+
+
+ Auto
+ Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Reprocessing.Tests/Tests/MicrosoftConfugurationTests.cs b/Extensions/Xtensive.Orm.Reprocessing.Tests/Tests/MicrosoftConfugurationTests.cs
new file mode 100644
index 0000000000..d6221b7018
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Reprocessing.Tests/Tests/MicrosoftConfugurationTests.cs
@@ -0,0 +1,346 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using System;
+using Microsoft.Extensions.Configuration;
+using NUnit.Framework;
+using Xtensive.Orm.Reprocessing.Configuration;
+
+namespace Xtensive.Orm.Reprocessing.Tests.Configuration
+{
+ public sealed class JsonConfigurationTest : MicrosoftConfigurationTest
+ {
+ protected override ConfigTypes ConfigFormat => ConfigTypes.Json;
+
+ protected override void AddConfigurationFile(IConfigurationBuilder configurationBuilder)
+ {
+ _ = configurationBuilder.AddJsonFile("reprocessingsettings.json");
+ }
+ }
+
+ public sealed class XmlConfigurationTest : MicrosoftConfigurationTest
+ {
+ protected override ConfigTypes ConfigFormat => ConfigTypes.Xml;
+
+ protected override void AddConfigurationFile(IConfigurationBuilder configurationBuilder)
+ {
+ _ = configurationBuilder.AddXmlFile("ReprocessingSettings.config");
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void EmptyNodesTest(bool useRoot)
+ {
+ var repConfig = LoadConfiguration("Xtensive.Orm.Reprocessing.Xml.EmptyNodes", useRoot);
+ CheckConfigIsDefault(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void EmptyValuesTest(bool useRoot)
+ {
+ var repConfig = LoadConfiguration("Xtensive.Orm.Reprocessing.Attributes.EmptyValues", useRoot);
+ CheckConfigIsDefault(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void EmptyTransactionModeOnlyTest(bool useRoot)
+ {
+ var repConfig = LoadConfiguration("Xtensive.Orm.Reprocessing.Attributes.EmptyTMOnly", useRoot);
+ CheckConfigIsDefault(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void EmptyStrategyOnlyTest(bool useRoot)
+ {
+ var repConfig = LoadConfiguration("Xtensive.Orm.Reprocessing.Attributes.EmptyStrategyOnly", useRoot);
+ CheckConfigIsDefault(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void AllAttributesHasValuesTest(bool useRoot)
+ {
+ var repConfig = LoadConfiguration("Xtensive.Orm.Reprocessing.Attributes.All", useRoot);
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.Auto));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleUniqueConstraintViolationStrategy)));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyTMAttributeHasValueTest(bool useRoot)
+ {
+ var repConfig = LoadConfiguration("Xtensive.Orm.Reprocessing.Attributes.OnlyTM", useRoot);
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.Auto));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleReprocessableExceptionStrategy)));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyStrategyAttributeHasValueTest(bool useRoot)
+ {
+ var repConfig = LoadConfiguration("Xtensive.Orm.Reprocessing.Attributes.OnlyStrategy", useRoot);
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.New));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleUniqueConstraintViolationStrategy)));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void AttributesInLowCase(bool useRoot)
+ {
+ var repConfig = LoadConfiguration("Xtensive.Orm.Reprocessing.Attributes.LC", useRoot);
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.Auto));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleUniqueConstraintViolationStrategy)));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void AttributesInUpperCase(bool useRoot)
+ {
+ var repConfig = LoadConfiguration("Xtensive.Orm.Reprocessing.Attributes.UC", useRoot);
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.Auto));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleUniqueConstraintViolationStrategy)));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void AttributesInPascalCase(bool useRoot)
+ {
+ var repConfig = LoadConfiguration("Xtensive.Orm.Reprocessing.Attributes.PC", useRoot);
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.Auto));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleUniqueConstraintViolationStrategy)));
+ }
+ }
+
+ public abstract class MicrosoftConfigurationTest : TestCommon.MicrosoftConfigurationTestBase
+ {
+ protected ReprocessingConfiguration LoadConfiguration(string sectionName, bool useRoot)
+ {
+ return useRoot
+ ? ReprocessingConfiguration.Load(configurationRoot, sectionName)
+ : ReprocessingConfiguration.Load(configurationRoot.GetSection(sectionName));
+ }
+
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void EmptySectionCase(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.Empty", useRoot);
+ CheckConfigIsDefault(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void EmptyNames(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.AllEmpty", useRoot);
+ CheckConfigIsDefault(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyTransactionOpenModeAndEmpty(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.OnlyTM.Empty", useRoot);
+ CheckConfigIsDefault(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyTransactionOpenModeAuto(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.OnlyTM.Auto", useRoot);
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.Auto));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleReprocessableExceptionStrategy)));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyTransactionOpenModeNew(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.OnlyTM.New", useRoot);
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.New));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleReprocessableExceptionStrategy)));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyTransactionOpenModeDefault(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.OnlyTM.Default", useRoot);
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.Default));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleReprocessableExceptionStrategy)));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyStrategyAndEmpty(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.OnlyStrategy.Empty", useRoot);
+ CheckConfigIsDefault(repConfig);
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.New));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleReprocessableExceptionStrategy)));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyStrategyHandleReprocessible(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.OnlyStrategy.HandleReprocessible", useRoot);
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.New));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleReprocessableExceptionStrategy)));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyStrategyHandleUnique(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.OnlyStrategy.HandleUnique", useRoot);
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.New));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleUniqueConstraintViolationStrategy)));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyStrategyNonExistent(bool useRoot)
+ {
+ _ = Assert.Throws(
+ () => LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.OnlyStrategy.NonExistent", useRoot));
+ }
+
+
+ #region Naming
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingInLowCase(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.Naming.LC", useRoot);
+ ValidateNamingConfigurationResults(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingInUpperCase(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.Naming.UC", useRoot);
+ ValidateNamingConfigurationResults(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingInCamelCase(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.Naming.CC", useRoot);
+ ValidateNamingConfigurationResults(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingInPascalCase(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.Naming.PC", useRoot);
+ ValidateNamingConfigurationResults(repConfig);
+ }
+
+ private static void ValidateNamingConfigurationResults(ReprocessingConfiguration repConfig)
+ {
+ Assert.That(repConfig, Is.Not.Null);
+ Assert.That(repConfig.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.Auto));
+ Assert.That(repConfig.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleUniqueConstraintViolationStrategy)));
+ }
+ #endregion
+
+ #region mistype cases
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MistypeInLowCase(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.Mistype.LC", useRoot);
+ ValidateMistypeConfigurationResults(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MistypeInUpperCase(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.Mistype.UC", useRoot);
+ ValidateMistypeConfigurationResults(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MistypeInCamelCase(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.Mistype.CC", useRoot);
+ ValidateMistypeConfigurationResults(repConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MistypeInPascalCase(bool useRoot)
+ {
+ var repConfig = LoadConfiguration($"Xtensive.Orm.Reprocessing.{ConfigFormat}.Mistype.PC", useRoot);
+ ValidateMistypeConfigurationResults(repConfig);
+ }
+
+ private static void ValidateMistypeConfigurationResults(ReprocessingConfiguration repConfig)
+ {
+ CheckConfigIsDefault(repConfig);
+ }
+
+ #endregion
+
+ protected static void CheckConfigIsDefault(ReprocessingConfiguration configuration)
+ {
+ Assert.That(configuration, Is.Not.Null);
+ Assert.That(configuration.DefaultTransactionOpenMode, Is.EqualTo(TransactionOpenMode.New));
+ Assert.That(configuration.DefaultExecuteStrategy, Is.EqualTo(typeof(HandleReprocessableExceptionStrategy)));
+ }
+ }
+}
diff --git a/Extensions/Xtensive.Orm.Reprocessing.Tests/Xtensive.Orm.Reprocessing.Tests.csproj b/Extensions/Xtensive.Orm.Reprocessing.Tests/Xtensive.Orm.Reprocessing.Tests.csproj
index 29421c8206..3c995ffcc5 100644
--- a/Extensions/Xtensive.Orm.Reprocessing.Tests/Xtensive.Orm.Reprocessing.Tests.csproj
+++ b/Extensions/Xtensive.Orm.Reprocessing.Tests/Xtensive.Orm.Reprocessing.Tests.csproj
@@ -8,6 +8,9 @@
+
+
+
@@ -20,4 +23,12 @@
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Reprocessing.Tests/reprocessingsettings.json b/Extensions/Xtensive.Orm.Reprocessing.Tests/reprocessingsettings.json
new file mode 100644
index 0000000000..9aa57971ef
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Reprocessing.Tests/reprocessingsettings.json
@@ -0,0 +1,72 @@
+{
+ "Xtensive.Orm.Reprocessing.Json.Empty": {
+
+ },
+
+ "Xtensive.Orm.Reprocessing.Json.AllEmpty": {
+ "DefaultTransactionOpenMode": "",
+ "DefaultExecuteStrategy": ""
+ },
+
+
+ "Xtensive.Orm.Reprocessing.Json.OnlyTM.Empty": {
+ "DefaultTransactionOpenMode": ""
+ },
+ "Xtensive.Orm.Reprocessing.Json.OnlyTM.Auto": {
+ "DefaultTransactionOpenMode": "Auto"
+ },
+ "Xtensive.Orm.Reprocessing.Json.OnlyTM.New": {
+ "DefaultTransactionOpenMode": "New"
+ },
+ "Xtensive.Orm.Reprocessing.Json.OnlyTM.Default": {
+ "DefaultTransactionOpenMode": "Default"
+ },
+
+ "Xtensive.Orm.Reprocessing.Json.OnlyStrategy.Empty": {
+ "DefaultExecuteStrategy": ""
+ },
+ "Xtensive.Orm.Reprocessing.Json.OnlyStrategy.HandleReprocessible": {
+ "DefaultExecuteStrategy": "Xtensive.Orm.Reprocessing.HandleReprocessableExceptionStrategy, Xtensive.Orm.Reprocessing"
+ },
+ "Xtensive.Orm.Reprocessing.Json.OnlyStrategy.HandleUnique": {
+ "DefaultExecuteStrategy": "Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing"
+ },
+ "Xtensive.Orm.Reprocessing.Json.OnlyStrategy.NonExistent": {
+ "DefaultExecuteStrategy": "Xtensive.Orm.Reprocessing.DummyStrategy, Xtensive.Orm.Reprocessing"
+ },
+
+
+ "Xtensive.Orm.Reprocessing.Json.Naming.LC": {
+ "defaulttransactionopenmode": "Auto",
+ "defaultexecutestrategy": "Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing"
+ },
+ "Xtensive.Orm.Reprocessing.Json.Naming.UC": {
+ "DEFAULTTRANSACTIONOPENMODE": "Auto",
+ "DEFAULTEXECUTESTRATEGY": "Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing"
+ },
+ "Xtensive.Orm.Reprocessing.Json.Naming.CC": {
+ "defaultTransactionOpenMode": "Auto",
+ "defaultExecuteStrategy": "Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing"
+ },
+ "Xtensive.Orm.Reprocessing.Json.Naming.PC": {
+ "DefaultTransactionOpenMode": "Auto",
+ "DefaultExecuteStrategy": "Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing"
+ },
+
+ "Xtensive.Orm.Reprocessing.Json.Mistype.LC": {
+ "defaultttransactionopenmode": "Auto",
+ "defaulttexecutestrategy": "Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing"
+ },
+ "Xtensive.Orm.Reprocessing.Json.Mistype.UC": {
+ "DEFAULTTTRANSACTIONOPENMODE": "Auto",
+ "DEFAULTTEXECUTESTRATEGY": "Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing"
+ },
+ "Xtensive.Orm.Reprocessing.Json.Mistype.CC": {
+ "defaultTtransactionOpenMode": "Auto",
+ "defaulttExecuteStrategy": "Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing"
+ },
+ "Xtensive.Orm.Reprocessing.Json.Mistype.PC": {
+ "DefaultTtransactionOpenMode": "Auto",
+ "DefaulttExecuteStrategy": "Xtensive.Orm.Reprocessing.HandleUniqueConstraintViolationStrategy, Xtensive.Orm.Reprocessing"
+ }
+}
diff --git a/Extensions/Xtensive.Orm.Reprocessing/Configuration/ConfigurationSection.cs b/Extensions/Xtensive.Orm.Reprocessing/Configuration/ConfigurationSection.cs
index d93d13802c..6b8d0d2718 100644
--- a/Extensions/Xtensive.Orm.Reprocessing/Configuration/ConfigurationSection.cs
+++ b/Extensions/Xtensive.Orm.Reprocessing/Configuration/ConfigurationSection.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
using System.Configuration;
@@ -11,8 +11,9 @@ public class ConfigurationSection : System.Configuration.ConfigurationSection
{
///
/// Gets default section name for reprocessing configuration.
- /// Value is "Xtensive.Reprocessing".
+ /// Value is "Xtensive.Orm.Reprocessing".
///
+ [Obsolete("Use ReprocessingConfiguration.DefaultSectionName instead")]
public static readonly string DefaultSectionName = "Xtensive.Orm.Reprocessing";
///
diff --git a/Extensions/Xtensive.Orm.Reprocessing/Configuration/ReprocessingConfiguration.cs b/Extensions/Xtensive.Orm.Reprocessing/Configuration/ReprocessingConfiguration.cs
index 12b6ec5612..6e7adbc458 100644
--- a/Extensions/Xtensive.Orm.Reprocessing/Configuration/ReprocessingConfiguration.cs
+++ b/Extensions/Xtensive.Orm.Reprocessing/Configuration/ReprocessingConfiguration.cs
@@ -1,14 +1,22 @@
-using System;
+using System;
using System.Configuration;
+using Microsoft.Extensions.Configuration;
using Xtensive.Core;
+using Xtensive.Orm.Configuration;
namespace Xtensive.Orm.Reprocessing.Configuration
{
///
/// The configuration of the reprocessing system.
///
- public class ReprocessingConfiguration
+ public class ReprocessingConfiguration : ConfigurationBase
{
+ ///
+ /// Gets default section name for reprocessing configuration.
+ /// Value is "Xtensive.Orm.Reprocessing".
+ ///
+ public static readonly string DefaultSectionName = "Xtensive.Orm.Reprocessing";
+
///
/// Gets default value of the property.
///
@@ -19,15 +27,46 @@ public class ReprocessingConfiguration
///
public static readonly Type DefaultDefaultExecuteStrategy = typeof (HandleReprocessableExceptionStrategy);
+ private TransactionOpenMode defaultTransactionOpenMode;
+ private Type defaultExecuteStrategy;
+
///
/// Gets or sets default value of the parameter.
///
- public TransactionOpenMode DefaultTransactionOpenMode { get; set; }
+ public TransactionOpenMode DefaultTransactionOpenMode {
+ get => defaultTransactionOpenMode;
+ set {
+ EnsureNotLocked();
+ defaultTransactionOpenMode = value;
+ }
+ }
///
/// Gets or sets default value of the parameter.
///
- public Type DefaultExecuteStrategy { get; set; }
+ public Type DefaultExecuteStrategy {
+ get => defaultExecuteStrategy;
+ set {
+ EnsureNotLocked();
+ defaultExecuteStrategy = value;
+ }
+ }
+
+ ///
+ protected override ReprocessingConfiguration CreateClone() => new ReprocessingConfiguration();
+
+ ///
+ protected override void CopyFrom(ConfigurationBase source)
+ {
+ base.CopyFrom(source);
+
+ var configuration = (ReprocessingConfiguration) source;
+ configuration.DefaultTransactionOpenMode = configuration.DefaultTransactionOpenMode;
+ configuration.DefaultExecuteStrategy = configuration.DefaultExecuteStrategy;
+ }
+
+ ///
+ public override ReprocessingConfiguration Clone() => (ReprocessingConfiguration) base.Clone();
///
/// Loads the reprocessing configuration from default section in application configuration file.
@@ -35,7 +74,7 @@ public class ReprocessingConfiguration
/// The reprocessing configuration.
public static ReprocessingConfiguration Load()
{
- return Load(ConfigurationSection.DefaultSectionName);
+ return Load(DefaultSectionName);
}
///
@@ -56,7 +95,7 @@ public static ReprocessingConfiguration Load(string sectionName)
/// The reprocessing configuration.
public static ReprocessingConfiguration Load(System.Configuration.Configuration configuration)
{
- return Load(configuration, ConfigurationSection.DefaultSectionName);
+ return Load(configuration, DefaultSectionName);
}
///
@@ -81,6 +120,52 @@ private static ReprocessingConfiguration GetConfigurationFromSection(Configurati
};
}
+ ///
+ /// Loads the from specified section of configuration root.
+ ///
+ /// to load section from.
+ /// Name of the section where configuration is stored. Not applied if
+ /// Loaded configuration or configuration with default settings.
+ public static ReprocessingConfiguration Load(IConfiguration configuration, string sectionName = null)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configuration, nameof(configuration));
+
+ if (configuration is IConfigurationRoot configurationRoot) {
+ return new ReprocessingConfigurationReader().Read(configurationRoot, sectionName ?? DefaultSectionName);
+ }
+ else if (configuration is IConfigurationSection configurationSection) {
+ return new ReprocessingConfigurationReader().Read(configurationSection);
+ }
+
+ throw new NotSupportedException("Type of configuration is not supported.");
+ }
+
+
+ ///
+ /// Loads the from specified section of configuration root.
+ ///
+ /// to load section from.
+ /// Name of the section where configuration is stored.
+ /// Loaded configuration or configuration with default settings.
+ public static ReprocessingConfiguration Load(IConfigurationRoot configurationRoot, string sectionName = null)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationRoot, nameof(configurationRoot));
+
+ return new ReprocessingConfigurationReader().Read(configurationRoot, sectionName ?? DefaultSectionName);
+ }
+
+ ///
+ /// Loads the from given configuration section.
+ ///
+ /// to load from.
+ /// Loaded configuration or configuration with default settings.
+ public static ReprocessingConfiguration Load(IConfigurationSection configurationSection)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationSection, nameof(configurationSection));
+
+ return new ReprocessingConfigurationReader().Read(configurationSection);
+ }
+
///
/// Initializes a new instance of the class.
///
diff --git a/Extensions/Xtensive.Orm.Reprocessing/Configuration/ReprocessingConfigurationReader.cs b/Extensions/Xtensive.Orm.Reprocessing/Configuration/ReprocessingConfigurationReader.cs
new file mode 100644
index 0000000000..36a553b34d
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Reprocessing/Configuration/ReprocessingConfigurationReader.cs
@@ -0,0 +1,65 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using System;
+using Microsoft.Extensions.Configuration;
+using Xtensive.Orm.Configuration;
+
+namespace Xtensive.Orm.Reprocessing.Configuration
+{
+ internal sealed class ReprocessingConfigurationReader : IConfigurationSectionReader
+ {
+ // intermediate class for reading section
+ private class ReprocessingOptions
+ {
+ public TransactionOpenMode? DefaultTransactionOpenMode { get; set; }
+
+ public string DefaultExecuteStrategy { get; set; }
+ }
+
+ public ReprocessingConfiguration Read(IConfigurationSection configurationSection) => ReadInternal(configurationSection);
+
+ public ReprocessingConfiguration Read(IConfigurationSection configurationSection, string nameOfConfiguration) =>
+ throw new NotSupportedException();
+
+ public ReprocessingConfiguration Read(IConfigurationRoot configurationRoot) =>
+ Read(configurationRoot, ReprocessingConfiguration.DefaultSectionName);
+
+ public ReprocessingConfiguration Read(IConfigurationRoot configurationRoot, string sectionName)
+ {
+ var section = configurationRoot.GetSection(sectionName);
+ return ReadInternal(section);
+ }
+
+ public ReprocessingConfiguration Read(IConfigurationRoot configurationRoot, string sectionName, string nameOfConfiguration) =>
+ throw new NotSupportedException();
+
+ private ReprocessingConfiguration ReadInternal(IConfigurationSection section)
+ {
+ var reprocessingOptions = section.Get();
+
+ if (reprocessingOptions == default) {
+ return new ReprocessingConfiguration();
+ }
+
+ if (reprocessingOptions.DefaultTransactionOpenMode == default
+ && reprocessingOptions.DefaultExecuteStrategy == default) {
+ // that means instance is default. probably invalid
+ return new ReprocessingConfiguration();
+ }
+
+ var result = new ReprocessingConfiguration();
+ if (reprocessingOptions.DefaultTransactionOpenMode != default) {
+ result.DefaultTransactionOpenMode = reprocessingOptions.DefaultTransactionOpenMode.Value;
+ }
+ if (!string.IsNullOrEmpty(reprocessingOptions.DefaultExecuteStrategy)) {
+ var type = Type.GetType(reprocessingOptions.DefaultExecuteStrategy, false);
+ if (type == null)
+ throw new InvalidOperationException($"Can't resolve type '{reprocessingOptions.DefaultExecuteStrategy}'. Note that DefaultExecuteStrategy value should be in form of Assembly Qualified Name");
+ result.DefaultExecuteStrategy = type;
+ }
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Reprocessing/DomainConfigurationExtensions.cs b/Extensions/Xtensive.Orm.Reprocessing/DomainConfigurationExtensions.cs
new file mode 100644
index 0000000000..ca17256a8c
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Reprocessing/DomainConfigurationExtensions.cs
@@ -0,0 +1,108 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using Microsoft.Extensions.Configuration;
+using Xtensive.Orm.Configuration;
+using Xtensive.Orm.Reprocessing.Configuration;
+
+namespace Xtensive.Orm.Reprocessing
+{
+ ///
+ /// Contains extensions for DomainConfiguration that help to configure the extension.
+ ///
+ public static class DomainConfigurationReprocessingExtensions
+ {
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureReprocessingExtension(this DomainConfiguration domainConfiguration) =>
+ ConfigureReprocessingExtension(domainConfiguration, ReprocessingConfiguration.Load());
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Section name.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureReprocessingExtension(this DomainConfiguration domainConfiguration,
+ string configurationSectionName) =>
+ ConfigureReprocessingExtension(domainConfiguration, ReprocessingConfiguration.Load(configurationSectionName));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration to load from.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureReprocessingExtension(this DomainConfiguration domainConfiguration,
+ System.Configuration.Configuration configuration) =>
+ ConfigureReprocessingExtension(domainConfiguration, ReprocessingConfiguration.Load(configuration));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration to load from.
+ /// Section in
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureReprocessingExtension(this DomainConfiguration domainConfiguration,
+ System.Configuration.Configuration configuration, string sectionName) =>
+ ConfigureReprocessingExtension(domainConfiguration, ReprocessingConfiguration.Load(configuration, sectionName));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration to load from.
+ /// Section in
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureReprocessingExtension(this DomainConfiguration domainConfiguration,
+ IConfiguration configuration, string sectionName = null) =>
+ ConfigureReprocessingExtension(domainConfiguration, ReprocessingConfiguration.Load(configuration, sectionName));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration to load from.
+ /// Section in
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureReprocessingExtension(this DomainConfiguration domainConfiguration,
+ IConfigurationRoot configurationRoot, string sectionName = null) =>
+ ConfigureReprocessingExtension(domainConfiguration, ReprocessingConfiguration.Load(configurationRoot, sectionName));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration section to load from.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureReprocessingExtension(this DomainConfiguration domainConfiguration,
+ IConfigurationSection configurationSection) =>
+ ConfigureReprocessingExtension(domainConfiguration, ReprocessingConfiguration.Load(configurationSection));
+
+ ///
+ /// Configures the extension with given reprocessing configuration instance.
+ ///
+ /// Domain configuration.
+ /// Security configuration instance.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureReprocessingExtension(this DomainConfiguration domainConfiguration,
+ ReprocessingConfiguration reprocessingConfiguration)
+ {
+ domainConfiguration.ExtensionConfigurations.Set(reprocessingConfiguration);
+ domainConfiguration.Types.Register(typeof(DomainConfigurationReprocessingExtensions).Assembly);
+ return domainConfiguration;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Reprocessing/DomainExtensions.cs b/Extensions/Xtensive.Orm.Reprocessing/DomainExtensions.cs
index 5e50e81184..dc14da20a0 100644
--- a/Extensions/Xtensive.Orm.Reprocessing/DomainExtensions.cs
+++ b/Extensions/Xtensive.Orm.Reprocessing/DomainExtensions.cs
@@ -1,5 +1,6 @@
using System;
using System.Transactions;
+using Xtensive.Orm.Configuration;
using Xtensive.Orm.Reprocessing.Configuration;
namespace Xtensive.Orm.Reprocessing
@@ -190,7 +191,8 @@ internal static T ExecuteInternal(
IExecuteActionStrategy strategy,
Func func)
{
- var config = domain.GetReprocessingConfiguration();
+ var config = domain.Configuration.ExtensionConfigurations.Get()
+ ?? domain.GetReprocessingConfiguration();
if (strategy == null) {
strategy = ExecuteActionStrategy.GetSingleton(config.DefaultExecuteStrategy);
}
@@ -208,7 +210,8 @@ internal static T ExecuteInternal(
IExecuteActionStrategy strategy,
Func func)
{
- var config = domain.GetReprocessingConfiguration();
+ var config = domain.Configuration.ExtensionConfigurations.Get()
+ ?? domain.GetReprocessingConfiguration();
if (strategy == null) {
strategy = ExecuteActionStrategy.GetSingleton(config.DefaultExecuteStrategy);
}
diff --git a/Extensions/Xtensive.Orm.Reprocessing/NugetContent/ReadMe.md b/Extensions/Xtensive.Orm.Reprocessing/NugetContent/ReadMe.md
index 8129b98f32..678a948360 100644
--- a/Extensions/Xtensive.Orm.Reprocessing/NugetContent/ReadMe.md
+++ b/Extensions/Xtensive.Orm.Reprocessing/NugetContent/ReadMe.md
@@ -8,7 +8,7 @@ should represent a separate block of logic, usually a delegate of a method and b
Prerequisites
-------------
-DataObjects.Net 7.0.x (http://dataobjects.net)
+DataObjects.Net 7.1.x (http://dataobjects.net)
Examples of usage
-----------------
@@ -40,10 +40,16 @@ To indicate that a particular strategy should be used, use the following syntax:
});
```
-**Expample #3**. Confugure reprocessing in configuration file. To omit setting up the strategy each time consider configuring it in
-application configuration file, e.g.:
+
+Examples of how to configure extension
+--------------------------------------
+
+Following examples show different ways to configure extension in configuration files of various types.
+
+**Example #1** Confugure in App.config/Web.config
```xml
+
+```
+
+Such configuration is usually read with ```System.Configuration.ConfigurationManager```.
+If project still supports such configurations then Reprocessing configuration will be read automatically when it needs to be read.
+Sometimes a work-around is needed to read such configuration, for more read Example #2 and Example #3
+
+
+**Example #2** Reading old-style configuration of an assembly in NET 5 and newer.
+
+Due to new architecture without AppDomain (which among the other things was in charge of gathering configuration files of loaded assemblies
+as it would be one configuration file) ```System.Configuration.ConfigurationManager``` now reads only configuration file of actual executable, loaded
+assemblies' configuration files stay unreachable by default, though there is need to read some data from them.
+A great example is test projects which are usually get loaded by test runner executable, and the only configuration accessible in this case
+is test runner one.
+
+Extra step is required to read configuration files in such cases. Thankfully, ```ConfigurationManager``` has methods to get access to assemblies' configuration files.
+
+To get access to an assembly configuration file it should be opened explicitly by
+
+```csharp
+ var configuration = ConfigurationManager.OpenExeConfiguration(typeof(SomeTypeInConfigOwnerAssembly).Assembly.Location);
+```
+
+The instance returned from ```OpenExeConfiguration``` provides access to sections of the assembly configuration. DataObjects.Net configurations
+(```DomainConfiguration```, ```ReprocessingConfiguration```, etc.) have ```Load()``` methods that can recieve this instance.
+```ReprocessingConfiguration``` can be read like so
+
+```csharp
+ var configuration = ConfigurationManager.OpenExeConfiguration(typeof(SomeTypeInConfigOwnerAssembly).Assembly.Location);
+ var reprocessingConfig = ReprocessingConfiguration.Load(configuration);
+
+ // loaded configuration should be manually placed to
+ domainConfiguration.ExtensionConfigurations.Set(reprocessingConfig);
+```
+
+The ```domainConfiguration.ExtensionConfigurations``` is a new unified place from which the extension will try to get its configuration
+instead of calling default parameterless ```Load()``` method, which has not a lot of sense now, though the method is kept as a second source
+for backwards compatibility.
+
+For more convenience, ```DomainConfiguration``` extensions are provided, which make code more neater.
+For instance,
+
+```csharp
+ var configuration = ConfigurationManager.OpenExeConfiguration(typeof(SomeTypeInConfigOwnerAssembly).Assembly.Location);
+
+ // the extension hides getting configuration with ReprocessingConfiguration.Load(configuration)
+ // and also putting it to ExtensionConfigurations collection.
+ domainConfiguration.ConfigureReprocessingExtension(configuration);
+```
+
+Custom section names are also supported if for some reason default section name is not used.
+
+
+**Example #3** Reading old-style configuration of an assembly in a project that uses appsettings.json file.
+
+If for some reason there is need to keep the old-style configuration then there is a work-around as well.
+Static configuration manager provides method ```OpenMappedExeConfiguration()``` which allows to get
+any *.config file as ```System.Configuration.Configuration``` instance. For example,
+
+```csharp
+ ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
+ configFileMap.ExeConfigFilename = "Orm.config"; //or other file name, the file should exist bin folder
+ var configuration = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
+```
+
+After that, as in previous example, the instance can be passed to ```Load``` method of ```ReprocessingConfiguration``` to read extension configuration
+and later put it to ```DomainConfiguration.ExtensionConfigurations```
+
+```csharp
+ var reprocessingConfiguration = ReprocessingConfiguration.Load(configuration);
+
+ domainConfiguration.ExtensionConfigurations.Set(reprocessingConfiguration);
+```
+
+Extension usage will look like
+
+```csharp
+ domainConfiguration.ConfigureReprocessingExtension(configuration);
+```
+
+
+**Example #4** Configure using Microsoft.Extensions.Configuration API.
+
+This API allows to have configurations in various forms including JSON and XML formats.
+Loading of such files may differ depending on .NET version, check Microsoft manuals for instructions.
+
+Allowed JSON and XML configuration definition look like below
+
+```xml
+
+
+ New
+ Xtensive.Orm.Reprocessing.HandleReprocessableExceptionStrategy, Xtensive.Orm.Reprocessing
+
+
+```
+
+```json
+{
+ "Xtensive.Orm.Reprocessing": {
+ "DefaultTransactionOpenMode" : "New",
+ "DefaultExecuteStrategy" : "Xtensive.Orm.Reprocessing.HandleReprocessableExceptionStrategy, Xtensive.Orm.Reprocessing"
+ }
+}
```
-Having that done, in scenarios with no strategy specified, the extension will automatically use
-the strategy from the configuration.
\ No newline at end of file
+The API has certain issues with Xml elements with attributes so it is recommended to use
+more up-to-date attributeless nodes.
+For JSON it is pretty clear, almost averyone knows its format.
+
+```ReprocessingConfiguration.Load``` method can accept different types of abstractions from the API, including
+- ```Microsoft.Extensions.Configuration.IConfiguration```;
+- ```Microsoft.Extensions.Configuration.IConfigurationRoot```;
+- ```Microsoft.Extensions.Configuration.IConfigurationSection```.
+
+Loading of configuration may look like
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ // tries to load from default section "Xtensive.Orm.Reprocessing"
+ var reprocessingConfig = ReprocessingConfiguration.Load(app.Configuration);
+
+ domainConfiguration.ExtensionConfigurations.Set(reprocessingConfig);
+```
+
+or, with use of extension, like
+
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ // tries to load from default section "Xtensive.Orm.Reprocessing"
+ // and put it into domainConfiguration.ExtensionConfigurations
+
+ domainConfiguration.ConfigureReprocessingExtension(app.Configuration);
+```
+
+
+
+**Example #5** Configure using Microsoft.Extensions.Configuration API from section with non-default name.
+
+For configurations like
+
+```xml
+
+
+ New
+ Xtensive.Orm.Reprocessing.HandleReprocessableExceptionStrategy, Xtensive.Orm.Reprocessing
+
+
+```
+
+```json
+{
+ "Orm.Reprocessing": {
+ "DefaultTransactionOpenMode" : "New",
+ "DefaultExecuteStrategy" : "Xtensive.Orm.Reprocessing.HandleReprocessableExceptionStrategy, Xtensive.Orm.Reprocessing"
+ }
+}
+```
+
+Loading of configuration may look like
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ var reprocessingConfig = ReprocessingConfiguration.Load(app.Configuration, "Orm.Reprocessing");
+
+ domainConfiguration.ExtensionConfigurations.Set(reprocessingConfig);
+```
+
+or, with use of extension, like
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ domainConfiguration.ConfigureReprocessingExtension(app.Configuration, "Orm.Reprocessing");
+```
+
+
+**Example #6** Configure using Microsoft.Extensions.Configuration API from sub-section deeper in section tree.
+
+If for some reason extension configuration should be moved deeper in section tree like something below
+
+```xml
+
+
+
+ New
+ Xtensive.Orm.Reprocessing.HandleReprocessableExceptionStrategy, Xtensive.Orm.Reprocessing
+
+
+
+```
+
+or in JSON
+
+```json
+{
+ "Orm.Extensions": {
+ "Xtensive.Orm.Reprocessing": {
+ "DefaultTransactionOpenMode" : "New",
+ "DefaultExecuteStrategy" : "Xtensive.Orm.Reprocessing.HandleReprocessableExceptionStrategy, Xtensive.Orm.Reprocessing"
+ }
+ }
+}
+```
+
+Then section must be provided manually, code may look like
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ var configurationRoot = app.Configuration;
+ var extensionsGroupSection = configurationRoot.GetSection("Orm.Extensions");
+ var reprocessingSection = extensionsGroupSection.GetSection("Xtensive.Orm.Reprocessing");
+
+ var reprocessingConfig = ReprocessingConfiguration.Load(reprocessingSection);
+
+ domainConfiguration.ExtensionConfigurations.Set(reprocessingConfig);
+```
+
+or, with use of extension method, like
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ var configurationRoot = app.Configuration;
+ var extensionsGroupSection = configurationRoot.GetSection("Orm.Extensions");
+ var reprocessingSection = extensionsGroupSection.GetSection("Xtensive.Orm.Reprocessing");
+
+ domainConfiguration.ConfigureReprocessingExtension(reprocessingSection);
+```
diff --git a/Extensions/Xtensive.Orm.Reprocessing/Xtensive.Orm.Reprocessing.csproj b/Extensions/Xtensive.Orm.Reprocessing/Xtensive.Orm.Reprocessing.csproj
index 99a2631fc2..dcc997498e 100644
--- a/Extensions/Xtensive.Orm.Reprocessing/Xtensive.Orm.Reprocessing.csproj
+++ b/Extensions/Xtensive.Orm.Reprocessing/Xtensive.Orm.Reprocessing.csproj
@@ -21,6 +21,10 @@
+
+
+
+
.
diff --git a/Extensions/Xtensive.Orm.Security.Tests/App.config b/Extensions/Xtensive.Orm.Security.Tests/App.config
index 5d3d3d31f0..8e2061897b 100644
--- a/Extensions/Xtensive.Orm.Security.Tests/App.config
+++ b/Extensions/Xtensive.Orm.Security.Tests/App.config
@@ -1,9 +1,10 @@
-
+
+
@@ -16,7 +17,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Security.Tests/SecuritySettings.config b/Extensions/Xtensive.Orm.Security.Tests/SecuritySettings.config
new file mode 100644
index 0000000000..e5ec7efdad
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Security.Tests/SecuritySettings.config
@@ -0,0 +1,225 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sha1
+
+
+ sha256
+
+
+ sha384
+
+
+ sha512
+
+
+ md5
+
+
+
+
+
+
+
+ NotDefault
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sha1
+ NotDefault
+
+
+
+ sha1
+ NotDefault
+
+
+
+ sha1
+ NotDefault
+
+
+
+ sha1
+ NotDefault
+
+
+
+
+
+
+
+
+ sha1
+ NotDefault
+
+
+
+ sha1
+ NotDefault
+
+
+
+ sha1
+ NotDefault
+
+
+
+ sha1
+ NotDefault
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sha1
+
+
+
+
+ sha256
+
+
+
+
+ sha384
+
+
+
+
+ sha512
+
+
+
+
+ md5
+
+
+
+
+
+
+
+
+
+
+ NotDefault
+
+
+
+
+
+
+
+
+
+
+ sha1
+
+
+ NotDefault
+
+
+
+
+
+ sha1
+
+
+ NotDefault
+
+
+
+
+
+ sha1
+
+
+ NotDefault
+
+
+
+
+
+ sha1
+
+
+ NotDefault
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Security.Tests/Tests/ConfigurationTests.cs b/Extensions/Xtensive.Orm.Security.Tests/Tests/ConfigurationTests.cs
index 885724895a..acb56731ae 100644
--- a/Extensions/Xtensive.Orm.Security.Tests/Tests/ConfigurationTests.cs
+++ b/Extensions/Xtensive.Orm.Security.Tests/Tests/ConfigurationTests.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2021 Xtensive LLC.
+// Copyright (C) 2011-2024 Xtensive LLC.
// This code is distributed under MIT license terms.
// See the License.txt file in the project root for more information.
// Created by: Dmitri Maximov
@@ -17,7 +17,7 @@ public class ConfigurationTests : HasConfigurationAccessTest
[Test]
public void HashingServiceNameTest()
{
- var section = (Configuration.ConfigurationSection) Configuration.GetSection("Xtensive.Orm.Security.WithName");
+ var section = (ConfigurationSection) Configuration.GetSection("Xtensive.Orm.Security.WithName");
Assert.That(section, Is.Not.Null);
Assert.That(section.HashingService, Is.Not.Null);
Assert.That(section.HashingService.Name, Is.Not.Null);
@@ -28,10 +28,27 @@ public void HashingServiceNameTest()
Assert.That(config.HashingServiceName, Is.EqualTo("md5"));
}
+ [Test]
+ public void HashingServiceAndAuthenticationServiceNameTest()
+ {
+ var section = (ConfigurationSection) Configuration.GetSection("Xtensive.Orm.Security.AllDeclared");
+ Assert.That(section, Is.Not.Null);
+ Assert.That(section.HashingService, Is.Not.Null);
+ Assert.That(section.HashingService.Name, Is.Not.Null);
+ Assert.That(section.HashingService.Name, Is.EqualTo("sha1"));
+ Assert.That(section.AuthenticationService.Name, Is.Not.Null);
+ Assert.That(section.AuthenticationService.Name, Is.EqualTo("notdefault"));
+
+ var config = SecurityConfiguration.Load(Configuration, "Xtensive.Orm.Security.AllDeclared");
+ Assert.That(config, Is.Not.Null);
+ Assert.That(config.HashingServiceName, Is.EqualTo("sha1"));
+ Assert.That(config.AuthenticationServiceName, Is.EqualTo("notdefault"));
+ }
+
[Test]
public void HashingServiceEmptyTest()
{
- var section = (Configuration.ConfigurationSection) Configuration.GetSection("Xtensive.Orm.Security.WithoutName");
+ var section = (ConfigurationSection) Configuration.GetSection("Xtensive.Orm.Security.WithoutName");
Assert.That(section, Is.Not.Null);
Assert.That(section.HashingService, Is.Not.Null);
Assert.That(section.HashingService.Name, Is.Null.Or.Empty);
@@ -44,7 +61,7 @@ public void HashingServiceEmptyTest()
[Test]
public void HashingServiceAbsentTest()
{
- var section = (Configuration.ConfigurationSection) Configuration.GetSection("Xtensive.Orm.Security.Empty");
+ var section = (ConfigurationSection) Configuration.GetSection("Xtensive.Orm.Security.Empty");
Assert.That(section, Is.Not.Null);
Assert.That(section.HashingService, Is.Not.Null);
Assert.That(section.HashingService.Name, Is.Null.Or.Empty);
@@ -57,7 +74,7 @@ public void HashingServiceAbsentTest()
[Test]
public void HashingServiceNoConfigTest()
{
- var section = (Configuration.ConfigurationSection) Configuration.GetSection("Xtensive.Orm.Security.XXX");
+ var section = (ConfigurationSection) Configuration.GetSection("Xtensive.Orm.Security.XXX");
Assert.That(section, Is.Null);
var config = SecurityConfiguration.Load(Configuration, "Xtensive.Orm.Security.XXX");
@@ -68,7 +85,7 @@ public void HashingServiceNoConfigTest()
[Test]
public void HashingServiceDefaultTest()
{
- var section = (Configuration.ConfigurationSection) Configuration.GetSection("Xtensive.Orm.Security");
+ var section = (ConfigurationSection) Configuration.GetSection("Xtensive.Orm.Security");
Assert.That(section, Is.Not.Null);
Assert.That(section.HashingService, Is.Not.Null);
Assert.That(section.HashingService.Name, Is.Not.Null.Or.Empty);
diff --git a/Extensions/Xtensive.Orm.Security.Tests/Tests/MicrosoftConfigurationTests.cs b/Extensions/Xtensive.Orm.Security.Tests/Tests/MicrosoftConfigurationTests.cs
new file mode 100644
index 0000000000..2a1fe93dd1
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Security.Tests/Tests/MicrosoftConfigurationTests.cs
@@ -0,0 +1,480 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using NUnit.Framework;
+using Microsoft.Extensions.Configuration;
+using Xtensive.Orm.Security.Configuration;
+
+namespace Xtensive.Orm.Security.Tests.Configuration
+{
+ public sealed class JsonConfigurationTests : MicrosoftConfigurationTests
+ {
+ protected override ConfigTypes ConfigFormat => ConfigTypes.Json;
+
+ protected override void AddConfigurationFile(IConfigurationBuilder configurationBuilder)
+ {
+ _ = configurationBuilder.AddJsonFile("securitysettings.json");
+ }
+ }
+
+ public sealed class XmlConfigurationTests : MicrosoftConfigurationTests
+ {
+ protected override ConfigTypes ConfigFormat => ConfigTypes.Xml;
+
+ protected override void AddConfigurationFile(IConfigurationBuilder configurationBuilder)
+ {
+ _ = configurationBuilder.AddXmlFile("SecuritySettings.config");
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeEmptyNamesTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration("Xtensive.Orm.Security.NameAttribute.NamesEmpty", useRoot);
+ CheckConfigurationIsDefault(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeEmptyAndNonExistentNameTest1(bool useRoot)
+ {
+ var secConfig = LoadConfiguration("Xtensive.Orm.Security.NameAttribute.NameExistPartially1", useRoot);
+ CheckConfigurationIsDefault(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeEmptyAndNonExistentNameTest2(bool useRoot)
+ {
+ var secConfig = LoadConfiguration("Xtensive.Orm.Security.NameAttribute.NameExistPartially2", useRoot);
+ CheckConfigurationIsDefault(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeAllNamesTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration("Xtensive.Orm.Security.NameAttribute.AllNames", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha1"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("notdefault"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeOnlyHashingServiceTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration("Xtensive.Orm.Security.NameAttribute.OnlyHashing", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha1"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("default"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeOnlyAuthServiceTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration("Xtensive.Orm.Security.NameAttribute.OnlyAuth", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("plain"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("notdefault"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeLowCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration("Xtensive.Orm.Security.NameAttribute.LC", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha1"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("notdefault"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributeUpperCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration("Xtensive.Orm.Security.NameAttribute.UC", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha1"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("notdefault"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameAttributePascalCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration("Xtensive.Orm.Security.NameAttribute.PC", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha1"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("notdefault"));
+ }
+ }
+
+
+ [TestFixture]
+ public abstract class MicrosoftConfigurationTests : TestCommon.MicrosoftConfigurationTestBase
+ {
+ protected SecurityConfiguration LoadConfiguration(string sectionName, bool useRoot)
+ {
+ return useRoot
+ ? SecurityConfiguration.Load(configurationRoot, sectionName)
+ : SecurityConfiguration.Load(configurationRoot.GetSection(sectionName));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void EmptySectionCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.Empty", useRoot);
+ CheckConfigurationIsDefault(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void EmptyNamesTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.AllEmpty", useRoot);
+ CheckConfigurationIsDefault(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyHashingServiceEmptyTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.OnlyHashing.Empty", useRoot);
+ CheckConfigurationIsDefault(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyHashingServiceMd5Test(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.OnlyHashing.Md5", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("md5"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("default"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyHashingServiceSha1Test(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.OnlyHashing.Sha1", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha1"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("default"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyHashingServiceSha256Test(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.OnlyHashing.Sha256", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha256"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("default"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyHashingServiceSha384Test(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.OnlyHashing.Sha384", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha384"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("default"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyHashingServiceSha512Test(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.OnlyHashing.Sha512", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha512"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("default"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyAuthenticationServiceEmptyNameTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.OnlyAuth.Empty", useRoot);
+ CheckConfigurationIsDefault(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyAuthenticationServiceNotDefaultTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.OnlyAuth", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("plain"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("notdefault"));
+ }
+
+ #region Naming
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingInLowCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.Naming.LC", useRoot);
+ ValidateNamingConfigurationResults(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingInUpperCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.Naming.UC", useRoot);
+ ValidateNamingConfigurationResults(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingInCamelCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.Naming.CC", useRoot);
+ ValidateNamingConfigurationResults(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingInPascalCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.Naming.PC", useRoot);
+ ValidateNamingConfigurationResults(secConfig);
+ }
+
+ private static void ValidateNamingConfigurationResults(SecurityConfiguration secConfig)
+ {
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha1"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("notdefault"));
+ }
+
+ #endregion
+
+ #region mistype cases
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MistypeInLowCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.Mistype.LC", useRoot);
+ ValidateMistypeConfigurationResults(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MistypeInUpperCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.Mistype.UC", useRoot);
+ ValidateMistypeConfigurationResults(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MistypeInCamelCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.Mistype.CC", useRoot);
+ ValidateMistypeConfigurationResults(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MistypeInPascalCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.Mistype.PC", useRoot);
+ ValidateMistypeConfigurationResults(secConfig);
+ }
+
+ private static void ValidateMistypeConfigurationResults(SecurityConfiguration secConfig)
+ {
+ CheckConfigurationIsDefault(secConfig);
+ }
+
+ #endregion
+
+ #region Name as node
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NoNameNodesTest(bool useRoot)
+ {
+ IgnoreIfXml();
+
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.Json.NameNode.AllEmpty", useRoot);
+ CheckConfigurationIsDefault(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameNodesAreEmptyTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.Json.NameNode.NamesEmpty", useRoot);
+ CheckConfigurationIsDefault(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyHashingServiceNameIsEmptyTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.NameNode.OnlyHashing.Empty", useRoot);
+ CheckConfigurationIsDefault(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyHashingServiceWithNameNodeMd5Test(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.NameNode.OnlyHashing.Md5", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("md5"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("default"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyHashingServiceWithNameNodeSha1Test(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.NameNode.OnlyHashing.Sha1", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha1"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("default"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyHashingServiceWithNameNodeSha256Test(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.NameNode.OnlyHashing.Sha256", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha256"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("default"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyHashingServiceWithNameNodeSha384Test(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.NameNode.OnlyHashing.Sha384", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha384"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("default"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyHashingServiceWithNameNodeSha512Test(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.NameNode.OnlyHashing.Sha512", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("sha512"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("default"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyAuthenticationServiceWithNameNodeEmptyNameTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.NameNode.OnlyAuth.Empty", useRoot);
+ CheckConfigurationIsDefault(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void OnlyAuthenticationServiceWithNameNodeNotDefaultTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.NameNode.OnlyAuth", useRoot);
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("plain"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("notdefault"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameNodeInLowCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.NameNode.Naming.LC", useRoot);
+ ValidateNamingConfigurationResults(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameNodeInUpperCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.NameNode.Naming.UC", useRoot);
+ ValidateNamingConfigurationResults(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameNodeInCamelCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.NameNode.Naming.CC", useRoot);
+ ValidateNamingConfigurationResults(secConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NameNodeInPascalCaseTest(bool useRoot)
+ {
+ var secConfig = LoadConfiguration($"Xtensive.Orm.Security.{ConfigFormat}.NameNode.Naming.PC", useRoot);
+ ValidateNamingConfigurationResults(secConfig);
+ }
+
+ #endregion
+
+ protected static void CheckConfigurationIsDefault(SecurityConfiguration secConfig)
+ {
+ Assert.That(secConfig, Is.Not.Null);
+ Assert.That(secConfig.HashingServiceName, Is.EqualTo("plain"));
+ Assert.That(secConfig.AuthenticationServiceName, Is.EqualTo("default"));
+ }
+ }
+}
diff --git a/Extensions/Xtensive.Orm.Security.Tests/Xtensive.Orm.Security.Tests.csproj b/Extensions/Xtensive.Orm.Security.Tests/Xtensive.Orm.Security.Tests.csproj
index 2d10b5ce07..b40c1f9f11 100644
--- a/Extensions/Xtensive.Orm.Security.Tests/Xtensive.Orm.Security.Tests.csproj
+++ b/Extensions/Xtensive.Orm.Security.Tests/Xtensive.Orm.Security.Tests.csproj
@@ -8,6 +8,9 @@
+
+
+
@@ -20,4 +23,12 @@
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Security.Tests/securitysettings.json b/Extensions/Xtensive.Orm.Security.Tests/securitysettings.json
new file mode 100644
index 0000000000..a7f9eff766
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Security.Tests/securitysettings.json
@@ -0,0 +1,147 @@
+{
+ "Xtensive.Orm.Security.Json.Empty": {
+
+ },
+
+ "Xtensive.Orm.Security.Json.AllEmpty": {
+ "HashingService": "",
+ "AuthenticationService": ""
+ },
+
+ "Xtensive.Orm.Security.Json.OnlyHashing.Sha1": {
+ "HashingService": "sha1"
+ },
+ "Xtensive.Orm.Security.Json.OnlyHashing.Sha256": {
+ "HashingService": "sha256"
+ },
+ "Xtensive.Orm.Security.Json.OnlyHashing.Sha384": {
+ "HashingService": "sha384"
+ },
+ "Xtensive.Orm.Security.Json.OnlyHashing.Sha512": {
+ "HashingService": "sha512"
+ },
+ "Xtensive.Orm.Security.Json.OnlyHashing.Md5": {
+ "HashingService": "md5"
+ },
+ "Xtensive.Orm.Security.Json.OnlyHashing.Empty": {
+ "HashingService": ""
+ },
+
+ "Xtensive.Orm.Security.Json.OnlyAuth": {
+ "AuthenticationService": "NotDefault"
+ },
+ "Xtensive.Orm.Security.Json.OnlyAuth.Empty": {
+ "AuthenticationService": ""
+ },
+
+ "Xtensive.Orm.Security.Json.Naming.LC": {
+ "hashingservice": "sha1",
+ "authenticationservice": "NotDefault"
+ },
+ "Xtensive.Orm.Security.Json.Naming.UC": {
+ "HASHINGSERVICE": "sha1",
+ "AUTHENTICATIONSERVICE": "NotDefault"
+ },
+ "Xtensive.Orm.Security.Json.Naming.CC": {
+ "hashingService": "sha1",
+ "authenticationService": "NotDefault"
+ },
+ "Xtensive.Orm.Security.Json.Naming.PC": {
+ "HashingService": "sha1",
+ "AuthenticationService": "NotDefault"
+ },
+
+ "Xtensive.Orm.Security.Json.Mistype.LC": {
+ "hashiingservice": "sha1",
+ "authentticationservice": "NotDefault"
+ },
+ "Xtensive.Orm.Security.Json.Mistype.UC": {
+ "HASHIINGSERVICE": "sha1",
+ "AUTHENTTICATIONSERVICE": "NotDefault"
+ },
+ "Xtensive.Orm.Security.Json.Mistype.CC": {
+ "hashiingService": "sha1",
+ "authentticationService": "NotDefault"
+ },
+ "Xtensive.Orm.Security.Json.Mistype.PC": {
+ "HashiingSeervice": "sha1",
+ "AuthentticationService": "NotDefault"
+ },
+
+
+ "Xtensive.Orm.Security.Json.NameNode.AllEmpty": {
+ "HashingService": {
+ },
+ "AuthenticationService": {
+ }
+ },
+
+ "Xtensive.Orm.Security.Json.NameNode.NamesEmpty": {
+ "HashingService": {
+ "Name": ""
+ },
+ "AuthenticationService": {
+ "Name": ""
+ }
+ },
+
+ "Xtensive.Orm.Security.Json.NameNode.OnlyHashing.Sha1": {
+ "HashingService": {
+ "Name": "sha1"
+ }
+ },
+ "Xtensive.Orm.Security.Json.NameNode.OnlyHashing.Sha256": {
+ "HashingService": {
+ "Name": "sha256"
+ }
+ },
+ "Xtensive.Orm.Security.Json.NameNode.OnlyHashing.Sha384": {
+ "HashingService": {
+ "Name": "sha384"
+ }
+ },
+ "Xtensive.Orm.Security.Json.NameNode.OnlyHashing.Sha512": {
+ "HashingService": {
+ "Name": "sha512"
+ }
+ },
+ "Xtensive.Orm.Security.Json.NameNode.OnlyHashing.Md5": {
+ "HashingService": {
+ "Name": "md5"
+ }
+ },
+ "Xtensive.Orm.Security.Json.NameNode.OnlyHashing.Empty": {
+ "HashingService": {
+ "Name": ""
+ }
+ },
+
+ "Xtensive.Orm.Security.Json.NameNode.OnlyAuth": {
+ "AuthenticationService": {
+ "Name": "NotDefault"
+ }
+ },
+ "Xtensive.Orm.Security.Json.NameNode.OnlyAuth.Empty": {
+ "AuthenticationService": {
+ "Name": ""
+ }
+ },
+
+
+ "Xtensive.Orm.Security.Json.NameNode.Naming.LC": {
+ "HashingService": { "name": "sha1" },
+ "AuthenticationService": { "name": "NotDefault" }
+ },
+ "Xtensive.Orm.Security.Json.NameNode.Naming.UC": {
+ "HashingService": { "NAME": "sha1" },
+ "AuthenticationService": { "NAME": "NotDefault" }
+ },
+ "Xtensive.Orm.Security.Json.NameNode.Naming.CC": {
+ "HashingService": { "naMe": "sha1" },
+ "AuthenticationService": { "naMe": "NotDefault" }
+ },
+ "Xtensive.Orm.Security.Json.NameNode.Naming.PC": {
+ "HashingService": { "NaMe": "sha1" },
+ "AuthenticationService": { "NaMe": "NotDefault" }
+ }
+}
diff --git a/Extensions/Xtensive.Orm.Security/Configuration/Elements/ConfigurationSection.cs b/Extensions/Xtensive.Orm.Security/Configuration/Elements/ConfigurationSection.cs
index ca3fe8cf06..86899f68d4 100644
--- a/Extensions/Xtensive.Orm.Security/Configuration/Elements/ConfigurationSection.cs
+++ b/Extensions/Xtensive.Orm.Security/Configuration/Elements/ConfigurationSection.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2011 Xtensive LLC.
+// Copyright (C) 2011-2024 Xtensive LLC.
// All rights reserved.
// For conditions of distribution and use, see license.
// Created by: Dmitri Maximov
@@ -18,6 +18,7 @@ public class ConfigurationSection : System.Configuration.ConfigurationSection
/// Gets default section name for security configuration.
/// Value is "Xtensive.Orm.Security".
///
+ [Obsolete("Use SecurityConfiguration.DefaultSectionName instead")]
public static readonly string DefaultSectionName = "Xtensive.Orm.Security";
private const string HashingServiceElementName = "hashingService";
diff --git a/Extensions/Xtensive.Orm.Security/Configuration/SecurityConfiguration.cs b/Extensions/Xtensive.Orm.Security/Configuration/SecurityConfiguration.cs
index 8346a6497c..2ecd46f8cb 100644
--- a/Extensions/Xtensive.Orm.Security/Configuration/SecurityConfiguration.cs
+++ b/Extensions/Xtensive.Orm.Security/Configuration/SecurityConfiguration.cs
@@ -1,11 +1,16 @@
-// Copyright (C) 2011 Xtensive LLC.
-// All rights reserved.
-// For conditions of distribution and use, see license.
+// Copyright (C) 2011-2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
// Created by: Dmitri Maximov
// Created: 2011.06.10
using System;
using System.Configuration;
+using System.Linq;
+using System.Threading;
+using Microsoft.Extensions.Configuration;
+using Xtensive.Core;
+using Xtensive.Orm.Configuration;
namespace Xtensive.Orm.Security.Configuration
{
@@ -13,25 +18,48 @@ namespace Xtensive.Orm.Security.Configuration
/// The configuration of the security system.
///
[Serializable]
- public class SecurityConfiguration
+ public class SecurityConfiguration : ConfigurationBase
{
///
- /// Default SectionName value:
- /// "".
+ /// Default section in configuration. Used if custom name is not provided.
///
public const string DefaultSectionName = "Xtensive.Orm.Security";
+ internal const string HashingServiceElementName = "HashingService";
+ internal const string AuthenticationServiceElementName = "AuthenticationService";
+
+ internal const string DefaultHashingServiceName = "plain";
+ internal const string DefaultAuthenticationServiceName = "default";
+
///
/// Gets or sets the name of the hashing service.
///
/// The name of the hashing service.
- public string HashingServiceName { get; private set; }
+ [ConfigurationKeyName(HashingServiceElementName)]
+ public string HashingServiceName { get; set; }
///
/// Gets or sets the name of the authentication service.
///
/// The name of the authentication service.
- public string AuthenticationServiceName { get; private set; }
+ [ConfigurationKeyName(AuthenticationServiceElementName)]
+ public string AuthenticationServiceName { get; set; }
+
+ ///
+ protected override SecurityConfiguration CreateClone() => new SecurityConfiguration();
+
+ ///
+ protected override void CopyFrom(ConfigurationBase source)
+ {
+ base.CopyFrom(source);
+
+ var configuration = (SecurityConfiguration) source;
+ configuration.HashingServiceName = configuration.HashingServiceName;
+ configuration.AuthenticationServiceName = configuration.AuthenticationServiceName;
+ }
+
+ ///
+ public override SecurityConfiguration Clone() => (SecurityConfiguration) base.Clone();
///
/// Loads the
@@ -79,29 +107,105 @@ public static SecurityConfiguration Load(System.Configuration.Configuration conf
/// The .
public static SecurityConfiguration Load(System.Configuration.Configuration configuration, string sectionName)
{
- var configurationSection = (ConfigurationSection)configuration.GetSection(sectionName);
+ var configurationSection = (ConfigurationSection) configuration.GetSection(sectionName);
return GetConfigurationFromSection(configurationSection);
}
private static SecurityConfiguration GetConfigurationFromSection(ConfigurationSection configurationSection)
{
- var result = new SecurityConfiguration();
+ var result = new SecurityConfiguration(true);
- string hashingService = configurationSection==null
- ? string.Empty
+ var hashingService = configurationSection == null
+ ? string.Empty
: configurationSection.HashingService.Name;
- if (string.IsNullOrEmpty(hashingService))
- hashingService = "plain";
- result.HashingServiceName = hashingService.ToLowerInvariant();
+ if (!string.IsNullOrEmpty(hashingService)) {
+ result.HashingServiceName = hashingService.ToLowerInvariant();
+ }
- string authenticationService = configurationSection==null
- ? string.Empty
+ var authenticationService = configurationSection == null
+ ? string.Empty
: configurationSection.AuthenticationService.Name;
- if (string.IsNullOrEmpty(authenticationService))
- authenticationService = "default";
- result.AuthenticationServiceName = authenticationService.ToLowerInvariant();
+ if (!string.IsNullOrEmpty(authenticationService)) {
+ result.AuthenticationServiceName = authenticationService.ToLowerInvariant();
+ }
return result;
}
+
+ ///
+ /// Loads the from given configuration section.
+ ///
+ /// to load section from.
+ /// Name of the section where configuration is stored.
+ /// Loaded configuration or configuration with default settings.
+ public static SecurityConfiguration Load(IConfiguration configuration, string sectionName = null)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configuration, nameof(configuration));
+
+ if (configuration is IConfigurationRoot configurationRoot) {
+ return Load(configurationRoot, sectionName);
+ }
+ else if (configuration is IConfigurationSection configurationSection) {
+ return Load(configurationSection);
+ }
+
+ throw new NotSupportedException("Type of configuration is not supported.");
+ }
+
+
+ ///
+ /// Loads the from given configuration section.
+ ///
+ /// to load section from.
+ /// Name of the section where configuration is stored.
+ /// Loaded configuration or configuration with default settings.
+ public static SecurityConfiguration Load(IConfigurationRoot configurationRoot, string sectionName = null)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationRoot, nameof(configurationRoot));
+
+ var configuration = new NamelessFormatSecurityConfigurationReader().Read(configurationRoot, sectionName ?? DefaultSectionName);
+ if (configuration != null) {
+ return configuration;
+ }
+
+ configuration = new BasedOnNamesFormatSecurityConfigurationReader().Read(configurationRoot, sectionName ?? DefaultSectionName);
+ return configuration ?? new SecurityConfiguration(true);
+ }
+
+ ///
+ /// Loads the from given configuration section.
+ ///
+ /// to load from.
+ /// Loaded configuration or configuration with default settings.
+ public static SecurityConfiguration Load(IConfigurationSection configurationSection)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationSection, nameof(configurationSection));
+
+ var configuration = new NamelessFormatSecurityConfigurationReader().Read(configurationSection);
+ if (configuration != null) {
+ return configuration;
+ }
+
+ configuration = new BasedOnNamesFormatSecurityConfigurationReader().Read(configurationSection);
+ return configuration ?? new SecurityConfiguration(true);
+ }
+
+ ///
+ /// Creates instance of with no properties initialized.
+ ///
+ public SecurityConfiguration()
+ {
+ }
+
+ ///
+ /// Creates instance of with properties initialized on demand.
+ ///
+ internal SecurityConfiguration(bool initWithDefaults)
+ {
+ if (initWithDefaults) {
+ HashingServiceName = DefaultHashingServiceName;
+ AuthenticationServiceName = DefaultAuthenticationServiceName;
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/Extensions/Xtensive.Orm.Security/Configuration/SecurityConfigurationReaders.cs b/Extensions/Xtensive.Orm.Security/Configuration/SecurityConfigurationReaders.cs
new file mode 100644
index 0000000000..8597af2750
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Security/Configuration/SecurityConfigurationReaders.cs
@@ -0,0 +1,106 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using System;
+using System.Linq;
+using Microsoft.Extensions.Configuration;
+using Xtensive.Core;
+using Xtensive.Orm.Configuration;
+
+namespace Xtensive.Orm.Security.Configuration
+{
+ internal sealed class NamelessFormatSecurityConfigurationReader : SecurityConfigurationReader
+ {
+ protected override SecurityConfiguration ReadInternal(IConfigurationSection configuration)
+ {
+ try {
+ var configAsIs = configuration.Get();
+ if (configAsIs != null && (configAsIs.AuthenticationServiceName ?? configAsIs.HashingServiceName) != null) {
+ configAsIs.HashingServiceName = string.IsNullOrEmpty(configAsIs.HashingServiceName)
+ ? SecurityConfiguration.DefaultHashingServiceName
+ : configAsIs.HashingServiceName.ToLowerInvariant();
+ configAsIs.AuthenticationServiceName = string.IsNullOrEmpty(configAsIs.AuthenticationServiceName)
+ ? SecurityConfiguration.DefaultAuthenticationServiceName
+ : (configAsIs.AuthenticationServiceName?.ToLowerInvariant());
+ return configAsIs;
+ }
+ }
+ catch {
+ return null;
+ }
+
+ var children = configuration.GetChildren();
+ return !children.Any()
+ ? new SecurityConfiguration(true)
+ : null;
+ }
+ }
+
+ internal sealed class BasedOnNamesFormatSecurityConfigurationReader : SecurityConfigurationReader
+ {
+ private const string ServiceNameAttributeName = "name";
+
+ protected override SecurityConfiguration ReadInternal(IConfigurationSection configuration)
+ {
+
+ var hashingServiceSection = configuration.GetSection(SecurityConfiguration.HashingServiceElementName);
+ var authenticationServiceSection = configuration.GetSection(SecurityConfiguration.AuthenticationServiceElementName);
+
+ if (hashingServiceSection == null && authenticationServiceSection == null) {
+ return null;
+ }
+
+ var hashingServiceName = hashingServiceSection.GetSection(ServiceNameAttributeName)?.Value;
+ if (hashingServiceName == null) {
+ var children = hashingServiceSection.GetChildren().ToList();
+ if (children.Count > 0) {
+ hashingServiceName = children[0].GetSection(ServiceNameAttributeName).Value;
+ }
+ }
+
+ var authenticationServiceName = authenticationServiceSection.GetSection(ServiceNameAttributeName)?.Value;
+ if (authenticationServiceName == null) {
+ var children = authenticationServiceSection.GetChildren().ToList();
+ if (children.Count > 0) {
+ authenticationServiceName = children[0].GetSection(ServiceNameAttributeName).Value;
+ }
+ }
+ if ((hashingServiceName ?? authenticationServiceName) != null) {
+ var securityConfiguration = new SecurityConfiguration(true);
+ if (!hashingServiceName.IsNullOrEmpty()) {
+ securityConfiguration.HashingServiceName = hashingServiceName.ToLowerInvariant();
+ }
+
+ if (!authenticationServiceName.IsNullOrEmpty()) {
+ securityConfiguration.AuthenticationServiceName = authenticationServiceName.ToLowerInvariant();
+ }
+
+ return securityConfiguration;
+ }
+ return null;
+ }
+ }
+
+ internal abstract class SecurityConfigurationReader : IConfigurationSectionReader
+ {
+ public SecurityConfiguration Read(IConfigurationSection configurationSection) => ReadInternal(configurationSection);
+
+ public SecurityConfiguration Read(IConfigurationSection configurationSection, string nameOfConfiguration) =>
+ throw new NotSupportedException();
+
+ public SecurityConfiguration Read(IConfigurationRoot configurationRoot) =>
+ Read(configurationRoot, SecurityConfiguration.DefaultSectionName);
+
+ public SecurityConfiguration Read(IConfigurationRoot configurationRoot, string sectionName)
+ {
+ var section = configurationRoot.GetSection(sectionName);
+ return ReadInternal(section);
+ }
+
+ public SecurityConfiguration Read(IConfigurationRoot configurationRoot, string sectionName, string nameOfConfiguration) =>
+ throw new NotSupportedException();
+
+ protected abstract SecurityConfiguration ReadInternal(IConfigurationSection section);
+ }
+}
diff --git a/Extensions/Xtensive.Orm.Security/DomainConfugurationExtensions.cs b/Extensions/Xtensive.Orm.Security/DomainConfugurationExtensions.cs
new file mode 100644
index 0000000000..d480dc4995
--- /dev/null
+++ b/Extensions/Xtensive.Orm.Security/DomainConfugurationExtensions.cs
@@ -0,0 +1,108 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using Microsoft.Extensions.Configuration;
+using Xtensive.Orm.Configuration;
+using Xtensive.Orm.Security.Configuration;
+
+namespace Xtensive.Orm.Security
+{
+ ///
+ /// Contains extensions for DomainConfiguration that help to configure the extension.
+ ///
+ public static class DomainConfugurationSecurityExtensions
+ {
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureSecurityExtension(this DomainConfiguration domainConfiguration) =>
+ ConfigureSecurityExtension(domainConfiguration, SecurityConfiguration.Load());
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Section name.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureSecurityExtension(this DomainConfiguration domainConfiguration,
+ string configurationSectionName) =>
+ ConfigureSecurityExtension(domainConfiguration, SecurityConfiguration.Load(configurationSectionName));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuraton to load from.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureSecurityExtension(this DomainConfiguration domainConfiguration,
+ System.Configuration.Configuration configuration) =>
+ ConfigureSecurityExtension(domainConfiguration, SecurityConfiguration.Load(configuration));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuraton to load from.
+ /// Section in
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureSecurityExtension(this DomainConfiguration domainConfiguration,
+ System.Configuration.Configuration configuration, string sectionName) =>
+ ConfigureSecurityExtension(domainConfiguration, SecurityConfiguration.Load(configuration, sectionName));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuraton to load from.
+ /// Section in
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureSecurityExtension(this DomainConfiguration domainConfiguration,
+ IConfiguration configuration, string sectionName = null) =>
+ ConfigureSecurityExtension(domainConfiguration, SecurityConfiguration.Load(configuration, sectionName));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuraton to load from.
+ /// Section in
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureSecurityExtension(this DomainConfiguration domainConfiguration,
+ IConfigurationRoot configurationRoot, string sectionName = null) =>
+ ConfigureSecurityExtension(domainConfiguration, SecurityConfiguration.Load(configurationRoot, sectionName));
+
+ ///
+ /// Loads configuration by calling
+ /// and uses it to configure the extension.
+ ///
+ /// Domain configuration.
+ /// Configuration section to load from.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureSecurityExtension(this DomainConfiguration domainConfiguration,
+ IConfigurationSection configurationSection) =>
+ ConfigureSecurityExtension(domainConfiguration, SecurityConfiguration.Load(configurationSection));
+
+ ///
+ /// Configures the extension with given security configuration instance.
+ ///
+ /// Domain configuration.
+ /// Security configuration instance.
+ /// instance with configured extension.
+ public static DomainConfiguration ConfigureSecurityExtension(this DomainConfiguration domainConfiguration,
+ SecurityConfiguration securityConfiguration)
+ {
+ domainConfiguration.ExtensionConfigurations.Set(securityConfiguration);
+ domainConfiguration.Types.Register(typeof(DomainConfugurationSecurityExtensions).Assembly);
+ return domainConfiguration;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Extensions/Xtensive.Orm.Security/NugetContent/ReadMe.md b/Extensions/Xtensive.Orm.Security/NugetContent/ReadMe.md
index 143b111b76..2b5f0f72d8 100644
--- a/Extensions/Xtensive.Orm.Security/NugetContent/ReadMe.md
+++ b/Extensions/Xtensive.Orm.Security/NugetContent/ReadMe.md
@@ -8,7 +8,7 @@ There are 2 main parts that can also be used separately: authentication services
Prerequisites
-------------
-DataObjects.Net 7.0.x or later (http://dataobjects.net)
+DataObjects.Net 7.1.x or later (http://dataobjects.net)
How to use
----------
@@ -43,6 +43,8 @@ and set up the desired hashing service:
```
+Other examples of how to configure the extension are in section below
+
Examples
--------
@@ -311,4 +313,263 @@ customers that have IsAutomobileIndustry property set to true, e.g.:
transaction.Complete();
}
}
-```
\ No newline at end of file
+```
+
+
+
+Examples of how to configure extension
+--------------------------------------
+
+Additionally to "How to use" section it provides extra examples of how to configure and/or read extension configuration.
+
+The example in "How to use" section uses old fasioned API of configuration files, yet usable in many applications. But
+there are some cases which may require usage of different API or work-around certain cases with existing one.
+
+**Example #1** Reading old-style configuration of an assembly in NET 5 and newer.
+
+Due to new architecture without AppDomain (which among the other things was in charge of gathering configuration files of loaded assemblies
+as it would be one configuration file) ```System.Configuration.ConfigurationManager``` now reads only configuration file of actual executable, loaded
+assemblies' configuration files stay unreachable by default, though there is need to read some data from them.
+A great example is test projects which are usually get loaded by test runner executable, and the only configuration accessible in this case
+is test runner one.
+
+Extra step is required to read configuration files in such cases. Thankfully, ```ConfigurationManager``` has methods to get access to assemblies' configurations.
+
+To get access to an assembly configuration file it should be opened explicitly by
+
+```csharp
+ var configuration = ConfigurationManager.OpenExeConfiguration(typeof(SomeTypeInConfigOwnerAssembly).Assembly.Location);
+```
+
+The instance returned from ```OpenExeConfiguration``` provides access to sections of the assembly configuration. DataObjects.Net configurations
+(```DomainConfiguration```, ```SecurityConfiguration```, etc.) have ```Load()``` methods that can recieve this instance.
+```SecurityConfiguration``` can be read like so
+
+```csharp
+ var configuration = ConfigurationManager.OpenExeConfiguration(typeof(SomeTypeInConfigOwnerAssembly).Assembly.Location);
+ var securityConfig = SecurityConfiguration.Load(configuration);
+
+ // loaded configuration should be manually placed to
+ domainConfiguration.ExtensionConfigurations.Set(securityConfig);
+```
+
+The ```domainConfiguration.ExtensionConfigurations``` is a new unified place from which the extension will try to get its configuration
+instead of calling default parameterless ```Load()``` method, which has not a lot of sense now, though the method is kept as a second source
+for backwards compatibility.
+
+For more convenience, ```DomainConfiguration``` extensions are provided, which make code more neater.
+For instance,
+
+```csharp
+ var configuration = ConfigurationManager.OpenExeConfiguration(typeof(SomeTypeInConfigOwnerAssembly).Assembly.Location);
+
+ var domainConfiguration = DomainConfiguration.Load(configuration);
+
+ // the extension hides getting configuration with SecurityConfiguration.Load(configuration)
+ // and also putting it to ExtensionConfigurations collection.
+ domainConfiguration.ConfigureSecurityExtension(configuration);
+```
+
+Remember the requirement to register ```Xtensive.Orm.Security``` to domain? The extension tries to register this assembly to ```DomainConfiguration.Types``` collection
+so even if you miss registration but called extension method required types of Security extension will be registered in Domain types.
+
+Custom section names are also supported if for some reason default section name is not used.
+
+
+**Example #2** Reading old-style configuration of an assembly in a project that uses appsettings.json file.
+
+If for some reason there is need to keep the old-style configuration then there is a work-around as well.
+Static configuration manager provides method ```OpenMappedExeConfiguration()``` which allows to get access to
+any *.config file as ```System.Configuration.Configuration``` instance. For example,
+
+```csharp
+ ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
+ configFileMap.ExeConfigFilename = "Orm.config"; //or other file name, the file should exist bin folder
+ var configuration = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
+```
+
+After that, as in previous example, the instance can be passed to ```Load``` method of ```SecurityConfiguration``` to read extension configuration
+and later put it to ```DomainConfiguration.ExtensionConfigurations```
+
+```csharp
+ var securityConfiguration = SecurityConfiguration.Load(configuration);
+
+ domainConfiguration.ExtensionConfigurations.Set(securityConfiguration);
+```
+
+Extension usage will look like
+
+```csharp
+ domainConfiguration.ConfigureSecurityExtension(configuration);
+```
+
+
+**Example #3** Configure using Microsoft.Extensions.Configuration API.
+
+This API allows to have configurations in various forms including JSON and XML formats.
+Loading of such files may differ depending on .NET version, check Microsoft manuals for instructions.
+
+Allowed JSON and XML configuration definition look like below
+
+```xml
+
+
+ sha512
+ CustomAuthenticationService
+
+
+```
+
+```json
+{
+ "Xtensive.Orm.Security": {
+ "HashingService" : "sha512",
+ "AuthenticationService" : "CustomAuthenticationService"
+ }
+}
+```
+
+The API has certain issues with XML elements with attributes so it is recommended to use
+more up-to-date attributeless nodes.
+For JSON it is pretty clear, almost averyone knows its format.
+
+```SecurityConfiguration.Load``` method can accept different types of abstractions from the API, including
+- ```Microsoft.Extensions.Configuration.IConfiguration```;
+- ```Microsoft.Extensions.Configuration.IConfigurationRoot```;
+- ```Microsoft.Extensions.Configuration.IConfigurationSection```.
+
+Loading of configuration may look like
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ // tries to load from default section "Xtensive.Orm.Security"
+ var securityConfig = SecurityConfiguration.Load(app.Configuration);
+
+ domainConfiguration.ExtensionConfigurations.Set(securityConfig);
+```
+
+or, with use of extension, like
+
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ // Tries to load from default section "Xtensive.Orm.Security"
+ // and put it into domainConfiguration.ExtensionConfigurations.
+ // Additionally, registers types of "Xtensive.Orm.Security" assembly.
+
+ domainConfiguration.ConfigureSecurityExtension(app.Configuration);
+```
+
+
+
+**Example #4** Configure using Microsoft.Extensions.Configuration API from section with non-default name.
+
+For configurations like
+
+```xml
+
+
+ sha512
+ CustomAuthenticationService
+
+
+```
+
+```json
+{
+ "Orm.Security": {
+ "HashingService" : "sha512",
+ "AuthenticationService" : "CustomAuthenticationService"
+ }
+}
+
+Loading of configuration may look like
+
+```csharp
+ var app = builder.Build();
+
+ //...
+
+ var securityConfig = SecurityConfiguration.Load(app.Configuration, "Orm.Security");
+
+ domainConfiguration.ExtensionConfigurations.Set(securityConfig);
+```
+
+or, with use of extension, like
+
+```csharp
+ var app = builder.Build();
+
+ //...
+
+ domainConfiguration.ConfigureSecurityExtension(app.Configuration, "Orm.Security");
+```
+
+
+**Example #5** Configure using Microsoft.Extensions.Configuration API from sub-section deeper in section tree.
+
+If for some reason extension configuration should be moved deeper in section tree like something below
+
+```xml
+
+
+
+ sha512
+ CustomAuthenticationService
+
+
+
+```
+
+or in JSON
+
+```json
+{
+ "Orm.Extensions": {
+ "Xtensive.Orm.Security": {
+ "HashingService" : "sha512",
+ "AuthenticationService" : "CustomAuthenticationService"
+ }
+ }
+}
+```
+
+Then section must be provided manually, code may look like
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ var configurationRoot = app.Configuration;
+ var extensionsGroupSection = configurationRoot.GetSection("Orm.Extensions");
+ var securitySection = extensionsGroupSection.GetSection("Xtensive.Orm.Security");
+
+ var securityConfig = SecurityConfiguration.Load(securitySection);
+
+ domainConfiguration.ExtensionConfigurations.Set(securityConfig);
+```
+
+or, with use of extension method, like
+
+```csharp
+
+ var app = builder.Build();
+
+ //...
+
+ var configurationRoot = app.Configuration;
+ var extensionsGroupSection = configurationRoot.GetSection("Orm.Extensions");
+ var securitySection = extensionsGroupSection.GetSection("Xtensive.Orm.Security");
+
+ domainConfiguration.ConfigureSecurityExtension(securitySection);
+```
diff --git a/Extensions/Xtensive.Orm.Security/SessionExtensions.cs b/Extensions/Xtensive.Orm.Security/SessionExtensions.cs
index 4df2ecefb4..39f02e4c89 100644
--- a/Extensions/Xtensive.Orm.Security/SessionExtensions.cs
+++ b/Extensions/Xtensive.Orm.Security/SessionExtensions.cs
@@ -4,6 +4,7 @@
// Created by: Dmitri Maximov
// Created: 2011.05.22
+using System;
using System.Security.Principal;
using Xtensive.Core;
using Xtensive.Orm.Security;
@@ -12,6 +13,7 @@
namespace Xtensive.Orm
{
+
///
/// Session extension methods for security-related stuff.
///
@@ -24,12 +26,16 @@ public static class SessionExtensions
/// instance.
public static SecurityConfiguration GetSecurityConfiguration(this Session session)
{
- var result = session.Domain.Extensions.Get();
- if (result == null) {
- result = SecurityConfiguration.Load();
- session.Domain.Extensions.Set(result);
+ var fromNewSource = session.Domain.Configuration.ExtensionConfigurations.Get();
+ if (fromNewSource!=null)
+ return fromNewSource;
+
+ var fromOldSource = session.Domain.Extensions.Get();
+ if (fromOldSource == null) {
+ fromOldSource = SecurityConfiguration.Load();
+ session.Domain.Extensions.Set(fromOldSource);
}
- return result;
+ return fromOldSource;
}
///
diff --git a/Extensions/Xtensive.Orm.Security/Xtensive.Orm.Security.csproj b/Extensions/Xtensive.Orm.Security/Xtensive.Orm.Security.csproj
index 91e131059a..4e15c012c7 100644
--- a/Extensions/Xtensive.Orm.Security/Xtensive.Orm.Security.csproj
+++ b/Extensions/Xtensive.Orm.Security/Xtensive.Orm.Security.csproj
@@ -22,6 +22,10 @@
+
+
+
+
diff --git a/Extensions/Xtensive.Orm.Tracking/NugetContent/ReadMe.md b/Extensions/Xtensive.Orm.Tracking/NugetContent/ReadMe.md
index baefbb2de6..81bf58b227 100644
--- a/Extensions/Xtensive.Orm.Tracking/NugetContent/ReadMe.md
+++ b/Extensions/Xtensive.Orm.Tracking/NugetContent/ReadMe.md
@@ -1,4 +1,4 @@
-Xtensive.Orm.Tracking
+Xtensive.Orm.Tracking
=====================
Summary
@@ -7,7 +7,7 @@ Provides tracking/auditing funtionality on Session/Domain level.
Prerequisites
-------------
-DataObjects.Net 7.0.x or later (http://dataobjects.net)
+DataObjects.Net 7.1.x or later (http://dataobjects.net)
Implementation
--------------
diff --git a/Extensions/Xtensive.Orm.Web/NugetContent/ReadMe.md b/Extensions/Xtensive.Orm.Web/NugetContent/ReadMe.md
index d19d1779f1..08223c0fb1 100644
--- a/Extensions/Xtensive.Orm.Web/NugetContent/ReadMe.md
+++ b/Extensions/Xtensive.Orm.Web/NugetContent/ReadMe.md
@@ -1,4 +1,4 @@
-Xtensive.Orm.Web
+Xtensive.Orm.Web
================
Summary
@@ -11,7 +11,7 @@ by default unless an exeption appeared. (more info on https://dataobjects.net)
Prerequisites
-------------
-DataObjects.Net 7 or later (https://dataobjects.net)
+DataObjects.Net 7.1 or later (https://dataobjects.net)
Usage of action filter
----------------------
diff --git a/Orm/Xtensive.Orm.Tests/Configuration/MicrosoftConfigurationTests.cs b/Orm/Xtensive.Orm.Tests/Configuration/MicrosoftConfigurationTests.cs
new file mode 100644
index 0000000000..c47023207a
--- /dev/null
+++ b/Orm/Xtensive.Orm.Tests/Configuration/MicrosoftConfigurationTests.cs
@@ -0,0 +1,2331 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Transactions;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using Microsoft.Extensions.Configuration;
+using NUnit.Framework;
+using Xtensive.Core;
+using Xtensive.Orm.Configuration;
+
+namespace Xtensive.Orm.Tests.Configuration.TypesToUseInTests
+{
+ namespace NestedNamespace
+ {
+ [HierarchyRoot]
+ public class DummyNestedEntity1 : Entity
+ {
+ [Key, Field]
+ public int Id { get; private set; }
+
+ [Field]
+ public string Name { get; set; }
+ }
+
+ [HierarchyRoot]
+ public class DummyNestedEntity2 : Entity
+ {
+ [Key, Field]
+ public int Id { get; private set; }
+
+ [Field]
+ public string Name { get; set; }
+ }
+ }
+
+ [HierarchyRoot]
+ public class DummyEntity1 : Entity
+ {
+ [Key, Field]
+ public int Id { get; private set; }
+
+ [Field]
+ public string Name { get; set; }
+ }
+
+ [HierarchyRoot]
+ public class DummyEntity2 : Entity
+ {
+ [Key, Field]
+ public int Id { get; private set; }
+
+ [Field]
+ public string Name { get; set; }
+ }
+
+ [HierarchyRoot]
+ public class DummyEntity3 : Entity
+ {
+ [Key, Field]
+ public int Id { get; private set; }
+
+ [Field]
+ public string Name { get; set; }
+ }
+}
+
+namespace Xtensive.Orm.Tests.Configuration
+{
+ [TestFixture]
+ public sealed class AppConfigStyleConfigurationTest : MicrosoftConfigurationTestBase
+ {
+ protected override string Postfix => "AppConfig";
+
+ protected override bool NameAttributeUnique => false;
+
+ protected override void RegisterConfigurationFile(ConfigurationBuilder builder)
+ {
+ _ = builder.AddXmlFile("domainSettings.config");
+ }
+ }
+
+ [TestFixture]
+ public sealed class XmlConfigurationTest : MicrosoftConfigurationTestBase
+ {
+ protected override string Postfix => "Xml";
+
+ protected override void RegisterConfigurationFile(ConfigurationBuilder builder)
+ {
+ _ = builder.AddXmlFile("domainSettings.config");
+ }
+ }
+
+ [TestFixture]
+ public sealed class JsonConfigurationTest : MicrosoftConfigurationTestBase
+ {
+ protected override string Postfix => "Json";
+
+ protected override void RegisterConfigurationFile(ConfigurationBuilder builder)
+ {
+ _ = builder.AddJsonFile("domainSettings.json");
+ }
+ }
+
+ public abstract class MicrosoftConfigurationTestBase
+ {
+ private IConfigurationRoot configuration;
+ private IConfigurationSection configurationSection;
+
+ protected abstract string Postfix { get; }
+ protected virtual bool NameAttributeUnique => true;
+
+ protected virtual string DefaultSectionName => $"Xtensive.Orm.{Postfix}";
+
+ [OneTimeSetUp]
+ public void OneTimeSetup()
+ {
+ var configurationBuilder = new ConfigurationBuilder();
+ _ = configurationBuilder.SetBasePath(Directory.GetCurrentDirectory());
+
+ RegisterConfigurationFile(configurationBuilder);
+ configuration = configurationBuilder.Build();
+
+ configurationSection = configuration.GetSection(DefaultSectionName);
+ if (!configurationSection.GetChildren().Any()) {
+ throw new InconclusiveException($"{DefaultSectionName} section seems to be empty. Check registered file with domain settings");
+ }
+ }
+
+ protected abstract void RegisterConfigurationFile(ConfigurationBuilder builder);
+
+ private DomainConfiguration LoadDomainConfiguration(string domainName, bool useRoot)
+ {
+ return useRoot
+ ? DomainConfiguration.Load(configuration, DefaultSectionName, domainName)
+ : DomainConfiguration.Load(configurationSection, domainName);
+ }
+ private LoggingConfiguration LoadLoggingConfiguration(IConfigurationSection customConfigurationSection = null)
+ {
+ var loggingConfiguration = LoggingConfiguration.Load(customConfigurationSection ?? configurationSection);
+ return loggingConfiguration;
+ }
+
+
+ #region Simple Domain settings that used to be attributes
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void ProviderAndConnectionStringTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithProviderAndConnectionString", useRoot);
+ Assert.That(domainConfig.ConnectionInfo.Provider, Is.EqualTo(WellKnown.Provider.Sqlite));
+ Assert.That(domainConfig.ConnectionInfo.ConnectionString, Is.EqualTo("Data Source=DO-Testsaaa.db3"));
+ Assert.That(domainConfig.ConnectionInfo.ConnectionUrl, Is.Null);
+
+ ValidateAllDefault(domainConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void ConnectionUrlTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithConnectionUrl", useRoot);
+ Assert.That(domainConfig.ConnectionInfo.Provider, Is.EqualTo(WellKnown.Provider.Sqlite));
+ Assert.That(domainConfig.ConnectionInfo.ConnectionString, Is.Null);
+ Assert.That(domainConfig.ConnectionInfo.ConnectionUrl.Url, Is.EqualTo("sqlite:///DO-Tests.db3"));
+
+ ValidateAllDefault(domainConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void CustomValidKeyCacheSizeTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithCustomValidKeyCacheSize", useRoot);
+
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.KeyCacheSize, 192));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void CustomInvalidKeyCacheSizeTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithCustomInvalidKeyCacheSize", useRoot));
+
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void CustomValidKeyGeneratorCacheSizeTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithCustomValidKeyGeneratorCacheSize", useRoot);
+
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.KeyGeneratorCacheSize, 192));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void CustomInvalidKeyGeneratorCacheSizeTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithCustomInvalidKeyGeneratorCacheSize", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void CustomValidQueryCacheSizeTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithCustomValidQueryCacheSize", useRoot);
+
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.QueryCacheSize, 192));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void CustomInvalidQueryCacheSizeTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithCustomInvalidQueryCacheSize", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void CustomValidRecordSetMappingCacheSizeTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithCustomValidRecordSetMappingCacheSize", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.RecordSetMappingCacheSize, 192));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void CustomInvalidRecordSetMappingCacheSizeTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithCustomInvalidRecordSetMappingCacheSize", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void CustomDefaultDatabaseTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithCustomDatabase", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.DefaultDatabase, "MyFancyDatabase"),
+ ((d) => d.IsMultidatabase, true),
+ ((d) => d.IsMultischema, true)
+ );
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void CustomDefaultSchemaTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithCustomSchema", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.DefaultSchema, "MyFancySchema"),
+ ((d) => d.IsMultidatabase, false),
+ ((d) => d.IsMultischema, true));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void UpgradeModesTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithUpgradeMode1", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.UpgradeMode, DomainUpgradeMode.Default));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithUpgradeMode2", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.UpgradeMode, DomainUpgradeMode.Recreate));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithUpgradeMode3", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.UpgradeMode, DomainUpgradeMode.Perform));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithUpgradeMode4", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.UpgradeMode, DomainUpgradeMode.PerformSafely));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithUpgradeMode5", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.UpgradeMode, DomainUpgradeMode.Validate));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithUpgradeMode6", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.UpgradeMode, DomainUpgradeMode.LegacyValidate));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithUpgradeMode7", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.UpgradeMode, DomainUpgradeMode.Skip));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithUpgradeMode8", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.UpgradeMode, DomainUpgradeMode.LegacySkip));
+ domainConfig.Lock();
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void WrongUpgradeModeTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithWrongUpgradeMode", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void ForeighKeyModesTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithForeignKeyMode1", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.ForeignKeyMode, ForeignKeyMode.None));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithForeignKeyMode2", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.ForeignKeyMode, ForeignKeyMode.Hierarchy));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithForeignKeyMode3", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.ForeignKeyMode, ForeignKeyMode.Reference));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithForeignKeyMode4", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.ForeignKeyMode, ForeignKeyMode.All));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithForeignKeyMode5", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.ForeignKeyMode, ForeignKeyMode.Default));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithForeignKeyMode6", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.ForeignKeyMode, ForeignKeyMode.Hierarchy | ForeignKeyMode.Reference));
+ domainConfig.Lock();
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InvalidForeighKeyModeTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidForeignKeyMode", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void ChangeTrackingModesTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithChangeTrackingMode1", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.FullTextChangeTrackingMode, FullTextChangeTrackingMode.Off));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithChangeTrackingMode2", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.FullTextChangeTrackingMode, FullTextChangeTrackingMode.Auto));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithChangeTrackingMode3", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.FullTextChangeTrackingMode, FullTextChangeTrackingMode.Manual));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithChangeTrackingMode4", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.FullTextChangeTrackingMode, FullTextChangeTrackingMode.OffWithNoPopulation));
+ domainConfig.Lock();
+
+ domainConfig = LoadDomainConfiguration("DomainWithChangeTrackingMode5", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.FullTextChangeTrackingMode, FullTextChangeTrackingMode.Default));
+ domainConfig.Lock();
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InvalidChangeTrackingModeTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidChangeTrackingMode", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DomainOptionsTest1(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithDomainOptionsValid1", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.Options, DomainOptions.Default));
+ domainConfig.Lock();
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DomainOptionsTest2(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithDomainOptionsValid2", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.Options, DomainOptions.None));
+ domainConfig.Lock();
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InvalidDomainOptionsTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithDomainOptionsInvalid", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void CollationTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithColation", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.Collation, "generalci"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void BriefSchemaSyncExceptionsTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithBriefSchemaSyncExceptions", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.SchemaSyncExceptionFormat, SchemaSyncExceptionFormat.Brief));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DetailedSchemaSyncExceptionsTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithDetailedSchemaSyncExceptions", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.SchemaSyncExceptionFormat, SchemaSyncExceptionFormat.Detailed));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DefaultSchemaSyncExceptionsTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithDefaultSchemaSyncExceptions", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.SchemaSyncExceptionFormat, SchemaSyncExceptionFormat.Default));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InvalidSchemaSyncExceptionsTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidSchemaSyncExceptions", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void TagsLocationNowhereTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithTagsLocationNowhere", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.TagsLocation, TagsLocation.Nowhere));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void TagsLocationBeforeTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithTagsLocationBefore", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.TagsLocation, TagsLocation.BeforeStatement));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void TagsLocationWithinTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithTagsLocationWithin", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.TagsLocation, TagsLocation.WithinStatement));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void TagsLocationAfterTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithTagsLocationAfter", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.TagsLocation, TagsLocation.AfterStatement));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void TagsLocationDefaultTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithTagsLocationDefault", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.TagsLocation, TagsLocation.Default));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InvalidTagsLocationTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithTagsLocationInvalid", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void ForcedServerVersionTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithForcedServerVersion", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.ForcedServerVersion, "10.0.0.0"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InitializationSqlTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithInitSql", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.ConnectionInitializationSql, "use [OtherDb]"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void IncludeSqlInExceptionsTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("IncludeSqlInExceptionsTrue", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.IncludeSqlInExceptions, true));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DontIncludeSqlInExceptionsTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("IncludeSqlInExceptionsFalse", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.IncludeSqlInExceptions, false));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void AllowCyclicDatabaseDependanciesTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("AllowCyclicDatabaseDependenciesTrue", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.AllowCyclicDatabaseDependencies, true));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DisallowCyclicDatabaseDependanciesTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("AllowCyclicDatabaseDependenciesFalse", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.AllowCyclicDatabaseDependencies, false));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void BuildInParallelTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("BuildInParallelTrue", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.BuildInParallel, true));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DontBuildInParallelTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("BuildInParallelFalse", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.BuildInParallel, false));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void AllowMultidatabaseKeysTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("MultidatabaseKeysTrue", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.MultidatabaseKeys, true));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DisallowMultidatabaseKeysTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("MultidatabaseKeysFalse", useRoot);
+ ValidateAllDefaultExcept(domainConfig, ((d) => d.MultidatabaseKeys, false));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void ShareStorageSchemaOverNodesTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("SharedStorageSchemaOn", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.ShareStorageSchemaOverNodes, true));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DontShareStorageSchemaOverNodesTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("SharedStorageSchemaOff", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.ShareStorageSchemaOverNodes, false));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void EnsureConnectionIsAliveTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("EnableConnectionIsAliveTrue", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.EnsureConnectionIsAlive, true));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DontCheckConnectionIsAliveTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("EnableConnectionIsAliveFalse", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.EnsureConnectionIsAlive, false));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void PreferTypeIdAsQueryParameterTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("PreferTypeIdsAsQueryParametersTrue", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.PreferTypeIdsAsQueryParameters, true));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DontPreferTypeIdAsQueryParameterTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("PreferTypeIdsAsQueryParametersFalse", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.PreferTypeIdsAsQueryParameters, false));
+ }
+ #endregion
+
+ #region NamingConvention
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionSettingsTest01(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithNamingConvention1", useRoot);
+ ValidateAllDefault(domainConfig);
+ var namingConvention = domainConfig.NamingConvention;
+ Assert.That(namingConvention.LetterCasePolicy, Is.EqualTo(LetterCasePolicy.Uppercase));
+ Assert.That(namingConvention.NamespacePolicy, Is.EqualTo(NamespacePolicy.Synonymize));
+ Assert.That(namingConvention.NamingRules, Is.EqualTo(NamingRules.UnderscoreHyphens | NamingRules.RemoveDots));
+
+ var synonyms = namingConvention.NamespaceSynonyms;
+ Assert.That(synonyms.Count, Is.EqualTo(2));
+ Assert.That(synonyms["Xtensive.Orm"], Is.EqualTo("system"));
+ Assert.That(synonyms["Xtensive.Orm.Tests"], Is.EqualTo("theRest"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionSettingsTest02(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithNamingConvention2", useRoot);
+ ValidateAllDefault(domainConfig);
+ var namingConvention = domainConfig.NamingConvention;
+ Assert.That(namingConvention.LetterCasePolicy, Is.EqualTo(LetterCasePolicy.Lowercase));
+ Assert.That(namingConvention.NamespacePolicy, Is.EqualTo(NamespacePolicy.Synonymize));
+ Assert.That(namingConvention.NamingRules, Is.EqualTo(NamingRules.UnderscoreHyphens | NamingRules.RemoveDots));
+
+ var synonyms = namingConvention.NamespaceSynonyms;
+ Assert.That(synonyms.Count, Is.EqualTo(2));
+ Assert.That(synonyms["Xtensive.Orm"], Is.EqualTo("system"));
+ Assert.That(synonyms["Xtensive.Orm.Tests"], Is.EqualTo("theRest"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionSettingsTest03(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithNamingConvention3", useRoot);
+ ValidateAllDefault(domainConfig);
+ var namingConvention = domainConfig.NamingConvention;
+ Assert.That(namingConvention.LetterCasePolicy, Is.EqualTo(LetterCasePolicy.AsIs));
+ Assert.That(namingConvention.NamespacePolicy, Is.EqualTo(NamespacePolicy.Synonymize));
+ Assert.That(namingConvention.NamingRules, Is.EqualTo(NamingRules.UnderscoreHyphens | NamingRules.RemoveDots));
+
+ var synonyms = namingConvention.NamespaceSynonyms;
+ Assert.That(synonyms.Count, Is.EqualTo(2));
+ Assert.That(synonyms["Xtensive.Orm"], Is.EqualTo("system"));
+ Assert.That(synonyms["Xtensive.Orm.Tests"], Is.EqualTo("theRest"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionSettingsTest04(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithNamingConvention4", useRoot);
+ ValidateAllDefault(domainConfig);
+ var namingConvention = domainConfig.NamingConvention;
+ Assert.That(namingConvention.LetterCasePolicy, Is.EqualTo(LetterCasePolicy.Default));
+ Assert.That(namingConvention.NamespacePolicy, Is.EqualTo(NamespacePolicy.Synonymize));
+ Assert.That(namingConvention.NamingRules, Is.EqualTo(NamingRules.UnderscoreHyphens | NamingRules.RemoveDots));
+
+ var synonyms = namingConvention.NamespaceSynonyms;
+ Assert.That(synonyms.Count, Is.EqualTo(2));
+ Assert.That(synonyms["Xtensive.Orm"], Is.EqualTo("system"));
+ Assert.That(synonyms["Xtensive.Orm.Tests"], Is.EqualTo("theRest"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionSettingsTest05(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithNamingConvention5", useRoot);
+ ValidateAllDefault(domainConfig);
+ var namingConvention = domainConfig.NamingConvention;
+ Assert.That(namingConvention.LetterCasePolicy, Is.EqualTo(LetterCasePolicy.Uppercase));
+ Assert.That(namingConvention.NamespacePolicy, Is.EqualTo(NamespacePolicy.AsIs));
+ Assert.That(namingConvention.NamingRules, Is.EqualTo(NamingRules.UnderscoreHyphens | NamingRules.RemoveDots));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionSettingsTest06(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithNamingConvention6", useRoot);
+ ValidateAllDefault(domainConfig);
+ var namingConvention = domainConfig.NamingConvention;
+ Assert.That(namingConvention.LetterCasePolicy, Is.EqualTo(LetterCasePolicy.Uppercase));
+ Assert.That(namingConvention.NamespacePolicy, Is.EqualTo(NamespacePolicy.Hash));
+ Assert.That(namingConvention.NamingRules, Is.EqualTo(NamingRules.UnderscoreHyphens | NamingRules.RemoveDots));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionSettingsTest07(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithNamingConvention7", useRoot);
+ ValidateAllDefault(domainConfig);
+ var namingConvention = domainConfig.NamingConvention;
+ Assert.That(namingConvention.LetterCasePolicy, Is.EqualTo(LetterCasePolicy.Uppercase));
+ Assert.That(namingConvention.NamespacePolicy, Is.EqualTo(NamespacePolicy.Omit));
+ Assert.That(namingConvention.NamingRules, Is.EqualTo(NamingRules.UnderscoreHyphens | NamingRules.RemoveDots));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionSettingsTest08(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithNamingConvention8", useRoot);
+ ValidateAllDefault(domainConfig);
+ var namingConvention = domainConfig.NamingConvention;
+ Assert.That(namingConvention.LetterCasePolicy, Is.EqualTo(LetterCasePolicy.Uppercase));
+ Assert.That(namingConvention.NamespacePolicy, Is.EqualTo(NamespacePolicy.Default));
+ Assert.That(namingConvention.NamingRules, Is.EqualTo(NamingRules.UnderscoreHyphens | NamingRules.RemoveDots));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionSettingsTest09(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithNamingConvention9", useRoot);
+ ValidateAllDefault(domainConfig);
+ var namingConvention = domainConfig.NamingConvention;
+ Assert.That(namingConvention.LetterCasePolicy, Is.EqualTo(LetterCasePolicy.Uppercase));
+ Assert.That(namingConvention.NamespacePolicy, Is.EqualTo(NamespacePolicy.Hash));
+ Assert.That(namingConvention.NamingRules, Is.EqualTo(NamingRules.UnderscoreDots | NamingRules.RemoveHyphens));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionSettingsTest10(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithNamingConvention10", useRoot);
+ ValidateAllDefault(domainConfig);
+ var namingConvention = domainConfig.NamingConvention;
+ Assert.That(namingConvention.LetterCasePolicy, Is.EqualTo(LetterCasePolicy.Uppercase));
+ Assert.That(namingConvention.NamespacePolicy, Is.EqualTo(NamespacePolicy.Hash));
+ Assert.That(namingConvention.NamingRules, Is.EqualTo(NamingRules.None));
+
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionSettingsTest11(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithNamingConvention11", useRoot);
+ ValidateAllDefault(domainConfig);
+ var namingConvention = domainConfig.NamingConvention;
+ Assert.That(namingConvention.LetterCasePolicy, Is.EqualTo(LetterCasePolicy.Uppercase));
+ Assert.That(namingConvention.NamespacePolicy, Is.EqualTo(NamespacePolicy.Hash));
+ Assert.That(namingConvention.NamingRules, Is.EqualTo(NamingRules.Default));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionInvalidSettingsTest1(bool useRoot)
+ {
+ _ = Assert.Throws (() => LoadDomainConfiguration("DomainWithInvalidNamingConvention1", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionInvalidSettingsTest2(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidNamingConvention2", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void NamingConventionInvalidSettingsTest3(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidNamingConvention3", useRoot));
+ }
+
+ #endregion
+
+ #region VersioningConvention
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void VersioningConventionPessimisticTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithVersioningConvention1", useRoot);
+ ValidateAllDefault(domainConfig);
+ Assert.That(domainConfig.VersioningConvention.EntityVersioningPolicy, Is.EqualTo(EntityVersioningPolicy.Pessimistic));
+ Assert.That(domainConfig.VersioningConvention.DenyEntitySetOwnerVersionChange, Is.EqualTo(false));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void VersioningConventionOptimisticTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithVersioningConvention2", useRoot);
+ ValidateAllDefault(domainConfig);
+ Assert.That(domainConfig.VersioningConvention.EntityVersioningPolicy, Is.EqualTo(EntityVersioningPolicy.Optimistic));
+ Assert.That(domainConfig.VersioningConvention.DenyEntitySetOwnerVersionChange, Is.EqualTo(false));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void VersioningConventionDefaultTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithVersioningConvention3", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ Assert.That(domainConfig.VersioningConvention.EntityVersioningPolicy, Is.EqualTo(EntityVersioningPolicy.Default));
+ Assert.That(domainConfig.VersioningConvention.DenyEntitySetOwnerVersionChange, Is.EqualTo(false));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void VersioningConventionDenyEntitySetChangeVersionTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithVersioningConvention4", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ Assert.That(domainConfig.VersioningConvention.EntityVersioningPolicy, Is.EqualTo(EntityVersioningPolicy.Optimistic));
+ Assert.That(domainConfig.VersioningConvention.DenyEntitySetOwnerVersionChange, Is.EqualTo(true));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void VersioningConventionAllowEntitySetChangeVersionTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithVersioningConvention5", useRoot);
+ ValidateAllDefault(domainConfig);
+ Assert.That(domainConfig.VersioningConvention.EntityVersioningPolicy, Is.EqualTo(EntityVersioningPolicy.Optimistic));
+ Assert.That(domainConfig.VersioningConvention.DenyEntitySetOwnerVersionChange, Is.EqualTo(false));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void VersioningConventionInvalidTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidVersioningConvention1", useRoot));
+ }
+
+ #endregion
+
+ #region Types registration
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void TypesRegistrationAsTypesTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithTypes", useRoot);
+ ValidateAllDefault(domainConfig);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.DummyEntity1)), Is.True);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.DummyEntity2)), Is.True);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.DummyEntity3)), Is.False);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void TypesRegistrationAsAssembliesTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithAssemblies", useRoot);
+ ValidateAllDefault(domainConfig);
+ var ormAssembly = typeof(DomainConfiguration).Assembly;
+ Assert.That(domainConfig.Types.Count, Is.GreaterThan(0));
+ Assert.That(domainConfig.Types.All((t) => t.Assembly == ormAssembly), Is.True);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void TypesRegistrationAsAssembliesWithNamespace(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithAssembliesAndNamespaces", useRoot);
+ ValidateAllDefault(domainConfig);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.NestedNamespace.DummyNestedEntity1)), Is.True);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.NestedNamespace.DummyNestedEntity2)), Is.True);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MixedTypeRegistration(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithMixedRegistrations", useRoot);
+ ValidateAllDefault(domainConfig);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.DummyEntity1)), Is.True);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.DummyEntity2)), Is.True);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.DummyEntity3)), Is.False);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.NestedNamespace.DummyNestedEntity1)), Is.True);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.NestedNamespace.DummyNestedEntity2)), Is.True);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InvalidTypeRegistration1(bool useRoot)
+ {
+ // same type twice
+
+ var domainConfig = LoadDomainConfiguration("DomainWithInvalidRegistrations1", useRoot);
+
+ ValidateAllDefault(domainConfig);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.DummyEntity1)), Is.True);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.DummyEntity2)), Is.False);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.DummyEntity3)), Is.False);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InvalidTypeRegistration2(bool useRoot)
+ {
+ // same Assembly
+ var domainConfig = LoadDomainConfiguration("DomainWithInvalidRegistrations2", useRoot);
+
+ ValidateAllDefault(domainConfig);
+ var ormAssembly = typeof(DomainConfiguration).Assembly;
+ Assert.That(domainConfig.Types.Count, Is.GreaterThan(0));
+ Assert.That(domainConfig.Types.All((t) => t.Assembly == ormAssembly), Is.True);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InvalidTypeRegistration3(bool useRoot)
+ {
+ // same assembly and namespace
+ var domainConfig = LoadDomainConfiguration("DomainWithInvalidRegistrations3", useRoot);
+ ValidateAllDefault(domainConfig);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.NestedNamespace.DummyNestedEntity1)), Is.True);
+ Assert.That(domainConfig.Types.Contains(typeof(TypesToUseInTests.NestedNamespace.DummyNestedEntity2)), Is.True);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InvalidTypeRegistration4(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidRegistrations4", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InvalidTypeRegistration5(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidRegistrations5", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InvalidTypeRegistration6(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidRegistrations6", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void InvalidTypeRegistration7(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidRegistrations7", useRoot));
+ }
+
+ #endregion
+
+ #region Sessions
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MultipleSessionsTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithMultipleSessionConfigurations", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(2));
+ var session = sessions[0];
+ Assert.That(session.Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(session,
+ ((s) => s.CacheType, SessionCacheType.Infinite),
+ ((s) => s.BatchSize, 20),
+ ((s) => s.EntityChangeRegistrySize, 255),
+ ((s) => s.Options, SessionOptions.AllowSwitching | SessionOptions.AutoActivation | SessionOptions.ReadRemovedObjects | SessionOptions.ValidateEntityVersions));
+
+ session = sessions[1];
+ Assert.That(session.Name, Is.EqualTo(WellKnown.Sessions.System));
+ ValidateAllDefaultExcept(session,
+ ((s) => s.CacheType, SessionCacheType.Infinite),
+ ((s) => s.BatchSize, 30),
+ ((s) => s.Options, SessionOptions.ServerProfile));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithEmptyNameTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionEmptyName", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.Options, SessionOptions.ClientProfile));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithCustomNameTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionCustomName", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.Not.EqualTo(WellKnown.Sessions.Default));
+ Assert.That(sessions[0].Name, Is.EqualTo("UserCreated"));
+
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.Options, SessionOptions.ClientProfile));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithCustomUserNameTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionCustomUser", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.UserName, "User"),
+ ((s) => s.Password, "126654"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithOptionsTest1(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionDefaultOptions", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.Options, SessionOptions.Default));
+
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithOptionsTest2(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionServerProfile", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.Options, SessionOptions.ServerProfile));
+
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithOptionsTest3(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionClientProfile", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.Options, SessionOptions.ClientProfile));
+
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithOptionsTest4(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionCustomOptions", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.Options, SessionOptions.AllowSwitching | SessionOptions.AutoActivation | SessionOptions.ReadRemovedObjects | SessionOptions.ValidateEntityVersions));
+
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithCollectionSizesTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionWithCollectionSizes", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.CacheSize, 399),
+ ((s) => s.BatchSize, 20),
+ ((s) => s.EntityChangeRegistrySize, 255));
+
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionCustomCacheTypeTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionCustomCacheType", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.CacheType, SessionCacheType.Infinite));
+
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionCustomIsolationLevelTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionCustomIsolationLevel", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.DefaultIsolationLevel, IsolationLevel.ReadCommitted));
+
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionCustomCommandTimeoutTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionCustomCommandTimeout", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.DefaultCommandTimeout, (int?)300));
+
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionCustomPreloadingPolicyTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionCustomPreloadingPolicy", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.ReaderPreloading, ReaderPreloadingPolicy.Always));
+
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionCustomConnectionStringTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionCustomConnectionString", useRoot);
+ ValidateAllDefault(domainConfig);
+
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.ConnectionInfo, new ConnectionInfo("_dummy_", "Data Source=localhost;Initial Catalog=DO-Tests;Integrated Security=True;MultipleActiveResultSets=True")));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionCustomConnectionUrlTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithSessionCustomConnectionUrl", useRoot);
+ ValidateAllDefault(domainConfig);
+ var sessions = domainConfig.Sessions;
+ Assert.That(sessions.Count, Is.EqualTo(1));
+ Assert.That(sessions[0].Name, Is.EqualTo(WellKnown.Sessions.Default));
+ ValidateAllDefaultExcept(sessions[0],
+ ((s) => s.ConnectionInfo, new ConnectionInfo("sqlserver://localhost/DO-Tests")));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithInvalidOptions(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithSessionInvalidOptions", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithInvalidCacheSizeTest1(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithSessionInvalidCacheSize1", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithInvalidCacheSizeTest2(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithSessionInvalidCacheSize2", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithInvalidCacheSizeTest3(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithSessionInvalidCacheSize3", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithInvalidBatchSizeTest1(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithSessionInvalidBatchSize1", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithInvalidBatchSizeTest2(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithSessionInvalidBatchSize2", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithInvalidEntityChangeRegistryTest1(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithSessionInvalidEntityChangeRegistry1", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithInvalidEntityChangeRegistryTest2(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithSessionInvalidEntityChangeRegistry2", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SessionWithInvalidCacheType1(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithSessionInvalidCacheType", useRoot));
+ }
+
+ #endregion
+
+ #region Databases configuration
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DatabaseConfigurationOnlyAliasTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithDatabases1", useRoot);
+ ValidateAllDefault(domainConfig);
+ Assert.That(domainConfig.Databases.Count, Is.EqualTo(2));
+ var db1 = domainConfig.Databases[0];
+ Assert.That(db1.Name, Is.EqualTo("main"));
+ Assert.That(db1.RealName, Is.EqualTo("DO-Tests-1"));
+ Assert.That(db1.MinTypeId, Is.EqualTo(Orm.Model.TypeInfo.MinTypeId));
+ Assert.That(db1.MaxTypeId, Is.EqualTo(int.MaxValue));
+ var db2 = domainConfig.Databases[1];
+ Assert.That(db2.Name, Is.EqualTo("other"));
+ Assert.That(db2.RealName, Is.EqualTo("DO-Tests-2"));
+ Assert.That(db2.MinTypeId, Is.EqualTo(Orm.Model.TypeInfo.MinTypeId));
+ Assert.That(db2.MaxTypeId, Is.EqualTo(int.MaxValue));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DatabaseConfigurationWithTypeIdsTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithDatabases2", useRoot);
+ ValidateAllDefault(domainConfig);
+ Assert.That(domainConfig.Databases.Count, Is.EqualTo(2));
+ var db1 = domainConfig.Databases[0];
+ Assert.That(db1.Name, Is.EqualTo("main"));
+ Assert.That(db1.RealName, Is.EqualTo("DO-Tests-1"));
+ Assert.That(db1.MinTypeId, Is.EqualTo(100));
+ Assert.That(db1.MaxTypeId, Is.EqualTo(1000));
+ var db2 = domainConfig.Databases[1];
+ Assert.That(db2.Name, Is.EqualTo("other"));
+ Assert.That(db2.RealName, Is.EqualTo("DO-Tests-2"));
+ Assert.That(db2.MinTypeId, Is.EqualTo(2000));
+ Assert.That(db2.MaxTypeId, Is.EqualTo(3000));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DatabaseConfigurationNegativeMinTypeIdTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidDatabases1", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DatabaseConfigurationInvalidMinTypeIdTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidDatabases2", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DatabaseConfigurationNegativeMaxTypeIdTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidDatabases3", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void DatabaseConfigurationInvalidMaxTypeIdTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidDatabases4", useRoot));
+ }
+
+ #endregion
+
+ #region KeyGenerators
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void SimpleKeyGeneratorTest(bool useRoot)
+ {
+ if (!NameAttributeUnique)
+ throw new IgnoreException("");
+ var domainConfig = LoadDomainConfiguration("DomainWithCustomGenerator", useRoot);
+ ValidateAllDefault(domainConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void KeyGeneratorWithDatabaseNamesTest(bool useRoot)
+ {
+ if (!NameAttributeUnique)
+ throw new IgnoreException("");
+ var domainConfig = LoadDomainConfiguration("DomainWithCustomGeneratorsWithDatabaseNames", useRoot);
+ ValidateAllDefault(domainConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void KeyGeneratorWithDatabaseNamesAllParamsTest(bool useRoot)
+ {
+ if (!NameAttributeUnique)
+ throw new IgnoreException("");
+ var domainConfig = LoadDomainConfiguration("DomainWithCustomGeneratorsWithDatabaseNamesAndKeyParams", useRoot);
+ ValidateAllDefault(domainConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void KeyGeneratorsWithDatabaseAliasesTest(bool useRoot)
+ {
+ if (!NameAttributeUnique)
+ throw new IgnoreException("");
+ var domainConfig = LoadDomainConfiguration("DomainWithCustomGeneratorsWithDatabasesAliases", useRoot);
+ ValidateAllDefault(domainConfig);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void KeyGeneratorsWithConflictByDatabaseTest1(bool useRoot)
+ {
+ if (!NameAttributeUnique)
+ throw new IgnoreException("");
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithCustomGeneratorsConflict1", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void KeyGeneratorsWithConflictByDatabaseTest2(bool useRoot)
+ {
+ if (!NameAttributeUnique)
+ throw new IgnoreException("");
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithCustomGeneratorsConflict2", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void KeyGeneratorWithNegativeSeedTest(bool useRoot)
+ {
+ if (!NameAttributeUnique)
+ throw new IgnoreException("");
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithCustomGeneratorNegativeSeed", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void KeyGeneratorWithNegativeCacheSizeTest(bool useRoot)
+ {
+ if (!NameAttributeUnique)
+ throw new IgnoreException("");
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithCustomGeneratorNegativeCache", useRoot));
+ }
+
+ #endregion
+
+ #region IgnoreRules
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void IgnoreRulesTest(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithIgnoreRules", useRoot);
+ ValidateAllDefault(domainConfig);
+ ValidateIgnoreRules(domainConfig.IgnoreRules);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void IgnoreColumnAndIndexAtTheSameTimeTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidIgnoreRules1", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void IgnoreTableAndColumnAndIndexAtTheSameTimeTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidIgnoreRules2", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void IgnoreDatabaseOnlyTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidIgnoreRules3", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void IgnoreDatabaseAndSchemaOnlyTest(bool useRoot)
+ {
+ _ = Assert.Throws(() => LoadDomainConfiguration("DomainWithInvalidIgnoreRules4", useRoot));
+ }
+
+ private void ValidateIgnoreRules(IgnoreRuleCollection rules)
+ {
+ Assert.That(rules.Count, Is.EqualTo(18));
+
+ var rule = rules[0];
+ Assert.That(rule.Database, Is.EqualTo("Other-DO40-Tests"));
+ Assert.That(rule.Schema, Is.EqualTo("some-schema1"));
+ Assert.That(rule.Table, Is.EqualTo("table1"));
+ Assert.That(rule.Column, Is.Null.Or.Empty);
+ Assert.That(rule.Index, Is.Null.Or.Empty);
+
+ rule = rules[1];
+ Assert.That(rule.Database, Is.EqualTo("some-database"));
+ Assert.That(rule.Schema, Is.EqualTo("some-schema2"));
+ Assert.That(rule.Table, Is.Null.Or.Empty);
+ Assert.That(rule.Column, Is.EqualTo("column2"));
+ Assert.That(rule.Index, Is.Null.Or.Empty);
+
+ rule = rules[2];
+ Assert.That(rule.Database, Is.EqualTo("some-database"));
+ Assert.That(rule.Schema, Is.EqualTo("some-schema2"));
+ Assert.That(rule.Table, Is.Null.Or.Empty);
+ Assert.That(rule.Column, Is.Null.Or.Empty);
+ Assert.That(rule.Index, Is.EqualTo("index2"));
+
+ rule = rules[3];
+ Assert.That(rule.Database, Is.EqualTo("some-database"));
+ Assert.That(rule.Schema, Is.EqualTo("some-schema3"));
+ Assert.That(rule.Table, Is.EqualTo("table2"));
+ Assert.That(rule.Column, Is.EqualTo("col3"));
+ Assert.That(rule.Index, Is.Null.Or.Empty);
+
+ rule = rules[4];
+ Assert.That(rule.Database, Is.EqualTo("some-database"));
+ Assert.That(rule.Schema, Is.EqualTo("some-schema3"));
+ Assert.That(rule.Table, Is.EqualTo("table2"));
+ Assert.That(rule.Column, Is.Null.Or.Empty);
+ Assert.That(rule.Index, Is.EqualTo("index3"));
+
+ rule = rules[5];
+ Assert.That(rule.Database, Is.EqualTo("another-some-database"));
+ Assert.That(rule.Schema, Is.Null.Or.Empty);
+ Assert.That(rule.Table, Is.EqualTo("some-table"));
+ Assert.That(rule.Column, Is.Null.Or.Empty);
+ Assert.That(rule.Index, Is.Null.Or.Empty);
+
+ rule = rules[6];
+ Assert.That(rule.Database, Is.EqualTo("database1"));
+ Assert.That(rule.Schema, Is.Null.Or.Empty);
+ Assert.That(rule.Table, Is.Null.Or.Empty);
+ Assert.That(rule.Column, Is.EqualTo("some-column"));
+ Assert.That(rule.Index, Is.Null.Or.Empty);
+
+ rule = rules[7];
+ Assert.That(rule.Database, Is.EqualTo("database1"));
+ Assert.That(rule.Schema, Is.Null.Or.Empty);
+ Assert.That(rule.Table, Is.Null.Or.Empty);
+ Assert.That(rule.Column, Is.Null.Or.Empty);
+ Assert.That(rule.Index, Is.EqualTo("some-index"));
+
+ rule = rules[8];
+ Assert.That(rule.Database, Is.Null.Or.Empty);
+ Assert.That(rule.Schema, Is.EqualTo("schema1"));
+ Assert.That(rule.Table, Is.EqualTo("table1"));
+ Assert.That(rule.Column, Is.Null.Or.Empty);
+ Assert.That(rule.Index, Is.Null.Or.Empty);
+
+ rule = rules[9];
+ Assert.That(rule.Database, Is.Null.Or.Empty);
+ Assert.That(rule.Schema, Is.EqualTo("schema1"));
+ Assert.That(rule.Table, Is.Null.Or.Empty);
+ Assert.That(rule.Column, Is.EqualTo("column2"));
+ Assert.That(rule.Index, Is.Null.Or.Empty);
+
+ rule = rules[10];
+ Assert.That(rule.Database, Is.Null.Or.Empty);
+ Assert.That(rule.Schema, Is.EqualTo("schema1"));
+ Assert.That(rule.Table, Is.Null.Or.Empty);
+ Assert.That(rule.Column, Is.Null.Or.Empty);
+ Assert.That(rule.Index, Is.EqualTo("index2"));
+
+ rule = rules[11];
+ Assert.That(rule.Database, Is.Null.Or.Empty);
+ Assert.That(rule.Schema, Is.EqualTo("schema1"));
+ Assert.That(rule.Table, Is.EqualTo("table2"));
+ Assert.That(rule.Column, Is.EqualTo("column3"));
+ Assert.That(rule.Index, Is.Null.Or.Empty);
+
+ rule = rules[12];
+ Assert.That(rule.Database, Is.Null.Or.Empty);
+ Assert.That(rule.Schema, Is.EqualTo("schema1"));
+ Assert.That(rule.Table, Is.EqualTo("table2"));
+ Assert.That(rule.Column, Is.Null.Or.Empty);
+ Assert.That(rule.Index, Is.EqualTo("index3"));
+
+ rule = rules[13];
+ Assert.That(rule.Database, Is.Null.Or.Empty);
+ Assert.That(rule.Schema, Is.Null.Or.Empty);
+ Assert.That(rule.Table, Is.EqualTo("table4"));
+ Assert.That(rule.Column, Is.EqualTo("column3"));
+ Assert.That(rule.Index, Is.Null.Or.Empty);
+
+ rule = rules[14];
+ Assert.That(rule.Database, Is.Null.Or.Empty);
+ Assert.That(rule.Schema, Is.Null.Or.Empty);
+ Assert.That(rule.Table, Is.EqualTo("table4"));
+ Assert.That(rule.Column, Is.Null.Or.Empty);
+ Assert.That(rule.Index, Is.EqualTo("index2"));
+
+ rule = rules[15];
+ Assert.That(rule.Database, Is.Null.Or.Empty);
+ Assert.That(rule.Schema, Is.Null.Or.Empty);
+ Assert.That(rule.Table, Is.EqualTo("single-table"));
+ Assert.That(rule.Column, Is.Null.Or.Empty);
+ Assert.That(rule.Index, Is.Null.Or.Empty);
+
+ rule = rules[16];
+ Assert.That(rule.Database, Is.Null.Or.Empty);
+ Assert.That(rule.Schema, Is.Null.Or.Empty);
+ Assert.That(rule.Table, Is.Null.Or.Empty);
+ Assert.That(rule.Column, Is.EqualTo("single-column"));
+ Assert.That(rule.Index, Is.Null.Or.Empty);
+
+ rule = rules[17];
+ Assert.That(rule.Database, Is.Null.Or.Empty);
+ Assert.That(rule.Schema, Is.Null.Or.Empty);
+ Assert.That(rule.Table, Is.Null.Or.Empty);
+ Assert.That(rule.Column, Is.Null.Or.Empty);
+ Assert.That(rule.Index, Is.EqualTo("single-index"));
+ }
+
+ #endregion
+
+ #region MappingRules
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRulesTest1(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithMappingRules1", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.DefaultDatabase, "main"),
+ ((d) => d.DefaultSchema, "dbo"),
+ ((d) => d.IsMultidatabase, true),
+ ((d) => d.IsMultischema, true));
+ var rules = domainConfig.MappingRules;
+
+ Assert.That(rules.Count, Is.EqualTo(1));
+ var rule = rules[0];
+ Assert.That(rule.Assembly, Is.EqualTo(typeof(JsonConfigurationTest).Assembly));
+ Assert.That(rule.Namespace, Is.Null);
+ Assert.That(rule.Database, Is.EqualTo("DO-Tests-1"));
+ Assert.That(rule.Schema, Is.Null);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRulesTest2(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithMappingRules2", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.DefaultDatabase, "main"),
+ ((d) => d.DefaultSchema, "dbo"),
+ ((d) => d.IsMultidatabase, true),
+ ((d) => d.IsMultischema, true));
+ var rules = domainConfig.MappingRules;
+
+ Assert.That(rules.Count, Is.EqualTo(1));
+ var rule = rules[0];
+ Assert.That(rule.Assembly, Is.EqualTo(typeof(JsonConfigurationTest).Assembly));
+ Assert.That(rule.Namespace, Is.Null);
+ Assert.That(rule.Database, Is.Null);
+ Assert.That(rule.Schema, Is.EqualTo("Model1"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRulesTest3(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithMappingRules3", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.DefaultDatabase, "main"),
+ ((d) => d.DefaultSchema, "dbo"),
+ ((d) => d.IsMultidatabase, true),
+ ((d) => d.IsMultischema, true));
+ var rules = domainConfig.MappingRules;
+
+ Assert.That(rules.Count, Is.EqualTo(1));
+ var rule = rules[0];
+
+ Assert.That(rule.Assembly, Is.Null);
+ Assert.That(rule.Namespace, Is.EqualTo("Xtensive.Orm.Configuration.Options"));
+ Assert.That(rule.Database, Is.EqualTo("DO-Tests-2"));
+ Assert.That(rule.Schema, Is.Null);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRulesTest4(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithMappingRules4", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.DefaultDatabase, "main"),
+ ((d) => d.DefaultSchema, "dbo"),
+ ((d) => d.IsMultidatabase, true),
+ ((d) => d.IsMultischema, true));
+ var rules = domainConfig.MappingRules;
+
+ Assert.That(rules.Count, Is.EqualTo(1));
+ var rule = rules[0];
+
+ Assert.That(rule.Assembly, Is.Null);
+ Assert.That(rule.Namespace, Is.EqualTo("Xtensive.Orm.Tests.Configuration"));
+ Assert.That(rule.Database, Is.Null);
+ Assert.That(rule.Schema, Is.EqualTo("Model2"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRulesTest5(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithMappingRules5", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.DefaultDatabase, "main"),
+ ((d) => d.DefaultSchema, "dbo"),
+ ((d) => d.IsMultidatabase, true),
+ ((d) => d.IsMultischema, true));
+ var rules = domainConfig.MappingRules;
+
+ Assert.That(rules.Count, Is.EqualTo(1));
+ var rule = rules[0];
+
+ Assert.That(rule.Assembly, Is.EqualTo(typeof (JsonConfigurationTest).Assembly));
+ Assert.That(rule.Namespace, Is.EqualTo("Xtensive.Orm.Tests.Configuration.TypesToUseInTests"));
+ Assert.That(rule.Database, Is.EqualTo("DO-Tests-3"));
+ Assert.That(rule.Schema, Is.Null);
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRulesTest6(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithMappingRules6", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.DefaultDatabase, "main"),
+ ((d) => d.DefaultSchema, "dbo"),
+ ((d) => d.IsMultidatabase, true),
+ ((d) => d.IsMultischema, true));
+ var rules = domainConfig.MappingRules;
+
+ Assert.That(rules.Count, Is.EqualTo(1));
+ var rule = rules[0];
+ Assert.That(rule.Assembly, Is.EqualTo(typeof(JsonConfigurationTest).Assembly));
+ Assert.That(rule.Namespace, Is.EqualTo("Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace"));
+ Assert.That(rule.Database, Is.Null);
+ Assert.That(rule.Schema, Is.EqualTo("Model3"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRulesTest7(bool useRoot)
+ {
+ var domainConfig = LoadDomainConfiguration("DomainWithMappingRules7", useRoot);
+ ValidateAllDefaultExcept(domainConfig,
+ ((d) => d.DefaultDatabase, "main"),
+ ((d) => d.DefaultSchema, "dbo"),
+ ((d) => d.IsMultidatabase, true),
+ ((d) => d.IsMultischema, true));
+ var rules = domainConfig.MappingRules;
+
+ Assert.That(rules.Count, Is.EqualTo(1));
+ var rule = rules[0];
+ Assert.That(rule.Assembly, Is.EqualTo(typeof(JsonConfigurationTest).Assembly));
+ Assert.That(rule.Namespace, Is.EqualTo("Xtensive.Orm.Tests.Indexing"));
+ Assert.That(rule.Database, Is.EqualTo("main"));
+ Assert.That(rule.Schema, Is.EqualTo("Model4"));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRuleWithConflictByAssemblyTest(bool useRoot)
+ {
+ var exception = Assert.Throws(
+ () => LoadDomainConfiguration("DomainWithConflictMappingRules1", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRuleWithConflictByNamespaceTest(bool useRoot)
+ {
+ var exception = Assert.Throws(
+ () => LoadDomainConfiguration("DomainWithConflictMappingRules2", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRulesInvalidTest1(bool useRoot)
+ {
+ var exception = Assert.Throws(
+ () => LoadDomainConfiguration("DomainWithInvalidMappingRules1", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRulesInvalidTest2(bool useRoot)
+ {
+ var exception = Assert.Throws(
+ () => LoadDomainConfiguration("DomainWithInvalidMappingRules2", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRulesInvalidTest3(bool useRoot)
+ {
+ var exception = Assert.Throws(
+ () => LoadDomainConfiguration("DomainWithInvalidMappingRules3", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRulesInvalidTest4(bool useRoot)
+ {
+ var exception = Assert.Throws(
+ () => LoadDomainConfiguration("DomainWithInvalidMappingRules4", useRoot));
+ }
+
+ [Test]
+ [TestCase(true)]
+ [TestCase(false)]
+ public void MappingRulesInvalidTest5(bool useRoot)
+ {
+ var exception = Assert.Throws(
+ () => LoadDomainConfiguration("DomainWithInvalidMappingRules5", useRoot));
+ }
+
+ #endregion
+
+ #region Logging
+
+ [Test]
+ public void LoggingConfigurationTest()
+ {
+ var configuration = LoadLoggingConfiguration();
+ ValidateLoggingConfiguration(configuration);
+ }
+
+ [Test]
+ public void LoggingEmptyLoggingSectionTest()
+ {
+ var section = configuration.GetSection($"Xtensive.Orm.EmptyLogging.{Postfix}");
+ _ = Assert.Throws(() => LoadLoggingConfiguration(section));
+ }
+
+ [Test]
+ public void LoggingEmptyLogsTest()
+ {
+ if (Postfix == "AppConfig")
+ throw new IgnoreException("");
+
+ var section = configuration.GetSection($"Xtensive.Orm.EmptyLogs.{Postfix}");
+ _ = Assert.Throws(() => LoadLoggingConfiguration(section));
+ }
+
+ [Test]
+ public void LoggingOnlyProviderDeclaredTest()
+ {
+ var section = configuration.GetSection($"Xtensive.Orm.OnlyLogProvider.{Postfix}");
+ var loggingConfig = LoadLoggingConfiguration(section);
+ Assert.That(loggingConfig.Logs.Count, Is.EqualTo(0));
+ Assert.That(loggingConfig.Provider, Is.EqualTo("Xtensive.Orm.Logging.log4net.LogProvider"));
+ }
+
+ [Test]
+ public void LoggingProviderAndEmptyLogsTest()
+ {
+ if (Postfix == "AppConfig")
+ throw new IgnoreException("");
+
+ var section = configuration.GetSection($"Xtensive.Orm.LogProviderAndEmptyLogs.{Postfix}");
+ var loggingConfig = LoadLoggingConfiguration(section);
+ Assert.That(loggingConfig.Logs.Count, Is.EqualTo(0));
+ Assert.That(loggingConfig.Provider, Is.EqualTo("Xtensive.Orm.Logging.log4net.LogProvider"));
+ }
+
+ #endregion
+
+ private void ValidateAllDefault(DomainConfiguration domainConfiguration)
+ {
+ Assert.That(domainConfiguration.AllowCyclicDatabaseDependencies, Is.EqualTo(DomainConfiguration.DefaultAllowCyclicDatabaseDependencies));
+ Assert.That(domainConfiguration.BuildInParallel, Is.EqualTo(DomainConfiguration.DefaultBuildInParallel));
+ Assert.That(domainConfiguration.Collation, Is.EqualTo(string.Empty));
+ Assert.That(domainConfiguration.ConnectionInitializationSql, Is.EqualTo(string.Empty));
+ Assert.That(domainConfiguration.DefaultDatabase, Is.EqualTo(string.Empty));
+ Assert.That(domainConfiguration.DefaultSchema, Is.EqualTo(string.Empty));
+ Assert.That(domainConfiguration.EnsureConnectionIsAlive, Is.EqualTo(DomainConfiguration.DefaultEnsureConnectionIsAlive));
+ Assert.That(domainConfiguration.ForcedServerVersion, Is.EqualTo(string.Empty));
+ Assert.That(domainConfiguration.ForeignKeyMode, Is.EqualTo(DomainConfiguration.DefaultForeignKeyMode));
+ Assert.That(domainConfiguration.FullTextChangeTrackingMode, Is.EqualTo(DomainConfiguration.DefaultFullTextChangeTrackingMode));
+ Assert.That(domainConfiguration.IncludeSqlInExceptions, Is.EqualTo(DomainConfiguration.DefaultIncludeSqlInExceptions));
+ Assert.That(domainConfiguration.IsMultidatabase, Is.False);
+ Assert.That(domainConfiguration.IsMultischema, Is.False);
+ Assert.That(domainConfiguration.KeyCacheSize, Is.EqualTo(DomainConfiguration.DefaultKeyCacheSize));
+ Assert.That(domainConfiguration.KeyGeneratorCacheSize, Is.EqualTo(DomainConfiguration.DefaultKeyGeneratorCacheSize));
+ Assert.That(domainConfiguration.MultidatabaseKeys, Is.EqualTo(DomainConfiguration.DefaultMultidatabaseKeys));
+ Assert.That(domainConfiguration.Options, Is.EqualTo(DomainConfiguration.DefaultDomainOptions));
+ Assert.That(domainConfiguration.PreferTypeIdsAsQueryParameters, Is.EqualTo(DomainConfiguration.DefaultPreferTypeIdsAsQueryParameters));
+ Assert.That(domainConfiguration.QueryCacheSize, Is.EqualTo(DomainConfiguration.DefaultQueryCacheSize));
+ Assert.That(domainConfiguration.RecordSetMappingCacheSize, Is.EqualTo(DomainConfiguration.DefaultRecordSetMappingCacheSize));
+ Assert.That(domainConfiguration.SchemaSyncExceptionFormat, Is.EqualTo(DomainConfiguration.DefaultSchemaSyncExceptionFormat));
+ Assert.That(domainConfiguration.ShareStorageSchemaOverNodes, Is.EqualTo(DomainConfiguration.DefaultShareStorageSchemaOverNodes));
+ Assert.That(domainConfiguration.TagsLocation, Is.EqualTo(DomainConfiguration.DefaultTagLocation));
+ Assert.That(domainConfiguration.UpgradeMode, Is.EqualTo(DomainConfiguration.DefaultUpgradeMode));
+ }
+
+ private void ValidateAllDefaultExcept(TConfiguration configuration,
+ (Expression> expression, T1 expectedValue) property)
+ {
+ Assert.That(configuration, Is.Not.Null);
+ var excludedProperties = new List();
+
+
+ if (!TryExtractPropertyFormLambda(property.expression, out var prop))
+ throw new InconclusiveException("");
+
+ Assert.That(property.expression.Compile()(configuration),
+ Is.EqualTo(property.expectedValue));
+
+ excludedProperties.Add(prop.Name);
+
+ if (configuration is DomainConfiguration domainConfiguration)
+ ValidateAllPropertiesExcept(domainConfiguration, excludedProperties);
+ else if (configuration is SessionConfiguration sessionConfiguration)
+ ValidateAllPropertiesExcept(sessionConfiguration, excludedProperties);
+ else
+ throw new ArgumentOutOfRangeException(nameof(configuration));
+
+ }
+
+
+ private void ValidateAllDefaultExcept(TConfiguration configuration,
+ (Expression> expression, T1 expectedValue) property1,
+ (Expression> expression, T2 expectedValue) property2)
+ {
+ Assert.That(configuration, Is.Not.Null);
+ var excludedProperties = new List();
+
+ if (!TryExtractPropertyFormLambda(property1.expression, out var prop))
+ throw new InconclusiveException("");
+
+ Assert.That(property1.expression.Compile()(configuration),
+ Is.EqualTo(property1.expectedValue));
+
+ excludedProperties.Add(prop.Name);
+
+ if (!TryExtractPropertyFormLambda(property2.expression, out prop))
+ throw new InconclusiveException("");
+
+ Assert.That(property2.expression.Compile()(configuration),
+ Is.EqualTo(property2.expectedValue));
+
+ excludedProperties.Add(prop.Name);
+
+ if(configuration is DomainConfiguration domainConfiguration)
+ ValidateAllPropertiesExcept(domainConfiguration, excludedProperties);
+ else if (configuration is SessionConfiguration sessionConfiguration)
+ ValidateAllPropertiesExcept(sessionConfiguration, excludedProperties);
+ else
+ throw new ArgumentOutOfRangeException(nameof(configuration));
+ }
+
+ private void ValidateAllDefaultExcept(TConfiguration configuration,
+ (Expression> expression, T1 expectedValue) property1,
+ (Expression> expression, T2 expectedValue) property2,
+ (Expression> expression, T3 expectedValue) property3)
+ {
+ Assert.That(configuration, Is.Not.Null);
+ var excludedProperties = new List();
+
+ if (!TryExtractPropertyFormLambda(property1.expression, out var prop))
+ throw new InconclusiveException("");
+
+ Assert.That(property1.expression.Compile()(configuration),
+ Is.EqualTo(property1.expectedValue));
+
+ excludedProperties.Add(prop.Name);
+
+ if (!TryExtractPropertyFormLambda(property2.expression, out prop))
+ throw new InconclusiveException("");
+
+ Assert.That(property2.expression.Compile()(configuration),
+ Is.EqualTo(property2.expectedValue));
+
+ excludedProperties.Add(prop.Name);
+
+ if (!TryExtractPropertyFormLambda(property3.expression, out prop))
+ throw new InconclusiveException("");
+
+ Assert.That(property3.expression.Compile()(configuration),
+ Is.EqualTo(property3.expectedValue));
+
+ excludedProperties.Add(prop.Name);
+
+ if (configuration is DomainConfiguration domainConfiguration)
+ ValidateAllPropertiesExcept(domainConfiguration, excludedProperties);
+ else if (configuration is SessionConfiguration sessionConfiguration)
+ ValidateAllPropertiesExcept(sessionConfiguration, excludedProperties);
+ else
+ throw new ArgumentOutOfRangeException(nameof(configuration));
+ }
+
+ private void ValidateAllDefaultExcept(TConfiguration configuration,
+ (Expression> expression, T1 expectedValue) property1,
+ (Expression> expression, T2 expectedValue) property2,
+ (Expression> expression, T3 expectedValue) property3,
+ (Expression> expression, T4 expectedValue) property4)
+ {
+ Assert.That(configuration, Is.Not.Null);
+ var excludedProperties = new List();
+
+ if (!TryExtractPropertyFormLambda(property1.expression, out var prop))
+ throw new InconclusiveException("");
+
+ Assert.That(property1.expression.Compile()(configuration),
+ Is.EqualTo(property1.expectedValue));
+
+ excludedProperties.Add(prop.Name);
+
+ if (!TryExtractPropertyFormLambda(property2.expression, out prop))
+ throw new InconclusiveException("");
+
+ Assert.That(property2.expression.Compile()(configuration),
+ Is.EqualTo(property2.expectedValue));
+
+ excludedProperties.Add(prop.Name);
+
+ if (!TryExtractPropertyFormLambda(property3.expression, out prop))
+ throw new InconclusiveException("");
+
+ Assert.That(property3.expression.Compile()(configuration),
+ Is.EqualTo(property3.expectedValue));
+
+ excludedProperties.Add(prop.Name);
+
+ if (!TryExtractPropertyFormLambda(property4.expression, out prop))
+ throw new InconclusiveException("");
+
+ Assert.That(property4.expression.Compile()(configuration),
+ Is.EqualTo(property4.expectedValue));
+
+ excludedProperties.Add(prop.Name);
+
+ if (configuration is DomainConfiguration domainConfiguration)
+ ValidateAllPropertiesExcept(domainConfiguration, excludedProperties);
+ else if (configuration is SessionConfiguration sessionConfiguration)
+ ValidateAllPropertiesExcept(sessionConfiguration, excludedProperties);
+ else
+ throw new ArgumentOutOfRangeException(nameof(configuration));
+ }
+
+ private bool TryExtractPropertyFormLambda(Expression> lambda,
+ out System.Reflection.PropertyInfo property)
+ {
+ if (lambda.Body.StripCasts() is MemberExpression mExpression && mExpression.Member is System.Reflection.PropertyInfo prop) {
+ property = prop;
+ return true;
+ }
+ property = null;
+ return false;
+ }
+
+ private void ValidateAllPropertiesExcept(DomainConfiguration domainConfiguration, List excludedProperties)
+ {
+ if (!nameof(domainConfiguration.AllowCyclicDatabaseDependencies).In(excludedProperties))
+ Assert.That(domainConfiguration.AllowCyclicDatabaseDependencies, Is.EqualTo(DomainConfiguration.DefaultAllowCyclicDatabaseDependencies));
+
+ if (!nameof(domainConfiguration.BuildInParallel).In(excludedProperties))
+ Assert.That(domainConfiguration.BuildInParallel, Is.EqualTo(DomainConfiguration.DefaultBuildInParallel));
+
+ if (!nameof(domainConfiguration.Collation).In(excludedProperties))
+ Assert.That(domainConfiguration.Collation, Is.EqualTo(string.Empty));
+
+ if (!nameof(domainConfiguration.ConnectionInitializationSql).In(excludedProperties))
+ Assert.That(domainConfiguration.ConnectionInitializationSql, Is.EqualTo(string.Empty));
+
+ if (!nameof(domainConfiguration.DefaultDatabase).In(excludedProperties))
+ Assert.That(domainConfiguration.DefaultDatabase, Is.EqualTo(string.Empty));
+
+ if (!nameof(domainConfiguration.DefaultSchema).In(excludedProperties))
+ Assert.That(domainConfiguration.DefaultSchema, Is.EqualTo(string.Empty));
+
+ if (!nameof(domainConfiguration.EnsureConnectionIsAlive).In(excludedProperties))
+ Assert.That(domainConfiguration.EnsureConnectionIsAlive, Is.EqualTo(DomainConfiguration.DefaultEnsureConnectionIsAlive));
+
+ if (!nameof(domainConfiguration.ForcedServerVersion).In(excludedProperties))
+ Assert.That(domainConfiguration.ForcedServerVersion, Is.EqualTo(string.Empty));
+
+ if (!nameof(domainConfiguration.ForeignKeyMode).In(excludedProperties))
+ Assert.That(domainConfiguration.ForeignKeyMode, Is.EqualTo(DomainConfiguration.DefaultForeignKeyMode));
+
+ if (!nameof(domainConfiguration.FullTextChangeTrackingMode).In(excludedProperties))
+ Assert.That(domainConfiguration.FullTextChangeTrackingMode, Is.EqualTo(DomainConfiguration.DefaultFullTextChangeTrackingMode));
+
+ if (!nameof(domainConfiguration.IncludeSqlInExceptions).In(excludedProperties))
+ Assert.That(domainConfiguration.IncludeSqlInExceptions, Is.EqualTo(DomainConfiguration.DefaultIncludeSqlInExceptions));
+
+ if (!nameof(domainConfiguration.IsMultidatabase).In(excludedProperties))
+ Assert.That(domainConfiguration.IsMultidatabase, Is.False);
+
+ if (!nameof(domainConfiguration.IsMultischema).In(excludedProperties))
+ Assert.That(domainConfiguration.IsMultischema, Is.False);
+
+ if (!nameof(domainConfiguration.KeyCacheSize).In(excludedProperties))
+ Assert.That(domainConfiguration.KeyCacheSize, Is.EqualTo(DomainConfiguration.DefaultKeyCacheSize));
+
+ if (!nameof(domainConfiguration.KeyGeneratorCacheSize).In(excludedProperties))
+ Assert.That(domainConfiguration.KeyGeneratorCacheSize, Is.EqualTo(DomainConfiguration.DefaultKeyGeneratorCacheSize));
+
+ if (!nameof(domainConfiguration.MultidatabaseKeys).In(excludedProperties))
+ Assert.That(domainConfiguration.MultidatabaseKeys, Is.EqualTo(DomainConfiguration.DefaultMultidatabaseKeys));
+
+ if (!nameof(domainConfiguration.Options).In(excludedProperties))
+ Assert.That(domainConfiguration.Options, Is.EqualTo(DomainConfiguration.DefaultDomainOptions));
+
+ if (!nameof(domainConfiguration.PreferTypeIdsAsQueryParameters).In(excludedProperties))
+ Assert.That(domainConfiguration.PreferTypeIdsAsQueryParameters, Is.EqualTo(DomainConfiguration.DefaultPreferTypeIdsAsQueryParameters));
+
+ if (!nameof(domainConfiguration.QueryCacheSize).In(excludedProperties))
+ Assert.That(domainConfiguration.QueryCacheSize, Is.EqualTo(DomainConfiguration.DefaultQueryCacheSize));
+
+ if (!nameof(domainConfiguration.RecordSetMappingCacheSize).In(excludedProperties))
+ Assert.That(domainConfiguration.RecordSetMappingCacheSize, Is.EqualTo(DomainConfiguration.DefaultRecordSetMappingCacheSize));
+
+ if (!nameof(domainConfiguration.SchemaSyncExceptionFormat).In(excludedProperties))
+ Assert.That(domainConfiguration.SchemaSyncExceptionFormat, Is.EqualTo(DomainConfiguration.DefaultSchemaSyncExceptionFormat));
+
+ if (!nameof(domainConfiguration.ShareStorageSchemaOverNodes).In(excludedProperties))
+ Assert.That(domainConfiguration.ShareStorageSchemaOverNodes, Is.EqualTo(DomainConfiguration.DefaultShareStorageSchemaOverNodes));
+
+ if (!nameof(domainConfiguration.TagsLocation).In(excludedProperties))
+ Assert.That(domainConfiguration.TagsLocation, Is.EqualTo(DomainConfiguration.DefaultTagLocation));
+
+ if (!nameof(domainConfiguration.UpgradeMode).In(excludedProperties))
+ Assert.That(domainConfiguration.UpgradeMode, Is.EqualTo(DomainConfiguration.DefaultUpgradeMode));
+ }
+
+ private void ValidateAllPropertiesExcept(SessionConfiguration sessionConfiguration, List excludedProperties)
+ {
+ if (!nameof(sessionConfiguration.UserName).In(excludedProperties))
+ Assert.That(sessionConfiguration.UserName, Is.Empty);
+
+ if (!nameof(sessionConfiguration.Password).In(excludedProperties))
+ Assert.That(sessionConfiguration.Password, Is.Empty);
+
+ if (!nameof(sessionConfiguration.Options).In(excludedProperties))
+ Assert.That(sessionConfiguration.Options, Is.EqualTo(SessionConfiguration.DefaultSessionOptions));
+
+ if (!nameof(sessionConfiguration.CacheSize).In(excludedProperties))
+ Assert.That(sessionConfiguration.CacheSize, Is.EqualTo(SessionConfiguration.DefaultCacheSize));
+
+ if (!nameof(sessionConfiguration.CacheType).In(excludedProperties))
+ Assert.That(sessionConfiguration.CacheType, Is.EqualTo(SessionConfiguration.DefaultCacheType));
+
+ if (!nameof(sessionConfiguration.DefaultIsolationLevel).In(excludedProperties))
+ Assert.That(sessionConfiguration.DefaultIsolationLevel, Is.EqualTo(SessionConfiguration.DefaultDefaultIsolationLevel));
+
+ if (!nameof(sessionConfiguration.DefaultCommandTimeout).In(excludedProperties))
+ Assert.That(sessionConfiguration.DefaultCommandTimeout, Is.Null);
+
+ if (!nameof(sessionConfiguration.BatchSize).In(excludedProperties))
+ Assert.That(sessionConfiguration.BatchSize, Is.EqualTo(SessionConfiguration.DefaultBatchSize));
+
+ if (!nameof(sessionConfiguration.EntityChangeRegistrySize).In(excludedProperties))
+ Assert.That(sessionConfiguration.EntityChangeRegistrySize, Is.EqualTo(SessionConfiguration.DefaultEntityChangeRegistrySize));
+
+ if (!nameof(sessionConfiguration.ReaderPreloading).In(excludedProperties))
+ Assert.That(sessionConfiguration.ReaderPreloading, Is.EqualTo(SessionConfiguration.DefaultReaderPreloadingPolicy));
+
+ if (!nameof(sessionConfiguration.ServiceContainerType).In(excludedProperties))
+ Assert.That(sessionConfiguration.ServiceContainerType, Is.Null);
+
+ if (!nameof(sessionConfiguration.ConnectionInfo).In(excludedProperties))
+ Assert.That(sessionConfiguration.ConnectionInfo, Is.Null);
+ }
+
+ private void ValidateLoggingConfiguration(LoggingConfiguration configuration)
+ {
+ Assert.That(configuration.Provider, Is.Not.Null.Or.Empty);
+ Assert.That(configuration.Provider, Is.EqualTo("Xtensive.Orm.Logging.log4net.LogProvider"));
+
+ Assert.That(configuration.Logs[0].Source, Is.EqualTo("*"));
+ Assert.That(configuration.Logs[0].Target, Is.EqualTo("Console"));
+
+ Assert.That(configuration.Logs[1].Source, Is.EqualTo("SomeLogName"));
+ Assert.That(configuration.Logs[1].Target, Is.EqualTo("DebugOnlyConsole"));
+
+ Assert.That(configuration.Logs[2].Source, Is.EqualTo("FirstLogName,SecondLogName"));
+ Assert.That(configuration.Logs[2].Target, Is.EqualTo(@"d:\log.txt"));
+
+ Assert.That(configuration.Logs[3].Source, Is.EqualTo("LogName, AnotherLogName"));
+ Assert.That(configuration.Logs[3].Target, Is.EqualTo("Console"));
+
+ Assert.That(configuration.Logs[4].Source, Is.EqualTo("FileLog"));
+ Assert.That(configuration.Logs[4].Target, Is.EqualTo("log.txt"));
+
+ Assert.That(configuration.Logs[5].Source, Is.EqualTo("NullLog"));
+ Assert.That(configuration.Logs[5].Target, Is.EqualTo("None"));
+
+ Assert.That(configuration.Logs[6].Source, Is.EqualTo("Trash"));
+ Assert.That(configuration.Logs[6].Target, Is.EqualTo("skjdhfjsdf sdfsdfksjdghj fgdfg"));
+ }
+ }
+}
diff --git a/Orm/Xtensive.Orm.Tests/Xtensive.Orm.Tests.csproj b/Orm/Xtensive.Orm.Tests/Xtensive.Orm.Tests.csproj
index ed55956c3b..f048eb412e 100644
--- a/Orm/Xtensive.Orm.Tests/Xtensive.Orm.Tests.csproj
+++ b/Orm/Xtensive.Orm.Tests/Xtensive.Orm.Tests.csproj
@@ -19,7 +19,6 @@
-
@@ -28,13 +27,21 @@
-
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
Always
@@ -96,4 +103,5 @@
TwoPartsModel.tt
+
\ No newline at end of file
diff --git a/Orm/Xtensive.Orm.Tests/domainSettings.config b/Orm/Xtensive.Orm.Tests/domainSettings.config
new file mode 100644
index 0000000000..7b6c54deb4
--- /dev/null
+++ b/Orm/Xtensive.Orm.Tests/domainSettings.config
@@ -0,0 +1,2453 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sqlite
+ Data Source=DO-Testsaaa.db3
+
+
+ sqlite:///DO-Tests.db3
+
+
+
+ sqlserver://localhost/DO40-Tests
+ 192
+
+
+ sqlserver://localhost/DO40-Tests
+ -256
+
+
+
+ sqlserver://localhost/DO40-Tests
+ 192
+
+
+ sqlserver://localhost/DO40-Tests
+ -256
+
+
+
+ sqlserver://localhost/DO40-Tests
+ 192
+
+
+ sqlserver://localhost/DO40-Tests
+ -256
+
+
+
+ sqlserver://localhost/DO40-Tests
+ 192
+
+
+ sqlserver://localhost/DO40-Tests
+ -256
+
+
+
+ sqlserver://localhost/DO40-Tests
+ MyFancyDatabase
+
+
+
+ sqlserver://localhost/DO40-Tests
+ MyFancySchema
+
+
+
+
+
+
+
+ sqlserver://localhost/DO40-Tests
+ Default
+
+
+ sqlserver://localhost/DO40-Tests
+ Recreate
+
+
+ sqlserver://localhost/DO40-Tests
+ Perform
+
+
+ sqlserver://localhost/DO40-Tests
+ PerformSafely
+
+
+ sqlserver://localhost/DO40-Tests
+ Validate
+
+
+ sqlserver://localhost/DO40-Tests
+ LegacyValidate
+
+
+ sqlserver://localhost/DO40-Tests
+ Skip
+
+
+ sqlserver://localhost/DO40-Tests
+ LegacySkip
+
+
+
+ sqlserver://localhost/DO40-Tests
+ Defailt
+
+
+
+ sqlserver://localhost/DO40-Tests
+ None
+
+
+ sqlserver://localhost/DO40-Tests
+ Hierarchy
+
+
+ sqlserver://localhost/DO40-Tests
+ Reference
+
+
+ sqlserver://localhost/DO40-Tests
+ All
+
+
+ sqlserver://localhost/DO40-Tests
+ Default
+
+
+ sqlserver://localhost/DO40-Tests
+ Hierarchy,Reference
+
+
+
+ sqlserver://localhost/DO40-Tests
+ Defailt
+
+
+
+ sqlserver://localhost/
+ Off
+
+
+ sqlserver://localhost/
+ Auto
+
+
+ sqlserver://localhost/
+ Manual
+
+
+ sqlserver://localhost/
+ OffWithNoPopulation
+
+
+ sqlserver://localhost/
+ Default
+
+
+
+ sqlserver://localhost/
+ Defailt
+
+
+
+ sqlserver://localhost/
+ Default
+
+
+ sqlserver://localhost/
+ None
+
+
+
+ sqlserver://localhost/
+ Defailt
+
+
+
+ sqlserver://localhost/
+ generalci
+
+
+
+ sqlserver://localhost/
+ Brief
+
+
+ sqlserver://localhost/
+ Detailed
+
+
+ sqlserver://localhost/
+ Default
+
+
+ sqlserver://localhost/
+ Defailt
+
+
+
+ sqlserver://localhost/
+ Nowhere
+
+
+ sqlserver://localhost/
+ BeforeStatement
+
+
+ sqlserver://localhost/
+ WithinStatement
+
+
+ sqlserver://localhost/
+ AfterStatement
+
+
+ sqlserver://localhost/
+ Default
+
+
+ sqlserver://localhost/
+ Defailt
+
+
+
+ sqlserver://localhost/
+ 10.0.0.0
+
+
+
+ sqlserver://localhost/
+ use [OtherDb]
+
+
+
+ sqlserver://localhost/
+ true
+
+
+ sqlserver://localhost/
+ false
+
+
+ sqlserver://localhost/
+ true
+
+
+ sqlserver://localhost/
+ false
+
+
+
+ sqlserver://localhost/
+ true
+
+
+ sqlserver://localhost/
+ false
+
+
+
+ sqlserver://localhost/
+ true
+
+
+ sqlserver://localhost/
+ false
+
+
+
+ sqlserver://localhost/
+ true
+
+
+ sqlserver://localhost/
+ false
+
+
+
+ sqlserver://localhost/
+ true
+
+
+ sqlserver://localhost/
+ false
+
+
+
+ sqlserver://localhost/
+ true
+
+
+ sqlserver://localhost/
+ false
+
+
+
+ sqlserver://localhost/
+
+ Uppercase
+ Synonymize
+ UnderscoreHyphens,RemoveDots
+
+
+ Xtensive.Orm
+ system
+
+
+ Xtensive.Orm.Tests
+ theRest
+
+
+
+
+
+
+ sqlserver://localhost/
+
+ Lowercase
+ Synonymize
+ UnderscoreHyphens,RemoveDots
+
+
+ Xtensive.Orm
+ system
+
+
+ Xtensive.Orm.Tests
+ theRest
+
+
+
+
+
+
+ sqlserver://localhost/
+
+ AsIs
+ Synonymize
+ UnderscoreHyphens,RemoveDots
+
+
+ Xtensive.Orm
+ system
+
+
+ Xtensive.Orm.Tests
+ theRest
+
+
+
+
+
+
+ sqlserver://localhost/
+
+ Default
+ Synonymize
+ UnderscoreHyphens,RemoveDots
+
+
+ Xtensive.Orm
+ system
+
+
+ Xtensive.Orm.Tests
+ theRest
+
+
+
+
+
+
+ sqlserver://localhost/
+
+ Uppercase
+ AsIs
+ UnderscoreHyphens,RemoveDots
+
+
+
+
+ sqlserver://localhost/
+
+ Uppercase
+ Hash
+ UnderscoreHyphens,RemoveDots
+
+
+
+
+ sqlserver://localhost/
+
+ Uppercase
+ Omit
+ UnderscoreHyphens,RemoveDots
+
+
+
+
+ sqlserver://localhost/
+
+ Uppercase
+ Default
+ UnderscoreHyphens,RemoveDots
+
+
+
+
+ sqlserver://localhost/
+
+ Uppercase
+ Hash
+ UnderscoreDots,RemoveHyphens
+
+
+
+
+ sqlserver://localhost/
+
+ Uppercase
+ Hash
+ None
+
+
+
+
+ sqlserver://localhost/
+
+ Uppercase
+ Hash
+ Default
+
+
+
+
+ sqlserver://localhost/
+
+ Defailt
+ Default
+ Default
+
+
+
+
+ sqlserver://localhost/
+
+ Default
+ Defailt
+ Default
+
+
+
+
+ sqlserver://localhost/
+
+ Default
+ Default
+ Defailt
+
+
+
+
+
+ sqlserver://localhost/
+
+ Pessimistic
+
+
+
+
+ sqlserver://localhost/
+
+ Optimistic
+
+
+
+
+ sqlserver://localhost/
+
+ Default
+
+
+
+
+ sqlserver://localhost/
+
+ Optimistic
+ true
+
+
+
+
+ sqlserver://localhost/
+
+ Optimistic
+ false
+
+
+
+
+ sqlserver://localhost/
+
+ Defauit
+
+
+
+
+ sqlserver://localhost/
+
+
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests
+
+
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity2, Xtensive.Orm.Tests
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Xtensive.Orm
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Xtensive.Orm.Tests
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests
+
+
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity2, Xtensive.Orm.Tests
+
+
+ Xtensive.Orm
+
+
+ Xtensive.Orm.Tests
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests
+
+
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Xtensive.Orm
+
+
+ Xtensive.Orm
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Xtensive.Orm.Tests
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace
+
+
+ Xtensive.Orm.Tests
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests
+ Xtensive.Orm
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests
+ Xtensive.Orm
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace
+
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ main
+ DO-Tests-1
+
+
+ other
+ DO-Tests-2
+
+
+
+
+ sqlserver://localhost/
+
+
+ main
+ DO-Tests-1
+ 100
+ 1000
+
+
+ other
+ DO-Tests-2
+ 2000
+ 3000
+
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ main
+ DO-Tests-1
+ -10
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ main
+ DO-Tests-1
+ 50
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ main
+ DO-Tests-1
+ -10
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ main
+ DO-Tests-1
+ 50
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Int32
+ 12
+ 127
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Int32
+ DO-Tests-1
+
+
+ Int32
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Int32
+ DO-Tests-1
+ 12
+ 127
+
+
+ Int32
+ DO-Tests-2
+ 13
+ 129
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Int32
+ main
+ 12
+ 127
+
+
+ Int32
+ other
+ 13
+ 129
+
+
+
+
+ main
+ DO-Tests-1
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Int32
+ DO-Tests-1
+
+
+ Int32
+ DO-Tests-1
+
+
+
+
+ sqlserver://localhost/
+
+
+ Int32
+ DO-Tests-1
+
+
+ Int32
+ main
+
+
+
+
+ main
+ DO-Tests-1
+
+
+ other
+ DO-Tests-2
+
+
+
+
+ sqlserver://localhost/
+
+
+ Int32
+ DO-Tests-1
+ -12
+
+
+
+
+ sqlserver://localhost/
+
+
+ Int32
+ DO-Tests-1
+ -127
+
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Other-DO40-Tests
+ some-schema1
+
+
+
+ some-database
+ some-schema2
+ column2
+
+
+ some-database
+ some-schema2
+ index2
+
+
+ some-database
+ some-schema3
+
+ col3
+
+
+ some-database
+ some-schema3
+
+ index3
+
+
+ another-some-database
+
+
+
+ database1
+ some-column
+
+
+ database1
+ some-index
+
+
+ schema1
+
+
+
+ schema1
+ column2
+
+
+ schema1
+ index2
+
+
+ schema1
+
+ column3
+
+
+ schema1
+
+ index3
+
+
+
+ column3
+
+
+
+ index2
+
+
+
+
+
+ single-column
+
+
+ single-index
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ column1
+ index1
+
+
+
+
+ sqlserver://localhost/
+
+
+
+ column1
+ index1
+
+
+
+
+ sqlserver://localhost/
+
+
+ database1
+
+
+
+
+ sqlserver://localhost/
+
+
+ Other-DO40-Tests
+ some-schema1
+
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Xtensive.Orm.Tests
+ DO-Tests-1
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Xtensive.Orm.Tests
+ Model1
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Xtensive.Orm.Configuration.Options
+ DO-Tests-2
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Xtensive.Orm.Tests.Configuration
+ Model2
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Xtensive.Orm.Tests
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests
+ DO-Tests-3
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Xtensive.Orm.Tests
+ Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace
+ Model3
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Xtensive.Orm.Tests
+ Xtensive.Orm.Tests.Indexing
+ main
+ Model4
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Xtensive.Orm.Tests
+ DO-Tests-1
+
+
+ Xtensive.Orm.Tests
+ Model1
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Xtensive.Orm.Tests.Configuration
+ DO-Tests-1
+
+
+ Xtensive.Orm.Tests.Configuration
+ Model2
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ DO-Tests-1
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Model4
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Xtensive.Orm.Tests
+ Xtensive.Orm.Tests.Configuration
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Xtensive.Orm.Tests
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+ sqlserver://localhost/
+ main
+ dbo
+
+
+ Xtensive.Orm.Tests.Indexing
+
+
+
+
+ main
+ DO-Tests
+
+
+ other
+ DO-Tests-2
+
+
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ ClientProfile
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ ClientProfile
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ User
+ 126654
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Default
+
+
+
+
+ sqlserver://localhost/
+
+
+ ServerProfile
+
+
+
+
+ sqlserver://localhost/
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ AllowSwitching, AutoActivation, ReadRemovedObjects, ValidateEntityVersions
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ 399
+ 20
+ 255
+
+
+
+
+ sqlserver://localhost/
+
+
+ Infinite
+
+
+
+
+ sqlserver://localhost/
+
+
+ ReadCommitted
+
+
+
+
+ sqlserver://localhost/
+
+
+ 300
+
+
+
+
+ sqlserver://localhost/
+
+
+ Always
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Data Source=localhost;Initial Catalog=DO-Tests;Integrated Security=True;MultipleActiveResultSets=True
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ sqlserver://localhost/DO-Tests
+
+
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Infinite
+ 20
+ 255
+ AllowSwitching, AutoActivation, ReadRemovedObjects, ValidateEntityVersions
+
+
+ Infinite
+ 30
+ ServerProfile
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Defailt
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ -5
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ 0
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ 1
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ -5
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ 0
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ -5
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ 0
+
+
+
+
+
+ sqlserver://localhost/
+
+
+ Defailt
+
+
+
+
+
+ Xtensive.Orm.Logging.log4net.LogProvider
+
+
+ *
+ Console
+
+
+ SomeLogName
+ DebugOnlyConsole
+
+
+ FirstLogName,SecondLogName
+ d:\log.txt
+
+
+ LogName, AnotherLogName
+ Console
+
+
+ FileLog
+ log.txt
+
+
+ NullLog
+ None
+
+
+ Trash
+ skjdhfjsdf sdfsdfksjdghj fgdfg
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Xtensive.Orm.Logging.log4net.LogProvider
+
+
+
+
+ Xtensive.Orm.Logging.log4net.LogProvider
+
+
+
+
+
\ No newline at end of file
diff --git a/Orm/Xtensive.Orm.Tests/domainSettings.json b/Orm/Xtensive.Orm.Tests/domainSettings.json
new file mode 100644
index 0000000000..7dde27cf13
--- /dev/null
+++ b/Orm/Xtensive.Orm.Tests/domainSettings.json
@@ -0,0 +1,1468 @@
+{
+ "Xtensive.Orm.Json": {
+ "Domains": {
+ "DomainWithProviderAndConnectionString": {
+ "Provider": "sqlite",
+ "ConnectionString": "Data Source=DO-Testsaaa.db3"
+ },
+
+ "DomainWithConnectionUrl": {
+ "ConnectionUrl": "sqlite:///DO-Tests.db3"
+ },
+
+ "DomainWithCustomValidKeyCacheSize": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "KeyCacheSize": 192
+ },
+ "DomainWithCustomInvalidKeyCacheSize": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "KeyCacheSize": -256
+ },
+
+ "DomainWithCustomValidKeyGeneratorCacheSize": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "KeyGeneratorCacheSize": 192
+ },
+ "DomainWithCustomInvalidKeyGeneratorCacheSize": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "KeyGeneratorCacheSize": -256
+ },
+
+ "DomainWithCustomValidQueryCacheSize": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "QueryCacheSize": 192
+ },
+ "DomainWithCustomInvalidQueryCacheSize": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "QueryCacheSize": -256
+ },
+
+ "DomainWithCustomValidRecordSetMappingCacheSize": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "RecordSetMappingCacheSize": 192
+ },
+ "DomainWithCustomInvalidRecordSetMappingCacheSize": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "RecordSetMappingCacheSize": -256
+ },
+
+ "DomainWithCustomDatabase": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "DefaultDatabase": "MyFancyDatabase"
+ },
+
+ "DomainWithCustomSchema": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "DefaultSchema": "MyFancySchema"
+ },
+
+ "DomainWithWrongConnectionInfo": {
+ "UpgradeMode": "Recreate",
+ "ConnectionString": "Data Source=localhost;Initial Catalog=DO40-Tests;Integrated Security=True;MultipleActiveResultSets=True"
+ },
+
+ "DomainWithUpgradeMode1": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "UpgradeMode": "Default"
+ },
+ "DomainWithUpgradeMode2": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "UpgradeMode": "Recreate"
+ },
+ "DomainWithUpgradeMode3": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "UpgradeMode": "Perform"
+ },
+ "DomainWithUpgradeMode4": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "UpgradeMode": "PerformSafely"
+ },
+ "DomainWithUpgradeMode5": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "UpgradeMode": "Validate"
+ },
+ "DomainWithUpgradeMode6": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "UpgradeMode": "LegacyValidate"
+ },
+ "DomainWithUpgradeMode7": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "UpgradeMode": "Skip"
+ },
+ "DomainWithUpgradeMode8": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "UpgradeMode": "LegacySkip"
+ },
+
+ "DomainWithWrongUpgradeMode": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "UpgradeMode": "Defailt"
+ },
+
+ "DomainWithForeignKeyMode1": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "ForeignKeyMode": "None"
+ },
+ "DomainWithForeignKeyMode2": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "ForeignKeyMode": "Hierarchy"
+ },
+ "DomainWithForeignKeyMode3": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "ForeignKeyMode": "Reference"
+ },
+ "DomainWithForeignKeyMode4": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "ForeignKeyMode": "All"
+ },
+ "DomainWithForeignKeyMode5": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "ForeignKeyMode": "Default"
+ },
+ "DomainWithForeignKeyMode6": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "ForeignKeyMode": "Hierarchy,Reference"
+ },
+ "DomainWithInvalidForeignKeyMode": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "ForeignKeyMode": "Defauit"
+ },
+
+ "DomainWithChangeTrackingMode1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "FullTextChangeTrackingMode": "Off"
+ },
+ "DomainWithChangeTrackingMode2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "FullTextChangeTrackingMode": "Auto"
+ },
+ "DomainWithChangeTrackingMode3": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "FullTextChangeTrackingMode": "Manual"
+ },
+ "DomainWithChangeTrackingMode4": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "FullTextChangeTrackingMode": "OffWithNoPopulation"
+ },
+ "DomainWithChangeTrackingMode5": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "FullTextChangeTrackingMode": "Default"
+ },
+ "DomainWithInvalidChangeTrackingMode": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "FullTextChangeTrackingMode": "Defauit"
+ },
+
+ "DomainWithDomainOptionsValid1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Options": "Default"
+ },
+ "DomainWithDomainOptionsValid2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Options": "None"
+ },
+ "DomainWithDomainOptionsInvalid": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Options": "Defauit"
+ },
+
+ "DomainWithColation": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Collation": "generalci"
+ },
+
+ "DomainWithBriefSchemaSyncExceptions": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "SchemaSyncExceptionFormat": "Brief"
+ },
+ "DomainWithDetailedSchemaSyncExceptions": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "SchemaSyncExceptionFormat": "Detailed"
+ },
+ "DomainWithDefaultSchemaSyncExceptions": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "SchemaSyncExceptionFormat": "Default"
+ },
+ "DomainWithInvalidSchemaSyncExceptions": {
+ "ConnectionUrl": "sqlserver://localhost/DO40-Tests",
+ "SchemaSyncExceptionFormat": "Defailt"
+ },
+
+ "DomainWithTagsLocationNowhere": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "TagsLocation": "Nowhere"
+ },
+ "DomainWithTagsLocationBefore": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "TagsLocation": "BeforeStatement"
+ },
+ "DomainWithTagsLocationWithin": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "TagsLocation": "WithinStatement"
+ },
+ "DomainWithTagsLocationAfter": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "TagsLocation": "AfterStatement"
+ },
+ "DomainWithTagsLocationDefault": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "TagsLocation": "Default"
+ },
+ "DomainWithTagsLocationInvalid": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "TagsLocation": "Defauit"
+ },
+
+ "DomainWithForcedServerVersion": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "ForcedServerVersion": "10.0.0.0"
+ },
+ "DomainWithInitSql": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "ConnectionInitializationSql": "use [OtherDb]"
+ },
+
+ "IncludeSqlInExceptionsTrue": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "IncludeSqlInExceptions": true
+ },
+ "IncludeSqlInExceptionsFalse": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "IncludeSqlInExceptions": false
+ },
+
+ "AllowCyclicDatabaseDependenciesTrue": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "AllowCyclicDatabaseDependencies": true
+ },
+ "AllowCyclicDatabaseDependenciesFalse": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "AllowCyclicDatabaseDependencies": false
+ },
+
+ "BuildInParallelTrue": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "BuildInParallel": true
+ },
+ "BuildInParallelFalse": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "BuildInParallel": false
+ },
+
+ "MultidatabaseKeysTrue": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "MultidatabaseKeys": true
+ },
+ "MultidatabaseKeysFalse": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "MultidatabaseKeys": false
+ },
+
+ "SharedStorageSchemaOn": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "ShareStorageSchemaOverNodes": true
+ },
+ "SharedStorageSchemaOff": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "ShareStorageSchemaOverNodes": false
+ },
+
+ "EnableConnectionIsAliveTrue": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "EnsureConnectionIsAlive": true
+ },
+ "EnableConnectionIsAliveFalse": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "EnsureConnectionIsAlive": false
+ },
+
+ "PreferTypeIdsAsQueryParametersTrue": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "PreferTypeIdsAsQueryParameters": true
+ },
+ "PreferTypeIdsAsQueryParametersFalse": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "PreferTypeIdsAsQueryParameters": false
+ },
+
+ "DomainWithNamingConvention1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Uppercase",
+ "NamespacePolicy": "Synonymize",
+ "NamingRules": "UnderscoreHyphens,RemoveDots",
+ "NamespaceSynonyms": [
+ {
+ "Namespace": "Xtensive.Orm",
+ "Synonym": "system"
+ },
+ {
+ "Namespace": "Xtensive.Orm.Tests",
+ "Synonym": "theRest"
+ }
+ ]
+ }
+ },
+ "DomainWithNamingConvention2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Lowercase",
+ "NamespacePolicy": "Synonymize",
+ "NamingRules": "UnderscoreHyphens,RemoveDots",
+ "NamespaceSynonyms": [
+ {
+ "Namespace": "Xtensive.Orm",
+ "Synonym": "system"
+ },
+ {
+ "Namespace": "Xtensive.Orm.Tests",
+ "Synonym": "theRest"
+ }
+ ]
+ }
+ },
+ "DomainWithNamingConvention3": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "AsIs",
+ "NamespacePolicy": "Synonymize",
+ "NamingRules": "UnderscoreHyphens,RemoveDots",
+ "NamespaceSynonyms": [
+ {
+ "Namespace": "Xtensive.Orm",
+ "Synonym": "system"
+ },
+ {
+ "Namespace": "Xtensive.Orm.Tests",
+ "Synonym": "theRest"
+ }
+ ]
+ }
+ },
+ "DomainWithNamingConvention4": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Default",
+ "NamespacePolicy": "Synonymize",
+ "NamingRules": "UnderscoreHyphens,RemoveDots",
+ "NamespaceSynonyms": [
+ {
+ "Namespace": "Xtensive.Orm",
+ "Synonym": "system"
+ },
+ {
+ "Namespace": "Xtensive.Orm.Tests",
+ "Synonym": "theRest"
+ }
+ ]
+ }
+ },
+ "DomainWithNamingConvention5": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Uppercase",
+ "NamespacePolicy": "AsIs",
+ "NamingRules": "UnderscoreHyphens,RemoveDots"
+ }
+ },
+ "DomainWithNamingConvention6": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Uppercase",
+ "NamespacePolicy": "Hash",
+ "NamingRules": "UnderscoreHyphens,RemoveDots"
+ }
+ },
+ "DomainWithNamingConvention7": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Uppercase",
+ "NamespacePolicy": "Omit",
+ "NamingRules": "UnderscoreHyphens,RemoveDots"
+ }
+ },
+ "DomainWithNamingConvention8": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Uppercase",
+ "NamespacePolicy": "Default",
+ "NamingRules": "UnderscoreHyphens,RemoveDots"
+ }
+ },
+ "DomainWithNamingConvention9": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Uppercase",
+ "NamespacePolicy": "Hash",
+ "NamingRules": "UnderscoreDots,RemoveHyphens"
+ }
+ },
+ "DomainWithNamingConvention10": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Uppercase",
+ "NamespacePolicy": "Hash",
+ "NamingRules": "None"
+ }
+ },
+ "DomainWithNamingConvention11": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Uppercase",
+ "NamespacePolicy": "Hash",
+ "NamingRules": "Default"
+ }
+ },
+
+ "DomainWithInvalidNamingConvention1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Defailt",
+ "NamespacePolicy": "Default",
+ "NamingRules": "Default"
+ }
+ },
+ "DomainWithInvalidNamingConvention2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Default",
+ "NamespacePolicy": "Defailt",
+ "NamingRules": "Default"
+ }
+ },
+ "DomainWithInvalidNamingConvention3": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "NamingConvention": {
+ "LetterCasePolicy": "Default",
+ "NamespacePolicy": "Default",
+ "NamingRules": "Defailt"
+ }
+ },
+
+ "DomainWithVersioningConvention1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "VersioningConvention": {
+ "EntityVersioningPolicy": "Pessimistic"
+ }
+ },
+ "DomainWithVersioningConvention2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "VersioningConvention": {
+ "EntityVersioningPolicy": "Optimistic"
+ }
+ },
+ "DomainWithVersioningConvention3": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "VersioningConvention": {
+ "EntityVersioningPolicy": "Default"
+ }
+ },
+ "DomainWithVersioningConvention4": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "VersioningConvention": {
+ "EntityVersioningPolicy": "Optimistic",
+ "DenyEntitySetOwnerVersionChange": true
+ }
+ },
+ "DomainWithVersioningConvention5": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "VersioningConvention": {
+ "EntityVersioningPolicy": "Optimistic",
+ "DenyEntitySetOwnerVersionChange": false
+ }
+ },
+
+ "DomainWithInvalidVersioningConvention1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "VersioningConvention": {
+ "EntityVersioningPolicy": "Defauit"
+ }
+ },
+
+ "DomainWithTypes": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Types": [
+ {
+ "Type": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests"
+ },
+ {
+ "Type": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity2, Xtensive.Orm.Tests"
+ }
+ ]
+ },
+ "DomainWithAssemblies": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Types": [
+ {
+ "Assembly": "Xtensive.Orm"
+ }
+ ]
+ },
+ "DomainWithAssembliesAndNamespaces": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Types": [
+ {
+ "Assembly": "Xtensive.Orm.Tests",
+ "Namespace": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace"
+ }
+ ]
+ },
+
+ "DomainWithMixedRegistrations": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Types": [
+ {
+ "Type": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests"
+ },
+ {
+ "Type": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity2, Xtensive.Orm.Tests"
+ },
+ {
+ "Assembly": "Xtensive.Orm"
+ },
+ {
+ "Assembly": "Xtensive.Orm.Tests",
+ "Namespace": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace"
+ }
+ ]
+ },
+
+ "DomainWithInvalidRegistrations1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Types": [
+ {
+ "Type": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests"
+ },
+ {
+ "Type": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests"
+ }
+ ]
+ },
+ "DomainWithInvalidRegistrations2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Types": [
+ {
+ "Assembly": "Xtensive.Orm"
+ },
+ {
+ "Assembly": "Xtensive.Orm"
+ }
+ ]
+ },
+ "DomainWithInvalidRegistrations3": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Types": [
+ {
+ "Assembly": "Xtensive.Orm.Tests",
+ "Namespace": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace"
+ },
+ {
+ "Assembly": "Xtensive.Orm.Tests",
+ "Namespace": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace"
+ }
+ ]
+ },
+ "DomainWithInvalidRegistrations4": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Types": [
+ {
+ "Type": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests",
+ "Assembly": "Xtensive.Orm",
+ "Namespace": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace"
+ }
+ ]
+ },
+ "DomainWithInvalidRegistrations5": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Types": [
+ {
+ "Type": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests",
+ "Assembly": "Xtensive.Orm"
+ }
+ ]
+ },
+ "DomainWithInvalidRegistrations6": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Types": [
+ {
+ "Type": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.DummyEntity1, Xtensive.Orm.Tests",
+ "Namespace": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace"
+ }
+ ]
+ },
+ "DomainWithInvalidRegistrations7": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Types": [
+ {
+ "Namespace": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace"
+ }
+ ]
+ },
+
+ "DomainWithDatabases1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests-1"
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithDatabases2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests-1",
+ "MinTypeId": 100,
+ "MaxTypeId": 1000
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2",
+ "MinTypeId": 2000,
+ "MaxTypeId": 3000
+ }
+ }
+ },
+
+ "DomainWithInvalidDatabases1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests-1",
+ "MinTypeId": -10
+ }
+ }
+ },
+ "DomainWithInvalidDatabases2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests-1",
+ "MinTypeId": 50
+ }
+ }
+ },
+ "DomainWithInvalidDatabases3": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests-1",
+ "MaxTypeId": -10
+ }
+ }
+ },
+ "DomainWithInvalidDatabases4": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests-1",
+ "MaxTypeId": 50
+ }
+ }
+ },
+
+ "DomainWithCustomGenerator": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "KeyGenerators": [
+ {
+ "Name": "Int32",
+ "Seed": 12,
+ "CacheSize": 127
+ }
+ ]
+ },
+ "DomainWithCustomGeneratorsWithDatabaseNames": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "KeyGenerators": [
+ {
+ "Name": "Int32",
+ "Database": "DO-Tests-1"
+ },
+ {
+ "Name": "Int32",
+ "Database": "DO-Tests-2"
+ }
+ ]
+ },
+ "DomainWithCustomGeneratorsWithDatabaseNamesAndKeyParams": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "KeyGenerators": [
+ {
+ "Name": "Int32",
+ "Database": "DO-Tests-1",
+ "Seed": 12,
+ "CacheSize": 127
+ },
+ {
+ "Name": "Int32",
+ "Database": "DO-Tests-2",
+ "Seed": 13,
+ "CacheSize": 129
+ }
+ ]
+ },
+ "DomainWithCustomGeneratorsWithDatabasesAliases": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "KeyGenerators": [
+ {
+ "Name": "Int32",
+ "Database": "main",
+ "Seed": 12,
+ "CacheSize": 127
+ },
+ {
+ "Name": "Int32",
+ "Database": "Other",
+ "Seed": 13,
+ "CacheSize": 129
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests-1"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+
+ "DomainWithCustomGeneratorsConflict1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "KeyGenerators": [
+ {
+ "Name": "Int32",
+ "Database": "DO-Tests-1"
+ },
+ {
+ "Name": "Int32",
+ "Database": "DO-Tests-1"
+ }
+ ]
+ },
+ "DomainWithCustomGeneratorsConflict2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "KeyGenerators": [
+ {
+ "Name": "Int32",
+ "Database": "DO-Tests-1"
+ },
+ {
+ "Name": "Int32",
+ "Database": "main"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests-1"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithCustomGeneratorNegativeSeed": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "KeyGenerators": [
+ {
+ "Name": "Int32",
+ "Database": "DO-Tests-1",
+ "Seed": -12
+ }
+ ]
+ },
+ "DomainWithCustomGeneratorNegativeCache": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "KeyGenerators": [
+ {
+ "Name": "Int32",
+ "Database": "DO-Tests-1",
+ "CacheSize": -127
+ }
+ ]
+ },
+
+ "DomainWithIgnoreRules": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "IgnoreRules": [
+ {
+ "Database": "Other-DO40-Tests",
+ "Schema": "some-schema1",
+ "Table": "table1"
+ },
+ {
+ "Database": "some-database",
+ "Schema": "some-schema2",
+ "Column": "column2"
+ },
+ {
+ "Database": "some-database",
+ "Schema": "some-schema2",
+ "Index": "index2"
+ },
+ {
+ "Database": "some-database",
+ "Schema": "some-schema3",
+ "Table": "table2",
+ "Column": "col3"
+ },
+ {
+ "Database": "some-database",
+ "Schema": "some-schema3",
+ "Table": "table2",
+ "Index": "index3"
+ },
+ {
+ "Database": "another-some-database",
+ "Table": "some-table"
+ },
+ {
+ "Database": "database1",
+ "Column": "some-column"
+ },
+ {
+ "Database": "database1",
+ "Index": "some-index"
+ },
+ {
+ "Schema": "schema1",
+ "Table": "table1"
+ },
+ {
+ "Schema": "schema1",
+ "Column": "column2"
+ },
+ {
+ "Schema": "schema1",
+ "Index": "index2"
+ },
+ {
+ "Schema": "schema1",
+ "Table": "table2",
+ "Column": "column3"
+ },
+ {
+ "Schema": "schema1",
+ "Table": "table2",
+ "Index": "index3"
+ },
+ {
+ "Table": "table4",
+ "Column": "column3"
+ },
+ {
+ "Table": "table4",
+ "Index": "index2"
+ },
+ {
+ "Table": "single-table"
+ },
+ {
+ "Column": "single-column"
+ },
+ {
+ "Index": "single-index"
+ }
+ ]
+ },
+ "DomainWithInvalidIgnoreRules1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "IgnoreRules": [
+ {
+ "Column": "column1",
+ "Index": "index1"
+ }
+ ]
+ },
+ "DomainWithInvalidIgnoreRules2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "IgnoreRules": [
+ {
+ "Table": "table1",
+ "Column": "column1",
+ "Index": "index1"
+ }
+ ]
+ },
+ "DomainWithInvalidIgnoreRules3": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "IgnoreRules": [
+ {
+ "Database": "database1"
+ }
+ ]
+ },
+ "DomainWithInvalidIgnoreRules4": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "IgnoreRules": [
+ {
+ "Database": "database1",
+ "Schema": "schema1"
+ }
+ ]
+ },
+
+ "DomainWithMappingRules1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Assembly": "Xtensive.Orm.Tests",
+ "Database": "DO-Tests-1"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithMappingRules2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Assembly": "Xtensive.Orm.Tests",
+ "Schema": "Model1"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithMappingRules3": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Namespace": "Xtensive.Orm.Configuration.Options",
+ "Database": "DO-Tests-2"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithMappingRules4": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Namespace": "Xtensive.Orm.Tests.Configuration",
+ "Schema": "Model2"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithMappingRules5": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Assembly": "Xtensive.Orm.Tests",
+ "Namespace": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests",
+ "Database": "DO-Tests-3"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithMappingRules6": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Assembly": "Xtensive.Orm.Tests",
+ "Namespace": "Xtensive.Orm.Tests.Configuration.TypesToUseInTests.NestedNamespace",
+ "Schema": "Model3"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithMappingRules7": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Assembly": "Xtensive.Orm.Tests",
+ "Namespace": "Xtensive.Orm.Tests.Indexing",
+ "Database": "main",
+ "Schema": "Model4"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+
+ "DomainWithConflictMappingRules1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Assembly": "Xtensive.Orm.Tests",
+ "Database": "DO-Tests-1"
+ },
+ {
+ "Assembly": "Xtensive.Orm.Tests",
+ "Schema": "Model1"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithConflictMappingRules2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Namespace": "Xtensive.Orm.Tests.Configuration",
+ "Database": "DO-Tests-2"
+ },
+ {
+ "Namespace": "Xtensive.Orm.Tests.Configuration",
+ "Schema": "Model2"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+
+ "DomainWithInvalidMappingRules1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Database": "main"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithInvalidMappingRules2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Schema": "Model4"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithInvalidMappingRules3": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Assembly": "Xtensive.Orm.Tests",
+ "Namespace": "Xtensive.Orm.Tests.Indexing"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithInvalidMappingRules4": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Assembly": "Xtensive.Orm.Tests"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+ "DomainWithInvalidMappingRules5": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "DefaultDatabase": "main",
+ "DefaultSchema": "dbo",
+ "MappingRules": [
+ {
+ "Namespace": "Xtensive.Orm.Tests.Indexing"
+ }
+ ],
+ "Databases": {
+ "main": {
+ "RealName": "DO-Tests"
+
+ },
+ "other": {
+ "RealName": "DO-Tests-2"
+ }
+ }
+ },
+
+ "DomainWithSessionEmptyName": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "": {
+ "Options": "ClientProfile"
+ }
+ }
+ },
+ "DomainWithSessionCustomName": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "UserCreated": {
+ "Options": "ClientProfile"
+ }
+ }
+ },
+ "DomainWithSessionCustomUser": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "UserName": "User",
+ "Password": "126654"
+ }
+ }
+ },
+ "DomainWithSessionDefaultOptions": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "Options": "Default"
+ }
+ }
+ },
+ "DomainWithSessionServerProfile": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "Options": "ServerProfile"
+ }
+ }
+ },
+ "DomainWithSessionClientProfile": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "Options": "ClientProfile"
+ }
+ }
+ },
+ "DomainWithSessionCustomOptions": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "Options": "AllowSwitching, AutoActivation, ReadRemovedObjects, ValidateEntityVersions"
+ }
+ }
+ },
+
+ "DomainWithSessionWithCollectionSizes": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "CacheSize": 399,
+ "BatchSize": 20,
+ "EntityChangeRegistrySize": 255
+ }
+ }
+ },
+
+ "DomainWithSessionCustomCacheType": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "CacheType": "Infinite"
+ }
+ }
+ },
+
+ "DomainWithSessionCustomIsolationLevel": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "DefaultIsolationLevel": "ReadCommitted"
+ }
+ }
+ },
+
+ "DomainWithSessionCustomCommandTimeout": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "DefaultCommandTimeout": 300
+ }
+ }
+ },
+
+ "DomainWithSessionCustomPreloadingPolicy": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "ReaderPreloading": "Always"
+ }
+ }
+ },
+
+ "DomainWithSessionCustomConnectionString": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "ConnectionString": "Data Source=localhost;Initial Catalog=DO-Tests;Integrated Security=True;MultipleActiveResultSets=True"
+ }
+ }
+ },
+ "DomainWithSessionCustomConnectionUrl": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "ConnectionUrl": "sqlserver://localhost/DO-Tests"
+ }
+ }
+ },
+
+ "DomainWithMultipleSessionConfigurations": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "CacheType": "Infinite",
+ "BatchSize": 20,
+ "EntityChangeRegistrySize": 255,
+ "Options": "AllowSwitching, AutoActivation, ReadRemovedObjects, ValidateEntityVersions"
+ },
+ "System": {
+ "CacheType": "Infinite",
+ "BatchSize": 30,
+ "Options": "ServerProfile"
+ }
+ }
+ },
+
+ "DomainWithSessionInvalidOptions": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "Options": "Defailt"
+ }
+ }
+ },
+ "DomainWithSessionInvalidCacheSize1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "CacheSize": -5
+ }
+ }
+ },
+ "DomainWithSessionInvalidCacheSize2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "CacheSize": 0
+ }
+ }
+ },
+ "DomainWithSessionInvalidCacheSize3": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "CacheSize": 1
+ }
+ }
+ },
+ "DomainWithSessionInvalidBatchSize1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "BatchSize": -5
+ }
+ }
+ },
+ "DomainWithSessionInvalidBatchSize2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "BatchSize": 0
+ }
+ }
+ },
+ "DomainWithSessionInvalidEntityChangeRegistry1": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "EntityChangeRegistrySize": -5
+ }
+ }
+ },
+ "DomainWithSessionInvalidEntityChangeRegistry2": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "EntityChangeRegistrySize": 0
+ }
+ }
+ },
+ "DomainWithSessionInvalidCacheType": {
+ "ConnectionUrl": "sqlserver://localhost/",
+ "Sessions": {
+ "Default": {
+ "CacheType": "Defailt"
+ }
+ }
+ }
+ },
+ "Logging": {
+ "Provider": "Xtensive.Orm.Logging.log4net.LogProvider",
+ "Logs": [
+ {
+ "Source": "*",
+ "Target": "Console"
+ },
+ {
+ "Source": "SomeLogName",
+ "Target": "DebugOnlyConsole"
+ },
+ {
+ "Source": "FirstLogName,SecondLogName",
+ "Target": "d:\\log.txt"
+ },
+ {
+ "Source": "LogName, AnotherLogName",
+ "Target": "Console"
+ },
+ {
+ "Source": "FileLog",
+ "Target": "log.txt"
+ },
+ {
+ "Source": "NullLog",
+ "Target": "None"
+ },
+ {
+ "Source": "Trash",
+ "Target": "skjdhfjsdf sdfsdfksjdghj fgdfg"
+ }
+ ]
+ }
+ },
+ "Xtensive.Orm.EmptyLogging.Json": {
+ "Logging": {
+
+ }
+ },
+ "Xtensive.Orm.EmptyLogs.Json": {
+ "Logging": {
+ "Logs": []
+ }
+ },
+ "Xtensive.Orm.OnlyLogProvider.Json": {
+ "Logging": {
+ "Provider": "Xtensive.Orm.Logging.log4net.LogProvider"
+ }
+ },
+ "Xtensive.Orm.LogProviderAndEmptyLogs.Json": {
+ "Logging": {
+ "Provider": "Xtensive.Orm.Logging.log4net.LogProvider",
+ "Logs": []
+ }
+ }
+}
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/ConfigurationBase.cs b/Orm/Xtensive.Orm/Orm/Configuration/ConfigurationBase.cs
index 30aed0ce16..8b10852968 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/ConfigurationBase.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/ConfigurationBase.cs
@@ -35,7 +35,7 @@ public override void Lock(bool recursive)
///
public virtual object Clone()
{
- ConfigurationBase clone = CreateClone();
+ var clone = CreateClone();
clone.CopyFrom(this);
return clone;
}
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/DatabaseConfiguration.cs b/Orm/Xtensive.Orm/Orm/Configuration/DatabaseConfiguration.cs
index 15deb1db86..83e0368494 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/DatabaseConfiguration.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/DatabaseConfiguration.cs
@@ -52,7 +52,7 @@ public string RealName
///
/// Gets or sets type ID minimal value
/// for types mapped to this database.
- /// Default value is 100.
+ /// Default value is .
///
public int MinTypeId
{
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/DomainConfiguration.cs b/Orm/Xtensive.Orm/Orm/Configuration/DomainConfiguration.cs
index f4bd9984c8..573c530650 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/DomainConfiguration.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/DomainConfiguration.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2022 Xtensive LLC.
+// Copyright (C) 2007-2024 Xtensive LLC.
// This code is distributed under MIT license terms.
// See the License.txt file in the project root for more information.
// Created by: Dmitri Maximov
@@ -8,16 +8,16 @@
using System.Configuration;
using System.Linq;
using JetBrains.Annotations;
+using Microsoft.Extensions.Configuration;
using Xtensive.Core;
-using Xtensive.Orm.Configuration.Elements;
using Xtensive.Orm.Internals;
-using ConfigurationSection=Xtensive.Orm.Configuration.Elements.ConfigurationSection;
+using ConfigurationSection = Xtensive.Orm.Configuration.Elements.ConfigurationSection;
namespace Xtensive.Orm.Configuration
{
///
/// The configuration of the .
- ///
+ ///
[Serializable]
public class DomainConfiguration : ConfigurationBase
{
@@ -67,12 +67,12 @@ public class DomainConfiguration : ConfigurationBase
///
/// Default value:
- ///
+ /// .
///
public const bool DefaultShareStorageSchemaOverNodes = false;
///
- /// Default value:
+ /// Default value: .
///
public const bool DefaultAllowCyclicDatabaseDependencies = false;
@@ -95,8 +95,14 @@ public class DomainConfiguration : ConfigurationBase
///
/// Default value.
///
+ [Obsolete ("User DefaultForeignKeyMode")]
public const ForeignKeyMode DefauktForeignKeyMode = ForeignKeyMode.Default;
+ ///
+ /// Default value.
+ ///
+ public const ForeignKeyMode DefaultForeignKeyMode = ForeignKeyMode.Default;
+
///
/// Default value.
///
@@ -138,6 +144,7 @@ public class DomainConfiguration : ConfigurationBase
private DatabaseConfigurationCollection databases = new();
private KeyGeneratorConfigurationCollection keyGenerators = new();
private IgnoreRuleCollection ignoreRules = new();
+ private ExtensionConfigurationCollection extensionConfigurations = new();
private NamingConvention namingConvention = new();
private VersioningConvention versioningConvention = new();
private int keyCacheSize = DefaultKeyCacheSize;
@@ -154,7 +161,7 @@ public class DomainConfiguration : ConfigurationBase
private bool ensureConnectionIsAlive = DefaultEnsureConnectionIsAlive;
private bool preferTypeIdsAsQueryParameters = DefaultPreferTypeIdsAsQueryParameters;
private DomainUpgradeMode upgradeMode = DefaultUpgradeMode;
- private ForeignKeyMode foreignKeyMode = DefauktForeignKeyMode;
+ private ForeignKeyMode foreignKeyMode = DefaultForeignKeyMode;
private FullTextChangeTrackingMode fullTextChangeTrackingMode = DefaultFullTextChangeTrackingMode;
private DomainOptions options = DefaultDomainOptions;
private SchemaSyncExceptionFormat schemaSyncExceptionFormat = DefaultSchemaSyncExceptionFormat;
@@ -347,6 +354,7 @@ public ForeignKeyMode ForeignKeyMode
///
/// Gets or sets a value indicating change tracking mode of full-text indexes.
/// The property may have no effect for certain storages where there is no support for such option.
+ /// Default value is .
///
public FullTextChangeTrackingMode FullTextChangeTrackingMode
{
@@ -436,6 +444,18 @@ public Type ServiceContainerType
}
}
+ ///
+ /// Gets collection of additional configurations (configurations of extensions)
+ /// that might be required during domain build process.
+ ///
+ public ExtensionConfigurationCollection ExtensionConfigurations {
+ get => extensionConfigurations;
+ set {
+ EnsureNotLocked();
+ extensionConfigurations = value;
+ }
+ }
+
///
/// Gets or sets value indicating whether SQL text of a query
/// that caused error should be included in exception message.
@@ -583,7 +603,7 @@ public bool ShareStorageSchemaOverNodes
}
///
- /// Gets or sets versioning convention.
+ /// Gets or sets rules of entity versioning.
///
public VersioningConvention VersioningConvention
{
@@ -596,6 +616,13 @@ public VersioningConvention VersioningConvention
///
/// Enables extra check if connection is not broken on its opening.
+ /// For some RDBMSs physical connection may become broken but connection pool, which
+ /// is in charge of logical connections, is not aware of it.
+ /// In such cases connection pool provides connection
+ /// which is dead on arrival and causes exception on first use of it.
+ /// Such connection should be re-created and re-opened. The extra check
+ /// tests connection by making "first use" of it, if check failed connection
+ /// will be restored automatically.
///
public bool EnsureConnectionIsAlive
{
@@ -640,6 +667,9 @@ public TagsLocation TagsLocation
///
public bool IsMultischema => isMultischema ?? GetIsMultischema();
+ internal bool Supports(DomainOptions optionsToCheck) => (options & optionsToCheck) == optionsToCheck;
+
+ internal bool Supports(ForeignKeyMode modeToCheck) => (foreignKeyMode & modeToCheck) == modeToCheck;
private bool GetIsMultidatabase()
{
@@ -676,6 +706,7 @@ public override void Lock(bool recursive)
keyGenerators.Lock(true);
ignoreRules.Lock(true);
versioningConvention.Lock(true);
+ extensionConfigurations.Lock(true);
base.Lock(recursive);
@@ -754,6 +785,7 @@ protected override void CopyFrom(ConfigurationBase source)
shareStorageSchemaOverNodes = configuration.ShareStorageSchemaOverNodes;
versioningConvention = (VersioningConvention) configuration.VersioningConvention.Clone();
preferTypeIdsAsQueryParameters = configuration.PreferTypeIdsAsQueryParameters;
+ ExtensionConfigurations = (ExtensionConfigurationCollection) configuration.ExtensionConfigurations.Clone();
}
///
@@ -762,6 +794,8 @@ protected override void CopyFrom(ConfigurationBase source)
/// The clone of this configuration.
public new DomainConfiguration Clone() => (DomainConfiguration) base.Clone();
+ #region Static DomainConfiguration.Load() methods
+
///
/// Loads the for
/// with the specified
@@ -841,11 +875,6 @@ public static DomainConfiguration Load(System.Configuration.Configuration config
return LoadConfigurationFromSection(section, name);
}
- internal bool Supports(DomainOptions optionsToCheck) => (options & optionsToCheck) == optionsToCheck;
-
- internal bool Supports(ForeignKeyMode modeToCheck) => (foreignKeyMode & modeToCheck) == modeToCheck;
-
-
private static DomainConfiguration LoadConfigurationFromSection(ConfigurationSection section, string name)
{
var domainElement = section.Domains[name];
@@ -856,6 +885,70 @@ private static DomainConfiguration LoadConfigurationFromSection(ConfigurationSec
return domainElement.ToNative();
}
+ ///
+ /// Loads the for
+ /// with the specified .
+ ///
+ /// Root configuration section where domain configurations are placed
+ /// Name of the .
+ /// Domain configuration.
+ /// The "domains" section is not found or domain with requested name is not found.
+ public static DomainConfiguration Load(IConfigurationSection configurationSection, string name)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationSection, nameof(configurationSection));
+
+ var jsonParser = new JsonToDomainConfigurationReader();
+ var xmlParser = new XmlToDomainConfigurationReader();
+
+ var parseResult = jsonParser.Read(configurationSection, name);
+ if (parseResult != null)
+ return parseResult;
+ parseResult = xmlParser.Read(configurationSection, name);
+ if (parseResult != null)
+ return parseResult;
+
+ throw new InvalidOperationException(string.Format(
+ Strings.ExConfigurationForDomainIsNotFoundInApplicationConfigurationFile, name, sectionName));
+ }
+
+ public static DomainConfiguration Load(IConfigurationRoot configurationRoot, string name)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationRoot, nameof(configurationRoot));
+
+ var jsonParser = new JsonToDomainConfigurationReader();
+ var xmlParser = new XmlToDomainConfigurationReader();
+
+ var parseResult = jsonParser.Read(configurationRoot, name);
+ if (parseResult != null)
+ return parseResult;
+ parseResult = xmlParser.Read(configurationRoot, name);
+ if (parseResult != null)
+ return parseResult;
+
+ throw new InvalidOperationException(string.Format(
+ Strings.ExConfigurationForDomainIsNotFoundInApplicationConfigurationFile, name, sectionName));
+ }
+
+ public static DomainConfiguration Load(IConfigurationRoot configurationRoot, string sectionName, string name)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationRoot, nameof(configurationRoot));
+
+ var jsonParser = new JsonToDomainConfigurationReader();
+ var xmlParser = new XmlToDomainConfigurationReader();
+
+ var parseResult = jsonParser.Read(configurationRoot, sectionName, name);
+ if (parseResult != null)
+ return parseResult;
+ parseResult = xmlParser.Read(configurationRoot, sectionName, name);
+ if (parseResult != null)
+ return parseResult;
+
+ throw new InvalidOperationException(string.Format(
+ Strings.ExConfigurationForDomainIsNotFoundInApplicationConfigurationFile, name, sectionName));
+ }
+
+ #endregion
+
// Constructors
///
@@ -907,4 +1000,4 @@ public DomainConfiguration()
types.Register(WellKnownOrmTypes.Persistent.Assembly, WellKnownOrmTypes.Persistent.Namespace);
}
}
-}
+}
\ No newline at end of file
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Elements/DatabaseConfigurationElement.cs b/Orm/Xtensive.Orm/Orm/Configuration/Elements/DatabaseConfigurationElement.cs
index 0e6fdafef0..487b8450a1 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/Elements/DatabaseConfigurationElement.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Elements/DatabaseConfigurationElement.cs
@@ -1,6 +1,6 @@
-// Copyright (C) 2012 Xtensive LLC.
-// All rights reserved.
-// For conditions of distribution and use, see license.
+// Copyright (C) 2012-2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
// Created by: Denis Krjuchkov
// Created: 2012.02.08
@@ -24,7 +24,7 @@ public class DatabaseConfigurationElement : ConfigurationCollectionElementBase
public override object Identifier { get { return Name; } }
///
- ///
+ ///
///
[ConfigurationProperty(NameElementName, IsKey = true)]
public string Name
@@ -34,7 +34,7 @@ public string Name
}
///
- ///
+ ///
///
[ConfigurationProperty(RealNameElementName)]
public string RealName
@@ -44,7 +44,7 @@ public string RealName
}
///
- ///
+ ///
///
[ConfigurationProperty(MinTypeIdElementName, DefaultValue = TypeInfo.MinTypeId)]
public int MinTypeId
@@ -54,7 +54,7 @@ public int MinTypeId
}
///
- ///
+ ///
///
[ConfigurationProperty(MaxTypeIdElementName, DefaultValue = int.MaxValue)]
public int MaxTypeId
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Elements/DomainConfigurationElement.cs b/Orm/Xtensive.Orm/Orm/Configuration/Elements/DomainConfigurationElement.cs
index d81753afa0..7837360145 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/Elements/DomainConfigurationElement.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Elements/DomainConfigurationElement.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2008-2023 Xtensive LLC.
+// Copyright (C) 2008-2024 Xtensive LLC.
// This code is distributed under MIT license terms.
// See the License.txt file in the project root for more information.
// Created by: Aleksey Gamzov
@@ -58,7 +58,7 @@ public class DomainConfigurationElement : ConfigurationCollectionElementBase
public override object Identifier { get { return Name; } }
///
- ///
+ ///
///
[ConfigurationProperty(NameElementName, IsKey = true, DefaultValue = "")]
public string Name
@@ -68,7 +68,7 @@ public string Name
}
///
- ///
+ ///
///
[ConfigurationProperty(ConnectionUrlElementName, DefaultValue = null)]
public string ConnectionUrl
@@ -78,7 +78,7 @@ public string ConnectionUrl
}
///
- ///
+ ///
///
[ConfigurationProperty(ConnectionStringElementName, DefaultValue = null)]
public string ConnectionString
@@ -88,7 +88,7 @@ public string ConnectionString
}
///
- ///
+ ///
///
[ConfigurationProperty(ProviderElementName, DefaultValue = WellKnown.Provider.SqlServer)]
public string Provider
@@ -98,7 +98,7 @@ public string Provider
}
///
- ///
+ ///
///
[ConfigurationProperty(TypesElementName, IsDefaultCollection = false)]
[ConfigurationCollection(typeof (ConfigurationCollection), AddItemName = "add")]
@@ -108,7 +108,7 @@ public ConfigurationCollection Types
}
///
- ///
+ ///
///
[ConfigurationProperty(NamingConventionElementName)]
public NamingConventionElement NamingConvention
@@ -118,7 +118,7 @@ public NamingConventionElement NamingConvention
}
///
- ///
+ ///
///
[ConfigurationProperty(KeyCacheSizeElementName, DefaultValue = DomainConfiguration.DefaultKeyCacheSize)]
[IntegerValidator(MinValue = 1, MaxValue = int.MaxValue)]
@@ -129,7 +129,7 @@ public int KeyCacheSize
}
///
- ///
+ ///
///
[ConfigurationProperty(KeyGeneratorCacheSizeElementName, DefaultValue = DomainConfiguration.DefaultKeyGeneratorCacheSize)]
[IntegerValidator(MinValue = 1, MaxValue = int.MaxValue)]
@@ -140,7 +140,7 @@ public int KeyGeneratorCacheSize
}
///
- ///
+ ///
///
[ConfigurationProperty(QueryCacheSizeElementName, DefaultValue = DomainConfiguration.DefaultQueryCacheSize)]
[IntegerValidator(MinValue = 1, MaxValue = int.MaxValue)]
@@ -151,7 +151,7 @@ public int QueryCacheSize
}
///
- ///
+ ///
///
[ConfigurationProperty(RecordSetMappingCacheSizeElementName, DefaultValue = DomainConfiguration.DefaultRecordSetMappingCacheSize)]
[IntegerValidator(MinValue = 1, MaxValue = int.MaxValue)]
@@ -162,7 +162,7 @@ public int RecordSetMappingCacheSize
}
///
- ///
+ ///
///
[ConfigurationProperty(UpgradeModeElementName, DefaultValue = "Default")]
public string UpgradeMode
@@ -172,7 +172,7 @@ public string UpgradeMode
}
///
- ///
+ ///
///
[ConfigurationProperty(SchemaSyncExceptionFormatElementName, DefaultValue = "Default")]
public string SchemaSyncExceptionFormat
@@ -182,7 +182,7 @@ public string SchemaSyncExceptionFormat
}
///
- ///
+ ///
///
[ConfigurationProperty(ForeignKeyModeElementName, DefaultValue = "Default")]
public string ForeignKeyMode
@@ -192,7 +192,7 @@ public string ForeignKeyMode
}
///
- ///
+ ///
///
[ConfigurationProperty(FullTextChangeTrackingModeElementName, DefaultValue = "Default")]
public string FullTextChangeTrackingMode
@@ -202,7 +202,7 @@ public string FullTextChangeTrackingMode
}
///
- ///
+ ///
///
[ConfigurationProperty(CollationElementName)]
public string Collation
@@ -212,7 +212,7 @@ public string Collation
}
///
- ///
+ ///
///
[ConfigurationProperty(SessionsElementName, IsDefaultCollection = false)]
[ConfigurationCollection(typeof (ConfigurationCollection), AddItemName = "session")]
@@ -222,7 +222,7 @@ public ConfigurationCollection Sessions
}
///
- ///
+ ///
///
[ConfigurationProperty(MappingRulesElementName, IsDefaultCollection = false)]
[ConfigurationCollection(typeof (ConfigurationCollection), AddItemName = "rule")]
@@ -232,7 +232,7 @@ public ConfigurationCollection MappingRules
}
///
- ///
+ ///
///
[ConfigurationProperty(DatabasesElementName, IsDefaultCollection = false)]
[ConfigurationCollection(typeof (ConfigurationCollection), AddItemName = "database")]
@@ -242,17 +242,17 @@ public ConfigurationCollection Databases
}
///
- ///
+ ///
///
[ConfigurationProperty(KeyGeneratorsElementName, IsDefaultCollection = false)]
- [ConfigurationCollection(typeof (ConfigurationCollection), AddItemName = "keyGenerator")]
+ [ConfigurationCollection(typeof (ConfigurationCollection), AddItemName = "keyGenerator")]
public ConfigurationCollection KeyGenerators
{
get { return (ConfigurationCollection) this[KeyGeneratorsElementName]; }
}
///
- ///
+ ///
///
[ConfigurationProperty(ServiceContainerTypeElementName, DefaultValue = null)]
public string ServiceContainerType
@@ -262,7 +262,7 @@ public string ServiceContainerType
}
///
- ///
+ ///
///
[ConfigurationProperty(DefaultSchemaElementName)]
public string DefaultSchema
@@ -272,7 +272,7 @@ public string DefaultSchema
}
///
- ///
+ ///
///
[ConfigurationProperty(DefaultDatabaseElementName)]
public string DefaultDatabase
@@ -282,7 +282,7 @@ public string DefaultDatabase
}
///
- ///
+ ///
///
[ConfigurationProperty(IncludeSqlInExceptionsElementName,
DefaultValue = DomainConfiguration.DefaultIncludeSqlInExceptions)]
@@ -293,9 +293,10 @@ public bool IncludeSqlInExceptions
}
///
- ///
+ ///
///
- [ConfigurationProperty(AllowCyclicDatabaseDependenciesElementName, DefaultValue = false)]
+ [ConfigurationProperty(AllowCyclicDatabaseDependenciesElementName,
+ DefaultValue = DomainConfiguration.DefaultAllowCyclicDatabaseDependencies)]
public bool AllowCyclicDatabaseDependencies
{
get { return (bool) this[AllowCyclicDatabaseDependenciesElementName]; }
@@ -303,7 +304,7 @@ public bool AllowCyclicDatabaseDependencies
}
///
- ///
+ ///
///
[ConfigurationProperty(BuildInParallelElementName,
DefaultValue = DomainConfiguration.DefaultBuildInParallel)]
@@ -314,7 +315,7 @@ public bool BuildInParallel
}
///
- ///
+ ///
///
[ConfigurationProperty(ForcedServerVersionElementName)]
public string ForcedServerVersion
@@ -324,7 +325,7 @@ public string ForcedServerVersion
}
///
- ///
+ ///
///
[ConfigurationProperty(ConnectionInitializationSqlElementName)]
public string ConnectionInitializationSql
@@ -334,7 +335,7 @@ public string ConnectionInitializationSql
}
///
- ///
+ ///
///
[ConfigurationProperty(IgnoreRulesElementName, IsDefaultCollection = false)]
[ConfigurationCollection(typeof (ConfigurationCollection), AddItemName = "rule")]
@@ -344,7 +345,7 @@ public ConfigurationCollection IgnoreRules
}
///
- ///
+ ///
///
[ConfigurationProperty(MultidatabaseKeysElementName,
DefaultValue = DomainConfiguration.DefaultMultidatabaseKeys)]
@@ -355,7 +356,7 @@ public bool MultidatabaseKeys
}
///
- ///
+ ///
///
[ConfigurationProperty(OptionsElementName, DefaultValue = "Default")]
public string Options
@@ -365,7 +366,7 @@ public string Options
}
///
- ///
+ ///
///
[ConfigurationProperty(ShareStorageSchemaOverNodesElementName, DefaultValue = false)]
public bool ShareStorageSchemaOverNodes
@@ -375,7 +376,7 @@ public bool ShareStorageSchemaOverNodes
}
///
- ///
+ ///
///
[ConfigurationProperty(VersioningConventionElementName)]
public VersioningConventionElement VersioningConvention
@@ -385,7 +386,7 @@ public VersioningConventionElement VersioningConvention
}
///
- ///
+ ///
///
[ConfigurationProperty(EnsureConnectionIsAliveElementName, DefaultValue = true)]
public bool EnsureConnectionIsAlive
@@ -395,7 +396,7 @@ public bool EnsureConnectionIsAlive
}
///
- /// .
+ /// .
///
[ConfigurationProperty(TagsLocationElementName, DefaultValue = "Default")]
public string TagsLocation
@@ -405,7 +406,7 @@ public string TagsLocation
}
///
- ///
+ ///
///
[ConfigurationProperty(PreferTypeIdsAsQueryParametersElementName, DefaultValue = true)]
public bool PreferTypeIdsAsQueryParameters
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Elements/IgnoreRuleElement.cs b/Orm/Xtensive.Orm/Orm/Configuration/Elements/IgnoreRuleElement.cs
index 583c2f1bb8..de43975349 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/Elements/IgnoreRuleElement.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Elements/IgnoreRuleElement.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Xtensive LLC.
+// Copyright (C) 2013-2024 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
@@ -31,7 +31,7 @@ public class IgnoreRuleElement : ConfigurationCollectionElementBase
Index ?? string.Empty);
///
- ///
+ ///
///
[ConfigurationProperty(DatabaseElementName)]
public string Database
@@ -41,7 +41,7 @@ public string Database
}
///
- ///
+ ///
///
[ConfigurationProperty(SchemaElementName)]
public string Schema
@@ -51,7 +51,7 @@ public string Schema
}
///
- ///
+ ///
///
[ConfigurationProperty(TableElementName)]
public string Table
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Elements/KeyGeneratorConfigurationElement.cs b/Orm/Xtensive.Orm/Orm/Configuration/Elements/KeyGeneratorConfigurationElement.cs
index c05b08e5a3..9c256bf689 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/Elements/KeyGeneratorConfigurationElement.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Elements/KeyGeneratorConfigurationElement.cs
@@ -1,6 +1,6 @@
-// Copyright (C) 2012 Xtensive LLC.
-// All rights reserved.
-// For conditions of distribution and use, see license.
+// Copyright (C) 2012-2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
// Created by: Denis Krjuchkov
// Created: 2012.03.28
@@ -25,7 +25,7 @@ public override object Identifier {
}
///
- ///
+ ///
///
[ConfigurationProperty(NameElementName, IsKey = true)]
public string Name
@@ -35,7 +35,7 @@ public string Name
}
///
- ///
+ ///
///
[ConfigurationProperty(DatabaseElementName, IsKey = true)]
public string Database
@@ -45,7 +45,7 @@ public string Database
}
///
- ///
+ ///
///
[ConfigurationProperty(SeedElementName, DefaultValue = 0L)]
public long Seed
@@ -55,7 +55,7 @@ public long Seed
}
///
- ///
+ ///
///
[ConfigurationProperty(CacheSizeElementName, DefaultValue = (long) DomainConfiguration.DefaultKeyGeneratorCacheSize)]
public long CacheSize
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Elements/MappingRuleElement.cs b/Orm/Xtensive.Orm/Orm/Configuration/Elements/MappingRuleElement.cs
index 22cc92471d..a2c00732cf 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/Elements/MappingRuleElement.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Elements/MappingRuleElement.cs
@@ -1,6 +1,6 @@
-// Copyright (C) 2012 Xtensive LLC.
-// All rights reserved.
-// For conditions of distribution and use, see license.
+// Copyright (C) 2012-2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
// Created by: Denis Krjuchkov
// Created: 2012.02.07
@@ -25,7 +25,7 @@ public override object Identifier {
}
///
- ///
+ ///
///
[ConfigurationProperty(AssemblyElementName, IsKey = true)]
public string Assembly
@@ -35,7 +35,7 @@ public string Assembly
}
///
- ///
+ ///
///
[ConfigurationProperty(NamespaceElementName, IsKey = true)]
public string Namespace
@@ -45,7 +45,7 @@ public string Namespace
}
///
- ///
+ ///
///
[ConfigurationProperty(DatabaseElementName)]
public string Database
@@ -55,7 +55,7 @@ public string Database
}
///
- ///
+ ///
///
[ConfigurationProperty(SchemaElementName)]
public string Schema
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Elements/NamingConventionElement.cs b/Orm/Xtensive.Orm/Orm/Configuration/Elements/NamingConventionElement.cs
index 4e891a8f42..3c3c84670e 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/Elements/NamingConventionElement.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Elements/NamingConventionElement.cs
@@ -1,6 +1,6 @@
-// Copyright (C) 2003-2010 Xtensive LLC.
-// All rights reserved.
-// For conditions of distribution and use, see license.
+// Copyright (C) 2008-2024 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 Gamzov
// Created: 2008.08.07
@@ -21,7 +21,7 @@ public class NamingConventionElement : ConfigurationElement
private const string NamespaceSynonymsElementName = "namespaceSynonyms";
///
- ///
+ ///
///
[ConfigurationProperty(LetterCasePolicyElementName, IsRequired = false, IsKey = false, DefaultValue = "Default")]
public string LetterCasePolicy
@@ -31,7 +31,7 @@ public string LetterCasePolicy
}
///
- ///
+ ///
///
[ConfigurationProperty(NamespacePolicyElementName, IsRequired = false, IsKey = false, DefaultValue = "Default")]
public string NamespacePolicy
@@ -41,7 +41,7 @@ public string NamespacePolicy
}
///
- ///
+ ///
///
[ConfigurationProperty(NamingRulesElementName, IsRequired = false, IsKey = false, DefaultValue = "Default")]
public string NamingRules
@@ -51,7 +51,7 @@ public string NamingRules
}
///
- ///
+ ///
///
[ConfigurationProperty(NamespaceSynonymsElementName, IsRequired = false, IsKey = false)]
[ConfigurationCollection(typeof (ConfigurationCollection), AddItemName = "synonym")]
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Elements/SessionConfigurationElement.cs b/Orm/Xtensive.Orm/Orm/Configuration/Elements/SessionConfigurationElement.cs
index 3bab8025ec..1aaa1b7c05 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/Elements/SessionConfigurationElement.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Elements/SessionConfigurationElement.cs
@@ -1,6 +1,6 @@
-// Copyright (C) 2003-2010 Xtensive LLC.
-// All rights reserved.
-// For conditions of distribution and use, see license.
+// Copyright (C) 2008-2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
// Created by: Aleksey Gamzov
// Created: 2008.08.11
@@ -37,7 +37,7 @@ public class SessionConfigurationElement : ConfigurationCollectionElementBase
public override object Identifier { get { return Name; } }
///
- ///
+ ///
///
[ConfigurationProperty(NameElementName, IsKey = true, DefaultValue = "Default")]
public string Name {
@@ -46,7 +46,7 @@ public string Name {
}
///
- ///
+ ///
///
[ConfigurationProperty(UserNameElementName)]
public string UserName {
@@ -55,7 +55,7 @@ public string UserName {
}
///
- ///
+ ///
///
[ConfigurationProperty(PasswordElementName)]
public string Password {
@@ -64,17 +64,18 @@ public string Password {
}
///
- ///
+ ///
///
[ConfigurationProperty(CacheSizeElementName,
DefaultValue = SessionConfiguration.DefaultCacheSize)]
+ [IntegerValidator(MinValue = 1, MaxValue = int.MaxValue)]
public int CacheSize {
get { return (int) this[CacheSizeElementName]; }
set { this[CacheSizeElementName] = value; }
}
///
- ///
+ ///
///
[ConfigurationProperty(CacheTypeElementName, DefaultValue = "Default")]
public string CacheType {
@@ -83,7 +84,7 @@ public string CacheType {
}
///
- ///
+ ///
///
[ConfigurationProperty(OptionsElementName, DefaultValue = "Default")]
public string Options {
@@ -92,7 +93,7 @@ public string Options {
}
///
- ///
+ ///
///
[ConfigurationProperty(IsolationLevelElementName, DefaultValue = "RepeatableRead")]
public string DefaultIsolationLevel {
@@ -101,7 +102,7 @@ public string DefaultIsolationLevel {
}
///
- ///
+ ///
///
[ConfigurationProperty(CommandTimeoutElementName, DefaultValue = null)]
public int? DefaultCommandTimeout {
@@ -110,17 +111,18 @@ public int? DefaultCommandTimeout {
}
///
- ///
+ ///
///
[ConfigurationProperty(BatchSizeElementName,
DefaultValue = SessionConfiguration.DefaultBatchSize)]
+ [IntegerValidator(MinValue = 1, MaxValue = int.MaxValue)]
public int BatchSize {
get { return (int) this[BatchSizeElementName]; }
set { this[BatchSizeElementName] = value; }
}
///
- ///
+ ///
///
[ConfigurationProperty(ReaderPreloadingElementName, DefaultValue = "Default")]
public string ReaderPreloading {
@@ -129,7 +131,7 @@ public string ReaderPreloading {
}
///
- ///
+ ///
///
[ConfigurationProperty(ServiceContainerTypeElementName, DefaultValue = null)]
public string ServiceContainerType {
@@ -138,10 +140,11 @@ public string ServiceContainerType {
}
///
- /// .
+ /// .
///
[ConfigurationProperty(EntityChangeRegistrySizeElementName,
DefaultValue = SessionConfiguration.DefaultEntityChangeRegistrySize)]
+ [IntegerValidator(MinValue = 1, MaxValue = int.MaxValue)]
public int EntityChangeRegistrySize {
get { return (int) this[EntityChangeRegistrySizeElementName]; }
set { this[EntityChangeRegistrySizeElementName] = value; }
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Elements/VersioningConventionElement.cs b/Orm/Xtensive.Orm/Orm/Configuration/Elements/VersioningConventionElement.cs
index 7cb63e06c3..ca4021dd89 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/Elements/VersioningConventionElement.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Elements/VersioningConventionElement.cs
@@ -1,6 +1,6 @@
-// Copyright (C) 2018 Xtensive LLC.
-// All rights reserved.
-// For conditions of distribution and use, see license.
+// Copyright (C) 2018-2024 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: 2018.03.14
@@ -15,7 +15,7 @@ public sealed class VersioningConventionElement : ConfigurationElement
private const string DenyEntitySetOwnerVersionChangeElementName = "denyEntitySetOwnerVersionChange";
///
- ///
+ ///
///
[ConfigurationProperty(EntityVersioningPolicyElementName, IsRequired = false, IsKey = false, DefaultValue = "Default")]
public string EntityVersioningPolicy
@@ -25,7 +25,7 @@ public string EntityVersioningPolicy
}
///
- ///
+ ///
///
[ConfigurationProperty(DenyEntitySetOwnerVersionChangeElementName, IsRequired = false, IsKey = false, DefaultValue = false)]
public bool DenyEntitySetOwnerVersionChange
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/ExtensionConfigurationCollection.cs b/Orm/Xtensive.Orm/Orm/Configuration/ExtensionConfigurationCollection.cs
new file mode 100644
index 0000000000..ec6fee1c60
--- /dev/null
+++ b/Orm/Xtensive.Orm/Orm/Configuration/ExtensionConfigurationCollection.cs
@@ -0,0 +1,135 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using Xtensive.Core;
+
+namespace Xtensive.Orm.Configuration
+{
+ ///
+ /// Collection of configurations of extensions.
+ ///
+ [Serializable]
+ public sealed class ExtensionConfigurationCollection : LockableBase,
+ IEnumerable
+ {
+ private Dictionary extensionConfigurations;
+
+ ///
+ /// Number of configurations in collection.
+ ///
+ public long Count
+ {
+ [DebuggerStepThrough]
+ get => extensionConfigurations != null ? extensionConfigurations.Count : 0;
+ }
+
+ ///
+ /// Gets configuration of certain type from collection.
+ ///
+ /// Type of configuration to get.
+ /// Found configuration or .
+ [DebuggerStepThrough]
+ public T Get()
+ where T : ConfigurationBase
+ {
+ return extensionConfigurations != null && extensionConfigurations.TryGetValue(typeof(T), out var result)
+ ? (T) result
+ : null;
+ }
+
+ ///
+ /// Adds or replace configuration of certain type
+ ///
+ /// Type of configuration.
+ /// Configuration to add or to replace existing one.
+ [DebuggerStepThrough]
+ public void Set(T value)
+ where T : ConfigurationBase
+ {
+ EnsureNotLocked();
+ ArgumentValidator.EnsureArgumentNotNull(value, nameof(value));
+
+ var extensionConfigurationType = typeof(T);
+
+ if (extensionConfigurations == null) {
+ extensionConfigurations = new Dictionary();
+ }
+
+ extensionConfigurations[extensionConfigurationType] = value;
+ }
+
+ ///
+ /// Clears the collection.
+ ///
+ public void Clear()
+ {
+ EnsureNotLocked();
+ extensionConfigurations = null;
+ }
+
+ ///
+ public override void Lock(bool recursive)
+ {
+ base.Lock(recursive);
+ if (extensionConfigurations != null)
+ foreach (var pair in extensionConfigurations) {
+ pair.Value.Lock(recursive);
+ }
+ }
+
+ #region ICloneable methods
+
+ ///
+ public object Clone()
+ {
+ return new ExtensionConfigurationCollection(this);
+ }
+
+ #endregion
+
+ #region IEnumerable methods
+
+ ///
+ [DebuggerStepThrough]
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ return extensionConfigurations != null
+ ? extensionConfigurations.Values.GetEnumerator()
+ : Enumerable.Empty().GetEnumerator();
+ }
+
+ #endregion
+
+
+ // Constructors
+
+ ///
+ /// Initializes new instance of this type.
+ ///
+ public ExtensionConfigurationCollection()
+ {
+ }
+
+ ///
+ /// Initializes new instance of this type.
+ ///
+ /// The source to copy into this collection.
+ public ExtensionConfigurationCollection(ExtensionConfigurationCollection source)
+ : this()
+ {
+ ArgumentValidator.EnsureArgumentNotNull(source, nameof(source));
+ if (source.Count == 0)
+ return;
+ extensionConfigurations = new Dictionary(source.extensionConfigurations);
+ }
+ }
+}
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Interfaces/IConfigurationSectionReader{T}.cs b/Orm/Xtensive.Orm/Orm/Configuration/Interfaces/IConfigurationSectionReader{T}.cs
new file mode 100644
index 0000000000..9227e82b2f
--- /dev/null
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Interfaces/IConfigurationSectionReader{T}.cs
@@ -0,0 +1,55 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+
+using Microsoft.Extensions.Configuration;
+
+namespace Xtensive.Orm.Configuration
+{
+ ///
+ /// Reads certain type of configuration (DomainConfiguration, LoggingConfiguration, etc.) from IConfigurationSection API
+ ///
+ ///
+ public interface IConfigurationSectionReader
+ {
+ ///
+ /// Reads configuration from given configuration section.
+ ///
+ /// Root configuration section where specific configuration is placed (for domain configuration - where all domain configurations).
+ /// Instance of configuration.
+ TConfiguration Read(IConfigurationSection configurationSection);
+
+ ///
+ /// Reads configuration from given configuration section with specified name (if named configuration supported e.g. doman configuration)
+ ///
+ /// Root configuration section where specific configuration is placed (for domain configuration - where all domain configurations).
+ /// Name of configuration.
+ /// Instance of configuration.
+ TConfiguration Read(IConfigurationSection configurationSection, string nameOfConfiguration);
+
+ ///
+ /// Reads configuration (with default name if name is required) from default section (and with default name) from given configuration root.
+ ///
+ /// Root of all configuration sections.
+ /// Instance of configuration.
+ TConfiguration Read(IConfigurationRoot configurationRoot);
+
+ ///
+ /// Reads configuration (with default name if name is required) from given section of given configuration root.
+ ///
+ /// Root of all configuration sections.
+ /// Specific section name from which configuration should be read.
+ /// Instance of configuration.
+ TConfiguration Read(IConfigurationRoot configurationRoot, string sectionName);
+
+ ///
+ /// Reads configuration with given name from given section of given configuration root.
+ ///
+ /// Root of all configuration sections.
+ /// Specific section name from which configuration should be read.
+ /// Name of configuration.
+ /// Instance of configuration.
+ TConfiguration Read(IConfigurationRoot configurationRoot, string sectionName, string nameOfConfiguration);
+ }
+}
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Internals/ConfigurationSectionExtensions.cs b/Orm/Xtensive.Orm/Orm/Configuration/Internals/ConfigurationSectionExtensions.cs
new file mode 100644
index 0000000000..35f4c1d3e5
--- /dev/null
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Internals/ConfigurationSectionExtensions.cs
@@ -0,0 +1,34 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Extensions.Configuration;
+using Xtensive.Collections;
+
+namespace Xtensive.Orm.Configuration
+{
+ internal static class ConfigurationSectionExtensions
+ {
+ public static IEnumerable GetSelfOrChildren(this IConfigurationSection section, bool requiredName = false)
+ {
+ var children = section.GetChildren().ToList();
+ if (children.Count > 0) {
+ if (requiredName) {
+ var anyItemWithName = children.Any(i => i["name"] != null);
+ if (anyItemWithName) {
+ return children;
+ }
+ }
+ var firstChild = children[0];
+ var isIndexed = firstChild.Key == "0";
+ if (isIndexed)
+ return children;
+ else
+ return EnumerableUtils.One(section);
+ }
+ return children;
+ }
+ }
+}
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Internals/ConnectionInfoParser.cs b/Orm/Xtensive.Orm/Orm/Configuration/Internals/ConnectionInfoParser.cs
index 452d684ab9..908bd3bbfc 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/Internals/ConnectionInfoParser.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Internals/ConnectionInfoParser.cs
@@ -5,17 +5,61 @@
// Created: 2013.09.27
using System;
+using System.Collections;
+using System.Collections.Generic;
using System.Configuration;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.Configuration;
using Xtensive.Core;
namespace Xtensive.Orm.Configuration
{
internal static class ConnectionInfoParser
{
+ private readonly struct UnifiedAccessor
+ {
+ private readonly IDictionary connectionStringsAsDict;
+ private readonly ConnectionStringSettingsCollection connectionStrings;
+
+ public string this[string key] {
+ get {
+ if (connectionStrings != null)
+ return connectionStrings[key]?.ConnectionString;
+ else if(connectionStringsAsDict!=null)
+ return connectionStringsAsDict[key];
+ return null;
+ }
+ }
+
+ public UnifiedAccessor(ConnectionStringSettingsCollection oldConnectionStrings)
+ {
+ connectionStringsAsDict = null;
+ connectionStrings = oldConnectionStrings;
+ }
+ public UnifiedAccessor(IDictionary connectionStrings)
+ {
+ connectionStringsAsDict = connectionStrings;
+ this.connectionStrings = null;
+ }
+ }
+
public static ConnectionInfo GetConnectionInfo(System.Configuration.Configuration configuration,string connectionUrl, string provider, string connectionString)
{
- bool connectionUrlSpecified = !string.IsNullOrEmpty(connectionUrl);
- bool connectionStringSpecified = !string.IsNullOrEmpty(connectionString) && !string.IsNullOrEmpty(provider);
+ var accessor = new UnifiedAccessor(configuration.ConnectionStrings.ConnectionStrings);
+ return GetConnectionInfoInternal(accessor, connectionUrl, provider, connectionString);
+ }
+
+ public static ConnectionInfo GetConnectionInfo(IDictionary connectionStrings, string connectionUrl, string provider, string connectionString)
+ {
+ var accessor = new UnifiedAccessor(connectionStrings);
+ return GetConnectionInfoInternal(accessor, connectionUrl, provider, connectionString);
+ }
+
+ private static ConnectionInfo GetConnectionInfoInternal(in UnifiedAccessor connectionStringAccessor,
+ string connectionUrl, string provider, string connectionString)
+ {
+ var connectionUrlSpecified = !string.IsNullOrEmpty(connectionUrl);
+ var connectionStringSpecified = !string.IsNullOrEmpty(connectionString) && !string.IsNullOrEmpty(provider);
if (connectionUrlSpecified && connectionStringSpecified)
throw new InvalidOperationException(Strings.ExConnectionInfoIsWrongYouShouldSetEitherConnectionUrlElementOrProviderAndConnectionStringElements);
@@ -24,30 +68,31 @@ public static ConnectionInfo GetConnectionInfo(System.Configuration.Configuratio
return new ConnectionInfo(connectionUrl);
if (connectionStringSpecified)
- return new ConnectionInfo(provider, ExpandConnectionString(configuration, connectionString));
+ return new ConnectionInfo(provider,
+ ExpandConnectionString(connectionStringAccessor, connectionString));
// Neither connection string, nor connection url specified.
// Leave connection information undefined.
return null;
}
- private static string ExpandConnectionString(System.Configuration.Configuration configuration, string connectionString)
+ private static string ExpandConnectionString(in UnifiedAccessor connectionStrings, string connectionString)
{
const string prefix = "#";
if (!connectionString.StartsWith(prefix, StringComparison.Ordinal))
return connectionString;
- string connectionStringName = connectionString.Substring(prefix.Length);
+ var connectionStringName = connectionString[prefix.Length..];
- var connectionStringSetting = configuration.ConnectionStrings.ConnectionStrings[connectionStringName];
- if (connectionStringSetting==null)
+ var connectionStringSetting = connectionStrings[connectionStringName];
+ if (connectionStringSetting == null)
throw new InvalidOperationException(string.Format(Strings.ExConnectionStringWithNameXIsNotFound, connectionStringName));
- if (string.IsNullOrEmpty(connectionStringSetting.ConnectionString))
+ if (string.IsNullOrEmpty(connectionStringSetting))
throw new InvalidOperationException(string.Format(Strings.ExConnectionStringWithNameXIsNullOrEmpty, connectionStringName));
- return connectionStringSetting.ConnectionString;
+ return connectionStringSetting;
}
}
}
\ No newline at end of file
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Internals/DomainConfigurationReaders.cs b/Orm/Xtensive.Orm/Orm/Configuration/Internals/DomainConfigurationReaders.cs
new file mode 100644
index 0000000000..73af7f4711
--- /dev/null
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Internals/DomainConfigurationReaders.cs
@@ -0,0 +1,399 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Extensions.Configuration;
+using Xtensive.Core;
+using Xtensive.Orm.Configuration.Options;
+
+namespace Xtensive.Orm.Configuration
+{
+ internal sealed class XmlToDomainConfigurationReader : DomainConfigurationReader
+ {
+ protected override bool ValidateCorrectFormat(IConfigurationSection allDomainsSection, string domainName)
+ {
+ return !allDomainsSection.GetSection(domainName).GetChildren().Any()
+ && allDomainsSection.GetSection(string.Format(NamedDomainTemplate, domainName)).GetChildren().Any();
+ }
+
+ protected override IConfigurationSection GetDomainSection(IConfigurationSection allDomainsSection, string domainName) =>
+ allDomainsSection.GetSection(string.Format(NamedDomainTemplate, domainName));
+
+ protected override void ProcessNamingConvention(IConfigurationSection namingConventionSection, DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ if (namingConventionSection == null) {
+ domainConfiguratonOptions.NamingConventionRaw = null;
+ return;
+ }
+
+ if (namingConventionSection.GetChildren().Any()) {
+ var namingConvetion = namingConventionSection.Get();
+ if (namingConvetion!= null) {
+ var synonymsSection = namingConventionSection.GetSection(NamespaceSynonymSectionName);
+ var synonyms = synonymsSection != null && synonymsSection.GetChildren().Any()
+ ? synonymsSection.GetSection(SynonymElementName)
+ .GetSelfOrChildren()
+ .Select(s => s.Get())
+ .Where(ns => ns != null)
+ .ToArray()
+ : Array.Empty();
+
+ namingConvetion.NamespaceSynonyms = synonyms;
+ domainConfiguratonOptions.NamingConventionRaw = namingConvetion;
+ }
+ else {
+ domainConfiguratonOptions.NamingConventionRaw = null;
+ }
+ }
+ }
+
+ protected override void ProcessVersioningConvention(IConfigurationSection versioningConventionSection, DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ if (versioningConventionSection == null) {
+ domainConfiguratonOptions.VersioningConvention = null;
+ return;
+ }
+
+ if (versioningConventionSection.GetChildren().Any()) {
+ var versioningConvention = versioningConventionSection.Get();
+
+ domainConfiguratonOptions.VersioningConvention = versioningConvention != null
+ ? versioningConvention
+ : null;
+ }
+ }
+
+ protected override void ProcessTypes(IConfigurationSection typesSection, DomainConfigurationOptions domainConfigurationOptions)
+ {
+ if (typesSection == null) {
+ domainConfigurationOptions.Types = Array.Empty();
+ return;
+ }
+ if (TryProcessTypeRegistrationsWithAttributes(typesSection, domainConfigurationOptions)
+ || TryProcessTypeRegistrationsWithNodes(typesSection, domainConfigurationOptions)) {
+ return;
+ }
+
+ domainConfigurationOptions.Types = Array.Empty();
+ }
+
+ private bool TryProcessTypeRegistrationsWithAttributes(IConfigurationSection typesSection,
+ DomainConfigurationOptions domainConfigurationOptions)
+ {
+ var registrations = typesSection.GetSection(OldStyleTypeRegistrationElementName);
+ if (registrations != null && registrations.GetChildren().Any()) {
+ domainConfigurationOptions.Types = registrations
+ .GetSelfOrChildren()
+ .Select(s => s.Get())
+ .Where(tr => tr != null)
+ .ToArray();
+ return true;
+ }
+ return false;
+ }
+
+ private bool TryProcessTypeRegistrationsWithNodes(IConfigurationSection typesSection,
+ DomainConfigurationOptions domainConfigurationOptions)
+ {
+ var registrations = typesSection.GetSection(TypeRegistrationElementName);
+ if (registrations == null)
+ return false;
+
+ domainConfigurationOptions.Types = registrations
+ .GetSelfOrChildren()
+ .Select(s => s.Get())
+ .Where(tr => tr != null)
+ .ToArray();
+ return true;
+ }
+
+ protected override void ProcessDatabases(IConfigurationSection databasesSection, DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ domainConfiguratonOptions.Databases.Clear();
+ if (databasesSection == null)
+ return;
+ var databaseElement = databasesSection.GetSection(DatabaseElementName);
+ if (databaseElement == null)
+ return;
+ foreach (var section in databaseElement.GetSelfOrChildren(true)) {
+ var dbItem = section.Get();
+ if (dbItem == null)
+ continue;
+ domainConfiguratonOptions.Databases.Add(dbItem.Name, dbItem);
+ }
+ }
+
+ protected override void ProcessKeyGenerators(IConfigurationSection keyGeneratorsSection, DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ domainConfiguratonOptions.KeyGenerators.Clear();
+ ProcessCollectionOfOptions(keyGeneratorsSection, KeyGeneratorElementName, domainConfiguratonOptions.KeyGenerators);
+ }
+
+ protected override void ProcessIgnoreRules(IConfigurationSection ignoreRulesSection, Options.DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ domainConfiguratonOptions.IgnoreRules.Clear();
+ ProcessCollectionOfOptions(ignoreRulesSection, RuleElementName, domainConfiguratonOptions.IgnoreRules);
+ }
+
+ protected override void ProcessMappingRules(IConfigurationSection mappingRulesSection, Options.DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ domainConfiguratonOptions.MappingRules.Clear();
+ ProcessCollectionOfOptions(mappingRulesSection, RuleElementName, domainConfiguratonOptions.MappingRules);
+ }
+
+ protected override void ProcessSessions(IConfigurationSection sessionsSection, Options.DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ domainConfiguratonOptions.Sessions.Clear();
+ if (sessionsSection == null)
+ return;
+ var sessionElement = sessionsSection.GetSection(SessionElementName);
+ if (sessionElement == null)
+ return;
+ foreach (var section in sessionElement.GetSelfOrChildren(true)) {
+ var sessionItem = section.Get();
+ if (sessionItem == null)
+ continue;
+ domainConfiguratonOptions.Sessions.Add(sessionItem.Name, sessionItem);
+ }
+ }
+
+ private void ProcessCollectionOfOptions(IConfigurationSection collectionSection, string itemKey, OptionsCollection collection)
+ where TOption : class, IIdentifyableOptions
+ {
+ if (collectionSection == null)
+ return;
+ var collectionElement = collectionSection.GetSection(itemKey);
+ if (collectionElement == null)
+ return;
+ foreach (var item in collectionElement.GetSelfOrChildren()) {
+ var optItem = item.Get();
+ collection.Add(optItem);
+ }
+ }
+ }
+
+ internal sealed class JsonToDomainConfigurationReader : DomainConfigurationReader
+ {
+ protected override bool ValidateCorrectFormat(IConfigurationSection allDomainsSection, string domainName)
+ {
+ return allDomainsSection.GetSection(domainName).GetChildren().Any()
+ && !allDomainsSection.GetSection(string.Format(NamedDomainTemplate, domainName)).GetChildren().Any();
+ }
+
+ protected override IConfigurationSection GetDomainSection(IConfigurationSection allDomainsSection, string domainName) =>
+ allDomainsSection.GetSection(domainName);
+
+ protected override void ProcessNamingConvention(IConfigurationSection namingConventionSection, DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ if (namingConventionSection == null || !namingConventionSection.GetChildren().Any())
+ return;
+ var jsonListVariant = namingConventionSection.Get();
+ if (jsonListVariant.NamespaceSynonyms == null)
+ jsonListVariant.NamespaceSynonyms = Array.Empty();
+ domainConfiguratonOptions.NamingConventionRaw = jsonListVariant;
+ }
+ }
+
+ internal abstract class DomainConfigurationReader : IConfigurationSectionReader
+ {
+ protected sealed class ConfigurationParserContext
+ {
+ public readonly IConfigurationRoot CurrentConfiguration;
+
+ public readonly IConfigurationSection CurrentSection;
+
+ public readonly string SectionName;
+
+ public readonly IDictionary ConnectionStrings;
+
+ public ConfigurationParserContext(IConfigurationRoot currentConfiguration, string sectionName)
+ {
+ CurrentConfiguration = currentConfiguration;
+ CurrentSection = currentConfiguration.GetSection(sectionName);
+ ConnectionStrings = currentConfiguration.Get>();
+ }
+
+ public ConfigurationParserContext(IConfigurationSection currentSection, IDictionary connectionStrings)
+ {
+ CurrentSection = currentSection;
+ ConnectionStrings = connectionStrings;
+ }
+ }
+
+ protected const string RuleElementName = "Rule";
+ protected const string KeyGeneratorElementName = "KeyGenerator";
+ protected const string OldStyleTypeRegistrationElementName = "Add";
+ protected const string TypeRegistrationElementName = "Registration";
+ protected const string SynonymElementName = "Synonym";
+ protected const string DatabaseElementName = "Database";
+ protected const string SessionElementName = "Session";
+
+ protected const string NamingConventionSectionName = "NamingConvention";
+ protected const string VersioningConventionSectionName = "VersioningConvention";
+ protected const string TypesSectionName = "Types";
+ protected const string DatabasesSectionName = "Databases";
+ protected const string KeyGeneratorsSectionName = "KeyGenerators";
+ protected const string IgnoreRulesSectionName = "IgnoreRules";
+ protected const string MappingRulesSectionName = "MappingRules";
+ protected const string SessionsSectionName = "Sessions";
+ protected const string NamespaceSynonymSectionName = "NamespaceSynonyms";
+ protected const string DomainsSectionName = "Domains";
+
+ protected const string NamedDomainTemplate = "Domain:{0}";
+
+ ///
+ /// Gets domain configuration with name "Default" from default section.
+ ///
+ /// Configration root.
+ /// DomainConfiguration instance if it was read successfully, otherwise, .
+ public DomainConfiguration Read(IConfigurationRoot configurationRoot) =>
+ Read(configurationRoot, WellKnown.DefaultConfigurationSection, WellKnown.DefaultDomainConfigurationName);
+
+ ///
+ /// Gets domain configuration with name "Default" from .
+ ///
+ /// Configration root
+ /// Custom section name where domains are placed.
+ /// DomainConfiguration instance if it was read successfully, otherwise, .
+ public DomainConfiguration Read(IConfigurationRoot configurationRoot, string sectionName) =>
+ Read(configurationRoot, sectionName, WellKnown.DefaultDomainConfigurationName);
+
+ ///
+ /// Gets domain configuration with given name from .
+ ///
+ /// Configration root.
+ /// Custom section name where domains are placed.
+ /// Name of domain configuration.
+ /// DomainConfiguration instance if it was read successfully, otherwise, .
+ public DomainConfiguration Read(IConfigurationRoot configurationRoot, string sectionName, string nameOfConfiguration)
+ {
+ var allDomainsSection = configurationRoot.GetSection(sectionName).GetSection(DomainsSectionName);
+ if (allDomainsSection == null || !allDomainsSection.GetChildren().Any())
+ return null;
+
+ var domainConfigurationSection = GetDomainSection(allDomainsSection, nameOfConfiguration);
+ if (domainConfigurationSection == null || !domainConfigurationSection.GetChildren().Any())
+ return null;
+
+ var connectionStrings = configurationRoot.GetSection("ConnectionStrings").Get>();
+ var context = new ConfigurationParserContext(domainConfigurationSection, connectionStrings);
+
+ return ReadInternal(context);
+ }
+
+ ///
+ /// Gets domain configuration with name "Default" from .
+ ///
+ /// Root section where all domain configurations stored, by default
+ /// DomainConfiguration instance if it was read successfully, otherwise, .
+ public DomainConfiguration Read(IConfigurationSection rootSection) =>
+ Parse(rootSection, WellKnown.DefaultDomainConfigurationName, null);
+
+ ///
+ /// Gets domain configuration with given name from .
+ ///
+ /// Root section where all domain configurations stored, by default
+ /// Name of domain configuration.
+ /// DomainConfiguration instance if it was read successfully, otherwise, .
+ public DomainConfiguration Read(IConfigurationSection rootSection, string nameOfConfiguration) =>
+ Parse(rootSection, nameOfConfiguration, null);
+
+ ///
+ /// Gets domain configuration with name "Default" from .
+ ///
+ /// Root section where all domain configurations stored, by default
+ ///
+ /// Connection strings in form of dictionary of strings. Required if connection string alias is used in domain configuration connection settings
+ ///
+ /// DomainConfiguration instance if it was read successfully, otherwise, .
+ public DomainConfiguration Parse(IConfigurationSection rootSection, Dictionary connectionStrings)
+ => Parse(rootSection, WellKnown.DefaultDomainConfigurationName, connectionStrings);
+
+ ///
+ /// Gets domain configuration with given name from .
+ ///
+ /// Root section where all domain configurations stored, by default
+ /// Name of domain configuration.
+ ///
+ /// Connection strings in form of dictionary of strings. Required if connection string alias is used in domain configuration connection settings
+ ///
+ /// DomainConfiguration instance if it was read successfully, otherwise, .
+ public DomainConfiguration Parse(IConfigurationSection rootSection, string nameOfConfiguration, Dictionary connectionStrings)
+ {
+ var allDomainsSection = rootSection.GetSection(DomainsSectionName);
+ if (allDomainsSection == null || !allDomainsSection.GetChildren().Any())
+ return null;
+
+ var domainConfigurationSection = GetDomainSection(allDomainsSection, nameOfConfiguration);
+ if (domainConfigurationSection == null || !domainConfigurationSection.GetChildren().Any())
+ return null;
+
+ var context = new ConfigurationParserContext(domainConfigurationSection, connectionStrings);
+ return ReadInternal(context);
+ }
+
+ protected abstract bool ValidateCorrectFormat(IConfigurationSection allDomainsSection, string domainName);
+
+ protected abstract IConfigurationSection GetDomainSection(IConfigurationSection allDomainsSection, string domainName);
+
+ private DomainConfiguration ReadInternal(ConfigurationParserContext context)
+ {
+ var domainByNameSection = context.CurrentSection;
+ if (domainByNameSection == null || !domainByNameSection.GetChildren().Any()) {
+ return null;
+ }
+ // this handles only root properties of domain configuration
+ var domainConfigurationOptions = context.CurrentSection.Get();
+ if (domainConfigurationOptions == null) {
+ return null;
+ }
+
+ // all sub-items require manual reading;
+ ProcessNamingConvention(domainByNameSection.GetSection(NamingConventionSectionName), domainConfigurationOptions);
+ ProcessVersioningConvention(domainByNameSection.GetSection(VersioningConventionSectionName), domainConfigurationOptions);
+ ProcessTypes(domainByNameSection.GetSection(TypesSectionName), domainConfigurationOptions);
+ ProcessDatabases(domainByNameSection.GetSection(DatabasesSectionName), domainConfigurationOptions);
+ ProcessKeyGenerators(domainByNameSection.GetSection(KeyGeneratorsSectionName), domainConfigurationOptions);
+ ProcessIgnoreRules(domainByNameSection.GetSection(IgnoreRulesSectionName), domainConfigurationOptions);
+ ProcessMappingRules(domainByNameSection.GetSection(MappingRulesSectionName), domainConfigurationOptions);
+ ProcessSessions(domainByNameSection.GetSection(SessionsSectionName), domainConfigurationOptions);
+
+ return domainConfigurationOptions.ToNative(context.ConnectionStrings);
+ }
+
+ protected virtual void ProcessNamingConvention(IConfigurationSection namingConventionSection, DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ }
+
+ protected virtual void ProcessVersioningConvention(IConfigurationSection versioningConventionSection, DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ }
+
+ protected virtual void ProcessTypes(IConfigurationSection typesSection, DomainConfigurationOptions domainConfigurationOptions)
+ {
+ }
+
+ protected virtual void ProcessDatabases(IConfigurationSection databasesSection, DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ }
+
+ protected virtual void ProcessKeyGenerators(IConfigurationSection keyGeneratorsSection, DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ }
+
+ protected virtual void ProcessIgnoreRules(IConfigurationSection ignoreRulesSection, DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ }
+
+ protected virtual void ProcessMappingRules(IConfigurationSection mappingRulesSection, DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ }
+
+ protected virtual void ProcessSessions(IConfigurationSection sessionsSection, DomainConfigurationOptions domainConfiguratonOptions)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Internals/LoggingConfigurationReader.cs b/Orm/Xtensive.Orm/Orm/Configuration/Internals/LoggingConfigurationReader.cs
new file mode 100644
index 0000000000..6308dc9fde
--- /dev/null
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Internals/LoggingConfigurationReader.cs
@@ -0,0 +1,98 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+
+using System;
+using System.Linq;
+using Microsoft.Extensions.Configuration;
+using Xtensive.Core;
+
+namespace Xtensive.Orm.Configuration
+{
+ internal sealed class LoggingConfigurationReader : IConfigurationSectionReader
+ {
+ private const string LoggingSectionName = "Logging";
+ private const string LogsSectionName = "Logs";
+
+ private const string ProviderElementName = "Provider";
+ private const string LogElementName = "Log";
+ private const string LogSourceElementName = "Source";
+ private const string LogTargerElementName = "Target";
+
+ ///
+ public LoggingConfiguration Read(IConfigurationRoot configurationRoot, string sectionName)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationRoot, nameof(configurationRoot));
+ ArgumentValidator.EnsureArgumentNotNullOrEmpty(sectionName, nameof(sectionName));
+
+ return Read(configurationRoot.GetSection(sectionName));
+ }
+
+ ///
+ public LoggingConfiguration Read(IConfigurationRoot configurationRoot)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationRoot, nameof(configurationRoot));
+
+ return Read(configurationRoot.GetSection(WellKnown.DefaultConfigurationSection));
+ }
+
+ ///
+ public LoggingConfiguration Read(IConfigurationRoot configurationRoot, string sectionName, string nameOfConfiguration)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationRoot, nameof(configurationRoot));
+ ArgumentValidator.EnsureArgumentNotNullOrEmpty(sectionName, nameof(sectionName));
+
+ return Read(configurationRoot.GetSection(sectionName));
+ }
+
+ ///
+ public LoggingConfiguration Read(IConfigurationSection configurationSection)
+ {
+ ArgumentValidator.EnsureArgumentNotNull(configurationSection, nameof(configurationSection));
+
+ var ormConfigurationSection = configurationSection;
+
+ var loggingSection = ormConfigurationSection.GetSection(LoggingSectionName);
+
+ if (loggingSection != null && loggingSection.GetChildren().Any()) {
+ var provider = loggingSection.GetSection(ProviderElementName)?.Value;
+ var logsSection = loggingSection.GetSection(LogsSectionName);
+ IConfigurationSection logElement;
+
+ if (logsSection != null && logsSection.GetChildren().Any()) {
+ logElement = logsSection.GetSection(LogElementName);
+ if (logElement == null || !logElement.GetChildren().Any()) {
+ logElement = logsSection;
+ }
+ }
+ else {
+ logElement = loggingSection.GetSection(LogElementName);
+ }
+
+ var configuration = new LoggingConfiguration(provider);
+ foreach (var logItem in logElement.GetSelfOrChildren()) {
+ var source = logItem.GetSection(LogSourceElementName).Value;
+ var target = logItem.GetSection(LogTargerElementName).Value;
+ if (source.IsNullOrEmpty() || target.IsNullOrEmpty())
+ throw new InvalidOperationException();
+ configuration.Logs.Add(new LogConfiguration(source, target));
+ }
+
+ if (configuration.Provider == null && configuration.Logs.Count==0)
+ throw new InvalidOperationException();
+
+ return configuration;
+ }
+
+ throw new InvalidOperationException(
+ string.Format(Strings.ExSectionIsNotFoundInApplicationConfigurationFile, WellKnown.DefaultConfigurationSection));
+ }
+
+ ///
+ public LoggingConfiguration Read(IConfigurationSection configurationSection, string nameOfConfiguration)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/LogConfiguration.cs b/Orm/Xtensive.Orm/Orm/Configuration/LogConfiguration.cs
index 79fdbea29e..8326636011 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/LogConfiguration.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/LogConfiguration.cs
@@ -1,25 +1,42 @@
-// Copyright (C) 2013 Xtensive LLC.
+// Copyright (C) 2013 Xtensive LLC.
// All rights reserved.
// For conditions of distribution and use, see license.
// Created by: Alexey Kulakov
// Created: 2013.09.17
+using Xtensive.Core;
+
namespace Xtensive.Orm.Configuration
{
///
/// Configuration of log.
///
- public class LogConfiguration
+ public class LogConfiguration : LockableBase
{
+ private string source;
+ private string target;
+
///
/// Gets or sets source or sources of log separated by comma.
///
- public string Source { get; set; }
+ public string Source {
+ get => source;
+ set {
+ EnsureNotLocked();
+ source = value;
+ }
+ }
///
/// Gets or sets targer of log.
///
- public string Target { get; set; }
+ public string Target {
+ get => target;
+ set {
+ EnsureNotLocked();
+ target = value;
+ }
+ }
///
/// Creates new instance of this class
@@ -28,8 +45,8 @@ public class LogConfiguration
/// Targer for new log
public LogConfiguration(string source, string target)
{
- Source = source;
- Target = target;
+ this.source = source;
+ this.target = target;
}
}
}
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/LoggingConfiguration.cs b/Orm/Xtensive.Orm/Orm/Configuration/LoggingConfiguration.cs
index 8975e4e86f..ed8e7cbaac 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/LoggingConfiguration.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/LoggingConfiguration.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2020 Xtensive LLC.
+// Copyright (C) 2013-2024 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
@@ -7,6 +7,8 @@
using System;
using System.Collections.Generic;
using System.Configuration;
+using System.Linq;
+using Microsoft.Extensions.Configuration;
using Xtensive.Core;
using ConfigurationSection = Xtensive.Orm.Configuration.Elements.ConfigurationSection;
@@ -15,17 +17,59 @@ namespace Xtensive.Orm.Configuration
///
/// Configuration of logging
///
- public sealed class LoggingConfiguration
+ public sealed class LoggingConfiguration : ConfigurationBase
{
+ private string provider;
+ private IList logs;
+
///
/// Gets or sets external provider. Provider's name specified as assembly qualified name.
///
- public string Provider { get; set; }
+ public string Provider {
+ get => provider;
+ set {
+ EnsureNotLocked();
+ provider = value;
+ }
+ }
///
/// Gets or sets list of
///
- public IList Logs { get; set; }
+ public IList Logs {
+ get => logs;
+ set { EnsureNotLocked(); logs = value; }
+ }
+
+ public override void Lock(bool recursive)
+ {
+ if (logs is ListnativeList) {
+ logs = nativeList.AsReadOnly();
+ }
+ else {
+ logs = logs.ToList().AsReadOnly();
+ }
+ base.Lock(recursive);
+
+ foreach (var log in logs) {
+ log.Lock(recursive);
+ }
+ }
+
+ ///
+ protected override LoggingConfiguration CreateClone() => new LoggingConfiguration();
+
+ ///
+ protected override void CopyFrom(ConfigurationBase source)
+ {
+ base.CopyFrom(source);
+ var configuration = source as LoggingConfiguration;
+ Logs = new List(configuration.Logs);
+ Provider = configuration.Provider;
+ }
+
+ ///
+ public override LoggingConfiguration Clone() => (LoggingConfiguration) base.Clone();
///
/// Loads logging configuration from the default configuration section.
@@ -43,7 +87,7 @@ public static LoggingConfiguration Load()
/// Loaded configuration.
public static LoggingConfiguration Load(string sectionName)
{
- ArgumentValidator.EnsureArgumentNotNullOrEmpty(sectionName, "sectionName");
+ ArgumentValidator.EnsureArgumentNotNullOrEmpty(sectionName, nameof(sectionName));
var section = (ConfigurationSection)ConfigurationManager.GetSection(sectionName);
if (section==null)
@@ -70,8 +114,8 @@ public static LoggingConfiguration Load(System.Configuration.Configuration confi
/// Loaded configuration.
public static LoggingConfiguration Load(System.Configuration.Configuration configuration, string sectionName)
{
- ArgumentValidator.EnsureArgumentNotNull(configuration, "configuration");
- ArgumentValidator.EnsureArgumentNotNullOrEmpty(sectionName, "sectionName");
+ ArgumentValidator.EnsureArgumentNotNull(configuration, nameof(configuration));
+ ArgumentValidator.EnsureArgumentNotNullOrEmpty(sectionName, nameof(sectionName));
var section = (ConfigurationSection) configuration.GetSection(sectionName);
if (section==null)
@@ -80,12 +124,46 @@ public static LoggingConfiguration Load(System.Configuration.Configuration confi
return loggingConfiguration;
}
+ ///
+ /// Loads logging configuration from the specified configuration section.
+ ///
+ /// Root configuration section where logging configuration is placed.
+ /// Logging configuration.
+ /// The logging section is not found.
+ public static LoggingConfiguration Load(IConfigurationSection configurationSection)
+ {
+ return new LoggingConfigurationReader().Read(configurationSection);
+ }
+
+ ///
+ /// Loads logging configuration from default section.
+ ///
+ /// Root of configuration.
+ /// Read configuration.
+ /// The logging section is not found.
+ public static LoggingConfiguration Load(IConfigurationRoot configurationRoot)
+ {
+ return new LoggingConfigurationReader().Read(configurationRoot);
+ }
+
+ ///
+ /// Loads logging configuration from specific section.
+ ///
+ /// Root of configuration.
+ /// Section name where logging configuration is defined.
+ /// Read configuration.
+ /// The logging section is not found.
+ public static LoggingConfiguration Load(IConfigurationRoot configurationRoot, string sectionName)
+ {
+ return new LoggingConfigurationReader().Read(configurationRoot, sectionName);
+ }
+
///
/// Creates instance of this class.
///
public LoggingConfiguration()
{
- Logs = new List();
+ logs = new List();
}
///
@@ -94,8 +172,8 @@ public LoggingConfiguration()
/// External provider for logging. Provider's name specified as assembly qualified name.
public LoggingConfiguration(string provider)
{
- Provider = provider;
- Logs = new List();
+ this.provider = provider;
+ logs = new List();
}
}
}
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/NamingConvention.cs b/Orm/Xtensive.Orm/Orm/Configuration/NamingConvention.cs
index 461b274273..5a71e0f53b 100644
--- a/Orm/Xtensive.Orm/Orm/Configuration/NamingConvention.cs
+++ b/Orm/Xtensive.Orm/Orm/Configuration/NamingConvention.cs
@@ -18,6 +18,10 @@ namespace Xtensive.Orm.Configuration
public class NamingConvention : LockableBase,
ICloneable
{
+ public const LetterCasePolicy DefaultLetterCasePolicy = LetterCasePolicy.Default;
+ public const NamespacePolicy DefaultNamespacePolicy = NamespacePolicy.Default;
+ public const NamingRules DefaultNamingRules = NamingRules.Default;
+
private LetterCasePolicy letterCasePolicy;
private NamespacePolicy namespacePolicy;
private NamingRules namingRules;
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Options/DatabaseOptions.cs b/Orm/Xtensive.Orm/Orm/Configuration/Options/DatabaseOptions.cs
new file mode 100644
index 0000000000..e609e57754
--- /dev/null
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Options/DatabaseOptions.cs
@@ -0,0 +1,69 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using System;
+using Xtensive.Core;
+using Xtensive.Orm.Model;
+
+namespace Xtensive.Orm.Configuration.Options
+{
+ internal sealed class DatabaseOptions : IIdentifyableOptions,
+ IValidatableOptions,
+ IToNativeConvertable,
+ INamedOptionsCollectionElement
+ {
+ public object Identifier => Name;
+
+ ///
+ /// Logical database name.
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// Physical database name.
+ ///
+ public string RealName { get; set; }
+
+ ///
+ /// Type ID minimal value
+ /// for types mapped to this database.
+ /// Default value is .
+ ///
+ public int MinTypeId { get; set; } = TypeInfo.MinTypeId;
+
+ ///
+ /// Type ID maximal value
+ /// for types mapped to this database.
+ /// Default value is .
+ ///
+ public int MaxTypeId { get; set; } = int.MaxValue;
+
+ ///
+ /// is empty or null.
+ /// or is not in valid range.
+ public void Validate()
+ {
+ if (Name.IsNullOrEmpty())
+ throw new ArgumentException(Strings.ExArgumentCannotBeEmptyString,"Name");
+ if (MinTypeId < TypeInfo.MinTypeId)
+ throw new ArgumentOutOfRangeException("MinTypeId", MinTypeId,
+ string.Format(Strings.ExArgumentMustBeGreaterThatOrEqualX, TypeInfo.MinTypeId));
+ if (MaxTypeId < TypeInfo.MinTypeId)
+ throw new ArgumentOutOfRangeException("MaxTypeId", MinTypeId,
+ string.Format(Strings.ExArgumentMustBeGreaterThatOrEqualX, TypeInfo.MinTypeId));
+ }
+
+ ///
+ public DatabaseConfiguration ToNative()
+ {
+ Validate();
+
+ return new DatabaseConfiguration(Name) {
+ RealName = RealName,
+ MinTypeId = MinTypeId,
+ MaxTypeId = MaxTypeId,
+ };
+ }
+ }
+}
diff --git a/Orm/Xtensive.Orm/Orm/Configuration/Options/DomainConfigurationOptions.cs b/Orm/Xtensive.Orm/Orm/Configuration/Options/DomainConfigurationOptions.cs
new file mode 100644
index 0000000000..fa29acef41
--- /dev/null
+++ b/Orm/Xtensive.Orm/Orm/Configuration/Options/DomainConfigurationOptions.cs
@@ -0,0 +1,311 @@
+// Copyright (C) 2024 Xtensive LLC.
+// This code is distributed under MIT license terms.
+// See the License.txt file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using Xtensive.Core;
+
+namespace Xtensive.Orm.Configuration.Options
+{
+ internal sealed class DomainConfigurationOptions
+ {
+ ///
+ /// Domain configuration name.
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// Url that will be used in .
+ ///
+ public string ConnectionUrl { get; set; } = null;
+
+ ///
+ /// Connection string that will be used in .
+ ///
+ public string ConnectionString { get; set; } = null;
+
+ ///
+ /// Provider that will be used in .
+ ///
+ public string Provider { get; set; } = WellKnown.Provider.SqlServer;
+
+ ///
+ /// Types tha are about to be registered in .
+ ///
+ public TypeRegistrationOptions[] Types { get; set; } = Array.Empty();
+
+ ///
+ /// Size of the key cache. Default value is
+ ///
+ public int KeyCacheSize { get; set; } = DomainConfiguration.DefaultKeyCacheSize;
+
+ ///
+ /// Size of the key generator cache size.
+ /// Default value is
+ ///
+ public int KeyGeneratorCacheSize { get; set; } = DomainConfiguration.DefaultKeyGeneratorCacheSize;
+
+ ///
+ /// Size of the query cache (see ).
+ /// Default value is .
+ ///
+ public int QueryCacheSize { get; set; } = DomainConfiguration.DefaultQueryCacheSize;
+
+ ///
+ /// Size of the record set mapping cache.
+ /// Default value is .
+ ///
+ public int RecordSetMappingCacheSize { get; set; } = DomainConfiguration.DefaultRecordSetMappingCacheSize;
+
+ ///
+ /// Domain upgrade behavior.
+ ///
+ public DomainUpgradeMode UpgradeMode { get; set; } = DomainConfiguration.DefaultUpgradeMode;
+
+ ///
+ /// format.
+ ///
+ public SchemaSyncExceptionFormat SchemaSyncExceptionFormat { get; set; } = DomainConfiguration.DefaultSchemaSyncExceptionFormat;
+
+ ///
+ /// Foreign key mode.
+ /// Default value is .
+ ///
+ public ForeignKeyMode ForeignKeyMode { get; set; } = DomainConfiguration.DefaultForeignKeyMode;
+
+ ///
+ /// Change tracking mode of full-text indexes.
+ /// Default value is .
+ ///
+ public FullTextChangeTrackingMode FullTextChangeTrackingMode { get; set; } = DomainConfiguration.DefaultFullTextChangeTrackingMode;
+
+ ///
+ /// Collation for all columns. See for details.
+ ///
+ public string Collation { get; set; } = string.Empty;
+
+ ///
+ /// Session configurations.
+ ///
+ public SessionOptionsCollection Sessions { get; set; } = new SessionOptionsCollection();
+
+ ///
+ /// Registered mapping rules.
+ ///
+ public MappingRuleOptionsCollection MappingRules { get; set; } = new MappingRuleOptionsCollection();
+
+ ///
+ /// Registered ignore rules.
+ ///
+ public IgnoreRuleOptionsCollection IgnoreRules { get; set; } = new IgnoreRuleOptionsCollection();
+
+ ///
+ /// Registered database aliases.
+ ///
+ public DatabaseOptionsCollection Databases { get; set; } = new DatabaseOptionsCollection();
+
+ ///
+ /// Key generators.
+ ///
+ public KeyGeneratorOptionsCollection KeyGenerators {get; set;} = new KeyGeneratorOptionsCollection();
+
+ ///
+ /// Type of service container
+ ///
+ public string ServiceContainerType { get; set; } = null;
+
+ ///
+ /// Default schema.
+ ///
+ public string DefaultSchema { get; set; } = string.Empty;
+
+ ///
+ /// Default database.
+ ///
+ public string DefaultDatabase { get; set; } = string.Empty;
+
+
+ ///
+ /// Value indicating whether SQL should be included in exception messages.
+ /// Default value is
+ ///
+ public bool IncludeSqlInExceptions { get; set; } = DomainConfiguration.DefaultIncludeSqlInExceptions;
+
+ ///
+ /// Value indicating whether cyclic database dependencied are allowed.
+ /// Default value is
+ ///
+ public bool AllowCyclicDatabaseDependencies { get; set; } = DomainConfiguration.DefaultAllowCyclicDatabaseDependencies;
+
+ ///
+ /// Value indicating whether parallel build should be used where and if it is possible.
+ /// Default value is
+ ///
+ public bool BuildInParallel { get; set; } = DomainConfiguration.DefaultBuildInParallel;
+
+ ///
+ /// Value indicating whether multidatabase keys should be used.
+ /// Default value is
+ ///
+ public bool MultidatabaseKeys { get; set; } = DomainConfiguration.DefaultMultidatabaseKeys;
+
+ ///
+ /// Value indicating whether same storage schema is shared across s.
+ /// Default value is
+ ///
+ public bool ShareStorageSchemaOverNodes { get; set; } = DomainConfiguration.DefaultShareStorageSchemaOverNodes;
+
+ ///
+ /// Enables extra check if connection is not broken on its opening.
+ ///
+ public bool EnsureConnectionIsAlive { get; set; } = DomainConfiguration.DefaultEnsureConnectionIsAlive;
+
+ ///
+ /// Makes queries use parameters instead of constant values for persistent type identifiers.
+ ///
+ public bool PreferTypeIdsAsQueryParameters { get; set; } = DomainConfiguration.DefaultPreferTypeIdsAsQueryParameters;
+
+ ///
+ /// Forced server version.
+ ///
+ public string ForcedServerVersion { get; set; } = string.Empty;
+
+ ///
+ /// Connection initialization SQL script.
+ ///
+ public string ConnectionInitializationSql { get; set; } = string.Empty;
+
+ ///
+ /// Domain options
+ ///
+ public DomainOptions Options { get; set; } = DomainConfiguration.DefaultDomainOptions;
+
+ ///
+ /// Naming convention.
+ ///
+ public NamingConventionOptions NamingConventionRaw { get; internal set; } = null;
+
+ ///
+ /// Versioning convention.
+ ///
+ public VersioningConventionOptions VersioningConvention { get; set; } = null;
+
+ ///
+ /// Defines tags location within query or turn them off if is set.
+ ///
+ public TagsLocation TagsLocation { get; set; } = DomainConfiguration.DefaultTagLocation;
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public DomainConfiguration ToNative(IDictionary connectionStrings)
+ {
+ var config = new DomainConfiguration {
+ Name = Name,
+ ConnectionInfo = ConnectionInfoParser.GetConnectionInfo(connectionStrings,
+ ConnectionUrl, Provider, ConnectionString),
+ KeyCacheSize = KeyCacheSize,
+ KeyGeneratorCacheSize = KeyGeneratorCacheSize,
+ QueryCacheSize = QueryCacheSize,
+ RecordSetMappingCacheSize = RecordSetMappingCacheSize,
+ DefaultSchema = DefaultSchema,
+ DefaultDatabase = DefaultDatabase,
+ UpgradeMode = UpgradeMode,
+ ForeignKeyMode = ForeignKeyMode,
+ SchemaSyncExceptionFormat = SchemaSyncExceptionFormat,
+ Options = Options,
+ ServiceContainerType = !ServiceContainerType.IsNullOrEmpty() ? Type.GetType(ServiceContainerType) : null,
+ IncludeSqlInExceptions = IncludeSqlInExceptions,
+ BuildInParallel = BuildInParallel,
+ AllowCyclicDatabaseDependencies = AllowCyclicDatabaseDependencies,
+ ForcedServerVersion = ForcedServerVersion,
+ Collation = Collation,
+ ConnectionInitializationSql = ConnectionInitializationSql,
+ MultidatabaseKeys = MultidatabaseKeys,
+ ShareStorageSchemaOverNodes = ShareStorageSchemaOverNodes,
+ EnsureConnectionIsAlive = EnsureConnectionIsAlive,
+ PreferTypeIdsAsQueryParameters = PreferTypeIdsAsQueryParameters,
+ FullTextChangeTrackingMode = FullTextChangeTrackingMode,
+ TagsLocation = TagsLocation,
+ };
+
+ if (NamingConventionRaw != null)
+ config.NamingConvention = NamingConventionRaw.ToNative();
+ if (VersioningConvention != null)
+ config.VersioningConvention = VersioningConvention.ToNative();
+
+ foreach (var element in Types) {
+ _ = config.Types.Register(element.ToNative());
+ }
+ HashSet
public const int DefaultCacheSize = 16 * 1024;
-
+
+ ///
+ /// Default session options.
+ ///
+ public const SessionOptions DefaultSessionOptions = SessionOptions.Default;
+
///
/// Default isolation level.
///
public const IsolationLevel DefaultDefaultIsolationLevel = IsolationLevel.RepeatableRead;
+ ///
+ /// Default cache type.
+ ///
+ public const SessionCacheType DefaultCacheType = SessionCacheType.Default;
+
+ ///
+ /// Default reader preloading policy.
+ ///
+ public const ReaderPreloadingPolicy DefaultReaderPreloadingPolicy = ReaderPreloadingPolicy.Default;
+
///
/// Default batch size.
///
@@ -46,13 +61,13 @@ public class SessionConfiguration : ConfigurationBase
///
public static readonly SessionConfiguration Default = new SessionConfiguration(WellKnown.Sessions.Default);
- private SessionOptions options = SessionOptions.Default;
+ private SessionOptions options = DefaultSessionOptions;
private string userName = string.Empty;
private string password = string.Empty;
private int cacheSize = DefaultCacheSize;
private int batchSize = DefaultBatchSize;
private int entityChangeRegistrySize = DefaultEntityChangeRegistrySize;
- private SessionCacheType cacheType = SessionCacheType.Default;
+ private SessionCacheType cacheType = DefaultCacheType;
private IsolationLevel defaultIsolationLevel = DefaultDefaultIsolationLevel; // what a fancy name?
private int? defaultCommandTimeout = null;
private ReaderPreloadingPolicy readerPreloading = ReaderPreloadingPolicy.Default;
@@ -103,6 +118,7 @@ public int CacheSize {
///
/// Gets or sets the type of the session cache.
+ /// Default value is .
///
public SessionCacheType CacheType {
get { return cacheType; }
@@ -145,6 +161,7 @@ public int? DefaultCommandTimeout {
///
/// Gets or sets the size of the batch.
/// This affects create, update, delete operations and future queries.
+ /// Default value is .
///
public int BatchSize {
get { return batchSize; }
@@ -168,6 +185,8 @@ public SessionOptions Options {
///
/// Gets or sets the reader preloading policy.
+ /// It affects query results reading.
+ /// Default value is .
///
public ReaderPreloadingPolicy ReaderPreloading
{
@@ -180,6 +199,7 @@ public ReaderPreloadingPolicy ReaderPreloading
///
/// Gets or sets the size of the entity change registry.
+ /// Default value is .
///
public int EntityChangeRegistrySize
{
diff --git a/Orm/Xtensive.Orm/Orm/Upgrade/UpgradingDomainBuilder.cs b/Orm/Xtensive.Orm/Orm/Upgrade/UpgradingDomainBuilder.cs
index 98e91c2910..a8e72e3e93 100644
--- a/Orm/Xtensive.Orm/Orm/Upgrade/UpgradingDomainBuilder.cs
+++ b/Orm/Xtensive.Orm/Orm/Upgrade/UpgradingDomainBuilder.cs
@@ -50,7 +50,11 @@ public static Domain Build(DomainConfiguration configuration)
configuration.Lock();
}
- LogManager.Default.AutoInitialize();
+ var logConfiguration = configuration.ExtensionConfigurations.Get();
+ if (logConfiguration != null)
+ LogManager.Default.Initialize(logConfiguration);
+ else
+ LogManager.Default.AutoInitialize();
var context = new UpgradeContext(configuration);
@@ -72,7 +76,11 @@ public static async Task BuildAsync(DomainConfiguration configuration, C
configuration.Lock();
}
- LogManager.Default.AutoInitialize();
+ var logConfiguration = configuration.ExtensionConfigurations.Get();
+ if (logConfiguration != null)
+ LogManager.Default.Initialize(logConfiguration);
+ else
+ LogManager.Default.AutoInitialize();
var context = new UpgradeContext(configuration);
diff --git a/Orm/Xtensive.Orm/Orm/WellKnown.cs b/Orm/Xtensive.Orm/Orm/WellKnown.cs
index 1df1e4de33..0e9e7ef5b3 100644
--- a/Orm/Xtensive.Orm/Orm/WellKnown.cs
+++ b/Orm/Xtensive.Orm/Orm/WellKnown.cs
@@ -32,6 +32,11 @@ public static partial class WellKnown
///
public const string DefaultConfigurationSection = "Xtensive.Orm";
+ ///
+ /// Default name of domain.
+ ///
+ public const string DefaultDomainConfigurationName = "Default";
+
///
/// Name of the field.
///
diff --git a/Orm/Xtensive.Orm/Xtensive.Orm.csproj b/Orm/Xtensive.Orm/Xtensive.Orm.csproj
index 316508f708..b6ed0b3f02 100644
--- a/Orm/Xtensive.Orm/Xtensive.Orm.csproj
+++ b/Orm/Xtensive.Orm/Xtensive.Orm.csproj
@@ -1,4 +1,4 @@
-
+
true
$(OutputPath)$(TargetFramework)\$(AssemblyName).xml
@@ -57,6 +57,8 @@
+
+