Search code examples
c#asp.net.netasp.net-mvcentity

How to save an object in Entity Framework while using a database generated option for one of its columns?


I have a webpage in my project where a user signs up with their info and that info is sent to my controller by an api call in the frontend. In my controller, I am trying to create an object with the data, but the only way to create the object is to use a hardcoded id in my parameters. I need the id to be created automatically without obviously hardcoding it in.

Controller

// POST api/values
[HttpPost]
public void Post(string username, string password, string email, string role)
{
    int id = 1;
    Users user = new Users(id, username, password, email, role);
    _repository.CreateUser(user);
    _repository.SaveChanges();
}

Model

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace IssueTracker.Models
{
    public class Users
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int id { get; set; }

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

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

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

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

        public Users(int id, string username, string password, string email, string role)
        {
            this.id = id;
            this.username = username;
            this.password = password;
            this.email = email;
            this.role = role;
        }

        public Users()
        {

        }

    }
}

Interface

using IssueTracker.Models;

namespace IssueTracker.Data
{
    public interface IIssueTrackerRepo
    {
        bool SaveChanges();
        //IEnumerable<Command> GetAllCommands();
        //Command GetCommandById(int id);
        void CreateUser(Users cmd);
        //void UpdateCommand(Command cmd);
        //void DeleteCommand(Command cmd);
    }
}

Interface Implementation

using System;
using IssueTracker.Models;
namespace IssueTracker.Data
{
    public class SqlUsersRepo: IIssueTrackerRepo
    {
        private readonly UsersContext _context;

        public SqlUsersRepo(UsersContext context)
        {
            _context = context;
        }

        public void CreateUser(Users user)
        {
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }
            
            _context.Users.Add(user);
        }

        public bool SaveChanges()
        {
            return (_context.SaveChanges() >= 0);
        }
    }
}

signup.js

import React, { Component } from "react";
import { useState, useEffect } from "react";
import { Grid, TextField, Button, Typography } from "@material-ui/core";

const Signup = () => {
    const [username, setUsername] = useState();
    const [password, setPassword] = useState();
    const [email, setEmail] = useState();
    const [role, setRole] = useState()

    const post = () => {
        const requestOptions = {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
                username: username,
                password: password,
                email: email,
                role: role
            }),
        };
        fetch("https://localhost:5001/api/IssueTracker", requestOptions)
            .then((response) => response.text())
            .then((data) => {

            })
    }

    return (
        <div>
            <body>
                <form action="#" method="POST">
                    <TextField onChange={(e) => setUsername(e.target.value)}> </TextField>
                    <br>
                    </br>
                    <TextField onChange={(e) => setPassword(e.target.value)}> </TextField>
                    <br>
                    </br>
                    <TextField onChange={(e) => setEmail(e.target.value)}> </TextField>
                    <br>
                    </br>
                    <TextField onChange={(e) => setRole(e.target.value)}> </TextField>
                    <br>
                    </br>
                    <Button onClick={() => post()}> Sign Up</Button>
                </form>
            </body>
        </div>
    );
}



export default Signup;

Solution

  • Remove the id parameter from your constructor -

    public Users(string username, string password, string email, string role)
    {
        this.username = username;
        this.password = password;
        this.email = email;
        this.role = role;
    }
    

    The annotation -

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    

    will handle the creation of an Id for you at the database end. Just create the entity without an Id and save it -

    [HttpPost]
    public void Post(string username, string password, string email, string role)
    {
        Users user = new Users(id, username, password, email, role);
        _repository.CreateUser(user);
        _repository.SaveChanges();
    }
    

    EDIT:
    To fix further issues you've mentioned in the comment, do the followings -

    • on the client end don't JSON.stringify the body -
    const requestOptions = {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: {
            username: username,
            password: password,
            email: email,
            role: role
        }
    };
    
    • at the Api end change your Post method as -
    [HttpPost]
    public void Post([FromBody]User user)
    {
        _repository.CreateUser(user);
        _repository.SaveChanges();
    }
    

    The built-in ModelBinding feature will create the user entity for you behind the scene, based on the json object you have passed in the body of the request.

    Note: For this to work, the json object's property names should match the User entity's property names.