### Quick Start with FreeRedis Source: https://github.com/2881099/freeredis/blob/master/README.md Initialize a RedisClient, configure serialization, and set up command logging. Demonstrates basic Set and MSet operations, and retrieving values with Get and MGet. ```csharp public static RedisClient cli = new RedisClient("127.0.0.1:6379,password=123,defaultDatabase=13"); cli.Serialize = obj => JsonConvert.SerializeObject(obj); cli.Deserialize = (json, type) => JsonConvert.DeserializeObject(json, type); cli.Notice += (s, e) => Console.WriteLine(e.Log); //print command log cli.Set("key1", "value1"); cli.MSet("key1", "value1", "key2", "value2"); string value1 = cli.Get("key1"); string[] vals = cli.MGet("key1", "key2"); ``` -------------------------------- ### String Commands: Set, Get, Incr, Append Source: https://context7.com/2881099/freeredis/llms.txt Examples of using string commands for setting and getting values, including options for expiry, conditional setting (NX/XX), and keeping TTL. Also shows counter operations and appending to strings. ```csharp // SET with expiry cli.Set("session:abc", "user_data_payload", 3600); // expires in 1 hour cli.Set("counter", 0); // SET NX — only if key does not exist bool created = cli.SetNx("lock:res1", "owner", 30); // returns true on first call // SET XX — only if key already exists bool updated = cli.SetXx("session:abc", "new_payload", 7200); // SET with KEEPTTL (Redis 6+) cli.Set("session:abc", "refreshed_payload", keepTtl: true); // GET string val = cli.Get("session:abc"); // GET with deserialization MyUser user = cli.Get("user:42"); // GETDEL — atomically get and delete string deleted = cli.GetDel("one_time_token"); // MSET / MGET cli.MSet("k1", "v1", "k2", "v2", "k3", "v3"); string[] vals = cli.MGet("k1", "k2", "k3"); // ["v1","v2","v3"] // Counters long count = cli.Incr("page:hits"); // 1 count = cli.IncrBy("page:hits", 10); // 11 count = cli.Decr("page:hits"); // 10 decimal fl = cli.IncrByFloat("price:usd", 1.25m); // Append long newLen = cli.Append("log", "2024-01-01 INFO start\n"); // Async equivalents await cli.SetAsync("key", "value", 60); string v = await cli.GetAsync("key"); ``` -------------------------------- ### Connect to Master-Slave Redis Source: https://github.com/2881099/freeredis/blob/master/index.md Connects to a Redis setup with master-slave replication. Writes go to the master, and reads are distributed among slaves. ```csharp public static RedisClient cli = new RedisClient( "127.0.0.1:6379,password=123,defaultDatabase=13", "127.0.0.1:6380,password=123,defaultDatabase=13", "127.0.0.1:6381,password=123,defaultDatabase=13" ); var value = cli.Get("key1"); ``` -------------------------------- ### Connect to Redis Cluster Source: https://github.com/2881099/freeredis/blob/master/index.md Connects to a Redis Cluster setup using an array of connection string builders for the master nodes. ```csharp public static RedisClient cli = new RedisClient( new ConnectionStringBuilder[] { "192.168.0.2:7001", "192.168.0.2:7001", "192.168.0.2:7003" } ); ``` -------------------------------- ### Connect to Redis Sentinel Source: https://github.com/2881099/freeredis/blob/master/index.md Connects to a Redis Sentinel setup for high availability. Supports read/write splitting. ```csharp public static RedisClient cli = new RedisClient( "mymaster,password=123", new [] { "192.169.1.10:26379", "192.169.1.11:26379", "192.169.1.12:26379" }, true //是否读写分离 ); ``` -------------------------------- ### String Commands — Set / Get / Incr / Append Source: https://context7.com/2881099/freeredis/llms.txt Details the core string commands for storing and retrieving data, including setting values with expiry, conditional sets (NX/XX), getting values (with deserialization), and atomic get-and-delete operations. ```APIDOC ## String Commands — Set / Get / Incr / Append String commands store scalars, serialized objects, counters, and bit fields. All accept generic type parameters for transparent (de)serialization. ```csharp // SET with expiry cli.Set("session:abc", "user_data_payload", 3600); // expires in 1 hour cli.Set("counter", 0); // SET NX — only if key does not exist bool created = cli.SetNx("lock:res1", "owner", 30); // returns true on first call // SET XX — only if key already exists bool updated = cli.SetXx("session:abc", "new_payload", 7200); // SET with KEEPTTL (Redis 6+) cli.Set("session:abc", "refreshed_payload", keepTtl: true); // GET string val = cli.Get("session:abc"); // GET with deserialization MyUser user = cli.Get("user:42"); // GETDEL — atomically get and delete string deleted = cli.GetDel("one_time_token"); // MSET / MGET cli.MSet("k1", "v1", "k2", "v2", "k3", "v3"); string[] vals = cli.MGet("k1", "k2", "k3"); // ["v1","v2","v3"] // Counters long count = cli.Incr("page:hits"); // 1 count = cli.IncrBy("page:hits", 10); // 11 count = cli.Decr("page:hits"); // 10 decimal fl = cli.IncrByFloat("price:usd", 1.25m); // Append long newLen = cli.Append("log", "2024-01-01 INFO start\n"); // Async equivalents await cli.SetAsync("key", "value", 60); string v = await cli.GetAsync("key"); ``` ``` -------------------------------- ### Redis Sentinel Configuration Source: https://github.com/2881099/freeredis/blob/master/README.md Connect to a Redis Sentinel setup. The first parameter is the master name, followed by an array of Sentinel addresses. The boolean parameter enables read-write separation. ```csharp public static RedisClient cli = new RedisClient( "mymaster,password=123", new [] { "192.168.1.10:26379", "192.168.1.11:26379", "192.168.1.12:26379" }, true //This variable indicates whether to use the read-write separation mode. ); ``` -------------------------------- ### Distributed Cache Integration Source: https://github.com/2881099/freeredis/blob/master/README.md Example of integrating FreeRedis with .NET's IDistributedCache. Ensure the RedisClient instance is properly initialized before use. ```csharp //FreeRedis.DistributedCache //services.AddSingleton(new FreeRedis.DistributedCache(cli)); ``` -------------------------------- ### Execute Redis Pipeline Commands Source: https://github.com/2881099/freeredis/blob/master/index.md Use pipelines to batch multiple Redis commands for improved performance. This example shows synchronous execution. ```csharp using (var pipe = cli.StartPipe()) { pipe.IncrBy("key1", 10); pipe.Set("key2", Null); pipe.Get("key1"); object[] ret = pipe.EndPipe(); Console.WriteLine(ret[0] + ", " + ret[2]); } ``` -------------------------------- ### Set Commands Source: https://context7.com/2881099/freeredis/llms.txt Operations for managing sets, including adding members, checking membership, removing members, getting cardinality, retrieving all members, performing set operations like intersection, union, and difference, and iterating over large sets. ```APIDOC ## Set Commands — SAdd / SMembers / SInter / SDiff Sets store unique, unordered string members and support intersection, union, and difference operations. ### Add members ```csharp long added = cli.SAdd("tags:post1", "redis", "dotnet", "csharp"); cli.SAdd("tags:post2", "redis", "python"); ``` ### Check membership / remove ```csharp bool isMember = cli.SIsMember("tags:post1", "redis"); // true long removed = cli.SRem("tags:post1", "csharp"); ``` ### Cardinality and all members ```csharp long count = cli.SCard("tags:post1"); string[] mems = cli.SMembers("tags:post1"); ``` ### Set operations ```csharp string[] inter = cli.SInter("tags:post1", "tags:post2"); // ["redis"] string[] union = cli.SUnion("tags:post1", "tags:post2"); string[] diff = cli.SDiff("tags:post1", "tags:post2"); // tags only in post1 ``` ### Store results ```csharp cli.SInterStore("common_tags", "tags:post1", "tags:post2"); ``` ### Random pop / sample ```csharp string popped = cli.SPop("tags:post1"); string[] sample = cli.SRandMember("tags:post1", 2); ``` ### Move a member between sets ```csharp bool ok = cli.SMove("tags:post1", "tags:post2", "dotnet"); ``` ### SSCAN — iterate large sets ```csharp ScanResult page = cli.SScan("tags:post1", 0, "*", 50); Console.WriteLine(string.Join(", ", page.items)); ``` ### Async ```csharp await cli.SAddAsync("tags:post1", "java"); bool exists = await cli.SIsMemberAsync("tags:post1", "java"); ``` ``` -------------------------------- ### Subscribe to Redis Streams Source: https://github.com/2881099/freeredis/blob/master/README.md Subscribes to a Redis stream and processes messages. This example also shows how to check pending messages using XPending. ```csharp using (cli.SubscribeStream("stream_key", ondata)) //wait .Dispose() { Console.ReadKey(); } void ondata(Dictionary streamValue) => Console.WriteLine(JsonConvert.SerializeObject(streamValue)); // NoAck xpending cli.XPending("stream_key", "FreeRedis__group", "-", "+", 10); ``` -------------------------------- ### Execute Redis Transaction Commands Source: https://github.com/2881099/freeredis/blob/master/index.md Utilize Redis transactions (MULTI/EXEC) to ensure atomicity of a group of commands. This example shows synchronous execution. ```csharp using (var tran = cli.Multi()) { tran.IncrBy("key1", 10); tran.Set("key2", Null); tran.Get("key1"); object[] ret = tran.Exec(); Console.WriteLine(ret[0] + ", " + ret[2]); } ``` -------------------------------- ### RedisClient Constructors and Connection Source: https://context7.com/2881099/freeredis/llms.txt Demonstrates different ways to construct a RedisClient instance for pooled single/master-slave, Redis Cluster, and Redis Sentinel configurations. Includes attaching custom serializers and logging. ```csharp using FreeRedis; using Newtonsoft.Json; // --- Pooled / Master-Slave --- public static RedisClient cli = new RedisClient( "127.0.0.1:6379,password=mypass,defaultDatabase=0,max poolsize=50,ssl=false", "127.0.0.1:6380,password=mypass", // slave 1 "127.0.0.1:6381,password=mypass" // slave 2 ); // Writes go to :6379; reads randomly distributed across :6380 and :6381. // Attach JSON serializer for complex types cli.Serialize = obj => JsonConvert.SerializeObject(obj); cli.Deserialize = (json, type) => JsonConvert.DeserializeObject(json, type); // Log every command cli.Notice += (s, e) => Console.WriteLine(e.Log); // --- Redis Cluster --- public static RedisClient cluster = new RedisClient(new ConnectionStringBuilder[] { "192.168.0.2:7001", "192.168.0.2:7002", "192.168.0.2:7003" }); // --- Redis Sentinel --- public static RedisClient sentinel = new RedisClient( "mymaster,password=mypass", new[] { "192.169.1.10:26379", "192.169.1.11:26379", "192.169.1.12:26379" }, true // enable read/write splitting ); ``` -------------------------------- ### RedisClient Constructors and Connection Source: https://context7.com/2881099/freeredis/llms.txt Demonstrates how to instantiate the RedisClient for different connection modes: pooled single/master-slave, Redis Cluster, and Redis Sentinel. It also shows how to attach custom serializers and logging. ```APIDOC ## RedisClient — Constructors and Connection `RedisClient` supports four construction modes: pooled single/master-slave, Redis Cluster, Norman (custom routing), and Sentinel. The connection string is implicitly convertible from a plain `string`. ```csharp using FreeRedis; using Newtonsoft.Json; // --- Pooled / Master-Slave --- public static RedisClient cli = new RedisClient( "127.0.0.1:6379,password=mypass,defaultDatabase=0,max poolsize=50,ssl=false", "127.0.0.1:6380,password=mypass", // slave 1 "127.0.0.1:6381,password=mypass" // slave 2 ); // Writes go to :6379; reads randomly distributed across :6380 and :6381. // Attach JSON serializer for complex types cli.Serialize = obj => JsonConvert.SerializeObject(obj); cli.Deserialize = (json, type) => JsonConvert.DeserializeObject(json, type); // Log every command cli.Notice += (s, e) => Console.WriteLine(e.Log); // --- Redis Cluster --- public static RedisClient cluster = new RedisClient(new ConnectionStringBuilder[] { "192.168.0.2:7001", "192.168.0.2:7002", "192.168.0.2:7003" }); // --- Redis Sentinel --- public static RedisClient sentinel = new RedisClient( "mymaster,password=mypass", new[] { "192.169.1.10:26379", "192.169.1.11:26379", "192.169.1.12:26379" }, true // enable read/write splitting ); ``` ``` -------------------------------- ### Connect to Single Redis Instance Source: https://github.com/2881099/freeredis/blob/master/index.md Establishes a connection to a single Redis instance with optional password and database selection. Logs commands to the console. ```csharp public static RedisClient cli = new RedisClient("127.0.0.1:6379,password=123,defaultDatabase=13"); //cli.Serialize = obj => JsonConvert.SerializeObject(obj); //cli.Deserialize = (json, type) => JsonConvert.DeserializeObject(json, type); cli.Notice += (s, e) => Console.WriteLine(e.Log); //print command log cli.Set("key1", "value1"); cli.MSet("key1", "value1", "key2", "value2"); string value1 = cli.Get("key1"); string[] vals = cli.MGet("key1", "key2"); ``` -------------------------------- ### Pipelining (StartPipe, EndPipe) Source: https://context7.com/2881099/freeredis/llms.txt Batch multiple commands into a single network round-trip for improved performance. All results are returned as an object array. ```APIDOC ## Pipeline — StartPipe / EndPipe Pipelining batches multiple commands into a single network round-trip and returns all results as an `object[]` array. ```csharp using (var pipe = cli.StartPipe()) { pipe.Set("p:key1", "hello"); pipe.IncrBy("p:counter", 5); pipe.Get("p:key1"); pipe.HSet("p:hash", "field1", "val1"); pipe.LPush("p:list", "a", "b", "c"); object[] results = pipe.EndPipe(); // results[0] = "OK" (SET) // results[1] = 5L (INCRBY) // results[2] = "hello" (GET) // results[3] = 1L (HSET new fields) // results[4] = 3L (LPUSH length) Console.WriteLine($"Counter={results[1]}, Key1={results[2]}"); } ``` ``` -------------------------------- ### ConnectionStringBuilder Parsing Source: https://context7.com/2881099/freeredis/llms.txt Demonstrates how to parse a connection string into a ConnectionStringBuilder object to access individual connection parameters. Shows how to convert back to a string. ```csharp var csb = ConnectionStringBuilder.Parse( "127.0.0.1:6379,password=secret,defaultDatabase=2," + "max poolsize=100,min poolsize=5," + "connectTimeout=10000,receiveTimeout=20000,sendTimeout=20000," + "idleTimeout=60000,ssl=false,prefix=app:,encoding=utf-8,retry=3" ); Console.WriteLine(csb.Host); // "127.0.0.1:6379" Console.WriteLine(csb.Database); // 2 Console.WriteLine(csb.Prefix); // "app:" Console.WriteLine(csb.MaxPoolSize); // 100 // Round-trip back to string string connStr = csb.ToString(); // "127.0.0.1:6379,password=secret,database=2,prefix=app:,retry=3, ..." ``` -------------------------------- ### Select Redis Database Source: https://github.com/2881099/freeredis/blob/master/index.md Demonstrates how to select and operate on a specific Redis database using GetDatabase. Ensure the database index is valid. ```csharp using (var db = cli.GetDatabase(10)) { db.Set("key1", 10); var val1 = db.Get("key1"); } ``` -------------------------------- ### Client-Side Caching Configuration Source: https://context7.com/2881099/freeredis/llms.txt Shows how to enable and configure client-side caching for GET/MGET/HGETALL/HGET/HMGET commands to improve read performance by caching results in process memory. ```APIDOC ## Client-Side Caching — UseClientSideCaching Client-side caching (requires Redis 6+) transparently caches `GET`/`MGET`/`HGETALL`/`HGET`/`HMGET` results in process memory and invalidates them via the `__redis__:invalidate` channel. ```csharp cli.UseClientSideCaching(new ClientSideCachingOptions { // Max cached keys (LRU eviction when full) Capacity = 1000, // Only cache keys matching this predicate KeyFilter = key => key.StartsWith("product:") || key.StartsWith("config:"), // Treat cached entry as expired if not accessed for > 2 minutes CheckExpired = (key, cachedAt) => DateTime.Now.Subtract(cachedAt) > TimeSpan.FromMinutes(2) }); // Subsequent reads hit local memory instead of Redis (until invalidated) string p1 = cli.Get("product:123"); // first call → Redis string p2 = cli.Get("product:123"); // second call → local cache ``` ``` -------------------------------- ### Redis Pub/Sub Commands in C# Source: https://context7.com/2881099/freeredis/llms.txt Implement publish/subscribe messaging patterns using Redis. Supports subscribing to exact channels, multiple channels, or pattern matching. Unsubscribe explicitly or implicitly via `IDisposable`. Inspect active subscriptions. ```csharp // Subscribe to exact channel using (cli.Subscribe("news:tech", (channel, data) => { Console.WriteLine($"[{channel}] {data}"); })) { Console.ReadKey(); } // unsubscribes on Dispose // Subscribe to multiple channels var sub = cli.Subscribe(new[] { "alerts:info", "alerts:warn" }, (channel, data) => Console.WriteLine($"[{channel}] {data}")); // Pattern subscribe using (cli.PSubscribe("news:*", (channel, data) => Console.WriteLine($"Pattern hit: {channel} -> {data}"))) { // Publish from another client cli.Publish("news:tech", "New .NET release!"); Console.ReadKey(); } // Async publish await cli.PublishAsync("alerts:info", "Server CPU high"); // Inspect active subscriptions string[] channels = cli.PubSubChannels("news:*"); long numSubs = cli.PubSubNumSub("news:tech"); // Explicit unsubscribe sub.Dispose(); cli.UnSubscribe("alerts:info"); cli.PUnSubscribe("news:*"); ``` -------------------------------- ### Execute Redis Pipeline Commands Asynchronously Source: https://github.com/2881099/freeredis/blob/master/index.md Demonstrates asynchronous execution of Redis pipeline commands using callbacks. Ensure all tasks are awaited. ```csharp using (var pipe = cli.StartPipe()) { var tasks = new List(); long t0 = 0; task.Add(pipe.IncrByAsync("key1", 10).ContinueWith(t => t0 = t.Result)); //callback pipe.SetAsync("key2", Null); string t2 = null; task.Add(pipe.GetAsync("key1").ContinueWith(t => t2 = t.Result)); //callback pipe.EndPipe(); Task.WaitAll(tasks.ToArray()); //wait all callback Console.WriteLine(t0 + ", " + t2); } ``` -------------------------------- ### Hash Commands - HSet / HGet / HGetAll Source: https://context7.com/2881099/freeredis/llms.txt Commands for managing hash data structures, which store field-value pairs under a single key. ```APIDOC ## Hash Commands — HSet / HGet / HGetAll Hashes store multiple field-value pairs under one key and support generic typed values. ### HSET — single or multiple fields - `cli.HSet(key, field, value)`: Sets a single field-value pair in a hash. - `cli.HSet(key, dictionary)`: Sets multiple field-value pairs from a dictionary. ### HGET / HMGET - `cli.HGet(key, field)`: Gets the value of a specific field in a hash. - `cli.HMGet(key, fields)`: Gets the values of multiple fields in a hash. ### HGETALL - `cli.HGetAll(key)`: Gets all field-value pairs from a hash. - `cli.HGetAll(key)`: Gets all field-value pairs deserialized into a specific type. ### HINCRBY / HINCRBYFLOAT - `cli.HIncrBy(key, field, increment)`: Increments the integer value of a field in a hash. - `cli.HIncrByFloat(key, field, increment)`: Increments the float value of a field in a hash. ### HEXISTS / HDEL / HLEN - `cli.HExists(key, field)`: Checks if a field exists in a hash. - `cli.HDel(key, fields)`: Deletes one or more fields from a hash. - `cli.HLen(key)`: Gets the number of fields in a hash. ### HKEYS / HVALS - `cli.HKeys(key)`: Gets all field names from a hash. - `cli.HVals(key)`: Gets all field values from a hash. ### HSCAN — lazy iteration - `cli.HScan(key, pattern, count)`: Iterates over fields and values in a hash matching a pattern. ### Async Operations - `await cli.HSetAsync(key, field, value)` - `await cli.HGetAsync(key, field)` ``` -------------------------------- ### Batch Commands with Pipeline Source: https://context7.com/2881099/freeredis/llms.txt Batches multiple commands into a single network round-trip for improved performance. All results are returned as an object array after EndPipe(). ```csharp using (var pipe = cli.StartPipe()) { pipe.Set("p:key1", "hello"); pipe.IncrBy("p:counter", 5); pipe.Get("p:key1"); pipe.HSet("p:hash", "field1", "val1"); pipe.LPush("p:list", "a", "b", "c"); object[] results = pipe.EndPipe(); Console.WriteLine($"Counter={results[1]}, Key1={results[2]}"); } ``` -------------------------------- ### RediSearch Integration Source: https://context7.com/2881099/freeredis/llms.txt Illustrates using FreeRedis to interact with RediSearch, covering both a low-level fluent builder API for creating indexes and searching, and an attribute-based document repository pattern. ```APIDOC ## RediSearch — FtCreate / FtSearch / FtDocumentRepository FreeRedis wraps the RediSearch module with fluent builders and an optional document-repository pattern backed by attributes. ```csharp // --- Low-level builder API --- cli.FtCreate("idx_products") .On("HASH").Prefix("product:") .Schema(s => s .TextField("name", weight: 5.0) .NumericField("price", sortable: true) .TagField("category")) .Execute(); var searchResult = cli.FtSearch("idx_products", "@name:redis @price:[10 100]") .Limit(0, 20) .SortBy("price", ascending: true) .Execute(); // --- Attribute-based document repository --- [FtDocument("idx_post", Prefix = "blog:post:")] class Post { [FtKey] public int Id { get; set; } [FtTextField("title", Weight = 5.0)] public string Title { get; set; } [FtTextField("content")] public string Content { get; set; } [FtTagField("tags")] public string Tags { get; set; } // comma-separated [FtNumericField("views", Sortable = true)] public int Views { get; set; } } var repo = cli.FtDocumentRepository(); repo.CreateIndex(); repo.Save(new Post { Id = 1, Title = "FreeRedis Guide", Content = "...", Tags = "redis,dotnet", Views = 500 }); repo.Save(new Post { Id = 2, Title = "Redis Clustering", Content = "...", Tags = "redis,cluster", Views = 300 }); var hits = repo.Search("@title:redis").Filter(p => p.Views, 100, 1000).ToList(); foreach (var post in hits) Console.WriteLine($"[{post.Id}] {post.Title} ({post.Views} views)"); repo.Delete(1, 2); ``` ``` -------------------------------- ### GetDatabase: switch database Source: https://github.com/2881099/freeredis/blob/master/README.md Allows switching to a different Redis database context for subsequent operations. ```APIDOC ## GetDatabase: switch database ### Description Allows switching to a different Redis database context for subsequent operations. ### Method ```csharp cli.GetDatabase(databaseIndex) ``` ### Parameters - **databaseIndex** (int) - The index of the database to switch to (0-15 by default). ### Example ```csharp using (var db = cli.GetDatabase(10)) { db.Set("key1", 10); var val1 = db.Get("key1"); } ``` ``` -------------------------------- ### Delay Queue Operations Source: https://context7.com/2881099/freeredis/llms.txt Demonstrates how to enqueue items with relative or absolute delays and how to consume messages asynchronously or synchronously from a delay queue. ```APIDOC ## Delay Queue — DelayQueue / Enqueue / DequeueAsync `DelayQueue` implements a Redis Sorted Set-backed delay queue with atomic Lua-script dequeue and multi-consumer support. ```csharp var dq = cli.DelayQueue("email_send_queue"); // Enqueue with relative delay dq.Enqueue("email:welcome:user42", TimeSpan.FromSeconds(5)); dq.Enqueue("email:followup:user42", TimeSpan.FromSeconds(60)); // Enqueue with absolute DateTime dq.Enqueue("email:promo:user42", DateTime.Now.AddHours(2)); // Consume — starts a background thread; safe for multiple consumers var cts = new CancellationTokenSource(); await dq.DequeueAsync(async message => { Console.WriteLine($"{DateTime.Now}: processing {message}"); await SendEmailAsync(message); }, choke: 400, token: cts); // Synchronous consume dq.Dequeue(message => { Console.WriteLine($"Sync: {message}"); }, choke: 500); // Stop consuming cts.Cancel(); ``` ``` -------------------------------- ### Execute Commands Transactionally Source: https://context7.com/2881099/freeredis/llms.txt Queues commands for atomic execution using MULTI/EXEC. Results are returned as an object array after Exec(). ```csharp using (var tran = cli.Multi()) { tran.Set("tran:key1", "value1"); tran.IncrBy("tran:counter", 10); tran.Get("tran:key1"); object[] results = tran.Exec(); Console.WriteLine($"Key={results[2]}, Counter={results[1]}"); } ``` -------------------------------- ### Manage Redis Lists Source: https://context7.com/2881099/freeredis/llms.txt Implement queues and stacks using Redis lists. Supports efficient push/pop operations from both ends, blocking operations for consumer scenarios, and range retrieval. Use `RPopLPush` for atomic move operations between lists. ```csharp // Push elements long len = cli.LPush("queue", "task3", "task2", "task1"); // head = task1 len = cli.RPush("queue", "task4"); // tail = task4 // Pop elements string head = cli.LPop("queue"); // "task1" string tail = cli.RPop("queue"); // "task4" // Blocking pop — waits up to 10 s for an element string item = cli.BLPop("queue", timeoutSeconds: 10); // Blocking pop across multiple keys KeyValue result = cli.BLPop(new[] { "q1", "q2" }, 5); if (result != null) Console.WriteLine($"Key={result.key} Value={result.value}"); // Range retrieval (0-based, -1 = last) string[] page = cli.LRange("queue", 0, 9); // first 10 elements // LInsert, LSet, LRem, LLen, LIndex cli.LInsert("queue", InsertDirection.before, "task3", "task2.5"); cli.LSet("queue", 0, "new_head"); long removed = cli.LRem("queue", 1, "task2"); // remove 1 occurrence long length = cli.LLen("queue"); string at1 = cli.LIndex("queue", 1); // Rotate: move last element of src to head of dst string moved = cli.RPopLPush("source", "dest"); // Async await cli.LPushAsync("queue", "async_task"); string[] items = await cli.LRangeAsync("queue", 0, -1); ``` -------------------------------- ### List Commands - LPush / RPush / BLPop / LRange Source: https://context7.com/2881099/freeredis/llms.txt Commands for managing list data structures, supporting operations at both ends, blocking pops, and range retrieval. ```APIDOC ## List Commands — LPush / RPush / BLPop / LRange Lists are ordered sequences supporting push/pop from both ends, blocking operations, and range retrieval. ### Push elements - `cli.LPush(key, values)`: Pushes elements to the head of a list. - `cli.RPush(key, values)`: Pushes elements to the tail of a list. ### Pop elements - `cli.LPop(key)`: Pops an element from the head of a list. - `cli.RPop(key)`: Pops an element from the tail of a list. ### Blocking pop - `cli.BLPop(key, timeoutSeconds)`: Blocks until an element is available at the head of the list or timeout occurs. - `cli.BLPop(keys, timeoutSeconds)`: Blocks until an element is available at the head of any of the specified lists. ### Range retrieval - `cli.LRange(key, start, stop)`: Retrieves a range of elements from a list (0-based, -1 = last). ### Other List Operations - `cli.LInsert(key, direction, pivot, value)`: Inserts a value before or after another element. - `cli.LSet(key, index, value)`: Sets the value of an element at a specific index. - `cli.LRem(key, count, value)`: Removes occurrences of a value from a list. - `cli.LLen(key)`: Gets the length of a list. - `cli.LIndex(key, index)`: Gets the element at a specific index. - `cli.RPopLPush(sourceKey, destinationKey)`: Atomically pops from source and pushes to destination. ### Async Operations - `await cli.LPushAsync(key, values)` - `await cli.LRangeAsync(key, start, stop)` ``` -------------------------------- ### Pub/Sub Commands Source: https://context7.com/2881099/freeredis/llms.txt Operations for real-time messaging using Publish/Subscribe pattern, including subscribing to channels, pattern subscriptions, publishing messages, and inspecting subscription status. ```APIDOC ## Pub/Sub — Subscribe / Publish / PSubscribe Pub/Sub delivers messages between publishers and subscribers over named channels or glob patterns. Subscriptions return `IDisposable` handles. ### Subscribe to exact channel ```csharp using (cli.Subscribe("news:tech", (channel, data) => { Console.WriteLine($"[{channel}] {data}"); })) { Console.ReadKey(); } // unsubscribes on Dispose ``` ### Subscribe to multiple channels ```csharp var sub = cli.Subscribe(new[] { "alerts:info", "alerts:warn" }, (channel, data) => Console.WriteLine($"[{channel}] {data}")); ``` ### Pattern subscribe ```csharp using (cli.PSubscribe("news:*", (channel, data) => Console.WriteLine($"Pattern hit: {channel} -> {data}"))) { // Publish from another client cli.Publish("news:tech", "New .NET release!"); Console.ReadKey(); } ``` ### Async publish ```csharp await cli.PublishAsync("alerts:info", "Server CPU high"); ``` ### Inspect active subscriptions ```csharp string[] channels = cli.PubSubChannels("news:*"); long numSubs = cli.PubSubNumSub("news:tech"); ``` ### Explicit unsubscribe ```csharp sub.Dispose(); cli.UnSubscribe("alerts:info"); cli.PUnSubscribe("news:*"); ``` ``` -------------------------------- ### Manage Redis Key Expiry and Metadata Source: https://context7.com/2881099/freeredis/llms.txt Control time-to-live (TTL) for keys, check their existence, and inspect their type. Use `Scan` for efficient key iteration, especially in clusters. `Dump` and `Restore` allow for key serialization and deserialization. ```csharp // Expiry cli.Set("tmp", "data"); cli.Expire("tmp", 120); // 120 seconds cli.PExpire("tmp", 120_000); // 120 000 milliseconds cli.ExpireAt("tmp", DateTime.UtcNow.AddHours(1)); // absolute DateTime cli.Persist("tmp"); // remove TTL // TTL inspection long ttlSec = cli.Ttl("tmp"); // seconds remaining, -1=persistent, -2=gone long ttlMs = cli.PTtl("tmp"); // milliseconds remaining // Existence and deletion bool exists = cli.Exists("tmp"); long count = cli.Exists(new[] { "k1", "k2", "k3" }); // count existing long removed = cli.Del("tmp", "k1"); long unlinked = cli.UnLink("big_key"); // async, non-blocking delete // Rename cli.Rename("old_name", "new_name"); bool ok = cli.RenameNx("old_name", "new_name"); // only if new_name absent // Type inspection KeyType kt = cli.Type("my_list"); // list / string / hash / set / zset / stream // Scan (cluster-aware, lazy IEnumerable) foreach (string[] batch in cli.Scan("user:*", 100, null)) Console.WriteLine($"Batch: {string.Join(", ", batch)}"); // Move / dump / restore bool moved = cli.Move("key", 3); // move to database 3 byte[] dump = cli.Dump("key"); cli.Restore("key_backup", dump); // Async await cli.ExpireAsync("tmp", 60); long remaining = await cli.TtlAsync("tmp"); ``` -------------------------------- ### Transactions (Multi, Exec) Source: https://context7.com/2881099/freeredis/llms.txt Execute a group of commands atomically using the MULTI/EXEC commands. All commands in the transaction are queued and executed sequentially. ```APIDOC ## Transaction — Multi / Exec Transactions queue commands with `MULTI`/`EXEC` for atomic execution. Results are returned as `object[]` after `Exec()`. ```csharp using (var tran = cli.Multi()) { tran.Set("tran:key1", "value1"); tran.IncrBy("tran:counter", 10); tran.Get("tran:key1"); object[] results = tran.Exec(); // results[0] = "OK" // results[1] = 10L // results[2] = "value1" Console.WriteLine($"Key={results[2]}, Counter={results[1]}"); } ``` ``` -------------------------------- ### Execute Redis Transaction Commands Asynchronously Source: https://github.com/2881099/freeredis/blob/master/index.md Shows asynchronous execution of Redis transaction commands using callbacks. All tasks must be awaited. ```csharp using (var tran = cli.Multi()) { var tasks = new List(); long t0 = 0; task.Add(tran.IncrByAsync("key1", 10).ContinueWith(t => t0 = t.Result)); //callback tran.SetAsync("key2", Null); string t2 = null; task.Add(tran.GetAsync("key1").ContinueWith(t => t2 = t.Result)); //callback tran.Exec(); Task.WaitAll(tasks.ToArray()); //wait all callback Console.WriteLine(t0 + ", " + t2); } ``` -------------------------------- ### Configure Client-Side Caching with FreeRedis Source: https://context7.com/2881099/freeredis/llms.txt Enable and configure client-side caching for Redis GET/MGET/HGETALL/HGET/HMGET commands using FreeRedis. Caching is transparently managed in process memory and invalidated via a dedicated Redis channel. Requires Redis 6+. ```csharp cli.UseClientSideCaching(new ClientSideCachingOptions { // Max cached keys (LRU eviction when full) Capacity = 1000, // Only cache keys matching this predicate KeyFilter = key => key.StartsWith("product:") || key.StartsWith("config:"), // Treat cached entry as expired if not accessed for > 2 minutes CheckExpired = (key, cachedAt) => DateTime.Now.Subtract(cachedAt) > TimeSpan.FromMinutes(2) }); // Subsequent reads hit local memory instead of Redis (until invalidated) string p1 = cli.Get("product:123"); // first call → Redis string p2 = cli.Get("product:123"); // second call → local cache ``` -------------------------------- ### Enable Client-Side Caching Source: https://github.com/2881099/freeredis/blob/master/index.md Configures client-side caching with options for capacity, key filtering, and checking expired items. Requires Redis server 6.0+. ```csharp cli.UseClientSideCaching(new ClientSideCachingOptions { //本地缓存的容量 Capacity = 3, //过滤哪些键能被本地缓存 KeyFilter = key => key.StartsWith("Interceptor"), //检查长期未使用的缓存 CheckExpired = (key, dt) => DateTime.Now.Subtract(dt) > TimeSpan.FromSeconds(2) }); ``` -------------------------------- ### RediSearch Document Repository with FreeRedis (Attribute-based) Source: https://context7.com/2881099/freeredis/llms.txt Implement a document repository pattern for RediSearch using attribute-based mapping in FreeRedis. This simplifies indexing, saving, searching, and deleting documents by defining a C# class with specific FreeRedis attributes. ```csharp // --- Attribute-based document repository --- [FtDocument("idx_post", Prefix = "blog:post:")] class Post { [FtKey] public int Id { get; set; } [FtTextField("title", Weight = 5.0)] public string Title { get; set; } [FtTextField("content")] public string Content { get; set; } [FtTagField("tags")] public string Tags { get; set; } // comma-separated [FtNumericField("views", Sortable = true)] public int Views { get; set; } } var repo = cli.FtDocumentRepository(); repo.CreateIndex(); repo.Save(new Post { Id = 1, Title = "FreeRedis Guide", Content = "...", Tags = "redis,dotnet", Views = 500 }); repo.Save(new Post { Id = 2, Title = "Redis Clustering", Content = "...", Tags = "redis,cluster", Views = 300 }); var hits = repo.Search("@title:redis").Filter(p => p.Views, 100, 1000).ToList(); foreach (var post in hits) Console.WriteLine($"[{post.Id}] {post.Title} ({post.Views} views)"); repo.Delete(1, 2); ``` -------------------------------- ### Key Commands - Expire / TTL / Del / Scan / Type Source: https://context7.com/2881099/freeredis/llms.txt Commands for managing key expiration, deletion, type inspection, and scanning keyspace. ```APIDOC ## Key Commands — Expire / TTL / Del / Scan / Type Key-space management commands control TTLs, iteration, and metadata introspection. ### Expiry - `cli.Set(key, data)`: Sets a key-value pair. - `cli.Expire(key, seconds)`: Sets a time-to-live in seconds. - `cli.PExpire(key, milliseconds)`: Sets a time-to-live in milliseconds. - `cli.ExpireAt(key, dateTime)`: Sets an absolute expiration time. - `cli.Persist(key)`: Removes the TTL from a key. ### TTL Inspection - `cli.Ttl(key)`: Gets the remaining time-to-live in seconds (-1=persistent, -2=gone). - `cli.PTtl(key)`: Gets the remaining time-to-live in milliseconds. ### Existence and Deletion - `cli.Exists(key)`: Checks if a key exists. - `cli.Exists(keys)`: Counts the number of existing keys from a list. - `cli.Del(keys)`: Deletes one or more keys. - `cli.UnLink(key)`: Asynchronously deletes a key. ### Rename - `cli.Rename(oldName, newName)`: Renames a key. - `cli.RenameNx(oldName, newName)`: Renames a key only if the new name does not exist. ### Type Inspection - `cli.Type(key)`: Gets the type of a key (list, string, hash, set, zset, stream). ### Scan - `cli.Scan(pattern, count, match)`: Iterates over keys matching a pattern (cluster-aware, lazy IEnumerable). ### Move / Dump / Restore - `cli.Move(key, database)`: Moves a key to a different database. - `cli.Dump(key)`: Returns a serialized representation of the key. - `cli.Restore(key, dump)`: Creates a key from a serialized representation. ### Async Operations - `await cli.ExpireAsync(key, seconds)` - `await cli.TtlAsync(key)` ``` -------------------------------- ### Execute Lua Scripts Source: https://context7.com/2881099/freeredis/llms.txt Executes Lua scripts atomically on the server. Pre-load scripts using ScriptLoad for efficiency. Handles inline scripts, atomic operations like increment-and-get with thresholds, and script management (exists, flush). ```csharp var result = cli.Eval( "return {KEYS[1], KEYS[2], ARGV[1], ARGV[2]}", keys: new[] { "key1", "key2" }, arguments: "first", "second" ) as object[]; ``` ```csharp long val = (long)(cli.Eval(@" local v = redis.call('INCR', KEYS[1]) if v > tonumber(ARGV[1]) then redis.call('SET', KEYS[1], '0') return 0 end return v", new[] { "counter" }, 100) ?? 0); ``` ```csharp string sha = cli.ScriptLoad("return redis.call('GET', KEYS[1])"); string value = (string)cli.EvalSha(sha, new[] { "my_key" }); ``` ```csharp bool exists = cli.ScriptExists(sha); cli.ScriptFlush(); ``` ```csharp object r = await cli.EvalAsync("return redis.call('SET',KEYS[1],ARGV[1])", new[] { "k" }, "hello"); ``` -------------------------------- ### ConnectionStringBuilder — Connection Parameters Source: https://context7.com/2881099/freeredis/llms.txt Explains how to use ConnectionStringBuilder to parse and manage Redis connection parameters from a string, including properties for host, port, authentication, pooling, timeouts, and more. ```APIDOC ## ConnectionStringBuilder — Connection Parameters `ConnectionStringBuilder` parses key=value pairs from a connection string and exposes every option as a strongly-typed property. Implicit conversion operators allow seamless assignment from/to `string`. ```csharp var csb = ConnectionStringBuilder.Parse( "127.0.0.1:6379,password=secret,defaultDatabase=2," "max poolsize=100,min poolsize=5," "connectTimeout=10000,receiveTimeout=20000,sendTimeout=20000," "idleTimeout=60000,ssl=false,prefix=app:,encoding=utf-8,retry=3" ); Console.WriteLine(csb.Host); // "127.0.0.1:6379" Console.WriteLine(csb.Database); // 2 Console.WriteLine(csb.Prefix); // "app:" Console.WriteLine(csb.MaxPoolSize); // 100 // Round-trip back to string string connStr = csb.ToString(); // "127.0.0.1:6379,password=secret,database=2,prefix=app:,retry=3, ..." ``` ``` -------------------------------- ### Switch Redis Database Context Source: https://context7.com/2881099/freeredis/llms.txt Obtains a scoped client to execute commands against a specific database index without affecting the parent client. The context is automatically reverted upon Dispose. ```csharp using (var db = cli.GetDatabase(5)) { db.Set("isolated:key", "db5_value"); string v = db.Get("isolated:key"); Console.WriteLine(v); } ```