TG.WXCRM.V4/DAL/Redis/RedisHash.cs

629 lines
23 KiB
C#

using StackExchange.Redis;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using WX.CRM.Model.Redis;
namespace WX.CRM.DAL.Redis
{
public abstract class RedisHashBase<TKey> : RedisStore
{
public RedisHashBase(RedisKey key, RedisConfig redisConfig = RedisConfig.Redis0) : base(key, redisConfig)
{
}
public RedisHashBase(RedisConfig redisConfig = RedisConfig.Redis0)
: base(redisConfig)
{
}
/// <summary>
/// HEXISTS http://redis.io/commands/hexists
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
public bool Exists(TKey field)
{
var rKey = Settings.ValueConverter.Serialize(field);
return Database.HashExists(Key, rKey);
}
/// <summary>
/// HEXISTS http://redis.io/commands/hexists
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
public async Task<bool> ExistsAsync(TKey field)
{
var rKey = Settings.ValueConverter.Serialize(field);
return await Database.HashExistsAsync(Key, rKey);
}
/// <summary>
/// HKEYS http://redis.io/commands/hkeys
/// </summary>
/// <returns></returns>
public async Task<IEnumerable<TKey>> KeysAsync()
{
var result = await Database.HashKeysAsync(Key);
return result.Select(x => Settings.ValueConverter.Deserialize<TKey>(x));
}
/// <summary>
/// HLEN http://redis.io/commands/hlen
/// </summary>
public async Task<long> LengthAsync()
{
return await Database.HashLengthAsync(Key);
}
/// <summary>
/// HINCRBY http://redis.io/commands/hincrby
/// </summary>
public async Task<long> IncrementAsync(TKey field, long value = 1)
{
var rKey = Settings.ValueConverter.Serialize(field);
return await Database.HashIncrementAsync(Key, rKey, value);
}
/// <summary>
/// HINCRBYFLOAT http://redis.io/commands/hincrbyfloat
/// </summary>
public async Task<double> IncrementAsync(TKey field, double value)
{
var rKey = Settings.ValueConverter.Serialize(field);
return await Database.HashIncrementAsync(Key, rKey, value);
}
/// <summary>
/// HDEL http://redis.io/commands/hdel
/// </summary>
public async Task<bool> DeleteAsync(TKey field)
{
var rKey = Settings.ValueConverter.Serialize(field);
return await Database.HashDeleteAsync(Key, rKey);
}
/// <summary>
/// HDEL http://redis.io/commands/hdel
/// </summary>
public async Task<long> DeleteAsync(TKey[] fields)
{
var hashFields = fields.Select(x => Settings.ValueConverter.Serialize(x)).ToArray();
return await Database.HashDeleteAsync(Key, hashFields);
}
}
public sealed class RedisHash<TKey> : RedisHashBase<TKey>
{
public RedisHash(RedisKey key, RedisConfig redisConfig = RedisConfig.Redis0) : base(key, redisConfig)
{
}
public RedisHash(RedisConfig redisConfig = RedisConfig.Redis0)
: base(redisConfig)
{
}
/// <summary>
/// HGET http://redis.io/commands/hget
/// </summary>
public TValue Get<TValue>(TKey field)
{
var rKey = Settings.ValueConverter.Serialize(field);
var rValue = Database.HashGet(Key, rKey);
return Settings.ValueConverter.Deserialize<TValue>(rValue);
}
/// <summary>
/// HGET http://redis.io/commands/hget
/// </summary>
public async Task<TValue> GetAsync<TValue>(TKey field)
{
var rKey = Settings.ValueConverter.Serialize(field);
var rValue = await Database.HashGetAsync(Key, rKey);
return Settings.ValueConverter.Deserialize<TValue>(rValue);
}
/// <summary>
/// HMGET http://redis.io/commands/hmget
/// </summary>
public async Task<Dictionary<TKey, TValue>> GetAsync<TValue>(TKey[] fields)
{
var hashFields = fields.Select(x => Settings.ValueConverter.Serialize(x)).ToArray();
var rValues = await Database.HashGetAsync(Key, hashFields);
return fields
.Zip(rValues, (key, x) =>
{
if (!x.HasValue)
return new { key, rValue = default(TValue), x.HasValue };
var rValue = Settings.ValueConverter.Deserialize<TValue>(x);
return new { key, rValue, x.HasValue };
})
.Where(x => x.HasValue)
.ToDictionary(x => x.key, x => x.rValue);
}
public Dictionary<string, TValue> GetALL<TValue>()
{
Dictionary<string, TValue> list = new Dictionary<string, TValue>();
var rValue = Database.HashGetAll(Key);
foreach (var item in rValue)
{
//var t = Common.Utility.JSONToObject<TValue>(item.Value);
//list.Add(item.Name, t);
var t = Settings.ValueConverter.Deserialize<TValue>(item.Value);
list.Add(item.Name, t);
}
return list;
}
/// <summary>
/// HSET, HSETNX http://redis.io/commands/hset http://redis.io/commands/hsetnx
/// </summary>
public bool Set<TValue>(TKey field, TValue value)
{
var rKey = Settings.ValueConverter.Serialize(field);
var rValue = Settings.ValueConverter.Serialize(value);
return Database.HashSet(Key, rKey, rValue);
}
/// <summary>
/// HSET, HSETNX http://redis.io/commands/hset http://redis.io/commands/hsetnx
/// </summary>
public async Task<bool> SetAsync<TValue>(RedisKey key, TKey field, TValue value)
{
var rKey = Settings.ValueConverter.Serialize(field);
var rValue = Settings.ValueConverter.Serialize(value);
return await Database.HashSetAsync(key, rKey, rValue);
}
/// <summary>
/// HSET, HSETNX http://redis.io/commands/hset http://redis.io/commands/hsetnx
/// </summary>
public async Task<bool> SetAsync<TValue>(TKey field, TValue value)
{
var rKey = Settings.ValueConverter.Serialize(field);
var rValue = Settings.ValueConverter.Serialize(value);
return await Database.HashSetAsync(Key, rKey, rValue);
}
/// <summary>
/// HMSET http://redis.io/commands/hmset
/// </summary>
public async Task SetAsync<TValue>(IEnumerable<KeyValuePair<TKey, TValue>> values)
{
if (!(values is ICollection))
{
values = values.ToArray(); // materialize
}
var hashFields = values.Select(x =>
{
var rKey = Settings.ValueConverter.Serialize(x.Key);
var rValue = Settings.ValueConverter.Serialize(x.Value);
return new HashEntry(rKey, rValue);
}).ToArray();
await Database.HashSetAsync(Key, hashFields);
}
public async Task SetByListAsync<TValue>(RedisKey key, IList<KeyValuePair<TKey, TValue>> values)
{
if (!(values is ICollection))
{
values = values.ToArray(); // materialize
}
var hashFields = values.Select(x =>
{
var rKey = Settings.ValueConverter.Serialize(x.Key);
var rValue = Settings.ValueConverter.Serialize(x.Value);
return new HashEntry(rKey, rValue);
}).ToArray();
await Database.HashSetAsync(key, hashFields);
}
}
public sealed class RedisDictionary<TKey, TValue> : RedisHashBase<TKey>
{
public RedisDictionary(RedisKey key, RedisConfig redisConfig = RedisConfig.Redis0) : base(key, redisConfig)
{
}
/// <summary>
/// HGET http://redis.io/commands/hget
/// </summary>
public async Task<TValue> GetAsync(TKey field)
{
var rKey = Settings.ValueConverter.Serialize(field);
var rValue = await Database.HashGetAsync(Key, rKey);
return Settings.ValueConverter.Deserialize<TValue>(rValue);
}
/// <summary>
/// HMGET http://redis.io/commands/hmget
/// </summary>
public async Task<Dictionary<TKey, TValue>> GetAsync(TKey[] fields)
{
var hashFields = fields.Select(x => Settings.ValueConverter.Serialize(x)).ToArray();
var rValues = await Database.HashGetAsync(Key, hashFields);
return fields
.Zip(rValues, (key, x) =>
{
if (!x.HasValue)
return new { key, rValue = default(TValue), x.HasValue };
var rValue = Settings.ValueConverter.Deserialize<TValue>(x);
return new { key, rValue, x.HasValue };
})
.Where(x => x.HasValue)
.ToDictionary(x => x.key, x => x.rValue);
}
/// <summary>
/// HGETALL http://redis.io/commands/hgetall
/// </summary>
public async Task<Dictionary<TKey, TValue>> GetAllAsync()
{
var hashEntries = await Database.HashGetAllAsync(Key);
return hashEntries.Select(x =>
{
var vk = Settings.ValueConverter.Deserialize<TKey>(x.Name);
var v = Settings.ValueConverter.Deserialize<TValue>(x.Value);
return new { key = vk, value = v };
}).ToDictionary(x => x.key, x => x.value);
}
/// <summary>
/// HVALS http://redis.io/commands/hvals
/// </summary>
public async Task<IEnumerable<TValue>> ValuesAsync()
{
var r = await Database.HashValuesAsync(Key);
return r.Select(x => Settings.ValueConverter.Deserialize<TValue>(x));
}
/// <summary>
/// HSET, HSETNX http://redis.io/commands/hset http://redis.io/commands/hsetnx
/// </summary>
public async Task<bool> Set(TKey field, TValue value)
{
var rKey = Settings.ValueConverter.Serialize(field);
var rValue = Settings.ValueConverter.Serialize(value);
return await Database.HashSetAsync(Key, rKey, rValue);
}
/// <summary>
/// HMSET http://redis.io/commands/hmset
/// </summary>
public async Task Set(IEnumerable<KeyValuePair<TKey, TValue>> values)
{
if (!(values is ICollection))
{
values = values.ToArray(); // materialize
}
var hashFields = values.Select(x =>
{
var rKey = Settings.ValueConverter.Serialize(x.Key);
var rValue = Settings.ValueConverter.Serialize(x.Value);
return new HashEntry(rKey, rValue);
}).ToArray();
await Database.HashSetAsync(Key, hashFields);
}
}
public class RedisClass<T> : RedisStore where T : class, new()
{
public RedisClass(RedisKey key, RedisConfig redisConfig = RedisConfig.Redis0) : base(key, redisConfig)
{
}
/// <summary>
/// All hash value map to class if key can't find returns null, includes HGETALL.
/// </summary>
public T Get()
{
var data = Database.HashGetAll(Key);
if (data == null || data.Length == 0)
{
return null;
}
var accessor = TypeAccessor.Lookup(typeof(T));
var result = (T)accessor.CreateNew();
foreach (var item in data)
{
IMemberAccessor memberAccessor;
if (accessor.TryGetValue(item.Name, out memberAccessor) && memberAccessor.IsReadable && memberAccessor.IsWritable)
{
var v = Settings.ValueConverter.Deserialize(memberAccessor.MemberType, item.Value);
memberAccessor.SetValue(result, v);
}
else
{
var buf = (byte[])item.Value;
}
}
return result;
}
/// <summary>
/// All hash value map to class if key can't find returns null, includes HGETALL.
/// </summary>
public async Task<T> GetAsync()
{
var data = await Database.HashGetAllAsync(Key);
if (data == null || data.Length == 0)
{
return null;
}
var accessor = TypeAccessor.Lookup(typeof(T));
var result = (T)accessor.CreateNew();
foreach (var item in data)
{
IMemberAccessor memberAccessor;
if (accessor.TryGetValue(item.Name, out memberAccessor) && memberAccessor.IsReadable && memberAccessor.IsWritable)
{
var v = Settings.ValueConverter.Deserialize(memberAccessor.MemberType, item.Value);
memberAccessor.SetValue(result, v);
}
else
{
var buf = (byte[])item.Value;
}
}
return result;
}
/// <summary>
/// Class fields set to hash, includes HMSET. If return value is null value == null.
/// </summary>
public void Set(T value)
{
if (value == null) return;
var accessor = TypeAccessor.Lookup(typeof(T));
var hashFields = accessor.Where(kvp => kvp.Value.IsReadable && kvp.Value.IsWritable)
.Select(kvp =>
{
var field = kvp.Value.GetValue(value);
var rv = Settings.ValueConverter.Serialize(field);
return new HashEntry(kvp.Key, rv);
})
.ToArray();
Database.HashSet(Key, hashFields);
}
/// <summary>
/// Class fields set to hash, includes HMSET. If return value is null value == null.
/// </summary>
public async Task SetAsync(T value)
{
if (value == null) return;
var accessor = TypeAccessor.Lookup(typeof(T));
var hashFields = accessor.Where(kvp => kvp.Value.IsReadable && kvp.Value.IsWritable)
.Select(kvp =>
{
var field = kvp.Value.GetValue(value);
var rv = Settings.ValueConverter.Serialize(field);
return new HashEntry(kvp.Key, rv);
})
.ToArray();
await Database.HashSetAsync(Key, hashFields);
}
/// <summary>
/// GET(HGETALL), SET(HMSET)
/// </summary>
public async Task<T> GetOrSet(Func<T> valueFactory)
{
var value = await GetAsync();
if (value == null)
{
value = valueFactory();
await SetAsync(value);
}
return value;
}
/// <summary>
/// GET(HGETALL), SET(HMSET)
/// </summary>
public async Task<T> GetOrSet(Func<Task<T>> valueFactory)
{
var value = await GetAsync();
if (value == null)
{
value = await valueFactory();
await SetAsync(value);
}
return value;
}
/// <summary>
/// HGET http://redis.io/commands/hget
/// </summary>
public async Task<TValue> GetMember<TValue>(Expression<Func<T, TValue>> memberSelector)
{
var memberExpr = memberSelector.Body as MemberExpression;
if (memberExpr == null) throw new ArgumentException("can't analyze selector expression");
return await GetMember<TValue>(memberExpr.Member.Name);
}
/// <summary>
/// HGET http://redis.io/commands/hget
/// </summary>
public async Task<TValue> GetMember<TValue>(string memberName)
{
//if (!TypeAccessor.Lookup(typeof (T)).ContainsKey(memberName))
// return await Task.FromResult(TValue);
var rValue = await Database.HashGetAsync(Key, memberName);
return Settings.ValueConverter.Deserialize<TValue>(rValue);
}
/// <summary>
/// HMGET http://redis.io/commands/hmget
/// </summary>
public async Task<Dictionary<string, TValue>> GetMembers<TValue>(Expression<Func<T, TValue[]>> memberSelector, CommandFlags commandFlags = CommandFlags.None)
{
var newArrayExpr = memberSelector.Body as NewArrayExpression;
if (newArrayExpr == null) throw new ArgumentException("can't analyze selector expression");
var fields = newArrayExpr.Expressions.OfType<MemberExpression>().Select(x => x.Member.Name).ToArray();
return await GetMembers<TValue>(fields);
}
/// <summary>
/// HMGET http://redis.io/commands/hmget
/// </summary>
public async Task<Dictionary<string, TValue>> GetMembers<TValue>(string[] memberNames)
{
var accesssor = TypeAccessor.Lookup(typeof(T));
memberNames = memberNames.Where(x => accesssor.ContainsKey(x)).ToArray();
if (memberNames.Length == 0)
return await Task.FromResult(new Dictionary<string, TValue>());
var hashFields = memberNames.Select(x => (RedisValue)x).ToArray();
var rValues = await Database.HashGetAsync(Key, hashFields);
return memberNames
.Zip(rValues, (key, x) =>
{
if (!x.HasValue) return new { key, rValue = default(TValue), x.HasValue };
var rValue = Settings.ValueConverter.Deserialize<TValue>(x);
return new { key, rValue, x.HasValue };
})
.Where(x => x.HasValue)
.ToDictionary(x => x.key, x => x.rValue);
}
/// <summary>
/// HSET, HSETNX http://redis.io/commands/hset http://redis.io/commands/hsetnx
/// </summary>
public async Task<bool> SetMember<TValue>(Expression<Func<T, TValue>> memberSelector, TValue value)
{
var memberExpr = memberSelector.Body as MemberExpression;
if (memberExpr == null) throw new ArgumentException("can't analyze selector expression");
return await SetMember<TValue>(memberExpr.Member.Name, value);
}
/// <summary>
/// HSET, HSETNX http://redis.io/commands/hset http://redis.io/commands/hsetnx
/// </summary>
public async Task<bool> SetMember<TValue>(string memberName, TValue value)
{
if (!TypeAccessor.Lookup(typeof(T)).ContainsKey(memberName)) return await Task.FromResult(false);
RedisValue rKey = memberName;
var rValue = Settings.ValueConverter.Serialize(value);
return await Database.HashSetAsync(Key, rKey, rValue);
}
/// <summary>
/// HSET, HSETNX http://redis.io/commands/hset http://redis.io/commands/hsetnx
/// </summary>
public bool SetMemberSync<TValue>(string memberName, TValue value)
{
if (!TypeAccessor.Lookup(typeof(T)).ContainsKey(memberName))
return Task.FromResult(false).Result;
RedisValue rKey = memberName;
var rValue = Settings.ValueConverter.Serialize(value);
return Database.HashSet(Key, rKey, rValue);
}
/// <summary>
/// HMSET http://redis.io/commands/hmset
/// </summary>
public async Task SetMembers<TValue>(Expression<Func<T, TValue[]>> memberSelector, TValue[] values)
{
var newArrayExpr = memberSelector.Body as NewArrayExpression;
if (newArrayExpr == null) throw new ArgumentException("can't analyze selector expression");
var fields = newArrayExpr.Expressions.OfType<MemberExpression>().Select(x => x.Member.Name).ToArray();
if (fields.Length != values.Length) throw new ArgumentException("member and value's count is mismatch - memberSelector.Length:{fields.Length}, values.Length:{values.Length}");
var pairs = fields.Zip(values, (key, value) => new KeyValuePair<string, TValue>(key, value)).ToArray();
await SetMembers<TValue>(pairs);
}
/// <summary>
/// HMSET http://redis.io/commands/hmset
/// </summary>
public async Task SetMembers<TValue>(IEnumerable<KeyValuePair<string, TValue>> values)
{
if (!(values is ICollection))
{
values = values.ToArray(); // materialize
}
var hashFields = values.Select(x =>
{
RedisValue rKey = x.Key;
var rValue = Settings.ValueConverter.Serialize(x.Value);
return new HashEntry(rKey, rValue);
}).ToArray();
await Database.HashSetAsync(Key, hashFields);
}
/// <summary>
/// HINCRBY http://redis.io/commands/hincrby
/// </summary>
public Task<long> Increment(Expression<Func<T, long>> memberSelector, long value = 1)
{
return Increment(GetMemberName(memberSelector), value);
}
/// <summary>
/// HINCRBY http://redis.io/commands/hincrby
/// </summary>
public async Task<long> Increment(string member, long value = 1)
{
var rKey = member;
return await Database.HashIncrementAsync(Key, rKey, value);
}
/// <summary>
/// HINCRBYFLOAT http://redis.io/commands/hincrbyfloat
/// </summary>
public Task<double> Increment(Expression<Func<T, double>> memberSelector, double value)
{
return Increment(GetMemberName(memberSelector), value);
}
/// <summary>
/// HINCRBYFLOAT http://redis.io/commands/hincrbyfloat
/// </summary>
public async Task<double> Increment(string member, double value)
{
var rKey = member;
return await Database.HashIncrementAsync(Key, rKey, value);
}
string GetMemberName(LambdaExpression memberSelector)
{
var unary = memberSelector.Body as UnaryExpression;
var memberExpr = (unary != null)
? unary.Operand as MemberExpression
: memberSelector.Body as MemberExpression;
if (memberExpr == null) throw new ArgumentException("can't analyze selector expression");
return memberExpr.Member.Name;
}
}
}