├── .gitattributes ├── .gitignore ├── .nuget └── packages.config ├── DragDrop ├── MvcDrag │ ├── Controllers │ │ ├── DragHub.cs │ │ └── HomeController.cs │ ├── DragMvc.csproj │ ├── Models │ │ ├── AccountViewModels.cs │ │ └── ErrorViewModel.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Startup.cs │ ├── Views │ │ ├── Home │ │ │ ├── About.cshtml │ │ │ ├── Contact.cshtml │ │ │ └── Index.cshtml │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ └── _Layout.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ ├── favicon.ico │ ├── libman.json │ └── wwwroot │ │ └── css │ │ └── Site.css ├── SignalrDragDrop.sln └── WpfApplication1 │ ├── App.config │ ├── App.xaml │ ├── App.xaml.cs │ ├── DragWpf.csproj │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── Properties │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings │ └── packages.config ├── EnumerableRange.linq ├── Getting Pushy with SignalR and Rx.pptx ├── LICENSE ├── ObservableRange.linq ├── ObservableSensor.linq ├── README.md ├── RxSignalR ├── BasicConsole │ ├── App.config │ ├── Module1.vb │ ├── My Project │ │ ├── Application.Designer.vb │ │ ├── Application.myapp │ │ ├── AssemblyInfo.vb │ │ ├── Resources.Designer.vb │ │ ├── Resources.resx │ │ ├── Settings.Designer.vb │ │ └── Settings.settings │ ├── RxSignalrConsole.vbproj │ ├── SensorData.vb │ └── packages.config ├── RxSignalR.sln ├── RxSignalrSharpWeb │ ├── Hubs │ │ ├── ChatHub.cs │ │ ├── SensorHub.cs │ │ └── StreamHub.cs │ ├── Pages │ │ ├── Chat.cshtml │ │ ├── Chat.cshtml.cs │ │ ├── Counter.cshtml │ │ ├── Counter.cshtml.cs │ │ ├── Error.cshtml │ │ ├── Error.cshtml.cs │ │ ├── Index.cshtml │ │ ├── Index.cshtml.cs │ │ ├── Privacy.cshtml │ │ ├── Privacy.cshtml.cs │ │ ├── Shared │ │ │ ├── _Layout.cshtml │ │ │ └── _ValidationScriptsPartial.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── RxSignalrSharpWeb.csproj │ ├── Startup.cs │ ├── appsettings.json │ ├── libman.json │ ├── package-lock.json │ ├── package.json │ └── wwwroot │ │ ├── css │ │ └── site.css │ │ ├── favicon.ico │ │ └── js │ │ ├── site.js │ │ ├── stream.js │ │ └── stream2.js ├── RxSignalrWeb │ ├── App_Code │ │ ├── ChatHub.vb │ │ ├── ObservableSensor.vb │ │ ├── ObservableSensorHub.vb │ │ ├── SensorData.vb │ │ └── Startup.vb │ ├── Chat.aspx │ ├── Chat.aspx.vb │ ├── Default.aspx │ ├── Default.aspx.vb │ ├── Global.asax │ ├── Scripts │ │ ├── jquery-3.1.1.intellisense.js │ │ ├── jquery-3.1.1.js │ │ ├── jquery-3.1.1.min.js │ │ ├── jquery-3.1.1.min.map │ │ ├── jquery-3.1.1.slim.js │ │ ├── jquery-3.1.1.slim.min.js │ │ ├── jquery-3.1.1.slim.min.map │ │ ├── jquery.signalR-2.2.2.js │ │ └── jquery.signalR-2.2.2.min.js │ ├── Web.config │ └── packages.config ├── SharpConsole │ ├── Program.cs │ ├── SensorData.cs │ └── SharpConsole.csproj └── WpfApplication1 │ ├── App.config │ ├── App.xaml │ ├── App.xaml.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── Properties │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings │ ├── RxSignalrWpf.csproj │ └── packages.config ├── Rxjs ├── .vscode │ ├── launch.json │ └── tasks.json ├── 5-1-Timer.html ├── 5-2-Timer-zip.html ├── 5-3-Doubleclick.html ├── 5-4-DragDrop.html ├── 5-5-DictionarySuggest.html ├── 5-7-Translator.html ├── readme.md └── translate.js ├── ShakeObserver.linq ├── SignalrRxSamples.sln ├── SignalrRxSamples.sln.ide ├── edb.chk ├── edbres00001.jrs ├── edbres00002.jrs └── storage.ide └── signalrNg ├── ClientApp ├── .browserslistrc ├── .editorconfig ├── README.md ├── angular.json ├── aspnetcore-https.js ├── karma.conf.js ├── package-lock.json ├── package.json ├── proxy.conf.js ├── src │ ├── app │ │ ├── app.component.html │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── app.server.module.ts │ │ ├── drag-drop │ │ │ ├── drag-drop.component.html │ │ │ └── drag-drop.component.ts │ │ ├── home │ │ │ ├── home.component.html │ │ │ └── home.component.ts │ │ ├── nav-menu │ │ │ ├── nav-menu.component.css │ │ │ ├── nav-menu.component.html │ │ │ └── nav-menu.component.ts │ │ ├── reactive-chat │ │ │ ├── reactive-chat.component.html │ │ │ └── reactive-chat.component.ts │ │ └── sensor │ │ │ ├── sensor.component.html │ │ │ └── sensor.component.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ └── test.ts ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json ├── Hubs ├── ChatHub.cs ├── DragHub.cs └── SensorHub.cs ├── Pages ├── Error.cshtml ├── Error.cshtml.cs └── _ViewImports.cshtml ├── Program.cs ├── Properties └── launchSettings.json ├── appsettings.Development.json ├── appsettings.json ├── signalrNg.csproj ├── signalrNg.sln └── wwwroot └── favicon.ico /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Properties/launchSettings.json 2 | 3 | ## Ignore Visual Studio temporary files, build results, and 4 | ## files generated by popular Visual Studio add-ons. 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | build/ 23 | bld/ 24 | bin/ 25 | Bin/ 26 | obj/ 27 | Obj/ 28 | wwwroot/lib/ 29 | 30 | # Visual Studio 2015 cache/options directory 31 | .vs/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | *_i.c 47 | *_p.c 48 | *_i.h 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.tmp_proj 63 | *.log 64 | *.vspscc 65 | *.vssscc 66 | .builds 67 | *.pidb 68 | *.svclog 69 | *.scc 70 | 71 | # Chutzpah Test files 72 | _Chutzpah* 73 | 74 | # Visual C++ cache files 75 | ipch/ 76 | *.aps 77 | *.ncb 78 | *.opendb 79 | *.opensdf 80 | *.sdf 81 | *.cachefile 82 | 83 | # Visual Studio profiler 84 | *.psess 85 | *.vsp 86 | *.vspx 87 | *.sap 88 | 89 | # TFS 2012 Local Workspace 90 | $tf/ 91 | 92 | # Guidance Automation Toolkit 93 | *.gpState 94 | 95 | # ReSharper is a .NET coding add-in 96 | _ReSharper*/ 97 | *.[Rr]e[Ss]harper 98 | *.DotSettings.user 99 | 100 | # JustCode is a .NET coding add-in 101 | .JustCode 102 | 103 | # TeamCity is a build add-in 104 | _TeamCity* 105 | 106 | # DotCover is a Code Coverage Tool 107 | *.dotCover 108 | 109 | # NCrunch 110 | _NCrunch_* 111 | .*crunch*.local.xml 112 | nCrunchTemp_* 113 | 114 | # MightyMoose 115 | *.mm.* 116 | AutoTest.Net/ 117 | 118 | # Web workbench (sass) 119 | .sass-cache/ 120 | 121 | # Installshield output folder 122 | [Ee]xpress/ 123 | 124 | # DocProject is a documentation generator add-in 125 | DocProject/buildhelp/ 126 | DocProject/Help/*.HxT 127 | DocProject/Help/*.HxC 128 | DocProject/Help/*.hhc 129 | DocProject/Help/*.hhk 130 | DocProject/Help/*.hhp 131 | DocProject/Help/Html2 132 | DocProject/Help/html 133 | 134 | # Click-Once directory 135 | publish/ 136 | 137 | # Publish Web Output 138 | *.[Pp]ublish.xml 139 | *.azurePubxml 140 | # TODO: Comment the next line if you want to checkin your web deploy settings 141 | # but database connection strings (with potential passwords) will be unencrypted 142 | *.pubxml 143 | *.publishproj 144 | 145 | # NuGet Packages 146 | *.nupkg 147 | # The packages folder can be ignored because of Package Restore 148 | **/packages/* 149 | # except build/, which is used as an MSBuild target. 150 | !**/packages/build/ 151 | # Uncomment if necessary however generally it will be regenerated when needed 152 | #!**/packages/repositories.config 153 | 154 | # Microsoft Azure Build Output 155 | csx/ 156 | *.build.csdef 157 | 158 | # Microsoft Azure Emulator 159 | ecf/ 160 | rcf/ 161 | 162 | # Microsoft Azure ApplicationInsights config file 163 | ApplicationInsights.config 164 | 165 | # Windows Store app package directory 166 | AppPackages/ 167 | BundleArtifacts/ 168 | 169 | # Visual Studio cache files 170 | # files ending in .cache can be ignored 171 | *.[Cc]ache 172 | # but keep track of directories ending in .cache 173 | !*.[Cc]ache/ 174 | 175 | # Others 176 | ClientBin/ 177 | ~$* 178 | *~ 179 | *.dbmdl 180 | *.dbproj.schemaview 181 | *.pfx 182 | *.publishsettings 183 | orleans.codegen.cs 184 | 185 | node_modules/ 186 | 187 | # RIA/Silverlight projects 188 | Generated_Code/ 189 | 190 | # Backup & report files from converting an old project file 191 | # to a newer Visual Studio version. Backup files are not needed, 192 | # because we have git ;-) 193 | _UpgradeReport_Files/ 194 | Backup*/ 195 | UpgradeLog*.XML 196 | UpgradeLog*.htm 197 | 198 | # SQL Server files 199 | *.mdf 200 | *.ldf 201 | 202 | # Business Intelligence projects 203 | *.rdl.data 204 | *.bim.layout 205 | *.bim_*.settings 206 | 207 | # Microsoft Fakes 208 | FakesAssemblies/ 209 | 210 | # GhostDoc plugin setting file 211 | *.GhostDoc.xml 212 | 213 | # Node.js Tools for Visual Studio 214 | .ntvs_analysis.dat 215 | 216 | # Visual Studio 6 build log 217 | *.plg 218 | 219 | # Visual Studio 6 workspace options file 220 | *.opt 221 | 222 | # Visual Studio LightSwitch build output 223 | **/*.HTMLClient/GeneratedArtifacts 224 | **/*.DesktopClient/GeneratedArtifacts 225 | **/*.DesktopClient/ModelManifest.xml 226 | **/*.Server/GeneratedArtifacts 227 | **/*.Server/ModelManifest.xml 228 | _Pvt_Extensions 229 | 230 | # Paket dependency manager 231 | .paket/paket.exe 232 | 233 | # FAKE - F# Make 234 | .fake/ 235 | 236 | **/wwwroot/lib 237 | .angular/ -------------------------------------------------------------------------------- /.nuget/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Controllers/DragHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.SignalR; 2 | 3 | namespace MvcDrag.Controllers 4 | { 5 | public class DragHub : Hub 6 | { 7 | public void ItemDragged(int x, int y) 8 | { 9 | Clients.Others.SendAsync("onDrag", new { X = x, Y = y }); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace MvcDrag.Controllers 4 | { 5 | public class HomeController : Controller 6 | { 7 | public ActionResult Index() 8 | { 9 | return View(); 10 | } 11 | 12 | public ActionResult About() 13 | { 14 | ViewBag.Message = "Your application description page."; 15 | 16 | return View(); 17 | } 18 | 19 | public ActionResult Contact() 20 | { 21 | ViewBag.Message = "Your contact page."; 22 | 23 | return View(); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /DragDrop/MvcDrag/DragMvc.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Models/AccountViewModels.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace MvcDrag.Models 4 | { 5 | public class ExternalLoginConfirmationViewModel 6 | { 7 | [Required] 8 | [EmailAddress] 9 | [Display(Name = "Email")] 10 | public string Email { get; set; } 11 | } 12 | 13 | public class ExternalLoginListViewModel 14 | { 15 | public string Action { get; set; } 16 | public string ReturnUrl { get; set; } 17 | } 18 | 19 | public class ManageUserViewModel 20 | { 21 | [Required] 22 | [DataType(DataType.Password)] 23 | [Display(Name = "Current password")] 24 | public string OldPassword { get; set; } 25 | 26 | [Required] 27 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 28 | [DataType(DataType.Password)] 29 | [Display(Name = "New password")] 30 | public string NewPassword { get; set; } 31 | 32 | [DataType(DataType.Password)] 33 | [Display(Name = "Confirm new password")] 34 | [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] 35 | public string ConfirmPassword { get; set; } 36 | } 37 | 38 | public class LoginViewModel 39 | { 40 | [Required] 41 | [EmailAddress] 42 | [Display(Name = "Email")] 43 | public string Email { get; set; } 44 | 45 | [Required] 46 | [DataType(DataType.Password)] 47 | [Display(Name = "Password")] 48 | public string Password { get; set; } 49 | 50 | [Display(Name = "Remember me?")] 51 | public bool RememberMe { get; set; } 52 | } 53 | 54 | public class RegisterViewModel 55 | { 56 | [Required] 57 | [EmailAddress] 58 | [Display(Name = "Email")] 59 | public string Email { get; set; } 60 | 61 | [Required] 62 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 63 | [DataType(DataType.Password)] 64 | [Display(Name = "Password")] 65 | public string Password { get; set; } 66 | 67 | [DataType(DataType.Password)] 68 | [Display(Name = "Confirm password")] 69 | [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 70 | public string ConfirmPassword { get; set; } 71 | } 72 | 73 | public class ResetPasswordViewModel 74 | { 75 | [Required] 76 | [EmailAddress] 77 | [Display(Name = "Email")] 78 | public string Email { get; set; } 79 | 80 | [Required] 81 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 82 | [DataType(DataType.Password)] 83 | [Display(Name = "Password")] 84 | public string Password { get; set; } 85 | 86 | [DataType(DataType.Password)] 87 | [Display(Name = "Confirm password")] 88 | [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 89 | public string ConfirmPassword { get; set; } 90 | 91 | public string Code { get; set; } 92 | } 93 | 94 | public class ForgotPasswordViewModel 95 | { 96 | [Required] 97 | [EmailAddress] 98 | [Display(Name = "Email")] 99 | public string Email { get; set; } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace DragMvc.Models 2 | { 3 | public class ErrorViewModel 4 | { 5 | public string RequestId { get; set; } 6 | 7 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | using MvcDrag; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace DragMvc 10 | { 11 | public class Program 12 | { 13 | public static void Main(string[] args) 14 | { 15 | CreateHostBuilder(args).Build().Run(); 16 | } 17 | 18 | public static IHostBuilder CreateHostBuilder(string[] args) => 19 | Host.CreateDefaultBuilder(args) 20 | .ConfigureWebHostDefaults(webBuilder => 21 | { 22 | webBuilder.UseStartup(); 23 | }); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:51371/", 7 | "sslPort": 44398 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "DragMvc": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using MvcDrag.Controllers; 7 | 8 | namespace MvcDrag 9 | { 10 | public partial class Startup 11 | { 12 | public IConfiguration Configuration { get; } 13 | public Startup(IConfiguration configuration) 14 | { 15 | Configuration = configuration; 16 | } 17 | public void ConfigureServices(IServiceCollection services) 18 | { 19 | services.AddSignalR() 20 | //.AddAzureSignalR() 21 | ; 22 | services.AddControllersWithViews(); 23 | } 24 | 25 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 26 | { 27 | if (env.IsDevelopment()) 28 | { 29 | app.UseDeveloperExceptionPage(); 30 | } 31 | else 32 | { 33 | app.UseExceptionHandler("/Home/Error"); 34 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 35 | app.UseHsts(); 36 | } 37 | app.UseHttpsRedirection(); 38 | app.UseStaticFiles(); 39 | 40 | app.UseRouting(); 41 | 42 | app.UseAuthorization(); 43 | 44 | app.UseEndpoints(endpoints => 45 | { 46 | endpoints.MapHub("/myDrag"); 47 | endpoints.MapControllerRoute( 48 | name: "default", 49 | pattern: "{controller=Home}/{action=Index}/{id?}"); 50 | 51 | }); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Views/Home/About.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "About"; 3 | } 4 |

@ViewBag.Title.

5 |

@ViewBag.Message

6 | 7 |

Use this area to provide additional information.

8 | -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Views/Home/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Contact"; 3 | } 4 |

@ViewBag.Title.

5 |

@ViewBag.Message

6 | 7 |
8 | One Microsoft Way
9 | Redmond, WA 98052-6399
10 | P: 11 | 425.555.0100 12 |
13 | 14 |
15 | Support: Support@example.com
16 | Marketing: Marketing@example.com 17 |
-------------------------------------------------------------------------------- /DragDrop/MvcDrag/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Home Page"; 3 | } 4 | 5 | 13 | 14 |
15 | 16 |
17 | 18 | @section Scripts { 19 | 20 | 21 | 22 | 43 | } 44 | -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewBag.Title - My ASP.NET Application 7 | 8 | 9 | 10 | 11 | 12 | 18 |
19 |
20 |
21 | @RenderBody() 22 |
23 |
24 |
25 |
26 | © 2020 - WebApplication4 - Privacy 27 |
28 |
29 |
30 | 31 | 32 | s @RenderSection("Scripts", required: false) 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using DragMvc 2 | @using DragMvc.Models 3 | 4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 5 | 6 | -------------------------------------------------------------------------------- /DragDrop/MvcDrag/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /DragDrop/MvcDrag/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwooley/SignalrRxSamples/276e13d13cea957af268dee5a0acb73de5b6023f/DragDrop/MvcDrag/favicon.ico -------------------------------------------------------------------------------- /DragDrop/MvcDrag/libman.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultProvider": "cdnjs", 4 | "libraries": [ 5 | { 6 | "library": "jquery@3.6.1", 7 | "destination": "wwwroot/lib/jquery/" 8 | }, 9 | { 10 | "library": "jqueryui@1.12.1", 11 | "destination": "wwwroot/lib/jqueryui/", 12 | "files": [ 13 | "jquery-ui.min.js" 14 | ] 15 | }, 16 | { 17 | "library": "twitter-bootstrap@5.2.1", 18 | "destination": "wwwroot/lib/twitter-bootstrap/" 19 | }, 20 | { 21 | "library": "microsoft-signalr@6.0.9", 22 | "destination": "wwwroot/lib/microsoft-signalr/" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /DragDrop/MvcDrag/wwwroot/css/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Set width on the form input elements since they're 100% wide by default */ 13 | input, 14 | select, 15 | textarea { 16 | max-width: 280px; 17 | } 18 | -------------------------------------------------------------------------------- /DragDrop/SignalrDragDrop.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30501.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfApplication1", "WebApplication1\WpfApplication1\WpfApplication1.csproj", "{4DA1668B-DF2E-49B3-82C7-1D12092A1F59}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F053A06B-A21F-4602-AEE8-7EAA2C807AAD}" 9 | ProjectSection(SolutionItems) = preProject 10 | WebApplication1\.nuget\NuGet.Config = WebApplication1\.nuget\NuGet.Config 11 | WebApplication1\.nuget\NuGet.exe = WebApplication1\.nuget\NuGet.exe 12 | WebApplication1\.nuget\NuGet.targets = WebApplication1\.nuget\NuGet.targets 13 | EndProjectSection 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvcDrag", "WebApplication1\MvcDrag\MvcDrag.csproj", "{DE39F1D6-1415-4EA1-AB2C-3DE5D3849C5A}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {4DA1668B-DF2E-49B3-82C7-1D12092A1F59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {4DA1668B-DF2E-49B3-82C7-1D12092A1F59}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {4DA1668B-DF2E-49B3-82C7-1D12092A1F59}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {4DA1668B-DF2E-49B3-82C7-1D12092A1F59}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {DE39F1D6-1415-4EA1-AB2C-3DE5D3849C5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {DE39F1D6-1415-4EA1-AB2C-3DE5D3849C5A}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {DE39F1D6-1415-4EA1-AB2C-3DE5D3849C5A}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {DE39F1D6-1415-4EA1-AB2C-3DE5D3849C5A}.Release|Any CPU.Build.0 = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(SolutionProperties) = preSolution 33 | HideSolutionNode = FALSE 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /DragDrop/WpfApplication1/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /DragDrop/WpfApplication1/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DragDrop/WpfApplication1/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace WpfApplication1 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /DragDrop/WpfApplication1/DragWpf.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net5.0-windows10.0.19041 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /DragDrop/WpfApplication1/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /DragDrop/WpfApplication1/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 |  2 | using Microsoft.AspNetCore.SignalR.Client; 3 | using System; 4 | using System.Reactive.Linq; 5 | using System.Reactive.Subjects; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | 12 | namespace WpfApplication1 13 | { 14 | /// 15 | /// Interaction logic for MainWindow.xaml 16 | /// 17 | public partial class MainWindow : Window 18 | { 19 | HubConnection cn; 20 | 21 | public MainWindow() 22 | { 23 | InitializeComponent(); 24 | } 25 | 26 | protected async override void OnInitialized(EventArgs e) 27 | { 28 | base.OnInitialized(e); 29 | cn = new HubConnectionBuilder() 30 | .WithUrl("https://localhost:5001/myDrag") 31 | .WithAutomaticReconnect() 32 | .Build(); 33 | await cn.StartAsync(); 34 | 35 | cn.Closed += async (error) => 36 | { 37 | await Task.Delay(1000); 38 | await cn.StartAsync(); 39 | }; 40 | 41 | ConfigureSignalR(); 42 | ConfigureRx(); 43 | ConfigureRxComplex(); 44 | SetupDrag(); 45 | } 46 | 47 | private void ConfigureSignalR() 48 | { 49 | cn.On("onDrag", val => 50 | { 51 | Canvas.SetLeft(myShape, val.x); 52 | Canvas.SetTop(myShape, val.y); 53 | }); 54 | } 55 | 56 | private void ConfigureRx() 57 | { 58 | cn.AsObservable("onDrag") 59 | .Delay(TimeSpan.FromMilliseconds(250)) 60 | .ObserveOnDispatcher() 61 | .Subscribe(val => 62 | { 63 | Canvas.SetLeft(myShape2, val.x); 64 | Canvas.SetTop(myShape2, val.y); 65 | }); 66 | } 67 | 68 | private void ConfigureRxComplex() 69 | { 70 | cn.AsObservable("onDrag") 71 | .Delay(TimeSpan.FromMilliseconds(250)) 72 | .ObserveOnDispatcher() 73 | .Subscribe(pos => 74 | { 75 | Canvas.SetLeft(myShape3, pos.x); 76 | Canvas.SetTop(myShape3, pos.y); 77 | byte colorVal = Convert.ToByte(Math.Abs(Math.Sin(((int)(pos.x) + (int)pos.y)) * 128) + 127); 78 | 79 | var brush = new SolidColorBrush(Color.FromRgb(colorVal, 0, 0)); 80 | var b2 = new RadialGradientBrush(Color.FromRgb(colorVal, 0, 0), Color.FromRgb(255, 255, 255)); 81 | myShape.Fill = b2; 82 | }); 83 | } 84 | 85 | private void SetupDrag() 86 | { 87 | var mouseDown = from evt in Observable.FromEventPattern(myShape, "MouseLeftButtonDown") 88 | select evt.EventArgs.GetPosition(myShape); 89 | var mouseUp = Observable.FromEventPattern(this, "MouseLeftButtonUp"); 90 | var mouseMove = from evt in Observable.FromEventPattern(this, "MouseMove") 91 | select evt.EventArgs.GetPosition(this); 92 | 93 | var query = from startLocation in mouseDown 94 | from endLocation in mouseMove 95 | .TakeUntil(mouseUp) 96 | select new 97 | { 98 | x = Convert.ToInt32(endLocation.X - startLocation.X), 99 | y = Convert.ToInt32(endLocation.Y - startLocation.Y) 100 | }; 101 | 102 | query.Subscribe(position => 103 | { 104 | Canvas.SetLeft(myShape, position.x); 105 | Canvas.SetTop(myShape, position.y); 106 | }); 107 | 108 | query.Throttle(TimeSpan.FromMilliseconds(250)) 109 | .Subscribe(position => cn.InvokeAsync("ItemDragged", position.x, position.y)); 110 | } 111 | } 112 | 113 | public static class ObservableExtensions 114 | { 115 | public static IObservable AsObservable(this HubConnection connection, string methodName) 116 | { 117 | var subject = new Subject(); 118 | connection.On(methodName, (val) => subject.OnNext(val)); 119 | 120 | return subject; 121 | } 122 | } 123 | public class Coord 124 | { 125 | public int x { get; set; } 126 | public int y { get; set; } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /DragDrop/WpfApplication1/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.32559 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WpfApplication1.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WpfApplication1.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /DragDrop/WpfApplication1/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /DragDrop/WpfApplication1/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.32559 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WpfApplication1.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /DragDrop/WpfApplication1/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /DragDrop/WpfApplication1/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /EnumerableRange.linq: -------------------------------------------------------------------------------- 1 | 2 | System.Interactive 3 | System.Reactive 4 | System.Collections.Generic 5 | System.Collections.ObjectModel 6 | System.Linq 7 | System.Reactive.Concurrency 8 | System.Reactive.Linq 9 | System.Threading.Tasks 10 | 11 | 12 | var sw = new Stopwatch(); 13 | sw.Start(); 14 | 15 | var query = Enumerable.Range(1,10) 16 | .Where(num => num % 2 == 0) 17 | .Do(num => Task.Delay((10 - num) * 200).Wait()) 18 | .Select(num => $"Num is {num}"); 19 | 20 | foreach (var num in query) 21 | { 22 | Console.WriteLine(num); 23 | } 24 | 25 | Console.WriteLine("Elapsed Time: " + sw.ElapsedTicks); 26 | 27 | Console.WriteLine("Done. You can continue working."); -------------------------------------------------------------------------------- /Getting Pushy with SignalR and Rx.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwooley/SignalrRxSamples/276e13d13cea957af268dee5a0acb73de5b6023f/Getting Pushy with SignalR and Rx.pptx -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jim Wooley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /ObservableRange.linq: -------------------------------------------------------------------------------- 1 | 2 | System.Interactive 3 | System.Reactive 4 | System.Collections.ObjectModel 5 | System.Reactive.Concurrency 6 | System.Reactive.Linq 7 | System.Threading.Tasks 8 | 9 | 10 | var sw = new Stopwatch(); 11 | sw.Start(); 12 | 13 | var query = Observable.Range(1, 10) 14 | .Where(num => num % 2 == 0) 15 | .ObserveOn(TaskPoolScheduler.Default) 16 | .Do(num => Task.Delay((10 - num) * 200).Wait()); 17 | 18 | query.Subscribe(num => Console.WriteLine(num), 19 | () => Console.WriteLine("Elapsed Time: " + sw.ElapsedTicks) 20 | ); 21 | 22 | Console.WriteLine("Done. You can continue working."); 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | //var query = Observable.Range(1,10) 31 | // .Where(num => num % 2 == 0) 32 | // .Do(num => Task.Delay((10 - num) * 200).Wait()); 33 | // 34 | //query.Subscribe(val => Console.WriteLine(val)); 35 | 36 | //var query = Observable.Range(1, 10) 37 | // .ObserveOn(TaskPoolScheduler.Default) 38 | // .Where(num => num % 2 == 0) 39 | // .Do(num => Task.Delay((10 - num) * 200).Wait()) 40 | // .Subscribe(num => Console.WriteLine(num), 41 | // () => 42 | // { 43 | // Console.WriteLine("Elapsed Time: " + sw.ElapsedTicks); 44 | // } 45 | // ); -------------------------------------------------------------------------------- /ObservableSensor.linq: -------------------------------------------------------------------------------- 1 | 2 | <RuntimeDirectory>\System.Runtime.dll 3 | System.Reactive 4 | System.Reactive.Linq 5 | System.Runtime 6 | 7 | 8 | Sub Main 9 | Dim sensor = GetSensor() 10 | 11 | sensor.DumpLatest("All Values") 12 | 13 | Dim Q1 = From s In sensor 14 | Where s.SensorValue < 3 15 | Select s 16 | 17 | Q1.DumpLatest("Low Value Sensors") 18 | 19 | Dim Q2 = From s In sensor 20 | Where s.SensorType = "2" 21 | Select s 22 | 23 | Q2.DumpLatest("Type 2 Sensors") 24 | 25 | Dim Heartbeat = (From s In sensor 26 | Where s.SensorType = "2"). 27 | Buffer(TimeSpan.FromSeconds(3)). 28 | Select(Function(b) b.Count * 20). 29 | DumpLatest("Approximate 2's per minute") 30 | 31 | Dim GroupTest = 32 | sensor. 33 | GroupBy(Function(s) s.SensorType). 34 | Select(Function(s) s.DumpLatest($"Latest for key: {s.Key}")). 35 | Subscribe() 36 | 37 | 38 | End Sub 39 | 40 | ' Define other methods and classes here 41 | Public Class SensorInfo 42 | Public Property TimeStamp As DateTime 43 | Public Property SensorType As String 44 | Public Property SensorValue As Double 45 | Public Overrides Function ToString() As String 46 | Return String.Format("Time: {0} , Type: {1} Value: {2}", TimeStamp, SensorType, SensorValue) 47 | End Function 48 | End Class 49 | 50 | Public Function GetSensor() As IObservable(Of SensorInfo) 51 | Dim rnd = New Random(Date.Now.Millisecond) 52 | Return Observable.Generate(Of Double, SensorInfo)( 53 | initialState:=0.0, 54 | condition:=Function(x) True, 55 | iterate:= Function(inVal) rnd.NextDouble, 56 | resultSelector:= Function(val) 57 | Return New SensorInfo With { 58 | .SensorType = (math.Floor(val * 4) + 1).ToString, 59 | .SensorValue = rnd.NextDouble * 20, 60 | .TimeStamp = DateTime.Now} 61 | End Function, 62 | timeSelector := Function(val) TimeSpan.FromMilliseconds(val * 100) 63 | ) 64 | End Function -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SignalrRxSamples 2 | ================ 3 | 4 | Demos for combining Signalr and Rx including Drag Drop, and Observable sensors 5 | -------------------------------------------------------------------------------- /RxSignalR/BasicConsole/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /RxSignalR/BasicConsole/Module1.vb: -------------------------------------------------------------------------------- 1 | Option Strict Off 2 | 3 | Imports Microsoft.AspNet.SignalR.Client.Hubs 4 | Imports System.Reactive.Linq 5 | Imports Microsoft.AspNet.SignalR.Client 6 | 7 | Module Module1 8 | 9 | Sub Main() 10 | Dim cn = New HubConnection("http://localhost:5687") 11 | Dim sensor = cn.CreateHubProxy("observableSensorHub") 12 | Dim items = From item In sensor.Observe("broadcast") 13 | Let instance = item(0).ToObject(Of SensorData)() 14 | Where instance.Value < 5 15 | Select instance 16 | 17 | Using items.Subscribe(Sub(value) Console.WriteLine(value.Value)) 18 | 19 | cn.Start().Wait() 20 | Console.ReadLine() 21 | 22 | End Using 23 | 24 | End Sub 25 | 26 | End Module -------------------------------------------------------------------------------- /RxSignalR/BasicConsole/My Project/Application.Designer.vb: -------------------------------------------------------------------------------- 1 | '------------------------------------------------------------------------------ 2 | ' 3 | ' This code was generated by a tool. 4 | ' Runtime Version:4.0.30319.17626 5 | ' 6 | ' Changes to this file may cause incorrect behavior and will be lost if 7 | ' the code is regenerated. 8 | ' 9 | '------------------------------------------------------------------------------ 10 | 11 | Option Strict On 12 | Option Explicit On 13 | 14 | -------------------------------------------------------------------------------- /RxSignalR/BasicConsole/My Project/Application.myapp: -------------------------------------------------------------------------------- 1 |  2 | 3 | false 4 | false 5 | 0 6 | true 7 | 0 8 | 2 9 | true 10 | 11 | -------------------------------------------------------------------------------- /RxSignalR/BasicConsole/My Project/AssemblyInfo.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Reflection 3 | Imports System.Runtime.InteropServices 4 | 5 | ' General Information about an assembly is controlled through the following 6 | ' set of attributes. Change these attribute values to modify the information 7 | ' associated with an assembly. 8 | 9 | ' Review the values of the assembly attributes 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 'The following GUID is for the ID of the typelib if this project is exposed to COM 21 | 22 | 23 | ' Version information for an assembly consists of the following four values: 24 | ' 25 | ' Major Version 26 | ' Minor Version 27 | ' Build Number 28 | ' Revision 29 | ' 30 | ' You can specify all the values or you can default the Build and Revision Numbers 31 | ' by using the '*' as shown below: 32 | ' 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /RxSignalR/BasicConsole/My Project/Resources.Designer.vb: -------------------------------------------------------------------------------- 1 | '------------------------------------------------------------------------------ 2 | ' 3 | ' This code was generated by a tool. 4 | ' Runtime Version:4.0.30319.17626 5 | ' 6 | ' Changes to this file may cause incorrect behavior and will be lost if 7 | ' the code is regenerated. 8 | ' 9 | '------------------------------------------------------------------------------ 10 | 11 | Option Strict On 12 | Option Explicit On 13 | 14 | 15 | Namespace My.Resources 16 | 17 | 'This class was auto-generated by the StronglyTypedResourceBuilder 18 | 'class via a tool like ResGen or Visual Studio. 19 | 'To add or remove a member, edit your .ResX file then rerun ResGen 20 | 'with the /str option, or rebuild your VS project. 21 | ''' 22 | ''' A strongly-typed resource class, for looking up localized strings, etc. 23 | ''' 24 | _ 28 | Friend Module Resources 29 | 30 | Private resourceMan As Global.System.Resources.ResourceManager 31 | 32 | Private resourceCulture As Global.System.Globalization.CultureInfo 33 | 34 | ''' 35 | ''' Returns the cached ResourceManager instance used by this class. 36 | ''' 37 | _ 38 | Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager 39 | Get 40 | If Object.ReferenceEquals(resourceMan, Nothing) Then 41 | Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("BasicConsole.Resources", GetType(Resources).Assembly) 42 | resourceMan = temp 43 | End If 44 | Return resourceMan 45 | End Get 46 | End Property 47 | 48 | ''' 49 | ''' Overrides the current thread's CurrentUICulture property for all 50 | ''' resource lookups using this strongly typed resource class. 51 | ''' 52 | _ 53 | Friend Property Culture() As Global.System.Globalization.CultureInfo 54 | Get 55 | Return resourceCulture 56 | End Get 57 | Set(ByVal value As Global.System.Globalization.CultureInfo) 58 | resourceCulture = value 59 | End Set 60 | End Property 61 | End Module 62 | End Namespace 63 | -------------------------------------------------------------------------------- /RxSignalR/BasicConsole/My Project/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /RxSignalR/BasicConsole/My Project/Settings.Designer.vb: -------------------------------------------------------------------------------- 1 | '------------------------------------------------------------------------------ 2 | ' 3 | ' This code was generated by a tool. 4 | ' Runtime Version:4.0.30319.17626 5 | ' 6 | ' Changes to this file may cause incorrect behavior and will be lost if 7 | ' the code is regenerated. 8 | ' 9 | '------------------------------------------------------------------------------ 10 | 11 | Option Strict On 12 | Option Explicit On 13 | 14 | 15 | Namespace My 16 | 17 | _ 20 | Partial Friend NotInheritable Class MySettings 21 | Inherits Global.System.Configuration.ApplicationSettingsBase 22 | 23 | Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings), MySettings) 24 | 25 | #Region "My.Settings Auto-Save Functionality" 26 | #If _MyType = "WindowsForms" Then 27 | Private Shared addedHandler As Boolean 28 | 29 | Private Shared addedHandlerLockObject As New Object 30 | 31 | _ 32 | Private Shared Sub AutoSaveSettings(ByVal sender As Global.System.Object, ByVal e As Global.System.EventArgs) 33 | If My.Application.SaveMySettingsOnExit Then 34 | My.Settings.Save() 35 | End If 36 | End Sub 37 | #End If 38 | #End Region 39 | 40 | Public Shared ReadOnly Property [Default]() As MySettings 41 | Get 42 | 43 | #If _MyType = "WindowsForms" Then 44 | If Not addedHandler Then 45 | SyncLock addedHandlerLockObject 46 | If Not addedHandler Then 47 | AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings 48 | addedHandler = True 49 | End If 50 | End SyncLock 51 | End If 52 | #End If 53 | Return defaultInstance 54 | End Get 55 | End Property 56 | End Class 57 | End Namespace 58 | 59 | Namespace My 60 | 61 | _ 64 | Friend Module MySettingsProperty 65 | 66 | _ 67 | Friend ReadOnly Property Settings() As Global.BasicConsole.My.MySettings 68 | Get 69 | Return Global.BasicConsole.My.MySettings.Default 70 | End Get 71 | End Property 72 | End Module 73 | End Namespace 74 | -------------------------------------------------------------------------------- /RxSignalR/BasicConsole/My Project/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RxSignalR/BasicConsole/RxSignalrConsole.vbproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {DB4DE111-4C53-43E0-894A-5D3B0C54A782} 8 | Exe 9 | BasicConsole.Module1 10 | BasicConsole 11 | BasicConsole 12 | 512 13 | Console 14 | v4.5 15 | C:\Users\Jim\AppData\Local\Temp\Temp1_RxSignalR (1).zip\RxSignalR\ 16 | true 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | true 23 | true 24 | bin\Debug\ 25 | BasicConsole.xml 26 | 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | false 32 | true 33 | true 34 | bin\Release\ 35 | BasicConsole.xml 36 | 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 37 | 38 | 39 | On 40 | 41 | 42 | Binary 43 | 44 | 45 | Off 46 | 47 | 48 | On 49 | 50 | 51 | 52 | ..\..\packages\Microsoft.AspNet.SignalR.Client.2.2.2\lib\net45\Microsoft.AspNet.SignalR.Client.dll 53 | 54 | 55 | ..\..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll 56 | 57 | 58 | ..\packages\SignalR.Client.0.5.2\lib\net40\SignalR.Client.dll 59 | 60 | 61 | 62 | 63 | 64 | False 65 | ..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll 66 | 67 | 68 | False 69 | ..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll 70 | 71 | 72 | False 73 | ..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll 74 | 75 | 76 | False 77 | ..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | True 100 | Application.myapp 101 | 102 | 103 | True 104 | True 105 | Resources.resx 106 | 107 | 108 | True 109 | Settings.settings 110 | True 111 | 112 | 113 | 114 | 115 | 116 | VbMyResourcesResXFileCodeGenerator 117 | Resources.Designer.vb 118 | My.Resources 119 | Designer 120 | 121 | 122 | 123 | 124 | MyApplicationCodeGenerator 125 | Application.Designer.vb 126 | 127 | 128 | SettingsSingleFileGenerator 129 | My 130 | Settings.Designer.vb 131 | 132 | 133 | 134 | 135 | 136 | 137 | 144 | -------------------------------------------------------------------------------- /RxSignalR/BasicConsole/SensorData.vb: -------------------------------------------------------------------------------- 1 | Imports Microsoft.VisualBasic 2 | 3 | Public Class SensorData 4 | Public Property Time As DateTime 5 | Public Property Category As String 6 | Public Property Value As Double 7 | End Class 8 | -------------------------------------------------------------------------------- /RxSignalR/BasicConsole/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalR.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.20617.1 PREVIEW 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "RxSignalrWeb", "http://localhost:5687", "{1B0911F0-46B4-4767-8358-494E64FA7DB7}" 7 | ProjectSection(WebsiteProperties) = preProject 8 | UseIISExpress = "true" 9 | TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.5" 10 | Debug.AspNetCompiler.VirtualPath = "/localhost_5687" 11 | Debug.AspNetCompiler.PhysicalPath = "RxSignalrWeb\" 12 | Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_5687\" 13 | Debug.AspNetCompiler.Updateable = "true" 14 | Debug.AspNetCompiler.ForceOverwrite = "true" 15 | Debug.AspNetCompiler.FixedNames = "false" 16 | Debug.AspNetCompiler.Debug = "True" 17 | Release.AspNetCompiler.VirtualPath = "/localhost_5687" 18 | Release.AspNetCompiler.PhysicalPath = "RxSignalrWeb\" 19 | Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_5687\" 20 | Release.AspNetCompiler.Updateable = "true" 21 | Release.AspNetCompiler.ForceOverwrite = "true" 22 | Release.AspNetCompiler.FixedNames = "false" 23 | Release.AspNetCompiler.Debug = "False" 24 | SlnRelativePath = "RxSignalrWeb\" 25 | DefaultWebSiteLanguage = "Visual Basic" 26 | EndProjectSection 27 | EndProject 28 | Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "BasicConsole", "BasicConsole\BasicConsole.vbproj", "{DB4DE111-4C53-43E0-894A-5D3B0C54A782}" 29 | EndProject 30 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfApplication1", "WpfApplication1\WpfApplication1.csproj", "{B7253637-3CDB-4774-8B4E-9E5E32C32214}" 31 | EndProject 32 | Global 33 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 34 | Debug|Any CPU = Debug|Any CPU 35 | Debug|ARM = Debug|ARM 36 | Debug|x64 = Debug|x64 37 | Debug|x86 = Debug|x86 38 | Release|Any CPU = Release|Any CPU 39 | Release|ARM = Release|ARM 40 | Release|x64 = Release|x64 41 | Release|x86 = Release|x86 42 | EndGlobalSection 43 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 44 | {1B0911F0-46B4-4767-8358-494E64FA7DB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {1B0911F0-46B4-4767-8358-494E64FA7DB7}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {1B0911F0-46B4-4767-8358-494E64FA7DB7}.Debug|ARM.ActiveCfg = Debug|Any CPU 47 | {1B0911F0-46B4-4767-8358-494E64FA7DB7}.Debug|x64.ActiveCfg = Debug|Any CPU 48 | {1B0911F0-46B4-4767-8358-494E64FA7DB7}.Debug|x86.ActiveCfg = Debug|Any CPU 49 | {1B0911F0-46B4-4767-8358-494E64FA7DB7}.Release|Any CPU.ActiveCfg = Debug|Any CPU 50 | {1B0911F0-46B4-4767-8358-494E64FA7DB7}.Release|Any CPU.Build.0 = Debug|Any CPU 51 | {1B0911F0-46B4-4767-8358-494E64FA7DB7}.Release|ARM.ActiveCfg = Debug|Any CPU 52 | {1B0911F0-46B4-4767-8358-494E64FA7DB7}.Release|x64.ActiveCfg = Debug|Any CPU 53 | {1B0911F0-46B4-4767-8358-494E64FA7DB7}.Release|x86.ActiveCfg = Debug|Any CPU 54 | {DB4DE111-4C53-43E0-894A-5D3B0C54A782}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {DB4DE111-4C53-43E0-894A-5D3B0C54A782}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {DB4DE111-4C53-43E0-894A-5D3B0C54A782}.Debug|ARM.ActiveCfg = Debug|Any CPU 57 | {DB4DE111-4C53-43E0-894A-5D3B0C54A782}.Debug|x64.ActiveCfg = Debug|Any CPU 58 | {DB4DE111-4C53-43E0-894A-5D3B0C54A782}.Debug|x86.ActiveCfg = Debug|Any CPU 59 | {DB4DE111-4C53-43E0-894A-5D3B0C54A782}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {DB4DE111-4C53-43E0-894A-5D3B0C54A782}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {DB4DE111-4C53-43E0-894A-5D3B0C54A782}.Release|ARM.ActiveCfg = Release|Any CPU 62 | {DB4DE111-4C53-43E0-894A-5D3B0C54A782}.Release|x64.ActiveCfg = Release|Any CPU 63 | {DB4DE111-4C53-43E0-894A-5D3B0C54A782}.Release|x86.ActiveCfg = Release|Any CPU 64 | {B7253637-3CDB-4774-8B4E-9E5E32C32214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 65 | {B7253637-3CDB-4774-8B4E-9E5E32C32214}.Debug|Any CPU.Build.0 = Debug|Any CPU 66 | {B7253637-3CDB-4774-8B4E-9E5E32C32214}.Debug|ARM.ActiveCfg = Debug|Any CPU 67 | {B7253637-3CDB-4774-8B4E-9E5E32C32214}.Debug|x64.ActiveCfg = Debug|Any CPU 68 | {B7253637-3CDB-4774-8B4E-9E5E32C32214}.Debug|x86.ActiveCfg = Debug|Any CPU 69 | {B7253637-3CDB-4774-8B4E-9E5E32C32214}.Release|Any CPU.ActiveCfg = Release|Any CPU 70 | {B7253637-3CDB-4774-8B4E-9E5E32C32214}.Release|Any CPU.Build.0 = Release|Any CPU 71 | {B7253637-3CDB-4774-8B4E-9E5E32C32214}.Release|ARM.ActiveCfg = Release|Any CPU 72 | {B7253637-3CDB-4774-8B4E-9E5E32C32214}.Release|x64.ActiveCfg = Release|Any CPU 73 | {B7253637-3CDB-4774-8B4E-9E5E32C32214}.Release|x86.ActiveCfg = Release|Any CPU 74 | EndGlobalSection 75 | GlobalSection(SolutionProperties) = preSolution 76 | HideSolutionNode = FALSE 77 | EndGlobalSection 78 | EndGlobal 79 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Hubs/ChatHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.SignalR; 2 | using System.Threading.Tasks; 3 | 4 | namespace RxSignalrSharpWeb.Hubs 5 | { 6 | public class ChatHub : Hub 7 | { 8 | public async Task Send(string message) 9 | { 10 | await Clients.All.SendAsync("addMessage", message); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Hubs/SensorHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.SignalR; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reactive.Linq; 6 | using System.Threading; 7 | using System.Threading.Channels; 8 | using System.Threading.Tasks; 9 | 10 | namespace RxSignalrSharpWeb.Hubs 11 | { 12 | public class SensorHub : Hub 13 | { 14 | public SensorHub() 15 | { 16 | if (_Sensor == null) 17 | { 18 | var rand = new Random(DateTime.Now.Millisecond); 19 | _Sensor = Observable.Generate( 20 | initialState: 0.0, 21 | condition: x => true, 22 | iterate: inVal => rand.NextDouble(), 23 | resultSelector: val => new SensorData 24 | { 25 | TimeStamp = DateTime.Now, 26 | SensorType = (Math.Floor(val * 4) + 1).ToString(), 27 | SensorValue = val * 20 28 | }, 29 | timeSelector: val => TimeSpan.FromMilliseconds(val * 1000)) 30 | .Publish() 31 | .AutoConnect(1); 32 | } 33 | } 34 | private static IObservable _Sensor = null; 35 | 36 | public IAsyncEnumerable Values() 37 | { 38 | return _Sensor.ToAsyncEnumerable(); 39 | } 40 | public ChannelReader Vs() 41 | { 42 | var cts = new CancellationTokenSource(); 43 | 44 | return _Sensor.Where(s => s.SensorType == "1") 45 | .AsChannelReader(cts.Token); 46 | } 47 | } 48 | 49 | public static class ObservableExtensions 50 | { 51 | public static ChannelReader AsChannelReader(this IObservable observable, CancellationToken cancellationToken) 52 | { 53 | // This sample shows adapting an observable to a ChannelReader without 54 | // back pressure, if the connection is slower than the producer, memory will 55 | // start to increase. 56 | 57 | // If the channel is bounded, TryWrite will return false and effectively 58 | // drop items. 59 | 60 | // The other alternative is to use a bounded channel, and when the limit is reached 61 | // block on WaitToWriteAsync. This will block a thread pool thread and isn't recommended and isn't shown here. 62 | var channel = Channel.CreateUnbounded(); 63 | 64 | var disposable = observable.Subscribe( 65 | value => channel.Writer.TryWrite(value), 66 | error => channel.Writer.TryComplete(error), 67 | () => channel.Writer.TryComplete()); 68 | 69 | // Complete the subscription on the reader completing 70 | //channel.Reader.Completion.ContinueWith(task => disposable.Dispose()); 71 | 72 | return channel.Reader; 73 | } 74 | } 75 | 76 | public class SensorData 77 | { 78 | public DateTime TimeStamp { get; set; } 79 | public String SensorType { get; set; } 80 | public double SensorValue { get; set; } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Hubs/StreamHub.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reactive.Linq; 4 | using System.Threading; 5 | using System.Threading.Channels; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.SignalR; 8 | using RxSignalrSharpWeb.Hubs; 9 | 10 | namespace SignalRChat.Hubs 11 | { 12 | public class StreamHub : Hub 13 | { 14 | public ChannelReader Counter( 15 | int count, 16 | int delay, 17 | CancellationToken cancellationToken) 18 | { 19 | var channel = Channel.CreateUnbounded(); 20 | 21 | // We don't want to await WriteItemsAsync, otherwise we'd end up waiting 22 | // for all the items to be written before returning the channel back to 23 | // the client. 24 | _ = WriteItemsAsync(channel.Writer, count, delay, cancellationToken); 25 | 26 | return channel.Reader; 27 | } 28 | 29 | private async Task WriteItemsAsync( 30 | ChannelWriter writer, 31 | int count, 32 | int delay, 33 | CancellationToken cancellationToken) 34 | { 35 | Exception localException = null; 36 | try 37 | { 38 | for (var i = 0; i < count; i++) 39 | { 40 | await writer.WriteAsync(i, cancellationToken); 41 | 42 | // Use the cancellationToken in other APIs that accept cancellation 43 | // tokens so the cancellation can flow down to them. 44 | await Task.Delay(delay, cancellationToken); 45 | } 46 | } 47 | catch (Exception ex) 48 | { 49 | localException = ex; 50 | } 51 | finally 52 | { 53 | writer.Complete(localException); 54 | } 55 | } 56 | 57 | public async Task UploadStream(ChannelReader stream) 58 | { 59 | while (await stream.WaitToReadAsync()) 60 | { 61 | while (stream.TryRead(out var item)) 62 | { 63 | // do something with the stream item 64 | Console.WriteLine(item); 65 | } 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/Chat.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | 3 | 4 | 5 |
    6 |
7 | 8 | 9 | 10 | 27 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/Chat.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace RxSignalrSharpWeb.Pages 9 | { 10 | public class ChatModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/Counter.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 |
3 |
 
4 |
5 |
 
6 |
7 | 8 | 9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
 
18 |
19 |
    20 |
    21 |
    22 |
    23 | 24 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/Counter.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | 4 | namespace RxSignalrSharpWeb.Pages 5 | { 6 | public class CounterModel : PageModel 7 | { 8 | public void OnGet() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

    Error.

    8 |

    An error occurred while processing your request.

    9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

    13 | Request ID: @Model.RequestId 14 |

    15 | } 16 | 17 |

    Development Mode

    18 |

    19 | Swapping to the Development environment displays detailed information about the error that occurred. 20 |

    21 |

    22 | The Development environment shouldn't be enabled for deployed applications. 23 | It can result in displaying sensitive information from exceptions to end users. 24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 25 | and restarting the app. 26 |

    27 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace RxSignalrSharpWeb.Pages 11 | { 12 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 13 | public class ErrorModel : PageModel 14 | { 15 | public string RequestId { get; set; } 16 | 17 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 18 | 19 | private readonly ILogger _logger; 20 | 21 | public ErrorModel(ILogger logger) 22 | { 23 | _logger = logger; 24 | } 25 | 26 | public void OnGet() 27 | { 28 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @{ 3 | ViewData["Title"] = "Sensor"; 4 | } 5 | 6 |

    Sensor Values

    7 |
      8 | 9 | 10 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace RxSignalrSharpWeb.Pages 10 | { 11 | public class IndexModel : PageModel 12 | { 13 | private readonly ILogger _logger; 14 | 15 | public IndexModel(ILogger logger) 16 | { 17 | _logger = logger; 18 | } 19 | 20 | public void OnGet() 21 | { 22 | 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model PrivacyModel 3 | @{ 4 | ViewData["Title"] = "Privacy Policy"; 5 | } 6 |

      @ViewData["Title"]

      7 | 8 |

      Use this page to detail your site's privacy policy.

      9 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/Privacy.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace RxSignalrSharpWeb.Pages 10 | { 11 | public class PrivacyModel : PageModel 12 | { 13 | private readonly ILogger _logger; 14 | 15 | public PrivacyModel(ILogger logger) 16 | { 17 | _logger = logger; 18 | } 19 | 20 | public void OnGet() 21 | { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - RxSignalrSharpWeb 7 | 8 | 9 | 10 | 11 |
      12 | 34 |
      35 |
      36 |
      37 | @RenderBody() 38 |
      39 |
      40 | 41 |
      42 |
      43 | © 2020 - RxSignalrSharpWeb - Privacy 44 |
      45 |
      46 | 47 | 48 | @* *@ 51 | 52 | 53 | 54 | @RenderSection("Scripts", required: false) 55 | 56 | 57 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using RxSignalrSharpWeb 2 | @namespace RxSignalrSharpWeb.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace RxSignalrSharpWeb 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:64505", 7 | "sslPort": 44359 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "RxSignalrSharpWeb": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/RxSignalrSharpWeb.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.HttpsPolicy; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Hosting; 11 | using RxSignalrSharpWeb.Hubs; 12 | using SignalRChat.Hubs; 13 | 14 | namespace RxSignalrSharpWeb 15 | { 16 | public class Startup 17 | { 18 | public Startup(IConfiguration configuration) 19 | { 20 | Configuration = configuration; 21 | } 22 | 23 | public IConfiguration Configuration { get; } 24 | 25 | // This method gets called by the runtime. Use this method to add services to the container. 26 | public void ConfigureServices(IServiceCollection services) 27 | { 28 | services.AddRazorPages(); 29 | services.AddSignalR(); 30 | } 31 | 32 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 33 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 34 | { 35 | if (env.IsDevelopment()) 36 | { 37 | app.UseDeveloperExceptionPage(); 38 | } 39 | else 40 | { 41 | app.UseExceptionHandler("/Error"); 42 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 43 | app.UseHsts(); 44 | } 45 | 46 | app.UseHttpsRedirection(); 47 | app.UseStaticFiles(); 48 | 49 | app.UseRouting(); 50 | 51 | app.UseAuthorization(); 52 | 53 | app.UseEndpoints(endpoints => 54 | { 55 | endpoints.MapHub("/sensorHub"); 56 | endpoints.MapHub("/streamHub"); 57 | endpoints.MapHub("hub/chat"); 58 | endpoints.MapRazorPages(); 59 | }); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/libman.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultProvider": "cdnjs", 4 | "libraries": [ 5 | { 6 | "library": "jquery@3.6.1", 7 | "destination": "wwwroot/lib/jquery/" 8 | }, 9 | { 10 | "library": "microsoft-signalr@6.0.9", 11 | "destination": "wwwroot/lib/microsoft-signalr/" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rxsignalrsharpweb", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@microsoft/signalr": "^6.0.9" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | /* Provide sufficient contrast against white background */ 11 | a { 12 | color: #0366d6; 13 | } 14 | 15 | .btn-primary { 16 | color: #fff; 17 | background-color: #1b6ec2; 18 | border-color: #1861ac; 19 | } 20 | 21 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 22 | color: #fff; 23 | background-color: #1b6ec2; 24 | border-color: #1861ac; 25 | } 26 | 27 | /* Sticky footer styles 28 | -------------------------------------------------- */ 29 | html { 30 | font-size: 14px; 31 | } 32 | @media (min-width: 768px) { 33 | html { 34 | font-size: 16px; 35 | } 36 | } 37 | 38 | .border-top { 39 | border-top: 1px solid #e5e5e5; 40 | } 41 | .border-bottom { 42 | border-bottom: 1px solid #e5e5e5; 43 | } 44 | 45 | .box-shadow { 46 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 47 | } 48 | 49 | button.accept-policy { 50 | font-size: 1rem; 51 | line-height: inherit; 52 | } 53 | 54 | /* Sticky footer styles 55 | -------------------------------------------------- */ 56 | html { 57 | position: relative; 58 | min-height: 100%; 59 | } 60 | 61 | body { 62 | /* Margin bottom by footer height */ 63 | margin-bottom: 60px; 64 | } 65 | .footer { 66 | position: absolute; 67 | bottom: 0; 68 | width: 100%; 69 | white-space: nowrap; 70 | line-height: 60px; /* Vertically center the text there */ 71 | } 72 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwooley/SignalrRxSamples/276e13d13cea957af268dee5a0acb73de5b6023f/RxSignalR/RxSignalrSharpWeb/wwwroot/favicon.ico -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your Javascript code. 5 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/wwwroot/js/stream.js: -------------------------------------------------------------------------------- 1 | // The following sample code uses TypeScript and must be compiled to JavaScript 2 | // before a browser can execute it. 3 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | const connection = new signalR.HubConnectionBuilder() 12 | .withUrl("/streamHub") 13 | .build(); 14 | document.getElementById("streamButton").addEventListener("click", (event) => __awaiter(this, void 0, void 0, function* () { 15 | try { 16 | connection.stream("Counter", 10, 500) 17 | .subscribe({ 18 | next: (item) => { 19 | var li = document.createElement("li"); 20 | li.textContent = item; 21 | document.getElementById("messagesList").appendChild(li); 22 | }, 23 | complete: () => { 24 | var li = document.createElement("li"); 25 | li.textContent = "Stream completed"; 26 | document.getElementById("messagesList").appendChild(li); 27 | }, 28 | error: (err) => { 29 | var li = document.createElement("li"); 30 | li.textContent = err; 31 | document.getElementById("messagesList").appendChild(li); 32 | }, 33 | }); 34 | } 35 | catch (e) { 36 | console.error(e.toString()); 37 | } 38 | event.preventDefault(); 39 | })); 40 | document.getElementById("uploadButton").addEventListener("click", (event) => __awaiter(this, void 0, void 0, function* () { 41 | const subject = new signalR.Subject(); 42 | yield connection.send("UploadStream", subject); 43 | var iteration = 0; 44 | const intervalHandle = setInterval(() => { 45 | iteration++; 46 | subject.next(iteration.toString()); 47 | if (iteration === 10) { 48 | clearInterval(intervalHandle); 49 | subject.complete(); 50 | } 51 | }, 500); 52 | event.preventDefault(); 53 | })); 54 | // We need an async function in order to use await, but we want this code to run immediately, 55 | // so we use an "immediately-executed async function" 56 | (() => __awaiter(this, void 0, void 0, function* () { 57 | try { 58 | yield connection.start(); 59 | } 60 | catch (e) { 61 | console.error(e.toString()); 62 | } 63 | }))(); -------------------------------------------------------------------------------- /RxSignalR/RxSignalrSharpWeb/wwwroot/js/stream2.js: -------------------------------------------------------------------------------- 1 | // The following sample code uses TypeScript and must be compiled to JavaScript 2 | // before a browser can execute it. 3 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | const connection = new signalR.HubConnectionBuilder() 12 | .withUrl("/sensorHub") 13 | .build(); 14 | document.getElementById("streamButton").addEventListener("click", (event) => __awaiter(this, void 0, void 0, function* () { 15 | try { 16 | connection.stream("Values", 1) 17 | .subscribe({ 18 | next: (item) => { 19 | var li = document.createElement("li"); 20 | li.textContent = item.timeStamp + ': ' + item.sensorValue; 21 | document.getElementById("messagesList").prepend(li); 22 | }, 23 | complete: () => { 24 | var li = document.createElement("li"); 25 | li.textContent = "Stream completed"; 26 | document.getElementById("messagesList").appendChild(li); 27 | }, 28 | error: (err) => { 29 | var li = document.createElement("li"); 30 | li.textContent = err; 31 | document.getElementById("messagesList").appendChild(li); 32 | }, 33 | }); 34 | } 35 | catch (e) { 36 | console.error(e.toString()); 37 | } 38 | event.preventDefault(); 39 | })); 40 | document.getElementById("uploadButton").addEventListener("click", (event) => __awaiter(this, void 0, void 0, function* () { 41 | const subject = new signalR.Subject(); 42 | yield connection.send("UploadStream", subject); 43 | var iteration = 0; 44 | const intervalHandle = setInterval(() => { 45 | iteration++; 46 | subject.next(iteration.toString()); 47 | if (iteration === 10) { 48 | clearInterval(intervalHandle); 49 | subject.complete(); 50 | } 51 | }, 500); 52 | event.preventDefault(); 53 | })); 54 | // We need an async function in order to use await, but we want this code to run immediately, 55 | // so we use an "immediately-executed async function" 56 | (() => __awaiter(this, void 0, void 0, function* () { 57 | try { 58 | yield connection.start(); 59 | } 60 | catch (e) { 61 | console.error(e.toString()); 62 | } 63 | }))(); -------------------------------------------------------------------------------- /RxSignalR/RxSignalrWeb/App_Code/ChatHub.vb: -------------------------------------------------------------------------------- 1 | Option Strict Off 2 | 3 | Imports Microsoft.AspNet.SignalR 4 | Imports Microsoft.VisualBasic 5 | 6 | Public Class Chat 7 | Inherits Hub 8 | 9 | Public Sub Send(message As String) 10 | Clients.All.addMessage(message) 11 | End Sub 12 | End Class 13 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrWeb/App_Code/ObservableSensor.vb: -------------------------------------------------------------------------------- 1 | Option Strict Off 2 | Option Infer On 3 | 4 | Imports System 5 | Imports System.Reactive.Linq 6 | Imports Microsoft.AspNet.SignalR 7 | 8 | Imports Microsoft.VisualBasic 9 | 10 | Public Class ObservableSensor 11 | 12 | Public Sub New() 13 | Dim rand = New Random(Now.Millisecond) 14 | 15 | Dim generator = Observable.Generate(Of Double, SensorData)( 16 | initialState:=0, 17 | condition:=Function(val) True, 18 | iterate:=Function(val) rand.NextDouble, 19 | resultSelector:=Function(val) New SensorData With 20 | { 21 | .Time = Now, 22 | .Value = val * 20, 23 | .Category = (CInt(val * 4)).ToString() 24 | }, 25 | timeSelector:=Function(val) TimeSpan.FromSeconds(val)) 26 | 27 | Dim query = From val In generator 28 | Select val 29 | 30 | query.Subscribe(Sub(value) 31 | GlobalHost.ConnectionManager. 32 | GetHubContext(Of ObservableSensorHub)(). 33 | Clients.All.Broadcast(value) 34 | End Sub) 35 | End Sub 36 | 37 | End Class 38 | 39 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrWeb/App_Code/ObservableSensorHub.vb: -------------------------------------------------------------------------------- 1 | Imports Microsoft.AspNet.SignalR 2 | Imports Microsoft.VisualBasic 3 | 4 | Public Class ObservableSensorHub 5 | Inherits Hub 6 | 7 | End Class 8 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrWeb/App_Code/SensorData.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports Microsoft.VisualBasic 3 | 4 | Public Class SensorData 5 | Public Property Time As DateTime 6 | Public Property Category As String 7 | Public Property Value As Double 8 | End Class 9 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrWeb/App_Code/Startup.vb: -------------------------------------------------------------------------------- 1 | Imports Microsoft.VisualBasic 2 | Imports Owin 3 | 4 | Public Class Startup 5 | Public Sub Configuration(app As IAppBuilder) 6 | app.MapSignalR() 7 | End Sub 8 | 9 | End Class 10 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrWeb/Chat.aspx: -------------------------------------------------------------------------------- 1 | <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Chat.aspx.vb" Inherits="Chat" %> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
        14 |
      15 | 16 | 17 | 18 | 19 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrWeb/Chat.aspx.vb: -------------------------------------------------------------------------------- 1 |  2 | Partial Class Chat 3 | Inherits System.Web.UI.Page 4 | 5 | End Class 6 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrWeb/Default.aspx: -------------------------------------------------------------------------------- 1 | <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 26 | 27 | 28 |
        29 |
      30 | 31 | 32 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrWeb/Default.aspx.vb: -------------------------------------------------------------------------------- 1 |  2 | Partial Class _Default 3 | Inherits System.Web.UI.Page 4 | 5 | End Class 6 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrWeb/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Language="VB" %> 2 | 3 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrWeb/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /RxSignalR/RxSignalrWeb/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /RxSignalR/SharpConsole/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.SignalR.Client; 2 | using System; 3 | using System.Reactive.Linq; 4 | using System.Reactive.Subjects; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpConsole 8 | { 9 | class Program 10 | { 11 | static async Task Main(string[] args) 12 | { 13 | var cn = new HubConnectionBuilder() 14 | .WithUrl("https://localhost:5001/sensorHub") 15 | .WithAutomaticReconnect() 16 | .Build(); 17 | 18 | await cn.StartAsync(); 19 | 20 | var items = 21 | from sensor in cn.AsObservable("Values") 22 | where sensor.SensorValue < 5 23 | select sensor; 24 | 25 | items.Subscribe(sensor => Console.WriteLine(sensor.SensorValue)); 26 | 27 | Console.WriteLine("Receiving Values. Press any key to exit."); 28 | Console.ReadLine(); 29 | } 30 | } 31 | public static class ObservableExtensions 32 | { 33 | public static IObservable AsObservable(this HubConnection connection, string methodName) 34 | { 35 | var subject = new Subject(); 36 | Task.Run(async () => 37 | { 38 | var stream = connection.StreamAsync(methodName); 39 | await foreach (var value in stream) 40 | { 41 | subject.OnNext(value); 42 | } 43 | }); 44 | return subject; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /RxSignalR/SharpConsole/SensorData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpConsole 8 | { 9 | class SensorData 10 | { 11 | public DateTime TimeStamp { get; set; } 12 | public string SensorType { get; set; } 13 | public double SensorValue { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /RxSignalR/SharpConsole/SharpConsole.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RxSignalR/WpfApplication1/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /RxSignalR/WpfApplication1/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RxSignalR/WpfApplication1/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace WpfApplication1 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /RxSignalR/WpfApplication1/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/app/nav-menu/nav-menu.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-nav-menu', 5 | templateUrl: './nav-menu.component.html', 6 | styleUrls: ['./nav-menu.component.css'] 7 | }) 8 | export class NavMenuComponent { 9 | isExpanded = false; 10 | 11 | collapse() { 12 | this.isExpanded = false; 13 | } 14 | 15 | toggle() { 16 | this.isExpanded = !this.isExpanded; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/app/reactive-chat/reactive-chat.component.html: -------------------------------------------------------------------------------- 1 |
      2 | 3 |

      Send some basic messages

      4 | 5 | 6 |
      7 |
      8 |
      9 | 10 | 11 | 12 |
      13 |
      14 |
      15 |
      16 |
      17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
      #Messages
      {{i + 1}}{{message}}
      31 |
      32 |
      33 |
      34 | No messages 35 |
      36 |
      37 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/app/reactive-chat/reactive-chat.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { distinctUntilChanged, debounceTime, filter } from 'rxjs/operators'; 3 | import { HubConnectionBuilder } from '@microsoft/signalr'; 4 | import { FormControl, FormGroup } from '@angular/forms'; 5 | 6 | @Component({ 7 | selector: 'app-reactive-chat', 8 | templateUrl: './reactive-chat.component.html' 9 | }) 10 | export class ReactiveChatComponent implements OnInit { 11 | public hubUrl: string = ""; 12 | _hubConnection: any; 13 | constructor() { 14 | this.hubUrl = 'https://localhost:7183/hubs/chat'; 15 | } 16 | message = new FormControl(); 17 | chatForm = new FormGroup({ 18 | message: this.message, 19 | }); 20 | messages: string[] = []; 21 | 22 | public sendMessage(text: string): void { 23 | const data = 'Sent: ' + text; 24 | this._hubConnection.invoke('Send', text); 25 | this.messages.push(data); 26 | } 27 | public send(): void { 28 | const text: string = (document.getElementById('message')).value; 29 | this.sendMessage(text); 30 | } 31 | ngOnInit() { 32 | this._hubConnection = new HubConnectionBuilder() 33 | .withUrl(this.hubUrl) 34 | .build(); 35 | 36 | this._hubConnection.start() 37 | .then(() => { 38 | console.log('Hub connection started'); 39 | this._hubConnection.on('Send', (data: any) => { 40 | const received = 'Received: ' + data; 41 | this.messages.push(received); 42 | }); 43 | }) 44 | .catch((err: any) => { console.log('Error establishing connection' + err); } 45 | ); 46 | 47 | 48 | 49 | 50 | // RxJS 1 51 | //this.message.valueChanges 52 | // .distinctUntilChanged() 53 | // .debounceTime(500) 54 | // .where(t => t !== 'foo') 55 | // .select(t => t); 56 | 57 | // Rxjs 6 and later 58 | this.message.valueChanges 59 | .pipe( 60 | distinctUntilChanged(), 61 | debounceTime(500), 62 | filter(t => t !== 'foo') 63 | ).subscribe(text => { 64 | this.sendMessage(text); 65 | }); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/app/sensor/sensor.component.html: -------------------------------------------------------------------------------- 1 |
      2 | 3 |

      Sensor Values

      4 |
        5 |
      • Type {{data.sensorType}} - Value {{data.sensorValue}} received on {{data.timeStamp}}
      • 6 |
      7 |
      8 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/app/sensor/sensor.component.ts: -------------------------------------------------------------------------------- 1 | 2 | import { filter } from 'rxjs/operators'; 3 | import { Component, OnInit } from '@angular/core'; 4 | import { Observable, BehaviorSubject } from 'rxjs'; 5 | import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr'; 6 | 7 | @Component({ 8 | selector: 'sensor', 9 | templateUrl: './sensor.component.html' 10 | }) 11 | export class SensorComponent implements OnInit { 12 | private _hubConnection = new HubConnectionBuilder().withUrl('https://localhost:7183/hubs/sensor').build(); 13 | dataStream: ISensorData[] = []; 14 | subject: BehaviorSubject = new BehaviorSubject({ timeStamp: new Date(), sensorType: '', sensorValue: 0 }); 15 | 16 | ngOnInit() { 17 | this.subject.pipe( 18 | filter(val => val.sensorType === '1')) 19 | .subscribe({ 20 | next: (value: ISensorData) => this.dataStream.push(value), 21 | error: function (err) { console.log(err); }, 22 | complete: () => console.log('done') 23 | }); 24 | 25 | this._hubConnection.start() 26 | .then(() => { 27 | this._hubConnection.stream('Values').subscribe({ 28 | next: val => this.subject.next(val), 29 | error: err => this.subject.error(err), 30 | complete: () => this.subject.complete() 31 | }); 32 | }); 33 | } 34 | } 35 | 36 | interface ISensorData { 37 | timeStamp: Date; 38 | sensorType: string; 39 | sensorValue: number; 40 | } 41 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwooley/SignalrRxSamples/276e13d13cea957af268dee5a0acb73de5b6023f/signalrNg/ClientApp/src/assets/.gitkeep -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SignalrNg 6 | 7 | 8 | 9 | 10 | 11 | 12 | Loading... 13 | 14 | 15 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | export function getBaseUrl() { 8 | return document.getElementsByTagName('base')[0].href; 9 | } 10 | 11 | const providers = [ 12 | { provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] } 13 | ]; 14 | 15 | if (environment.production) { 16 | enableProdMode(); 17 | } 18 | 19 | platformBrowserDynamic(providers).bootstrapModule(AppModule) 20 | .catch(err => console.log(err)); 21 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * IE11 requires the following for NgClass support on SVG elements 23 | */ 24 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 25 | 26 | /** 27 | * Web Animations `@angular/platform-browser/animations` 28 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 29 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 30 | */ 31 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 32 | 33 | /** 34 | * By default, zone.js will patch all possible macroTask and DomEvents 35 | * user can disable parts of macroTask/DomEvents patch by setting following flags 36 | * because those flags need to be set before `zone.js` being loaded, and webpack 37 | * will put import in the top of bundle, so user need to create a separate file 38 | * in this directory (for example: zone-flags.ts), and put the following flags 39 | * into that file, and then add the following code before importing zone.js. 40 | * import './zone-flags'; 41 | * 42 | * The flags allowed in zone-flags.ts are listed here. 43 | * 44 | * The following flags will work for all browsers. 45 | * 46 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 47 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 48 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 49 | * 50 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 51 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 52 | * 53 | * (window as any).__Zone_enable_cross_context_check = true; 54 | * 55 | */ 56 | 57 | /*************************************************************************************************** 58 | * Zone JS is required by default for Angular itself. 59 | */ 60 | import 'zone.js'; // Included with Angular CLI. 61 | 62 | 63 | /*************************************************************************************************** 64 | * APPLICATION IMPORTS 65 | */ 66 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | /* Provide sufficient contrast against white background */ 4 | a { 5 | color: #0366d6; 6 | } 7 | 8 | code { 9 | color: #e01a76; 10 | } 11 | 12 | .btn-primary { 13 | color: #fff; 14 | background-color: #1b6ec2; 15 | border-color: #1861ac; 16 | } 17 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context(path: string, deep?: boolean, filter?: RegExp): { 12 | keys(): string[]; 13 | (id: string): T; 14 | }; 15 | }; 16 | 17 | // First, initialize the Angular testing environment. 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting() 21 | ); 22 | // Then we find all the tests. 23 | const context = require.context('./', true, /\.spec\.ts$/); 24 | // And load the modules. 25 | context.keys().map(context); 26 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "noImplicitReturns": true, 9 | "noFallthroughCasesInSwitch": true, 10 | "sourceMap": true, 11 | "declaration": false, 12 | "downlevelIteration": true, 13 | "experimentalDecorators": true, 14 | "moduleResolution": "node", 15 | "importHelpers": true, 16 | "target": "es2017", 17 | "module": "es2020", 18 | "lib": [ 19 | "es2018", 20 | "dom" 21 | ] 22 | }, 23 | "angularCompilerOptions": { 24 | "enableI18nLegacyMessageIdFormat": false, 25 | "strictInjectionParameters": true, 26 | "strictInputAccessModifiers": true, 27 | "strictTemplates": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /signalrNg/ClientApp/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine", 8 | "node" 9 | ] 10 | }, 11 | "files": [ 12 | "src/test.ts", 13 | "src/polyfills.ts" 14 | ], 15 | "include": [ 16 | "src/**/*.spec.ts", 17 | "src/**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /signalrNg/Hubs/ChatHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.SignalR; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace SignalrNg.Hubs 6 | { 7 | public class ChatHub : Hub 8 | { 9 | public Task Send(string data) 10 | { 11 | var currentUser = new List { Context.ConnectionId }; 12 | return Clients.AllExcept(currentUser).SendAsync("Send", data); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /signalrNg/Hubs/DragHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.SignalR; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace SignalrNg.Hubs 8 | { 9 | public class DragHub : Hub 10 | { 11 | public async Task DragDrop(Coords position) 12 | { 13 | await Clients.Others.SendAsync("Dragged", position); 14 | } 15 | } 16 | public class Coords 17 | { 18 | public int X { get; set; } 19 | public int Y { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /signalrNg/Hubs/SensorHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.SignalR; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reactive.Linq; 6 | using System.Threading.Channels; 7 | using System.Threading.Tasks; 8 | 9 | namespace SignalrNg.Hubs 10 | { 11 | public class SensorHub : Hub 12 | { 13 | private static IObservable? _Sensor = null; 14 | 15 | public ChannelReader Values() 16 | { 17 | if (_Sensor == null) 18 | { 19 | var rand = new Random(DateTime.Now.Millisecond); 20 | _Sensor = Observable.Generate( 21 | initialState: 0.0, 22 | condition: x => true, 23 | iterate: inVal => rand.NextDouble(), 24 | resultSelector: val => new SensorData 25 | { 26 | TimeStamp = DateTime.Now, 27 | SensorType = (Math.Floor(val * 4) + 1).ToString(), 28 | SensorValue = val * 20 29 | }, 30 | timeSelector: val => TimeSpan.FromMilliseconds(val * 1000)) 31 | .Publish() 32 | .AutoConnect(1); 33 | } 34 | return _Sensor.AsChannelReader(); 35 | } 36 | } 37 | 38 | public static class ObservableExtensions 39 | { 40 | public static ChannelReader AsChannelReader(this IObservable observable, int? maxBufferSize = null) 41 | { 42 | // This sample shows adapting an observable to a ChannelReader without 43 | // back pressure, if the connection is slower than the producer, memory will 44 | // start to increase. 45 | 46 | // If the channel is bounded, TryWrite will return false and effectively 47 | // drop items. 48 | 49 | // The other alternative is to use a bounded channel, and when the limit is reached 50 | // block on WaitToWriteAsync. This will block a thread pool thread and isn't recommended and isn't shown here. 51 | var channel = maxBufferSize != null ? Channel.CreateBounded(maxBufferSize.Value) : Channel.CreateUnbounded(); 52 | 53 | var disposable = observable.Subscribe( 54 | value => channel.Writer.TryWrite(value), 55 | error => channel.Writer.TryComplete(error), 56 | () => channel.Writer.TryComplete()); 57 | 58 | // Complete the subscription on the reader completing 59 | channel.Reader.Completion.ContinueWith(task => disposable.Dispose()); 60 | 61 | return channel.Reader; 62 | } 63 | } 64 | 65 | public class SensorData 66 | { 67 | public DateTime TimeStamp { get; set; } 68 | public string SensorType { get; set; } = ""; 69 | public double SensorValue { get; set; } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /signalrNg/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

      Error.

      8 |

      An error occurred while processing your request.

      9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

      13 | Request ID: @Model.RequestId 14 |

      15 | } 16 | 17 |

      Development Mode

      18 |

      19 | Swapping to the Development environment displays detailed information about the error that occurred. 20 |

      21 |

      22 | The Development environment shouldn't be enabled for deployed applications. 23 | It can result in displaying sensitive information from exceptions to end users. 24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 25 | and restarting the app. 26 |

      27 | -------------------------------------------------------------------------------- /signalrNg/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Mvc.RazorPages; 4 | 5 | namespace SignalrNg.Pages; 6 | 7 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 8 | public class ErrorModel : PageModel 9 | { 10 | private readonly ILogger _logger; 11 | 12 | public ErrorModel(ILogger logger) 13 | { 14 | _logger = logger; 15 | } 16 | 17 | public string? RequestId { get; set; } 18 | 19 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 20 | 21 | public void OnGet() 22 | { 23 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /signalrNg/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using SignalrNg 2 | @namespace SignalrNg.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /signalrNg/Program.cs: -------------------------------------------------------------------------------- 1 | using SignalrNg.Hubs; 2 | 3 | var builder = WebApplication.CreateBuilder(args); 4 | 5 | builder.Services.AddSignalR(); 6 | builder.Services.AddControllers(); 7 | builder.Services.AddCors(options => 8 | { 9 | options.AddDefaultPolicy( 10 | builder => 11 | { 12 | builder.WithOrigins("https://localhost:44451") 13 | .AllowAnyHeader() 14 | .WithMethods("GET", "POST") 15 | .AllowCredentials(); 16 | }); 17 | }); 18 | 19 | var app = builder.Build(); 20 | 21 | // Configure the HTTP request pipeline. 22 | if (!app.Environment.IsDevelopment()) 23 | { 24 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 25 | app.UseHsts(); 26 | } 27 | 28 | app.UseHttpsRedirection(); 29 | app.UseStaticFiles(); 30 | app.UseRouting(); 31 | 32 | app.UseAuthorization(); 33 | app.UseCors(); 34 | app.MapControllers(); 35 | app.UseEndpoints(config => 36 | { 37 | config.MapHub("/hubs/chat"); 38 | config.MapHub("/hubs/sensor"); 39 | config.MapHub("/hubs/drag"); 40 | }); 41 | 42 | app.Run(); 43 | -------------------------------------------------------------------------------- /signalrNg/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:33076", 7 | "sslPort": 44318 8 | } 9 | }, 10 | "profiles": { 11 | "SignalrNg": { 12 | "commandName": "Project", 13 | "launchBrowser": true, 14 | "applicationUrl": "https://localhost:7183;http://localhost:5207", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development", 17 | "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy" 18 | } 19 | }, 20 | "IIS Express": { 21 | "commandName": "IISExpress", 22 | "launchBrowser": true, 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development", 25 | "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy" 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /signalrNg/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.AspNetCore.SpaProxy": "Information", 7 | "Microsoft.Hosting.Lifetime": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /signalrNg/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /signalrNg/signalrNg.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | false 7 | ClientApp\ 8 | https://localhost:44451 9 | npm start 10 | enable 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | wwwroot\%(RecursiveDir)%(FileName)%(Extension) 57 | PreserveNewest 58 | true 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /signalrNg/signalrNg.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.4.32916.344 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "signalrNg", "signalrNg.csproj", "{0C00066B-694E-4B61-A81A-AA4F130D0B08}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {0C00066B-694E-4B61-A81A-AA4F130D0B08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {0C00066B-694E-4B61-A81A-AA4F130D0B08}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {0C00066B-694E-4B61-A81A-AA4F130D0B08}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {0C00066B-694E-4B61-A81A-AA4F130D0B08}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {2CA7B90E-EC5B-4CC1-8D33-15A6CF3BBD66} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /signalrNg/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwooley/SignalrRxSamples/276e13d13cea957af268dee5a0acb73de5b6023f/signalrNg/wwwroot/favicon.ico --------------------------------------------------------------------------------