ā 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
@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ā¢2y ago
Message Not Public
Sign In & Join Server To View
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ā¢2y ago
Message Not Public
Sign In & Join Server To View
TS, JS, Vue
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
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ā¢2y ago
Message Not Public
Sign In & Join Server To View
i do not
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.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 somewhereIt'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).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
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.in this fashion, would
api/customers?id=5
be bad practice?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
etcyeah, this'll likely be a struggle point for me
choosing what to put into the url, and what to put into the query
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
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
Yup, very much so.
Not to mention that "best pratice" changes every few weeks, š
truuue
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
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
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
i believe a minimal API is all i need as well?
hm, maybe not
I think with .net 7 you can do almost everything with either approach
the project is big. like thousands of users with a massive amount of DB data big
minimal has better performance, but its not like controllers are terribly slow or anything
it all comes down to code style preference
(i already know this, because it's gonna be a competitor to an existing site which basically went to shit under new management)
with controllers, you tend to make stuff like...
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 efforti 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
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
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
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
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))
What we do at work, is we have two separate APIs more or less
one for our own frontend, and one for external users
and each leaderboard contains hundreds of submissions
with potentially multiple players
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
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?
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
right
but it also means you might get issues/PRs to help fix
free pentesting, so to speak š
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?
Test it out, see how it feels.
this video from nick explains the "abstraction" level I tend to use for minimal apis
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...
I quite like FastEndpoints too thou, if only it would let you instantiate the response model yourself so records would be usable :((((
You can add an empty ctor to records, no?
Absolutely but then you can't use the short form definition
mh, right
so, where should i start?
dotnet new webapi
šhah, fair
(and pretty much exactly what i wanted to hear)
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
perhaps somewhat unrelated, but i care a lot about project structure
let me run by you what i'd do
Makes sense
Project structure is important
I think tebe recently made a tag for a project scaffold that looked nice. Was it...
$scaffolding
?
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 projectPossible, but would require changing the default web host builder a bit
Ie, not using the default :p
fine by me
shouldn't be more than 2 lines right?
Eh, probably closer to 10
But still doable
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.@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?
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
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
minimal apis only really replace the controller
and the entity?
or the dto i guess?
you still need those.
and DTOs too
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?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?
this could be handled by that
HttpModel
attribute too i guessor if you have multiple endpoints using the same entity with different information, like an admin vs a user endpoint
i feel like minimal apis just aren't it
Controllers are still a thing
so you can absolutely just use them if you prefer that
mh. is there like anything to get me started on this?
the source gen stuff?
or just the controllers?
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
right
i don't know what they do
well lets go though that then, might clear some things up
i don't know how to test things, how to run things to test them
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
what's a domain object
this project, you said its about game leaderboards right?
yup
i mean i know what my entities are right
like User, Leaderboard, Submission
yeah exactly
those are your entities.
its the stuff you save in your database and your core logic work with
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
?usually just
x.Domain
a classlib that contains your entities and little else.really
an entire project just for the entities
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
would you call them
UserEntity
or just User
?User
would they be records or classes?
i believe you've said you use records for everything
depends on what database ORM you use
EF for example doesnt like record entities
if you use dapper, I think its fine
still? in net7?
well, EFs primary thing is the change tracker
if your entites are immutable...
no changes to track :p
right
class it is
so, guid or ulong for ids?
I like guids.
prevents object enumeration, which is a security flaw
but i do want users to be enumerated
i guess not the entities in particular
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 IDwhy'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
alright, if thats a desired trait then go ahead and use ulongs
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 discordShouldn't be much of a problem.
also i've heard that
ulong
is a problem, but long
less so
is there any merit to that?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
also by this i mean that
_'-
, if used at all, must be surrounded by alphanum characters
so __
or a-
don't work for usernamesright.
Thats all up to you, not a problem either way
but also then the user page link will have to contain their id, which is ugly
if you want a discord like system, there are two unique identifiers for a user right
the ID, and the "name#number"
right
I believe the discord API term for the id is the snowflake
yup
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 fineis that a valid url?
didn't know if you could include the hash
hm right, hash is used for html anchors
so probably not
would it be very difficult to change from usernames to tags later on?
i imagine so
hm, unless
like going from just ID + name, to ID + tag + name?
yeah
probably not, as long as you used the ID for all relationships
right
i'll go with normal usernames then for now
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
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
that's understandable
i'll leave the attributes out for now
so we're here now
seems good!
things subject to change of course
ofc
and you just have this in the root of the
.Domain
project?
not like .Domain.Entities
or something?probably something like the latter, as I tend to eventually add more things to Domain
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
yup, next step is figuring out what endpoints you want
so that means deciding on minimal vs controller
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 jobprimarily 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
this would make the switch from usernames to tags more difficult i think
indeed it would
or well
would it?
well, not for me
your endpoint would swap to be /u/tag
but it'd result in a lot of broken links
like if someone has their user page linked somewhere
right, true
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
well, then go with tags from the start?
if its a feature you know you'll want later on
i'd just gonna go with IDs in the url i think
because people can change their tags too right
š
that's the whole point
yep
so you'd get broken links anyways
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?
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)
does any of this have to do with the speed of the requests?
i care a lot about speed
it does. last time I saw numbers, I think it was 11-15% faster
with minimal apis
oh boy
granted, you're still in the "handles 10000 requests per second" bracket without even trying
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
sounds neat, just worried it might be hard to implement nicely
yeah...
for example an
UpdateUser
endpoint might not take in the same DTO as a CreateUser
mh
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
i've been wanting to use commands ever since i saw someone (i believe it was you?) use source gen with them as well
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
this is what someone has posted in the past
i saw this and liked it a lot
and then you have
Hm, thats neat. And it would generate the Http endpoint I assume
or something
so the
Create.Command
record would be my Create
dto i guessexactly.
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
alright
just the full thing
minimal api, commands
š
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 muchThere 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
besides me not even knowing what a repository is in this context
I'd probably stick them in
X.Web.Endpoints.Users
and that would contain the endpoints themselves, the DTOs, etc
possible as subnamespacesso let's say, something like this?
yup
well
I'd scope it down a bit more
the
CreateUser
dto is only ever relevant within the CreateUser endpoint reallyoh, that was supposed to be in
Lbgg.Endpoints.Users.Dtos
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
i wonder...
how fun would this be
it just like generates
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
Not a problem, thats available with both minimal APIs and controllers
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.
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
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
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 usersprobably 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
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?yeah
i deleted everything though
like the stupid ass weather forecast stuff
not a problem
what does your program.cs look like?
man
can't display empty blocks
it's empty
okay, lets add the basics back in then
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
lots of things there that i still don't know
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
right
development cert for HTTPS
oh yeah i wanna use postgres
so you dont get warnings that
localhost
doesnt have a valid trusted HTTPS certi guess that doesn't have anything to do with the web part actually
nope, thats fine
npgsql has excellent connectors for C# and EF, or dapper or whatever you wanna use
ok yeah that works
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:
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 contexti don't really know what DI or DI containers are
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();
i guess i just don't really understand what we're injecting?
instead, we say that the controller gets a constructor parameter for the database connection
what dependency are we injecting
the database connection.
to where
for once we have one
to the controller, in this case.
where are we injecting it
i'm so confused
we're not doing anything though
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
alright
swagger ain't too smart, huh?
hm?
this is what I get.
should be
uint64
hm, yeah it should. curious that it didnt figure that out tbh
and yet it knows that
-123
is invalidOpenAPI 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.
no unsigned types in that specification
bit silly
i guess i might just go with long then?
Sure, thats fine
but hey, we have a working endpoint that responds as expected
yup
whats the next step? making it actually remember something?
i suppose, yeah
alright, the simplest "in memory database" is just a class with lists in it š
so we can make one of those for now
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 staticwhy not?
what part? not being static?
yeah the static part
not like i'm looking for reasons to use static
i just wanna know why
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
EF was the idea yeah
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:
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"alright
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
oh sick
so a logical rewrite of our get user method would be...
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 wantmakes sense
okay cool.
lets try making the "CreateUser" endpoint now
wanna give it a go?
sure thing
hm
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 š
but what do i return?
there's no
Success()
and no ActionResult.Success
oh it's Ok()
why are they even methods
pisses me offWell, because they often take arguments
ah, right
is there a clear "next step"?
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
let's do this first and i'll worry about the rest after
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
i do wanna go through the hassle for now
so i at least understand
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 timeso i really didn't understand a lot of that
what's a dispatcher
what's a handler in this context
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...
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
but as said, that means instead of just injecting the dispatcher, you'll need to know what handlers you need
i was wondering more about the actual implementation, not just how it'll be used in the end
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
ah, that was part of the nick video
yeah he does assembly scanning there
You can then resolve the handlers based on the command type
surprisingly less lines than i expected
Yeah the reflection stuff isnt too bad. In general the very basics isn't too much code in general, but it adds up
need to wait for patrick to go back to bed to ask my source gen question i guess
you realise im asking those questions to make you realise it's backward
won't leave me alone
because making remarks about someone behind their back... is great?
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
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
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
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
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
both
ok so what is it
what's what
your project
gaming leaderboards
for score and time
ok and are you going to let people add/manage those?
indeed
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/etci don't know what that means
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
there's also like dozens of objects more
both...? i guess?
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
.like i'd like to make sign up as easy as possible and would like to support at least google and github
is this an MVC site?
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
ok well you'll need a controller to handle manual sign up
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
i don't exactly... get the point of telling me this?
i know that
because you don't know what a controller is
giving me the code doesn't tell me what a controller is
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
the user controller controls things that have to do with the user
cool
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.
hm
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 š
i don't want to publish an incomplete product
i want it to be perfect the moment it goes online
won't happen, that's not how software is built
it's supposed to be a competitor to another website. what's the point in releasing something that isn't already better
and your competitor is out there growing and improving, while you're here pondering class structure
i want it to be easy to read, easy to understand, easy to maintain, and easy to contribute to
competing in the software space is about MVPs
while also being fast, and feature complete
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
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
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ā¢2y ago
Message Not Public
Sign In & Join Server To View
just any of the dozens which are .net 7 minimal api tutorial playlists
right
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
Yeah very few changes in asp.net from 6 to 7
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
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
ugh i don't know what to do :/
Start out small.
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
i can't think of a single thing that doesn't depend on something else
What is a serie and what does it do?
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)
I see
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
And a form haa said posts, a leaderboard has submissions?
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 willSo 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
"different types"?
Previously you described an owner and a site admin
why should those be different?
can't i just give them like a
Role
enum?
or use claims or whatever they're calledYup, either claims or roles would work here.
roles likely being the easiest.
but also a
User
can be the Owner
of a Series
, a Leaderboard
, or a (Forum)Post
the "owner" wasn't about the websitewell, isnt that more that the
Series
etc has an Owner
prop?i suppose
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
maybe it's pretentious, but i think this is far from simple
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
Even so, nothing wrong with trying
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
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?
(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
I know I know š
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)I see. I assume those leaderboards are pre-existing leaderboards or do you create them as part of the creation of a series?
they have to be pre-existing
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):
The name being input that I fill in
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.
i mean you wouldn't send anything
CreateSeries
would not be AllowAnonymous
Right, but assuming I am a logged in user, this is what I would send.
I will get back to that part
CreateSeriesRequest
, would users not be able to do that?i mean there's another layer of abstraction by the frontend i'd guess
AcceptSeriesRequest
obviously requires some form of admin rights
even so, your backend can never fully trust the frontend and should validate everything itself tooof course
Still, lets stay with the request for "creating" the series before the rest of the flow
Start simple and all that
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
Yep, though the ID matters a bit on your implementation of authorization and how you'd pass that
ID of the user is implied by being the user issuing the call to the backend, no?
i don't know how frontend works
But for the sake of simplicity, lets stay away from the user part
it shouldn't be the user issuing the call, right?
like that doesn't sound right
via the frontend, it is
It does, let me see if I can clarify it
my frontend sends me data
frontend is an application running on the users computer
and the frontend should also check whether the request can be made at all
thats fine
but its possible to get around those š
like a request with 1 leaderboard is invalid
sure
and a request where the user isn't an owner of any of the boards is also invalid
Yep
so the "make request" button should simply not be clickable
this is stuff you'd slap in a FluentValidation validator or something, imho
on the DTO
Long story short, the backend will be the source of truth for that.
i should not even be able to receive a request which is not valid
Why not?
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
because i say so
because if you cannot click the button, you cannot make a request
wrong
That's the fun part š
devtools/insomnia/curl says you can š
If I can use your API, I can, without using your front-end
Which is spooky
yeah. This is why we say "never trust the frontend"
But which is why you at the minimum add your validation in the backend system
Your frontend can also have validation, that's fine
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
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
if i don't send the user ID, how would i validate anything?
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.
api tokens, right...
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?
right
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
no token would be bad request, no?
wrong token would be unauthorized
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
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
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
but then why is passing the wrong token to a series creation request unauthorized?
Because you're not authorized to perform the request. Performing meaning that the server will try to process it at all
thoroughly confused
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.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.i mean, that seems obvious yeah
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
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.
and break the loop š
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/1046411827317321819Pobiega#2671
Quoted by
<@!205052896599867392> from #Creating a backend in .NET (click here)
React with ā to remove this embed.
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
see and that's why i want source gen right? having to make the services and validation all separately is like
Oh yeah I don't do app logic in controllers.
ĀÆ\_(ć)_/ĀÆ
Services sounds a bit scary but eh
I don't think you can generate all those parts, since they are not just copypaste
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
command+handler/service + fluentvalidation + functional result (Result<T>) is a very good start.
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?
hard to believe it'll only be 10
Right, but even if you double it, still managable š
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.
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
so uhh. how do i start?
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
Alright yeah pobiega and i already did that earlier
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
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?
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 ofchm, i see
the core class there is your
DbContext
childthat much i've gathered
what's that? classlib?
yap
everything is a lib, except the web api itself
i mean????
and the whole splitting in multiple projects is not a strict requirement either, its just common practice
its output type is also Library?
yeah why not?
so the api is also a lib
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
oh, not the api
yeah that's a mega no for me
the api is a console app or whatever
the 1 project thing
is it?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
yup
but wouldn't the csproj need to specify <OutputType>Exe</> for that?
also, i'm torn on what to call the solutions
this is what I have in the one I've been using as we "coded along"
and it works fine š
i guess it's the Sdk.Web part that changes it
likely, yes
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 namesthats fine.
but i changed the namespace to
Lbgg
should that be LBGG
, LbGg
?Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
does to me
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
iirc conventions would say
Lbgg
or LbGg
two letter acronyms is the only thing that goes allcapsUnknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
even
API
gets turned into Api
when you follow conventionsLeaderboardsGG
/ Lbgg
both are fine so ehhhhyup
Id prefer the first one
so should i change the project names too?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
I prefer to keep project name and namespace synced up
does my dbcontext go into some namespace too?
usually "top level" of your data project/namespace
Basically every class you make will be in a namespace. Just further namespaces are applied depending on which folders you have
oh lol no i know that
So if you use those namespaces you're good š
my infrastructure project needs to know about my domain project, right?
so it knows about my entities?
yup
You need savechanges but sure
i've been told not to use
AddAsync
?Yes donāt use it avoid it if you can
why is that?
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.
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
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
i meant like
return Ok(createdUser);
If createduser is an instance of your entity then my point stands
mh
If createduser is a DTO then fine
i mean, what does "show my schema" mean?
the entire backend is open source
Exposes your database design, columns
yeah it's. it's open source
Sure thatās irrelevant
i mean, doesn't that expose my database design more than anything lol
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?
i don't want to do anything
i asked if you should do that
Right and I said no
i was there
Good luck.
ok...?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
what's that got anything to do with what i said lol
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
i mean i already do...?
i don't understand what's going on
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
because i've seen people do that
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
could i instead return something like a
UserViewDto
?Hi! Just joined
What is your entity called?
User
!You usually just append
Dto
should i?
Kinda depends. Some people always create dto classes
I don't
i don't know if that makes sense here
I would do it if you are a beginner
Don't think about it too much
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 loadedI see! How would that differ from a
UserDto
?maybe a
MinimalUserViewDto
for clicking a user's profile picture from a forum post
which perhaps yields a pop up like on discordWhere is that
View
infix coming from?dto = view already
AutoMapper
is great btw, without it the dto mapping code will become unmaintainable, especially if you need to map nested thingsthat'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 dependencyi just don't see how i can use a singular dto for everything
you don't have to
do whatever makes sense
especially because my
CreateUserDto
contains a Password
property
it just doesn't make sense to meit's fine to use multiple ones then
just try to keep it slim as much as possible
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 funi don't use ef core so I can't tell
what do dtos have to do with the database btw?
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
it should be a dto <-> entity relationship
you get the entity from the db and convert it to a dto and vice versa
No, that's the incorrect way of doing it
Doing that you fetch all data of that entity when you only need a subset
The right way is to
.Select
into a dto, if you use efcore
if you just write raw queries, that would mean select into dictionaries or anonymous objects or whatever, and then mapping into the target typeIf we're being a bit more strict, it should be
Database Model <---> Domain Model <---> DTO Model
Do you have an example? @AntonC
To get familiar with the ef core approach
either select out the properties like a normal select statement into the dto, or use an expression like
With automapper, you do
dbContext.Users.ProjectTo<MinimalUserDto>()
, which would be equivalent to
I see
Which generates the following SQL
So it doesn't fetch the whole user
So you could just implement
ToDto()
methods I guess?
And use it on each property you actually needIt just being a method is not enough, that thing you see in the argument of select is an
Expression<Func<User, MinimalUserDto>>
, it's not just a func
It builds an actual expression treemy entities don't evne know about my dtos
If you were to use a normal extension method instead, it would query the whole user and then do the mapping after the fact
they're in different projects entirely
or it would just be an error, I don't remember
What about that?
This would query all permissions, then map the permissions after the fact
Or it's not even allowed possibly
You canāt just run arbitrary C# in a database engine
I don't remember
I see, thanks
Your projection has to be something that can be translated.
So you need to either build the expression tree manually, or copy-paste
Also stay away from automapper.
ty, didn't touch ef core that much. always sticked to more bare-metal stuff which does not have that "quirks"
AutoMapper
does this for you, it builds the right expression tree
It might be worse for perf, but it's way more maintainable
like orders of magnitudeUntil you get into more complex mappings and then it becomes more of an annoyance š
Less performant and less maintainable
i am not a big fan of magic āØ
what happens if property names don't match?
AutoMapper
is plenty configurablehow does it know that property names are supposed to match?
It's convention-based. It has a bunch of predefined rules
You end up writing the same code people try to avoid
But you can add more fine grained things if you need to
Itās quite funny to read configurations for automapper
imo automapper is less about not writing that mapping code than not writing it more than once. without manually combining expression trees, you'd have to copy paste some code multiple times if you have many dtos, especially with shared bits
actually, pobiega did a similar thing here, returning the entire database entity
Stack Overflow
Can I reuse code for selecting a custom DTO object for a child prop...
When querying using Entity Framework Core, I am using expressions to convert to DTO objects, which works well for the object, and any child collections.
A simplified example:
Model:
public class Mo...
I just saw that you could turn that
ToDto()
methods into expressions which totally makes sense
That's probably what I would do. But everyone can figure out what they like the most so after converting my database to an actual DbContext, i'm getting
That's a dependency injection exception
It is trying to create a
LbggDbContext
instance which requires DbContextOptions<T>
which are not registered
Did you add a AddDbContext(...)
call?i haven't
so i have an
Id
for my users which i'd like to be auto-incrementing
first off, is that sensible, and if so, how do i do it?That is sensible and secondly, it depends on the database type you're using
Generated Values - EF Core
How to configure value generation for properties when using Entity Framework Core
So if you ensure you define your Key in your database model or context. You might also need to apply a migration (google the concepts as I can't explain them in a short time :x )
this happens when i try to call my
CreateUser
endpoint
hm.
yes
it was the
required
keyword
which makes total sense
naw wait nevermind
builder.Services.AddDbContext<LbggDbContext>();
is there anything else i need to do?I think you have to setup the options iirc
Just look it up on the web. I gtg, see ya!
or whatever provider you're using
you'll need the connection string to the database, which usually comes from the configuration
for development there's a cool extension method for
app
, EnsureDatabaseCreate<context>
useful for setting up the db on new machinesalright, getting back into it a little (at work, so can't do all that much)
just using an in memory database for testing right now
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
i'll worry about it later, yeah
what i did for the auto-incrementing ID for now is this in my user entity:
and this in my DbContext
is that right?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
oh?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
huh
other thing. is there any way to do enums properly in swagger?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
that's annoying
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
i mean it just shows the enum as an array of numbers
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
can't
it's for pronouns
Funky thing about enums is they are just spicy ints š
which is a flag enum
yeah i'm aware lol
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
yeah that doesn't exist in my version
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
Just a quick question if you don't mind me asking Ero, you seem knowledgeable about a lot of things that relates to memory, performance etc - but i'm a little surprised backend is unfamiliar to you. What do you usually work with since you know your way around C# but not specifically backend tasks?
apparently it's obsolete and was removed
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
that's what i went with yeah
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
i mean i don't really see another way
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
i mean i'm not gonna use random ints that i forget what they mean...
process memory, specifically reading and writing remote memory, injecting, the like
so anyway. i've decided to make the namespaces
Api.Users.DataTransfer.In
and Api.Users.DataTransfer.Out
. this obviously doesn't seem right, i've certainly never seen anyone else do it
the In
one contains for example the CreateUserDto
which is the object passed into my controller by whoever is calling the endpoint
the Out
namespace for example contains GeneralUserDto
, or MinimalUserDto
since i don't super like this solution, are there any better suggestions?Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
re. structure, I usually have this structure:
Just posting this as an alternative seeing as you asked for suggestions
yes
Ā AddEnumsWithValuesFixFilters
in the package Ā Ā Ā Ā <PackageReferenceĀ Include="Unchase.Swashbuckle.AspNetCore.Extensions"Ā Version="2.6.12"Ā />
I think
this effectively copies the enum declarations
so both names and their int valuesi'm not really sure what to do next. i also have a big concern about how to store user data
surely i can't just have a
string Password
property
and what about Google or GitHub logins? don't those also need to be stored as users?
or are those separate entities?auth is a pretty big topic, I'm figuring it out myself atm
you would still have your own user saved, for your own data
YouTube
ASP.NET Core Authentication and Authorization Tutorials
asp.net core authentication and authorization tutorials. Covering asp.net core cookie authentication and authorization, jwt token authentication and authoriz...
check out these vids
but they wouldn't have a password and instead have an identifier and way to store it in your database
the passwords you store should never be plaintext and be hashed/salted
BCrypt and several other popular libraries exist to make that painless.
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
does it make sense to have a sort of base entity?
something like this maybe?
Instant
is from NodaTime
?Thats fairly normal, especially if your app has "all entities" fields except ID.
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
We have it at work, with very similar CreatedAt/UpdatedAt etc
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
i see...
i mean i don't know, i feel conflicted about that
interfaces to me most of the time are like "this is what i expose"
and i don't really want to "expose" setting anything
Depends on if you need composability in your entities I suppose.
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
If everything uses softdelete and CreatedAt/UpdatedAt timestamps, you dont need it.
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
I like the composable interfaces a lot.
also is the
bool IsDeleted
really necessary if the DeletedAt
instant is nullable?
i don't really think it makes sense to make it non-nullableUnknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
Instant? UpdatedAt
as well, imoUnknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
i guess that one you can set to the same time as CreatedAt
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
normally if you care about when something happened but its optional, letting the timestamp be nullable is a good option
and if you check this a lot, you can just add a helper property for the bool value
public bool IsDeleted => DeletedAt is not null;
i don't expect to be checking it a lot
not yet anyway
like one use i can think of is checking whether a username is available after a user chose to delete their account
like wait 30 days after or something
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
hm, not sure tbh.
I would explicitly ignore it with the entity configuration for sure
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
whatever that means
do i need to set
CreatedAt
and UpdatedAt
and all that manually? does that go in some method in my dbcontext class?Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
the reason i wanted to use a base class is so i don't have to implement the things like ID and the Instance on every single one of my entities every time
cause i mean, that's just unnecessary code i feel like
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
i mean vs does too, it's just
why explicitly have those properties in the entities
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
i can't think of a situation where i wouldn't want something to be soft deletable
i think i would want to be able to track each and every single thing my site staff did
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
and be able to easily undo it
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
using base classes as mixins isn't flexible, but I guess it's better than boilerplate
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
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.i'm not super sure on what my next steps are. i have a "working" user controller (literally only posts and gets by id), but it's obviously far from done
i was thinking... setting up a proper database? i was thinking postgres
or, perhaps work on encrypting passwords?
If you're using EF, swapping your db drivers shouldn't require many modifications
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
people have recommended BCrypt?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
i don't really understand why you would hash a password?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
don't you need to decrypt it to check for a valid login?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
Encrypting passwords would mean the person with the access to the key can decrypt all passwords
hm
so, what about bcrypt? is it like, the "industry standard"?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
i see
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
you can use microsoft identity since you're already using ef core
what's that?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
it dsows everything for you basically
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
no i mean ef core, what's that
sorry, trolling
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
thought it was funny
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.How would one implement a "edit history" feature, say for posts?
I'll be honest, i haven't worked on anything since i last posted here, just a bit busy and also not feeling like working on something i don't understand lol
another table that retains versions of posts
Hm, so I'd add the post's current information into a new instance of a PostVersion, add that to that post's Versions, and then overwrite it with the new information?
yes
And i guess i could do that in overwrite SaveChangesAsync?
no, that would just be part of your service
Mh
there's no overriding of ef behaviour
What? Sure there is
In my DbContext?
no, if you're overriding save changes for some business logic it's generally incorrect
What if i want to change the entity's UpdatedAt time?
i do that in my services
ĀÆ\_(ć)_/ĀÆ
You guys fight it out i guess lol
yeah i dont like that - every entity is beholden to that logic
what if i change a property on an entity via the system and it shouldn't update the last updated column
It should, at least in my opinion
code like this is hidden from developers and is why EF gets a bad wrap
obscuring what happens when i call
SaveChanges
by injecting business logic, without a developer knowing, is less maintainable, readable and leads to debugging sessions asking wtf is going onLol i had to Google that.
Bad wrap is considered wrong and is best saved for referring to wraps and tortillas.ā Merriam-Webster Found that funny
it's autocorrect
it's bad rap, not wrap
I believe it, don't worry
It's rep
From reputation
no bad rap is an idiom
i mean bad rap
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
i dont know what that interface does
Ah, you're right
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
think about the semantics of it, "savechanges" but it actually sets some properties
code like this is only made possible because EF allows you to override save change behaviour, if you didn't have EF how would you implement it with Dapper?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
yes
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
that's an entirely different concept
multi tenanted apps are not on the same level as "im calling savechanges"
initialising a db connection and having that go to the right tenant in a multi tenanted application is pretty standard
nor is it hidden code, because the initialisation is expected to... well... initialise
global query filters are as evil as this - let me just add a global filter real quick and have every query in my system affected by this, unless i opt out
you don't get global query filters in sql, so why should i let some magic dictate that for me
again - this is EF's doing and habits like that kill maintainability
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
sure, it's just a code smell
I'm gonna agree with Duke i think, i prefer not forgetting to update all that stuff, especially since all my entities will be softdeletable and lots will be updateable and versionable
easy to fall into the trap when you're new to this š
I just don't see the issue really
obscuring what happens when i call SaveChanges by injecting business logic, without a developer knowing, is less maintainable, readable and leads to debugging sessions asking wtf is going oni won't bother to mention the perf hit
I don't know, i just disagree? Save changes means applying all changes to the db. That implicitly means updating the UpdatedAt time to me
to you being the key
the argument against this is you forget
you can also forget to add the interface
then what?
I'm not sure how you mean
Forget it where?
forget the interface
Yes, where?
your class
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
How would I forget it? It's the very thing that makes something versionable
Exactly
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
so an interface dictates business behaviour?
the mere presence injects behaviour that you can't see anywhere in the entity or service or anywhere discernible other than your database layer
because it's the argument against this
you're human and forget things
everyone does
This is incredibly doubtful, I'm gonna be real
If this is the only way I've ever worked like in my project, it's unlikely I'll change how i work on a whim
not impossible, then
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
Damn, my man is creating a whole project in this thread
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.@Pobiega could you look over this and offer some advice?
the thing here is that see people use services for things like those checks (whether a user with the given email or username already exists)
so for example they have
_userService.GetByUsername()
and _userService.GetByEmail()
and i'm not sure whether that's really required, if there's a better way
i feel like doing 2 queries, once for email and once for username, is just unnecessary
also is CreatedAtRoute
correct here? should i return something else?
i see CreatedAtAction
being used, with a GetUserById
endpoint in the controllerUnknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
Necessary comment
So, this is fine, but having application logic in the controller directly is controversial, but it's a valid choice
Doing one query could work, but might give a worse error message back, which may or may not be desired
What's your suggestion?
Probably passing the entire request object into either a command handler or a service class method, and copy-pasting the entire method minus the HTTP specific bits (conflict, createdatroute) into there
that would be the "other way" to do things. If this project will only ever be a web api, that isnt strictly needed and for shorter methods like this, this is tbh fine
which one do you think is more, i guess modern? more maintainable, more easily expandible?
Imho command handlers, but as said, this is controversial and everyone has their own opinions
you'll probably need something like a
Result<T>
type if you go that way, so that the handler can indicate what went wrong (was the email/username taken, was your password not long enough, etc) and let hte controller return the correct http response
as long as you keep your controllers focused and specific, its fine to inline code in them. It becomes a problem when you have 500+ lines of code controllers that know everything about your appwhere in your solution would you say you'd put those?
the handlers? in your application logic/business logic project, usually grouped by some kind of feature. Like, a
UserRegistrationHandler
would live in the App.Business.Users
namespace
and if you are fine with having multiple types in a single file, I'd probably put my command in the same file as the handler
as they are extremely closely relatedso i have an
App.Api.Users
namespace, which has my UsersController
. would you still say the handler goes into a App.Business.Users
namespace?If you have an
App.Business
project, yes
if you dont, then its fine to have it in your App.Apimh, i have an
App.Shared
project?isnt that more for DTOs and contract classes?
which has my request and response records
right, so thats not the place for a handler
oh so the "command" isn't the request
it should be where your core logic is, wherever that might be
well, it can be
there are certainly cases where you can literally use a request as your command object
hm
and just have a handler use that.
sorta depends on what level of validation you do and where
so considering this
then probably in your API
i feel like i need to change this a bit
i'm find with adding an
App.Business
project, no harm there
i'm just still a bit confused on how things are split up
mainly Infrastructure and Domainthats understandable, there are thousands of ways to do this š
the main concerns apply
make it easy for contributors to find their way around and make contributions easily
well, domain is the core things that your program revolves around - users, categories, time series, individual submissions etc
make the code concise, maintainable, and performant
"time series"?
infrastructure will contain your EF specific stuff related to those core things, like your configuration classes, your dbcontext, etc
just an example of an entity?
well you were making a speedrun website?
yeah
right yeah
not just speedrunning, score "runs" too
but yeah
yeah it was just an example of an entity
alright good
so the command handlers just genuinely don't go in either domain or infra
this is a lot
not "traditionally" no, they are what contains most of your application logic
some people have "had it" with this entireprise splitting of everything, and there are merits to that approach too
that's what the project is doing currently, and i'm not a huge fan of it...
it's just very cluttered
would you say validation goes into
App.Business
then too?
https://github.com/leaderboardsgg/leaderboard-backend here's the current setup, by the way. feel free to absolutely criticize every little thing
some of us know absolutely no db stuff, others are new to c#. so it's a bit of a mess
which is why i'm planning this refactor
get myself familiar with db things, the c# part is easy for meSure, any explicit validation. You get some for free by ASP.NET, like respecting
required
and stuffwould you say making up a custom identification type is worth it? the site needs an id for each individual submission, which can become a lot. we're thinking of using something like discord's snowflakes; a 64 bit number where part of it consists of a timestamp and another of an incrementing number
ĀÆ\_(ć)_/ĀÆ
if you use
long
for IDs, they grow very large without needing snowflake style IDs
but if you dont want enumerability, then go for a guid or a snowflakeaw really
i mean you get the idea
yeah urls get nasty with guids in them
and auto-incrementing sort of enables this "race" to get a low ID somewhere
and enumerability
so yeah, if you dont like that, go for a snowflake
is enumerability a bad thing?
can be
like, if my user profile page is
lb.gg/user/6123
, what stops me from scraping all the users?the rate limit of the website!
or something
true
obviously more of a problem if there is some valuable information on that page, like linkedin etc
i guess that's a valid concern though
only "downside" will be that you wont be able to have your database generate IDs, but thats easy enough to work around
could i have guids as the internal storage, but get them via some other converted value?
guids do store their bytes after all
ĀÆ\_(ć)_/ĀÆ
never tried
easily solved by having the public entity ctor assign a snowflake, and have a protected one for EF to use
i don't even know how to begin doing that lmao
just have your "first" constructor be a protected one and have an argument for every property
EF will use that to create its entities
and when you make new users or whatever with
new User()
you use the public ctor, that creates a snowflake as part of object initialization
as for generating the snowflake, thats on you, i have no idea how they work šhow would i tell EF that my custom snowflake type is the primary key?
just slap that attribute on it?
call the property
Id
šah, right
it does do that
or attribute, or set it with your
EntityConfiguration<T>
class, or in the OnModelCreate (dont do this)should things like migrations be pushed to the source repo?
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
they don't
Unknown Userā¢2y ago
Message Not Public
Sign In & Join Server To View
why should other people besides us get a working copy of the db lol
Migrations are... a tradeoff.
They generate a database snapshot for each migration, so in a long-living application with hundreds of migration, a very significant part of your compile time will be those snapshots
however, once this thing goes live you dont want to have to restart the DB from scratch every time there is an update
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.do you mind
It does this way too quickly man
24 hours isnāt way too quickly
disagree i guess?
what an unnecessary comment
Iām glad you consider a comment telling you 24 hours isnāt too quickly as unnecessary in the face of a post that has well exceeded the general use case of the help forum we have on this channel as unnecessary. Consider moving this thread elsewhere if you believe the tools that we have in place that fit the general use of the forum as invasive.
suggest a place to move the thread to
Looks like code review to me, now.
nearly 1500 messages <:PES_OMGNO:681925594312736811>
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.