【问题标题】:StackExchange.Redis casting RedisValue to byte[] via "as byte[]" returns nullStackExchange.Redis 通过“as byte[]”将 RedisValue 转换为 byte[] 返回 null
【发布时间】:2014-10-03 17:23:46
【问题描述】:

我正在尝试为 Strathweb.CacheOutput.WebApi2 创建 Redis 提供程序,但尝试从 byte[] -> RedisValue -> byte[] 转换返回 null。

我可以手动将对象类型设置为 byte[] 而不是 var / RedisValue,它会正确地将值返回为 byte[],但是在将其设置为 RedisValue 之后,它无法将其转换为 byte[ ].

他的接口有 Get 总是返回一个对象,所以我不能强制类型或使用单独的调用,而不必修改接口。

如果我尝试做一个result as byte[] 我得到 Cannot convert type 'StackExchange.Redis.RedisValue' to 'byte[]' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion

如果我尝试执行(byte[])result,我会得到Cannot cast 'result' (which has an actual type of 'StackExchange.Redis.RedisValue') to 'byte[]'

我是否遗漏了某些东西,或者我将不得不通过根据密钥检查它要查找的数据类型来以某种方式破解它?

界面如下:

namespace WebApi.OutputCache.Core.Cache
{
    public interface IApiOutputCache
    {
        void RemoveStartsWith(string key);
        T Get<T>(string key) where T : class;
        object Get(string key);
        void Remove(string key);
        bool Contains(string key);
        void Add(string key, object o, DateTimeOffset expiration, string dependsOnKey = null);
        IEnumerable<string> AllKeys { get; }
    }
}

这就是它的名称:

        var val = _webApiCache.Get(cachekey) as byte[];
        if (val == null) return;

编辑:添加我使用 ServiceStack.Redis v3 实现的 API 示例(工作 atm,因为它只使用 object 和 StackExchange.Redis 不起作用)

https://github.com/mackayj/WebApi.OutputCache.Redis.ServiceStack

https://github.com/mackayj/WebApi.OutputCache.Redis.StackExchange

【问题讨论】:

    标签: c# stackexchange.redis


    【解决方案1】:

    以下代码使用 StackExchange.Redis 可以设置/获取泛型类型的值,并将 RedisValue 转换为 byte[],它应该适用于任何可序列化类型。

        public static void SetItem<T>(string key, T value)
        {
    
            IDatabase redDb = GetDB();
            redDb.StringSet(key, ToByteArray<T>(value));
        }
    
        public static T GetItem<T>(string key)
        {
            IDatabase redDb = GetDB();
            RedisValue redisResult = redDb.StringGet(key);
            T objResult = FromByteArray<T>(redisResult);
            return objResult;
        }
    
       public static byte[] ToByteArray<T>(T obj)
       {
            if (obj == null)
                return null;
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                bf.Serialize(ms, obj);
                return ms.ToArray();
            }
        }
    
        public static T FromByteArray<T>(byte[] data)
        {
            if (data == null)
                return default(T);
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream(data))
            {
                object obj = bf.Deserialize(ms);
                return (T)obj;
            }
        }
    

    【讨论】:

      【解决方案2】:

      byte[]RedisValue 之间的转换使用conversion operator。但是,编译器只知道该值是object,因此它不知道它需要调用转换运算符。 IE。如果你写如下代码:

      object result = someRedisValue;
      byte[] bytes = (byte[])result;
      

      编译器为最后一行写了如下内容,但失败了:

      cast result to byte[] // runtime error: it's not a byte[]!
      store that in 'bytes'
      

      您可以通过在尝试转换对象之前让编译器知道对象的真实类型来解决此问题。

      byte[] bytes = (RedisValue)result;
      

      这会导致编译器编写如下代码:

      cast result to RedisValue
      call RedisValue's implicit RedisValue to byte[] conversion on that
      store that in 'bytes'
      

      【讨论】:

      • 问题是接口正在返回一个对象,而调用代码正在执行as byte[]。我试图不修改它的接口或主代码,但我想我现在只是要添加一个返回 byte[] 的新方法。我编辑了我的帖子以显示正确的调用代码as byte[]
      【解决方案3】:

      这是一个有趣的问题。有几种方法我可以想到接近它:

      • 在存储之前将其转换为 byte[]
      • dynamic作弊
      • 在存储之前将其包装在其他可识别的包装中

      基本上,自定义转换运算符在拆箱时不起作用,除非您使用dynamic

      我还可以实现IConvertible 或其他一些众所周知的接口。

      【讨论】:

      • 我想得越多,我就越确定RedisValue应该: IConvertible。那可能会在下一个下降中。
      • 感谢马克的快速回复!我用返回 byte[] 而不是 object 的直接调用替换了该代码,并注意到它实际上在多个其他地方也失败了(as stringas MediaTypeHeaderValue 调用)。我尝试将其设置为dynamic result = _database.GetString(key),但这给出了相同的结果,你用动态欺骗它是什么意思?有没有办法让它返回一个对象而不是将其转换为 RedisValue?
      • 我最终只是将StackExchange.Redis 换成了ServiceStack.Redis,而且我能够毫无问题地做到这一点。我真的更喜欢你的版本,因为它要小得多。如果有办法只使用object 而不是RedisValue 作为返回类型,请告诉我,以便我可以将其换回来:)
      • @John,问题很简单:“object”应该是什么? byte[]? string? int?如果你总是想要byte[],那么只需在object之前转换为byte[],即RedisValue val = ...; object o = (byte[])val; // &lt;=== use that
      • 不幸的是,代码在多个位置重复使用,一个用于获取 etag(字符串),一个用于获取数据(byte[]),另一个用于获取标头(MediaTypeHeaderValue),所以我不能只告诉它在返回之前总是转换为 byte[] 。我希望这些是来自 API 的三个单独的调用,因为那时它很容易做到。
      猜你喜欢
      • 2011-06-08
      • 2018-06-16
      • 2011-02-15
      • 2014-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-06
      • 2020-02-03
      相关资源
      最近更新 更多