0

First, I want to say [HttpGet], [HttpGet("{id}")], and [HttpPost] is working correctly. However I'm running into a challenge with [HttpPut], and almost everywhere I look, the solution is to return without a status code.

I'm using visual studio 2019 with a project type of "ASP.NET Core Web Application" and "API" (ASP.NET Core 3.1).

I'm also using a secondary project in the same visual studio solution with the type(C#) "Class Library (.NET Standard)".

I'm using Postman to test the http request calls.

The following (Additional) NuGet Packages are required to be installed:

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.InMemory
  • System.ComponentModel.Annotations

The Solution Explorer:

enter image description here

There is a lot of code to cover with the .net core, and I will show it all here (as far as what has changed).

The Project "CoreStudy.Api" Code:

Startup.cs

using CoreStudy.Data.Context;
using CoreStudy.Data.Repository;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace CoreStudy.Api
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddScoped<PeopleRepository>(); // add this line
            services.AddDbContext<PersonContext>(opt => opt.UseInMemoryDatabase("PeopleInventory")); // add this line, requires NuGet package "Microsoft.EntityFrameworkCore.InMemory"
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

PeopleController.cs

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using CoreStudy.Data.Repository;
using CoreStudy.Data.Models;
using Microsoft.AspNetCore.Http;

namespace CoreStudy.Api.Controllers
{
    [Route("people")]
    [ApiController]
    public class PeopleController : ControllerBase
    {
        private readonly PeopleRepository _repository;

        public PeopleController(PeopleRepository repository)
        {
            _repository = repository;
        }

        [HttpGet]
        [ProducesResponseType(typeof(List<PersonModel>), StatusCodes.Status200OK)]
        public IActionResult GetPeople()
        {
            return Ok(_repository.GetPeople());
        }

        [HttpGet("{id}")]
        [ProducesResponseType(typeof(PersonModel), StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        public IActionResult GetPersonById(int id)
        {
            var person = _repository.GetPersonById(id);
            if (person == null)
            {
                return NotFound();
            }
            return Ok(person);
        }

        [HttpPost]
        [ProducesResponseType(StatusCodes.Status201Created)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        public async Task<IActionResult> AddPersonAsync([FromBody] PersonModel person)
        {
            if((_repository.GetPersonById(person.id) != null) || String.IsNullOrWhiteSpace(person.name))
            {
                return BadRequest();
            }
            int peopleAdded = await _repository.AddPersonAsync(person);
            return CreatedAtAction(nameof(GetPersonById), new { person.id }, person);
        }

        [HttpPut]
        [ProducesResponseType(typeof(PersonModel), StatusCodes.Status202Accepted)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        public async Task<IActionResult> ChangePersonNameByIdAsync([FromBody] PersonModel person)
        {
            if (_repository.GetPersonById(person.id) == null)
            {
                return NotFound();
            }
            else if (String.IsNullOrWhiteSpace(person.name))
            {
                return BadRequest();
            }
            PersonModel updatedPerson = await _repository.ChangePersonNameAsync(person);
            return Ok(updatedPerson);
        }
    }
}

The Project "CoreStudy.Data" Code:

PersonContext.cs

using Microsoft.EntityFrameworkCore;
using CoreStudy.Data.Models;

namespace CoreStudy.Data.Context
{
    public class PersonContext : DbContext
    {
        public PersonContext(DbContextOptions<PersonContext> options) : base(options)
        {

        }

        public DbSet<PersonModel> people { get; set; }
    }
}

PersonModel.cs

using System.ComponentModel.DataAnnotations;

namespace CoreStudy.Data.Models
{
    public class PersonModel
    {
        public int id { get; set; }

        [Required]
        public string name { get; set; }

        public string position { get; set; }

        public PersonModel() { }
        public PersonModel(string name, string position)
        {
            this.name = name;
            this.position = position;
        }
        public PersonModel(int id, string name, string position)
        {
            this.id = id;
            this.name = name;
            this.position = position;
        }
    }
}

PeopleRepository.cs

using System.Collections.Generic;
using CoreStudy.Data.Models;
using CoreStudy.Data.Context;
using System.Linq;
using System.Threading.Tasks;

namespace CoreStudy.Data.Repository
{
    public class PeopleRepository
    {
        private readonly PersonContext context;

        public PeopleRepository(PersonContext context)
        {
            this.context = context;

            if (context.people.Count() == 0)
            {
                context.people.AddRange(
                    new PersonModel
                    {
                        name = "shaggy",
                        position = "screwball"
                    },
                    new PersonModel
                    {
                        name = "scooby",
                        position = "screwball dog"
                    },
                    new PersonModel
                    {
                        name = "fred",
                        position = "leader"
                    },
                    new PersonModel
                    {
                        name = "velma",
                        position = "smart one"
                    },
                    new PersonModel
                    {
                        name = "daphne",
                        position = "popular one"
                    });
                context.SaveChanges();
            }
        }

        public List<PersonModel> GetPeople()
        {
            return context.people.ToList();
        }

        public PersonModel GetPersonById(int id)
        {
            PersonModel person = context.people.Find(id); // null if not found
            return person;
        }

        public async Task<int> AddPersonAsync(PersonModel person)
        {
            int rowsAffected = 0;
            context.people.Add(person);
            rowsAffected = await context.SaveChangesAsync();
            return rowsAffected;
        }

        public async Task<PersonModel> ChangePersonNameAsync(PersonModel person)
        {
            context.people.Update(person);
            await context.SaveChangesAsync();
            return GetPersonById(person.id);
        }
    }
}

When trying to make a PUT request with postman, I get the following error:

enter image description here

The problem is either in one of these two snippets:

public async Task<PersonModel> ChangePersonNameAsync(PersonModel person)
{
    context.people.Update(person); // I thought Update() would be best used here, but not sure
    await context.SaveChangesAsync();
    return GetPersonById(person.id);
}

[HttpPut]
[ProducesResponseType(typeof(PersonModel), StatusCodes.Status202Accepted)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> ChangePersonNameByIdAsync([FromBody] PersonModel person)
{
    if (_repository.GetPersonById(person.id) == null)
    {
        return NotFound();
    }
    else if (String.IsNullOrWhiteSpace(person.name))
    {
        return BadRequest();
    }
    PersonModel updatedPerson = await _repository.ChangePersonNameAsync(person);
    return Ok(updatedPerson); // not sure if I should be using Ok() for a PUT
}

If anyone could help me resolve this, myself (and I'm sure much of the internet) would thank you.

2 Answers 2

1

This already gets the entity:

if (_repository.GetPersonById(person.id) == null) { ... }

So you need to take that result:

var personDB = _repository.GetPersonById(person.id);

Then check if the variable is null

if(personDB != null) { ... }

Then you will need to change the personDB properties values with the person (from the PUT parameter) values.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, this works. Note: I also had to change [HttpPut] to [HttpPut("{id}")]
1

After the line

if (_repository.GetPersonById(person.id) == null)

The person entity is already being tracked by the DbContext.

You don't need the extra repository layer at all. Your PersonContext already is a perfectly good repository. Just run:

context.people.Update(person); 
await context.SaveChangesAsync();
return Ok(person);

in your controller.

4 Comments

The problem here is context is defined in the repository, not the controller.
I think you and ljcordero had the same root cause. Looks like the solution is to just store that call in memory, and recall it... PersonModel personToUpdate = _repository.GetPersonById(person.id);, then personToUpdate.name = person.name;, then await _repository.ChangePersonNameAsync(personToUpdate);
"The problem here is context is defined in the repository, not the controller" Yes that is a problem. You introduced an unnecessary layer in your application. At least put an Update(PersonModel) method, and a SaveChanges() method on your PersonRepository.
I appreciate the insight on this. I was searching through over 20 different types of videos, documentation, articles, blogs, ect... and am trying to stick with a pattern to what I was using with nodejs/express. So far, this pattern seems to be easy to work with and read for me. Thanks for the input.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.