0

Problem Statement

I've seen something similar with Remora Results, but I can't use it because of the license. My goal is to instantiate a FooBarResult either from a result or from an error, then pass it to another component. In the other component, it is checked for IsSuccesful and depending on that, either BadRequest(FooBarResult.ErrorMsg) or Ok(FooBarResult.Payload) is executed (just as an example).

I've heard about the Either monad, but I can't really make sense of it. Instead, I tried to do it the simple way with the FooBarResult class. However, it is not really safe to use, since I could still use the object in the wrong way.

public class FooBarResult
{
    public bool IsSuccessful { get; set; }
    public string? Payload { get; set; }
    public string? ErrorMsg { get; set; }

    public FooBarResult(string error)
    {
        IsSuccessful = false;
        ErrorMsg = error;
    }
    
    public FooBarResult(string payload) // that won't work
    {
        IsSuccessful = true;
        Payload = payload;
    }
}

Question

How can one make a result class, that contains payload and error message, depending on the success of the operation generating the result? Can payload maybe even be a generic T object and the error also?

jkbx
  • 414
  • 1
  • 6
  • 21
  • Do you have special purposes for setting _string_ as nullable? String is a reference type and always nullable. – RelativeLayouter May 17 '22 at 12:59
  • 1
    Maybe add static Factorymethods: `FromResult(string)` and `FromError(string)` ? And since you've been considering 3rd parties already: Maybe check [Ardalis/Result](https://github.com/ardalis/Result) – Fildor May 17 '22 at 13:01
  • How about `public FooBarResult(string stringValue, bool isError = false)`? – Mathias R. Jessen May 17 '22 at 13:01
  • _"Can payload maybe even be a generic T object and the error also?"_ - Sure. But for the Error, this doesn't make too much sense, though. What makes a good Error representation, anyway: A descriptive text, maybe a code and maybe a causing exception, right? – Fildor May 17 '22 at 13:06
  • @RelativeLayouter - Since C#8, you can set up the compiler so that reference types are NOT nullable by default so that you don't have to have null checks everywhere – scottdavidwalker May 17 '22 at 13:09
  • If you're not aware, that library just changed (2 days ago) from AGPL to LGPL. I don't know if that makes a difference to whether you can use it or not. – ProgrammingLlama May 17 '22 at 13:10
  • @Fildor The static factory methods are a fantastic idea, but I can still use it "wrong". IsSuccessful can be 'false', yet I can still try to access the payload, which may be null. – jkbx May 17 '22 at 13:18
  • @jkbx I don't think you can 100% avoid that if you want/need to represent both error and success in one abstraction. You _could_ of course implement the getters to throw some Exception if used "incorrectly", so you "fail fast" if there's a usage error in the code. – Fildor May 17 '22 at 13:21

1 Answers1

1

For the "avoid wrong usage" part, you could do something like this (I personally wouldn't, but hey... )

public class FooBarResult
{
     public bool IsSuccessful {get; set;}

     // just doing one to demonstrate
     private string _payload;
     public string Payload
     {
         // InvalidOperationException is the ...
         // "The exception that is thrown when a method call is invalid 
         //  for the object's current state."
         get { 
               if(IsSuccessful) return _payload; 
               throw new InvalidOperationException(); 
             }
         set { _payload = value; }
     }
}

See https://dotnetfiddle.net/fqpSQS

For reference:

InvalidOperationException

Mind : This may get you into trouble as soon as you try to serialize / deserialize.

Fildor
  • 14,510
  • 4
  • 35
  • 67
  • That works for me. Serializing/Deserializing the result won't work, is that what you mean? – jkbx May 17 '22 at 13:58
  • It wouldn't work out of the box, yes. A serializer may try to read all the properties, of course not aware of semantic realtionships between them ( "X is only valid if Y is true ...). – Fildor May 17 '22 at 14:01