I have parent object (LoanApplication) with a child (LoanApplicationQualificationTypes) that is a custom many-to-many table. The reason I have a custom is that it has two audit columns that need to be populated (ModifiedBy, ModifiedDate).
To get the children that were added or removed from the child collection to be persisted in the database correctly, I had to explicitly handle.
Below is the code (simplified by removing other properties that were germane to the question).
Parent (part of many-to-many):
[Serializable]
[Table("LoanApplication")]
public class LoanApplication : BaseDomainModelWithId, ILoanApplication
{
[Key]
[Column("LoanApplicationId")]
public override int? Id { get; set; }
[ForeignKey("LoanApplicationId")]
public virtual ICollection<LoanApplicationQualificationTypes> LoanApplicationQualificationTypes { get; set; }
IReadOnlyCollection<ILoanApplicationQualificationTypes> ILoanApplication.LoanApplicationQualificationTypes
{
get
{
var loanApplicationQualificationTypes = new List<ILoanApplicationQualificationTypes>();
if (LoanApplicationQualificationTypes == null) return loanApplicationQualificationTypes;
loanApplicationQualificationTypes.AddRange(LoanApplicationQualificationTypes);
return loanApplicationQualificationTypes.AsReadOnly();
}
set
{
foreach (var item in value)
{
LoanApplicationQualificationTypes.Add((LoanApplicationQualificationTypes)item);
}
}
}
public LoanApplication() : base()
{
LoanApplicationQualificationTypes = new List<LoanApplicationQualificationTypes>();
}
}
public interface ILoanApplication : IDomainModel, ILoanApplicationBase, IKeyIntId
{
IReadOnlyCollection<ILoanApplicationQualificationTypes> LoanApplicationQualificationTypes { get; set; }
}
Object part of many-to-many:
[Serializable]
[Table("QualificationType")]
public class QualificationType : IQualificationType
{
[Key]
[Column("QualificationTypeId")]
public override int? Id { get; set; }
[Required]
public string TypeName { get; set; }
[Required]
public bool IsActive { get; set; }
public virtual string ModifiedBy { get; set; }
public virtual DateTimeOffset? ModifiedDate { get; set; }
public QualificationType() : { }
}
Custom Many-to-Many:
[Serializable]
[Table("LoanApplicationQualificationTypes")]
public class LoanApplicationQualificationTypes : ILoanApplicationQualificationTypes
{
[Key]
[Column(Order = 1)]
public int? LoanApplicationId { get; set; }
[ForeignKey("LoanApplicationId")]
public virtual LoanApplication LoanApplication { get; set; }
ILoanApplication ILoanApplicationQualificationTypes.LoanApplication
{
get
{
return this.LoanApplication;
}
set
{
this.LoanApplication = (LoanApplication)value;
}
}
[Required]
[Key]
[Column(Order = 2)]
public int QualificationTypeId { get; set; }
[ForeignKey("QualificationTypeId")]
public virtual QualificationType QualificationType { get; set; }
IQualificationType ILoanApplicationQualificationTypes.QualificationType
{
get
{
return this.QualificationType;
}
set
{
this.QualificationType = (QualificationType)value;
}
}
public virtual string ModifiedBy { get; set; }
public virtual DateTimeOffset? ModifiedDate { get; set; }
public LoanApplicationQualificationTypes() { }
}
Update method in LoanApplication Repository:
public bool Update(ILoanApplication entity)
{
using (var db = new MainContext())
{
entity.ModifiedDate = DateTime.UtcNow;
entity.ModifiedBy = UserOrProcessName;
// Add / Remove LoanApplicationQualificationTypes and populate audit columns
if (entity.LoanApplicationQualificationTypes?.Count > 0)
{
var existingItems = db.LoanApplicationQualificationTypes.Where(q => q.LoanApplicationId == entity.Id.Value).ToList();
var newItems = entity.LoanApplicationQualificationTypes.Where(q => existingItems.All(e => e.QualificationTypeId != q.QualificationTypeId));
var deletedItems = existingItems.Where(q => entity.LoanApplicationQualificationTypes.All(e => e.QualificationTypeId != q.QualificationTypeId));
foreach (var newItem in newItems)
{
newItem.ModifiedBy = UserOrProcessName;
newItem.ModifiedDate = DateTime.UtcNow;
db.LoanApplicationQualificationTypes.Add((LoanApplicationQualificationTypes)newItem);
}
foreach (var deletedItem in deletedItems)
{
db.LoanApplicationQualificationTypes.Remove((LoanApplicationQualificationTypes)deletedItem);
}
// Need to clear to avoid duplicate objects
((LoanApplication)entity).LoanApplicationQualificationTypes.Clear();
}
db.Entry(entity).State = EntityState.Modified;
db.SaveChanges();
}
return true;
}
Is there a way implement the Update without the explicitly handling adds/updates?