@@ -26,6 +26,22 @@ public class Application
26
26
internal const string AppConfigFileName = "applications.config" ;
27
27
private static string _appConfig ;
28
28
private static readonly object Locker = new object ( ) ;
29
+ private static IEnumerable < Application > _allAvailableSections ;
30
+ private static volatile bool _isInitialized = false ;
31
+
32
+ /// <summary>
33
+ /// Initializes the service with all available application plugins
34
+ /// </summary>
35
+ /// <param name="allAvailableSections">
36
+ /// All application plugins found in assemblies
37
+ /// </param>
38
+ /// <remarks>
39
+ /// This is used to populate the app.config file with any applications declared in plugins that don't exist in the file
40
+ /// </remarks>
41
+ internal static void Initialize ( IEnumerable < Application > allAvailableSections )
42
+ {
43
+ _allAvailableSections = allAvailableSections ;
44
+ }
29
45
30
46
/// <summary>
31
47
/// gets/sets the application.config file path
@@ -49,52 +65,75 @@ internal static string AppConfigFilePath
49
65
/// <summary>
50
66
/// The cache storage for all applications
51
67
/// </summary>
52
- internal static List < Application > Apps
68
+ public static List < Application > GetSections ( )
53
69
{
54
- get
55
- {
56
- return ApplicationContext . Current . ApplicationCache . GetCacheItem (
57
- CacheKeys . ApplicationsCacheKey ,
58
- ( ) =>
59
- {
60
- ////used for unit tests
61
- //if (_testApps != null)
62
- // return _testApps;
70
+ return ApplicationContext . Current . ApplicationCache . GetCacheItem < List < Application > > (
71
+ CacheKeys . ApplicationsCacheKey ,
72
+ ( ) =>
73
+ {
74
+ ////used for unit tests
75
+ //if (_testApps != null)
76
+ // return _testApps;
63
77
64
- var tmp = new List < Application > ( ) ;
78
+ var list = ReadFromXmlAndSort ( ) ;
65
79
66
- try
80
+ //On first access we need to do some initialization
81
+ if ( _isInitialized == false )
82
+ {
83
+ lock ( Locker )
84
+ {
85
+ if ( _isInitialized == false )
67
86
{
68
- LoadXml ( doc =>
87
+ //now we can check the non-volatile flag
88
+ if ( _allAvailableSections != null )
89
+ {
90
+ var hasChanges = false ;
91
+
92
+ LoadXml ( doc =>
69
93
{
70
- foreach ( var addElement in doc . Root . Elements ( "add" ) . OrderBy ( x =>
71
- {
72
- var sortOrderAttr = x . Attribute ( "sortOrder" ) ;
73
- return sortOrderAttr != null ? Convert . ToInt32 ( sortOrderAttr . Value ) : 0 ;
74
- } ) )
94
+ //Now, load in the xml structure and update it with anything that is not declared there and save the file.
95
+
96
+ //NOTE: On the first iteration here, it will lazily scan all apps, etc... this is because this ienumerable is lazy
97
+ // based on the ApplicationRegistrar - and as noted there this is not an ideal way to do things but were stuck like this
98
+ // currently because of the legacy assemblies and types not in the Core.
99
+
100
+ //Get all the trees not registered in the config
101
+ var unregistered = _allAvailableSections
102
+ . Where ( x => list . Any ( l => l . alias == x . alias ) == false )
103
+ . ToArray ( ) ;
104
+
105
+ hasChanges = unregistered . Any ( ) ;
106
+
107
+ var count = 0 ;
108
+ foreach ( var attr in unregistered )
75
109
{
76
- var sortOrderAttr = addElement . Attribute ( "sortOrder" ) ;
77
- tmp . Add ( new Application ( addElement . Attribute ( "name" ) . Value ,
78
- addElement . Attribute ( "alias" ) . Value ,
79
- addElement . Attribute ( "icon" ) . Value ,
80
- sortOrderAttr != null ? Convert . ToInt32 ( sortOrderAttr . Value ) : 0 ) ) ;
110
+ doc . Root . Add ( new XElement ( "add" ,
111
+ new XAttribute ( "alias" , attr . alias ) ,
112
+ new XAttribute ( "name" , attr . name ) ,
113
+ new XAttribute ( "icon" , attr . icon ) ,
114
+ new XAttribute ( "sortOrder" , attr . sortOrder ) ) ) ;
115
+ count ++ ;
81
116
}
82
117
83
- } , false ) ;
84
- return tmp ;
85
- }
86
- catch
87
- {
88
- //this is a bit of a hack that just ensures the application doesn't crash when the
89
- //installer is run and there is no database or connection string defined.
90
- //the reason this method may get called during the installation is that the
91
- //SqlHelper of this class is shared amongst everything "Application" wide.
118
+ //don't save if there's no changes
119
+ return count > 0 ;
120
+ } , true ) ;
92
121
93
- //TODO: Perhaps we should log something here??
94
- return null ;
122
+ if ( hasChanges )
123
+ {
124
+ //If there were changes, we need to re-read the structures from the XML
125
+ list = ReadFromXmlAndSort ( ) ;
126
+ }
127
+ }
128
+
129
+ _isInitialized = true ;
95
130
}
96
- } ) ;
97
- }
131
+ }
132
+ }
133
+
134
+ return list ;
135
+
136
+ } ) ;
98
137
}
99
138
100
139
/// <summary>
@@ -200,7 +239,7 @@ public Application(string name, string alias, string icon, int sortOrder)
200
239
[ MethodImpl ( MethodImplOptions . Synchronized ) ]
201
240
public static void MakeNew ( string name , string alias , string icon )
202
241
{
203
- MakeNew ( name , alias , icon , Apps . Max ( x => x . sortOrder ) + 1 ) ;
242
+ MakeNew ( name , alias , icon , GetSections ( ) . Max ( x => x . sortOrder ) + 1 ) ;
204
243
}
205
244
206
245
/// <summary>
@@ -224,6 +263,9 @@ public static void MakeNew(string name, string alias, string icon, int sortOrder
224
263
new XAttribute ( "name" , name ) ,
225
264
new XAttribute ( "icon" , icon ) ,
226
265
new XAttribute ( "sortOrder" , sortOrder ) ) ) ;
266
+
267
+ return true ;
268
+
227
269
} , true ) ;
228
270
229
271
//raise event
@@ -238,7 +280,7 @@ public static void MakeNew(string name, string alias, string icon, int sortOrder
238
280
/// <returns></returns>
239
281
public static Application getByAlias ( string appAlias )
240
282
{
241
- return Apps . Find ( t => t . alias == appAlias ) ;
283
+ return GetSections ( ) . Find ( t => t . alias == appAlias ) ;
242
284
}
243
285
244
286
/// <summary>
@@ -259,6 +301,9 @@ public void Delete()
259
301
LoadXml ( doc =>
260
302
{
261
303
doc . Root . Elements ( "add" ) . Where ( x => x . Attribute ( "alias" ) != null && x . Attribute ( "alias" ) . Value == this . alias ) . Remove ( ) ;
304
+
305
+ return true ;
306
+
262
307
} , true ) ;
263
308
264
309
//raise event
@@ -271,7 +316,7 @@ public void Delete()
271
316
/// <returns>Returns a Application Array</returns>
272
317
public static List < Application > getAll ( )
273
318
{
274
- return Apps ;
319
+ return GetSections ( ) ;
275
320
}
276
321
277
322
/// <summary>
@@ -282,8 +327,32 @@ public static void RegisterIApplications()
282
327
{
283
328
ApplicationStartupHandler . RegisterHandlers ( ) ;
284
329
}
285
-
286
- internal static void LoadXml ( Action < XDocument > callback , bool saveAfterCallback )
330
+
331
+ private static List < Application > ReadFromXmlAndSort ( )
332
+ {
333
+ var tmp = new List < Application > ( ) ;
334
+
335
+ LoadXml ( doc =>
336
+ {
337
+ foreach ( var addElement in doc . Root . Elements ( "add" ) . OrderBy ( x =>
338
+ {
339
+ var sortOrderAttr = x . Attribute ( "sortOrder" ) ;
340
+ return sortOrderAttr != null ? Convert . ToInt32 ( sortOrderAttr . Value ) : 0 ;
341
+ } ) )
342
+ {
343
+ var sortOrderAttr = addElement . Attribute ( "sortOrder" ) ;
344
+ tmp . Add ( new Application ( addElement . Attribute ( "name" ) . Value ,
345
+ addElement . Attribute ( "alias" ) . Value ,
346
+ addElement . Attribute ( "icon" ) . Value ,
347
+ sortOrderAttr != null ? Convert . ToInt32 ( sortOrderAttr . Value ) : 0 ) ) ;
348
+ }
349
+ return false ;
350
+ } , false ) ;
351
+
352
+ return tmp ;
353
+ }
354
+
355
+ internal static void LoadXml ( Func < XDocument , bool > callback , bool saveAfterCallbackIfChanged )
287
356
{
288
357
lock ( Locker )
289
358
{
@@ -293,9 +362,9 @@ internal static void LoadXml(Action<XDocument> callback, bool saveAfterCallback)
293
362
294
363
if ( doc . Root != null )
295
364
{
296
- callback . Invoke ( doc ) ;
365
+ var changed = callback . Invoke ( doc ) ;
297
366
298
- if ( saveAfterCallback )
367
+ if ( saveAfterCallbackIfChanged && changed )
299
368
{
300
369
//ensure the folder is created!
301
370
Directory . CreateDirectory ( Path . GetDirectoryName ( AppConfigFilePath ) ) ;
0 commit comments