Skip to content

Commit ceac4a0

Browse files
committed
Support for connection hanlders on high level
1 parent a0cc7a9 commit ceac4a0

File tree

4 files changed

+134
-13
lines changed

4 files changed

+134
-13
lines changed

Orm/Xtensive.Orm/Orm/Configuration/DomainTypeRegistry.cs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ public class DomainTypeRegistry : TypeRegistry
2525
private readonly static Type iModuleType = typeof(IModule);
2626
private readonly static Type iUpgradeHandlerType = typeof(IUpgradeHandler);
2727
private readonly static Type keyGeneratorType = typeof(KeyGenerator);
28-
private static readonly Type ifulltextCatalogNameBuilder = typeof(IFullTextCatalogNameBuilder);
28+
private readonly static Type ifulltextCatalogNameBuilder = typeof(IFullTextCatalogNameBuilder);
29+
private readonly static Type iConnectionHandlerType = typeof(IConnectionHandler);
30+
31+
private Type[] connectionHandlers;
32+
2933

3034
/// <summary>
3135
/// Gets all the registered persistent types.
@@ -68,6 +72,27 @@ public class DomainTypeRegistry : TypeRegistry
6872
/// </summary>
6973
public IEnumerable<Type> FullTextCatalogResolvers => this.Where(IsFullTextCatalogNameBuilder);
7074

75+
/// <summary>
76+
/// Gets all the registered <see cref="IConnectionHandler"/> implementations.
77+
/// </summary>
78+
public IEnumerable<Type> ConnectionHandlers
79+
{
80+
get {
81+
// a lot of access to this property. better to have items cached;
82+
if (IsLocked) {
83+
if(connectionHandlers == null) {
84+
var container = new List<Type>(10);// not so many handlers expected
85+
foreach (var type in this.Where(IsConnectionHandler))
86+
container.Add(type);
87+
connectionHandlers = container.Count == 0 ? Array.Empty<Type>() : container.ToArray();
88+
}
89+
return connectionHandlers;
90+
}
91+
// if instacne is not locked then there is a chance of new handlers appeared
92+
return this.Where(IsConnectionHandler);
93+
}
94+
}
95+
7196
#region IsXxx method group
7297

7398
/// <summary>
@@ -85,7 +110,8 @@ public static bool IsInterestingType(Type type) =>
85110
IsUpgradeHandler(type) ||
86111
IsKeyGenerator(type) ||
87112
IsCompilerContainer(type) ||
88-
IsFullTextCatalogNameBuilder(type);
113+
IsFullTextCatalogNameBuilder(type) ||
114+
IsConnectionHandler(type);
89115

90116
/// <summary>
91117
/// Determines whether a <paramref name="type"/>
@@ -201,6 +227,21 @@ public static bool IsFullTextCatalogNameBuilder(Type type)
201227
return ifulltextCatalogNameBuilder.IsAssignableFrom(type) && ifulltextCatalogNameBuilder != type;
202228
}
203229

230+
/// <summary>
231+
/// Determines whether the <paramref name="type"/> is
232+
/// a connection handler.
233+
/// </summary>
234+
/// <param name="type">The type to check.</param>
235+
/// <returns>Check result.</returns>
236+
public static bool IsConnectionHandler(Type type)
237+
{
238+
if (type.IsAbstract) {
239+
return false;
240+
}
241+
242+
return iConnectionHandlerType.IsAssignableFrom(type) && iConnectionHandlerType != type;
243+
}
244+
204245
#endregion
205246

206247
#region ICloneable members

Orm/Xtensive.Orm/Orm/Providers/StorageDriver.Operations.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
namespace Xtensive.Orm.Providers
1616
{
17-
partial class StorageDriver
17+
public partial class StorageDriver
1818
{
1919
private sealed class InitializationSqlExtension
2020
{
@@ -50,6 +50,10 @@ public SqlConnection CreateConnection(Session session)
5050
catch (Exception exception) {
5151
throw ExceptionBuilder.BuildException(exception);
5252
}
53+
if (handlerFactoriesCache != null) {
54+
connection.Extensions.Set(
55+
new ConnectionHandlersExtension(CreateHandlersForConnection(configuration.Types.ConnectionHandlers)));
56+
}
5357

5458
var sessionConfiguration = GetConfiguration(session);
5559
connection.CommandTimeout = sessionConfiguration.DefaultCommandTimeout;
@@ -76,7 +80,7 @@ public void OpenConnection(Session session, SqlConnection connection)
7680
var script = extension?.Script;
7781
try {
7882
if (!string.IsNullOrEmpty(script)) {
79-
connection.OpenAndInitialize(extension.Script);
83+
connection.OpenAndInitialize(script);
8084
}
8185
else {
8286
connection.Open();
@@ -100,8 +104,9 @@ public async Task OpenConnectionAsync(Session session, SqlConnection connection,
100104
var extension = connection.Extensions.Get<InitializationSqlExtension>();
101105

102106
try {
103-
if (!string.IsNullOrEmpty(extension?.Script)) {
104-
await connection.OpenAndInitializeAsync(extension.Script, cancellationToken).ConfigureAwait(false);
107+
var script = extension?.Script;
108+
if (!string.IsNullOrEmpty(script)) {
109+
await connection.OpenAndInitializeAsync(script, cancellationToken).ConfigureAwait(false);
105110
}
106111
else {
107112
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);

Orm/Xtensive.Orm/Orm/Providers/StorageDriver.cs

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
using Xtensive.Sql.Info;
1919
using Xtensive.Sql.Model;
2020
using Xtensive.Tuples;
21+
using System.Collections.Concurrent;
22+
using Xtensive.Linq;
23+
using System.Linq.Expressions;
24+
using System.Reflection;
2125

2226
namespace Xtensive.Orm.Providers
2327
{
@@ -26,13 +30,17 @@ namespace Xtensive.Orm.Providers
2630
/// </summary>
2731
public sealed partial class StorageDriver
2832
{
33+
private static readonly MethodInfo FactoryCreatorMethod = typeof(StorageDriver).GetMethod(nameof(NewFactory), BindingFlags.Static | BindingFlags.NonPublic);
34+
2935
private readonly DomainConfiguration configuration;
3036
private readonly SqlDriver underlyingDriver;
3137
private readonly SqlTranslator translator;
3238
private readonly TypeMappingRegistry allMappings;
3339
private readonly bool isLoggingEnabled;
3440
private readonly bool hasSavepoints;
3541

42+
private readonly ConcurrentDictionary<Type, Func<IConnectionHandler>> handlerFactoriesCache;
43+
3644
public ProviderInfo ProviderInfo { get; private set; }
3745

3846
public StorageExceptionBuilder ExceptionBuilder { get; private set; }
@@ -97,7 +105,7 @@ public DbDataReaderAccessor GetDataReaderAccessor(TupleDescriptor descriptor)
97105
public StorageDriver CreateNew(Domain domain)
98106
{
99107
ArgumentValidator.EnsureArgumentNotNull(domain, "domain");
100-
return new StorageDriver(underlyingDriver, ProviderInfo, domain.Configuration, GetModelProvider(domain));
108+
return new StorageDriver(underlyingDriver, ProviderInfo, domain.Configuration, GetModelProvider(domain), handlerFactoriesCache);
101109
}
102110

103111
private static DomainModel GetNullModel()
@@ -151,14 +159,51 @@ private void FixExtractionResultSqlServerFamily(SqlExtractionResult result)
151159
}
152160
}
153161

162+
private IReadOnlyCollection<IConnectionHandler> CreateHandlersForConnection(IEnumerable<Type> connectionHandlerTypes)
163+
{
164+
if (handlerFactoriesCache == null)
165+
return Array.Empty<IConnectionHandler>();
166+
var instances = new List<IConnectionHandler>(handlerFactoriesCache.Count);
167+
foreach (var type in connectionHandlerTypes) {
168+
if (handlerFactoriesCache.TryGetValue(type, out var factory))
169+
instances.Add(factory());
170+
}
171+
return instances.ToArray();
172+
}
173+
174+
// used on driver creation so it is very first time handlers are created.
175+
// great place to create and cache factories for faster initialization on session opening later
176+
private static IReadOnlyCollection<IConnectionHandler> CreateConnectionHandlers(IEnumerable<Type> connectionHandlerTypes,
177+
out ConcurrentDictionary<Type, Func<IConnectionHandler>> factories)
178+
{
179+
var instances = new List<IConnectionHandler>();
180+
factories = new ConcurrentDictionary<Type, Func<IConnectionHandler>>();
181+
foreach (var item in connectionHandlerTypes) {
182+
var handlerFactory = (Func<IConnectionHandler>) FactoryCreatorMethod.MakeGenericMethod(item).Invoke(null, null);
183+
instances.Add(handlerFactory());
184+
factories[item] = handlerFactory;
185+
}
186+
if (factories.Count == 0)
187+
factories = null;
188+
return instances.ToArray();
189+
}
190+
191+
private static Func<IConnectionHandler> NewFactory<T>() where T : IConnectionHandler
192+
{
193+
return FastExpression.Lambda<Func<IConnectionHandler>>(
194+
Expression.Convert(Expression.New(typeof(T)), typeof(IConnectionHandler)))
195+
.Compile();
196+
}
197+
154198
// Constructors
155199

156200
public static StorageDriver Create(SqlDriverFactory driverFactory, DomainConfiguration configuration)
157201
{
158202
ArgumentValidator.EnsureArgumentNotNull(driverFactory, nameof(driverFactory));
159203
ArgumentValidator.EnsureArgumentNotNull(configuration, nameof(configuration));
160204

161-
var driverConfiguration = new SqlDriverConfiguration {
205+
var handlers = CreateConnectionHandlers(configuration.Types.ConnectionHandlers, out var factories);
206+
var driverConfiguration = new SqlDriverConfiguration(handlers) {
162207
ForcedServerVersion = configuration.ForcedServerVersion,
163208
ConnectionInitializationSql = configuration.ConnectionInitializationSql,
164209
EnsureConnectionIsAlive = configuration.EnsureConnectionIsAlive,
@@ -167,7 +212,7 @@ public static StorageDriver Create(SqlDriverFactory driverFactory, DomainConfigu
167212
var driver = driverFactory.GetDriver(configuration.ConnectionInfo, driverConfiguration);
168213
var providerInfo = ProviderInfoBuilder.Build(configuration.ConnectionInfo.Provider, driver);
169214

170-
return new StorageDriver(driver, providerInfo, configuration, GetNullModel);
215+
return new StorageDriver(driver, providerInfo, configuration, GetNullModel, factories);
171216
}
172217

173218
public static async Task<StorageDriver> CreateAsync(
@@ -176,7 +221,8 @@ public static async Task<StorageDriver> CreateAsync(
176221
ArgumentValidator.EnsureArgumentNotNull(driverFactory, nameof(driverFactory));
177222
ArgumentValidator.EnsureArgumentNotNull(configuration, nameof(configuration));
178223

179-
var driverConfiguration = new SqlDriverConfiguration {
224+
var handlers = CreateConnectionHandlers(configuration.Types.ConnectionHandlers, out var factories);
225+
var driverConfiguration = new SqlDriverConfiguration(handlers) {
180226
ForcedServerVersion = configuration.ForcedServerVersion,
181227
ConnectionInitializationSql = configuration.ConnectionInitializationSql,
182228
EnsureConnectionIsAlive = configuration.EnsureConnectionIsAlive,
@@ -186,11 +232,14 @@ public static async Task<StorageDriver> CreateAsync(
186232
.ConfigureAwait(false);
187233
var providerInfo = ProviderInfoBuilder.Build(configuration.ConnectionInfo.Provider, driver);
188234

189-
return new StorageDriver(driver, providerInfo, configuration, GetNullModel);
235+
return new StorageDriver(driver, providerInfo, configuration, GetNullModel, factories);
190236
}
191237

192-
private StorageDriver(
193-
SqlDriver driver, ProviderInfo providerInfo, DomainConfiguration configuration, Func<DomainModel> modelProvider)
238+
private StorageDriver(SqlDriver driver,
239+
ProviderInfo providerInfo,
240+
DomainConfiguration configuration,
241+
Func<DomainModel> modelProvider,
242+
ConcurrentDictionary<Type,Func<IConnectionHandler>> factoryCache)
194243
{
195244
underlyingDriver = driver;
196245
ProviderInfo = providerInfo;
@@ -201,6 +250,7 @@ private StorageDriver(
201250
hasSavepoints = underlyingDriver.ServerInfo.ServerFeatures.Supports(ServerFeatures.Savepoints);
202251
isLoggingEnabled = SqlLog.IsLogged(LogLevel.Info); // Just to cache this value
203252
ServerInfo = underlyingDriver.ServerInfo;
253+
handlerFactoriesCache = factoryCache;
204254
}
205255
}
206256
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (C) 2021 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.Collections.Generic;
6+
using Xtensive.Orm;
7+
8+
namespace Xtensive.Sql
9+
{
10+
/// <summary>
11+
/// Wrapper to pass handlers to connection.
12+
/// </summary>
13+
public sealed class ConnectionHandlersExtension
14+
{
15+
/// <summary>
16+
/// Collection of <see cref="IConnectionHandler"/> instances.
17+
/// </summary>
18+
public IReadOnlyCollection<IConnectionHandler> Handlers { get; }
19+
20+
internal ConnectionHandlersExtension(IReadOnlyCollection<IConnectionHandler> handlers)
21+
{
22+
Handlers = handlers;
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)