views:

4874

answers:

4

I have an application that was written in vanilla ASP.NET that I would like to port over to ASP.NET MVC.

I, however, am confused about the right place to persist objects. I need to persist for a couple reasons:

  1. I would like all to have a single database connection, wrapped in a "repository" or "manager" style object.
  2. Each user has a user object that needs to be saved on a per-session basis.

Normally, I would say that #1 would be saved as a static item in the Globals.asax that can be hit using Global.Repository or similar.

And I would normally say that #2 should be a property with a session backing store somewhere in the base class of the pages.

Now the reason I am confused is that I have heard that sessions have changed in MVC, and the Global.asax no longer holds the same class. Also, the concept of pages has been removed, so adding a property to the base class of a controller seems... wrong.

What say yall?

+2  A: 

Sessions haven't changed at all in MVC. The GlobalApplication class in Global.asax still exists, too. Pages exist, also, that you would want to refer to a repository in a controller rather than a page. Adding a property to a base controller class is fine; I do it all the time.

Craig Stuntz
+3  A: 

Your database would go in a base class for your controllers. This base class should extend Controller, and all your controllers should extend the base class. Here's a little example:

public class BaseController : Controller
{
    private AuthServices _auth;
    private LogHelper _log;
    private Repository _repository;

    /// <summary>
    /// <see cref="AuthServices"/>
    /// </summary>
    protected AuthServices Authorization
    {
        get { return _auth ?? (_auth = new AuthServices()); }
    }

    /// <summary>
    /// <see cref="LogHelper"/>
    /// </summary>
    protected LogHelper Log
    {
        get { return _log ?? (_log = new LogHelper()); }
    }

    /// <summary>
    /// <see cref="Repository"/>
    /// </summary>
    protected Repository Repository
    {
        get { return _repository ?? (_repository = new Repository()); }
    }
}

Notice the lazy instantiation. That allows me to sneak in before running tests and set my private fields with mocks.

As for the session, your User object can still be saved in the session just as in a traditional ASP.NET application. Almost everything is still around (Response, Cache, Session, etc), but some of them have been wrapped with classes from System.Web.Abstractions so that they can be mocked for testing. They all still behave the same way, though you shouldn't use some of them in their traditional role (e.g., don't Response.Redirect, return a ViewResult that performs your redirection).

As for the reasoning behind your questions....

Don't stress on a single db connection. Depending on your implementation it may even be a bad idea, as requests may step on each other. Just open your connex, use it, and dispose/close it when done.

Also, one of the biggest changes that MVC brings is the rejection of the stateful model that traditional ASP.NET attempted to bring to web development. All that framework and viewstate doesn't exist anymore (pay no attention to the man behind the curtain). The less state you hold onto the less complex and more robust your web application is. Try it, you might like it.

Will
"Notice the lazy instantiation. That allows me to sneak in before running tests and set my private fields with mocks." Why not use IoC?
Todd Smith
Baby steps...... You should answer with an example of doing this.
Will
I actually use connection pooling. Anyways, thanks. This is almost exactly what I was planning, good to know I'm following fairly close to best-practice.
John Gietzen
+1  A: 

If you use sessions I would recommend having a session class, so that you only need to specify the string name once in code and this will give you IntelliSence also.

 public static class SessionHandler
{   
    // User Values
    private static string _userID = "UserID";
    private static string _userRole = "UserRole";

    public static string UserID
    {
        get
        {
            if (HttpContext.Current.Session[SessionHandler._userID] == null)
            { return string.Empty; }
            else
            { return HttpContext.Current.Session[SessionHandler._userID].ToString(); }
        }
        set
        { HttpContext.Current.Session[SessionHandler._userID] = value; }

    }

    public static string UserRole
    {
        get
        {
            if (HttpContext.Current.Session[SessionHandler._userRole] == null)
            { return string.Empty; }
            else
            { return HttpContext.Current.Session[SessionHandler._userRole].ToString(); }
        }
        set
        { HttpContext.Current.Session[SessionHandler._userRole] = value; }

    }
}
Tony Borf
I like this, but an instance would have to be created for each Controller. I would rather have all controllers in a session share session-based-objects, so I'm going to place them as properties in a BaseController class.
John Gietzen
A: 

You can create a model binder to encapsulate state.

(See Steve Sanderson's mvc book on his shopping cart implementation)

With the model binder, you have access to the controllerContext - which has the HttpContext.

Steve