From f1bb505b97b7b45b5ecb00348d62c155e4c5a34b Mon Sep 17 00:00:00 2001 From: BoBoBaSs84 <73112377+BoBoBaSs84@users.noreply.github.com> Date: Sun, 22 Dec 2024 17:38:39 +0100 Subject: [PATCH 1/8] feat: abstractions added and modified --- .../Components/IAudited.cs | 26 -------------- .../Components/IConcurrency.cs | 4 +-- .../Components/IEnumerator.cs | 2 +- .../Components/IIdentity.cs | 4 +-- .../Components/ISoftDeletable.cs | 4 +-- .../Components/ITimeAudited.cs | 17 ++++++++++ .../Components/IUserAudited.cs | 26 ++++++++++++++ .../IAuditedCompositeModel.cs | 11 +++--- .../IAuditedModel.cs | 25 ++++++++++---- .../ICompositeModel.cs | 2 +- .../IEnumeratorModel.cs | 15 ++++++-- .../IFullAuditedModel.cs | 34 +++++++++++++++++++ .../IIdentityModel.cs | 8 ++--- 13 files changed, 126 insertions(+), 52 deletions(-) delete mode 100644 src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IAudited.cs create mode 100644 src/BB84.EntityFrameworkCore.Models.Abstractions/Components/ITimeAudited.cs create mode 100644 src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IUserAudited.cs create mode 100644 src/BB84.EntityFrameworkCore.Models.Abstractions/IFullAuditedModel.cs diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IAudited.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IAudited.cs deleted file mode 100644 index a923fce..0000000 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IAudited.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace BB84.EntityFrameworkCore.Models.Abstractions.Components; - -/// -/// The audited interface. -/// -/// The type of the created by column. -/// The type of the modified by column. -public interface IAudited -{ - /// - /// The creator of the data row. - /// - TCreated CreatedBy { get; set; } - - /// - /// The last modifier of the data row. - /// - TModified ModifiedBy { get; set; } -} - -/// -/// -/// The auditing key is of type . -/// -public interface IAudited : IAudited -{ } diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IConcurrency.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IConcurrency.cs index 9fc3fab..86834db 100644 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IConcurrency.cs +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IConcurrency.cs @@ -1,12 +1,12 @@ namespace BB84.EntityFrameworkCore.Models.Abstractions.Components; /// -/// The concurrency interface. +/// The interface for concurrency based components. /// public interface IConcurrency { /// - /// The current timestamp or row version. + /// The current timestamp or row version of the data row. /// byte[] Timestamp { get; } } diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IEnumerator.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IEnumerator.cs index 784f344..2033a4b 100644 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IEnumerator.cs +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IEnumerator.cs @@ -1,7 +1,7 @@ namespace BB84.EntityFrameworkCore.Models.Abstractions.Components; /// -/// The enumerator interface. +/// The interface for enumerator based components. /// public interface IEnumerator { diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IIdentity.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IIdentity.cs index 8f26b9b..3748502 100644 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IIdentity.cs +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IIdentity.cs @@ -1,7 +1,7 @@ namespace BB84.EntityFrameworkCore.Models.Abstractions.Components; /// -/// The identity interface. +/// The interface for identity based components. /// /// The type of the primary key. public interface IIdentity where TKey : IEquatable @@ -12,7 +12,7 @@ public interface IIdentity where TKey : IEquatable TKey Id { get; set; } } -/// +/// /// /// The primary key is of type . /// diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/ISoftDeletable.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/ISoftDeletable.cs index a17215f..3795dbc 100644 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/ISoftDeletable.cs +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/ISoftDeletable.cs @@ -1,12 +1,12 @@ namespace BB84.EntityFrameworkCore.Models.Abstractions.Components; /// -/// The soft deleteable interface. +/// The interface for soft deleteable components. /// public interface ISoftDeletable { /// - /// Is the data row in a soft deleted state? + /// Indicates if the data row in a soft deleted state. /// bool IsDeleted { get; set; } } diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/ITimeAudited.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/ITimeAudited.cs new file mode 100644 index 0000000..1b538ed --- /dev/null +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/ITimeAudited.cs @@ -0,0 +1,17 @@ +namespace BB84.EntityFrameworkCore.Models.Abstractions.Components; + +/// +/// The interface for time audited components. +/// +public interface ITimeAudited +{ + /// + /// The initial creation time of the data row. + /// + DateTime Created { get; set; } + + /// + /// The last editing time of the data row. + /// + DateTime? Edited { get; set; } +} diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IUserAudited.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IUserAudited.cs new file mode 100644 index 0000000..84959c1 --- /dev/null +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IUserAudited.cs @@ -0,0 +1,26 @@ +namespace BB84.EntityFrameworkCore.Models.Abstractions.Components; + +/// +/// The interface for user audited components. +/// +/// The type of the creator column. +/// The type of the editor column. +public interface IUserAudited +{ + /// + /// The initial creator of the data row. + /// + TCreator Creator { get; set; } + + /// + /// The last editor of the data row. + /// + TEditor Editor { get; set; } +} + +/// +/// +/// The user auditing columns are of type . +/// +public interface IUserAudited : IUserAudited +{ } diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/IAuditedCompositeModel.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/IAuditedCompositeModel.cs index a2f8f88..ad968bb 100644 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/IAuditedCompositeModel.cs +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/IAuditedCompositeModel.cs @@ -3,13 +3,16 @@ namespace BB84.EntityFrameworkCore.Models.Abstractions; /// -/// The audited composite model interface. +/// The interface for the audited composite models. /// /// -/// -public interface IAuditedCompositeModel : IConcurrency, IAudited +/// +public interface IAuditedCompositeModel : IConcurrency, IUserAudited { } -/// +/// +/// +/// The user auditing columns are of type . +/// public interface IAuditedCompositeModel : IAuditedCompositeModel { } diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/IAuditedModel.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/IAuditedModel.cs index 11dc79f..92f135f 100644 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/IAuditedModel.cs +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/IAuditedModel.cs @@ -3,20 +3,31 @@ namespace BB84.EntityFrameworkCore.Models.Abstractions; /// -/// The audited model base interface. +/// The interface for the audited models. /// /// -/// -public interface IAuditedModel : IIdentityModel, IAudited where TKey : IEquatable +/// +public interface IAuditedModel : IIdentityModel, IUserAudited where TKey : IEquatable { } -/// +/// /// -/// The primary key is of type . +/// The user auditing columns are of type . +/// +public interface IAuditedModel : IAuditedModel, IUserAudited where TKey : IEquatable +{ } + +/// +/// +/// The identity column is of type . /// public interface IAuditedModel : IAuditedModel, IIdentityModel { } -/// -public interface IAuditedModel : IAuditedModel +/// +/// +/// The identity column is of type . +/// The user auditing columns are of type . +/// +public interface IAuditedModel : IAuditedModel, IIdentityModel, IUserAudited { } diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/ICompositeModel.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/ICompositeModel.cs index 3ef4623..2b9b59a 100644 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/ICompositeModel.cs +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/ICompositeModel.cs @@ -3,7 +3,7 @@ namespace BB84.EntityFrameworkCore.Models.Abstractions; /// -/// The composite model interface. +/// The interface for the composite models. /// public interface ICompositeModel : IConcurrency { } diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/IEnumeratorModel.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/IEnumeratorModel.cs index c6fa815..f1e0ae1 100644 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/IEnumeratorModel.cs +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/IEnumeratorModel.cs @@ -3,8 +3,17 @@ namespace BB84.EntityFrameworkCore.Models.Abstractions; /// -/// The enumerator model interface. +/// The interface for the enumerator models. /// -/// -public interface IEnumeratorModel : IIdentityModel, IEnumerator, ISoftDeletable +/// +/// +/// +public interface IEnumeratorModel : IIdentityModel, IEnumerator, ISoftDeletable where TKey : IEquatable +{ } + +/// +/// +/// The identity column is of type . +/// +public interface IEnumeratorModel : IEnumeratorModel { } diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/IFullAuditedModel.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/IFullAuditedModel.cs new file mode 100644 index 0000000..851b895 --- /dev/null +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/IFullAuditedModel.cs @@ -0,0 +1,34 @@ +using BB84.EntityFrameworkCore.Models.Abstractions.Components; + +namespace BB84.EntityFrameworkCore.Models.Abstractions; + +/// +/// The interface for full audited entities. +/// +/// +/// +/// +public interface IFullAuditedModel : IIdentityModel, IUserAudited, ITimeAudited where TKey : IEquatable +{ } + +/// +/// +/// The user auditing columns are of type . +/// +public interface IFullAuditedModel : IFullAuditedModel, IUserAudited where TKey : IEquatable +{ } + +/// +/// +/// The identity column is of type . +/// +public interface IFullAuditedModel : IFullAuditedModel, IIdentityModel +{ } + +/// +/// +/// The identity column is of type . +/// The user auditing columns are of type . +/// +public interface IFullAuditedModel : IFullAuditedModel, IIdentityModel, IUserAudited +{ } diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/IIdentityModel.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/IIdentityModel.cs index 6c80382..f155611 100644 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/IIdentityModel.cs +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/IIdentityModel.cs @@ -3,16 +3,16 @@ namespace BB84.EntityFrameworkCore.Models.Abstractions; /// -/// The model base interface. +/// The interface for the identity models. /// /// -/// +/// public interface IIdentityModel : IIdentity, IConcurrency where TKey : IEquatable { } -/// +/// /// -/// The primary key is of type . +/// The identity column is of type . /// public interface IIdentityModel : IIdentityModel { } From f92215ea27511589f2ca0d070a905e138e99ff3d Mon Sep 17 00:00:00 2001 From: BoBoBaSs84 <73112377+BoBoBaSs84@users.noreply.github.com> Date: Sun, 22 Dec 2024 17:42:40 +0100 Subject: [PATCH 2/8] fix: abstraction fixes applied --- .../IAuditedModel.cs | 12 ++++++------ .../IFullAuditedModel.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/IAuditedModel.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/IAuditedModel.cs index 92f135f..337a468 100644 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/IAuditedModel.cs +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/IAuditedModel.cs @@ -6,25 +6,25 @@ namespace BB84.EntityFrameworkCore.Models.Abstractions; /// The interface for the audited models. /// /// -/// -public interface IAuditedModel : IIdentityModel, IUserAudited where TKey : IEquatable +/// +public interface IAuditedModel : IIdentityModel, IUserAudited where TKey : IEquatable { } -/// +/// /// /// The user auditing columns are of type . /// public interface IAuditedModel : IAuditedModel, IUserAudited where TKey : IEquatable { } -/// +/// /// /// The identity column is of type . /// -public interface IAuditedModel : IAuditedModel, IIdentityModel +public interface IAuditedModel : IAuditedModel, IIdentityModel { } -/// +/// /// /// The identity column is of type . /// The user auditing columns are of type . diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/IFullAuditedModel.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/IFullAuditedModel.cs index 851b895..13d85d2 100644 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/IFullAuditedModel.cs +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/IFullAuditedModel.cs @@ -6,26 +6,26 @@ namespace BB84.EntityFrameworkCore.Models.Abstractions; /// The interface for full audited entities. /// /// -/// +/// /// -public interface IFullAuditedModel : IIdentityModel, IUserAudited, ITimeAudited where TKey : IEquatable +public interface IFullAuditedModel : IIdentityModel, IUserAudited, ITimeAudited where TKey : IEquatable { } -/// +/// /// /// The user auditing columns are of type . /// public interface IFullAuditedModel : IFullAuditedModel, IUserAudited where TKey : IEquatable { } -/// +/// /// /// The identity column is of type . /// -public interface IFullAuditedModel : IFullAuditedModel, IIdentityModel +public interface IFullAuditedModel : IFullAuditedModel, IIdentityModel { } -/// +/// /// /// The identity column is of type . /// The user auditing columns are of type . From 87c172e40c54c109342f82f90c7e481e9cadc608 Mon Sep 17 00:00:00 2001 From: BoBoBaSs84 <73112377+BoBoBaSs84@users.noreply.github.com> Date: Sun, 22 Dec 2024 17:51:03 +0100 Subject: [PATCH 3/8] feat: model base implementations added and modified --- .../AuditedCompositeModel.cs | 13 +++---- .../AuditedModel.cs | 23 +++++++----- .../CompositeModel.cs | 2 +- .../EnumeratorModel.cs | 27 +++++++------- .../FullAuditedModel.cs | 35 +++++++++++++++++++ .../IdentityModel.cs | 5 +-- 6 files changed, 74 insertions(+), 31 deletions(-) create mode 100644 src/BB84.EntityFrameworkCore.Models/FullAuditedModel.cs diff --git a/src/BB84.EntityFrameworkCore.Models/AuditedCompositeModel.cs b/src/BB84.EntityFrameworkCore.Models/AuditedCompositeModel.cs index 9733c69..ab7adf9 100644 --- a/src/BB84.EntityFrameworkCore.Models/AuditedCompositeModel.cs +++ b/src/BB84.EntityFrameworkCore.Models/AuditedCompositeModel.cs @@ -3,21 +3,22 @@ namespace BB84.EntityFrameworkCore.Models; /// -/// The audited composite model class. +/// The base implementation for the audited composite models. /// -/// -public abstract class AuditedCompositeModel : IAuditedCompositeModel +/// +public abstract class AuditedCompositeModel : IAuditedCompositeModel { /// public byte[] Timestamp { get; } = default!; /// - public TCreated CreatedBy { get; set; } = default!; + public TCreator Creator { get; set; } = default!; /// - public TModified ModifiedBy { get; set; } = default!; + public TEdited Editor { get; set; } = default!; } -/// +/// +/// public abstract class AuditedCompositeModel : AuditedCompositeModel, IAuditedCompositeModel { } diff --git a/src/BB84.EntityFrameworkCore.Models/AuditedModel.cs b/src/BB84.EntityFrameworkCore.Models/AuditedModel.cs index e235874..26eb5c5 100644 --- a/src/BB84.EntityFrameworkCore.Models/AuditedModel.cs +++ b/src/BB84.EntityFrameworkCore.Models/AuditedModel.cs @@ -3,22 +3,29 @@ namespace BB84.EntityFrameworkCore.Models; /// -/// The audited model class. +/// The base implementation for the audited models. /// -/// -public abstract class AuditedModel : IdentityModel, IAuditedModel where TKey : IEquatable +/// +public abstract class AuditedModel : IdentityModel, IAuditedModel where TKey : IEquatable { /// - public TCreated CreatedBy { get; set; } = default!; + public TCreator Creator { get; set; } = default!; /// - public TModified ModifiedBy { get; set; } = default!; + public TEdited Editor { get; set; } = default!; } -/// -public abstract class AuditedModel : AuditedModel, IAuditedModel +/// +/// +public abstract class AuditedModel : AuditedModel, IAuditedModel where TKey : IEquatable { } -/// +/// +/// +public abstract class AuditedModel : AuditedModel, IAuditedModel +{ } + +/// +/// public abstract class AuditedModel : AuditedModel, IAuditedModel { } diff --git a/src/BB84.EntityFrameworkCore.Models/CompositeModel.cs b/src/BB84.EntityFrameworkCore.Models/CompositeModel.cs index b4ea8a2..a0f4b37 100644 --- a/src/BB84.EntityFrameworkCore.Models/CompositeModel.cs +++ b/src/BB84.EntityFrameworkCore.Models/CompositeModel.cs @@ -3,7 +3,7 @@ namespace BB84.EntityFrameworkCore.Models; /// -/// The composite model class. +/// The base implementation for the composite models. /// /// public abstract class CompositeModel : ICompositeModel diff --git a/src/BB84.EntityFrameworkCore.Models/EnumeratorModel.cs b/src/BB84.EntityFrameworkCore.Models/EnumeratorModel.cs index 1cd846c..6cbf1f2 100644 --- a/src/BB84.EntityFrameworkCore.Models/EnumeratorModel.cs +++ b/src/BB84.EntityFrameworkCore.Models/EnumeratorModel.cs @@ -1,31 +1,30 @@ -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; - -using BB84.EntityFrameworkCore.Models.Abstractions; +using BB84.EntityFrameworkCore.Models.Abstractions; namespace BB84.EntityFrameworkCore.Models; /// -/// The enumerator model class. +/// The base implementation for the enumerator models. /// -/// -public abstract class EnumeratorModel : IEnumeratorModel +/// +public abstract class EnumeratorModel : IEnumeratorModel where TKey : IEquatable { /// - public int Id { get; set; } = default!; + public TKey Id { get; set; } = default!; /// public byte[] Timestamp { get; } = default!; /// - [MaxLength(50)] - public string Name { get; set; } = default!; + public required string Name { get; set; } /// - [MaxLength(250)] - public string Description { get; set; } = default!; + public required string Description { get; set; } /// - [DefaultValue(false)] - public bool IsDeleted { get; set; } = default!; + public bool IsDeleted { get; set; } } + +/// +/// +public abstract class EnumeratorModel : EnumeratorModel, IEnumeratorModel +{ } diff --git a/src/BB84.EntityFrameworkCore.Models/FullAuditedModel.cs b/src/BB84.EntityFrameworkCore.Models/FullAuditedModel.cs new file mode 100644 index 0000000..ec2fc7f --- /dev/null +++ b/src/BB84.EntityFrameworkCore.Models/FullAuditedModel.cs @@ -0,0 +1,35 @@ +using BB84.EntityFrameworkCore.Models.Abstractions; + +namespace BB84.EntityFrameworkCore.Models; + +/// +/// The base implementation for the full audited models. +/// +/// +public abstract class FullAuditedModel : IdentityModel, IFullAuditedModel where TKey : IEquatable +{ + /// + public TCreator Creator { get; set; } = default!; + /// + public DateTime Created { get; set; } = default!; + /// + public TEdited Editor { get; set; } = default!; + /// + public DateTime? Edited { get; set; } = default!; +} + +/// +/// +public abstract class FullAuditedModel : FullAuditedModel, IFullAuditedModel where TKey : IEquatable +{ } + + +/// +/// +public abstract class FullAuditedModel : FullAuditedModel, IFullAuditedModel +{ } + +/// +/// +public abstract class FullAuditedModel : FullAuditedModel, IFullAuditedModel +{ } diff --git a/src/BB84.EntityFrameworkCore.Models/IdentityModel.cs b/src/BB84.EntityFrameworkCore.Models/IdentityModel.cs index 93e3cd0..3e92f40 100644 --- a/src/BB84.EntityFrameworkCore.Models/IdentityModel.cs +++ b/src/BB84.EntityFrameworkCore.Models/IdentityModel.cs @@ -3,7 +3,7 @@ namespace BB84.EntityFrameworkCore.Models; /// -/// The identity model class. +/// The base implementation for the identity models. /// /// public abstract class IdentityModel : IIdentityModel where TKey : IEquatable @@ -15,6 +15,7 @@ public abstract class IdentityModel : IIdentityModel where TKey : IE public byte[] Timestamp { get; } = default!; } -/// +/// +/// public abstract class IdentityModel : IdentityModel, IIdentityModel { } From e69980095e9ab25718f115127d7e5c2d22d681d4 Mon Sep 17 00:00:00 2001 From: BoBoBaSs84 <73112377+BoBoBaSs84@users.noreply.github.com> Date: Sun, 22 Dec 2024 18:08:45 +0100 Subject: [PATCH 4/8] feat: repository abstractions and implementations modified --- .../IEnumeratorRepository.cs | 19 +++++++++++++------ .../IGenericRepository.cs | 3 ++- .../IIdentityRepository.cs | 18 ++++++++++-------- .../EnumeratorRepository.cs | 15 +++++++++++---- .../GenericRepository.cs | 5 +++-- .../IdentityRepository.cs | 11 +++++++---- 6 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IEnumeratorRepository.cs b/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IEnumeratorRepository.cs index 74f7583..4f4b1d3 100644 --- a/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IEnumeratorRepository.cs +++ b/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IEnumeratorRepository.cs @@ -3,13 +3,12 @@ namespace BB84.EntityFrameworkCore.Repositories.Abstractions; /// -/// The enumerator repository interface. +/// The repository interface for enumerator based entities. /// -/// -/// must implement the interface. -/// -/// -public interface IEnumeratorRepository : IIdentityRepository where TEntity : class, IEnumeratorModel +/// +public interface IEnumeratorRepository : IIdentityRepository + where TEntity : class, IEnumeratorModel + where TKey : IEquatable { /// /// Returns an by its name or . @@ -67,3 +66,11 @@ Task> GetByNamesAsync( CancellationToken cancellationToken = default ); } + +/// +/// +/// The identity column is of type . +/// +public interface IEnumeratorRepository : IEnumeratorRepository + where TEntity : class, IEnumeratorModel +{ } diff --git a/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IGenericRepository.cs b/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IGenericRepository.cs index 8a2b341..f53379b 100644 --- a/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IGenericRepository.cs +++ b/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IGenericRepository.cs @@ -8,7 +8,8 @@ namespace BB84.EntityFrameworkCore.Repositories.Abstractions; /// The generic repository interface. /// /// The entity to work with. -public interface IGenericRepository where TEntity : class +public interface IGenericRepository + where TEntity : class { /// /// Creates a new database row for the instance. diff --git a/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IIdentityRepository.cs b/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IIdentityRepository.cs index 81b616a..bf8ac19 100644 --- a/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IIdentityRepository.cs +++ b/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IIdentityRepository.cs @@ -7,14 +7,12 @@ namespace BB84.EntityFrameworkCore.Repositories.Abstractions; /// -/// The identity repository interface. +/// The repository interface for identity model base entities. /// -/// -/// must implement the interface. -/// /// -/// -public interface IIdentityRepository : IGenericRepository where TEntity : class, IIdentityModel where TKey : IEquatable +public interface IIdentityRepository : IGenericRepository + where TEntity : class, IIdentityModel + where TKey : IEquatable { /// /// Deletes the database row for the instance which matches @@ -191,6 +189,10 @@ Task UpdateAsync( ); } -/// -public interface IIdentityRepository : IIdentityRepository where TEntity : class, IIdentityModel +/// +/// +/// The identity column is of type . +/// +public interface IIdentityRepository : IIdentityRepository + where TEntity : class, IIdentityModel { } diff --git a/src/BB84.EntityFrameworkCore.Repositories/EnumeratorRepository.cs b/src/BB84.EntityFrameworkCore.Repositories/EnumeratorRepository.cs index 363f11b..9844c62 100644 --- a/src/BB84.EntityFrameworkCore.Repositories/EnumeratorRepository.cs +++ b/src/BB84.EntityFrameworkCore.Repositories/EnumeratorRepository.cs @@ -6,11 +6,12 @@ namespace BB84.EntityFrameworkCore.Repositories; /// -/// The enumerator repository class. +/// The repository base implementation for enumerator based entities. /// -/// -public abstract class EnumeratorRepository(IDbContext dbContext) : IdentityRepository(dbContext), - IEnumeratorRepository where TEntity : class, IEnumeratorModel +/// +public abstract class EnumeratorRepository(IDbContext dbContext) : IdentityRepository(dbContext), IEnumeratorRepository + where TEntity : class, IEnumeratorModel + where TKey : IEquatable { /// public TEntity? GetByName(string name, bool ignoreQueryFilters = false, bool trackChanges = false) @@ -60,3 +61,9 @@ public async Task> GetByNamesAsync(IEnumerable name return await query.ToListAsync(cancellationToken); } } + +/// +/// +public abstract class EnumeratorRepository(IDbContext dbContext) : EnumeratorRepository(dbContext), IEnumeratorRepository + where TEntity : class, IEnumeratorModel +{ } diff --git a/src/BB84.EntityFrameworkCore.Repositories/GenericRepository.cs b/src/BB84.EntityFrameworkCore.Repositories/GenericRepository.cs index 18097be..07e7a2d 100644 --- a/src/BB84.EntityFrameworkCore.Repositories/GenericRepository.cs +++ b/src/BB84.EntityFrameworkCore.Repositories/GenericRepository.cs @@ -8,11 +8,12 @@ namespace BB84.EntityFrameworkCore.Repositories; /// -/// The generic repository class. +/// The generic repository implementation. /// /// /// The database context to work with. -public abstract class GenericRepository(IDbContext dbContext) : IGenericRepository where TEntity : class +public abstract class GenericRepository(IDbContext dbContext) : IGenericRepository + where TEntity : class { /// /// The collection of all within the database context. diff --git a/src/BB84.EntityFrameworkCore.Repositories/IdentityRepository.cs b/src/BB84.EntityFrameworkCore.Repositories/IdentityRepository.cs index b0e8271..f5dd9ed 100644 --- a/src/BB84.EntityFrameworkCore.Repositories/IdentityRepository.cs +++ b/src/BB84.EntityFrameworkCore.Repositories/IdentityRepository.cs @@ -9,10 +9,11 @@ namespace BB84.EntityFrameworkCore.Repositories; /// -/// The identity repository class. +/// The repository base implementation for identity based entities. /// /// -public abstract class IdentityRepository(IDbContext dbContext) : GenericRepository(dbContext), IIdentityRepository where TEntity : class, IIdentityModel where TKey : IEquatable +public abstract class IdentityRepository(IDbContext dbContext) : GenericRepository(dbContext), IIdentityRepository + where TEntity : class, IIdentityModel where TKey : IEquatable { /// public int Delete(TKey id) @@ -101,6 +102,8 @@ public async Task UpdateAsync(IEnumerable ids, Expression await UpdateAsync(x => ids.Contains(x.Id), setPropertyCalls, token).ConfigureAwait(false); } -/// -public abstract class IdentityRepository(IDbContext dbContext) : IdentityRepository(dbContext), IIdentityRepository where TEntity : class, IIdentityModel +/// +/// +public abstract class IdentityRepository(IDbContext dbContext) : IdentityRepository(dbContext), IIdentityRepository + where TEntity : class, IIdentityModel { } From 40dd05adebe690dbe84e242e0ad2abeb7ffaeac0 Mon Sep 17 00:00:00 2001 From: BoBoBaSs84 <73112377+BoBoBaSs84@users.noreply.github.com> Date: Sun, 22 Dec 2024 18:37:31 +0100 Subject: [PATCH 5/8] fix: small model fixes applied --- .../Components/IEnumerator.cs | 2 +- src/BB84.EntityFrameworkCore.Models/EnumeratorModel.cs | 2 +- .../IIdentityRepository.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IEnumerator.cs b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IEnumerator.cs index 2033a4b..3c89441 100644 --- a/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IEnumerator.cs +++ b/src/BB84.EntityFrameworkCore.Models.Abstractions/Components/IEnumerator.cs @@ -13,5 +13,5 @@ public interface IEnumerator /// /// The description of the enumerator. /// - string Description { get; set; } + string? Description { get; set; } } diff --git a/src/BB84.EntityFrameworkCore.Models/EnumeratorModel.cs b/src/BB84.EntityFrameworkCore.Models/EnumeratorModel.cs index 6cbf1f2..cce8045 100644 --- a/src/BB84.EntityFrameworkCore.Models/EnumeratorModel.cs +++ b/src/BB84.EntityFrameworkCore.Models/EnumeratorModel.cs @@ -18,7 +18,7 @@ public abstract class EnumeratorModel : IEnumeratorModel where TKey public required string Name { get; set; } /// - public required string Description { get; set; } + public string? Description { get; set; } /// public bool IsDeleted { get; set; } diff --git a/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IIdentityRepository.cs b/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IIdentityRepository.cs index bf8ac19..c5de744 100644 --- a/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IIdentityRepository.cs +++ b/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IIdentityRepository.cs @@ -7,7 +7,7 @@ namespace BB84.EntityFrameworkCore.Repositories.Abstractions; /// -/// The repository interface for identity model base entities. +/// The repository interface for identity model based entities. /// /// public interface IIdentityRepository : IGenericRepository From ac83ab3c3836f269506731d4dd92a0eb671bff3c Mon Sep 17 00:00:00 2001 From: BoBoBaSs84 <73112377+BoBoBaSs84@users.noreply.github.com> Date: Mon, 23 Dec 2024 17:00:48 +0100 Subject: [PATCH 6/8] feat: base configurations and interceptors added and modified --- .../AuditedCompositeConfiguration.cs | 17 ++- .../Configurations/AuditedConfiguration.cs | 51 +++++++-- .../Configurations/CompositeConfiguration.cs | 3 +- .../Configurations/EnumeratorConfiguration.cs | 28 ++++- .../FullAuditedConfiguration.cs | 106 ++++++++++++++++++ .../Configurations/IdentityConfiguration.cs | 12 +- .../Interceptors/SoftDeletableInterceptor.cs | 28 ++--- .../Interceptors/TimeAuditedInterceptor.cs | 49 ++++++++ 8 files changed, 251 insertions(+), 43 deletions(-) create mode 100644 src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/FullAuditedConfiguration.cs create mode 100644 src/BB84.EntityFrameworkCore.Repositories.SqlServer/Interceptors/TimeAuditedInterceptor.cs diff --git a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/AuditedCompositeConfiguration.cs b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/AuditedCompositeConfiguration.cs index 2f810b1..65fec71 100644 --- a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/AuditedCompositeConfiguration.cs +++ b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/AuditedCompositeConfiguration.cs @@ -9,13 +9,12 @@ namespace BB84.EntityFrameworkCore.Repositories.SqlServer.Configurations; /// -/// The audited composite configuration class. +/// The entity configuration for audited composite based entities. /// /// -/// [SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] -public abstract class AuditedCompositeConfiguration : IEntityTypeConfiguration - where TEntity : class, IAuditedCompositeModel +public abstract class AuditedCompositeConfiguration : IEntityTypeConfiguration + where TEntity : class, IAuditedCompositeModel { /// public virtual void Configure(EntityTypeBuilder builder) @@ -25,17 +24,17 @@ public virtual void Configure(EntityTypeBuilder builder) .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate(); - builder.Property(e => e.CreatedBy) + builder.Property(e => e.Creator) .HasColumnOrder(4) .IsRequired(); - builder.Property(e => e.ModifiedBy) + builder.Property(e => e.Editor) .HasColumnOrder(5) .IsRequired(false); } } -/// +/// [SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] public abstract class AuditedCompositeConfiguration : AuditedCompositeConfiguration, IEntityTypeConfiguration where TEntity : class, IAuditedCompositeModel @@ -45,10 +44,10 @@ public override void Configure(EntityTypeBuilder builder) { base.Configure(builder); - builder.Property(e => e.CreatedBy) + builder.Property(e => e.Creator) .IsSysNameColumn(); - builder.Property(e => e.ModifiedBy) + builder.Property(e => e.Editor) .IsSysNameColumn(); } } diff --git a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/AuditedConfiguration.cs b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/AuditedConfiguration.cs index f356e03..1cf3213 100644 --- a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/AuditedConfiguration.cs +++ b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/AuditedConfiguration.cs @@ -9,13 +9,12 @@ namespace BB84.EntityFrameworkCore.Repositories.SqlServer.Configurations; /// -/// The audited base configuration class. +/// The entity configuration for audited based entities. /// /// -/// [SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] -public abstract class AuditedConfiguration : IEntityTypeConfiguration - where TEntity : class, IAuditedModel +public abstract class AuditedConfiguration : IEntityTypeConfiguration + where TEntity : class, IAuditedModel where TKey : IEquatable { /// @@ -33,17 +32,51 @@ public virtual void Configure(EntityTypeBuilder builder) .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate(); - builder.Property(e => e.CreatedBy) + builder.Property(e => e.Creator) .HasColumnOrder(3) .IsRequired(); - builder.Property(e => e.ModifiedBy) + builder.Property(e => e.Editor) .HasColumnOrder(4) .IsRequired(false); } } -/// +/// +[SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] +public abstract class AuditedConfiguration : AuditedConfiguration, IEntityTypeConfiguration + where TEntity : class, IAuditedModel + where TKey : IEquatable +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(e => e.Creator) + .IsSysNameColumn(); + + builder.Property(e => e.Editor) + .IsSysNameColumn(); + } +} + +/// +[SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] +public abstract class AuditedConfiguration : AuditedConfiguration, IEntityTypeConfiguration + where TEntity : class, IAuditedModel +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(p => p.Id) + .HasDefaultValueSql("NEWID()"); + } +} + +/// [SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] public abstract class AuditedConfiguration : AuditedConfiguration, IEntityTypeConfiguration where TEntity : class, IAuditedModel @@ -56,10 +89,10 @@ public override void Configure(EntityTypeBuilder builder) builder.Property(p => p.Id) .HasDefaultValueSql("NEWID()"); - builder.Property(e => e.CreatedBy) + builder.Property(e => e.Creator) .IsSysNameColumn(); - builder.Property(e => e.ModifiedBy) + builder.Property(e => e.Editor) .IsSysNameColumn(); } } diff --git a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/CompositeConfiguration.cs b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/CompositeConfiguration.cs index e86c806..9e5ec89 100644 --- a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/CompositeConfiguration.cs +++ b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/CompositeConfiguration.cs @@ -8,10 +8,9 @@ namespace BB84.EntityFrameworkCore.Repositories.SqlServer.Configurations; /// -/// The composite base configuration class. +/// The entity configuration for composite based entities. /// /// -/// [SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] public abstract class CompositeConfiguration : IEntityTypeConfiguration where TEntity : class, ICompositeModel diff --git a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/EnumeratorConfiguration.cs b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/EnumeratorConfiguration.cs index 6e1c932..92a578d 100644 --- a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/EnumeratorConfiguration.cs +++ b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/EnumeratorConfiguration.cs @@ -8,17 +8,19 @@ namespace BB84.EntityFrameworkCore.Repositories.SqlServer.Configurations; /// -/// The enumerator base configuration class. +/// The entity configuration for enumerator based entities. /// /// [SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] -public abstract class EnumeratorConfiguration : IEntityTypeConfiguration where TEntity : class, IEnumeratorModel +public abstract class EnumeratorConfiguration : IEntityTypeConfiguration + where TEntity : class, IEnumeratorModel + where TKey : IEquatable { /// public virtual void Configure(EntityTypeBuilder builder) { builder.HasKey(x => x.Id) - .IsClustered(); + .IsClustered(false); builder.Property(e => e.Id) .HasColumnOrder(1) @@ -45,7 +47,25 @@ public virtual void Configure(EntityTypeBuilder builder) .HasColumnOrder(5) .HasDefaultValue(false); - builder.HasIndex(e => e.Name) + builder.HasIndex(e => e.Name) .IsUnique(); } } + +/// +/// +/// The identity column is of type . +/// +[SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] +public abstract class EnumeratorConfiguration : EnumeratorConfiguration + where TEntity : class, IEnumeratorModel +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.HasKey(x => x.Id) + .IsClustered(); + } +} diff --git a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/FullAuditedConfiguration.cs b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/FullAuditedConfiguration.cs new file mode 100644 index 0000000..48b795f --- /dev/null +++ b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/FullAuditedConfiguration.cs @@ -0,0 +1,106 @@ +using System.Diagnostics.CodeAnalysis; + +using BB84.EntityFrameworkCore.Models.Abstractions; +using BB84.EntityFrameworkCore.Repositories.SqlServer.Extensions; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace BB84.EntityFrameworkCore.Repositories.SqlServer.Configurations; + +/// +/// The entity configuration for full audited based entities. +/// +/// +[SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] +public abstract class FullAuditedConfiguration : IEntityTypeConfiguration + where TEntity : class, IFullAuditedModel + where TKey : IEquatable +{ + /// + public virtual void Configure(EntityTypeBuilder builder) + { + builder.HasKey(k => k.Id) + .IsClustered(false); + + builder.Property(p => p.Id) + .HasColumnOrder(1) + .ValueGeneratedOnAdd(); + + builder.Property(p => p.Timestamp) + .HasColumnOrder(2) + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate(); + + builder.Property(p => p.Creator) + .HasColumnOrder(3) + .IsRequired(); + + builder.Property(p => p.Created) + .HasColumnOrder(4) + .IsRequired(); + + builder.Property(p => p.Editor) + .HasColumnOrder(5) + .IsRequired(false); + + builder.Property(p => p.Edited) + .HasColumnOrder(6) + .IsRequired(false); + } +} + +/// +[SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] +public abstract class FullAuditeConfiguration : FullAuditedConfiguration, IEntityTypeConfiguration + where TEntity : class, IFullAuditedModel + where TKey : IEquatable +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(p => p.Creator) + .IsSysNameColumn(); + + builder.Property(p => p.Editor) + .IsSysNameColumn(); + } +} + +/// +[SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] +public abstract class FullAuditeConfiguration : FullAuditedConfiguration, IEntityTypeConfiguration + where TEntity : class, IFullAuditedModel +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(p => p.Id) + .HasDefaultValueSql("NEWID()"); + } +} + +/// +[SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] +public abstract class FullAuditeConfiguration : FullAuditedConfiguration, IEntityTypeConfiguration + where TEntity : class, IFullAuditedModel +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(p => p.Id) + .HasDefaultValueSql("NEWID()"); + + builder.Property(p => p.Creator) + .IsSysNameColumn(); + + builder.Property(p => p.Editor) + .IsSysNameColumn(); + } +} diff --git a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/IdentityConfiguration.cs b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/IdentityConfiguration.cs index 21fd82f..495d7c3 100644 --- a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/IdentityConfiguration.cs +++ b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Configurations/IdentityConfiguration.cs @@ -8,10 +8,9 @@ namespace BB84.EntityFrameworkCore.Repositories.SqlServer.Configurations; /// -/// The identity base configuration class. +/// The entity configuration for identity based entities. /// /// -/// [SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] public abstract class IdentityConfiguration : IEntityTypeConfiguration where TEntity : class, IIdentityModel @@ -34,10 +33,13 @@ public virtual void Configure(EntityTypeBuilder builder) } } -/// +/// +/// +/// The identity column is of type . +/// [SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, entity type configuration.")] -public abstract class IdentityConfiguration : IdentityConfiguration, - IEntityTypeConfiguration where TEntity : class, IIdentityModel +public abstract class IdentityConfiguration : IdentityConfiguration, IEntityTypeConfiguration + where TEntity : class, IIdentityModel { /// public override void Configure(EntityTypeBuilder builder) diff --git a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Interceptors/SoftDeletableInterceptor.cs b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Interceptors/SoftDeletableInterceptor.cs index 2d4ba10..4097c27 100644 --- a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Interceptors/SoftDeletableInterceptor.cs +++ b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Interceptors/SoftDeletableInterceptor.cs @@ -7,38 +7,38 @@ namespace BB84.EntityFrameworkCore.Repositories.SqlServer.Interceptors; /// -/// The soft deletable interceptor class. +/// The save changes interceptor for soft deletable entities. /// -/// +/// public sealed class SoftDeletableInterceptor : SaveChangesInterceptor { /// public override InterceptionResult SavingChanges(DbContextEventData eventData, InterceptionResult result) { - UpdateEntities(eventData.Context); + InterceptEntities(eventData.Context); return base.SavingChanges(eventData, result); } /// public override ValueTask> SavingChangesAsync(DbContextEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default) { - UpdateEntities(eventData.Context); + InterceptEntities(eventData.Context); return base.SavingChangesAsync(eventData, result, cancellationToken); } - private static void UpdateEntities(DbContext? context) + private static void InterceptEntities(DbContext? dbContext) { - if (context is null) - return; - - IEnumerable> entites = context.ChangeTracker.Entries(); - - foreach (EntityEntry entry in entites) + if (dbContext is not null) { - if (entry.State is EntityState.Deleted) + IEnumerable> entries = dbContext.ChangeTracker.Entries(); + + foreach (EntityEntry entry in entries) { - entry.Entity.IsDeleted = true; - entry.State = EntityState.Modified; + if (entry.State is EntityState.Deleted) + { + entry.Entity.IsDeleted = true; + entry.State = EntityState.Modified; + } } } } diff --git a/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Interceptors/TimeAuditedInterceptor.cs b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Interceptors/TimeAuditedInterceptor.cs new file mode 100644 index 0000000..c9a412b --- /dev/null +++ b/src/BB84.EntityFrameworkCore.Repositories.SqlServer/Interceptors/TimeAuditedInterceptor.cs @@ -0,0 +1,49 @@ +using BB84.EntityFrameworkCore.Models.Abstractions.Components; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace BB84.EntityFrameworkCore.Repositories.SqlServer.Interceptors; + +/// +/// The save changes interceptor for time audited entities. +/// +/// +public sealed class TimeAuditedInterceptor : SaveChangesInterceptor +{ + /// + public override InterceptionResult SavingChanges(DbContextEventData eventData, InterceptionResult result) + { + InterceptEntities(eventData.Context); + return base.SavingChanges(eventData, result); + } + + /// + public override ValueTask> SavingChangesAsync(DbContextEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default) + { + InterceptEntities(eventData.Context); + return base.SavingChangesAsync(eventData, result, cancellationToken); + } + + private static void InterceptEntities(DbContext? dbContext) + { + if (dbContext is not null) + { + IEnumerable> entries = dbContext.ChangeTracker.Entries(); + + foreach (EntityEntry entry in entries) + { + switch (entry.State) + { + case EntityState.Added: + entry.Entity.Created = DateTime.UtcNow; + continue; + case EntityState.Modified: + entry.Entity.Edited = DateTime.UtcNow; + continue; + } + } + } + } +} From aac964cf456c5e1e81f2e67dd83f2741aaf57a92 Mon Sep 17 00:00:00 2001 From: BoBoBaSs84 <73112377+BoBoBaSs84@users.noreply.github.com> Date: Mon, 23 Dec 2024 17:01:09 +0100 Subject: [PATCH 7/8] feat: unit tests added and modified --- .../AuditedCompositeModelTests.cs | 4 +- .../AuditedModelTests.cs | 6 +-- .../EnumeratorModelTests.cs | 22 +++++++--- .../FullAuditedModelTests.cs | 33 ++++++++++++++ .../Abstractions/ITestDbContext.cs | 8 +--- .../Configurations/SkillConfiguration.cs | 31 +++++++++++++ .../Interceptors/UserAuditedInterceptor.cs | 44 +++++++++++++++++++ .../Persistence/Models/Job.cs | 1 + .../Persistence/Models/Person.cs | 1 + .../Persistence/Models/Skill.cs | 13 ++++++ .../Repositories/SkillRepository.cs | 8 ++++ .../Persistence/TestDbContext.cs | 12 ++--- .../SkillTests.cs | 37 ++++++++++++++++ .../UnitTestBase.cs | 27 ++++++++---- 14 files changed, 212 insertions(+), 35 deletions(-) create mode 100644 tests/BB84.EntityFrameworkCore.ModelsTests/FullAuditedModelTests.cs create mode 100644 tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Configurations/SkillConfiguration.cs create mode 100644 tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Interceptors/UserAuditedInterceptor.cs create mode 100644 tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Models/Skill.cs create mode 100644 tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Repositories/SkillRepository.cs create mode 100644 tests/BB84.EntityFrameworkCore.RepositoriesTests/SkillTests.cs diff --git a/tests/BB84.EntityFrameworkCore.ModelsTests/AuditedCompositeModelTests.cs b/tests/BB84.EntityFrameworkCore.ModelsTests/AuditedCompositeModelTests.cs index 30e4ea9..e9bc039 100644 --- a/tests/BB84.EntityFrameworkCore.ModelsTests/AuditedCompositeModelTests.cs +++ b/tests/BB84.EntityFrameworkCore.ModelsTests/AuditedCompositeModelTests.cs @@ -15,8 +15,8 @@ public void AuditedCompositeModelTest() Assert.IsNotNull(model); Assert.IsNull(model.Timestamp); - Assert.IsNull(model.CreatedBy); - Assert.IsNull(model.ModifiedBy); + Assert.IsNull(model.Creator); + Assert.IsNull(model.Editor); } private sealed class TestClass : AuditedCompositeModel diff --git a/tests/BB84.EntityFrameworkCore.ModelsTests/AuditedModelTests.cs b/tests/BB84.EntityFrameworkCore.ModelsTests/AuditedModelTests.cs index 698f9bf..85543b6 100644 --- a/tests/BB84.EntityFrameworkCore.ModelsTests/AuditedModelTests.cs +++ b/tests/BB84.EntityFrameworkCore.ModelsTests/AuditedModelTests.cs @@ -9,15 +9,15 @@ public sealed class AuditedModelTests [TestMethod] public void AuditedModelTest() { - IAuditedModel model; + IAuditedModel? model; model = new TestClass(); Assert.IsNotNull(model); Assert.AreEqual(Guid.Empty, model.Id); Assert.IsNull(model.Timestamp); - Assert.IsNull(model.CreatedBy); - Assert.IsNull(model.ModifiedBy); + Assert.IsNull(model.Creator); + Assert.IsNull(model.Editor); } private sealed class TestClass : AuditedModel diff --git a/tests/BB84.EntityFrameworkCore.ModelsTests/EnumeratorModelTests.cs b/tests/BB84.EntityFrameworkCore.ModelsTests/EnumeratorModelTests.cs index d037396..68e55e2 100644 --- a/tests/BB84.EntityFrameworkCore.ModelsTests/EnumeratorModelTests.cs +++ b/tests/BB84.EntityFrameworkCore.ModelsTests/EnumeratorModelTests.cs @@ -9,16 +9,26 @@ public sealed class EnumeratorModelTests [TestMethod] public void EnumeratorModelTest() { - IEnumeratorModel model; + IEnumeratorModel? model; + int id = int.MaxValue; + string name = "Name"; + string description = "Description"; + bool isDeleted = true; - model = new TestClass(); + model = new TestClass() + { + Id = id, + Name = name, + Description = description, + IsDeleted = isDeleted + }; Assert.IsNotNull(model); - Assert.AreEqual(0, model.Id); + Assert.AreEqual(id, model.Id); Assert.IsNull(model.Timestamp); - Assert.IsNull(model.Name); - Assert.IsNull(model.Description); - Assert.IsFalse(model.IsDeleted); + Assert.AreEqual(name, model.Name); + Assert.AreEqual(description, model.Description); + Assert.AreEqual(isDeleted, model.IsDeleted); } private sealed class TestClass : EnumeratorModel diff --git a/tests/BB84.EntityFrameworkCore.ModelsTests/FullAuditedModelTests.cs b/tests/BB84.EntityFrameworkCore.ModelsTests/FullAuditedModelTests.cs new file mode 100644 index 0000000..1e2d73e --- /dev/null +++ b/tests/BB84.EntityFrameworkCore.ModelsTests/FullAuditedModelTests.cs @@ -0,0 +1,33 @@ +using BB84.EntityFrameworkCore.Models; +using BB84.EntityFrameworkCore.Models.Abstractions; + +namespace BB84.EntityFrameworkCore.ModelsTests; + +[TestClass] +public sealed class FullAuditedModelTests +{ + [TestMethod] + public void PropertySetCorrectValuesTest() + { + IFullAuditedModel? model; + + model = new TestClass() + { + Id = Guid.NewGuid(), + Creator = "UnitTest", + Created = DateTime.UtcNow, + Editor = "UnitTest", + Edited = DateTime.UtcNow + }; + + Assert.IsNotNull(model); + Assert.AreNotEqual(Guid.Empty, model.Id); + Assert.AreNotEqual("Test", model.Creator); + Assert.AreNotEqual(DateTime.UtcNow, model.Created); + Assert.AreNotEqual("Test", model.Editor); + Assert.AreNotEqual(DateTime.UtcNow, model.Edited); + } + + private sealed class TestClass : FullAuditedModel + { } +} diff --git a/tests/BB84.EntityFrameworkCore.RepositoriesTests/Abstractions/ITestDbContext.cs b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Abstractions/ITestDbContext.cs index d5abc63..11224b7 100644 --- a/tests/BB84.EntityFrameworkCore.RepositoriesTests/Abstractions/ITestDbContext.cs +++ b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Abstractions/ITestDbContext.cs @@ -1,12 +1,6 @@ using BB84.EntityFrameworkCore.Repositories.Abstractions; -using BB84.EntityFrameworkCore.RepositoriesTests.Persistence.Models; - -using Microsoft.EntityFrameworkCore; namespace BB84.EntityFrameworkCore.RepositoriesTests.Abstractions; public interface ITestDbContext : IDbContext -{ - DbSet Persons { get; set; } - DbSet PersonType { get; set; } -} +{ } diff --git a/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Configurations/SkillConfiguration.cs b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Configurations/SkillConfiguration.cs new file mode 100644 index 0000000..83fac72 --- /dev/null +++ b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Configurations/SkillConfiguration.cs @@ -0,0 +1,31 @@ +using BB84.EntityFrameworkCore.Repositories.SqlServer.Configurations; +using BB84.EntityFrameworkCore.RepositoriesTests.Persistence.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace BB84.EntityFrameworkCore.RepositoriesTests.Persistence.Configurations; + +internal sealed class SkillConfiguration : FullAuditeConfiguration +{ + public override void Configure(EntityTypeBuilder builder) + { + builder.Property(p => p.Name) + .HasMaxLength(128) + .IsRequired() + .IsUnicode(false); + + builder.HasIndex(p => p.Name) + .IsUnique(); + + builder.Property(p => p.Description) + .HasMaxLength(512) + .IsRequired() + .IsUnicode(); + + builder.Property(p => p.IsCritical) + .HasDefaultValue(false); + + base.Configure(builder); + } +} diff --git a/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Interceptors/UserAuditedInterceptor.cs b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Interceptors/UserAuditedInterceptor.cs new file mode 100644 index 0000000..bafce56 --- /dev/null +++ b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Interceptors/UserAuditedInterceptor.cs @@ -0,0 +1,44 @@ +using BB84.EntityFrameworkCore.Models.Abstractions.Components; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace BB84.EntityFrameworkCore.RepositoriesTests.Persistence.Interceptors; + +public sealed class UserAuditedInterceptor : SaveChangesInterceptor +{ + public override InterceptionResult SavingChanges(DbContextEventData eventData, InterceptionResult result) + { + InterceptEntities(eventData.Context); + return base.SavingChanges(eventData, result); + } + + public override ValueTask> SavingChangesAsync(DbContextEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default) + { + InterceptEntities(eventData.Context); + return base.SavingChangesAsync(eventData, result, cancellationToken); + } + + private static void InterceptEntities(DbContext? dbContext) + { + if (dbContext is not null) + { + IEnumerable> entries = dbContext.ChangeTracker.Entries(); + string userName = $"{Environment.MachineName}\\{Environment.UserName}"; + + foreach (EntityEntry entry in entries) + { + switch (entry.State) + { + case EntityState.Added: + entry.Entity.Creator = userName; + continue; + case EntityState.Modified: + entry.Entity.Editor = userName; + continue; + } + } + } + } +} diff --git a/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Models/Job.cs b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Models/Job.cs index c4c93ed..1ddaef6 100644 --- a/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Models/Job.cs +++ b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Models/Job.cs @@ -8,4 +8,5 @@ public sealed class Job : AuditedModel public required string Description { get; set; } public ICollection? PersonJobs { get; set; } + public ICollection? Requirements { get; set; } } diff --git a/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Models/Person.cs b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Models/Person.cs index 8e2052a..2cd33f0 100644 --- a/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Models/Person.cs +++ b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Models/Person.cs @@ -13,4 +13,5 @@ public sealed class Person : AuditedModel public PersonType Type { get; set; } = default!; public ICollection? PersonJobs { get; set; } + public ICollection? Skills { get; set; } } diff --git a/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Models/Skill.cs b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Models/Skill.cs new file mode 100644 index 0000000..f37f3d3 --- /dev/null +++ b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Models/Skill.cs @@ -0,0 +1,13 @@ +using BB84.EntityFrameworkCore.Models; + +namespace BB84.EntityFrameworkCore.RepositoriesTests.Persistence.Models; + +public sealed class Skill : FullAuditedModel +{ + public required string Name { get; set; } + public required string Description { get; set; } + public bool IsCritical { get; set; } + + public ICollection? Persons { get; set; } + public ICollection? Jobs { get; set; } +} diff --git a/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Repositories/SkillRepository.cs b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Repositories/SkillRepository.cs new file mode 100644 index 0000000..98e9284 --- /dev/null +++ b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/Repositories/SkillRepository.cs @@ -0,0 +1,8 @@ +using BB84.EntityFrameworkCore.Repositories; +using BB84.EntityFrameworkCore.Repositories.Abstractions; +using BB84.EntityFrameworkCore.RepositoriesTests.Persistence.Models; + +namespace BB84.EntityFrameworkCore.RepositoriesTests.Persistence.Repositories; + +internal sealed class SkillRepository(IDbContext dbContext) : IdentityRepository(dbContext) +{ } diff --git a/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/TestDbContext.cs b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/TestDbContext.cs index 785078d..21d6391 100644 --- a/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/TestDbContext.cs +++ b/tests/BB84.EntityFrameworkCore.RepositoriesTests/Persistence/TestDbContext.cs @@ -1,28 +1,24 @@ using BB84.EntityFrameworkCore.Repositories.SqlServer.Interceptors; using BB84.EntityFrameworkCore.RepositoriesTests.Abstractions; -using BB84.EntityFrameworkCore.RepositoriesTests.Persistence.Models; +using BB84.EntityFrameworkCore.RepositoriesTests.Persistence.Interceptors; using Microsoft.EntityFrameworkCore; namespace BB84.EntityFrameworkCore.RepositoriesTests.Persistence; [SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, unit testing.")] -public sealed class TestDbContext(DbContextOptions options, SoftDeletableInterceptor softDeletableInterceptor) : DbContext(options), ITestDbContext +public sealed class TestDbContext(DbContextOptions options, SoftDeletableInterceptor softDeletableInterceptor, TimeAuditedInterceptor timeAuditedInterceptor, UserAuditedInterceptor userAuditedInterceptor) : DbContext(options), ITestDbContext { - private readonly SoftDeletableInterceptor _softDeletableInterceptor = softDeletableInterceptor; - - public DbSet Persons { get; set; } - public DbSet PersonType { get; set; } - protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); + modelBuilder.HasDefaultSchema("testing"); modelBuilder.ApplyConfigurationsFromAssembly(typeof(UnitTestBase).Assembly); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); - optionsBuilder.AddInterceptors(_softDeletableInterceptor); + optionsBuilder.AddInterceptors(softDeletableInterceptor, timeAuditedInterceptor, userAuditedInterceptor); } } diff --git a/tests/BB84.EntityFrameworkCore.RepositoriesTests/SkillTests.cs b/tests/BB84.EntityFrameworkCore.RepositoriesTests/SkillTests.cs new file mode 100644 index 0000000..e9df384 --- /dev/null +++ b/tests/BB84.EntityFrameworkCore.RepositoriesTests/SkillTests.cs @@ -0,0 +1,37 @@ +using BB84.EntityFrameworkCore.RepositoriesTests.Persistence.Models; +using BB84.EntityFrameworkCore.RepositoriesTests.Persistence.Repositories; + +namespace BB84.EntityFrameworkCore.RepositoriesTests; + +[TestClass] +public sealed class SkillTests : UnitTestBase +{ + [TestMethod] + public void SkillCreateTest() + { + using Persistence.TestDbContext context = GetTestContext(); + SkillRepository repository = new(context); + Skill newSkill = new() + { + Name = "FancySkill", + Description = "This is a fancy skill", + IsCritical = true + }; + + repository.Create(newSkill); + int result = context.SaveChanges(); + Assert.AreEqual(1, result); + + Skill? dbSkill = repository.GetByCondition(x => x.Name == newSkill.Name, trackChanges: true); + Assert.IsNotNull(dbSkill); + Assert.AreNotEqual(DateTime.MinValue, dbSkill.Created); + + dbSkill.IsCritical = false; + result = context.SaveChanges(); + Assert.AreEqual(1, result); + + dbSkill = repository.GetByCondition(x => x.Name == newSkill.Name, trackChanges: true); + Assert.IsNotNull(dbSkill); + Assert.AreNotEqual(DateTime.MinValue, dbSkill.Edited); + } +} diff --git a/tests/BB84.EntityFrameworkCore.RepositoriesTests/UnitTestBase.cs b/tests/BB84.EntityFrameworkCore.RepositoriesTests/UnitTestBase.cs index 2e71ed8..df1708b 100644 --- a/tests/BB84.EntityFrameworkCore.RepositoriesTests/UnitTestBase.cs +++ b/tests/BB84.EntityFrameworkCore.RepositoriesTests/UnitTestBase.cs @@ -1,5 +1,6 @@ using BB84.EntityFrameworkCore.Repositories.SqlServer.Interceptors; using BB84.EntityFrameworkCore.RepositoriesTests.Persistence; +using BB84.EntityFrameworkCore.RepositoriesTests.Persistence.Interceptors; using Microsoft.EntityFrameworkCore; @@ -9,31 +10,39 @@ namespace BB84.EntityFrameworkCore.RepositoriesTests; [SuppressMessage("Style", "IDE0058", Justification = "Not relevant here, unit testing.")] public abstract class UnitTestBase { - private static readonly SoftDeletableInterceptor Interceptor = new(); + private static readonly SoftDeletableInterceptor SoftDeletableInterceptor = new(); + private static readonly TimeAuditedInterceptor TimeAuditedInterceptor = new(); + private static readonly UserAuditedInterceptor UserAuditedInterceptor = new(); [AssemblyInitialize] public static void AssemblyInitialize(TestContext context) { - using TestDbContext dbContext = new(GetContextOptions(), Interceptor); - + using TestDbContext dbContext = GetTestContext(); dbContext.Database.EnsureCreated(); } [AssemblyCleanup] public static void AssemblyCleanup() { - using TestDbContext dbContext = new(GetContextOptions(), Interceptor); - + using TestDbContext dbContext = GetTestContext(); dbContext.Database.EnsureDeleted(); } - public static DbContextOptions GetContextOptions() + [TestMethod] + public void GenerateCreateScriptTest() + { + using TestDbContext dbContext = GetTestContext(); + string sqlScript = dbContext.Database.GenerateCreateScript(); + File.WriteAllText("CreateScript.sql", sqlScript); + } + + public static TestDbContext GetTestContext() + => new(GetContextOptions(), SoftDeletableInterceptor, TimeAuditedInterceptor, UserAuditedInterceptor); + + private static DbContextOptions GetContextOptions() { return new DbContextOptionsBuilder() .UseSqlServer("Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=TestDb;Integrated Security=True") .Options; } - - public static TestDbContext GetTestContext() - => new(GetContextOptions(), Interceptor); } From d198b3977e473e1c9b0e59619efa92bcf298e669 Mon Sep 17 00:00:00 2001 From: BoBoBaSs84 <73112377+BoBoBaSs84@users.noreply.github.com> Date: Wed, 25 Dec 2024 14:50:56 +0100 Subject: [PATCH 8/8] fix: supress pragma warning added to project file --- .../BB84.EntityFrameworkCore.Repositories.Abstractions.csproj | 1 + .../IDbContext.cs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BB84.EntityFrameworkCore.Repositories.Abstractions/BB84.EntityFrameworkCore.Repositories.Abstractions.csproj b/src/BB84.EntityFrameworkCore.Repositories.Abstractions/BB84.EntityFrameworkCore.Repositories.Abstractions.csproj index 51ed0b0..f1e02de 100644 --- a/src/BB84.EntityFrameworkCore.Repositories.Abstractions/BB84.EntityFrameworkCore.Repositories.Abstractions.csproj +++ b/src/BB84.EntityFrameworkCore.Repositories.Abstractions/BB84.EntityFrameworkCore.Repositories.Abstractions.csproj @@ -1,6 +1,7 @@  This package contains frequently used repository abstractions. + CA1716 diff --git a/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IDbContext.cs b/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IDbContext.cs index a6d5c38..6280b11 100644 --- a/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IDbContext.cs +++ b/src/BB84.EntityFrameworkCore.Repositories.Abstractions/IDbContext.cs @@ -1,5 +1,4 @@ -#pragma warning disable CA1716 // Identifiers should not match keywords -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata;