21 Commits

Author SHA1 Message Date
Developer 02
e0737299cf Bump version to 1.1.1 in project file
Updated DigitalData.Core.Exceptions.csproj to set <Version>, <AssemblyVersion>, and <FileVersion> to 1.1.1, replacing the previous 1.1.0 values. No other changes were made.
2026-01-21 22:06:35 +01:00
Developer 02
2db99edcda Add constructor to BadRequestException for inner exceptions
Added a new constructor to BadRequestException that accepts both a message and an inner exception, enabling improved exception chaining and more detailed error reporting.
2026-01-21 22:05:16 +01:00
65186b4f47 chore(Infrastructure): bump to 2.6.1 2026-01-12 10:23:26 +01:00
eb3a5b8991 Apply AsNoTracking to raw/interpolated queries in DbRepository
QueryRaw and QueryInterpolated now always return results with AsNoTracking applied, ensuring entities are not tracked by EF Core. Both methods are now virtual, allowing for easier customization in derived classes. This change improves performance and prevents unintended side effects from entity tracking.
2026-01-12 10:02:56 +01:00
908d85c648 refactor(Core.Abstraction.Application): bump to 1.6.0 2025-12-19 10:23:45 +01:00
d0f055e066 Bump version to 2.6.0 in csproj metadata
Updated <Version>, <AssemblyVersion>, and <FileVersion> fields in DigitalData.Core.Infrastructure.csproj from 2.5.2 to 2.6.0 to reflect the new release.
2025-12-19 10:13:25 +01:00
7f9e6155fe Add repository registration to DI setup
Added RegsRepository.InvokeAll(services) to the dependency injection configuration, ensuring repository types are registered alongside entities and DbSet factories.
2025-12-19 10:11:14 +01:00
1e3cba6fdf Refactor DependencyInjection and RepositoryConfiguration classes
Restructure code for improved readability and maintainability. Move RepositoryConfiguration as a nested public class inside DependencyInjection, group related service registration logic, and clarify method organization. No functional changes.
2025-12-19 10:09:04 +01:00
daa36d767d Refactor repository registration to be context-based
Changed RegisterDefaultRepository to register repositories by DbContext only, removing the entity-specific generic parameter. Added a private InvokeAll<T> helper to process queued service registrations. This streamlines repository registration and improves code maintainability.
2025-12-19 10:03:25 +01:00
3021fd36f6 Implement IRepository in DbRepository<TDbContext>
DbRepository<TDbContext> now implements the IRepository interface, ensuring it conforms to the expected repository contract and interface requirements. This change improves consistency and enforces interface adherence across repository implementations.
2025-12-19 10:01:20 +01:00
2c704c1231 Add raw SQL support to repository interfaces and DI
Extended IRepository interfaces and DbRepository implementation to support raw and interpolated SQL query execution (sync/async) and querying. Updated DependencyInjection to allow registration of default repository implementations. Added Microsoft.EntityFrameworkCore.Abstractions as a dependency. Performed minor refactoring and cleanup.
2025-12-19 09:54:56 +01:00
2bf2bb2276 Refactor namespace and add System.Linq import
Removed conditional compilation around the namespace in DbRepository.cs, making it always use standard braces. Added System.Linq using directive to support LINQ operations.
2025-12-19 09:51:22 +01:00
Developer 02
d2302560f1 Add repository registration to DependencyInjection
Added a `RegsRepository` queue to manage repository registrations.
Introduced `RegisterDefaultRepository<TDbContext, TEntity>()` to
enqueue scoped repository registrations. Updated `RegisterAllServices`
to process the `RegsRepository` queue during service registration.
2025-12-19 09:25:58 +01:00
Developer 02
42c0dc7206 Refactor method names for clarity in IRepository/DbRepository
Renamed methods in `IRepository` and `DbRepository` to replace
`Sql` with `Query` for improved clarity and consistency.

- Updated `IRepository` methods:
  - `ExecuteSqlRawAsync` -> `ExecuteQueryRawAsync`
  - `ExecuteSqlInterpolatedAsync` -> `ExecuteQueryInterpolatedAsync`
  - `ExecuteSqlRaw` -> `ExecuteQueryRaw`
  - `ExecuteSqlInterpolated` -> `ExecuteQueryInterpolated`

- Updated `DbRepository` methods:
  - `ExecuteSqlRawAsync` -> `ExecuteQueryRawAsync`
  - `ExecuteSqlInterpolatedAsync` -> `ExecuteQueryInterpolatedAsync`
  - `ExecuteSqlRaw` -> `ExecuteQueryRaw`
  - `ExecuteSqlInterpolated` -> `ExecuteQueryInterpolated`

- Fixed `DbRepository` class declaration by removing the incorrect
  constraint requiring `TDbContext` to implement `IRepository`.
2025-12-19 09:20:18 +01:00
Developer 02
82686db38b Add QueryRaw and QueryInterpolated methods to repository
Added `QueryRaw` and `QueryInterpolated` methods to the
`IRepository` interface for raw and interpolated SQL queries,
conditionally compiled for the `NET` target framework.

Removed the `Sql` method from `DbRepository` and replaced it
with implementations of `QueryRaw` and `QueryInterpolated`
using `Entities.FromSqlRaw` and `Entities.FromSqlInterpolated`.

Updated the `Query` property in `DbRepository` to use
`Entities.AsNoTracking()` for read-only queries.
2025-12-19 09:05:59 +01:00
Developer 02
ce5c59dfc2 Update DbRepository to require IRepository constraint
The `DbRepository` class now enforces that the generic type
parameter `TDbContext` must implement the `IRepository`
interface in addition to inheriting from `DbContext`. This
ensures that `DbRepository` can leverage functionality
provided by the `IRepository` interface, improving type
safety and consistency.
2025-12-19 08:57:18 +01:00
Developer 02
5c3db6886a Add EF Core support and refactor IRepository interface
Updated project dependencies to include `Microsoft.EntityFrameworkCore.Abstractions` for multiple target frameworks (`net462`, `net7.0`, `net8.0`, `net9.0`). Added `Microsoft.Extensions.*` package references to the project file.

Enhanced `IRepository` interface with methods for executing raw and interpolated SQL queries (`ExecuteSqlRawAsync`, `ExecuteSqlInterpolatedAsync`, etc.). Adjusted method declarations to support conditional compilation for `NET` and `NETFRAMEWORK`.

Refactored namespace structure in `IRepository.cs` to simplify and remove unnecessary conditional compilation directives.
2025-12-18 22:09:45 +01:00
Developer 02
144178a504 Add async SQL execution methods to DbRepository
Added `ExecuteSqlRawAsync` and `ExecuteSqlInterpolatedAsync` methods to the `DbRepository` class for asynchronous SQL execution. Updated the `System.Linq` namespace import to support LINQ operations. Simplified conditional compilation directives by consistently enclosing the class declaration in braces `{}` across frameworks. Adjusted closing braces to align with the updated structure.
2025-12-18 21:54:22 +01:00
6717aa37ab Add generic DbRepository for raw SQL execution
Introduced DbRepository<TDbContext> to provide basic database operations, including methods for executing raw SQL commands via ExecuteSqlRaw and ExecuteSqlInterpolated. The class exposes the underlying DbContext for use in derived classes. Existing DbRepository<TDbContext, TEntity> remains unchanged.
2025-12-18 13:44:45 +01:00
bf418e986b Add Sql method to DbRepository and EFCore.Relational refs
Added a Sql method to DbRepository for executing raw SQL queries
via FromSqlRaw. Included Microsoft.EntityFrameworkCore.Relational
package references for all target frameworks to support this
functionality. Cleaned up unused using directives.
2025-12-18 13:21:35 +01:00
9f2a13df6f bump to 2.5.2 2025-11-06 00:25:15 +01:00
7 changed files with 196 additions and 125 deletions

View File

@@ -12,9 +12,9 @@
<PackageIcon>core_icon.png</PackageIcon>
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<PackageTags>digital data core application clean architecture abstraction</PackageTags>
<Version>1.5.0</Version>
<AssemblyVersion>1.5.0</AssemblyVersion>
<FileVersion>1.5.0</FileVersion>
<Version>1.6.0</Version>
<AssemblyVersion>1.6.0</AssemblyVersion>
<FileVersion>1.6.0</FileVersion>
</PropertyGroup>
<ItemGroup>
@@ -37,7 +37,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.16" />
@@ -52,24 +52,28 @@
<PackageReference Include="AutoMapper" Version="10.1.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="3.1.32" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="7.0.20" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.15" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="9.0.5" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,4 +1,5 @@
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query;
#if NETFRAMEWORK
using System;
using System.Collections.Generic;
@@ -8,11 +9,29 @@ using System.Linq;
#endif
namespace DigitalData.Core.Abstraction.Application.Repository
#if NET
;
#elif NETFRAMEWORK
{
public interface IRepository
{
#if NET
public
#endif
Task<int> ExecuteQueryRawAsync([NotParameterized] string sql, IEnumerable<object> parameters, CancellationToken cancel = default);
#if NET
public
#endif
Task<int> ExecuteQueryInterpolatedAsync(FormattableString sql, CancellationToken cancel = default);
#if NET
public
#endif
int ExecuteQueryRaw([NotParameterized] string sql, params object[] parameters);
#if NET
public
#endif
int ExecuteQueryInterpolated(FormattableString sql);
}
public interface IRepository<TEntity>
{
@@ -44,6 +63,16 @@ namespace DigitalData.Core.Abstraction.Application.Repository
#endif
IQueryable<TEntity> Query { get; }
#if NET
public
#endif
IQueryable<TEntity> QueryRaw([NotParameterized] string sql, params object[] parameters);
#if NET
public
#endif
IQueryable<TEntity> QueryInterpolated([NotParameterized] FormattableString sql);
#if NET
public
#endif
@@ -108,6 +137,4 @@ namespace DigitalData.Core.Abstraction.Application.Repository
IQueryable<TEntity> ReadOnly();
#endregion
}
#if NETFRAMEWORK
}
#endif
}

View File

@@ -19,4 +19,13 @@ public class BadRequestException : Exception
public BadRequestException(string? message) : base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BadRequestException"/> class with a specified error message and inner exception.
/// </summary>
/// <param name="message">The message that describes the error.</param>
/// <param name="innerException">The exception that caused the current exception.</param>
public BadRequestException(string? message, Exception? innerException) : base(message, innerException)
{
}
}

View File

@@ -17,9 +17,9 @@
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<PackAsTool>False</PackAsTool>
<PackageIcon>core_icon.png</PackageIcon>
<Version>1.1.0</Version>
<AssemblyVersion>1.1.0</AssemblyVersion>
<FileVersion>1.1.0</FileVersion>
<Version>1.1.1</Version>
<AssemblyVersion>1.1.1</AssemblyVersion>
<FileVersion>1.1.1</FileVersion>
</PropertyGroup>
<ItemGroup>

View File

@@ -1,11 +1,9 @@
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Abstractions.Interfaces;
using DigitalData.Core.Infrastructure.Factory;
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore.Query;
#if NETFRAMEWORK
using System.Collections.Generic;
using System;
@@ -15,11 +13,36 @@ using System.Threading.Tasks;
#endif
namespace DigitalData.Core.Infrastructure
#if NET
;
#elif NETFRAMEWORK
{
public class DbRepository<TDbContext> : IRepository where TDbContext : DbContext
{
#endif
protected internal readonly TDbContext Context;
public DbRepository(TDbContext context)
{
Context = context;
}
public Task<int> ExecuteQueryRawAsync([NotParameterized] string sql, IEnumerable<object> parameters, CancellationToken cancel = default)
{
return Context.Database.ExecuteSqlRawAsync(sql, parameters, cancel);
}
public Task<int> ExecuteQueryInterpolatedAsync(FormattableString sql, CancellationToken cancel = default)
{
return Context.Database.ExecuteSqlInterpolatedAsync(sql, cancel);
}
public int ExecuteQueryRaw([NotParameterized] string sql, params object[] parameters)
{
return Context.Database.ExecuteSqlRaw(sql, parameters);
}
public int ExecuteQueryInterpolated(FormattableString sql)
{
return Context.Database.ExecuteSqlInterpolated(sql);
}
}
public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbContext : DbContext where TEntity : class
{
@@ -77,6 +100,10 @@ namespace DigitalData.Core.Infrastructure
#region Read
public virtual IQueryable<TEntity> Query => Entities.AsNoTracking();
public virtual IQueryable<TEntity> QueryRaw([NotParameterized] string sql, params object[] parameters) => Entities.FromSqlRaw(sql, parameters).AsNoTracking();
public virtual IQueryable<TEntity> QueryInterpolated([NotParameterized] FormattableString sql) => Entities.FromSqlInterpolated(sql).AsNoTracking();
public virtual IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> expression) => Entities.AsNoTracking().Where(expression);
public virtual IEnumerable<TEntity> GetAll() => Entities.AsNoTracking().ToList();
@@ -132,6 +159,4 @@ namespace DigitalData.Core.Infrastructure
public virtual IQueryable<TEntity> ReadOnly() => Entities.AsNoTracking();
#endregion
}
#if NETFRAMEWORK
}
#endif
}

View File

@@ -11,135 +11,137 @@ using System.Linq;
#endif
namespace DigitalData.Core.Infrastructure
#if NET
;
#elif NETFRAMEWORK
{
#endif
public static class DependencyInjection
{
public static IServiceCollection AddDbRepository(this IServiceCollection services, Action<RepositoryConfiguration> options)
public static class DependencyInjection
{
// register services from configuration
var cfg = new RepositoryConfiguration();
options.Invoke(cfg);
cfg.RegisterAllServices(services);
return services;
}
public class RepositoryConfiguration
{
// 1. register from assembly
private readonly Queue<Action<IServiceCollection>> RegsFromAssembly = new Queue<Action<IServiceCollection>>();
// 2. register entities (can overwrite)
private readonly Queue<Action<IServiceCollection>> RegsEntity = new Queue<Action<IServiceCollection>>();
// 3. register db set factories (can overwrite)
private readonly Queue<Action<IServiceCollection>> RegsDbSetFactory = new Queue<Action<IServiceCollection>>();
internal void RegisterAllServices(IServiceCollection services)
public static IServiceCollection AddDbRepository(this IServiceCollection services, Action<RepositoryConfiguration> options)
{
// 1. register from assembly
RegsFromAssembly.InvokeAll(services);
// register services from configuration
var cfg = new RepositoryConfiguration();
options.Invoke(cfg);
cfg.RegisterAllServices(services);
// 2. register entities (can overwrite)
RegsEntity.InvokeAll(services);
// 3. register db set factories (can overwrite)
RegsDbSetFactory.InvokeAll(services);
return services;
}
internal RepositoryConfiguration() { }
public void RegisterFromAssembly<TDbContext>(Assembly assembly) where TDbContext : DbContext
public class RepositoryConfiguration
{
void reg(IServiceCollection services)
// 1. register from assembly
private readonly Queue<Action<IServiceCollection>> RegsFromAssembly = new Queue<Action<IServiceCollection>>();
// 2. register entities (can overwrite)
private readonly Queue<Action<IServiceCollection>> RegsEntity = new Queue<Action<IServiceCollection>>();
// 3. register db set factories (can overwrite)
private readonly Queue<Action<IServiceCollection>> RegsDbSetFactory = new Queue<Action<IServiceCollection>>();
// 4. register repository
private readonly Queue<Action<IServiceCollection>> RegsRepository = new Queue<Action<IServiceCollection>>();
internal void RegisterAllServices(IServiceCollection services)
{
// scan all types in the Assembly
var entityTypes = assembly.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && t.GetCustomAttribute<TableAttribute>() != null);
// 1. register from assembly
RegsFromAssembly.InvokeAll(services);
foreach (var entityType in entityTypes)
// 2. register entities (can overwrite)
RegsEntity.InvokeAll(services);
// 3. register db set factories (can overwrite)
RegsDbSetFactory.InvokeAll(services);
// 4. register repository
RegsRepository.InvokeAll(services);
}
internal RepositoryConfiguration() { }
public void RegisterFromAssembly<TDbContext>(Assembly assembly) where TDbContext : DbContext
{
void reg(IServiceCollection services)
{
#region Repository
/// register repository
// create generic DbRepository<DbContext, TEntity> type
var repositoryType = typeof(DbRepository<,>).MakeGenericType(typeof(TDbContext), entityType);
var interfaceType = typeof(IRepository<>).MakeGenericType(entityType);
// scan all types in the Assembly
var entityTypes = assembly.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && t.GetCustomAttribute<TableAttribute>() != null);
// add into DI container as Scoped
services.AddScoped(interfaceType, repositoryType);
#endregion Repository
foreach (var entityType in entityTypes)
{
#region Repository
/// register repository
// create generic DbRepository<DbContext, TEntity> type
var repositoryType = typeof(DbRepository<,>).MakeGenericType(typeof(TDbContext), entityType);
var interfaceType = typeof(IRepository<>).MakeGenericType(entityType);
#region DbSetFactory
/// register DbSetFactory
var addDbSetFactoryMethod = typeof(DependencyInjection)
.GetMethod(nameof(AddDbSetFactory),
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
// add into DI container as Scoped
services.AddScoped(interfaceType, repositoryType);
#endregion Repository
var genericMethod = addDbSetFactoryMethod
#region DbSetFactory
/// register DbSetFactory
var addDbSetFactoryMethod = typeof(DependencyInjection)
.GetMethod(nameof(AddDbSetFactory),
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
var genericMethod = addDbSetFactoryMethod
#if NET
!
#endif
.MakeGenericMethod(typeof(TDbContext), entityType);
genericMethod.Invoke(null, new [] { services, null });
#endregion DbSetFactory
.MakeGenericMethod(typeof(TDbContext), entityType);
genericMethod.Invoke(null, new [] { services, null });
#endregion DbSetFactory
}
}
RegsFromAssembly.Enqueue(reg);
}
RegsFromAssembly.Enqueue(reg);
}
public void RegisterEntity<TDbContext, TEntity>(Func<TDbContext, DbSet<TEntity>>
public void RegisterEntity<TDbContext, TEntity>(Func<TDbContext, DbSet<TEntity>>
#if NET
?
#endif
dbSetFactory = null)
where TDbContext : DbContext
where TEntity : class
{
void reg(IServiceCollection services)
=> services
.AddScoped<IRepository<TEntity>, DbRepository<TDbContext, TEntity>>()
.AddDbSetFactory(dbSetFactory);
dbSetFactory = null)
where TDbContext : DbContext
where TEntity : class
{
void reg(IServiceCollection services)
=> services
.AddScoped<IRepository<TEntity>, DbRepository<TDbContext, TEntity>>()
.AddDbSetFactory(dbSetFactory);
RegsEntity.Enqueue(reg);
RegsEntity.Enqueue(reg);
}
public void RegisterDbSetFactory<TDbContext, TEntity>(Func<TDbContext, DbSet<TEntity>> dbSetFactory)
where TDbContext : DbContext
where TEntity : class
=> RegsDbSetFactory.Enqueue(s => s.AddDbSetFactory(dbSetFactory));
public void RegisterDefaultRepository<TDbContext>()
where TDbContext : DbContext
=> RegsRepository.Enqueue(s => s.AddScoped<IRepository, DbRepository<TDbContext>>());
}
public void RegisterDbSetFactory<TDbContext, TEntity>(Func<TDbContext, DbSet<TEntity>> dbSetFactory)
where TDbContext : DbContext
where TEntity : class
=> RegsDbSetFactory.Enqueue(s => s.AddDbSetFactory(dbSetFactory));
}
private static void InvokeAll<T>(this Queue<Action<T>> queue, T services)
{
while (queue.Count > 0)
queue.Dequeue().Invoke(services);
}
private static void InvokeAll<T>(this Queue<Action<T>> queue, T services)
{
while (queue.Count > 0)
queue.Dequeue().Invoke(services);
}
internal static IServiceCollection AddDbSetFactory<TDbContext, TEntity>(this IServiceCollection services, Func<TDbContext, DbSet<TEntity>>
internal static IServiceCollection AddDbSetFactory<TDbContext, TEntity>(this IServiceCollection services, Func<TDbContext, DbSet<TEntity>>
#if NET
?
#endif
create = null)
where TDbContext : DbContext
where TEntity : class
{
create = null)
where TDbContext : DbContext
where TEntity : class
{
#if NET
create ??= ctx => ctx.Set<TEntity>();
create ??= ctx => ctx.Set<TEntity>();
#elif NETFRAMEWORK
if(create is null)
create = ctx => ctx.Set<TEntity>();
if(create is null)
create = ctx => ctx.Set<TEntity>();
#endif
services.AddSingleton(_ => new DbSetFactory<TDbContext, TEntity>(create));
return services;
services.AddSingleton(_ => new DbSetFactory<TDbContext, TEntity>(create));
return services;
}
}
}
#if NETFRAMEWORK
}
#endif
}

View File

@@ -13,9 +13,9 @@
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<RepositoryType>digital data core abstractions clean architecture</RepositoryType>
<PackageTags>digital data core infrastructure clean architecture</PackageTags>
<Version>2.5.0</Version>
<AssemblyVersion>2.5.0</AssemblyVersion>
<FileVersion>2.5.0</FileVersion>
<Version>2.6.1</Version>
<AssemblyVersion>2.6.1</AssemblyVersion>
<FileVersion>2.6.1</FileVersion>
</PropertyGroup>
<ItemGroup>
@@ -41,18 +41,22 @@
<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.32" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.32" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.20" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.20" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.15" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.5" />
</ItemGroup>
<ItemGroup>