C
C#β€’3y ago
ero

❔ Creating a backend in .NET

Hi! I have absolutely zero knowledge about web development, databases, or making any kinds of web requests. Basically, I'm a complete noob when it comes to literally anything web-related (and I know databases aren't necessarily web, but you get the idea). I would like to build a backend for a website idea that's already pretty big on paper. I have a lot of ideas, have a lot of thoughts about how things should work, I just can't create it. I was wondering if there are any very beginner friendly, modern tutorials on this stuff? Preferrably videos, as I have a hard time focussing on reading a lot. If I should go into any more detail, let me know.
770 Replies
Yawnder
Yawnderβ€’3y ago
@Ero Break your problem pieces by pieces, as you do with everything. Ignore the database aspect of it. Start with the API project template (the one that has a Weather controller endpoint) and see how you can modify it to understand. Something important for you to understand is the life cycle of an http request. How all the middleware are linked together. https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-7.0
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
Pobiega
Pobiegaβ€’3y ago
What kind of frontend are you making for this? If its a JS based one like React, Vue, Svelte or whatever they are called these days, your backend will likely be a pure web API where all requests and responses are JSON (if any content at all). This matters because ASP.NET can do a lot of different things, including generating and serving up HTML (MVC, Razor pages), so you'll want to focus your attention on the aspects of it that you need.
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOPβ€’3y ago
TS, JS, Vue
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
Pobiega
Pobiegaβ€’3y ago
Great, then a bog standard ASP.NET Web API will be your best bet. Do you know how basic HTTP stuff works, with the request/responses etc?
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOPβ€’3y ago
i do not
Pobiega
Pobiegaβ€’3y ago
super simplified version: Every data exchange begins with the client submitting a request and the server sending a response back. You can only respond once to each request. Each request has a "method" or verb (GET, POST, PUT, DELETE, PATCH) that partially restricts what it can do but also indicates intent. You can have a webserver.com/hello endpoint respond only to GET, or GET and POST. with ASP.NET its easy to write a method that handles the get, and another method that handles the post. All requests and responses consist of several parts: the URI (path), the headers (a key value pair list) and the body (a stream, but often just JSON) when a request contains data, it can be as part of the URI, aka "query string": www.webserver.com/hello?query=floop <-- query is the parameter, floop is the value. or it can be part of the headers (they are just string-string key value pairs, so you can create them at will) or it can be the body (for apis, very common to accept a JSON object. ASP.NET has excellent support for this, you can make a record/class and just specify it as an input to your method) responses have 3 parts: the response code (indicates success or failure, and what kind of failure), headers, and optionally a body so your "endpoint" (what we call a unique path or route) can respond with 200 OK, or 404 Not Found, or 400 Bad Request, for example.
ero
eroOPβ€’3y ago
were these just some examples or is that all of them? i thought i'd seen SET and FETCH too at some point so, what exactly is a "header"? where does it come from? i understand it's a key-value pair, but it has to be stored somewhere, no? or like, input somewhere
Esa
Esaβ€’3y ago
It's part of the response object. a body contained inside a response object may have headers of its own in addition to the overall response object having some. Headers usually contain information such as the content-type (text/plain, application/json). These things also dictate what you can expect in return, as this is a string and "this is a string" are the result of different content-types (text/plain vs application/json) set in the Content-Type header. There may also be custom headers, some times these are prefixed by x- etc. Webservices don't usually support every http method for every endpoint, it's usually just a few if at all more than one. /api/customers/{customerId} indicates a search for a customer by a specific ID (the customerId variable) inside a collection of customers, so this one is a GET. If you remove the customerId variable it by convention becomes a POST (insert), and you'd be expected to provide the payload inside the body of your request. However that would require the webservice to have a route with a POST (as in, there needs to be an actual explicit endpoint for that path that accepts a POST httpmethod).
Pobiega
Pobiegaβ€’3y ago
It's all the ones you will likely use. There are others thou, such as HEAD, TRACE, OPTIONS, CONNECT Have not used any of them ever So yeah headers is just part of the protocol and the http client has some it adds by default, and same for the response ( added by the server) Here is a sample GET request I just googled
GET /hello.htm HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
GET /hello.htm HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
you wont be making these yourself quite as raw as this, as most http clients do most of the job for you for example, if you are gonna use the fetch api you literally just need the method and the URL most of the time the three most common methods are GET, POST and PUT. GET is for querying data. Sending a GET to api/customers would, if supported, return you some kind of list of customers. GET to api/customers?category=3&startsWith=steve would do similar, but filter based on the query string parameters (assuming the backend was implemented as such, but this is convention) GET to api/customers/5 would get you data about customer number 5 POST adds to collections or submits general data. POST to api/customers would register a new customer based on the body data, and return either the whole customer or just the ID (as it was assigned by the backend). PUT is "create or replace at the given id", so you could PUT to api/customers/5 to edit(replace) that customer record. PATCH is for making smaller edits than a full on replace, but its tricky to implement right. A fairly common thing to use is "jsonpatch" which has a way to indicate what to replace with what, but dont think System.Text.Json supports it just yet.
ero
eroOPβ€’3y ago
in this fashion, would api/customers?id=5 be bad practice?
Pobiega
Pobiegaβ€’3y ago
It would not follow convention, but is technically valid The nice thing about using it as part of the URL is that you can keep going. Let's say that customers have users, you could show them at api/customers/5/users/63 etc
ero
eroOPβ€’3y ago
yeah, this'll likely be a struggle point for me choosing what to put into the url, and what to put into the query
Pobiega
Pobiegaβ€’3y ago
Welcome to the family πŸ™‚ I tend to use url parameters for IDs and "hard" identifiers, and query strings for "filtering" multiple choices url params are obviously not optional, while query strings usually are
ero
eroOPβ€’3y ago
i'll start setting everything up later today. i'm excited, but i'm scared i won't get far. i have a hard time learning through reading, and finding complete, modern, best practice examples is obviously impossible, because a lot of it is too personal
Pobiega
Pobiegaβ€’3y ago
Yup, very much so. Not to mention that "best pratice" changes every few weeks, πŸ˜„
ero
eroOPβ€’3y ago
truuue
Pobiega
Pobiegaβ€’3y ago
So now that we've got most of the theory out of the way, ASP is actually really nice to work with. It handles most of the stuff for you
ero
eroOPβ€’3y ago
i know that you use source gen for your stuff and it's like. yes. i want that. but i don't understand source generators for shit
Pobiega
Pobiegaβ€’3y ago
It has two modes thou, old-school controllers and the more modern (and highly divisive) minimal apis where endpoints are literally a single method, usually a lambda
ero
eroOPβ€’3y ago
i believe a minimal API is all i need as well? hm, maybe not
Pobiega
Pobiegaβ€’3y ago
I think with .net 7 you can do almost everything with either approach
ero
eroOPβ€’3y ago
the project is big. like thousands of users with a massive amount of DB data big
Pobiega
Pobiegaβ€’3y ago
minimal has better performance, but its not like controllers are terribly slow or anything it all comes down to code style preference
ero
eroOPβ€’3y ago
(i already know this, because it's gonna be a competitor to an existing site which basically went to shit under new management)
Pobiega
Pobiegaβ€’3y ago
with controllers, you tend to make stuff like...
[Route("api/customers")]
public class CustomersController : ControllerBase
{
[HttpGet]
// no route, so it matches the "root" route of the controller
public List<Customer> GetAll() { ... }

[HttpPost]
// no route, so it matches the "root" route of the controller
public CustomerDto AddCustomer(CreateCustomerDto customer) { ... }
}
[Route("api/customers")]
public class CustomersController : ControllerBase
{
[HttpGet]
// no route, so it matches the "root" route of the controller
public List<Customer> GetAll() { ... }

[HttpPost]
// no route, so it matches the "root" route of the controller
public CustomerDto AddCustomer(CreateCustomerDto customer) { ... }
}
I actually kind of like minimal apis, but its a little bit more work getting it set up in a nice way. Just throwing api methods together in program.cs gets messy real fast (once you have hundreds of endpoints for example) so you need to add your own abstraction to separate the mapping into different files with logical responsibilities and registration of dependencies There are ofc packages that do this already, such as FastEndpoints (which I almost love, but it demands that your models are where T : new() which rules out records), and writing your own is like... 10 minutes of effort
ero
eroOPβ€’3y ago
i mean, i don't mind that. what i just care about is doing it "properly". in such a way that makes it easy to use and especially to maintain and expand
Pobiega
Pobiegaβ€’3y ago
sure dont think there is a problem with that then if you make a boilerplate asp.net app with and without controllers you can compare them
ero
eroOPβ€’3y ago
and also to make it as extensive as possible. like i want a very rich and intuitive api the competitor site's api is absolutely abyssmal and has zero documentation
Pobiega
Pobiegaβ€’3y ago
hm, so the API is not just for your frontends use? like, you plan to actually support external users interacting with it directly? Then you will 100% want to read up on API versioning
ero
eroOPβ€’3y ago
yeah, it'll be a public api for fetching heaps of data don't know if you remember, but it's that leaderboards project. there will be hundreds and thousands of gaming leaderboards there will also be hundreds of "series" (a series contains multiple games (leaderboards))
Pobiega
Pobiegaβ€’3y ago
What we do at work, is we have two separate APIs more or less one for our own frontend, and one for external users
ero
eroOPβ€’3y ago
and each leaderboard contains hundreds of submissions with potentially multiple players
Pobiega
Pobiegaβ€’3y ago
the second is heavily versioned and protected against breaking changes, while the first one we can change as we want since we use command pattern internally, most of the external stuff uses the same commands as the internal one, just with a different layer of very strict DTO mappings applied
ero
eroOPβ€’3y ago
hm. so the entire thing (and i mean the entire thing) is gonna be open source is it safe to have the internal API open source?
Pobiega
Pobiegaβ€’3y ago
just make sure you have very good security on it πŸ˜› ie, validate absolutely everything everywhere open source means that hackers wont have to guess for exploits
ero
eroOPβ€’3y ago
right
Pobiega
Pobiegaβ€’3y ago
but it also means you might get issues/PRs to help fix free pentesting, so to speak πŸ˜„
ero
eroOPβ€’3y ago
i don't know whether the split between public and internal makes sense yet like if that's really necessary maybe i'll see as it moves on would you recommend going for the minimal api approach for now?
Pobiega
Pobiegaβ€’3y ago
Test it out, see how it feels. this video from nick explains the "abstraction" level I tend to use for minimal apis
Pobiega
Pobiegaβ€’3y ago
Nick Chapsas
YouTube
In defence of .NET Minimal APIs | Refactoring
Become a Patreon and get source code access: https://www.patreon.com/nickchapsas Check out my courses: https://nickchapsas.com Hello everybody I'm Nick and in this video I wanna address a lot of the critisism that Minimal APIs have been getting in the past few weeks as we are getting closer and closer to the release of .NET 6. In this video I w...
Pobiega
Pobiegaβ€’3y ago
I quite like FastEndpoints too thou, if only it would let you instantiate the response model yourself so records would be usable :((((
ero
eroOPβ€’3y ago
You can add an empty ctor to records, no?
Pobiega
Pobiegaβ€’3y ago
Absolutely but then you can't use the short form definition
ero
eroOPβ€’3y ago
mh, right so, where should i start?
Pobiega
Pobiegaβ€’3y ago
dotnet new webapi πŸ™‚
ero
eroOPβ€’3y ago
hah, fair (and pretty much exactly what i wanted to hear)
Pobiega
Pobiegaβ€’3y ago
It really is that easy to get started. Asp.net is very well made and extremely modular So you start out there with like 4 lines of code and add as you go
ero
eroOPβ€’3y ago
perhaps somewhat unrelated, but i care a lot about project structure let me run by you what i'd do
Pobiega
Pobiegaβ€’3y ago
Makes sense Project structure is important I think tebe recently made a tag for a project scaffold that looked nice. Was it... $scaffolding?
ero
eroOPβ€’3y ago
LeaderboardsGG.Backend
β”œβ”€ .github
β”‚ β”œβ”€ ISSUE_TEMPLATES
β”‚ β”‚ └─ ...
β”‚ └─ workflows
β”‚ └─ ...
β”œβ”€ assets
β”‚ └─ ...
β”œβ”€ src
β”‚ β”œβ”€ LeaderboardsGG.Backend
β”‚ β”‚ β”œβ”€ Properties
β”‚ β”‚ β”‚ β”œβ”€ appsettings.json
β”‚ β”‚ β”‚ β”œβ”€ appsettings.Development.json
β”‚ β”‚ β”‚ └─ launchSettings.json
β”‚ β”‚ └─ LeaderboardsGG.Backend.csproj
β”‚ └─ LeaderboardsGG.Backend.Tests
β”‚ └─ LeaderboardsGG.Backend.Tests.csproj
β”œβ”€ .editorconfig
β”œβ”€ .gitignore
└─ LeaderboardsGG.Backend.sln
LeaderboardsGG.Backend
β”œβ”€ .github
β”‚ β”œβ”€ ISSUE_TEMPLATES
β”‚ β”‚ └─ ...
β”‚ └─ workflows
β”‚ └─ ...
β”œβ”€ assets
β”‚ └─ ...
β”œβ”€ src
β”‚ β”œβ”€ LeaderboardsGG.Backend
β”‚ β”‚ β”œβ”€ Properties
β”‚ β”‚ β”‚ β”œβ”€ appsettings.json
β”‚ β”‚ β”‚ β”œβ”€ appsettings.Development.json
β”‚ β”‚ β”‚ └─ launchSettings.json
β”‚ β”‚ └─ LeaderboardsGG.Backend.csproj
β”‚ └─ LeaderboardsGG.Backend.Tests
β”‚ └─ LeaderboardsGG.Backend.Tests.csproj
β”œβ”€ .editorconfig
β”œβ”€ .gitignore
└─ LeaderboardsGG.Backend.sln
is it possible at all to put the appsettings.json into the Properties folder? i don't like having it in the root of the project
Pobiega
Pobiegaβ€’3y ago
Possible, but would require changing the default web host builder a bit Ie, not using the default :p
ero
eroOPβ€’3y ago
fine by me shouldn't be more than 2 lines right?
Pobiega
Pobiegaβ€’3y ago
Eh, probably closer to 10 But still doable
Accord
Accordβ€’3y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
ero
eroOPβ€’3y ago
@Pobiega so the minimal api stuff is pretty clean, but i really don't like how it's set up. it just feels kinda... weird to split everything apart like that. because of that, i'd like to look into source gen. do you think something like this would make sense?
[HttpRepo] // custom attribute to generate .MapGet, .MapPost, etc.
public partial class CustomerRepository
{
[HttpModel] // custom attribute to identify the repo's model
public record Customer(Guid Id, string FullName);

private readonly Dictionary<Guid, Customer> _customers = new();

[HttpGet("/customers")] // not used for anything besides identifying what to generate
public List<Customer> GetAll()
{
return _customers.Values.ToList();
}
}
[HttpRepo] // custom attribute to generate .MapGet, .MapPost, etc.
public partial class CustomerRepository
{
[HttpModel] // custom attribute to identify the repo's model
public record Customer(Guid Id, string FullName);

private readonly Dictionary<Guid, Customer> _customers = new();

[HttpGet("/customers")] // not used for anything besides identifying what to generate
public List<Customer> GetAll()
{
return _customers.Values.ToList();
}
}
Pobiega
Pobiegaβ€’3y ago
Could work for sure. if you just want to generate CRUD stuff for you, that'll do it Might get complicated when you try and add validation of inputs and stuff thou
ero
eroOPβ€’3y ago
the thing is, i don't know if i need anything more in the past, i've only experienced the entity + dto + controller + service way of making a web api which feels a lot more complete
Pobiega
Pobiegaβ€’3y ago
minimal apis only really replace the controller
ero
eroOPβ€’3y ago
and the entity? or the dto i guess?
Pobiega
Pobiegaβ€’3y ago
you still need those. and DTOs too
ero
eroOPβ€’3y ago
but isn't the entity the dto in minimal apis? like aren't they the same thing in this? Customer is both the entity and the dto, no?
Pobiega
Pobiegaβ€’3y ago
in this case yes but that comes with issues what if you want to add some calculated properties to the response object, but not to your database entity? or if you need to add some hidden fields to the entity that should not be shown to the user?
ero
eroOPβ€’3y ago
this could be handled by that HttpModel attribute too i guess
Pobiega
Pobiegaβ€’3y ago
or if you have multiple endpoints using the same entity with different information, like an admin vs a user endpoint
ero
eroOPβ€’3y ago
i feel like minimal apis just aren't it
Pobiega
Pobiegaβ€’3y ago
Controllers are still a thing so you can absolutely just use them if you prefer that
ero
eroOPβ€’3y ago
mh. is there like anything to get me started on this?
Pobiega
Pobiegaβ€’3y ago
the source gen stuff? or just the controllers?
ero
eroOPβ€’3y ago
just, everything else i don't know how to explain it. like i don't know anything about anything web dev related i don't even really know what entities and dtos and controllers and services are i don't know how they work together
Pobiega
Pobiegaβ€’3y ago
right
ero
eroOPβ€’3y ago
i don't know what they do
Pobiega
Pobiegaβ€’3y ago
well lets go though that then, might clear some things up
ero
eroOPβ€’3y ago
i don't know how to test things, how to run things to test them
Pobiega
Pobiegaβ€’3y ago
Entity is your core domain object. its the class that represents the true state of an item you care about its usually saved in your database
ero
eroOPβ€’3y ago
what's a domain object
Pobiega
Pobiegaβ€’3y ago
this project, you said its about game leaderboards right?
ero
eroOPβ€’3y ago
yup i mean i know what my entities are right like User, Leaderboard, Submission
Pobiega
Pobiegaβ€’3y ago
yeah exactly those are your entities. its the stuff you save in your database and your core logic work with
ero
eroOPβ€’3y ago
ok wait wait i wanna write code on the side so i can remember this and since i still care about project structure; do you think entities should be in their own namespace X.Entities?
Pobiega
Pobiegaβ€’3y ago
usually just x.Domain a classlib that contains your entities and little else.
ero
eroOPβ€’3y ago
really an entire project just for the entities
Pobiega
Pobiegaβ€’3y ago
its very common, yes. the reason being what if you want to reuse those entities for a cli tool, or a WPF admin app, or something but you can ofc just put it in a namespace somewhere
ero
eroOPβ€’3y ago
would you call them UserEntity or just User?
Pobiega
Pobiegaβ€’3y ago
User
ero
eroOPβ€’3y ago
would they be records or classes? i believe you've said you use records for everything
Pobiega
Pobiegaβ€’3y ago
depends on what database ORM you use EF for example doesnt like record entities if you use dapper, I think its fine
ero
eroOPβ€’3y ago
still? in net7?
Pobiega
Pobiegaβ€’3y ago
well, EFs primary thing is the change tracker if your entites are immutable... no changes to track :p
ero
eroOPβ€’3y ago
right class it is so, guid or ulong for ids?
Pobiega
Pobiegaβ€’3y ago
I like guids. prevents object enumeration, which is a security flaw
ero
eroOPβ€’3y ago
but i do want users to be enumerated i guess not the entities in particular
Pobiega
Pobiegaβ€’3y ago
in what way? ie, if you see the url: api/user/500 you can try to access api/user/501 too and if my code sucks, perhaps you get in or see some data you shouldnt or even NOT get a 404 which tells you there is a user with that ID
ero
eroOPβ€’3y ago
why's that a problem? i want anyone to be able to GET a user by their id of course the information they get back is limited and if someone wants to GET all users on the site, then go right ahead
Pobiega
Pobiegaβ€’3y ago
alright, if thats a desired trait then go ahead and use ulongs
ero
eroOPβ€’3y ago
so, about usernames. the original idea was to have it be [a-zA-Z0-9_'-]{2,32} but perhaps i don't want usernames like that maybe i want a system like discord
Pobiega
Pobiegaβ€’3y ago
Shouldn't be much of a problem.
ero
eroOPβ€’3y ago
also i've heard that ulong is a problem, but long less so is there any merit to that?
Pobiega
Pobiegaβ€’3y ago
Not sure why ulong would be a problem, but long/int are very common as ID types, even if you never really expect negative ids
ero
eroOPβ€’3y ago
also by this i mean that _'-, if used at all, must be surrounded by alphanum characters so __ or a- don't work for usernames
Pobiega
Pobiegaβ€’3y ago
right. Thats all up to you, not a problem either way
ero
eroOPβ€’3y ago
but also then the user page link will have to contain their id, which is ugly
Pobiega
Pobiegaβ€’3y ago
if you want a discord like system, there are two unique identifiers for a user right the ID, and the "name#number"
ero
eroOPβ€’3y ago
right
Pobiega
Pobiegaβ€’3y ago
I believe the discord API term for the id is the snowflake
ero
eroOPβ€’3y ago
yup
Pobiega
Pobiegaβ€’3y ago
yeah, not very pretty, but also primarily used when its important it cant be changed like in a ban list for a user endpoint, api/users/Ero#1111 would be fine
ero
eroOPβ€’3y ago
is that a valid url? didn't know if you could include the hash
Pobiega
Pobiegaβ€’3y ago
hm right, hash is used for html anchors so probably not
ero
eroOPβ€’3y ago
would it be very difficult to change from usernames to tags later on? i imagine so hm, unless
Pobiega
Pobiegaβ€’3y ago
like going from just ID + name, to ID + tag + name?
ero
eroOPβ€’3y ago
yeah
Pobiega
Pobiegaβ€’3y ago
probably not, as long as you used the ID for all relationships
ero
eroOPβ€’3y ago
right i'll go with normal usernames then for now
namespace Lbgg.Domain;

public sealed class User
{
[Required]
public required ulong Id { get; set; }

[Required]
[MinLength(2)]
[MaxLength(32)]
[RegularExpression(@"^[a-zA-Z0-9][a-zA-Z0-9_'-]*[a-zA-Z0-9]$")]
public required string Name { get; set; }
}
namespace Lbgg.Domain;

public sealed class User
{
[Required]
public required ulong Id { get; set; }

[Required]
[MinLength(2)]
[MaxLength(32)]
[RegularExpression(@"^[a-zA-Z0-9][a-zA-Z0-9_'-]*[a-zA-Z0-9]$")]
public required string Name { get; set; }
}
is this acceptable? should i not be using those attributes? also i just wanna thank you a bunch for doing this with me, i'd be absolutely lost otherwise
Pobiega
Pobiegaβ€’3y ago
I personally dislike attribute validation I prefer something like FluentValidation instead but thats preference my reason being that its not clear WHEN attribute validation takes place
ero
eroOPβ€’3y ago
that's understandable i'll leave the attributes out for now
public sealed class User
{
public required ulong Id { get; set; }
public required string Name { get; set; }
public required string Email { get; set; }
public required string Password { get; set; }

public string? About { get; set; }
}
public sealed class User
{
public required ulong Id { get; set; }
public required string Name { get; set; }
public required string Email { get; set; }
public required string Password { get; set; }

public string? About { get; set; }
}
so we're here now
Pobiega
Pobiegaβ€’3y ago
seems good!
ero
eroOPβ€’3y ago
things subject to change of course
Pobiega
Pobiegaβ€’3y ago
ofc
ero
eroOPβ€’3y ago
and you just have this in the root of the .Domain project? not like .Domain.Entities or something?
Pobiega
Pobiegaβ€’3y ago
probably something like the latter, as I tend to eventually add more things to Domain
ero
eroOPβ€’3y ago
sounds good what'd be my next step? i guess at this point i have to decide whether i want a minimal api or not, huh
Pobiega
Pobiegaβ€’3y ago
yup, next step is figuring out what endpoints you want so that means deciding on minimal vs controller
ero
eroOPβ€’3y ago
so, i want my actual user pages to be /u/{username} does that have anything to do with anything? or is that the frontend's job
Pobiega
Pobiegaβ€’3y ago
primarily frontend, but you'll need to consider the data the frontend has if thats the route you want, you'll need an API endpoint that takes in a username and returns a user object
ero
eroOPβ€’3y ago
this would make the switch from usernames to tags more difficult i think
Pobiega
Pobiegaβ€’3y ago
indeed it would or well would it?
ero
eroOPβ€’3y ago
well, not for me
Pobiega
Pobiegaβ€’3y ago
your endpoint would swap to be /u/tag
ero
eroOPβ€’3y ago
but it'd result in a lot of broken links like if someone has their user page linked somewhere
Pobiega
Pobiegaβ€’3y ago
right, true
ero
eroOPβ€’3y ago
argh, really struggling over this using the ID isn't particularly weird, lots of sites do it but i find it ugly and using the username directly is a lot more intuitive and leads to more readable links
Pobiega
Pobiegaβ€’3y ago
well, then go with tags from the start? if its a feature you know you'll want later on
ero
eroOPβ€’3y ago
i'd just gonna go with IDs in the url i think because people can change their tags too right
Pobiega
Pobiegaβ€’3y ago
πŸ‘
ero
eroOPβ€’3y ago
that's the whole point
Pobiega
Pobiegaβ€’3y ago
yep so you'd get broken links anyways
ero
eroOPβ€’3y ago
and if they change their tag, or their numbers, links broken again alright do you have any suggestions on what my next step could be?
Pobiega
Pobiegaβ€’3y ago
Controllers vs minimal apis. Controllers gives you a natural way to group similar endpoints together, but also comes with a little bloat in that every action (endpoint) will have all the dependencies resolved for it so even if only 1 out of 5 endpoints in your controller require the database connection, it will be resolved for all of them (since the controller is the item being resolved)
ero
eroOPβ€’3y ago
does any of this have to do with the speed of the requests? i care a lot about speed
Pobiega
Pobiegaβ€’3y ago
it does. last time I saw numbers, I think it was 11-15% faster with minimal apis
ero
eroOPβ€’3y ago
oh boy
Pobiega
Pobiegaβ€’3y ago
granted, you're still in the "handles 10000 requests per second" bracket without even trying
ero
eroOPβ€’3y ago
oh lol yeah i doubt that's ever gonna happen maybe at peak times i mean i like the source gen approach the one i came up with seemed nice and tidy to me
Pobiega
Pobiegaβ€’3y ago
sounds neat, just worried it might be hard to implement nicely
ero
eroOPβ€’3y ago
yeah...
Pobiega
Pobiegaβ€’3y ago
for example an UpdateUser endpoint might not take in the same DTO as a CreateUser
ero
eroOPβ€’3y ago
mh
Pobiega
Pobiegaβ€’3y ago
where do you want your business logic to live btw? You normally have two options: service classes or command pattern actually its a very similar decision as controller vs minimal api
ero
eroOPβ€’3y ago
i've been wanting to use commands ever since i saw someone (i believe it was you?) use source gen with them as well
Pobiega
Pobiegaβ€’3y ago
hmm Don't remember using source gen with them, but I know you can ie, use a generated dispatcher so there is not even startup cost to initializing it haha, found this while googling: https://github.com/Burgyn/MMLib.MediatR.Generators it generates HTTP endpoints based on your commands ah here it is: https://github.com/martinothamar/Mediator source generated command/message dΓ­spatcher
ero
eroOPβ€’3y ago
namespace Lbgg.Controllers.Users;

[HttpCommand]
public static partial class Create
{
public sealed partial record Command(
ulong Id,
string Name)
{
private static void AddValidation(AbstractValidator<Command> v)
{
v.RuleFor(u => u.Name).NotEmpty().MinimumLength(2).MaximumLength(32);
}
}

private static async Task CommandHandler(
Command command,
ApplicationDbContext dbContext,
ILogger logger)
{
if (await context.Users.AnyAsync(u => u.Id == command.Id))
throw new InvalidOperationException($"User '{command.Id}' already exists.");

var user = new UserEntity
{
UserName = command.UserName,
Id = command.Id,
};

context.Users.Add(user);
await context.SaveChangesAsync();

using (logger.AddProperties(("@NewUserInformation", command)))
logger.LogInformation("New user created");
}
}
namespace Lbgg.Controllers.Users;

[HttpCommand]
public static partial class Create
{
public sealed partial record Command(
ulong Id,
string Name)
{
private static void AddValidation(AbstractValidator<Command> v)
{
v.RuleFor(u => u.Name).NotEmpty().MinimumLength(2).MaximumLength(32);
}
}

private static async Task CommandHandler(
Command command,
ApplicationDbContext dbContext,
ILogger logger)
{
if (await context.Users.AnyAsync(u => u.Id == command.Id))
throw new InvalidOperationException($"User '{command.Id}' already exists.");

var user = new UserEntity
{
UserName = command.UserName,
Id = command.Id,
};

context.Users.Add(user);
await context.SaveChangesAsync();

using (logger.AddProperties(("@NewUserInformation", command)))
logger.LogInformation("New user created");
}
}
this is what someone has posted in the past i saw this and liked it a lot and then you have
public sealed partial class UserController
{
[HttpPost]
public async Task Create(Create.Command command)
{
return await _sender.Send(command);
}
}
public sealed partial class UserController
{
[HttpPost]
public async Task Create(Create.Command command)
{
return await _sender.Send(command);
}
}
Pobiega
Pobiegaβ€’3y ago
Hm, thats neat. And it would generate the Http endpoint I assume
ero
eroOPβ€’3y ago
or something so the Create.Command record would be my Create dto i guess
Pobiega
Pobiegaβ€’3y ago
exactly.
ero
eroOPβ€’3y ago
i don't really know how to approach that yet, even in the slightest so let's just try to write it without any source gen in mind yet
Pobiega
Pobiegaβ€’3y ago
alright
ero
eroOPβ€’3y ago
just the full thing minimal api, commands
Pobiega
Pobiegaβ€’3y ago
πŸ‘
ero
eroOPβ€’3y ago
so first about namespaces. how should i split things up, what do i put where Nick had Repositories, Models and EndpointDefinitions this seems like so much
Pobiega
Pobiegaβ€’3y ago
There are a bunch of different approaches for sure I like to group mine by ... "area", if that makes sense like, lets say I have a few user endpoints
ero
eroOPβ€’3y ago
besides me not even knowing what a repository is in this context
Pobiega
Pobiegaβ€’3y ago
I'd probably stick them in X.Web.Endpoints.Users and that would contain the endpoints themselves, the DTOs, etc possible as subnamespaces
ero
eroOPβ€’3y ago
so let's say, something like this?
namespace Lbgg.Endpoints.Dtos;

public sealed record CreateUser(
string Name,
string Email,
string Password);
namespace Lbgg.Endpoints.Dtos;

public sealed record CreateUser(
string Name,
string Email,
string Password);
Pobiega
Pobiegaβ€’3y ago
yup well I'd scope it down a bit more the CreateUser dto is only ever relevant within the CreateUser endpoint really
ero
eroOPβ€’3y ago
oh, that was supposed to be in Lbgg.Endpoints.Users.Dtos
Pobiega
Pobiegaβ€’3y ago
right then that is perfectly fine there is an interesting idea someone raise a while back, which is just straight up using your commands as DTOs its doable, but comes with the problem that changing your command at all will likely be a breaking API change
ero
eroOPβ€’3y ago
i wonder...
[HttpEndpointGenerator]
public static class UserEndpoints
{
// generates the dto, the controller, and whatever else is needed
public static async Task Create(string name, string email, string password)
{
// create user
}
}
[HttpEndpointGenerator]
public static class UserEndpoints
{
// generates the dto, the controller, and whatever else is needed
public static async Task Create(string name, string email, string password)
{
// create user
}
}
how fun would this be it just like generates
namespace Lbgg.Endpoints.Users;

file static class Create
{
record Command(string Name, string Email, string Password);

static async Task Execute(Command c) => UserEndpoints.Create(c.Name, c.Email, c.Password);
}
namespace Lbgg.Endpoints.Users;

file static class Create
{
record Command(string Name, string Email, string Password);

static async Task Execute(Command c) => UserEndpoints.Create(c.Name, c.Email, c.Password);
}
or something but i mean, i don't even know what you actually need to generate to make it work swagger integration is a pretty important to me too i want very good api documentation
Pobiega
Pobiegaβ€’3y ago
Not a problem, thats available with both minimal APIs and controllers
Pobiega
Pobiegaβ€’3y ago
I'll throw in another recommendation for https://fast-endpoints.com/
FastEndpoints
FastEndpoints
FastEndpoints is a developer friendly alternative to Minimal APIs & MVC for rapid REST API development.
Pobiega
Pobiegaβ€’3y ago
like I said, my only gripe with this is that it creates the response instance for you by default, which ruins records for me Should doublecheck if thats still a thing, doesnt seem like it is by a quick glance at the code
ero
eroOPβ€’3y ago
i mean, yeah, right, that's cool and all, but i don't relaly understand what any of that does or means. like i'm still very much lost on how to actually create an api. what services are, what controllers are, what endpoints really are i don't know how to explain it. like i feel like i'm in the middle of the ocean with no support everything is going on at once and i don't know where to go
Pobiega
Pobiegaβ€’3y ago
well lets just start out easy then an endpoint is just some piece of code that is registered to handle requests to a given URL for example, POST api/users could be the route for an endpoint that adds users
ero
eroOPβ€’3y ago
probably a language thing but the words chosen for these things are very weird to me requests endpoints so what i wanna do right now is just, get to the point where i can run my api, open it in swagger, and add stuff in my in-memory db i think once that all works, i can expand on that a bit more easily
Pobiega
Pobiegaβ€’3y ago
Sure Lets start out with controllers for now then, as thats the easiest way to move forward, imho I guess you have an asp.net webapi project made already?
ero
eroOPβ€’3y ago
yeah i deleted everything though like the stupid ass weather forecast stuff
Pobiega
Pobiegaβ€’3y ago
not a problem what does your program.cs look like?
ero
eroOPβ€’3y ago




man can't display empty blocks it's empty
Pobiega
Pobiegaβ€’3y ago
okay, lets add the basics back in then
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();
this will add all controllers to your DI container, add swagger, then build the app (looks at configurations etc), then configure the HTTP pipeline by adding swagger if we are in development mode, adding HTTPS redirection, adding support for authorization and finally mapping your controllers this should compile and start up a fairly empty swagger UI on boot
ero
eroOPβ€’3y ago
lots of things there that i still don't know
Pobiega
Pobiegaβ€’3y ago
lets just leave it like this for now so we can get to the point where your API can recieve a request and do something with it
ero
eroOPβ€’3y ago
right
ero
eroOPβ€’3y ago
Pobiega
Pobiegaβ€’3y ago
development cert for HTTPS
ero
eroOPβ€’3y ago
oh yeah i wanna use postgres
Pobiega
Pobiegaβ€’3y ago
so you dont get warnings that localhost doesnt have a valid trusted HTTPS cert
ero
eroOPβ€’3y ago
i guess that doesn't have anything to do with the web part actually
Pobiega
Pobiegaβ€’3y ago
nope, thats fine npgsql has excellent connectors for C# and EF, or dapper or whatever you wanna use
ero
eroOPβ€’3y ago
ok yeah that works
ero
eroOPβ€’3y ago
Pobiega
Pobiegaβ€’3y ago
cool lets add our first controller make a new class anywhere in the web project, something like UsersController make it inherit ControllerBase, and decorate it with [ApiController] also, give it a [Route("api/users")] attribute you can ofc change that string to be whatever base URI you want for this controller finally, lets make a simple method:
[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
[HttpGet("{id}")]
public User GetUser(ulong id)
{
return new User()
{
Id = id,
About = "not yet",
Email = "[email protected]",
Name = "Steve",
Password = "oh dear god"
};
}
}
[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
[HttpGet("{id}")]
public User GetUser(ulong id)
{
return new User()
{
Id = id,
About = "not yet",
Email = "[email protected]",
Name = "Steve",
Password = "oh dear god"
};
}
}
now, this is obviously a stupid method, but it should work and whatever ID you give as a parameter should be present in the json response controllers are added to the DI container, so you can just add a constructor with whatever dependencies you want access to, and they will be injected when the controller is resolved for example, a database context
ero
eroOPβ€’3y ago
i don't really know what DI or DI containers are
Pobiega
Pobiegaβ€’3y ago
Hm, okay. DI is a common way to solve the IoC problem (inversion of control) like, we want access to the database inside our controller, so we can query a user from the database but we dont want the controller to be responsible for creating connections to the database directly via something like var db = new DatabaseConnection();
ero
eroOPβ€’3y ago
i guess i just don't really understand what we're injecting?
Pobiega
Pobiegaβ€’3y ago
instead, we say that the controller gets a constructor parameter for the database connection
ero
eroOPβ€’3y ago
what dependency are we injecting
Pobiega
Pobiegaβ€’3y ago
the database connection.
ero
eroOPβ€’3y ago
to where
Pobiega
Pobiegaβ€’3y ago
for once we have one to the controller, in this case.
ero
eroOPβ€’3y ago
where are we injecting it i'm so confused we're not doing anything though
Pobiega
Pobiegaβ€’3y ago
well currently we are not injecting anything either since the method just returns dummy data each time lets play around with this a bit more and worry about the database stuff later
ero
eroOPβ€’3y ago
alright swagger ain't too smart, huh?
Pobiega
Pobiegaβ€’3y ago
hm?
ero
eroOPβ€’3y ago
Pobiega
Pobiegaβ€’3y ago
Pobiega
Pobiegaβ€’3y ago
this is what I get.
ero
eroOPβ€’3y ago
should be uint64
Pobiega
Pobiegaβ€’3y ago
hm, yeah it should. curious that it didnt figure that out tbh
ero
eroOPβ€’3y ago
and yet it knows that -123 is invalid
ero
eroOPβ€’3y ago
Pobiega
Pobiegaβ€’3y ago
Pobiega
Pobiegaβ€’3y ago
OpenAPI Specification - Version 3.0.3 | Swagger
The OpenAPI Specification defines a standard interface to RESTful APIs which allows both humans and computers to understand service capabilities without access to source code, documentation, or network traffic inspection.
Pobiega
Pobiegaβ€’3y ago
no unsigned types in that specification bit silly
ero
eroOPβ€’3y ago
i guess i might just go with long then?
Pobiega
Pobiegaβ€’3y ago
Sure, thats fine but hey, we have a working endpoint that responds as expected
ero
eroOPβ€’3y ago
yup
Pobiega
Pobiegaβ€’3y ago
whats the next step? making it actually remember something?
ero
eroOPβ€’3y ago
i suppose, yeah
Pobiega
Pobiegaβ€’3y ago
alright, the simplest "in memory database" is just a class with lists in it πŸ™‚ so we can make one of those for now
public class Database
{
public List<User> Users { get; } = new();
}
public class Database
{
public List<User> Users { get; } = new();
}
there we go, almost a database Now, we want to get this into our controller so we can start using it there but we just var database = new Database(); it wont work, as we'll get a new database on each request and lets not use static either, since we want this to simulate being a real database connection, which we cant have static
ero
eroOPβ€’3y ago
why not?
Pobiega
Pobiegaβ€’3y ago
what part? not being static?
ero
eroOPβ€’3y ago
yeah the static part not like i'm looking for reasons to use static i just wanna know why
Pobiega
Pobiegaβ€’3y ago
has to do with object lifetimes etc the connection itself is disposable I'm sort of assuming you will be using EF at some point for now but I think this holds true for dapper or similar too
ero
eroOPβ€’3y ago
EF was the idea yeah
Pobiega
Pobiegaβ€’3y ago
cool then we'll go with that so, to get access to the database, we need to inject it add a constructor to your controller:
[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
private readonly Database _database;

public UsersController(Database database)
{
_database = database;
}
[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
private readonly Database _database;

public UsersController(Database database)
{
_database = database;
}
it takes in a database and just saves the reference in a readonly field. this is very standard now if we run this, we get an error because we never told the DI container what a Database is so back to program.cs we go and... builder.Services.AddSingleton<Database>(); there are 3 methods of note on Services here (which is an IServiceCollection, btw) AddSingleton, AddScoped and AddTransient singelton just means "there will only ever be one instance of this" scoped means "there will only ever be one instance of this, PER SCOPE" (in ASP.NET thats usually request scope, meaning a single http request transient means "you'll get a new one each time"
ero
eroOPβ€’3y ago
alright
Pobiega
Pobiegaβ€’3y ago
so since we want our database to live as long as the application itself, we say its singleton now we can use it in our controller
ero
eroOPβ€’3y ago
oh sick
Pobiega
Pobiegaβ€’3y ago
so a logical rewrite of our get user method would be...
[HttpGet("{id}")]
public ActionResult<User> GetUser(long id)
{
var user = _database.Users.FirstOrDefault(x => x.Id == id);

if (user is null)
{
return NotFound();
}

return user;
}
[HttpGet("{id}")]
public ActionResult<User> GetUser(long id)
{
var user = _database.Users.FirstOrDefault(x => x.Id == id);

if (user is null)
{
return NotFound();
}

return user;
}
we look in the database. nothing found? well we want to return a 404 NOT FOUND http response controllers have helper methods for that. But we need to change the return type a bit ActionResult<T> lets us be strictly typed but return whatever http response codes we want
ero
eroOPβ€’3y ago
makes sense
Pobiega
Pobiegaβ€’3y ago
okay cool. lets try making the "CreateUser" endpoint now wanna give it a go?
ero
eroOPβ€’3y ago
sure thing
public record CreateUserDto(
string Name,
string Email,
string Password);


public ActionResult CreateUser(CreateUserDto userToCreate)
{
if (_database.Users.Any(u => u.Name == userToCreate.Name))
{
return Conflict();
}

_database.Users.Add(new()
{
Id = /* ? */,
Name = userToCreate.Name,
Email = userToCreate.Email,
Password = userToCreate.Password
});

return Success(); // The name 'Success' does not exist in the current context
}
public record CreateUserDto(
string Name,
string Email,
string Password);


public ActionResult CreateUser(CreateUserDto userToCreate)
{
if (_database.Users.Any(u => u.Name == userToCreate.Name))
{
return Conflict();
}

_database.Users.Add(new()
{
Id = /* ? */,
Name = userToCreate.Name,
Email = userToCreate.Email,
Password = userToCreate.Password
});

return Success(); // The name 'Success' does not exist in the current context
}
hm
Pobiega
Pobiegaβ€’3y ago
yeah, pretty much! normally we'd get the next available ID from the database itself, but our memory collection doesnt support that so we can add a simple tracker if we want, but idk maybe thats overkill πŸ˜›
ero
eroOPβ€’3y ago
but what do i return? there's no Success() and no ActionResult.Success oh it's Ok() why are they even methods pisses me off
Pobiega
Pobiegaβ€’3y ago
Well, because they often take arguments
ero
eroOPβ€’3y ago
ah, right is there a clear "next step"?
Pobiega
Pobiegaβ€’3y ago
I'd say introduce command pattern so instead of the controllers doing actual logic, they'd only handle HTTP stuff next step is probably adding an actual database context via EF, so you can start persisting stuff Would highly recommend reading up/watching some videos on dependency injection and EF
ero
eroOPβ€’3y ago
let's do this first and i'll worry about the rest after
Pobiega
Pobiegaβ€’3y ago
Okay. https://github.com/martinothamar/Mediator seems good, so you can use that if you don't want to go through the hassle of writing your own dispatcher
ero
eroOPβ€’3y ago
i do wanna go through the hassle for now so i at least understand
Pobiega
Pobiegaβ€’3y ago
Okay, so the simplest version of the command pattern with DI is to register your handlers with AddScoped (most will need to be scoped, because the database context will be scoped), and simply inject them into your controller pros: no dispatcher, yay cons: a controller might use many handlers, but perhaps only one at a time
ero
eroOPβ€’3y ago
so i really didn't understand a lot of that what's a dispatcher what's a handler in this context
Pobiega
Pobiegaβ€’3y ago
Handler = command handler a command is just a POCO, a stupid no-logic C# class/record that only guarantees that its "valid" (ie, if it needs an ID that an ID was provided) a dispatcher lets you use the fancy calls, like...
var command = new CreateUserCommand(...);
var result = _dispatcher.Run(command);
var command = new CreateUserCommand(...);
var result = _dispatcher.Run(command);
and result here would be your User, or whatever the handler for CreateUser responds with the type inference here is based on the fact that the command is generic over its result type, btw so a dispatch-less alterantive is
var command = new CreateUserCommand(...);
var result = _createUserHandler.Handle(command);
var command = new CreateUserCommand(...);
var result = _createUserHandler.Handle(command);
but as said, that means instead of just injecting the dispatcher, you'll need to know what handlers you need
ero
eroOPβ€’3y ago
i was wondering more about the actual implementation, not just how it'll be used in the end
Pobiega
Pobiegaβ€’3y ago
Well, they are non trivial and I really CBA making one from scratch right now :p But the idea is that you assembly scan for your handlers at startup (or sourcegen) And build a lookup of command type to handler type
ero
eroOPβ€’3y ago
ah, that was part of the nick video yeah he does assembly scanning there
Pobiega
Pobiegaβ€’3y ago
You can then resolve the handlers based on the command type
ero
eroOPβ€’3y ago
surprisingly less lines than i expected
Pobiega
Pobiegaβ€’3y ago
Yeah the reflection stuff isnt too bad. In general the very basics isn't too much code in general, but it adds up
ero
eroOPβ€’3y ago
need to wait for patrick to go back to bed to ask my source gen question i guess
Patrick
Patrickβ€’3y ago
you realise im asking those questions to make you realise it's backward
ero
eroOPβ€’3y ago
won't leave me alone
Patrick
Patrickβ€’3y ago
because making remarks about someone behind their back... is great?
Patrick
Patrickβ€’3y ago
if you can answer the question it'll help you build a better backend, considering im reading this now and you've never done it before
ero
eroOPβ€’3y ago
i can't answer the question i still don't even really know what a controller or a service or mediatr or whatever are i just know that what i have been doing so far is fucking annoying
Patrick
Patrickβ€’3y ago
so your protest to me asking questions has actually uncovered you don't know what you're doing here's a simple scenario: write a controller for what you want to do and leave it there
ero
eroOPβ€’3y ago
i don't know what i want to do there is so much this site needs to do i don't know where to start
Patrick
Patrickβ€’3y ago
im not sure if this has become a question of how to structure your API or how to start a project im now supposing the latter
ero
eroOPβ€’3y ago
both
Patrick
Patrickβ€’3y ago
ok so what is it
ero
eroOPβ€’3y ago
what's what
Patrick
Patrickβ€’3y ago
your project
ero
eroOPβ€’3y ago
gaming leaderboards for score and time
Patrick
Patrickβ€’3y ago
ok and are you going to let people add/manage those?
ero
eroOPβ€’3y ago
indeed
Patrick
Patrickβ€’3y ago
ok so we already have 2 domain objects, Leaderboard and User are you going to store users yourself or use a third party i.e. OAuth/etc
ero
eroOPβ€’3y ago
i don't know what that means
Patrick
Patrickβ€’3y ago
are you going to have a sign up page where users put in their email and a password, or are you going to shell out authentication to Discord/GitHub/Facebook/etc
ero
eroOPβ€’3y ago
there's also like dozens of objects more both...? i guess?
Patrick
Patrickβ€’3y ago
sure, im starting from scratch to look at requirements, because you said you don't know where to start ok so immediately you're going to need to build from the ground up a user controller which can handle the sign up so a request to the API would look like POST /api/user which adds a new user, so we'll have a UserController.
ero
eroOPβ€’3y ago
like i'd like to make sign up as easy as possible and would like to support at least google and github
Patrick
Patrickβ€’3y ago
is this an MVC site?
ero
eroOPβ€’3y ago
but also give the option not to use either of course it's not a site at all just the api frontend is TS and Vue
Patrick
Patrickβ€’3y ago
ok well you'll need a controller to handle manual sign up
[ApiController]
public class UserController : ControllerBase
{
private readonly EntityContext _db;

public UserController(EntityContext db) => _db = db;

[HttpPost]
public async Task<IActionResult> Add([FromBody] UserDto user)
{
var userEntity = new User
{
Username = user.Username,
Password = HashPassword(user.Password)
};

_db.Add(userEntity);
await _db.SaveChangesAsync();
return Ok();
}
}
[ApiController]
public class UserController : ControllerBase
{
private readonly EntityContext _db;

public UserController(EntityContext db) => _db = db;

[HttpPost]
public async Task<IActionResult> Add([FromBody] UserDto user)
{
var userEntity = new User
{
Username = user.Username,
Password = HashPassword(user.Password)
};

_db.Add(userEntity);
await _db.SaveChangesAsync();
return Ok();
}
}
the controller just handles the request and orchestrates, in this case we're just putting the business logic of how to sign up a user into the controller
ero
eroOPβ€’3y ago
i don't exactly... get the point of telling me this? i know that
Patrick
Patrickβ€’3y ago
because you don't know what a controller is
ero
eroOPβ€’3y ago
giving me the code doesn't tell me what a controller is
Patrick
Patrickβ€’3y ago
the controller just handles the request and orchestrates, in this case we're just putting the business logic of how to sign up a user into the controller
ero
eroOPβ€’3y ago
the user controller controls things that have to do with the user cool
Patrick
Patrickβ€’3y ago
ASP.NET Core is a phone book, a controller is just a name for a class that is a page in that phone book, every public method in that controller is a phone number someone can call.
ero
eroOPβ€’3y ago
hm
Patrick
Patrickβ€’3y ago
i can't do much with "hm", i suppose you're not interested in me helping which is all you need to say. my final advice is; since you haven't done this before you don't need to over engineer it. Scalability of users rarely depends on class structure or frameworks you use πŸ‘
ero
eroOPβ€’3y ago
i don't want to publish an incomplete product i want it to be perfect the moment it goes online
Patrick
Patrickβ€’3y ago
won't happen, that's not how software is built
ero
eroOPβ€’3y ago
it's supposed to be a competitor to another website. what's the point in releasing something that isn't already better
Patrick
Patrickβ€’3y ago
and your competitor is out there growing and improving, while you're here pondering class structure
ero
eroOPβ€’3y ago
i want it to be easy to read, easy to understand, easy to maintain, and easy to contribute to
Patrick
Patrickβ€’3y ago
competing in the software space is about MVPs
ero
eroOPβ€’3y ago
while also being fast, and feature complete
Patrick
Patrickβ€’3y ago
here's what's going to happen - you build your software today on the requirements/priorities you think are important. You release the software. You're going to run into problems. Your users are going to request features, your priorities change. Suddenly the beautiful architecture you have isn't proven, falls apart and you need to reprioritise. this is why software engineers exist; no one builds or ships perfect products
Timtier
Timtierβ€’3y ago
I do understand the desire to get a "perfect" or "complete" solution. Though I often have had projects grind to a halt because of it so I would be hesitant to start small and improve iteratively instead I follow "Done is better than perfect" as I otherwise get stuck with an overengineered solution; nicely designed but potentially overkill for what the customer needs I'd recommend to first and foremost look into; how will what you create have the competitive edge over other leaderbord services/sites that you are competing against? And put your eggs in that basket. Just start simple to realize the basic functionality for that, gather feedback and iterate on that. Often, simplest code is best anyhow You can still go with the flow of your source generator and project layout, but will you need it for the key functionality your API needs? I still have to a bunch prev messages so apologies if I make any assumptions
ero
eroOPβ€’3y ago
i mean again, i just don't know where to start like it's not that i don't know how to do it that's part of it but i also just don't know how to even begin there's so much i need to do, and so much i don't know, it's like impossible to do anything and i can't even lay it all out! because i don't know how to best put things together, or what's even possible
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOPβ€’3y ago
just any of the dozens which are .net 7 minimal api tutorial playlists right
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
Pobiega
Pobiegaβ€’3y ago
Yeah very few changes in asp.net from 6 to 7
ero
eroOPβ€’3y ago
i don't even really know like... what to do first? like do i create users first? series? leaderboards? categories? submissions? posts? or do i not create any models at all first and worry about the database instead setting up postgres and whatever
Pobiega
Pobiegaβ€’3y ago
Could start with sqlite or an in-memory EF database while you learn the ropes no reason to go ham with migrations and stuff before you know what you are doing tbh And doing github + google + manual users all at once will take some tinkering to get going, authentication can be a lot of work
ero
eroOPβ€’3y ago
ugh i don't know what to do :/
Pobiega
Pobiegaβ€’3y ago
Start out small.
Timtier
Timtierβ€’3y ago
And if you dont know how to start; go by your data structure and start with the objects that do not depend on the others
ero
eroOPβ€’3y ago
i can't think of a single thing that doesn't depend on something else
Timtier
Timtierβ€’3y ago
What is a serie and what does it do?
ero
eroOPβ€’3y ago
a series is a collection of 1 or more leaderboards it has 1 or more moderating users with one of those users being the "owner" so to speak (the moderator with the most rights)
Timtier
Timtierβ€’3y ago
I see
ero
eroOPβ€’3y ago
a series also has a forum, a user which requested the series in the first place (this is the user who is made "owner" initially. the owner can change later), and a user which accepted the series (a site admin) or rejected it, as the case may be
Timtier
Timtierβ€’3y ago
And a form haa said posts, a leaderboard has submissions?
ero
eroOPβ€’3y ago
a forum is a collection of 0 or more posts, where a post is something like a linked list (child posts == comments) a post can be locked (or not) and stickied (or not) maybe i'll make a distinction between a ForumPost and a normal Post a leaderboard is a collection of 0 or more Categories where there'll have to be a distinction between level categories and game categories, maybe they'll certainly be displayed separately a category has a lot of settings, so does a leaderboard and a category is basically a collection of submissions also a category must be either go by time, or by score INumericalMetric, if you will
Timtier
Timtierβ€’3y ago
So in this case, the different type of users might be the only entity that doesn't have any direct dependencies? So we could start with the different user types
ero
eroOPβ€’3y ago
"different types"?
Timtier
Timtierβ€’3y ago
Previously you described an owner and a site admin
ero
eroOPβ€’3y ago
why should those be different? can't i just give them like a Role enum? or use claims or whatever they're called
Pobiega
Pobiegaβ€’3y ago
Yup, either claims or roles would work here. roles likely being the easiest.
ero
eroOPβ€’3y ago
but also a User can be the Owner of a Series, a Leaderboard, or a (Forum)Post the "owner" wasn't about the website
Pobiega
Pobiegaβ€’3y ago
well, isnt that more that the Series etc has an Owner prop?
ero
eroOPβ€’3y ago
i suppose
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOPβ€’3y ago
maybe it's pretentious, but i think this is far from simple
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
Timtier
Timtierβ€’3y ago
Even so, nothing wrong with trying
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
Timtier
Timtierβ€’3y ago
So going back to the previous models discussed Ero, let's say I'd want to create a Series on your frontend, what would I need to fill in?
ero
eroOPβ€’3y ago
(singular of series is series) i mean come on i even put it in parens to show that i don't care about it that much
Timtier
Timtierβ€’3y ago
I know I know πŸ˜‚
ero
eroOPβ€’3y ago
you have to provide the name of the series, you have to provide which leaderboards go into it (a minimum of 2), and you have to be the Owner of at least one of those leaderboards such a series request opens a "post" where a site admin and the requesting user can discuss things if needed (kinda like a pull request)
Timtier
Timtierβ€’3y ago
I see. I assume those leaderboards are pre-existing leaderboards or do you create them as part of the creation of a series?
ero
eroOPβ€’3y ago
they have to be pre-existing
Timtier
Timtierβ€’3y ago
Alright So based on this, if I were to mock what I would send to the backend, I would send something along the lines of (probably simplified):
{
"name" : "Name of series",
"leaderboards": [1, 2, 3]
}
{
"name" : "Name of series",
"leaderboards": [1, 2, 3]
}
The name being input that I fill in
Pobiega
Pobiegaβ€’3y ago
You'll likely want to model that as some kind of "SeriesRequest" entity, that can be created if conditions are fulfilled (owner of at least one included leaderboard etc), that then has its own lifecycle. Only once accepted does it actually "turn into" a new series.
ero
eroOPβ€’3y ago
i mean you wouldn't send anything CreateSeries would not be AllowAnonymous
Timtier
Timtierβ€’3y ago
Right, but assuming I am a logged in user, this is what I would send. I will get back to that part
Pobiega
Pobiegaβ€’3y ago
CreateSeriesRequest, would users not be able to do that?
ero
eroOPβ€’3y ago
i mean there's another layer of abstraction by the frontend i'd guess
Pobiega
Pobiegaβ€’3y ago
AcceptSeriesRequest obviously requires some form of admin rights even so, your backend can never fully trust the frontend and should validate everything itself too
ero
eroOPβ€’3y ago
of course
Timtier
Timtierβ€’3y ago
Still, lets stay with the request for "creating" the series before the rest of the flow Start simple and all that
ero
eroOPβ€’3y ago
but yeah, i guess the frontend would send the name of the series, the leaderboard IDs, and the ID of the user who created the request
Timtier
Timtierβ€’3y ago
Yep, though the ID matters a bit on your implementation of authorization and how you'd pass that
Pobiega
Pobiegaβ€’3y ago
ID of the user is implied by being the user issuing the call to the backend, no?
ero
eroOPβ€’3y ago
i don't know how frontend works
Timtier
Timtierβ€’3y ago
But for the sake of simplicity, lets stay away from the user part
ero
eroOPβ€’3y ago
it shouldn't be the user issuing the call, right? like that doesn't sound right
Pobiega
Pobiegaβ€’3y ago
via the frontend, it is
Timtier
Timtierβ€’3y ago
It does, let me see if I can clarify it
ero
eroOPβ€’3y ago
my frontend sends me data
Pobiega
Pobiegaβ€’3y ago
frontend is an application running on the users computer
ero
eroOPβ€’3y ago
and the frontend should also check whether the request can be made at all
Pobiega
Pobiegaβ€’3y ago
thats fine but its possible to get around those πŸ™‚
ero
eroOPβ€’3y ago
like a request with 1 leaderboard is invalid
Pobiega
Pobiegaβ€’3y ago
sure
ero
eroOPβ€’3y ago
and a request where the user isn't an owner of any of the boards is also invalid
Timtier
Timtierβ€’3y ago
Yep
ero
eroOPβ€’3y ago
so the "make request" button should simply not be clickable
Pobiega
Pobiegaβ€’3y ago
this is stuff you'd slap in a FluentValidation validator or something, imho on the DTO
Timtier
Timtierβ€’3y ago
Long story short, the backend will be the source of truth for that.
ero
eroOPβ€’3y ago
i should not even be able to receive a request which is not valid
Timtier
Timtierβ€’3y ago
Why not?
Pobiega
Pobiegaβ€’3y ago
Yeah. Your frontend wont allow users to click the button to issue the request if not, but even so, people can and will try and send invalid requests via devtools
ero
eroOPβ€’3y ago
because i say so because if you cannot click the button, you cannot make a request
Pobiega
Pobiegaβ€’3y ago
wrong
Timtier
Timtierβ€’3y ago
That's the fun part πŸ˜„
Pobiega
Pobiegaβ€’3y ago
devtools/insomnia/curl says you can πŸ˜›
Timtier
Timtierβ€’3y ago
If I can use your API, I can, without using your front-end Which is spooky
Pobiega
Pobiegaβ€’3y ago
yeah. This is why we say "never trust the frontend"
Timtier
Timtierβ€’3y ago
But which is why you at the minimum add your validation in the backend system Your frontend can also have validation, that's fine
Pobiega
Pobiegaβ€’3y ago
99.9% of all requests will be via your frontend and not manipulated, but there will be some that are entirely bogus or contain malicious input
Timtier
Timtierβ€’3y ago
But to go back to the first part. So you send the name and leaderboard IDs that you wish to use to create a series
ero
eroOPβ€’3y ago
if i don't send the user ID, how would i validate anything?
Timtier
Timtierβ€’3y ago
Usually, you would not just send an ID, because that can be faked easily. You would send something within your request, like a token or something similar, that the backend can recognize and make use of.
ero
eroOPβ€’3y ago
api tokens, right...
Timtier
Timtierβ€’3y ago
Since you want to support Google users and such, this would be an OAuth token or something similar. Anyhow, don't worry about that part yet But once it reaches your backend, somewhere in your backend you will have to perform the checks you described, starting with; - Does this request have a token? - Do I recognize this token as a User of my system?
ero
eroOPβ€’3y ago
right
Timtier
Timtierβ€’3y ago
This might be some built-in functionality of ASP.NET, it might be some library or package, doesn't quite matter. Or well, not at this point of the discussion πŸ˜„ So if the request does not have a token or is not a recognized user, you would usually return a response along the lines of Unauthorized Status Code 401, which basically means; you tried to do something to my backend, but I do not allow you to
Timtier
Timtierβ€’3y ago
Unrelated shoutout to https://http.cat/ πŸ˜„
HTTP Status Cats API
HTTP Cats
API for HTTP Cats
ero
eroOPβ€’3y ago
no token would be bad request, no? wrong token would be unauthorized
Timtier
Timtierβ€’3y ago
Hmmm. I would argue both are unauthorized The wrong auth is the same as using no auth You can use whatever you want but I generally keep BadRequest for; you're allowed to make a request, but you filled in something wrong. For example; a leader board you do not own
ero
eroOPβ€’3y ago
that's unauthorized to me lol like "hey, you don't have rights to do anything here" as in, you're not authorized to unauthorized
Timtier
Timtierβ€’3y ago
I get why you'd think that would be unauthorized, though usually after you pass the authorization as a user, people use BadRequest to say "Hi, I know who you are but you're not allowed to do this" Anyhow, you can use either, your implementation But lets say a user sends this request to create a series, goes past the check which verifies their token. They'll end up in the "actual" implementation of the endpoint
ero
eroOPβ€’3y ago
but then why is passing the wrong token to a series creation request unauthorized?
Timtier
Timtierβ€’3y ago
Because you're not authorized to perform the request. Performing meaning that the server will try to process it at all
ero
eroOPβ€’3y ago
thoroughly confused
Timtier
Timtierβ€’3y ago
At this point it doesn't care what the contents of the series creation request are or if they're correct The endpoint I chose as example is a bit annoying because of the ownership part of leaderboards. So to maybe simplify it a bit; what about if a user wants to make a post on a forum Completely different request of course The request towards the backend will probably contain something along the lines of; their post text and the ID of the forum they are posting on Similarly, it gets to the backend and checks (assuming part of your design here); hey, this endpoint requires a user, as I do not allow anonymous people to post. So it checks if you have a token and if it is a token of a known user. So in this example, lets assume yes for both; it will enter the endpoint logic. Endpoint logic being whatever you put in the Controller method or minimal API route.
Pobiega
Pobiegaβ€’3y ago
Regarding Authorization, you normally do that before entering the actual code that handles your request itself, ie the controller action or "endpoint" code. ASP.NET will look at the attributes you've assigned on the controller and action and on the users claims/roles etc and see if they are allowed to even access the route - only then does your code take over and can look at the specifics, such as "is the leaderboard specified one that you own?" business authorization takes place after API level authorization for example, at work our apis are quite large and in several places contain lines like SecurityAssert.StaffUserCanAccessPatient(dto.PatientId) just because you are a valid staff user with a care assignment doesnt mean you can access this patient.
ero
eroOPβ€’3y ago
i mean, that seems obvious yeah
Pobiega
Pobiegaβ€’3y ago
yup. its just that ASP can block a lot of requests from even reaching that point, because their token is invalid or expired or doesnt contain the right roles meaning that for us to do the expensive stuff (database queries usually), we know you are quite likely to be a real user and not doing funky stuff
Timtier
Timtierβ€’3y ago
So once a request enters your Create Series endpoints, the following would probably happen; First, if you add validation, the "stupid" validation comes first; is the name not empty, are all leaderboard ids above 0, etc. If all that's fine, you would get to the more "expensive" logic which would be the actual validation; do I own one of these leaderboards? Ofc this check entirely depends on your design but lets say you have some LeaderboardService which abstracts some logic away, it could be something like.
foreach(var leaderboardId in requests.leaderboard)
{
var leaderboard = await _leaderboardService.GetLeaderboardByIdAsync(leaderboardId);

if (leaderboard.Owner.Id == UserId)
{
// You own at least one, you set a bool here or something.
}
}
foreach(var leaderboardId in requests.leaderboard)
{
var leaderboard = await _leaderboardService.GetLeaderboardByIdAsync(leaderboardId);

if (leaderboard.Owner.Id == UserId)
{
// You own at least one, you set a bool here or something.
}
}
Pobiega
Pobiegaβ€’3y ago
and break the loop πŸ˜„
Timtier
Timtierβ€’3y ago
Depends on the checks, its a dumb poc example Otherwise some sort of _leaderboardsService.GetUserLeaderBoards(UserId);, bla bla and then go through that collection to see if it contains at least one of the request leaderboard ids ^ but i digress If it matches your own rule of "I must own at least one" then you pass the request values to the logic that actually creates the series Which might at this point just be a dumb database insert, who knows How the rest of your application does its' logic depends entirely on how you design it You can take the approach of Pobiega and put it in the controller, like here: https://discord.com/channels/143867839282020352/1045068329502646372/1046411827317321819
MODiX
MODiXβ€’3y ago
Pobiega#2671
[HttpGet("{id}")]
public ActionResult<User> GetUser(long id)
{
var user = _database.Users.FirstOrDefault(x => x.Id == id);

if (user is null)
{
return NotFound();
}

return user;
}
[HttpGet("{id}")]
public ActionResult<User> GetUser(long id)
{
var user = _database.Users.FirstOrDefault(x => x.Id == id);

if (user is null)
{
return NotFound();
}

return user;
}
Quoted by
<@!205052896599867392> from #Creating a backend in .NET (click here)
React with ❌ to remove this embed.
Timtier
Timtierβ€’3y ago
Or a different way entirely I have my own gripes with that structure, though it is very quick to get something up and running with it
ero
eroOPβ€’3y ago
see and that's why i want source gen right? having to make the services and validation all separately is like
Pobiega
Pobiegaβ€’3y ago
Oh yeah I don't do app logic in controllers. Β―\_(ツ)_/Β―
Timtier
Timtierβ€’3y ago
Services sounds a bit scary but eh
Pobiega
Pobiegaβ€’3y ago
I don't think you can generate all those parts, since they are not just copypaste
Timtier
Timtierβ€’3y ago
That's just your logic, moved to a different class your controllers are generally stupid They verify requests, pass it along, possibly catch unexpected results, and give a response
Pobiega
Pobiegaβ€’3y ago
command+handler/service + fluentvalidation + functional result (Result<T>) is a very good start.
Timtier
Timtierβ€’3y ago
Even command/handler/CQRS might be a bit overkill here But if you want reusability, source generator is not really the solution here It's an overly complex solution for the what, 10 models you are making at the most?
ero
eroOPβ€’3y ago
hard to believe it'll only be 10
Timtier
Timtierβ€’3y ago
Right, but even if you double it, still managable πŸ˜›
Patrick
Patrickβ€’3y ago
Respectfully you need to actually start building something. All the architecture and frameworks here are over engineered when you can’t build a simple API There’s a reason why MS documentation for ASP.NET doesn’t include services, CQRS or other convoluted measures for architecture.
Timtier
Timtierβ€’3y ago
I'd also say indeed; just start implementing one endpoint in one controller; as simple as possible Once that works, you can refactor it to be more "flexible". But at that point you have something working you can fall back to
ero
eroOPβ€’3y ago
so uhh. how do i start?
Patrick
Patrickβ€’3y ago
Open up your IDE of choice and start a new ASP.NET Core API project Then make your first controller and work on one action Use the swagger UI to interact with your API
ero
eroOPβ€’3y ago
Alright yeah pobiega and i already did that earlier
Pobiega
Pobiegaβ€’3y ago
You could go on with the usercontroller we made maybe start experimenting with authentication too, as it seems to be very important to your application but start with just doing it locally, dont involve github/google auth for now
ero
eroOPβ€’3y ago
i also need like 2FA, and 3FA for site staff from a project architecture point of view, where would you put the database? like in what namespace?
Pobiega
Pobiegaβ€’3y ago
add 2fa/3fa later database stuff usually goes in its own project, often called Infrastructure or Data, but you can just make that a namespace if you want ofc
ero
eroOPβ€’3y ago
hm, i see
Pobiega
Pobiegaβ€’3y ago
the core class there is your DbContext child
ero
eroOPβ€’3y ago
that much i've gathered what's that? classlib?
Pobiega
Pobiegaβ€’3y ago
yap everything is a lib, except the web api itself
ero
eroOPβ€’3y ago
i mean????
Pobiega
Pobiegaβ€’3y ago
and the whole splitting in multiple projects is not a strict requirement either, its just common practice
ero
eroOPβ€’3y ago
its output type is also Library?
Pobiega
Pobiegaβ€’3y ago
yeah why not?
ero
eroOPβ€’3y ago
so the api is also a lib
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
Pobiega
Pobiegaβ€’3y ago
oh, not the api
ero
eroOPβ€’3y ago
yeah that's a mega no for me
Pobiega
Pobiegaβ€’3y ago
the api is a console app or whatever
ero
eroOPβ€’3y ago
the 1 project thing is it?
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
Pobiega
Pobiegaβ€’3y ago
yup
ero
eroOPβ€’3y ago
but wouldn't the csproj need to specify <OutputType>Exe</> for that? also, i'm torn on what to call the solutions
Pobiega
Pobiegaβ€’3y ago
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>

</Project>
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>

</Project>
this is what I have in the one I've been using as we "coded along" and it works fine πŸ˜›
ero
eroOPβ€’3y ago
i guess it's the Sdk.Web part that changes it
Pobiega
Pobiegaβ€’3y ago
likely, yes
ero
eroOPβ€’3y ago
if you do just dotnet new console, you have that tag so the site is leaderboards.gg, so i just went with LeaderboardsGG.Backend for the solution and project names
Pobiega
Pobiegaβ€’3y ago
thats fine.
ero
eroOPβ€’3y ago
but i changed the namespace to Lbgg should that be LBGG, LbGg?
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOPβ€’3y ago
does to me
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
Pobiega
Pobiegaβ€’3y ago
iirc conventions would say Lbgg or LbGg two letter acronyms is the only thing that goes allcaps
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
Pobiega
Pobiegaβ€’3y ago
even API gets turned into Api when you follow conventions
Timtier
Timtierβ€’3y ago
LeaderboardsGG / Lbgg both are fine so ehhhh
Pobiega
Pobiegaβ€’3y ago
yup
Timtier
Timtierβ€’3y ago
Id prefer the first one
ero
eroOPβ€’3y ago
so should i change the project names too?
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
Pobiega
Pobiegaβ€’3y ago
I prefer to keep project name and namespace synced up
ero
eroOPβ€’3y ago
does my dbcontext go into some namespace too?
Pobiega
Pobiegaβ€’3y ago
usually "top level" of your data project/namespace
Timtier
Timtierβ€’3y ago
Basically every class you make will be in a namespace. Just further namespaces are applied depending on which folders you have
ero
eroOPβ€’3y ago
oh lol no i know that
Timtier
Timtierβ€’3y ago
So if you use those namespaces you're good πŸ˜„
ero
eroOPβ€’3y ago
my infrastructure project needs to know about my domain project, right? so it knows about my entities?
Pobiega
Pobiegaβ€’3y ago
yup
ero
eroOPβ€’3y ago
namespace Lbgg.Infrastructure;

public class LbggDbContext : DbContext
{
public LbggDbContext(DbContextOptions<LbggDbContext> options)
: base(options) { }

public required DbSet<User> Users { get; set; }
}
namespace Lbgg.Infrastructure;

public class LbggDbContext : DbContext
{
public LbggDbContext(DbContextOptions<LbggDbContext> options)
: base(options) { }

public required DbSet<User> Users { get; set; }
}
[HttpPost]
public async Task<IActionResult> Create(CreateUserDto dto)
{
if (await _db.Users.AnyAsync(u => u.Username == dto.Username))
{
return BadRequest();
}

await _db.Users.AddAsync(new()
{
Username = dto.Username,
Email = dto.Email,
Password = dto.Password
});

return Ok();
}
[HttpPost]
public async Task<IActionResult> Create(CreateUserDto dto)
{
if (await _db.Users.AnyAsync(u => u.Username == dto.Username))
{
return BadRequest();
}

await _db.Users.AddAsync(new()
{
Username = dto.Username,
Email = dto.Email,
Password = dto.Password
});

return Ok();
}
Patrick
Patrickβ€’3y ago
You need savechanges but sure
ero
eroOPβ€’3y ago
i've been told not to use AddAsync?
Patrick
Patrickβ€’3y ago
Yes don’t use it avoid it if you can
ero
eroOPβ€’3y ago
why is that?
Patrick
Patrickβ€’3y ago
Because it’s a specialised method to allow for an operation to occur during the Adding process. In almost all cases all you’re doing is adding to a collection in memory which doesn’t need to be an async operation. Needless overhead and complication.
ero
eroOPβ€’3y ago
so i know that sometimes you also return the created user entity should you really do that? it contains the password after all that sounds pretty bad to return
Patrick
Patrickβ€’3y ago
You shouldn’t ever send any entity over the wire Dumping your entity model back to consumers of an API exposes primary and foreign keys, it shows your schema and tightly couples usage of your API with your database
ero
eroOPβ€’3y ago
i meant like return Ok(createdUser);
Patrick
Patrickβ€’3y ago
If createduser is an instance of your entity then my point stands
ero
eroOPβ€’3y ago
mh
Patrick
Patrickβ€’3y ago
If createduser is a DTO then fine
ero
eroOPβ€’3y ago
i mean, what does "show my schema" mean? the entire backend is open source
Patrick
Patrickβ€’3y ago
Exposes your database design, columns
ero
eroOPβ€’3y ago
yeah it's. it's open source
Patrick
Patrickβ€’3y ago
Sure that’s irrelevant
ero
eroOPβ€’3y ago
i mean, doesn't that expose my database design more than anything lol
Patrick
Patrickβ€’3y ago
Yes that’s by virtue of being open source… a couple hours ago you were concerned with building the perfect solution now you want to expose database entities over the wire and circumvent best practice Which is it?
ero
eroOPβ€’3y ago
i don't want to do anything i asked if you should do that
Patrick
Patrickβ€’3y ago
Right and I said no
ero
eroOPβ€’3y ago
i was there
Patrick
Patrickβ€’3y ago
Good luck.
ero
eroOPβ€’3y ago
ok...?
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOPβ€’3y ago
what's that got anything to do with what i said lol
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOPβ€’3y ago
i mean i already do...? i don't understand what's going on
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOPβ€’3y ago
because i've seen people do that
Unknown User
Unknown Userβ€’3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOPβ€’3y ago
could i instead return something like a UserViewDto?
qqdev
qqdevβ€’3y ago
Hi! Just joined What is your entity called?
ero
eroOPβ€’3y ago
User!
qqdev
qqdevβ€’3y ago
You usually just append Dto
ero
eroOPβ€’3y ago
should i?
qqdev
qqdevβ€’3y ago
Kinda depends. Some people always create dto classes I don't
ero
eroOPβ€’3y ago
i don't know if that makes sense here
qqdev
qqdevβ€’3y ago
I would do it if you are a beginner Don't think about it too much
ero
eroOPβ€’3y ago
because i have a CreateUserDto. it's what's passed into the UsersController.CreateUser endpoint(?) then i would maybe have a GeneralUserViewDto which is perhaps passed when a user page is loaded
qqdev
qqdevβ€’3y ago
I see! How would that differ from a UserDto?
ero
eroOPβ€’3y ago
maybe a MinimalUserViewDto for clicking a user's profile picture from a forum post which perhaps yields a pop up like on discord
qqdev
qqdevβ€’3y ago
Where is that View infix coming from?
Anton
Antonβ€’3y ago
dto = view already AutoMapper is great btw, without it the dto mapping code will become unmaintainable, especially if you need to map nested things
qqdev
qqdevβ€’3y ago
that's not true got big dtos at work and it is easy to maintain without AutoMapper Just implement ToDto() methods and you are good to go Or use AutoMapper if you don't want to deal with that and can bear another dependency
ero
eroOPβ€’3y ago
i just don't see how i can use a singular dto for everything
qqdev
qqdevβ€’3y ago
you don't have to do whatever makes sense
ero
eroOPβ€’3y ago
especially because my CreateUserDto contains a Password property it just doesn't make sense to me
qqdev
qqdevβ€’3y ago
it's fine to use multiple ones then just try to keep it slim as much as possible
Anton
Antonβ€’3y ago
Not exactly. If you use EF Core and want to project the database entities, just ToDto is not enough, because EF Core needs expressions, so you'd have to repeat your mapping code for every request, or deal with lower lever expressions. This becomes bad if you have nested things, because then the amount of copypasta multiplies. Either copy pasta, or dealing with combining expression trees, which is no fun
qqdev
qqdevβ€’3y ago
i don't use ef core so I can't tell what do dtos have to do with the database btw?
Anton
Antonβ€’3y ago
It's even rougher for you then. If you want efficient queries, you'd have to select only the things you need in your sql queries