1

I'm using ASP.NET MVC 4 and have a situation where Im saving some data in a MasterController because I want to pre-load some data in a cross-site nav bar.

abstract class MasterController : Controller
{
    FakeObject MyData { get; set; }

    if (this.MyData == null)
    {
        this.MyData = // do something crazy and load lots of data
    }
}

every controller inherits from the MasterController

class HomeController : MasterController
{
    ActionResults Index()
    {
        return View();
    }
}

The problem is that everytime there is a post back, MyData gets overwritten. I'm trying to find a way to store this data and retrieve it between PostBacks without using SessionState. Could this be done? Perhaps my use of the MasterController is incorrect.

I tried the following, but it doesn't work :(

TempData.Add("mydata", MyData);
TempData.Keep("mydata");

2 Answers 2

1

I'm not sure where your if(String.IsNullOrEmpty... is being used (I think you're missing a containing member; that's not valid code.

However, I think you may want to consider dependency injection and loose coupling, rather than hardcoding business logic in a controller. Here are two suggestions.

Option 1: Dependency Injection and TempData

public interface IFakeObjectFactory
{
      FakeObject Create();
}

public class FakeObjectFactory : IFakeObjectFactory
{
      public FakeObject Create()
      {
          // create FakeObject
      }
}

Then your controllers would look more like this:

public abstract class MasterController : Controller
{
    protected IFakeObjectFactory FakeObjectFactory { get; private set; }

    private FakeObject _myData;

    protected FakeObject EnsureMyData() 
    {
         if (_myData != null) return _myData;

          _myData = TempData["myData"] as FakeObject ?? FakeObjectFactory.Create();

          TempData["myData"] = _myData;
    }

    protected MasterController(IFakeObjectFactory fakeDataFactory)
    {
        FakeDataFactory = fakeDataFactory;
    }
}


public class HomeController : MasterController
{
    public HomeController(IFakeObjectFactory fakeObjectFactory)
      : base(fakeObjectFactory)
    { }

    ActionResults Index()
    {
        ViewBag.MyData = EnsureMyData();

        return View(); // you could also use MyData as the view model, rather than using ViewBag above. not sure what you need from here.
    }
}

Now, you can use an Inversion of Control container to create your controller and inject dependencies for you (some popular IoC are Ninject and Autofac). If you don't feel you are ready for that, you can always hardcode creating an instance of the factory in your EnsureMyData method, and get rid of the constructor parameters in HomeController and MasterController.

The benefit of using a factory interface and dependency injection is that you can create, test, and maintain the factory separately from your controller. You can also swap out implementations in the future if need be very easily.

Option 2: Use a static readonly field

Another option, if MyData is constant, is that you can make it a static member of MasterController, rather than using TempData.

public abstract MasterController : Controller
{
    public static readonly FakeData MyData;

    static MasterController()
    {
       // Initialize MyData
    }
}

public HomeController : MasterController
{
    public ActionResult Index()
    {
        ViewBag.MyData = MyData;

        View(); // you could also use MyData as the view model, rather than using ViewBag above. not sure what you need from here.
    }
}

This approach I do not recommend unless the logic is dead simple or you just don't care and want something quick and dirty. Testing singletons and static methods is a pain in the butt.

Sign up to request clarification or add additional context in comments.

5 Comments

sorry about that, i updated code. Was using a string before and decided to call it FakeObject but forgot to update dummy code.
agreed, IOC would be much better, but in this case, this is a tiny app for test purpose and putting in IOC would be overkill. I should have clarified that in my question. So my main goal is to just get it done quick and hard coding in this case is perfectly ok. In any case, you are absolutely correct in regards to IOC.
Sure, like I said, you can just eliminate the IFakeObjectFactory parameters on both controllers, and the property on MasterController, and just create the factory in a hardcoded fashion right inside EnsureMyData.
I added another possible solution.
After studying your answers carefully, they are very good; unfortunatly they don't solve my problem. I want to load data into site wide nav bar, I don't want to return it on every Controller method, I want it to be globally available so that every view can access the data without it being passed in, which is why the MasterController. This is why I wanted to use Session, but I'm trying to follow the MVC way and NOT use Session. Unfortunatly, it looks like I may have to use Session or Cache as indicated by another answer. Thank you much for your answer and investment of time in the code sample.
1

If this is basically static data, you can use the builtin cache.

abstract class MasterController : Controller
{
    public MasterController() { if (Cache["Foo"] == null) Cache["Foo"] = Something; }
}

class HomeController : MasterController
{
    ActionResults Index()
    {
        return View(Cache["Foo"]);
    }
}

3 Comments

Although in this case, using the Cache is better than using Session because I don't care about each user Session, the same data exists for all users. I was hoping to get this done using either TempData, ViewBag or ViewData and was hoping there is a way. I will keep the question open for a little more, in case someone has a more elegant solution. If not, I'll come back to mark the answer.
@Tomaszewski - FYI, TempData uses the session for it's storage, and it's unreliable, since once you access it, it's deleted. Thus, if a user refreshes the page, it's gone. As for ViewBag/ViewData, those are possible, but they're action method specific, so they're not so global in nature.
yes, I agree. Like I said above, I tried using TempData including the .Keep() method, but to no avail it wasn't working.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.