.NET Core中Session复杂对象仅修改时序列化保存的实现方法
解决.NET Core Session复杂对象仅修改时序列化保存的问题
嘿,我太懂你这种刚接触.NET Core Session的困惑了!从传统ASP.NET转过来的时候,我也吐槽过为啥好好的自动追踪没了,非得手动序列化存来存去的。其实原因很简单:.NET Core的Session设计更轻量、更灵活,默认是基于字节流存储的,不像老ASP.NET那样靠引用追踪自动感知对象变化。不过咱完全可以自己实现类似的“仅修改时保存”的效果,下面给你几种实用的方案:
方案一:用包装类+脏标记追踪变化
核心思路是给你的复杂对象套一层带IsDirty标记的包装类,修改对象时自动标记为“脏”,然后在请求结束时统一检查并保存脏对象。
1. 定义通用包装类
public class SessionTrackedObject<T> { private T _value; public T Value { get => _value; set { _value = value; IsDirty = true; // 修改时自动标记为脏状态 } } public bool IsDirty { get; set; } = false; public SessionTrackedObject(T initialValue) { _value = initialValue; } }
2. 实现自动保存的过滤器
用ActionFilter在请求执行完成后,统一处理Session里的脏对象:
public class AutoSaveSessionFilter : IAsyncActionFilter { private readonly IHttpContextAccessor _httpContextAccessor; public AutoSaveSessionFilter(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // 先执行控制器的业务逻辑 var resultContext = await next(); // 遍历Session所有键,检查并保存脏对象 var session = _httpContextAccessor.HttpContext.Session; foreach (var key in session.Keys.ToList()) // 转成List避免遍历中修改集合引发异常 { var json = session.GetString(key); if (string.IsNullOrEmpty(json)) continue; var trackedObj = JsonSerializer.Deserialize<SessionTrackedObject<object>>(json); if (trackedObj?.IsDirty == true) { // 重新序列化并保存 session.SetString(key, JsonSerializer.Serialize(trackedObj)); // 重置脏标记,避免下次重复保存 trackedObj.IsDirty = false; } } } }
3. 注册过滤器和必要服务
在Program.cs中配置MVC和相关服务:
builder.Services.AddControllersWithViews(options => { options.Filters.Add<AutoSaveSessionFilter>(); }); // 注册HttpContextAccessor,用于过滤器中获取Session builder.Services.AddHttpContextAccessor();
4. 实际使用示例
// 从Session加载对象,不存在则初始化 var trackedUser = HttpContext.Session.GetString("CurrentUser") != null ? JsonSerializer.Deserialize<SessionTrackedObject<User>>(HttpContext.Session.GetString("CurrentUser")) : new SessionTrackedObject<User>(new User()); // 修改对象——赋值时自动标记为脏 trackedUser.Value.UserName = "UpdatedNickname"; trackedUser.Value.Age = 32; // 无需手动调用Session.SetString!过滤器会自动保存脏对象
方案二:封装扩展方法简化操作
如果觉得包装类的写法有点繁琐,可以封装一组Session扩展方法,把加载、修改、标记脏的逻辑整合在一起:
public static class SessionExtensions { // 从Session加载追踪对象 public static T GetTrackedObject<T>(this ISession session, string key) where T : new() { var json = session.GetString(key); if (json == null) return new T(); var wrapper = JsonSerializer.Deserialize<SessionTrackedObject<T>>(json); return wrapper?.Value ?? new T(); } // 保存追踪对象并标记为脏 public static void SetTrackedObject<T>(this ISession session, string key, T value) { var wrapper = new SessionTrackedObject<T>(value) { IsDirty = true }; session.SetString(key, JsonSerializer.Serialize(wrapper)); } // 直接更新对象并自动标记脏 public static void UpdateTrackedObject<T>(this ISession session, string key, Action<T> updateAction) where T : new() { var obj = session.GetTrackedObject<T>(key); updateAction(obj); session.SetTrackedObject(key, obj); } }
使用时会更简洁:
// 直接更新用户信息,自动处理脏标记和后续保存 HttpContext.Session.UpdateTrackedObject("CurrentUser", user => { user.UserName = "NewName"; user.Email = "new@example.com"; });
额外小贴士
- 如果你的复杂对象本身实现了
INotifyPropertyChanged接口,可以结合这个接口更精准地标记脏状态,而不是每次赋值都标记。 - 序列化方式可以根据需求替换,比如用Newtonsoft.Json代替默认的System.Text.Json,只需要修改序列化/反序列化的代码即可。
总的来说,虽然.NET Core没有原生提供ASP.NET那种自动Session状态追踪,但通过这些小封装,完全能实现“仅修改时保存”的效果,既减少了不必要的序列化开销,又能贴近你习惯的开发方式。
内容的提问来源于stack exchange,提问作者mirko federico
相关产品推荐
相关产品推荐

