【问题标题】:Save complex object to session ASP .NET CORE 2.0将复杂对象保存到会话 ASP .NET CORE 2.0
【发布时间】:2020-01-11 15:56:01
【问题描述】:

我对 ASP .NET 核心很陌生,所以请帮忙。我想避免 ASP .NET 核心应用程序的数据库往返。我有在数据网格中动态添加列的功能。列设置(可见性、启用、宽度、标题)存储在 DB 中。

所以我想存储 List 在服务器上 仅用于实际会话。但我无法做到这一点。我已经使用 JsonConvert 方法将对象序列化和反序列化到/从会话。这适用于 List 或具有简单属性的对象,但不适用于具有嵌套属性的复杂对象。

我想要存储到会话的对象如下所示:

[Serializable]
  public class PersonColumns
  {
    public Int64 PersonId { get; set; }
    List<ViewPersonColumns> PersonCols { get; set; }

    public PersonColumns(Int64 personId)
    {
      this.PersonId = personId;
    }

    public void LoadPersonColumns(dbContext dbContext)
    {
      LoadPersonColumns(dbContext, null);
    }

    public void LoadPersonColumns(dbContext dbContext, string code)
    {
      PersonCols = ViewPersonColumns.GetPersonColumns(dbContext, code, PersonId);
    } 

    public static List<ViewPersonColumns> GetFormViewColumns(SatisDbContext dbContext, string code, Int64 formId, string viewName, Int64  personId)
    {
      var columns = ViewPersonColumns.GetPersonColumns(dbContext, code, personId);

      return columns.Where(p => p.FormId == formId && p.ObjectName == viewName).ToList();
    }


  }

我还想问一下,将 600 条记录的列表保存到会话中是否还不错?每次用户想要显示网格时访问数据库和加载列会更好吗?

任何建议表示赞赏

谢谢

EDIT:我已测试存储在会话 List 中,并且已正确保存。当我保存 List 为属性的对象时,仅保存 内置类型,List 属性为空。

我要保存在会话中的对象

[Serializable]
  public class UserManagement
  {
    public String PersonUserName { get; set; }
    public Int64 PersonId { get; set; }
    public List<ViewPersonColumns> PersonColumns { get; set; } //not saved to session??

    public UserManagement() { }

    public UserManagement(DbContext dbContext, string userName)
    {
      var person = dbContext.Person.Single(p => p.UserName == userName);

      PersonUserName = person.UserName;
      PersonId = person.Id;
    }

    /*public void PrepareUserData(DbContext dbContext)
    {
      LoadPersonColumns(dbContext);
    }*/

    public void LoadPersonColumns(DbContext dbContext)
    {
      LoadPersonColumns(dbContext, null);
    }

    public void LoadPersonColumns(DbContext dbContext, string code)
    {
      PersonColumns = ViewPersonColumns.GetPersonColumns(dbContext, code, PersonId);
    }

    public List<ViewPersonColumns> GetFormViewColumns(Int64 formId, string viewName)
    {
      if (PersonColumns == null)
        return null;

      return PersonColumns.Where(p => p.FormId == formId && p.ObjectName == viewName).ToList();
    }

  }

将列保存到会话中

UserManagement userManagement = new UserManagement(_context, user.UserName);
userManagement.LoadPersonColumns(_context);
HttpContext.Session.SetObject("ActualPersonContext", userManagement);
HttpContext.Session.SetObject("ActualPersonColumns", userManagement.PersonColumns);

从会话中加载列

//userManagement build-in types are set. The PersonColumns is null - not correct
UserManagement userManagement = session.GetObject<UserManagement>("ActualPersonContext");
//The cols is filled from session with 600 records - correct
List<ViewPersonColumns> cols = session.GetObject<List<ViewPersonColumns>>("ActualPersonColumns");

【问题讨论】:

    标签: asp.net-core


    【解决方案1】:

    每列使用列表优于使用数据库。 您无法在 .net 核心中创建和存储会话,例如 .net framework 4.0

    这样试试

    Startup.cs

     public void ConfigureServices(IServiceCollection services)
            {
                //services.AddDbContext<GeneralDBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));        
    
                services.AddMvc().AddSessionStateTempDataProvider();
                services.AddSession();
            }

    Common/SessionExtensions.cs

    sing Microsoft.AspNetCore.Http;
    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace IMAPApplication.Common
    {
        public static class SessionExtensions
        {
            public static T GetComplexData<T>(this ISession session, string key)
            {
                var data = session.GetString(key);
                if (data == null)
                {
                    return default(T);
                }
                return JsonConvert.DeserializeObject<T>(data);
            }
    
            public static void SetComplexData(this ISession session, string key, object value)
            {
                session.SetString(key, JsonConvert.SerializeObject(value));
            }
        }
    }

    用法

    • ==> 创建会话*

    public IActionResult Login([FromBody]LoginViewModel model)
            {               
              LoggedUserVM user = GetUserDataById(model.userId);   
              
              //Create Session with complex object   
              HttpContext.Session.SetComplexData("loggerUser", user);         
                    
              return Json(new { status = result.Status, message = result.Message });              
    
            }
    • ==> 获取会话数据*

     public IActionResult Index()
            {
                //Get Session data     
                LoggedUserVM loggedUser = HttpContext.Session.GetComplexData<LoggedUserVM>("loggerUser");                         
            }

    希望这会有所帮助。祝你好运。

    【讨论】:

    • 感谢您的建议。我觉得我几乎一样,唯一的区别是我没有使用 services.AddMvc().AddSessionStateTempDataProvider();我使用了 services.AddDistributedMemoryCache。其实不知道有什么区别。但是,如果您可以看到我的编辑,我会以某种方式使其工作,但它比解决方案更适合我:) 因为我只需要一个会话密钥用于整个用户对象。
    • 默认情况下,TempData 专门使用 cookie 将日期保存到下一个请求。添加AddSessionStateTempDataProvider,使用Session 来处理TempData。它与你在这里发生的事情没有任何关系。出于同样的原因,AddDistributedMemoryCache 只是切线相关,因为Session 在后台使用分布式缓存提供程序。该行本质上是说您将在内存缓存中使用,因此意味着您的Session 也将在内存中。但是,这是默认的无论如何,因此您可以将其删除。
    • 很高兴你能做到这一点。如果你接受我的回答,请将其标记为答案。它会帮助其他人找到.. :)
    • 不错的扩展方法!
    【解决方案2】:

    这是一个常青的帖子,尽管 Microsoft 已建议序列化以将对象存储在会话中 - 这不是正确的解决方案,除非您的对象是只读的,我有一个博客解释所有场景 here,我什至指出在问题 id 18159 中找出 Asp.Net Core 的 GitHub 中的问题

    问题概要在这里:

    A.序列化与对象不同,它确实有助于分布式服务器场景,但它带有一个微软未能强调的警告 - 只有当对象意味着它时,它才会在没有任何不可预知的故障的情况下工作被读取而不是被写回。

    B.如果您在会话中寻找读写对象,则每次更改反序列化后从会话中读取的对象时 - 它需要通过调用序列化再次写回会话 - 仅此一项就可能导致多种复杂性因为您将需要跟踪更改 - 或在任何属性的每次更改后继续回写会话。在对服务器的一次请求中,您将遇到对象被多次写回直到返回响应的情况。

    C.对于会话中的读写对象,即使在单个服务器上它也会失败,因为用户的操作可以触发对服务器的多个快速请求,并且系统经常会发现自己处于对象正在被由一个线程序列化或反序列化并被另一个线程编辑然后写回,结果是您最终会被线程覆盖对象状态 - 甚至锁定也无济于事,因为对象不是真实对象而是反序列化创建的临时对象。

    D.序列化复杂对象存在问题 - 它不仅会影响性能,甚至在某些情况下可能会失败 - 特别是如果您有深度嵌套的对象,这些对象有时会引用自身。

    解决方案的概要在这里,完整的实现和代码在博客link

    1. 首先将其实现为 Cache 对象,在 IMemoryCache 中为每个唯一会话创建一项。

    2. 将缓存保持在滑动过期模式,以便每次读取它都会恢复过期时间 - 从而只要会话处于活动状态,就会将对象保留在缓存中。

    3. 仅第二点是不够的,您需要实现心跳技术 - 每 T 减去 1 分钟左右从 javascript 触发对会话的调用。 (我们过去常常这样做,甚至在用户使用浏览器之前保持会话处于活动状态,所以不会有任何不同

    其他建议

    A.创建一个名为 SessionManager 的对象 - 以便所有与会话读/写相关的代码都放在一个地方。

    B.不要为会话超时保持很高的值 - 如果您正在实施心跳技术,即使 3 分钟的会话超时也足够了。

    【讨论】:

    • 总是序列化对象和更新会话真是一场噩梦(性能等),天哪,微软。
    猜你喜欢
    • 2019-11-11
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    • 2018-04-02
    • 1970-01-01
    • 2014-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多