├── .gitignore ├── LICENCE.txt ├── README.md ├── appveyor.yml ├── examples └── ReactiveModel │ ├── Esp.Net.Examples.ReactiveModel │ ├── App.config │ ├── App.xaml │ ├── App.xaml.cs │ ├── Bootstrapper.cs │ ├── ClientApp │ │ ├── ClientAppBootstrapper.cs │ │ ├── Model │ │ │ ├── Entities │ │ │ │ ├── Model.cd │ │ │ │ ├── OrderInputs │ │ │ │ │ └── OrderInputs.cs │ │ │ │ ├── OrderScreen.cs │ │ │ │ └── Rfq │ │ │ │ │ └── Rfq.cs │ │ │ ├── Events │ │ │ │ ├── AcceptQuoteEvent.cs │ │ │ │ ├── CurrencyPairChangedEvent.cs │ │ │ │ ├── InitialiseEvent.cs │ │ │ │ ├── NotionalChangedEvent.cs │ │ │ │ ├── OrderResponseReceivedEvent.cs │ │ │ │ ├── ReferenceDataReceivedEvent.cs │ │ │ │ ├── RejectQuoteEvent.cs │ │ │ │ └── RequestQuoteEvent.cs │ │ │ └── Gateways │ │ │ │ ├── IReferenceDataGateway.cs │ │ │ │ ├── IRequestForQuoteGateway.cs │ │ │ │ ├── ReferenceDataGateway.cs │ │ │ │ └── RequestForQuoteGateway.cs │ │ ├── Services │ │ │ ├── Entities │ │ │ │ ├── CurrencyPair.cs │ │ │ │ ├── QuoteStatus.cs │ │ │ │ ├── QuoteStatusExt.cs │ │ │ │ ├── RfqRequest.cs │ │ │ │ └── RfqResponse.cs │ │ │ ├── IReferenceDataServiceClient.cs │ │ │ └── IRfqServiceClient.cs │ │ └── UI │ │ │ ├── RfqScreen │ │ │ ├── ClientRfqScreenView.xaml │ │ │ ├── ClientRfqScreenView.xaml.cs │ │ │ └── ClientRfqScreenViewModel.cs │ │ │ └── Shell │ │ │ ├── ClientAppShellView.xaml │ │ │ ├── ClientAppShellView.xaml.cs │ │ │ └── ClientAppShellViewModel.cs │ ├── Common │ │ ├── FakeMiddleware.cs │ │ ├── Model │ │ │ └── Entities │ │ │ │ └── Fields │ │ │ │ ├── Field.cs │ │ │ │ └── SelectionField.cs │ │ ├── Services │ │ │ └── SchedulerService.cs │ │ └── UI │ │ │ ├── DelegateCommand .cs │ │ │ ├── EntryMonitor.cs │ │ │ ├── Fields │ │ │ ├── FieldViewModel.cs │ │ │ └── SelectionFieldViewModel.cs │ │ │ ├── InverseBooleanToVisibilityConverter .cs │ │ │ ├── NotifyingBase .cs │ │ │ └── ObservableExtensions.cs │ ├── Esp.Net.Examples.ReactiveModel.csproj │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ ├── Themes │ │ └── MetroDark │ │ │ ├── HowToApplyTheme.txt │ │ │ ├── MetroDark.MSControls.Core.Implicit.xaml │ │ │ ├── MetroDark.MSControls.Toolkit.Implicit.xaml │ │ │ ├── Styles.Shared.xaml │ │ │ ├── Styles.WPF.xaml │ │ │ └── Theme.Colors.xaml │ ├── TraderApp │ │ ├── Model │ │ │ ├── Entities │ │ │ │ ├── Model.cd │ │ │ │ ├── RfqDetails.cs │ │ │ │ └── RfqScreen.cs │ │ │ ├── Events │ │ │ │ ├── ClientAcceptedQuoteEvent.cs │ │ │ │ ├── ClientClosedQuoteEvent.cs │ │ │ │ ├── ClientRejectQuoteEvent.cs │ │ │ │ ├── InitialiseEvent.cs │ │ │ │ ├── RfqRateChangedEvent.cs │ │ │ │ ├── RfqReceivedEvent.cs │ │ │ │ ├── TraderRejectQuoteEvent.cs │ │ │ │ └── TraderSendQuoteEvent.cs │ │ │ └── Gateways │ │ │ │ ├── IRfqServiceGateway.cs │ │ │ │ └── RfqServiceGateway.cs │ │ ├── Services │ │ │ ├── Entities │ │ │ │ ├── CurrencyPair.cs │ │ │ │ ├── QuoteStatus.cs │ │ │ │ ├── RfqRequest.cs │ │ │ │ └── RfqResponse.cs │ │ │ └── IRfqService.cs │ │ ├── TraderAppBootstrapper.cs │ │ └── UI │ │ │ ├── RfqScreen │ │ │ ├── RfqDetailsView.xaml │ │ │ ├── RfqDetailsView.xaml.cs │ │ │ ├── RfqDetailsViewModel.cs │ │ │ ├── TraderRfqScreenView.xaml │ │ │ ├── TraderRfqScreenView.xaml.cs │ │ │ └── TraderRfqScreenViewModel.cs │ │ │ └── Shell │ │ │ ├── TraderAppShellView.xaml │ │ │ ├── TraderAppShellView.xaml.cs │ │ │ └── TraderAppShellViewModel.cs │ └── packages.config │ ├── ReactiveModel.sln │ ├── ReactiveModel.sln.DotSettings │ ├── ReadMe.md │ └── doco │ ├── client.png │ └── trader.png └── src ├── AssemblyInfo.Common.cs ├── Esp.Net.Dispatchers ├── Esp.Net.Dispatchers.SourcePackage.nuspec ├── Esp.Net.Dispatchers.csproj ├── Esp.Net.Dispatchers.nuspec ├── NewThreadRouterDispatcher.cs └── Properties │ └── AssemblyInfo.cs ├── Esp.Net.Rx ├── Esp.Net.Rx.SourcePackage.nuspec ├── Esp.Net.Rx.csproj ├── Esp.Net.Rx.nuspec ├── Properties │ └── AssemblyInfo.cs ├── RouterScheduler.cs └── packages.config ├── Esp.Net.Tests ├── Dispatchers │ └── NewThreadRouterDispatcherTests.cs ├── Disposables │ └── EspDisposableTests.cs ├── Esp.Net.Tests.csproj ├── HeldEvents │ └── HeldEventTests.cs ├── Properties │ └── AssemblyInfo.cs ├── Reactive │ ├── ReactiveTests.cs │ ├── StubEventObservable.cs │ └── StubEventObservationRegistrar.cs ├── RouterTests.Ctor.cs ├── RouterTests.ErrorFlow.Halting.cs ├── RouterTests.ErrorFlows.cs ├── RouterTests.EventObservationDisposal.cs ├── RouterTests.EventWorkflow.cs ├── RouterTests.ModelObservation.cs ├── RouterTests.ModelRouter.cs ├── RouterTests.ModelSubRouter.cs ├── RouterTests.ObserveEventsOn.cs ├── RouterTests.RegisterModel.cs ├── RouterTests.RemoveModel.cs ├── RouterTests.RouterDispatcher.cs ├── RouterTests.RunAction.cs ├── RouterTests.cs ├── Rx │ └── RxSubscribeTests.cs ├── StubRouterDispatcher.cs ├── Utils │ └── ReflectionHelperTests.cs └── packages.config ├── Esp.Net.sln └── Esp.Net ├── CurrentThreadDispatcher.cs ├── Disposables ├── CollectionDisposable.cs ├── DictionaryDisposable.cs ├── DisposableBase.cs ├── EspDisposable.cs └── EspSerialDisposable.cs ├── Esp.Net.SourcePackage.nuspec ├── Esp.Net.csproj ├── Esp.Net.nuspec ├── EventContext.cs ├── HeldEvents ├── HeldEventAction.cs ├── HeldEventActionEvent.cs ├── IEventDescription.cs ├── IEventHoldingStrategy.cs └── IHeldEventStore.cs ├── IClonable.cs ├── IEventContext.cs ├── IIdentifiableEvent.cs ├── IPostEventProcessor.cs ├── IPreEventProcessor.cs ├── IRouter.`1.cs ├── IRouter.cs ├── IRouterDispatcher.cs ├── ITerminalErrorHandler.cs ├── Meta ├── EventObservations.cs ├── IEventObservationRegistrar.cs ├── IEventsObservationRegistrar.cs ├── ModelEventObservations.cs └── ModelsEventsObservations.cs ├── ModelChangedEvent.cs ├── ObservationStage.cs ├── ObserveEventAttribute.cs ├── Properties └── AssemblyInfo.cs ├── Reactive ├── EventObservable.cs ├── EventObserver.cs ├── EventSubject.cs ├── ModelObservable.cs ├── ModelObserver.cs └── ModelSubject.cs ├── Router.ModelRouter.cs ├── Router.State.cs ├── Router.Status.cs ├── Router.`1.cs ├── Router.`2.cs ├── Router.cs ├── RouterExt.HeldEvents.cs ├── RouterExt.ObserveEventsOn.cs └── Utils ├── Guard.cs ├── ReflectionExt.cs └── ReflectionHelper.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # MSTest test Results 20 | [Tt]est[Rr]esult*/ 21 | [Bb]uild[Ll]og.* 22 | 23 | #NUNIT 24 | *.VisualState.xml 25 | TestResult.xml 26 | 27 | # Build Results of an ATL Project 28 | [Dd]ebugPS/ 29 | [Rr]eleasePS/ 30 | dlldata.c 31 | 32 | *_i.c 33 | *_p.c 34 | *_i.h 35 | *.ilk 36 | *.meta 37 | *.obj 38 | *.pch 39 | *.pdb 40 | *.pgc 41 | *.pgd 42 | *.rsp 43 | *.sbr 44 | *.tlb 45 | *.tli 46 | *.tlh 47 | *.tmp 48 | *.tmp_proj 49 | *.log 50 | *.vspscc 51 | *.vssscc 52 | .builds 53 | *.pidb 54 | *.svclog 55 | *.scc 56 | 57 | # Chutzpah Test files 58 | _Chutzpah* 59 | 60 | # Visual C++ cache files 61 | ipch/ 62 | *.aps 63 | *.ncb 64 | *.opensdf 65 | *.sdf 66 | *.cachefile 67 | 68 | # Visual Studio profiler 69 | *.psess 70 | *.vsp 71 | *.vspx 72 | 73 | # TFS 2012 Local Workspace 74 | $tf/ 75 | 76 | # Guidance Automation Toolkit 77 | *.gpState 78 | 79 | # ReSharper is a .NET coding add-in 80 | _ReSharper*/ 81 | *.[Rr]e[Ss]harper 82 | *.DotSettings.user 83 | 84 | # JustCode is a .NET coding addin-in 85 | .JustCode 86 | 87 | # TeamCity is a build add-in 88 | _TeamCity* 89 | 90 | # DotCover is a Code Coverage Tool 91 | *.dotCover 92 | 93 | # NCrunch 94 | *.ncrunch* 95 | _NCrunch_* 96 | .*crunch*.local.xml 97 | 98 | # MightyMoose 99 | *.mm.* 100 | AutoTest.Net/ 101 | 102 | # Web workbench (sass) 103 | .sass-cache/ 104 | 105 | # Installshield output folder 106 | [Ee]xpress/ 107 | 108 | # DocProject is a documentation generator add-in 109 | DocProject/buildhelp/ 110 | DocProject/Help/*.HxT 111 | DocProject/Help/*.HxC 112 | DocProject/Help/*.hhc 113 | DocProject/Help/*.hhk 114 | DocProject/Help/*.hhp 115 | DocProject/Help/Html2 116 | DocProject/Help/html 117 | 118 | # Click-Once directory 119 | publish/ 120 | 121 | # Publish Web Output 122 | *.[Pp]ublish.xml 123 | *.azurePubxml 124 | 125 | # NuGet Packages Directory 126 | packages/ 127 | ## TODO: If the tool you use requires repositories.config uncomment the next line 128 | #!packages/repositories.config 129 | 130 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 131 | # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) 132 | !packages/build/ 133 | 134 | # nuget exe 135 | src/.nuget 136 | 137 | # Windows Azure Build Output 138 | csx/ 139 | *.build.csdef 140 | 141 | # Windows Store app package directory 142 | AppPackages/ 143 | 144 | # Others 145 | sql/ 146 | *.Cache 147 | ClientBin/ 148 | [Ss]tyle[Cc]op.* 149 | ~$* 150 | *~ 151 | *.dbmdl 152 | *.dbproj.schemaview 153 | *.pfx 154 | *.publishsettings 155 | node_modules/ 156 | 157 | # RIA/Silverlight projects 158 | Generated_Code/ 159 | 160 | # Backup & report files from converting an old project file to a newer 161 | # Visual Studio version. Backup files are not needed, because we have git ;-) 162 | _UpgradeReport_Files/ 163 | Backup*/ 164 | UpgradeLog*.XML 165 | UpgradeLog*.htm 166 | 167 | # SQL Server files 168 | *.mdf 169 | *.ldf 170 | 171 | # Business Intelligence projects 172 | *.rdl.data 173 | *.bim.layout 174 | *.bim_*.settings 175 | 176 | # Microsoft Fakes 177 | FakesAssemblies/ 178 | *.orig 179 | 180 | *.nupkg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp-net 2 | Please refer to the JS [documentation](https://keithwoods.gitbooks.io/esp-js/content/) 3 | 4 | [![NuGet version](https://img.shields.io/nuget/v/esp-net.svg)](http://nuget.org/List/Packages/esp-net) [![NuGet downloads](https://img.shields.io/nuget/dt/esp-net.svg)](http://nuget.org/List/Packages/esp-net) [![Build status](https://ci.appveyor.com/api/projects/status/2pthpwm3hb36i605/branch/master?svg=true)](https://ci.appveyor.com/project/esp/esp-net/branch/master) 5 | [![Join the chat at https://gitter.im/esp/chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/esp/chat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | nuget_version: 0.6.4 3 | 4 | platform: Any CPU 5 | configuration: Release 6 | 7 | nuget: 8 | account_feed: false 9 | project_feed: true 10 | disable_publish_on_pr: false 11 | 12 | assembly_info: 13 | patch: true 14 | file: src\AssemblyInfo.Common.cs 15 | assembly_version: '$(nuget_version)' 16 | assembly_file_version: '$(nuget_version)' 17 | assembly_informational_version: '$(APPVEYOR_REPO_COMMIT)' 18 | 19 | before_build: 20 | - nuget restore .\src\Esp.Net.sln 21 | 22 | build: 23 | project: src\Esp.Net.sln 24 | verbosity: detailed 25 | 26 | after_build: 27 | - cmd: nuget pack .\src\Esp.Net\Esp.Net.nuspec -version "%nuget_version%" -prop "target=%CONFIGURATION%" 28 | - cmd: nuget pack .\src\Esp.Net\Esp.Net.nuspec -version "%nuget_version%" -prop "target=%CONFIGURATION%" -Symbols 29 | - cmd: nuget pack .\src\Esp.Net\Esp.Net.SourcePackage.nuspec -version "%nuget_version%" 30 | - cmd: nuget pack .\src\Esp.Net.Dispatchers\Esp.Net.Dispatchers.nuspec -version "%nuget_version%" -prop "target=%CONFIGURATION%" 31 | - cmd: nuget pack .\src\Esp.Net.Dispatchers\Esp.Net.Dispatchers.nuspec -version "%nuget_version%" -prop "target=%CONFIGURATION%" -Symbols 32 | - cmd: nuget pack .\src\Esp.Net.Dispatchers\Esp.Net.Dispatchers.SourcePackage.nuspec -version "%nuget_version%" 33 | 34 | artifacts: 35 | - path: '*.nupkg' 36 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/App.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel 4 | { 5 | public partial class App 6 | { 7 | private readonly Bootstrapper _bootstrapper = new Bootstrapper(); 8 | 9 | protected override void OnStartup(StartupEventArgs e) 10 | { 11 | base.OnStartup(e); 12 | _bootstrapper.Run(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Bootstrapper.cs: -------------------------------------------------------------------------------- 1 | using Esp.Net.Examples.ReactiveModel.ClientApp; 2 | using Esp.Net.Examples.ReactiveModel.TraderApp; 3 | using log4net; 4 | using log4net.Appender; 5 | using log4net.Config; 6 | using log4net.Core; 7 | using log4net.Layout; 8 | 9 | namespace Esp.Net.Examples.ReactiveModel 10 | { 11 | public class Bootstrapper 12 | { 13 | private static readonly ILog Log = LogManager.GetLogger(typeof(Bootstrapper)); 14 | 15 | private ClientAppBootstrapper _clientAppBootstrapper; 16 | private TraderAppBootstrapper _traderAppBootstrapper; 17 | 18 | public void Run() 19 | { 20 | ConfigureLogging(); 21 | Log.Debug("Running"); 22 | 23 | _clientAppBootstrapper = new ClientAppBootstrapper(); 24 | _clientAppBootstrapper.Run(); 25 | _traderAppBootstrapper = new TraderAppBootstrapper(); 26 | _traderAppBootstrapper.Run(); 27 | } 28 | 29 | private void ConfigureLogging() 30 | { 31 | // Fun tip: if you change the startup type of a WPF project to console you'll see 32 | // both the app windows and a console displaying the log output. 33 | 34 | var appender = new ColoredConsoleAppender 35 | { 36 | Threshold = Level.All, 37 | Layout = new PatternLayout( 38 | "[%logger{1}] - %message%newline" 39 | ), 40 | }; 41 | appender.AddMapping(new ColoredConsoleAppender.LevelColors 42 | { 43 | Level = Level.Debug, 44 | ForeColor = ColoredConsoleAppender.Colors.White 45 | }); 46 | appender.AddMapping(new ColoredConsoleAppender.LevelColors 47 | { 48 | Level = Level.Info, 49 | ForeColor = ColoredConsoleAppender.Colors.Green 50 | }); 51 | appender.AddMapping(new ColoredConsoleAppender.LevelColors 52 | { 53 | Level = Level.Warn, 54 | ForeColor = ColoredConsoleAppender.Colors.Yellow 55 | }); 56 | appender.AddMapping(new ColoredConsoleAppender.LevelColors 57 | { 58 | Level = Level.Error, 59 | ForeColor = ColoredConsoleAppender.Colors.Red 60 | }); 61 | appender.AddMapping(new ColoredConsoleAppender.LevelColors 62 | { 63 | Level = Level.Fatal, 64 | ForeColor = ColoredConsoleAppender.Colors.Red | ColoredConsoleAppender.Colors.HighIntensity, 65 | BackColor = ColoredConsoleAppender.Colors.Red 66 | }); 67 | appender.ActivateOptions(); 68 | BasicConfigurator.Configure(appender); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Entities/Model.cd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 15 | 16 | JAgAVABAAAQAMAAAAAAAQAAAlAgAAAAAAAAgAAAgAAA= 17 | ClientApp\Model\Entities\OrderScreen.cs 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 39 | 40 | 41 | 42 | ClientApp\Model\Entities\Rfq\Rfq.cs 43 | 44 | 45 | 46 | 47 | QEAACEAECABAQAAAFABBABAAAAgAAAAAAAAAAQkAAAQ= 48 | ClientApp\Model\Entities\Rfq\Rfq.cs 49 | 50 | 51 | 52 | 53 | 54 | 59 | 60 | AAACIACABAAAAAAAAQABAAAAAAgAIACAAAAAAAEgAAA= 61 | ClientApp\Model\Entities\OrderInputs\OrderInputs.cs 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Entities/OrderInputs/OrderInputs.cs: -------------------------------------------------------------------------------- 1 | using Esp.Net.Examples.ReactiveModel.ClientApp.Model.Events; 2 | using Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities; 3 | using Esp.Net.Examples.ReactiveModel.Common.Model.Entities.Fields; 4 | 5 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Entities.OrderInputs 6 | { 7 | public class OrderInputs : DisposableBase 8 | { 9 | private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(OrderInputs)); 10 | 11 | private readonly SelectionField _currencyPair; 12 | private readonly Field _notional; 13 | private string _orderSummary; 14 | 15 | public OrderInputs() 16 | { 17 | _currencyPair = new SelectionField(); 18 | _notional = new Field(); 19 | } 20 | 21 | public ISelectionField CurrencyPair 22 | { 23 | get { return _currencyPair; } 24 | } 25 | 26 | public IField Notional 27 | { 28 | get { return _notional; } 29 | } 30 | 31 | public string OrderSummary 32 | { 33 | get { return _orderSummary; } 34 | } 35 | 36 | [ObserveEvent(typeof(NotionalChangedEvent))] 37 | private void OnNotionalChangedEvent(NotionalChangedEvent e) 38 | { 39 | Log.DebugFormat("Setting notional to {0}", e.Notional); 40 | _notional.Value = e.Notional; 41 | } 42 | 43 | [ObserveEvent(typeof(CurrencyPairChangedEvent))] 44 | private void OnCurrencyPairChangedEvent(CurrencyPairChangedEvent e) 45 | { 46 | Log.DebugFormat("Setting selected currency pair to {0}", e.CurrencyPair.IsoCode); 47 | _currencyPair.Value = e.CurrencyPair; 48 | } 49 | 50 | [ObserveEvent(typeof(ReferenceDataReceivedEvent), ObservationStage.Committed)] 51 | private void OnReferenceDataReceivedEvent(OrderScreen model) 52 | { 53 | Log.DebugFormat("Applying reference data symbols"); 54 | _currencyPair.Items.AddRange(model.CurrencyPairs); 55 | } 56 | 57 | public void OnPostProcessing(OrderScreen orderScreen) 58 | { 59 | var isEnabled = orderScreen.CurrencyPairs != null && orderScreen.CurrencyPairs.Count > 0 && !orderScreen.Rfq.Status.RfqInFlight(); 60 | _currencyPair.IsEnabled = isEnabled; 61 | _notional.IsEnabled = isEnabled; 62 | if (_notional.HasValue && _currencyPair.HasValue) 63 | { 64 | _orderSummary = string.Format( 65 | "You BUY {0} {1} against {2}", 66 | _notional.Value, 67 | _currencyPair.Value.Base, 68 | _currencyPair.Value.Counter 69 | ); 70 | } 71 | else 72 | { 73 | _orderSummary = "Please select both notional and currency pair above"; 74 | } 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Entities/OrderScreen.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | using System.Reactive.Disposables; 4 | using Esp.Net.Examples.ReactiveModel.ClientApp.Model.Events; 5 | using Esp.Net.Examples.ReactiveModel.ClientApp.Model.Gateways; 6 | using Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities; 7 | using log4net; 8 | 9 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Entities 10 | { 11 | public class OrderScreen 12 | : DisposableBase 13 | , IPreEventProcessor 14 | , IPostEventProcessor 15 | { 16 | private static readonly ILog Log = LogManager.GetLogger(typeof(OrderScreen)); 17 | private readonly IRouter _router; 18 | private readonly IReferenceDataGateway _referenceDataGateway; 19 | private readonly OrderInputs.OrderInputs _orderInputs; 20 | private readonly Rfq.Rfq _rfq; 21 | private readonly SerialDisposable _referenceDataDisposable = new SerialDisposable(); 22 | private IReadOnlyCollection _currentyPairs; 23 | private int _version; 24 | 25 | public OrderScreen( 26 | IRouter router, 27 | IReferenceDataGateway referenceDataGateway, 28 | OrderInputs.OrderInputs orderInputs, 29 | Rfq.Rfq rfq) 30 | { 31 | _router = router; 32 | _referenceDataGateway = referenceDataGateway; 33 | _orderInputs = orderInputs; 34 | _rfq = rfq; 35 | AddDisposable(_referenceDataDisposable); 36 | } 37 | 38 | public int Version 39 | { 40 | get { return _version; } 41 | } 42 | 43 | public IReadOnlyCollection CurrencyPairs 44 | { 45 | get { return _currentyPairs; } 46 | } 47 | 48 | public OrderInputs.OrderInputs Inputs 49 | { 50 | get { return _orderInputs; } 51 | } 52 | 53 | public Rfq.Rfq Rfq 54 | { 55 | get { return _rfq; } 56 | } 57 | 58 | public void ObserveEvents() 59 | { 60 | _router.ObserveEventsOn(this); 61 | _router.ObserveEventsOn(_orderInputs); 62 | _router.ObserveEventsOn(_rfq); 63 | } 64 | 65 | void IPreEventProcessor.Process() 66 | { 67 | _version++; 68 | Log.DebugFormat("Model version is at {0}", _version); 69 | } 70 | 71 | void IPostEventProcessor.Process() 72 | { 73 | _rfq.OnPostProcessing(); 74 | _orderInputs.OnPostProcessing(this); 75 | } 76 | 77 | [ObserveEvent(typeof(ReferenceDataReceivedEvent))] 78 | private void OnReferenceDataReceivedEvent(ReferenceDataReceivedEvent e, IEventContext context) 79 | { 80 | Log.DebugFormat("Applying reference data"); 81 | _currentyPairs = new ReadOnlyCollection(e.CurrencyPairs); 82 | context.Commit(); 83 | } 84 | 85 | [ObserveEvent(typeof(InitialiseEvent))] 86 | private void OnInitialiseEvent() 87 | { 88 | _referenceDataDisposable.Disposable = _referenceDataGateway.BeginGetReferenceData(); 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Events/AcceptQuoteEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Events 4 | { 5 | public class AcceptQuoteEvent 6 | { 7 | public AcceptQuoteEvent(Guid quoteId) 8 | { 9 | QuoteId = quoteId; 10 | } 11 | 12 | public Guid QuoteId { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Events/CurrencyPairChangedEvent.cs: -------------------------------------------------------------------------------- 1 | using Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Events 4 | { 5 | public class CurrencyPairChangedEvent 6 | { 7 | public CurrencyPairChangedEvent(CurrencyPair currencyPair) 8 | { 9 | CurrencyPair = currencyPair; 10 | } 11 | 12 | public CurrencyPair CurrencyPair { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Events/InitialiseEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Events 2 | { 3 | public class InitialiseEvent 4 | { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Events/NotionalChangedEvent.cs: -------------------------------------------------------------------------------- 1 |  namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Events 2 | { 3 | public class NotionalChangedEvent 4 | { 5 | public NotionalChangedEvent(decimal? notional) 6 | { 7 | Notional = notional; 8 | } 9 | 10 | public decimal? Notional { get; private set; } 11 | } 12 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Events/OrderResponseReceivedEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities; 3 | 4 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Events 5 | { 6 | public class OrderResponseReceivedEvent 7 | { 8 | public OrderResponseReceivedEvent(Guid quoteId, CurrencyPair currencyPair, decimal notional, decimal? rate, bool isLastMessage, QuoteStatus status) 9 | { 10 | QuoteId = quoteId; 11 | CurrencyPair = currencyPair; 12 | Notional = notional; 13 | Rate = rate; 14 | IsLastMessage = isLastMessage; 15 | Status = status; 16 | } 17 | 18 | public OrderResponseReceivedEvent(Exception exception) 19 | { 20 | Exception = exception; 21 | HasException = true; 22 | } 23 | 24 | public bool HasException { get; private set; } 25 | public Exception Exception { get; private set; } 26 | public Guid QuoteId { get; private set; } 27 | public CurrencyPair CurrencyPair { get; private set; } 28 | public decimal Notional { get; private set; } 29 | public decimal? Rate { get; private set; } 30 | public QuoteStatus Status { get; private set; } 31 | public bool IsLastMessage { get; private set; } 32 | } 33 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Events/ReferenceDataReceivedEvent.cs: -------------------------------------------------------------------------------- 1 | using Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Events 4 | { 5 | public class ReferenceDataReceivedEvent 6 | { 7 | public ReferenceDataReceivedEvent(CurrencyPair[] currencyPairs) 8 | { 9 | CurrencyPairs = currencyPairs; 10 | } 11 | 12 | public CurrencyPair[] CurrencyPairs { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Events/RejectQuoteEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Events 4 | { 5 | public class RejectQuoteEvent 6 | { 7 | public RejectQuoteEvent(Guid quoteId) 8 | { 9 | QuoteId = quoteId; 10 | } 11 | 12 | public Guid QuoteId { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Events/RequestQuoteEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Events 2 | { 3 | public class RequestQuoteEvent 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Gateways/IReferenceDataGateway.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Gateways 4 | { 5 | public interface IReferenceDataGateway 6 | { 7 | IDisposable BeginGetReferenceData(); 8 | } 9 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Gateways/IRequestForQuoteGateway.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities; 3 | 4 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Gateways 5 | { 6 | public interface IRequestForQuoteGateway 7 | { 8 | IDisposable BegingGetQuote(Guid quoteId, CurrencyPair currencyPair, decimal notional); 9 | IDisposable BegingAcceptQuote(Guid quoteId); 10 | IDisposable BegingRejectQuote(Guid quoteId); 11 | } 12 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Gateways/ReferenceDataGateway.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Esp.Net.Examples.ReactiveModel.ClientApp.Model.Entities; 4 | using Esp.Net.Examples.ReactiveModel.ClientApp.Model.Events; 5 | using Esp.Net.Examples.ReactiveModel.ClientApp.Services; 6 | using Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities; 7 | 8 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Gateways 9 | { 10 | public class ReferenceDataGateway : IReferenceDataGateway 11 | { 12 | private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ReferenceDataGateway)); 13 | 14 | private readonly IRouter _router; 15 | private readonly IReferenceDataServiceClient _referenceDataServiceClient; 16 | 17 | public ReferenceDataGateway(IRouter router, IReferenceDataServiceClient referenceDataServiceClient) 18 | { 19 | _router = router; 20 | _referenceDataServiceClient = referenceDataServiceClient; 21 | } 22 | 23 | public IDisposable BeginGetReferenceData() 24 | { 25 | Log.Debug("Getting reference Data"); 26 | return _referenceDataServiceClient.GetCurrencyPairs().Subscribe(currencyPairsDtos => 27 | { 28 | Log.Debug("Reference Data received"); 29 | CurrencyPair[] currencyPairs = currencyPairsDtos.Select(p => new CurrencyPair(p.IsoCode, p.Precision)).ToArray(); 30 | _router.PublishEvent(new ReferenceDataReceivedEvent(currencyPairs)); 31 | }, 32 | ex => 33 | { 34 | // TODO 35 | }); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Model/Gateways/RequestForQuoteGateway.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Esp.Net.Examples.ReactiveModel.ClientApp.Model.Entities; 3 | using Esp.Net.Examples.ReactiveModel.ClientApp.Model.Events; 4 | using Esp.Net.Examples.ReactiveModel.ClientApp.Services; 5 | using Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities; 6 | 7 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Model.Gateways 8 | { 9 | public class RequestForQuoteGateway : IRequestForQuoteGateway 10 | { 11 | private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(RequestForQuoteGateway)); 12 | 13 | private readonly IRfqServiceClient _rfqServiceClient; 14 | private readonly IRouter _router; 15 | 16 | public RequestForQuoteGateway(IRfqServiceClient rfqServiceClient, IRouter router) 17 | { 18 | _rfqServiceClient = rfqServiceClient; 19 | _router = router; 20 | } 21 | 22 | public IDisposable BegingGetQuote(Guid quoteId, CurrencyPair currencyPair, decimal notional) 23 | { 24 | Log.DebugFormat("Getting quote. Id {0}, {1}, {2}", quoteId, currencyPair.IsoCode, notional); 25 | return _rfqServiceClient.RequestQuote(new RfqRequest(quoteId, currencyPair, notional)).Subscribe( 26 | response => 27 | { 28 | Log.DebugFormat("Quote response received. Id {0}, {1}, {2}", quoteId, currencyPair.IsoCode, notional); 29 | _router.PublishEvent( 30 | new OrderResponseReceivedEvent( 31 | response.QuoteId, 32 | new CurrencyPair(response.CurrencyPair.IsoCode, response.CurrencyPair.Precision), 33 | response.Notional, 34 | response.Rate, 35 | response.IsLastMessage, 36 | response.QuoteStatus 37 | ) 38 | ); 39 | }, 40 | ex => 41 | { 42 | Log.ErrorFormat("Quote error. Id {0}, {1}, {2}", quoteId, currencyPair.IsoCode, notional); 43 | _router.PublishEvent(new OrderResponseReceivedEvent(ex)); 44 | }, 45 | () => 46 | { 47 | // publish other event for unexpected completed cases if required 48 | } 49 | ); 50 | } 51 | 52 | public IDisposable BegingAcceptQuote(Guid quoteId) 53 | { 54 | Log.DebugFormat("Accepting quote with id {0}", quoteId); 55 | return _rfqServiceClient.AcceptQuote(quoteId).Subscribe( 56 | ack => 57 | { 58 | Log.DebugFormat("Quote {0} accept ack received", quoteId); 59 | }, 60 | ex => 61 | { 62 | // TODO 63 | } 64 | ); 65 | } 66 | 67 | public IDisposable BegingRejectQuote(Guid quoteId) 68 | { 69 | Log.DebugFormat("Rejecting quote with id {0}", quoteId); 70 | return _rfqServiceClient.RejectQuote(quoteId).Subscribe( 71 | ack => 72 | { 73 | Log.DebugFormat("Quote {0} reject ack received", quoteId); 74 | }, 75 | ex => 76 | { 77 | // TODO 78 | } 79 | ); 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Services/Entities/CurrencyPair.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities 2 | { 3 | public class CurrencyPair 4 | { 5 | public CurrencyPair(string isoCode, int precision) 6 | { 7 | IsoCode = isoCode; 8 | Precision = precision; 9 | 10 | Base = IsoCode.Substring(0, 3); 11 | Counter = IsoCode.Substring(3, 3); 12 | } 13 | 14 | public string IsoCode { get; private set; } 15 | 16 | public int Precision { get; private set; } 17 | 18 | public string Base { get; private set; } 19 | 20 | public string Counter { get; private set; } 21 | } 22 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Services/Entities/QuoteStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities 2 | { 3 | public enum QuoteStatus 4 | { 5 | New, 6 | Requesting, 7 | Quoting, 8 | Booking, 9 | Rejecting, 10 | ClientRejected, 11 | TraderRejected, 12 | Booked 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Services/Entities/QuoteStatusExt.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities 2 | { 3 | public static class QuoteStatusExt 4 | { 5 | public static bool IsEndState(this QuoteStatus status) 6 | { 7 | var isEndState = status == QuoteStatus.ClientRejected || status == QuoteStatus.TraderRejected || status == QuoteStatus.Booked; 8 | return isEndState; 9 | } 10 | 11 | public static bool RfqInFlight(this QuoteStatus status) 12 | { 13 | var rfqInFlight = status == QuoteStatus.Quoting || status == QuoteStatus.Requesting || status == QuoteStatus.Booking || status == QuoteStatus.Rejecting; 14 | return rfqInFlight; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Services/Entities/RfqRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities 4 | { 5 | public class RfqRequest 6 | { 7 | public RfqRequest(Guid quoteId, CurrencyPair currencyPair, decimal notional) 8 | { 9 | QuoteId = quoteId; 10 | CurrencyPair = currencyPair; 11 | Notional = notional; 12 | } 13 | 14 | public Guid QuoteId { get; private set; } 15 | public CurrencyPair CurrencyPair { get; private set; } 16 | public decimal Notional { get; private set; } 17 | } 18 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Services/Entities/RfqResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities 4 | { 5 | public class RfqResponse 6 | { 7 | public RfqResponse(Guid quoteId, CurrencyPair currencyPair, decimal notional, decimal? rate, QuoteStatus quoteStatus, bool isLastMessage = false) 8 | { 9 | QuoteId = quoteId; 10 | CurrencyPair = currencyPair; 11 | Notional = notional; 12 | Rate = rate; 13 | QuoteStatus = quoteStatus; 14 | IsLastMessage = isLastMessage; 15 | } 16 | 17 | public Guid QuoteId { get; private set; } 18 | public CurrencyPair CurrencyPair { get; private set; } 19 | public decimal Notional { get; private set; } 20 | public decimal? Rate { get; private set; } 21 | public QuoteStatus QuoteStatus { get; private set; } 22 | public bool IsLastMessage { get; private set; } 23 | } 24 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Services/IReferenceDataServiceClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities; 3 | 4 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Services 5 | { 6 | public interface IReferenceDataServiceClient 7 | { 8 | IObservable GetCurrencyPairs(); 9 | } 10 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/Services/IRfqServiceClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive; 3 | using Esp.Net.Examples.ReactiveModel.ClientApp.Services.Entities; 4 | 5 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.Services 6 | { 7 | public interface IRfqServiceClient 8 | { 9 | IObservable RequestQuote(RfqRequest request); 10 | 11 | IObservable AcceptQuote(Guid quoteId); 12 | 13 | IObservable RejectQuote(Guid quoteId); 14 | } 15 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/UI/RfqScreen/ClientRfqScreenView.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 48 | 49 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/UI/RfqScreen/ClientRfqScreenView.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.UI.RfqScreen 2 | { 3 | public partial class ClientRfqScreenView 4 | { 5 | public ClientRfqScreenView() 6 | { 7 | InitializeComponent(); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/UI/Shell/ClientAppShellView.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/UI/Shell/ClientAppShellView.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.UI.Shell 2 | { 3 | public partial class ClientAppShellView 4 | { 5 | public ClientAppShellView(ClientAppShellViewModel viewModel) 6 | { 7 | InitializeComponent(); 8 | DataContext= viewModel; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/ClientApp/UI/Shell/ClientAppShellViewModel.cs: -------------------------------------------------------------------------------- 1 | using Esp.Net.Examples.ReactiveModel.ClientApp.UI.RfqScreen; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.ClientApp.UI.Shell 4 | { 5 | public class ClientAppShellViewModel 6 | { 7 | public ClientAppShellViewModel(ClientRfqScreenViewModel clientRfqScreenViewModel) 8 | { 9 | ClientRfqScreenViewModel = clientRfqScreenViewModel; 10 | } 11 | 12 | public ClientRfqScreenViewModel ClientRfqScreenViewModel { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Common/Model/Entities/Fields/Field.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.Common.Model.Entities.Fields 4 | { 5 | public interface IField : IField 6 | { 7 | T Value { get; } 8 | } 9 | 10 | public interface IField 11 | { 12 | bool IsEnabled { get; } 13 | bool IsValid { get; } 14 | bool HasValue { get; } 15 | } 16 | 17 | public abstract class Field : IField 18 | { 19 | public bool IsEnabled { get; set; } 20 | 21 | public bool IsValid { get; set; } 22 | 23 | public abstract bool HasValue { get; } 24 | } 25 | 26 | public class Field : Field, IField 27 | { 28 | public T Value { get; set; } 29 | 30 | public override bool HasValue 31 | { 32 | get 33 | { 34 | return !EqualityComparer.Default.Equals(Value, default(T)); 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Common/Model/Entities/Fields/SelectionField.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace Esp.Net.Examples.ReactiveModel.Common.Model.Entities.Fields 5 | { 6 | public interface ISelectionField : IField 7 | { 8 | IReadOnlyCollection Items { get; } 9 | } 10 | 11 | public class SelectionField : Field, ISelectionField 12 | { 13 | private readonly List _items; 14 | private readonly ReadOnlyCollection _readOnlyItems; 15 | 16 | public SelectionField() 17 | { 18 | _items = new List(); 19 | _readOnlyItems = new ReadOnlyCollection(_items); 20 | } 21 | 22 | public List Items 23 | { 24 | get { return _items; } 25 | } 26 | 27 | public void ResetItems(IEnumerable items) 28 | { 29 | _items.Clear(); 30 | _items.AddRange(items); 31 | } 32 | 33 | IReadOnlyCollection ISelectionField.Items 34 | { 35 | get 36 | { 37 | return _readOnlyItems; 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Common/Services/SchedulerService.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive.Concurrency; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.Common.Services 4 | { 5 | public interface ISchedulerService 6 | { 7 | IScheduler ThreadPool { get; } 8 | IScheduler Ui { get; } 9 | } 10 | 11 | public class SchedulerService : ISchedulerService 12 | { 13 | public SchedulerService() 14 | { 15 | ThreadPool = ThreadPoolScheduler.Instance; 16 | Ui = DispatcherScheduler.Instance; 17 | } 18 | 19 | public IScheduler ThreadPool { get; private set; } 20 | 21 | public IScheduler Ui { get; private set; } 22 | } 23 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Common/UI/DelegateCommand .cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | 4 | namespace Esp.Net.Examples.ReactiveModel.Common.UI 5 | { 6 | public class DelegateCommand : ICommand 7 | { 8 | private readonly Predicate _canExecute; 9 | private readonly Action _execute; 10 | 11 | public event EventHandler CanExecuteChanged; 12 | 13 | public DelegateCommand(Action execute) : this(execute, null) 14 | { 15 | } 16 | 17 | public DelegateCommand(Action execute, Predicate canExecute) 18 | { 19 | _execute = execute; 20 | _canExecute = canExecute; 21 | } 22 | 23 | public bool CanExecute(object parameter) 24 | { 25 | if (_canExecute == null) 26 | { 27 | return true; 28 | } 29 | 30 | return _canExecute(parameter); 31 | } 32 | 33 | public void Execute(object parameter) 34 | { 35 | _execute(parameter); 36 | } 37 | 38 | public void RaiseCanExecuteChanged() 39 | { 40 | if (CanExecuteChanged != null) 41 | { 42 | CanExecuteChanged(this, EventArgs.Empty); 43 | } 44 | } 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Common/UI/EntryMonitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive.Disposables; 3 | 4 | namespace Esp.Net.Examples.ReactiveModel.Common.UI 5 | { 6 | public class EntryMonitor 7 | { 8 | private int _enterCount; 9 | 10 | public bool IsBusy 11 | { 12 | get { return _enterCount > 0; } 13 | } 14 | 15 | public IDisposable Enter() 16 | { 17 | _enterCount++; 18 | return Disposable.Create(() => _enterCount-- ); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Common/UI/Fields/FieldViewModel.cs: -------------------------------------------------------------------------------- 1 | using Esp.Net.Examples.ReactiveModel.Common.Model.Entities.Fields; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.Common.UI.Fields 4 | { 5 | public class FieldViewModel : ViewModelBase 6 | { 7 | private T _value; 8 | public T Value 9 | { 10 | get { return _value; } 11 | set 12 | { 13 | SetProperty(ref _value, value); 14 | } 15 | } 16 | 17 | private bool _isValid; 18 | public bool IsValid 19 | { 20 | get { return _isValid; } 21 | set 22 | { 23 | SetProperty(ref _isValid, value); 24 | } 25 | } 26 | 27 | private bool _hasValue; 28 | public bool HasValue 29 | { 30 | get { return _hasValue; } 31 | set 32 | { 33 | SetProperty(ref _hasValue, value); 34 | } 35 | } 36 | 37 | private bool _isEnabled; 38 | public bool IsEnabled 39 | { 40 | get { return _isEnabled; } 41 | set 42 | { 43 | SetProperty(ref _isEnabled, value); 44 | } 45 | } 46 | 47 | public void Sync(IField model) 48 | { 49 | Value = model.Value; 50 | IsValid = model.IsValid; 51 | IsEnabled = model.IsEnabled; 52 | HasValue = model.HasValue; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Common/UI/Fields/SelectionFieldViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using System.Linq; 3 | using Esp.Net.Examples.ReactiveModel.Common.Model.Entities.Fields; 4 | 5 | namespace Esp.Net.Examples.ReactiveModel.Common.UI.Fields 6 | { 7 | public class SelectionFieldViewModel : FieldViewModel 8 | { 9 | private readonly ObservableCollection _items; 10 | 11 | public SelectionFieldViewModel() 12 | { 13 | _items = new ObservableCollection(); 14 | Items = new ReadOnlyObservableCollection(_items); 15 | } 16 | 17 | public ReadOnlyObservableCollection Items { get; private set; } 18 | 19 | public void Sync(ISelectionField model) 20 | { 21 | base.Sync(model); 22 | 23 | // Note: just using a bruit force clear->repopulate approach here. 24 | // In reality you'd build a deferring ObservableCollection 25 | // that supports 'AddRange' (i.e. doesn't raise events for bulk updates) and doesn't trash all the items. 26 | // You don't want to trash all the items as something on the UI may be binding to them, 27 | // you'll suffer a performance hit with the below approach. 28 | // It's fine for a demo 29 | if (!model.Items.SequenceEqual(Items)) 30 | { 31 | _items.Clear(); 32 | foreach (T item in model.Items) 33 | { 34 | _items.Add(item); 35 | } 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Common/UI/InverseBooleanToVisibilityConverter .cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace Esp.Net.Examples.ReactiveModel.Common.UI 7 | { 8 | public class InverseBooleanToVisibilityConverter : IValueConverter 9 | { 10 | public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | return value is bool && ((bool)value) ? Visibility.Collapsed : Visibility.Visible; 13 | } 14 | 15 | public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 16 | { 17 | throw new NotImplementedException(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Common/UI/NotifyingBase .cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace Esp.Net.Examples.ReactiveModel.Common.UI 6 | { 7 | public class ViewModelBase : DisposableBase, INotifyPropertyChanged 8 | { 9 | public event PropertyChangedEventHandler PropertyChanged; 10 | 11 | protected bool SetProperty(ref T backingField, T newValue, [CallerMemberName] string propertyName = null) 12 | { 13 | if (!EqualityComparer.Default.Equals(backingField, newValue)) 14 | { 15 | backingField = newValue; 16 | OnPropertyChanged(propertyName); 17 | return true; 18 | } 19 | 20 | return false; 21 | } 22 | 23 | protected void OnPropertyChanged(string propertyName) 24 | { 25 | var handler = PropertyChanged; 26 | if (handler != null) 27 | { 28 | var e = new PropertyChangedEventArgs(propertyName); 29 | handler(this, e); 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Common/UI/ObservableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Linq.Expressions; 4 | using System.Reactive.Linq; 5 | 6 | namespace Esp.Net.Examples.ReactiveModel.Common.UI 7 | { 8 | public static class ObservableExtensions 9 | { 10 | public static IObservable ObserveProperty( 11 | this T source, 12 | Expression> propertyExpression 13 | ) 14 | where T : INotifyPropertyChanged 15 | { 16 | return source.ObserveProperty(propertyExpression, false); 17 | } 18 | 19 | public static IObservable ObserveProperty( 20 | this T source, 21 | Expression> propertyExpression, 22 | bool observeInitialValue 23 | ) 24 | where T : INotifyPropertyChanged 25 | { 26 | var mExpr = (MemberExpression)propertyExpression.Body; 27 | 28 | var getValue = propertyExpression.Compile(); 29 | 30 | var observable = from evt in Observable 31 | .FromEvent 32 | (h => (s, e) => h(e), 33 | h => source.PropertyChanged += h, 34 | h => source.PropertyChanged -= h) 35 | where evt.PropertyName == mExpr.Member.Name 36 | select getValue(source); 37 | 38 | if (observeInitialValue) 39 | return observable.Merge(Observable.Return(getValue(source))); 40 | 41 | return observable; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("Gui")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("Gui")] 15 | [assembly: AssemblyCopyright("Copyright © 2015")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 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 Esp.Net.Examples.ReactiveModel.Properties { 12 | using System; 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 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Esp.Net.Examples.ReactiveModel.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 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 Esp.Net.Examples.ReactiveModel.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/Themes/MetroDark/HowToApplyTheme.txt: -------------------------------------------------------------------------------- 1 | http://brianlagunas.com/free-metro-light-and-dark-themes-for-wpf-and-silverlight-microsoft-controls/ 2 | 3 | To apply the Infragistics WPF MetroDark theme to your WPF application, paste the following code into your App.xaml 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Model/Entities/Model.cd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 14 | 15 | UAECAACADAAAQEAAAAMDABAAAAAAQAAAAAAAAQkCAAA= 16 | TraderApp\Model\Entities\RfqDetails.cs 17 | 18 | 19 | 20 | 21 | 22 | 32 | 33 | IAAAEABAAAAAMGgACiAIAAAAhggAAAAAAAAAAAAACAA= 34 | TraderApp\Model\Entities\RfqScreen.cs 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Model/Entities/RfqScreen.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Reactive.Disposables; 5 | using Esp.Net.Examples.ReactiveModel.TraderApp.Model.Events; 6 | using Esp.Net.Examples.ReactiveModel.TraderApp.Model.Gateways; 7 | using Esp.Net.Examples.ReactiveModel.TraderApp.Services.Entities; 8 | using log4net; 9 | 10 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Model.Entities 11 | { 12 | public class RfqScreen 13 | : DisposableBase 14 | , IPreEventProcessor 15 | , IPostEventProcessor 16 | { 17 | private static readonly ILog Log = LogManager.GetLogger(typeof(RfqScreen)); 18 | private readonly IRouter _router; 19 | private readonly IRfqServiceGateway _rfqServiceGateway; 20 | private readonly Func _rfqDetailsFactory; 21 | private readonly SerialDisposable _rfqEventsDisposable = new SerialDisposable(); 22 | private readonly List _rfqs = new List(); 23 | private readonly Dictionary _rfqsById = new Dictionary(); 24 | private readonly IReadOnlyCollection _readOnlyRfqs; 25 | private int _version; 26 | 27 | public RfqScreen( 28 | IRouter router, 29 | IRfqServiceGateway rfqServiceGateway, 30 | Func rfqDetailsFactory // container provided factory function 31 | ) 32 | { 33 | _router = router; 34 | _rfqServiceGateway = rfqServiceGateway; 35 | _rfqDetailsFactory = rfqDetailsFactory; 36 | _readOnlyRfqs = new ReadOnlyCollection(_rfqs); 37 | 38 | AddDisposable(_rfqEventsDisposable); 39 | } 40 | 41 | public int Version 42 | { 43 | get { return _version; } 44 | } 45 | 46 | public RfqDetails this[Guid rfqId] 47 | { 48 | get 49 | { 50 | return _rfqsById[rfqId]; 51 | } 52 | } 53 | 54 | public IReadOnlyCollection Rfqs 55 | { 56 | get { return _readOnlyRfqs; } 57 | } 58 | 59 | public void ObserveEvents() 60 | { 61 | _router.ObserveEventsOn(this); 62 | } 63 | 64 | void IPreEventProcessor.Process() 65 | { 66 | _version++; 67 | Log.DebugFormat("Model version is at {0}", _version); 68 | } 69 | 70 | void IPostEventProcessor.Process() 71 | { 72 | foreach (RfqDetails rfqDetailse in _rfqs) 73 | { 74 | rfqDetailse.OnPostProcessing(); 75 | } 76 | } 77 | 78 | [ObserveEvent(typeof(InitialiseEvent))] 79 | private void OnInitialiseEvent() 80 | { 81 | _rfqEventsDisposable.Disposable = _rfqServiceGateway.BeginReceiveRfqEvents(); 82 | } 83 | 84 | [ObserveEvent(typeof(RfqReceivedEvent))] 85 | private void OnRfqReceivedEvent(RfqReceivedEvent e) 86 | { 87 | RfqDetails rfqDetails = _rfqDetailsFactory(e.Request.CurrencyPair, e.Request.Notional, e.Request.QuoteId); 88 | IDisposable eventSubscriptions = _router.ObserveEventsOn(rfqDetails); // in this example we just hold onto rfqDetails for ever, never dispose 89 | // in this example we just hold onto rfqDetails for ever, never dispose 90 | _rfqs.Insert(0, rfqDetails); 91 | _rfqsById.Add(rfqDetails.QuoteId, rfqDetails); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Model/Events/ClientAcceptedQuoteEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Model.Events 4 | { 5 | public class ClientAcceptedQuoteEvent 6 | { 7 | public ClientAcceptedQuoteEvent(Guid quoteId) 8 | { 9 | QuoteId = quoteId; 10 | } 11 | 12 | public Guid QuoteId { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Model/Events/ClientClosedQuoteEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Model.Events 4 | { 5 | public class ClientClosedQuoteEvent 6 | { 7 | public ClientClosedQuoteEvent(Guid quoteId) 8 | { 9 | QuoteId = quoteId; 10 | } 11 | 12 | public Guid QuoteId { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Model/Events/ClientRejectQuoteEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Model.Events 4 | { 5 | public class ClientRejectQuoteEvent 6 | { 7 | public ClientRejectQuoteEvent(Guid quoteId) 8 | { 9 | QuoteId = quoteId; 10 | } 11 | 12 | public Guid QuoteId { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Model/Events/InitialiseEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Model.Events 2 | { 3 | public class InitialiseEvent 4 | { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Model/Events/RfqRateChangedEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Model.Events 4 | { 5 | public class RfqRateChangedEvent 6 | { 7 | public RfqRateChangedEvent(decimal? rate, Guid rfqCorrelationId) 8 | { 9 | Rate = rate; 10 | RfqCorrelationId = rfqCorrelationId; 11 | } 12 | 13 | public Guid RfqCorrelationId { get; private set; } 14 | 15 | public decimal? Rate { get; private set; } 16 | } 17 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Model/Events/RfqReceivedEvent.cs: -------------------------------------------------------------------------------- 1 | using Esp.Net.Examples.ReactiveModel.TraderApp.Services.Entities; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Model.Events 4 | { 5 | public class RfqReceivedEvent 6 | { 7 | public RfqReceivedEvent(RfqRequest request) 8 | { 9 | Request = request; 10 | } 11 | 12 | public RfqRequest Request { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Model/Events/TraderRejectQuoteEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Model.Events 4 | { 5 | public class TraderRejectQuoteEvent 6 | { 7 | public TraderRejectQuoteEvent(Guid quoteId) 8 | { 9 | QuoteId = quoteId; 10 | } 11 | 12 | public Guid QuoteId { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Model/Events/TraderSendQuoteEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Model.Events 4 | { 5 | public class TraderSendQuoteEvent 6 | { 7 | public TraderSendQuoteEvent(Guid quoteId) 8 | { 9 | QuoteId = quoteId; 10 | } 11 | 12 | public Guid QuoteId { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Model/Gateways/IRfqServiceGateway.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Esp.Net.Examples.ReactiveModel.TraderApp.Model.Entities; 3 | 4 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Model.Gateways 5 | { 6 | public interface IRfqServiceGateway 7 | { 8 | IDisposable BeginReceiveRfqEvents(); 9 | void SendUpdate(RfqDetails rfqDetails, bool isLastmessage = false); 10 | } 11 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Model/Gateways/RfqServiceGateway.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive.Disposables; 3 | using Esp.Net.Examples.ReactiveModel.TraderApp.Model.Entities; 4 | using Esp.Net.Examples.ReactiveModel.TraderApp.Model.Events; 5 | using Esp.Net.Examples.ReactiveModel.TraderApp.Services; 6 | using Esp.Net.Examples.ReactiveModel.TraderApp.Services.Entities; 7 | 8 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Model.Gateways 9 | { 10 | public class RfqServiceGateway : IRfqServiceGateway 11 | { 12 | private readonly IRfqService _rfqService; 13 | private readonly IRouter _router; 14 | 15 | public RfqServiceGateway(IRfqService rfqService, IRouter router) 16 | { 17 | _rfqService = rfqService; 18 | _router = router; 19 | } 20 | 21 | public IDisposable BeginReceiveRfqEvents() 22 | { 23 | var disposables = new CompositeDisposable(); 24 | disposables.Add( 25 | _rfqService.QuoteRequests.Subscribe( 26 | rfqRequest => _router.PublishEvent(new RfqReceivedEvent(rfqRequest)) 27 | ) 28 | ); 29 | disposables.Add( 30 | _rfqService.QuoteAccepts.Subscribe( 31 | quoteId => _router.PublishEvent(new ClientAcceptedQuoteEvent(quoteId)) 32 | ) 33 | ); 34 | disposables.Add( 35 | _rfqService.QuoteRejects.Subscribe( 36 | quoteId => _router.PublishEvent(new ClientRejectQuoteEvent(quoteId)) 37 | ) 38 | ); 39 | disposables.Add( 40 | _rfqService.QuotesClosed.Subscribe( 41 | quoteId => _router.PublishEvent(new ClientClosedQuoteEvent(quoteId)) 42 | ) 43 | ); 44 | return disposables; 45 | } 46 | 47 | public void SendUpdate(RfqDetails rfqDetails, bool isLastmessage = false) 48 | { 49 | var response = new RfqResponse( 50 | rfqDetails.QuoteId, 51 | rfqDetails.CurrencyPair, 52 | rfqDetails.Notional, 53 | rfqDetails.Rate.Value, 54 | rfqDetails.QuoteStatus, 55 | isLastmessage 56 | ); 57 | _rfqService.SendUpdate(response); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Services/Entities/CurrencyPair.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Services.Entities 2 | { 3 | public class CurrencyPair 4 | { 5 | public CurrencyPair(string isoCode, int precision) 6 | { 7 | IsoCode = isoCode; 8 | Precision = precision; 9 | 10 | Base = IsoCode.Substring(0, 3); 11 | Counter = IsoCode.Substring(3, 3); 12 | } 13 | 14 | public string IsoCode { get; private set; } 15 | 16 | public int Precision { get; private set; } 17 | 18 | public string Base { get; private set; } 19 | 20 | public string Counter { get; private set; } 21 | } 22 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Services/Entities/QuoteStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Services.Entities 2 | { 3 | public enum QuoteStatus 4 | { 5 | New, 6 | Quoting, 7 | ClientRejected, 8 | TraderRejected, 9 | Booked 10 | } 11 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Services/Entities/RfqRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Services.Entities 4 | { 5 | public class RfqRequest 6 | { 7 | public RfqRequest(Guid quoteId, CurrencyPair currencyPair, decimal notional) 8 | { 9 | QuoteId = quoteId; 10 | CurrencyPair = currencyPair; 11 | Notional = notional; 12 | } 13 | 14 | public Guid QuoteId { get; private set; } 15 | public CurrencyPair CurrencyPair { get; private set; } 16 | public decimal Notional { get; private set; } 17 | } 18 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Services/Entities/RfqResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Services.Entities 4 | { 5 | public class RfqResponse 6 | { 7 | public RfqResponse(Guid quoteId, CurrencyPair currencyPair, decimal notional, decimal? rate, QuoteStatus quoteStatus, bool isLastMessage = false) 8 | { 9 | QuoteId = quoteId; 10 | CurrencyPair = currencyPair; 11 | Notional = notional; 12 | Rate = rate; 13 | QuoteStatus = quoteStatus; 14 | IsLastMessage = isLastMessage; 15 | } 16 | 17 | public Guid QuoteId { get; private set; } 18 | public CurrencyPair CurrencyPair { get; private set; } 19 | public decimal Notional { get; private set; } 20 | public decimal? Rate { get; private set; } 21 | public QuoteStatus QuoteStatus { get; private set; } 22 | public bool IsLastMessage { get; private set; } 23 | } 24 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/Services/IRfqService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Esp.Net.Examples.ReactiveModel.TraderApp.Services.Entities; 3 | 4 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.Services 5 | { 6 | public interface IRfqService 7 | { 8 | void SendUpdate(RfqResponse response); 9 | 10 | IObservable QuoteRequests { get; } 11 | 12 | IObservable QuoteAccepts { get; } 13 | 14 | IObservable QuoteRejects { get; } 15 | 16 | IObservable QuotesClosed { get; } 17 | } 18 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/TraderAppBootstrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Autofac; 3 | using Esp.Net.Examples.ReactiveModel.Common; 4 | using Esp.Net.Examples.ReactiveModel.Common.Services; 5 | using Esp.Net.Examples.ReactiveModel.TraderApp.Model.Entities; 6 | using Esp.Net.Examples.ReactiveModel.TraderApp.Model.Events; 7 | using Esp.Net.Examples.ReactiveModel.TraderApp.Model.Gateways; 8 | using Esp.Net.Examples.ReactiveModel.TraderApp.Services; 9 | using Esp.Net.Examples.ReactiveModel.TraderApp.UI.RfqScreen; 10 | using Esp.Net.Examples.ReactiveModel.TraderApp.UI.Shell; 11 | using log4net; 12 | 13 | namespace Esp.Net.Examples.ReactiveModel.TraderApp 14 | { 15 | public class TraderAppBootstrapper 16 | { 17 | private static readonly ILog Log = LogManager.GetLogger(typeof(TraderAppBootstrapper)); 18 | 19 | private IContainer _container; 20 | 21 | public void Run() 22 | { 23 | Log.Debug("Running"); 24 | ConfigureContainer(); 25 | CreateAndRegisterModel(); 26 | StartUi(); 27 | } 28 | 29 | private void ConfigureContainer() 30 | { 31 | ContainerBuilder builder = new ContainerBuilder(); 32 | 33 | // views and view models 34 | builder 35 | .RegisterType() 36 | .SingleInstance(); 37 | 38 | builder 39 | .RegisterType() 40 | .SingleInstance(); 41 | 42 | builder 43 | .RegisterType() 44 | .SingleInstance(); 45 | builder 46 | .RegisterType(); 47 | 48 | // services 49 | builder 50 | .RegisterType() 51 | .As() 52 | .SingleInstance(); 53 | builder 54 | .RegisterInstance(FakeMiddleware.Instance) 55 | .As() 56 | .SingleInstance(); 57 | 58 | // router 59 | builder 60 | .RegisterType() 61 | .SingleInstance(); 62 | 63 | // model 64 | builder 65 | .RegisterType() 66 | .As() 67 | .SingleInstance(); 68 | builder 69 | .RegisterType() 70 | .SingleInstance(); 71 | builder 72 | .RegisterType(); 73 | 74 | _container = builder.Build(); 75 | } 76 | 77 | private void CreateAndRegisterModel() 78 | { 79 | ContainerBuilder builder = new ContainerBuilder(); 80 | var router = _container.Resolve(); 81 | 82 | var modelId = Guid.NewGuid(); 83 | IRouter modelRouter = router.CreateModelRouter(modelId); 84 | builder.RegisterInstance(modelRouter); 85 | builder.Update(_container); 86 | var model = _container.Resolve(); 87 | router.AddModel(modelId, model); 88 | } 89 | 90 | private void StartUi() 91 | { 92 | var model = _container.Resolve(); 93 | var router = _container.Resolve>(); 94 | var rfqScreenViewModel = _container.Resolve(); 95 | var traderAppShellView = _container.Resolve(); 96 | 97 | model.ObserveEvents(); 98 | rfqScreenViewModel.Start(); 99 | traderAppShellView.Show(); 100 | 101 | router.PublishEvent(new InitialiseEvent()); 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/UI/RfqScreen/RfqDetailsView.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/UI/RfqScreen/RfqDetailsView.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.UI.RfqScreen 2 | { 3 | public partial class RfqDetailsView 4 | { 5 | public RfqDetailsView() 6 | { 7 | InitializeComponent(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/UI/RfqScreen/TraderRfqScreenView.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 25 | 26 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/UI/RfqScreen/TraderRfqScreenView.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.UI.RfqScreen 2 | { 3 | public partial class TraderRfqScreenView 4 | { 5 | public TraderRfqScreenView() 6 | { 7 | InitializeComponent(); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/UI/RfqScreen/TraderRfqScreenViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using Esp.Net.Examples.ReactiveModel.Common.UI; 5 | using Esp.Net.Examples.ReactiveModel.TraderApp.Model.Entities; 6 | 7 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.UI.RfqScreen 8 | { 9 | public class TraderRfqScreenViewModel : ViewModelBase 10 | { 11 | private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(TraderRfqScreenViewModel)); 12 | private readonly IRouter _router; 13 | private readonly Func _rfqDetailsViewModelFactory; 14 | private readonly ObservableCollection _rfqs = new ObservableCollection(); 15 | private readonly Dictionary _rfqsById = new Dictionary(); 16 | 17 | public TraderRfqScreenViewModel(IRouter router, Func rfqDetailsViewModelFactory) 18 | { 19 | _router = router; 20 | _rfqDetailsViewModelFactory = rfqDetailsViewModelFactory; 21 | } 22 | 23 | public ObservableCollection Rfqs 24 | { 25 | get { return _rfqs; } 26 | } 27 | 28 | public void Start() 29 | { 30 | SyncViewWithModel(); 31 | } 32 | 33 | private void SyncViewWithModel() 34 | { 35 | AddDisposable(_router.GetModelObservable().Observe(model => 36 | { 37 | Log.DebugFormat("Model update received. Version: {0}", model.Version); 38 | foreach (RfqDetails rfq in model.Rfqs) 39 | { 40 | RfqDetailsViewModel vm; 41 | // we never delete RFQs on the GUI so this sync is easy 42 | if (!_rfqsById.TryGetValue(rfq.QuoteId, out vm)) 43 | { 44 | vm = _rfqDetailsViewModelFactory(); 45 | _rfqsById.Add(rfq.QuoteId, vm); 46 | Rfqs.Insert(0, vm); 47 | vm.Start(rfq); 48 | } 49 | } 50 | })); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/UI/Shell/TraderAppShellView.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/UI/Shell/TraderAppShellView.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.UI.Shell 2 | { 3 | public partial class TraderAppShellView 4 | { 5 | public TraderAppShellView(TraderAppShellViewModel viewModel) 6 | { 7 | InitializeComponent(); 8 | DataContext = viewModel; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/TraderApp/UI/Shell/TraderAppShellViewModel.cs: -------------------------------------------------------------------------------- 1 | using Esp.Net.Examples.ReactiveModel.TraderApp.UI.RfqScreen; 2 | 3 | namespace Esp.Net.Examples.ReactiveModel.TraderApp.UI.Shell 4 | { 5 | public class TraderAppShellViewModel 6 | { 7 | public TraderAppShellViewModel(TraderRfqScreenViewModel traderRfqScreenViewModel) 8 | { 9 | TraderRfqScreenViewModel = traderRfqScreenViewModel; 10 | } 11 | 12 | public TraderRfqScreenViewModel TraderRfqScreenViewModel { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/ReactiveModel/Esp.Net.Examples.ReactiveModel/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/ReactiveModel/ReactiveModel.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Esp.Net.Examples.ReactiveModel", "Esp.Net.Examples.ReactiveModel\Esp.Net.Examples.ReactiveModel.csproj", "{E9BC9DDF-A98B-4A4E-B043-E45C774ADAA8}" 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 | {E9BC9DDF-A98B-4A4E-B043-E45C774ADAA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {E9BC9DDF-A98B-4A4E-B043-E45C774ADAA8}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {E9BC9DDF-A98B-4A4E-B043-E45C774ADAA8}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {E9BC9DDF-A98B-4A4E-B043-E45C774ADAA8}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /examples/ReactiveModel/ReactiveModel.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | capitalize(Name) 4 | -1 5 | nprop 6 | private $Type$ _$Name$; 7 | public $Type$ $NameUpper$ 8 | { 9 | get { return _$Name$; } 10 | private set 11 | { 12 | SetProperty(ref _$Name$, value); 13 | } 14 | } 15 | 16 | 17 | True 18 | True 19 | InCSharpFile 20 | 2.0 21 | True 22 | 0 23 | True 24 | 1 25 | True 26 | 2 -------------------------------------------------------------------------------- /examples/ReactiveModel/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Evented State Processor Reactive Model Example 2 | 3 | This example shows a simplistic client/trader request for quote (RFQ) workflow. 4 | The example has both a client and trader application built using the [reactive domain model approach](http://esp.readthedocs.org/en/latest/modeling-approaches/reactive-domain-model.html). 5 | For simplicity both the client and trader apps run within the same process, middleware is fudged. 6 | 7 | Client terminal allowing a users to buy some currency : 8 | ![Client terminal](doco/client.png) 9 | 10 | Trader terminal allow you to pick up an RFQ and send a quote to the client: 11 | ![Trader terminal](doco/trader.png) 12 | 13 | ## Learning ESP 14 | 15 | - [Documentation](http://esp.readthedocs.org/en/latest/) 16 | 17 | ### Get help from other users: 18 | 19 | - [esp/chat on Gitter Chat](https://gitter.im/esp/chat) 20 | - [Questions tagged esp on StackOverflow](http://stackoverflow.com/questions/tagged/esp) 21 | - [GitHub Issues](https://github.com/esp/esp-net/issues) 22 | 23 | *Let us [know](https://github.com/esp/esp-net/issues) if you discover anything worth sharing!* 24 | 25 | ## Running 26 | 27 | Requires Visual Studio 2013 and .Net 4.5. 28 | 29 | Open the solution, restore the nuget packages and run up in the debugger (F5). -------------------------------------------------------------------------------- /examples/ReactiveModel/doco/client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp/esp-net/ee1c0fe1b9ae6ca9631b046c1505236917aa9f9f/examples/ReactiveModel/doco/client.png -------------------------------------------------------------------------------- /examples/ReactiveModel/doco/trader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp/esp-net/ee1c0fe1b9ae6ca9631b046c1505236917aa9f9f/examples/ReactiveModel/doco/trader.png -------------------------------------------------------------------------------- /src/AssemblyInfo.Common.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System.Reflection; 18 | 19 | [assembly: AssemblyCompany("")] 20 | [assembly: AssemblyProduct("Esp.Net")] 21 | [assembly: AssemblyCopyright("Copyright © 2015")] 22 | [assembly: AssemblyTrademark("")] 23 | [assembly: AssemblyCulture("")] 24 | 25 | [assembly: AssemblyVersion("0.0.0.1")] 26 | [assembly: AssemblyFileVersion("0.0.0.1")] 27 | -------------------------------------------------------------------------------- /src/Esp.Net.Dispatchers/Esp.Net.Dispatchers.SourcePackage.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | esp-net-dispatchers-source 5 | $version$ 6 | esp-net-dispatchers-source 7 | Dev Shop Limited 8 | Dev Shop Limited 9 | https://github.com/esp/esp-net/LICENSE.txt 10 | https://github.com/esp/esp-net 11 | https://raw.github.com/esp/esp-net/master/package_icon.png 12 | false 13 | Source Only Package - Evented State Processor (ESP) adds specific processing workflow around changes to a model's state. This package adds functionality to integrate ESP with RX. 14 | Copyright Dev Shop Limited 2015 15 | evented state processor reactive router event loop rx 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Esp.Net.Dispatchers/Esp.Net.Dispatchers.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {769ECEB6-57D8-47B9-8E5F-651F003F609A} 8 | Library 9 | Properties 10 | Esp.Net 11 | Esp.Net.Dispatchers 12 | v4.0 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | bin\Debug\Esp.Net.Dispatchers.XML 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | bin\Release\Esp.Net.Dispatchers.XML 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | Properties\AssemblyInfo.Common.cs 48 | 49 | 50 | 51 | 52 | 53 | 54 | {2C08EEBF-99F4-43AD-B87F-2B90DEB6F8DE} 55 | Esp.Net 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 70 | -------------------------------------------------------------------------------- /src/Esp.Net.Dispatchers/Esp.Net.Dispatchers.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | esp-net-dispatchers 5 | $version$ 6 | esp-net-dispatchers 7 | Dev Shop Limited 8 | Dev Shop Limited 9 | https://github.com/esp/esp-net/LICENSE.txt 10 | https://github.com/esp/esp-net 11 | https://raw.github.com/esp/esp-net/master/package_icon.png 12 | false 13 | Evented State Processor (ESP) adds specific processing workflow around changes to a model's state. This package adds functionality to integrate ESP with RX. 14 | Copyright Dev Shop Limited 2015 15 | evented state processor reactive router event loop rx 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Esp.Net.Dispatchers/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System.Resources; 18 | using System.Reflection; 19 | using System.Runtime.CompilerServices; 20 | 21 | [assembly: AssemblyTitle("Esp.Net.Dispatchers")] 22 | [assembly: AssemblyDescription("")] 23 | [assembly: AssemblyConfiguration("")] 24 | [assembly: NeutralResourcesLanguage("en")] 25 | 26 | [assembly: InternalsVisibleTo("Esp.Net.Tests")] 27 | [assembly: InternalsVisibleTo("Esp.Net")] 28 | -------------------------------------------------------------------------------- /src/Esp.Net.Rx/Esp.Net.Rx.SourcePackage.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | esp-net-rx-source 5 | $version$ 6 | esp-net-rx-source 7 | Dev Shop Limited 8 | Dev Shop Limited 9 | https://github.com/esp/esp-net/LICENSE.txt 10 | https://github.com/esp/esp-net 11 | https://raw.github.com/esp/esp-net/master/package_icon.png 12 | false 13 | Source Only Package - Evented State Processor (ESP) adds specific processing workflow around changes to a model's state. This package adds functionality to integrate ESP with RX. 14 | Copyright Dev Shop Limited 2015 15 | evented state processor reactive router event loop rx 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Esp.Net.Rx/Esp.Net.Rx.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | esp-net-rx 5 | $version$ 6 | esp-net-rx 7 | Dev Shop Limited 8 | Dev Shop Limited 9 | https://github.com/esp/esp-net/LICENSE.txt 10 | https://github.com/esp/esp-net 11 | https://raw.github.com/esp/esp-net/master/package_icon.png 12 | false 13 | Evented State Processor (ESP) adds specific processing workflow around changes to a model's state. This package adds functionality to integrate ESP with RX. 14 | Copyright Dev Shop Limited 2015 15 | evented state processor reactive router event loop rx 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Esp.Net.Rx/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | 5 | [assembly: AssemblyTitle("Esp.Net.Workflow")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: NeutralResourcesLanguage("en")] 9 | 10 | [assembly: InternalsVisibleTo("Esp.Net.Tests")] 11 | [assembly: InternalsVisibleTo("Esp.Net")] 12 | -------------------------------------------------------------------------------- /src/Esp.Net.Rx/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Esp.Net.Tests/Dispatchers/NewThreadRouterDispatcherTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using NUnit.Framework; 5 | using Shouldly; 6 | 7 | namespace Esp.Net.Dispatchers 8 | { 9 | [TestFixture] 10 | public class NewThreadRouterDispatcherTests 11 | { 12 | private NewThreadRouterDispatcher _dispatcher; 13 | 14 | [SetUp] 15 | public void SetUp() 16 | { 17 | _dispatcher = new NewThreadRouterDispatcher(ts => new Thread(ts)); 18 | } 19 | 20 | [Test] 21 | public void CheckAccessReturnsFalseOnOtherThread() 22 | { 23 | _dispatcher.CheckAccess().ShouldBe(false); 24 | } 25 | 26 | [Test] 27 | public void CheckAccessReturnsTrueOnDispatcherThread() 28 | { 29 | ManualResetEvent gate = new ManualResetEvent(false); 30 | bool? checkAccess = null; 31 | _dispatcher.Dispatch(() => 32 | { 33 | checkAccess = _dispatcher.CheckAccess(); 34 | gate.Set(); 35 | }); 36 | gate.WaitOne(100); 37 | checkAccess.ShouldNotBe(null); 38 | checkAccess.Value.ShouldBe(true); 39 | } 40 | 41 | [Test] 42 | public void EnsureAccessThrowsOnOtherThread() 43 | { 44 | var exception = Assert.Throws(() => _dispatcher.EnsureAccess()); 45 | exception.Message.ShouldBe("Router accessed on invalid thread"); 46 | } 47 | 48 | [Test] 49 | public void DispatchThrowsIfActionNull() 50 | { 51 | Assert.Throws(() => _dispatcher.Dispatch(null)); 52 | } 53 | 54 | [Test] 55 | public void DispatchThrowsIfDisposed() 56 | { 57 | _dispatcher.Dispose(); 58 | Assert.Throws(() => _dispatcher.Dispatch(() => { })); 59 | } 60 | 61 | [Test] 62 | public void DispatchRunsAction() 63 | { 64 | ManualResetEvent gate = new ManualResetEvent(false); 65 | bool? wasRun = null; 66 | _dispatcher.Dispatch(() => 67 | { 68 | wasRun = true; 69 | gate.Set(); 70 | }); 71 | gate.WaitOne(100); 72 | wasRun.ShouldNotBe(null); 73 | wasRun.Value.ShouldBe(true); 74 | } 75 | 76 | [Test] 77 | public void DispatchQueuesSubsequentActionPostedOnDispatcherThread() 78 | { 79 | AutoResetEvent gate = new AutoResetEvent(false); 80 | bool? pass = null; 81 | bool? wasRun = null; 82 | _dispatcher.Dispatch(() => 83 | { 84 | _dispatcher.Dispatch(() => 85 | { 86 | // we can rely on this thread being the same as the outer 87 | // dispatcher call so we can make assumptions about it being 88 | // the only thread that can set wasRun. 89 | pass = wasRun == true; 90 | gate.Set(); 91 | }); 92 | wasRun = true; 93 | }); 94 | gate.WaitOne(); 95 | wasRun.Value.ShouldBe(true); 96 | pass.Value.ShouldBe(true); 97 | } 98 | 99 | [Test] 100 | public void DisposingFromDispatcherThreadStopFurtherProcessing() 101 | { 102 | AutoResetEvent gate = new AutoResetEvent(false); 103 | List processed = new List(); 104 | _dispatcher.Dispatch(() => 105 | { 106 | processed.Add(1); 107 | _dispatcher.Dispatch(() => 108 | { 109 | processed.Add(2); 110 | gate.Set(); 111 | }); 112 | _dispatcher.Dispose(); 113 | }); 114 | gate.WaitOne(500).ShouldBe(false); 115 | processed.ShouldBe(new int[] { 1 }); 116 | Assert.Throws(() => _dispatcher.Dispatch(() => processed.Add(3))); 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/Disposables/EspDisposableTests.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using NUnit.Framework; 19 | 20 | namespace Esp.Net.Disposables 21 | { 22 | [TestFixture] 23 | public sealed class EspDisposableTests 24 | { 25 | [Test] 26 | public void ShouldThrowWithNullAction() 27 | { 28 | Assert.Throws(() => EspDisposable.Create(null)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Esp.Net.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System.Reflection; 18 | using System.Runtime.InteropServices; 19 | 20 | // General Information about an assembly is controlled through the following 21 | // set of attributes. Change these attribute values to modify the information 22 | // associated with an assembly. 23 | [assembly: AssemblyTitle("Esp.Net.Tests")] 24 | [assembly: AssemblyDescription("")] 25 | [assembly: AssemblyConfiguration("")] 26 | 27 | // Setting ComVisible to false makes the types in this assembly not visible 28 | // to COM components. If you need to access a type in this assembly from 29 | // COM, set the ComVisible attribute to true on that type. 30 | [assembly: ComVisible(false)] 31 | 32 | // The following GUID is for the ID of the typelib if this project is exposed to COM 33 | [assembly: Guid("55102c33-9ad4-4145-b4c4-66770fbf37df")] -------------------------------------------------------------------------------- /src/Esp.Net.Tests/Reactive/StubEventObservable.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | namespace Esp.Net.Reactive 20 | { 21 | public class StubEventObservable : IEventObservable 22 | { 23 | public bool IsDisposed { get; private set; } 24 | public bool IsObserved { get; set; } 25 | 26 | public IDisposable Observe(Action onNext) 27 | { 28 | return EspDisposable.Create(() => IsDisposed = true); 29 | } 30 | 31 | public IDisposable Observe(Action onNext, Action onCompleted) 32 | { 33 | return EspDisposable.Create(() => IsDisposed = true); 34 | } 35 | 36 | public IDisposable Observe(Action onNext) 37 | { 38 | return EspDisposable.Create(() => IsDisposed = true); 39 | } 40 | 41 | public IDisposable Observe(Action onNext, Action onCompleted) 42 | { 43 | return EspDisposable.Create(() => IsDisposed = true); 44 | } 45 | 46 | public IDisposable Observe(Action onNext) 47 | { 48 | return EspDisposable.Create(() => IsDisposed = true); 49 | } 50 | 51 | public IDisposable Observe(Action onNext, Action onCompleted) 52 | { 53 | return EspDisposable.Create(() => IsDisposed = true); 54 | } 55 | 56 | public IDisposable Observe(IEventObserver observer) 57 | { 58 | return EspDisposable.Create(() => IsDisposed = true); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/Reactive/StubEventObservationRegistrar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Esp.Net.Meta; 4 | 5 | namespace Esp.Net.Reactive 6 | { 7 | public class StubEventObservationRegistrar : IEventObservationRegistrar 8 | { 9 | public StubEventObservationRegistrar() 10 | { 11 | Register = new Dictionary(); 12 | } 13 | 14 | public Dictionary Register { get; private set; } 15 | 16 | public void IncrementRegistration() 17 | { 18 | if (Register.ContainsKey(typeof (TEvent))) 19 | { 20 | Register[typeof (TEvent)]++; 21 | } 22 | else 23 | { 24 | Register[typeof(TEvent)] = 1; 25 | } 26 | } 27 | 28 | public void DecrementRegistration() 29 | { 30 | Register[typeof(TEvent)]--; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/RouterTests.Ctor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | 4 | namespace Esp.Net 5 | { 6 | public partial class RouterTests 7 | { 8 | public class Ctor 9 | { 10 | [Test] 11 | public void ThrowsIfIRouterDispatcherNull() 12 | { 13 | Assert.Throws(() => new Router((IRouterDispatcher)null)); 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/RouterTests.ErrorFlows.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using Shouldly; 4 | 5 | namespace Esp.Net 6 | { 7 | public partial class RouterTests 8 | { 9 | public class ErrorFlows : RouterTests 10 | { 11 | [Test] 12 | public void CancelingAtNormalObservationStageThrows() 13 | { 14 | var event1 = new Event1 15 | { 16 | ShouldCancel = true, 17 | CancelAtStage = ObservationStage.Normal, 18 | CancelAtEventProcesserId = EventProcessor1Id, 19 | }; 20 | _router.PublishEvent(_model1.Id, event1); 21 | _terminalErrorHandler.Errors.Count.ShouldBe(1); 22 | _terminalErrorHandler.Errors[0].ShouldBeOfType(); 23 | } 24 | 25 | [Test] 26 | public void CancelingAtCommittedObservationStageThrows() 27 | { 28 | var event1 = new Event1 29 | { 30 | ShouldCommit = true, 31 | CommitAtStage = ObservationStage.Normal, 32 | CommitAtEventProcesserId = EventProcessor1Id, 33 | 34 | ShouldCancel = true, 35 | CancelAtStage = ObservationStage.Committed, 36 | CancelAtEventProcesserId = EventProcessor1Id, 37 | }; 38 | _router.PublishEvent(_model1.Id, event1); 39 | _terminalErrorHandler.Errors.Count.ShouldBe(1); 40 | _terminalErrorHandler.Errors[0].ShouldBeOfType(); 41 | } 42 | 43 | [Test] 44 | public void CommittingAtPreviewObservationStageThrows() 45 | { 46 | var event1 = new Event1 47 | { 48 | ShouldCommit = true, 49 | CommitAtStage = ObservationStage.Preview, 50 | CommitAtEventProcesserId = EventProcessor1Id, 51 | }; 52 | _router.PublishEvent(_model1.Id, event1); 53 | _terminalErrorHandler.Errors.Count.ShouldBe(1); 54 | _terminalErrorHandler.Errors[0].ShouldBeOfType(); 55 | } 56 | 57 | [Test] 58 | public void ObservingAPrivateEventThrowsAnInvalidOperationException() 59 | { 60 | _router.GetEventObservable(_model1.Id).Observe((model, ev) => { }); 61 | _router.PublishEvent(_model1.Id, new PrivateEvent()); 62 | _terminalErrorHandler.Errors.Count.ShouldBe(1); 63 | _terminalErrorHandler.Errors[0].Message.ShouldContain("Is this event scoped as private or internal"); 64 | } 65 | 66 | [Test] 67 | public void PublishingAPrivateEventThrowsAnInvalidOperationException() 68 | { 69 | _router.GetEventObservable(_model1.Id).Observe((model, ev) => { }); 70 | _router.PublishEvent(_model1.Id, new PrivateEvent()); 71 | _terminalErrorHandler.Errors.Count.ShouldBe(1); 72 | _terminalErrorHandler.Errors[0].ShouldBeOfType(); 73 | _terminalErrorHandler.Errors[0].Message.ShouldContain("Is this event scoped as private or internal"); 74 | } 75 | 76 | [Test] 77 | public void RethrowsWhenNoTerminalErrorHandlerRegistered() 78 | { 79 | var router = new Router(new TestModel()); 80 | router.GetEventObservable().Observe((m, e) => 81 | { 82 | throw new InvalidOperationException("Boom"); 83 | }); 84 | Assert.Throws(() => 85 | { 86 | router.PublishEvent(new Event1()); 87 | }); 88 | } 89 | 90 | private class PrivateEvent : PrivateBaseEvent { } 91 | private class PrivateBaseEvent { } 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/RouterTests.EventObservationDisposal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using Shouldly; 4 | 5 | namespace Esp.Net 6 | { 7 | public partial class RouterTests 8 | { 9 | public class EventObservationDisposal : RouterTests 10 | { 11 | [Test] 12 | public void DisposedPreviewObservationStageObserversDontRecievEvent() 13 | { 14 | _model1EventProcessor.Event1Details.PreviewStage.ObservationDisposable.Dispose(); 15 | _router.PublishEvent(_model1.Id, new Event1()); 16 | _model1EventProcessor.Event1Details.PreviewStage.ReceivedEvents.Count.ShouldBe(0); 17 | _model1EventProcessor.Event1Details.NormalStage.ReceivedEvents.Count.ShouldBe(1); 18 | } 19 | 20 | [Test] 21 | public void DisposedNormalObservationStageObserversDontRecievEvent() 22 | { 23 | _model1EventProcessor.Event1Details.NormalStage.ObservationDisposable.Dispose(); 24 | _router.PublishEvent(_model1.Id, new Event1()); 25 | _model1EventProcessor.Event1Details.PreviewStage.ReceivedEvents.Count.ShouldBe(1); 26 | _model1EventProcessor.Event1Details.NormalStage.ReceivedEvents.Count.ShouldBe(0); 27 | } 28 | 29 | [Test] 30 | public void DisposedCommittedObservationStageObserversDontRecievEvent() 31 | { 32 | _model1EventProcessor.Event1Details.CommittedStage.ObservationDisposable.Dispose(); 33 | _router.PublishEvent(_model1.Id, new Event1 { ShouldCommit = true, CommitAtStage = ObservationStage.Normal, CommitAtEventProcesserId = EventProcessor1Id }); 34 | _model1EventProcessor.Event1Details.NormalStage.ReceivedEvents.Count.ShouldBe(1); 35 | _model1EventProcessor.Event1Details.CommittedStage.ReceivedEvents.Count.ShouldBe(0); 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/RouterTests.ModelObservation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | using Shouldly; 5 | 6 | namespace Esp.Net 7 | { 8 | public partial class RouterTests 9 | { 10 | public class ModelObservation : RouterTests 11 | { 12 | [Test] 13 | public void ThrowsIfModelIdNull() 14 | { 15 | Assert.Throws(() => _router.GetModelObservable(null)); 16 | } 17 | 18 | [Test] 19 | public void ObserversReceiveModelOnEventWorkflowCompleted() 20 | { 21 | _router.PublishEvent(_model1.Id, new Event1()); 22 | _model1Controller.ReceivedModels.Count.ShouldBe(1); 23 | } 24 | 25 | [Test] 26 | public void DisposedObserversReceiveDontModelOnEventWorkflowCompleted() 27 | { 28 | _model1Controller.ModelObservationDisposable.Dispose(); 29 | _router.PublishEvent(_model1.Id, new Event1()); 30 | _model1Controller.ReceivedModels.Count.ShouldBe(0); 31 | } 32 | 33 | [Test] 34 | public void MutipleSubsequentEventsOnlyYield1ModelUpdate() 35 | { 36 | PublishEventWithMultipeSubsequentEvents(5); 37 | _model1EventProcessor.Event1Details.NormalStage.ReceivedEvents.Count.ShouldBe(6); 38 | _model1Controller.ReceivedModels.Count.ShouldBe(1); 39 | } 40 | 41 | [Test] 42 | public void EventsPublishedDuringModelDispatchGetProcessed() 43 | { 44 | bool publishedEventFromController = false; 45 | _model1Controller.RegisterAction(m => 46 | { 47 | if (!publishedEventFromController) 48 | { 49 | publishedEventFromController = true; 50 | _router.PublishEvent(_model1.Id, new Event1()); 51 | } 52 | }); 53 | _router.PublishEvent(_model1.Id, new Event1()); 54 | publishedEventFromController.ShouldBe(true); 55 | _model1Controller.ReceivedModels.Count.ShouldBe(2); 56 | } 57 | 58 | [Test] 59 | public void ModelIsDeliveredToObserversImmediately() 60 | { 61 | _model1Controller.ReceivedModels.Count.ShouldBe(0); 62 | _router.PublishEvent(_model1.Id, new Event1()); 63 | _model1Controller.ReceivedModels.Count.ShouldBe(1); 64 | int receivedCount = 0; 65 | _router 66 | .GetModelObservable(_model1.Id) 67 | .Observe(m => receivedCount++); 68 | receivedCount.ShouldBe(1); 69 | } 70 | } 71 | 72 | public class ModelCloning : RouterTests 73 | { 74 | [Test] 75 | public void DispatchesAModelCloneIfTheModelImplementsIClonable() 76 | { 77 | var receivedModels = new List(); 78 | _router.GetEventObservable(_model4.Id).Observe((m, e) => { /*noop*/ }); 79 | _router.GetModelObservable(_model4.Id).Observe(m => receivedModels.Add(m)); 80 | _router.PublishEvent(_model4.Id, 2); 81 | _router.PublishEvent(_model4.Id, 4); 82 | receivedModels.Count.ShouldBe(2); 83 | receivedModels[0].IsClone.ShouldBe(true); 84 | receivedModels[1].IsClone.ShouldBe(true); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Esp.Net.Tests/RouterTests.ModelRouter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using Shouldly; 4 | 5 | namespace Esp.Net 6 | { 7 | public partial class RouterTests 8 | { 9 | public class ModelRouterCtor : RouterTests 10 | { 11 | [SetUp] 12 | public override void SetUp() 13 | { 14 | 15 | } 16 | 17 | [Test] 18 | public void CanOnlySetModelOnce() 19 | { 20 | var router = new Router(); 21 | router.SetModel(new TestModel()); 22 | Assert.Throws(() => { router.SetModel(new TestModel()); }); 23 | } 24 | 25 | [Test] 26 | public void ThrowsIfModelNotSet() 27 | { 28 | var router = new Router(); 29 | var ex = Assert.Throws(() => { router.PublishEvent("SomeEvent"); }); 30 | ex.Message.ShouldContain("Model not set. You must call router.SetModel(model) passing the model."); 31 | } 32 | } 33 | 34 | public class ModelRouter : RouterTests 35 | { 36 | private IRouter _modelRouter; 37 | 38 | [SetUp] 39 | public override void SetUp() 40 | { 41 | base.SetUp(); 42 | _modelRouter = new Router(_model1.Id, _router); 43 | } 44 | 45 | [Test] 46 | public void CanPublishAndObserveProxiedEvent() 47 | { 48 | var receivedEventCount = 0; 49 | _modelRouter.GetEventObservable().Observe((m, e, c) => receivedEventCount++); 50 | _modelRouter.PublishEvent(new Event1()); 51 | receivedEventCount.ShouldBe(1); 52 | } 53 | 54 | [Test] 55 | public void RouterCreateModelRouterRetunsModelRouter() 56 | { 57 | var model1 = new TestModel(); 58 | var receivedEventCount = 0; 59 | _router.AddModel(model1.Id, model1); 60 | IRouter modelRouter = _router.CreateModelRouter(model1.Id); 61 | modelRouter.GetEventObservable().Observe((m, e, c) => receivedEventCount++); 62 | modelRouter.PublishEvent(new Event1()); 63 | receivedEventCount.ShouldBe(1); 64 | } 65 | 66 | [Test] 67 | public void CanObserveProxiedModel() 68 | { 69 | var receivedModelCount = 0; 70 | _modelRouter.GetModelObservable().Observe(m => receivedModelCount++); 71 | _modelRouter.PublishEvent(new Event1()); 72 | receivedModelCount.ShouldBe(1); 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/RouterTests.ModelSubRouter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | using Shouldly; 5 | 6 | namespace Esp.Net 7 | { 8 | public partial class RouterTests 9 | { 10 | public class ModelSubRouter : RouterTests 11 | { 12 | private IRouter _modelRouter; 13 | 14 | [SetUp] 15 | public override void SetUp() 16 | { 17 | base.SetUp(); 18 | _modelRouter = new Router(_model1.Id, _router, m => m.SubTestModel); 19 | } 20 | 21 | [Test] 22 | public void CanPublishAndObserveProxiedEvent() 23 | { 24 | List> receivedSubModels = new List>(); 25 | _modelRouter.GetEventObservable().Observe((e, c, m) => receivedSubModels.Add(Tuple.Create(e, c, m))); 26 | _modelRouter.PublishEvent(new Event1()); 27 | receivedSubModels.Count.ShouldBe(1); 28 | receivedSubModels[0].Item3.ShouldBe(_model1.SubTestModel); 29 | } 30 | 31 | [Test] 32 | public void CanObserveProxiedModel() 33 | { 34 | var receivedModelCount = 0; 35 | _modelRouter.GetModelObservable().Observe(m => receivedModelCount++); 36 | _modelRouter.PublishEvent(new Event1()); 37 | receivedModelCount.ShouldBe(1); 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/RouterTests.RegisterModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | 4 | namespace Esp.Net 5 | { 6 | public partial class RouterTests 7 | { 8 | public class RegisterModel : RouterTests 9 | { 10 | [Test] 11 | public void ThrowsIfModelIdNull() 12 | { 13 | Assert.Throws(() => _router.AddModel(null, new object())); 14 | } 15 | 16 | [Test] 17 | public void ThrowsIfPreEventProcessorNull() 18 | { 19 | Assert.Throws(() => _router.AddModel(Guid.NewGuid(), new object(), (IPreEventProcessor)null)); 20 | } 21 | 22 | [Test] 23 | public void ThrowsIfPostEventProcessorNull() 24 | { 25 | Assert.Throws(() => _router.AddModel(Guid.NewGuid(), new object(), (IPostEventProcessor)null)); 26 | } 27 | 28 | [Test] 29 | public void ThrowsIfModelNull() 30 | { 31 | Assert.Throws(() => _router.AddModel(Guid.NewGuid(), (object)null)); 32 | } 33 | 34 | [Test] 35 | public void ThrowsIfModelAlreadyRegistered() 36 | { 37 | Assert.Throws(() => _router.AddModel(_model1.Id, new TestModel())); 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/RouterTests.RouterDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using Shouldly; 4 | 5 | namespace Esp.Net 6 | { 7 | public partial class RouterTests 8 | { 9 | public class RouterDispatcher : RouterTests 10 | { 11 | [SetUp] 12 | public override void SetUp() 13 | { 14 | base.SetUp(); 15 | _routerDispatcher.HasAccess = false; 16 | } 17 | 18 | [Test] 19 | public void ShouldDispatchRemoveCallViaDispatcher() 20 | { 21 | _router.RemoveModel(_model1.Id); 22 | _model1EventProcessor.Event1Details.NormalStage.StreamCompletedCount.ShouldBe(0); 23 | _routerDispatcher.InvokeDispatchedActions(1); 24 | _model1EventProcessor.Event1Details.NormalStage.StreamCompletedCount.ShouldBe(1); 25 | } 26 | 27 | [Test] 28 | public void ShouldDispatchPublishCallViaDispatcher() 29 | { 30 | _router.PublishEvent(_model1.Id, new Event1()); 31 | _model1EventProcessor.Event1Details.NormalStage.ReceivedEvents.Count.ShouldBe(0); 32 | _routerDispatcher.InvokeDispatchedActions(1); 33 | _model1EventProcessor.Event1Details.NormalStage.ReceivedEvents.Count.ShouldBe(1); 34 | } 35 | 36 | [Test] 37 | public void ShouldDispatchBroadcastEventCallViaDispatcher() 38 | { 39 | _router.BroadcastEvent(new Event1()); 40 | _model1EventProcessor.Event1Details.NormalStage.ReceivedEvents.Count.ShouldBe(0); 41 | _routerDispatcher.InvokeDispatchedActions(1); 42 | _model1EventProcessor.Event1Details.NormalStage.ReceivedEvents.Count.ShouldBe(1); 43 | } 44 | 45 | [Test] 46 | public void ShouldThrowIfExecuteEventCalledOnInvalidThread() 47 | { 48 | Assert.Throws(() => _router.ExecuteEvent(_model1.Id, new Event1())); 49 | } 50 | } 51 | 52 | public class DefaultRouterDispatcher : RouterTests 53 | { 54 | [SetUp] 55 | public override void SetUp() 56 | { 57 | _router = new Router(); // default ctor 58 | 59 | _model1 = new TestModel(); 60 | _router.AddModel(_model1.Id, _model1); 61 | _model1EventProcessor = new GenericModelEventProcessor(_router, _model1.Id, EventProcessor1Id); 62 | _model1Controller = new TestModelController(_router, _model1.Id); 63 | } 64 | 65 | [Test] 66 | public void ShouldDispatchRemoveOnCurrentThread() 67 | { 68 | _router.RemoveModel(_model1.Id); 69 | _model1EventProcessor.Event1Details.NormalStage.StreamCompletedCount.ShouldBe(1); 70 | } 71 | 72 | [Test] 73 | public void ShouldDispatchPublishCallOnCurrentThread() 74 | { 75 | _router.PublishEvent(_model1.Id, new Event1()); 76 | _model1EventProcessor.Event1Details.NormalStage.ReceivedEvents.Count.ShouldBe(1); 77 | } 78 | 79 | [Test] 80 | public void ShouldDispatchBroadcastEventCallOnCurrentThread() 81 | { 82 | _router.BroadcastEvent(new Event1()); 83 | _model1EventProcessor.Event1Details.NormalStage.ReceivedEvents.Count.ShouldBe(1); 84 | } 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/RouterTests.RunAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive.Linq; 3 | using System.Threading; 4 | using Microsoft.Reactive.Testing; 5 | using NUnit.Framework; 6 | using Shouldly; 7 | 8 | namespace Esp.Net 9 | { 10 | public partial class RouterTests 11 | { 12 | public class ErrorRunActionFlows : RouterTests 13 | { 14 | [Test] 15 | public void CancelingAtNormalObservationStageThrows() 16 | { 17 | var event1 = new Event1 18 | { 19 | ShouldCancel = true, 20 | CancelAtStage = ObservationStage.Normal, 21 | CancelAtEventProcesserId = EventProcessor1Id, 22 | }; 23 | _router.PublishEvent(_model1.Id, event1); 24 | _terminalErrorHandler.Errors.Count.ShouldBe(1); 25 | _terminalErrorHandler.Errors[0].ShouldBeOfType(); 26 | } 27 | 28 | [Test] 29 | public void RunningAnActionPushesAModelUpdate() 30 | { 31 | bool wasRun = false; 32 | _router.RunAction(_model1.Id, () => 33 | { 34 | wasRun = true; 35 | }); 36 | _model1Controller.ReceivedModels.Count.ShouldBe(1); 37 | _model2Controller.ReceivedModels.Count.ShouldBe(0); 38 | wasRun.ShouldBe(true); 39 | } 40 | 41 | [Test] 42 | public void RunningAnActionReceivesModel() 43 | { 44 | bool wasRun = false, modelCorrect = false; 45 | 46 | _router.RunAction(_model1.Id, model => 47 | { 48 | wasRun = true; 49 | modelCorrect = model.Id == _model1.Id; 50 | }); 51 | _model1Controller.ReceivedModels.Count.ShouldBe(1); 52 | _model2Controller.ReceivedModels.Count.ShouldBe(0); 53 | modelCorrect.ShouldBe(true); 54 | } 55 | 56 | [Test] 57 | public void RunningAnActionRunsAction() 58 | { 59 | int action1RunCount = 0, action2RunCount = 0; 60 | bool modelUpdated = false; 61 | var testScheduler = new TestScheduler(); 62 | var router = new Router(new TestModel()); 63 | router.GetEventObservable().Observe((m, e) => 64 | { 65 | var observable = Observable.Timer(TimeSpan.FromSeconds(1), testScheduler); 66 | observable.Subscribe(i => 67 | { 68 | router.RunAction(() => 69 | { 70 | action1RunCount++; 71 | }); 72 | router.RunAction((model) => 73 | { 74 | action2RunCount++; 75 | model.Count++; 76 | }); 77 | }); 78 | }); 79 | router.GetModelObservable().Observe(m => 80 | { 81 | Console.WriteLine(m.Count); 82 | modelUpdated = m.Count == 1 && m.Version == 3; 83 | }); 84 | router.PublishEvent(1); 85 | testScheduler.AdvanceBy(TimeSpan.FromSeconds(1).Ticks); 86 | action1RunCount.ShouldBe(1); 87 | action2RunCount.ShouldBe(1); 88 | modelUpdated.ShouldBe(true); 89 | } 90 | 91 | public class TestModel : IPreEventProcessor 92 | { 93 | public int Version { get; set; } 94 | 95 | public int Count { get; set; } 96 | 97 | public void Process() 98 | { 99 | Version++; 100 | } 101 | } 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/Rx/RxSubscribeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reactive.Concurrency; 4 | using System.Reactive.Linq; 5 | using Microsoft.Reactive.Testing; 6 | using NUnit.Framework; 7 | using Shouldly; 8 | 9 | namespace Esp.Net.Rx 10 | { 11 | [TestFixture] 12 | public class RxSubscribeTests 13 | { 14 | private Router _router; 15 | private TestModel _model; 16 | private TestScheduler _testScheduler; 17 | 18 | [SetUp] 19 | public void SetUp() 20 | { 21 | // need to solve this chicken and egg problem : creating the router with a model which takes a router 22 | // might have to make the router take a factory 23 | 24 | _testScheduler = new TestScheduler(); 25 | var router = new Router(); 26 | _router = new Router("modelId", router); 27 | _model = new TestModel(_router, _testScheduler); 28 | router.AddModel("modelId", _model); 29 | } 30 | 31 | [Test] 32 | public void ObserverInvokedOnRouterDispatchLoop() 33 | { 34 | _model.ObserveEvents(); 35 | bool modelUpdated = false; 36 | _router.GetModelObservable().Observe(m => 37 | { 38 | modelUpdated = m.ReceivedInts.Count == 1 && m.Version == 2; 39 | }); 40 | _router.PublishEvent(new InitialEvent()); 41 | _testScheduler.AdvanceBy(1); 42 | _model.ReceivedInts.Count.ShouldBe(1); 43 | _model.ReceivedInts[0].ShouldBe(0); 44 | modelUpdated.ShouldBe(true); 45 | } 46 | 47 | public class TestModel : DisposableBase, IPreEventProcessor 48 | { 49 | private readonly IRouter _rouer; 50 | private readonly IScheduler _rxScheduler; 51 | 52 | public TestModel(IRouter rouer, IScheduler rxScheduler) 53 | { 54 | _rouer = rouer; 55 | _rxScheduler = rxScheduler; 56 | ReceivedInts = new List(); 57 | Id = Guid.NewGuid(); 58 | } 59 | 60 | public void ObserveEvents() 61 | { 62 | _rouer.ObserveEventsOn(this); 63 | } 64 | 65 | public Guid Id { get; private set; } 66 | 67 | public int Version { get; private set; } 68 | 69 | public List ReceivedInts { get; set; } 70 | 71 | [ObserveEvent(typeof(InitialEvent))] 72 | private void ObserveIntEvent() 73 | { 74 | AddDisposable( 75 | Observable 76 | .Timer(TimeSpan.FromTicks(1), _rxScheduler) 77 | .Do(_ => 78 | { 79 | int i = 0; 80 | }) 81 | .SubscribeWithRouter( 82 | _rouer, 83 | i => 84 | { 85 | // on the dispatch loop, updating private state is ok 86 | ReceivedInts.Add(i); 87 | } 88 | ) 89 | ); 90 | } 91 | 92 | void IPreEventProcessor.Process() 93 | { 94 | Version++; 95 | } 96 | } 97 | 98 | public class InitialEvent { } 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Esp.Net.Tests/StubRouterDispatcher.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | 20 | namespace Esp.Net 21 | { 22 | public class StubRouterDispatcher : IRouterDispatcher 23 | { 24 | private readonly List _actions = new List(); 25 | 26 | public StubRouterDispatcher() 27 | { 28 | HasAccess = true; 29 | } 30 | 31 | public bool HasAccess { get; set; } 32 | 33 | public bool IsDisposed { get; set; } 34 | 35 | public int QueuedActionCount { get { return _actions.Count; } } 36 | 37 | public bool CheckAccess() 38 | { 39 | ThrowIfDisposed(); 40 | return HasAccess; 41 | } 42 | 43 | public void EnsureAccess() 44 | { 45 | ThrowIfDisposed(); 46 | if (!HasAccess) throw new InvalidOperationException("Invalid access"); 47 | } 48 | 49 | public void Dispatch(Action action) 50 | { 51 | ThrowIfDisposed(); 52 | _actions.Add(action); 53 | } 54 | 55 | public void Dispose() 56 | { 57 | IsDisposed = true; 58 | } 59 | 60 | private void ThrowIfDisposed() 61 | { 62 | if(IsDisposed) throw new ObjectDisposedException(string.Empty); 63 | } 64 | 65 | public void InvokeDispatchedActions(int numberToInvoke) 66 | { 67 | var oldHasAccess = HasAccess; 68 | HasAccess = true; 69 | for (int i = 0; i < numberToInvoke || i < _actions.Count - 1; i++) 70 | { 71 | _actions[i](); 72 | } 73 | HasAccess = oldHasAccess; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/Utils/ReflectionHelperTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | 4 | namespace Esp.Net.Utils 5 | { 6 | [TestFixture] 7 | public class ReflectionHelperTests 8 | { 9 | public class Event { } 10 | public class Event_ { } 11 | 12 | public class Event1 : Event { } 13 | public class Event2 : Event1 { } 14 | public class Event3 : Event2 { } 15 | public class EventA : Event { } 16 | public class Event__ : Event_ { } 17 | public class Event___ : Event__ { } 18 | 19 | [Test] 20 | public void TryGetCommonBaseType_WhenTypesShareCommonTypeItIsReturned() 21 | { 22 | Type commonType; 23 | bool hasCommonType = ReflectionHelper.TryGetCommonBaseType(out commonType, typeof (Event1), typeof (EventA)); 24 | Assert.AreEqual(typeof(Event), commonType); 25 | Assert.IsTrue(hasCommonType); 26 | 27 | hasCommonType = ReflectionHelper.TryGetCommonBaseType(out commonType, typeof(Event1), typeof(EventA), typeof(Event3)); 28 | Assert.AreEqual(typeof(Event), commonType); 29 | Assert.IsTrue(hasCommonType); 30 | } 31 | 32 | [Test] 33 | public void TryGetCommonBaseType_WhenTypesDoNotShareCommonTypeNullIsReturned() 34 | { 35 | Type commonType; 36 | bool hasCommonType = ReflectionHelper.TryGetCommonBaseType(out commonType, typeof(Event__), typeof(Event3)); 37 | Assert.IsNull(commonType); 38 | Assert.IsFalse(hasCommonType); 39 | } 40 | 41 | [Test] 42 | public void SharesBaseType_WhenTypesShareCommonTypeReturnsTrue() 43 | { 44 | bool sharesCommonType = ReflectionHelper.SharesBaseType(typeof(Event), typeof (Event1), typeof (EventA)); 45 | Assert.IsTrue(sharesCommonType); 46 | } 47 | 48 | [Test] 49 | public void SharesBaseType_WhenTypesDoNotShareCommonTypeReturnsFalse() 50 | { 51 | bool sharesCommonType = ReflectionHelper.SharesBaseType(typeof(Event), typeof(Event__), typeof(Event3)); 52 | Assert.IsFalse(sharesCommonType); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/Esp.Net.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Esp.Net.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Esp.Net.Tests", "Esp.Net.Tests\Esp.Net.Tests.csproj", "{4C8F25D6-B592-489F-918C-51ACF3C595F8}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Esp.Net", "Esp.Net\Esp.Net.csproj", "{2C08EEBF-99F4-43AD-B87F-2B90DEB6F8DE}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EF0A7DF3-F983-47A7-A9A5-D1C2C876AFF4}" 11 | ProjectSection(SolutionItems) = preProject 12 | ..\appveyor.yml = ..\appveyor.yml 13 | ..\README.md = ..\README.md 14 | EndProjectSection 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Esp.Net.Rx", "Esp.Net.Rx\Esp.Net.Rx.csproj", "{E9844144-0F47-40D3-BC35-6135AF535732}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Esp.Net.Dispatchers", "Esp.Net.Dispatchers\Esp.Net.Dispatchers.csproj", "{769ECEB6-57D8-47B9-8E5F-651F003F609A}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {4C8F25D6-B592-489F-918C-51ACF3C595F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {4C8F25D6-B592-489F-918C-51ACF3C595F8}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {4C8F25D6-B592-489F-918C-51ACF3C595F8}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {4C8F25D6-B592-489F-918C-51ACF3C595F8}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {2C08EEBF-99F4-43AD-B87F-2B90DEB6F8DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {2C08EEBF-99F4-43AD-B87F-2B90DEB6F8DE}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {2C08EEBF-99F4-43AD-B87F-2B90DEB6F8DE}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {2C08EEBF-99F4-43AD-B87F-2B90DEB6F8DE}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {E9844144-0F47-40D3-BC35-6135AF535732}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {E9844144-0F47-40D3-BC35-6135AF535732}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {E9844144-0F47-40D3-BC35-6135AF535732}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {E9844144-0F47-40D3-BC35-6135AF535732}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {769ECEB6-57D8-47B9-8E5F-651F003F609A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {769ECEB6-57D8-47B9-8E5F-651F003F609A}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {769ECEB6-57D8-47B9-8E5F-651F003F609A}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {769ECEB6-57D8-47B9-8E5F-651F003F609A}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /src/Esp.Net/CurrentThreadDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace Esp.Net 5 | { 6 | public class CurrentThreadDispatcher : IRouterDispatcher 7 | { 8 | private readonly int _threadId; 9 | private bool _isDisposed; 10 | 11 | public CurrentThreadDispatcher() 12 | { 13 | _threadId = Thread.CurrentThread.ManagedThreadId; 14 | } 15 | 16 | public void Dispose() 17 | { 18 | EnsureAccess(); 19 | if (!_isDisposed) _isDisposed = true; 20 | } 21 | 22 | public bool CheckAccess() 23 | { 24 | return Thread.CurrentThread.ManagedThreadId == _threadId; 25 | } 26 | 27 | public void EnsureAccess() 28 | { 29 | if (!CheckAccess()) 30 | { 31 | throw new InvalidOperationException("Router accessed on invalid thread"); 32 | } 33 | } 34 | 35 | public void Dispatch(Action action) 36 | { 37 | if (!CheckAccess()) 38 | throw new InvalidOperationException( 39 | string.Format( 40 | "The dispatcher [{0}] can not marshal a dispatch call onto the thread with id {1}. If you want to access the router from any thread uses a dispatcher that supports multi threaded applications. Alternatively ensure that you're always on the same thread that created the Router (which his thread with id {1})", 41 | GetType().FullName, 42 | _threadId 43 | ) 44 | ); 45 | 46 | if (!_isDisposed) 47 | { 48 | action(); 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/Esp.Net/Disposables/CollectionDisposable.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | 20 | // ReSharper disable once CheckNamespace 21 | namespace Esp.Net 22 | { 23 | public class CollectionDisposable : IDisposable 24 | { 25 | private readonly List _disposables; 26 | 27 | public CollectionDisposable(params IDisposable[] disposables) 28 | { 29 | _disposables = new List(disposables); 30 | } 31 | 32 | public bool IsDisposed { get; private set; } 33 | 34 | public void Add(IDisposable disposable) 35 | { 36 | if (IsDisposed) 37 | { 38 | disposable.Dispose(); 39 | return; 40 | } 41 | _disposables.Add(disposable); 42 | } 43 | 44 | public void Dispose() 45 | { 46 | if(IsDisposed) return; 47 | IsDisposed = true; 48 | foreach (IDisposable disposable in _disposables) 49 | { 50 | disposable.Dispose(); 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/Esp.Net/Disposables/DictionaryDisposable.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | 20 | // ReSharper disable once CheckNamespace 21 | namespace Esp.Net 22 | { 23 | public class DictionaryDisposable : IDisposable 24 | { 25 | private readonly Dictionary _disposables = new Dictionary(); 26 | 27 | public bool IsDisposed { get; private set; } 28 | 29 | public void Add(TKey key, IDisposable disposable) 30 | { 31 | if (IsDisposed) 32 | { 33 | disposable.Dispose(); 34 | return; 35 | } 36 | _disposables.Add(key, disposable); 37 | } 38 | 39 | public bool Remove(TKey key) 40 | { 41 | return _disposables.Remove(key); 42 | } 43 | 44 | public void Dispose() 45 | { 46 | if(IsDisposed) return; 47 | IsDisposed = true; 48 | 49 | foreach (IDisposable disposable in _disposables.Values) 50 | { 51 | disposable.Dispose(); 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/Esp.Net/Disposables/DisposableBase.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | using System; 17 | 18 | // ReSharper disable once CheckNamespace 19 | namespace Esp.Net 20 | { 21 | public abstract class DisposableBase : IDisposable 22 | { 23 | private readonly CollectionDisposable _disposables = new CollectionDisposable(); 24 | 25 | public void AddDisposable(IDisposable disposable) 26 | { 27 | _disposables.Add(disposable); 28 | } 29 | 30 | public void Dispose() 31 | { 32 | _disposables.Dispose(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Esp.Net/Disposables/EspDisposable.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | // ReSharper disable once CheckNamespace 20 | namespace Esp.Net 21 | { 22 | public class EspDisposable : IDisposable 23 | { 24 | public static IDisposable Empty { get; private set; } 25 | 26 | static EspDisposable() 27 | { 28 | Empty = new EspDisposable(() => { /* Noop*/ }); 29 | } 30 | public bool IsDisposed { get; private set; } 31 | 32 | public static IDisposable Create(Action action) 33 | { 34 | return new EspDisposable(action); 35 | } 36 | 37 | private readonly Action _action; 38 | 39 | private EspDisposable(Action action) 40 | { 41 | if (action == null) 42 | { 43 | throw new ArgumentNullException("action", "Action must not be null."); 44 | } 45 | _action = action; 46 | } 47 | 48 | public void Dispose() 49 | { 50 | if (IsDisposed) return; 51 | IsDisposed = true; 52 | _action(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/Esp.Net/Disposables/EspSerialDisposable.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | // ReSharper disable once CheckNamespace 20 | namespace Esp.Net 21 | { 22 | public class EspSerialDisposable : IDisposable 23 | { 24 | private bool _isDisposed; 25 | 26 | private IDisposable _disposable; 27 | 28 | public IDisposable Disposable 29 | { 30 | get { return _disposable; } 31 | set 32 | { 33 | using (_disposable) { } 34 | if (_isDisposed) 35 | using (value) { } 36 | else 37 | _disposable = value; 38 | } 39 | } 40 | 41 | public void Dispose() 42 | { 43 | if (_isDisposed) return; 44 | _isDisposed = true; 45 | using (_disposable) { } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/Esp.Net/Esp.Net.SourcePackage.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | esp-net-source 5 | $version$ 6 | esp-net-source 7 | Dev Shop Limited 8 | Dev Shop Limited 9 | https://github.com/esp/esp-net/LICENSE.txt 10 | https://github.com/esp/esp-net 11 | https://raw.github.com/esp/esp-net/master/package_icon.png 12 | false 13 | Source Only Package - Evented State Processor (ESP) adds specific processing workflow around changes to a model's state 14 | Copyright Dev Shop Limited 2015 15 | evented state processor reactive router event loop 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Esp.Net/Esp.Net.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | esp-net 5 | $version$ 6 | esp-net 7 | Dev Shop Limited 8 | Dev Shop Limited 9 | https://github.com/esp/esp-net/LICENSE.txt 10 | https://github.com/esp/esp-net 11 | https://raw.github.com/esp/esp-net/master/package_icon.png 12 | false 13 | Evented State Processor (ESP) adds specific processing workflow around changes to a model's state 14 | Copyright Dev Shop Limited 2015 15 | evented state processor reactive router event loop 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Esp.Net/EventContext.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | namespace Esp.Net 20 | { 21 | internal class EventContext : IEventContext 22 | { 23 | private bool _isCanceled; 24 | private bool _isCommitted; 25 | 26 | public ObservationStage CurrentStage { get; set; } 27 | 28 | public bool IsCanceled 29 | { 30 | get { return _isCanceled; } 31 | } 32 | 33 | public bool IsCommitted 34 | { 35 | get { return _isCommitted; } 36 | } 37 | 38 | public void Cancel() 39 | { 40 | if(_isCanceled) throw new Exception("Already canceled"); 41 | _isCanceled = true; 42 | } 43 | 44 | public void Commit() 45 | { 46 | if (_isCommitted) throw new Exception("Already committed"); 47 | _isCommitted = true; 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/Esp.Net/HeldEvents/HeldEventAction.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | // ReSharper disable once CheckNamespace 18 | namespace Esp.Net 19 | { 20 | public enum HeldEventAction 21 | { 22 | Release, 23 | Ignore 24 | } 25 | } -------------------------------------------------------------------------------- /src/Esp.Net/HeldEvents/HeldEventActionEvent.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | // ReSharper disable once CheckNamespace 19 | namespace Esp.Net 20 | { 21 | public class HeldEventActionEvent 22 | { 23 | public HeldEventActionEvent(Guid eventId, HeldEventAction action) 24 | { 25 | Action = action; 26 | EventId = eventId; 27 | } 28 | 29 | public HeldEventAction Action { get; private set; } 30 | 31 | public Guid EventId { get; private set; } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Esp.Net/HeldEvents/IEventDescription.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | // ReSharper disable once CheckNamespace 19 | namespace Esp.Net 20 | { 21 | public interface IEventDescription 22 | { 23 | Guid EventId { get; } 24 | string Category { get; } 25 | string Description { get; } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Esp.Net/HeldEvents/IEventHoldingStrategy.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | // ReSharper disable once CheckNamespace 18 | namespace Esp.Net 19 | { 20 | public interface IEventHoldingStrategy where TEvent : IIdentifiableEvent 21 | { 22 | bool ShouldHold(TEvent @event, IEventContext context, TModel model); 23 | IEventDescription GetEventDescription(TEvent @event, TModel model); 24 | } 25 | 26 | public interface IEventHoldingStrategy : IEventHoldingStrategy 27 | where TEvent : IIdentifiableEvent, TBaseEvent 28 | { 29 | } 30 | } -------------------------------------------------------------------------------- /src/Esp.Net/HeldEvents/IHeldEventStore.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | // ReSharper disable once CheckNamespace 18 | namespace Esp.Net 19 | { 20 | public interface IHeldEventStore 21 | { 22 | void AddHeldEventDescription(IEventDescription description); 23 | void RemoveHeldEventDescription(IEventDescription description); 24 | } 25 | } -------------------------------------------------------------------------------- /src/Esp.Net/IClonable.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | namespace Esp.Net 18 | { 19 | public interface ICloneable 20 | { 21 | T Clone(); 22 | } 23 | } -------------------------------------------------------------------------------- /src/Esp.Net/IEventContext.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | namespace Esp.Net 17 | { 18 | public interface IEventContext 19 | { 20 | ObservationStage CurrentStage { get; } 21 | bool IsCanceled { get; } 22 | bool IsCommitted { get; } 23 | void Cancel(); 24 | void Commit(); 25 | } 26 | } -------------------------------------------------------------------------------- /src/Esp.Net/IIdentifiableEvent.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | namespace Esp.Net 20 | { 21 | public interface IIdentifiableEvent 22 | { 23 | Guid Id { get; } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Esp.Net/IPostEventProcessor.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | namespace Esp.Net 18 | { 19 | public interface IPostEventProcessor 20 | { 21 | void Process(TModel model); 22 | } 23 | 24 | public interface IPostEventProcessor 25 | { 26 | void Process(); 27 | } 28 | } -------------------------------------------------------------------------------- /src/Esp.Net/IPreEventProcessor.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | namespace Esp.Net 17 | { 18 | public interface IPreEventProcessor 19 | { 20 | void Process(TModel model); 21 | } 22 | 23 | public interface IPreEventProcessor 24 | { 25 | void Process(); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Esp.Net/IRouter.`1.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | namespace Esp.Net 20 | { 21 | public interface IRouter 22 | { 23 | IModelObservable GetModelObservable(); 24 | 25 | IEventObservable GetEventObservable(ObservationStage observationStage = ObservationStage.Normal); 26 | 27 | void PublishEvent(TEvent @event); 28 | void PublishEvent(object @event); 29 | 30 | void ExecuteEvent(TEvent @event); 31 | void ExecuteEvent(object @event); 32 | 33 | void RunAction(Action action); 34 | void RunAction(Action action); 35 | } 36 | } -------------------------------------------------------------------------------- /src/Esp.Net/IRouter.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | namespace Esp.Net 20 | { 21 | public interface IRouter 22 | { 23 | void AddModel(object modelId, TModel model); 24 | void AddModel(object modelId, TModel model, IPreEventProcessor preEventProcessor); 25 | void AddModel(object modelId, TModel model, IPostEventProcessor postEventProcessor); 26 | void AddModel(object modelId, TModel model, IPreEventProcessor preEventProcessor, IPostEventProcessor postEventProcessor); 27 | void RemoveModel(object modelId); 28 | IRouter CreateModelRouter(object modelId); 29 | 30 | IModelObservable GetModelObservable(object modelId); 31 | 32 | IEventObservable GetEventObservable(object modelId, ObservationStage observationStage = ObservationStage.Normal); 33 | 34 | void PublishEvent(object modelId, TEvent @event); 35 | void PublishEvent(object modelId, object @event); 36 | 37 | void ExecuteEvent(object modelId, TEvent @event); 38 | void ExecuteEvent(object modelId, object @event); 39 | 40 | void BroadcastEvent(TEvent @event); 41 | void BroadcastEvent(object @event); 42 | 43 | void RunAction(object modelId, Action action); 44 | void RunAction(object modelId, Action action); 45 | } 46 | } -------------------------------------------------------------------------------- /src/Esp.Net/IRouterDispatcher.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | namespace Esp.Net 20 | { 21 | public interface IRouterDispatcher : IDisposable 22 | { 23 | bool CheckAccess(); 24 | void EnsureAccess(); 25 | void Dispatch(Action action); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Esp.Net/ITerminalErrorHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esp.Net 4 | { 5 | public interface ITerminalErrorHandler 6 | { 7 | void OnError(Exception exception); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Esp.Net/Meta/EventObservations.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | using System; 17 | 18 | namespace Esp.Net.Meta 19 | { 20 | public class EventObservations 21 | { 22 | public EventObservations(Type eventType) 23 | { 24 | EventType = eventType; 25 | } 26 | 27 | public EventObservations(Type eventType, int numberOfObservers) 28 | { 29 | EventType = eventType; 30 | NumberOfObservers = numberOfObservers; 31 | } 32 | 33 | public Type EventType { get; private set; } 34 | 35 | public int NumberOfObservers { get; internal set; } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Esp.Net/Meta/IEventObservationRegistrar.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | namespace Esp.Net.Meta 18 | { 19 | internal interface IEventObservationRegistrar 20 | { 21 | void IncrementRegistration(); 22 | void DecrementRegistration(); 23 | } 24 | } -------------------------------------------------------------------------------- /src/Esp.Net/Meta/IEventsObservationRegistrar.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | using System; 17 | using System.Collections.Generic; 18 | 19 | namespace Esp.Net.Meta 20 | { 21 | public interface IEventsObservationRegistrar 22 | { 23 | int GetEventObservationCount(object modelId); 24 | int GetEventObservationCount(object modelId, Type eventType); 25 | IList GetEventObservations(object modelId); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Esp.Net/Meta/ModelEventObservations.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Collections.ObjectModel; 19 | using System.Linq; 20 | 21 | namespace Esp.Net.Meta 22 | { 23 | internal class ModelEventObservations 24 | { 25 | private readonly Dictionary _eventObservations = new Dictionary(); 26 | private readonly object _gate = new object(); 27 | 28 | public void IncrementRegistration() 29 | { 30 | lock (_gate) 31 | { 32 | EventObservations eventObservations = GetEventObservations(typeof (TEventType)); 33 | eventObservations.NumberOfObservers++; 34 | } 35 | } 36 | 37 | public void DecrementRegistration() 38 | { 39 | lock (_gate) 40 | { 41 | EventObservations eventObservations = GetEventObservations(typeof (TEventType)); 42 | eventObservations.NumberOfObservers--; 43 | } 44 | } 45 | 46 | public int GetEventObservationCount() 47 | { 48 | lock (_gate) 49 | return GetEventObservationCount(typeof(TEventType)); 50 | } 51 | 52 | public int GetEventObservationCount(Type eventType) 53 | { 54 | lock (_gate) 55 | { 56 | EventObservations eventObservations = GetEventObservations(eventType); 57 | return eventObservations.NumberOfObservers; 58 | } 59 | } 60 | 61 | public IList GetEventObservations() 62 | { 63 | List results; 64 | lock (_gate) 65 | { 66 | results = _eventObservations 67 | .Values 68 | .Select(v => new EventObservations(v.EventType, v.NumberOfObservers)) 69 | .ToList(); 70 | } 71 | return new ReadOnlyCollection(results); 72 | } 73 | 74 | private EventObservations GetEventObservations(Type eventType) 75 | { 76 | EventObservations eventObservations; 77 | if (!_eventObservations.TryGetValue(eventType, out eventObservations)) 78 | { 79 | eventObservations = new EventObservations(eventType); 80 | _eventObservations.Add(eventType, eventObservations); 81 | } 82 | return eventObservations; 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /src/Esp.Net/Meta/ModelsEventsObservations.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | 20 | namespace Esp.Net.Meta 21 | { 22 | internal class ModelsEventsObservations : IEventsObservationRegistrar 23 | { 24 | private readonly Dictionary _modelRegistries; 25 | private readonly object _gate = new object(); 26 | 27 | public ModelsEventsObservations() 28 | { 29 | _modelRegistries = new Dictionary(); 30 | } 31 | 32 | public void IncrementRegistration(object modelId) 33 | { 34 | ModelEventObservations eventObservations = GetEventRegistrations(modelId); 35 | eventObservations.IncrementRegistration(); 36 | } 37 | 38 | public void DecrementRegistration(object modelId) 39 | { 40 | ModelEventObservations eventObservations = GetEventRegistrations(modelId); 41 | eventObservations.DecrementRegistration(); 42 | } 43 | 44 | int IEventsObservationRegistrar.GetEventObservationCount(object modelId) 45 | { 46 | ModelEventObservations eventObservations = GetEventRegistrations(modelId); 47 | return eventObservations.GetEventObservationCount(); 48 | } 49 | 50 | int IEventsObservationRegistrar.GetEventObservationCount(object modelId, Type eventType) 51 | { 52 | ModelEventObservations eventObservations = GetEventRegistrations(modelId); 53 | return eventObservations.GetEventObservationCount(eventType); 54 | } 55 | 56 | IList IEventsObservationRegistrar.GetEventObservations(object modelId) 57 | { 58 | ModelEventObservations eventObservations = GetEventRegistrations(modelId); 59 | return eventObservations.GetEventObservations(); 60 | } 61 | 62 | private ModelEventObservations GetEventRegistrations(object modelId) 63 | { 64 | ModelEventObservations eventObservations; 65 | lock (_gate) 66 | { 67 | if (!_modelRegistries.TryGetValue(modelId, out eventObservations)) 68 | { 69 | eventObservations = new ModelEventObservations(); 70 | _modelRegistries.Add(modelId, eventObservations); 71 | } 72 | } 73 | return eventObservations; 74 | } 75 | 76 | internal IEventObservationRegistrar CreateForModel(object modelId) 77 | { 78 | return new ModelEventObservationRegistrar(modelId, this); 79 | } 80 | 81 | private class ModelEventObservationRegistrar : IEventObservationRegistrar 82 | { 83 | private readonly object _modelId; 84 | private readonly ModelsEventsObservations _parent; 85 | 86 | public ModelEventObservationRegistrar(object modelId, ModelsEventsObservations parent) 87 | { 88 | _modelId = modelId; 89 | _parent = parent; 90 | } 91 | 92 | public void IncrementRegistration() 93 | { 94 | _parent.IncrementRegistration(_modelId); 95 | } 96 | 97 | public void DecrementRegistration() 98 | { 99 | _parent.DecrementRegistration(_modelId); 100 | } 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /src/Esp.Net/ModelChangedEvent.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | namespace Esp.Net 17 | { 18 | public class ModelChangedEvent 19 | { 20 | public ModelChangedEvent(object modelId, TModel model) 21 | { 22 | Model = model; 23 | ModelId = modelId; 24 | } 25 | 26 | public TModel Model { get; private set; } 27 | 28 | public object ModelId { get; private set; } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Esp.Net/ObservationStage.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | namespace Esp.Net 18 | { 19 | public enum ObservationStage 20 | { 21 | Preview, 22 | Normal, 23 | Committed 24 | } 25 | } -------------------------------------------------------------------------------- /src/Esp.Net/ObserveEventAttribute.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | using System; 17 | 18 | namespace Esp.Net 19 | { 20 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 21 | public class ObserveEventAttribute : Attribute 22 | { 23 | public ObserveEventAttribute(Type eventType) : this(eventType, ObservationStage.Normal) 24 | { 25 | } 26 | 27 | public ObserveEventAttribute(Type eventType, ObservationStage stage) 28 | { 29 | EventType = eventType; 30 | Stage = stage; 31 | } 32 | 33 | public Type EventType { get; private set; } 34 | 35 | public ObservationStage Stage { get; private set; } 36 | 37 | public override string ToString() 38 | { 39 | return string.Format("Observe event [{0}] at stage [{1}]", EventType.FullName, Stage); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Esp.Net/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System.Reflection; 18 | using System.Runtime.CompilerServices; 19 | 20 | // General Information about an assembly is controlled through the following 21 | // set of attributes. Change these attribute values to modify the information 22 | // associated with an assembly. 23 | [assembly: AssemblyTitle("Esp.Net")] 24 | [assembly: AssemblyDescription("")] 25 | [assembly: AssemblyConfiguration("")] 26 | 27 | [assembly: InternalsVisibleTo("Esp.Net.Tests")] 28 | [assembly: InternalsVisibleTo("Esp.Net.Rx")] -------------------------------------------------------------------------------- /src/Esp.Net/Reactive/EventObserver.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | // ReSharper disable once CheckNamespace 20 | namespace Esp.Net 21 | { 22 | public interface IEventObserver 23 | { 24 | void OnNext(TEvent @event, TContext context, TModel model); 25 | void OnCompleted(); 26 | } 27 | 28 | internal class EventObserver : IEventObserver 29 | { 30 | private readonly Action _onCompleted; 31 | private readonly Action _onNext; 32 | 33 | public EventObserver(Action onNext) 34 | : this(onNext, null) 35 | { 36 | } 37 | 38 | public EventObserver(Action onNext, Action onCompleted) 39 | : this((e, c, m) => onNext(e), onCompleted) 40 | { 41 | } 42 | 43 | public EventObserver(Action onNext) 44 | : this(onNext, null) 45 | { 46 | } 47 | 48 | public EventObserver(Action onNext, Action onCompleted) 49 | : this((e, c, m) => onNext(e, c), onCompleted) 50 | { 51 | } 52 | 53 | public EventObserver(Action onNext) 54 | : this(onNext, null) 55 | { 56 | } 57 | 58 | public EventObserver(Action onNext, Action onCompleted) 59 | : this((e, c, m) => onNext(e, m), onCompleted) 60 | { 61 | } 62 | 63 | public EventObserver(Action onNext) 64 | : this(onNext, null) 65 | { 66 | } 67 | 68 | public EventObserver(Action onNext, Action onCompleted) 69 | { 70 | _onNext = onNext; 71 | _onCompleted = onCompleted; 72 | } 73 | 74 | public void OnNext(TEvent @event, TContext context, TModel model) 75 | { 76 | _onNext(@event, context, model); 77 | } 78 | 79 | public void OnCompleted() 80 | { 81 | if (_onCompleted != null) _onCompleted(); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /src/Esp.Net/Reactive/ModelObserver.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | // ReSharper disable once CheckNamespace 20 | namespace Esp.Net 21 | { 22 | public interface IModelObserver 23 | { 24 | void OnNext(T item); 25 | void OnCompleted(); 26 | } 27 | 28 | internal class ModelObserver : IModelObserver 29 | { 30 | private readonly Action _onNext; 31 | private readonly Action _onCompleted; 32 | 33 | public ModelObserver(Action onNext) 34 | : this(onNext, null) 35 | { 36 | } 37 | 38 | public ModelObserver(Action onNext, Action onCompleted) 39 | { 40 | _onNext = onNext; 41 | _onCompleted = onCompleted; 42 | } 43 | 44 | public void OnNext(T item) 45 | { 46 | _onNext(item); 47 | } 48 | 49 | public void OnCompleted() 50 | { 51 | if (_onCompleted != null) _onCompleted(); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/Esp.Net/Reactive/ModelSubject.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | 20 | // ReSharper disable once CheckNamespace 21 | namespace Esp.Net 22 | { 23 | internal class ModelSubject : IModelObservable, IModelObserver 24 | { 25 | readonly List> _observers = new List>(); 26 | private readonly object _gate = new object(); 27 | private bool _hasCompleted = false; 28 | private T _lastValue = default(T); 29 | private bool _lastValueSet = false; 30 | 31 | public void OnNext(T item) 32 | { 33 | _lastValue = item; 34 | _lastValueSet = true; 35 | var observers = _observers.ToArray(); 36 | foreach(var observer in observers) 37 | { 38 | if (_hasCompleted) break; 39 | observer.OnNext(item); 40 | } 41 | } 42 | 43 | public void OnCompleted() 44 | { 45 | if (!_hasCompleted) 46 | { 47 | _hasCompleted = true; 48 | var observers = _observers.ToArray(); 49 | foreach (var observer in observers) 50 | { 51 | observer.OnCompleted(); 52 | } 53 | } 54 | } 55 | 56 | public IDisposable Observe(Action onNext) 57 | { 58 | var observer = new ModelObserver(onNext); 59 | return Observe(observer); 60 | } 61 | 62 | public IDisposable Observe(Action onNext, Action onCompleted) 63 | { 64 | var observer = new ModelObserver(onNext, onCompleted); 65 | return Observe(observer); 66 | } 67 | 68 | public IDisposable Observe(IModelObserver observer) 69 | { 70 | lock (_gate) 71 | { 72 | _observers.Add(observer); 73 | } 74 | if (_lastValueSet) observer.OnNext(_lastValue); 75 | return EspDisposable.Create(() => 76 | { 77 | lock (_gate) 78 | { 79 | _observers.Remove(observer); 80 | } 81 | }); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /src/Esp.Net/Router.State.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | namespace Esp.Net 20 | { 21 | public partial class Router 22 | { 23 | private class State 24 | { 25 | private readonly ITerminalErrorHandler _terminalErrorHandler; 26 | private object _modelBeingProcessed; 27 | 28 | public State(ITerminalErrorHandler terminalErrorHandler) 29 | { 30 | _terminalErrorHandler = terminalErrorHandler; 31 | CurrentStatus = Status.Idle; 32 | } 33 | 34 | public Exception HaltingException { get; private set; } 35 | 36 | public Status CurrentStatus { get; private set; } 37 | 38 | public void MoveToPreProcessing(object modelId) 39 | { 40 | _modelBeingProcessed = modelId; 41 | CurrentStatus = Status.PreEventProcessing; 42 | } 43 | 44 | public void MoveToEventDispatch() 45 | { 46 | CurrentStatus = Status.EventProcessorDispatch; 47 | } 48 | 49 | public void MoveToPostProcessing() 50 | { 51 | CurrentStatus = Status.PostProcessing; 52 | } 53 | 54 | public void MoveToDispatchModelUpdates() 55 | { 56 | _modelBeingProcessed = null; 57 | CurrentStatus = Status.DispatchModelUpdates; 58 | } 59 | 60 | public void MoveToHalted(Exception exception) 61 | { 62 | HaltingException = exception; 63 | CurrentStatus = Status.Halted; 64 | } 65 | 66 | public void MoveToIdle() 67 | { 68 | _modelBeingProcessed = null; 69 | CurrentStatus = Status.Idle; 70 | } 71 | 72 | public void MoveToExecuting(object modelId) 73 | { 74 | var canExecute = 75 | CurrentStatus == Status.EventProcessorDispatch && 76 | _modelBeingProcessed.Equals(modelId); 77 | if (canExecute) 78 | { 79 | CurrentStatus = Status.Executing; 80 | } 81 | else 82 | { 83 | throw new InvalidOperationException("Can't execute event. You can only execute an event 1) from within the observer passed to IEventObservable.Observe(IEventObserver), 2) when the router is within an existing event loop, 3) when the current event loop is for the same model you are executing against"); 84 | } 85 | } 86 | 87 | public void EndExecuting() 88 | { 89 | if (CurrentStatus != Status.Executing) 90 | { 91 | throw new InvalidOperationException("Can't end executing state as event execution isn't underway."); 92 | } 93 | CurrentStatus = Status.EventProcessorDispatch; 94 | } 95 | 96 | public void ThrowIfHalted() 97 | { 98 | if (CurrentStatus == Status.Halted) 99 | { 100 | var error = new Exception("Router halted due to previous error", HaltingException); 101 | if (_terminalErrorHandler != null) 102 | _terminalErrorHandler.OnError(error); 103 | else 104 | throw error; 105 | } 106 | } 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /src/Esp.Net/Router.Status.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | namespace Esp.Net 18 | { 19 | public partial class Router 20 | { 21 | private enum Status 22 | { 23 | Idle, 24 | PreEventProcessing, 25 | EventProcessorDispatch, 26 | PostProcessing, 27 | DispatchModelUpdates, 28 | Halted, 29 | Executing 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Esp.Net/Router.`2.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | namespace Esp.Net 20 | { 21 | internal class Router : IRouter 22 | { 23 | private readonly Func _selector; 24 | private readonly object _modelIid; 25 | private readonly IRouter _underlying; 26 | 27 | public Router(object modelIid, IRouter underlying, Func selector) 28 | { 29 | _underlying = underlying; 30 | _modelIid = modelIid; 31 | _selector = selector; 32 | } 33 | 34 | public IModelObservable GetModelObservable() 35 | { 36 | return _underlying.GetModelObservable(_modelIid).Select(_selector); 37 | } 38 | 39 | public IEventObservable GetEventObservable(ObservationStage observationStage = ObservationStage.Normal) 40 | { 41 | return _underlying.GetEventObservable(_modelIid, observationStage).Select(_selector); 42 | } 43 | 44 | public void PublishEvent(TEvent @event) 45 | { 46 | _underlying.PublishEvent(_modelIid, @event); 47 | } 48 | 49 | public void PublishEvent(object @event) 50 | { 51 | _underlying.PublishEvent(_modelIid, @event); 52 | } 53 | 54 | public void ExecuteEvent(TEvent @event) 55 | { 56 | _underlying.ExecuteEvent(_modelIid, @event); 57 | } 58 | 59 | public void ExecuteEvent(object @event) 60 | { 61 | _underlying.ExecuteEvent(_modelIid, @event); 62 | } 63 | 64 | public void RunAction(Action action) 65 | { 66 | _underlying.RunAction(_modelIid, action); 67 | } 68 | 69 | public void RunAction(Action action) 70 | { 71 | _underlying.RunAction(_modelIid, action); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/Esp.Net/Utils/Guard.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | 19 | namespace Esp.Net.Utils 20 | { 21 | internal static class Guard 22 | { 23 | public static void Requires(bool check, string format, params object[] args) 24 | where TException : Exception 25 | { 26 | if (!check) 27 | { 28 | var errorMessage = string.Format(format, args); 29 | var exception = (TException)Activator.CreateInstance(typeof(TException), errorMessage); 30 | throw exception; 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Esp.Net/Utils/ReflectionExt.cs: -------------------------------------------------------------------------------- 1 | #region copyright 2 | // Copyright 2015 Dev Shop Limited 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Reflection; 21 | 22 | namespace Esp.Net.Utils 23 | { 24 | internal static class ReflectionExt 25 | { 26 | internal static T GetCustomAttribute(this MemberInfo element, bool inherit) where T : Attribute 27 | { 28 | return (T) GetCustomAttribute(element, typeof (T), inherit); 29 | } 30 | 31 | internal static Attribute GetCustomAttribute(this MemberInfo element, Type attributeType, bool inherit) 32 | { 33 | return Attribute.GetCustomAttribute(element, attributeType, inherit); 34 | } 35 | 36 | internal static IEnumerable GetCustomAttributes(this MemberInfo element, bool inherit) where T : Attribute 37 | { 38 | return (IEnumerable) GetCustomAttributes(element, typeof (T), inherit); 39 | } 40 | 41 | internal static IEnumerable GetCustomAttributes(this MemberInfo element, Type attributeType, bool inherit) 42 | { 43 | return (IEnumerable)Attribute.GetCustomAttributes(element, attributeType, inherit); 44 | } 45 | 46 | internal static IEnumerable GetMethodsRecursive(this Type type, BindingFlags bindingAttr) 47 | { 48 | if(type == null) yield break; 49 | var children = type.GetMethods(bindingAttr); 50 | foreach (MethodInfo methodInfo in children) 51 | { 52 | yield return methodInfo; 53 | } 54 | var grandChildren = type.BaseType.GetMethodsRecursive(bindingAttr); 55 | foreach (MethodInfo methodInfo in grandChildren) 56 | { 57 | yield return methodInfo; 58 | } 59 | } 60 | } 61 | } 62 | --------------------------------------------------------------------------------