6

I have almost exactly the same scenario described by Nathon Taylor in ASP.NET MVC - Sharing Session State Between Controllers. The problem is that if I save the path to the images inside a Session variable List<string> it is not being defined back in the ItemController so all the paths are being lost... Here's my setup:

Inside ImageController I have the Upload() action method:

    public ActionResult Upload()
    {
        var newFile = System.Web.HttpContext.Current.Request.Files["Filedata"];
        string guid = Guid.NewGuid() + newFile.FileName;
        string itemImagesFolder = Server.MapPath(Url.Content("~/Content/ItemImages/"));
        string fileName = itemImagesFolder + "originals/" + guid;
        newFile.SaveAs(fileName);

        var resizePath = itemImagesFolder + "temp/";
        string finalPath;
        foreach (var dim in _dimensions)
        {
            var resizedPath = _imageService.ResizeImage(fileName, resizePath, dim.Width + (dim.Width * 10/100), guid);
            var bytes = _imageService.CropImage(resizedPath, dim.Width, dim.Height, 0, 0);
            finalPath = itemImagesFolder + dim.Title + "/" + guid;
            _imageService.SaveImage(bytes, finalPath);
        }
        AddToSession(guid);
        var returnPath = Url.Content("~/Content/ItemImages/150x150/" + guid);
        return Content(returnPath);
    }

    private void AddToSession(string fileName)
    {
        if(Session[SessionKeys.Images] == null)
        {
            var imageList = new List<string>();
            Session[SessionKeys.Images] = imageList;
        }
        ((List<string>)Session[SessionKeys.Images]).Add(fileName);
    }

Then inside my ItemController I have the New() action method which has the following code:

        List<string> imageNames;
        var images = new List<Image>();
        if (Session[SessionKeys.Images] != null) //always returns false
        {
            imageNames = Session[SessionKeys.Images] as List<string>;
            int rank = 1;
            foreach (var name in imageNames)
            {
                var img = new Image {Name = name, Rank = rank};
                images.Add(img);
                rank++;
            }
        }

Ok so why is this happening and how do I solve it?

Also, I was thinking of whether I could move the ActionMethod that takes care of the upload of the images into the ItemController and store the image paths inside a List property on the ItemController itself, would that actually work? Note though, that images are being uploaded and taken care of via an AJAX request. Then when the user submits the item entry form, all the data about the Item along with the images should be saved to the database...

Update:

I've updated the code. Also I think I should add that I'm using StructureMap as my controller factorory. Could it be a scoping issue? What is the default scope that is usually used by StructureMap?

public class StructureMapDependencyResolver : IDependencyResolver
{
    public StructureMapDependencyResolver(IContainer container)
    {
        _container = container;
    }

    public object GetService(Type serviceType)
    {
        if (serviceType.IsAbstract || serviceType.IsInterface)
        {
            return _container.TryGetInstance(serviceType);
        }
        else
        {
            return _container.GetInstance(serviceType);
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return _container.GetAllInstances<object>()

            .Where(s => s.GetType() == serviceType);
    }

    private readonly IContainer _container;
}

And inside my Global.asax file:

    private static IContainer ConfigureStructureMap()
    {
        ObjectFactory.Configure(x =>
        {
            x.For<IDatabaseFactory>().Use<EfDatabaseFactory>();
            x.For<IUnitOfWork>().Use<UnitOfWork>();
            x.For<IGenericMethodsRepository>().Use<GenericMethodsRepository>();
            x.For<IUserService>().Use<UsersManager>();
            x.For<IBiddingService>().Use<BiddingService>();
            x.For<ISearchService>().Use<SearchService>();
            x.For<IFaqService>().Use<FaqService>();
            x.For<IItemsService>().Use<ItemsService>();
            x.For<IMessagingService>().Use<MessagingService>();
            x.For<IStaticQueriesService>().Use<StaticQueriesService>();
            x.For < IImagesService<Image>>().Use<ImagesService>();
            x.For<ICommentingService>().Use<CommentingService>();
            x.For<ICategoryService>().Use<CategoryService>();
            x.For<IHelper>().Use<Helper>();
            x.For<HttpContext>().HttpContextScoped().Use(HttpContext.Current);

            x.For(typeof(Validator<>)).Use(typeof(NullValidator<>));

            x.For<Validator<Rating>>().Use<RatingValidator>();
            x.For<Validator<TopLevelCategory>>().Use<TopLevelCategoryValidator>();
        });

        Func<Type, IValidator> validatorFactory = type =>
        {
            var valType = typeof(Validator<>).MakeGenericType(type);
            return (IValidator)ObjectFactory.GetInstance(valType);
        };

        ObjectFactory.Configure(x => x.For<IValidationProvider>().Use(() => new ValidationProvider(validatorFactory)));
        return ObjectFactory.Container;
    }

Any thoughts?

Community
  • 1
  • 1
Kassem
  • 8,116
  • 17
  • 75
  • 116
  • can you add some more code? specifically your controllers where you are calling the AddToSession method ? This should not happen unless you are accessing Session from a different thread. – neebz Apr 30 '11 at 21:27
  • @nEEbz: I've updated the post. Check it out please. – Kassem May 01 '11 at 07:10

3 Answers3

14

I just added this to Global.asax.cs

   protected void Session_Start()
   {
   }

It seems that this fixed the issue. I set a breakpoint that gets hit only once per session (as expected).

3

One possible reason for this is that the application domain restarts between the first and the second actions and because session is stored in memory it will be lost. This could happen if you recompile the application between the two. Try putting a breakpoints in the Application_Start and Session_Start callbacks in Global.asax and see if they are called twice.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • @Darin Dimitrov: I'm not sure this is what's happening... I did what you said and here's what happened: When the app first started, the breakpoints in both callbacks got triggered. Then when I clicked the button that uploads the image, only the breakpoint inside the Session_Start callback got triggered. Then I uploaded another image (a user can upload several images before submitting the item) and no breakpoints got triggered. But eventually, the session variable was not defined inside the New() action method of the ItemController... What do you think? – Kassem May 01 '11 at 09:03
  • @Kassem, are cookies enabled in the browser? Can you inspect with FireBug if the cookie initially created when uploading the files is transmitted along with the request to the New() action? – Darin Dimitrov May 01 '11 at 09:06
  • @Darin Dimitrov: There was no cookie in the first place... In firebug the only cookies that showed up were the .ASPXAUTH and ASP.NET_SessionId cookies... – Kassem May 01 '11 at 09:23
  • @Kassem, so is the `ASP.NET_SessionId` cookie sent in the request to the New() action? Can you loop in the New() action through all session values? – Darin Dimitrov May 01 '11 at 10:02
  • @Darin Dimitrov: I tried that... but the Session is empty! The Count property is 0 and the Keys' count is also 0. What could be wrong? – Kassem May 01 '11 at 10:25
  • @Kassem, no more clues. I would start by progressively removing parts of the code until I am able to isolate the cause of the problem. Try doing this with two new actions which are very simple: the first stores something into the session and redirects to another which tries to fetch it from the session. – Darin Dimitrov May 01 '11 at 10:25
  • @Darin Dimitrov: I'm not really redirecting or anything. What I'm doing exactly is this: I have an Item entry form which is a wizard. The user fills in basic info on the first 2 pages of the wizard, on the third he gets to upload as many images as he wants which get processed and stored, then I save the paths (file names) in the session but the page does not refresh or anything, then finally the user clicks next which shows the next wizard page (hides a div, shows another) where they can click on Submit which calls the New() action method... – Kassem May 01 '11 at 10:31
  • @Kassem, yes I understand that. I suggested you to write two completely new actions in order to try to isolate the problem. – Darin Dimitrov May 01 '11 at 10:33
  • @Darin Dimitrov: I solved the problem, but not the session issue... Rather than saving the file names of the images inside the session, I saved them inside hidden input fields... Anyway, thanks for the help, I really appreciate it :) – Kassem May 01 '11 at 14:18
0

Are you ever using it other than accessing HttpContext.Current directly in your code? In other words, are there any places where you're injecting the HttpContext for the sake of mocking in unit tests?

If you're only accessing it directly in your methods, then there's no reason to have the entry x.For<HttpContext>().HttpContextScoped().Use(HttpContext.Current); in you application startup. I wonder if it would start working if you removed it.

ataddeini
  • 4,931
  • 26
  • 34