APIs with Entity Framework Core: POST

APIs with Entity Framework Core

Continuing the topic, I want to give a complete example of minimal APIs in Blazor with Entity Framework Core with complex objects.I always struggle to have a solution working when my model has dependencies with other object. Here I show my test and my code. The code is in NET9. In the Microsoft documentation, there are some examples, but it is not complex enough. A few days ago, I posted about another problem I had with NET9 and Entity Framework Core.

The full source code of this post is available on GitHub. If you have any questions, please comment below or post in the forum.

The complete code is spanned through those posts:

Send the first POST request

In my previous post, I prepared the minimal APIs using the Scaffolded Item tool in Visual Studio. Also, Swagger was added to the project. Now, I want to submit a json to create a new Client record. Then, open Swagger and under the Clients section. Now, I have to use the POST verb to send a valid json with all the details.

This is a valid json to use in order to create a new record.

{
	"firstname": "Test1",
	"lastname": "Test2",
	"channels": [
		{
			"Id": 1,
			"name": "Google"
		},
		{
			"Id": 2,
			"name": "Bing"
		}
	]
}

As a reminder, I have created the 2 records for the Channels. Now, when I submit this request, the code raises an error because the 2 entities for the channel already exist in the database.

The initial code

So, in order to avoid this error, the strategy is to ready the channels from the database and add them to the object. The code generated by Visual Studio was this one:

group.MapPost("/", async(Domain.Client client, MyDbContext db) =>
    {
        db.Clients.Add(client);
        await db.SaveChangesAsync();
        return TypedResults.Created($"/api/Client/{client.Id}", client);
    })
    .WithName("CreateClient")
    .WithOpenApi();

That means the POST verb receives in the variable client the json object. Then, it is translated to the Client model. When I add this object to the database using db.Clients.Add(client); the record for the channels that already exist. So, Entity Framework raises an error because I can’t add to the database those values.

After saving in the database successfully, the API returns an HTTP Created 201.

The fix

As I said before, what I have to do is to replace the channels in the Client model with the data from the database. Here is the code.

group.MapPost("/", async (Domain.Client client, MyDbContext db) =>
    {
        if (client.Channels != null && client.Channels.Count > 0)
        {
            var list = client.Channels;
            client.Channels = new List<Channel>();
            foreach (var c in list)
            {
                var channel = await db.Channels.FirstOrDefaultAsync(ch => ch.Id == c.Id);
                if (channel != null)
                    client.Channels.Add(channel);
            }
        }

        db.Clients.Add(client);
        await db.SaveChangesAsync();
        return TypedResults.Created($"/api/Client/{client.Id}", client);
    })
    .WithName("CreateClient")
    .WithOpenApi();

What is new here? The lines are retrieving the data from the database and adding the object (if exists) to the Client object. Now, I run again the creation of the record using Swagger and it is working.

The result

Therefore, the result is that in the database I see the records in the Clients table as in the json.

Clients table content - APIs with Entity Framework Core: POST
Clients table content

Also, I see that in the ChannelClient table, there are records for a ClientId with the related ChannelsId.

ChannelClient table content - APIs with Entity Framework Core: POST
ChannelClient table content

POST APIs with Entity Framework Core video

Now, to help you and as a future reference, I created a video with this code. Please, click on it and subscribe to my YouTube channel.

Retrieve the data with GET

At this point, I have a nice POST that saves a Client object with its dependencies. What if I want to retrieve this Client with the dependencies using the APIs?

For that, the GET implementation is what I need. The initial implementation doesn’t include the Channel details. For that, I have to change the function that returns the Client object by Id. The code is the following:

group.MapGet("/{id}", async Task<Results<Ok<Domain.Client>, NotFound>> 
    (long id, MyDbContext db) =>
    {
        return await db.Clients.AsNoTracking()
                .Include(c => c.Channels)
                .FirstOrDefaultAsync(model => model.Id == id)
            is Domain.Client model
            ? TypedResults.Ok(model)
            : TypedResults.NotFound();
    })
    .WithName("GetClientById")
    .WithOpenApi();

The only change in this code from the one generated by Visual Studio is that I added the include to ask Entity Framework Core to consider the linked Channels table. The result is the full Client object.

Using Swagger to retrieve a full Client object
Using Swagger to retrieve a full Client object

Wrap up

In conclusion, in this post I show how to change the minimal APIs with Entity Framework Core and fix the POST. Also, I updated the GET method to return the full Client object. But this is not the end. Look for the next post.

Happy coding!

Related posts

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.