0

I am using automapper to map one complex object to another where the source object has a list of objects whose properties do not match the list of objects in the destination.

So, i am manually going through the list in the source using Linq and mapping it to the destination object.

The issue is, the destination object is a database object:

Source Object:

public class AutomationDetailsResponse
    {        
        public Guid ServiceId { get; set; }    
        
        public int EngagementId { get; set; } 
        
        public string ServiceRequestName { get; set; }      
        
        public Guid ServiceRequestGUID { get; set; }    
        
        public string OfficeId { get; set; }
        
        public string CountryId { get; set; }  
        
        public string EngagementCode { get; set; }  
        
        public string CanvasDocumentUri { get; set; }  
        
        public string CanvasAudienceUri { get; set; }     

        public List<RequestFile> InputRequestFiles { get; set; }  
        
        public List<RequestFile> OutputRequestFiles { get; set; }  
        
        public string Status { get; set; }       

        public string RequestInitiatedByName { get; set; }    
        
        public string RequestInitiatedBy { get; set; }   
        
        public int DataCenterId { get; set; }   
        
        public DateTime CreatedAt { get; set; }  
        
        public DateTime LastUpdatedAt { get; set; }       

        public List<ActivityFeed> ActivityFeeds { get; set; }   
        
        public List<Guid> TaskIds { get; set; }

        [JsonProperty("Id")]
        public int CanvasRequestId { get; set; }
    }

    public class RequestFile
    {       
        public Guid Id { get; set; }       
        public int FileSequence { get; set; }       
        public Guid GroupId { get; set; }       
        public string GroupName { get; set; }       
        public string FileType { get; set; }       
        public string FileName { get; set; }       
        public int FileSize { get; set; }        
        public Guid DocumentId { get; set; }

        public DateTime CreatedAt { get; set; }
    }

    public class ActivityFeed
    {       
        public int CreatedById { get; set; }       
        public DateTime CreatedAt { get; set; }      
        public string Description { get; set; }       
        public int ActivityType { get; set; }       
        public string UserName { get; set; }        
        public int Id { get; set; }
    }

Destination Object:

[Table(nameof(AutomationRequest), Schema = Schemas.SAH)]
    public class AutomationRequest : EntityBase, ICreatedDate, IModifiedDate
    {
        [Required]
        public int CanvasEnvironmentId { get; set; }

        [Required]
        public Guid AppInstanceId { get; set; }

        public Guid CanvasRequestGUID { get; set; }

        public int CanvasRequestId { get; set; }      
        
        public int DownstreamStatusId { get; set; }

        [Required]
        public int CanvasStatusId { get; set; }

        [Required]
        public Guid ServiceCatalogId { get; set; }

        [Required]
        public string RequestName { get; set; }

        [Required]
        [MaxLength(255)]
        public string EngagementId { get; set; }        

        [Required]
        public int CountryId { get; set; }

        [Required]
        public int DataCenterId { get; set; }

        public bool IsDeletedFromSAH { get; set; }

        public bool IsDeletedFromCAH { get; set; }

        [MaxLength(500)]
        public string CreatedByEmail { get; set; }

        [MaxLength(500)]
        public string CreatedByName { get; set; }

        [Required]
        public DateTime CreatedDate { get; set; }

        [MaxLength(38)]
        public string ModifiedBy { get; set; }

        [MaxLength(50)]
        public string OriginatedFrom { get; set; }

        [Required]
        public DateTime ModifiedDate { get; set; }

        public string NavigationUrl { get; set; }

        public int? Version { get; set; }

        [ForeignKey(nameof(CanvasEnvironmentId))]
        public virtual Environment CanvasEnvironment { get; set; }

        [ForeignKey(nameof(CountryId))]
        public virtual Country Country { get; set; }

        [ForeignKey(nameof(ServiceCatalogId))]
        public virtual ServiceCatalog ServiceCatalog { get; set; }

        [ForeignKey(nameof(CanvasStatusId))]
        public virtual AutomationRequestStatus CanvasStatus { get; set; }        

        public virtual ICollection<AutomationRequestInputFile> InputFiles { get; set; }

        public virtual ICollection<AutomationRequestOutputFile> OutputFiles { get; set; }

        public virtual ICollection<AutomationRequestTask> Tasks { get; set; }
    }
[Table(nameof(AutomationRequestInputFile), Schema = Schemas.SAH)]
    public class AutomationRequestInputFile : EntityBase, ICreatedDate, IModifiedDate
    { 
        [Required]
        public Guid CanvasGroupId { get; set; }
        
        public Guid CanvasDocumentId { get; set; }

        [MaxLength(255)]
        public string FileName { get; set; }

        [MaxLength(38)]
        public string CreatedBy { get; set; }

        [Required]
        public DateTime CreatedDate { get; set; }

        [MaxLength(38)]
        public string ModifiedBy { get; set; }

        [Required]
        public DateTime ModifiedDate { get; set; }

        [Required]        
        public Guid AutomationRequestId { get; set; }      
        
        public string FileType { get; set; }

        [ForeignKey(nameof(AutomationRequestId))]
        public virtual AutomationRequest AutomationRequest { get; set; }

       
    }
 public class EntityBase
    {
        public EntityBase()
        {
            Id = Guid.NewGuid();
        }

        public Guid Id { get; set; }
    }

This is the mapping configuration:

CreateMap<AutomationDetailsResponse, AutomationRequest>()
                .ForMember(dst => dst.Id, opt => opt.Ignore())
                .ForMember(
                    dst => dst.CanvasRequestGUID,
                    opt => opt.MapFrom(src => src.ServiceRequestGUID))
                .ForMember(
                    dst => dst.RequestName,
                    opt => opt.MapFrom(src => src.ServiceRequestName))
                .ForMember(
                    dst => dst.InputFiles,
                    opt => opt.MapFrom(src => src.InputRequestFiles.Select(x => new AutomationRequestInputFile { CanvasGroupId = x.GroupId, CanvasDocumentId = x.DocumentId, FileName = x.FileName, FileType = x.FileType })))
                .ForMember(
                    dst => dst.OutputFiles,
                    opt => opt.MapFrom(src => src.OutputRequestFiles.Select(x => new AutomationRequestOutputFile { CanvasDocumentId = x.DocumentId,FileName = x.FileName, FileType = x.FileType, FileSize = x.FileSize, CreatedDate = x.CreatedAt })))
                .ForMember(
                    dst => dst.CreatedByName,
                    opt => opt.MapFrom(src => src.RequestInitiatedByName))
                 .ForMember(
                    dst => dst.CreatedDate,
                    opt => opt.MapFrom(src => src.CreatedAt))
                 .ForMember(
                    dst => dst.ModifiedDate,
                    opt => opt.MapFrom(src => src.LastUpdatedAt))
                 .ForMember(
                    dst => dst.Tasks,
                    opt => opt.MapFrom(src => src.TaskIds.Select(x => new AutomationRequestTask { CanvasId = x })))
                ;

The mapping works. The issue is this:

var automation = await _dbContext.AutomationRequests.FirstOrDefaultAsync(x => x.CanvasRequestGUID == automationDetails.ServiceRequestGUID);
 _mapper.Map(automationDetails, automation);
 _dbContext.Update(automation);
 await _dbContext.SaveChangesAsync();

The update fails because, in the mapping configuration, we are create a new instance AutomationRequestInputFile which triggers the constructor in EntityBase, creating a new Id. When EF Core tries to update the row, it does not find the record because the Id has changed.

I have been trying to solve this for 1 day now and havent made any progress.

Any help is appreciated.

Thanks.

7
  • would you please share the error message please? Commented Oct 31, 2020 at 1:28
  • database operation expected to affect 1 row(s) but actually affected 0 row(s). This is the error because the automapper changes the Id of the AutomationRequestInputFile and when i try to update, the update fails because EF Core can't find the record in db because the id is wrong. Commented Oct 31, 2020 at 1:36
  • What is the business requirement that you need to solve with this code? It's sound counter intuitive that you pull a record from a database, then map it to another object and update that record. 90% of the times you pull out a record from the database, make some changes on it and sent it right back to the database. Why would you map it to another object. Commented Oct 31, 2020 at 1:52
  • There is a business requirement to do this. Commented Oct 31, 2020 at 1:56
  • have you tried _dbContext.AutomationRequests.Update(automation) instead of_dbContext.Update(automation); Commented Oct 31, 2020 at 2:00

1 Answer 1

2

You're not using AutoMapper correctly since you're still manually constructing the AutomationRequestInputFile objects (by using LINQ .Select()).

The solution is to add a second mapping for RequestFile to AutomationRequestInputFile and remove the Select in the MapFrom config of the ´InputFiles´ property.

Here's a working .NET Fiddle (properties & types not provided in your question have been omitted): https://dotnetfiddle.net/9qizfk

Sign up to request clarification or add additional context in comments.

1 Comment

I thought this would solve it. I did exactly what you suggested but still a new id is being created for each inputrequestfile after the mapping and the db update still fails. I even added: .ForMember(dst => dst.Id, opt => opt.Ignore()) for this map: CreateMap<RequestFile, AutomationRequestInputFile>()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.