Skip to content

Commit 46a755d

Browse files
authored
Merge pull request #744 from PHOENIXCONTACT/rework/model-configuration
Reworked model configuration
2 parents 9fd4716 + ed651b8 commit 46a755d

File tree

104 files changed

+892
-1427
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+892
-1427
lines changed

MORYX-Framework.sln

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moryx.Shifts.Management", "
315315
EndProject
316316
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moryx.Shifts.Web", "src\Moryx.Shifts.Web\Moryx.Shifts.Web.csproj", "{22358E04-0AE4-4ADA-9018-56C881E313F1}"
317317
EndProject
318-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moryx.Shifts.Management.Integration.Tests", "src\Tests\Moryx.Shifts.Management.Integration.Tests\Moryx.Shifts.Management.Integration.Tests.csproj", "{5E02B439-B91F-4297-9AE4-7B92F2AA2AFF}"
318+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moryx.Shifts.Management.IntegrationTests", "src\Tests\Moryx.Shifts.Management.IntegrationTests\Moryx.Shifts.Management.IntegrationTests.csproj", "{5E02B439-B91F-4297-9AE4-7B92F2AA2AFF}"
319319
EndProject
320320
Global
321321
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -965,8 +965,8 @@ Global
965965
{5E02B439-B91F-4297-9AE4-7B92F2AA2AFF} = {8517D209-5BC1-47BD-A7C7-9CF9ADD9F5B6}
966966
EndGlobalSection
967967
GlobalSection(ExtensibilityGlobals) = postSolution
968-
SolutionGuid = {36EFC961-F4E7-49DC-A36A-99594FFB8243}
968+
SolutionGuid = {36EFC961-F4E7-49DC-A36A-99594FFB8243}
969969
RESX_TaskErrorCategory = Message
970-
RESX_ShowErrorsInErrorList = True
970+
RESX_ShowErrorsInErrorList = True
971971
EndGlobalSection
972972
EndGlobal

docs/migrations/v8_to_v10.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ The simulator module has also been renamed, and its namespace and package id hav
2222
- ResourceRelationType.CurrentExchangablePart -> ResourceRelationType.CurrentExchangeablePart
2323
- ResourceRelationType.PossibleExchangablePart -> ResourceRelationType.PossibleExchangeablePart
2424

25+
## Data Model Changes
2526

26-
## Removal of ProductFileEntity
27+
With MORYX 10, several changes have been made to the data model to improve performance and maintainability. Notable changes include:
2728

28-
The `ProductFileEntity` was not used in the product-model. There was no api to access it in a simple way. With the implementation of the [`FileSystem`](https://github.com/PHOENIXCONTACT/MORYX-Framework/pull/517) it is also not required anymore.
29+
- Harmonized Naming Conventions: Naming conventions across the data model have been standardized to ensure consistency and clarity.
30+
- Derived data models are now fully supported. This needs some changes how the model is defined in code first scenarios. Please refer to the [Data Model Tutorial](/docs/tutorials/data-model/CodeFirst.md) for more information.
31+
32+
### Removal of ProductFileEntity
33+
34+
The `ProductFileEntity` has been removed from the data model as it was not utilized effectively. This change helps streamline the data structure and reduce unnecessary complexity.

docs/tutorials/data-model/CodeFirst.md

Lines changed: 79 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class SolarSystemContext : MoryxDbContext
3030

3131
public SolarSystemContext(DbContextOptions options) : base(options)
3232
{
33-
// Optional: If Moryx.Model.InMemory is used for testing
33+
// Necessary for DesignTimeDbContextFactory
3434
}
3535

3636
public virtual DbSet<Planet> Planets { get; set; }
@@ -48,6 +48,50 @@ public class SolarSystemContext : MoryxDbContext
4848
}
4949
````
5050

51+
If you want to provide database provider specific implementations you can derive from this context. The database specific attribute provides an argument for the base context. This is important to use the base context in a generic way, without knowledge about the specific database providers. For example for PostgreSQL:
52+
53+
````cs
54+
[NpgsqlDbContext(typeof(SolarSystemContext))]
55+
public class NpgsqlSolarSystemContext : SolarSystemContext
56+
{
57+
public NpgsqlSolarSystemContext()
58+
{
59+
}
60+
61+
public NpgsqlSolarSystemContext(DbContextOptions options) : base(options)
62+
{
63+
// Necessary for DesignTimeDbContextFactory
64+
}
65+
}
66+
````
67+
68+
or for Sqlite:
69+
70+
````cs
71+
[SqliteDbContext(typeof(SolarSystemContext))]
72+
public class SqliteSolarSystemContext : SolarSystemContext
73+
{
74+
public SqliteSolarSystemContext()
75+
{
76+
}
77+
78+
public SqliteSolarSystemContext(DbContextOptions options) : base(options)
79+
{
80+
// Necessary for DesignTimeDbContextFactory
81+
}
82+
}
83+
````
84+
85+
If you just want to support one database provider, you can use the database specific attribute on the base context directly.
86+
87+
````cs
88+
[NpgsqlDbContext]
89+
public class SolarSystemContext : MoryxDbContext
90+
{
91+
...
92+
}
93+
````
94+
5195
## Entities
5296

5397
Then you need to define your entities which will be represented as tables in your database.
@@ -126,11 +170,11 @@ You can decide between three options of tracking:
126170
If an entity consists of one or more navigation properties you might want to have access to them.
127171
There are three ways to access these kind of properties:
128172

129-
| Mode | Explanation |
130-
|---------------------|---|
131-
| `Eager loading` | With eager loading it is possible to configure which relations shall be loaded every time (Have a look on the [Include extension](https://msdn.microsoft.com/en-us/library/system.data.entity.dbextensions.include.aspx)) |
132-
| `Lazy loading` | Lazy loading loads relations when you access them |
133-
| `Explicit loading` | You can load a relation explicitly when lazy loading was turned off |
173+
| Mode | Explanation |
174+
|--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
175+
| `Eager loading` | With eager loading it is possible to configure which relations shall be loaded every time (Have a look on the [Include extension](https://msdn.microsoft.com/en-us/library/system.data.entity.dbextensions.include.aspx)) |
176+
| `Lazy loading` | Lazy loading loads relations when you access them |
177+
| `Explicit loading` | You can load a relation explicitly when lazy loading was turned off |
134178

135179
### Enable automatic lazy loading
136180

@@ -252,63 +296,60 @@ Database migration consists of a migration configuration that is needed for ever
252296

253297
The entity framework comes with two cli commands to do database model migration:
254298

255-
| Script | Explanation |
256-
|---------------------|---|
257-
| `dotnet ef migrations add` | This command is executed every time you want to create a new migration |
258-
| `dotnet ef database update` | This command runs all database migrations found in the corresponding assembly. This step can also be called from code. |
299+
| Script | Explanation |
300+
|-----------------------------|------------------------------------------------------------------------------------------------------------------------|
301+
| `dotnet ef migrations add` | This command is executed every time you want to create a new migration |
302+
| `dotnet ef database update` | This command runs all database migrations found in the corresponding assembly. This step can also be called from code. |
259303

260304
When EF guys are talking about the *CodeFirst Migrations* approach they mean exactly these commands.
261305

262306
### Creation of migration configuration
263307

264308
Before we implement the UnitOfWork you can configure your migration. The ef migration every time needs an executable for the startup project.
265309
It is necessary to have one if you project is just a class library.
266-
We use the default StartProject of an application to create the migration. Keep in mind that the project must be referenced in the start project. The migration tool needs to know the data model connection string for the migration. MORYX cannot configure the migration process therefore we must put the connection string in the default `appsettings.json`. Make this file copy to output directory in build process.
310+
We use the default StartProject of an application to create the migration. Keep in mind that the project must be referenced in the start project. The migration tool needs to know the data model connection string for the migration. MORYX cannot configure the migration process therefore we provide two options by default:
267311

268-
````json
269-
{
270-
"ConnectionStrings": {
271-
"Moryx.TestTools.Test.Model": "Username=postgres;Password=postgres;Host=localhost;Port=5432;Persist Security Info=True;Database=Moryx.TestTools.Test.Model"
272-
}
273-
}
274-
````
312+
- Pass the connection string as parameter to the migration command
313+
- Use `EFCORETOOLSDB` environment variable
275314

276-
The model will be instantiated without any configuration and therfore it is necessary to tell the model where the connectionString can be found. This must be done within the DbContext implementation `OnConfiguring` method.
315+
For both options we need implementations of the `IDesignTimeDbContextFactory<TContext>` interface for every `DbContext` you want to migrate ([Microsoft Docs](https://learn.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=dotnet-core-cli#from-a-design-time-factory)). This factory is used by the migration tool to create an instance of your context during design time. MORYX provides a base implementation for you to make your life easier and avoid code duplication. Each database specific implementation provides a base class for you to derive from. For PostgreSQL it is the `NpgsqlDesignTimeDbContextFactory<TContext>` located in the `Moryx.Model.PostgreSQL` package. For Sqlite it is the `SqliteDesignTimeDbContextFactory<TContext>` located in the `Moryx.Model.Sqlite` package.
316+
317+
Samples for the SolarSystemContext could look as follows. There is no additional code necessary within the classes.
277318

278319
````cs
279-
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
320+
// Npgsql
321+
public class NpgsqlSolarSystemContextDesignTimeFactory : NpgsqlDesignTimeDbContextFactory<NpgsqlSolarSystemContext>
280322
{
281-
base.OnConfiguring(optionsBuilder);
323+
}
282324

283-
if (!optionsBuilder.IsConfigured)
284-
{
285-
var configuration = new ConfigurationBuilder()
286-
.SetBasePath(Directory.GetCurrentDirectory())
287-
.AddJsonFile("appsettings.json")
288-
.Build();
289-
var connectionString = configuration.GetConnectionString("Moryx.TestTools.Test.Model");
290-
optionsBuilder.UseNpgsql(connectionString);
291-
}
325+
// Sqlite
326+
public class SqliteSolarSystemContextDesignTimeFactory : NpgsqlDesignTimeDbContextFactory<SqliteSolarSystemContext>
327+
{
292328
}
293329
````
294330

295-
After you configured the connection string, navigate with your command line to the folder of the data model project. To create a migration call:
331+
The `Moryx.Model.DesignTimeFactory<TContext>` base classes already implement the logic to read the connection string from the command line parameters or the `EFCORETOOLSDB` environment variable.
296332

297333
````ps
298-
dotnet ef migrations add InitialCreate --startup-project ..\StartProject.Core\StartProject.Core.csproj
299-
````
300-
301-
An alternative is that the StartProject is the default visual studio start up project and you use the `Package Manager Console`.
334+
// Using command line parameter
335+
dotnet ef migrations add InitialCreate -s .path/to/StartProject.csproj -c SolarSystemContext -- --connection "YourConnectionString"
302336

303-
This will create a folder named Migrations in the project and a `Configurations.cs` file within.
304-
As mentioned before MORYX has already done some work for you.
337+
// Using environment variable
338+
$env:EFCORETOOLSDB="YourConnectionString"
339+
dotnet ef migrations add InitialCreate -s .path/to/StartProject.csproj -c SolarSystemContext
340+
````
305341

306342
### Update-Database
307343

308344
The last step to do, is to apply the migrations to the database so that your context and its entities are on the same version. Copy the following line to the command line:
309345

310346
````ps
311-
dotnet ef database update InitialCreate --startup-project ..\StartProject.Core\StartProject.Core.csproj
347+
// Using command line parameter
348+
dotnet ef database update InitialCreate -s path/to/StartProject.csproj -c SolarSystemContext -- --connection "YourConnectionString"
349+
350+
// Using environment variable
351+
$env:EFCORETOOLSDB="YourConnectionString"
352+
dotnet ef database update InitialCreate -s path/to/StartProject.csproj -c SolarSystemContext
312353
````
313354

314355
The database should now have been updated. Have a look.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
dotnet ef migrations add InitialCreate -s ../StartProject.Asp/StartProject.Asp.csproj -o ./Migrations/Npgsql/ -c NpgsqlProcessContext -- --connection "Host=localhost;Database=ProcessContext;Username=postgres;Password=postgres"
2+
dotnet ef database update InitialCreate -s ../StartProject.Asp/StartProject.Asp.csproj -c NpgsqlProcessContext -- --connection "Host=localhost;Database=ProcessContext;Username=postgres;Password=postgres"
3+
4+
dotnet ef migrations add InitialCreate -s ../StartProject.Asp/StartProject.Asp.csproj -o ./Migrations/Sqlite/ -c SqliteProcessContext -- --connection "Data Source=ProcessContext.db"
5+
dotnet ef database update InitialCreate -s ../StartProject.Asp/StartProject.Asp.csproj -c SqliteProcessContext -- --connection "Data Source=ProcessContext.db"

src/Moryx.ControlSystem.ProcessEngine/Model/NpgsqlProcessContext.cs

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
// Licensed under the Apache License, Version 2.0
33

44
using Microsoft.EntityFrameworkCore;
5-
using Microsoft.Extensions.Configuration;
6-
using Moryx.Model.PostgreSQL.Attributes;
5+
using Moryx.Model.PostgreSQL;
76

87
namespace Moryx.ControlSystem.ProcessEngine.Model
98
{
109
/// <summary>
1110
/// The Npgsql DbContext of this database model.
1211
/// </summary>
13-
[NpgsqlDatabaseContext]
12+
[NpgsqlDbContext(typeof(ProcessContext))]
1413
public class NpgsqlProcessContext : ProcessContext
1514
{
1615
/// <inheritdoc />
@@ -22,22 +21,6 @@ public NpgsqlProcessContext()
2221
public NpgsqlProcessContext(DbContextOptions options) : base(options)
2322
{
2423
}
25-
26-
/// <inheritdoc />
27-
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
28-
{
29-
base.OnConfiguring(optionsBuilder);
30-
31-
if (!optionsBuilder.IsConfigured)
32-
{
33-
var configuration = new ConfigurationBuilder()
34-
.SetBasePath(Directory.GetCurrentDirectory())
35-
.AddJsonFile("appsettings.json")
36-
.Build();
37-
var connectionString = configuration.GetConnectionString("Moryx.ControlSystem.ProcessEngine.Model");
38-
optionsBuilder.UseNpgsql(connectionString);
39-
}
40-
}
4124
}
4225
}
4326

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) 2025, Phoenix Contact GmbH & Co. KG
2+
// Licensed under the Apache License, Version 2.0
3+
4+
using Moryx.Model.PostgreSQL;
5+
6+
namespace Moryx.ControlSystem.ProcessEngine.Model;
7+
8+
/// <summary>
9+
/// Design time factory for the <see cref="NpgsqlProcessContext"/>
10+
/// </summary>
11+
public class NpgsqlProcessContextDesignTimeFactory : NpgsqlDesignTimeDbContextFactory<NpgsqlProcessContext>
12+
{
13+
14+
}

src/Moryx.ControlSystem.ProcessEngine/Model/SqliteProcessContext.cs

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

44
using Microsoft.EntityFrameworkCore;
5-
using Moryx.Model.Sqlite.Attributes;
5+
using Moryx.Model.Sqlite;
66

77
namespace Moryx.ControlSystem.ProcessEngine.Model
88
{
99
/// <summary>
1010
/// The Sqlite DbContext of this database model.
1111
/// </summary>
12-
[SqliteContext]
12+
[SqliteDbContext(typeof(ProcessContext))]
1313
public class SqliteProcessContext : ProcessContext
1414
{
1515
/// <inheritdoc />
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) 2025, Phoenix Contact GmbH & Co. KG
2+
// Licensed under the Apache License, Version 2.0
3+
4+
using Moryx.Model.Sqlite;
5+
6+
namespace Moryx.ControlSystem.ProcessEngine.Model;
7+
8+
/// <summary>
9+
/// Design time factory for the <see cref="SqliteProcessContext"/>
10+
/// </summary>
11+
public class SqliteProcessContextDesignTimeFactory : SqliteDesignTimeDbContextFactory<SqliteProcessContext>
12+
{
13+
14+
}

src/Moryx.Model.InMemory/InMemoryDbContextManager.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ public IModelConfigurator GetConfigurator(Type contextType)
3939
throw new NotImplementedException();
4040
}
4141

42+
/// <inheritdoc />
43+
public Type[] GetConfigurators(Type contextType)
44+
{
45+
throw new NotImplementedException();
46+
}
47+
4248
/// <inheritdoc />
4349
public IModelSetupExecutor GetSetupExecutor(Type contextType)
4450
{

src/Moryx.Model.PostgreSQL/Attributes/NpgsqlDatabaseContextAttribute.cs

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)