├── .gitattributes ├── .gitignore ├── Cadence.md ├── Dispatcher.md ├── Hub.md ├── Observer.md ├── Reduce.md ├── Smart Cache.md ├── images ├── cadence-structure.png ├── cadence-structure.pptx ├── dispatcher-structure.png ├── dispatcher-structure.pptx ├── hub-structure.png ├── hub-structure.pptx ├── observer-structure.png ├── observer-structure.pptx ├── reduce-structure.png ├── reduce-structure.pptx ├── smart-cache-structure.png └── smart-cache-structure.pptx ├── readme.md └── samples ├── Hub ├── Azure │ ├── Azure.ccproj │ ├── ServiceConfiguration.Cloud.cscfg │ ├── ServiceConfiguration.Local.cscfg │ └── ServiceDefinition.csdef ├── Client │ ├── App_Start │ │ ├── BundleConfig.cs │ │ ├── FilterConfig.cs │ │ └── RouteConfig.cs │ ├── Client.csproj │ ├── ClientConfiguration.xml │ ├── Content │ │ ├── Site.css │ │ ├── bootstrap.css │ │ └── bootstrap.min.css │ ├── Controllers │ │ └── HomeController.cs │ ├── Global.asax │ ├── Global.asax.cs │ ├── Hubs │ │ └── Relay.cs │ ├── Packages.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Scripts │ │ ├── _references.js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ ├── jquery-1.10.2.intellisense.js │ │ ├── jquery-1.10.2.js │ │ ├── jquery-1.10.2.min.js │ │ ├── jquery-1.10.2.min.map │ │ ├── jquery.signalR-2.0.1.js │ │ ├── jquery.signalR-2.0.1.min.js │ │ ├── jquery.validate-vsdoc.js │ │ ├── jquery.validate.js │ │ ├── jquery.validate.min.js │ │ ├── jquery.validate.unobtrusive.js │ │ ├── jquery.validate.unobtrusive.min.js │ │ ├── modernizr-2.6.2.js │ │ ├── respond.js │ │ └── respond.min.js │ ├── Startup.cs │ ├── Views │ │ ├── Home │ │ │ ├── Observe.cshtml │ │ │ └── Spawn.cshtml │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ └── _Layout.cshtml │ │ ├── Web.config │ │ └── _ViewStart.cshtml │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ ├── favicon.ico │ └── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff ├── Grains │ ├── Grains.csproj │ ├── HubBufferGrain.cs │ ├── HubGateway.cs │ ├── HubGrain.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── PublisherGrain.cs ├── Hub.sln ├── Interfaces │ ├── IHub.cs │ ├── IPublisher.cs │ ├── Interfaces.csproj │ └── Properties │ │ └── AssemblyInfo.cs ├── README.md └── Silo │ ├── App.config │ ├── OrleansConfiguration.xml │ ├── Properties │ └── AssemblyInfo.cs │ ├── Silo.csproj │ └── WorkerRole.cs └── Observer ├── GrainInterfaces ├── GrainInterfaces.csproj ├── IGrain1.cs └── Properties │ ├── AssemblyInfo.cs │ └── orleans.codegen.cs ├── Grains ├── Grain1.cs ├── Grains.csproj └── Properties │ └── AssemblyInfo.cs ├── Observer.sln └── Observer ├── App.config ├── DevTestClientConfiguration.xml ├── DevTestServerConfiguration.xml ├── Observer.csproj ├── OrleansHostWrapper.cs ├── Program.cs └── Properties └── AssemblyInfo.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## 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 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 19 | !packages/*/build/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | *_i.c 26 | *_p.c 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.scc 48 | 49 | # Visual C++ cache files 50 | ipch/ 51 | *.aps 52 | *.ncb 53 | *.opensdf 54 | *.sdf 55 | *.cachefile 56 | 57 | # Visual Studio profiler 58 | *.psess 59 | *.vsp 60 | *.vspx 61 | 62 | # Guidance Automation Toolkit 63 | *.gpState 64 | 65 | # ReSharper is a .NET coding add-in 66 | _ReSharper*/ 67 | *.[Rr]e[Ss]harper 68 | 69 | # TeamCity is a build add-in 70 | _TeamCity* 71 | 72 | # DotCover is a Code Coverage Tool 73 | *.dotCover 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress/ 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish/ 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | 98 | # NuGet Packages Directory 99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 100 | Packages/ 101 | 102 | # Windows Azure Build Output 103 | csx 104 | *.build.csdef 105 | 106 | # Windows Store app package directory 107 | AppPackages/ 108 | 109 | # Others 110 | sql/ 111 | *.Cache 112 | ClientBin/ 113 | [Ss]tyle[Cc]op.* 114 | ~$* 115 | *~ 116 | *.dbmdl 117 | *.[Pp]ublish.xml 118 | *.pfx 119 | *.publishsettings 120 | 121 | # RIA/Silverlight projects 122 | Generated_Code/ 123 | 124 | # Backup & report files from converting an old project file to a newer 125 | # Visual Studio version. Backup files are not needed, because we have git ;-) 126 | _UpgradeReport_Files/ 127 | Backup*/ 128 | UpgradeLog*.XML 129 | UpgradeLog*.htm 130 | 131 | # SQL Server files 132 | App_Data/*.mdf 133 | App_Data/*.ldf 134 | 135 | 136 | #LightSwitch generated files 137 | GeneratedArtifacts/ 138 | _Pvt_Extensions/ 139 | ModelManifest.xml 140 | 141 | # ========================= 142 | # Windows detritus 143 | # ========================= 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | ehthumbs.db 148 | 149 | # Folder config file 150 | Desktop.ini 151 | 152 | # Recycle Bin used on file shares 153 | $RECYCLE.BIN/ 154 | 155 | # Mac desktop service store files 156 | .DS_Store 157 | 158 | #ignore codegen stuff 159 | orleans.codegen.cs -------------------------------------------------------------------------------- /Cadence.md: -------------------------------------------------------------------------------- 1 | # Orleans Cadence Pattern 2 | 3 | ## Intent 4 | 5 | Decouple the rhythm of grain interactions from external input using timers 6 | 7 | ## Also Known As 8 | 9 | ## Motivation 10 | 11 | A peak burst of calls into outside the system could result in an avalanche of messages flowing through the grains and silos. Grains buffer the data and only pass it on to other grains at fixed intervals, using timer ticks at set intervals. 12 | 13 | ## Applicability 14 | 15 | The cadence pattern can be used when grains accepts messages from external sources, where the frequency can vary and peak activity could disrupt the stability of the system. 16 | 17 | It could also be used to to control flow from out of Orleans into an external system, such as monitoring or collation. 18 | 19 | Often used with aggregator or reduce pattern. 20 | 21 | ## Structure 22 | 23 | ![observer structure diagram](images/cadence-structure.png) 24 | 25 | ## Participants 26 | 27 | ## Collaborations 28 | 29 | ## Consequences 30 | 31 | ## Implementation 32 | 33 | Orleans supports timers that enable developers to specify periodic behavior for grains. They are subject to single threaded execution guarantees within the grain activation, which is key to Orleans. 34 | 35 | The timers are typically set in the grain's ActivateAsync() call, using RegisterTimer(asyncCallbackFunction, object anyState, TimeSpan startTime, TimeSpan tickPeriod) 36 | 37 | ## Sample Code 38 | 39 | The timer is typically set up in the grain's ActivateAsync() call 40 | 41 | ```cs 42 | public override Task ActivateAsync() 43 | { 44 | RegisterTimer(SendUpdate, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); 45 | 46 | ``` 47 | 48 | The grain buffers incoming data 49 | 50 | ```cs 51 | public Task TrackCurrentOutputLevel(long latestOutputLevel) 52 | { 53 | _outputRunningTotal += latestOutputLevel; 54 | ``` 55 | 56 | SendUpdate is called on each timer tick, and checks if there is new buffered data to send before making its call 57 | 58 | ```cs 59 | async Task SendUpdate(object _) 60 | { 61 | if (_outputRunningTotal == 0) 62 | return; // nothing to send 63 | 64 | await _counterGrain.TrackOutputLevel(_outputRunningTotal); 65 | ``` 66 | 67 | ## Known Issues 68 | 69 | ## Related Patterns 70 | 71 | -------------------------------------------------------------------------------- /Dispatcher.md: -------------------------------------------------------------------------------- 1 | # Orleans Dispatcher Pattern 2 | 3 | ## Intent 4 | 5 | A technique to send a batch of messages into Orleans in a single call, and have them distributed internally to the correct grains. 6 | 7 | ## Also Known As 8 | 9 | ## Motivation 10 | 11 | When sending messages into an Orleans system, it can sometimes be more efficient to send a batch of messages in a single call, rather than a message at a time. A dispatcher grain can distribute these messages to the required grains internally. 12 | 13 | ## Applicability 14 | 15 | Use the Dispatcher pattern in the following situations: 16 | 17 | * When messages are received in batches (i.e. when popping messages off an Azure CloudQueue, 32 can be received at a time) 18 | * When a batch of messages requires decoding before the target grains are known. 19 | * When latency between client code and the Orleans Silo is high. 20 | 21 | ## Structure 22 | 23 | ![dispatcher structure](images/dispatcher-structure.png) 24 | 25 | ## Participants 26 | 27 | * __Client Code__ 28 | * Generates batches of messages to send to Orleans. 29 | * __Dispatcher Grain__ 30 | * A stateless worker, responsible for processing a batch of messages, and fanning these out to the target grains. 31 | * __Target Grain__ 32 | * The grain we want to ultimately send the message to 33 | 34 | ## Collaborations 35 | 36 | * The 'Client Code' sends a batch of messages to the 'Dispatcher Grain'. 37 | * The 'Dispatcher Grain' enumerates through the batch, and send a message to each target grain. 38 | 39 | ## Consequences 40 | 41 | * For Orleans systems with a small number of silos, the efficiency is improved, as there is a greater chance of the target grain being on the same node as the dispatcher grain, which leads to less inter-silo communication. 42 | * The pattern is more useful when the message batch requires some decoding, and the target grain is not known by the client code. The dispatcher grain will auto-scale when required, in response to the load on the system. 43 | 44 | ## Implementation 45 | 46 | ## Sample Code 47 | 48 | Target Grain interface: 49 | 50 | ```cs 51 | public interface ITargetGrain : IGrain 52 | { 53 | Task Send(string message); 54 | } 55 | ``` 56 | 57 | Dispatcher Grain interface: 58 | 59 | ```cs 60 | [StatelessWorker] 61 | public interface IDispatcherGrain : IGrain 62 | { 63 | Task Send(Tuple[] messages); 64 | } 65 | ``` 66 | 67 | Dispatcher Grain class: 68 | 69 | ```cs 70 | public class DispatcherGrain : IDispatcherGrain 71 | { 72 | public Task Send(Tuple[] messages) 73 | { 74 | var tasks = new List(); 75 | foreach (var message in messages) 76 | { 77 | var grain = TargetGrainFactory.GetGrain(message.Item1); 78 | tasks.Add(grain.Send(message.Item2)); 79 | } 80 | return Task.WhenAll(tasks); 81 | } 82 | } 83 | ``` 84 | 85 | ## Known Issues 86 | 87 | * Although this pattern can reduce the amount of network activity for sending a batch of messages, it does introduce an extra step between the client code, and the target grain, which can increase the latency of the solution. 88 | 89 | ## Related Patterns 90 | 91 | -------------------------------------------------------------------------------- /Hub.md: -------------------------------------------------------------------------------- 1 | # Orleans Hub Pattern 2 | 3 | ## Intent 4 | 5 | Provides clients with a single well-known publishing endpoint by channeling events from multiple event sources, dramatically simplifying registration and management of transient subscriptions 6 | 7 | ## Also Known As 8 | 9 | Event Aggregator [[Fowler]](http://martinfowler.com/eaaDev/EventAggregator.html) 10 | 11 | ## Motivation 12 | 13 | A system with lots of observable grains can lead to complexities when a client wants to subscribe to all of them. The client has to find and register for each grain individually. 14 | 15 | Moreover, not all grains are active all the time, but due to client subscription requests a grain will be spawned and will keep occupying memory, despite of whether it has any useful work to do or not. This is because observable subscriptions are transient, which requires client to periodically re-subscribe and thus needlessly resurrect all grains. 16 | 17 | A Hub acts as a single source of events for many of grains, allowing clients to register with just the hub. Since hub is essentially a Singleton it might introduce a severe performance penalty in the system. This is further optimized by application of [Reduce](Reduce.md) and [Cadence](Cadence.md) patterns, and also by making sure that all calls to a hub are local only. 18 | 19 | Since a separate instance of hub is created for each silo, there is no single address to which client could subscribe. This requires some sophistication from client but existing infrastructure management capabilities built into Orleans framework makes it nearly trivial. 20 | 21 | ## Applicability 22 | 23 | Use the Hub pattern in the following situations: 24 | 25 | * Hub is a good choice when you have lots of grains that are potential event sources. Rather than have the client deal with registering with them all, you can centralize the registration logic to the Hub. As well as simplifying registration, a Hub also simplifies the memory management issues in using observable grains. 26 | * You need to periodically gather silo-specific information, such as performance/throughput metrics and Hub grain which is guaranteed to be local to a silo could be a good choice, allowing you to reuse existing communication infrastructure provided by Orleans framework. 27 | 28 | ## Structure 29 | 30 | ![hub structure diagram](images/hub-structure.png) 31 | 32 | ## Participants 33 | 34 | * __Publisher Grain__ 35 | * The actual event source. Can publish a number of different events 36 | * __Buffer Grain__ 37 | * Provides an interface for grains to publish individual events into a hub 38 | * Buffers and propagates events to the hub 39 | * This grain is a `[StatelessWorker]` and will be auto-scaled by Orleans 40 | * __Hub Grain__ 41 | * Provides an interface for clients to subscribe for event notifications 42 | * This is a Singleton hosted in each silo 43 | * __Hub Monitor__ 44 | * Listens to hub notifications 45 | * Periodically resubscribe to all hubs by acquiring information on active silos via Orleans management infrastructure 46 | * Provides a unified easy-to-use interface for clients by hiding complexities of managing transient subscriptions 47 | 48 | ## Collaborations 49 | 50 | * The 'Publisher Grain' publishes individual events to the 'Buffer Grain', which simply puts them into internal queue. 51 | * The 'Buffer Grain' periodically sends collected events to the 'Hub Grain' effectively batching them. 52 | * The 'Hub Grain' upon receival of batch of events from 'Buffer Grain' immediately publish them to all registered subscribers by utilizing built-in one-way communication capabilities of Orleans' framework. 53 | * The 'Hub Monitor' subscribes to 'Hub Grain' by using management infrastructure provided by Orleans' framework. Upon receival of batch of events will simply dispatch them locally to all interested subscribers. 54 | 55 | ## Consequences 56 | 57 | * __Out-of-order processing__ As the 'Buffer Grain' is a stateless worker, there will be multiple instances activated in each silo, so each time the 'Publisher Grain's publish an event it might go to a different instance of 'Buffer Grain', thus breaking an original order of events. This could be fixed by applying [Resequencer](http://www.eaipatterns.com/Resequencer.html) EIP pattern either on client side or within 'Hub Grain' itself. 58 | * __Increased latency__ between the moment when event was generated and the moment it was received by the client. 59 | 60 | ## Implementation 61 | 62 | Due to virtual nature of actors in Orleans, there some complexities and dangers for proper implementation of this pattern. Still, with few special hints and hooks available in Orleans framework it is possible to have a reliable implementation. 63 | 64 | ## Sample Code 65 | 66 | See included sample at [samples/Hub](samples/Hub) 67 | 68 | ## Known Issues 69 | 70 | - Can be activated only from within a Bootstrap Provider. 71 | - All client requests could only be made after first consulting Orleans' management infrastructure. 72 | 73 | ## Related Patterns 74 | - [Observer](Observer.md) 75 | - [Reduce](Reduce.md) 76 | - [Cadence](Cadence.md) 77 | - [Dispatcher](Dispatcher.md) 78 | -------------------------------------------------------------------------------- /Observer.md: -------------------------------------------------------------------------------- 1 | # Orleans Observer Pattern 2 | 3 | ## Intent 4 | 5 | Allows an observer to be notified of any state changes that a grain can choose to publish 6 | 7 | ## Also Known As 8 | 9 | Pub/Sub, Events 10 | 11 | ## Motivation 12 | 13 | The observer pattern is a well known software design pattern in which an object, called the subject, maintains a list of interested parties, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. Because this pattern is so well established, Orleans supports the capability natively, along with a helper class to assist implementation. 14 | 15 | ## Applicability 16 | 17 | You would use the Observer pattern when want a grain to allow subscribers to register their interest in specific events. 18 | 19 | ## Structure 20 | 21 | ![observer structure diagram](images/observer-structure.png) 22 | 23 | ## Participants 24 | 25 | The subject (aka observed, source) grain 26 | 27 | The observer (aka watcher, sink) grain or class 28 | 29 | 30 | ## Collaborations 31 | 32 | The source grain will notify all observers via an interface method as and when it has updates and information it wishes to share. 33 | 34 | The observer must explicitly set up a subscription when it wishes to receive updates, providing a link to itself if it is a grain, or a proxy if it is not a grain. It should also explicitly cancel the subscription when it no longer requires updates. 35 | 36 | 37 | ## Consequences 38 | 39 | ## Implementation 40 | 41 | A grain type that supports observation will define an observer interface that inherits from the IGrainObserver interface. Methods on this observer interface correspond to events that the observed grain makes available. 42 | 43 | An observer implements this interface and then subscribe to notifications from a particular grain. The observed grain would call back to the observer through the observer interface methods when an event has occurred. 44 | 45 | Methods on observer interfaces must be void since event messages are one-way. If the observer needs to interact with the observed grain as a result of a notification, it must do so by invoking normal methods on the observed grain. 46 | 47 | The observed grain type must expose a method to allow observers to subscribe to event notifications from a grain. In addition, it is usually convenient to expose a method that allows an existing subscription to be canceled. Grain developers may use the Orleans ObserverSubscriptionManager class to simplify the subscriptions and notifications. 48 | 49 | If the observer is not a grain, it must first create a local C# class that implements the observer interface, then call a static method on the observer factory, CreateObjectReference(), to turn the C# object into a grain reference, which can then be passed to the subscription method on the notifying grain. 50 | 51 | 52 | ## Sample Code 53 | 54 | The actual notification is the Update() method on the IGrainObserver derived IObserve interface 55 | 56 | ```cs 57 | public interface IObserve : Orleans.IGrainObserver 58 | { 59 | void Update(int data); 60 | } 61 | ``` 62 | 63 | The source grain allows subscriber to register for notifications, using the ObserverSubscriptionManager<> helper class 64 | 65 | ```cs 66 | public Task SubscribeForUpdates(IObserve subscriber) 67 | { 68 | subscribers.Subscribe(subscriber); 69 | return TaskDone.Done; 70 | } 71 | ``` 72 | 73 | The source grain send out notifications as and when, using the ObserverSubscriptionManager<> helper class to dispatch the messages 74 | 75 | ```cs 76 | private Task SendOutUpdates() 77 | { 78 | subscribers.Notify( s => s.Update(latestData)); 79 | return TaskDone.Done; 80 | } 81 | ``` 82 | 83 | The observer must define which class or grain is to receive the notifications 84 | 85 | ```cs 86 | private class TheObserver : IObserve 87 | { 88 | public void Update(int latestData) 89 | { 90 | Console.WriteLine("New stuff has happened: {0}", latestData); 91 | } 92 | } 93 | ``` 94 | 95 | The observer must then register this sink 96 | 97 | ```cs 98 | var theObserver = new TheObserver(); 99 | var obj = ObserveFactory.CreateObjectReference(theObserver).Result; 100 | grain.SubscribeForUpdates(obj); 101 | ``` 102 | 103 | 104 | ## Known Issues 105 | 106 | ## Related Patterns 107 | 108 | -------------------------------------------------------------------------------- /Reduce.md: -------------------------------------------------------------------------------- 1 | # Orleans Reduce Pattern 2 | 3 | ## Intent 4 | 5 | Provides a hierarchical structure to aggregate a value stored in many grains, which would be unachievable with a fan-out. 6 | 7 | ## Also Known As 8 | 9 | ## Motivation 10 | 11 | A side-effect to isolating state in grains, which are in turn distributed across a cluster of machines is that it is hard to retrieve an aggregate, for example a total, or average of a variable held by the grain. 12 | 13 | A fan-out could be used to retrieve the value from a small number of grains, but approach starts to fail when the number of grains increase. It would also be necessary to know in advance which grains need to participate in the fan-out. 14 | 15 | The Reduce approach is for a Singleton grain to record the total value, and for all grains contributing to this value should call this Singleton. The problem with doing this directly is that it introduces a severe performance penalty in the system. It's conceivable that every request to the system could ultimately result in the call to the Singleton, thus creating a bottleneck, as the Singleton would be unlikely to cope with the volume of requests. Many of these requests would also cross Silo boundaries, which are expensive. 16 | 17 | The Reduce pattern solves this problem by introducing a StatelessWorker grain which is responsible for collecting results in each of the silos. This grain then periodically publishes the number to the Singleton, which may be in a different silo. 18 | 19 | ## Applicability 20 | 21 | Use the Reduce pattern in the following situations: 22 | 23 | * You want to aggregate a value, or perform a count over a large number of grains, distributed across silos. For example you want an average, min/max of a value held by all grains. 24 | 25 | ## Structure 26 | 27 | ![reduce structure diagram](images/reduce-structure.png) 28 | 29 | ## Participants 30 | 31 | * __Value Grain__ 32 | * Holds the value for which you want to apply an aggregate function to 33 | * __Subtotal Grain__ 34 | * Holds the sub total for local silo 35 | * Provides an interface for values grains to report changes in their value 36 | * This grain is marked as a `[StatelessWorker]` 37 | * __Total Grain__ 38 | * Holds the overall total 39 | * Provides an interface for 'Subtotal Grains' to report changes in their value 40 | 41 | ## Collaborations 42 | 43 | * The 'Value Grain' reports changes in it's internal state to the 'Subtotal Grain', which maintain the current sub-total for the silo. 44 | * The 'Subtotal Grain' periodically reports changes in it's internal state to the 'Total Grain'. 45 | * The 'Total Grain' can be queried at any time to retrieve the last known total value for the system 46 | 47 | ## Consequences 48 | 49 | * __Quick to update the subtotal grain__ As the 'Subtotal Grain' is a stateless worker, it will be activated in each silo. Calls to it will not cross a process boundary, and will therefore be fast. 50 | * __The subtotal grain auto scales__ If a queue of calls does build up on the 'Subtotal Grain', it will be automatically scaled. This should have no lasting side effect, as the aggregate function will simply be aggregated across more of these grains. 51 | * __The system is eventually consistent__ The 'Subtotal Grain' must update the 'Total Grain' periodically, to avoid network congestion. Therefore the total value held by this grain will not reflect the exact real value. It has eventual consistency. 52 | * __Only simple aggregations can be used__ Attempting to transmit more than simple state up the hierarchy will result in a large memory overhead in the 'Total Grain' and and a large amount of data transmitted over the network. 53 | 54 | ## Implementation 55 | 56 | ## Sample Code 57 | 58 | Value Grain Interface 59 | 60 | ```cs 61 | public interface IValueGrain : Orleans.IGrain 62 | { 63 | Task SetValue(int value); 64 | } 65 | ``` 66 | 67 | Value Grain Implementation 68 | 69 | ```cs 70 | public class ValueGrain : Orleans.GrainBase, IValueGrain 71 | { 72 | int localValue = 0; 73 | public async Task SetValue(int value) 74 | { 75 | if (localValue != value) 76 | { 77 | var grain = SubtotalGrainFactory.GetGrain(0); 78 | await grain.SetDelta(localValue - value); 79 | } 80 | localValue = value; 81 | } 82 | } 83 | ``` 84 | 85 | Subtotal Grain Interface 86 | 87 | ```cs 88 | [StatelessWorker] 89 | public interface ISubtotalGrain : Orleans.IGrain 90 | { 91 | Task SetDelta(int value); 92 | } 93 | ``` 94 | 95 | Subtotal Grain Implementation 96 | 97 | ```cs 98 | public class SubTotalGrain : Orleans.GrainBase, ISubtotalGrain 99 | { 100 | int runningTotal = 0; 101 | 102 | public override Task ActivateAsync() 103 | { 104 | RegisterTimer(ReportRunningTotal, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); 105 | return base.ActivateAsync(); 106 | } 107 | 108 | async Task ReportRunningTotal(object _) 109 | { 110 | var totalGrain = TotalGrainFactory.GetGrain(0); 111 | await totalGrain.SetDelta(runningTotal); 112 | runningTotal = 0; 113 | } 114 | 115 | public Task SetDelta(int value) 116 | { 117 | runningTotal += value; 118 | return TaskDone.Done; 119 | } 120 | } 121 | ``` 122 | 123 | Total Grain Interface 124 | 125 | ```cs 126 | public interface ITotalGrain : Orleans.IGrain 127 | { 128 | Task SetDelta(int value); 129 | Task GetTotal(); 130 | } 131 | ``` 132 | 133 | Total Grain Implementation 134 | 135 | ```cs 136 | public class TotalGrain : Orleans.GrainBase, ITotalGrain 137 | { 138 | int total; 139 | 140 | public Task SetDelta(int value) 141 | { 142 | total += value; 143 | return TaskDone.Done; 144 | } 145 | 146 | public Task GetTotal() 147 | { 148 | return Task.FromResult(total); 149 | } 150 | } 151 | ``` 152 | 153 | ## Known Issues 154 | 155 | ## Related Patterns 156 | 157 | -------------------------------------------------------------------------------- /Smart Cache.md: -------------------------------------------------------------------------------- 1 | # Orleans Smart Cache Pattern 2 | 3 | ## Intent 4 | 5 | A performance optimization which uses Orleans as a distributed caching system in front of a storage system. Allows reads to be served from memory, and provides the option for buffering writes. 6 | 7 | ## Also Known As 8 | 9 | ## Motivation 10 | 11 | Storage is often the bottleneck in a large system. Caching data in fast-access memory both reduces the latency of the overall system, and reduces the load on storage. 12 | 13 | Orleans can provide a cache with immediate consistency, with the efficiency of having one instance of each object in memory across the cluster. 14 | 15 | ## Applicability 16 | 17 | Use the Smart Cache pattern in the following situations: 18 | 19 | * You have a storage system (such as a database) which is introducing an unacceptable latency to your system. 20 | * Or you have a storage system which cannot handle the number of requests per second required by your system. 21 | * Or you have a system which is predominantly read, rather than write. 22 | * Or you have a system which has a high transaction rate for writes, and you can accept an increased risk in data loss. 23 | * You have a system which uses data in a document-orientated approach, rather than by composing queries. 24 | 25 | ## Structure 26 | 27 | ![smart cache structure](images/smart-cache-structure.png) 28 | 29 | ## Participants 30 | 31 | * __Database__ 32 | * Database system which you wish to cache 33 | * __Cache Grain__ 34 | * Maintains the value of a record in the database. There is an instance of the cache grain for every record in the database. 35 | * __Client Code__ 36 | * Consumes the cache data. 37 | 38 | ## Collaborations 39 | 40 | * The 'Client Code' calls the 'Cache Grain' to read and write values stored in the 'Database'. 41 | * When the 'Cache Grain' activates, it loads the record from it's primary key from the 'Database' and stores it in memory. 42 | * When 'Client Code' sends a read operation to the 'Cache Grain', the 'Cache Grain' returns the value from memory. 43 | * When the 'Client Code' sends a write operation to the 'Cache Grain' it can either pass the write through immediately to the 'Database', or buffer the write, and perform the write to the database on a timer. This means the 'Database' and the cache can become out of step. The chance of data loss increases, but will reduce write transactions to the 'Database'. 44 | 45 | ## Consequences 46 | 47 | * __Read hits to the system are fast__ as the grain holds the data in memory. 48 | * __Writes to the system can be buffered__ at the cost of increased chance of data loss. 49 | * __Using Orleans Persistence Providers could reduce development time__ as the data access code is already provided. 50 | 51 | ## Implementation 52 | 53 | ## Sample Code 54 | 55 | Cache Grain Interface: 56 | 57 | ```cs 58 | public interface ICacheGrain : IGrain 59 | { 60 | Task Write(string value); 61 | Task Read(); 62 | } 63 | ``` 64 | Cache Grain Implementation (without write buffering): 65 | 66 | ```cs 67 | public interface IDatabaseState : IGrainState 68 | { 69 | string Value { get; set; } 70 | } 71 | 72 | [StorageProvider(ProviderName="Database")] 73 | public class CacheGrain : Orleans.GrainBase, ICacheGrain 74 | { 75 | public async Task Write(string value) 76 | { 77 | if (value == State.Value) return; 78 | State.Value = value; 79 | await State.WriteStateAsync(); 80 | } 81 | 82 | public Task Read() 83 | { 84 | return Task.FromResult(State.Value); 85 | } 86 | } 87 | ``` 88 | 89 | Cache Grain Implementation (with a write buffer): 90 | 91 | ```cs 92 | [StorageProvider(ProviderName="Database")] 93 | public class CacheGrain : Orleans.GrainBase, ICacheGrain 94 | { 95 | bool stateChanged = false; 96 | 97 | public override Task ActivateAsync() 98 | { 99 | RegisterTimer(WriteState, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); 100 | return base.ActivateAsync(); 101 | } 102 | 103 | async Task WriteState(object _) 104 | { 105 | if (!stateChanged) return; 106 | stateChanged = false; 107 | await State.WriteStateAsync(); 108 | } 109 | 110 | public Task Write(string value) 111 | { 112 | if (value == State.Value) return TaskDone.Done; 113 | State.Value = value; 114 | stateChanged = true; 115 | return TaskDone.Done; 116 | } 117 | 118 | public Task Read() 119 | { 120 | return Task.FromResult(State.Value); 121 | } 122 | } 123 | ``` 124 | 125 | Note the implementations use a Storage Provider to write to the database. 126 | 127 | 128 | ## Known Issues 129 | 130 | * Queries (such as aggregates, filters and joins) cannot easily be cached, as it is hard to know when they have been invalidated. Therefore the cache is suitable for records by record access more so than a querying system. 131 | * In case of cache misses, the cache introduces a latency, which will slow down the system. 132 | * If writes are buffered, there is an increased risk of data loss, of the silo terminates unexpectedly before an outstanding write it committed. 133 | 134 | ## Related Patterns 135 | 136 | -------------------------------------------------------------------------------- /images/cadence-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/images/cadence-structure.png -------------------------------------------------------------------------------- /images/cadence-structure.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/images/cadence-structure.pptx -------------------------------------------------------------------------------- /images/dispatcher-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/images/dispatcher-structure.png -------------------------------------------------------------------------------- /images/dispatcher-structure.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/images/dispatcher-structure.pptx -------------------------------------------------------------------------------- /images/hub-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/images/hub-structure.png -------------------------------------------------------------------------------- /images/hub-structure.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/images/hub-structure.pptx -------------------------------------------------------------------------------- /images/observer-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/images/observer-structure.png -------------------------------------------------------------------------------- /images/observer-structure.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/images/observer-structure.pptx -------------------------------------------------------------------------------- /images/reduce-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/images/reduce-structure.png -------------------------------------------------------------------------------- /images/reduce-structure.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/images/reduce-structure.pptx -------------------------------------------------------------------------------- /images/smart-cache-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/images/smart-cache-structure.png -------------------------------------------------------------------------------- /images/smart-cache-structure.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/images/smart-cache-structure.pptx -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Orleans Design Patterns 2 | 3 | 4 | * __[Observer](Observer.md)__ Allows an observer to be notified of any state changes that a grain can choose to publish. 5 | * __[Cadence](Cadence.md)__ Decouple the rhythm of grain interactions from external input using timers. 6 | * __[Reduce](Reduce.md)__ Provides a hierarchical structure to aggregate a value stored in many grains, which would unachievable with a fan-out. 7 | * __[Smart Cache](Smart%20Cache.md)__ A performance optimization which uses Orleans as a distributed caching system in front of a storage system. Allows reads to be served from memory, and writes to be optionally buffered. 8 | * __[Dispatcher](Dispatcher.md)__ A technique to send a batch of messages into Orleans in a single call, and have them distributed internally to the correct grains. 9 | * __[Hub](Hub.md)__ Provides clients with a single well-known publishing endpoint by channeling events from multiple event sources, dramatically simplifying registration and management of transient subscriptions. 10 | 11 | 12 | # Approximate Performance Expectations 13 | 14 | Using X-Large VMs (8 CPU Cores / 14 GB RAM) on Microsoft Azure, with one silo per VM: 15 | 16 | * A grain will handle a maximum of 1,000 requests per second. 17 | * A silo will handle a maximum of 10,000 requests per second. 18 | * A silo will hold 100,000 active grains. 19 | -------------------------------------------------------------------------------- /samples/Hub/Azure/Azure.ccproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.4 8 | {492563fc-84b6-41c5-a776-394a8bbf72c6} 9 | Library 10 | Properties 11 | Sample 12 | Sample.Azure 13 | True 14 | Azure 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | False 24 | False 25 | True 26 | 27 | 28 | true 29 | full 30 | false 31 | bin\Debug\ 32 | DEBUG;TRACE 33 | prompt 34 | 4 35 | 36 | 37 | pdbonly 38 | true 39 | bin\Release\ 40 | TRACE 41 | prompt 42 | 4 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Client 53 | {3d7d093a-afe7-438c-b9aa-9c3097e86050} 54 | True 55 | Web 56 | Client 57 | True 58 | 59 | 60 | Silo 61 | {6b7e7c7d-fcb5-415c-a361-e653cb9ead72} 62 | True 63 | Worker 64 | Silo 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 11.0 74 | $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Windows Azure Tools\2.4\ 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /samples/Hub/Azure/ServiceConfiguration.Cloud.cscfg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /samples/Hub/Azure/ServiceConfiguration.Local.cscfg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /samples/Hub/Azure/ServiceDefinition.csdef: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /samples/Hub/Client/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Optimization; 2 | 3 | namespace Sample 4 | { 5 | public static class BundleConfig 6 | { 7 | public static void RegisterBundles(BundleCollection bundles) 8 | { 9 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 10 | "~/Scripts/jquery-{version}.js")); 11 | 12 | bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 13 | "~/Scripts/jquery.validate*")); 14 | 15 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 16 | "~/Scripts/modernizr-*")); 17 | 18 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 19 | "~/Scripts/bootstrap.js", 20 | "~/Scripts/respond.js")); 21 | 22 | bundles.Add(new StyleBundle("~/Content/css").Include( 23 | "~/Content/bootstrap.css", 24 | "~/Content/site.css")); 25 | 26 | BundleTable.EnableOptimizations = true; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /samples/Hub/Client/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | namespace Sample 4 | { 5 | public static class FilterConfig 6 | { 7 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 8 | { 9 | filters.Add(new HandleErrorAttribute()); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /samples/Hub/Client/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Web.Mvc; 4 | using System.Web.Routing; 5 | 6 | namespace Sample 7 | { 8 | public static class RouteConfig 9 | { 10 | public static void RegisterRoutes(RouteCollection routes) 11 | { 12 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 13 | 14 | routes.MapRoute( 15 | name: "Default", 16 | url: "{controller}/{action}/{id}", 17 | defaults: new { controller = "Home", action = "Spawn", id = UrlParameter.Optional } 18 | ); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /samples/Hub/Client/Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 8 | 9 | 2.0 10 | {3D7D093A-AFE7-438C-B9AA-9C3097E86050} 11 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 12 | Library 13 | Properties 14 | Sample 15 | Sample.Client 16 | v4.5.1 17 | false 18 | false 19 | 20 | 21 | 22 | 23 | 24 | 25 | true 26 | full 27 | false 28 | bin\ 29 | DEBUG;TRACE 30 | prompt 31 | 4 32 | 33 | 34 | pdbonly 35 | true 36 | bin\ 37 | TRACE 38 | prompt 39 | 4 40 | 41 | 42 | 43 | ..\packages\Microsoft.AspNet.SignalR.Core.2.1.2\lib\net45\Microsoft.AspNet.SignalR.Core.dll 44 | 45 | 46 | ..\packages\Microsoft.AspNet.SignalR.SystemWeb.2.0.1\lib\net45\Microsoft.AspNet.SignalR.SystemWeb.dll 47 | 48 | 49 | 50 | ..\packages\Microsoft.Data.Edm.5.6.0\lib\net40\Microsoft.Data.Edm.dll 51 | 52 | 53 | ..\packages\Microsoft.Data.OData.5.6.0\lib\net40\Microsoft.Data.OData.dll 54 | 55 | 56 | ..\packages\Microsoft.Data.Services.Client.5.6.0\lib\net40\Microsoft.Data.Services.Client.dll 57 | 58 | 59 | ..\packages\Microsoft.Owin.2.0.1\lib\net45\Microsoft.Owin.dll 60 | 61 | 62 | ..\packages\Microsoft.Owin.Host.SystemWeb.2.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll 63 | 64 | 65 | ..\packages\Microsoft.Owin.Security.2.0.1\lib\net45\Microsoft.Owin.Security.dll 66 | 67 | 68 | ..\packages\Microsoft.WindowsAzure.ConfigurationManager.2.0.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll 69 | 70 | 71 | True 72 | 73 | 74 | False 75 | 76 | 77 | False 78 | ..\packages\WindowsAzure.Storage.4.0.1\lib\net40\Microsoft.WindowsAzure.Storage.dll 79 | 80 | 81 | $(OrleansSDK)\Binaries\OrleansServer\Microsoft.WindowsAzure.StorageClient.dll 82 | 83 | 84 | False 85 | ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll 86 | 87 | 88 | $(OrleansSDK)\Binaries\OrleansServer\Orleans.dll 89 | 90 | 91 | $(OrleansSDK)\Binaries\OrleansServer\OrleansAzureUtils.dll 92 | 93 | 94 | $(OrleansSDK)\Binaries\OrleansServer\OrleansRuntime.dll 95 | 96 | 97 | ..\packages\Owin.1.0\lib\net40\Owin.dll 98 | 99 | 100 | 101 | 102 | 103 | ..\packages\System.Spatial.5.6.0\lib\net40\System.Spatial.dll 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | True 122 | ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll 123 | 124 | 125 | 126 | 127 | 128 | 129 | True 130 | ..\packages\Microsoft.AspNet.WebPages.3.2.0\lib\net45\System.Web.Helpers.dll 131 | 132 | 133 | True 134 | ..\packages\Microsoft.AspNet.Mvc.5.2.0\lib\net45\System.Web.Mvc.dll 135 | 136 | 137 | ..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll 138 | 139 | 140 | True 141 | ..\packages\Microsoft.AspNet.Razor.3.2.0\lib\net45\System.Web.Razor.dll 142 | 143 | 144 | True 145 | ..\packages\Microsoft.AspNet.WebPages.3.2.0\lib\net45\System.Web.WebPages.dll 146 | 147 | 148 | True 149 | ..\packages\Microsoft.AspNet.WebPages.3.2.0\lib\net45\System.Web.WebPages.Deployment.dll 150 | 151 | 152 | True 153 | ..\packages\Microsoft.AspNet.WebPages.3.2.0\lib\net45\System.Web.WebPages.Razor.dll 154 | 155 | 156 | True 157 | ..\packages\WebGrease.1.5.2\lib\WebGrease.dll 158 | 159 | 160 | True 161 | ..\packages\Antlr.3.4.1.9004\lib\Antlr3.Runtime.dll 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | Global.asax 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | Web.config 203 | 204 | 205 | Web.config 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | {98D4DB83-175E-44D5-A14B-9C66EE89F037} 218 | Grains 219 | 220 | 221 | {163CCE80-C506-468C-9B28-E2FF198097E3} 222 | Interfaces 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 10.0 233 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | False 246 | True 247 | 39559 248 | / 249 | 250 | 251 | False 252 | False 253 | 254 | 255 | False 256 | 257 | 258 | 259 | 260 | 266 | -------------------------------------------------------------------------------- /samples/Hub/Client/ClientConfiguration.xml: -------------------------------------------------------------------------------- 1 |  2 | 8 | 9 | 15 | 16 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /samples/Hub/Client/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Override the default bootstrap behavior where horizontal description lists 13 | will truncate terms that are too long to fit in the left column 14 | */ 15 | .dl-horizontal dt { 16 | white-space: normal; 17 | } 18 | 19 | /* Set width on the form input elements since they're 100% wide by default */ 20 | input, 21 | select, 22 | textarea { 23 | max-width: 280px; 24 | } 25 | -------------------------------------------------------------------------------- /samples/Hub/Client/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using System.Web.Mvc; 7 | 8 | using Orleans; 9 | using Orleans.Host; 10 | 11 | using Sample.Hubs; 12 | 13 | namespace Sample.Controllers 14 | { 15 | public class HomeController : Controller 16 | { 17 | [HttpGet] 18 | public ViewResult Observe() 19 | { 20 | return View(); 21 | } 22 | 23 | [HttpGet] 24 | public ViewResult Spawn() 25 | { 26 | return View(); 27 | } 28 | 29 | [HttpPost] 30 | public async Task Spawn(int publishers) 31 | { 32 | if (!OrleansAzureClient.IsInitialized) 33 | { 34 | InitializeOrleansClient(); 35 | InitializeHubClient(); 36 | } 37 | 38 | await Init(publishers); 39 | 40 | return View("Observe"); 41 | } 42 | 43 | static void InitializeOrleansClient() 44 | { 45 | var clientConfigFile = AzureConfigUtils.ClientConfigFileLocation; 46 | 47 | if (!clientConfigFile.Exists) 48 | throw new FileNotFoundException(clientConfigFile.FullName); 49 | 50 | OrleansAzureClient.Initialize(clientConfigFile); 51 | } 52 | 53 | static void InitializeHubClient() 54 | { 55 | HubClient.Initialize(); 56 | } 57 | 58 | static Task Init(int publishers) 59 | { 60 | var activations = new List(); 61 | 62 | foreach (var i in Enumerable.Range(1, publishers)) 63 | { 64 | var activation = GrainFactory.GetGrain(i); 65 | activations.Add(activation.Init()); 66 | } 67 | 68 | return Task.WhenAll(activations.ToArray()); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /samples/Hub/Client/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Sample.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /samples/Hub/Client/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Web; 4 | using System.Web.Mvc; 5 | using System.Web.Optimization; 6 | using System.Web.Routing; 7 | 8 | namespace Sample 9 | { 10 | public class MvcApplication : HttpApplication 11 | { 12 | protected void Application_Start() 13 | { 14 | AreaRegistration.RegisterAllAreas(); 15 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 16 | RouteConfig.RegisterRoutes(RouteTable.Routes); 17 | BundleConfig.RegisterBundles(BundleTable.Bundles); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/Hub/Client/Hubs/Relay.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | using Microsoft.AspNet.SignalR.Hubs; 6 | using Microsoft.AspNet.SignalR; 7 | 8 | using Orleans.Runtime; 9 | 10 | namespace Sample.Hubs 11 | { 12 | public class Relay : Hub 13 | {} 14 | 15 | public class HubClient : IHubObserver 16 | { 17 | static IHubObserver proxy; 18 | static IHubObserver client; 19 | static IHubConnectionContext clients; 20 | 21 | public static void Initialize() 22 | { 23 | clients = GlobalHost.ConnectionManager.GetHubContext().Clients; 24 | 25 | client = new HubClient(); 26 | proxy = Task.Run(()=> HubObserverFactory.CreateObjectReference(client)).Result; 27 | 28 | Task.Run(() => Subscribe()) 29 | .Wait(); 30 | 31 | Task.Run(() => Resubscribe()); 32 | } 33 | 34 | static async Task Subscribe() 35 | { 36 | var orleans = OrleansManagementGrainFactory.GetGrain(0); 37 | var hosts = await orleans.GetHosts(true); 38 | 39 | foreach (var silo in hosts) 40 | { 41 | var address = silo.Key; 42 | var status = silo.Value; 43 | 44 | if (status != SiloStatus.Active) 45 | continue; 46 | 47 | var hub = HubGateway.GetHub(address.Endpoint); 48 | 49 | try 50 | { 51 | await hub.Subscribe(proxy); 52 | } 53 | catch (AggregateException e) 54 | { 55 | if (IsAlreadySubscribed(e)) 56 | continue; 57 | 58 | throw; 59 | } 60 | } 61 | } 62 | 63 | static bool IsAlreadySubscribed(AggregateException e) 64 | { 65 | var ex = e.InnerException as OrleansException; 66 | return ex != null && ex.Message.Contains("Cannot subscribe already subscribed observer"); 67 | } 68 | 69 | static async Task Resubscribe() 70 | { 71 | while (true) 72 | { 73 | await Task.Delay(TimeSpan.FromSeconds(120)); 74 | await Subscribe(); 75 | } 76 | } 77 | 78 | public void On(Notification[] notifications, string hub) 79 | { 80 | clients.All.receive(notifications.Select(notification => Format(notification, hub))); 81 | } 82 | 83 | static string Format(Notification notification, string hub) 84 | { 85 | var @event = notification.Event; 86 | var latency = (DateTime.Now - notification.Received).TotalSeconds; 87 | 88 | return string.Format("{0} published {1} on {2} via {4}. Latency: {3} s", 89 | @event.Sender, @event.Id, @event.Time, latency, hub); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /samples/Hub/Client/Packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /samples/Hub/Client/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Client")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Client")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("d13b5913-ac4e-4702-8b36-09f906c75fd3")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /samples/Hub/Client/Scripts/_references.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/samples/Hub/Client/Scripts/_references.js -------------------------------------------------------------------------------- /samples/Hub/Client/Scripts/jquery.validate.unobtrusive.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! 16 | ** Unobtrusive validation support library for jQuery and jQuery Validate 17 | ** Copyright (C) Microsoft Corporation. All rights reserved. 18 | */ 19 | 20 | /*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */ 21 | /*global document: false, jQuery: false */ 22 | 23 | (function ($) { 24 | var $jQval = $.validator, 25 | adapters, 26 | data_validation = "unobtrusiveValidation"; 27 | 28 | function setValidationValues(options, ruleName, value) { 29 | options.rules[ruleName] = value; 30 | if (options.message) { 31 | options.messages[ruleName] = options.message; 32 | } 33 | } 34 | 35 | function splitAndTrim(value) { 36 | return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g); 37 | } 38 | 39 | function escapeAttributeValue(value) { 40 | // As mentioned on http://api.jquery.com/category/selectors/ 41 | return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1"); 42 | } 43 | 44 | function getModelPrefix(fieldName) { 45 | return fieldName.substr(0, fieldName.lastIndexOf(".") + 1); 46 | } 47 | 48 | function appendModelPrefix(value, prefix) { 49 | if (value.indexOf("*.") === 0) { 50 | value = value.replace("*.", prefix); 51 | } 52 | return value; 53 | } 54 | 55 | function onError(error, inputElement) { // 'this' is the form element 56 | var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"), 57 | replaceAttrValue = container.attr("data-valmsg-replace"), 58 | replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null; 59 | 60 | container.removeClass("field-validation-valid").addClass("field-validation-error"); 61 | error.data("unobtrusiveContainer", container); 62 | 63 | if (replace) { 64 | container.empty(); 65 | error.removeClass("input-validation-error").appendTo(container); 66 | } 67 | else { 68 | error.hide(); 69 | } 70 | } 71 | 72 | function onErrors(event, validator) { // 'this' is the form element 73 | var container = $(this).find("[data-valmsg-summary=true]"), 74 | list = container.find("ul"); 75 | 76 | if (list && list.length && validator.errorList.length) { 77 | list.empty(); 78 | container.addClass("validation-summary-errors").removeClass("validation-summary-valid"); 79 | 80 | $.each(validator.errorList, function () { 81 | $("
  • ").html(this.message).appendTo(list); 82 | }); 83 | } 84 | } 85 | 86 | function onSuccess(error) { // 'this' is the form element 87 | var container = error.data("unobtrusiveContainer"), 88 | replaceAttrValue = container.attr("data-valmsg-replace"), 89 | replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null; 90 | 91 | if (container) { 92 | container.addClass("field-validation-valid").removeClass("field-validation-error"); 93 | error.removeData("unobtrusiveContainer"); 94 | 95 | if (replace) { 96 | container.empty(); 97 | } 98 | } 99 | } 100 | 101 | function onReset(event) { // 'this' is the form element 102 | var $form = $(this); 103 | $form.data("validator").resetForm(); 104 | $form.find(".validation-summary-errors") 105 | .addClass("validation-summary-valid") 106 | .removeClass("validation-summary-errors"); 107 | $form.find(".field-validation-error") 108 | .addClass("field-validation-valid") 109 | .removeClass("field-validation-error") 110 | .removeData("unobtrusiveContainer") 111 | .find(">*") // If we were using valmsg-replace, get the underlying error 112 | .removeData("unobtrusiveContainer"); 113 | } 114 | 115 | function validationInfo(form) { 116 | var $form = $(form), 117 | result = $form.data(data_validation), 118 | onResetProxy = $.proxy(onReset, form), 119 | defaultOptions = $jQval.unobtrusive.options || {}, 120 | execInContext = function (name, args) { 121 | var func = defaultOptions[name]; 122 | func && $.isFunction(func) && func.apply(form, args); 123 | } 124 | 125 | if (!result) { 126 | result = { 127 | options: { // options structure passed to jQuery Validate's validate() method 128 | errorClass: defaultOptions.errorClass || "input-validation-error", 129 | errorElement: defaultOptions.errorElement || "span", 130 | errorPlacement: function () { 131 | onError.apply(form, arguments); 132 | execInContext("errorPlacement", arguments); 133 | }, 134 | invalidHandler: function () { 135 | onErrors.apply(form, arguments); 136 | execInContext("invalidHandler", arguments); 137 | }, 138 | messages: {}, 139 | rules: {}, 140 | success: function () { 141 | onSuccess.apply(form, arguments); 142 | execInContext("success", arguments); 143 | } 144 | }, 145 | attachValidation: function () { 146 | $form 147 | .off("reset." + data_validation, onResetProxy) 148 | .on("reset." + data_validation, onResetProxy) 149 | .validate(this.options); 150 | }, 151 | validate: function () { // a validation function that is called by unobtrusive Ajax 152 | $form.validate(); 153 | return $form.valid(); 154 | } 155 | }; 156 | $form.data(data_validation, result); 157 | } 158 | 159 | return result; 160 | } 161 | 162 | $jQval.unobtrusive = { 163 | adapters: [], 164 | 165 | parseElement: function (element, skipAttach) { 166 | /// 167 | /// Parses a single HTML element for unobtrusive validation attributes. 168 | /// 169 | /// The HTML element to be parsed. 170 | /// [Optional] true to skip attaching the 171 | /// validation to the form. If parsing just this single element, you should specify true. 172 | /// If parsing several elements, you should specify false, and manually attach the validation 173 | /// to the form when you are finished. The default is false. 174 | var $element = $(element), 175 | form = $element.parents("form")[0], 176 | valInfo, rules, messages; 177 | 178 | if (!form) { // Cannot do client-side validation without a form 179 | return; 180 | } 181 | 182 | valInfo = validationInfo(form); 183 | valInfo.options.rules[element.name] = rules = {}; 184 | valInfo.options.messages[element.name] = messages = {}; 185 | 186 | $.each(this.adapters, function () { 187 | var prefix = "data-val-" + this.name, 188 | message = $element.attr(prefix), 189 | paramValues = {}; 190 | 191 | if (message !== undefined) { // Compare against undefined, because an empty message is legal (and falsy) 192 | prefix += "-"; 193 | 194 | $.each(this.params, function () { 195 | paramValues[this] = $element.attr(prefix + this); 196 | }); 197 | 198 | this.adapt({ 199 | element: element, 200 | form: form, 201 | message: message, 202 | params: paramValues, 203 | rules: rules, 204 | messages: messages 205 | }); 206 | } 207 | }); 208 | 209 | $.extend(rules, { "__dummy__": true }); 210 | 211 | if (!skipAttach) { 212 | valInfo.attachValidation(); 213 | } 214 | }, 215 | 216 | parse: function (selector) { 217 | /// 218 | /// Parses all the HTML elements in the specified selector. It looks for input elements decorated 219 | /// with the [data-val=true] attribute value and enables validation according to the data-val-* 220 | /// attribute values. 221 | /// 222 | /// Any valid jQuery selector. 223 | 224 | // $forms includes all forms in selector's DOM hierarchy (parent, children and self) that have at least one 225 | // element with data-val=true 226 | var $selector = $(selector), 227 | $forms = $selector.parents() 228 | .addBack() 229 | .filter("form") 230 | .add($selector.find("form")) 231 | .has("[data-val=true]"); 232 | 233 | $selector.find("[data-val=true]").each(function () { 234 | $jQval.unobtrusive.parseElement(this, true); 235 | }); 236 | 237 | $forms.each(function () { 238 | var info = validationInfo(this); 239 | if (info) { 240 | info.attachValidation(); 241 | } 242 | }); 243 | } 244 | }; 245 | 246 | adapters = $jQval.unobtrusive.adapters; 247 | 248 | adapters.add = function (adapterName, params, fn) { 249 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation. 250 | /// The name of the adapter to be added. This matches the name used 251 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 252 | /// [Optional] An array of parameter names (strings) that will 253 | /// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and 254 | /// mmmm is the parameter name). 255 | /// The function to call, which adapts the values from the HTML 256 | /// attributes into jQuery Validate rules and/or messages. 257 | /// 258 | if (!fn) { // Called with no params, just a function 259 | fn = params; 260 | params = []; 261 | } 262 | this.push({ name: adapterName, params: params, adapt: fn }); 263 | return this; 264 | }; 265 | 266 | adapters.addBool = function (adapterName, ruleName) { 267 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 268 | /// the jQuery Validate validation rule has no parameter values. 269 | /// The name of the adapter to be added. This matches the name used 270 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 271 | /// [Optional] The name of the jQuery Validate rule. If not provided, the value 272 | /// of adapterName will be used instead. 273 | /// 274 | return this.add(adapterName, function (options) { 275 | setValidationValues(options, ruleName || adapterName, true); 276 | }); 277 | }; 278 | 279 | adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) { 280 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 281 | /// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and 282 | /// one for min-and-max). The HTML parameters are expected to be named -min and -max. 283 | /// The name of the adapter to be added. This matches the name used 284 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 285 | /// The name of the jQuery Validate rule to be used when you only 286 | /// have a minimum value. 287 | /// The name of the jQuery Validate rule to be used when you only 288 | /// have a maximum value. 289 | /// The name of the jQuery Validate rule to be used when you 290 | /// have both a minimum and maximum value. 291 | /// [Optional] The name of the HTML attribute that 292 | /// contains the minimum value. The default is "min". 293 | /// [Optional] The name of the HTML attribute that 294 | /// contains the maximum value. The default is "max". 295 | /// 296 | return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) { 297 | var min = options.params.min, 298 | max = options.params.max; 299 | 300 | if (min && max) { 301 | setValidationValues(options, minMaxRuleName, [min, max]); 302 | } 303 | else if (min) { 304 | setValidationValues(options, minRuleName, min); 305 | } 306 | else if (max) { 307 | setValidationValues(options, maxRuleName, max); 308 | } 309 | }); 310 | }; 311 | 312 | adapters.addSingleVal = function (adapterName, attribute, ruleName) { 313 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 314 | /// the jQuery Validate validation rule has a single value. 315 | /// The name of the adapter to be added. This matches the name used 316 | /// in the data-val-nnnn HTML attribute(where nnnn is the adapter name). 317 | /// [Optional] The name of the HTML attribute that contains the value. 318 | /// The default is "val". 319 | /// [Optional] The name of the jQuery Validate rule. If not provided, the value 320 | /// of adapterName will be used instead. 321 | /// 322 | return this.add(adapterName, [attribute || "val"], function (options) { 323 | setValidationValues(options, ruleName || adapterName, options.params[attribute]); 324 | }); 325 | }; 326 | 327 | $jQval.addMethod("__dummy__", function (value, element, params) { 328 | return true; 329 | }); 330 | 331 | $jQval.addMethod("regex", function (value, element, params) { 332 | var match; 333 | if (this.optional(element)) { 334 | return true; 335 | } 336 | 337 | match = new RegExp(params).exec(value); 338 | return (match && (match.index === 0) && (match[0].length === value.length)); 339 | }); 340 | 341 | $jQval.addMethod("nonalphamin", function (value, element, nonalphamin) { 342 | var match; 343 | if (nonalphamin) { 344 | match = value.match(/\W/g); 345 | match = match && match.length >= nonalphamin; 346 | } 347 | return match; 348 | }); 349 | 350 | if ($jQval.methods.extension) { 351 | adapters.addSingleVal("accept", "mimtype"); 352 | adapters.addSingleVal("extension", "extension"); 353 | } else { 354 | // for backward compatibility, when the 'extension' validation method does not exist, such as with versions 355 | // of JQuery Validation plugin prior to 1.10, we should use the 'accept' method for 356 | // validating the extension, and ignore mime-type validations as they are not supported. 357 | adapters.addSingleVal("extension", "extension", "accept"); 358 | } 359 | 360 | adapters.addSingleVal("regex", "pattern"); 361 | adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"); 362 | adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range"); 363 | adapters.addMinMax("minlength", "minlength").addMinMax("maxlength", "minlength", "maxlength"); 364 | adapters.add("equalto", ["other"], function (options) { 365 | var prefix = getModelPrefix(options.element.name), 366 | other = options.params.other, 367 | fullOtherName = appendModelPrefix(other, prefix), 368 | element = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(fullOtherName) + "']")[0]; 369 | 370 | setValidationValues(options, "equalTo", element); 371 | }); 372 | adapters.add("required", function (options) { 373 | // jQuery Validate equates "required" with "mandatory" for checkbox elements 374 | if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") { 375 | setValidationValues(options, "required", true); 376 | } 377 | }); 378 | adapters.add("remote", ["url", "type", "additionalfields"], function (options) { 379 | var value = { 380 | url: options.params.url, 381 | type: options.params.type || "GET", 382 | data: {} 383 | }, 384 | prefix = getModelPrefix(options.element.name); 385 | 386 | $.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) { 387 | var paramName = appendModelPrefix(fieldName, prefix); 388 | value.data[paramName] = function () { 389 | return $(options.form).find(":input").filter("[name='" + escapeAttributeValue(paramName) + "']").val(); 390 | }; 391 | }); 392 | 393 | setValidationValues(options, "remote", value); 394 | }); 395 | adapters.add("password", ["min", "nonalphamin", "regex"], function (options) { 396 | if (options.params.min) { 397 | setValidationValues(options, "minlength", options.params.min); 398 | } 399 | if (options.params.nonalphamin) { 400 | setValidationValues(options, "nonalphamin", options.params.nonalphamin); 401 | } 402 | if (options.params.regex) { 403 | setValidationValues(options, "regex", options.params.regex); 404 | } 405 | }); 406 | 407 | $(function () { 408 | $jQval.unobtrusive.parse(document); 409 | }); 410 | }(jQuery)); -------------------------------------------------------------------------------- /samples/Hub/Client/Scripts/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /* 16 | ** Unobtrusive validation support library for jQuery and jQuery Validate 17 | ** Copyright (C) Microsoft Corporation. All rights reserved. 18 | */ 19 | (function(a){var d=a.validator,b,e="unobtrusiveValidation";function c(a,b,c){a.rules[b]=c;if(a.message)a.messages[b]=a.message}function j(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function f(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function h(a){return a.substr(0,a.lastIndexOf(".")+1)}function g(a,b){if(a.indexOf("*.")===0)a=a.replace("*.",b);return a}function m(c,e){var b=a(this).find("[data-valmsg-for='"+f(e[0].name)+"']"),d=b.attr("data-valmsg-replace"),g=d?a.parseJSON(d)!==false:null;b.removeClass("field-validation-valid").addClass("field-validation-error");c.data("unobtrusiveContainer",b);if(g){b.empty();c.removeClass("input-validation-error").appendTo(b)}else c.hide()}function l(e,d){var c=a(this).find("[data-valmsg-summary=true]"),b=c.find("ul");if(b&&b.length&&d.errorList.length){b.empty();c.addClass("validation-summary-errors").removeClass("validation-summary-valid");a.each(d.errorList,function(){a("
  • ").html(this.message).appendTo(b)})}}function k(d){var b=d.data("unobtrusiveContainer"),c=b.attr("data-valmsg-replace"),e=c?a.parseJSON(c):null;if(b){b.addClass("field-validation-valid").removeClass("field-validation-error");d.removeData("unobtrusiveContainer");e&&b.empty()}}function n(){var b=a(this);b.data("validator").resetForm();b.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors");b.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}function i(b){var c=a(b),f=c.data(e),i=a.proxy(n,b),g=d.unobtrusive.options||{},h=function(e,d){var c=g[e];c&&a.isFunction(c)&&c.apply(b,d)};if(!f){f={options:{errorClass:g.errorClass||"input-validation-error",errorElement:g.errorElement||"span",errorPlacement:function(){m.apply(b,arguments);h("errorPlacement",arguments)},invalidHandler:function(){l.apply(b,arguments);h("invalidHandler",arguments)},messages:{},rules:{},success:function(){k.apply(b,arguments);h("success",arguments)}},attachValidation:function(){c.off("reset."+e,i).on("reset."+e,i).validate(this.options)},validate:function(){c.validate();return c.valid()}};c.data(e,f)}return f}d.unobtrusive={adapters:[],parseElement:function(b,h){var d=a(b),f=d.parents("form")[0],c,e,g;if(!f)return;c=i(f);c.options.rules[b.name]=e={};c.options.messages[b.name]=g={};a.each(this.adapters,function(){var c="data-val-"+this.name,i=d.attr(c),h={};if(i!==undefined){c+="-";a.each(this.params,function(){h[this]=d.attr(c+this)});this.adapt({element:b,form:f,message:i,params:h,rules:e,messages:g})}});a.extend(e,{__dummy__:true});!h&&c.attachValidation()},parse:function(c){var b=a(c),e=b.parents().addBack().filter("form").add(b.find("form")).has("[data-val=true]");b.find("[data-val=true]").each(function(){d.unobtrusive.parseElement(this,true)});e.each(function(){var a=i(this);a&&a.attachValidation()})}};b=d.unobtrusive.adapters;b.add=function(c,a,b){if(!b){b=a;a=[]}this.push({name:c,params:a,adapt:b});return this};b.addBool=function(a,b){return this.add(a,function(d){c(d,b||a,true)})};b.addMinMax=function(e,g,f,a,d,b){return this.add(e,[d||"min",b||"max"],function(b){var e=b.params.min,d=b.params.max;if(e&&d)c(b,a,[e,d]);else if(e)c(b,g,e);else d&&c(b,f,d)})};b.addSingleVal=function(a,b,d){return this.add(a,[b||"val"],function(e){c(e,d||a,e.params[b])})};d.addMethod("__dummy__",function(){return true});d.addMethod("regex",function(b,c,d){var a;if(this.optional(c))return true;a=(new RegExp(d)).exec(b);return a&&a.index===0&&a[0].length===b.length});d.addMethod("nonalphamin",function(c,d,b){var a;if(b){a=c.match(/\W/g);a=a&&a.length>=b}return a});if(d.methods.extension){b.addSingleVal("accept","mimtype");b.addSingleVal("extension","extension")}else b.addSingleVal("extension","extension","accept");b.addSingleVal("regex","pattern");b.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");b.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range");b.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength");b.add("equalto",["other"],function(b){var i=h(b.element.name),j=b.params.other,d=g(j,i),e=a(b.form).find(":input").filter("[name='"+f(d)+"']")[0];c(b,"equalTo",e)});b.add("required",function(a){(a.element.tagName.toUpperCase()!=="INPUT"||a.element.type.toUpperCase()!=="CHECKBOX")&&c(a,"required",true)});b.add("remote",["url","type","additionalfields"],function(b){var d={url:b.params.url,type:b.params.type||"GET",data:{}},e=h(b.element.name);a.each(j(b.params.additionalfields||b.element.name),function(i,h){var c=g(h,e);d.data[c]=function(){return a(b.form).find(":input").filter("[name='"+f(c)+"']").val()}});c(b,"remote",d)});b.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&c(a,"minlength",a.params.min);a.params.nonalphamin&&c(a,"nonalphamin",a.params.nonalphamin);a.params.regex&&c(a,"regex",a.params.regex)});a(function(){d.unobtrusive.parse(document)})})(jQuery); -------------------------------------------------------------------------------- /samples/Hub/Client/Scripts/respond.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ 16 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ 17 | window.matchMedia = window.matchMedia || (function(doc, undefined){ 18 | 19 | var bool, 20 | docElem = doc.documentElement, 21 | refNode = docElem.firstElementChild || docElem.firstChild, 22 | // fakeBody required for 23 | fakeBody = doc.createElement('body'), 24 | div = doc.createElement('div'); 25 | 26 | div.id = 'mq-test-1'; 27 | div.style.cssText = "position:absolute;top:-100em"; 28 | fakeBody.style.background = "none"; 29 | fakeBody.appendChild(div); 30 | 31 | return function(q){ 32 | 33 | div.innerHTML = '­'; 34 | 35 | docElem.insertBefore(fakeBody, refNode); 36 | bool = div.offsetWidth == 42; 37 | docElem.removeChild(fakeBody); 38 | 39 | return { matches: bool, media: q }; 40 | }; 41 | 42 | })(document); 43 | 44 | 45 | 46 | 47 | /*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 48 | (function( win ){ 49 | //exposed namespace 50 | win.respond = {}; 51 | 52 | //define update even in native-mq-supporting browsers, to avoid errors 53 | respond.update = function(){}; 54 | 55 | //expose media query support flag for external use 56 | respond.mediaQueriesSupported = win.matchMedia && win.matchMedia( "only all" ).matches; 57 | 58 | //if media queries are supported, exit here 59 | if( respond.mediaQueriesSupported ){ return; } 60 | 61 | //define vars 62 | var doc = win.document, 63 | docElem = doc.documentElement, 64 | mediastyles = [], 65 | rules = [], 66 | appendedEls = [], 67 | parsedSheets = {}, 68 | resizeThrottle = 30, 69 | head = doc.getElementsByTagName( "head" )[0] || docElem, 70 | base = doc.getElementsByTagName( "base" )[0], 71 | links = head.getElementsByTagName( "link" ), 72 | requestQueue = [], 73 | 74 | //loop stylesheets, send text content to translate 75 | ripCSS = function(){ 76 | var sheets = links, 77 | sl = sheets.length, 78 | i = 0, 79 | //vars for loop: 80 | sheet, href, media, isCSS; 81 | 82 | for( ; i < sl; i++ ){ 83 | sheet = sheets[ i ], 84 | href = sheet.href, 85 | media = sheet.media, 86 | isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet"; 87 | 88 | //only links plz and prevent re-parsing 89 | if( !!href && isCSS && !parsedSheets[ href ] ){ 90 | // selectivizr exposes css through the rawCssText expando 91 | if (sheet.styleSheet && sheet.styleSheet.rawCssText) { 92 | translate( sheet.styleSheet.rawCssText, href, media ); 93 | parsedSheets[ href ] = true; 94 | } else { 95 | if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base) 96 | || href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){ 97 | requestQueue.push( { 98 | href: href, 99 | media: media 100 | } ); 101 | } 102 | } 103 | } 104 | } 105 | makeRequests(); 106 | }, 107 | 108 | //recurse through request queue, get css text 109 | makeRequests = function(){ 110 | if( requestQueue.length ){ 111 | var thisRequest = requestQueue.shift(); 112 | 113 | ajax( thisRequest.href, function( styles ){ 114 | translate( styles, thisRequest.href, thisRequest.media ); 115 | parsedSheets[ thisRequest.href ] = true; 116 | makeRequests(); 117 | } ); 118 | } 119 | }, 120 | 121 | //find media blocks in css text, convert to style blocks 122 | translate = function( styles, href, media ){ 123 | var qs = styles.match( /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi ), 124 | ql = qs && qs.length || 0, 125 | //try to get CSS path 126 | href = href.substring( 0, href.lastIndexOf( "/" )), 127 | repUrls = function( css ){ 128 | return css.replace( /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + href + "$2$3" ); 129 | }, 130 | useMedia = !ql && media, 131 | //vars used in loop 132 | i = 0, 133 | j, fullq, thisq, eachq, eql; 134 | 135 | //if path exists, tack on trailing slash 136 | if( href.length ){ href += "/"; } 137 | 138 | //if no internal queries exist, but media attr does, use that 139 | //note: this currently lacks support for situations where a media attr is specified on a link AND 140 | //its associated stylesheet has internal CSS media queries. 141 | //In those cases, the media attribute will currently be ignored. 142 | if( useMedia ){ 143 | ql = 1; 144 | } 145 | 146 | 147 | for( ; i < ql; i++ ){ 148 | j = 0; 149 | 150 | //media attr 151 | if( useMedia ){ 152 | fullq = media; 153 | rules.push( repUrls( styles ) ); 154 | } 155 | //parse for styles 156 | else{ 157 | fullq = qs[ i ].match( /@media *([^\{]+)\{([\S\s]+?)$/ ) && RegExp.$1; 158 | rules.push( RegExp.$2 && repUrls( RegExp.$2 ) ); 159 | } 160 | 161 | eachq = fullq.split( "," ); 162 | eql = eachq.length; 163 | 164 | for( ; j < eql; j++ ){ 165 | thisq = eachq[ j ]; 166 | mediastyles.push( { 167 | media : thisq.split( "(" )[ 0 ].match( /(only\s+)?([a-zA-Z]+)\s?/ ) && RegExp.$2 || "all", 168 | rules : rules.length - 1, 169 | hasquery: thisq.indexOf("(") > -1, 170 | minw : thisq.match( /\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ), 171 | maxw : thisq.match( /\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ) 172 | } ); 173 | } 174 | } 175 | 176 | applyMedia(); 177 | }, 178 | 179 | lastCall, 180 | 181 | resizeDefer, 182 | 183 | // returns the value of 1em in pixels 184 | getEmValue = function() { 185 | var ret, 186 | div = doc.createElement('div'), 187 | body = doc.body, 188 | fakeUsed = false; 189 | 190 | div.style.cssText = "position:absolute;font-size:1em;width:1em"; 191 | 192 | if( !body ){ 193 | body = fakeUsed = doc.createElement( "body" ); 194 | body.style.background = "none"; 195 | } 196 | 197 | body.appendChild( div ); 198 | 199 | docElem.insertBefore( body, docElem.firstChild ); 200 | 201 | ret = div.offsetWidth; 202 | 203 | if( fakeUsed ){ 204 | docElem.removeChild( body ); 205 | } 206 | else { 207 | body.removeChild( div ); 208 | } 209 | 210 | //also update eminpx before returning 211 | ret = eminpx = parseFloat(ret); 212 | 213 | return ret; 214 | }, 215 | 216 | //cached container for 1em value, populated the first time it's needed 217 | eminpx, 218 | 219 | //enable/disable styles 220 | applyMedia = function( fromResize ){ 221 | var name = "clientWidth", 222 | docElemProp = docElem[ name ], 223 | currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp, 224 | styleBlocks = {}, 225 | lastLink = links[ links.length-1 ], 226 | now = (new Date()).getTime(); 227 | 228 | //throttle resize calls 229 | if( fromResize && lastCall && now - lastCall < resizeThrottle ){ 230 | clearTimeout( resizeDefer ); 231 | resizeDefer = setTimeout( applyMedia, resizeThrottle ); 232 | return; 233 | } 234 | else { 235 | lastCall = now; 236 | } 237 | 238 | for( var i in mediastyles ){ 239 | var thisstyle = mediastyles[ i ], 240 | min = thisstyle.minw, 241 | max = thisstyle.maxw, 242 | minnull = min === null, 243 | maxnull = max === null, 244 | em = "em"; 245 | 246 | if( !!min ){ 247 | min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); 248 | } 249 | if( !!max ){ 250 | max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); 251 | } 252 | 253 | // if there's no media query at all (the () part), or min or max is not null, and if either is present, they're true 254 | if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){ 255 | if( !styleBlocks[ thisstyle.media ] ){ 256 | styleBlocks[ thisstyle.media ] = []; 257 | } 258 | styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] ); 259 | } 260 | } 261 | 262 | //remove any existing respond style element(s) 263 | for( var i in appendedEls ){ 264 | if( appendedEls[ i ] && appendedEls[ i ].parentNode === head ){ 265 | head.removeChild( appendedEls[ i ] ); 266 | } 267 | } 268 | 269 | //inject active styles, grouped by media type 270 | for( var i in styleBlocks ){ 271 | var ss = doc.createElement( "style" ), 272 | css = styleBlocks[ i ].join( "\n" ); 273 | 274 | ss.type = "text/css"; 275 | ss.media = i; 276 | 277 | //originally, ss was appended to a documentFragment and sheets were appended in bulk. 278 | //this caused crashes in IE in a number of circumstances, such as when the HTML element had a bg image set, so appending beforehand seems best. Thanks to @dvelyk for the initial research on this one! 279 | head.insertBefore( ss, lastLink.nextSibling ); 280 | 281 | if ( ss.styleSheet ){ 282 | ss.styleSheet.cssText = css; 283 | } 284 | else { 285 | ss.appendChild( doc.createTextNode( css ) ); 286 | } 287 | 288 | //push to appendedEls to track for later removal 289 | appendedEls.push( ss ); 290 | } 291 | }, 292 | //tweaked Ajax functions from Quirksmode 293 | ajax = function( url, callback ) { 294 | var req = xmlHttp(); 295 | if (!req){ 296 | return; 297 | } 298 | req.open( "GET", url, true ); 299 | req.onreadystatechange = function () { 300 | if ( req.readyState != 4 || req.status != 200 && req.status != 304 ){ 301 | return; 302 | } 303 | callback( req.responseText ); 304 | } 305 | if ( req.readyState == 4 ){ 306 | return; 307 | } 308 | req.send( null ); 309 | }, 310 | //define ajax obj 311 | xmlHttp = (function() { 312 | var xmlhttpmethod = false; 313 | try { 314 | xmlhttpmethod = new XMLHttpRequest(); 315 | } 316 | catch( e ){ 317 | xmlhttpmethod = new ActiveXObject( "Microsoft.XMLHTTP" ); 318 | } 319 | return function(){ 320 | return xmlhttpmethod; 321 | }; 322 | })(); 323 | 324 | //translate CSS 325 | ripCSS(); 326 | 327 | //expose update for re-running respond later on 328 | respond.update = ripCSS; 329 | 330 | //adjust on resize 331 | function callMedia(){ 332 | applyMedia( true ); 333 | } 334 | if( win.addEventListener ){ 335 | win.addEventListener( "resize", callMedia, false ); 336 | } 337 | else if( win.attachEvent ){ 338 | win.attachEvent( "onresize", callMedia ); 339 | } 340 | })(this); 341 | -------------------------------------------------------------------------------- /samples/Hub/Client/Scripts/respond.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ 16 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ 17 | window.matchMedia=window.matchMedia||(function(e,f){var c,a=e.documentElement,b=a.firstElementChild||a.firstChild,d=e.createElement("body"),g=e.createElement("div");g.id="mq-test-1";g.style.cssText="position:absolute;top:-100em";d.style.background="none";d.appendChild(g);return function(h){g.innerHTML='­';a.insertBefore(d,b);c=g.offsetWidth==42;a.removeChild(d);return{matches:c,media:h}}})(document); 18 | 19 | /*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 20 | (function(e){e.respond={};respond.update=function(){};respond.mediaQueriesSupported=e.matchMedia&&e.matchMedia("only all").matches;if(respond.mediaQueriesSupported){return}var w=e.document,s=w.documentElement,i=[],k=[],q=[],o={},h=30,f=w.getElementsByTagName("head")[0]||s,g=w.getElementsByTagName("base")[0],b=f.getElementsByTagName("link"),d=[],a=function(){var D=b,y=D.length,B=0,A,z,C,x;for(;B-1,minw:F.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:F.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}}j()},l,r,v=function(){var z,A=w.createElement("div"),x=w.body,y=false;A.style.cssText="position:absolute;font-size:1em;width:1em";if(!x){x=y=w.createElement("body");x.style.background="none"}x.appendChild(A);s.insertBefore(x,s.firstChild);z=A.offsetWidth;if(y){s.removeChild(x)}else{x.removeChild(A)}z=p=parseFloat(z);return z},p,j=function(I){var x="clientWidth",B=s[x],H=w.compatMode==="CSS1Compat"&&B||w.body[x]||B,D={},G=b[b.length-1],z=(new Date()).getTime();if(I&&l&&z-l-1?(p||v()):1)}if(!!J){J=parseFloat(J)*(J.indexOf(y)>-1?(p||v()):1)}if(!K.hasquery||(!A||!L)&&(A||H>=C)&&(L||H<=J)){if(!D[K.media]){D[K.media]=[]}D[K.media].push(k[K.rules])}}for(var E in q){if(q[E]&&q[E].parentNode===f){f.removeChild(q[E])}}for(var E in D){var M=w.createElement("style"),F=D[E].join("\n");M.type="text/css";M.media=E;f.insertBefore(M,G.nextSibling);if(M.styleSheet){M.styleSheet.cssText=F}else{M.appendChild(w.createTextNode(F))}q.push(M)}},n=function(x,z){var y=c();if(!y){return}y.open("GET",x,true);y.onreadystatechange=function(){if(y.readyState!=4||y.status!=200&&y.status!=304){return}z(y.responseText)};if(y.readyState==4){return}y.send(null)},c=(function(){var x=false;try{x=new XMLHttpRequest()}catch(y){x=new ActiveXObject("Microsoft.XMLHTTP")}return function(){return x}})();a();respond.update=a;function t(){j(true)}if(e.addEventListener){e.addEventListener("resize",t,false)}else{if(e.attachEvent){e.attachEvent("onresize",t)}}})(this); -------------------------------------------------------------------------------- /samples/Hub/Client/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Owin; 5 | using Microsoft.Owin; 6 | [assembly: OwinStartup(typeof(Sample.Startup))] 7 | 8 | namespace Sample 9 | { 10 | public class Startup 11 | { 12 | public void Configuration(IAppBuilder app) 13 | { 14 | app.MapSignalR(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /samples/Hub/Client/Views/Home/Observe.cshtml: -------------------------------------------------------------------------------- 1 | 
    2 |
    Notifications:
    3 |
    4 |
    5 | @section scripts { 6 | 7 | 8 | 30 | } -------------------------------------------------------------------------------- /samples/Hub/Client/Views/Home/Spawn.cshtml: -------------------------------------------------------------------------------- 1 | 
    2 | 3 |
    4 |
    # Grains:
    5 | @Html.TextBox("publishers") 6 | 7 |
    8 | 9 |
    10 | 11 | -------------------------------------------------------------------------------- /samples/Hub/Client/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = null; 3 | } 4 | 5 | 6 | 7 | 8 | 9 | Error 10 | 11 | 12 |
    13 |

    Error.

    14 |

    An error occurred while processing your request.

    15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/Hub/Client/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Orleans Hub Sample Application 7 | @Styles.Render("~/Content/css") 8 | @Scripts.Render("~/bundles/modernizr") 9 | 10 | 11 |
    12 | @RenderBody() 13 |
    14 | @Scripts.Render("~/bundles/jquery") 15 | @Scripts.Render("~/bundles/bootstrap") 16 | @RenderSection("scripts", required: false) 17 | 18 | 19 | -------------------------------------------------------------------------------- /samples/Hub/Client/Views/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 |
    7 |
    8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /samples/Hub/Client/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /samples/Hub/Client/Web.Debug.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /samples/Hub/Client/Web.Release.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /samples/Hub/Client/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /samples/Hub/Client/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/samples/Hub/Client/favicon.ico -------------------------------------------------------------------------------- /samples/Hub/Client/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/samples/Hub/Client/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /samples/Hub/Client/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/samples/Hub/Client/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /samples/Hub/Client/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrleansContrib/DesignPatterns/7d18dd3103ace6b9b3257b08fac3b5948efd5537/samples/Hub/Client/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /samples/Hub/Grains/Grains.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {98D4DB83-175E-44D5-A14B-9C66EE89F037} 9 | Library 10 | Properties 11 | Sample 12 | Sample.Grains 13 | v4.5 14 | 512 15 | 16 | 17 | Program 18 | $(OrleansSDK)\LocalSilo\OrleansHost.exe 19 | $(OrleansSDK)\LocalSilo 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | $(OrleansSDK)\Binaries\OrleansClient\Orleans.dll 49 | False 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | {163cce80-c506-468c-9b28-e2ff198097e3} 63 | Interfaces 64 | 65 | 66 | 67 | 68 | Server 69 | 70 | 71 | 72 | 73 | if exist "$(OrleansSDK)\LocalSilo" ( 74 | if not exist "$(OrleansSDK)\LocalSilo\Applications" (md "$(OrleansSDK)\LocalSilo\Applications") 75 | if not exist "$(OrleansSDK)\LocalSilo\Applications\$(RootNamespace)" (md "$(OrleansSDK)\LocalSilo\Applications\$(RootNamespace)") 76 | copy /y *.dll "$(OrleansSDK)\LocalSilo\Applications\$(RootNamespace)\" 77 | copy /y *.pdb "$(OrleansSDK)\LocalSilo\Applications\$(RootNamespace)\" 78 | ) 79 | if exist "$(OrleansSDK)\Binaries" ( 80 | if not exist "$(OrleansSDK)\Binaries\Applications" (md "$(OrleansSDK)\Binaries\Applications") 81 | if not exist "$(OrleansSDK)\Binaries\Applications\$(RootNamespace)" (md "$(OrleansSDK)\Binaries\Applications\$(RootNamespace)") 82 | copy /y *.dll "$(OrleansSDK)\Binaries\Applications\$(RootNamespace)\" 83 | copy /y *.pdb "$(OrleansSDK)\Binaries\Applications\$(RootNamespace)\" 84 | ) 85 | 86 | 87 | 91 | -------------------------------------------------------------------------------- /samples/Hub/Grains/HubBufferGrain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using Orleans; 7 | using Orleans.Concurrency; 8 | 9 | namespace Sample 10 | { 11 | [Reentrant] 12 | public class HubBufferGrain : Grain, IHubBuffer 13 | { 14 | readonly TimeSpan flushPeriod = TimeSpan.FromSeconds(1); 15 | 16 | IHub hub; 17 | Queue buffer; 18 | 19 | public override Task ActivateAsync() 20 | { 21 | hub = HubGateway.GetLocalHub(); 22 | buffer = new Queue(); 23 | 24 | RegisterTimer(Flush, null, flushPeriod, flushPeriod); 25 | return TaskDone.Done; 26 | } 27 | 28 | Task Flush(object arg) 29 | { 30 | if (buffer.Count == 0) 31 | return TaskDone.Done; 32 | 33 | var notifications = buffer.ToArray(); 34 | buffer.Clear(); 35 | 36 | return hub.Publish(notifications); 37 | } 38 | 39 | public Task Publish(Event @event) 40 | { 41 | buffer.Enqueue(new Notification(@event, DateTime.Now)); 42 | return TaskDone.Done; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /samples/Hub/Grains/HubGateway.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | 7 | using Orleans.Providers; 8 | 9 | namespace Sample 10 | { 11 | public class HubGateway : IBootstrapProvider 12 | { 13 | public async Task Init(string name, IProviderRuntime providerRuntime, IProviderConfiguration config) 14 | { 15 | Name = name; 16 | await GetLocalHub().Init(); 17 | } 18 | 19 | public string Name 20 | { 21 | get; private set; 22 | } 23 | 24 | public static Task Publish(Event e) 25 | { 26 | return HubBufferFactory.GetGrain(0).Publish(e); 27 | } 28 | 29 | public static IHub GetHub(IPEndPoint endpoint) 30 | { 31 | return HubFactory.GetGrain(HubId(endpoint)); 32 | } 33 | 34 | public static IHub GetLocalHub() 35 | { 36 | return GetHub(LocalEndPoint); 37 | } 38 | 39 | static string HubId(IPEndPoint endpoint) 40 | { 41 | Debug.Assert(endpoint != null); 42 | return "HUB" + endpoint.Address; 43 | } 44 | 45 | public static string LocalHubId() 46 | { 47 | return HubId(LocalEndPoint); 48 | } 49 | 50 | public static IPAddress LocalAddress() 51 | { 52 | return LocalEndPoint.Address; 53 | } 54 | 55 | public static IPEndPoint LocalEndPoint 56 | { 57 | get; set; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /samples/Hub/Grains/HubGrain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | using Orleans; 6 | using Orleans.Concurrency; 7 | 8 | namespace Sample 9 | { 10 | [Reentrant] 11 | public class HubGrain : Grain, IHub 12 | { 13 | ObserverSubscriptionManager listeners; 14 | 15 | public override Task ActivateAsync() 16 | { 17 | listeners = new ObserverSubscriptionManager(); 18 | return TaskDone.Done; 19 | } 20 | 21 | public Task Init() 22 | { 23 | return TaskDone.Done; 24 | } 25 | 26 | public Task Subscribe(IHubObserver observer) 27 | { 28 | listeners.Subscribe(observer); 29 | return TaskDone.Done; 30 | } 31 | 32 | public Task Unsubscribe(IHubObserver observer) 33 | { 34 | listeners.Unsubscribe(observer); 35 | return TaskDone.Done; 36 | } 37 | 38 | public Task Publish(Notification[] notifications) 39 | { 40 | if (listeners.Count > 0) 41 | listeners.Notify(x => x.On(notifications, HubGateway.LocalHubId())); 42 | 43 | return TaskDone.Done; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /samples/Hub/Grains/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | [assembly: AssemblyTitle("Sample.Grains")] 4 | [assembly: AssemblyVersion("1.0.0.0")] 5 | [assembly: AssemblyFileVersion("1.0.0.0")] 6 | -------------------------------------------------------------------------------- /samples/Hub/Grains/PublisherGrain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | using Orleans; 6 | 7 | namespace Sample 8 | { 9 | public class PublisherGrain : Grain, IPublisher 10 | { 11 | static readonly Random rand = new Random(); 12 | 13 | Task IPublisher.Init() 14 | { 15 | return TaskDone.Done; 16 | } 17 | 18 | public override Task ActivateAsync() 19 | { 20 | RegisterTimer(Publish, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(rand.Next(3, 10))); 21 | return TaskDone.Done; 22 | } 23 | 24 | Task Publish(object arg) 25 | { 26 | return HubGateway.Publish(Event()); 27 | } 28 | 29 | Event Event() 30 | { 31 | var senderId = ThisId() + "##" + HubGateway.LocalAddress(); 32 | var eventId = DateTime.Now.Ticks ^ ThisId(); 33 | return new Event(senderId, eventId, DateTime.Now); 34 | } 35 | 36 | long ThisId() 37 | { 38 | return this.GetPrimaryKeyLong(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /samples/Hub/Hub.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30723.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{CC5FD16D-436D-48AD-A40C-5A424C6E3E79}") = "Azure", "Azure\Azure.ccproj", "{492563FC-84B6-41C5-A776-394A8BBF72C6}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silo", "Silo\Silo.csproj", "{6B7E7C7D-FCB5-415C-A361-E653CB9EAD72}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Interfaces", "Interfaces\Interfaces.csproj", "{163CCE80-C506-468C-9B28-E2FF198097E3}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grains", "Grains\Grains.csproj", "{98D4DB83-175E-44D5-A14B-9C66EE89F037}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{3D7D093A-AFE7-438C-B9AA-9C3097E86050}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {492563FC-84B6-41C5-A776-394A8BBF72C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {492563FC-84B6-41C5-A776-394A8BBF72C6}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {492563FC-84B6-41C5-A776-394A8BBF72C6}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {492563FC-84B6-41C5-A776-394A8BBF72C6}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {6B7E7C7D-FCB5-415C-A361-E653CB9EAD72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {6B7E7C7D-FCB5-415C-A361-E653CB9EAD72}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {6B7E7C7D-FCB5-415C-A361-E653CB9EAD72}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {6B7E7C7D-FCB5-415C-A361-E653CB9EAD72}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {163CCE80-C506-468C-9B28-E2FF198097E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {163CCE80-C506-468C-9B28-E2FF198097E3}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {163CCE80-C506-468C-9B28-E2FF198097E3}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {163CCE80-C506-468C-9B28-E2FF198097E3}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {98D4DB83-175E-44D5-A14B-9C66EE89F037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {98D4DB83-175E-44D5-A14B-9C66EE89F037}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {98D4DB83-175E-44D5-A14B-9C66EE89F037}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {98D4DB83-175E-44D5-A14B-9C66EE89F037}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {3D7D093A-AFE7-438C-B9AA-9C3097E86050}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {3D7D093A-AFE7-438C-B9AA-9C3097E86050}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {3D7D093A-AFE7-438C-B9AA-9C3097E86050}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {3D7D093A-AFE7-438C-B9AA-9C3097E86050}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /samples/Hub/Interfaces/IHub.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | using Orleans; 6 | using Orleans.Concurrency; 7 | using Orleans.Placement; 8 | 9 | namespace Sample 10 | { 11 | [StatelessWorker] 12 | public interface IHubBuffer : IGrainWithIntegerKey 13 | { 14 | Task Publish(Event e); 15 | } 16 | 17 | [ExtendedPrimaryKey] 18 | [PreferLocalPlacement] 19 | public interface IHub : IGrainWithStringKey 20 | { 21 | Task Init(); 22 | 23 | Task Subscribe(IHubObserver observer); 24 | Task Unsubscribe(IHubObserver observer); 25 | 26 | Task Publish(Notification[] notifications); 27 | } 28 | 29 | public interface IHubObserver : IGrainObserver 30 | { 31 | void On(Notification[] notifications, string hub); 32 | } 33 | 34 | [Immutable] 35 | [Serializable] 36 | public class Event 37 | { 38 | public readonly string Sender; 39 | public readonly long Id; 40 | public readonly DateTime Time; 41 | 42 | public Event(string sender, long id, DateTime time) 43 | { 44 | Sender = sender; 45 | Id = id; 46 | Time = time; 47 | } 48 | } 49 | 50 | [Immutable] 51 | [Serializable] 52 | public class Notification 53 | { 54 | public readonly Event Event; 55 | public readonly DateTime Received; 56 | 57 | public Notification(Event e, DateTime received) 58 | { 59 | Event = e; 60 | Received = received; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /samples/Hub/Interfaces/IPublisher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | namespace Sample 6 | { 7 | public interface IPublisher : Orleans.IGrainWithIntegerKey 8 | { 9 | Task Init(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /samples/Hub/Interfaces/Interfaces.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {163CCE80-C506-468C-9B28-E2FF198097E3} 9 | Library 10 | Properties 11 | Sample 12 | Sample.Interfaces 13 | v4.5 14 | 512 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | $(OrleansSDK)\Binaries\OrleansClient\Orleans.dll 38 | False 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | Client 50 | 51 | 52 | 53 | 54 | 55 | 56 | 63 | -------------------------------------------------------------------------------- /samples/Hub/Interfaces/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | [assembly: AssemblyTitle("Sample.Interfaces")] 4 | [assembly: AssemblyVersion("1.0.0.0")] 5 | [assembly: AssemblyFileVersion("1.0.0.0")] 6 | -------------------------------------------------------------------------------- /samples/Hub/README.md: -------------------------------------------------------------------------------- 1 | ### Prerequisites 2 | 3 | * Azure SDK 2.4 4 | * Azure Compute Emulator v2.4 5 | * Preview refresh of [Orleans SDK](https://orleans.codeplex.com/releases/view/134727) built against Azure SDK 2.4 6 | 7 | ### How to run 8 | 9 | * Start compute emulator as administrator. 10 | * Make sure you've also started Visual Studio as administrator. 11 | * Build solution and make sure there is no build errors due to missing packages 12 | * Choose Azure project to be a "Startup project" 13 | * Hit F5 to deploy everything into emulator 14 | * In compute emulator you should see 2 roles: Client (web role) running 1 instance, and Silo (worker role) running 3 instances 15 | * Client is an ASP.NET application and after the role is started up, the Visual Studio should open the entry page in your browser 16 | * Enter the count of publisher grains you want to spawn and click "Spawn" 17 | * After some delay you will be redirected to notifications page where you can observe all events generated by all grains in a cluster 18 | * You can click "Back" in a browser and spawn more grains any time you wish 19 | 20 | ### Have fun! 21 | -------------------------------------------------------------------------------- /samples/Hub/Silo/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /samples/Hub/Silo/OrleansConfiguration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /samples/Hub/Silo/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | [assembly: AssemblyTitle("Sample.Silo")] 4 | [assembly: AssemblyVersion("1.0.0.0")] 5 | [assembly: AssemblyFileVersion("1.0.0.0")] 6 | -------------------------------------------------------------------------------- /samples/Hub/Silo/Silo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {6B7E7C7D-FCB5-415C-A361-E653CB9EAD72} 9 | Library 10 | Properties 11 | Sample 12 | Sample.Silo 13 | v4.5 14 | 512 15 | AnyCPU 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Worker 25 | false 26 | publish\ 27 | true 28 | Disk 29 | false 30 | Foreground 31 | 7 32 | Days 33 | false 34 | false 35 | true 36 | 0 37 | 1.0.0.%2a 38 | false 39 | true 40 | 41 | 42 | 43 | true 44 | full 45 | false 46 | bin\Debug\ 47 | DEBUG;TRACE 48 | prompt 49 | 4 50 | AllRules.ruleset 51 | false 52 | 53 | 54 | pdbonly 55 | true 56 | bin\Release\ 57 | TRACE 58 | prompt 59 | 4 60 | AllRules.ruleset 61 | false 62 | 63 | 64 | 65 | $(OrleansSDK)\Binaries\OrleansServer\CounterControl.exe 66 | True 67 | 68 | 69 | 70 | False 71 | $(OrleansSDK)\Binaries\OrleansServer\Microsoft.WindowsAzure.Configuration.dll 72 | 73 | 74 | True 75 | 76 | 77 | False 78 | 79 | 80 | False 81 | $(OrleansSDK)\Binaries\OrleansServer\Microsoft.WindowsAzure.StorageClient.dll 82 | 83 | 84 | $(OrleansSDK)\Binaries\OrleansClient\Orleans.dll 85 | 86 | 87 | $(OrleansSDK)\Binaries\OrleansClient\OrleansAzureUtils.dll 88 | 89 | 90 | $(OrleansSDK)\Binaries\OrleansServer\OrleansRuntime.dll 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | Designer 108 | 109 | 110 | 111 | 112 | Always 113 | Designer 114 | 115 | 116 | 117 | 118 | False 119 | Microsoft .NET Framework 4 %28x86 and x64%29 120 | true 121 | 122 | 123 | False 124 | .NET Framework 3.5 SP1 Client Profile 125 | false 126 | 127 | 128 | False 129 | .NET Framework 3.5 SP1 130 | false 131 | 132 | 133 | False 134 | Windows Installer 3.1 135 | true 136 | 137 | 138 | 139 | 140 | {98d4db83-175e-44d5-a14b-9c66ee89f037} 141 | Grains 142 | 143 | 144 | {163cce80-c506-468c-9b28-e2ff198097e3} 145 | Interfaces 146 | 147 | 148 | 149 | 156 | -------------------------------------------------------------------------------- /samples/Hub/Silo/WorkerRole.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Net; 5 | 6 | using Microsoft.WindowsAzure; 7 | using Microsoft.WindowsAzure.Diagnostics; 8 | using Microsoft.WindowsAzure.ServiceRuntime; 9 | 10 | using Orleans.Host; 11 | 12 | namespace Sample 13 | { 14 | public class WorkerRole : RoleEntryPoint 15 | { 16 | OrleansAzureSilo silo; 17 | 18 | public override bool OnStart() 19 | { 20 | SetupWorker(); 21 | return SetupSilo(); 22 | } 23 | 24 | public override void Run() 25 | { 26 | RunSilo(); 27 | } 28 | 29 | public override void OnStop() 30 | { 31 | StopSilo(); 32 | StopWorker(); 33 | } 34 | 35 | static void SetupWorker() 36 | { 37 | ServicePointManager.DefaultConnectionLimit = 100; 38 | RoleEnvironment.Changing += RoleEnvironmentChanging; 39 | 40 | SetupEnvironmentChangeHandlers(); 41 | SetupDiagnostics(); 42 | } 43 | 44 | void StopWorker() 45 | { 46 | RoleEnvironment.Changing -= RoleEnvironmentChanging; 47 | base.OnStop(); 48 | } 49 | 50 | static void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e) 51 | { 52 | var i = 1; 53 | foreach (var c in e.Changes) 54 | Trace.WriteLine(string.Format("RoleEnvironmentChanging: #{0} Type={1} Change={2}", i++, c.GetType().FullName, c)); 55 | 56 | if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange)) 57 | e.Cancel = true; 58 | } 59 | 60 | static void SetupEnvironmentChangeHandlers() 61 | { 62 | CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) => 63 | { 64 | configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)); 65 | 66 | RoleEnvironment.Changed += (sender, e) => 67 | { 68 | var configSettingsChanged = e.Changes 69 | .OfType() 70 | .Any(change => (change.ConfigurationSettingName == configName)); 71 | 72 | if (!configSettingsChanged) 73 | return; 74 | 75 | if (!configSetter(RoleEnvironment.GetConfigurationSettingValue(configName))) 76 | RoleEnvironment.RequestRecycle(); 77 | }; 78 | }); 79 | } 80 | 81 | static bool CollectPerfCounters = false; 82 | static bool CollectWindowsEventLogs = false; 83 | static bool FullCrashDumps = false; 84 | 85 | static void SetupDiagnostics() 86 | { 87 | var cfg = DiagnosticMonitor.GetDefaultInitialConfiguration(); 88 | 89 | if (CollectPerfCounters) 90 | { 91 | AddPerformanceCounter(cfg, @"\Processor(_Total)\% Processor Time", TimeSpan.FromSeconds(5)); 92 | AddPerformanceCounter(cfg, @"\Memory\Available Mbytes", TimeSpan.FromSeconds(5)); 93 | } 94 | 95 | if (CollectWindowsEventLogs) 96 | { 97 | AddEventSource(cfg, "System!*"); 98 | AddEventSource(cfg, "Application!*"); 99 | } 100 | 101 | cfg.DiagnosticInfrastructureLogs.ScheduledTransferLogLevelFilter = LogLevel.Information; 102 | cfg.DiagnosticInfrastructureLogs.ScheduledTransferPeriod = TimeSpan.FromMinutes(5); 103 | 104 | CrashDumps.EnableCollection(FullCrashDumps); 105 | DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", cfg); 106 | } 107 | 108 | static void AddPerformanceCounter(DiagnosticMonitorConfiguration cfg, string counter, TimeSpan frequency) 109 | { 110 | cfg.PerformanceCounters.DataSources.Add(new PerformanceCounterConfiguration 111 | { 112 | CounterSpecifier = counter, 113 | SampleRate = frequency 114 | }); 115 | } 116 | 117 | static void AddEventSource(DiagnosticMonitorConfiguration cfg, string source) 118 | { 119 | cfg.WindowsEventLog.DataSources.Add(source); 120 | } 121 | 122 | bool SetupSilo() 123 | { 124 | var siloEndpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["OrleansSiloEndpoint"]; 125 | HubGateway.LocalEndPoint = siloEndpoint.IPEndpoint; 126 | 127 | silo = new OrleansAzureSilo(); 128 | return silo.Start(RoleEnvironment.DeploymentId, RoleEnvironment.CurrentRoleInstance); 129 | } 130 | 131 | void StopSilo() 132 | { 133 | silo.Stop(); 134 | } 135 | 136 | void RunSilo() 137 | { 138 | silo.Run(); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /samples/Observer/GrainInterfaces/GrainInterfaces.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {08B13230-C29B-4579-A0C9-0400EA74F8FF} 9 | Library 10 | Properties 11 | GrainInterfaces 12 | GrainInterfaces 13 | v4.5 14 | 512 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | $(OrleansSDK)\Binaries\OrleansClient\Orleans.dll 43 | False 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Client 54 | 55 | 56 | 57 | 58 | 59 | 60 | 67 | -------------------------------------------------------------------------------- /samples/Observer/GrainInterfaces/IGrain1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Orleans; 5 | 6 | namespace ObserverInterfaces 7 | { 8 | /// 9 | /// Orleans grain communication interface IMyGrain1 10 | /// 11 | public interface IDoStuff : Orleans.IGrain 12 | { 13 | Task SubscribeForUpdates(IObserve subscriber); 14 | 15 | } 16 | 17 | 18 | public interface IObserve : Orleans.IGrainObserver 19 | { 20 | void StuffUpdate(int data); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /samples/Observer/GrainInterfaces/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("GrainInterfaces")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("GrainInterfaces")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("bed8c955-1e3b-4352-b244-12423d9c319b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /samples/Observer/GrainInterfaces/Properties/orleans.codegen.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.34014 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | #if !EXCLUDE_CODEGEN 11 | #pragma warning disable 162 12 | #pragma warning disable 219 13 | #pragma warning disable 693 14 | #pragma warning disable 1591 15 | #pragma warning disable 1998 16 | 17 | namespace ObserverInterfaces 18 | { 19 | using System; 20 | using System.Net; 21 | using System.Runtime.Serialization; 22 | using System.Runtime.Serialization.Formatters.Binary; 23 | using System.IO; 24 | using System.Collections.Generic; 25 | using Orleans; 26 | 27 | 28 | [System.CodeDom.Compiler.GeneratedCodeAttribute("Orleans-CodeGenerator", "1.0.814.60418")] 29 | [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()] 30 | public class DoStuffFactory 31 | { 32 | 33 | 34 | public static IDoStuff GetGrain(long primaryKey) 35 | { 36 | return Cast(GrainFactoryBase.MakeGrainReferenceInternal(typeof(IDoStuff), 1237371746, primaryKey)); 37 | } 38 | 39 | public static IDoStuff GetGrain(long primaryKey, string grainClassNamePrefix) 40 | { 41 | return Cast(GrainFactoryBase.MakeGrainReferenceInternal(typeof(IDoStuff), 1237371746, primaryKey, grainClassNamePrefix)); 42 | } 43 | 44 | public static IDoStuff GetGrain(Guid primaryKey) 45 | { 46 | return Cast(GrainFactoryBase.MakeGrainReferenceInternal(typeof(IDoStuff), 1237371746, primaryKey)); 47 | } 48 | 49 | public static IDoStuff GetGrain(Guid primaryKey, string grainClassNamePrefix) 50 | { 51 | return Cast(GrainFactoryBase.MakeGrainReferenceInternal(typeof(IDoStuff), 1237371746, primaryKey, grainClassNamePrefix)); 52 | } 53 | 54 | public static IDoStuff Cast(IAddressable grainRef) 55 | { 56 | 57 | return DoStuffReference.Cast(grainRef); 58 | } 59 | 60 | [System.CodeDom.Compiler.GeneratedCodeAttribute("Orleans-CodeGenerator", "1.0.814.60418")] 61 | [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()] 62 | [System.SerializableAttribute()] 63 | [Orleans.GrainReferenceAttribute("ObserverInterfaces.IDoStuff")] 64 | internal class DoStuffReference : Orleans.GrainReference, IDoStuff, Orleans.IAddressable 65 | { 66 | 67 | 68 | public static IDoStuff Cast(IAddressable grainRef) 69 | { 70 | 71 | return (IDoStuff) GrainReference.CastInternal(typeof(IDoStuff), (GrainReference gr) => { return new DoStuffReference(gr);}, grainRef, 1237371746); 72 | } 73 | 74 | protected override int InterfaceId { get { return 1237371746; } } 75 | 76 | public override bool IsCompatible(int interfaceId) { return interfaceId == this.InterfaceId; } 77 | 78 | protected override string InterfaceName { get { return "ObserverInterfaces.IDoStuff"; } } 79 | 80 | protected override string GetMethodName(int interfaceId, int methodId) { return DoStuffMethodInvoker.GetMethodName(interfaceId, methodId); } 81 | 82 | protected internal DoStuffReference(GrainReference reference) : 83 | base(reference) 84 | { 85 | } 86 | 87 | [Orleans.CopierMethodAttribute()] 88 | public static object _Copier(object original) 89 | { 90 | DoStuffReference input = ((DoStuffReference)(original)); 91 | return ((DoStuffReference)(GrainReference.CopyGrainReference(input))); 92 | } 93 | 94 | [Orleans.SerializerMethodAttribute()] 95 | public static void _Serializer(object original, Orleans.Serialization.BinaryTokenStreamWriter stream, System.Type expected) 96 | { 97 | DoStuffReference input = ((DoStuffReference)(original)); 98 | GrainReference.SerializeGrainReference(input, stream, expected); 99 | } 100 | 101 | [Orleans.DeserializerMethodAttribute()] 102 | public static object _Deserializer(System.Type expected, Orleans.Serialization.BinaryTokenStreamReader stream) 103 | { 104 | return DoStuffReference.Cast(((Orleans.GrainReference)(GrainReference.DeserializeGrainReference(expected, stream)))); 105 | } 106 | 107 | public System.Threading.Tasks.Task SubscribeForUpdates(ObserverInterfaces.IObserve subscriber) 108 | { 109 | GrainFactoryBase.CheckGrainObserverParamInternal(subscriber); 110 | 111 | return base.InvokeMethodAsync(989382207, new object[] {subscriber is GrainBase ? ObserverInterfaces.ObserveFactory.Cast(subscriber.AsReference()) : subscriber}, TimeSpan.Zero ); 112 | } 113 | } 114 | } 115 | 116 | [System.CodeDom.Compiler.GeneratedCodeAttribute("Orleans-CodeGenerator", "1.0.814.60418")] 117 | [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()] 118 | [Orleans.MethodInvokerAttribute("ObserverInterfaces.IDoStuff", 1237371746)] 119 | internal class DoStuffMethodInvoker : IGrainMethodInvoker 120 | { 121 | 122 | public int InterfaceId 123 | { 124 | get 125 | { 126 | return 1237371746; 127 | } 128 | } 129 | 130 | public async System.Threading.Tasks.Task Invoke(IAddressable grain, int interfaceId, int methodId, object[] arguments) 131 | { 132 | if (grain == null) throw new System.ArgumentNullException("grain"); 133 | switch (interfaceId) 134 | { 135 | case 1237371746: // IDoStuff 136 | switch (methodId) 137 | { 138 | case 989382207: 139 | await ((IDoStuff)grain).SubscribeForUpdates((IObserve)arguments[0]); 140 | return true; 141 | default: 142 | throw new NotImplementedException("interfaceId="+interfaceId+",methodId="+methodId); 143 | } 144 | default: 145 | throw new System.InvalidCastException("interfaceId="+interfaceId); 146 | } 147 | } 148 | 149 | public static string GetMethodName(int interfaceId, int methodId) 150 | { 151 | 152 | switch (interfaceId) 153 | { 154 | 155 | case 1237371746: // IDoStuff 156 | switch (methodId) 157 | { 158 | case 989382207: 159 | return "SubscribeForUpdates"; 160 | case -606142484: 161 | return "GetProperties"; 162 | 163 | default: 164 | throw new NotImplementedException("interfaceId="+interfaceId+",methodId="+methodId); 165 | } 166 | 167 | default: 168 | throw new System.InvalidCastException("interfaceId="+interfaceId); 169 | } 170 | } 171 | } 172 | 173 | [Serializable()] 174 | [System.CodeDom.Compiler.GeneratedCodeAttribute("Orleans-CodeGenerator", "1.0.814.60418")] 175 | [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()] 176 | public class DoStuffProperties 177 | { 178 | 179 | 180 | public Dictionary AsDictionary() 181 | { 182 | var retValue = new Dictionary(); 183 | return retValue; 184 | } 185 | } 186 | 187 | [System.CodeDom.Compiler.GeneratedCodeAttribute("Orleans-CodeGenerator", "1.0.814.60418")] 188 | [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()] 189 | public class ObserveFactory 190 | { 191 | 192 | 193 | public static IObserve Cast(IAddressable grainRef) 194 | { 195 | 196 | return ObserveReference.Cast(grainRef); 197 | } 198 | 199 | private static IGrainMethodInvoker methodInvoker; 200 | 201 | public async static System.Threading.Tasks.Task CreateObjectReference(IObserve obj) 202 | { 203 | if (methodInvoker == null) methodInvoker = new ObserveMethodInvoker(); 204 | return ObserveFactory.Cast(await GrainReference.CreateObjectReference(obj, methodInvoker)); 205 | } 206 | 207 | public static void DeleteObjectReference(IObserve reference) 208 | { 209 | GrainReference.DeleteObjectReference(reference); 210 | } 211 | 212 | [System.CodeDom.Compiler.GeneratedCodeAttribute("Orleans-CodeGenerator", "1.0.814.60418")] 213 | [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()] 214 | [System.SerializableAttribute()] 215 | [Orleans.GrainReferenceAttribute("ObserverInterfaces.IObserve")] 216 | internal class ObserveReference : Orleans.GrainReference, IObserve, Orleans.IAddressable 217 | { 218 | 219 | 220 | public static IObserve Cast(IAddressable grainRef) 221 | { 222 | 223 | return (IObserve) GrainReference.CastInternal(typeof(IObserve), (GrainReference gr) => { return new ObserveReference(gr);}, grainRef, -1853968243); 224 | } 225 | 226 | protected override int InterfaceId { get { return -1853968243; } } 227 | 228 | public override bool IsCompatible(int interfaceId) { return interfaceId == this.InterfaceId; } 229 | 230 | protected override string InterfaceName { get { return "ObserverInterfaces.IObserve"; } } 231 | 232 | protected override string GetMethodName(int interfaceId, int methodId) { return ObserveMethodInvoker.GetMethodName(interfaceId, methodId); } 233 | 234 | protected internal ObserveReference(GrainReference reference) : 235 | base(reference) 236 | { 237 | } 238 | 239 | [Orleans.CopierMethodAttribute()] 240 | public static object _Copier(object original) 241 | { 242 | ObserveReference input = ((ObserveReference)(original)); 243 | return ((ObserveReference)(GrainReference.CopyGrainReference(input))); 244 | } 245 | 246 | [Orleans.SerializerMethodAttribute()] 247 | public static void _Serializer(object original, Orleans.Serialization.BinaryTokenStreamWriter stream, System.Type expected) 248 | { 249 | ObserveReference input = ((ObserveReference)(original)); 250 | GrainReference.SerializeGrainReference(input, stream, expected); 251 | } 252 | 253 | [Orleans.DeserializerMethodAttribute()] 254 | public static object _Deserializer(System.Type expected, Orleans.Serialization.BinaryTokenStreamReader stream) 255 | { 256 | return ObserveReference.Cast(((Orleans.GrainReference)(GrainReference.DeserializeGrainReference(expected, stream)))); 257 | } 258 | 259 | public void StuffUpdate(int data) 260 | { 261 | 262 | base.InvokeOneWayMethod(570614880, new object[] {data} ); 263 | } 264 | } 265 | } 266 | 267 | [System.CodeDom.Compiler.GeneratedCodeAttribute("Orleans-CodeGenerator", "1.0.814.60418")] 268 | [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()] 269 | [Orleans.MethodInvokerAttribute("ObserverInterfaces.IObserve", -1853968243)] 270 | internal class ObserveMethodInvoker : IGrainMethodInvoker 271 | { 272 | 273 | public int InterfaceId 274 | { 275 | get 276 | { 277 | return -1853968243; 278 | } 279 | } 280 | 281 | public async System.Threading.Tasks.Task Invoke(IAddressable grain, int interfaceId, int methodId, object[] arguments) 282 | { 283 | if (grain == null) throw new System.ArgumentNullException("grain"); 284 | switch (interfaceId) 285 | { 286 | case -1853968243: // IObserve 287 | switch (methodId) 288 | { 289 | case 570614880: 290 | ((IObserve)grain).StuffUpdate((Int32)arguments[0]); return true; 291 | default: 292 | throw new NotImplementedException("interfaceId="+interfaceId+",methodId="+methodId); 293 | } 294 | default: 295 | throw new System.InvalidCastException("interfaceId="+interfaceId); 296 | } 297 | } 298 | 299 | public static string GetMethodName(int interfaceId, int methodId) 300 | { 301 | 302 | switch (interfaceId) 303 | { 304 | 305 | case -1853968243: // IObserve 306 | switch (methodId) 307 | { 308 | case 570614880: 309 | return "StuffUpdate"; 310 | case -606142484: 311 | return "GetProperties"; 312 | 313 | default: 314 | throw new NotImplementedException("interfaceId="+interfaceId+",methodId="+methodId); 315 | } 316 | 317 | default: 318 | throw new System.InvalidCastException("interfaceId="+interfaceId); 319 | } 320 | } 321 | } 322 | 323 | [Serializable()] 324 | [System.CodeDom.Compiler.GeneratedCodeAttribute("Orleans-CodeGenerator", "1.0.814.60418")] 325 | [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()] 326 | public class ObserveProperties 327 | { 328 | 329 | 330 | public Dictionary AsDictionary() 331 | { 332 | var retValue = new Dictionary(); 333 | return retValue; 334 | } 335 | } 336 | } 337 | #pragma warning restore 162 338 | #pragma warning restore 219 339 | #pragma warning restore 693 340 | #pragma warning restore 1591 341 | #pragma warning restore 1998 342 | #endif 343 | -------------------------------------------------------------------------------- /samples/Observer/Grains/Grain1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Text; 6 | using Orleans; 7 | 8 | namespace ObserverGrains 9 | { 10 | using ObserverInterfaces; 11 | 12 | public class MyGrain : Orleans.GrainBase 13 | { 14 | // manage subscriptions 15 | private ObserverSubscriptionManager subscribers; 16 | 17 | public override Task ActivateAsync() 18 | { 19 | subscribers = new ObserverSubscriptionManager(); 20 | 21 | // set up timer to simulate events to subscribe to 22 | RegisterTimer(SendOutUpdates, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); 23 | 24 | return TaskDone.Done; 25 | } 26 | 27 | 28 | // accept subscriptions 29 | public Task SubscribeForUpdates(IObserve subscriber) 30 | { 31 | // add new subscriber to list of subscribers 32 | subscribers.Subscribe(subscriber); 33 | return TaskDone.Done; 34 | } 35 | 36 | 37 | // Notify subscribers 38 | private Task SendOutUpdates(object _) 39 | { 40 | subscribers.Notify( s => s.StuffUpdate(DateTime.Now.Millisecond)); 41 | 42 | return TaskDone.Done; 43 | } 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /samples/Observer/Grains/Grains.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {5C7F734D-2C11-4382-B00A-C46D92350A4D} 9 | Library 10 | Properties 11 | Grains 12 | Grains 13 | v4.5 14 | 512 15 | 16 | 17 | Program 18 | $(OrleansSDK)\LocalSilo\OrleansHost.exe 19 | $(OrleansSDK)\LocalSilo 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | $(OrleansSDK)\Binaries\OrleansClient\Orleans.dll 48 | False 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | {08b13230-c29b-4579-a0c9-0400ea74f8ff} 58 | GrainInterfaces 59 | 60 | 61 | 62 | 63 | Server 64 | 65 | 66 | 67 | 68 | if exist "$(OrleansSDK)\LocalSilo" ( 69 | if not exist "$(OrleansSDK)\LocalSilo\Applications" (md "$(OrleansSDK)\LocalSilo\Applications") 70 | if not exist "$(OrleansSDK)\LocalSilo\Applications\$(RootNamespace)" (md "$(OrleansSDK)\LocalSilo\Applications\$(RootNamespace)") 71 | copy /y *.dll "$(OrleansSDK)\LocalSilo\Applications\$(RootNamespace)\" 72 | copy /y *.pdb "$(OrleansSDK)\LocalSilo\Applications\$(RootNamespace)\" 73 | ) 74 | if exist "$(OrleansSDK)\Binaries" ( 75 | if not exist "$(OrleansSDK)\Binaries\Applications" (md "$(OrleansSDK)\Binaries\Applications") 76 | if not exist "$(OrleansSDK)\Binaries\Applications\$(RootNamespace)" (md "$(OrleansSDK)\Binaries\Applications\$(RootNamespace)") 77 | copy /y *.dll "$(OrleansSDK)\Binaries\Applications\$(RootNamespace)\" 78 | copy /y *.pdb "$(OrleansSDK)\Binaries\Applications\$(RootNamespace)\" 79 | ) 80 | 81 | 82 | 86 | -------------------------------------------------------------------------------- /samples/Observer/Grains/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Grains")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Grains")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b4977912-0d29-4cef-8894-9e189a6fd335")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /samples/Observer/Observer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30501.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Observer", "Observer\Observer.csproj", "{DE31A956-E9AE-4340-8DA9-239EE03FB81E}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {08B13230-C29B-4579-A0C9-0400EA74F8FF} = {08B13230-C29B-4579-A0C9-0400EA74F8FF} 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grains", "Grains\Grains.csproj", "{5C7F734D-2C11-4382-B00A-C46D92350A4D}" 12 | ProjectSection(ProjectDependencies) = postProject 13 | {08B13230-C29B-4579-A0C9-0400EA74F8FF} = {08B13230-C29B-4579-A0C9-0400EA74F8FF} 14 | EndProjectSection 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrainInterfaces", "GrainInterfaces\GrainInterfaces.csproj", "{08B13230-C29B-4579-A0C9-0400EA74F8FF}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {DE31A956-E9AE-4340-8DA9-239EE03FB81E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {DE31A956-E9AE-4340-8DA9-239EE03FB81E}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {DE31A956-E9AE-4340-8DA9-239EE03FB81E}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {DE31A956-E9AE-4340-8DA9-239EE03FB81E}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {5C7F734D-2C11-4382-B00A-C46D92350A4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {5C7F734D-2C11-4382-B00A-C46D92350A4D}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {5C7F734D-2C11-4382-B00A-C46D92350A4D}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {5C7F734D-2C11-4382-B00A-C46D92350A4D}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {08B13230-C29B-4579-A0C9-0400EA74F8FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {08B13230-C29B-4579-A0C9-0400EA74F8FF}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {08B13230-C29B-4579-A0C9-0400EA74F8FF}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {08B13230-C29B-4579-A0C9-0400EA74F8FF}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /samples/Observer/Observer/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /samples/Observer/Observer/DevTestClientConfiguration.xml: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /samples/Observer/Observer/DevTestServerConfiguration.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /samples/Observer/Observer/Observer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {DE31A956-E9AE-4340-8DA9-239EE03FB81E} 9 | Exe 10 | Properties 11 | ObserverInterfaces 12 | Observer 13 | v4.5 14 | 512 15 | 16 | 17 | true 18 | full 19 | false 20 | $(OrleansSDK)\LocalSilo\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | pdbonly 28 | true 29 | $(OrleansSDK)\LocalSilo\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | $(OrleansSDK)\Binaries\OrleansClient\Orleans.dll 45 | False 46 | 47 | 48 | $(OrleansSDK)\Binaries\OrleansServer\OrleansRuntime.dll 49 | False 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | PreserveNewest 63 | 64 | 65 | PreserveNewest 66 | 67 | 68 | 69 | 70 | {08b13230-c29b-4579-a0c9-0400ea74f8ff} 71 | GrainInterfaces 72 | 73 | 74 | 75 | 82 | -------------------------------------------------------------------------------- /samples/Observer/Observer/OrleansHostWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | using Orleans.Host.SiloHost; 5 | 6 | namespace ObserverInterfaces 7 | { 8 | internal class OrleansHostWrapper : IDisposable 9 | { 10 | public bool Debug 11 | { 12 | get { return siloHost != null && siloHost.Debug; } 13 | set { siloHost.Debug = value; } 14 | } 15 | 16 | private OrleansSiloHost siloHost; 17 | 18 | public OrleansHostWrapper(string[] args) 19 | { 20 | ParseArguments(args); 21 | Init(); 22 | } 23 | 24 | public bool Run() 25 | { 26 | bool ok = false; 27 | 28 | try 29 | { 30 | siloHost.InitializeOrleansSilo(); 31 | 32 | ok = siloHost.StartOrleansSilo(); 33 | 34 | if (ok) 35 | { 36 | Console.WriteLine(string.Format("Successfully started Orleans silo '{0}' as a {1} node.", siloHost.SiloName, siloHost.SiloType)); 37 | } 38 | else 39 | { 40 | throw new SystemException(string.Format("Failed to start Orleans silo '{0}' as a {1} node.", siloHost.SiloName, siloHost.SiloType)); 41 | } 42 | } 43 | catch (Exception exc) 44 | { 45 | siloHost.ReportStartupError(exc); 46 | var msg = string.Format("{0}:\n{1}\n{2}", exc.GetType().FullName, exc.Message, exc.StackTrace); 47 | Console.WriteLine(msg); 48 | } 49 | 50 | return ok; 51 | } 52 | 53 | public bool Stop() 54 | { 55 | bool ok = false; 56 | 57 | try 58 | { 59 | siloHost.StopOrleansSilo(); 60 | 61 | Console.WriteLine(string.Format("Orleans silo '{0}' shutdown.", siloHost.SiloName)); 62 | } 63 | catch (Exception exc) 64 | { 65 | siloHost.ReportStartupError(exc); 66 | var msg = string.Format("{0}:\n{1}\n{2}", exc.GetType().FullName, exc.Message, exc.StackTrace); 67 | Console.WriteLine(msg); 68 | } 69 | 70 | return ok; 71 | } 72 | 73 | private void Init() 74 | { 75 | siloHost.LoadOrleansConfig(); 76 | } 77 | 78 | private bool ParseArguments(string[] args) 79 | { 80 | bool debug = false; 81 | string deploymentId = null; 82 | 83 | string configFileName = "DevTestServerConfiguration.xml"; 84 | string siloName = Dns.GetHostName(); // Default to machine name 85 | 86 | int argPos = 1; 87 | for (int i = 0; i < args.Length; i++) 88 | { 89 | string a = args[i]; 90 | if (a.StartsWith("-") || a.StartsWith("/")) 91 | { 92 | switch (a.ToLowerInvariant()) 93 | { 94 | case "/?": 95 | case "/help": 96 | case "-?": 97 | case "-help": 98 | // Query usage help 99 | return false; 100 | case "/debug": 101 | debug = true; 102 | break; 103 | default: 104 | Console.WriteLine("Bad command line arguments supplied: " + a); 105 | return false; 106 | } 107 | } 108 | else if (a.Contains("=")) 109 | { 110 | string[] split = a.Split('='); 111 | if (String.IsNullOrEmpty(split[1])) 112 | { 113 | Console.WriteLine("Bad command line arguments supplied: " + a); 114 | return false; 115 | } 116 | switch (split[0].ToLowerInvariant()) 117 | { 118 | case "deploymentid": 119 | deploymentId = split[1]; 120 | break; 121 | case "deploymentgroup": 122 | // TODO: Remove this at some point in future 123 | Console.WriteLine("Ignoring deprecated command line argument: " + a); 124 | break; 125 | default: 126 | Console.WriteLine("Bad command line arguments supplied: " + a); 127 | return false; 128 | } 129 | } 130 | // unqualified arguments below 131 | else if (argPos == 1) 132 | { 133 | siloName = a; 134 | argPos++; 135 | } 136 | else if (argPos == 2) 137 | { 138 | configFileName = a; 139 | argPos++; 140 | } 141 | else 142 | { 143 | // Too many command line arguments 144 | Console.WriteLine("Too many command line arguments supplied: " + a); 145 | return false; 146 | } 147 | } 148 | 149 | siloHost = new OrleansSiloHost(siloName); 150 | siloHost.ConfigFileName = configFileName; 151 | if (deploymentId != null) 152 | siloHost.DeploymentId = deploymentId; 153 | 154 | return true; 155 | } 156 | 157 | public void PrintUsage() 158 | { 159 | Console.WriteLine( 160 | @"USAGE: 161 | OrleansHost.exe [ []] [DeploymentId=] [/debug] 162 | Where: 163 | - Name of this silo in the Config file list (optional) 164 | - Path to the Config file to use (optional) 165 | DeploymentId= 166 | - Which deployment group this host instance should run in (optional) 167 | /debug - Turn on extra debug output during host startup (optional)"); 168 | } 169 | 170 | public void Dispose() 171 | { 172 | Dispose(true); 173 | } 174 | 175 | protected virtual void Dispose(bool dispose) 176 | { 177 | siloHost.Dispose(); 178 | siloHost = null; 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /samples/Observer/Observer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ObserverPattern 4 | { 5 | using ObserverInterfaces; 6 | 7 | /// 8 | /// Orleans test silo host 9 | /// 10 | public class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | // The Orleans silo environment is initialized in its own app domain in order to more 15 | // closely emulate the distributed situation, when the client and the server cannot 16 | // pass data via shared memory. 17 | AppDomain hostDomain = AppDomain.CreateDomain("OrleansHost", null, new AppDomainSetup 18 | { 19 | AppDomainInitializer = InitSilo, 20 | AppDomainInitializerArguments = args, 21 | }); 22 | 23 | Orleans.OrleansClient.Initialize("DevTestClientConfiguration.xml"); 24 | 25 | // Observer 26 | 27 | IDoStuff grain = DoStuffFactory.GetGrain(0); 28 | 29 | var theObserver = new TheObserver(); 30 | var obj = ObserveFactory.CreateObjectReference(theObserver).Result; // factory from IObserve 31 | 32 | grain.SubscribeForUpdates(obj); 33 | 34 | 35 | // close down 36 | 37 | Console.WriteLine("Orleans Silo is running.\nPress Enter to terminate..."); 38 | Console.ReadLine(); 39 | 40 | hostDomain.DoCallBack(ShutdownSilo); 41 | } 42 | 43 | 44 | // class for handling updates from grain 45 | private class TheObserver : IObserve 46 | { 47 | // Receive updates 48 | public void StuffUpdate(int data) 49 | { 50 | Console.WriteLine("New stuff: {0}", data); 51 | } 52 | } 53 | 54 | 55 | 56 | static void InitSilo(string[] args) 57 | { 58 | hostWrapper = new OrleansHostWrapper(args); 59 | 60 | if (!hostWrapper.Run()) 61 | { 62 | Console.Error.WriteLine("Failed to initialize Orleans silo"); 63 | } 64 | } 65 | 66 | static void ShutdownSilo() 67 | { 68 | if (hostWrapper != null) 69 | { 70 | hostWrapper.Dispose(); 71 | GC.SuppressFinalize(hostWrapper); 72 | } 73 | } 74 | 75 | private static OrleansHostWrapper hostWrapper; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /samples/Observer/Observer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Observer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Observer")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("2b527923-0b53-47ae-a405-7566104e5283")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | --------------------------------------------------------------------------------