How to add Generic List to Redis via StackExchange.Redis?
Solution 1
StackExchange.Redis is a raw client - it talks in Redis terms only. It does not attempt to be an ORM of any kind. It will, however, store any string
or byte[]
that you care to throw at it - which means you should have your choice of serializers. JSON would be a reasonable default (Jil is awesome), although we tend to use protocol-buffers ourselves (via protobuf-net).
It you intend to use list semantics, I strongly recommend starting with the List* commands - sets have different semantics to lists - sets are unordered and only store unique values; lists preserve order and allow duplicates.
Solution 2
May be it helps. I also faced the same question at the beginning of my diving into StackExchange.Redis. In my project I created 2 extension methods, which help me serialize/deserialize complex type for Redis database. You may extend them to your needs.
Methods:
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;
}
}
Usage:
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);
Solution 3
An improvement to Andrey Gubal's answer, to handle nullable properties or null values:
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;
}
}
Solution 4
This is an option
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 is СonnectionMultiplexer.Connect and GetDatabase();
Comments
-
Shane about 4 years
For example, if I have a model called 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; } }
Example:
var customers = new List<Customer>();
How would I added a List of Customer? How would I do this?
using (var redis = ConnectionMultiplexer.Connect(this.redisServer)) { var db = redis.GetDatabase(); db.SetAdd(key, ?????); }
I think SetAdd is the right method, but I can't see how to get my generic list of Customer (i.e. List into the format of RedisValue.
-
Shane over 9 yearsUnderstood. So I would maybe serialize my generic list of Customers to Json (via newtonsoft or what not), then I could save as a string and just serialize/deserialize back and forth? I'll check out the List commands.
-
Marc Gravell over 9 years@Shane two options - if you want to use the list features of redis, serialize each object separately and send as separate list rows. If you just want "store my list, fetch my list" - then: serialize the list and use the String* redis functions. A redis "string" just means "a single value" - it doesn't need to be a string (if that makes sense)
-
Philip P. over 9 yearsOR, you could store objects like Customer as hashes, and then have sorted sets to group them into "lists". That's actually cool because you can have same customer be in different "lists" (entirely reasonable) and store them only once. And easy to move between "lists".
-
MickyD about 9 yearsGenerally avoid code-only answers. Consider adding a
description
that helps to explain your code. Thanks -
MickyD almost 8 years@alex Code-only answers may fall under 'Very Low Quality' ...and are candidates for deletion....We've always touted that we aren't a code factory. We are the people who teach others to fish. Code-only answers only feed a person for a day. I'm pretty sure I've never seen a Learn c# book consisting of a sequence of code listings with no dialog in between
-
Karan Desai about 7 yearsSuppose Customer class asked by OP has a custom object of say Product. In that case can you please explain how to proceed?
-
knocte over 5 yearsstoring binary data is a really bad approach IMO