You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
747 lines
24 KiB
747 lines
24 KiB
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
|
|
namespace Apewer.Source
|
|
{
|
|
|
|
/// <summary></summary>
|
|
public sealed class Redis : IDisposable
|
|
{
|
|
|
|
/// <summary>最大长度。</summary>
|
|
public const int MaxLength = 1073741824;
|
|
|
|
private const int BufferSize = 1048576;
|
|
|
|
#region Instance
|
|
|
|
string _host = null;
|
|
string _pass = null;
|
|
int _port = 6379;
|
|
int _db = 0;
|
|
|
|
int _timeout = 1000; // 1000 毫秒。
|
|
Socket socket;
|
|
BufferedStream buffered;
|
|
|
|
/// <summary>连接指定主机。</summary>
|
|
public Redis(string host, int port = 6379, string password = null)
|
|
{
|
|
_host = string.IsNullOrEmpty(host) ? "localhost" : host;
|
|
_port = (port < 1 || port > 65535) ? 6379 : port;
|
|
_pass = password;
|
|
_timeout = -1;
|
|
}
|
|
|
|
/// <summary>连接 127.0.0.1 的指定端口。</summary>
|
|
public Redis(int port, string host = "127.0.0.1", string password = null) : this(host, port, password) { }
|
|
|
|
/// <summary>连接 127.0.0.1 的 6379 端口。</summary>
|
|
public Redis() : this("127.0.0.1", 6379, null) { }
|
|
|
|
/// <summary>获取或设置文本。</summary>
|
|
public string this[string key]
|
|
{
|
|
get { return GetText(key); }
|
|
set { SetText(key, value); }
|
|
}
|
|
|
|
/// <summary>获取错误信息。</summary>
|
|
private Action<string> Error { get; set; }
|
|
|
|
/// <summary></summary>
|
|
~Redis()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
/// <summary></summary>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
if (socket != null)
|
|
{
|
|
SendCommand("QUIT");
|
|
SendBySuccess();
|
|
socket.Close();
|
|
socket = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>连接 Redis 服务。</summary>
|
|
public string Connect()
|
|
{
|
|
try
|
|
{
|
|
if (socket != null && socket.Connected) return null;
|
|
|
|
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
socket.NoDelay = true;
|
|
socket.SendTimeout = _timeout;
|
|
socket.Connect(_host, _port);
|
|
if (!socket.Connected)
|
|
{
|
|
socket.Close();
|
|
socket = null;
|
|
return "Socket 连接失败。";
|
|
}
|
|
buffered = new BufferedStream(new NetworkStream(socket), BufferSize);
|
|
|
|
if (_pass != null) return SendBySuccess("AUTH", _pass);
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
var error = ex.GetType().Name + ": " + ex.Message;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Static
|
|
|
|
// byte[] end_data = new byte[] { (byte)'\r', (byte)'\n' };
|
|
byte[] CRLF = new byte[] { 13, 10 };
|
|
|
|
static byte[] ToBytes(string text)
|
|
{
|
|
if (string.IsNullOrEmpty(text)) return new byte[0];
|
|
return Encoding.UTF8.GetBytes(text);
|
|
}
|
|
|
|
static int Length(string text)
|
|
{
|
|
return ToBytes(text).Length;
|
|
}
|
|
|
|
static string ToText(byte[] bytes)
|
|
{
|
|
if (bytes != null && bytes.Length > 0)
|
|
{
|
|
try
|
|
{
|
|
return Encoding.UTF8.GetString(bytes);
|
|
}
|
|
catch { }
|
|
}
|
|
return "";
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Common
|
|
|
|
T OnError<T>(string message, T @return)
|
|
{
|
|
Error?.Invoke(message);
|
|
return @return;
|
|
}
|
|
|
|
object OnError(string message) => OnError<object>(message, null);
|
|
|
|
string ReadLine()
|
|
{
|
|
var sb = new StringBuilder();
|
|
int c;
|
|
while ((c = buffered.ReadByte()) != -1)
|
|
{
|
|
if (c == '\r') continue;
|
|
if (c == '\n') break;
|
|
sb.Append((char)c);
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
|
|
byte[] ReadData()
|
|
{
|
|
string line = ReadLine();
|
|
if (line.Length < 1) return null;
|
|
if (line == "$-1") return null;
|
|
|
|
char flag = line[0];
|
|
if (flag == '-') return OnError(line.Substring(1), null as byte[]);
|
|
if (flag == '$')
|
|
{
|
|
int length;
|
|
if (!int.TryParse(line.Substring(1), out length)) return null;
|
|
|
|
var buffer = new byte[length];
|
|
int writted = 0;
|
|
while (writted < length)
|
|
{
|
|
int read = buffered.Read(buffer, writted, length - writted);
|
|
if (read < 1) return null;
|
|
writted += read;
|
|
}
|
|
|
|
if (buffered.ReadByte() != '\r' || buffered.ReadByte() != '\n') return null;
|
|
return buffer;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
bool Send(string head, byte[] data)
|
|
{
|
|
if (string.IsNullOrEmpty(head)) return false;
|
|
if (!string.IsNullOrEmpty(Connect())) return false;
|
|
|
|
byte[] HeadBytes = ToBytes(head);
|
|
try
|
|
{
|
|
socket.Send(HeadBytes);
|
|
if (!head.EndsWith("\r\n")) socket.Send(CRLF);
|
|
if (data != null)
|
|
{
|
|
if (data.Length > 0) socket.Send(data);
|
|
socket.Send(CRLF);
|
|
}
|
|
return true;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// SocketException 已超时。
|
|
socket.Close();
|
|
socket = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <exception cref="ArgumentException"></exception>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
bool SendCommand(byte[] data, string cmd, params object[] args)
|
|
{
|
|
if (string.IsNullOrEmpty(cmd)) throw new ArgumentException(nameof(cmd));
|
|
|
|
var argsLength = (args != null || args is object[]) ? args.Length : 0;
|
|
argsLength += 1; // cmd
|
|
if (data != null) argsLength += 1; // data
|
|
|
|
var sb = new StringBuilder();
|
|
sb.Append("*");
|
|
sb.Append(argsLength.ToString());
|
|
sb.Append("\r\n");
|
|
|
|
sb.Append("$");
|
|
sb.Append(cmd.Length);
|
|
sb.Append("\r\n");
|
|
sb.Append(cmd);
|
|
sb.Append("\r\n");
|
|
|
|
if (args != null)
|
|
{
|
|
foreach (var arg in args)
|
|
{
|
|
var line = arg == null ? "" : arg.ToString();
|
|
sb.Append("$");
|
|
sb.Append(Length(line).ToString());
|
|
sb.Append("\r\n");
|
|
sb.Append(line);
|
|
sb.Append("\r\n");
|
|
}
|
|
}
|
|
|
|
if (data != null)
|
|
{
|
|
sb.Append("$");
|
|
sb.Append(data.Length.ToString());
|
|
sb.Append("\r\n");
|
|
}
|
|
|
|
return Send(sb.ToString(), data);
|
|
}
|
|
|
|
/// <exception cref="ArgumentException"></exception>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
bool SendCommand(string cmd, params object[] args) => SendCommand(null, cmd, args);
|
|
|
|
string SendBySuccess(string cmd = null, params object[] args)
|
|
{
|
|
if (!string.IsNullOrEmpty(cmd))
|
|
{
|
|
if (!SendCommand(cmd, args)) return "连接 Redis 服务器失败。";
|
|
}
|
|
|
|
// 获取错误信息。
|
|
int c = buffered.ReadByte();
|
|
if (c == -1) return "没有更多字节。";
|
|
|
|
string s = ReadLine();
|
|
if (c == '-') return s.StartsWith("ERR ") ? s.Substring(4) : s;
|
|
|
|
return null;
|
|
}
|
|
|
|
long SendByInt(byte[] data, string cmd, params object[] args)
|
|
{
|
|
if (!SendCommand(data, cmd, args)) return 0;
|
|
|
|
int c = buffered.ReadByte();
|
|
if (c == -1) return 0;
|
|
|
|
string line = ReadLine();
|
|
if (c == '-') return 0;
|
|
if (c == ':')
|
|
{
|
|
if (long.TryParse(line, out long value)) return value;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
long SendByInt(string cmd, params object[] args) => SendByInt(null, cmd, args);
|
|
|
|
string SendByString(string cmd, params object[] args)
|
|
{
|
|
if (!SendCommand(cmd, args)) return null;
|
|
|
|
int c = buffered.ReadByte();
|
|
if (c == -1) return null;
|
|
|
|
var line = ReadLine();
|
|
if (line.Length > 0)
|
|
{
|
|
if (line[0] == '-') return OnError(line, line);
|
|
if (line[0] == '+') return OnError(line, line);
|
|
}
|
|
return line;
|
|
}
|
|
|
|
byte[] SendByData(string cmd, params object[] args) => SendCommand(cmd, args) ? ReadData() : null;
|
|
|
|
/// <summary></summary>
|
|
byte[][] SendByDataArray(string cmd, params object[] args)
|
|
{
|
|
if (!SendCommand(cmd, args)) return null;
|
|
|
|
int c = buffered.ReadByte();
|
|
if (c == -1) return null;
|
|
|
|
string s = ReadLine();
|
|
if (c == '-') return OnError(s, null as byte[][]);
|
|
if (c == '*')
|
|
{
|
|
int count;
|
|
if (int.TryParse(s, out count))
|
|
{
|
|
byte[][] result = new byte[count][];
|
|
for (int i = 0; i < count; i++) result[i] = ReadData();
|
|
return result;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary></summary>
|
|
string[] SendByStringArray(string cmd, params object[] args)
|
|
{
|
|
byte[][] reply = SendByDataArray(cmd, args);
|
|
if (reply == null) return null;
|
|
|
|
string[] keys = new string[reply.Length];
|
|
for (int i = 0; i < reply.Length; i++) keys[i] = ToText(reply[i]);
|
|
|
|
return keys;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DB
|
|
|
|
/// <summary>获取服务器信息。</summary>
|
|
public Dictionary<string, string> Info()
|
|
{
|
|
byte[] r = SendByData("INFO");
|
|
var dict = new Dictionary<string, string>();
|
|
foreach (var line in ToText(r).Split('\n'))
|
|
{
|
|
int p = line.IndexOf(':');
|
|
if (p < 0) continue;
|
|
dict.Add(line.Substring(0, p), line.Substring(p + 1));
|
|
}
|
|
return dict;
|
|
}
|
|
|
|
/// <summary>选择数据库,默认为 0。</summary>
|
|
/// <returns>错误信息。</returns>
|
|
public string SelectDB(int db = 0)
|
|
{
|
|
_db = db;
|
|
return SendBySuccess("SELECT", _db);
|
|
}
|
|
|
|
/// <summary>清空 DB。</summary>
|
|
/// <returns>错误信息。</returns>
|
|
public string Flush(bool allDB = false)
|
|
{
|
|
return allDB ? SendBySuccess("FLUSHALL") : SendBySuccess("FLUSHDB");
|
|
}
|
|
|
|
/// <summary>执行同步保存操作,将所有数据的快照以 RDB 文件的形式保存到磁盘上。</summary>
|
|
/// <returns>错误信息。</returns>
|
|
public string Save(bool background = true)
|
|
{
|
|
return background ? SendBySuccess("BGSAVE") : SendBySuccess("SAVE");
|
|
}
|
|
|
|
/// <summary>以 UNIX 时间戳格式返回最近一次 Redis 成功将数据保存到磁盘上的时间。</summary>
|
|
public DateTime LastSave()
|
|
{
|
|
const long UnixEpoch = 621355968000000000L;
|
|
long t = SendByInt("LASTSAVE");
|
|
return new DateTime(UnixEpoch) + TimeSpan.FromSeconds(t);
|
|
}
|
|
|
|
/// <summary>断开所有客户端,并关闭 Redis 服务进程。</summary>
|
|
public void Shutdown()
|
|
{
|
|
SendCommand("SHUTDOWN");
|
|
try
|
|
{
|
|
string s = ReadLine(); // 服务器可能会返回错误。
|
|
if (s.Length == 0) return; // throw new ResponseException("Zero length respose");
|
|
// throw new ResponseException(s.StartsWith("-ERR ") ? s.Substring(5) : s.Substring(1));
|
|
}
|
|
catch (IOException)
|
|
{
|
|
// this is the expected good result
|
|
socket.Close();
|
|
socket = null;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Key
|
|
|
|
/// <summary>获取 DB 中 Keys 的数量。</summary>
|
|
/// <remarks>执行 DBSIZE 命令。</remarks>
|
|
public long CountKeys()
|
|
{
|
|
return SendByInt("DBSIZE");
|
|
}
|
|
|
|
/// <summary>列出 Keys。</summary>
|
|
/// <remarks>执行 KEYS 命令。</remarks>
|
|
/// <returns>发生错误时返回 NULL 值。</returns>
|
|
public string[] ListKeys(string pattern = "*")
|
|
{
|
|
var p = string.IsNullOrEmpty(pattern) ? "*" : pattern;
|
|
return SendByStringArray("KEYS", p);
|
|
}
|
|
|
|
/// <summary>包含指定 Key。</summary>
|
|
public bool ContainsKey(string key)
|
|
{
|
|
if (key == null) return false;
|
|
return SendByInt("EXISTS", key) == 1;
|
|
}
|
|
|
|
/// <summary>删除指定 Key。</summary>
|
|
public bool Delete(string key)
|
|
{
|
|
if (key == null) return false; // throw new ArgumentNullException("key");
|
|
return SendByInt("DEL", key) == 1;
|
|
}
|
|
|
|
/// <summary>删除指定的多个 Key。不存在的 key 会被忽略。</summary>
|
|
/// <returns>删除的 Key 的数量。</returns>
|
|
public long Delete(params string[] keys)
|
|
{
|
|
if (keys == null || keys.Length < 1) return 0;
|
|
return SendByInt("DEL", keys);
|
|
}
|
|
|
|
/// <summary>获取 Key 的值类型,正确类型为 none | string | set | list。</summary>
|
|
public string TypeOf(string key)
|
|
{
|
|
if (key == null) return "";
|
|
return SendByString("TYPE", key);
|
|
}
|
|
|
|
/// <summary>随机返回一个已有的 Key。</summary>
|
|
public string RandomKey()
|
|
{
|
|
return SendByString("RANDOMKEY");
|
|
}
|
|
|
|
/// <summary>对 Key 重命名。</summary>
|
|
/// <remarks>已经存在的 newKey 会被覆盖。</remarks>
|
|
public bool Rename(string oldKey, string newKey)
|
|
{
|
|
// Exceptions
|
|
if (string.IsNullOrEmpty(oldKey)) return false;
|
|
if (string.IsNullOrEmpty(newKey)) return false;
|
|
|
|
return SendByString("RENAME", oldKey, newKey)[0] == '+';
|
|
}
|
|
|
|
/// <summary>设置 Key 的过期时间,过期后 Key 会被自动删除。</summary>
|
|
/// <remarks>在 Redis 2.1.3 之前的版本中,修改已经设置过生存时间的 Key,将会和删除 Key 有同样的效果。</remarks>
|
|
public bool Expire(string key, int seconds)
|
|
{
|
|
// Exceptions
|
|
if (string.IsNullOrEmpty(key)) return false;
|
|
if (seconds < 1) return false;
|
|
|
|
return SendByInt("EXPIRE", key, seconds) == 1L;
|
|
}
|
|
|
|
/// <summary>设置 Key 的过期时间,过期后 Key 会被自动删除。</summary>
|
|
public bool ExpireAt(string key, DateTime time)
|
|
{
|
|
// Exceptions
|
|
if (string.IsNullOrEmpty(key)) return false;
|
|
|
|
var stamp = Convert.ToInt64((time - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds);
|
|
return SendByInt("EXPIREAT", key, stamp) == 1L;
|
|
}
|
|
|
|
/// <summary>获取 Key 过期前的剩余时间。</summary>
|
|
/// <remarks>秒数。当 Key 不存在时返回 -1 值。</remarks>
|
|
public long TimeToLive(string key)
|
|
{
|
|
// Exceptions
|
|
if (string.IsNullOrEmpty(key)) return -1;
|
|
|
|
return SendByInt("TTL", key);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Bytes 字节数组
|
|
|
|
/// <summary>获取值。Key 无效时获取 NULL 值。</summary>
|
|
public byte[] GetBytes(string key)
|
|
{
|
|
// Exceptions
|
|
if (string.IsNullOrEmpty(key)) return null;
|
|
|
|
return SendByData("GET", key);
|
|
}
|
|
|
|
/// <summary>获取多个 Key 值。</summary>
|
|
/// <returns>参数无效时返回 NULL 值。</returns>
|
|
public byte[][] GetMultipleBytes(params string[] keys)
|
|
{
|
|
// Exceptions
|
|
if (keys == null || keys.Length == 0) return null;
|
|
|
|
return SendByDataArray("MGET", keys);
|
|
}
|
|
|
|
/// <summary>设置字节数组。</summary>
|
|
/// <returns>错误信息。</returns>
|
|
public string SetBytes(string key, byte[] bytes, bool replace = true)
|
|
{
|
|
// Exceptions
|
|
if (key == null) return "Key 无效。";
|
|
if (bytes == null) return "Bytes 无效";
|
|
if (bytes.Length > MaxLength) return "Value[] 长度超出限制。";
|
|
|
|
var cmd = replace ? "SET" : "SETNX";
|
|
var success = SendCommand(bytes, cmd, key);
|
|
if (!success) return "设置失败。";
|
|
|
|
return SendBySuccess();
|
|
}
|
|
|
|
/// <summary>设置多个值。</summary>
|
|
/// <returns>错误信息。</returns>
|
|
public string SetBytes(IEnumerable<KeyValuePair<string, byte[]>> pairs)
|
|
{
|
|
// Exceptions
|
|
if (pairs == null) return "键值对无效。";
|
|
|
|
var bytes = null as byte[];
|
|
var keys = new List<string>();
|
|
using (var memory = new MemoryStream())
|
|
{
|
|
foreach (var pair in pairs)
|
|
{
|
|
if (string.IsNullOrEmpty(pair.Key)) continue;
|
|
if (keys.Contains(pair.Key)) continue;
|
|
|
|
var keyBytes = ToBytes(pair.Key);
|
|
var keyLength = ToBytes("$" + keyBytes.Length.ToString() + "\r\n");
|
|
memory.Write(keyLength, 0, keyLength.Length);
|
|
memory.Write(keyBytes, 0, keyBytes.Length);
|
|
memory.Write(CRLF, 0, CRLF.Length);
|
|
|
|
var valueBytes = pair.Value ?? new byte[0];
|
|
var valueLength = ToBytes("$" + valueBytes.Length + "\r\n");
|
|
memory.Write(valueLength, 0, valueLength.Length);
|
|
memory.Write(valueBytes, 0, valueBytes.Length);
|
|
memory.Write(CRLF, 0, CRLF.Length);
|
|
|
|
keys.Add(pair.Key);
|
|
}
|
|
if (keys.Count > 0) bytes = memory.ToArray();
|
|
}
|
|
|
|
if (keys.Count < 1 || bytes == null || bytes.Length < 1) return "无法生成指令。";
|
|
|
|
var head = "*" + (keys.Count * 2 + 1).ToString() + "\r\n$4\r\nMSET\r\n";
|
|
var sent = Send(head, bytes);
|
|
if (!sent) return "发送指令失败。";
|
|
return SendBySuccess();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Text
|
|
|
|
/// <summary>获取值。Key 无效时获取 NULL 值。</summary>
|
|
public string GetText(string key)
|
|
{
|
|
// Exceptions
|
|
if (key == null) return null;
|
|
|
|
var bytes = GetBytes(key);
|
|
if (bytes == null) return null;
|
|
return ToText(bytes);
|
|
}
|
|
|
|
/// <summary>设置 UTF-8 文本。返回错误信息。</summary>
|
|
public string SetText(string key, string value, bool replace = true)
|
|
{
|
|
// Exceptions
|
|
if (key == null) return "Key 无效。";
|
|
if (value == null) return "Value 无效";
|
|
|
|
var bytes = value.Length < 1 ? new byte[0] : ToBytes(value);
|
|
return SetBytes(key, bytes, replace);
|
|
}
|
|
|
|
/// <summary>设置多个值。返回错误信息。</summary>
|
|
public string SetText(IEnumerable<KeyValuePair<string, string>> pairs)
|
|
{
|
|
// Exceptions
|
|
if (pairs == null) return "键值对无效。";
|
|
|
|
var list = new List<KeyValuePair<string, byte[]>>();
|
|
foreach (var pair in pairs)
|
|
{
|
|
var value = ToBytes(pair.Value);
|
|
list.Add(new KeyValuePair<string, byte[]>(pair.Key, value));
|
|
}
|
|
return SetBytes(list);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Value 数值
|
|
|
|
/// <summary>增大 Key 中储存的数值,当 Key 不存在,则先初始化为 0,再执行操作。</summary>
|
|
/// <remarks>参数 By 为 1 时执行 INCR,否则执行 INCRBY。</remarks>
|
|
/// <returns>执行操作后的值。发生错误时返回 0 值。</returns>
|
|
public long Increment(string key, long by = 1)
|
|
{
|
|
// Exceptions
|
|
if (string.IsNullOrEmpty(key)) return 0;
|
|
|
|
if (by == 1) return SendByInt("INCR", key);
|
|
else return SendByInt("INCRBY", key, by);
|
|
}
|
|
|
|
/// <summary>减小 Key 中储存的数值,当 Key 不存在,则先初始化为 0,再执行操作。</summary>
|
|
/// <remarks>参数 By 为 1 时执行 DECR,否则执行 DECRBY。</remarks>
|
|
/// <returns>执行操作后的值。发生错误时返回 0 值。</returns>
|
|
public long Decrement(string key, long by = 1)
|
|
{
|
|
// Exceptions
|
|
if (string.IsNullOrEmpty(key)) return 0;
|
|
|
|
if (by == 1) return SendByInt("DECR", key);
|
|
return SendByInt("DECRBY", key, by);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Set 集合。
|
|
|
|
/// <summary>返回 list, set 或 sorted set 中的元素。</summary>
|
|
/// <returns>排序后的元素列表。</returns>
|
|
public byte[][] Sort(string key, int skip = 0, int count = 0, bool alpha = false, bool desc = false, string by = null, string get = null)
|
|
{
|
|
// Exceptions
|
|
if (string.IsNullOrEmpty(key)) return null;
|
|
|
|
var array = new ArrayList();
|
|
array.Add(key);
|
|
if (skip > 0 || count > 0)
|
|
{
|
|
array.Add("LIMIT");
|
|
array.Add(skip > 0 ? skip : 0);
|
|
array.Add(count > 0 ? count : 0);
|
|
}
|
|
if (alpha) array.Add("ALPHA");
|
|
if (desc) array.Add("DESC");
|
|
if (!string.IsNullOrEmpty(by))
|
|
{
|
|
array.Add("BY");
|
|
array.Add(by);
|
|
}
|
|
if (!string.IsNullOrEmpty(get))
|
|
{
|
|
array.Add("GET");
|
|
array.Add(get);
|
|
}
|
|
|
|
var options = array.ToArray();
|
|
return SendByDataArray("SORT", options);
|
|
}
|
|
|
|
/// <summary>存储 list, set 或 sorted set 中的元素。</summary>
|
|
/// <returns>存储在目的列表中元素个数。</returns>
|
|
public long Sort(string key, string store, int skip = 0, int count = 0, bool alpha = false, bool desc = false, string by = null, string get = null)
|
|
{
|
|
// Exceptions
|
|
if (string.IsNullOrEmpty(key)) return -1;
|
|
if (string.IsNullOrEmpty(store)) return -1;
|
|
|
|
var array = new ArrayList();
|
|
array.Add(key);
|
|
array.Add("STORE");
|
|
array.Add(store);
|
|
if (skip > 0 || count > 0)
|
|
{
|
|
array.Add("LIMIT");
|
|
array.Add(skip > 0 ? skip : 0);
|
|
array.Add(count > 0 ? count : 0);
|
|
}
|
|
if (alpha) array.Add("ALPHA");
|
|
if (desc) array.Add("DESC");
|
|
if (!string.IsNullOrEmpty(by))
|
|
{
|
|
array.Add("BY");
|
|
array.Add(by);
|
|
}
|
|
if (!string.IsNullOrEmpty(get))
|
|
{
|
|
array.Add("GET");
|
|
array.Add(get);
|
|
}
|
|
|
|
var options = array.ToArray();
|
|
return SendByInt("SORT", options);
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|