Best practice for inserting/updating data in multiple tables at once
So I have two tables in my database, Contacts and Addresses:
Contacts : ContactID | AddressID | FirstName | LastName
Addresses: AddressID | Address1 | Address2 | City | State | Zipcode
I have a page where you can add a contact. It contains all the information for a contact and an address for a contact. Here's my code for when you submit the form:
[HttpPost]
public ActionResult开发者_Python百科 Edit(ContactsViewModel viewModel)
{
//if editing an contact, fetch it; otherwise, create a new one
Contact contact = viewModel.ContactId == 0
? new Contact()
: _adminRepository.GetContact(viewModel.ContactId);
TryUpdateModel(contact);
if (ModelState.IsValid)
{
_adminRepository.SaveAddress(contact.Address);
_adminRepository.SaveContact(contact);
return RedirectToAction("Index");
}
return View(viewModel); // validation error, so redisplay same view
}
Now my main concern is that the address is added successfully, but then an error occurs when I try to save the contact, leaving the address in the database with no contact.
What's the best practice here for rolling back any changes if an error occurs?
Well that should be one method, not two.
If an Address is always related to a Contact (it seems that way), then you should only have one repository - e.g a ContactRepository.
You should have one repository per aggregate root, or in simple terms - a repository for each entity that needs to be retrieved on it's own.
I don't know what _adminRepository
is, but i think you just just have a SaveContact(contact)
method.
Are you using an ORM like Entity Framework? If so, both inserts will be done in one transaction (handled by EF), so if one fails, both will fail.
If you start making changes against multiple repositories/aggregate roots, then you will need something like a Unit of Work, or a TransactionScope.
So it sounds like you need to rethink your repository design a little bit. There should be a generic Save method for a single aggregate entity (Contact), not seperate ones for each relation.
"Unit Of Work" patterns nice and accepted solution for multiple operations on db -or anothers operations also- please read sample from Faisal Mohamood, Program Manager, Entity Framework blogs.
You need to use TransactionScope() in your repository. Check out MSDN forum entry. First reply shows a simple example for integrating it into a method. The key is that without it there isn't a way to specifically rollback changes.
My preference would be to make a single stored procedure to insert both records. You can use TSQL transactions. Using a TransactionScope() would also work, so the decision should probably be based on your data access layer. If you're already using a lot of SPROCs, make make a complex one with a transaction.
If you're using Entity Framework (... the pain!!!!) then a .net TransactionScope() would be better.
精彩评论