This is a code post. Skip it if you do not read code.
Have you ever wondered how easy would it be to have a simple synchronized counter? This is my version of it.
The value and the date is saved in the registry. It will infer the registry path via these attributes:
In Vista and later you will need to add permissions for the program to modify the registry at its location.
14 internal static class Extensions
15 {
16 public static T ToConsole<T>(this T e)
17 {
18 Console.WriteLine(e.ToString());
19
20 return e;
21 }
22
23
24 public static string PathCombine(this string[] e)
25 {
26 var p = e[0];
27
28 for (int i = 1; i < e.Length; i++)
29 {
30 p = Path.Combine(p, e[i]);
31 }
32
33 return p;
34 }
35
36 public static TReturn ReadWrite<TReturn, A, B>(this RegistryKey k, Func<A, B, TReturn> h)
37 {
38 return InternalReadWrite<TReturn>(k, h);
39 }
40
41 public static TReturn SynchronizedReadWrite<TReturn, A, B>(this RegistryKey k, Func<A, B, TReturn> h)
42 {
43 var n = typeof(TReturn).GUID.ToString();
44 var s = new Semaphore(1, 1, n);
45 s.WaitOne();
46 try
47 {
48 return InternalReadWrite<TReturn>(k.CreateSubKey(n), h);
49 }
50 finally
51 {
52 s.Release();
53 s.Close();
54 }
55 }
56
57 static TReturn InternalReadWrite<TReturn>(this RegistryKey k, Delegate h)
58 {
59 var s = (TReturn)h.DynamicInvoke(
60 Enumerable.ToArray(
61 from p in h.Method.GetParameters()
62 let kv = k.GetValue(p.Name)
63 let v = kv == null ?
64 Activator.CreateInstance(p.ParameterType) :
65 Convert.ChangeType(kv, p.ParameterType)
66 select v
67 )
68 );
69
70 foreach (var p in typeof(TReturn).GetProperties())
71 k.SetValue(p.Name, Convert.ToString(p.GetValue(s, null)));
72
73 return s;
74 }
75 }
76
77 public class SynchronizedCounter
78 {
79 /// <summary>
80 /// Given the last date and value you will need to provide the next value
81 /// </summary>
82 public Func<DateTime, int, int> IncrementImplementation;
83
84 public SynchronizedCounter(string Name)
85 : this(Assembly.GetEntryAssembly(), Name)
86 {
87
88 }
89
90 public SynchronizedCounter(Assembly SoftwareAssembly, string Name)
91 {
92 if (SoftwareAssembly == null)
93 {
94 // In design mode we cannot save to registry
95 Increment = () => 1;
96 return;
97 }
98
99 this.IncrementImplementation = (Date, Counter) => Counter + 1;
100
101 var k = Registry.LocalMachine.CreateSubKey(
102 new[]
103 {
104 "Software",
105 ((AssemblyCompanyAttribute)SoftwareAssembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false).Single()).Company,
106 ((AssemblyTitleAttribute)SoftwareAssembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false).Single()).Title,
107 Name
108 }.PathCombine()
109 );
110
111 Increment =
112 () =>
113 k.SynchronizedReadWrite(
114 (DateTime Date, int Counter) =>
115 {
116 return new
117 {
118 Date = DateTime.Now,
119 Counter = IncrementImplementation(Date, Counter)
120 };
121 }
122 ).Counter;
123 }
124
125 /// <summary>
126 /// This function will read the registry, apply the increment implementation,
127 /// write to the registry and return the new counter
128 /// </summary>
129 public readonly Func<int> Increment;
130
131 public class ResetEachDay : SynchronizedCounter
132 {
133 public ResetEachDay(string Name)
134 : base(Name)
135 {
136 this.IncrementImplementation =
137 (Date, Counter) =>
138 Date.Day == DateTime.Now.Day ? Counter + 1 : 1;
139 }
140 }
141
142 public class ResetEachMinute : SynchronizedCounter
143 {
144 public ResetEachMinute(string Name)
145 : base(Name)
146 {
147 this.IncrementImplementation =
148 (Date, Counter) =>
149 Date.Minute == DateTime.Now.Minute ? Counter + 1 : 1;
150 }
151 }
152 }