views:

731

answers:

3

Hi All,

I have an Action method in an ASP.NET MVC Controller class that handles form posts from a fairly basic "create/edit a user" page. I'm new to MVC so I've been following code samples from various Microsoft tutorials, this is how the method looks currently:

[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Save([Bind(Prefix = "ServiceUser")]ServiceUser SUser)
{
        if (SUser.ServiceUserId == 0) //new service user
            ServiceUserHelper.AddServiceUser(SUser);
        else //update to existing service user
        {
            using (ProjectDataContext db = DatabaseHelper.CreateContext())
            {
                this.UpdateModel(db.ServiceUsers.Single(su => su.ServiceUserId == SUser.ServiceUserId), "ServiceUser");
                db.SubmitChanges();
            }
        }

        //return a confirmation view
}

This works fine; however my instincts tell me that the 'ProjectDataContext...' code doesn't belong in the controller. If I were to move the Update functionality to an other class (in the way I have done with the Insert method), I'd lose the convenience of the Controller's UpdateModel() method, and probably end up having to do something quite verbose to read the existing entity, update its properties, and submit the changes.

So my question is, what is the best way to achieve this? Is there a method similar to UpdateModel() somewhere in LINQ that can merge two entities of the same type together before submitting?

Thanks.

+3  A: 
Erv Walter
I've taken a look at Rob Conery's storefront app but I can't find any examples of how to implement what I'm looking for specifically; there's alot of video there to wade through. I just need an example of how to implement the Update functionality I mentioned outside of the controller.
Lee D
Excellent, thanks!
Lee D
do yourself a favour and "wade thru" RobCon's stuff. all of it. even if you dont choose to adopt his more 'higher level' archecture (vs the Nerdinner example) you will get a better complete feel for MVC. for the record, i used Robs approach - seperate Service layer, and OO domain model (rather than using the LINQ classes).
cottsak
By moving your GetDinner and SaveDinner to a service (another level of indirection) you can remove the need for data access in the controller. This is arguably more testable and removes the data access responsibility to the service, even though it may seem like only a thin facade when you are writing it.
Liam
A: 

I agree with Lee D I've been looking for something as well. I did a similar thing with reflections early in the MVC previews that was used in the model and not the controller. It wasn't the best code and thought that something would be added to MVC final. Not something stuck in the controller. It would be ideal to pass form, or model if using strongly typed views, through the controller into the model and let all the validation and data moving be done there. Now even a pooryly done controller can't hose the data.

A: 

You currently have what I would call a 2 layer architecture which includes your MVC application layer (ie Controllers) and your data access layer.

You probably want to move to a 3 or 4 layer architecture by inserting a service layer between your Controllers and DAL. So you would end up with:

Controllers -> Services -> DAL

A 4 layer architecture might include a repository layer

Controllers -> Services -> Repository -> DAL

Your controllers would only be responsible for 3 things

1) argument handling 2) calling your service layer to do work 2) application flow

Your above example might look something like:

[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Save([Bind(Prefix = "ServiceUser")]ServiceUser SUser)
{
        // validate arguments
        if (SUser == null)
        {
              throw new ArgumentException("SUser can not be null");
        }

        // process form fields / query params / etc.
        this.TryUpdateModel(SUser, "ServiceUser");

        // update your model 
        // (toss in a try/catch to deal with validation errors etc)
        _userService.Save(SUser);

        //return a confirmation view
}

Then your service layer would be responsible for doing the actual work:

  public class UserService : IUserService
   {
       public void Save(ServiceUser SUser)
       {
            // insert or update user info

            if (SUser.ServiceUserId == 0) //new service user
                ServiceUserHelper.AddServiceUser(SUser);
            else //update to existing service user
            {
                using (ProjectDataContext db = DatabaseHelper.CreateContext())
                {
                    db.ServiceUsers.Single(su => su.ServiceUserId == 
                                           SUser.ServiceUserId);
                    db.SubmitChanges();
                }
            }
       }
   }
Todd Smith