如何通过StackExchange.Redis将通用列表添加到Redis?

15
例如,如果我有一个名为Customer的模型。
public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Address1 { get; set; }
        public string City { get; set; }
        public string State { get; set; }
    }

例子:

var customers = new List<Customer>();

我该如何添加客户列表?怎么做呢?

 using (var redis = ConnectionMultiplexer.Connect(this.redisServer))
            {
                var db = redis.GetDatabase();

                db.SetAdd(key, ?????);
}

我认为SetAdd是正确的方法,但我不知道如何将我的通用顾客列表(即List)转换成RedisValue格式。

4个回答

16

也许这会有所帮助。我在开始学习 StackExchange.Redis 时也遇到了同样的问题。在我的项目中,我创建了2个扩展方法,用于将复杂类型序列化/反序列化到Redis数据库中。您可以根据需要进行扩展。

方法:

    public static class RedisUtils
        {
//Serialize in Redis format:
            public static HashEntry[] ToHashEntries(this object obj)
            {
                PropertyInfo[] properties = obj.GetType().GetProperties();
                return properties.Select(property => new HashEntry(property.Name, property.GetValue(obj).ToString())).ToArray();
            }
    //Deserialize from Redis format
            public static T ConvertFromRedis<T>(this HashEntry[] hashEntries)
            {
                PropertyInfo[] properties = typeof(T).GetProperties();
                var obj = Activator.CreateInstance(typeof(T));
                foreach (var property in properties)
                {
                    HashEntry entry = hashEntries.FirstOrDefault(g => g.Name.ToString().Equals(property.Name));
                    if (entry.Equals(new HashEntry())) continue;
                    property.SetValue(obj, Convert.ChangeType(entry.Value.ToString(), property.PropertyType));
                }
                return (T)obj;
            }
        }

使用方法:

var customer = new Customer
{
//Initialization
};

Db.HashSet("customer", customer.ToHashEntries());
Customer result = Db.HashGetAll("customer").ConvertFromRedis<Customer>();

Assert.AreEqual(customer.FirstName, result.FirstName);
Assert.AreEqual(customer.LastName, result.LastName);
Assert.AreEqual(customer.Address1, result.Address1);

假设 OP 所要求的 Customer 类具有一个自定义对象,比如 Product。在这种情况下,您能否解释一下如何继续? - Karan Desai

16

StackExchange.Redis是一个原始客户端,它只使用Redis术语进行通信。它不会尝试成为任何类型的ORM。但是,它可以存储您想要处理的任何stringbyte[] - 这意味着您可以选择序列化程序。JSON可能是合理的默认选择(Jil很棒),尽管我们倾向于使用协议缓冲区(通过protobuf-net)。

如果您打算使用列表语义,强烈建议从List *命令开始 - 集合具有与列表不同的语义 - 集合是无序的,并且仅存储唯一值; 列表保留顺序并允许重复。


明白了。那么我可能会将我的通用客户列表序列化为 Json(通过 newtonsoft 或其他方式),然后我可以将其保存为字符串,只需序列化/反序列化即可?我会查看列表命令的。 - Shane
3
@Shane 有两个选项 - 如果您想使用 Redis 的列表功能,请将每个对象单独序列化并作为单独的列表行发送。如果您只想“存储我的列表,获取我的列表”-那么:序列化列表并使用 String* Redis 函数。Redis 中的“字符串”只是指“单个值”-它不需要是字符串(如果这有意义)。 - Marc Gravell
1
或者,您可以将像客户这样的对象存储为哈希值,然后使用排序集将它们分组到“列表”中。这实际上很酷,因为您可以让同一个客户出现在不同的“列表”中(完全合理),并且仅存储一次。而且很容易在“列表”之间移动。 - Philip P.

8

对于Andrey Gubal的回答,以下是一种改进方案,可以处理可空属性或null值:

public static class RedisUtils
{
    //Serialize in Redis format:
    public static HashEntry[] ToHashEntries(this object obj)
    {
        PropertyInfo[] properties = obj.GetType().GetProperties();
        return properties
            .Where(x=> x.GetValue(obj)!=null) // <-- PREVENT NullReferenceException
            .Select(property => new HashEntry(property.Name, property.GetValue(obj)
            .ToString())).ToArray();
    }

    //Deserialize from Redis format
    public static T ConvertFromRedis<T>(this HashEntry[] hashEntries)
    {
        PropertyInfo[] properties = typeof(T).GetProperties();
        var obj = Activator.CreateInstance(typeof(T));
        foreach (var property in properties)
        {
            HashEntry entry = hashEntries.FirstOrDefault(g => g.Name.ToString().Equals(property.Name));
            if (entry.Equals(new HashEntry())) continue;
            property.SetValue(obj, Convert.ChangeType(entry.Value.ToString(), property.PropertyType));
        }
        return (T)obj;
    }
}

4
这是一个选项。
public static class StackExchangeRedisExtensions
{

    public static T Get<T>(string key)
    {
        var connect = AzureredisDb.Cache;
        var r = AzureredisDb.Cache.StringGet(key);
        return Deserialize<T>(r);
    }

    public static List<T> GetList<T>(string key)
    {                       
        return (List<T>)Get(key);
    }

    public static void SetList<T>(string key, List<T> list)
    {
        Set(key, list);
    }

    public static object Get(string key)
    {
        return Deserialize<object>(AzureredisDb.Cache.StringGet(key));
    }

    public static void Set(string key, object value)
    {
        AzureredisDb.Cache.StringSet(key, Serialize(value));
    }

    static byte[] Serialize(object o)
    {
        if (o == null)
        {
            return null;
        }

        BinaryFormatter binaryFormatter = new BinaryFormatter();
        using (MemoryStream memoryStream = new MemoryStream())
        {
            binaryFormatter.Serialize(memoryStream, o);
            byte[] objectDataAsStream = memoryStream.ToArray();
            return objectDataAsStream;
        }
    }

    static T Deserialize<T>(byte[] stream)
    {
        if (stream == null)
        {
            return default(T);
        }

        BinaryFormatter binaryFormatter = new BinaryFormatter();
        using (MemoryStream memoryStream = new MemoryStream(stream))
        {
            T result = (T)binaryFormatter.Deserialize(memoryStream);
            return result;
        }
    }
}

AzureredisDb.Cache是通过ConnectionMultiplexer.Connect和GetDatabase()连接的。

3
尽量避免仅包含代码的回答。考虑添加一个“描述”来帮助解释您的代码。谢谢。 - user585968
将二进制数据存储起来是我个人认为非常糟糕的方法。 - knocte

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接