Skip to content

Commit 991e45f

Browse files
authored
Merge pull request #742 from PHOENIXCONTACT/fix/specific-dbcontexts
Fixed usage of specific DbContext implementation
2 parents 26a171a + 1f158df commit 991e45f

File tree

8 files changed

+84
-22
lines changed

8 files changed

+84
-22
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6.3.1
1+
6.5.0
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) 2025, Phoenix Contact GmbH & Co. KG
2+
// Licensed under the Apache License, Version 2.0
3+
4+
using System;
5+
6+
namespace Moryx.Model.Attributes;
7+
8+
/// <summary>
9+
/// Registration attribute for data model configurators
10+
/// </summary>
11+
[AttributeUsage(AttributeTargets.Class)]
12+
public class ModelConfiguratorAttribute : Attribute
13+
{
14+
/// <summary>
15+
/// Configurator used for the context
16+
/// </summary>
17+
public Type ConfiguratorType { get; }
18+
19+
/// <summary>
20+
/// Creates a new instance of <see cref="ModelConfiguratorAttribute"/>
21+
/// </summary>
22+
public ModelConfiguratorAttribute(Type configuratorType)
23+
{
24+
ConfiguratorType = configuratorType;
25+
}
26+
}

src/Moryx.Model/Configuration/ModelConfiguratorBase.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
using System.Linq;
88
using System.Threading.Tasks;
99
using Microsoft.EntityFrameworkCore;
10-
using Microsoft.EntityFrameworkCore.Infrastructure;
1110
using Microsoft.Extensions.Logging;
1211
using Moryx.Configuration;
12+
using Moryx.Model.Attributes;
1313
using Moryx.Tools;
1414

1515
namespace Moryx.Model.Configuration
@@ -46,12 +46,16 @@ public void Initialize(Type contextType, IConfigManager configManager, ILogger l
4646
Logger = logger;
4747

4848
// Load Config
49-
_configName = contextType.FullName + ".DbConfig";
49+
// TODO: Config name should be the name of the base type of the generic DbContext e.g. FooDbContext instead of SqliteFooDbContext. Rework in future version and use base context-type or config wrapper directly.
50+
var modelConfiguratorAttr = contextType.GetCustomAttribute<ModelConfiguratorAttribute>();
51+
var contextBaseType = modelConfiguratorAttr != null ? contextType.BaseType : contextType;
52+
53+
_configName = contextBaseType!.FullName + ".DbConfig";
5054
Config = _configManager.GetConfiguration<TConfig>(_configName);
5155

5256
// If database is empty, fill with TargetModel name
5357
if (string.IsNullOrWhiteSpace(Config.ConnectionSettings.Database))
54-
Config.ConnectionSettings.Database = contextType.Name;
58+
Config.ConnectionSettings.Database = contextBaseType.Name;
5559
}
5660

5761
/// <inheritdoc />

src/Moryx.Model/DbContextManager.cs

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,39 +24,40 @@ public class DbContextManager : IDbContextManager
2424
private ModelWrapper[] _knownModels;
2525
private readonly ILoggerFactory _loggerFactory;
2626
private readonly IConfigManager _configManager;
27+
private static readonly Type[] AllDbContextTypes;
28+
static DbContextManager()
29+
{
30+
AllDbContextTypes = ReflectionTool.GetPublicClasses(typeof(DbContext))
31+
.Where(type => type != typeof(DbContext) && typeof(DbContext).IsAssignableFrom(type)).ToArray();
32+
}
2733

2834
/// <inheritdoc />
2935
public DbContextManager(IConfigManager configManager, ILoggerFactory loggerFactory)
3036
{
3137
_loggerFactory = loggerFactory;
3238
_configManager = configManager;
3339

34-
var dbContextTypes = ReflectionTool.GetPublicClasses(
35-
typeof(DbContext),
36-
type => type != typeof(DbContext) && !type.GetCustomAttributes<DatabaseSpecificContextAttribute>().Any());
37-
38-
_knownModels = dbContextTypes
40+
var baseDbContextTypes = AllDbContextTypes
41+
.Where(type => !type.GetCustomAttributes<DatabaseSpecificContextAttribute>().Any());
42+
43+
_knownModels = baseDbContextTypes
3944
.Select(dbContextType =>
4045
{
4146
var config = configManager.GetConfiguration<DatabaseConfig<DatabaseConnectionSettings>>(ConfigFilename(dbContextType));
42-
4347
var configuratorType = !string.IsNullOrEmpty(config.ConfiguratorTypename)
4448
? Type.GetType(config.ConfiguratorTypename)
4549
: DefaultConfigurator();
4650

47-
return new
51+
// Try to find specific DbContext for the configurator
52+
// If no specific context found, use the base one
53+
var specificDbContext = GetSpecificDbContext(dbContextType, configuratorType);
54+
55+
return new ModelWrapper
4856
{
4957
DbContextType = dbContextType,
50-
ConfiguratorType = configuratorType,
58+
SpecificDbContext = specificDbContext,
59+
Configurator = (IModelConfigurator)Activator.CreateInstance(configuratorType)
5160
};
52-
}).Select(t =>
53-
{
54-
var wrapper = new ModelWrapper
55-
{
56-
DbContextType = t.DbContextType,
57-
Configurator = (IModelConfigurator)Activator.CreateInstance(t.ConfiguratorType)
58-
};
59-
return wrapper;
6061
}).ToArray();
6162

6263
foreach (var wrapper in _knownModels)
@@ -65,6 +66,7 @@ public DbContextManager(IConfigManager configManager, ILoggerFactory loggerFacto
6566
}
6667
}
6768

69+
// TODO: Reference to an assembly which might not be referencesd in certain setups
6870
private Type DefaultConfigurator()
6971
{
7072
var sqliteModelConfigurator = ReflectionTool.GetAssemblies()
@@ -82,15 +84,31 @@ public void UpdateConfig(Type dbContextType, Type configuratorType, IDatabaseCon
8284

8385
var modelWrapper = _knownModels.First(w => w.DbContextType == dbContextType);
8486
modelWrapper.Configurator = (IModelConfigurator)Activator.CreateInstance(configuratorType);
85-
87+
modelWrapper.SpecificDbContext = GetSpecificDbContext(dbContextType, configuratorType);
88+
8689
InitializeConfigurator(modelWrapper);
8790
}
8891

92+
private static Type GetSpecificDbContext(Type dbContextType, Type configuratorType)
93+
{
94+
return AllDbContextTypes.FirstOrDefault(type =>
95+
{
96+
if (!dbContextType.IsAssignableFrom(type))
97+
return false;
98+
99+
var modelConfiguratorAttr = type.GetCustomAttribute<ModelConfiguratorAttribute>();
100+
if (modelConfiguratorAttr == null)
101+
return false;
102+
103+
return modelConfiguratorAttr.ConfiguratorType == configuratorType;
104+
}) ?? dbContextType;
105+
}
106+
89107
private void InitializeConfigurator(ModelWrapper modelWrapper)
90108
{
91109
var configuratorType = modelWrapper.Configurator.GetType();
92110
var logger = _loggerFactory.CreateLogger(configuratorType);
93-
modelWrapper.Configurator.Initialize(modelWrapper.DbContextType, _configManager, logger);
111+
modelWrapper.Configurator.Initialize(modelWrapper.SpecificDbContext, _configManager, logger);
94112
}
95113

96114
private string ConfigFilename(Type dbContextType)
@@ -132,6 +150,8 @@ private class ModelWrapper
132150
public Type DbContextType { get; set; }
133151

134152
public IModelConfigurator Configurator { get; set; }
153+
154+
public Type SpecificDbContext { get; set; }
135155
}
136156
}
137157
}

src/Moryx.Products.Model/NpgsqlProductsContext.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System.IO;
55
using Microsoft.EntityFrameworkCore;
66
using Microsoft.Extensions.Configuration;
7+
using Moryx.Model.Attributes;
8+
using Moryx.Model.PostgreSQL;
79
using Moryx.Model.PostgreSQL.Attributes;
810

911
namespace Moryx.Products.Model
@@ -12,6 +14,7 @@ namespace Moryx.Products.Model
1214
/// Npgsql specific implementation of <see cref="ProductsContext"/>
1315
/// </summary>
1416
[NpgsqlDatabaseContext]
17+
[ModelConfigurator(typeof(NpgsqlModelConfigurator))]
1518
public class NpgsqlProductsContext : ProductsContext
1619
{
1720
/// <inheritdoc />

src/Moryx.Products.Model/SqliteProductsContext.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the Apache License, Version 2.0
33

44
using Microsoft.EntityFrameworkCore;
5+
using Moryx.Model.Attributes;
6+
using Moryx.Model.Sqlite;
57
using Moryx.Model.Sqlite.Attributes;
68

79
namespace Moryx.Products.Model
@@ -10,6 +12,7 @@ namespace Moryx.Products.Model
1012
/// Sqlite specific implementation of <see cref="ProductsContext"/>
1113
/// </summary>
1214
[SqliteContext]
15+
[ModelConfigurator(typeof(SqliteModelConfigurator))]
1316
public class SqliteProductsContext : ProductsContext
1417
{
1518
/// <inheritdoc />

src/Moryx.Resources.Management/Model/NpgsqlResourcesContext.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System.IO;
55
using Microsoft.EntityFrameworkCore;
66
using Microsoft.Extensions.Configuration;
7+
using Moryx.Model.Attributes;
8+
using Moryx.Model.PostgreSQL;
79
using Moryx.Model.PostgreSQL.Attributes;
810

911
// ReSharper disable once CheckNamespace
@@ -13,6 +15,7 @@ namespace Moryx.Resources.Model
1315
/// Npgsql specific implementation of <see cref="ResourcesContext"/>
1416
/// </summary>
1517
[NpgsqlDatabaseContext]
18+
[ModelConfigurator(typeof(NpgsqlModelConfigurator))]
1619
public class NpgsqlResourcesContext : ResourcesContext
1720
{
1821
/// <inheritdoc />

src/Moryx.Resources.Management/Model/SqliteResourcesContext.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the Apache License, Version 2.0
33

44
using Microsoft.EntityFrameworkCore;
5+
using Moryx.Model.Attributes;
6+
using Moryx.Model.Sqlite;
57
using Moryx.Model.Sqlite.Attributes;
68

79
// ReSharper disable once CheckNamespace
@@ -11,6 +13,7 @@ namespace Moryx.Resources.Model
1113
/// Sqlite specific implementation of <see cref="ResourcesContext"/>
1214
/// </summary>
1315
[SqliteContext]
16+
[ModelConfigurator(typeof(SqliteModelConfigurator))]
1417
public class SqliteResourcesContext : ResourcesContext
1518
{
1619
/// <inheritdoc />

0 commit comments

Comments
 (0)