Skip to content

Commit 2999139

Browse files
committed
Add XxxOptions for reading configuration
1 parent 3f66942 commit 2999139

16 files changed

Lines changed: 1217 additions & 0 deletions
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright (C) 2024 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
using Xtensive.Core;
7+
using Xtensive.Orm.Model;
8+
9+
namespace Xtensive.Orm.Configuration.Options
10+
{
11+
internal sealed class DatabaseOptions : IIdentifyableOptions,
12+
IValidatableOptions,
13+
IToNativeConvertable<DatabaseConfiguration>,
14+
INamedOptionsCollectionElement
15+
{
16+
public object Identifier => Name;
17+
18+
/// <summary>
19+
/// Logical database name.
20+
/// </summary>
21+
public string Name { get; set; }
22+
23+
/// <summary>
24+
/// Physical database name.
25+
/// </summary>
26+
public string RealName { get; set; }
27+
28+
/// <summary>
29+
/// Type ID minimal value
30+
/// for types mapped to this database.
31+
/// Default value is <see cref="TypeInfo.MinTypeId"/>.
32+
/// </summary>
33+
public int MinTypeId { get; set; } = TypeInfo.MinTypeId;
34+
35+
/// <summary>
36+
/// Type ID maximal value
37+
/// for types mapped to this database.
38+
/// Default value is <see cref="int.MaxValue"/>.
39+
/// </summary>
40+
public int MaxTypeId { get; set; } = int.MaxValue;
41+
42+
/// <inheritdoc/>
43+
/// <exception cref="ArgumentException"><see cref="Name"/> is empty or null.</exception>
44+
/// <exception cref="ArgumentOutOfRangeException"><see cref="MinTypeId"/> or <see cref="MaxTypeId"/> is not in valid range.</exception>
45+
public void Validate()
46+
{
47+
if (Name.IsNullOrEmpty())
48+
throw new ArgumentException(Strings.ExArgumentCannotBeEmptyString,"Name");
49+
if (MinTypeId < TypeInfo.MinTypeId)
50+
throw new ArgumentOutOfRangeException("MinTypeId", MinTypeId,
51+
string.Format(Strings.ExArgumentMustBeGreaterThatOrEqualX, TypeInfo.MinTypeId));
52+
if (MaxTypeId < TypeInfo.MinTypeId)
53+
throw new ArgumentOutOfRangeException("MaxTypeId", MinTypeId,
54+
string.Format(Strings.ExArgumentMustBeGreaterThatOrEqualX, TypeInfo.MinTypeId));
55+
}
56+
57+
/// <inheritdoc />
58+
public DatabaseConfiguration ToNative()
59+
{
60+
Validate();
61+
62+
return new DatabaseConfiguration(Name) {
63+
RealName = RealName,
64+
MinTypeId = MinTypeId,
65+
MaxTypeId = MaxTypeId,
66+
};
67+
}
68+
}
69+
}
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
// Copyright (C) 2024 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using Xtensive.Core;
8+
9+
namespace Xtensive.Orm.Configuration.Options
10+
{
11+
internal sealed class DomainConfigurationOptions
12+
{
13+
/// <summary>
14+
/// Domain configuration name.
15+
/// </summary>
16+
public string Name { get; set; } = string.Empty;
17+
18+
/// <summary>
19+
/// Url that will be used in <see cref="DomainConfiguration.ConnectionInfo"/>.
20+
/// </summary>
21+
public string ConnectionUrl { get; set; } = null;
22+
23+
/// <summary>
24+
/// Connection string that will be used in <see cref="DomainConfiguration.ConnectionInfo"/>.
25+
/// </summary>
26+
public string ConnectionString { get; set; } = null;
27+
28+
/// <summary>
29+
/// Provider that will be used in <see cref="DomainConfiguration.ConnectionInfo"/>.
30+
/// </summary>
31+
public string Provider { get; set; } = WellKnown.Provider.SqlServer;
32+
33+
/// <summary>
34+
/// Types tha are about to be registered in <see cref="Domain"/>.
35+
/// </summary>
36+
public TypeRegistrationOptions[] Types { get; set; } = Array.Empty<TypeRegistrationOptions>();
37+
38+
/// <summary>
39+
/// Size of the key cache. Default value is <see cref="DomainConfiguration.DefaultKeyCacheSize"/>
40+
/// </summary>
41+
public int KeyCacheSize { get; set; } = DomainConfiguration.DefaultKeyCacheSize;
42+
43+
/// <summary>
44+
/// Size of the key generator cache size.
45+
/// Default value is <see cref="DomainConfiguration.DefaultKeyGeneratorCacheSize"/>
46+
/// </summary>
47+
public int KeyGeneratorCacheSize { get; set; } = DomainConfiguration.DefaultKeyGeneratorCacheSize;
48+
49+
/// <summary>
50+
/// Size of the query cache (see <see cref="Query.Execute{TElement}(System.Func{System.Linq.IQueryable{TElement}})"/>).
51+
/// Default value is <see cref="DomainConfiguration.DefaultQueryCacheSize"/>.
52+
/// </summary>
53+
public int QueryCacheSize { get; set; } = DomainConfiguration.DefaultQueryCacheSize;
54+
55+
/// <summary>
56+
/// Size of the record set mapping cache.
57+
/// Default value is <see cref="DomainConfiguration.DefaultRecordSetMappingCacheSize"/>.
58+
/// </summary>
59+
public int RecordSetMappingCacheSize { get; set; } = DomainConfiguration.DefaultRecordSetMappingCacheSize;
60+
61+
/// <summary>
62+
/// Domain upgrade behavior.
63+
/// </summary>
64+
public DomainUpgradeMode UpgradeMode { get; set; } = DomainConfiguration.DefaultUpgradeMode;
65+
66+
/// <summary>
67+
/// <see cref="SchemaSynchronizationException"/> format.
68+
/// </summary>
69+
public SchemaSyncExceptionFormat SchemaSyncExceptionFormat { get; set; } = DomainConfiguration.DefaultSchemaSyncExceptionFormat;
70+
71+
/// <summary>
72+
/// Foreign key mode.
73+
/// Default value is <see cref="Orm.ForeignKeyMode.Default"/>.
74+
/// </summary>
75+
public ForeignKeyMode ForeignKeyMode { get; set; } = DomainConfiguration.DefaultForeignKeyMode;
76+
77+
/// <summary>
78+
/// Change tracking mode of full-text indexes.
79+
/// Default value is <see cref="DomainConfiguration.DefaultFullTextChangeTrackingMode"/>.
80+
/// </summary>
81+
public FullTextChangeTrackingMode FullTextChangeTrackingMode { get; set; } = DomainConfiguration.DefaultFullTextChangeTrackingMode;
82+
83+
/// <summary>
84+
/// Collation for all columns. See <see cref="DomainConfiguration.Collation" /> for details.
85+
/// </summary>
86+
public string Collation { get; set; } = string.Empty;
87+
88+
/// <summary>
89+
/// Session configurations.
90+
/// </summary>
91+
public SessionOptionsCollection Sessions { get; set; } = new SessionOptionsCollection();
92+
93+
/// <summary>
94+
/// Registered mapping rules.
95+
/// </summary>
96+
public MappingRuleOptionsCollection MappingRules { get; set; } = new MappingRuleOptionsCollection();
97+
98+
/// <summary>
99+
/// Registered ignore rules.
100+
/// </summary>
101+
public IgnoreRuleOptionsCollection IgnoreRules { get; set; } = new IgnoreRuleOptionsCollection();
102+
103+
/// <summary>
104+
/// Registered database aliases.
105+
/// </summary>
106+
public DatabaseOptionsCollection Databases { get; set; } = new DatabaseOptionsCollection();
107+
108+
/// <summary>
109+
/// Key generators.
110+
/// </summary>
111+
public KeyGeneratorOptionsCollection KeyGenerators {get; set;} = new KeyGeneratorOptionsCollection();
112+
113+
/// <summary>
114+
/// Type of service container
115+
/// </summary>
116+
public string ServiceContainerType { get; set; } = null;
117+
118+
/// <summary>
119+
/// Default schema.
120+
/// </summary>
121+
public string DefaultSchema { get; set; } = string.Empty;
122+
123+
/// <summary>
124+
/// Default database.
125+
/// </summary>
126+
public string DefaultDatabase { get; set; } = string.Empty;
127+
128+
129+
/// <summary>
130+
/// Value indicating whether SQL should be included in exception messages.
131+
/// Default value is <see cref="DomainConfiguration.DefaultIncludeSqlInExceptions"/>
132+
/// </summary>
133+
public bool IncludeSqlInExceptions { get; set; } = DomainConfiguration.DefaultIncludeSqlInExceptions;
134+
135+
/// <summary>
136+
/// Value indicating whether cyclic database dependencied are allowed.
137+
/// Default value is <see cref="DomainConfiguration.DefaultAllowCyclicDatabaseDependencies"/>
138+
/// </summary>
139+
public bool AllowCyclicDatabaseDependencies { get; set; } = DomainConfiguration.DefaultAllowCyclicDatabaseDependencies;
140+
141+
/// <summary>
142+
/// Value indicating whether parallel build should be used where and if it is possible.
143+
/// Default value is <see cref="DomainConfiguration.DefaultBuildInParallel"/>
144+
/// </summary>
145+
public bool BuildInParallel { get; set; } = DomainConfiguration.DefaultBuildInParallel;
146+
147+
/// <summary>
148+
/// Value indicating whether multidatabase keys should be used.
149+
/// Default value is <see cref="DomainConfiguration.DefaultMultidatabaseKeys"/>
150+
/// </summary>
151+
public bool MultidatabaseKeys { get; set; } = DomainConfiguration.DefaultMultidatabaseKeys;
152+
153+
/// <summary>
154+
/// Value indicating whether same storage schema is shared across <see cref="StorageNode"/>s.
155+
/// Default value is <see cref="DomainConfiguration.DefaultShareStorageSchemaOverNodes"/>
156+
/// </summary>
157+
public bool ShareStorageSchemaOverNodes { get; set; } = DomainConfiguration.DefaultShareStorageSchemaOverNodes;
158+
159+
/// <summary>
160+
/// Enables extra check if connection is not broken on its opening.
161+
/// </summary>
162+
public bool EnsureConnectionIsAlive { get; set; } = DomainConfiguration.DefaultEnsureConnectionIsAlive;
163+
164+
/// <summary>
165+
/// Makes queries use parameters instead of constant values for persistent type identifiers.
166+
/// </summary>
167+
public bool PreferTypeIdsAsQueryParameters { get; set; } = DomainConfiguration.DefaultPreferTypeIdsAsQueryParameters;
168+
169+
/// <summary>
170+
/// Forced server version.
171+
/// </summary>
172+
public string ForcedServerVersion { get; set; } = string.Empty;
173+
174+
/// <summary>
175+
/// Connection initialization SQL script.
176+
/// </summary>
177+
public string ConnectionInitializationSql { get; set; } = string.Empty;
178+
179+
/// <summary>
180+
/// Domain options
181+
/// </summary>
182+
public DomainOptions Options { get; set; } = DomainConfiguration.DefaultDomainOptions;
183+
184+
/// <summary>
185+
/// Naming convention.
186+
/// </summary>
187+
public NamingConventionOptions NamingConventionRaw { get; internal set; } = null;
188+
189+
/// <summary>
190+
/// Versioning convention.
191+
/// </summary>
192+
public VersioningConventionOptions VersioningConvention { get; set; } = null;
193+
194+
/// <summary>
195+
/// Defines tags location within query or turn them off if <see cref="TagsLocation.Nowhere"/> is set.
196+
/// </summary>
197+
public TagsLocation TagsLocation { get; set; } = DomainConfiguration.DefaultTagLocation;
198+
199+
/// <summary>
200+
///
201+
/// </summary>
202+
/// <param name="connectionStrings"></param>
203+
/// <returns></returns>
204+
/// <exception cref="System.AggregateException"></exception>
205+
/// <exception cref="ArgumentException"></exception>
206+
/// <exception cref="Exception"></exception>
207+
public DomainConfiguration ToNative(IDictionary<string, string> connectionStrings)
208+
{
209+
var config = new DomainConfiguration {
210+
Name = Name,
211+
ConnectionInfo = ConnectionInfoParser.GetConnectionInfo(connectionStrings,
212+
ConnectionUrl, Provider, ConnectionString),
213+
KeyCacheSize = KeyCacheSize,
214+
KeyGeneratorCacheSize = KeyGeneratorCacheSize,
215+
QueryCacheSize = QueryCacheSize,
216+
RecordSetMappingCacheSize = RecordSetMappingCacheSize,
217+
DefaultSchema = DefaultSchema,
218+
DefaultDatabase = DefaultDatabase,
219+
UpgradeMode = UpgradeMode,
220+
ForeignKeyMode = ForeignKeyMode,
221+
SchemaSyncExceptionFormat = SchemaSyncExceptionFormat,
222+
Options = Options,
223+
ServiceContainerType = !ServiceContainerType.IsNullOrEmpty() ? Type.GetType(ServiceContainerType) : null,
224+
IncludeSqlInExceptions = IncludeSqlInExceptions,
225+
BuildInParallel = BuildInParallel,
226+
AllowCyclicDatabaseDependencies = AllowCyclicDatabaseDependencies,
227+
ForcedServerVersion = ForcedServerVersion,
228+
Collation = Collation,
229+
ConnectionInitializationSql = ConnectionInitializationSql,
230+
MultidatabaseKeys = MultidatabaseKeys,
231+
ShareStorageSchemaOverNodes = ShareStorageSchemaOverNodes,
232+
EnsureConnectionIsAlive = EnsureConnectionIsAlive,
233+
PreferTypeIdsAsQueryParameters = PreferTypeIdsAsQueryParameters,
234+
FullTextChangeTrackingMode = FullTextChangeTrackingMode,
235+
TagsLocation = TagsLocation,
236+
};
237+
238+
if (NamingConventionRaw != null)
239+
config.NamingConvention = NamingConventionRaw.ToNative();
240+
if (VersioningConvention != null)
241+
config.VersioningConvention = VersioningConvention.ToNative();
242+
243+
foreach (var element in Types) {
244+
_ = config.Types.Register(element.ToNative());
245+
}
246+
HashSet<object> uniqueElements;
247+
if (Databases.AnyExceptionOccur) {
248+
var exceptions = Databases.Exceptions;
249+
if (exceptions.Count == 1)
250+
throw exceptions[0];
251+
throw new System.AggregateException(Strings.ExASetOfExceptionsIsCaught + " during reading 'Databases' section of configuration", exceptions.ToArray());
252+
}
253+
else if (Databases.Count > 0) {
254+
foreach (var element in Databases) {
255+
config.Databases.Add(element.Value.ToNative());
256+
}
257+
}
258+
259+
if (KeyGenerators.AnyExceptionOccur) {
260+
var exceptions = KeyGenerators.Exceptions;
261+
if (exceptions.Count == 1)
262+
throw exceptions[0];
263+
throw new System.AggregateException(Strings.ExASetOfExceptionsIsCaught + " during reading 'KeyGenerators' section of configuration", exceptions.ToArray());
264+
}
265+
else if (KeyGenerators.Count > 0) {
266+
uniqueElements = new HashSet<object>();
267+
foreach (var element in KeyGenerators) {
268+
//resolves cases when alias and database was used which provides different indetifiers but database conflict exists
269+
var identifier = element.GetMappedIdentifier(Databases);
270+
if (!uniqueElements.Add(identifier))
271+
throw new ArgumentException($"Key generator with name '{element.Name}' for database '{element.Database}' has already been declared.");
272+
273+
config.KeyGenerators.Add(element.ToNative());
274+
}
275+
}
276+
if (IgnoreRules.AnyExceptionOccur) {
277+
var exceptions = IgnoreRules.Exceptions;
278+
if (exceptions.Count==1)
279+
throw exceptions[0];
280+
throw new System.AggregateException("Set of exceptions occured during reading 'IgnoreRules' section of configuration", exceptions.ToArray());
281+
}
282+
else if (IgnoreRules.Count > 0) {
283+
uniqueElements = new HashSet<object>();
284+
foreach (var element in IgnoreRules) {
285+
var identifier = element.GetMappedIdentifier(Databases);
286+
if (!uniqueElements.Add(identifier))
287+
throw new Exception("Ignore rule with same set of properties has already been declared.");
288+
config.IgnoreRules.Add(element.ToNative());
289+
}
290+
}
291+
292+
if (MappingRules.AnyExceptionOccur) {
293+
var exceptions = MappingRules.Exceptions;
294+
if (exceptions.Count == 1)
295+
throw exceptions[0];
296+
throw new System.AggregateException("Set of exceptions occured during reading 'MappingRules' section of configuration", exceptions.ToArray());
297+
}
298+
else if (MappingRules.Count > 0) {
299+
foreach (var element in MappingRules) {
300+
config.MappingRules.Add(element.ToNative());
301+
}
302+
}
303+
304+
foreach (var element in Sessions)
305+
config.Sessions.Add(element.Value.ToNative(connectionStrings));
306+
307+
return config;
308+
}
309+
310+
private static T ParseEnum<T>(string value)
311+
where T : struct
312+
{
313+
if (!Enum.TryParse<T>(value, true, out var result)) {
314+
throw new ArgumentException($"Can't parse given value '{value}' to enum type '{typeof(T).FullName}'");
315+
}
316+
return result;
317+
}
318+
}
319+
}

0 commit comments

Comments
 (0)