{"id":19672309,"url":"https://github.com/iggy-rs/iggy-dotnet-client","last_synced_at":"2025-04-29T01:30:41.431Z","repository":{"id":182591595,"uuid":"668750947","full_name":"iggy-rs/iggy-dotnet-client","owner":"iggy-rs","description":"Official C# dotnet client SDK for Iggy.rs message streaming.","archived":false,"fork":false,"pushed_at":"2025-01-12T10:55:43.000Z","size":666,"stargazers_count":26,"open_issues_count":1,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-05T12:11:26.454Z","etag":null,"topics":["csharp","dotnet","iggy","sdk"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iggy-rs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-20T14:06:23.000Z","updated_at":"2025-01-12T10:55:47.000Z","dependencies_parsed_at":"2023-10-13T12:53:01.851Z","dependency_job_id":"361acc0a-001f-4a8f-896a-0be88f03fdf9","html_url":"https://github.com/iggy-rs/iggy-dotnet-client","commit_stats":null,"previous_names":["iggy-rs/iggy-dotnet-client"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iggy-rs%2Figgy-dotnet-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iggy-rs%2Figgy-dotnet-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iggy-rs%2Figgy-dotnet-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iggy-rs%2Figgy-dotnet-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iggy-rs","download_url":"https://codeload.github.com/iggy-rs/iggy-dotnet-client/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251415592,"owners_count":21585857,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet","iggy","sdk"],"created_at":"2024-11-11T17:11:43.012Z","updated_at":"2025-04-29T01:30:40.785Z","avatar_url":"https://github.com/iggy-rs.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n    \n[![.NET](https://github.com/iggy-rs/iggy-dotnet-client/actions/workflows/dotnet.yml/badge.svg)](https://github.com/iggy-rs/iggy-dotnet-client/actions/workflows/dotnet.yml)\n[![Nuget (with prereleases)](https://img.shields.io/nuget/vpre/Iggy)](https://www.nuget.org/packages/Iggy)\n\n\u003c/div\u003e\n\n## THE DEVELOPMENT OF SDK IS CURRENTLY ON HALT, DUE TO BIG WORKLOAD IN MAIN REPOSITORY \n# C# SDK for [Iggy](https://github.com/spetz/iggy)\n\n\n### Getting Started\n\nCurrently supported transfer protocols\n-  TCP\n-  HTTP\n\nThe whole SDK revolves around `IIggyClient` interface to create an instance of it, use following code\n```c#\nvar loggerFactory = LoggerFactory.Create(builder =\u003e\n{\n    builder\n        .AddFilter(\"Iggy_SDK.MessageStream.Implementations;\", LogLevel.Trace)\n        .AddConsole();\n});\nvar bus = MessageStreamFactory.CreateMessageStream(options =\u003e\n{\n    options.BaseAdress = \"127.0.0.1:8090\";\n    options.Protocol = Protocol.Tcp;\n    options.TlsSettings = x =\u003e\n    {\n        x.Enabled = false;\n        x.Hostname = \"iggy\";\n        x.Authenticate = false;\n    };\n}, loggerFactory);\n```\nIggy necessitates the use of `ILoggerFactory` to generate logs from locations that are inaccessible to the user.\n\nIn addition to the basic configuration settings, Iggy provides support for batching send/poll messages at intervals, which effectively decreases the frequency of network calls, this option is enabled by default.\n```c#\n//---Snip---\nvar bus = MessageStreamFactory.CreateMessageStream(options =\u003e\n{\n    options.BaseAdress = \"127.0.0.1:8090\";\n    options.Protocol = protocol;\n    options.TlsSettings = x =\u003e\n    {\n        x.Enabled = false;\n        x.Hostname = \"iggy\";\n        x.Authenticate = false;\n    };\n\n    options.IntervalBatchingConfig = x =\u003e\n    {\n        x.Enabled = true;\n        x.Interval = TimeSpan.FromMilliseconds(100);\n        x.MaxMessagesPerBatch = 1000;\n        x.MaxRequests = 4096;\n    };\n    options.MessagePollingSettings = x =\u003e\n    {\n        x.Interval = TimeSpan.FromMilliseconds(100);\n        x.StoreOffsetStrategy = StoreOffset.AfterProcessingEachMessage;\n    };\n}, loggerFactory);\n```\n### Creating and logging in a user\nTo begin, utilize the root account (note that the root account cannot be removed or updated).\n```c#\nvar response = await bus.LoginUser(new LoginUserRequest\n{\n    Username = \"iggy\",\n    Password = \"iggy\",\n});\n```\nFurthermore, after logging in, you have the option to create an account with customizable `Permissions`.\n```c#\n//---Snip---\nawait bus.CreateUser(new CreateUserRequest\n{\n    Username = \"test_user\",\n    Password = \"pa55w0rD!@\",\n    Status = UserStatus.Active,\n    Permissions = new Permissions\n    {\n        Global = new GlobalPermissions\n        {\n            ManageServers = true,\n            ManageUsers = true,\n            ManageStreams = true,\n            ManageTopics = true,\n            PollMessages = true,\n            ReadServers = true,\n            ReadStreams = true,\n            ReadTopics = true,\n            ReadUsers = true,\n            SendMessages = true\n        },\n        Streams = new Dictionary\u003cint, StreamPermissions\u003e\n        {\n            {\n                streamId, new StreamPermissions\n                {\n                    ManageStream = true,\n                    ReadStream = true,\n                    SendMessages = true,\n                    PollMessages = true,\n                    ManageTopics = true,\n                    ReadTopics = true,\n                    Topics = new Dictionary\u003cint, TopicPermissions\u003e\n                    {\n                        {\n                            topicId, new TopicPermissions\n                            {\n                                ManageTopic = true,\n                                ReadTopic = true,\n                                PollMessages = true,\n                                SendMessages = true\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n});\n\nvar response = await bus.LoginUser(new LoginUserRequest\n{\n    Username = \"test_user\",\n    Password = \"pa55w0rD!@\",\n});\n```\nAlternatively, once you've logged in, you can create a `Personal Access Token` that can be reused for further logins.\n```c#\nvar response = await bus.LoginUser(new LoginUserRequest\n{\n    Username = \"your_username\",\n    Password = \"your_password\",\n});\n\nvar patResponse = await bus.CreatePersonalAccessTokenAsync(new CreatePersonalAccessTokenRequest\n{\n    Name = \"first-pat\",\n    Expiry = 60, // seconds from creation time\n});\nawait bus.LoginWithPersonalAccessToken(new LoginWithPersonalAccessToken\n{\n    Token = patResponse.Token\n});\n```\n### Creating first stream and topic\nIn order to create stream use `CreateStreamAsync` method.\n```c#\nawait bus.CreateStreamAsync(new StreamRequest\n{\n    StreamId = 1,\n    Name = \"first-stream\",\n});\n```\nEvery stream has a topic to which you can broadcast messages, for the purpose of create one\nuse `CreateTopicAsync` method.\n```c#\nvar streamId = Identifier.Numeric(1);\nawait bus.CreateTopicAsync(streamId, new TopicRequest\n{\n    Name = \"first-topic\",\n    PartitionsCount = 3,\n    TopicId = 1\n});\n```\nNotice that both Stream aswell as Topic use `-` instead of space in its name, Iggy will replace any spaces in\nname with `-` instead, so keep that in mind.\n\n### Sending messages\nTo send messages you can use `SendMessagesAsync` method.\n```c#\nFunc\u003cbyte[], byte[]\u003e encryptor = static payload =\u003e\n{\n    string aes_key = \"AXe8YwuIn1zxt3FPWTZFlAa14EHdPAdN9FaZ9RQWihc=\";\n    string aes_iv = \"bsxnWolsAyO7kCfWuyrnqg==\";\n    \n    var key = Convert.FromBase64String(aes_key);\n    var iv = Convert.FromBase64String(aes_iv);\n    \n    using Aes aes = Aes.Create();\n    ICryptoTransform encryptor = aes.CreateEncryptor(key, iv);\n    \n    using MemoryStream memoryStream = new MemoryStream();\n    using CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);\n    using BinaryWriter streamWriter = new BinaryWriter(cryptoStream);\n    streamWriter.Write(payload);\n    \n    return memoryStream.ToArray();\n};\n\nvar messages = new List\u003cMessage\u003e(); // your messages\nvar streamId = Identifier.Numeric(1);\nvar topicId = Identifier.Numeric(1);\nawait bus.SendMessagesAsync(new MessageSendRequest\n{\n    Messages = new List\u003cMessage\u003e(),\n    Partitioning = Partitioning.PartitionId(1),\n    StreamId = streamId,\n    TopicId = topicId,\n}, encryptor); //encryptor is optional\n```\nThe `Message` struct has two fields `Id` and `Payload`.\n```c#\nstruct Message\n{\n    public required Guid Id { get; init; }\n    public required byte[] Payload { get; init; }\n}\n```\nFurthermore, there's a generic overload for this method that takes binary serializer as argument.\n```c#\n//---Snip---\nFunc\u003cEnvelope, byte[]\u003e serializer = static envelope =\u003e\n{\n    Span\u003cbyte\u003e buffer = stackalloc byte[envelope.MessageType.Length + 4 + envelope.Payload.Length];\n    \n    BinaryPrimitives.WriteInt32LittleEndian(buffer[..4], envelope.MessageType.Length);\n    Encoding.UTF8.GetBytes(envelope.MessageType).CopyTo(buffer[4..(envelope.MessageType.Length + 4)]);\n    Encoding.UTF8.GetBytes(envelope.Payload).CopyTo(buffer[(envelope.MessageType.Length + 4)..]);\n    \n    return buffer.ToArray();\n};\n\nvar messages = new List\u003cEnvelope\u003e(); // your messages\nawait bus.SendMessagesAsync(new MessageSendRequest\u003cEnvelope\u003e\n{\n    StreamId = streamId,\n    TopicId = topicId,\n    Partitioning = Partitioning.PartitionId(1),\n    Messages = messages\n},\nserializer,\nencryptor);\n```\nBoth generic and non generic method accept optional `Headers` dictionary.\n```c#\n//---Snip---\nvar headers = new Dictionary\u003cHeaderKey, HeaderValue\u003e\n{\n    { new HeaderKey { Value = \"key_1\".ToLower() }, HeaderValue.FromString(\"test-value-1\") },\n    { new HeaderKey { Value = \"key_2\".ToLower() }, HeaderValue.FromInt32(69) },\n    { new HeaderKey { Value = \"key_3\".ToLower() }, HeaderValue.FromFloat(420.69f) },\n    { new HeaderKey { Value = \"key_4\".ToLower() }, HeaderValue.FromBool(true) },\n    { new HeaderKey { Value = \"key_5\".ToLower() }, HeaderValue.FromBytes(byteArray) },\n    { new HeaderKey { Value = \"key_6\".ToLower() }, HeaderValue.FromInt128(new Int128(6969696969, 420420420)) },\n    { new HeaderKey { Value = \"key7\".ToLower() }, HeaderValue.FromGuid(Guid.NewGuid()) }\n};\n\nawait bus.SendMessagesAsync\u003cEnvelope\u003e(new MessageSendRequest\u003cEnvelope\u003e\n{\n    StreamId = streamId,\n    TopicId = topicId,\n    Partitioning = Partitioning.PartitionId(1),\n    Messages = messages\n},\nserializer,\nencryptor,\nheaders);\n```\n### Fetching Messages\nFetching messages is done with `FetchMessagesAsync`.\n```c#\nFunc\u003cbyte[], byte[]\u003e decryptor = static payload =\u003e\n{\n    string aes_key = \"AXe8YwuIn1zxt3FPWTZFlAa14EHdPAdN9FaZ9RQWihc=\";\n    string aes_iv = \"bsxnWolsAyO7kCfWuyrnqg==\";\n    \n    var key = Convert.FromBase64String(aes_key);\n    var iv = Convert.FromBase64String(aes_iv);\n    \n    using Aes aes = Aes.Create();\n    ICryptoTransform decryptor = aes.CreateDecryptor(key, iv);\n    \n    using MemoryStream memoryStream = new MemoryStream(payload);\n    using CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);\n    using BinaryReader binaryReader = new BinaryReader(cryptoStream);\n    \n    return binaryReader.ReadBytes(payload.Length);\n};\n\nvar messages = await bus.FetchMessagesAsync(new MessageFetchRequest\n{\n    StreamId = streamId,\n    TopicId = topicId,\n    Consumer = Consumer.New(1),\n    Count = 1,\n    PartitionId = 1,\n    PollingStrategy = PollingStrategy.Next(),\n    AutoCommit = true\n},\ndecryptor);\n```\nSimilarly, as with `SendMessagesAsync`, there's a generic overload that accepts a binary deserializer.\n```c#\n//---Snip---\nFunc\u003cbyte[], Envelope\u003e deserializer = serializedData =\u003e\n{\n    Envelope envelope = new Envelope();\n    int messageTypeLength = BitConverter.ToInt32(serializedData, 0);\n    envelope.MessageType = Encoding.UTF8.GetString(serializedData, 4, messageTypeLength);\n    envelope.Payload = Encoding.UTF8.GetString(serializedData, 4 + messageTypeLength, serializedData.Length - (4 + messageTypeLength));\n    return envelope;\n};\n\nvar messages = await bus.FetchMessagesAsync\u003cEnvelope\u003e(new MessageFetchRequest\n{\n    StreamId = streamId,\n    TopicId = topicId,\n    Consumer = Consumer.New(1),\n    Count = 1,\n    PartitionId = 1,\n    PollingStrategy = PollingStrategy.Next(),\n    AutoCommit = true\n}, deserializer, decryptor);\n```\nBeyond the `FetchMessagesAsync` functionality, there's also a `PollMessagesAsync` method that spawns new thread which polls messages in background.\n```c#\n//---Snip---\nawait foreach (var messageResponse in bus.PollMessagesAsync\u003cEnvelope\u003e(new PollMessagesRequest\n{\n    Consumer = Consumer.New(consumerId),\n    Count = 1,\n    TopicId = topicId,\n    StreamId = streamId,\n    PartitionId = 1,\n    PollingStrategy = PollingStrategy.Next(),\n}, deserializer, decryptor))\n{\n    //handle the message response\n}\n\n```\nIt is worth noting that every method (except `PollMessagesAsync`) will throw an `InvalidResponseException` when encountering an error.\u003cbr\u003e\u003cbr\u003e\nIf you register `IIggyClient` in a dependency injection container, you will have access to interfaces\nthat encapsulate smaller parts of the system `IIggyStream` `IIggyTopic` `IIggyPublisher` `IIggyConsumer` `IIggyConsumerGroup` `IIggyOffset`\n`IIggyPartition` `IIggyUsers` `IIggyUtils`\n\nFor more information about how Iggy works check its [documentation](https://docs.iggy.rs/)\n\n# Producer / Consumer Sample\n\nhttps://github.com/iggy-rs/iggy-dotnet-client/assets/112548209/3a89d2f5-d066-40d2-8b82-96c3e338007e\n\nTo run the samples, first get [Iggy](https://github.com/spetz/iggy), Run the server with `cargo r --bin server`, then get the SDK, cd into `Iggy_SDK`\nand run following commands: `dotnet run -c Release --project Iggy_Sample_Producer` for producer, `dotnet run -c Release --project Iggy_Sample_Consumer`\nfor consumer.\n\n## TODO \n- Add support for `ASP.NET Core` Dependency Injection\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figgy-rs%2Figgy-dotnet-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Figgy-rs%2Figgy-dotnet-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figgy-rs%2Figgy-dotnet-client/lists"}