I have an issue with AutoQuery’s AutoCrud that is causing a DTO property to be incorrectly removed because of a value in meta.RemoveDtoProps.
The model definition for the CreateLocationGroup service endpoint is
[Tag("Internal.LocationGroup")]
[Api("internal only")]
[ValidateIsAuthenticated]
[ValidateHasRole(UserRole.ManageReference)]
[Route("/api/internal/location-group", "POST")]
public class CreateLocationGroup : ICreateDb<LocationGroup>, IReturn<CodeResponse>, IPost
{
[ValidateNotEmpty, ValidateLength(1, 8)]
public string PkLocationCode { get; set; } = null!;
[ValidateNotEmpty, ValidateLength(1, 8)]
public string PkLocationTypeCode { get; set; } = null!;
[ValidateNotEmpty]
public int TransId { get; set; }
[ValidateNotEmpty, ValidateLength(1, 8)]
public string PkParentLocationCode { get; set; } = Constant.ParentLocationCode; // "/"
[ValidateNotEmpty, ValidateExactLength(1)]
public string VirtualIndicator { get; set; } = Constant.StatusNo; // "N"
}
The DTO, LocationGroup, is defined as follows (it is an external vendor database)
[Schema("dbo")]
[Alias("location_group")]
[NamedConnection(DbConstant.Trade)]
[DataContract]
[UniqueConstraint(nameof(PkParentLocationCode), nameof(PkLocationCode), nameof(PkLocationCodeType))] // compound PK
public class LocationGroup
{
[Alias("parent_loc_code")]
[DataMember, PrimaryKey, Required]
[CustomField("CHAR(8)")]
[ValidateNotEmpty, ValidateLength(1, 8)]
public string PkParentLocationCode { get; set; } = Constant.ParentLocationCode; // "/"
[Alias("loc_code")]
[DataMember, PrimaryKey, Required]
[CustomField("CHAR(8)")]
[ValidateNotEmpty, ValidateLength(1, 8)]
public string PkLocationCode { get; set; } = null!;
[Alias("loc_type_code")]
[DataMember, PrimaryKey, Required]
[CustomField("CHAR(8)")]
[ValidateNotEmpty, ValidateLength(1, 8)]
public string PkLocationCodeType { get; set; } = null!;
[Alias("virtual_ind")]
[DataMember, Required]
[ValidateNotEmpty, ValidateExactLength(1)]
public string VirtualIndicator { get; } = Constant.StatusNo; // "N"
[Alias("trans_id")]
[DataMember, Required]
[ValidateNotEmpty]
public int TransId { get; set; }
}
The create service implementation in ServiceInterface is:
public CodeResponse Post(CreateLocationGroup request)
{
ParamValidator.CheckNotNullOrWhiteSpaceEx(request.PkLocationCode);
ParamValidator.CheckNotNullOrWhiteSpaceEx(request.PkLocationTypeCode);
using var db = AutoQuery!.GetDb<LocationGroup>(Request);
var req = FromCreateLocationGroup(request);
try
{
var unused = (CodeResponse)AutoQuery.Create(req, Request);
}
catch (OptimisticConcurrencyException e)
{
// triggers on the db table may cause an OptimisticConcurrencyException on insert
_log.Error(e.Message);
}
finally
{
using var auditService = base.ResolveService<AuditService>();
auditService.Post(new CreateDataTrack
{
ReferenceCode = RefCode,
PkCode = request.PkLocationCode,
ReferenceData = req.ToJson(),
AuditEventId = AuditEvent.Created
});
}
return Read(request.PkLocationCode, request.PkLocationTypeCode);
}
An error is thrown because a NULL value is being inserted in the loc_type_code column (i.e. PkLocationCodeType in the DTO).
Tracing through the problem, it is being caused by the following ServiceStack AutoQueryFeature.cs code:
namespace ServiceStack
{
public partial class AutoQueryFeature
{
// code hidden
private Dictionary<string, object> ResolveDtoValues(AutoCrudMetadata meta, IRequest req, object dto, bool skipDefaults=false)
{
ILog log = null;
var dtoValues = dto.ToObjectDictionary();
foreach (var entry in meta.MapAttrs)
{
if (dtoValues.TryRemove(entry.Key, out var value))
{
dtoValues[entry.Value.To] = value;
}
}
// code hidden
List<string> removeKeys = null;
foreach (var removeDtoProp in meta.RemoveDtoProps)
{
removeKeys ??= new List<string>();
removeKeys.Add(removeDtoProp);
}
// code hidden
if (removeKeys != null)
{
foreach (var key in removeKeys)
{
dtoValues.RemoveKey(key); // *** this causes DTO's PkLocationCodeType to be removed WHY?
}
}
// code hidden
return dtoValues;
}
}
}
Where or how is meta.RemoveDtoProps getting populated?
I am using ServiceStack 8.0.0, NET8, Windows.