{"id":19585402,"url":"https://github.com/liammorrow/orgnalr","last_synced_at":"2025-04-27T11:33:31.787Z","repository":{"id":40322112,"uuid":"203361185","full_name":"LiamMorrow/OrgnalR","owner":"LiamMorrow","description":"SignalR backplane implemented through Orleans","archived":false,"fork":false,"pushed_at":"2024-04-11T23:02:23.000Z","size":835,"stargazers_count":32,"open_issues_count":5,"forks_count":7,"subscribers_count":8,"default_branch":"main","last_synced_at":"2024-04-12T07:19:54.084Z","etag":null,"topics":["csharp","dotnet","orleans","orleans-framework","signalr","signalr-core"],"latest_commit_sha":null,"homepage":null,"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/LiamMorrow.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2019-08-20T11:20:39.000Z","updated_at":"2024-04-15T06:55:59.585Z","dependencies_parsed_at":"2024-04-15T06:55:17.094Z","dependency_job_id":"79a6d5a8-c83b-44db-80e9-8ea0a211138b","html_url":"https://github.com/LiamMorrow/OrgnalR","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiamMorrow%2FOrgnalR","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiamMorrow%2FOrgnalR/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiamMorrow%2FOrgnalR/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiamMorrow%2FOrgnalR/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LiamMorrow","download_url":"https://codeload.github.com/LiamMorrow/OrgnalR/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224069542,"owners_count":17250455,"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","orleans","orleans-framework","signalr","signalr-core"],"created_at":"2024-11-11T07:54:01.127Z","updated_at":"2024-11-11T07:54:02.702Z","avatar_url":"https://github.com/LiamMorrow.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OrgnalR\n\n[![Actions Status](https://github.com/LiamMorrow/OrgnalR/workflows/build/badge.svg)](https://github.com/LiamMorrow/OrgnalR/actions)\n[![Actions Status](https://github.com/LiamMorrow/OrgnalR/workflows/test/badge.svg)](https://github.com/LiamMorrow/OrgnalR/actions)  \nOrgnalR is a backplane for [SignalR core](https://github.com/aspnet/AspNetCore/tree/master/src/SignalR), implemented through [Orleans](https://github.com/dotnet/orleans)!\nIt allows your SignalR servers to scale out with all the capacity of Orleans grains.\n\nThis is an alternative to the Redis backplane, and [SignalR.Orleans](https://github.com/OrleansContrib/SignalR.Orleans). This implementation does not use Orleans streams at all. This project was born out of issues with deadlocks that occured with Orleans streams, and since [SignalR.Orleans](https://github.com/OrleansContrib/SignalR.Orleans) uses them, issues with signalr messages not going through.\n\n## Getting started\n### Compatibility\nOrleans `7.0.0` introduced a large breaking change around serialization and other Orleans primitives.  As of `^2.0.0`, OrgnalR only supports .net/Orleans `7.0.0` and up.  If you need to use this package for an older release of Orleans and .net (including .netstandard), see the `1.X.X` releases.\n\n### Installing\n\nOrgnalR comes in two packages, one for the Orleans Silo, and one for the SignalR application.\n\n#### SignalR\n\n\u003ca href=\"https://www.nuget.org/packages/OrgnalR.Signalr\"\u003e![OrgnalR SignalR](https://img.shields.io/nuget/v/OrgnalR.SignalR?logo=SignalR)\u003c/a\u003e\n\n```\ndotnet add package OrgnalR.SignalR\n```\n\n#### Orleans Silo\n\n\u003ca href=\"https://www.nuget.org/packages/OrgnalR.OrleansSilo\"\u003e![OrgnalR OrleansSilo](https://img.shields.io/nuget/v/OrgnalR.OrleansSilo?logo=OrleansSilo)\u003c/a\u003e\n\n```\ndotnet add package OrgnalR.OrleansSilo\n```\n\n### Configuring\n\nOrgnalR can be configured via extension methods on both the Orleans client/silo builders, and the SignalR builder.\n\n#### SignalR\n\nSomewhere in your `Startup.cs` (or wherever you configure your SignalR server), you will need to add an extension method to the SignalR builder. The extension method lives in the `OrgnalR.SignalR` namespace, so be sure to add a using for that namespace.\n\n```c#\nusing OrgnalR.SignalR;\nclass Startup {\n    public void ConfigureServices(IServiceCollection services)\n    {\n        services.AddSignalR()\n                .UseOrgnalR();\n    }\n}\n```\n\nNext, Orleans needs to know how to serialize your Hub Requests and Responses.\nThe easiest way to achieve this is to annotate your request/response classes with the `GenerateSerializer` attribute, as you would with any of your normal grain models.\n\nExample:\n\n```c#\n[GenerateSerializer]\npublic record SendMessageRequest(string ChatName, string SenderName, string Message);\n\n// Usage in a hub\npublic class ChatHub : Hub\u003cIChatClient\u003e\n{\n\n    public async Task SendMessage(SendMessageRequest request)\n}\n```\n\nPlease see the [example directory](example) for a fully working solution.\n\n#### Orleans Silo\n\nWherever you configure your orleans Silo, you will need to configure OrgnalR's grains. This is again accomplished by an extension method, however there are two different modes. For development, it is easiest to use the `AddOrgnalRWithMemoryGrainStorage` extension method, which registers the storage providers for the grains with memory storage. This is undesirable for production as if the silo dies, the information on connections in which groups is lost.\n\nFor production usage it is best to configure actual persistent storage for `ORGNALR_USER_STORAGE`, `ORGNALR_GROUP_STORAGE`, and `MESSAGE_STORAGE_PROVIDER`, then use the `AddOrgnalR` extension method.\n\nBoth of these methods are found in the `OrgnalR.Silo` namespace.\n\n##### Development\n\n```c#\nvar builder = new SiloHostBuilder()\n/* Your other configuration options */\n// Note here we use the memory storage option.\n// This is good for quick development, but we should register proper storage for production\n                .AddOrgnalRWithMemoryGrainStorage()\n```\n\n##### Production\n\n```c#\nvar builder = new SiloHostBuilder()\n/* Your other configuration options */\n// Note here we specify the storage we will use for group and user membership\n                .ConfigureServices(services =\u003e\n                {\n                    services.AddSingletonNamedService\u003cIGrainStorage, YourStorageProvider\u003e(Extensions.USER_STORAGE_PROVIDER);\n                    services.AddSingletonNamedService\u003cIGrainStorage, YourStorageProvider\u003e(Extensions.GROUP_STORAGE_PROVIDER);\n                    services.AddSingletonNamedService\u003cIGrainStorage, YourStorageProvider\u003e(Extensions.MESSAGE_STORAGE_PROVIDER);\n                })\n                .AddOrgnalR()\n```\n\nAnd that's it! Your SignalR server will now use the OrgnalR backplane to send messages, and maintain groups / users.\n\n## Sending Messages to clients from grains\n\nSometimes it is useful to send messages to clients from outside of the Hub. SignalR exposes an interface `IHubContext\u003cT\u003e` for this mechanism inside of the server apps which expose SignalR hubs.\n\nHowever, in the context of an orleans app, this requirement might still be necessary from the Silo host. To facilitate this, OrgnalR exposes a interface: [`IHubContextProvider`](/src/OrgnalR.Core/Provider/HubContextProvider.cs).\n\nTo send messages to connected clients in a hub, simply inject this interface into your grain (or service).\nIt exposes methods for getting clients by group/user/connectionID. You can then call `SendAsync` to send them a message.\nNote that `SendAsync` is an extension method provided by the `Microsoft.AspNetCore.SignalR` namespace.\n\nExample:\n\n```csharp\nclass MyGrain : IMyGrain{\n\n    private readonly IHubContextProvider hubContextProvider;\n\n    constructor(IHubContextProvider hubContextProvider)\n    {\n        this.hubContextProvider = hubContextProvider;\n    }\n\n    async Task MyGrainMethod()\n    {\n        // Do stuff\n        // ...\n        // Send message to all connected clients in \"MyHub\"\n        await hubContextProvider\n            .GetHubContext\u003cIMyHub\u003e()\n            .Clients\n            .All // can also use Group, or User, or Connection\n            .SendAsync(\"MyClientMethod\", new MyClientMethodRequest(\"Sent a message from a grain!\"));\n    }\n}\n```\n\nAlternatively, if your application has defined interfaces for strongly typed client methods, you can use the generic form:\n\n```csharp\ninterface IMyClient{\n    Task MyClientMethod(MyClientMethodRequest request);\n}\n\nclass MyGrain : IMyGrain{\n\n    private readonly IHubContextProvider hubContextProvider;\n\n    constructor(IHubContextProvider hubContextProvider)\n    {\n        this.hubContextProvider = hubContextProvider;\n    }\n\n    async Task MyGrainMethod()\n    {\n        // Do stuff\n        // ...\n        // Send message to all connected clients in \"MyHub\"\n        await hubContextProvider\n            .GetHubContext\u003cIMyHub, IMyClient\u003e()\n            .Clients\n            .All // can also use Group, or User, or Connection\n            .MyClientMethod(new MyClientMethodRequest(\"Sent a message from a grain!\"));\n    }\n}\n```\n\n# Examples\n\nExamples can be found in the [example directory](example)\nThe current example is a chat room which uses grains to store the messages, and OrgnalR as a SignalR backplane. React frontend.\n\n# Contributing\n\nContributions are welcome! Simply fork the repository, and submit a PR. If you have an issue, feel free to submit an issue :)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliammorrow%2Forgnalr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliammorrow%2Forgnalr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliammorrow%2Forgnalr/lists"}