• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

HicServices / RDMP / 13493861722

24 Feb 2025 08:35AM UTC coverage: 57.414% (-0.03%) from 57.446%
13493861722

push

github

web-flow
marge v8.4.3 into main (#2145)

* simple db update

* add changelog

* revert csproj

* Fix up some codeql/inspection code issues (#2087)

* Update OverviewModel.cs

Fix up some .Dispose/using issues, make finding most recent load ID more efficient

* LINQ tidying

* CodeQL fixups

* Update Catalogue.cs

Add Hashcode, Equals methods.

* Update Catalogue.cs

Tweak equality semantics for RDMP DB oddities

* Bump actions/setup-dotnet from 4.1.0 to 4.2.0

Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v4.1.0...v4.2.0)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump NUnit.Analyzers from 4.4.0 to 4.5.0

Bumps [NUnit.Analyzers](https://github.com/nunit/nunit.analyzers) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/nunit/nunit.analyzers/releases)
- [Changelog](https://github.com/nunit/nunit.analyzers/blob/master/CHANGES.md)
- [Commits](https://github.com/nunit/nunit.analyzers/compare/4.4.0...4.5.0)

---
updated-dependencies:
- dependency-name: NUnit.Analyzers
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump Minio from 6.0.3 to 6.0.4

Bumps [Minio](https://github.com/minio/minio-dotnet) from 6.0.3 to 6.0.4.
- [Release notes](https://github.com/minio/minio-dotnet/releases)
- [Commits](https://github.com/minio/minio-dotnet/compare/6.0.3...6.0.4)

---
updated-dependencies:
- dependency-name: Minio
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump the aws-sdk group across 1 directory with 4 updates

Bumps the aws-sdk grou... (continued)

11358 of 21338 branches covered (53.23%)

Branch coverage included in aggregate %.

1116 of 1482 new or added lines in 50 files covered. (75.3%)

19 existing lines in 10 files now uncovered.

32283 of 54673 relevant lines covered (59.05%)

17450.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

53.81
/Rdmp.Core/CommandExecution/BasicCommandExecution.cs
1
// Copyright (c) The University of Dundee 2018-2019
2
// This file is part of the Research Data Management Platform (RDMP).
3
// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
4
// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
5
// You should have received a copy of the GNU General Public License along with RDMP. If not, see <https://www.gnu.org/licenses/>.
6

7
using System;
8
using System.Collections.Generic;
9
using System.Linq;
10
using System.Reflection;
11
using System.Threading;
12
using System.Threading.Tasks;
13
using FAnsi.Discovery;
14
using Rdmp.Core.CommandExecution.AtomicCommands;
15
using Rdmp.Core.CommandLine.Interactive.Picking;
16
using Rdmp.Core.Curation.Data;
17
using Rdmp.Core.MapsDirectlyToDatabaseTable;
18
using Rdmp.Core.MapsDirectlyToDatabaseTable.Revertable;
19
using Rdmp.Core.ReusableLibraryCode;
20
using Rdmp.Core.ReusableLibraryCode.Checks;
21
using Rdmp.Core.ReusableLibraryCode.Comments;
22
using Rdmp.Core.ReusableLibraryCode.Icons.IconProvision;
23
using SixLabors.ImageSharp;
24
using SixLabors.ImageSharp.PixelFormats;
25

26
namespace Rdmp.Core.CommandExecution;
27

28
/// <summary>
29
/// Basic implementation of ICommandExecution ensures that if a command is marked IsImpossible then it cannot be run.  Call SetImpossible to render your command
30
/// un runnable with the given arguments.  You cannot make an IsImpossible command Possible again (therefore you should probably make this discision in your
31
/// constructor).  Override Execute to provide the implementation logic of your command but make sure to leave the base.Execute() call in first to ensure
32
/// IsImpossible is respected in the unlikely event that some code or user attempts to execute an impossible command.
33
/// 
34
/// <para>Override GetCommandHelp and GetCommandName to change the presentation layer of the command (if applicable).</para>
35
/// </summary>
36
public abstract class BasicCommandExecution : IAtomicCommand
37
{
38
    /// <summary>
39
    /// The last command executed by RDMP (will be null at start)
40
    /// </summary>
41
    public static IAtomicCommand LastCommand;
42

43
    public IBasicActivateItems BasicActivator { get; }
976✔
44

45
    public bool IsImpossible { get; private set; }
2,928✔
46
    public string ReasonCommandImpossible { get; private set; }
570✔
47
    public string OverrideCommandName { get; set; }
2,828✔
48

49
    public Image<Rgba32> OverrideIcon { get; set; }
1,536✔
50

51
    /// <summary>
52
    /// Set to true to suppress the <see cref="Publish(IMapsDirectlyToDatabaseTable)"/> method.  Only use if you are running multiple commands one after the other and don't want to wait for state updates
53
    /// </summary>
54
    public bool NoPublish { get; set; }
236✔
55

56
    /// <summary>
57
    /// The prefix that must appear on all Types derived from <see cref="BasicCommandExecution"/> in order to be rendered correctly in
58
    /// menus, called from the command line etc.
59
    /// </summary>
60
    public const string ExecuteCommandPrefix = "ExecuteCommand";
61

62
    /// <summary>
63
    /// True to add "..." to the end of the <see cref="ICommandExecution.GetCommandName"/>
64
    /// </summary>
65
    protected bool UseTripleDotSuffix { get; set; }
928✔
66

67

68
    /// <summary>
69
    /// When presenting the command in a hierarchical presentation should it be under a subheading
70
    /// (e.g. in a context menu).  Null if not
71
    /// </summary>
72
    public string SuggestedCategory { get; set; }
574✔
73

74
    /// <summary>
75
    /// Key which should result in this command being fired e.g. "F2"
76
    /// </summary>
77
    public string SuggestedShortcut { get; set; }
10✔
78

79
    /// <summary>
80
    /// True to require Ctrl key to be pressed when <see cref="SuggestedShortcut"/> is entered
81
    /// </summary>
82
    public bool Ctrl { get; set; }
10✔
83

84
    /// <inheritdoc/>
85
    public float Weight { get; set; }
978✔
86

87
    protected void SetImpossibleIfReadonly(IMightBeReadOnly m)
88
    {
89
        if (m?.ShouldBeReadOnly(out var reason) == true)
18!
90
            SetImpossible(
×
NEW
91
                $"{(m is IContainer ? "Container" : '\'' + m.ToString() + '\'')} is readonly because:{reason}");
×
92
    }
18✔
93

94
    public BasicCommandExecution()
78✔
95
    {
96
    }
78✔
97

98
    public BasicCommandExecution(IBasicActivateItems basicActivator)
1,640✔
99
    {
100
        BasicActivator = basicActivator;
1,640✔
101
    }
1,640✔
102

103
    public virtual void Execute()
104
    {
105
        LastCommand = this;
506✔
106

107
        if (IsImpossible)
506✔
108
            throw new ImpossibleCommandException(this, ReasonCommandImpossible);
8✔
109
    }
498✔
110

111
    /// <summary>
112
    /// Returns human readable name for the command (This includes spaces and may have triple dots at the end, see <see cref="UseTripleDotSuffix"/>).
113
    /// For a programmatic name e.g. if user is to type command name into a CLI then use the static method <see cref="GetCommandName(string)"/>
114
    /// </summary>
115
    /// <returns></returns>
116
    public virtual string GetCommandName()
117
    {
118
        if (!string.IsNullOrWhiteSpace(OverrideCommandName))
858✔
119
            return UseTripleDotSuffix ? $"{OverrideCommandName}..." : OverrideCommandName;
584!
120

121
        var name = GetType().Name;
274✔
122
        var adjusted = name.Replace(ExecuteCommandPrefix, "");
274✔
123

124
        if (UseTripleDotSuffix)
274✔
125
            adjusted += "...";
12✔
126

127
        return UsefulStuff.PascalCaseStringToHumanReadable(adjusted);
274✔
128
    }
129

130
    /// <summary>
131
    /// Returns the name of the command in programmatic format e.g. no spaces no triple dot suffix etc.
132
    /// </summary>
133
    /// <param name="typeName"></param>
134
    /// <returns></returns>
135
    public static string GetCommandName(string typeName) => typeName.Replace(ExecuteCommandPrefix, "");
10,824✔
136

137
    /// <summary>
138
    /// Returns the name of the command in programmatic format e.g. no spaces no triple dot suffix etc.
139
    /// </summary>
140
    /// <returns></returns>
141
    public static string GetCommandName<T>() where T : BasicCommandExecution => GetCommandName(typeof(T).Name);
×
142

143
    public virtual string GetCommandHelp() => string.Empty;
212✔
144

145
    public virtual Image<Rgba32> GetImage(IIconProvider iconProvider) => OverrideIcon;
574✔
146

147
    /// <summary>
148
    /// disables the command because of the given reason.  This will result in grayed out menu items, crashes when executed programmatically etc.
149
    /// </summary>
150
    /// <param name="reason"></param>
151
    protected void SetImpossible(string reason)
152
    {
153
        IsImpossible = true;
278✔
154
        ReasonCommandImpossible = reason;
278✔
155
    }
278✔
156

157
    /// <summary>
158
    /// disables the command because of the given reason.  This will result in grayed out menu items, crashes when executed programmatically etc.
159
    /// This overload calls string.Format with the <paramref name="objects"/>
160
    /// </summary>
161
    /// /// <param name="reason"></param>
162
    /// <param name="objects">Objects to pass to string.Format</param>
163
    protected void SetImpossible(string reason, params object[] objects)
164
    {
165
        IsImpossible = true;
×
166
        ReasonCommandImpossible = string.Format(reason, objects);
×
167
    }
×
168

169
    /// <summary>
170
    /// Resets the IsImpossible status of the command
171
    /// </summary>
172
    protected void ResetImpossibleness()
173
    {
174
        IsImpossible = false;
×
175
        ReasonCommandImpossible = null;
×
176
    }
×
177

178
    /// <summary>
179
    /// Offers the user a binary choice and returns true if they accept it.  This method is blocking.
180
    /// </summary>
181
    /// <param name="text">The question to pose</param>
182
    /// <param name="caption"></param>
183
    /// <returns></returns>
184
    protected bool YesNo(string text, string caption) => BasicActivator.YesNo(text, caption);
6✔
185

186
    protected virtual void Publish(IMapsDirectlyToDatabaseTable o)
187
    {
188
        if (NoPublish)
218✔
189
            return;
2✔
190

191
        if (o is DatabaseEntity d)
216✔
192
            BasicActivator.Publish(d);
216✔
193
    }
216✔
194

195
    /// <summary>
196
    /// Reports a low visibility error to the <see cref="IBasicActivateItems.GlobalErrorCheckNotifier"/>.  Throws <paramref name="ex"/>
197
    /// with <paramref name="msg"/> if no global errors handler is registered
198
    /// </summary>
199
    /// <param name="msg"></param>
200
    /// <param name="ex"></param>
201
    protected void GlobalError(string msg, Exception ex)
202
    {
203
        if (BasicActivator?.GlobalErrorCheckNotifier == null)
×
204
            throw new Exception(msg, ex);
×
205

206
        BasicActivator.GlobalErrorCheckNotifier.OnCheckPerformed(new CheckEventArgs(msg, CheckResult.Fail, ex));
×
207
    }
×
208

209
    /// <summary>
210
    /// Displays the given message to the user
211
    /// </summary>
212
    /// <param name="message"></param>
213
    protected void Show(string message)
214
    {
215
        BasicActivator.Show(message);
4✔
216
    }
4✔
217

218
    /// <summary>
219
    /// Displays the given message to the user with the given title
220
    /// </summary>
221
    /// <param name="title"></param>
222
    /// <param name="message"></param>
223
    protected void Show(string title, string message)
224
    {
225
        BasicActivator.Show(title, message);
×
226
    }
×
227

228
    /// <summary>
229
    /// Displays the given message to the user, calling String.Format
230
    /// </summary>
231
    /// <param name="message"></param>
232
    /// <param name="objects">Objects to use for {0},{1} etc tokens in <paramref name="message"/></param>
233
    protected void Show(string message, params object[] objects)
234
    {
235
        BasicActivator.Show(string.Format(message, objects));
×
236
    }
×
237

238

239
    /// <summary>
240
    /// Prompts the user to type in some text (up to a maximum length).  Returns true if they supplied some text or false if they didn't or it was blank/cancelled etc
241
    /// </summary>
242
    /// <param name="header"></param>
243
    /// <param name="prompt"></param>
244
    /// <param name="maxLength"></param>
245
    /// <param name="initialText"></param>
246
    /// <param name="text"></param>
247
    /// <param name="requireSaneHeaderText"></param>
248
    /// <returns></returns>
249
    protected bool TypeText(string header, string prompt, int maxLength, string initialText, out string text,
250
        bool requireSaneHeaderText = false) =>
251
        BasicActivator.TypeText(header, prompt, maxLength, initialText, out text, requireSaneHeaderText);
8✔
252

253
    /// <inheritdoc cref="TypeText(string, string, int, string, out string,bool)"/>
254
    protected bool TypeText(string header, string prompt, out string text) =>
255
        TypeText(header, prompt, 500, null, out text);
×
256

257
    /// <inheritdoc cref="IBasicActivateItems.ShowException"/>
258
    protected void ShowException(string message, Exception exception)
259
    {
260
        BasicActivator.ShowException(message, exception);
×
261
    }
×
262

263
    /// <summary>
264
    /// Prompts user to select 1 of the objects of type T in the list you provide
265
    /// </summary>
266
    /// <typeparam name="T"></typeparam>
267
    /// <param name="availableObjects"></param>
268
    /// <param name="initialSearchText"></param>
269
    /// <param name="allowAutoSelect">True to silently auto select the object if there are only 1 <paramref name="availableObjects"/></param>
270
    /// <returns></returns>
271
    protected T SelectOne<T>(IList<T> availableObjects, string initialSearchText = null, bool allowAutoSelect = false)
272
        where T : DatabaseEntity =>
273
        SelectOne(new DialogArgs
×
274
        {
×
275
            InitialSearchText = initialSearchText,
×
276
            AllowAutoSelect = allowAutoSelect
×
277
        }, availableObjects, out var selected)
×
278
            ? selected
×
279
            : null;
×
280

281
    /// <summary>
282
    /// Prompts user to select 1 of the objects of type T in the list you provide
283
    /// </summary>
284
    /// <typeparam name="T"></typeparam>
285
    /// <param name="args"></param>
286
    /// <param name="availableObjects"></param>
287
    /// <returns></returns>
288
    protected T SelectOne<T>(DialogArgs args, IList<T> availableObjects) where T : DatabaseEntity =>
289
        SelectOne(args, availableObjects, out var selected) ? selected : null;
×
290

291
    /// <summary>
292
    /// Prompts user to select 1 object of type T from all the ones stored in the repository provided
293
    /// </summary>
294
    /// <typeparam name="T"></typeparam>
295
    /// <param name="repository"></param>
296
    /// <param name="initialSearchText"></param>
297
    /// <param name="allowAutoSelect">True to silently auto select the object if there are only 1 compatible object in the <paramref name="repository"/></param>
298
    /// <returns></returns>
299
    protected T SelectOne<T>(IRepository repository, string initialSearchText = null, bool allowAutoSelect = false)
300
        where T : DatabaseEntity =>
301
        SelectOne(new DialogArgs
×
302
        {
×
303
            InitialSearchText = initialSearchText,
×
304
            AllowAutoSelect = allowAutoSelect
×
305
        }, repository.GetAllObjects<T>().ToList(), out var answer)
×
306
            ? answer
×
307
            : null;
×
308

309
    /// <summary>
310
    /// Prompts user to select 1 of the objects of type T from all the ones stored in the repository provided, returns true if they made a non null selection
311
    /// </summary>
312
    /// <typeparam name="T"></typeparam>
313
    /// <param name="repository"></param>
314
    /// <param name="selected"></param>
315
    /// <param name="initialSearchText"></param>
316
    /// <param name="allowAutoSelect">True to silently auto select the object if there are only 1 compatible object in the <paramref name="repository"/></param>
317
    /// <returns></returns>
318
    protected bool SelectOne<T>(IRepository repository, out T selected, string initialSearchText = null,
319
        bool allowAutoSelect = false) where T : DatabaseEntity =>
320
        SelectOne(new DialogArgs
×
321
        {
×
322
            InitialSearchText = initialSearchText,
×
323
            AllowAutoSelect = allowAutoSelect
×
324
        }, repository.GetAllObjects<T>().ToList(), out selected);
×
325

326
    /// <summary>
327
    /// Prompts user to select 1 of the objects of type T in the list you provide, returns true if they made a non null selection
328
    /// </summary>
329
    /// <typeparam name="T"></typeparam>
330
    /// <param name="availableObjects"></param>
331
    /// <param name="selected"></param>
332
    /// <param name="initialSearchText"></param>
333
    /// <param name="allowAutoSelect">True to silently auto select the object if there are only 1 <paramref name="availableObjects"/></param>
334
    /// <returns></returns>
335
    protected bool SelectOne<T>(IList<T> availableObjects, out T selected, string initialSearchText = null,
336
        bool allowAutoSelect = false) where T : DatabaseEntity =>
337
        SelectOne(new DialogArgs
×
338
        {
×
339
            InitialSearchText = initialSearchText,
×
340
            AllowAutoSelect = allowAutoSelect
×
341
        }, availableObjects, out selected);
×
342

343
    /// <summary>
344
    /// Prompts user to select 1 of the objects of type T in the list you provide, returns true if they made a non null selection
345
    /// </summary>
346
    /// <typeparam name="T"></typeparam>
347
    /// <param name="args"></param>
348
    /// <param name="availableObjects"></param>
349
    /// <param name="selected"></param>
350
    /// <returns></returns>
351
    protected bool SelectOne<T>(DialogArgs args, IList<T> availableObjects, out T selected) where T : DatabaseEntity
352
    {
353
        selected = (T)BasicActivator.SelectOne(args, availableObjects.ToArray());
×
354
        return selected != null;
×
355
    }
356

357
    /// <summary>
358
    /// Prompts user to select 1 of the objects of type T from the objects existing in <paramref name="repository"/>, returns true if they made a non null selection
359
    /// </summary>
360
    /// <typeparam name="T"></typeparam>
361
    /// <param name="args"></param>
362
    /// <param name="repository"></param>
363
    /// <param name="selected"></param>
364
    /// <returns></returns>
365
    protected bool SelectOne<T>(DialogArgs args, IRepository repository, out T selected) where T : DatabaseEntity
366
    {
367
        selected = (T)BasicActivator.SelectOne(args, repository.GetAllObjects<T>().ToArray());
×
368
        return selected != null;
×
369
    }
370

371
    protected bool SelectMany<T>(T[] available, out T[] selected, string initialSearchText = null)
372
        where T : DatabaseEntity
373
    {
374
        selected = BasicActivator.SelectMany("Select Objects", typeof(T), available, initialSearchText)?.Cast<T>()
×
375
            ?.ToArray();
×
376
        return selected != null && selected.Any();
×
377
    }
378

379
    protected bool SelectMany<T>(DialogArgs dialogArgs, T[] available, out T[] selected) where T : DatabaseEntity
380
    {
381
        selected = BasicActivator.SelectMany(dialogArgs, typeof(T), available)?.Cast<T>()?.ToArray();
×
382
        return selected != null && (dialogArgs.AllowSelectingNull || selected.Any());
×
383
    }
384

385
    protected void Wait(string title, Task task, CancellationTokenSource cts)
386
    {
387
        BasicActivator.Wait(title, task, cts);
4✔
388
    }
4✔
389

390
    protected void Emphasise(object o, int expansionDepth = 0)
391
    {
392
        BasicActivator.RequestItemEmphasis(this, new EmphasiseRequest(o, expansionDepth));
102✔
393
    }
102✔
394

395
    protected DiscoveredDatabase SelectDatabase(bool allowDatabaseCreation, string taskDescription) =>
396
        BasicActivator.SelectDatabase(allowDatabaseCreation, taskDescription);
×
397

398
    protected DiscoveredTable SelectTable(bool allowDatabaseCreation, string taskDescription) =>
399
        BasicActivator.SelectTable(allowDatabaseCreation, taskDescription);
×
400

401
    protected virtual void Activate(DatabaseEntity o)
402
    {
403
        BasicActivator.Activate(o);
20✔
404
    }
20✔
405

406
    /// <summary>
407
    /// Executes a given constructor (identified by <paramref name="constructorSelector"/>) by reading values out of the picker
408
    /// (or prompting the user if <paramref name="pickerArgsIfAny"/> is null)
409
    /// </summary>
410
    /// <param name="toConstruct">The Type you want to construct</param>
411
    /// <param name="constructorSelector">Selects which constructor on <paramref name="toConstruct"/> you want to invoke</param>
412
    /// <param name="pickerArgsIfAny"></param>
413
    /// <returns></returns>
414
    protected object Construct(Type toConstruct, Func<ConstructorInfo> constructorSelector,
415
        IEnumerable<CommandLineObjectPickerArgumentValue> pickerArgsIfAny = null)
416
    {
417
        var invoker = new CommandInvoker(BasicActivator);
32✔
418

419
        var constructor = constructorSelector();
32✔
420

421
        var constructorValues = new List<object>();
32✔
422

423
        var pickerEnumerator = pickerArgsIfAny?.GetEnumerator();
32!
424

425
        foreach (var parameterInfo in constructor.GetParameters())
210✔
426
        {
427
            var required = new RequiredArgument(parameterInfo);
74✔
428
            var parameterDelegate = invoker.GetDelegate(required);
74✔
429

430
            if (parameterDelegate.IsAuto)
74✔
431
            {
432
                constructorValues.Add(parameterDelegate.Run(required));
32✔
433
            }
434
            else
435
            {
436
                //it's not auto
437
                if (pickerEnumerator != null)
42!
438
                {
439
                    pickerEnumerator.MoveNext();
42✔
440

441
                    if (pickerEnumerator.Current == null)
42✔
442
                        throw new ArgumentException(
2✔
443
                            $"Value needed for parameter '{required.Name}' (of type '{required.Type}')");
2✔
444

445
                    //construct with the picker arguments
446
                    if (!pickerEnumerator.Current.HasValueOfType(required.Type))
40!
447
                        throw new NotSupportedException(
×
448
                            $"Argument '{pickerEnumerator.Current.RawValue}' could not be converted to required Type '{required.Type}' for argument {required.Name}");
×
449

450
                    //it is a valid object yay!
451
                    constructorValues.Add(pickerEnumerator.Current.GetValueForParameterOfType(required.Type));
40✔
452
                }
453
                else
454
                {
455
                    //construct by prompting user for the values
456
                    constructorValues.Add(invoker.GetValueForParameterOfType(parameterInfo));
×
457
                }
458
            }
459
        }
460

461
        pickerEnumerator?.Dispose();
30✔
462

463
        return constructor.Invoke(constructorValues.ToArray());
30✔
464
    }
465

466
    /// <summary>
467
    /// Runs checks on the <paramref name="checkable"/> and calls <see cref="SetImpossible(string)"/> if there are any failures
468
    /// </summary>
469
    /// <param name="checkable"></param>
470
    protected void SetImpossibleIfFailsChecks(ICheckable checkable)
471
    {
472
        try
473
        {
474
            checkable.Check(ThrowImmediatelyCheckNotifier.Quiet);
14✔
475
        }
8✔
476
        catch (Exception e)
6✔
477
        {
478
            SetImpossible(ExceptionHelper.ExceptionToListOfInnerMessages(e));
6✔
479
        }
6✔
480
    }
14✔
481

482
    public override string ToString() => GetCommandName();
18✔
483

484
    protected static CommentStore CreateCommentStore()
485
    {
486
        var help = new CommentStore();
8✔
487
        help.ReadComments(Environment.CurrentDirectory);
8✔
488
        return help;
8✔
489
    }
490

491
    /// <summary>
492
    /// Returns true if the supplied command Type is known (directly or via alias)
493
    /// as <paramref name="name"/>
494
    /// </summary>
495
    public static bool HasCommandNameOrAlias(Type commandType, string name)
496
    {
497
        return
×
498
            commandType.Name.Equals(ExecuteCommandPrefix + name, StringComparison.InvariantCultureIgnoreCase)
×
499
            ||
×
500
            commandType.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)
×
501
            ||
×
502
            commandType.GetCustomAttributes<AliasAttribute>(false)
×
503
                .Any(a => a.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
×
504
    }
505

506

507
    /// <summary>
508
    /// <para>
509
    /// Performs the <paramref name="toRun"/> action within a <see cref="Commit"/> (if
510
    /// commits are supported by platform).  Returns true if no commit was used or commit
511
    /// was completed successfully.  Returns false if commit was abandoned (e.g. by user cancelling).
512
    /// </para>
513
    /// <remarks> If commit is abandoned then <paramref name="trackObjects"/> will all be reverted
514
    /// to database state (i.e. local changes discarded)</remarks>
515
    /// </summary>
516
    /// <param name="toRun"></param>
517
    /// <param name="description"></param>
518
    /// <param name="trackObjects">Objects to do change tracking on within the transaction</param>
519
    /// <returns></returns>
520
    protected bool ExecuteWithCommit(Action toRun, string description,
521
        params IMapsDirectlyToDatabaseTable[] trackObjects)
522
    {
523
        CommitInProgress commit = null;
12✔
524
        var revert = false;
12✔
525

526
        if (BasicActivator.UseCommits())
12✔
527
            commit = new CommitInProgress(BasicActivator.RepositoryLocator, new CommitInProgressSettings(trackObjects)
4✔
528
            {
4✔
529
                UseTransactions = true,
4✔
530
                Description = description
4✔
531
            });
4✔
532

533
        try
534
        {
535
            toRun();
12✔
536

537
            // if user cancels transaction
538
            if (commit != null && commit.TryFinish(BasicActivator) == null) revert = true;
10!
539
        }
10✔
540
        finally
541
        {
542
            commit?.Dispose();
12✔
543
        }
12✔
544

545
        if (revert)
10!
546
            foreach (var o in trackObjects)
×
547
                if (o is IRevertable re)
×
548
                    re.RevertToDatabaseState();
×
549

550
        return !revert;
10✔
551
    }
552
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc