Most people will suggest using the "Repository Pattern" to move that data access code out of the controller (and to enable unit testing with mock objects instead of the real database).
Here are some places to read more:
Edit:
I highly recommend reading the entire Scott Guthrie chapter linked above. It has a wealth of good advice in it. That said, here are some relevant examples (excepted from the chapter)...
First, I generally like to have different actions for "Update" vs. "Add". Even if they are the same View to render the form, it generally feels cleaner to have different URLs for POSTing an edit vs POSTing a new record. So, here is what the repository pattern in use looks like in a controller's update action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues)
{
//get the current object from the database using the repository class
Dinner dinner = dinnerRepository.GetDinner(id);
try
{
//update the object with the values submitted
UpdateModel(dinner);
//save the changes
dinnerRepository.Save();
//redirect the user back to the read-only action for what they just edited
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
catch
{
//exception occurred, probably from UpdateModel, so handle the validation errors
// (read the full chapter to learn what this extention method is)
ModelState.AddRuleViolations(dinner.GetRuleViolations());
//render a view that re-shows the form with the validation rules shown
return View(dinner);
}
}
Here is the "Add" example:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create()
{
//create a new empty object
Dinner dinner = new Dinner();
try
{
//populate it with the values submitted
UpdateModel(dinner);
//add it to the database
dinnerRepository.Add(dinner);
//save the changes
dinnerRepository.Save();
//redirect the user back to the read-only action for what they just added
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
catch
{
//exception occurred, probably from UpdateModel, so handle the validation errors
// (read the full chapter to learn what this extention method is)
ModelState.AddRuleViolations(dinner.GetRuleViolations());
//render a view that re-shows the form with the validation rules shown
return View(dinner);
}
}
For both examples above, the DinnerRepository looks like this:
public class DinnerRepository
{
private NerdDinnerDataContext db = new NerdDinnerDataContext();
//
// Query Methods
public IQueryable<Dinner> FindAllDinners()
{
return db.Dinners;
}
public IQueryable<Dinner> FindUpcomingDinners()
{
return from dinner in db.Dinners
where dinner.EventDate > DateTime.Now
orderby dinner.EventDate
select dinner;
}
public Dinner GetDinner(int id)
{
return db.Dinners.SingleOrDefault(d => d.DinnerID == id);
}
//
// Insert/Delete Methods
public void Add(Dinner dinner)
{
db.Dinners.InsertOnSubmit(dinner);
}
public void Delete(Dinner dinner)
{
db.RSVPs.DeleteAllOnSubmit(dinner.RSVPs);
db.Dinners.DeleteOnSubmit(dinner);
}
//
// Persistence
public void Save()
{
db.SubmitChanges();
}
}