C
C#β€’14mo ago
Shinigami

βœ… why IActionResult and why not ActionResult?

It's recommended to use IActionResult as the return type when we are returning any action method, but why are we using the interface? Why not just ActionResult? I've also seen while returning a dictionary we use IDictionary as the return type, why is it like this? I know interfaces contain definition of methods which has to be implemented when inherited by a class.
72 Replies
Pobiega
Pobiegaβ€’14mo ago
When a controller action returns an object directly, it will only be able to return 200 OK, 401 Unauthorized or 500 Error (unless you have some exception middlware, but lets assume not for now) If you want more control over the return code, thats when ActionResult and IActionResult come in it allows you to use the helper methods on the controller to change the status code, like Ok(value); or BadRequest("error message"); etc IActionResult is the untyped version of ActionResult and should only be used with MVC if this is an API, always use ActionResult<T>
Shinigami
ShinigamiOPβ€’14mo ago
It's not just the status code right? What I've learnt is if i had to return multiple types of data types (not at the same time) like contentresult or fileresult we use IActionResult since it's the interface which is implemented by all the action results But still, why do we keep interface as the return type instead of a class?
Pobiega
Pobiegaβ€’14mo ago
Why not? But yeah, if you need FileResult you'd probably also use that as your return type. I've used FileResult a grand total of maybe once or twice in my entire 20 years of C# πŸ˜›
Shinigami
ShinigamiOPβ€’14mo ago
Usually if I'd want to return a string, Don't we just Public string method1(){ Return "rjdjr"; } But not Public Istring method1(){ Return "rjdjr"; } Right?
Pobiega
Pobiegaβ€’14mo ago
Correct, but string is a very bad example here πŸ™‚
Pobiega
Pobiegaβ€’14mo ago
No description
Pobiega
Pobiegaβ€’14mo ago
all these implement IActionResult
Shinigami
ShinigamiOPβ€’14mo ago
Right
Pobiega
Pobiegaβ€’14mo ago
that means they are all considered valid returns for a method that says it returns ActionResult
Shinigami
ShinigamiOPβ€’14mo ago
Why i said string because i was comparing with dictionary
Pobiega
Pobiegaβ€’14mo ago
If you want to return a dictionary, you either return the dictionary, or ActionResult<Dictionary<TKey, TValue>> depends on if you need to be able to change the status code or not
Shinigami
ShinigamiOPβ€’14mo ago
Ahh! Apologies maybe i should have created another post about string and IDictionary
Pobiega
Pobiegaβ€’14mo ago
we're still talking about controllers and actions, right? :p
Shinigami
ShinigamiOPβ€’14mo ago
Yes, kinda. But what i want to know is in general why do we have interfaces as return types instead of class be it mvc or in general
Pobiega
Pobiegaβ€’14mo ago
because sometimes you dont know all the implementations what if someone wants to be able to add their own implementation to your library? or you add something else down the line interfaces allow loose coupling
Shinigami
ShinigamiOPβ€’14mo ago
I saw somewhere with the return type of IDictionary instead of Dictionary and here in MVC it's IActionResult instead of ActionResult, so why interface instead of class, i was curious about that. You mean, as long as i inherit the interface, i can create a class basically and that can be used by someone else by implementing the interface of the class? That's the crux of it right? For example in case of MVC, i can create a maybe Yamlresult class which inherits the IActionResult interface.
Pobiega
Pobiegaβ€’14mo ago
yes, exactly
Shinigami
ShinigamiOPβ€’14mo ago
And someone can return the YamlResult using IActionResult as the return type
Pobiega
Pobiegaβ€’14mo ago
using interfaces in methods as both inputs and outputs allow you to very clearly specify whats important, and allow future code changes to be more smooth
Shinigami
ShinigamiOPβ€’14mo ago
I understood, but what's the scene with us never using Istring or Iint as return type?
Pobiega
Pobiegaβ€’14mo ago
those interfaces dont exist πŸ™‚ interfaces are for complex types, not primitives
Shinigami
ShinigamiOPβ€’14mo ago
Ohh primitive types Right right Since dictionary isn't primitive, we use IDictionary IList etc...
Pobiega
Pobiegaβ€’14mo ago
yeah. If it makes sense. You don't have to return the interface in some cases you want to, in some cases you don't. most BCL (base class library) classes also implmenent several interfaces a List<T> implements not only IList<T> but also IReadOnlyList<T> which means we can have a list, but return it as readonly, if we don't intend the caller to mutate it it shows our intentions better
Shinigami
ShinigamiOPβ€’14mo ago
Ohhhh! So the return type can be Ireadonlylist<t> and we return the list And it means like you said, it's only read only
Pobiega
Pobiegaβ€’14mo ago
yup well, it behaves as if it was readonly you could "get around it" if you really wanted, but thats hacky and bad
Shinigami
ShinigamiOPβ€’14mo ago
I seee, i got it why we use interface as return type. Thanks @Pobiega
Pobiega
Pobiegaβ€’14mo ago
Like, look at this
public class Program
{
public static void Main()
{
var strings = GetStrings();

foreach (var s in strings)
{
Console.WriteLine(s);
}
}

public static IReadOnlyList<string> GetStrings()
{
return new List<string>
{
"Hello",
"world"
};
}
}
public class Program
{
public static void Main()
{
var strings = GetStrings();

foreach (var s in strings)
{
Console.WriteLine(s);
}
}

public static IReadOnlyList<string> GetStrings()
{
return new List<string>
{
"Hello",
"world"
};
}
}
because strings will be IReadOnlyList, I as a developer can see that "oh im not supposed to modify this"
Shinigami
ShinigamiOPβ€’14mo ago
Right right, it makes more sense now.
Pobiega
Pobiegaβ€’14mo ago
however
public class Program
{
public static void Main()
{
var strings = GetStrings();

var mutableStrings = (List<string>)strings;

mutableStrings.Add("Muahahah!");

foreach (var s in strings)
{
Console.WriteLine(s);
}
}

public static IReadOnlyList<string> GetStrings()
{
return new List<string>
{
"Hello",
"world"
};
}
}
public class Program
{
public static void Main()
{
var strings = GetStrings();

var mutableStrings = (List<string>)strings;

mutableStrings.Add("Muahahah!");

foreach (var s in strings)
{
Console.WriteLine(s);
}
}

public static IReadOnlyList<string> GetStrings()
{
return new List<string>
{
"Hello",
"world"
};
}
}
this works, because I KNOW its actually a List<string> this would crash horribly if it wasn't
Shinigami
ShinigamiOPβ€’14mo ago
Also while implementing IActionResult basically says that the return type can be any of the action results?
Pobiega
Pobiegaβ€’14mo ago
yes this is why we usually avoid returning that its too broad in MVC, you dont have much of an option so thats fine but in an API, you should be as specific as you can be, so ActionResult<T> is better
Shinigami
ShinigamiOPβ€’14mo ago
You're basically converting the read-only list to a normal list and adding a string but if you didn't know that it is a list beforehand it would fail right?
Pobiega
Pobiegaβ€’14mo ago
it lets tools like swagger inspect the return type (for the happy case) automatically correct
Shinigami
ShinigamiOPβ€’14mo ago
Gotcha, I'll try using this In general, not considering mvc, using interface as return type helps in readability of the code
Pobiega
Pobiegaβ€’14mo ago
helps in showing intentions, while also allowing more easy... adaptions/additions if a system uses interfaces, I can easily add a new type to that system without modifying any code within the system itself I just make my new type have the correct interfaces and implement them properly
Florian Voß
Florian VoΓŸβ€’14mo ago
so when we don't plan to return a status code other than those three, then we return the object directly? thats nice to know
Pobiega
Pobiegaβ€’14mo ago
yeah, or if you use an exception handling middlware I'm not personally a fan of using exceptions to handle http status codes, so I don't use it for that.. so most of my endpoints return ActionResult<T>
Florian Voß
Florian VoΓŸβ€’14mo ago
but don't most of your endpoints only return 200, 401, and 500? so return object directly, no?
Pobiega
Pobiegaβ€’14mo ago
yes and no. I like returning bad request on bad requests.
Florian Voß
Florian VoΓŸβ€’14mo ago
I see
Pobiega
Pobiegaβ€’14mo ago
and since I'm a huge fan of result types, I handle the mapping from a negative result => http error in the endpoint
Florian Voß
Florian VoΓŸβ€’14mo ago
would you mind sharing a sample of that anywhere? here or in new thread or DMs
K.
K.β€’14mo ago
I guess its about standards and how .NET defines the standards, nothing more. No one limits you to return any object you want but you will not follow the standard, not attaching the status code etc.
Florian Voß
Florian VoΓŸβ€’14mo ago
I'm not sure how to approach this, would we use an enum for our result type and have a switch statement in the endpoint?
K.
K.β€’14mo ago
I think he probably means the Result type library and then matching the errors to the status codes properly.
Pobiega
Pobiegaβ€’14mo ago
yes
K.
K.β€’14mo ago
Nick had a video on it. Give me a sec
Florian Voß
Florian VoΓŸβ€’14mo ago
that would be great πŸ™‚
Pobiega
Pobiegaβ€’14mo ago
its a functional programming concept originally, and since we dont have true DUs its not perfect in C#
K.
K.β€’14mo ago
Nick Chapsas
YouTube
What’s the Result Type Everyone Is Using in .NET?
Check out my courses: https://dometrain.com Become a Patreon and get source code access: https://www.patreon.com/nickchapsas Hello, everybody, I'm Nick, and in this video, I will introduce you to the Result type in .NET. It might not be a native .NET type but it exists in tons of libraries and codebases so in this video I'll help you understand...
Pobiega
Pobiegaβ€’14mo ago
but it has too many upsides to ignore for me πŸ˜›
K.
K.β€’14mo ago
Its about discriminated unions being possibe to be used in C# in a hacky way - the library is however pretty polished and you wont notice it.
Florian Voß
Florian VoΓŸβ€’14mo ago
no clue what that is will have a look
K.
K.β€’14mo ago
Discriminated unions are in typescript defined like this: const myType: string | number = "some string but could have been a number as well"; myType = 5;
Florian Voß
Florian VoΓŸβ€’14mo ago
oh I did know it just forgot the name
K.
K.β€’14mo ago
the idea is that you can return you base default result which is T or you return some other which is another class which represents an error. Then you don't need to rethrow the errors but rather you expect always the posibility that its the result or some possible error results The advantage is that you will have it in a typesafe way and it will not be possible that you dont cache an error, cause you have to match all possible cases.
Florian Voß
Florian VoΓŸβ€’14mo ago
great idea
K.
K.β€’14mo ago
also its more efficient then throwing errors cause throwing errors is expencive
Florian Voß
Florian VoΓŸβ€’14mo ago
gotta drive to work now sadge
K.
K.β€’14mo ago
ok im on vacation πŸ˜„
Pobiega
Pobiegaβ€’14mo ago
the big "split" in result types in C# is wether you are explicit about all the error types a method can throw or not
K.
K.β€’14mo ago
yea expecting is the right word
Pobiega
Pobiegaβ€’14mo ago
Compare OneOf (explicit) to Remora.Results(not explicit) The downside of being explicit is that your type declarations can get pretty wacky
K.
K.β€’14mo ago
in my oppinion in result type you dont expect as you do expect then 'throw' cause its typesafe also true
Pobiega
Pobiegaβ€’14mo ago
public Result<Movie, ValidationFailed, MovieNotFound, DatabaseError, DannyDevitoNotInMovie> GetByName(string name) is a fairly annoying method declaration πŸ˜„
K.
K.β€’14mo ago
idk I like it πŸ˜„ in typescript you can use string as type directly making it quicker to write
Pobiega
Pobiegaβ€’14mo ago
TS has true DUs thou. C# does not. we can't emulate it perfectly
K.
K.β€’14mo ago
I have been using it so far from time to time to define cases where I have one object but it has different states, so its not null or T but rather 'LOADING' | T | 'UNDEIFNED'. Thats some cases where I would use it i think in C# its still pretty good Pobiega, we have been talking on discord once about rust, right?
Pobiega
Pobiegaβ€’14mo ago
Its good enough to use for sure, but if you've done TS or F# etc it still not perfect πŸ˜› maybe? idk But lets not go offtopic in here
K.
K.β€’14mo ago
I miss the true functional stuff from F# in C# still, I hope they make progress there
Pobiega
Pobiegaβ€’14mo ago
This thread is ready to be closed.
K.
K.β€’14mo ago
yea, true.
Want results from more Discord servers?
Add your server