For each row in parent table I have many child rows in child table. But child row is unique for each parent row with "JointId" and "NdtId" and when i call "GetLineJointsForNdtSelectionAsync" from repository I want filter data with "JointId" and "NdtId". "JointId" is accessible in resolver and "NdtId" mustbe passed from "GetLineJointsForNdtSelectionAsync" in respository:
.ProjectTo<Models.Output.Piping.LineJoints.LineJointForNdtSelection>(
_mapper.ConfigurationProvider,
new { ndtId = query.NdtId })
But passed value from "GetLineJointsForNdtSelectionAsync" to resolver class is empty GUID. Then I used "(Guid)context.Items["ndtId"]" to get passed value in profile class but the following exception occures:
Unable to create a map expression from . (Ces.Caspian.Domain.Entities.Piping.LineJoints.LineJoint) to String.NdtType (System.String) Mapping types: LineJoint -> LineJointForNdtSelection Ces.Caspian.Domain.Entities.Piping.LineJoints.LineJoint -> Ces.Caspian.Models.Output.Piping.LineJoints.LineJointForNdtSelection Type Map configuration: LineJoint -> LineJointForNdtSelection Ces.Caspian.Domain.Entities.Piping.LineJoints.LineJoint -> Ces.Caspian.Models.Output.Piping.LineJoints.LineJointForNdtSelection Destination Member: NdtType.
Parent table:
public class LineJoint : EntityBase
{
public const int JointNoIndexMaxLength = 5;
public const int JointNoMaxLength = 20;
public LineJoint()
{
}
public LineJoint(Guid? userId) : base(userId)
{
}
public Guid? ContractorId { get; set; }
public Guid? LineSheetId { get; set; }
public string? JointNo { get; set; }
public Guid? ScheduleId { get; set; }
public Guid? ConnectionTypeId { get; set; }
public Guid? EndStatusId { get; set; }
public Guid? EndTypeId { get; set; }
public Guid? JointTypeId { get; set; }
public Guid? LocationId { get; set; }
public Guid? WeldTypeId { get; set; }
public int Revision { get; set; }
public Guid? TestPackageId { get; set; }
// Navigation
public Domain.Entities.Project.Contractors.Contractor? Contractor { get; set; }
public Domain.Entities.Piping.LineSheets.LineSheet? LineSheet { get; set; }
public Domain.Entities.Piping.ConnectionTypes.ConnectionType? ConnectionType { get; set; }
public Domain.Entities.Piping.EndTypes.EndType? EndType { get; set; }
public Domain.Entities.Piping.EndStatuses.EndStatus? EndStatus { get; set; }
public Domain.Entities.Piping.JointTypes.JointType? JointType { get; set; }
public Domain.Entities.Piping.Locations.Location? Location { get; set; }
public Domain.Entities.Piping.WeldTypes.WeldType? WeldType { get; set; }
public Domain.Entities.TestPackage.TestPackages.TestPackage? TestPackage { get; set; }
public Domain.Entities.Piping.Schedules.Schedule? Schedule { get; set; }
public List<Domain.Entities.Piping.NdtStatuses.NdtStatus>? NdtStatuses { get; set; }
}
Child table:
public class NdtStatus : Entities.EntityBase
{
public const int NDTNoMaxLength = 50;
public NdtStatus()
{
}
public NdtStatus(Guid? userId) : base(userId)
{
}
public Guid? LineJointId { get; set; }
public Guid? NdtId { get; set; }
public bool Required { get; set; }
public bool Exempted { get; set; }
public string? NdtNo { get; set; }
public Guid? NdtTypeId { get; set; }
// Navigation
public Domain.Entities.Piping.LineJoints.LineJoint? LineJoint { get; set; }
public Domain.Entities.Piping.Ndts.Ndt? Ndt { get; set; }
public Domain.Entities.Piping.NdtTypes.NdtType? NdtType { get; set; }
}
Output model:
public class LineJointForNdtSelection : OutputModelEssentials
{
// From parent table
public Guid JointId { get; set; }
public string? Contractor { get; set; }
public string? SheetNo { get; set; }
public string? JointNo { get; set; }
public string? ConnectionType { get; set; }
public string? EndStatus { get; set; }
public string? EndType { get; set; }
public string? JointType { get; set; }
public string? Location { get; set; }
public string? PackageNo { get; set; }
// From child table
public string? Ndt { get; set; }
public string? NdtType { get; set; }
public bool Required { get; set; }
public bool Exempted { get; set; }
public string? NdtNo { get; set; }
public string? RequestNo { get; set; }
public string? ReportNo { get; set; }
public string? Result { get; set; }
}
AutoMapper profile class for mapping:
public class LineJointForNdtSelection : AutoMapper.Profile
{
public LineJointForNdtSelection()
{
CreateMap<
Domain.Entities.Piping.LineJoints.LineJoint,
Models.Output.Piping.LineJoints.LineJointForNdtSelection>()
// From parent table
.ForMember(x => x.JointId, x => x.MapFrom(x => x.Id))
.ForMember(x => x.Contractor, x => x.MapFrom(x => x.Contractor.Name))
.ForMember(x => x.SheetNo, x => x.MapFrom(x => x.LineSheet.SheetNo))
.ForMember(x => x.ConnectionType, x => x.MapFrom(x => x.ConnectionType.Name))
.ForMember(x => x.EndStatus, x => x.MapFrom(x => x.EndStatus.Name))
.ForMember(x => x.EndType, x => x.MapFrom(x => x.EndType.Name))
.ForMember(x => x.JointType, x => x.MapFrom(x => x.JointType.Name))
.ForMember(x => x.Location, x => x.MapFrom(x => x.Location.Name))
.ForMember(x => x.PackageNo, x => x.MapFrom(x => x.TestPackage.TestPackageNo))
// From child table
.ForMember(x => x.NdtType, opt => opt.MapFrom((src, dest, destMember, context)
=> new Profiles.Piping.NdtStatuses.Resolvers.NdtTypeResolver((Guid)context.Items["ndtId"])
.Resolve(src, dest, destMember, context)));
;
}
}
Custome Resolver:
public class NdtTypeResolver : IValueResolver<
Ces.Caspian.Domain.Entities.Piping.LineJoints.LineJoint,
Ces.Caspian.Models.Output.Piping.LineJoints.LineJointForNdtSelection,
string?>
{
private Guid? _ndtId;
public NdtTypeResolver()
{
}
public NdtTypeResolver(Guid? ndtId = null)
{
_ndtId = ndtId;
}
public string? Resolve(
Ces.Caspian.Domain.Entities.Piping.LineJoints.LineJoint source,
Ces.Caspian.Models.Output.Piping.LineJoints.LineJointForNdtSelection destination,
string? destMember,
ResolutionContext context)
{
return source.NdtStatuses?.FirstOrDefault(c
=> c.LineJointId == source.Id && c.NdtId == _ndtId)?.NdtType?.Name ?? string.Empty;
}
}
Repository Method:
public async Task<List<Models.Output.Piping.LineJoints.LineJointForNdtSelection>> GetLineJointsForNdtSelectionAsync(
Models.Input.Queries.Piping.LineJoints.GetLineJointsForNdtSelection query)
{
var result = await _dbContext.LineJoints
.Where(x => x.LineSheet.LineNumberId == query.LineNumberId)
.ProjectTo<Models.Output.Piping.LineJoints.LineJointForNdtSelection>(
_mapper.ConfigurationProvider,
new { ndtId = query.NdtId })
.AsNoTracking()
.ToListAsync()
;
return result;
}
I have registered resolver to service but didn't solve problem. There is a simple solution but I think it is not best solution but works. in my last solution I must create multi profile class for each "NdtId" then in my repository check which "Id" passed to method then call an internal method to get desired data then return to user.
My problem has been solved. When I use ProjectTo for mapping data from database to class, second paramter after _mapper.ConfigurationProvider can not assign value to ResolutionContext. The final solution is that, first of all mapping data from database then, for mapping data to output model, we can assign value to ResolutionContext to pass value to resolver. In fact, I just refactored my repository method:
public async Task<List<Models.Output.Piping.LineJoints.LineJointForNdtSelection>> GetLineJointsForNdtSelectionAsync(
Models.Input.Queries.Piping.LineJoints.GetLineJointsForNdtSelection query)
{
var initialResult = await _dbContext.LineJoints
.Where(x => x.LineSheet.LineNumberId == query.LineNumberId)
.Select(s => new Models.Output.Piping.LineJoints.LineJointForNdtSelection
{
JointId = s.Id,
Contractor = s.Contractor == null ? null : s.Contractor.Name,
SheetNo = s.LineSheet == null ? null : s.LineSheet.SheetNo.ToString(),
JointNo = s.JointNo == null ? null : s.JointNo,
ConnectionType = s.ConnectionType == null ? null : s.ConnectionType.Name,
EndStatus = s.EndStatus == null ? null : s.EndStatus.Name,
EndType = s.EndType == null ? null : s.EndType.Name,
JointType = s.JointType == null ? null : s.JointType.Name,
Location = s.Location == null ? null : s.Location.Name,
PackageNo = s.TestPackage == null ? null : s.TestPackage.TestPackageNo,
// NDT Status
Ndt = s.NdtStatuses == null ? null :
s.NdtStatuses.FirstOrDefault(x => x.LineJointId == s.Id && x.NdtId == query.NdtId) == null ? null :
s.NdtStatuses.FirstOrDefault(x => x.LineJointId == s.Id && x.NdtId == query.NdtId).Ndt.Name,
NdtType = s.NdtStatuses == null ? null :
s.NdtStatuses.FirstOrDefault(x => x.LineJointId == s.Id && x.NdtId == query.NdtId) == null ? null :
s.NdtStatuses.FirstOrDefault(x => x.LineJointId == s.Id && x.NdtId == query.NdtId).NdtType.Name,
Required = s.NdtStatuses == null ? false :
s.NdtStatuses.FirstOrDefault(x => x.LineJointId == s.Id && x.NdtId == query.NdtId) == null ? false :
s.NdtStatuses.FirstOrDefault(x => x.LineJointId == s.Id && x.NdtId == query.NdtId).Required,
Exempted = s.NdtStatuses == null ? false :
s.NdtStatuses.FirstOrDefault(x => x.LineJointId == s.Id && x.NdtId == query.NdtId) == null ? false :
s.NdtStatuses.FirstOrDefault(x => x.LineJointId == s.Id && x.NdtId == query.NdtId).Exempted,
NdtNo = s.NdtStatuses == null ? null :
s.NdtStatuses.FirstOrDefault(x => x.LineJointId == s.Id && x.NdtId == query.NdtId) == null ? null :
s.NdtStatuses.FirstOrDefault(x => x.LineJointId == s.Id && x.NdtId == query.NdtId).NdtNo,
RequestNo = string.Empty,
ReportNo = string.Empty,
Result = string.Empty,
})
.AsNoTracking()
.ToListAsync()
;
var data = _mapper.Map<List<Models.Output.Piping.LineJoints.LineJointForNdtSelection>>(initialResult, option =>
{
option.Items["ndtId"] = query.NdtId;
});
return data;
}
Mapping required property by custom resolver (now I can use context.Items["ndtId"]):
.ForMember(x => x.Required, opt => opt.MapFrom((src, dest, destMember, context)
=> new Profiles.Piping.NdtStatuses.Resolvers.ExemptedResolver((Guid?)context.Items["ndtId"])
.Resolve(src, dest, destMember, context)))