I am mapping a Linq-to-SQL object to a domain model using AutoMapper. This Linq-to-SQL object has child objects. After mapping the Linq-to-SQL object to it's domain model all values are correct. However after mapping the domain model back to it's Linq-to-SQL counterpart all child objects lose their parent id.
I have narrowed it down to the SetElementValue() method in AutoMapper's EnumerableMapper class. The child object reference (an argument named mappedValue in SetElementValue()) keeps it's parent id up until it is added to the enumerable which in this case is an implementation of IBindingList. After being added to said implementation it's parent id is set to 0. I have compared all values in both the Linq-to-SQL object and domain model object, and all values are equal to their original state except for the parent id.
I am inclined to believe that this is related to the usage of IBindingList, does it make any sense at all that the parent id should be removed here? Am I overlooking something? I'm not sure about what source code you would require to provide any useful advice, just ask and you shall receive whatever you require.
Activity (parent) domain model definition:
public class Activity : IActivityEntity
{
public int Id { get; set; }
public string NameOrDescription { get; set; }
public int ClientId;
public int ActivityTypeId;
public int PerformedByEmployeeId;
public bool IsBillable;
public DateTime PerformedDate { get; set; }
public DateTime StartTime { get; set; }
public DateTime StopTime { get; set; }
public string Description { get; set; }
public int ProjectId;
public int WorkOrderId;
public IList<ProductLine> ProductLines;
public Activity()
{
this.ProductLines = new List<ProductLine>();
}
public Activity(ActivityType activityType, int clientId, int employeeId, int workOrderId, DateTime startTime, DateTime stopTime, DateTime performedDate, string description)
{
this.ClientId = clientId;
this.ActivityTypeId = activityType.Id;
this.PerformedByEmployeeId = employeeId;
this.Description = description ?? "";
this.IsBillable = activityType.IsBillable;
this.StartTime = startTime;
this.StopTime = stopTime;
this.PerformedDate = performedDate;
this.WorkOrderId = workOrderId;
}
}
ProductLine (child) domain model definition:
public class ProductLine : IEntity
{
public int Id;
public int ClientId;
public int ProductId;
public int ProductGroupId;
public string ProductNumber;
public int SupplierId;
public string Name;
public int ActivityId;
public int WorkOrderId;
public float Amount;
public decimal InPriceWithoutVAT;
public decimal OutPriceWithoutVAT;
public bool IsBillable;
public double? DiscountPercentage;
public string Denomination;
public bool CreatedBySystem;
public bool StructureProductLine;
public int SellerEmployeeId;
public int ConsultantEmployeeId;
public ProductLine()
{
}
}
Activity domain to DTO rules:
Mapper.CreateMap<Activity, D.Activity>()
.ForMember(dto => dto.ActivityID, options => options.MapFrom(activity => activity.Id))
.ForMember(dto => dto.ClientID, options => options.MapFrom(activity => activity.ClientId))
.ForMember(dto => dto.ActivityTypeID, options => options.MapFrom(activity => activity.ActivityTypeId))
.ForMember(dto => dto.Description, options => options.MapFrom(activity => activity.Description))
.ForMember(dto => dto.StartTime, options => options.MapFrom(activity => activity.StartTime))
.ForMember(dto => dto.StopTime, options => options.MapFrom(activity => activity.StopTime))
.ForMember(dto => dto.IsBillable, options => options.MapFrom(activity => activity.IsBillable))
.ForMember(dto => dto.WorkOrderID, options => options.MapFrom(activity => activity.WorkOrderId))
.ForMember(dto => dto.PerformedByEmployeeID, options => options.MapFrom(activity => activity.PerformedByEmployeeId));
ProductLine domain to DTO rules:
Mapper.CreateMap<ProductLine, D.ProductLine>()
.ForMember(dto => dto.ProductLineID, options => options.MapFrom(productLine => productLine.Id))
.ForMember(dto => dto.ActivityID, options => options.MapFrom(productLine => productLine.ActivityId))
.ForMember(dto => dto.Amount, options => options.MapFrom(productLine => productLine.Amount))
.ForMember(dto => dto.Billable, options => options.MapFrom(productLine => productLine.IsBillable))
.ForMember(dto => dto.ClientID, options => options.MapFrom(productLine => productLine.ClientId))
.ForMember(dto => dto.Name, options => options.MapFrom(productLine => productLine.Name))
.ForMember(dto => dto.OutPriceWithoutWAT, options => options.MapFrom(productLine => productLine.OutPriceWithoutVAT))
.ForMember(dto => dto.ProductGroupID, options => options.MapFrom(productLine => productLine.ProductGroupId))
.ForMember(dto => dto.ProductID, options => options.MapFrom(productLine => productLine.ProductId))
.ForMember(dto => dto.ProductNumber, options => options.MapFrom(productLine => productLine.ProductNumber))
.ForMember(dto => dto.StructureProductLine, options => options.MapFrom(productLine => productLine.StructureProductLine))
.ForMember(dto => dto.SupplierID, options => options.MapFrom(productLine => productLine.SupplierId))
.ForMember(dto => dto.InPriceWithoutWAT, options => options.MapFrom(productLine => productLine.InPriceWithoutVAT))
.ForMember(dto => dto.Denomination, options => options.MapFrom(productLine => productLine.Denomination))
.ForMember(dto => dto.SoldByEmployeeID, options => options.MapFrom(productLine => productLine.SellerEmployeeId))
.ForMember(dto => dto.ConsultantEmployeeID, options => options.MapFrom(productLine => productLine.ConsultantEmployeeId))
.ForMember(dto => dto.WorkOrderID, options => options.MapFrom(productLine => productLine.WorkOrderId));