Я разрабатываю приложение ASP.NET Core 8 и использую шаблон Singleton для управления сеансами, но когда я хочу прочитать значения, оно отображает нулевые значения!
Здесь вы можете увидеть код:
SessionSingleton.cs
:
[Serializable]
public sealed class SessionSingleton : IDisposable
{
#region Private Members
private const string SessionSingletonName = "SessionKey_502E69E5-668B-E011-951F-00155DF26207";
private static readonly object LockObject = new();
#endregion
#region Singleton
private SessionSingleton()
{
}
public static SessionSingleton Current
{
get
{
lock (LockObject)
{
string? value;
SessionSingleton sessionSingleton;
var session = CustomHttpContext.Current.Session;
if (session == null)
return null;
if (session.GetString(SessionSingletonName) is not null)
{
value = session.GetString(SessionSingletonName);
sessionSingleton = JsonSerializer.Deserialize<SessionSingleton>(value!)!;
}
else
{
sessionSingleton = new SessionSingleton();
var sessionText = JsonSerializer.Serialize(sessionSingleton);
session.SetString(SessionSingletonName, sessionText);
}
return sessionSingleton;
}
}
}
#endregion
#region Public properties
public string Name { get; set; }
#endregion
#region Dctor
public void Dispose()
{
// HttpContext.Current.Session[SessionSingletonName] = null;
// HttpContext.Current.Session.Remove(SessionSingletonName);
}
#endregion
}
CustomHttpContext
:
public static class CustomHttpContext
{
private static IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor();
public static HttpContext? Current
{
get
{
if (_httpContextAccessor == null)
_httpContextAccessor = new HttpContextAccessor();
return _httpContextAccessor.HttpContext;
}
}
}
HomeController
:
public partial class HomeController : BaseController
{
public IActionResult Index()
{
SessionSingleton.Current.Name = "Test";
var value = SessionSingleton.Current.Name; // value is always null
SessionSingleton.Current.Name = "Test2";
value = SessionSingleton.Current.Name;// value is always null
return Ok(true);
}
}
Весь код, такой как сериализация и десериализация, работает нормально, но почему в HomeController
значение всегда равно нулю?
Пожалуйста, предложите способ хранения информации пользователя и обеспечения доступа к этой информации просто во время выполнения программы пользователем.
Потому что я хочу прочитать его значение из сеанса, а с помощью этого метода это невозможно.
Прежде всего рассмотрите возможность использования инструментов, предоставляемых ASP.NET Core, для управления сеансом — см. документацию Управление сеансами и состояниями в ASP.NET Core.
Что касается использования IHttpContextAccessor
, то это не так. Вам следует избегать использования его по умолчанию — он предназначен для весьма специфических случаев использования (и требует некоторых затрат производительности). IHttpContextAccessor
обрабатывается DI и обрабатывается самим ASP.NET Core. Вам (возможно) потребуется зарегистрировать его в DI:
builder.Services.AddHttpContextAccessor();
А потом решать там, где это необходимо. См. раздел Доступ к HttpContext из пользовательских компонентов документации.
Но в случае действия контроллера это не требуется, просто используйте this.HttpContext
для доступа к текущему контексту.
Я использую паттерн Singleton, по этой причине документация Microsoft у меня не работает. Кроме того, из-за использования шаблона Singleton и того, что это статический класс, невозможно внедрить HttpContextAccessor в класс SessionSingleton.
@MohamadGhafari «Я использую шаблон Singleton» 1) почему? Возможно, вам не следует 2) даже если вы хотите/нужно его использовать - используйте синглтоны через DI (т. е. через AddSingleton
)
Потому что я хочу прочитать его значение из сеанса, а с помощью этого метода это невозможно.
@MohamadGhafari в целом вам не следует использовать шаблон Singleton в современном ASP.NET Core. Почему именно вам нужно/хотите использовать это здесь?
Потому что я хочу просто и понятно использовать свойства класса Singleton. Также участвует в сложностях использования HttpContext.Session.SetString() и HttpContext.Session.GetString(). Если у вас есть лучшее предложение, пожалуйста, помогите мне.
Наконец, после различных проверок я изменил свой код, как показано ниже, и это хорошо решило мою проблему.
После многих исследований я понял, что по какой-либо причине использование комбинации Singleton с HttpContextAccessor
не приведет к желаемому результату. (Возможно, из-за логики внутреннего промежуточного программного обеспечения asp.net)
По этой причине я попытался удовлетворить свои потребности с помощью чистого и расширяемого кода.
Поэтому я использовал интерфейс и поместил в него свои поля, чтобы можно было легко его реализовать, а также использовать функцию внедрения зависимостей.
ISessionHandler
public interface ISessionHandler : IDisposable
{
public int UserId { get; set; }
public string Name { get; set; }
}
Менеджер сеансов
internal sealed class SessionManager : ISessionHandler
{
#region Private Members
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ISession _session;
#endregion
#region Ctor
public SessionManager(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
_session = _httpContextAccessor.HttpContext?.Session!;
}
#endregion
#region Dctor
public void Dispose()
{
_session.Clear();
}
#endregion
public int UserId
{
get => _session.Get<int>();
set => _session.Set(value);
}
public string Name
{
get => _session.Get<string>();
set => _session.Set(value);
}
}
Затем я создал статический вспомогательный класс для установки и получения информации о сеансе.
Расширение сеанса
internal static class SessionExtension
{
public static void Set<T>(this ISession session, string key, T value)
{
session.SetString(key, JsonSerializer.Serialize(value));
}
public static T Get<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default! : JsonSerializer.Deserialize<T>(value)!;
}
public static void Set<T>(this ISession session, T value)
{
var key = GetKey();
session.SetString(key, JsonSerializer.Serialize(value));
}
public static T Get<T>(this ISession session)
{
var key = GetKey();
var value = session.GetString(key);
return value == null ? default! : JsonSerializer.Deserialize<T>(value)!;
}
private static string GetKey()
{
var frame = new StackTrace().GetFrame(2);
var instanceName = frame?.GetMethod()?.DeclaringType?.Name;
var methodName = frame?.GetMethod()?.Name;
var key = $"{instanceName}_{methodName}";
return key.Replace("_set", "").Replace("_get", "");
}
}
программа.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<ISessionHandler, SessionManager>();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.Cookie.Name = ".Test.Session";
options.IdleTimeout = TimeSpan.FromMinutes(30);
options.Cookie.IsEssential = true;
});
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseSession();
app.UseRequestLocalization();
app.MapDefaultControllerRoute();
app.Run();
И, наконец, как показано в коде ниже, я использовал его в своем контроллере и там, где это было необходимо.
Логинконтроллер
public partial class LoginController : BaseController
{
public LoginController(ISessionHandler sessionHandler)
{
}
public IActionResult Index()
{
SessionHandler.Name = "Test";
var name = SessionHandler.Name; // It's OK
return Ok();
}
}
Благодарим вас за вклад в сообщество Stack Overflow. Возможно, это правильный ответ, но было бы очень полезно предоставить дополнительные пояснения к вашему коду, чтобы разработчики могли понять ваши рассуждения. Это особенно полезно для новых разработчиков, которые не так хорошо знакомы с синтаксисом или пытаются понять концепции. Не могли бы вы отредактировать свой ответ, включив в него дополнительную информацию на благо сообщества?
Прежде всего, я не думаю, что вы должны создавать экземпляр HttpContextAccessor, вызывая new, а скорее получать его из DI. Во-вторых, почему вы не используете стандартный механизм внедрения зависимостей, который уже предоставляет ASP.NET Core? Эту ненужную часть о блокировке синглтона можно решить одной строкой. В-третьих: какова цель этого кода?