Compare commits

...

7 Commits

Author SHA1 Message Date
bb29b1563a Refactor DTOs and MappingProfile for consistency
Standardized namespaces for DTO classes and redefined several records to include necessary properties. Removed mapping configurations from MappingProfile, indicating a potential refactor. Introduced ProfileObjStateDto with a States property and ensured ProfileControlsTFDto and StateDto are properly encapsulated.
2025-08-01 02:53:52 +02:00
ad023b01d3 Enhance state management in DTOs and entities
- Added `States` property to `ProfileObjStateDto` and `ObjectDto`.
- Updated `MappingProfile` to include new `States` mapping.
- Made `StateId` in `ProfileObjState` required and added `State1` navigation property.
- Changed `ProfileObject` to use `States` instead of `State`.
- Cleaned up `State` class structure and removed redundant namespaces.
- Updated `ProfileObjRepository` to fetch related state data.
2025-08-01 02:50:19 +02:00
7309b968fe Refactor ProfileObjState and ProfileObject relationships
Removed the State property from ProfileObjState and added
ChangedWho and ChangedWhen properties. Updated ProfileObject
to reference ProfileObjState instead of State. Modified
ProfileObjRepository to include the new State property
when querying ProfileObject instances.
2025-08-01 02:21:48 +02:00
1159f3f575 Update ProfileObjState class and DI registration 2025-08-01 02:13:44 +02:00
8f2261f0fa Refactor ProfileObjState properties and add documentation
- Changed properties from `init` to `set` for mutability.
- Updated `ProfileId`, `UserId`, `ObjId`, and `StateId` to nullable types.
- Added XML documentation for clarity on property usage.
- Reduced maximum length of `State2`, `State3`, and `State4` to 100 characters.
- Modified `AddedWho` to nullable with default value "SYS".
- Changed `AddedWhen` to nullable with default value of `DateTime.Now`.
- Introduced `ChangedWho` and `ChangedWhen` properties for tracking changes.
- Updated navigation properties to reflect new accessors.
2025-08-01 02:11:49 +02:00
c779dd4a47 Refactor ReadProfileHandler and add Buttons property
Updated `ReadProfileHandler` to assign buttons directly to the `profile` object before mapping to `ProfileDto`. This ensures the `Buttons` property is populated correctly. Added a new `[NotMapped]` property `Buttons` of type `IEnumerable<Button>?` to the `Profile` class to hold associated buttons.
2025-08-01 01:58:15 +02:00
709ebea097 Refactor profile handling and error management
- Updated `ProfileController` to always return `Ok(profile)` instead of `NotFound()`.
- Changed `ReadProfileQuery` to return a non-nullable `ProfileDto`, throwing `NotFoundException` if not found.
- Modified `ReadProfileHandler` to handle the new return type and throw exceptions appropriately.
- Adjusted `ReadProfileAsync` to align with the new non-nullable return type.
- Added dependency on `DigitalData.Core.Exceptions` for improved error handling.
2025-08-01 01:41:21 +02:00
24 changed files with 124 additions and 167 deletions

View File

@@ -32,6 +32,6 @@ public class ProfileController : ControllerBase
}
var profile = await _mediator.ReadProfileAsync(userId);
return profile is null ? NotFound() : Ok(profile);
return Ok(profile);
}
}

View File

@@ -1,4 +0,0 @@
namespace WorkFlow.Application.Dto.Config
{
public record ConfigCreateDto(string Title, string String);
}

View File

@@ -1,4 +0,0 @@
namespace WorkFlow.Application.Dto.Config
{
public record ConfigUpdateDto(string Title, string String) : BaseUpdateDto;
}

View File

@@ -1,14 +0,0 @@
namespace WorkFlow.Application.Dto.ProfileControlsTF
{
public record ProfileControlsTFCreateDto(
int ProfileId,
int UserId,
long ObjId,
string ObjType,
string AttrName,
string CtrlType,
string CtrlCaption,
bool Mandatory,
bool ReadOnly,
string? ChoiceList = null) : BaseCreateDto;
}

View File

@@ -1,6 +0,0 @@
namespace WorkFlow.Application.Dto.ProfileControlsTF;
/// <summary>
/// This Data Transfer Object (DTO) serves as a placeholder and does not support updates.
/// </summary>
public record ProfileControlsTFUpdateDto(long Id);

View File

@@ -1,11 +0,0 @@
namespace WorkFlow.Application.Dto.ProfileObjState
{
public record ProfileObjStateCreateDto(
int ProfileId,
int UserId,
long ObjId,
int StateId,
string? State2 = null,
string? State3 = null,
string? State4 = null) : BaseCreateDto;
}

View File

@@ -1,18 +0,0 @@
using DigitalData.UserManager.Application.DTOs.User;
using WorkFlow.Application.Dto.State;
namespace WorkFlow.Application.Dto.ProfileObjState
{
public record ProfileObjStateDto(int Id,
int ProfileId,
int UserId,
long ObjId,
int StateId,
string AddedWho,
DateTime AddedWhen,
string? State2 = null,
string? State3 = null,
string? State4 = null,
UserReadDto? User = null,
StateDto? State = null);
}

View File

@@ -1,6 +0,0 @@
namespace WorkFlow.Application.Dto.ProfileObjState;
/// <summary>
/// This Data Transfer Object (DTO) serves as a placeholder and does not support updates.
/// </summary>
public record ProfileObjStateUpdateDto(long Id);

View File

@@ -1,4 +0,0 @@
namespace WorkFlow.Application.Dto.State
{
public record StateCreateDto(string IntlState) : BaseCreateDto;
}

View File

@@ -1,6 +0,0 @@
namespace WorkFlow.Application.Dto.State;
/// <summary>
/// This Data Transfer Object (DTO) serves as a placeholder and does not support updates.
/// </summary>
public record StateUpdateDto(int Id);

View File

@@ -1,4 +1,4 @@
namespace WorkFlow.Application.Dto.Config
namespace WorkFlow.Application.Dto
{
public record ConfigDto(int Id,
string Title,

View File

@@ -11,4 +11,6 @@ public class ObjectDto
public IEnumerable<string> Sublines { get; set; } = Array.Empty<string>();
public string? CmdCheckIn { get; set; }
public IEnumerable<string?> States { get; set; } = Array.Empty<string>();
}

View File

@@ -1,6 +1,6 @@
using DigitalData.UserManager.Application.DTOs.User;
namespace WorkFlow.Application.Dto.ProfileControlsTF
namespace WorkFlow.Application.Dto
{
public record ProfileControlsTFDto(int Id,
int ProfileId,

View File

@@ -0,0 +1,6 @@
namespace WorkFlow.Application.Dto;
public class ProfileObjStateDto
{
public IEnumerable<string?> States { get; set; } = Array.Empty<string>();
}

View File

@@ -1,4 +1,4 @@
namespace WorkFlow.Application.Dto.State
namespace WorkFlow.Application.Dto
{
public record StateDto(int Id, string IntlState, string AddedWho, DateTime AddedWhen);
}

View File

@@ -1,9 +1,5 @@
using WorkFlow.Application.Buttons;
using WorkFlow.Application.Dto;
using WorkFlow.Application.Dto.Config;
using WorkFlow.Application.Dto.ProfileControlsTF;
using WorkFlow.Application.Dto.ProfileObjState;
using WorkFlow.Application.Dto.State;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application;
@@ -23,19 +19,7 @@ public class MappingProfile : AutoMapper.Profile
.ForMember(dest => dest.Headlines, opt => opt.MapFrom(src =>
new[] { src.Headline1, src.Headline2 }))
.ForMember(dest => dest.Sublines, opt => opt.MapFrom(src =>
new[] { src.Subline1, src.Subline2 }));
// Mapping create-DTO to entity
CreateMap<ConfigCreateDto, Config>();
CreateMap<ProfileControlsTFCreateDto, ProfileControlsTF>();
CreateMap<ProfileObjStateCreateDto, ProfileObjState>();
CreateMap<StateCreateDto, State>();
// Mapping update-DTO to entity
CreateMap<ConfigUpdateDto, Config>();
CreateMap<ProfileControlsTFUpdateDto, ProfileControlsTF>();
CreateMap<ProfileObjStateUpdateDto, ProfileObjState>();
CreateMap<StateUpdateDto, State>();
new[] { src.Subline1, src.Subline2 }))
.ForMember(dest => dest.States, opt => opt.MapFrom(src => src.States));
}
}

View File

@@ -1,5 +1,6 @@
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using MediatR;
using WorkFlow.Application.Buttons;
using WorkFlow.Application.Contracts.Repositories;
@@ -12,13 +13,13 @@ namespace WorkFlow.Application.Profiles;
/// Represents a request to read a user profile by their user ID.
/// </summary>
/// <param name="UserId">The ID of the user whose profile is being requested.</param>
public record ReadProfileQuery(int UserId, bool IncludeObject = true) : IRequest<ProfileDto?>;
public record ReadProfileQuery(int UserId, bool IncludeObject = true) : IRequest<ProfileDto>;
/// <summary>
/// Handles the <see cref="ReadProfileQuery"/> request by retrieving the user profile
/// from the data store using the <see cref="IProfileRepository"/>.
/// </summary>
public class ReadProfileHandler : IRequestHandler<ReadProfileQuery, ProfileDto?>
public class ReadProfileHandler : IRequestHandler<ReadProfileQuery, ProfileDto>
{
private readonly IProfileRepository _profileRepository;
@@ -48,26 +49,23 @@ public class ReadProfileHandler : IRequestHandler<ReadProfileQuery, ProfileDto?>
/// <param name="request">The request containing the user ID.</param>
/// <param name="cancel">A cancellation token for the operation.</param>
/// <returns>The user profile if found; otherwise, <c>null</c>.</returns>
public async Task<ProfileDto?> Handle(ReadProfileQuery request, CancellationToken cancel = default)
public async Task<ProfileDto> Handle(ReadProfileQuery request, CancellationToken cancel = default)
{
var profile = await _profileRepository.ReadAsync(request.UserId, cancel);
if (request.IncludeObject && profile?.Id is int profileId)
var profile = await _profileRepository.ReadAsync(request.UserId, cancel)
?? throw new NotFoundException();
if (request.IncludeObject && profile.Id is int profileId)
profile.Objects = await _objRepository.ReadAsync(request.UserId, profileId, cancel);
var profileDto = _mapper.Map<ProfileDto>(profile);
if (profile.Id is int pId)
profile.Buttons = await _bttnRepository.Read(b => b.ProfileId == pId).ToListAsync(cancel);
if (profile?.Id is int pId)
{
var bttns = await _bttnRepository.Read(b => b.ProfileId == pId).ToListAsync(cancel);
profileDto.Buttons = _mapper.Map<IEnumerable<ButtonDto>>(bttns);
}
return profileDto;
return _mapper.Map<ProfileDto>(profile);
}
}
public static class ReadProfileQueryExtensions
{
public static Task<ProfileDto?> ReadProfileAsync(this IMediator mediator, int userId, bool includeObject = true)
public static Task<ProfileDto> ReadProfileAsync(this IMediator mediator, int userId, bool includeObject = true)
=> mediator.Send(new ReadProfileQuery(UserId: userId, IncludeObject: includeObject));
}

View File

@@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.0.0" />
<PackageReference Include="DigitalData.Core.Application" Version="3.3.4" />
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.0.1" />
<PackageReference Include="MediatR" Version="13.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.20" />
<PackageReference Include="UserManager" Version="1.1.2" />

View File

@@ -27,4 +27,7 @@ public class Profile
[NotMapped]
public IEnumerable<ProfileObject>? Objects { get; set; }
[NotMapped]
public IEnumerable<Button>? Buttons { get; set; }
}

View File

@@ -1,5 +1,4 @@
using DigitalData.UserManager.Domain.Entities;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities;
@@ -8,48 +7,84 @@ namespace WorkFlow.Domain.Entities;
public class ProfileObjState
{
[Key]
[Column("GUID")]
public long Id { get; init; }
[Column("GUID", TypeName = "bigint")]
public long Id { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("MWF_PROFILE_ID", TypeName = "int")]
public int? ProfileId { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("USR_ID", TypeName = "int")]
public int? UserId { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("OBJ_ID", TypeName = "bigint")]
public long? ObjectId { get; set; }
[Required]
[Column("MWF_PROFILE_ID")]
public required int ProfileId { get; init; }
[Column("STATE_ID", TypeName = "int")]
public int StateId { get; set; }
[Required]
[Column("USR_ID")]
public required int UserId { get; init; }
[Column("STATE2", TypeName = "nvarchar(100)")]
[StringLength(100)]
public string? State2 { get; set; }
[Required]
[Column("OBJ_ID")]
public required long ObjId { get; init; }
[Column("STATE3", TypeName = "nvarchar(100)")]
[StringLength(100)]
public string? State3 { get; set; }
[Required]
[Column("STATE_ID")]
public required int StateId { get; init; }
[Column("STATE4", TypeName = "nvarchar(100)")]
[StringLength(100)]
public string? State4 { get; set; }
[Column("STATE2", TypeName = "nvarchar(3000)")]
public string? State2 { get; init; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("ADDED_WHO", TypeName = "nvarchar(100)")]
[StringLength(100)]
public string? AddedWho { get; set; } = "SYS";
[Column("STATE3", TypeName = "nvarchar(3000)")]
public string? State3 { get; init; }
[Column("STATE4", TypeName = "nvarchar(3000)")]
public string? State4 { get; init; }
[Required]
[Column("ADDED_WHO", TypeName = "varchar(30)")]
public required string AddedWho { get; init; }
[Required]
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("ADDED_WHEN", TypeName = "datetime")]
public required DateTime AddedWhen { get; init; }
public DateTime? AddedWhen { get; set; } = DateTime.Now;
[ForeignKey("ProfileId")]
public Profile? Profile { get; init; } = null;
[Column("CHANGED_WHO", TypeName = "nvarchar(100)")]
[StringLength(100)]
public string? ChangedWho { get; set; }
[ForeignKey("UserId")]
public User? User { get; init; } = null;
[Column("CHANGED_WHEN", TypeName = "datetime")]
public DateTime? ChangedWhen { get; set; }
[ForeignKey("StateId")]
public State? State { get; init; } = null;
public virtual State? State1 { get; set; }
[NotMapped]
public IEnumerable<string?> States => new List<string?>
{
State1?.IntlState, State2, State3, State4
};
}

View File

@@ -24,4 +24,7 @@ public class ProfileObject
[Column("CMD_CheckIn")]
public string? CmdCheckIn { get; set; }
[ForeignKey("ObjStateId")]
public virtual ProfileObjState? States { get; set; }
}

View File

@@ -1,25 +1,24 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities
namespace WorkFlow.Domain.Entities;
[Table("TBMWF_WF_STATE", Schema = "dbo")]
public class State
{
[Table("TBMWF_WF_STATE", Schema = "dbo")]
public class State
{
[Key]
[Column("GUID")]
public int Id { get; init; }
[Key]
[Column("GUID")]
public int Id { get; init; }
[Required]
[Column("INTL_STATE", TypeName = "varchar(100)")]
public required string IntlState { get; init; }
[Required]
[Column("INTL_STATE", TypeName = "varchar(100)")]
public required string IntlState { get; init; }
[Required]
[Column("ADDED_WHO", TypeName = "varchar(30)")]
public required string AddedWho { get; init; }
[Required]
[Column("ADDED_WHO", TypeName = "varchar(30)")]
public required string AddedWho { get; init; }
[Required]
[Column("ADDED_WHEN", TypeName = "datetime")]
public required DateTime AddedWhen { get; init; }
}
[Required]
[Column("ADDED_WHEN", TypeName = "datetime")]
public required DateTime AddedWhen { get; init; }
}

View File

@@ -3,9 +3,6 @@ using DigitalData.Core.Infrastructure.AutoMapper;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using WorkFlow.Application.Contracts.Repositories;
using WorkFlow.Application.Dto.Config;
using WorkFlow.Application.Dto.ProfileControlsTF;
using WorkFlow.Application.Dto.State;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Repositories;
@@ -18,11 +15,12 @@ public static class DependencyInjection
services.TryAddScoped<IProfileRepository, ProfileRepository>();
services.TryAddScoped<IProfileObjRepository, ProfileObjRepository>();
services.AddDbRepository<WFDBContext, Config>(c => c.Configs).UseAutoMapper(typeof(ConfigUpdateDto));
services.AddDbRepository<WFDBContext, ProfileControlsTF>(c => c.ProfileControlsTFs).UseAutoMapper(typeof(ProfileControlsTFUpdateDto));
services.AddDbRepository<WFDBContext, State>(c => c.States).UseAutoMapper(typeof(StateUpdateDto));
services.AddDbRepository<WFDBContext, Config>(c => c.Configs).UseAutoMapper();
services.AddDbRepository<WFDBContext, ProfileControlsTF>(c => c.ProfileControlsTFs).UseAutoMapper();
services.AddDbRepository<WFDBContext, State>(c => c.States).UseAutoMapper();
services.AddDbRepository<WFDBContext, Button>(c => c.Buttons).UseAutoMapper();
services.AddDbRepository<WFDBContext, State>(c => c.States).UseAutoMapper(typeof(StateUpdateDto));
services.AddDbRepository<WFDBContext, State>(c => c.States).UseAutoMapper();
services.AddDbRepository<WFDBContext, ProfileObjState>(c => c.ProfileObjStates).UseAutoMapper();
return services;
}

View File

@@ -35,5 +35,6 @@ public class ProfileObjRepository : IProfileObjRepository
public async Task<IEnumerable<ProfileObject>> ReadAsync(int userId, int profileId, CancellationToken cancel = default)
=> await _context.Objects
.FromSqlRaw("SELECT * FROM [FNMWF_GET_PROFILE_OBJECTS] ({0}, {1})", userId, profileId)
.Include(obj => obj.States).ThenInclude(objState => objState.State1)
.ToListAsync(cancel);
}