Following is the exact scenario in my application.
There are two entities:
- Customer: CustomerId, CustomerName
- CustomerAddress: AddressId, CustomerId (FK), Area, City
A customer has one-to-many relation with CustomerAddress.
A customer can have addresses of more than one cities.
When I save changes to the existing customer - at that time, its underlying CustomerAddress collection will be having all addresses of one particular city only.
So when I update that customer in the database, it should -
- Add all new addresses of that city for the customer (i.e. the one exists in collection but not in db for that customer)
- Retain those addresses which are common in both
- Delete all those addresses which exist in database but not in collection for that city.
I know one method, wherein I can fetch Customer entity from database, and can add, delete, retain by looping through Addresses collection there.
But I am more interested in knowing the best practices to achieve this.
Any idea on this much appreciated?
This is what I normally do, just like you said, looping through child navigations, that means loads everything into memory with a single round trip and then process the logic in the application, that's the main reason we use ORM.
// Loads contacts.
if (customerDb.Id != Guid.Empty)
{
context.Entry(customerDb).Collection(c => c.customercontactxrefs).Load();
foreach (var xref in customerDb.customercontactxrefs)
{
context.Entry(xref).Reference(x => x.contact).Load();
}
}
// Deletes missing contacts.
var deletedXrefs = customerDb.customercontactxrefs.Where(xrefDb => !customer.Contacts.Any(contact => xrefDb.ContactId == contact.Id)).ToArray();
foreach (var xref in deletedXrefs)
{
customerDb.customercontactxrefs.Remove(xref);
context.Set<customercontactxref>().Remove(xref);
}
// Edits existing contacts.
foreach (var xrefDb in customerDb.customercontactxrefs)
{
var foundContact = customer.Contacts.FirstOrDefault(contact => contact.Id == xrefDb.ContactId);
if (foundContact != null && xrefDb.contact != null)
{
xrefDb.contact.Name = foundContact.Name;
xrefDb.contact.Phone = foundContact.Phone;
xrefDb.contact.Mobile = foundContact.Mobile;
xrefDb.contact.Fax = foundContact.Fax;
xrefDb.contact.Email = foundContact.Email;
}
}
// Adds new contacts.
var newContacts = customer.Contacts.Where(contact => contact.Id == Guid.Empty).ToArray();
foreach (var contact in newContacts)
{
customerDb.customercontactxrefs.Add(new customercontactxref
{
Id = Guid.NewGuid(),
contact = new contact
{
Id = Guid.NewGuid(),
Name = contact.Name,
Phone = contact.Phone,
Mobile = contact.Mobile,
Fax = contact.Fax,
Email = contact.Email
}
});
}
Or
using (RSDContext context = new RSDContext())
{
var details = order.OrderDetails;
order.OrderDetails = null;
context.Entry(order).State = EntityState.Modified;
foreach (var detail in details)
{
if (detail.Id == 0)
{
// Adds.
detail.OrderId = order.Id;
context.Entry(detail).State = EntityState.Added;
}
else if (detail.IsDeleted)
// Adds new property called 'IsDeleted'
// and add [NotMapped] attribute
// then mark this property as true from the UI for deleted items.
{
// Deletes.
context.Entry(detail).State = EntityState.Deleted;
}
else
{
// Updates.
context.Entry(detail).State = EntityState.Modified;
}
}
order.OrderDetails = details;
context.SaveChanges();
}