├── .gitignore ├── Booking.sln ├── BookingAzureServices ├── AzureChannel.cs ├── AzureMonthViewStore.cs ├── AzureReservationRepository.cs ├── BookingAzureServices.csproj ├── CapacityGuard.cs ├── IRequiresInitialization.cs ├── MailContext.cs ├── MailMessageTableEntity.cs ├── Properties │ └── AssemblyInfo.cs ├── ReservationContext.cs ├── ReservationTableEntity.cs ├── SerializerBlob.cs └── TableStorageMailClient.cs ├── BookingCloudService ├── BookingCloudService.ccproj ├── ServiceConfiguration.cscfg └── ServiceDefinition.csdef ├── BookingDomainModel ├── AcceptingReservationConsumer.cs ├── BookingDomainModel.csproj ├── Capacity.cs ├── CompositeMessageConsumer.cs ├── ConditionalMessageConsumer.cs ├── IChannel.cs ├── IMessageConsumer.cs ├── IReservationRepository.cs ├── MakeReservationCommand.cs ├── Properties │ └── AssemblyInfo.cs ├── RejectingReservationConsumer.cs ├── ReservationAcceptedEvent.cs ├── ReservationRejectedEvent.cs ├── ReservationWriter.cs └── SoldOutEvent.cs ├── BookingDomainModelUnitTest ├── AcceptingReservationConsumerFacts.cs ├── App.config ├── AutoDomainDataAttribute.cs ├── BookingDomainModelUnitTest.csproj ├── CapacityFacts.cs ├── CompositeMessageConsumerFacts.cs ├── ConditionalMessageConsumerFacts.cs ├── DomainCustomization.cs ├── FuncCustomization.cs ├── MakeReservationCommandFacts.cs ├── Properties │ └── AssemblyInfo.cs ├── RejectingReservationConsumerFacts.cs ├── ReservationAcceptedEventFacts.cs ├── ReservationRejectedEventFacts.cs ├── ReservationWriterFacts.cs ├── SoldOutEventFacts.cs └── packages.config ├── BookingWebModel ├── BookingViewModel.cs ├── BookingWebModel.csproj ├── HomeController.cs ├── IDayViewReader.cs ├── IMonthViewReader.cs ├── IMonthViewWriter.cs ├── MonthViewModelUpdater.cs └── Properties │ └── AssemblyInfo.cs ├── BookingWebModelUnitTest ├── App.config ├── AutoWebDataAttribute.cs ├── BookingViewModelFacts.cs ├── BookingWebModelCustomization.cs ├── BookingWebModelUnitTest.csproj ├── HomeControllerFacts.cs ├── MonthViewModelUpdaterFacts.cs ├── MvcCustomization.cs ├── Properties │ └── AssemblyInfo.cs └── packages.config ├── BookingWebRole ├── AzureServicesWindsorInstaller.cs ├── BookingWebRole.csproj ├── BookingWebRoleWindsorInstaller.cs ├── Content │ ├── Site.css │ ├── images │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_2e83ff_256x240.png │ │ ├── ui-icons_454545_256x240.png │ │ ├── ui-icons_888888_256x240.png │ │ └── ui-icons_cd0a0a_256x240.png │ ├── jquery-ui-1.8.7.custom.css │ ├── jquery.ui.all.css │ ├── jquery.ui.base.css │ ├── jquery.ui.core.css │ ├── jquery.ui.datepicker.css │ └── jquery.ui.theme.css ├── Footer.cs ├── Global.asax ├── Global.asax.cs ├── Properties │ └── AssemblyInfo.cs ├── Scripts │ ├── MicrosoftAjax.debug.js │ ├── MicrosoftAjax.js │ ├── MicrosoftMvcAjax.debug.js │ ├── MicrosoftMvcAjax.js │ ├── MicrosoftMvcValidation.debug.js │ ├── MicrosoftMvcValidation.js │ ├── jquery-1.4.1-vsdoc.js │ ├── jquery-1.4.1.js │ ├── jquery-1.4.1.min.js │ ├── jquery-1.4.4.min.js │ ├── jquery.ui.core.js │ ├── jquery.ui.core.min.js │ ├── jquery.ui.datepicker.js │ ├── jquery.ui.datepicker.min.js │ ├── jquery.ui.widget.js │ ├── jquery.ui.widget.min.js │ ├── jquery.validate-vsdoc.js │ ├── jquery.validate.js │ └── jquery.validate.min.js ├── Views │ ├── Home │ │ ├── BookingReceipt.aspx │ │ ├── Index.aspx │ │ └── NewBooking.aspx │ ├── Shared │ │ ├── EditorTemplates │ │ │ └── BookingViewModel.ascx │ │ ├── Error.aspx │ │ └── Site.Master │ └── Web.config ├── Web.config ├── WebModelWindsorInstaller.cs ├── WebRole.cs ├── WindsorControllerFactory.cs └── packages.config ├── BookingWorkerRole ├── AzureMailServicesWindsorInstaller.cs ├── AzureQueueMessageProcessor.cs ├── AzureServicesWindsorInstaller.cs ├── BookingWorkerRole.csproj ├── BookingWorkerRoleWindsorInstaller.cs ├── DomainModelWindsorInstaller.cs ├── ExceptionPolicy.cs ├── MailAddressWindsorInstaller.cs ├── Properties │ └── AssemblyInfo.cs ├── SmtpServicesWindsorInstaller.cs ├── WorkerRole.cs ├── app.config └── packages.config ├── ReadMe.txt ├── ReplaceAzureConfig.ps1 ├── SmtpServices ├── AcceptedReserverationMailer.cs ├── Properties │ └── AssemblyInfo.cs ├── RejectedReservationMailer.cs └── SmtpServices.csproj └── packages ├── AutoFixture.2.1 ├── AutoFixture.2.1.nupkg └── lib │ ├── Ploeh.AutoFixture.XML │ ├── Ploeh.AutoFixture.dll │ ├── Ploeh.SemanticComparison.XML │ └── Ploeh.SemanticComparison.dll ├── AutoFixture.AutoMoq.2.1 ├── AutoFixture.AutoMoq.2.1.nupkg └── lib │ ├── Ploeh.AutoFixture.AutoMoq.XML │ └── Ploeh.AutoFixture.AutoMoq.dll ├── AutoFixture.Xunit.2.1 ├── AutoFixture.Xunit.2.1.nupkg └── lib │ ├── Ploeh.AutoFixture.Xunit.XML │ └── Ploeh.AutoFixture.Xunit.dll ├── Castle.Core.2.5.2 ├── Castle.Core.2.5.2.nupkg └── lib │ ├── ASL - Apache Software Foundation License.txt │ ├── BreakingChanges.txt │ ├── Changes.txt │ ├── Committers.txt │ ├── NET35 │ ├── Castle.Core.dll │ └── Castle.Core.xml │ ├── NET40ClientProfile │ ├── Castle.Core.dll │ └── Castle.Core.xml │ ├── SL3 │ ├── Castle.Core.dll │ └── Castle.Core.xml │ ├── SL4 │ ├── Castle.Core.dll │ └── Castle.Core.xml │ └── releaseNotes.txt ├── Castle.Windsor.2.5.3 ├── Castle.Windsor.2.5.3.nupkg └── lib │ ├── ASL - Apache Software Foundation License.txt │ ├── BreakingChanges.txt │ ├── Changes.txt │ ├── Committers.txt │ ├── NET35 │ ├── Castle.Windsor.dll │ ├── Castle.Windsor.pdb │ └── Castle.Windsor.xml │ ├── NET40-Client │ ├── Castle.Windsor.dll │ ├── Castle.Windsor.pdb │ └── Castle.Windsor.xml │ ├── NET40 │ ├── Castle.Windsor.dll │ ├── Castle.Windsor.pdb │ └── Castle.Windsor.xml │ ├── SL3 │ ├── Castle.Windsor.dll │ ├── Castle.Windsor.pdb │ └── Castle.Windsor.xml │ ├── SL4 │ ├── Castle.Windsor.dll │ ├── Castle.Windsor.pdb │ └── Castle.Windsor.xml │ └── releaseNotes.txt ├── Moq.4.0.10827 ├── License.txt ├── Moq.4.0.10827.nupkg ├── Moq.chm └── lib │ ├── NET35 │ ├── Moq.dll │ ├── Moq.pdb │ └── Moq.xml │ ├── NET40 │ ├── Moq.dll │ ├── Moq.pdb │ └── Moq.xml │ └── Silverlight4 │ ├── Castle.Core.dll │ ├── Moq.Silverlight.dll │ ├── Moq.Silverlight.pdb │ └── Moq.Silverlight.xml ├── repositories.config ├── xunit.1.8.0.1545 ├── lib │ ├── xunit.dll │ └── xunit.xml └── xunit.1.8.0.1545.nupkg └── xunit.extensions.1.8.0.1545 ├── lib ├── xunit.extensions.dll └── xunit.extensions.xml └── xunit.extensions.1.8.0.1545.nupkg /.gitignore: -------------------------------------------------------------------------------- 1 | BookingCloudService/*.cscfg 2 | */bin/ 3 | */obj/ 4 | *.user 5 | *.build.csdef 6 | *.suo -------------------------------------------------------------------------------- /Booking.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{CC5FD16D-436D-48AD-A40C-5A424C6E3E79}") = "BookingCloudService", "BookingCloudService\BookingCloudService.ccproj", "{76232EC3-D7E4-4F4B-8540-15C9F7CAF24B}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookingWebRole", "BookingWebRole\BookingWebRole.csproj", "{847E7EFD-9987-492F-A2E5-B6C45D464BA1}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookingWorkerRole", "BookingWorkerRole\BookingWorkerRole.csproj", "{9DFCF7A2-24F0-4EA8-AF67-ACD355F04F8F}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookingWebModel", "BookingWebModel\BookingWebModel.csproj", "{6772D240-02BB-4FC6-AEC9-452CE0E7F62C}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookingWebModelUnitTest", "BookingWebModelUnitTest\BookingWebModelUnitTest.csproj", "{8EA5B532-E684-4AAF-A334-CB5D24B5DB59}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookingDomainModel", "BookingDomainModel\BookingDomainModel.csproj", "{8D093E4A-467D-4009-AF62-773F325363A4}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookingDomainModelUnitTest", "BookingDomainModelUnitTest\BookingDomainModelUnitTest.csproj", "{57467E8E-79EF-458D-A1E2-4D9074A7645E}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookingAzureServices", "BookingAzureServices\BookingAzureServices.csproj", "{F6CD88B3-257E-4729-AD92-0D22D3FD3116}" 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmtpServices", "SmtpServices\SmtpServices.csproj", "{352EBB32-7FAC-4969-9EEC-2FE018CC3444}" 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Release|Any CPU = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {76232EC3-D7E4-4F4B-8540-15C9F7CAF24B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {76232EC3-D7E4-4F4B-8540-15C9F7CAF24B}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {76232EC3-D7E4-4F4B-8540-15C9F7CAF24B}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {76232EC3-D7E4-4F4B-8540-15C9F7CAF24B}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {847E7EFD-9987-492F-A2E5-B6C45D464BA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {847E7EFD-9987-492F-A2E5-B6C45D464BA1}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {847E7EFD-9987-492F-A2E5-B6C45D464BA1}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {847E7EFD-9987-492F-A2E5-B6C45D464BA1}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {9DFCF7A2-24F0-4EA8-AF67-ACD355F04F8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {9DFCF7A2-24F0-4EA8-AF67-ACD355F04F8F}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {9DFCF7A2-24F0-4EA8-AF67-ACD355F04F8F}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {9DFCF7A2-24F0-4EA8-AF67-ACD355F04F8F}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {6772D240-02BB-4FC6-AEC9-452CE0E7F62C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {6772D240-02BB-4FC6-AEC9-452CE0E7F62C}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {6772D240-02BB-4FC6-AEC9-452CE0E7F62C}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {6772D240-02BB-4FC6-AEC9-452CE0E7F62C}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {8EA5B532-E684-4AAF-A334-CB5D24B5DB59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {8EA5B532-E684-4AAF-A334-CB5D24B5DB59}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {8EA5B532-E684-4AAF-A334-CB5D24B5DB59}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {8EA5B532-E684-4AAF-A334-CB5D24B5DB59}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {8D093E4A-467D-4009-AF62-773F325363A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {8D093E4A-467D-4009-AF62-773F325363A4}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {8D093E4A-467D-4009-AF62-773F325363A4}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {8D093E4A-467D-4009-AF62-773F325363A4}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {57467E8E-79EF-458D-A1E2-4D9074A7645E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {57467E8E-79EF-458D-A1E2-4D9074A7645E}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {57467E8E-79EF-458D-A1E2-4D9074A7645E}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {57467E8E-79EF-458D-A1E2-4D9074A7645E}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {F6CD88B3-257E-4729-AD92-0D22D3FD3116}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {F6CD88B3-257E-4729-AD92-0D22D3FD3116}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {F6CD88B3-257E-4729-AD92-0D22D3FD3116}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {F6CD88B3-257E-4729-AD92-0D22D3FD3116}.Release|Any CPU.Build.0 = Release|Any CPU 60 | {352EBB32-7FAC-4969-9EEC-2FE018CC3444}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {352EBB32-7FAC-4969-9EEC-2FE018CC3444}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {352EBB32-7FAC-4969-9EEC-2FE018CC3444}.Release|Any CPU.ActiveCfg = Release|Any CPU 63 | {352EBB32-7FAC-4969-9EEC-2FE018CC3444}.Release|Any CPU.Build.0 = Release|Any CPU 64 | EndGlobalSection 65 | GlobalSection(SolutionProperties) = preSolution 66 | HideSolutionNode = FALSE 67 | EndGlobalSection 68 | EndGlobal 69 | -------------------------------------------------------------------------------- /BookingAzureServices/AzureChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.Samples.Booking.DomainModel; 6 | using Microsoft.WindowsAzure.StorageClient; 7 | using System.Runtime.Serialization.Formatters.Binary; 8 | using System.IO; 9 | 10 | namespace Ploeh.Samples.Booking.Azure 11 | { 12 | public class AzureChannel : IChannel 13 | { 14 | private readonly CloudQueue queue; 15 | 16 | public AzureChannel(CloudQueue queue) 17 | { 18 | if (queue == null) 19 | { 20 | throw new ArgumentNullException("queue"); 21 | } 22 | 23 | this.queue = queue; 24 | } 25 | 26 | #region ICommandChannel Members 27 | 28 | public void Send(object command) 29 | { 30 | var formatter = new BinaryFormatter(); 31 | using (var s = new MemoryStream()) 32 | { 33 | formatter.Serialize(s, command); 34 | var msg = new CloudQueueMessage(s.ToArray()); 35 | this.queue.AddMessage(msg); 36 | } 37 | } 38 | 39 | #endregion 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /BookingAzureServices/AzureMonthViewStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.Samples.Booking.WebModel; 6 | using Microsoft.WindowsAzure.StorageClient; 7 | 8 | namespace Ploeh.Samples.Booking.Azure 9 | { 10 | public class AzureMonthViewStore : IMonthViewWriter, IMonthViewReader 11 | { 12 | private readonly CloudBlobContainer viewContainer; 13 | 14 | public AzureMonthViewStore(CloudBlobContainer viewContainer) 15 | { 16 | if (viewContainer == null) 17 | { 18 | throw new ArgumentNullException("viewContainer"); 19 | } 20 | 21 | this.viewContainer = viewContainer; 22 | } 23 | 24 | #region IMonthViewWriter Members 25 | 26 | public void Disable(DateTime date) 27 | { 28 | var viewBlob = this.GetViewBlob(date); 29 | DateTime[] disabledDates = viewBlob.DownloadItem(); 30 | viewBlob.Upload(disabledDates 31 | .Union(new[] { date }).ToArray()); 32 | } 33 | 34 | #endregion 35 | 36 | #region IMonthViewReader Members 37 | 38 | public IEnumerable Read(int year, int month) 39 | { 40 | DateTime[] disabledDates = 41 | this.GetViewBlob(year, month).DownloadItem(); 42 | return (from d in disabledDates 43 | select d.ToString("yyyy.MM.dd")); 44 | } 45 | 46 | #endregion 47 | 48 | private SerializerBlob GetViewBlob(DateTime date) 49 | { 50 | var viewName = string.Format("view.month.{0}", date.ToString("yyyyMM")); 51 | return this.GetViewBlob(viewName); 52 | } 53 | 54 | private SerializerBlob GetViewBlob(int year, int month) 55 | { 56 | var viewName = string.Format("view.month.{0:D2}{1:D2}", year, month); 57 | return this.GetViewBlob(viewName); 58 | } 59 | 60 | private SerializerBlob GetViewBlob(string viewName) 61 | { 62 | return new SerializerBlob(this.viewContainer.GetBlobReference(viewName), () => new DateTime[0]); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /BookingAzureServices/AzureReservationRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.Samples.Booking.DomainModel; 6 | using System.Data.Services.Client; 7 | 8 | namespace Ploeh.Samples.Booking.Azure 9 | { 10 | public class AzureReservationRepository : IReservationRepository 11 | { 12 | private readonly ReservationContext context; 13 | 14 | public AzureReservationRepository(ReservationContext context) 15 | { 16 | if (context == null) 17 | { 18 | throw new ArgumentNullException("context"); 19 | } 20 | 21 | this.context = context; 22 | } 23 | 24 | #region IReservationRoot Members 25 | 26 | public void AddReservation(ReservationAcceptedEvent reservation) 27 | { 28 | var partitionKey = reservation.Date.Date.ToString("yyyyMMdd"); 29 | var rowKey = reservation.Id.ToString(); 30 | 31 | if (this.IsReplay(partitionKey, rowKey)) 32 | { 33 | return; 34 | } 35 | 36 | var r = new ReservationTableEntity 37 | { 38 | PartitionKey = reservation.Date.Date.ToString("yyyyMMdd"), 39 | RowKey = reservation.Id.ToString(), 40 | Date = reservation.Date, 41 | Name = reservation.Name, 42 | Email = reservation.Email, 43 | Quantity = reservation.Quantity 44 | }; 45 | 46 | this.context.AddReservation(r); 47 | this.context.SaveChanges(); 48 | } 49 | 50 | #endregion 51 | 52 | private bool IsReplay(string partitionKey, string rowKey) 53 | { 54 | try 55 | { 56 | return this.context.Reservations.Where(x => x.PartitionKey == partitionKey && x.RowKey == rowKey).AsEnumerable().Any(); 57 | } 58 | catch (DataServiceQueryException e) 59 | { 60 | var ie = e.InnerException as DataServiceClientException; 61 | if ((ie != null) && (ie.StatusCode == 404)) 62 | { 63 | return false; 64 | } 65 | throw; 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /BookingAzureServices/BookingAzureServices.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {F6CD88B3-257E-4729-AD92-0D22D3FD3116} 9 | Library 10 | Properties 11 | Ploeh.Samples.Booking.Azure 12 | Ploeh.Samples.Booking.Azure 13 | v4.0 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 | False 36 | ..\..\..\..\..\..\..\Program Files\Windows Azure SDK\v1.3\ref\Microsoft.WindowsAzure.StorageClient.dll 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 | {8D093E4A-467D-4009-AF62-773F325363A4} 64 | BookingDomainModel 65 | 66 | 67 | {6772D240-02BB-4FC6-AEC9-452CE0E7F62C} 68 | BookingWebModel 69 | 70 | 71 | 72 | 79 | -------------------------------------------------------------------------------- /BookingAzureServices/CapacityGuard.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.Samples.Booking.DomainModel; 6 | using Microsoft.WindowsAzure.StorageClient; 7 | using System.Runtime.Serialization.Formatters.Binary; 8 | using System.IO; 9 | using Ploeh.Samples.Booking.WebModel; 10 | 11 | namespace Ploeh.Samples.Booking.Azure 12 | { 13 | public class CapacityGuard : IMessageConsumer, IDayViewReader 14 | { 15 | private readonly CloudBlobContainer capacityContainer; 16 | private readonly IChannel channel; 17 | 18 | public CapacityGuard(CloudBlobContainer capacityContainer, IChannel channel) 19 | { 20 | if (capacityContainer == null) 21 | { 22 | throw new ArgumentNullException("capacityContainer"); 23 | } 24 | if (channel == null) 25 | { 26 | throw new ArgumentNullException("channel"); 27 | } 28 | 29 | this.capacityContainer = capacityContainer; 30 | this.channel = channel; 31 | } 32 | 33 | public bool HasCapacity(MakeReservationCommand reservation) 34 | { 35 | return this.GetCapacityBlob(reservation) 36 | .DownloadItem() 37 | .CanReserve(reservation.Quantity, reservation.Id); 38 | } 39 | 40 | #region IMessageConsumer Members 41 | 42 | public void Consume(MakeReservationCommand message) 43 | { 44 | var blob = this.GetCapacityBlob(message); 45 | var originalCapacity = blob.DownloadItem(); 46 | 47 | var newCapacity = originalCapacity.Reserve( 48 | message.Quantity, message.Id); 49 | 50 | if (!newCapacity.Equals(originalCapacity)) 51 | { 52 | blob.Upload(newCapacity); 53 | if (newCapacity.Remaining <= 0) 54 | { 55 | var e = new SoldOutEvent(message.Date); 56 | this.channel.Send(e); 57 | } 58 | } 59 | } 60 | 61 | #endregion 62 | 63 | #region IDayViewReader Members 64 | 65 | public int GetRemainingCapacity(DateTime date) 66 | { 67 | return this.GetCapacityBlob(date).DownloadItem().Remaining; 68 | } 69 | 70 | #endregion 71 | 72 | private SerializerBlob GetCapacityBlob(DateTime date) 73 | { 74 | var capacityName = CapacityGuard.GetCapacityName(date); 75 | return new SerializerBlob(this.capacityContainer.GetBlobReference(capacityName), () => new Capacity(10)); 76 | } 77 | 78 | private SerializerBlob GetCapacityBlob(MakeReservationCommand reservation) 79 | { 80 | return this.GetCapacityBlob(reservation.Date); 81 | } 82 | 83 | private static string GetCapacityName(DateTime date) 84 | { 85 | return string.Format("date.{0}", date.ToString("yyyyMMdd")); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /BookingAzureServices/IRequiresInitialization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.Azure 7 | { 8 | public interface IRequiresInitialization 9 | { 10 | void Initialize(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BookingAzureServices/MailContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.WindowsAzure.StorageClient; 6 | using Microsoft.WindowsAzure; 7 | using System.Data.Services.Client; 8 | 9 | namespace Ploeh.Samples.Booking.Azure 10 | { 11 | public class MailContext : TableServiceContext, IRequiresInitialization 12 | { 13 | private const string tableName = "MailMessages"; 14 | 15 | public MailContext(CloudStorageAccount account) 16 | : base(account.TableEndpoint.AbsoluteUri, account.Credentials) 17 | { 18 | } 19 | 20 | public DataServiceQuery MailMessages 21 | { 22 | get { return this.CreateQuery(MailContext.tableName); } 23 | } 24 | 25 | public void AddMailMessage(MailMessageTableEntity m) 26 | { 27 | this.AddObject(MailContext.tableName, m); 28 | } 29 | 30 | public void Initialize() 31 | { 32 | new CloudTableClient(this.BaseUri.ToString(), this.StorageCredentials).CreateTableIfNotExist(MailContext.tableName); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /BookingAzureServices/MailMessageTableEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.WindowsAzure.StorageClient; 6 | 7 | namespace Ploeh.Samples.Booking.Azure 8 | { 9 | public class MailMessageTableEntity : TableServiceEntity 10 | { 11 | public string Body { get; set; } 12 | 13 | public string Sender { get; set; } 14 | 15 | public string Subject { get; set; } 16 | 17 | public string Recipients { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BookingAzureServices/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("BookingAzureServices")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("BookingAzureServices")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2010")] 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("45ee9759-4cfe-4ef1-9bc0-c00113634889")] 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 | -------------------------------------------------------------------------------- /BookingAzureServices/ReservationContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.WindowsAzure.StorageClient; 6 | using Microsoft.WindowsAzure; 7 | using System.Data.Services.Client; 8 | 9 | namespace Ploeh.Samples.Booking.Azure 10 | { 11 | public class ReservationContext : TableServiceContext, IRequiresInitialization 12 | { 13 | private const string tableName = "Reservations"; 14 | 15 | public ReservationContext(CloudStorageAccount account) 16 | : base(account.TableEndpoint.AbsoluteUri, account.Credentials) 17 | { 18 | } 19 | 20 | public DataServiceQuery Reservations 21 | { 22 | get { return this.CreateQuery(ReservationContext.tableName); } 23 | } 24 | 25 | public void AddReservation(ReservationTableEntity reservation) 26 | { 27 | this.AddObject(ReservationContext.tableName, reservation); 28 | } 29 | 30 | public void Initialize() 31 | { 32 | new CloudTableClient(this.BaseUri.ToString(), this.StorageCredentials).CreateTableIfNotExist(ReservationContext.tableName); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /BookingAzureServices/ReservationTableEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.WindowsAzure.StorageClient; 6 | 7 | namespace Ploeh.Samples.Booking.Azure 8 | { 9 | public class ReservationTableEntity : TableServiceEntity 10 | { 11 | public DateTime Date { get; set; } 12 | 13 | public string Email { get; set; } 14 | 15 | public string Name { get; set; } 16 | 17 | public int Quantity { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BookingAzureServices/SerializerBlob.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.WindowsAzure.StorageClient; 6 | using Ploeh.Samples.Booking.DomainModel; 7 | using System.IO; 8 | using System.Runtime.Serialization.Formatters.Binary; 9 | 10 | namespace Ploeh.Samples.Booking.Azure 11 | { 12 | public class SerializerBlob : CloudBlob 13 | { 14 | private readonly Func createNewItem; 15 | private readonly BinaryFormatter formatter; 16 | 17 | public SerializerBlob(CloudBlob cloudBlob, Func initialValueFactory) 18 | : base(cloudBlob) 19 | { 20 | if (initialValueFactory == null) 21 | { 22 | throw new ArgumentNullException("initialValueFactory"); 23 | } 24 | 25 | this.createNewItem = initialValueFactory; 26 | this.formatter = new BinaryFormatter(); 27 | } 28 | 29 | public T DownloadItem() 30 | { 31 | var s = new MemoryStream(); 32 | try 33 | { 34 | this.DownloadToStream(s); 35 | s.Position = 0; 36 | 37 | return (T)this.formatter.Deserialize(s); 38 | } 39 | catch (StorageClientException e) 40 | { 41 | if (e.ErrorCode != StorageErrorCode.BlobNotFound) 42 | { 43 | throw; 44 | } 45 | 46 | return this.createNewItem(); 47 | } 48 | finally 49 | { 50 | s.Dispose(); 51 | } 52 | } 53 | 54 | public void Upload(T item) 55 | { 56 | using (var s = new MemoryStream()) 57 | { 58 | this.formatter.Serialize(s, item); 59 | s.Position = 0; 60 | 61 | var options = this.CreateBlobRequestOptions(); 62 | this.UploadFromStream(s, options); 63 | } 64 | } 65 | 66 | private BlobRequestOptions CreateBlobRequestOptions() 67 | { 68 | var etag = this.Properties.ETag; 69 | var options = new BlobRequestOptions(); 70 | options.AccessCondition = etag == null ? 71 | AccessCondition.IfNoneMatch("*") : 72 | AccessCondition.IfMatch(etag); 73 | return options; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /BookingAzureServices/TableStorageMailClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Net.Mail; 6 | using System.Data.Services.Client; 7 | 8 | namespace Ploeh.Samples.Booking.Azure 9 | { 10 | public class TableStorageMailClient 11 | { 12 | private readonly MailContext context; 13 | 14 | public TableStorageMailClient(MailContext context) 15 | { 16 | if (context == null) 17 | { 18 | throw new ArgumentNullException("context"); 19 | } 20 | 21 | this.context = context; 22 | } 23 | 24 | public void Send(MailMessage msg) 25 | { 26 | var time = DateTime.UtcNow; 27 | 28 | var partitionKey = time.Date.ToString("yyyyMMdd"); 29 | var rowKey = Guid.NewGuid().ToString("N"); 30 | if (this.IsReplay(partitionKey, rowKey)) 31 | { 32 | return; 33 | } 34 | 35 | var m = new MailMessageTableEntity 36 | { 37 | PartitionKey = partitionKey, 38 | RowKey = rowKey, 39 | Body = msg.Body, 40 | Recipients = msg.To.Select(a => a.ToString()).Aggregate((x, y) => x + "; " + y), 41 | Sender = msg.From.ToString(), 42 | Subject = msg.Subject 43 | }; 44 | 45 | this.context.AddMailMessage(m); 46 | this.context.SaveChanges(); 47 | } 48 | 49 | private bool IsReplay(string partitionKey, string rowKey) 50 | { 51 | try 52 | { 53 | return this.context.MailMessages.Where(x => x.PartitionKey == partitionKey && x.RowKey == rowKey).AsEnumerable().Any(); 54 | } 55 | catch (DataServiceQueryException e) 56 | { 57 | var ie = e.InnerException as DataServiceClientException; 58 | if ((ie != null) && (ie.StatusCode == 404)) 59 | { 60 | return false; 61 | } 62 | throw; 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /BookingCloudService/BookingCloudService.ccproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 1.3.0 7 | {76232ec3-d7e4-4f4b-8540-15c9f7caf24b} 8 | Library 9 | Properties 10 | BookingCloudService 11 | BookingCloudService 12 | True 13 | BookingCloudService 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | BookingWebRole 40 | {847e7efd-9987-492f-a2e5-b6c45d464ba1} 41 | True 42 | Web 43 | BookingWebRole 44 | 45 | 46 | BookingWorkerRole 47 | {9dfcf7a2-24f0-4ea8-af67-acd355f04f8f} 48 | True 49 | Worker 50 | BookingWorkerRole 51 | 52 | 53 | 54 | 55 | $(MSBuildExtensionsPath)\Microsoft\Cloud Service\1.0\Visual Studio 10.0\ 56 | 57 | 58 | -------------------------------------------------------------------------------- /BookingCloudService/ServiceConfiguration.cscfg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BookingCloudService/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 | -------------------------------------------------------------------------------- /BookingDomainModel/AcceptingReservationConsumer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | public class AcceptingReservationConsumer : IMessageConsumer 9 | { 10 | private readonly IChannel channel; 11 | 12 | public AcceptingReservationConsumer(IChannel channel) 13 | { 14 | if (channel == null) 15 | { 16 | throw new ArgumentNullException("channel"); 17 | } 18 | 19 | this.channel = channel; 20 | } 21 | 22 | #region IMessageConsumer Members 23 | 24 | public void Consume(MakeReservationCommand message) 25 | { 26 | if (message == null) 27 | { 28 | throw new ArgumentNullException("message"); 29 | } 30 | 31 | this.channel.Send(message.Accept()); 32 | } 33 | 34 | #endregion 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /BookingDomainModel/BookingDomainModel.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {8D093E4A-467D-4009-AF62-773F325363A4} 9 | Library 10 | Properties 11 | Ploeh.Samples.Booking.DomainModel 12 | Ploeh.Samples.Booking.DomainModel 13 | v4.0 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 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 66 | -------------------------------------------------------------------------------- /BookingDomainModel/Capacity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | [Serializable] 9 | public class Capacity : IEquatable 10 | { 11 | private readonly int remaining; 12 | private readonly IEnumerable acceptedReservations; 13 | 14 | public Capacity(int remaining, params Guid[] acceptedReservations) 15 | { 16 | if (acceptedReservations == null) 17 | { 18 | throw new ArgumentNullException("acceptedReservations"); 19 | } 20 | 21 | this.remaining = remaining; 22 | this.acceptedReservations = acceptedReservations; 23 | } 24 | 25 | public int Remaining 26 | { 27 | get { return this.remaining; } 28 | } 29 | 30 | public bool CanReserve(int quantity, Guid id) 31 | { 32 | if (this.IsReplay(id)) 33 | { 34 | return true; 35 | } 36 | return this.remaining >= quantity; 37 | } 38 | 39 | private bool IsReplay(Guid id) 40 | { 41 | return this.acceptedReservations.Contains(id); 42 | } 43 | 44 | public override bool Equals(object obj) 45 | { 46 | var other = obj as Capacity; 47 | if (other != null) 48 | { 49 | return this.Equals(other); 50 | } 51 | return base.Equals(obj); 52 | } 53 | 54 | public override int GetHashCode() 55 | { 56 | return this.acceptedReservations 57 | .Select(g => g.GetHashCode()) 58 | .Aggregate((x, y) => x ^ y) ^ this.Remaining; 59 | } 60 | 61 | public Capacity Reserve(int quantity, Guid id) 62 | { 63 | if (!this.CanReserve(quantity, id)) 64 | { 65 | throw new ArgumentOutOfRangeException("quantity", "The quantity must be less than or equal to the remaining quantity."); 66 | } 67 | 68 | if (this.IsReplay(id)) 69 | { 70 | return this; 71 | } 72 | 73 | return new Capacity(this.Remaining - quantity, 74 | this.acceptedReservations 75 | .Concat(new[] { id }).ToArray()); 76 | } 77 | 78 | #region IEquatable Members 79 | 80 | public bool Equals(Capacity other) 81 | { 82 | if (other == null) 83 | { 84 | return false; 85 | } 86 | 87 | return (this.Remaining == other.Remaining) 88 | && !this.acceptedReservations.Except(other.acceptedReservations).Any(); 89 | } 90 | 91 | #endregion 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /BookingDomainModel/CompositeMessageConsumer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | public class CompositeMessageConsumer : IMessageConsumer 9 | { 10 | private readonly IEnumerable> consumers; 11 | 12 | public CompositeMessageConsumer(params IMessageConsumer[] consumers) 13 | { 14 | if (consumers == null) 15 | { 16 | throw new ArgumentNullException("consumers"); 17 | } 18 | 19 | this.consumers = consumers; 20 | } 21 | 22 | public IEnumerable> Consumers 23 | { 24 | get { return this.consumers; } 25 | } 26 | 27 | #region IMessageConsumer Members 28 | 29 | public void Consume(T message) 30 | { 31 | foreach (var consumer in this.Consumers) 32 | { 33 | consumer.Consume(message); 34 | } 35 | } 36 | 37 | #endregion 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /BookingDomainModel/ConditionalMessageConsumer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | public class ConditionalMessageConsumer : IMessageConsumer 9 | { 10 | private Func condition; 11 | private IMessageConsumer first; 12 | private IMessageConsumer second; 13 | 14 | public ConditionalMessageConsumer(Func condition, IMessageConsumer first, IMessageConsumer second) 15 | { 16 | if (condition == null) 17 | { 18 | throw new ArgumentNullException("condition"); 19 | } 20 | if (first == null) 21 | { 22 | throw new ArgumentNullException("first"); 23 | } 24 | if (second == null) 25 | { 26 | throw new ArgumentNullException("second"); 27 | } 28 | 29 | this.condition = condition; 30 | this.first = first; 31 | this.second = second; 32 | } 33 | 34 | public Func Condition 35 | { 36 | get { return this.condition; } 37 | } 38 | 39 | public IMessageConsumer First 40 | { 41 | get { return this.first; } 42 | } 43 | 44 | public IMessageConsumer Second 45 | { 46 | get { return this.second; } 47 | } 48 | 49 | #region IMessageConsumer Members 50 | 51 | public void Consume(T message) 52 | { 53 | (this.condition(message) ? this.first : this.second).Consume(message); 54 | } 55 | 56 | #endregion 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /BookingDomainModel/IChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | public interface IChannel 9 | { 10 | void Send(object message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BookingDomainModel/IMessageConsumer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | public interface IMessageConsumer 9 | { 10 | void Consume(T message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BookingDomainModel/IReservationRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | public interface IReservationRepository 9 | { 10 | void AddReservation(ReservationAcceptedEvent reservation); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BookingDomainModel/MakeReservationCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | [Serializable] 9 | public class MakeReservationCommand 10 | { 11 | private readonly Guid id; 12 | private readonly DateTime date; 13 | private readonly string name; 14 | private readonly string email; 15 | private readonly int quantity; 16 | 17 | public MakeReservationCommand(DateTime date, string name, string email, int quantity) 18 | { 19 | if (name == null) 20 | { 21 | throw new ArgumentNullException("name"); 22 | } 23 | if (email == null) 24 | { 25 | throw new ArgumentNullException("email"); 26 | } 27 | 28 | this.id = Guid.NewGuid(); 29 | this.date = date; 30 | this.name = name; 31 | this.email = email; 32 | this.quantity = quantity; 33 | } 34 | 35 | public DateTime Date 36 | { 37 | get { return this.date; } 38 | } 39 | 40 | public string Email 41 | { 42 | get { return this.email; } 43 | } 44 | 45 | public Guid Id 46 | { 47 | get { return this.id; } 48 | } 49 | 50 | public string Name 51 | { 52 | get { return this.name; } 53 | } 54 | 55 | public int Quantity 56 | { 57 | get { return this.quantity; } 58 | } 59 | 60 | public ReservationAcceptedEvent Accept() 61 | { 62 | return new ReservationAcceptedEvent(this.Id, this.Date, this.Name, this.Email, this.Quantity); 63 | } 64 | 65 | public ReservationRejectedEvent Reject() 66 | { 67 | return new ReservationRejectedEvent(this.Id, this.Date, this.Name, this.Email, this.Quantity); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /BookingDomainModel/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("BookingDomainModel")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("BookingDomainModel")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2010")] 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("be0ca37b-aea7-443f-9a35-75488b1d82c2")] 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 | -------------------------------------------------------------------------------- /BookingDomainModel/RejectingReservationConsumer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | public class RejectingReservationConsumer : IMessageConsumer 9 | { 10 | private readonly IChannel channel; 11 | 12 | public RejectingReservationConsumer(IChannel channel) 13 | { 14 | if (channel == null) 15 | { 16 | throw new ArgumentNullException("channel"); 17 | } 18 | 19 | this.channel = channel; 20 | } 21 | 22 | #region IMessageConsumer Members 23 | 24 | public void Consume(MakeReservationCommand message) 25 | { 26 | if (message == null) 27 | { 28 | throw new ArgumentNullException("message"); 29 | } 30 | 31 | this.channel.Send(message.Reject()); 32 | } 33 | 34 | #endregion 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /BookingDomainModel/ReservationAcceptedEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | [Serializable] 9 | public class ReservationAcceptedEvent 10 | { 11 | private readonly Guid id; 12 | private readonly DateTime date; 13 | private readonly string name; 14 | private readonly string email; 15 | private readonly int quantity; 16 | 17 | public ReservationAcceptedEvent(Guid id, DateTime date, string name, string email, int quantity) 18 | { 19 | if (id == Guid.Empty) 20 | { 21 | throw new ArgumentException("Guid.Empty is not a valid value for id.", "id"); 22 | } 23 | if (name == null) 24 | { 25 | throw new ArgumentNullException("name"); 26 | } 27 | if (email == null) 28 | { 29 | throw new ArgumentNullException("email"); 30 | } 31 | 32 | this.id = id; 33 | this.date = date; 34 | this.name = name; 35 | this.email = email; 36 | this.quantity = quantity; 37 | } 38 | 39 | public DateTime Date 40 | { 41 | get { return this.date; } 42 | } 43 | 44 | public string Email 45 | { 46 | get { return this.email; } 47 | } 48 | 49 | public Guid Id 50 | { 51 | get { return this.id; } 52 | } 53 | 54 | public string Name 55 | { 56 | get { return this.name; } 57 | } 58 | 59 | public int Quantity 60 | { 61 | get { return this.quantity; } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /BookingDomainModel/ReservationRejectedEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | [Serializable] 9 | public class ReservationRejectedEvent 10 | { 11 | private readonly Guid id; 12 | private readonly DateTime date; 13 | private readonly string name; 14 | private readonly string email; 15 | private readonly int quantity; 16 | 17 | public ReservationRejectedEvent(Guid id, DateTime date, string name, string email, int quantity) 18 | { 19 | if (id == Guid.Empty) 20 | { 21 | throw new ArgumentException("Guid.Empty is not a valid value for id.", "id"); 22 | } 23 | if (name == null) 24 | { 25 | throw new ArgumentNullException("name"); 26 | } 27 | if (email == null) 28 | { 29 | throw new ArgumentNullException("email"); 30 | } 31 | 32 | this.id = id; 33 | this.date = date; 34 | this.name = name; 35 | this.email = email; 36 | this.quantity = quantity; 37 | } 38 | 39 | public DateTime Date 40 | { 41 | get { return this.date; } 42 | } 43 | 44 | public string Email 45 | { 46 | get { return this.email; } 47 | } 48 | 49 | public Guid Id 50 | { 51 | get { return this.id; } 52 | } 53 | 54 | public string Name 55 | { 56 | get { return this.name; } 57 | } 58 | 59 | public int Quantity 60 | { 61 | get { return this.quantity; } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /BookingDomainModel/ReservationWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | public class ReservationWriter : IMessageConsumer 9 | { 10 | private readonly IReservationRepository repository; 11 | 12 | public ReservationWriter(IReservationRepository repository) 13 | { 14 | if (repository == null) 15 | { 16 | throw new ArgumentNullException("repository"); 17 | } 18 | 19 | this.repository = repository; 20 | } 21 | 22 | #region ICommandConsumer Members 23 | 24 | public void Consume(MakeReservationCommand command) 25 | { 26 | if (command == null) 27 | { 28 | throw new ArgumentNullException("command"); 29 | } 30 | 31 | this.repository.AddReservation(command.Accept()); 32 | } 33 | 34 | #endregion 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /BookingDomainModel/SoldOutEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.DomainModel 7 | { 8 | [Serializable] 9 | public class SoldOutEvent 10 | { 11 | private readonly DateTime date; 12 | 13 | public SoldOutEvent(DateTime date) 14 | { 15 | this.date = date; 16 | } 17 | 18 | public DateTime Date 19 | { 20 | get { return this.date; } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/AcceptingReservationConsumerFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit.Extensions; 6 | using Xunit; 7 | using Ploeh.AutoFixture.Xunit; 8 | using Ploeh.SemanticComparison.Fluent; 9 | using Moq; 10 | using Ploeh.AutoFixture; 11 | 12 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 13 | { 14 | public class AcceptingReservationConsumerFacts 15 | { 16 | [Theory, AutoDomainData] 17 | public void SutIsCorrectMessageConsumer(AcceptingReservationConsumer sut) 18 | { 19 | Assert.IsAssignableFrom>(sut); 20 | } 21 | 22 | [Theory, AutoDomainData] 23 | public void ConsumePublishesEvent([Frozen]Mock channelMock, AcceptingReservationConsumer sut, MakeReservationCommand cmd) 24 | { 25 | var expectedEvent = cmd.Accept().AsSource().OfLikeness(); 26 | sut.Consume(cmd); 27 | channelMock.Verify(c => c.Send(expectedEvent)); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/AutoDomainDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.AutoFixture.Xunit; 6 | using Ploeh.AutoFixture; 7 | 8 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 9 | { 10 | public class AutoDomainDataAttribute : AutoDataAttribute 11 | { 12 | public AutoDomainDataAttribute() 13 | : base(new Fixture().Customize(new DomainCustomization())) 14 | { 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/BookingDomainModelUnitTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {57467E8E-79EF-458D-A1E2-4D9074A7645E} 9 | Library 10 | Properties 11 | Ploeh.Samples.Booking.DomainModel.UnitTest 12 | Ploeh.Samples.Booking.DomainModel.UnitTest 13 | v4.0 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 | False 36 | ..\packages\Moq.4.0.10827\lib\NET40\Moq.dll 37 | 38 | 39 | False 40 | ..\packages\AutoFixture.2.1\lib\Ploeh.AutoFixture.dll 41 | 42 | 43 | False 44 | ..\packages\AutoFixture.AutoMoq.2.1\lib\Ploeh.AutoFixture.AutoMoq.dll 45 | 46 | 47 | False 48 | ..\packages\AutoFixture.Xunit.2.1\lib\Ploeh.AutoFixture.Xunit.dll 49 | 50 | 51 | False 52 | ..\packages\AutoFixture.2.1\lib\Ploeh.SemanticComparison.dll 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | False 63 | ..\packages\xunit.1.8.0.1545\lib\xunit.dll 64 | 65 | 66 | False 67 | ..\packages\xunit.extensions.1.8.0.1545\lib\xunit.extensions.dll 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | {8D093E4A-467D-4009-AF62-773F325363A4} 89 | BookingDomainModel 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 104 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/CapacityFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit.Extensions; 6 | using Xunit; 7 | using Ploeh.AutoFixture.Xunit; 8 | using Ploeh.Samples.Booking.DomainModel; 9 | using Ploeh.AutoFixture; 10 | 11 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 12 | { 13 | public class CapacityFacts 14 | { 15 | [Theory, AutoDomainData] 16 | public void RemainingIsCorrect([Frozen]int capacity, Capacity sut) 17 | { 18 | Assert.Equal(capacity, sut.Remaining); 19 | } 20 | 21 | [Theory, AutoDomainData] 22 | public void CanReserveReturnsFalseWhenQuantityIsGreaterThanRemaining(Capacity sut, Guid id) 23 | { 24 | var greaterQuantity = sut.Remaining + 1; 25 | bool result = sut.CanReserve(greaterQuantity, id); 26 | Assert.False(result); 27 | } 28 | 29 | [Theory, AutoDomainData] 30 | public void CanReserveReturnsTrueWhenQuantityIsEqualToRemaining(Capacity sut, Guid id) 31 | { 32 | var result = sut.CanReserve(sut.Remaining, id); 33 | Assert.True(result); 34 | } 35 | 36 | [Theory, AutoDomainData] 37 | public void CanReserveReturnsTrueWhenQuantityIsLessThanRemaining(Capacity sut, Guid id) 38 | { 39 | var lesserQuantity = sut.Remaining - 1; 40 | var result = sut.CanReserve(lesserQuantity, id); 41 | Assert.True(result); 42 | } 43 | 44 | [Theory, AutoDomainData] 45 | public void CanReserveIsConsistentAccrossReplays(Capacity sut, Guid id) 46 | { 47 | var remaining = sut.Remaining; 48 | sut.Reserve(remaining, id); 49 | var result = sut.CanReserve(remaining, id); 50 | Assert.True(result); 51 | } 52 | 53 | [Theory, AutoDomainData] 54 | public void ReserveThrowsWhenQuantityIsGreaterThanRemaining(Capacity sut, Guid id) 55 | { 56 | var greaterQuantity = sut.Remaining + 1; 57 | Assert.Throws(() => 58 | sut.Reserve(greaterQuantity, id)); 59 | } 60 | 61 | [Theory, AutoDomainData] 62 | public void ReserveDoesNotThrowWhenQuantityIsEqualToRemaining(Capacity sut, Guid id) 63 | { 64 | Assert.DoesNotThrow(() => 65 | sut.Reserve(sut.Remaining, id)); 66 | } 67 | 68 | [Theory, AutoDomainData] 69 | public void ReserveDoesNotThrowWhenQuantityIsLessThanRemaining(Capacity sut, Guid id) 70 | { 71 | var lesserQuantity = sut.Remaining - 1; 72 | Assert.DoesNotThrow(() => 73 | sut.Reserve(lesserQuantity, id)); 74 | } 75 | 76 | [Theory, AutoDomainData] 77 | public void ReserveReturnsInstanceWithCorrectlyDecrementedRemaining(Guid id, int quantity, Capacity sut) 78 | { 79 | var expected = sut.Remaining - quantity; 80 | var result = sut.Reserve(quantity, id); 81 | Assert.Equal(expected, result.Remaining); 82 | } 83 | 84 | [Theory, AutoDomainData] 85 | public void ReserveReturnsEquivalentInstanceWhenReplayed(Guid id, int quantity, Capacity sut) 86 | { 87 | var expected = sut.Reserve(quantity, id); 88 | var result = sut.Reserve(quantity, id); 89 | Assert.Equal(expected, result); 90 | } 91 | 92 | [Theory, AutoDomainData] 93 | public void ReserveDoesNotHaveSideEffects(Guid id, int quantity, Capacity sut) 94 | { 95 | var result = sut.Reserve(quantity, id); 96 | Assert.NotEqual(result, sut); 97 | } 98 | 99 | [Theory, AutoDomainData] 100 | public void ReserveReturnsInstanceWithWithoutDecrementingRemainingWhenIdAlreadyExists([Frozen]Guid[] ids, int quantity, Capacity sut) 101 | { 102 | var existingId = ids.First(); 103 | var expectedRemaining = sut.Remaining; 104 | var result = sut.Reserve(quantity, existingId); 105 | Assert.Equal(expectedRemaining, result.Remaining); 106 | } 107 | 108 | [Theory, AutoDomainData] 109 | public void SutIsEquatable(Capacity sut) 110 | { 111 | Assert.IsAssignableFrom>(sut); 112 | } 113 | 114 | [Theory, AutoDomainData] 115 | public void SutDoesNotEqualNullObject(Capacity sut) 116 | { 117 | Assert.False(sut.Equals((object)null)); 118 | } 119 | 120 | [Theory, AutoDomainData] 121 | public void SutDoesNotEqualNullSut(Capacity sut) 122 | { 123 | Assert.False(sut.Equals((Capacity)null)); 124 | } 125 | 126 | [Theory, AutoDomainData] 127 | public void SutDoesNotEqualArbitraryOtherObject(Capacity sut, object other) 128 | { 129 | Assert.False(sut.Equals(other)); 130 | } 131 | 132 | [Theory, AutoDomainData] 133 | public void SutDoesNotEqualOtherObjectWhenRemainingDiffers(Capacity sut, Capacity other) 134 | { 135 | Assert.NotEqual(sut.Remaining, other.Remaining); 136 | Assert.False(sut.Equals((object)other)); 137 | } 138 | 139 | [Theory, AutoDomainData] 140 | public void SutDoesNotEqualOtherSutWhenRemainingDiffers(Capacity sut, Capacity other) 141 | { 142 | Assert.NotEqual(sut.Remaining, other.Remaining); 143 | Assert.False(sut.Equals(other)); 144 | } 145 | 146 | [Theory, AutoDomainData] 147 | public void SutDoesNotEqualOtherObjectWhenDifferentReservationsHaveBeenMade([Frozen]int remaining, Capacity sut, Capacity other) 148 | { 149 | Assert.False(sut.Equals((object)other)); 150 | } 151 | 152 | [Theory, AutoDomainData] 153 | public void SutDoesNotEqualOtherSutWhenDifferentReservationsHaveBeenMade([Frozen]int remaining, Capacity sut, Capacity other) 154 | { 155 | Assert.False(sut.Equals(other)); 156 | } 157 | 158 | [Theory, AutoDomainData] 159 | public void SutEqualsOtherObjectWhenBothReservationsAndRemainingAreEqual([Frozen]int remaining, [Frozen]Guid[] ids, Capacity sut, Capacity other) 160 | { 161 | Assert.True(sut.Equals((object)other)); 162 | } 163 | 164 | [Theory, AutoDomainData] 165 | public void SutEqualsOtherSutWhenBothReservationsAndRemainingAreEqual([Frozen]int remaining, [Frozen]Guid[] ids, Capacity sut, Capacity other) 166 | { 167 | Assert.True(sut.Equals(other)); 168 | } 169 | 170 | [Theory, AutoDomainData] 171 | public void GetHashCodeReturnsCorrectResult([Frozen]Guid[] ids, Capacity sut) 172 | { 173 | var expectedHashCode = ids.Select(g => g.GetHashCode()).Aggregate((x, y) => x ^ y) ^ sut.Remaining.GetHashCode(); 174 | Assert.Equal(expectedHashCode, sut.GetHashCode()); 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/CompositeMessageConsumerFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Moq; 4 | using Ploeh.AutoFixture.Xunit; 5 | using Xunit; 6 | using Xunit.Extensions; 7 | 8 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 9 | { 10 | public class ObjectCompositeMessageConsumerFacts : CompositeMessageConsumerFacts { } 11 | public class Int32CompositeMessageConsumerFacts : CompositeMessageConsumerFacts { } 12 | public class StringCompositeMessageConsumerFacts : CompositeMessageConsumerFacts { } 13 | public class VersionCompositeMessageConsumerFacts : CompositeMessageConsumerFacts { } 14 | 15 | public abstract class CompositeMessageConsumerFacts 16 | { 17 | [Theory, AutoDomainData] 18 | public void SutIsMessageConsumer(CompositeMessageConsumer sut) 19 | { 20 | Assert.IsAssignableFrom>(sut); 21 | } 22 | 23 | [Theory, AutoDomainData] 24 | public void ConsumersIsCorrect([Frozen]IMessageConsumer[] consumers, CompositeMessageConsumer sut) 25 | { 26 | Assert.True(consumers.SequenceEqual(sut.Consumers)); 27 | } 28 | 29 | [Theory, AutoDomainData] 30 | public void ConsumeConsumesAllConsumers(CompositeMessageConsumer sut, T message) 31 | { 32 | sut.Consume(message); 33 | var mocks = (from c in sut.Consumers 34 | select Mock.Get(c)).ToList(); 35 | mocks.ForEach(m => 36 | m.Verify(c => 37 | c.Consume(message))); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/ConditionalMessageConsumerFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Moq; 3 | using Ploeh.AutoFixture.Xunit; 4 | using Xunit; 5 | using Xunit.Extensions; 6 | 7 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 8 | { 9 | public class ObjectConditionalMessageConsumerFacts : ConditionalMessageConsumerFacts { } 10 | public class Int32ConditionalMessageConsumerFacts : ConditionalMessageConsumerFacts { } 11 | public class StringConditionalMessageConsumerFacts : ConditionalMessageConsumerFacts { } 12 | public class VersionConditionalMessageConsumerFacts : ConditionalMessageConsumerFacts { } 13 | 14 | public abstract class ConditionalMessageConsumerFacts 15 | { 16 | [Theory, AutoDomainData] 17 | public void SutIsMessageConsumer(ConditionalMessageConsumer sut) 18 | { 19 | Assert.IsAssignableFrom>(sut); 20 | } 21 | 22 | [Theory, AutoDomainData] 23 | public void FirstIsCorrect(IMessageConsumer first, IMessageConsumer second, Func condition) 24 | { 25 | var sut = ConditionalMessageConsumerFacts.CreateSut(first, second, condition); 26 | IMessageConsumer result = sut.First; 27 | Assert.Equal(first, result); 28 | } 29 | 30 | [Theory, AutoDomainData] 31 | public void SecondIsCorrect(IMessageConsumer first, IMessageConsumer second, Func condition) 32 | { 33 | var sut = ConditionalMessageConsumerFacts.CreateSut(first, second, condition); 34 | IMessageConsumer result = sut.Second; 35 | Assert.Equal(second, result); 36 | } 37 | 38 | [Theory, AutoDomainData] 39 | public void ConditionIsCorrect([Frozen]Func condition, ConditionalMessageConsumer sut) 40 | { 41 | Func result = sut.Condition; 42 | Assert.Equal(condition, result); 43 | } 44 | 45 | [Theory, AutoDomainData] 46 | public void FirstIsInvokedWhenConditionIsTrue(Mock> firstMock, Mock> secondStub, T message) 47 | { 48 | var sut = ConditionalMessageConsumerFacts.CreateSut(firstMock.Object, secondStub.Object, m => true); 49 | sut.Consume(message); 50 | firstMock.Verify(c => c.Consume(message)); 51 | } 52 | 53 | [Theory, AutoDomainData] 54 | public void SecondIsNotInvokedWhenConditionIsTrue(Mock> firstStub, Mock> secondMock, T message) 55 | { 56 | var sut = ConditionalMessageConsumerFacts.CreateSut(firstStub.Object, secondMock.Object, m => true); 57 | sut.Consume(message); 58 | secondMock.Verify(c => c.Consume(It.IsAny()), Times.Never()); 59 | } 60 | 61 | [Theory, AutoDomainData] 62 | public void SecondIsInvokedWhenConditionIsFalse(Mock> firstStub, Mock> secondMock, T message) 63 | { 64 | var sut = ConditionalMessageConsumerFacts.CreateSut(firstStub.Object, secondMock.Object, m => false); 65 | sut.Consume(message); 66 | secondMock.Verify(c => c.Consume(message)); 67 | } 68 | 69 | [Theory, AutoDomainData] 70 | public void FirstIsNotInvokedWhenConditionIsFalse(Mock> firstMock, Mock> secondStub, T message) 71 | { 72 | var sut = ConditionalMessageConsumerFacts.CreateSut(firstMock.Object, secondStub.Object, m => false); 73 | sut.Consume(message); 74 | firstMock.Verify(c => c.Consume(It.IsAny()), Times.Never()); 75 | } 76 | 77 | private static ConditionalMessageConsumer CreateSut(IMessageConsumer first, IMessageConsumer second, Func condition) 78 | { 79 | return new ConditionalMessageConsumer(condition, first, second); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/DomainCustomization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.AutoFixture; 6 | using Ploeh.AutoFixture.AutoMoq; 7 | 8 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 9 | { 10 | public class DomainCustomization : CompositeCustomization 11 | { 12 | public DomainCustomization() 13 | : base( 14 | new AutoMoqCustomization(), 15 | new FuncCustomization()) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/FuncCustomization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.AutoFixture; 6 | 7 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 8 | { 9 | public class FuncCustomization : ICustomization 10 | { 11 | #region ICustomization Members 12 | 13 | public void Customize(IFixture fixture) 14 | { 15 | fixture.Inject>(() => false); 16 | fixture.Inject>(x => false); 17 | fixture.Inject>(x => false); 18 | fixture.Inject>(x => false); 19 | fixture.Inject>(x => false); 20 | } 21 | 22 | #endregion 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/MakeReservationCommandFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit.Extensions; 6 | using Xunit; 7 | using Ploeh.AutoFixture.Xunit; 8 | using Ploeh.Samples.Booking.DomainModel; 9 | using Ploeh.SemanticComparison.Fluent; 10 | using Ploeh.AutoFixture; 11 | 12 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 13 | { 14 | public class MakeReservationCommandFacts 15 | { 16 | [Theory, AutoDomainData] 17 | public void DateIsCorrect([Frozen]DateTime date, MakeReservationCommand sut) 18 | { 19 | Assert.Equal(date, sut.Date); 20 | } 21 | 22 | [Theory, AutoDomainData] 23 | public void EmailIsCorrect([Frozen]string email, MakeReservationCommand sut) 24 | { 25 | Assert.Equal(email, sut.Email); 26 | } 27 | 28 | [Theory, AutoDomainData] 29 | public void NameIsCorrect([Frozen]string name, MakeReservationCommand sut) 30 | { 31 | Assert.Equal(name, sut.Name); 32 | } 33 | 34 | [Theory, AutoDomainData] 35 | public void QuantityIsCorrect([Frozen]int quantity, MakeReservationCommand sut) 36 | { 37 | Assert.Equal(quantity, sut.Quantity); 38 | } 39 | 40 | [Theory, AutoDomainData] 41 | public void IdIsUnique(MakeReservationCommand sut, MakeReservationCommand other) 42 | { 43 | Assert.NotEqual(other.Id, sut.Id); 44 | } 45 | 46 | [Theory, AutoDomainData] 47 | public void IdIsStable(MakeReservationCommand sut) 48 | { 49 | Assert.Equal(sut.Id, sut.Id); 50 | } 51 | 52 | [Theory, AutoDomainData] 53 | public void AcceptReturnsCorrectResult(MakeReservationCommand sut) 54 | { 55 | var expected = sut.AsSource().OfLikeness(); 56 | ReservationAcceptedEvent result = sut.Accept(); 57 | expected.ShouldEqual(result); 58 | } 59 | 60 | [Theory, AutoDomainData] 61 | public void RejectReturnsCorrectResult(MakeReservationCommand sut) 62 | { 63 | var expected = sut.AsSource().OfLikeness(); 64 | ReservationRejectedEvent result = sut.Reject(); 65 | expected.ShouldEqual(result); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/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("BookingDomainModelUnitTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("BookingDomainModelUnitTest")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2010")] 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("1cefd7d6-861f-4927-8622-fbdf789e5e06")] 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 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/RejectingReservationConsumerFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit.Extensions; 6 | using Xunit; 7 | using Ploeh.Samples.Booking.DomainModel; 8 | using Ploeh.AutoFixture.Xunit; 9 | using Moq; 10 | using Ploeh.SemanticComparison.Fluent; 11 | using Ploeh.AutoFixture; 12 | 13 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 14 | { 15 | public class RejectingReservationConsumerFacts 16 | { 17 | [Theory, AutoDomainData] 18 | public void SutIsCorrectConsumer(RejectingReservationConsumer sut) 19 | { 20 | Assert.IsAssignableFrom>(sut); 21 | } 22 | 23 | [Theory, AutoDomainData] 24 | public void ConsumePublishesEvent([Frozen]Mock channelMock, RejectingReservationConsumer sut, MakeReservationCommand cmd) 25 | { 26 | var expectedEvent = cmd.Reject().AsSource().OfLikeness(); 27 | sut.Consume(cmd); 28 | channelMock.Verify(c => c.Send(expectedEvent)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/ReservationAcceptedEventFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit.Extensions; 6 | using Xunit; 7 | using Ploeh.AutoFixture.Xunit; 8 | using Ploeh.Samples.Booking.DomainModel; 9 | using Ploeh.AutoFixture; 10 | 11 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 12 | { 13 | public class ReservationAcceptedEventFacts 14 | { 15 | [Theory, AutoDomainData] 16 | public void IdIsCorrect([Frozen]Guid id, ReservationAcceptedEvent sut) 17 | { 18 | Assert.Equal(id, sut.Id); 19 | } 20 | 21 | [Theory, AutoDomainData] 22 | public void DateIsCorrect([Frozen]DateTime date, ReservationAcceptedEvent sut) 23 | { 24 | Assert.Equal(date, sut.Date); 25 | } 26 | 27 | [Theory, AutoDomainData] 28 | public void NameIsCorrect([Frozen]string name, ReservationAcceptedEvent sut) 29 | { 30 | Assert.Equal(name, sut.Name); 31 | } 32 | 33 | [Theory, AutoDomainData] 34 | public void EmailIsCorrect([Frozen]string email, ReservationAcceptedEvent sut) 35 | { 36 | Assert.Equal(email, sut.Email); 37 | } 38 | 39 | [Theory, AutoDomainData] 40 | public void QuantityIsCorrect([Frozen]int quantity, ReservationAcceptedEvent sut) 41 | { 42 | Assert.Equal(quantity, sut.Quantity); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/ReservationRejectedEventFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit.Extensions; 6 | using Ploeh.AutoFixture.Xunit; 7 | using Xunit; 8 | using Ploeh.Samples.Booking.DomainModel; 9 | using Ploeh.AutoFixture; 10 | 11 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 12 | { 13 | public class ReservationRejectedEventFacts 14 | { 15 | [Theory, AutoDomainData] 16 | public void IdIsCorrect([Frozen]Guid id, ReservationRejectedEvent sut) 17 | { 18 | Assert.Equal(id, sut.Id); 19 | } 20 | 21 | [Theory, AutoDomainData] 22 | public void DateIsCorrect([Frozen]DateTime date, ReservationRejectedEvent sut) 23 | { 24 | Assert.Equal(date, sut.Date); 25 | } 26 | 27 | [Theory, AutoDomainData] 28 | public void NameIsCorrect([Frozen]string name, ReservationRejectedEvent sut) 29 | { 30 | Assert.Equal(name, sut.Name); 31 | } 32 | 33 | [Theory, AutoDomainData] 34 | public void EmailIsCorrect([Frozen]string email, ReservationRejectedEvent sut) 35 | { 36 | Assert.Equal(email, sut.Email); 37 | } 38 | 39 | [Theory, AutoDomainData] 40 | public void QuantityIsCorrect([Frozen]int quantity, ReservationRejectedEvent sut) 41 | { 42 | Assert.Equal(quantity, sut.Quantity); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/ReservationWriterFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit.Extensions; 6 | using Xunit; 7 | using Ploeh.Samples.Booking.DomainModel; 8 | using Ploeh.AutoFixture.Xunit; 9 | using Moq; 10 | using Ploeh.SemanticComparison.Fluent; 11 | using Ploeh.AutoFixture; 12 | 13 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 14 | { 15 | public class ReservationWriterFacts 16 | { 17 | [Theory, AutoDomainData] 18 | public void SutIsMessageConsumer(ReservationWriter sut) 19 | { 20 | Assert.IsAssignableFrom>(sut); 21 | } 22 | 23 | [Theory, AutoDomainData] 24 | public void ConsumeAddsReservationToRepository([Frozen]Mock repositoryMock, ReservationWriter sut, MakeReservationCommand cmd) 25 | { 26 | var expectedReseveration = cmd.Accept().AsSource().OfLikeness(); 27 | sut.Consume(cmd); 28 | repositoryMock.Verify(r => r.AddReservation(It.Is(e => expectedReseveration.Equals(e)))); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/SoldOutEventFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit.Extensions; 6 | using Xunit; 7 | using Ploeh.Samples.Booking.DomainModel; 8 | using Ploeh.AutoFixture.Xunit; 9 | using Ploeh.AutoFixture; 10 | 11 | namespace Ploeh.Samples.Booking.DomainModel.UnitTest 12 | { 13 | public class SoldOutEventFacts 14 | { 15 | [Theory, AutoDomainData] 16 | public void DateIsCorrect([Frozen]DateTime date, SoldOutEvent sut) 17 | { 18 | Assert.Equal(date, sut.Date); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /BookingDomainModelUnitTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BookingWebModel/BookingViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.Samples.Booking.DomainModel; 6 | 7 | namespace Ploeh.Samples.Booking.WebModel 8 | { 9 | public class BookingViewModel 10 | { 11 | public DateTime Date { get; set; } 12 | 13 | public string Email { get; set; } 14 | 15 | public string Name { get; set; } 16 | 17 | public int Quantity { get; set; } 18 | 19 | public int Remaining { get; set; } 20 | 21 | public MakeReservationCommand MakeNewReservation() 22 | { 23 | return new MakeReservationCommand(this.Date, 24 | this.Name, this.Email, this.Quantity); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /BookingWebModel/BookingWebModel.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {6772D240-02BB-4FC6-AEC9-452CE0E7F62C} 9 | Library 10 | Properties 11 | Ploeh.Samples.Booking.WebModel 12 | Ploeh.Samples.Booking.WebModel 13 | v4.0 14 | 512 15 | publish\ 16 | true 17 | Disk 18 | false 19 | Foreground 20 | 7 21 | Days 22 | false 23 | false 24 | true 25 | 0 26 | 1.0.0.%2a 27 | false 28 | false 29 | true 30 | 31 | 32 | true 33 | full 34 | false 35 | bin\Debug\ 36 | DEBUG;TRACE 37 | prompt 38 | 4 39 | AllRules.ruleset 40 | 41 | 42 | pdbonly 43 | true 44 | bin\Release\ 45 | TRACE 46 | prompt 47 | 4 48 | AllRules.ruleset 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | False 74 | Microsoft .NET Framework 4 %28x86 and x64%29 75 | true 76 | 77 | 78 | False 79 | .NET Framework 3.5 SP1 Client Profile 80 | false 81 | 82 | 83 | False 84 | .NET Framework 3.5 SP1 85 | false 86 | 87 | 88 | False 89 | Windows Installer 3.1 90 | true 91 | 92 | 93 | 94 | 95 | {8D093E4A-467D-4009-AF62-773F325363A4} 96 | BookingDomainModel 97 | 98 | 99 | 100 | 107 | -------------------------------------------------------------------------------- /BookingWebModel/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Web.Mvc; 6 | using Ploeh.Samples.Booking.DomainModel; 7 | 8 | namespace Ploeh.Samples.Booking.WebModel 9 | { 10 | public class HomeController : Controller 11 | { 12 | private readonly IChannel channel; 13 | private readonly IMonthViewReader monthReader; 14 | private readonly IDayViewReader dayReader; 15 | 16 | public HomeController(IChannel channel, IMonthViewReader monthReader, IDayViewReader dayReader) 17 | { 18 | if (channel == null) 19 | { 20 | throw new ArgumentNullException("channel"); 21 | } 22 | if (monthReader == null) 23 | { 24 | throw new ArgumentNullException("monthReader"); 25 | } 26 | if (dayReader == null) 27 | { 28 | throw new ArgumentNullException("dayReader"); 29 | } 30 | 31 | this.channel = channel; 32 | this.monthReader = monthReader; 33 | this.dayReader = dayReader; 34 | } 35 | 36 | public ViewResult Index() 37 | { 38 | var now = DateTime.Now; 39 | var model = this.monthReader.Read(now.Year, now.Month); 40 | return this.View(model); 41 | } 42 | 43 | [OutputCache(Duration = 0, VaryByParam = "none")] 44 | public JsonResult DisabledDays(int year, int month) 45 | { 46 | var data = this.monthReader.Read(year, month); 47 | return this.Json(data, JsonRequestBehavior.AllowGet); 48 | } 49 | 50 | public ViewResult NewBooking(int year, int month, int day) 51 | { 52 | var date = new DateTime(year, month, day); 53 | var remaining = this.dayReader.GetRemainingCapacity(date); 54 | var model = new BookingViewModel { Date = date, Remaining = remaining }; 55 | return this.View(model); 56 | } 57 | 58 | [HttpPost] 59 | public ViewResult NewBooking(BookingViewModel model) 60 | { 61 | if (model == null) 62 | { 63 | throw new ArgumentNullException("model"); 64 | } 65 | 66 | this.channel.Send(model.MakeNewReservation()); 67 | return this.View("BookingReceipt", model); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /BookingWebModel/IDayViewReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.WebModel 7 | { 8 | public interface IDayViewReader 9 | { 10 | int GetRemainingCapacity(DateTime date); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BookingWebModel/IMonthViewReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.WebModel 7 | { 8 | public interface IMonthViewReader 9 | { 10 | IEnumerable Read(int year, int month); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BookingWebModel/IMonthViewWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.WebModel 7 | { 8 | public interface IMonthViewWriter 9 | { 10 | void Disable(DateTime date); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BookingWebModel/MonthViewModelUpdater.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.Samples.Booking.DomainModel; 6 | 7 | namespace Ploeh.Samples.Booking.WebModel 8 | { 9 | public class MonthViewModelUpdater : IMessageConsumer 10 | { 11 | private readonly IMonthViewWriter writer; 12 | 13 | public MonthViewModelUpdater(IMonthViewWriter writer) 14 | { 15 | if (writer == null) 16 | { 17 | throw new ArgumentNullException("writer"); 18 | } 19 | 20 | this.writer = writer; 21 | } 22 | 23 | #region IMessageConsumer Members 24 | 25 | public void Consume(SoldOutEvent message) 26 | { 27 | if (message == null) 28 | { 29 | throw new ArgumentNullException("message"); 30 | } 31 | 32 | this.writer.Disable(message.Date); 33 | } 34 | 35 | #endregion 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /BookingWebModel/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("BookingWebModel")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("BookingWebModel")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2010")] 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("a9710151-1f28-46a5-9ebb-569531de0168")] 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 | -------------------------------------------------------------------------------- /BookingWebModelUnitTest/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /BookingWebModelUnitTest/AutoWebDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.AutoFixture.Xunit; 6 | using Ploeh.AutoFixture; 7 | 8 | namespace Ploeh.Samples.Booking.WebModel.UnitTest 9 | { 10 | public class AutoWebDataAttribute : AutoDataAttribute 11 | { 12 | public AutoWebDataAttribute() 13 | : base(new Fixture().Customize(new BookingWebModelCustomization())) 14 | { 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /BookingWebModelUnitTest/BookingViewModelFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit.Extensions; 6 | using Xunit; 7 | using Ploeh.AutoFixture.Xunit; 8 | using Ploeh.Samples.Booking.WebModel; 9 | using Ploeh.Samples.Booking.DomainModel; 10 | using Ploeh.SemanticComparison.Fluent; 11 | using Ploeh.AutoFixture; 12 | 13 | namespace Ploeh.Samples.Booking.WebModel.UnitTest 14 | { 15 | public class BookingViewModelFacts 16 | { 17 | [Theory, AutoWebData] 18 | public void DateIsCorrect([Frozen]DateTime date, BookingViewModel sut) 19 | { 20 | Assert.Equal(date, sut.Date); 21 | } 22 | 23 | [Theory, AutoWebData] 24 | public void NameIsCorrect([Frozen]string name, BookingViewModel sut) 25 | { 26 | Assert.Equal(name, sut.Name); 27 | } 28 | 29 | [Theory, AutoWebData] 30 | public void EmailIsCorrect([Frozen]string email, BookingViewModel sut) 31 | { 32 | Assert.Equal(email, sut.Email); 33 | } 34 | 35 | [Theory, AutoWebData] 36 | public void QuantityIsCorrect([Frozen]int quantity, BookingViewModel sut) 37 | { 38 | Assert.Equal(quantity, sut.Quantity); 39 | } 40 | 41 | [Theory, AutoWebData] 42 | public void RemainingEstimateIsProperWritableProper(BookingViewModel sut, int remaining) 43 | { 44 | sut.Remaining = remaining; 45 | var result = sut.Remaining; 46 | Assert.Equal(remaining, result); 47 | } 48 | 49 | [Theory, AutoWebData] 50 | public void MakeNewReservationReturnsCorrectResult(BookingViewModel sut) 51 | { 52 | MakeReservationCommand result = sut.MakeNewReservation(); 53 | var expected = sut.AsSource().OfLikeness().Without(d => d.Id); 54 | expected.ShouldEqual(result); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /BookingWebModelUnitTest/BookingWebModelCustomization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.AutoFixture; 6 | using Ploeh.AutoFixture.AutoMoq; 7 | 8 | namespace Ploeh.Samples.Booking.WebModel.UnitTest 9 | { 10 | public class BookingWebModelCustomization : CompositeCustomization 11 | { 12 | public BookingWebModelCustomization() 13 | : base( 14 | new AutoMoqCustomization(), 15 | new MvcCustomization()) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BookingWebModelUnitTest/HomeControllerFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit.Extensions; 6 | using Xunit; 7 | using Ploeh.Samples.Booking.WebModel; 8 | using System.Web.Mvc; 9 | using Ploeh.AutoFixture.Xunit; 10 | using Moq; 11 | using Ploeh.Samples.Booking.DomainModel; 12 | using Ploeh.SemanticComparison.Fluent; 13 | using Ploeh.AutoFixture; 14 | 15 | namespace Ploeh.Samples.Booking.WebModel.UnitTest 16 | { 17 | public class HomeControllerFacts 18 | { 19 | [Theory, AutoWebData] 20 | public void SutIsController(HomeController sut) 21 | { 22 | Assert.IsAssignableFrom(sut); 23 | } 24 | 25 | [Theory, AutoWebData] 26 | public void IndexReturnsCorrectModel([Frozen]MockreaderStub, string[] dates, HomeController sut) 27 | { 28 | var start = DateTime.Now; 29 | readerStub.Setup(r => r.Read(It.Is(y => start.Year <= y && y <= DateTime.Now.Year), It.Is(m => start.Month <= m && m <= DateTime.Now.Month))).Returns(dates); 30 | ViewResult result = sut.Index(); 31 | Assert.Equal(dates, result.ViewData.Model); 32 | } 33 | 34 | [Theory, AutoWebData] 35 | public void NewBookingReturnsInstance(HomeController sut, int year, int month, int day) 36 | { 37 | ViewResult result = sut.NewBooking(year, month, day); 38 | Assert.NotNull(result); 39 | } 40 | 41 | [Theory, AutoWebData] 42 | public void NewBookingReturnsCorrectTypeOfModel(HomeController sut, int year, int month, int day) 43 | { 44 | var result = sut.NewBooking(year, month, day); 45 | Assert.IsAssignableFrom(result.ViewData.Model); 46 | } 47 | 48 | [Theory, AutoWebData] 49 | public void NewBookingReturnsModelWithCorrectDate(HomeController sut, int year, int month, int day) 50 | { 51 | var result = sut.NewBooking(year, month, day); 52 | var vm = Assert.IsAssignableFrom(result.ViewData.Model); 53 | Assert.Equal(new DateTime(year, month, day), vm.Date); 54 | } 55 | 56 | [Theory, AutoWebData] 57 | public void NewBookingReturnsModelWithCorrectRemaining([Frozen]Mock readerStub, int remaining, HomeController sut, int year, int month, int day) 58 | { 59 | readerStub.Setup(r => r.GetRemainingCapacity(new DateTime(year, month, day))).Returns(remaining); 60 | var result = sut.NewBooking(year, month, day); 61 | var vm = Assert.IsAssignableFrom(result.ViewData.Model); 62 | Assert.Equal(remaining, vm.Remaining); 63 | } 64 | 65 | [Theory, AutoWebData] 66 | public void NewBookingPostReturnsInstance(HomeController sut, BookingViewModel model) 67 | { 68 | ViewResult result = sut.NewBooking(model); 69 | Assert.NotNull(result); 70 | } 71 | 72 | [Theory, AutoWebData] 73 | public void NewBookingPostReturnsCorrectViewName(HomeController sut, BookingViewModel model) 74 | { 75 | var result = sut.NewBooking(model); 76 | Assert.Equal("BookingReceipt", result.ViewName); 77 | } 78 | 79 | [Theory, AutoWebData] 80 | public void NewBookingPostReturnsCorrectTypeOfModel(HomeController sut, BookingViewModel model) 81 | { 82 | var result = sut.NewBooking(model); 83 | Assert.IsAssignableFrom(result.ViewData.Model); 84 | } 85 | 86 | [Theory, AutoWebData] 87 | public void NewBookingPostReturnsCorrectModel(HomeController sut, BookingViewModel model) 88 | { 89 | var result = sut.NewBooking(model); 90 | Assert.Equal(model, result.ViewData.Model); 91 | } 92 | 93 | [Theory, AutoWebData] 94 | public void NewBookingPostCorrectlySendsOnChannel([Frozen]Mock channelMock, HomeController sut, BookingViewModel model) 95 | { 96 | sut.NewBooking(model); 97 | var expected = model.AsSource().OfLikeness().Without(d => d.Id); 98 | channelMock.Verify(c => c.Send(expected)); 99 | } 100 | 101 | [Theory, AutoWebData] 102 | public void DisabledDaysReturnsCorrectResult([Frozen]Mock readerStub, string[] disableDays, HomeController sut, int year, int month) 103 | { 104 | readerStub.Setup(r => r.Read(year, month)).Returns(disableDays); 105 | JsonResult result = sut.DisabledDays(year, month); 106 | Assert.Equal(disableDays, result.Data); 107 | } 108 | 109 | [Theory, AutoWebData] 110 | public void DisabledDaysReturnsCorrectJsonBehavior(HomeController sut, int year, int month) 111 | { 112 | var result = sut.DisabledDays(year, month); 113 | Assert.Equal(JsonRequestBehavior.AllowGet, result.JsonRequestBehavior); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /BookingWebModelUnitTest/MonthViewModelUpdaterFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit.Extensions; 6 | using Xunit; 7 | using Ploeh.Samples.Booking.WebModel; 8 | using Ploeh.Samples.Booking.DomainModel; 9 | using Ploeh.AutoFixture.Xunit; 10 | using Moq; 11 | using Ploeh.AutoFixture; 12 | 13 | namespace Ploeh.Samples.Booking.WebModel.UnitTest 14 | { 15 | public class MonthViewModelUpdaterFacts 16 | { 17 | [Theory, AutoWebData] 18 | public void SutIsConsumer(MonthViewModelUpdater sut) 19 | { 20 | Assert.IsAssignableFrom>(sut); 21 | } 22 | 23 | [Theory, AutoWebData] 24 | public void ConsumeCorrectlyUpdatesViewStore([Frozen]Mock storeMock, MonthViewModelUpdater sut, SoldOutEvent @event) 25 | { 26 | sut.Consume(@event); 27 | storeMock.Verify(s => s.Disable(@event.Date)); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BookingWebModelUnitTest/MvcCustomization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.AutoFixture; 6 | using System.Web.Mvc; 7 | 8 | namespace Ploeh.Samples.Booking.WebModel.UnitTest 9 | { 10 | public class MvcCustomization : ICustomization 11 | { 12 | #region ICustomization Members 13 | 14 | public void Customize(IFixture fixture) 15 | { 16 | fixture.Customize(c => 17 | c.Without(vdd => vdd.ModelMetadata)); 18 | } 19 | 20 | #endregion 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /BookingWebModelUnitTest/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("BookingWebModelUnitTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("BookingWebModelUnitTest")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2010")] 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("15527f98-7a15-4c23-b7e9-63a8b5f11ada")] 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 | -------------------------------------------------------------------------------- /BookingWebModelUnitTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BookingWebRole/AzureServicesWindsorInstaller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using Castle.MicroKernel.Registration; 6 | using Castle.Windsor; 7 | using Castle.MicroKernel.SubSystems.Configuration; 8 | using Ploeh.Samples.Booking.WebModel; 9 | using Ploeh.Samples.Booking.Azure; 10 | using Ploeh.Samples.Booking.DomainModel; 11 | using Microsoft.WindowsAzure.StorageClient; 12 | using Microsoft.WindowsAzure; 13 | 14 | namespace Ploeh.Samples.Booking.WebUI 15 | { 16 | public class AzureServicesWindsorInstaller : IWindsorInstaller 17 | { 18 | #region IWindsorInstaller Members 19 | 20 | public void Install(IWindsorContainer container, IConfigurationStore store) 21 | { 22 | container.Register(Component 23 | .For() 24 | .ImplementedBy() 25 | .ServiceOverrides(new { viewContainer = "viewContainer" })); 26 | container.Register(Component 27 | .For() 28 | .ImplementedBy() 29 | .ServiceOverrides(new { viewContainer = "capacityContainer" })); 30 | container.Register(Component 31 | .For() 32 | .ImplementedBy() 33 | .LifeStyle.PerWebRequest); 34 | container.Register(Component 35 | .For() 36 | .UsingFactoryMethod(k => 37 | { 38 | var queueName = "messagequeue"; 39 | var client = k.Resolve(); 40 | var queue = client.GetQueueReference(queueName); 41 | queue.CreateIfNotExist(); 42 | return queue; 43 | }) 44 | .LifeStyle.PerWebRequest); 45 | container.Register(Component 46 | .For() 47 | .UsingFactoryMethod(k => k.Resolve().CreateCloudQueueClient()) 48 | .LifeStyle.PerWebRequest); 49 | container.Register(Component 50 | .For() 51 | .UsingFactoryMethod(k => 52 | { 53 | var c = k.Resolve().GetContainerReference("capacity"); 54 | c.CreateIfNotExist(); 55 | return c; 56 | }) 57 | .Named("capacityContainer")); 58 | container.Register(Component 59 | .For() 60 | .UsingFactoryMethod(k => 61 | { 62 | var c = k.Resolve().GetContainerReference("view"); 63 | c.CreateIfNotExist(); 64 | return c; 65 | }) 66 | .Named("viewContainer")); 67 | container.Register(Component 68 | .For() 69 | .UsingFactoryMethod(k => k.Resolve().CreateCloudBlobClient())); 70 | container.Register(Component 71 | .For() 72 | .UsingFactoryMethod(() => CloudStorageAccount.FromConfigurationSetting("DataConnectionString")) 73 | .LifeStyle.PerWebRequest); 74 | } 75 | 76 | #endregion 77 | } 78 | } -------------------------------------------------------------------------------- /BookingWebRole/BookingWebRoleWindsorInstaller.cs: -------------------------------------------------------------------------------- 1 | using Castle.MicroKernel.Registration; 2 | using Castle.MicroKernel.SubSystems.Configuration; 3 | using Castle.Windsor; 4 | 5 | namespace Ploeh.Samples.Booking.WebUI 6 | { 7 | public class BookingWebRoleWindsorInstaller : IWindsorInstaller 8 | { 9 | #region IWindsorInstaller Members 10 | 11 | public void Install(IWindsorContainer container, IConfigurationStore store) 12 | { 13 | new WebModelWindsorInstaller().Install(container, store); 14 | new AzureServicesWindsorInstaller().Install(container, store); 15 | } 16 | 17 | #endregion 18 | } 19 | } -------------------------------------------------------------------------------- /BookingWebRole/Content/Site.css: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------- 2 | The base color for this template is #5c87b2. If you'd like 3 | to use a different color start by replacing all instances of 4 | #5c87b2 with your new color. 5 | ----------------------------------------------------------*/ 6 | body 7 | { 8 | background-color: #5c87b2; 9 | font-size: .75em; 10 | font-family: Verdana, Helvetica, Sans-Serif; 11 | margin: 0; 12 | padding: 0; 13 | color: #696969; 14 | } 15 | 16 | a:link 17 | { 18 | color: #034af3; 19 | text-decoration: underline; 20 | } 21 | a:visited 22 | { 23 | color: #505abc; 24 | } 25 | a:hover 26 | { 27 | color: #1d60ff; 28 | text-decoration: none; 29 | } 30 | a:active 31 | { 32 | color: #12eb87; 33 | } 34 | 35 | p, ul 36 | { 37 | margin-bottom: 20px; 38 | line-height: 1.6em; 39 | } 40 | 41 | /* HEADINGS 42 | ----------------------------------------------------------*/ 43 | h1, h2, h3, h4, h5, h6 44 | { 45 | font-size: 1.5em; 46 | color: #000; 47 | font-family: Arial, Helvetica, sans-serif; 48 | } 49 | 50 | h1 51 | { 52 | font-size: 2em; 53 | padding-bottom: 0; 54 | margin-bottom: 0; 55 | } 56 | h2 57 | { 58 | padding: 0 0 10px 0; 59 | } 60 | h3 61 | { 62 | font-size: 1.2em; 63 | } 64 | h4 65 | { 66 | font-size: 1.1em; 67 | } 68 | h5, h6 69 | { 70 | font-size: 1em; 71 | } 72 | 73 | /* this rule styles

tags that are the 74 | first child of the left and right table columns */ 75 | .rightColumn > h1, .rightColumn > h2, .leftColumn > h1, .leftColumn > h2 76 | { 77 | margin-top: 0; 78 | } 79 | 80 | /* PRIMARY LAYOUT ELEMENTS 81 | ----------------------------------------------------------*/ 82 | 83 | /* you can specify a greater or lesser percentage for the 84 | page width. Or, you can specify an exact pixel width. */ 85 | .page 86 | { 87 | width: 90%; 88 | margin-left: auto; 89 | margin-right: auto; 90 | } 91 | 92 | #header 93 | { 94 | position: relative; 95 | margin-bottom: 0px; 96 | color: #000; 97 | padding: 0; 98 | } 99 | 100 | #header h1 101 | { 102 | font-weight: bold; 103 | padding: 5px 0; 104 | margin: 0; 105 | color: #fff; 106 | border: none; 107 | line-height: 2em; 108 | font-family: Arial, Helvetica, sans-serif; 109 | font-size: 32px !important; 110 | } 111 | 112 | #main 113 | { 114 | padding: 30px 30px 15px 30px; 115 | background-color: #fff; 116 | margin-bottom: 30px; 117 | _height: 1px; /* only IE6 applies CSS properties starting with an underscore */ 118 | } 119 | 120 | #footer 121 | { 122 | color: #999; 123 | padding: 10px 0; 124 | text-align: center; 125 | line-height: normal; 126 | margin: 0; 127 | font-size: .9em; 128 | } 129 | 130 | /* TAB MENU 131 | ----------------------------------------------------------*/ 132 | ul#menu 133 | { 134 | border-bottom: 1px #5C87B2 solid; 135 | padding: 0 0 2px; 136 | position: relative; 137 | margin: 0; 138 | text-align: right; 139 | } 140 | 141 | ul#menu li 142 | { 143 | display: inline; 144 | list-style: none; 145 | } 146 | 147 | ul#menu li#greeting 148 | { 149 | padding: 10px 20px; 150 | font-weight: bold; 151 | text-decoration: none; 152 | line-height: 2.8em; 153 | color: #fff; 154 | } 155 | 156 | ul#menu li a 157 | { 158 | padding: 10px 20px; 159 | font-weight: bold; 160 | text-decoration: none; 161 | line-height: 2.8em; 162 | background-color: #e8eef4; 163 | color: #034af3; 164 | } 165 | 166 | ul#menu li a:hover 167 | { 168 | background-color: #fff; 169 | text-decoration: none; 170 | } 171 | 172 | ul#menu li a:active 173 | { 174 | background-color: #a6e2a6; 175 | text-decoration: none; 176 | } 177 | 178 | ul#menu li.selected a 179 | { 180 | background-color: #fff; 181 | color: #000; 182 | } 183 | 184 | /* FORM LAYOUT ELEMENTS 185 | ----------------------------------------------------------*/ 186 | 187 | fieldset 188 | { 189 | margin: 1em 0; 190 | padding: 1em; 191 | border: 1px solid #CCC; 192 | } 193 | 194 | fieldset p 195 | { 196 | margin: 2px 12px 10px 10px; 197 | } 198 | 199 | legend 200 | { 201 | font-size: 1.1em; 202 | font-weight: 600; 203 | padding: 2px 4px 8px 4px; 204 | } 205 | 206 | input[type="text"] 207 | { 208 | width: 200px; 209 | border: 1px solid #CCC; 210 | } 211 | 212 | input[type="password"] 213 | { 214 | width: 200px; 215 | border: 1px solid #CCC; 216 | } 217 | 218 | /* TABLE 219 | ----------------------------------------------------------*/ 220 | 221 | table 222 | { 223 | border: solid 1px #e8eef4; 224 | border-collapse: collapse; 225 | } 226 | 227 | table td 228 | { 229 | padding: 5px; 230 | border: solid 1px #e8eef4; 231 | } 232 | 233 | table th 234 | { 235 | padding: 6px 5px; 236 | text-align: left; 237 | background-color: #e8eef4; 238 | border: solid 1px #e8eef4; 239 | } 240 | 241 | /* MISC 242 | ----------------------------------------------------------*/ 243 | .clear 244 | { 245 | clear: both; 246 | } 247 | 248 | .error 249 | { 250 | color:Red; 251 | } 252 | 253 | #menucontainer 254 | { 255 | margin-top:40px; 256 | } 257 | 258 | div#title 259 | { 260 | display:block; 261 | float:left; 262 | text-align:left; 263 | } 264 | 265 | #logindisplay 266 | { 267 | font-size:1.1em; 268 | display:block; 269 | text-align:right; 270 | margin:10px; 271 | color:White; 272 | } 273 | 274 | #logindisplay a:link 275 | { 276 | color: white; 277 | text-decoration: underline; 278 | } 279 | 280 | #logindisplay a:visited 281 | { 282 | color: white; 283 | text-decoration: underline; 284 | } 285 | 286 | #logindisplay a:hover 287 | { 288 | color: white; 289 | text-decoration: none; 290 | } 291 | 292 | /* Styles for validation helpers 293 | -----------------------------------------------------------*/ 294 | .field-validation-error 295 | { 296 | color: #ff0000; 297 | } 298 | 299 | .field-validation-valid 300 | { 301 | display: none; 302 | } 303 | 304 | .input-validation-error 305 | { 306 | border: 1px solid #ff0000; 307 | background-color: #ffeeee; 308 | } 309 | 310 | .validation-summary-errors 311 | { 312 | font-weight: bold; 313 | color: #ff0000; 314 | } 315 | 316 | .validation-summary-valid 317 | { 318 | display: none; 319 | } 320 | 321 | /* Styles for editor and display helpers 322 | ----------------------------------------------------------*/ 323 | .display-label, 324 | .editor-label, 325 | .display-field, 326 | .editor-field 327 | { 328 | margin: 0.5em 0; 329 | } 330 | 331 | .text-box 332 | { 333 | width: 30em; 334 | } 335 | 336 | .text-box.multi-line 337 | { 338 | height: 6.5em; 339 | } 340 | 341 | .tri-state 342 | { 343 | width: 6em; 344 | } 345 | -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /BookingWebRole/Content/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/BookingWebRole/Content/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /BookingWebRole/Content/jquery.ui.all.css: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI CSS Framework 1.8.7 3 | * 4 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * http://jquery.org/license 7 | * 8 | * http://docs.jquery.com/UI/Theming 9 | */ 10 | @import "jquery.ui.base.css"; 11 | @import "jquery.ui.theme.css"; 12 | -------------------------------------------------------------------------------- /BookingWebRole/Content/jquery.ui.base.css: -------------------------------------------------------------------------------- 1 | @import url("jquery.ui.core.css"); 2 | @import url("jquery.ui.datepicker.css"); -------------------------------------------------------------------------------- /BookingWebRole/Content/jquery.ui.core.css: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI CSS Framework 1.8.7 3 | * 4 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * http://jquery.org/license 7 | * 8 | * http://docs.jquery.com/UI/Theming/API 9 | */ 10 | 11 | /* Layout helpers 12 | ----------------------------------*/ 13 | .ui-helper-hidden { display: none; } 14 | .ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } 15 | .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } 16 | .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } 17 | .ui-helper-clearfix { display: inline-block; } 18 | /* required comment for clearfix to work in Opera \*/ 19 | * html .ui-helper-clearfix { height:1%; } 20 | .ui-helper-clearfix { display:block; } 21 | /* end clearfix */ 22 | .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } 23 | 24 | 25 | /* Interaction Cues 26 | ----------------------------------*/ 27 | .ui-state-disabled { cursor: default !important; } 28 | 29 | 30 | /* Icons 31 | ----------------------------------*/ 32 | 33 | /* states and images */ 34 | .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } 35 | 36 | 37 | /* Misc visuals 38 | ----------------------------------*/ 39 | 40 | /* Overlays */ 41 | .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } 42 | -------------------------------------------------------------------------------- /BookingWebRole/Content/jquery.ui.datepicker.css: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI Datepicker 1.8.7 3 | * 4 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * http://jquery.org/license 7 | * 8 | * http://docs.jquery.com/UI/Datepicker#theming 9 | */ 10 | .ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } 11 | .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } 12 | .ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } 13 | .ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } 14 | .ui-datepicker .ui-datepicker-prev { left:2px; } 15 | .ui-datepicker .ui-datepicker-next { right:2px; } 16 | .ui-datepicker .ui-datepicker-prev-hover { left:1px; } 17 | .ui-datepicker .ui-datepicker-next-hover { right:1px; } 18 | .ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } 19 | .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } 20 | .ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } 21 | .ui-datepicker select.ui-datepicker-month-year {width: 100%;} 22 | .ui-datepicker select.ui-datepicker-month, 23 | .ui-datepicker select.ui-datepicker-year { width: 49%;} 24 | .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } 25 | .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } 26 | .ui-datepicker td { border: 0; padding: 1px; } 27 | .ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } 28 | .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } 29 | .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } 30 | .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } 31 | 32 | /* with multiple calendars */ 33 | .ui-datepicker.ui-datepicker-multi { width:auto; } 34 | .ui-datepicker-multi .ui-datepicker-group { float:left; } 35 | .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } 36 | .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } 37 | .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } 38 | .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } 39 | .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } 40 | .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } 41 | .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } 42 | .ui-datepicker-row-break { clear:both; width:100%; } 43 | 44 | /* RTL support */ 45 | .ui-datepicker-rtl { direction: rtl; } 46 | .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } 47 | .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } 48 | .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } 49 | .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } 50 | .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } 51 | .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } 52 | .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } 53 | .ui-datepicker-rtl .ui-datepicker-group { float:right; } 54 | .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } 55 | .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } 56 | 57 | /* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ 58 | .ui-datepicker-cover { 59 | display: none; /*sorry for IE5*/ 60 | display/**/: block; /*sorry for IE5*/ 61 | position: absolute; /*must have*/ 62 | z-index: -1; /*must have*/ 63 | filter: mask(); /*must have*/ 64 | top: -4px; /*must have*/ 65 | left: -4px; /*must have*/ 66 | width: 200px; /*must have*/ 67 | height: 200px; /*must have*/ 68 | } -------------------------------------------------------------------------------- /BookingWebRole/Footer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using Microsoft.WindowsAzure.ServiceRuntime; 6 | 7 | namespace Ploeh.Samples.Booking.WebUI 8 | { 9 | public static class Footer 10 | { 11 | public static string RoleId 12 | { 13 | get 14 | { 15 | if (RoleEnvironment.IsAvailable) 16 | { 17 | return RoleEnvironment.CurrentRoleInstance.Id; 18 | } 19 | return "(site is running outside of Azure, so no role instance is available.)"; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /BookingWebRole/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Ploeh.Samples.Booking.WebUI.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /BookingWebRole/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Routing; 7 | using Ploeh.Samples.Booking.WebModel; 8 | using Castle.Windsor; 9 | using Microsoft.WindowsAzure; 10 | using Microsoft.WindowsAzure.ServiceRuntime; 11 | 12 | namespace Ploeh.Samples.Booking.WebUI 13 | { 14 | // Note: For instructions on enabling IIS6 or IIS7 classic mode, 15 | // visit http://go.microsoft.com/?LinkId=9394801 16 | 17 | public class MvcApplication : HttpApplication 18 | { 19 | private readonly IWindsorContainer container; 20 | 21 | public MvcApplication() 22 | { 23 | this.container = new WindsorContainer().Install(new BookingWebRoleWindsorInstaller()); 24 | } 25 | 26 | public static void RegisterRoutes(RouteCollection routes) 27 | { 28 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 29 | 30 | routes.MapRoute( 31 | name: "Date", 32 | url: "{controller}/{action}/{year}.{month}.{day}", 33 | defaults: new { controller = "Home", action = "Index" }, 34 | namespaces: new[] { typeof(HomeController).Namespace } 35 | ); 36 | routes.MapRoute( 37 | name: "Default", 38 | url: "{controller}/{action}/{id}", 39 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, 40 | namespaces: new[] { typeof(HomeController).Namespace } 41 | ); 42 | 43 | } 44 | 45 | protected void Application_Start() 46 | { 47 | AreaRegistration.RegisterAllAreas(); 48 | 49 | MvcApplication.RegisterRoutes(RouteTable.Routes); 50 | 51 | CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) => 52 | configSetter(RoleEnvironment.GetConfigurationSettingValue(configName))); 53 | 54 | ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(this.container)); 55 | } 56 | 57 | public override void Dispose() 58 | { 59 | this.container.Dispose(); 60 | base.Dispose(); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /BookingWebRole/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("BookingWebRole")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("BookingWebRole")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2010")] 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("c4cef498-8dc2-441a-b06a-fb764bbcef6d")] 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 | -------------------------------------------------------------------------------- /BookingWebRole/Scripts/MicrosoftMvcAjax.js: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------- 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | //---------------------------------------------------------- 4 | // MicrosoftMvcAjax.js 5 | 6 | Type.registerNamespace('Sys.Mvc');Sys.Mvc.$create_AjaxOptions=function(){return {};} 7 | Sys.Mvc.InsertionMode=function(){};Sys.Mvc.InsertionMode.prototype = {replace:0,insertBefore:1,insertAfter:2} 8 | Sys.Mvc.InsertionMode.registerEnum('Sys.Mvc.InsertionMode',false);Sys.Mvc.AjaxContext=function(request,updateTarget,loadingElement,insertionMode){this.$3=request;this.$4=updateTarget;this.$1=loadingElement;this.$0=insertionMode;} 9 | Sys.Mvc.AjaxContext.prototype={$0:0,$1:null,$2:null,$3:null,$4:null,get_data:function(){if(this.$2){return this.$2.get_responseData();}else{return null;}},get_insertionMode:function(){return this.$0;},get_loadingElement:function(){return this.$1;},get_object:function(){var $0=this.get_response();return ($0)?$0.get_object():null;},get_response:function(){return this.$2;},set_response:function(value){this.$2=value;return value;},get_request:function(){return this.$3;},get_updateTarget:function(){return this.$4;}} 10 | Sys.Mvc.AsyncHyperlink=function(){} 11 | Sys.Mvc.AsyncHyperlink.handleClick=function(anchor,evt,ajaxOptions){evt.preventDefault();Sys.Mvc.MvcHelpers.$2(anchor.href,'post','',anchor,ajaxOptions);} 12 | Sys.Mvc.MvcHelpers=function(){} 13 | Sys.Mvc.MvcHelpers.$0=function($p0,$p1,$p2){if($p0.disabled){return null;}var $0=$p0.name;if($0){var $1=$p0.tagName.toUpperCase();var $2=encodeURIComponent($0);var $3=$p0;if($1==='INPUT'){var $4=$3.type;if($4==='submit'){return $2+'='+encodeURIComponent($3.value);}else if($4==='image'){return $2+'.x='+$p1+'&'+$2+'.y='+$p2;}}else if(($1==='BUTTON')&&($0.length)&&($3.type==='submit')){return $2+'='+encodeURIComponent($3.value);}}return null;} 14 | Sys.Mvc.MvcHelpers.$1=function($p0){var $0=$p0.elements;var $1=new Sys.StringBuilder();var $2=$0.length;for(var $4=0;$4<$2;$4++){var $5=$0[$4];var $6=$5.name;if(!$6||!$6.length){continue;}var $7=$5.tagName.toUpperCase();if($7==='INPUT'){var $8=$5;var $9=$8.type;if(($9==='text')||($9==='password')||($9==='hidden')||((($9==='checkbox')||($9==='radio'))&&$5.checked)){$1.append(encodeURIComponent($6));$1.append('=');$1.append(encodeURIComponent($8.value));$1.append('&');}}else if($7==='SELECT'){var $A=$5;var $B=$A.options.length;for(var $C=0;$C<$B;$C++){var $D=$A.options[$C];if($D.selected){$1.append(encodeURIComponent($6));$1.append('=');$1.append(encodeURIComponent($D.value));$1.append('&');}}}else if($7==='TEXTAREA'){$1.append(encodeURIComponent($6));$1.append('=');$1.append(encodeURIComponent(($5.value)));$1.append('&');}}var $3=$p0._additionalInput;if($3){$1.append($3);$1.append('&');}return $1.toString();} 15 | Sys.Mvc.MvcHelpers.$2=function($p0,$p1,$p2,$p3,$p4){if($p4.confirm){if(!confirm($p4.confirm)){return;}}if($p4.url){$p0=$p4.url;}if($p4.httpMethod){$p1=$p4.httpMethod;}if($p2.length>0&&!$p2.endsWith('&')){$p2+='&';}$p2+='X-Requested-With=XMLHttpRequest';var $0=$p1.toUpperCase();var $1=($0==='GET'||$0==='POST');if(!$1){$p2+='&';$p2+='X-HTTP-Method-Override='+$0;}var $2='';if($0==='GET'||$0==='DELETE'){if($p0.indexOf('?')>-1){if(!$p0.endsWith('&')){$p0+='&';}$p0+=$p2;}else{$p0+='?';$p0+=$p2;}}else{$2=$p2;}var $3=new Sys.Net.WebRequest();$3.set_url($p0);if($1){$3.set_httpVerb($p1);}else{$3.set_httpVerb('POST');$3.get_headers()['X-HTTP-Method-Override']=$0;}$3.set_body($2);if($p1.toUpperCase()==='PUT'){$3.get_headers()['Content-Type']='application/x-www-form-urlencoded;';}$3.get_headers()['X-Requested-With']='XMLHttpRequest';var $4=null;if($p4.updateTargetId){$4=$get($p4.updateTargetId);}var $5=null;if($p4.loadingElementId){$5=$get($p4.loadingElementId);}var $6=new Sys.Mvc.AjaxContext($3,$4,$5,$p4.insertionMode);var $7=true;if($p4.onBegin){$7=$p4.onBegin($6)!==false;}if($5){Sys.UI.DomElement.setVisible($6.get_loadingElement(),true);}if($7){$3.add_completed(Function.createDelegate(null,function($p1_0){ 16 | Sys.Mvc.MvcHelpers.$3($3,$p4,$6);}));$3.invoke();}} 17 | Sys.Mvc.MvcHelpers.$3=function($p0,$p1,$p2){$p2.set_response($p0.get_executor());if($p1.onComplete&&$p1.onComplete($p2)===false){return;}var $0=$p2.get_response().get_statusCode();if(($0>=200&&$0<300)||$0===304||$0===1223){if($0!==204&&$0!==304&&$0!==1223){var $1=$p2.get_response().getResponseHeader('Content-Type');if(($1)&&($1.indexOf('application/x-javascript')!==-1)){eval($p2.get_data());}else{Sys.Mvc.MvcHelpers.updateDomElement($p2.get_updateTarget(),$p2.get_insertionMode(),$p2.get_data());}}if($p1.onSuccess){$p1.onSuccess($p2);}}else{if($p1.onFailure){$p1.onFailure($p2);}}if($p2.get_loadingElement()){Sys.UI.DomElement.setVisible($p2.get_loadingElement(),false);}} 18 | Sys.Mvc.MvcHelpers.updateDomElement=function(target,insertionMode,content){if(target){switch(insertionMode){case 0:target.innerHTML=content;break;case 1:if(content&&content.length>0){target.innerHTML=content+target.innerHTML.trimStart();}break;case 2:if(content&&content.length>0){target.innerHTML=target.innerHTML.trimEnd()+content;}break;}}} 19 | Sys.Mvc.AsyncForm=function(){} 20 | Sys.Mvc.AsyncForm.handleClick=function(form,evt){var $0=Sys.Mvc.MvcHelpers.$0(evt.target,evt.offsetX,evt.offsetY);form._additionalInput = $0;} 21 | Sys.Mvc.AsyncForm.handleSubmit=function(form,evt,ajaxOptions){evt.preventDefault();var $0=form.validationCallbacks;if($0){for(var $2=0;$2<$0.length;$2++){var $3=$0[$2];if(!$3()){return;}}}var $1=Sys.Mvc.MvcHelpers.$1(form);Sys.Mvc.MvcHelpers.$2(form.action,form.method||'post',$1,form,ajaxOptions);} 22 | Sys.Mvc.AjaxContext.registerClass('Sys.Mvc.AjaxContext');Sys.Mvc.AsyncHyperlink.registerClass('Sys.Mvc.AsyncHyperlink');Sys.Mvc.MvcHelpers.registerClass('Sys.Mvc.MvcHelpers');Sys.Mvc.AsyncForm.registerClass('Sys.Mvc.AsyncForm'); 23 | // ---- Do not remove this footer ---- 24 | // Generated using Script# v0.5.0.0 (http://projects.nikhilk.net) 25 | // ----------------------------------- 26 | -------------------------------------------------------------------------------- /BookingWebRole/Scripts/jquery.ui.core.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI 1.8.7 3 | * 4 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * http://jquery.org/license 7 | * 8 | * http://docs.jquery.com/UI 9 | */ 10 | (function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.7",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106, 11 | NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this, 12 | "position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position"); 13 | if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f, 14 | "border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h, 15 | d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}}); 16 | c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a 2 | 3 | 4 | BookingReceipt 5 | 6 | 7 |

8 | Thank your for your reservation!

9 |
10 | We have recorded the following about your request: 11 |
12 | Date
13 |
14 | <%: this.Model.Date.ToShortDateString() %>
15 |
16 | Name
17 |
18 | <%: this.Model.Name %>
19 |
20 | Email 21 |
22 |
23 | <%: this.Model.Email %> 24 |
25 |
26 | Quantity
27 |
28 | <%: this.Model.Quantity %>
29 |
30 |

31 | Please note that this page only verifies that we have received your request. It 32 | does not guarantee that we can fulfill your reservation, but in either case we will 33 | send you an email as soon as we know. 34 |

35 |

36 | <%: Html.ActionLink("Back to List", "Index") %> 37 |

38 |
39 | -------------------------------------------------------------------------------- /BookingWebRole/Views/Home/Index.aspx: -------------------------------------------------------------------------------- 1 | <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage>" %> 2 | 3 | 4 | Select a date 5 | 6 | 7 | 8 | 9 | 10 | 11 | 46 |

47 | Please select a date

48 |
49 |
50 |
51 | -------------------------------------------------------------------------------- /BookingWebRole/Views/Home/NewBooking.aspx: -------------------------------------------------------------------------------- 1 | <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> 2 | <%@ Import Namespace="Ploeh.Samples.Booking.WebModel" %> 3 | 4 | 5 | Make a reservation 6 | 7 | 8 |

9 | Make a reservation

10 | <%: this.Html.EditorForModel() %> 11 |
12 | -------------------------------------------------------------------------------- /BookingWebRole/Views/Shared/EditorTemplates/BookingViewModel.ascx: -------------------------------------------------------------------------------- 1 | <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> 2 | <% using (Html.BeginForm()) 3 | {%> 4 | <%: Html.ValidationSummary(true) %> 5 |
6 | Booking 7 |
8 | <%: this.Html.LabelFor(model => model.Date) %> 9 |
10 |
11 | <%: this.Model.Date.ToShortDateString() %> 12 |
13 |
14 | <%: this.Html.LabelFor(model => model.Name) %> 15 |
16 |
17 | <%: this.Html.TextBoxFor(model => model.Name)%> 18 | <%: this.Html.ValidationMessageFor(model => model.Name)%> 19 |
20 |
21 | <%: this.Html.LabelFor(model => model.Email)%> 22 |
23 |
24 | <%: this.Html.TextBoxFor(model => model.Email)%> 25 | <%: this.Html.ValidationMessageFor(model => model.Email)%> 26 |
27 |
28 | <%: this.Html.LabelFor(model => model.Quantity)%> 29 |
30 |
31 | <%: this.Html.DropDownListFor(model => model.Quantity, Enumerable.Range(1, this.Model.Remaining).Select(i => new SelectListItem { Value = i.ToString(), Text = i.ToString() })) %> 32 | <%: this.Html.ValidationMessageFor(model => model.Quantity)%> 33 |
34 |

35 | <%: this.Html.HiddenFor(model => model.Date) %> 36 | 37 |

38 |
39 | <% } %> 40 |
41 | <%: Html.ActionLink("Back to List", "Index") %> 42 |
43 | -------------------------------------------------------------------------------- /BookingWebRole/Views/Shared/Error.aspx: -------------------------------------------------------------------------------- 1 | <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> 2 | 3 | 4 | Error 5 | 6 | 7 | 8 |

9 | Sorry, an error occurred while processing your request. 10 |

11 |
12 | -------------------------------------------------------------------------------- /BookingWebRole/Views/Shared/Site.Master: -------------------------------------------------------------------------------- 1 | <%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %> 2 | 3 | 4 | 5 | 6 | <asp:ContentPlaceHolder ID="TitleContent" runat="server" /> 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 31 | 32 |
33 | 34 | 35 | 38 |
39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /BookingWebRole/Views/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /BookingWebRole/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | 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 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /BookingWebRole/WebModelWindsorInstaller.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using Castle.MicroKernel.Registration; 3 | using Castle.MicroKernel.SubSystems.Configuration; 4 | using Castle.Windsor; 5 | using Ploeh.Samples.Booking.WebModel; 6 | 7 | namespace Ploeh.Samples.Booking.WebUI 8 | { 9 | public class WebModelWindsorInstaller : IWindsorInstaller 10 | { 11 | #region IWindsorInstaller Members 12 | 13 | public void Install(IWindsorContainer container, IConfigurationStore store) 14 | { 15 | container.Register(AllTypes 16 | .FromAssemblyContaining() 17 | .BasedOn() 18 | .Configure(r => r.LifeStyle.PerWebRequest)); 19 | } 20 | 21 | #endregion 22 | } 23 | } -------------------------------------------------------------------------------- /BookingWebRole/WebRole.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.WindowsAzure; 5 | using Microsoft.WindowsAzure.Diagnostics; 6 | using Microsoft.WindowsAzure.ServiceRuntime; 7 | 8 | namespace Ploeh.Samples.Booking.WebUI 9 | { 10 | public class WebRole : RoleEntryPoint 11 | { 12 | public override bool OnStart() 13 | { 14 | //DiagnosticMonitor.Start("DiagnosticsConnectionString"); 15 | 16 | // For information on handling configuration changes 17 | // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357. 18 | //RoleEnvironment.Changing += RoleEnvironmentChanging; 19 | 20 | return base.OnStart(); 21 | } 22 | 23 | //private void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e) 24 | //{ 25 | // // If a configuration setting is changing 26 | // if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange)) 27 | // { 28 | // // Set e.Cancel to true to restart this role instance 29 | // e.Cancel = true; 30 | // } 31 | //} 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /BookingWebRole/WindsorControllerFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using Castle.Windsor; 3 | using System; 4 | using System.Web.Routing; 5 | 6 | namespace Ploeh.Samples.Booking.WebUI 7 | { 8 | public class WindsorControllerFactory : DefaultControllerFactory 9 | { 10 | private readonly IWindsorContainer container; 11 | 12 | public WindsorControllerFactory(IWindsorContainer container) 13 | { 14 | if (container == null) 15 | { 16 | throw new ArgumentNullException("container"); 17 | } 18 | 19 | this.container = container; 20 | } 21 | 22 | protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) 23 | { 24 | return (IController)this.container.Resolve(controllerType); 25 | } 26 | 27 | public override void ReleaseController(IController controller) 28 | { 29 | this.container.Release(controller); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /BookingWebRole/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /BookingWorkerRole/AzureMailServicesWindsorInstaller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Castle.MicroKernel.Registration; 6 | using Castle.MicroKernel.SubSystems.Configuration; 7 | using Castle.Windsor; 8 | using System.Net.Mail; 9 | using Ploeh.Samples.Booking.Azure; 10 | 11 | namespace Ploeh.Samples.Booking.WorkerRole 12 | { 13 | public class AzureMailServicesWindsorInstaller : IWindsorInstaller 14 | { 15 | #region IWindsorInstaller Members 16 | 17 | public void Install(IWindsorContainer container, IConfigurationStore store) 18 | { 19 | container.Register(Component 20 | .For>() 21 | .UsingFactoryMethod>(k => k.Resolve().Send)); 22 | container.Register(Component 23 | .For()); 24 | 25 | new MailAddressWindsorInstaller().Install(container, store); 26 | } 27 | 28 | #endregion 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BookingWorkerRole/AzureQueueMessageProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.WindowsAzure.StorageClient; 6 | using Castle.Windsor; 7 | using System.Threading; 8 | using System.Runtime.Serialization.Formatters.Binary; 9 | using System.IO; 10 | using Ploeh.Samples.Booking.DomainModel; 11 | using System.Diagnostics; 12 | 13 | namespace Ploeh.Samples.Booking.WorkerRole 14 | { 15 | public class AzureQueueMessageProcessor 16 | { 17 | private IWindsorContainer container; 18 | 19 | public AzureQueueMessageProcessor(IWindsorContainer container) 20 | { 21 | this.container = container; 22 | } 23 | 24 | public void Run(CancellationToken token) 25 | { 26 | var queue = this.container.Resolve(); 27 | 28 | while (!token.IsCancellationRequested) 29 | { 30 | Trace.WriteLine("Polling for message.", "Verbose"); 31 | 32 | this.PollForMessage(queue); 33 | } 34 | } 35 | 36 | public void PollForMessage(CloudQueue queue) 37 | { 38 | var message = queue.GetMessage(); 39 | if (message == null) 40 | { 41 | Thread.Sleep(500); 42 | return; 43 | } 44 | 45 | try 46 | { 47 | this.Handle(message); 48 | queue.DeleteMessage(message); 49 | } 50 | catch (Exception e) 51 | { 52 | if (e.IsUnsafeToSuppress()) 53 | { 54 | throw; 55 | } 56 | Trace.TraceError(e.ToString()); 57 | } 58 | } 59 | 60 | public void Handle(CloudQueueMessage message) 61 | { 62 | if (message == null) 63 | { 64 | throw new ArgumentNullException("message"); 65 | } 66 | 67 | var formatter = new BinaryFormatter(); 68 | using (var s = new MemoryStream(message.AsBytes)) 69 | { 70 | dynamic consumable = formatter.Deserialize(s); 71 | Trace.TraceInformation("Received {0} message.", (object)consumable); 72 | 73 | var consumerType = typeof(IMessageConsumer<>).MakeGenericType(consumable.GetType()); 74 | dynamic consumer = this.container.Resolve(consumerType); 75 | consumer.Consume(consumable); 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /BookingWorkerRole/AzureServicesWindsorInstaller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Castle.MicroKernel.Registration; 6 | using Castle.Windsor; 7 | using Castle.MicroKernel.SubSystems.Configuration; 8 | using Ploeh.Samples.Booking.DomainModel; 9 | using Ploeh.Samples.Booking.Azure; 10 | using Ploeh.Samples.Booking.WebModel; 11 | using Microsoft.WindowsAzure.StorageClient; 12 | using Microsoft.WindowsAzure; 13 | 14 | namespace Ploeh.Samples.Booking.WorkerRole 15 | { 16 | public class AzureServicesWindsorInstaller : IWindsorInstaller 17 | { 18 | 19 | #region IWindsorInstaller Members 20 | 21 | public void Install(IWindsorContainer container, IConfigurationStore store) 22 | { 23 | container.Register(Component 24 | .For() 25 | .ImplementedBy()); 26 | 27 | container.Register(AllTypes.FromAssemblyContaining() 28 | .Where(t => t.Name.EndsWith("Context") && typeof(IRequiresInitialization).IsAssignableFrom(t)) 29 | .WithService.Select((type, baseTypes) => new[] { type, typeof(IRequiresInitialization) })); 30 | 31 | container.Register(Component 32 | .For() 33 | .ImplementedBy()); 34 | container.Register(Component 35 | .For() 36 | .ServiceOverrides(new { capacityContainer = "capacityContainer" })); 37 | container.Register(Component 38 | .For() 39 | .ImplementedBy() 40 | .ServiceOverrides(new { viewContainer = "viewContainer" })); 41 | 42 | container.Register(Component 43 | .For() 44 | .UsingFactoryMethod(k => 45 | { 46 | var queueName = "messagequeue"; 47 | var client = k.Resolve(); 48 | var queue = client.GetQueueReference(queueName); 49 | queue.CreateIfNotExist(); 50 | return queue; 51 | })); 52 | container.Register(Component 53 | .For() 54 | .UsingFactoryMethod(k => 55 | { 56 | var c = k.Resolve().GetContainerReference("capacity"); 57 | c.CreateIfNotExist(); 58 | return c; 59 | }) 60 | .Named("capacityContainer")); 61 | container.Register(Component 62 | .For() 63 | .UsingFactoryMethod(k => 64 | { 65 | var c = k.Resolve().GetContainerReference("view"); 66 | c.CreateIfNotExist(); 67 | return c; 68 | }) 69 | .Named("viewContainer")); 70 | container.Register(Component 71 | .For() 72 | .UsingFactoryMethod(k => k.Resolve().CreateCloudQueueClient())); 73 | container.Register(Component 74 | .For() 75 | .UsingFactoryMethod(k => k.Resolve().CreateCloudBlobClient())); 76 | container.Register(Component 77 | .For() 78 | .UsingFactoryMethod(() => CloudStorageAccount.FromConfigurationSetting("DataConnectionString"))); 79 | 80 | foreach (var i in container.ResolveAll()) 81 | { 82 | i.Initialize(); 83 | } 84 | } 85 | 86 | #endregion 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /BookingWorkerRole/BookingWorkerRoleWindsorInstaller.cs: -------------------------------------------------------------------------------- 1 | using Castle.MicroKernel.Registration; 2 | using Castle.MicroKernel.SubSystems.Configuration; 3 | using Castle.Windsor; 4 | 5 | namespace Ploeh.Samples.Booking.WorkerRole 6 | { 7 | public class BookingWorkerRoleWindsorInstaller : IWindsorInstaller 8 | { 9 | #region IWindsorInstaller Members 10 | 11 | public void Install(IWindsorContainer container, IConfigurationStore store) 12 | { 13 | new DomainModelWindsorInstaller().Install(container, store); 14 | 15 | new SmtpServicesWindsorInstaller().Install(container, store); 16 | //new AzureMailServicesWindsorInstaller().Install(container, store); 17 | 18 | new AzureServicesWindsorInstaller().Install(container, store); 19 | } 20 | 21 | #endregion 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BookingWorkerRole/DomainModelWindsorInstaller.cs: -------------------------------------------------------------------------------- 1 | using Castle.MicroKernel.Registration; 2 | using Castle.MicroKernel.SubSystems.Configuration; 3 | using Castle.Windsor; 4 | using Ploeh.Samples.Booking.Azure; 5 | using Ploeh.Samples.Booking.DomainModel; 6 | using Ploeh.Samples.Booking.Smtp; 7 | using Ploeh.Samples.Booking.WebModel; 8 | 9 | namespace Ploeh.Samples.Booking.WorkerRole 10 | { 11 | public class DomainModelWindsorInstaller : IWindsorInstaller 12 | { 13 | #region IWindsorInstaller Members 14 | 15 | public void Install(IWindsorContainer container, IConfigurationStore store) 16 | { 17 | container.Register(Component 18 | .For>() 19 | .UsingFactoryMethod(k => 20 | { 21 | var guard = k.Resolve(); 22 | var first = new CompositeMessageConsumer(guard, k.Resolve(), k.Resolve()); 23 | var second = k.Resolve(); 24 | return new ConditionalMessageConsumer(guard.HasCapacity, first, second); 25 | })); 26 | container.Register(Component 27 | .For>() 28 | .ImplementedBy()); 29 | container.Register(Component 30 | .For>() 31 | .ImplementedBy()); 32 | container.Register(Component 33 | .For>() 34 | .ImplementedBy()); 35 | container.Register(Component 36 | .For()); 37 | container.Register(Component 38 | .For()); 39 | container.Register(Component 40 | .For()); 41 | } 42 | 43 | #endregion 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /BookingWorkerRole/ExceptionPolicy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ploeh.Samples.Booking.WorkerRole 7 | { 8 | public static class ExceptionPolicy 9 | { 10 | public static bool IsUnsafeToSuppress(this Exception e) 11 | { 12 | // Cheating by suppressing every caught exception. Don't do this in production code. 13 | return false; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BookingWorkerRole/MailAddressWindsorInstaller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Castle.MicroKernel.Registration; 6 | using Castle.Windsor; 7 | using Castle.MicroKernel.SubSystems.Configuration; 8 | using System.Net.Mail; 9 | using Microsoft.WindowsAzure.ServiceRuntime; 10 | 11 | namespace Ploeh.Samples.Booking.WorkerRole 12 | { 13 | public class MailAddressWindsorInstaller : IWindsorInstaller 14 | { 15 | #region IWindsorInstaller Members 16 | 17 | public void Install(IWindsorContainer container, IConfigurationStore store) 18 | { 19 | container.Register(Component 20 | .For() 21 | .UsingFactoryMethod(() => 22 | { 23 | var senderAddress = RoleEnvironment.GetConfigurationSettingValue("EmailSenderSmtpAddress"); 24 | var senderDisplayName = RoleEnvironment.GetConfigurationSettingValue("EmailSenderDisplayName"); 25 | return new MailAddress(senderAddress, senderDisplayName); 26 | })); 27 | } 28 | 29 | #endregion 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BookingWorkerRole/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("BookingWorkerRole")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("BookingWorkerRole")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2010")] 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("18b9d631-4e53-4d9c-b641-c9cee3647f12")] 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 | -------------------------------------------------------------------------------- /BookingWorkerRole/SmtpServicesWindsorInstaller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Castle.MicroKernel.Registration; 6 | using Castle.MicroKernel.SubSystems.Configuration; 7 | using Castle.Windsor; 8 | using System.Net.Mail; 9 | using Microsoft.WindowsAzure.ServiceRuntime; 10 | using System.Net; 11 | 12 | namespace Ploeh.Samples.Booking.WorkerRole 13 | { 14 | public class SmtpServicesWindsorInstaller : IWindsorInstaller 15 | { 16 | #region IWindsorInstaller Members 17 | 18 | public void Install(IWindsorContainer container, IConfigurationStore store) 19 | { 20 | container.Register(Component 21 | .For>() 22 | .UsingFactoryMethod>(k => k.Resolve().Send)); 23 | container.Register(Component 24 | .For() 25 | .UsingFactoryMethod(() => 26 | { 27 | var smtpServerAddress = RoleEnvironment.GetConfigurationSettingValue("SmtpServerAddress"); 28 | var smtpUserName = RoleEnvironment.GetConfigurationSettingValue("SmtpUserName"); 29 | var smtpPassword = RoleEnvironment.GetConfigurationSettingValue("SmtpPassword"); 30 | var client = new SmtpClient(smtpServerAddress); 31 | client.Credentials = new NetworkCredential(smtpUserName, smtpPassword); 32 | return client; 33 | })); 34 | 35 | new MailAddressWindsorInstaller().Install(container, store); 36 | } 37 | 38 | #endregion 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BookingWorkerRole/WorkerRole.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Threading; 7 | using Microsoft.WindowsAzure; 8 | using Microsoft.WindowsAzure.Diagnostics; 9 | using Microsoft.WindowsAzure.ServiceRuntime; 10 | using Microsoft.WindowsAzure.StorageClient; 11 | using Castle.Windsor; 12 | 13 | namespace Ploeh.Samples.Booking.WorkerRole 14 | { 15 | public class WorkerRole : RoleEntryPoint 16 | { 17 | private IWindsorContainer container; 18 | private readonly CancellationTokenSource cancellationTokenSource; 19 | 20 | public WorkerRole() 21 | { 22 | this.cancellationTokenSource = new CancellationTokenSource(); 23 | } 24 | 25 | public override void Run() 26 | { 27 | Trace.WriteLine("BookingWorkerRole entry point called", "Verbose"); 28 | 29 | var messageHandler = new AzureQueueMessageProcessor(this.container); 30 | messageHandler.Run(this.cancellationTokenSource.Token); 31 | } 32 | 33 | public override bool OnStart() 34 | { 35 | // Set the maximum number of concurrent connections 36 | ServicePointManager.DefaultConnectionLimit = 12; 37 | 38 | //DiagnosticMonitor.Start("DiagnosticsConnectionString"); 39 | 40 | // For information on handling configuration changes 41 | // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357. 42 | //RoleEnvironment.Changing += RoleEnvironmentChanging; 43 | 44 | CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) => 45 | configSetter(RoleEnvironment.GetConfigurationSettingValue(configName))); 46 | 47 | this.container = new WindsorContainer().Install(new BookingWorkerRoleWindsorInstaller()); 48 | 49 | return base.OnStart(); 50 | } 51 | 52 | public override void OnStop() 53 | { 54 | this.cancellationTokenSource.Cancel(); 55 | this.container.Dispose(); 56 | base.OnStop(); 57 | } 58 | 59 | //private void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e) 60 | //{ 61 | // // If a configuration setting is changing 62 | // if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange)) 63 | // { 64 | // // Set e.Cancel to true to restart this role instance 65 | // e.Cancel = true; 66 | // } 67 | //} 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /BookingWorkerRole/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /BookingWorkerRole/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ReadMe.txt: -------------------------------------------------------------------------------- 1 | This is a more updated and polished version of the demo code accompanying the MSDN Magazine article CQRS on Azure (http://msdn.microsoft.com/en-us/magazine/gg983487.aspx). 2 | 3 | To enable sending of email you must edit the BookingCloudService\ServiceConfiguration.cscfg to configure an SMTP server. 4 | 5 | These are the settings which should be configured: 6 | 7 | 8 | 9 | 10 | 11 | 12 | As this is Windows Azure demo code, you must have the Windows Azure SDK installed in Visual Studio 2010 in order to compile. -------------------------------------------------------------------------------- /ReplaceAzureConfig.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/ReplaceAzureConfig.ps1 -------------------------------------------------------------------------------- /SmtpServices/AcceptedReserverationMailer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Ploeh.Samples.Booking.DomainModel; 6 | using System.Net.Mail; 7 | 8 | namespace Ploeh.Samples.Booking.Smtp 9 | { 10 | public class AcceptedReserverationMailer : IMessageConsumer 11 | { 12 | private readonly Action send; 13 | private readonly MailAddress sender; 14 | 15 | public AcceptedReserverationMailer(Action mailClient, MailAddress sender) 16 | { 17 | this.send = mailClient; 18 | this.sender = sender; 19 | } 20 | 21 | #region IMessageConsumer Members 22 | 23 | public void Consume(ReservationAcceptedEvent message) 24 | { 25 | var msg = new MailMessage(); 26 | msg.From = this.sender; 27 | msg.To.Add(new MailAddress(message.Email)); 28 | msg.Subject = "Your Reservation Was Accepted"; 29 | msg.Body = string.Format("Dear {1}{0}{0}Thank you for your inquiry. We have the pleasure to inform you that we can accomodate {2} guest(s) at {3}. We look forward to seeing you.{0}{0}Regards", Environment.NewLine, message.Name, message.Quantity, message.Date.ToShortDateString()); 30 | 31 | this.send(msg); 32 | } 33 | 34 | #endregion 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SmtpServices/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("SmtpServices")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("SmtpServices")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2010")] 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("985a35dc-b7b8-42da-8f7f-04dd987b1967")] 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 | -------------------------------------------------------------------------------- /SmtpServices/RejectedReservationMailer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Net.Mail; 6 | using Ploeh.Samples.Booking.DomainModel; 7 | 8 | namespace Ploeh.Samples.Booking.Smtp 9 | { 10 | public class RejectedReservationMailer : IMessageConsumer 11 | { 12 | private readonly Action send; 13 | private readonly MailAddress sender; 14 | 15 | public RejectedReservationMailer(Action mailClient, MailAddress sender) 16 | { 17 | this.send = mailClient; 18 | this.sender = sender; 19 | } 20 | 21 | #region IMessageConsumer Members 22 | 23 | public void Consume(ReservationRejectedEvent message) 24 | { 25 | var msg = new MailMessage(); 26 | msg.From = this.sender; 27 | msg.To.Add(new MailAddress(message.Email)); 28 | msg.Subject = "Your Reservation Could Not Be Fulfilled"; 29 | msg.Body = string.Format("Dear {1}{0}{0}Thank you for your inquiry. Unfortunately we must inform you that we can't accomodate {2} guest(s) at {3}. Please inquire again about another date.{0}{0}Regards", Environment.NewLine, message.Name, message.Quantity, message.Date.ToShortDateString()); 30 | 31 | this.send(msg); 32 | } 33 | 34 | #endregion 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SmtpServices/SmtpServices.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {352EBB32-7FAC-4969-9EEC-2FE018CC3444} 9 | Library 10 | Properties 11 | Ploeh.Samples.Booking.Smtp 12 | Ploeh.Samples.Booking.Smtp 13 | v4.0 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 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | {8D093E4A-467D-4009-AF62-773F325363A4} 50 | BookingDomainModel 51 | 52 | 53 | 54 | 61 | -------------------------------------------------------------------------------- /packages/AutoFixture.2.1/AutoFixture.2.1.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/AutoFixture.2.1/AutoFixture.2.1.nupkg -------------------------------------------------------------------------------- /packages/AutoFixture.2.1/lib/Ploeh.AutoFixture.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/AutoFixture.2.1/lib/Ploeh.AutoFixture.dll -------------------------------------------------------------------------------- /packages/AutoFixture.2.1/lib/Ploeh.SemanticComparison.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/AutoFixture.2.1/lib/Ploeh.SemanticComparison.dll -------------------------------------------------------------------------------- /packages/AutoFixture.AutoMoq.2.1/AutoFixture.AutoMoq.2.1.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/AutoFixture.AutoMoq.2.1/AutoFixture.AutoMoq.2.1.nupkg -------------------------------------------------------------------------------- /packages/AutoFixture.AutoMoq.2.1/lib/Ploeh.AutoFixture.AutoMoq.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/AutoFixture.AutoMoq.2.1/lib/Ploeh.AutoFixture.AutoMoq.dll -------------------------------------------------------------------------------- /packages/AutoFixture.Xunit.2.1/AutoFixture.Xunit.2.1.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/AutoFixture.Xunit.2.1/AutoFixture.Xunit.2.1.nupkg -------------------------------------------------------------------------------- /packages/AutoFixture.Xunit.2.1/lib/Ploeh.AutoFixture.Xunit.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/AutoFixture.Xunit.2.1/lib/Ploeh.AutoFixture.Xunit.dll -------------------------------------------------------------------------------- /packages/Castle.Core.2.5.2/Castle.Core.2.5.2.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Core.2.5.2/Castle.Core.2.5.2.nupkg -------------------------------------------------------------------------------- /packages/Castle.Core.2.5.2/lib/BreakingChanges.txt: -------------------------------------------------------------------------------- 1 | ================================================================================================ 2 | change - Removed WebLogger and WebLoggerFactory 3 | 4 | impact - low 5 | fixability - medium 6 | revision - 7 | 8 | description - To minimize management overhead the classes were removed so that only single 9 | Client Profile version of Castle.Core can be distributed. 10 | 11 | fix - You can use NLog or Log4Net web logger integration, or reuse implementation of existing 12 | web logger and use it as a custom logger. 13 | 14 | ================================================================================================ 15 | change - Removed obsolete overload of ProxyGenerator.CreateClassProxy 16 | 17 | impact - low 18 | fixability - trivial 19 | revision - 20 | 21 | description - Deprecated overload of ProxyGenerator.CreateClassProxy was removed to keep the 22 | method consistent with other methods and to remove confusion 23 | 24 | fix - whenever removed overload was used, use one of the other overloads. 25 | 26 | ================================================================================================ 27 | change - IProxyGenerationHook.NonVirtualMemberNotification method was renamed 28 | 29 | impact - high 30 | fixability - easy 31 | revision - 32 | 33 | description - to accommodate class proxies with target method NonVirtualMemberNotification on 34 | IProxyGenerationHook type was renamed to more accurate NonProxyableMemberNotification 35 | since for class proxies with target not just methods but also fields and other member that 36 | break the abstraction will be passed to this method. 37 | 38 | fix - whenever NonVirtualMemberNotification is used/implemented change the method name to 39 | NonProxyableMemberNotification. Implementors should also accommodate possibility that not 40 | only MethodInfos will be passed as method's second parameter. 41 | 42 | ================================================================================================ 43 | change - DynamicProxy will now allow to intercept members of System.Object 44 | 45 | impact - very low 46 | fixability - easy 47 | revision - 48 | 49 | description - to allow scenarios like mocking of System.Object members, DynamicProxy will not 50 | disallow proxying of these methods anymore. AllMethodsHook (default IProxyGenerationHook) 51 | will still filter them out though. 52 | 53 | fix - whenever custom IProxyGenerationHook is used, user should account for System.Object's 54 | members being now passed to ShouldInterceptMethod and NonVirtualMemberNotification methods 55 | and if neccessary update the code to handle them appropriately. -------------------------------------------------------------------------------- /packages/Castle.Core.2.5.2/lib/Changes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Core.2.5.2/lib/Changes.txt -------------------------------------------------------------------------------- /packages/Castle.Core.2.5.2/lib/Committers.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Core.2.5.2/lib/Committers.txt -------------------------------------------------------------------------------- /packages/Castle.Core.2.5.2/lib/NET35/Castle.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Core.2.5.2/lib/NET35/Castle.Core.dll -------------------------------------------------------------------------------- /packages/Castle.Core.2.5.2/lib/NET40ClientProfile/Castle.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Core.2.5.2/lib/NET40ClientProfile/Castle.Core.dll -------------------------------------------------------------------------------- /packages/Castle.Core.2.5.2/lib/SL3/Castle.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Core.2.5.2/lib/SL3/Castle.Core.dll -------------------------------------------------------------------------------- /packages/Castle.Core.2.5.2/lib/SL4/Castle.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Core.2.5.2/lib/SL4/Castle.Core.dll -------------------------------------------------------------------------------- /packages/Castle.Core.2.5.2/lib/releaseNotes.txt: -------------------------------------------------------------------------------- 1 | You can find full list of changes in changes.txt, list of breaking changes in breakingchanges.txt (there are no known breaking changes between 2.5.1 and 2.5.2 release). 2 | 3 | Issue tracker: - http://issues.castleproject.org/dashboard 4 | Documentation (work in progress): 5 | Dictionary Adapter - http://stw.castleproject.org/Tools.Castle-DictionaryAdapter.ashx 6 | DynamicProxy - http://stw.castleproject.org/Tools.DynamicProxy.ashx 7 | Discusssion group: - http://groups.google.com/group/castle-project-users 8 | StackOverflow tags: - castle-dynamicproxy, castle-dictionaryadapter, castle -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/Castle.Windsor.2.5.3.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/Castle.Windsor.2.5.3.nupkg -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/Changes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/lib/Changes.txt -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/Committers.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/lib/Committers.txt -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/NET35/Castle.Windsor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/lib/NET35/Castle.Windsor.dll -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/NET35/Castle.Windsor.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/lib/NET35/Castle.Windsor.pdb -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/NET40-Client/Castle.Windsor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/lib/NET40-Client/Castle.Windsor.dll -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/NET40-Client/Castle.Windsor.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/lib/NET40-Client/Castle.Windsor.pdb -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/NET40/Castle.Windsor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/lib/NET40/Castle.Windsor.dll -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/NET40/Castle.Windsor.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/lib/NET40/Castle.Windsor.pdb -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/SL3/Castle.Windsor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/lib/SL3/Castle.Windsor.dll -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/SL3/Castle.Windsor.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/lib/SL3/Castle.Windsor.pdb -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/SL4/Castle.Windsor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/lib/SL4/Castle.Windsor.dll -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/SL4/Castle.Windsor.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Castle.Windsor.2.5.3/lib/SL4/Castle.Windsor.pdb -------------------------------------------------------------------------------- /packages/Castle.Windsor.2.5.3/lib/releaseNotes.txt: -------------------------------------------------------------------------------- 1 | You can find full list of changes in changes.txt, list of breaking changes in breakingchanges.txt. 2 | 3 | Online release notes: http://stw.castleproject.org/Windsor.Windsor_25_release_notes.ashx 4 | Documentation: http://stw.castleproject.org/Windsor.MainPage.ashx 5 | Samples: http://stw.castleproject.org/Windsor.MainPage.ashx?#Samples_8 6 | Issue tracker: http://issues.castleproject.org/dashboard 7 | Discusssion group: http://groups.google.com/group/castle-project-users 8 | StackOverflow tags: castle-windsor, castle -------------------------------------------------------------------------------- /packages/Moq.4.0.10827/License.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007. Clarius Consulting, Manas Technology Solutions, InSTEDD 2 | http://code.google.com/p/moq/ 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, 6 | with or without modification, are permitted provided 7 | that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the 10 | above copyright notice, this list of conditions and 11 | the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce 14 | the above copyright notice, this list of conditions 15 | and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | * Neither the name of Clarius Consulting, Manas Technology Solutions or InSTEDD nor the 19 | names of its contributors may be used to endorse 20 | or promote products derived from this software 21 | without specific prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 24 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 25 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 28 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 33 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 34 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 | SUCH DAMAGE. 37 | 38 | [This is the BSD license, see 39 | http://www.opensource.org/licenses/bsd-license.php] -------------------------------------------------------------------------------- /packages/Moq.4.0.10827/Moq.4.0.10827.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Moq.4.0.10827/Moq.4.0.10827.nupkg -------------------------------------------------------------------------------- /packages/Moq.4.0.10827/Moq.chm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Moq.4.0.10827/Moq.chm -------------------------------------------------------------------------------- /packages/Moq.4.0.10827/lib/NET35/Moq.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Moq.4.0.10827/lib/NET35/Moq.dll -------------------------------------------------------------------------------- /packages/Moq.4.0.10827/lib/NET35/Moq.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Moq.4.0.10827/lib/NET35/Moq.pdb -------------------------------------------------------------------------------- /packages/Moq.4.0.10827/lib/NET40/Moq.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Moq.4.0.10827/lib/NET40/Moq.dll -------------------------------------------------------------------------------- /packages/Moq.4.0.10827/lib/NET40/Moq.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Moq.4.0.10827/lib/NET40/Moq.pdb -------------------------------------------------------------------------------- /packages/Moq.4.0.10827/lib/Silverlight4/Castle.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Moq.4.0.10827/lib/Silverlight4/Castle.Core.dll -------------------------------------------------------------------------------- /packages/Moq.4.0.10827/lib/Silverlight4/Moq.Silverlight.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Moq.4.0.10827/lib/Silverlight4/Moq.Silverlight.dll -------------------------------------------------------------------------------- /packages/Moq.4.0.10827/lib/Silverlight4/Moq.Silverlight.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/Moq.4.0.10827/lib/Silverlight4/Moq.Silverlight.pdb -------------------------------------------------------------------------------- /packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packages/xunit.1.8.0.1545/lib/xunit.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/xunit.1.8.0.1545/lib/xunit.dll -------------------------------------------------------------------------------- /packages/xunit.1.8.0.1545/xunit.1.8.0.1545.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/xunit.1.8.0.1545/xunit.1.8.0.1545.nupkg -------------------------------------------------------------------------------- /packages/xunit.extensions.1.8.0.1545/lib/xunit.extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/xunit.extensions.1.8.0.1545/lib/xunit.extensions.dll -------------------------------------------------------------------------------- /packages/xunit.extensions.1.8.0.1545/xunit.extensions.1.8.0.1545.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ploeh/CQRSonAzureDemo/f1364ea4518e1885b7a8da14f1fb0d6e7566cfd8/packages/xunit.extensions.1.8.0.1545/xunit.extensions.1.8.0.1545.nupkg --------------------------------------------------------------------------------