41

I am trying to use TempData in asp.net core However I am getting a null value on the get method of TempData. Can anyone please let me know how can I use TempData in asp.net core

Below are the things I, have added as per the research.

Project.json file

{
  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.1",
      "type": "platform"
    },
    "Microsoft.AspNetCore.Mvc": "1.0.1",
    "Microsoft.AspNetCore.Routing": "1.0.1",
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.1",
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
    "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
    "Microsoft.Extensions.Configuration.Json": "1.0.0",
    "Microsoft.Extensions.Logging": "1.1.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0",
    "Microsoft.Extensions.Logging.Debug": "1.0.0",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
    "Microsoft.EntityFrameworkCore.SqlServer": "1.1.0",
    "Microsoft.EntityFrameworkCore.Tools": "1.1.0-preview4-final",
    "Microsoft.EntityFrameworkCore.Design": "1.1.0",
    "Microsoft.EntityFrameworkCore.SqlServer.Design": "1.1.0",
    "DataBase": "1.0.0-*",
    "UnitOfWork": "1.0.0-*",
    "ViewModel": "1.0.0-*",
    "Common": "1.0.0-*",
    "System.IdentityModel.Tokens.Jwt": "5.0.0",
    "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0",
    "Microsoft.AspNetCore.Diagnostics": "1.0.0",
    "Microsoft.AspNetCore.StaticFiles": "1.0.0",
    "Microsoft.AspNetCore.Session": "1.1.0",
    "Microsoft.Extensions.Caching.Memory": "1.1.0"
  },

  "tools": {
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final",
    "Microsoft.EntityFrameworkCore.Tools.DotNet": "1.0.0-preview3-final",
    "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final"
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dotnet5.6",
        "portable-net45+win8"
      ]
    }
  },

  "buildOptions": {
    "emitEntryPoint": true,
    "preserveCompilationContext": true
  },

  "runtimeOptions": {
    "configProperties": {
      "System.GC.Server": true
    }
  },

  "publishOptions": {
    "include": [
      "wwwroot",
      "**/*.cshtml",
      "appsettings.json",
      "web.config"
    ]
  },

  "scripts": {
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
  }
}

start.cs file

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    services.AddSession();
    // Add framework services.
    services.AddMvc();
    services.AddTransient<IMarketUOW, MarketUow>();
    services.AddTransient<ICategoryUow, CategoryUow>();
    services.AddTransient<IUserProfileUow, UserProfileUow>();
    services.AddTransient<IItemUow, ItemUow>();

    services.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>));
    var connection = Configuration.GetConnectionString("DefaultConnection");
    services.AddDbContext<EmakitiContext>(options => options.UseSqlServer(connection));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseSession();
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
        });
    }

Here is the implementation of tempdata.When this method is called I can see the value in TempData.

[HttpGet("{pageNumber}")]
public GenericResponseObject<List<MarketViewModel>> GetMarketList(int pageNumber)
{
    TempData["Currentpage"] = pageNumber;
    TempData.Keep("Currentpage");
    GenericResponseObject<List<MarketViewModel>> genericResponseObject = new GenericResponseObject<List<MarketViewModel>>();
    genericResponseObject.IsSuccess = false;
    genericResponseObject.Message = ConstaintStingValue.Tag_ConnectionFailed;
    try
    {
        var marketItem = _iMarketUow.GetMarketList(pageNumber);
        genericResponseObject.Data = marketItem.Item1;
        var totalPages = (int)Math.Ceiling((decimal)marketItem.Item2 / (decimal)10);
        genericResponseObject.TotalPage = totalPages;
        genericResponseObject.IsSuccess = true;
        genericResponseObject.Message = ConstaintStingValue.Tag_SuccessMessageRecord;
        genericResponseObject.Message = ConstaintStingValue.Tag_ConnectionSuccess;
    }
    catch (Exception exception)
    {
        genericResponseObject.IsSuccess = false;
        genericResponseObject.Message = exception.Message;
        genericResponseObject.ErrorCode = exception.HResult;
        genericResponseObject.ExceptionErrorMessage = exception.StackTrace;
    }
    return genericResponseObject;
}

But the below method has null value in the temp data.

[HttpPost]
public GenericResponseObject<List<MarketViewModel>> AddUpdateMarket([FromBody] MarketViewModel marketViewModel)
{
    GenericResponseObject<List<MarketViewModel>> genericResponseObject = new GenericResponseObject<List<MarketViewModel>>();
    genericResponseObject.IsSuccess = false;
    genericResponseObject.Message = ConstaintStingValue.Tag_ConnectionFailed;

    if (marketViewModel!= null && ModelState.IsValid)
    {
        try
        {
            _iMarketUow.AddUpdateMarketList(marketViewModel);
            genericResponseObject = GetMarketList(Convert.ToInt16(TempData.Peek("Currentpage")));
        }
        catch (Exception exception)
        {
            genericResponseObject.IsSuccess = false;
            genericResponseObject.Message = exception.Message;
            genericResponseObject.ErrorCode = exception.HResult;
            genericResponseObject.ExceptionErrorMessage = exception.StackTrace;
        }
    }
    else
    {
        genericResponseObject.Message = ConstaintStingValue.Tag_InputDataFormatNotMatch;
    }
    return genericResponseObject;
}

Here is the image of the debug session

first request of http which hold the value to the tempdata

null value on the second request

Muhammed Shevil KP
  • 1,404
  • 1
  • 16
  • 21
San Jaisy
  • 15,327
  • 34
  • 171
  • 290

3 Answers3

83

Edited:

As Described in https://learn.microsoft.com/en-us/aspnet/core/security/gdpr?view=aspnetcore-2.2#tempdata-provider-and-session-state-cookies-arent-essential by default TempData cookies are removed by the CookiePolicy Middleware. this can be changed by putting this in Startup.ConfigureServices():

// The TempData provider cookie is not essential. Make it essential
// so TempData is functional when tracking is disabled.
services.Configure<CookieTempDataProviderOptions>(options => {
    options.Cookie.IsEssential = true;
}); 

=============================================

Old Answer:

After Migrating to ASP Core 2.1 I had this issue and after working for a day find the solution:

in Startup.Configure() app.UseCookiePolicy(); should be after app.UseMVC();

HamedH
  • 2,814
  • 1
  • 26
  • 37
  • I think there is a lake of documentation here. I have created an issue about it: https://github.com/aspnet/Docs/issues/8041 – HamedH Aug 17 '18 at 08:39
  • This was also the reason why recovery codes aren't shown when using ASP.NET Core Identity and you have 2 Factor Authentication enabled. – ZippyV Sep 07 '18 at 14:12
  • 2
    *Thank you for this!* This issue is made all the more confusing, because the session state docs (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-2.1) for netcore 2.1 and 2.2 show `UseCookiePolicy()` being configured _before_ `UseMVC()`, which is evidently incorrect. – Katstevens Dec 30 '18 at 05:32
  • Thank you! Saved me a day of research, I was having the same problem and just doing this fix it. – franklores Jan 22 '19 at 10:42
  • 7
    @Katstevens nothing's wrong with the order. check my answer below. – Nugi Feb 21 '19 at 18:36
  • On ASP.NET Core 3.1 (not sure about 3, I tested on 3.1 only), you will need to add `services.AddDistributedMemoryCache();` in `ConfigureServices` as well – Ryan Teh Apr 25 '20 at 17:15
46

Nothing's wrong with middleware order as described on official docs, which is:

  1. Exception/error handling
  2. HTTP Strict Transport Security Protocol
  3. HTTPS redirection
  4. Static file server
  5. Cookie policy enforcement
  6. Authentication
  7. Session
  8. MVC

But when we use Cookie policy enforcement (UseCookiePolicy), only essential cookie that will be sent to the browser and Cookie from Tempdata provider is not essential hence the problem. So we have to make it essential according to official documentation:

The Tempdata provider cookie isn't essential. If tracking is disabled, the Tempdata provider isn't functional. To enable the Tempdata provider when tracking is disabled, mark the TempData cookie as essential in Startup.ConfigureServices

// The Tempdata provider cookie is not essential. Make it essential
// so Tempdata is functional when tracking is disabled.
services.Configure<CookieTempDataProviderOptions>(options => {
   options.Cookie.IsEssential = true;
});

Adding those lines should solve your issue without reordering middleware.

Nugi
  • 862
  • 6
  • 11
  • 8
    This is the right answer and should be used (not changing the order of services) – Tony Basallo May 16 '19 at 17:31
  • 3
    This is the 100% true and correct answer - I ended up deleting my tempdata cookie while debugging another issue and suddenly tempdata stopped working completely! An hour later I found this and now the issue is resolved. – Iofacture Jun 08 '19 at 05:54
  • After a couple hours of trying to figure out why Tempdata was null...this worked. – John81 Jan 17 '20 at 14:48
  • Thank you!. I Spent a significant amount of time debugging this issue. Tempdata was working with vanilla return Views(), but any return RedirectToAction() calls would result in tempdata being empty. This solved the issue ( .net core 3.1 ) – Sleepless Ninja May 21 '20 at 15:32
20

Please upgrade your all packages to same version 1.1.0 add caching service as well. Here is the required things for using TempData in Asp.Net Core

Project.json

"Microsoft.AspNetCore.Session": "1.1.0"

Here is the Startup.cs file. - ConfigureServices method

public void ConfigureServices(IServiceCollection services)
{
     services.AddMemoryCache();
     services.AddSession();
     services.AddMvc();
}

And Configure method.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseSession();
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Now try with TempData, it will work.

And you can set the environment with set ASPNETCORE_ENVIRONMENT=Development environment variable.

PS

ASP.NET Core MVC exposes the TempData property on a Controller. TempData can be used for storing transient data that only needs to be available for a single request after the current request.

When an object in a TempDataDictionary is read, it will be marked for deletion at the end of that request.

The Peek and Keep methods allow you to read the value without marking it for deletion. Say we get back to the first request where the value was saved to TempData.

With Peek you get the value without marking it for deletion with a single call.

//second request, PEEK value so it is not deleted at the end of the request
object value = TempData.Peek("value");

//third request, read value and mark it for deletion
object value = TempData["value"];

With Keep you specify a key that was marked for deletion that you want to keep. Retrieving the object and later on saving it from deletion are 2 different calls.

//second request, get value marking it from deletion
object value = TempData["value"];
//later on decide to keep it
TempData.Keep("value");

//third request, read value and mark it for deletion
object value = TempData["value"];
Ahmar
  • 3,717
  • 2
  • 24
  • 42