├── .gitignore ├── AbstractFactory ├── AbstractFactory.csproj ├── App.config ├── IProduct.cs ├── MyAbstractFactory.cs ├── ProductOne.cs ├── ProductThree.cs ├── ProductTwo.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── README.md ├── C.Sharp.Patterns.sln ├── CompositionOverInheritance ├── App.config ├── Composed │ ├── EnhancedOrder1Composed.cs │ └── EnhancedOrder1ComposedAlternate.cs ├── CompositionOverInheritance.csproj ├── IAddress.cs ├── IOrder.cs ├── IOrderEnhanced.cs ├── Inherited │ ├── EnhancedOrder1Inherited.cs │ └── Order1.cs ├── OrderProcessor.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── README.md ├── DbVersionMigration ├── App.config ├── DbVersionMigration.csproj ├── IMigration.cs ├── IMigrationManager.cs ├── MigrationManager.cs ├── Migrations │ ├── MigrationAttribute.cs │ ├── Version0001.cs │ ├── Version0002.cs │ └── Version0003.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── README.md ├── Decorator ├── App.config ├── CachedFoo.cs ├── Decorator.csproj ├── Foo.cs ├── IDoSomething.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── README.md ├── Delegate ├── App.config ├── ClassThatTakesDelegateOne.cs ├── ClassThatTakesDelegateThree.cs ├── ClassThatTakesDelegateTwo.cs ├── Delegate.csproj ├── Model │ └── Foo.cs ├── NoDelegate.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── README.md ├── DistributedLocking ├── DistributedLocking.csproj ├── Locking │ ├── DatabaseDistributedLocked.cs │ ├── IDistributedLock.cs │ └── InMemoryDistributedLock.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── README.md ├── DynamicDispatch ├── App.config ├── DynamicDispatch.csproj ├── Messaging │ ├── Dispatching │ │ ├── IDispatchMessages.cs │ │ └── MessageDispatcher.cs │ ├── Handlers │ │ ├── BarHandler.cs │ │ ├── FooHandler.cs │ │ └── IHandleMessages.cs │ ├── Helpers │ │ └── MessageHelper.cs │ └── Messages │ │ ├── BarMessage.cs │ │ ├── FooMessage.cs │ │ └── IMessage.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── README.md ├── Observer ├── App.config ├── BubbleObserver.cs ├── ICanBeObserved.cs ├── Observer.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── README.md ├── SomethingAmazingEventArgs.cs ├── UnicornDancer.cs └── UnicornObserver.cs ├── Pipeline ├── App.config ├── IPipeline.cs ├── ImagePipeline.cs ├── PipeModel │ ├── IModel.cs │ └── ImageModel.cs ├── Pipeline.csproj ├── Pipes │ ├── CropAction.cs │ ├── DesaturateAction.cs │ ├── IPipe.cs │ └── RotateAction.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── README.md ├── PoorMansDependencyInjection ├── App.config ├── Bar.cs ├── Bar2.cs ├── BestFoo.cs ├── BetterFoo.cs ├── BetterFoo2.cs ├── Foo.cs ├── IBar.cs ├── IFoo.cs ├── PoorMansDependencyInjection.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── README.md ├── README.md ├── SimpleFactory ├── App.config ├── Bar.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── README.md └── SimpleFactory.csproj ├── Singleton ├── App.config ├── NonThreadSafeSingleton.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── README.md ├── Singleton.csproj └── ThreadSafeSingleton.cs ├── Solid ├── App.config ├── EmailDependency.cs ├── IEmailDependency.cs ├── ISolidEmailer.cs ├── ISolidUserRecordUpdater.cs ├── IUpdateUserRecords.cs ├── NotSolid.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── README.md ├── Solid.csproj ├── SolidEmailer.cs ├── SolidUserRecordUpdater.cs └── UpdateUserRecords.cs ├── Strategy ├── App.config ├── BadGuy.cs ├── BadGuyFactory.cs ├── Henchman.cs ├── IBadGuy.cs ├── MovementStrategies │ ├── IMovementStrategy.cs │ ├── SideToSideStrategy.cs │ └── UpAndDownStrategy.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── README.md └── Strategy.csproj ├── Tasks ├── App.config ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Tasks.csproj └── Work │ └── DoSomething.cs ├── Threading ├── App.config ├── IManageThreads.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── README.md ├── ThreadManager.cs ├── Threading.csproj └── Work │ ├── DoSomething.cs │ └── IDoSomething.cs ├── UnitOfWork ├── App.config ├── DAL │ ├── IUnitOfWork.cs │ ├── MockUnitOfWork.cs │ ├── PetaPoco.cs │ ├── PetaPocoUnitOfWork.cs │ └── UnitOfWorkFactory.cs ├── Models │ ├── MyDbEntity.cs │ └── MyOtherDbEntity.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── README.md ├── Repositories │ ├── BarRepository.cs │ └── FooRepository.cs ├── UnitOfWork.csproj └── packages.config └── Visitor ├── AnotherBar.cs ├── App.config ├── Bar.cs ├── Component1.cs ├── Component2.cs ├── Component3.cs ├── Foo.cs ├── IFoo.cs ├── ISaySomething.cs ├── IVisitFoo.cs ├── IVisitable.cs ├── Program.cs ├── Properties └── AssemblyInfo.cs ├── README.md └── Visitor.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .AppleDouble 3 | .LSOverride 4 | Icon 5 | ._* 6 | .Spotlight-V100 7 | .Trashes 8 | Thumbs.db 9 | ehthumbs.db 10 | Desktop.ini 11 | $RECYCLE.BIN/ 12 | 13 | app/less/*.css 14 | dist/ 15 | node_modules/ 16 | tmp/ 17 | pkg/*.nupkg 18 | pkg/*.zip 19 | 20 | *_versioned* 21 | 22 | 23 | [Dd]ebug/ 24 | [Bb]uild/ 25 | [Rr]elease/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Pp]ackages/ 29 | VersionInfo.cs 30 | 31 | *.aps 32 | *.bak 33 | *.backup 34 | *.build.csdef 35 | *.cache 36 | *.ilk 37 | *.lib 38 | *.log 39 | *.meta 40 | *.ncb 41 | *.obj 42 | *.orig 43 | *.pch 44 | *.pdb 45 | *.pgc 46 | *.pgd 47 | *.rsp 48 | *.sbr 49 | *.scc 50 | *.sdf 51 | *.sln.docstates 52 | *.suo 53 | *.tlb 54 | *.tlh 55 | *.tli 56 | *.tmp 57 | *.user 58 | *.vspscc 59 | *.[Pp]ublish.xml 60 | *_i.c 61 | *_p.c 62 | .builds 63 | .svn 64 | .hg 65 | .hgignore -------------------------------------------------------------------------------- /AbstractFactory/AbstractFactory.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {0D318994-0B51-4DF6-99AB-2C94FAB46A3B} 8 | Exe 9 | Properties 10 | AbstractFactory 11 | AbstractFactory 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 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 | 64 | -------------------------------------------------------------------------------- /AbstractFactory/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AbstractFactory/IProduct.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | //an interface (or abstract class) is used to allow us to not commit to a particular concrete class until we decided in our factory 4 | public interface IProduct 5 | { 6 | void MyMethod(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /AbstractFactory/MyAbstractFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Configuration; 2 | 3 | namespace AbstractFactory 4 | { 5 | public class MyAbstractFactory 6 | { 7 | //an abstract factory only needs one method that returns an abstraction 8 | public static IProduct Create() 9 | { 10 | //as long as it returns an 'IProduct', the client application doesn't care which concrete class is returned. 11 | return new ProductTwo(); 12 | } 13 | 14 | //however to make it more useful, a creation method could react to a condition 15 | public static IProduct CreateBasedOnConfig() 16 | { 17 | var setting = ConfigurationManager.AppSettings["ProductType"]; 18 | 19 | switch(setting) 20 | { 21 | case "1": 22 | return new ProductOne(); 23 | 24 | case "2": 25 | return new ProductTwo(); 26 | 27 | case "3": 28 | return new ProductThree(); 29 | 30 | default: 31 | return new ProductTwo(); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /AbstractFactory/ProductOne.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AbstractFactory 4 | { 5 | //a generic model that implements IProduct 6 | public class ProductOne : IProduct 7 | { 8 | public void MyMethod() 9 | { 10 | Console.WriteLine("Invoking method from product 1!"); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /AbstractFactory/ProductThree.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AbstractFactory 4 | { 5 | //a generic model that implements IProduct 6 | public class ProductThree : IProduct 7 | { 8 | public void MyMethod() 9 | { 10 | Console.WriteLine("Invoking method from product 3!"); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /AbstractFactory/ProductTwo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AbstractFactory 4 | { 5 | //a generic model that implements IProduct 6 | public class ProductTwo : IProduct 7 | { 8 | public void MyMethod() 9 | { 10 | Console.WriteLine("Invoking method from product 2!"); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /AbstractFactory/Program.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | class Program 4 | { 5 | static void Main(string[] args) 6 | { 7 | //if a model is public, we could just 'new' a product 8 | ProductOne myNewProduct = new ProductOne(); 9 | myNewProduct.MyMethod(); 10 | 11 | //but if we need to use a new product model (ProductTwo), we'll have to change all instances where 'ProductOne' is used. 12 | //This may not be a problem if you control all of the code, but what if your code is used by a third-party? 13 | //If that happens, you'd have to ask them to replace all references to 'ProductOne' and substitute 'ProductTwo' in their application and then recompile. 14 | //This is not ideal, we'd really like our client to not have to do anything with their code. 15 | 16 | //If you use an interface and a factory, your client wouldn't have to change any of their code because the client delegates the creation of 17 | //the product to the factory. Therefore as long as the factory returns an 'IProduct', the fact that it's returning 'ProductTwo' or 'ProductThree' matters not to 18 | //the client application. 19 | 20 | //this returns ProductTwo but could just as well return ProductThree and no changes would be needed to the code below. 21 | IProduct myFactoryProduct = MyAbstractFactory.Create(); 22 | myFactoryProduct.MyMethod(); 23 | 24 | //we can make this even sexier if we want some sort of condition to dictate what the factory should return 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /AbstractFactory/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("ConsoleApplication1")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("University of Notre Dame")] 12 | [assembly: AssemblyProduct("ConsoleApplication1")] 13 | [assembly: AssemblyCopyright("Copyright © University of Notre Dame 2016")] 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("2d4f04cd-22ae-458e-bf0d-5d95427eabb9")] 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 | -------------------------------------------------------------------------------- /AbstractFactory/README.md: -------------------------------------------------------------------------------- 1 | # Abstract Factory 2 | 3 | ## Problem it solves 4 | Super similar to the [Simple Factory](../SimpleFactory/README.md) except the `.Create()` method returns an abstraction. 5 | 6 | ## Common Uses 7 | The advantage over the Simple Factory is that this only depends on an abstraction whereas the Simple Factory depends on a concrete class. 8 | 9 | Check out a video I made on factories: https://www.youtube.com/watch?v=qzxp9p7UP_Y. -------------------------------------------------------------------------------- /CompositionOverInheritance/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CompositionOverInheritance/Composed/EnhancedOrder1Composed.cs: -------------------------------------------------------------------------------- 1 | namespace CompositionOverInheritance.Composed 2 | { 3 | public class EnhancedOrder1Composed : IOrder, IOrderEnhanced 4 | { 5 | //this class uses composotion and essentially does repeat what Order1 is doing but is now free to evolve independently 6 | //this could be good or bad but this is the design decision you make by doing compostion vs inheritance 7 | public int OrderNumber { get; } 8 | public IAddress ShippingAddress { get; } 9 | public IAddress BillingAddress { get; } 10 | public string EnhancedField { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CompositionOverInheritance/Composed/EnhancedOrder1ComposedAlternate.cs: -------------------------------------------------------------------------------- 1 | namespace CompositionOverInheritance.Composed 2 | { 3 | public class EnhancedOrder1ComposedAlternate : IOrderEnhanced 4 | { 5 | public IOrder Order; 6 | public string EnhancedField { get; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /CompositionOverInheritance/CompositionOverInheritance.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {9381B46C-2EF6-434C-B1E9-A3853DA3190D} 8 | Exe 9 | Properties 10 | CompositionOverInheritance 11 | CompositionOverInheritance 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 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 | 69 | -------------------------------------------------------------------------------- /CompositionOverInheritance/IAddress.cs: -------------------------------------------------------------------------------- 1 | namespace CompositionOverInheritance 2 | { 3 | public interface IAddress 4 | { 5 | string Address1 { get; } 6 | string Address2 { get; } 7 | string City { get; } 8 | string State { get; } 9 | string Zip { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CompositionOverInheritance/IOrder.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace CompositionOverInheritance 4 | { 5 | public interface IOrder 6 | { 7 | int OrderNumber { get; } 8 | IAddress ShippingAddress { get; } 9 | IAddress BillingAddress { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CompositionOverInheritance/IOrderEnhanced.cs: -------------------------------------------------------------------------------- 1 | namespace CompositionOverInheritance 2 | { 3 | public interface IOrderEnhanced 4 | { 5 | string EnhancedField { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /CompositionOverInheritance/Inherited/EnhancedOrder1Inherited.cs: -------------------------------------------------------------------------------- 1 | namespace CompositionOverInheritance.Inherited 2 | { 3 | public class EnhancedOrder1Inherited : Order1, IOrderEnhanced 4 | { 5 | //this class will be tied to any changes that happens in Order1 which could be good or bad 6 | //but this is the design\decision pattern you make when you use inheritance 7 | public string EnhancedField { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /CompositionOverInheritance/Inherited/Order1.cs: -------------------------------------------------------------------------------- 1 | namespace CompositionOverInheritance.Inherited 2 | { 3 | public class Order1 : IOrder 4 | { 5 | public int OrderNumber { get; } 6 | public IAddress ShippingAddress { get; } 7 | public IAddress BillingAddress { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /CompositionOverInheritance/OrderProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CompositionOverInheritance 4 | { 5 | public class OrderProcessor 6 | { 7 | public void Process(IOrder order) 8 | { 9 | Console.WriteLine("Processing an IOrder..."); 10 | } 11 | 12 | public void Process(IOrderEnhanced order) 13 | { 14 | Console.WriteLine("Processing an IEnhancedOrder..."); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CompositionOverInheritance/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CompositionOverInheritance.Composed; 3 | using CompositionOverInheritance.Inherited; 4 | 5 | namespace CompositionOverInheritance 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | /* 12 | * You've heard it before 'favor composition over inheritance. 13 | * I take that to mean tha when you inherit, you are making a lot of assumptions about the future. When the 'base' class evolves, so will 14 | * everything 'downstream' on the inheritance chain. This could be desirable or it could create a bunch of un-used properties when dealing 15 | * with edge cases. 16 | * 17 | * I tend to prefer composition. Meaning that rather than inherit, I just implement an inteface multiple times to better tailor the modeling. 18 | * In this way you can evolve each piece separately and not have to deal with the baggage of inheritance. 19 | */ 20 | 21 | //the base class 22 | var order1 = new Order1(); 23 | 24 | //inheritance 25 | var order1EnhancedInherited = new EnhancedOrder1Inherited(); 26 | 27 | //composition 28 | var order1EnhancedComposed = new EnhancedOrder1Composed(); 29 | 30 | //another way to compose is to wrap the original that I tend to prefer 31 | var order1EnhancedComposedAlternate = new EnhancedOrder1ComposedAlternate 32 | { 33 | Order = new Order1() 34 | }; 35 | 36 | 37 | //only to show each one being used 38 | var processor = new OrderProcessor(); 39 | 40 | //base 41 | processor.Process(order1); 42 | 43 | //inherited 44 | processor.Process((IOrder)order1EnhancedInherited); 45 | processor.Process((IOrderEnhanced)order1EnhancedInherited); 46 | 47 | //composed 48 | processor.Process((IOrder)order1EnhancedComposed); 49 | processor.Process((IOrderEnhanced)order1EnhancedComposed); 50 | 51 | //composed alternate 52 | processor.Process(order1EnhancedComposedAlternate.Order); 53 | processor.Process(order1EnhancedComposedAlternate); 54 | 55 | Console.ReadKey(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /CompositionOverInheritance/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("CompositionOverInheritance")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("CompositionOverInheritance")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("9381b46c-2ef6-434c-b1e9-a3853da3190d")] 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 | -------------------------------------------------------------------------------- /CompositionOverInheritance/README.md: -------------------------------------------------------------------------------- 1 | # Composition over Inheritance 2 | 3 | ## Problem it solves 4 | Inheritance isn't evil, but it does lock a class to another classes' destiny. Over-use of inheritance can lead to classes with default values\null as the 'norm' rather than having a class that models exactly what is needed. 5 | 6 | Further when such classes become bloated, serialization and persistence do more work than they need to. 7 | 8 | I prefer to compose a class by assembling it's parts and implement a common interface. While it may be more work upfront to model, it'll keep things nice and tidy. 9 | 10 | ## Common Uses 11 | Often time as a business evolves, there is a natural tendency to simply 'just add a new field' to a class to cover the new requirement or create a derived class and add properties there. 12 | 13 | If an ordering scheme requires new functionality, you should consider composing a new object rather than inherit and add properties. Inheritance should be used in moderation. 14 | -------------------------------------------------------------------------------- /DbVersionMigration/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DbVersionMigration/DbVersionMigration.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {52D003BC-DA68-4BF6-A46F-07923FF2885E} 8 | Exe 9 | Properties 10 | DbVersionMigration 11 | DbVersionMigration 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 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 | 67 | -------------------------------------------------------------------------------- /DbVersionMigration/IMigration.cs: -------------------------------------------------------------------------------- 1 | namespace DbVersionMigration 2 | { 3 | //define what each migration should do 4 | //you may want to do upgrades only 5 | public interface IMigration 6 | { 7 | void Up(); 8 | void Down(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /DbVersionMigration/IMigrationManager.cs: -------------------------------------------------------------------------------- 1 | namespace DbVersionMigration 2 | { 3 | public interface IMigrationManager 4 | { 5 | //just a single method 6 | void HandleMigrations(int currentDbVersion, int targetVersion); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /DbVersionMigration/MigrationManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using DbVersionMigration.Migrations; 5 | 6 | namespace DbVersionMigration 7 | { 8 | public class MigrationManager : IMigrationManager 9 | { 10 | private readonly Dictionary _migrationLookup = new Dictionary(); 11 | 12 | public void HandleMigrations(int currentDbVersion, int targetVersion) 13 | { 14 | //if we don't need to upgrade, let's get out of here 15 | if (currentDbVersion == targetVersion) 16 | { 17 | return; 18 | } 19 | 20 | //grab all of the migrations via reflection 21 | var migrationType = typeof(IMigration); 22 | var migrations = AppDomain.CurrentDomain.GetAssemblies() 23 | .SelectMany(s => s.GetTypes()) 24 | .Where(p => migrationType.IsAssignableFrom(p) && !p.IsInterface) 25 | .ToList(); 26 | //.Select(t => (IMigration)Activator.CreateInstance(t)); 27 | 28 | _migrationLookup.Clear(); 29 | _initLookup(migrations); 30 | 31 | try 32 | { 33 | //decide if we're upgrading or downgrading 34 | if (currentDbVersion > targetVersion) 35 | { 36 | _downgrade(migrations, currentDbVersion, targetVersion); 37 | } 38 | else 39 | { 40 | _upgrade(migrations, currentDbVersion, targetVersion); 41 | } 42 | } 43 | catch (Exception ex) 44 | { 45 | //log it 46 | } 47 | } 48 | 49 | private void _upgrade(IEnumerable migrations, int currentDbVersion, int targetVersion) 50 | { 51 | //only run the migrations from a version forward 52 | var migrationsToExecute = migrations 53 | .Where(x => _migrationLookup[x.Name] > currentDbVersion && _migrationLookup[x.Name] <= targetVersion) 54 | .OrderBy(x => _migrationLookup[x.Name]); 55 | 56 | foreach (var migration in migrationsToExecute) 57 | { 58 | var migrationInstance = (IMigration)Activator.CreateInstance(migration); 59 | 60 | Console.WriteLine($"Upgrading to v{_migrationLookup[migration.Name]}..."); 61 | 62 | migrationInstance.Up(); 63 | } 64 | } 65 | 66 | private void _downgrade(IEnumerable migrations, int currentDbVersion, int targetVersion) 67 | { 68 | //only run the migrations for a version backwards 69 | var migrationsToExecute = migrations 70 | .Where(x => _migrationLookup[x.Name] < currentDbVersion && _migrationLookup[x.Name] >= targetVersion) 71 | .OrderByDescending(x => _migrationLookup[x.Name]); 72 | 73 | foreach (var migration in migrationsToExecute) 74 | { 75 | var migrationInstance = (IMigration)Activator.CreateInstance(migration); 76 | 77 | Console.WriteLine($"Downgrading to v{_migrationLookup[migration.Name]}..."); 78 | 79 | migrationInstance.Down(); 80 | } 81 | } 82 | 83 | private void _initLookup(IEnumerable types) 84 | { 85 | foreach (var type in types) 86 | { 87 | if (_migrationLookup.ContainsKey(type.Name)) 88 | { 89 | throw new Exception($"Looks like you have multiple types with the same name {type.Name}!"); 90 | } 91 | 92 | var version = _getMigrationVersion(type); 93 | 94 | if (version == null) 95 | { 96 | throw new Exception($"Looks like you have multiple types with the same name {type.FullName}!"); 97 | } 98 | 99 | if (_migrationLookup.ContainsValue(version.Value)) 100 | { 101 | throw new Exception($"Looks like you have multiple types with the same version: {version}!"); 102 | } 103 | 104 | _migrationLookup.Add(type.Name, version.Value); 105 | } 106 | } 107 | 108 | private int? _getMigrationVersion(Type type) 109 | { 110 | int? version = null; 111 | 112 | var attributes = type.GetCustomAttributes(false); 113 | 114 | foreach (var attribute in attributes) 115 | { 116 | var migrationAttribute = attribute as MigrationAttribute; 117 | 118 | if (migrationAttribute != null) 119 | { 120 | version = migrationAttribute.Version; 121 | } 122 | } 123 | 124 | return version; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /DbVersionMigration/Migrations/MigrationAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DbVersionMigration.Migrations 4 | { 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public class MigrationAttribute : Attribute 7 | { 8 | public int Version { get; set; } 9 | 10 | public MigrationAttribute(int version) 11 | { 12 | Version = version; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /DbVersionMigration/Migrations/Version0001.cs: -------------------------------------------------------------------------------- 1 | namespace DbVersionMigration.Migrations 2 | { 3 | //a simple migration 4 | [Migration(1)] 5 | public class Version0001 : IMigration 6 | { 7 | //run you custom sql here in a transaction 8 | public void Up() 9 | { 10 | //put your sql stuff here in a transaction 11 | } 12 | 13 | public void Down() 14 | { 15 | //put your sql stuff here in a transaction 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /DbVersionMigration/Migrations/Version0002.cs: -------------------------------------------------------------------------------- 1 | namespace DbVersionMigration.Migrations 2 | { 3 | //another simple migration 4 | [Migration(2)] 5 | public class Version0002 : IMigration 6 | { 7 | public void Up() 8 | { 9 | //put your sql stuff here in a transaction 10 | } 11 | 12 | public void Down() 13 | { 14 | //put your sql stuff here in a transaction 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /DbVersionMigration/Migrations/Version0003.cs: -------------------------------------------------------------------------------- 1 | namespace DbVersionMigration.Migrations 2 | { 3 | //another simple migration 4 | [Migration(3)] 5 | public class Version0003 : IMigration 6 | { 7 | public void Up() 8 | { 9 | //put your sql stuff here in a transaction 10 | } 11 | 12 | public void Down() 13 | { 14 | //put your sql stuff here in a transaction 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /DbVersionMigration/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DbVersionMigration 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | /* 10 | * The idea here is to only modify the DB via a migration. 11 | * 12 | * In this way you can easily keep track of what state a DB schema should be in. 13 | * 14 | * You can also run this against the dev, prod, staging DB's to ensure they are all a particular version. 15 | */ 16 | 17 | //you'll want to store the 'current DB version' somewhere 18 | //this could be the web config, or a DB table 19 | var currentDbVersion = 0; 20 | 21 | //we'll use our manager to do the dirty work 22 | //you can run this on startup or you can run this explicitly 23 | var migrationManager = new MigrationManager(); 24 | 25 | //simply use integers for versioning 26 | //here we're going from v0 to v1 27 | migrationManager.HandleMigrations(currentDbVersion, 1); 28 | currentDbVersion = 1; 29 | 30 | //here we go from v1 to v2 31 | migrationManager.HandleMigrations(currentDbVersion, 2); 32 | currentDbVersion = 2; 33 | 34 | //let's go back to v1 35 | //be careful if you choose to allow for downgrades as you could drop a table 36 | //you might just want to 'go forward' with upgrades only 37 | //maybe leave table drops as a manual process 38 | migrationManager.HandleMigrations(currentDbVersion, 1); 39 | currentDbVersion = 1; 40 | 41 | //let's go all the way up 42 | migrationManager.HandleMigrations(currentDbVersion, 3); 43 | currentDbVersion = 3; 44 | 45 | //all the way down 46 | migrationManager.HandleMigrations(currentDbVersion, 0); 47 | currentDbVersion = 0; 48 | 49 | 50 | Console.ReadKey(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /DbVersionMigration/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("DbVersionMigration")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("DbVersionMigration")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("52d003bc-da68-4bf6-a46f-07923ff2885e")] 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 | -------------------------------------------------------------------------------- /DbVersionMigration/README.md: -------------------------------------------------------------------------------- 1 | # DB Version Migration 2 | 3 | ## Problem it solves 4 | When sync'ing DB schemas against code or across environments, it's good to have a mechanism to version and upgrade (or even downgrade) a schema via a script. 5 | 6 | ## Common Uses 7 | If you alter the Dev DB but now need to deploy your schema, this is very useful if using a micro-ORM and now need to deploy the schema changes to one or more DB servers without manually doing so. 8 | -------------------------------------------------------------------------------- /Decorator/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Decorator/CachedFoo.cs: -------------------------------------------------------------------------------- 1 | namespace Decorator 2 | { 3 | class CachedFoo : IDoSomething 4 | { 5 | private readonly IDoSomething _nonCachedFoo; 6 | private string _whatIdid; 7 | 8 | public CachedFoo(IDoSomething nonCachedFoo) 9 | { 10 | _nonCachedFoo = nonCachedFoo; 11 | } 12 | 13 | public string DoSomething() 14 | { 15 | //if i don't have the stored item, go get it from the class I don't have the source 16 | if (string.IsNullOrEmpty(_whatIdid)) 17 | { 18 | _whatIdid = _nonCachedFoo.DoSomething(); 19 | } 20 | 21 | return _whatIdid; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Decorator/Decorator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {15C1C244-A7BE-4AD1-B9AC-C814ED39E025} 8 | Exe 9 | Properties 10 | Decorator 11 | Decorator 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 63 | -------------------------------------------------------------------------------- /Decorator/Foo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Decorator 4 | { 5 | //imagine this class contains highly secretive source code and is closed from prying eyes! 6 | class Foo : IDoSomething 7 | { 8 | public string DoSomething() 9 | { 10 | return "I just did something!"; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Decorator/IDoSomething.cs: -------------------------------------------------------------------------------- 1 | namespace Decorator 2 | { 3 | interface IDoSomething 4 | { 5 | string DoSomething(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Decorator/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Decorator 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | //the decorator pattern is used to augment an existing implementation without creating overriding behavior in the traditional sense 10 | 11 | //if we have class foo we might use it simply like so: 12 | var foo = new Foo(); 13 | 14 | foo.DoSomething(); 15 | 16 | //however eventually we may want to add caching to this class method 17 | //we could update the class to support it directly, but what happens if we DON'T have the code access? 18 | //we could use the decorator pattern to simply augment it 19 | //for this part, assume that class Foo is in another assembly and locked away as closed source 20 | 21 | //this is most elegant when the class you want to augment implements an interface. In our case Foo impl IDoSomething. 22 | //using this pattern with an abstraction allows your new class to work like the old class with augmentation 23 | 24 | var cachedFoo = new CachedFoo(foo); 25 | Console.WriteLine(cachedFoo.DoSomething()); //will invoke the inner IDoSomething (Foo class) 26 | Console.WriteLine(cachedFoo.DoSomething()); //this will output the cached value the second time 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Decorator/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("Decorator")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Decorator")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("15c1c244-a7be-4ad1-b9ac-c814ed39e025")] 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 | -------------------------------------------------------------------------------- /Decorator/README.md: -------------------------------------------------------------------------------- 1 | # Decorator 2 | 3 | ## Problem it solves 4 | If you have a class that you cannot (no access to source) or do not want to alter (pick a reason), you can still augment a method even if it is not marked `virtual` provided the class to be augmented uses an abstraction. 5 | 6 | ## Common Uses 7 | Let's say you have a third party class that is closed source. You want one of the methods to be able to return cached responses. The method in question is not marked virtual but the class itself uses a public interface. The example contained here shows how to do such a task. 8 | 9 | This pattern is also known as the wrapper pattern. 10 | 11 | Here's a video I've put together for you to help out: https://www.youtube.com/watch?v=brsmxMkTM8I 12 | -------------------------------------------------------------------------------- /Delegate/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Delegate/ClassThatTakesDelegateOne.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Delegate 4 | { 5 | //this class doesn't know anything about the Foo class 6 | //it won't care if Foo goes away, never existed at all or what planet it was born on 7 | class ClassThatTakesDelegateOne 8 | { 9 | //we can pass in a function to be run, this uses 'Action' which has no return type and no args 10 | public void DoMagicalThings(Action aFunctionWeCanRun) 11 | { 12 | aFunctionWeCanRun(); 13 | } 14 | 15 | //we can pass in a function to be run, this uses 'Action' which has no return type but takes an arg (bool in this case) 16 | //you can have more than one passed in arg 17 | public void DoMagicalThings(Action aFunctionWeCanRun) 18 | { 19 | aFunctionWeCanRun(true); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Delegate/ClassThatTakesDelegateThree.cs: -------------------------------------------------------------------------------- 1 | namespace Delegate 2 | { 3 | //you can even name a delegate (old school way :) ) 4 | delegate int OldSchoolNamedDelegate(bool someBool); 5 | 6 | class ClassThatTakesDelegateThree 7 | { 8 | //some like it named as such 9 | public void DoMagicalThings(OldSchoolNamedDelegate aFunctionWeCanRun) 10 | { 11 | //based on the signature of the Func, we are passing a bool to the function and expecting an int to be returned 12 | int result = aFunctionWeCanRun(true); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Delegate/ClassThatTakesDelegateTwo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Delegate 4 | { 5 | //this class doesn't know anything about the Foo class 6 | //it won't care if Foo goes away, never existed at all or what planet it was born on 7 | class ClassThatTakesDelegateTwo 8 | { 9 | //we can pass in a function to be run, this uses 'Func' which has both a return type and an arg list 10 | public void DoMagicalThings(Func aFunctionWeCanRun) 11 | { 12 | //based on the signature of the Func, we are passing a bool to the function and expecting an int to be returned 13 | int result = aFunctionWeCanRun(true); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Delegate/Delegate.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {CB4DE76E-CC5B-4911-9848-8D5ED09E9B57} 8 | Exe 9 | Properties 10 | Delegate 11 | Delegate 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 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 | 65 | -------------------------------------------------------------------------------- /Delegate/Model/Foo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Delegate.Model 4 | { 5 | class Foo 6 | { 7 | public void DoSomething() 8 | { 9 | Console.WriteLine("I'm doing something!"); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Delegate/NoDelegate.cs: -------------------------------------------------------------------------------- 1 | using Delegate.Model; 2 | 3 | namespace Delegate 4 | { 5 | //this is a normal class that uses Foo to do something 6 | //there is nothing wrong with this code but you should know that this creates a tightly coupled dependency on Foo 7 | //this class knows about Foo. If Foo goes away, this class needs updated 8 | class NoDelegate 9 | { 10 | public void DoMagicalThings() 11 | { 12 | new Foo().DoSomething(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Delegate/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Delegate.Model; 3 | 4 | namespace Delegate 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | //nothing special here 11 | var noDelegate = new NoDelegate(); 12 | noDelegate.DoMagicalThings(); 13 | 14 | //pass in a function that has one arg and no return type (void) 15 | var delegateOne = new ClassThatTakesDelegateOne(); 16 | delegateOne.DoMagicalThings(aFunctionWeCanRunFromDelegateOne); 17 | 18 | //you can also tighten it up with a lambda to do it inline 19 | delegateOne.DoMagicalThings(someBool => 20 | { 21 | //this is a delegate function inline 22 | if (someBool) 23 | { 24 | //only the Program class has to know about Foo 25 | new Foo().DoSomething(); 26 | } 27 | }); 28 | 29 | //what if you want to pass an arg and get a return type? 30 | var delegateTwo = new ClassThatTakesDelegateTwo(); 31 | delegateTwo.DoMagicalThings(aFunctionWeCanRunFromDelegateTwo); 32 | 33 | //just like Action, we can use a lambda as well 34 | delegateTwo.DoMagicalThings(someBool => 35 | { 36 | //this is a delegate function inline 37 | if (someBool) 38 | { 39 | //only the Program class has to know about Foo 40 | new Foo().DoSomething(); 41 | 42 | return 1; 43 | } 44 | 45 | return 0; 46 | }); 47 | 48 | //finally we have the original delegate from early C# that works just like delegate one or two based on the delegate at the top 49 | //of the ClassThatTakesDelegateThree 50 | var delegateThree = new ClassThatTakesDelegateThree(); 51 | delegateThree.DoMagicalThings(aFunctionWeCanRunFromDelegateTwo); 52 | } 53 | 54 | //this is a delegate function 55 | private static int aFunctionWeCanRunFromDelegateTwo(bool someBool) 56 | { 57 | if (someBool) 58 | { 59 | //only the Program class has to know about Foo 60 | new Foo().DoSomething(); 61 | 62 | return 1; 63 | } 64 | 65 | return 0; 66 | } 67 | 68 | //this is a delegate function 69 | private static void aFunctionWeCanRunFromDelegateOne(bool someBool) 70 | { 71 | if (someBool) 72 | { 73 | //only the Program class has to know about Foo 74 | new Foo().DoSomething(); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Delegate/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("Delegate")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Delegate")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("cb4de76e-cc5b-4911-9848-8d5ed09e9b57")] 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 | -------------------------------------------------------------------------------- /Delegate/README.md: -------------------------------------------------------------------------------- 1 | # Delegate 2 | 3 | ## Problem it solves 4 | Some times a class\method can ALMOST be purely free of specific logic but needs a few lines of code to be removed to achieve this. The delegate pattern allows the executing class to execute a passed in method thus relieving a class of having to know specific details of an implementation. 5 | 6 | ## Common Uses 7 | Often times code needs a value and it likely doesn't care how the value is obtained. The delegate pattern can be used to get a value from the UI and not be coupled to any specific UI implementation. This pattern is highly useful to decouple logic from another class. 8 | -------------------------------------------------------------------------------- /DistributedLocking/DistributedLocking.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BEFFC0AF-6683-4E0A-8A32-5E99F11C38C4} 8 | Exe 9 | Properties 10 | DistributedLocking 11 | DistributedLocking 12 | v4.5.2 13 | 512 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 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /DistributedLocking/Locking/DatabaseDistributedLocked.cs: -------------------------------------------------------------------------------- 1 | namespace DistributedLocking.Locking 2 | { 3 | public class DatabaseDistributedLock : IDistributedLock 4 | { 5 | /* 6 | * This assumes you have a column to hold the semaphore and it is unique 7 | * 8 | * Any attempt to insert a semaphore already in the db will fail 9 | * 10 | * Actual db imple left out as it's trivial and based on whatever you want it to be 11 | * 12 | */ 13 | 14 | public bool AcquireLock(string semaphore) 15 | { 16 | var alreadyInDb = false; //get from DB 17 | 18 | if (alreadyInDb) 19 | { 20 | return false; 21 | } 22 | 23 | try 24 | { 25 | //insert into db 26 | var wasInserted = true; 27 | 28 | if (wasInserted) 29 | { 30 | //success 31 | return true; 32 | } 33 | 34 | return false; 35 | } 36 | catch 37 | { 38 | return false; 39 | } 40 | } 41 | 42 | public void ReleaseLock(string semaphore) 43 | { 44 | //delete from db 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /DistributedLocking/Locking/IDistributedLock.cs: -------------------------------------------------------------------------------- 1 | namespace DistributedLocking.Locking 2 | { 3 | public interface IDistributedLock 4 | { 5 | bool AcquireLock(string semaphore); 6 | void ReleaseLock(string semaphore); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /DistributedLocking/Locking/InMemoryDistributedLock.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DistributedLocking.Locking 4 | { 5 | public class InMemoryDistributedLock : IDistributedLock 6 | { 7 | private readonly HashSet _locks = new HashSet(); 8 | private readonly object _padLock = new object(); 9 | 10 | public bool AcquireLock(string semaphore) 11 | { 12 | lock (_padLock) 13 | { 14 | if (_locks.Contains(semaphore)) 15 | { 16 | return false; 17 | } 18 | 19 | try 20 | { 21 | _locks.Add(semaphore); 22 | } 23 | catch 24 | { 25 | return false; 26 | } 27 | 28 | return true; 29 | } 30 | } 31 | 32 | public void ReleaseLock(string semaphore) 33 | { 34 | lock (_padLock) 35 | { 36 | if (_locks.Contains(semaphore)) 37 | { 38 | _locks.Remove(semaphore); 39 | } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DistributedLocking/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using DistributedLocking.Locking; 5 | 6 | namespace DistributedLocking 7 | { 8 | class Program 9 | { 10 | private static readonly IDistributedLock _locker = new InMemoryDistributedLock(); 11 | private static string mySemaphore = "1234customerPayment"; 12 | private static Random _random = new Random(); 13 | 14 | static void Main(string[] args) 15 | { 16 | while (true) 17 | { 18 | Task.Run(() => _doWork(mySemaphore)); 19 | 20 | Thread.Sleep(_random.Next(100, 500)); 21 | } 22 | } 23 | 24 | private static void _doWork(string semaphore) 25 | { 26 | var isThreadLocked = _locker.AcquireLock(semaphore); 27 | 28 | Console.WriteLine($"Did we get a lock => {isThreadLocked}"); 29 | 30 | if (isThreadLocked) 31 | { 32 | Console.WriteLine("Working..."); 33 | 34 | Thread.Sleep(_random.Next(1000, 3000)); 35 | 36 | _locker.ReleaseLock(semaphore); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /DistributedLocking/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("DistributedLocking")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("DistributedLocking")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 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("beffc0af-6683-4e0a-8a32-5e99f11c38c4")] 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 | -------------------------------------------------------------------------------- /DistributedLocking/README.md: -------------------------------------------------------------------------------- 1 | # Distributed Locking 2 | 3 | ## Problem it solves 4 | In a single host environment it is a trivial task to ensure that certain bits of code execute one thread at a time using locks. In a horizontally scaled environment, traditional locking will not work as the workers are spread across several nodes and/or processes on the the same node. In order to ensure that only one node may perform a particular task at a given time in a distributed environment, distributed locking can be used. 5 | 6 | Scenario: If a payment collection system is distributed, how can we ensure that we don't accidentally capture a payment twice (e.g. more than one worker is collecting payment for the same customer at the same time)? 7 | 8 | Naive solution: Use a DB to keep track of the current 'status' of a customer. For example we set a status field that says "in progress". Any other processes/nodes check this field before proceeding. 9 | 10 | The problem with the naive solution is that a race condition still exists. Two queue messages could be picked up at the same time and two workers attempt to read the status field and get the OK to proceed b/c the each process have yet to set the status to 'in progress'. 11 | 12 | Distributed locking requires that a semaphore be used to ensure that one and only one process can work a particular message at a time. This can be done with two simple methods: 13 | 14 | ``` 15 | bool AcquireLock(string semaphore) 16 | void ReleaseLock(string semaphore) 17 | ``` 18 | 19 | The semaphore is a deterministic value based on whatever you want so long as it is unique to the task at hand. Some examples of a good semaphore might be: 20 | 21 | ``` 22 | customerId + "collectPayment" 23 | customerId + "refundPayment" 24 | ``` 25 | 26 | The actual usage of our distributed lock would be the following pseduocode: 27 | 28 | ``` 29 | if(AcquireLock("1234collectPayment") 30 | { 31 | //collectPayment 32 | } 33 | else 34 | { 35 | //could not get a lock, ignore this message as someone else is working on this already 36 | } 37 | ``` 38 | 39 | ## Common Uses 40 | The exact implementation of you distributed lock can vary easily. You could choose to: 41 | - Store the key in a SQL DB and require that the key be unique. During lock acquisition, if it fails to insert, then you can fail to get a lock. If you do get a lock, be sure to call `ReleaseLock` in a `finally` statement when finished. 42 | - You could dedicate a single process and store the locks into memory. You'd want to make sure you use traditional locking to force threads to get into a single line. 43 | - You could use a distributed cache like Redis to simply store the lock keys. Existence of a key would equate to a lock already being issued. Simply remove the key from Redis to release the lock. Be sure to account for using traditional locking when setting the locks. 44 | -------------------------------------------------------------------------------- /DynamicDispatch/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DynamicDispatch/DynamicDispatch.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {44DD3435-713A-43C5-A35E-E5C56373AB75} 8 | Exe 9 | Properties 10 | DynamicDispatch 11 | DynamicDispatch 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 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 | 70 | -------------------------------------------------------------------------------- /DynamicDispatch/Messaging/Dispatching/IDispatchMessages.cs: -------------------------------------------------------------------------------- 1 | using DynamicDispatch.Messaging.Messages; 2 | 3 | namespace DynamicDispatch.Messaging.Dispatching 4 | { 5 | public interface IDispatchMessages 6 | { 7 | object Dispatch(IMessage message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /DynamicDispatch/Messaging/Dispatching/MessageDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DynamicDispatch.Messaging.Helpers; 3 | using DynamicDispatch.Messaging.Messages; 4 | 5 | namespace DynamicDispatch.Messaging.Dispatching 6 | { 7 | public class MessageDispatcher : IDispatchMessages 8 | { 9 | //this class uses a helper for conciseness, but based on the IMessage class name, we can instantiate a handler 10 | //and send the message to it 11 | public object Dispatch(IMessage message) 12 | { 13 | //in the case of 'FooMessage' this will return 'Foo' 14 | var nameOfMessageClass = message.GetType().Name.ToMessageName(); 15 | 16 | //this goes and gets the handler type based on the name of the message class 17 | var handlerType = MessageHelper.GetMessageHandlerByMessageTypeName(nameOfMessageClass); 18 | 19 | //this creates an instance at runtime 20 | var handlerInstance = Activator.CreateInstance(handlerType); 21 | 22 | //finally we execute the new handler instance 23 | //i'm not a fan of the 'dynamic' keyword but it works well here 24 | return ((dynamic) handlerInstance).Handle((dynamic)message); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DynamicDispatch/Messaging/Handlers/BarHandler.cs: -------------------------------------------------------------------------------- 1 | using DynamicDispatch.Messaging.Messages; 2 | 3 | namespace DynamicDispatch.Messaging.Handlers 4 | { 5 | public class BarHandler : IHandleMessages 6 | { 7 | //fully typed for the message type 8 | public object Handle(BarMessage message) 9 | { 10 | return $"Hello from the Bar handler: {message.Age}"; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /DynamicDispatch/Messaging/Handlers/FooHandler.cs: -------------------------------------------------------------------------------- 1 | using DynamicDispatch.Messaging.Messages; 2 | 3 | namespace DynamicDispatch.Messaging.Handlers 4 | { 5 | public class FooHandler : IHandleMessages 6 | { 7 | //fully typed for the message type 8 | public object Handle(FooMessage message) 9 | { 10 | return $"Hello from the Foo Handler! Foo Id: {message.FooId} {message.Name}"; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /DynamicDispatch/Messaging/Handlers/IHandleMessages.cs: -------------------------------------------------------------------------------- 1 | using DynamicDispatch.Messaging.Messages; 2 | 3 | namespace DynamicDispatch.Messaging.Handlers 4 | { 5 | public interface IHandleMessages where TMessage : IMessage 6 | { 7 | object Handle(TMessage message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /DynamicDispatch/Messaging/Helpers/MessageHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using DynamicDispatch.Messaging.Handlers; 5 | 6 | namespace DynamicDispatch.Messaging.Helpers 7 | { 8 | public static class MessageHelper 9 | { 10 | private static IEnumerable _allHandlerTypes; 11 | private static string _handlerSuffix = "Handler"; 12 | private static string _messageSuffix = "Message"; 13 | 14 | //based on message name, fine the corresponding handler type 15 | public static Type GetMessageHandlerByMessageTypeName(string messageType) 16 | { 17 | var handler = GetAllMessageHandlers() 18 | .FirstOrDefault(x => x.Name.ToHandlerName() == messageType); 19 | 20 | return handler; 21 | } 22 | 23 | //reflection utility to cache the known handler types 24 | public static IEnumerable GetAllMessageHandlers() 25 | { 26 | if (_allHandlerTypes != null) return _allHandlerTypes; 27 | 28 | var openGenericType = typeof(IHandleMessages<>); 29 | 30 | _allHandlerTypes = from x in AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()) 31 | from z in x.GetInterfaces() 32 | let y = x.BaseType 33 | where 34 | ((y != null && y.IsGenericType && openGenericType.IsAssignableFrom(y.GetGenericTypeDefinition())) 35 | || (z.IsGenericType && openGenericType.IsAssignableFrom(z.GetGenericTypeDefinition()))) 36 | && !x.IsAbstract && !x.IsInterface 37 | select x; 38 | 39 | return _allHandlerTypes; 40 | } 41 | 42 | //given an input, returns the raw message name when given a handler name 43 | public static string ToHandlerName(this string input) 44 | { 45 | if (input == null) 46 | { 47 | return string.Empty; 48 | } 49 | 50 | return !input.EndsWith(_handlerSuffix) ? input : input.Remove(input.LastIndexOf(_handlerSuffix)); 51 | } 52 | 53 | //given an input, returns the raw message name when given a message name 54 | public static string ToMessageName(this string input) 55 | { 56 | if (input == null) 57 | { 58 | return string.Empty; 59 | } 60 | 61 | return !input.EndsWith(_messageSuffix) ? input : input.Remove(input.LastIndexOf(_messageSuffix)); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /DynamicDispatch/Messaging/Messages/BarMessage.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicDispatch.Messaging.Messages 2 | { 3 | //simple POCO with a marker interface 4 | public class BarMessage : IMessage 5 | { 6 | public int Age { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /DynamicDispatch/Messaging/Messages/FooMessage.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicDispatch.Messaging.Messages 2 | { 3 | //simple POCO with a marker interface 4 | public class FooMessage : IMessage 5 | { 6 | public int FooId { get; set; } 7 | public string Name { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /DynamicDispatch/Messaging/Messages/IMessage.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicDispatch.Messaging.Messages 2 | { 3 | public interface IMessage 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /DynamicDispatch/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DynamicDispatch.Messaging.Dispatching; 3 | using DynamicDispatch.Messaging.Messages; 4 | 5 | namespace DynamicDispatch 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | //this class will 'route' our message to a handler 12 | IDispatchMessages messageDispatcher = new MessageDispatcher(); 13 | 14 | //send a FooMessage and get a response from IHandleMessages 15 | //I'm using a convention here, of the message class and the handler class should be named like so: 16 | /* 17 | * Message and Handler 18 | * 19 | * So in the case of a message/handler pair named Foo, you'd create a FooMessage class and a FooHandler class 20 | */ 21 | 22 | //the messages are unique and don't need anything other than a marker interface called IMessage 23 | var response = messageDispatcher.Dispatch(new FooMessage 24 | { 25 | FooId = 1234, 26 | Name = "Mr. Foo" 27 | }); 28 | 29 | Console.WriteLine(response); 30 | 31 | //this message class is completely different 32 | response = messageDispatcher.Dispatch(new BarMessage() 33 | { 34 | Age = 99 35 | }); 36 | 37 | Console.WriteLine(response); 38 | 39 | //once the dispatcher is created, to add new functionality is a matter of adding a new message\handler pair 40 | 41 | Console.ReadKey(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /DynamicDispatch/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("DynamicDispatch")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("DynamicDispatch")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("44dd3435-713a-43c5-a35e-e5c56373ab75")] 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 | -------------------------------------------------------------------------------- /DynamicDispatch/README.md: -------------------------------------------------------------------------------- 1 | # Dynamic Dispatch 2 | 3 | ## Problem it solves 4 | Sometimes you'll want to be able to 'publish something' and it be 'handled' by another class. 5 | 6 | The effect you'd like to create is a 'routed' message; from client to handler. 7 | 8 | Dynamic dispatch allows for a one-time setup of a 'dispatcher' that routes a published POCO to the appropriate handler. 9 | 10 | Subsequent message\handler pairs can be created quickly. 11 | 12 | i.e. `FooMessage` gets published and handled by `IHandleMessages` 13 | 14 | ## Common Uses 15 | 16 | Messaging systems can send\receive messages that get handled by the appropriate handler class. 17 | 18 | This allows for quick and easy publish\handle pattern. 19 | 20 | The example contained can be modified to publish data on one box and the handlers on another box. To do that will require a transport such as a queue or http and the appropriate serialization. 21 | 22 | Combined with threading you can make a high-performance service router. 23 | -------------------------------------------------------------------------------- /Observer/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Observer/BubbleObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Observer 4 | { 5 | public class BubbleObserver 6 | { 7 | public BubbleObserver(ICanBeObserved observableThing) 8 | { 9 | //let's register our event handler 10 | if (observableThing != null) 11 | { 12 | observableThing.OnSomethingAmazingJustHappened += _observableThing_OnSomethingAmazingJustHappened; 13 | } 14 | } 15 | 16 | private void _observableThing_OnSomethingAmazingJustHappened(object sender, SomethingAmazingEventArgs e) 17 | { 18 | Console.WriteLine($"I don't care about Unicorns, but I noticed that something amazing happened and there was a bubble factor of: {e.BubbleFactor}!"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Observer/ICanBeObserved.cs: -------------------------------------------------------------------------------- 1 | namespace Observer 2 | { 3 | //NOTE: this lives outside of the interface 4 | public delegate void SomethingAmazingJustHappenedHandler(object sender, SomethingAmazingEventArgs e); 5 | 6 | public interface ICanBeObserved 7 | { 8 | //this is the name of the 'broadcasting' method 9 | event SomethingAmazingJustHappenedHandler OnSomethingAmazingJustHappened; 10 | 11 | //this has nothing to do with observables, just regular 'ol biz logic 12 | void DoSomethingAmazing(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Observer/Observer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E742DE42-717D-4560-87E9-0AECE9DCD6E3} 8 | Exe 9 | Properties 10 | Observer 11 | Observer 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 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 | 65 | -------------------------------------------------------------------------------- /Observer/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Observer 2 | { 3 | class Program 4 | { 5 | static void Main(string[] args) 6 | { 7 | //this class is observable 8 | var unicornDancer = new UnicornDancer(); 9 | 10 | //these two classes will do the observing 11 | new BubbleObserver(unicornDancer); 12 | new UnicornObserver(unicornDancer); 13 | 14 | //You could create an observer to send these events to Slack. Only the SlackObserver would need to know about Slack. 15 | 16 | /* 17 | * You may have a ton of other code in between the observer registrations and the actual execution of the observed class 18 | */ 19 | 20 | //unicorns will dance, bubbles will be produced and the observers will get a chance to do something with the event arguments broadcast 21 | unicornDancer.DoSomethingAmazing(); 22 | 23 | //NOTE: order of the observer execution is driven by the order by which the observers are registered. 24 | //However it is considered bad form to rely on this order. 25 | 26 | //we can do more amazing stuff and the observers will still be listening 27 | unicornDancer.DoSomethingAmazing(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Observer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Observer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Observer")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("e742de42-717d-4560-87e9-0aece9dcd6e3")] 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 | -------------------------------------------------------------------------------- /Observer/README.md: -------------------------------------------------------------------------------- 1 | # Observer 2 | 3 | ## Problem it solves 4 | Often time a class can get wrapped up into handling things such as logging, performance monitoring or even specific business cases which sully up a class or couple it to something. 5 | 6 | The observer pattern is a great way to decouple specifics from a core\engine. 7 | 8 | ## Common Uses 9 | 10 | The observer pattern can be used to broadcast important steps of an execution to other classes. For instance when a long running process begins\ends, you could broadcast these events and act upon them by updating Slack. 11 | 12 | By doing so the core code (observable) wouldn't have to know anything about Slack, only the observing class would. 13 | 14 | ## Demo 15 | 16 | I've created a video demo of the Observer Pattern that you can have a peek at here: https://www.youtube.com/watch?v=DgGKt0YgIBY&feature=youtu.be 17 | -------------------------------------------------------------------------------- /Observer/SomethingAmazingEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Observer 4 | { 5 | //NOTE: the inheritance of EventArgs 6 | public class SomethingAmazingEventArgs : EventArgs 7 | { 8 | public int NumberOfUnicornsDancing { get; set; } 9 | public int BubbleFactor { get; set; } 10 | 11 | //create a constructor with anything you want any observers to know about this amazing event 12 | public SomethingAmazingEventArgs(int numberOfUnicornsDancing, int bubbleFactor) 13 | { 14 | NumberOfUnicornsDancing = numberOfUnicornsDancing; 15 | BubbleFactor = bubbleFactor; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Observer/UnicornDancer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Observer 4 | { 5 | public class UnicornDancer : ICanBeObserved 6 | { 7 | private static readonly Random Rand = new Random(); 8 | 9 | public event SomethingAmazingJustHappenedHandler OnSomethingAmazingJustHappened; 10 | public void DoSomethingAmazing() 11 | { 12 | Console.WriteLine("Doing something amazing with Unicorns in here, we should broadcast this out!"); 13 | 14 | //NOTE: we're using the `?` in case of no observers 15 | //we can pass things to the event args and they will get send to all observers 16 | OnSomethingAmazingJustHappened?.Invoke(this, new SomethingAmazingEventArgs(Rand.Next(100), Rand.Next(10000))); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Observer/UnicornObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Observer 4 | { 5 | public class UnicornObserver 6 | { 7 | public UnicornObserver(ICanBeObserved observableThing) 8 | { 9 | //let's register our event handler 10 | if (observableThing != null) 11 | { 12 | observableThing.OnSomethingAmazingJustHappened += ObservableThing_OnSomethingAmazingJustHappened; 13 | } 14 | } 15 | 16 | private void ObservableThing_OnSomethingAmazingJustHappened(object sender, SomethingAmazingEventArgs e) 17 | { 18 | Console.WriteLine($"I don't care about Bubbles, but I noticed that something amazing happened and there were {e.NumberOfUnicornsDancing} unicorns dancing!"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Pipeline/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Pipeline/IPipeline.cs: -------------------------------------------------------------------------------- 1 | using Pipeline.PipeModel; 2 | using Pipeline.Pipes; 3 | 4 | namespace Pipeline 5 | { 6 | //an interface that represents the required methods of a pipeline 7 | //the TModel and where are generic and require the model used to be of type IModel 8 | public interface IPipeline where TModel : IModel 9 | { 10 | void Register(IPipe pipe); 11 | void RemoveAt(int index); 12 | void Process(TModel model); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Pipeline/ImagePipeline.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Pipeline.PipeModel; 3 | using Pipeline.Pipes; 4 | 5 | namespace Pipeline 6 | { 7 | //implementation of a pipeline 8 | public class ImagePipeline : IPipeline 9 | { 10 | //we'll store the registered actions here 11 | private readonly List> _pipeline = new List>(); 12 | 13 | public void Register(IPipe pipe) 14 | { 15 | _pipeline.Add(pipe); 16 | } 17 | 18 | public void RemoveAt(int index) 19 | { 20 | _pipeline.RemoveAt(index); 21 | } 22 | 23 | //just a simple loop that calls process on each pipe segment 24 | public void Process(ImageModel model) 25 | { 26 | foreach (var pipe in _pipeline) 27 | { 28 | pipe.Process(model); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Pipeline/PipeModel/IModel.cs: -------------------------------------------------------------------------------- 1 | namespace Pipeline.PipeModel 2 | { 3 | //a simple model that must be used in the pipeline 4 | //adjust this to suit your needs 5 | public interface IModel 6 | { 7 | string ProcessLog { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Pipeline/PipeModel/ImageModel.cs: -------------------------------------------------------------------------------- 1 | namespace Pipeline.PipeModel 2 | { 3 | //a token model wrapper that implements IModel 4 | public class ImageModel : IModel 5 | { 6 | public string ProcessLog { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Pipeline/Pipeline.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {91D2F6F9-7E2B-44D9-A6B8-12626B786139} 8 | Exe 9 | Properties 10 | Pipeline 11 | Pipeline 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 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 | 68 | -------------------------------------------------------------------------------- /Pipeline/Pipes/CropAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Pipeline.PipeModel; 3 | 4 | namespace Pipeline.Pipes 5 | { 6 | //a 'pipe' is just a very focused action, this one represents 'cropping' 7 | //the real 'crop' code would obviously be different, but the plumbing would remain the same 8 | public class CropAction : IPipe 9 | { 10 | public ImageModel Process(ImageModel model) 11 | { 12 | Console.WriteLine("Cropping!"); 13 | 14 | model.ProcessLog += "==>Cropping!"; 15 | 16 | return model; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Pipeline/Pipes/DesaturateAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Pipeline.PipeModel; 3 | 4 | namespace Pipeline.Pipes 5 | { 6 | public class DesaturateAction : IPipe 7 | { 8 | public ImageModel Process(ImageModel model) 9 | { 10 | Console.WriteLine("Desaturating!"); 11 | 12 | model.ProcessLog += "==>Desaturating!"; 13 | 14 | return model; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Pipeline/Pipes/IPipe.cs: -------------------------------------------------------------------------------- 1 | using Pipeline.PipeModel; 2 | 3 | namespace Pipeline.Pipes 4 | { 5 | //an interface that represents a 'pipe' action 6 | //i've constrained the TModel to be of type IModel 7 | public interface IPipe where TModel : IModel 8 | { 9 | //what comes in will be altered and then returned as output 10 | TModel Process(TModel input); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pipeline/Pipes/RotateAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Pipeline.PipeModel; 3 | 4 | namespace Pipeline.Pipes 5 | { 6 | public class RotateAction : IPipe 7 | { 8 | public ImageModel Process(ImageModel model) 9 | { 10 | Console.WriteLine("Rotating!"); 11 | 12 | model.ProcessLog += "==>Rotating!"; 13 | 14 | return model; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Pipeline/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Pipeline.PipeModel; 3 | using Pipeline.Pipes; 4 | 5 | namespace Pipeline 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | //create a new pipeline 12 | var pipeLine = new ImagePipeline(); 13 | //create a simple object to represent an image 14 | //the real image manipulation details would be much more involved 15 | var image = new ImageModel(); 16 | 17 | //add the actions that the pipeline should do in a particular order 18 | pipeLine.Register(new CropAction()); 19 | pipeLine.Register(new DesaturateAction()); 20 | pipeLine.Register(new RotateAction()); 21 | 22 | //process the image based on the the above 23 | pipeLine.Process(image); 24 | 25 | //let's see what steps actually were taken 26 | Console.WriteLine(image.ProcessLog); 27 | 28 | //let's remove the desaturate action 29 | pipeLine.RemoveAt(1); 30 | //we'll add a rotation 31 | pipeLine.Register(new RotateAction()); 32 | //let's reprocess the image 33 | pipeLine.Process(image); 34 | 35 | //let's see what steps ran 36 | Console.WriteLine(image.ProcessLog); 37 | 38 | Console.ReadKey(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Pipeline/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("Pipeline")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Pipeline")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("91d2f6f9-7e2b-44d9-a6b8-12626b786139")] 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 | -------------------------------------------------------------------------------- /Pipeline/README.md: -------------------------------------------------------------------------------- 1 | # Pipeline 2 | 3 | ## Problem it solves 4 | When an object needs to be processed conditionally based on a series of steps, the Pipeline pattern separates each step in the process into discrete classes. 5 | 6 | The pipeline is just a series of ordered steps that can be adjusted to suit custom needs without having a large class that tries to handle all situations. 7 | 8 | ## Common Uses 9 | Image processing makes use of pipelines quite often. You may need to first crop, adjust color and then save an image. An e-commerce system (like [uCommerce](https://ucommerce.net)) uses pipelines to allow the default behavior of a checkout process to be altered as needed by the developer. 10 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/Bar.cs: -------------------------------------------------------------------------------- 1 | namespace PoorMansDependencyInjection 2 | { 3 | class Bar : IBar 4 | { 5 | public void DoSomething() 6 | { 7 | 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/Bar2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PoorMansDependencyInjection 4 | { 5 | class Bar2 : IBar 6 | { 7 | public void DoSomething() 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/BestFoo.cs: -------------------------------------------------------------------------------- 1 | namespace PoorMansDependencyInjection 2 | { 3 | class BestFoo : IFoo 4 | { 5 | private readonly IBar _bar; 6 | 7 | public BestFoo() 8 | : this(new Bar()) 9 | { 10 | } 11 | 12 | public BestFoo(IBar bar) 13 | { 14 | _bar = bar; 15 | } 16 | 17 | public void Blah() 18 | { 19 | //I'm using bar, but I can swap it out easily without this line of code caring 20 | _bar.DoSomething(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/BetterFoo.cs: -------------------------------------------------------------------------------- 1 | namespace PoorMansDependencyInjection 2 | { 3 | class BetterFoo : IFoo 4 | { 5 | private readonly IBar _bar; 6 | 7 | public BetterFoo(IBar bar) 8 | { 9 | _bar = bar; 10 | } 11 | 12 | public void Blah() 13 | { 14 | //I'm using bar, but I can swap it out easily without this line of code caring 15 | _bar.DoSomething(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/BetterFoo2.cs: -------------------------------------------------------------------------------- 1 | namespace PoorMansDependencyInjection 2 | { 3 | class BetterFoo2 : IFoo 4 | { 5 | private IBar _bar; 6 | 7 | public IBar Bar 8 | { 9 | //if I don't have a IBar impl, use a default one 10 | get { return _bar ?? (_bar = new Bar()); } 11 | 12 | set { _bar = value; } 13 | } 14 | 15 | public void Blah() 16 | { 17 | _bar.DoSomething(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/Foo.cs: -------------------------------------------------------------------------------- 1 | namespace PoorMansDependencyInjection 2 | { 3 | public class Foo : IFoo 4 | { 5 | public void Blah() 6 | { 7 | //this will be near impossible to swap out 8 | var bar = new Bar(); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/IBar.cs: -------------------------------------------------------------------------------- 1 | namespace PoorMansDependencyInjection 2 | { 3 | interface IBar 4 | { 5 | void DoSomething(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/IFoo.cs: -------------------------------------------------------------------------------- 1 | namespace PoorMansDependencyInjection 2 | { 3 | interface IFoo 4 | { 5 | void Blah(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/PoorMansDependencyInjection.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {08D03EC8-6693-4A80-BC65-E685B2ABA75B} 8 | Exe 9 | Properties 10 | PoorMansDependencyInjection 11 | PoorMansDependencyInjection 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 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 | 68 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/Program.cs: -------------------------------------------------------------------------------- 1 | namespace PoorMansDependencyInjection 2 | { 3 | class Program 4 | { 5 | static void Main(string[] args) 6 | { 7 | /* 8 | * Poor man's DI is just a way to keep your code SOLID without the use of a DI container 9 | */ 10 | 11 | //for instance a typical developer might write a class like Foo where it's dependencies are trapped in the body of a method 12 | var foo = new Foo(); 13 | foo.Blah(); 14 | 15 | //in general, that may be good enough but if you inspect class Foo, you'll see that it needs class Bar. At test time 16 | //you'll have a difficult time mocking or switching off that code without doing some pretty strange configuration setups 17 | 18 | //meet class BetterFoo, it takes a dependency via the constructor 19 | var betterFoo = new BetterFoo(new Bar()); 20 | betterFoo.Blah(); 21 | 22 | //meet yet another twist on it by meeting class BetterFoo2. This class has a default use of IBar and doesn't need to be passed 23 | //explicitly AND it can be swapped out 24 | var betterFoo2 = new BetterFoo2(); 25 | betterFoo2.Blah(); 26 | 27 | //as-is, betterFoo2 will use Bar as the IBar impl, however if I want to use Bar2 instead, I can do the following 28 | betterFoo2.Bar = new Bar2(); 29 | betterFoo2.Blah(); 30 | 31 | //as my buddy @bleedo (on Twitter) pointed out, you can have yet another way to have a better foo. BestFoo has a default constructor 32 | //with the ability to pass dependencies via an overloaded constructor 33 | var bestFoo = new BestFoo(); 34 | bestFoo.Blah(); 35 | 36 | bestFoo = new BestFoo(new Bar2()); 37 | bestFoo.Blah(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/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("PoorMansDependencyInjection")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("PoorMansDependencyInjection")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("08d03ec8-6693-4a80-bc65-e685b2aba75b")] 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 | -------------------------------------------------------------------------------- /PoorMansDependencyInjection/README.md: -------------------------------------------------------------------------------- 1 | # Poor Man's Dependency Injection 2 | 3 | ## Problem it solves 4 | This pattern allows for depenencies to be easily changed for unit testing without the need for magical things like IoC containers. 5 | 6 | ## Common Uses 7 | Rather than `new` a dependency within a constructor or method in a class, all dependencies are taken via the contructor or a default is provided with a public getter. 8 | 9 | This is most useful when performing unit testing so you can easily return happy signals on irrelevant dependencies (things you didn't want to test anyway). 10 | 11 | Checkout a video I made on Poor man's DI and IoC containers: https://www.youtube.com/watch?v=xR0LotJQzlk 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # c-sharp-patterns 2 | 3 | This is yet another repo dedicated to design patterns in C#. This e-book will focus on the Gang of Four (https://en.wikipedia.org/wiki/Design_Patterns) patterns and other things such as SOLID, Dependency Injection and composition over inheritance. 4 | 5 | ## Contributors 6 | 7 | Anyone can contribute by sending a PR. 8 | 9 | 10 | ## Disclaimer 11 | 12 | As with many things in programming, pattern implementations can be pretty subjective. The main takeway is that each pattern solves a particular pain point commonly found in modern object-oriented programming. 13 | -------------------------------------------------------------------------------- /SimpleFactory/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SimpleFactory/Bar.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFactory 2 | { 3 | class Bar 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SimpleFactory/Program.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFactory 2 | { 3 | class Program 4 | { 5 | static void Main(string[] args) 6 | { 7 | //traditionally objects are created with the 'new' operator 8 | var foo = new Bar(); 9 | 10 | //over time if you change the constructor, you'll have to hunt\peck all over to update the signature in many places 11 | 12 | //a simple factory simply is a method that return an instance of an object thus allowing one place to make any changes 13 | foo = Create(); 14 | } 15 | 16 | static Bar Create() 17 | { 18 | return new Bar(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SimpleFactory/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("SimpleFactory")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SimpleFactory")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("d1953d7f-f0af-46d1-8171-b44f80d7832e")] 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 | -------------------------------------------------------------------------------- /SimpleFactory/README.md: -------------------------------------------------------------------------------- 1 | # Simple Factory 2 | 3 | ## Problem it solves 4 | Often times code will have many places where the `new` operator is used to create a new class. However over time if the implementation needs altered (signature update) or completely swapped out, this can become cumbersome. The simple factory pattern allows to centralize the creation of a class. 5 | 6 | ## Common Uses 7 | The most common use is to create a class with a single method named `.Create()`. This method does one thing, it creates another object and any class who needs a new object it provides will delegate the task to the method. This greatly increases future maintainability. 8 | 9 | A better factory can be found by using the [Abstract Factory](../AbstractFactory/README.md). 10 | 11 | Check out a video I made on factories: https://www.youtube.com/watch?v=qzxp9p7UP_Y 12 | -------------------------------------------------------------------------------- /SimpleFactory/SimpleFactory.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {D1953D7F-F0AF-46D1-8171-B44F80D7832E} 8 | Exe 9 | Properties 10 | SimpleFactory 11 | SimpleFactory 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 61 | -------------------------------------------------------------------------------- /Singleton/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Singleton/NonThreadSafeSingleton.cs: -------------------------------------------------------------------------------- 1 | namespace Singleton 2 | { 3 | public class NonThreadSafeSingleton 4 | { 5 | //holds our one and only instance 6 | private static NonThreadSafeSingleton _nonThreadSafeSingleton; 7 | 8 | //private constructor to disallow anyone 'new'ing one up 9 | private NonThreadSafeSingleton() 10 | { 11 | 12 | } 13 | 14 | //add as many properties as you want 15 | public string PropertyOne { get; set; } 16 | 17 | //the 'instance' property initializes our private instance or returns if already initialized 18 | public static NonThreadSafeSingleton Instance => _nonThreadSafeSingleton ?? (_nonThreadSafeSingleton = new NonThreadSafeSingleton()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Singleton/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Singleton 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | //many times we should use a thread safe implementation 10 | Console.WriteLine(ThreadSafeSingleton.Instance.PropertyOne); //hello world 11 | 12 | Console.WriteLine(ThreadSafeSingleton.Instance.PropertyOne); //hello world 13 | 14 | ThreadSafeSingleton.Instance.Clear(); //will require a re-initialization on the next usage 15 | 16 | Console.WriteLine(ThreadSafeSingleton.Instance.PropertyOne); //hello world 17 | 18 | //we can also use a non-thread safe singleton if you need to but you'll wanna understand when this is ok 19 | NonThreadSafeSingleton.Instance.PropertyOne = "Foo"; 20 | 21 | Console.WriteLine(NonThreadSafeSingleton.Instance.PropertyOne); 22 | 23 | Console.ReadKey(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Singleton/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("c-sharp-patterns")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("University of Notre Dame")] 12 | [assembly: AssemblyProduct("c-sharp-patterns")] 13 | [assembly: AssemblyCopyright("Copyright © University of Notre Dame 2016")] 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("c995f399-fa01-4d04-b235-2590f54479af")] 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 | -------------------------------------------------------------------------------- /Singleton/README.md: -------------------------------------------------------------------------------- 1 | # Singleton 2 | 3 | ## Problem it solves 4 | Often times you will want one global instance of an object instead of using `static` objects. Instance objects can utilize extension methods whereas a purely static class cannot. 5 | 6 | ## Common Uses 7 | The Singleton pattern is a good option to use when you'd like to restrict the creation of an object to only one instance. 8 | 9 | A common use may be to use a global object to work as a service object. 10 | 11 | There are both thread-safe and non-thread-safe versions to choose from. The main thing to look out for when using non-thread-safe Singletons is during intialization. If your startup code has more than one thread that could invoke your class, you will need to use the thread-safe version. However if you're startup code is single-threaded; you can create a non-thread-safe version (less code). Keep in mind that thread-safety is its own topic and if you manage state in your Singleton, you'll have other things to consider. 12 | 13 | A video for you to watch if you so desire: https://www.youtube.com/watch?v=XZl3FgkYazI 14 | -------------------------------------------------------------------------------- /Singleton/Singleton.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F97FEF4A-DE8C-452C-9535-08CA5E977011} 8 | Exe 9 | Properties 10 | Singleton 11 | Singleton 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 62 | -------------------------------------------------------------------------------- /Singleton/ThreadSafeSingleton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Singleton 4 | { 5 | public class ThreadSafeSingleton 6 | { 7 | //this will hold the internal instance of our singleton 8 | private static volatile ThreadSafeSingleton _instance; 9 | 10 | //this is used purely for thread safety 11 | private static readonly object _padLock = new object(); 12 | 13 | //you can create as many properties as you'd like 14 | public string PropertyOne; 15 | 16 | //note that the constructor is private and only the class itself can create a new instance 17 | private ThreadSafeSingleton() 18 | { 19 | } 20 | 21 | //this is where the instance will live, notice the return value is the singleton class itself 22 | public static ThreadSafeSingleton Instance 23 | { 24 | get 25 | { 26 | //using double check locking to make this thread-safe 27 | //if already initialized, it'll just return the already created instance 28 | if (_instance == null) 29 | { 30 | lock (_padLock) 31 | { 32 | if (_instance == null) 33 | { 34 | //this will only run if the singleton has never been initialized or has been set to null 35 | //otherwise this code will never run after the first time 36 | Console.WriteLine("Initializing singleton..."); 37 | 38 | //create an instance and set it to your private member 39 | _instance = new ThreadSafeSingleton(); 40 | 41 | //set the values of any properties you have 42 | _instance.PropertyOne = "Hello World!"; 43 | } 44 | } 45 | } 46 | 47 | return _instance; 48 | } 49 | } 50 | 51 | //a helper method designed to set the singleton to null which triggers a rebuilding of the instance on next use 52 | public void Clear() 53 | { 54 | Console.WriteLine("Clearing singleton..."); 55 | 56 | //by setting this to null, the next thread that uses this singleton will cause it to reinitialize 57 | _instance = null; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Solid/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Solid/EmailDependency.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Solid 4 | { 5 | public class EmailDependency :IEmailDependency 6 | { 7 | public void SendMail() 8 | { 9 | Console.WriteLine("Sending an email..."); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Solid/IEmailDependency.cs: -------------------------------------------------------------------------------- 1 | namespace Solid 2 | { 3 | public interface IEmailDependency 4 | { 5 | void SendMail(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Solid/ISolidEmailer.cs: -------------------------------------------------------------------------------- 1 | namespace Solid 2 | { 3 | public interface ISolidEmailer 4 | { 5 | void SendMail(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Solid/ISolidUserRecordUpdater.cs: -------------------------------------------------------------------------------- 1 | namespace Solid 2 | { 3 | public interface ISolidUserRecordUpdater 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Solid/IUpdateUserRecords.cs: -------------------------------------------------------------------------------- 1 | namespace Solid 2 | { 3 | public interface IUpdateUserRecords 4 | { 5 | void DoSomeUpdates(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Solid/NotSolid.cs: -------------------------------------------------------------------------------- 1 | namespace Solid 2 | { 3 | public class NotSolid 4 | { 5 | //This class violates 'S', 'O', 'L' and 'D' 6 | //'S' because it does two seemingly unrelated things, emailing and updating user records 7 | public NotSolid() 8 | { 9 | 10 | } 11 | 12 | public void DoSomething() 13 | { 14 | //this will work, however it 'traps' this dependency in this method 15 | //there is no way to swap this for testing 16 | //Violates 'O', 'D' and 'L' 17 | //'L' and 'D' because we depend on a concrete class 18 | //'O' because there is no way to change the email dependency 19 | var emailDependency = new EmailDependency(); 20 | 21 | emailDependency.SendMail(); 22 | } 23 | 24 | public void DoSomethingElse() 25 | { 26 | //also trapping a dependency here 27 | //seems to be doing something unrelated to sending emails 28 | var update = new UpdateUserRecords(); 29 | update.DoSomeUpdates(); 30 | } 31 | 32 | //Violates L and D 33 | //Both because we're not using and interface 34 | public void DoSomethingElseAgain(UpdateUserRecords update) 35 | { 36 | update.DoSomeUpdates(); 37 | var emailDependency = new EmailDependency(); 38 | 39 | emailDependency.SendMail(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Solid/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Solid 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | //SOLID programming is a way to keep your code maintainable 10 | /* 11 | * It is an acronym and the Wikipedia definition can be found here: https://en.wikipedia.org/wiki/SOLID_(object-oriented_design) 12 | * 13 | * S - Single Responsibility. This one is the most subjective of the definitions. However you should favor smaller classes 14 | * that do specific things as opposed to large classes that do too much. 15 | * O - Open\Closed Principle. Classes should be 'open' for extension and 'closed' for modification. Again more subjectivity 16 | * but if a class doesn't need to be altered and offers extension points; it adheres better to this principle than a class 17 | * that needs to be updated often. 18 | * L - Liskov Substitution Principle. This is more about polymorphism and abstraction. You should be using interfaces and\or 19 | * abstract classes when depending on another class. Therefore when you want to change out a dependency, the contract (interface) 20 | * allows you to. 21 | * I - Interface Segregation Principle - This sort of blends the 'O' and the 'I' with a little bit of 'S'. Favor smaller specific 22 | * purpose interfaces to large interfaces that do too much. There is no example here, but just think of a giant abstraction that does 23 | * too much. 24 | * D - Dependency Inversion Principle - Depend on abstractions, not concrete classes. More blending of the above, but dependencies 25 | * should be pass thru a constructor or property as an abstraction and not a concrete class. Also do not 'trap' a dependency. 26 | * 27 | * 28 | * ** Remember, these are guidelines, not hard-fast rules. Feel free to make exceptions as needed. ** 29 | */ 30 | 31 | var notSolid = new NotSolid(); 32 | 33 | notSolid.DoSomething(); 34 | notSolid.DoSomethingElse(); 35 | 36 | //we take our dependencies via the constructor, view the definition to notice we are depending on an interface and not a concrete mailer 37 | var solidEmailer = new SolidEmailer(new EmailDependency()); 38 | solidEmailer.SendMail(); 39 | 40 | //same as above, but we split the user record updates from the emails 41 | //we take the email dependency as a dependency 42 | var solidUpdater = new SolidUserRecordUpdater(new UpdateUserRecords(), new EmailDependency()); 43 | solidUpdater.DoSomethingElse(); 44 | solidUpdater.DoSomethingElseAgain(); 45 | 46 | Console.ReadKey(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Solid/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("Solid")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Solid")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("d0500f3b-e9f2-4534-ae9a-980cc3f0c9a4")] 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 | -------------------------------------------------------------------------------- /Solid/README.md: -------------------------------------------------------------------------------- 1 | # SOLID 2 | 3 | ## Problem it solves 4 | SOLID is a set of principles that help to keep your code maintainable. It's best to know the rules so when you want to break them, you know the repercussions. 5 | 6 | https://en.wikipedia.org/wiki/SOLID_(object-oriented_design) 7 | 8 | ## Common Uses 9 | Many developers use these principles to seperate out dependencies and to guide how to decompose large blocks of logic into modular components. 10 | -------------------------------------------------------------------------------- /Solid/Solid.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {D0500F3B-E9F2-4534-AE9A-980CC3F0C9A4} 8 | Exe 9 | Properties 10 | Solid 11 | Solid 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 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 | 69 | -------------------------------------------------------------------------------- /Solid/SolidEmailer.cs: -------------------------------------------------------------------------------- 1 | namespace Solid 2 | { 3 | public class SolidEmailer :ISolidEmailer 4 | { 5 | private readonly IEmailDependency _emailDependency; 6 | 7 | //we take our dependencies via the constructor and they are of an abstracted type 8 | //swapping which emailer we use is simple 9 | public SolidEmailer(IEmailDependency emailDependency) 10 | { 11 | _emailDependency = emailDependency; 12 | } 13 | 14 | //we limit this class to only handle emailing 15 | public void SendMail() 16 | { 17 | _emailDependency.SendMail(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Solid/SolidUserRecordUpdater.cs: -------------------------------------------------------------------------------- 1 | namespace Solid 2 | { 3 | public class SolidUserRecordUpdater : ISolidUserRecordUpdater 4 | { 5 | private readonly IUpdateUserRecords _updater; 6 | private readonly IEmailDependency _emailDependency; 7 | 8 | public SolidUserRecordUpdater(IUpdateUserRecords updater, IEmailDependency emailDependency) 9 | { 10 | _updater = updater; 11 | _emailDependency = emailDependency; 12 | } 13 | 14 | public void DoSomethingElse() 15 | { 16 | _updater.DoSomeUpdates(); 17 | } 18 | 19 | public void DoSomethingElseAgain() 20 | { 21 | _updater.DoSomeUpdates(); 22 | _emailDependency.SendMail(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Solid/UpdateUserRecords.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Solid 4 | { 5 | public class UpdateUserRecords : IUpdateUserRecords 6 | { 7 | public void DoSomeUpdates() 8 | { 9 | Console.WriteLine("Updating records..."); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Strategy/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Strategy/BadGuy.cs: -------------------------------------------------------------------------------- 1 | using Strategy.MovementStrategies; 2 | 3 | namespace Strategy 4 | { 5 | public class BadGuy : IBadGuy 6 | { 7 | //this is our bad guy 8 | public string Name { get; set; } 9 | public IMovementStrategy MovementStrategy { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Strategy/BadGuyFactory.cs: -------------------------------------------------------------------------------- 1 | using Strategy.MovementStrategies; 2 | 3 | namespace Strategy 4 | { 5 | public static class BadGuyFactory 6 | { 7 | public static IBadGuy Create(string name) 8 | { 9 | //default for guys like Fred 10 | IMovementStrategy movementStrategy = new SideToSideStrategy(); 11 | 12 | //Dave is special, so let's assign him up and down 13 | if (name == "Dave") 14 | { 15 | movementStrategy = new UpAndDownStrategy(); 16 | } 17 | 18 | //let's construct our bad guys now that our movement strategy has been selected 19 | return new BadGuy 20 | { 21 | Name = name, 22 | MovementStrategy = movementStrategy 23 | }; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Strategy/Henchman.cs: -------------------------------------------------------------------------------- 1 | namespace Strategy 2 | { 3 | public class Henchman 4 | { 5 | public string Name { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Strategy/IBadGuy.cs: -------------------------------------------------------------------------------- 1 | using Strategy.MovementStrategies; 2 | 3 | namespace Strategy 4 | { 5 | //our bad guys abstracted 6 | public interface IBadGuy 7 | { 8 | string Name { get; set; } 9 | IMovementStrategy MovementStrategy { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Strategy/MovementStrategies/IMovementStrategy.cs: -------------------------------------------------------------------------------- 1 | namespace Strategy.MovementStrategies 2 | { 3 | //this interface is used as an abstraction to our strategies 4 | public interface IMovementStrategy 5 | { 6 | void Move(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Strategy/MovementStrategies/SideToSideStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Strategy.MovementStrategies 4 | { 5 | //a concrete movement strategy 6 | public class SideToSideStrategy : IMovementStrategy 7 | { 8 | //in an actual game, you can implement the details of how to move side to side 9 | public void Move() 10 | { 11 | Console.WriteLine("I'm moving side to side!"); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Strategy/MovementStrategies/UpAndDownStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Strategy.MovementStrategies 4 | { 5 | //a concrete movement strategy 6 | public class UpAndDownStrategy : IMovementStrategy 7 | { 8 | //in an actual game, you can implement the details of how to move up and down 9 | public void Move() 10 | { 11 | Console.WriteLine("I'm moving up and down."); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Strategy/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Strategy 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | //for this example we are creating two henchmen and bad guys that will appear in a video game 10 | //each henchmen\bad guy normally moves side to side, but for henchmen\bad guys named 'Dave', they get to move up and down. 11 | 12 | //henchmen do not use a strategy pattern 13 | var daveHenchman = new Henchman 14 | { 15 | Name = "Dave" 16 | }; 17 | 18 | var fredHenchman = new Henchman 19 | { 20 | Name = "Fred" 21 | }; 22 | 23 | //we have to do a more explicit move for our henchmen 24 | _move(daveHenchman); 25 | _move(fredHenchman); 26 | 27 | //bad guys do use a strategy pattern 28 | //this example is also using a factory to create our bad guys 29 | //inside the factory you'll see that each bad guy is created and a movement strategy is selected 30 | 31 | //the strategy pattern is encapsulating a behavior in a call class through a concrete implementation of an abstract interface or class 32 | 33 | //the 'how' to move is encapsulated in to discrete classes 34 | 35 | //dave will move up and down 36 | var daveBadGuy = BadGuyFactory.Create("Dave"); 37 | 38 | daveBadGuy.MovementStrategy.Move(); 39 | 40 | //fred will move side to side 41 | var fredBadGuy = BadGuyFactory.Create("Fred"); 42 | 43 | fredBadGuy.MovementStrategy.Move(); 44 | 45 | Console.ReadKey(); 46 | } 47 | 48 | //this tangles up both up and down and side to side movement 49 | //the strategy pattern helps us make a class per movement type 50 | //the details of the movement are not encapsulated here 51 | //this will lead to a large class with a bunch of logic conditionals 52 | private static void _move(Henchman henchman) 53 | { 54 | if (henchman.Name == "Dave") 55 | { 56 | Console.WriteLine("Moving Up and down!"); 57 | } 58 | else 59 | { 60 | Console.WriteLine("Moving side to side!"); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Strategy/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("Strategy")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Strategy")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("202078d6-bf8c-4d11-931e-a6a7bfb98b1f")] 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 | -------------------------------------------------------------------------------- /Strategy/README.md: -------------------------------------------------------------------------------- 1 | # Strategy 2 | 3 | ## Problem it solves 4 | Often times we will write a class then ends up having quite a bit of conditional logic that handles many behaviors via if\then\else\switch. 5 | This leads to having classes that are long with trapped logic. The strategy pattern allows us to encapsulate each behavior as a discrete class that we can assign to another which makes it easier to read and the code becomes portable. 6 | 7 | ## Common Uses 8 | When more than one distinct behavior is possible based on some sort of condition, it is much cleaner to refactor the behaviors into their own classes. 9 | In the video game world, you may want Goomba's to react a certain way to a character while having Bowser react another. Rather than put all of that into a single class, you can put each behvavior into its own class. 10 | 11 | In finance, you may want checking accounts to handle overdrawn accounts differently based upon their account type. For example, a free checking account may have a fee incurred immediately while a premium account may have forgiveness up to a point. 12 | Each behavior can be handled separately without having a mess of if\then\else\switch statements. 13 | -------------------------------------------------------------------------------- /Strategy/Strategy.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {202078D6-BF8C-4D11-931E-A6A7BFB98B1F} 8 | Exe 9 | Properties 10 | Strategy 11 | Strategy 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 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 | 67 | -------------------------------------------------------------------------------- /Tasks/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Tasks/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Tasks.Work; 4 | 5 | namespace Tasks 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | var work = new DoSomething(); 12 | 13 | //immediately runs in a separate thread 14 | var process1Task = Task.Run(() => work.Process1()); 15 | 16 | //immediately runs in a separate thread 17 | var process2Task = Task.FromResult(work.Process2()); 18 | 19 | //blocks until it's done due to the 'Result' keyword 20 | Console.WriteLine($"Status: {process2Task.Result}"); 21 | 22 | //let's kick off process3 immediately 23 | var process3Task = _process3Async(work); 24 | 25 | //and wait for it (meanwhile process 1 is still going likely) 26 | var process3TaskResult = process3Task.Result; 27 | 28 | Console.WriteLine($"Process3: {process3TaskResult}"); 29 | 30 | //if we don't wait, the main thread will exit before Process1 is complete. 31 | //we'll never risk this with Process2 since we block until it's done 32 | process1Task.Wait(); 33 | 34 | Console.WriteLine("All tasks complete!"); 35 | 36 | Console.ReadKey(); 37 | 38 | //now let's run three tasks but wait for them all to complete 39 | //they will end in reverse order 40 | Task.WaitAll(new Task[] 41 | { 42 | work.Process3Async(3000), 43 | work.Process3Async(2000), 44 | work.Process3Async(1000) 45 | }); 46 | 47 | Console.WriteLine("All tasks complete!"); 48 | 49 | Console.ReadKey(); 50 | 51 | //now let's run three tasks but wait for ONLY ONE of them to complete 52 | //they will end in reverse order 53 | Task.WaitAny(new Task[] 54 | { 55 | work.Process3Async(3000), 56 | work.Process3Async(2000), 57 | work.Process3Async(1000) 58 | }); 59 | 60 | Console.WriteLine("One task complete!"); 61 | 62 | Console.ReadKey(); 63 | } 64 | 65 | //you can define an async task by using the 'async' keyword, the 'Task` return type and the 'await keyword on something 66 | //defined as a task 67 | private static async Task _process3Async(DoSomething work) 68 | { 69 | return await work.Process3Async(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Tasks/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("Tasks")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Tasks")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("6262a23d-0e46-4492-8b98-105bce96a8ac")] 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 | -------------------------------------------------------------------------------- /Tasks/Tasks.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {6262A23D-0E46-4492-8B98-105BCE96A8AC} 8 | Exe 9 | Properties 10 | Tasks 11 | Tasks 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 61 | -------------------------------------------------------------------------------- /Tasks/Work/DoSomething.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Tasks.Work 6 | { 7 | public class DoSomething 8 | { 9 | public void Process1() 10 | { 11 | Console.WriteLine("Doing Process1...."); 12 | 13 | Thread.Sleep(10000); 14 | 15 | Console.WriteLine("Process1 Done."); 16 | } 17 | 18 | public string Process2() 19 | { 20 | Console.WriteLine("Doing Process2...."); 21 | 22 | Thread.Sleep(1000); 23 | 24 | Console.WriteLine("Process2 Done."); 25 | 26 | return "All done."; 27 | } 28 | 29 | public async Task Process3Async(int sleepInMs = 5000) 30 | { 31 | //should run immediately 32 | Console.WriteLine($"Doing Process3 ({sleepInMs})..."); 33 | 34 | //simulates a blocking task 35 | await Task.Run(() => 36 | { 37 | Thread.Sleep(sleepInMs); 38 | }); 39 | 40 | //this should run afterwards 41 | Console.WriteLine($"Process3 ({sleepInMs}) Done."); 42 | 43 | return "All done."; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Threading/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Threading/IManageThreads.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Threading 4 | { 5 | public interface IManageThreads 6 | { 7 | void Queue(object o); 8 | void ProcessQueue(Action action, int maxThreads); 9 | int NumberThreadsRunning { get; } 10 | int MaxConcurrentThreads { get; } 11 | void RemoveThread(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Threading/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Threading.Work; 4 | 5 | namespace Threading 6 | { 7 | class Program 8 | { 9 | private static readonly IManageThreads _threadManager = new ThreadManager(); 10 | private static int _mainThreadId; 11 | 12 | static void Main(string[] args) 13 | { 14 | var rand = new Random(); 15 | 16 | _mainThreadId = Thread.CurrentThread.ManagedThreadId; 17 | 18 | Console.WriteLine($"Main thread Id: {_mainThreadId}"); 19 | 20 | //queue up 10 things that need done 21 | for (var i = 1; i < 10; i++) 22 | { 23 | _threadManager.Queue(new DoSomething 24 | { 25 | Id = i, 26 | SleepFactor = rand.Next(1, 5) * 1000 27 | }); 28 | } 29 | 30 | //work thru the queue, set max threads 31 | //the _workerBee method gets run by a new thread 32 | //the work object will get injected as an arg 33 | _threadManager.ProcessQueue(_workerBee, 5); 34 | 35 | //since work is outside of this thread, we have to wait for the other threads to finish 36 | while (_threadManager.NumberThreadsRunning > 0) 37 | { 38 | 39 | } 40 | 41 | Console.WriteLine("All threads complete."); 42 | Console.WriteLine($"Current number of threads: {_threadManager.NumberThreadsRunning}, Max Threads: {_threadManager.MaxConcurrentThreads}"); 43 | Console.ReadKey(); 44 | } 45 | 46 | //this is the method that does the work 47 | //many threads will be in this method at the same time, so don't assume you get to do whatever you want without locking 48 | //work inside the DoSomething class should be thread-safe already 49 | private static void _workerBee(object o) 50 | { 51 | //illustrating that the main thread and this method should always be executing on different threads 52 | if (_mainThreadId == Thread.CurrentThread.ManagedThreadId) 53 | { 54 | throw new Exception("This shouldn't be the case!"); 55 | } 56 | 57 | //the thread manager is using , so we need to cast 58 | var myDoerClass = o as DoSomething; 59 | 60 | myDoerClass?.Process(); 61 | 62 | //this could be done differently if we want the worker method to know nothing about thread management 63 | //perhaps we could raise an event that the thread manager subscribes to 64 | //otherwise I don't see an easy way for the thread to be removed from the manager 65 | _threadManager.RemoveThread(); 66 | 67 | Thread.Sleep(5000); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Threading/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("Threading")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Threading")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("11694a72-4733-491c-811c-f103556fcaae")] 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 | -------------------------------------------------------------------------------- /Threading/README.md: -------------------------------------------------------------------------------- 1 | # Threading 2 | 3 | ## Problem it solves 4 | Unless utilizing a framework that handles multithreading, a program can only do one thing at a time. If you need additional independent processes to perform work, you can use a new thread. 5 | 6 | ## Common Uses 7 | A web application can serve more than one request at a time due to threading. Otherwise a website could only serve one visitor at a time creating a long line of visitors waiting to get a webpage served. 8 | 9 | The example contained uses creates a thread pool with the `Thread` class and keeps track of the work before exiting. 10 | 11 | Threads are not commonly used directly as this is more of the 'bare metal'. You should have a look at [ThreadPool](https://msdn.microsoft.com/en-us/library/system.threading.threadpool(v=vs.110).aspx) and [Tasks](https://msdn.microsoft.com/en-us/library/system.threading.tasks.task(v=vs.110).aspx) which are abstracted uses of Threads. 12 | -------------------------------------------------------------------------------- /Threading/ThreadManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading; 6 | 7 | namespace Threading 8 | { 9 | public class ThreadManager : IManageThreads 10 | { 11 | //let's keep track of our threads 12 | private readonly ConcurrentDictionary _currentRunningThreads = new ConcurrentDictionary(); 13 | 14 | //let's keep track of our work to do 15 | private readonly Queue _workQueue = new Queue(); 16 | 17 | //helper to add a new worker 18 | public void Queue(object o) 19 | { 20 | _workQueue.Enqueue(o); 21 | } 22 | 23 | //process the queue 24 | //essentially this is the brains that spawns a new thread 25 | //this runs on the main thread 26 | public void ProcessQueue(Action action, int maxThreads) 27 | { 28 | //throttle the work 29 | MaxConcurrentThreads = maxThreads; 30 | 31 | //if anything to do... 32 | while (_workQueue.Any()) 33 | { 34 | //using a count on a concurrent dictionary should get us the right count (I hope) 35 | //limit to the max threads 36 | if (_currentRunningThreads.Count < MaxConcurrentThreads) 37 | { 38 | Console.WriteLine($"Work queue items: {_workQueue.Count}"); 39 | 40 | //grab some work off the top of the queue 41 | var work = _workQueue.Dequeue(); 42 | 43 | //define a new thread 44 | var thread = new Thread(new ParameterizedThreadStart(action)); 45 | 46 | //try to add it to the dictionary 47 | //maybe we should add some logic here in case the dictionary doesn't let us 48 | _currentRunningThreads.TryAdd(thread.ManagedThreadId, thread); 49 | 50 | Console.WriteLine($"Spawning new thread: {thread.ManagedThreadId}..."); 51 | Console.WriteLine($"Current number of threads: {_currentRunningThreads.Count}, Max Threads: {MaxConcurrentThreads}"); 52 | 53 | //add some delay so that the console messages slowly crawl 54 | Thread.Sleep(5000); 55 | 56 | //begin the work 57 | thread.Start(work); 58 | } 59 | else 60 | { 61 | //guess we should wait until we get a thread to use 62 | //Console.WriteLine($"Waiting for a thread to come free..."); 63 | Thread.Sleep(1000); 64 | } 65 | } 66 | } 67 | 68 | public int NumberThreadsRunning => _currentRunningThreads.Count; 69 | public int MaxConcurrentThreads { get; set; } 70 | public void RemoveThread() 71 | { 72 | Thread t; 73 | 74 | //take the thread out of the mix, this might need better error handling 75 | _currentRunningThreads.TryRemove(Thread.CurrentThread.ManagedThreadId, out t); 76 | 77 | Console.WriteLine($"Ending thread Id: {t.ManagedThreadId}..."); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Threading/Threading.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {11694A72-4733-491C-811C-F103556FCAAE} 8 | Exe 9 | Properties 10 | Threading 11 | Threading 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 64 | -------------------------------------------------------------------------------- /Threading/Work/DoSomething.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace Threading.Work 5 | { 6 | public class DoSomething : IDoSomething 7 | { 8 | //just a placeholder class that is unaware that it's in a multithreaded env 9 | public int Id { get; set; } 10 | public int SleepFactor { get; set; } 11 | 12 | public void Process() 13 | { 14 | for (var i = 0; i < 10; i++) 15 | { 16 | Console.WriteLine($"Thread Id: {Thread.CurrentThread.ManagedThreadId} Processing Object: {Id} Task: {i + 1}/10"); 17 | 18 | Thread.Sleep(SleepFactor); 19 | } 20 | 21 | Console.WriteLine($"Thread Id: {Thread.CurrentThread.ManagedThreadId} Object {Id} is complete."); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Threading/Work/IDoSomething.cs: -------------------------------------------------------------------------------- 1 | namespace Threading.Work 2 | { 3 | public interface IDoSomething 4 | { 5 | int Id { get; set; } 6 | int SleepFactor { get; set; } 7 | void Process(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /UnitOfWork/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /UnitOfWork/DAL/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using PetaPoco; 3 | 4 | namespace UnitOfWork.DAL 5 | { 6 | public interface IUnitOfWork : IDisposable 7 | { 8 | void Commit(); 9 | IDatabase Database { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /UnitOfWork/DAL/MockUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using PetaPoco; 2 | 3 | namespace UnitOfWork.DAL 4 | { 5 | //class does nothing with a DB and is used for testing 6 | public class MockUnitOfWork : IUnitOfWork 7 | { 8 | private MockUnitOfWork() 9 | { 10 | 11 | } 12 | 13 | private static IUnitOfWork _uow; 14 | public static IUnitOfWork Instance => _uow ?? (_uow = new MockUnitOfWork()); 15 | 16 | public void Dispose() 17 | { 18 | 19 | } 20 | 21 | public void Commit() 22 | { 23 | 24 | } 25 | 26 | public IDatabase Database { get; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /UnitOfWork/DAL/PetaPocoUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using PetaPoco; 2 | 3 | namespace UnitOfWork.DAL 4 | { 5 | //NOTE: this class is a wrapper for PetaPoco transactions 6 | public class PetaPocoUnitOfWork : IUnitOfWork 7 | { 8 | private readonly Transaction _petaTransaction; 9 | private readonly Database _database; 10 | public static string ConnectionString = "foo"; 11 | 12 | public PetaPocoUnitOfWork(string connectionString = "") 13 | { 14 | if (!string.IsNullOrEmpty(connectionString)) 15 | { 16 | _database = new Database(connectionString); 17 | } 18 | else 19 | { 20 | _database = new Database(ConnectionString); 21 | } 22 | 23 | _petaTransaction = new Transaction(_database); 24 | } 25 | 26 | public void Dispose() 27 | { 28 | _petaTransaction.Dispose(); 29 | } 30 | 31 | public IDatabase Database => _database; 32 | 33 | public void Commit() 34 | { 35 | _petaTransaction.Complete(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /UnitOfWork/DAL/UnitOfWorkFactory.cs: -------------------------------------------------------------------------------- 1 | namespace UnitOfWork.DAL 2 | { 3 | public class UnitOfWorkFactory 4 | { 5 | //having this setter her allows the unit tests to get a mocked uow if it wants or a DB bound one for integration\normal ops 6 | public static UnitOfWorkType UnitOfWorkType { get; set; } = UnitOfWorkType.Database; 7 | public static IUnitOfWork Get(string connectionString = "") 8 | { 9 | if (UnitOfWorkType == UnitOfWorkType.Mock) 10 | { 11 | //using a singleton here so that the mocking fw is happy that we've got the same instance 12 | return MockUnitOfWork.Instance; 13 | } 14 | 15 | return new PetaPocoUnitOfWork(connectionString); 16 | } 17 | } 18 | 19 | public enum UnitOfWorkType 20 | { 21 | Database = 1, 22 | Mock 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UnitOfWork/Models/MyDbEntity.cs: -------------------------------------------------------------------------------- 1 | using PetaPoco; 2 | 3 | namespace UnitOfWork.Models 4 | { 5 | [TableName("MyDbEntities")] 6 | [PrimaryKey("Id")] 7 | public class MyDbEntity 8 | { 9 | public long Id { get; set; } 10 | public string Name { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UnitOfWork/Models/MyOtherDbEntity.cs: -------------------------------------------------------------------------------- 1 | using PetaPoco; 2 | 3 | namespace UnitOfWork.Models 4 | { 5 | [PrimaryKey("Id")] 6 | [TableName("MyOtherDbEntities")] 7 | public class MyOtherDbEntity 8 | { 9 | public long Id { get; set; } 10 | public string PhoneNumber { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UnitOfWork/Program.cs: -------------------------------------------------------------------------------- 1 | using UnitOfWork.DAL; 2 | using UnitOfWork.Models; 3 | using UnitOfWork.Repositories; 4 | 5 | namespace UnitOfWork 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | //a unit of work is created 12 | using (var uow = UnitOfWorkFactory.Get()) 13 | { 14 | //get an instance of a repo 15 | var repo = new FooRepository(); 16 | 17 | var entity = repo.Get(uow, 2); 18 | 19 | //no committing is necessary on a SELECT 20 | } 21 | 22 | //let's save something 23 | using (var uow = UnitOfWorkFactory.Get()) 24 | { 25 | var repo = new FooRepository(); 26 | 27 | var entity = repo.Get(uow, 0) ?? new MyDbEntity(); 28 | 29 | entity.Name = "Minnie Mouse"; 30 | 31 | repo.Save(uow, entity); 32 | 33 | //we need to commit our changes or else it won't be saved! 34 | //if the code throws before this point, the entire transaction is rolled back 35 | uow.Commit(); 36 | } 37 | 38 | //let's delete something 39 | using (var uow = UnitOfWorkFactory.Get()) 40 | { 41 | var repo = new FooRepository(); 42 | 43 | var entity = repo.Get(uow, 2); 44 | 45 | if (entity != null) 46 | { 47 | repo.Delete(uow, entity); 48 | 49 | //if we don't make it here or this is never called, the transaction gets rolled back 50 | uow.Commit(); 51 | } 52 | } 53 | 54 | //the best part of a UOW pattern is that you can do many things across more than one repo and have them linked as a single transaction 55 | using (var uow = UnitOfWorkFactory.Get()) 56 | { 57 | //look, we need to manipulate TWO repos in the same transaction 58 | var fooRepo = new FooRepository(); 59 | var barRepo = new BarRepository(); 60 | 61 | var entity = fooRepo.Get(uow, 0) ?? new MyDbEntity(); 62 | 63 | entity.Name = "Minnie Mouse"; 64 | 65 | fooRepo.Save(uow, entity); 66 | 67 | var phoneNumber = new MyOtherDbEntity 68 | { 69 | PhoneNumber = "5558675309" 70 | }; 71 | 72 | barRepo.Save(uow, phoneNumber); 73 | 74 | //we need to commit our changes or else it won't be saved! 75 | //if the code throws before this point, the entire transaction is rolled back 76 | uow.Commit(); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /UnitOfWork/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("UnitOfWork")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UnitOfWork")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("17983068-72db-450a-8448-16e396cf1daa")] 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 | -------------------------------------------------------------------------------- /UnitOfWork/README.md: -------------------------------------------------------------------------------- 1 | # Database Unit of Work 2 | 3 | ## Problem it solves 4 | Often times developers will want to only do certain DB actions together as one atomic transaction. Either succeed together or fail together. 5 | The unit of work pattern helps wrap one or more database actions into a transaction easily across multiple repositories. 6 | 7 | ## Common Uses 8 | If a user performs an action which will affect several entities\repos in the DB, it's best to make sure they are wrapped in a transaction. This pattern uses the unit of work to scope the items that need to work together or fail together. 9 | 10 | ## DB Schema 11 | If you'd like to test this out live, you may want the schema below: 12 | 13 | ```GO 14 | CREATE TABLE [dbo].[MyDbEntities]( 15 | [Id] [bigint] IDENTITY(1,1) NOT NULL, 16 | [Name] [nvarchar](50) NULL, 17 | CONSTRAINT [PK_MyDbEntity] PRIMARY KEY CLUSTERED 18 | ( 19 | [Id] ASC 20 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 21 | ) ON [PRIMARY] 22 | 23 | GO 24 | /****** Object: Table [dbo].[MyOtherDbEntities] Script Date: 5/29/2017 2:37:42 PM ******/ 25 | SET ANSI_NULLS ON 26 | GO 27 | SET QUOTED_IDENTIFIER ON 28 | GO 29 | CREATE TABLE [dbo].[MyOtherDbEntities]( 30 | [Id] [bigint] IDENTITY(1,1) NOT NULL, 31 | [PhoneNumber] [nchar](10) NULL, 32 | CONSTRAINT [PK_MyOtherDbEntities] PRIMARY KEY CLUSTERED 33 | ( 34 | [Id] ASC 35 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 36 | ) ON [PRIMARY] 37 | 38 | GO 39 | -------------------------------------------------------------------------------- /UnitOfWork/Repositories/BarRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using UnitOfWork.DAL; 3 | using UnitOfWork.Models; 4 | 5 | namespace UnitOfWork.Repositories 6 | { 7 | public class BarRepository 8 | { 9 | public MyOtherDbEntity Get(IUnitOfWork uow, long id) 10 | { 11 | var sql = @" 12 | SELECT * 13 | FROM MyOtherDbEntities 14 | WHERE id = @id 15 | "; 16 | 17 | return uow.Database.Fetch(sql, new { id = id }).SingleOrDefault(); 18 | } 19 | 20 | public void Save(IUnitOfWork uow, MyOtherDbEntity entity) 21 | { 22 | uow.Database.Save(entity); 23 | } 24 | 25 | public void Delete(IUnitOfWork uow, MyOtherDbEntity entity) 26 | { 27 | uow.Database.Delete(entity); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /UnitOfWork/Repositories/FooRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using UnitOfWork.DAL; 3 | using UnitOfWork.Models; 4 | 5 | namespace UnitOfWork.Repositories 6 | { 7 | //nothing crazy here, note that each CRUD operation needs a UOW to operate on 8 | //the UOW is not managed by the repo but could possibly be passed in at the class level 9 | //instead of each method 10 | public class FooRepository 11 | { 12 | public MyDbEntity Get(IUnitOfWork uow, long id) 13 | { 14 | var sql = @" 15 | SELECT * 16 | FROM MyDbEntities 17 | WHERE id = @id 18 | "; 19 | 20 | return uow.Database.Fetch(sql, new { id = id}).SingleOrDefault(); 21 | } 22 | 23 | public void Save(IUnitOfWork uow, MyDbEntity entity) 24 | { 25 | uow.Database.Save(entity); 26 | } 27 | 28 | public void Delete(IUnitOfWork uow, MyDbEntity entity) 29 | { 30 | uow.Database.Delete(entity); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /UnitOfWork/UnitOfWork.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {17983068-72DB-450A-8448-16E396CF1DAA} 8 | Exe 9 | Properties 10 | UnitOfWork 11 | UnitOfWork 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 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 | 71 | -------------------------------------------------------------------------------- /UnitOfWork/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Visitor/AnotherBar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Visitor 4 | { 5 | public class AnotherBar : IVisitFoo 6 | { 7 | public void Visit(IFoo foo) 8 | { 9 | //I can't do anything with component 1 here, but 2 and 3 are public so I could use those 10 | Console.WriteLine("I'm visiting foo and could do something with it if I chose to."); 11 | } 12 | 13 | public void Visit(Component1 component1) 14 | { 15 | //this is a way to control access to a private var if I wanted to 16 | Console.WriteLine("I'm visiting component1 and could do something with it if I chose to."); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Visitor/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Visitor/Bar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Visitor 4 | { 5 | //Bar's whole purpose in life is to gain a reference to an IFoo and a Component1 6 | //it can either do nothing with each or do something with each 7 | public class Bar : IVisitFoo 8 | { 9 | public void Visit(IFoo foo) 10 | { 11 | Console.WriteLine($"Let's visit the IFoo!, and make component 2 and 3 say something!"); 12 | foo.Component2.SaySomething("Hi there!"); 13 | foo.Component3.SaySomething("What's your name?"); 14 | } 15 | 16 | public void Visit(Component1 component1) 17 | { 18 | Console.WriteLine($"Let's visit the Component1 and make it say something!"); 19 | component1.SaySomething("Howdy!"); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Visitor/Component1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Visitor 4 | { 5 | public class Component1 : ISaySomething, IVisitable 6 | { 7 | public void SaySomething(string message) 8 | { 9 | Console.WriteLine($"Hello from {GetType().Name}, a visitor wants me to say: {message}!"); 10 | } 11 | 12 | public void HandleVisitor(IVisitFoo visitor) 13 | { 14 | //this exposes the public methods of this class to the visitor 15 | visitor.Visit(this); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Visitor/Component2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Visitor 4 | { 5 | public class Component2 : ISaySomething 6 | { 7 | public void SaySomething(string message) 8 | { 9 | Console.WriteLine($"Hello from {GetType().Name}, a visitor wants me to say: {message}!"); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Visitor/Component3.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Visitor 4 | { 5 | public class Component3 : ISaySomething 6 | { 7 | public void SaySomething(string message) 8 | { 9 | Console.WriteLine($"Hello from {GetType().Name}, a visitor wants me to say: {message}!"); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Visitor/Foo.cs: -------------------------------------------------------------------------------- 1 | namespace Visitor 2 | { 3 | public class Foo : IFoo 4 | { 5 | //this is a private var which we can expose it's object to a visitor (below) 6 | private Component1 _component1 => new Component1(); 7 | 8 | private Component2 _component2; 9 | public Component2 Component2 => _component2 ?? (_component2 = new Component2()); 10 | 11 | private Component3 _component3; 12 | public Component3 Component3 => _component3 ?? (_component3 = new Component3()); 13 | 14 | public void HandleVisitor(IVisitFoo visitor) 15 | { 16 | //this will allow for _component1 to be visible to the visitor 17 | _component1.HandleVisitor(visitor); 18 | 19 | //this will give a reference of all of the public members to the visitor 20 | visitor.Visit(this); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Visitor/IFoo.cs: -------------------------------------------------------------------------------- 1 | namespace Visitor 2 | { 3 | public interface IFoo : IVisitable 4 | { 5 | Component2 Component2 { get; } 6 | Component3 Component3 { get; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Visitor/ISaySomething.cs: -------------------------------------------------------------------------------- 1 | namespace Visitor 2 | { 3 | public interface ISaySomething 4 | { 5 | void SaySomething(string message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Visitor/IVisitFoo.cs: -------------------------------------------------------------------------------- 1 | namespace Visitor 2 | { 3 | public interface IVisitFoo 4 | { 5 | void Visit(IFoo foo); 6 | void Visit(Component1 component1); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Visitor/IVisitable.cs: -------------------------------------------------------------------------------- 1 | namespace Visitor 2 | { 3 | public interface IVisitable 4 | { 5 | void HandleVisitor(IVisitFoo visitor); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Visitor/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Visitor 2 | { 3 | class Program 4 | { 5 | static void Main(string[] args) 6 | { 7 | //if we have class Foo which implements IFoo, we may want to add functionality to it without modifying Foo 8 | //the gist of this pattern is that we'll make available the components so that another class can do some custom logic 9 | //to do so we can use double dispatch to essentially provide a callback to another class with Foo as an arg 10 | //this pattern is known as the Visitor pattern 11 | IFoo foo = new Foo(); 12 | 13 | //class Bar will add extra functionality to Foo and it can even allow for private vars to be available to Bar 14 | IVisitFoo bar = new Bar(); 15 | 16 | //Foo makes itself and optionally private args to Bar 17 | //Bar can optionally only use certain items inside Foo or use all of them and Foo controls what it makes available to Bar 18 | foo.HandleVisitor(bar); 19 | 20 | //the visitor pattern allows for swappable behavior by simply changing the visitor 21 | //it can also be used to handle a pipeline of steps that must be performed 22 | 23 | foo.HandleVisitor(new AnotherBar()); 24 | 25 | //this pattern can be super useful if the author of Foo didn't want to share their code nor wanted to allow anyone to modify it 26 | //the visitor pattern can let another author do custom work without having to worry about leaking business logic into a core class 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Visitor/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("Visitor")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Visitor")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 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("e7d10bb4-41d9-45be-9219-6aba1858d4c2")] 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 | -------------------------------------------------------------------------------- /Visitor/README.md: -------------------------------------------------------------------------------- 1 | # Visitor 2 | 3 | ## Problem it solves 4 | An author of a class may not make his\her code accessible for modification but may share some of it's implementation with another class. 5 | The author of the original class can allow another class to receive a callback with specific information from the class. 6 | 7 | ## Common Uses 8 | The visitor pattern can be used to create a pipeline of behavior. 9 | 10 | For example a visitor class can be given to a visitable class in an e-commerce system. The visitable class may have information about the current transaction and one or more visitor classes can be given to this class to perform additional processing. 11 | -------------------------------------------------------------------------------- /Visitor/Visitor.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E7D10BB4-41D9-45BE-9219-6ABA1858D4C2} 8 | Exe 9 | Properties 10 | Visitor 11 | Visitor 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 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 | 70 | --------------------------------------------------------------------------------