Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 有没有一种方法可以在不增加二进制序列化程序开销的情况下利用.NET core中的会话化存储?_C#_Asp.net Core - Fatal编程技术网

C# 有没有一种方法可以在不增加二进制序列化程序开销的情况下利用.NET core中的会话化存储?

C# 有没有一种方法可以在不增加二进制序列化程序开销的情况下利用.NET core中的会话化存储?,c#,asp.net-core,C#,Asp.net Core,net核心应用程序中的会话存储要求您在检索之前先将其转换为字节数组。与.net framework存储相比,这些序列化和反序列化操作的开销会带来大量开销。在.Net framework应用程序中需要5毫秒的操作在.Net core中需要850毫秒以上。我需要能够以高性能的方式从服务器缓存中存储和检索相当大数量的数据,类似于我们在.Net framework中使用会话存储的方式 public class SessionManager : ISessionManager {

net核心应用程序中的会话存储要求您在检索之前先将其转换为字节数组。与.net framework存储相比,这些序列化和反序列化操作的开销会带来大量开销。在.Net framework应用程序中需要5毫秒的操作在.Net core中需要850毫秒以上。我需要能够以高性能的方式从服务器缓存中存储和检索相当大数量的数据,类似于我们在.Net framework中使用会话存储的方式

public class SessionManager : ISessionManager
    {
        private readonly IHttpContextAccessor _contextAccessor;
        private readonly IMemoryCache _cache;
        public SessionManager(IHttpContextAccessor contextAccessor
                            , IMemoryCache cache
        ) {
            _contextAccessor = contextAccessor;
            _cache = cache;
         }
        public T get<T>(string key)
        {
           object data;
           _cache.TryGetValue(buildSessionKey(key), out data);
           return data == null ? default(T) : (T)data;
        }

        public void set(string key, object data, TimeSpan maxLifeTime = default(TimeSpan))
        {
            TimeSpan adjustLifeTime = maxLifeTime.TotalMinutes < 5 ? TimeSpan.FromMinutes(20) : maxLifeTime;
            if (adjustLifeTime.TotalMinutes > 90) adjustLifeTime = TimeSpan.FromMinutes(90);
            MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromMinutes(20))
            .SetAbsoluteExpiration(adjustLifeTime);
            _cache.Set(buildSessionKey(key), data);
        }

        public void remove(string key) {
            _cache.Remove(buildSessionKey(key));
        }

        private string buildSessionKey(string partialKey) {
            string sessionID = _contextAccessor.HttpContext.Session.Id;
            return sessionID + partialKey;
        } 
    }
我们的应用程序消耗了很多相当大的ADO.NET数据表。它们包含数千行和几十列的情况并不少见。在过去,我们使用带有会话存储的.Net framework在会话之间快速检索ADO.Net数据表对象

DataTable dt = new DataTable();
HttpContext.Current.Session["data"] = dt; // store in session
dt = (DataTable)HttpContext.Current.Session["data"]; // retrieve from session
我们也有自己的自定义类,这些类的成员是datatables。这些类以不同的方式操作数据。我们还从会话中存储和检索它们

[Serializable]
 MyClass {
    public DataTable dt;
 }
在我们的.NET framework应用程序中,过滤和分页数据的典型请求大约需要5毫秒的往返时间。会话访问速度非常快,每个get和set操作的性能损失可以忽略不计

我们一直在尝试过渡到.NETCore,它对会话的管理稍有不同。我首先必须将其转换为字节数组,而不是在会话中存储和检索任何对象。我使用带有一点逻辑的二进制格式化程序来处理get和set操作。下面的代码没有它可能的那么有效,但是到目前为止最大的瓶颈是retrieveData方法中的反序列化操作

Public class SessionManager : ISessionManager
    {
        private readonly IHttpContextAccessor _contextAccessor;
        public SessionManager(IHttpContextAccessor contextAccessor) {
            _contextAccessor = contextAccessor;
         }
        public T get<T>(string key)
        {
            object data = retrieveData(key);
            return data == null ? default(T) : (T)data;
        }

        public void set(string key, object data)
        {
            serializeBinary(key, data); 
        }

        public void remove(string key) {
            _contextAccessor.HttpContext.Session.Remove(key);
        }

        private void serializeBinary(string key, object data) {
            BinaryFormatter bf = new BinaryFormatter();
            using (var ms = new MemoryStream())
            {
                bf.Serialize(ms, data);
                var bytes = ms.ToArray();
                _contextAccessor.HttpContext.Session.Set(key, bytes);
            }
        }

        private object retrieveData(string key) {
            byte[] data = null;
            _contextAccessor.HttpContext.Session.TryGetValue(key, out data);
            if (data == null) return null;
            using (MemoryStream ms = new MemoryStream(data))
            {
                IFormatter br = new BinaryFormatter();
                return br.Deserialize(ms);
            }
        }
    }
}
公共类会话管理器:ISessionManager
{
专用只读IHttpContextAccessor\u contextAccessor;
公共会话管理器(IHttpContextAccessor contextAccessor){
_contextAccessor=contextAccessor;
}
公共T获取(字符串键)
{
对象数据=检索数据(键);
返回数据==null?默认值(T):(T)数据;
}
公共无效集(字符串键、对象数据)
{
序列化二进制(键、数据);
}
公共无效删除(字符串键){
_contextAccessor.HttpContext.Session.Remove(键);
}
私有二进制(字符串键、对象数据){
BinaryFormatter bf=新的BinaryFormatter();
使用(var ms=new MemoryStream())
{
序列化(ms、数据);
var bytes=ms.ToArray();
_contextAccessor.HttpContext.Session.Set(键,字节);
}
}
私有对象检索数据(字符串键){
字节[]数据=null;
_contextAccessor.HttpContext.Session.TryGetValue(键,输出数据);
if(data==null)返回null;
使用(MemoryStream ms=新的MemoryStream(数据))
{
IFormatter br=新的二进制格式化程序();
返回br.反序列化(ms);
}
}
}
}
用法:

MyClass c;
c.dt = _myRepo.getLotsOfData();
_SessionManager.set("data", c);
c = _SessionManager.get<MyClass>("data");
myc类;
c、 dt=_myRepo.getLotsOfData();
_SessionManager.set(“数据”,c);
c=_SessionManager.get(“数据”);
与我们的.Net Framework应用程序需要5毫秒的时间相比,我们使用相同的类在DataTables上执行相同的分页和筛选操作需要850ms-950ms。在VisualStudio中评测性能时,反序列化操作占用了大部分时间,仅此操作就占用了600毫秒

我知道其他库(比如protobuf)比二进制格式化程序快得多,这可能是我下一步要做的。但是,如果我将反序列化时间减少到300毫秒甚至200毫秒,与.Net Framework相比,我仍然损失了很多性能

在我看来,需要一种不同的缓存策略。有没有一种方法可以在.Net核心应用程序中存储和检索数据,而不需要先对数据进行序列化和反序列化的开销

我们的应用程序消耗了很多相当大的ADO.NET数据表。它们包含数千行和几十列的情况并不少见

我想我们已经发现了问题:)

建议:

  • 不要试图在这种状态下序列化巨大的数据集;它们太贵了
  • 启用
    dataSet.RemotingFormat=System.Data.SerializationFormat.BinaryBinaryFormatter
  • 停止使用
    数据集
    并停止使用
    二进制格式化程序
    ;我并没有说得很松——它们非常昂贵,所以<强>如果数据实际上是固定的<强>,考虑使用具有不同序列化器的POCO类型,在CPU和带宽(数据量)
  • 中,OrthBuf将更有效。
    我最终使用了JohanP推荐的IMemoryCache。IMemoryCache是一个系统存储,它不需要您序列化对象来存储和检索它们

    我将应用程序配置为会话,以便.NET将继续为会话ID提供cookie。然后,在存储或检索项目之前,我将会话ID附加到用户提供的密钥,这或多或少是在.NET framework上管理会话的方式

    public class SessionManager : ISessionManager
        {
            private readonly IHttpContextAccessor _contextAccessor;
            private readonly IMemoryCache _cache;
            public SessionManager(IHttpContextAccessor contextAccessor
                                , IMemoryCache cache
            ) {
                _contextAccessor = contextAccessor;
                _cache = cache;
             }
            public T get<T>(string key)
            {
               object data;
               _cache.TryGetValue(buildSessionKey(key), out data);
               return data == null ? default(T) : (T)data;
            }
    
            public void set(string key, object data, TimeSpan maxLifeTime = default(TimeSpan))
            {
                TimeSpan adjustLifeTime = maxLifeTime.TotalMinutes < 5 ? TimeSpan.FromMinutes(20) : maxLifeTime;
                if (adjustLifeTime.TotalMinutes > 90) adjustLifeTime = TimeSpan.FromMinutes(90);
                MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions()
                .SetSlidingExpiration(TimeSpan.FromMinutes(20))
                .SetAbsoluteExpiration(adjustLifeTime);
                _cache.Set(buildSessionKey(key), data);
            }
    
            public void remove(string key) {
                _cache.Remove(buildSessionKey(key));
            }
    
            private string buildSessionKey(string partialKey) {
                string sessionID = _contextAccessor.HttpContext.Session.Id;
                return sessionID + partialKey;
            } 
        }
    

    使用这种方法,性能与我们的.NET framework应用程序类似

    您可以尝试
    IMemoryCache
    ,而不是
    Session
    ,您可以在其中插入任何对象。谢谢Johan,我会尝试一下。似乎我必须围绕内存缓存创建自己的会话管理,因为它是一个系统缓存而不是会话缓存。这是推荐的方法吗?是的,您必须使缓存密钥使用用户的某个唯一id、用户id或当前标识用户的任何东西。否则,请使用Marc Gravell的方法,充分理解数据集周围的情绪,我们将