1

I have business object classes with several methods that typically follows the pattern:

  • always return an object Result
  • test a boolean pre-condition: IsAllowed (to check for instance if someone can do something (MyAction enum) at a given context (MyContext enum).
  • if the test passes then do something (typically at the database level returning a Result object) otherwise return PermissionDenied() which returns a Result (a simple object with a boolean result set as false and other properties including error messages).

Example:

protected MyContext Context;
protected MyAction Action;

public Result List1(int p1, int p2, int p3, object p4, string p5)
    => IsAllowed(MyContext.Page, MyAction.List) ? DB.List1(p1, p2, p3, p4, p5) : PermissionDenied();

public Result View2(int p1, int p2)
    => IsAllowed(this.Context, this.Action) ? DB.View2(p1, p2) : PermissionDenied();

public Result Edit3(int p1, string p2)
    => IsAllowed(MyContext.Document, MyAction.Edit) ? DB.Edit3(p1, p2) : PermissionDenied();

(...)

I'd like guidance on how to improve and simplify this pattern by having a method that could handle that grant condition. I imagine something like GrantDo(), but how can that method handle/receive the inner method, represented here as ??? what

Public Result GrantDo(??? what, MyContext? ctx=null, MyAction? act=null){
    if (IsAllowed(ctx == null ? this.Context : ctx, act == null ? this.Action : act))
        return what();    
    else
        return PermissionDenied();
}

That way, I imagine the above methods could be re-written like:

public Result List1(int p1, int p2, int p3, object p4, string p5)
    => GrantDo(DB.List1(p1, p2, p3, p4, p5), MyContext.Page, MyAction.List);

public Result View2(int p1, int p2) => GrantDo(DB.View2(p1, p2));

(...)

Thank you for any suggestion and ideas.

To Zé
  • 83
  • 5
  • Well you can use `generic` of T for each parameter. For example see how `Dictionary` is implemented -> https://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs,d3599058f8d79be0 you could set sth like `T` which would be your int and `E` which would be your object and overload method to match your need. – panoskarajohn Jan 07 '20 at 18:44
  • Those are pretty large function signatures. Consider if the parameters should be refactored into a class. –  Jan 07 '20 at 19:03

2 Answers2

1

You just need to pass a function (an Action a Func) and you can simplify the null checks:

public Result GrantDo(Func<Result> action, MyContext? ctx = null, MyAction? act = null) =>
    IsAllowed(context ?? this.Context, act ?? this.Action) 
        ? action() 
        : PermissionDenied();
Alvin Sartor
  • 2,249
  • 4
  • 20
  • 36
1

I finally got this working solution (thank you for your guidance @Alvin).

public Result GrantDo(Func<Result> method, MyAction action, MyContext? context = null)
=> IsAllowed(context ?? Context, action) ? method() : PermissionDenied();

Found more info about the distinct concepts:

Action is a delegate (pointer) to a method, that takes zero, one or more input parameters, but does not return anything.

Func is a delegate (pointer) to a method, that takes zero, one or more input parameters, and returns a value (or reference).

Predicate is a special kind of Func often used for comparisons.

source: Func vs. Action vs. Predicate

To Zé
  • 83
  • 5