{"id":19992296,"url":"https://github.com/OrleansContrib/SignalR.Orleans","last_synced_at":"2025-05-04T11:30:55.556Z","repository":{"id":38552421,"uuid":"109354214","full_name":"OrleansContrib/SignalR.Orleans","owner":"OrleansContrib","description":"SignalR backend based on Orleans.","archived":false,"fork":false,"pushed_at":"2024-07-17T05:52:29.000Z","size":256,"stargazers_count":300,"open_issues_count":24,"forks_count":67,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-04-08T11:13:30.418Z","etag":null,"topics":["aspnet-core","backplane","dotnet-core","orleans","orleans-cluster","real-time","realtime-messaging","signalr"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OrleansContrib.png","metadata":{"files":{"readme":"README.Nuget.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-03T05:03:40.000Z","updated_at":"2025-03-25T06:01:50.000Z","dependencies_parsed_at":"2024-06-21T07:30:35.931Z","dependency_job_id":"c2aa6039-bdcf-4324-b819-0ec315fdfb53","html_url":"https://github.com/OrleansContrib/SignalR.Orleans","commit_stats":{"total_commits":112,"total_committers":16,"mean_commits":7.0,"dds":0.6964285714285714,"last_synced_commit":"d2e81c8c9597ffe44ecc3356b8dfb1649e191041"},"previous_names":["orleanscontrib/orleansr"],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrleansContrib%2FSignalR.Orleans","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrleansContrib%2FSignalR.Orleans/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrleansContrib%2FSignalR.Orleans/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrleansContrib%2FSignalR.Orleans/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OrleansContrib","download_url":"https://codeload.github.com/OrleansContrib/SignalR.Orleans/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252329201,"owners_count":21730563,"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":["aspnet-core","backplane","dotnet-core","orleans","orleans-cluster","real-time","realtime-messaging","signalr"],"created_at":"2024-11-13T04:52:06.834Z","updated_at":"2025-05-04T11:30:51.416Z","avatar_url":"https://github.com/OrleansContrib.png","language":"C#","funding_links":[],"categories":["C\\#"],"sub_categories":[],"readme":"[![Build](https://github.com/OrleansContrib/SignalR.Orleans/workflows/CI/badge.svg)](https://github.com/OrleansContrib/SignalR.Orleans/actions)\n[![Package Version](https://img.shields.io/nuget/v/SignalR.Orleans.svg)](https://www.nuget.org/packages/SignalR.Orleans)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/SignalR.Orleans.svg)](https://www.nuget.org/packages/SignalR.Orleans)\n[![License](https://img.shields.io/github/license/OrleansContrib/SignalR.Orleans.svg)](https://github.com/OrleansContrib/SignalR.Orleans/blob/master/LICENSE)\n[![Discord](https://img.shields.io/discord/333727978460676096)](https://discord.gg/TCSAgM9k)\n\n[Orleans](https://learn.microsoft.com/en-us/dotnet/orleans/overview) is a cross-platform framework for building robust, scalable distributed applications. Distributed applications are defined as apps that span more than a single process, often beyond hardware boundaries using peer-to-peer communication. Orleans scales from a single on-premises server to hundreds to thousands of distributed, highly available applications in the cloud. [See Orleans source code on Github](https://github.com/dotnet/orleans)\n\n[ASP.NET Core SignalR](https://learn.microsoft.com/en-us/aspnet/core/signalr/introduction?view=aspnetcore-7.0) is an open-source library that simplifies adding real-time web functionality to apps. Real-time web functionality enables server-side code to push content to clients instantly.\n\n**SignalR.Orleans** is a package that gives you two abilities: \n\n1. Use your Orleans cluster as a backplane for SignalR. [Learn about scaling out SignalR on multiple servers.](https://learn.microsoft.com/en-us/aspnet/core/signalr/scale?view=aspnetcore-7.0)\n\n\u003e There are various choices of backplane that you can use for SignalR, as you will see in the link above. If you're already using Orleans, then you might want to use Orleans as the backplane to reduce the number of dependencies used by your application and to reduce the number of network hops (and latency) that would be required when calling an external service.\n\n2. Send messages from Orleans grains to SignalR clients.\n\n\u003e If the SignalR component of your application is cohosted with Orleans (same server, same process and `Microsoft.AspNetCore.SignalR.IHubContext\u003cMyHub\u003e` can be injected into an Orleans gain), you already have this ability without installing this package.\n\n\u003e However, if the SignalR component of your application is \"remote\" from the grains, this package will give the grains a way of sending messages to SignalR clients by injecting `SignalR.Orleans.Core.HubContext\u003cMyHub\u003e`.\n\nTODO: These two abilities should be provided independently of each other. Unfortunately at this stage, ability #2 is only provided if ability #1 is used as well.\n\n# Installation\n\nInstallation is performed via [NuGet.](https://www.nuget.org/packages/SignalR.Orleans/)\n\nPackages with version `7.x.x` are compatible with Orleans `v7.x.x` and above. If you're still using an earlier version of Orleans, you will need to use earlier versions of the package.\n\nPackage Manager:\n\n\u003e PS\u003e Install-Package SignalR.Orleans\n\n.Net CLI:\n\n\u003e \\# dotnet add package SignalR.Orleans\n\nPaket:\n\n\u003e \\# paket add SignalR.Orleans\n\n---\n# Version 7.0.0 documentation\n\u003e Scroll down to see documentation for earlier versions.\n\nHere is a complete starter example featuring cohosted aspnetcore app with SignalR and Orleans.\n\n```csharp\nusing Microsoft.AspNetCore.SignalR;\nusing Orleans.Hosting;\nusing SignalR.Orleans;\n\n// Create a host that can cohost aspnetcore AND orleans together in a single process.\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Host.UseOrleans(siloBuilder =\u003e \n{\n    siloBuilder.UseLocalhostClustering();\n    siloBuilder.UseSignalR(); // Adds ability #1 and #2 to Orleans.\n    siloBuilder.RegisterHub\u003cMyHub\u003e(); // Required for each hub type if the backplane ability #1 is being used.\n});\n\nbuilder.Services\n    .AddSignalR()  // Adds SignalR hubs to the web application\n    .AddOrleans(); // Tells the SignalR hubs in the web application to use Orleans as a backplane (ability #1)\n\nvar app = builder.Build();\napp.MapHub\u003cMyHub\u003e(\"/myhub\");\nawait app.RunAsync();\n\n// A SignalR Hub. https://learn.microsoft.com/en-us/aspnet/core/signalr/hubs?view=aspnetcore-7.0\nclass MyHub : Hub\n{\n}\n```\n\n### Silo configuration - grain storage\n\nThe SignalR.Orleans backplane (ability #1) uses grains under the hood that use storage to keep track of where each SignalR client is connected and what groups it belongs to. The storage used by default is `MemoryStorage`.\n\nUse the given storage name constant to configure the correct storage provider.\n\n```csharp\n// **************************************************************************\n// Use memory storage ONLY when your app is not clustered, otherwise you'll\n// need to use proper external storage providers\n// **************************************************************************\n          \n// Customize the storage used by the SignalR Orleans backplane grains.\nsiloBuilder.AddSomeOtherGrainStorage(SignalROrleansConstants.SIGNALR_ORLEANS_STORAGE_PROVIDER);\n\n// THEN\nsiloBuilder.UseSignalR();\n```\n\n### Silo configuration - stream type and stream storage\n\nSignalR.Orleans uses streams under the hood to provide the backplane (ability #1). The default stream type is `MemoryStream`. All streams in a given Orleans instance must use the same storage provider, named `PubSubStore`, currently defaulted to `MemoryStorage`.\n\n```csharp\n// FIRST customize the storage used by ALL stream providers in the entire Orleans host:\n// Remember, memory storage won't work if you're using a cluster.\nsiloBuilder.AddSomeOtherGrainStorage(\"PubSubStore\");\n\n// THEN customize the type of stream used for the backplane, using the correct stream provider name\nsiloBuilder.AddPersistentStreams(SignalROrleansConstants.SIGNALR_ORLEANS_STREAM_PROVIDER, adapterFactory, configureStream);\n\n// THEN\nsiloBuilder.UseSignalR();\n```\n\n## Sending messages from Orleans grains\n\nIf the SignalR app is cohosted as demonstrated above, you don't need this package to send messages from an Orleans grain. Simply inject `IHubContext\u003cMyHub\u003e` to the grain's constructor and call its methods to send messages. [Read more about it here.](https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-7.0)\n\nHowever, if the SignalR app is not cohosted, and if it's using Orleans as a backplane, then it's possible to use this package to send messages to the SignalR clients using the backplane streams in Orleans as a conduit (ability #2).\n\n```csharp\nclass SampleGrain : Grain, ISampleGrain\n{\n  private readonly SignalR.Orleans.Core.HubContext\u003cMyHub\u003e _hubContext;\n\n  public SampleGrain(SignalR.Orleans.Core.HubContext\u003cMyHub\u003e hubContext)\n  {\n    _hubContext = hubContext;\n  }\n\n  public async Task SendMessageToClients()\n  {\n    // Create an invocation message\n    var msg = new InvocationMessage(\"method\", new object?[]{ 1, 2, 3 }).ToImmutable();\n\n    // Send a message to a single client\n    await _hubContext.Client(\"someConnectionId\").Send(msg);\n\n    // Send a message to a group\n    await _hubContext.Group(\"someGroupName\").Send(msg);\n\n    // Send a message to all connections made by a particular authenticated user\n    await _hubContext.Group(\"someUserId\").Send(msg);\n\n    // TODO: We have not implemented ability to send a message to ALL clients yet.\n  }\n}\n```\n\n## Configuring the `IClusterClient`\nIf your SignalR app is cohosted with Orleans, it will automatically grab an `IClusterClient` from the service provider and connect to the Orleans backplane. \n\nHowever, if it's not cohosted, you'll have to give it an `IClusterClient` to use: \n\n```csharp\nusing Microsoft.AspNetCore.SignalR;\nusing Orleans.Hosting;\nusing SignalR.Orleans;\n\n// Create a web application that will connect to a remote Orleans cluster\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services\n    // Adds an IClusterClient to the service provider.\n    .AddOrleansClient(clientBuilder =\u003e\n    {\n        // Tell the client how to connect to Orleans (you'll need to customize this for yourself)\n        clientBuilder.UseLocalhostClustering();\n        // Tells the client how to connect to the SignalR.Orleans backplane.\n        clientBuilder.UseSignalR(configure: null);\n    })\n    .AddSignalR()  // Adds SignalR hubs to the web application\n    .AddOrleans(); // Tells SignalR to use Orleans as a backplane (ability #1)\n\nvar app = builder.Build();\napp.MapHub\u003cMyHub\u003e(\"/myhub\");\nawait app.RunAsync();\n\n// A SignalR Hub. https://learn.microsoft.com/en-us/aspnet/core/signalr/hubs?view=aspnetcore-7.0\nclass MyHub : Hub\n{\n}\n```\n\nThis is the end of documentation for versions \u003e= 7.0.0. Below is older documenation for previous versions.\n\n---\n# Earlier version documentation\n\n## Configure the Silo\nWe need to configure the Orleans Silo with the below:\n* Use `.UseSignalR()` on `ISiloHostBuilder`.\n* Make sure to call `RegisterHub\u003cTHub\u003e()` where `THub` is the type of the Hub you want to be added to the backplane.\n\n***Example***\n```cs\nvar silo = new SiloHostBuilder()\n  .UseSignalR()\n  .RegisterHub\u003cMyHub\u003e() // You need to call this per `Hub` type.\n  .AddMemoryGrainStorage(\"PubSubStore\") // You can use any other storage provider as long as you have one registered as \"PubSubStore\".\n  .Build();\n\nawait silo.StartAsync();\n```\n\n### Configure Silo Storage Provider and Grain Persistance\nOptional configuration to override the default implementation for both providers which by default are set as `Memory`.\n\n***Example***\n```cs\n.UseSignalR(cfg =\u003e\n{\n  cfg.Configure((builder, config) =\u003e\n  {\n      builder\n          .AddMemoryGrainStorage(config.PubSubProvider)\n          .AddMemoryGrainStorage(config.StorageProvider);\n  });\n})\n.RegisterHub\u003cMyHub\u003e()\n```\n\n## Client\nNow your SignalR application needs to connect to the Orleans Cluster by using an Orleans Client:\n* Use `.UseSignalR()` on `IClientBuilder`.\n\n***Example***\n```cs\nvar client = new ClientBuilder()\n  .UseSignalR()\n  // optional: .ConfigureApplicationParts(parts =\u003e parts.AddApplicationPart(typeof(IClientGrain).Assembly).WithReferences())\n  .Build();\n\nawait client.Connect();\n```\n\nSomewhere in your `Startup.cs`:\n* Add `IClusterClient` (created in the above example) to `IServiceCollection`.\n* Use `.AddSignalR()` on `IServiceCollection` (this is part of `Microsoft.AspNetCore.SignalR` nuget package).\n* Use `AddOrleans()` on `.AddSignalR()`.\n\n***Example***\n```cs\npublic void ConfigureServices(IServiceCollection services)\n{\n  ...\n  services\n    .AddSingleton\u003cIClusterClient\u003e(client)\n    .AddSignalR()\n    .AddOrleans();\n  ...\n}\n```\nGreat! Now you have SignalR configured and Orleans SignalR backplane built in Orleans!\n\n# Features\n## Hub Context\n`HubContext` gives you the ability to communicate with the client from orleans grains (outside the hub).\n\nSample usage: Receiving server push notifications from message brokers, web hooks, etc. Ideally first update your grain state and then push signalr message to the client.\n\n### Example\n```cs\npublic class UserNotificationGrain : Grain\u003cUserNotificationState\u003e, IUserNotificationGrain\n{\n  private HubContext\u003cIUserNotificationHub\u003e _hubContext;\n\n  public override async Task OnActivateAsync()\n  {\n    _hubContext = GrainFactory.GetHub\u003cIUserNotificationHub\u003e();\n    // some code...\n    await _hubContext.User(this.GetPrimaryKeyString()).Send(\"Broadcast\", State.UserNotification);\n  }\n}\n```\n\n# Complete examples\n\n### Cohosting aspnetcore website and orleans\n\n```cs\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.AspNetCore.ResponseCompression;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Hosting;\nusing Orleans;\nusing Orleans.Hosting;\n\n// Cohosting aspnetcore website and Orleans with signalR\nvar host = Host.CreateDefaultBuilder(args)\n\n  // Add the webhost with SignalR configured.\n  .ConfigureWebHostDefaults(webBuilder =\u003e\n  {\n    webBuilder.ConfigureServices((webBuilderContext, services) =\u003e\n    {\n      // Add response compression used by the SignalR hubs.\n      services.AddResponseCompression(opts =\u003e\n      {\n        opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(\n            new[] { \"application/octet-stream\" });\n      });\n\n      // Adds SignalR hubs to the aspnetcore website \n      services.AddSignalR(options =\u003e\n      {\n      })\n      .AddOrleans(); // Tells SignalR to use Orleans as the backplane.\n    });\n\n    webBuilder.Configure((ctx, app) =\u003e\n    {\n      // Adds response compression for use by the SignalR hubs\n      app.UseResponseCompression();\n      \n      // Map SignalR hub endpoints\n      app.UseEndpoints(endpoints =\u003e\n      {\n        endpoints.MapHub\u003cMyHubType1\u003e(\"/hub1\"); // use your own hub types\n        endpoints.MapHub\u003cMyHubType2\u003e(\"/hub2\"); // use your own hub types\n        // ... etc\n      });\n    });\n  })\n\n  // Add Orleans with SignalR configured\n  .UseOrleans((context, siloBuilder) =\u003e\n  {\n    siloBuilder\n      .UseSignalR(signalRConfig =\u003e\n      {\n        // Optional.\n        signalRConfig.UseFireAndForgetDelivery = true;\n\n        signalRConfig.Configure((siloBuilder, signalRConstants) =\u003e\n        {\n          // **************************************************************************\n          // Use memory storage ONLY when your app is not clustered, otherwise you'll\n          // need to use proper external storage providers\n          // **************************************************************************\n\n          siloBuilder.AddMemoryGrainStorage(signalRConstants.StorageProvider);\n          // This wouldn't be be necessary if you already added \"PubSubStore\" elsewhere.\n          siloBuilder.AddMemoryGrainStorage(signalRConstants.PubSubProvider /*Same as \"PubSubStore\"*/);\n        });\n      })\n\n      // Allows Orleans grains to inject IHubContext\u003cHubType\u003e\n      .RegisterHub\u003cMyHubType1\u003e()\n      .RegisterHub\u003cMyHubType2\u003e();\n      // ... etc\n  })\n  .UseConsoleLifetime()\n  .Build();\n\nawait host.StartAsync();\nawait host.WaitForShutdownAsync(default);\n```\n\n\n# Contributions\nPRs and feedback are **very** welcome!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOrleansContrib%2FSignalR.Orleans","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FOrleansContrib%2FSignalR.Orleans","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOrleansContrib%2FSignalR.Orleans/lists"}