DEV Community

Brandon Weaver
Brandon Weaver

Posted on

Entity Framework

In this part of the series we'll use Entity framework to create migrations and manage database transactions.

First, we'll add the NuGet packages we need. I'll be using a SQLite database throughout this series, so we'll add Entity Framework and the SQLite extension for Entity Framework.

dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
Enter fullscreen mode Exit fullscreen mode

If you're using Visual Studio, I believe you can simply right click on the solution in the solution explorer, select add NuGet package, and search for the packages you want to add.

Now we'll install the Entity Framework tools which will allow us to generate migrations from the models we've added to our application. You can check to see if the tools are already installed by running dotnet ef.

To install the Entity Framework tools, run the following.

dotnet tool install --global dotnet-ef
Enter fullscreen mode Exit fullscreen mode

Now that we have the packages and tools we need, we'll revisit our user model and add some attributes which will provide additional information regarding how our schema should be generated.

using System.ComponentModel.DataAnnotations;

namespace AspNetCoreWebApiIntro.Models
{
    public class User
    {
        [Key]
        public int Id { get; set; }

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

        [Required]
        public string LastName { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

The [Key] attribute will ensure that the Id property of our user model is automatically incremented and set each time a user is added to the users table of our database. The [Required] attribute simply ensures that these properties are not null before adding a new user.

Previously, we created a mock repository and referenced a collection of users which we created from that repository. Since we're going to be referencing 'real' data, we'll use a DbContext and DbSet collection type provided by Entity Framework to reference data stored in our database.

Within the 'Data' folder, add a class named 'AppContext.cs', and add the following code.

using Microsoft.EntityFrameworkCore;

using AspNetCoreWebApiIntro.Models;

namespace AspNetCoreWebApiIntro.Data
{
    public class AppContext : DbContext
    {
        public DbSet<User> Users { get; set; }

        public AppContext(DbContextOptions<AppContext> opt) : base(opt)
        {

        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The DbContextOptions<AppContext> parameter will allow us to signify the type and location of our database when add our AppContext service to the application.

We'll also take this time to create our application repository. Again, within the 'Data' folder, add a class named 'AppRepo.cs', and add the following code.

using System.Collections.Generic;
using System.Linq;

using AspNetCoreWebApiIntro.Models;

namespace AspNetCoreWebApiIntro.Data
{
    public class AppRepo
    {
        private readonly AppContext _context;

        public AppRepo(AppContext context)
        {
            this._context = context;
        }

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

        public IEnumerable<User> GetAllUsers()
        {
            return this._context.Users;
        }

        public User GetUserById(int id)
        {
            return this._context.Users.FirstOrDefault(u => u.Id == id);
        }

        public void CreateUser(User user)
        {
            this._context.Users.Add(user);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

You'll notice that our repository now accepts a reference to our DbContext and references the users collection located in that context. Another aspect of this class worth mentioning is the SaveChanges method; this method returns false in the event that our database fails to update. Although we could simply call _context.SaveChanges to update the database, it is often useful to check that this is successful in order to take appropriate actions if that isn't the case.

We now have almost everything in place; we just need to set the location of our database, and add and configure our new services. We'll also set an extremely relaxed CORS policy to make things a little easier moving forward.

Within the 'Data' folder, create a new folder named 'Database'. This is where our database will be located. Now, we'll open up 'appsettings.json' and add a reference to that location.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": { // Add this
    "AppConnection": "Data Source=./Data/Database/app_database.db" // Add this
  } // Add this
}
Enter fullscreen mode Exit fullscreen mode

Now open up 'Startup.cs' and add the following code to the top of the ConfigureServices method.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<Data.AppContext>(opt => opt.UseSqlite(Configuration.GetConnectionString("AppConnection")));

    services.AddScoped<AppRepo, AppRepo>();

    services.AddCors(opt => {
        opt.AddPolicy("AppPolicy", builder => {
            builder
            .AllowAnyOrigin()
            .AllowAnyHeader()
            .AllowAnyMethod();
        });
    });

    ...
Enter fullscreen mode Exit fullscreen mode

You'll also need to add references to the required namespaces.

using Microsoft.EntityFrameworkCore;
using AspNetCoreWebApiIntro.Data;
Enter fullscreen mode Exit fullscreen mode

Next, at the top of the Configure method add the following line.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseCors("AppPolicy");

    ...
Enter fullscreen mode Exit fullscreen mode

Finally, we'll add a create user action to our users controller and create our initial migration.

Within 'UsersController.cs', add the following code to the bottom of the class.

[HttpPost]
public ActionResult<User> Create(User user)
{
    this._repo.CreateUser(user);
    this._repo.SaveChanges();

    return CreatedAtRoute(nameof(Show), new { Id = user.Id }, user);
}
Enter fullscreen mode Exit fullscreen mode

We'll also need to name our Show action attribute to ensure that our response provides the appropriate endpoint.

[HttpGet("{id}", Name = "Show")] // Change this
public ActionResult<User> Show(int id)
{
    ...
}
Enter fullscreen mode Exit fullscreen mode

Now we simply create our initial migration and create our database by running the two following commands.

dotnet ef migrations add InitialMigration
dotnet ef database update
Enter fullscreen mode Exit fullscreen mode

That's it! You'll now be able to create and persist users. If you want to ensure that everything is working, build and run the application then execute the following code in your browser's console.

const user = { firstName: "Jane", lastName: "Doe" }
fetch("https://localhost:5001/api/users", {
    method: "POST",
    headers: {
        "Content-Type": "application/json"
    },
    body: JSON.stringify(user)
})
.then(r => r.json())
.then(o => console.log(o));
Enter fullscreen mode Exit fullscreen mode

You should see the following log.

{id: 1, firstName: "Jane", lastName: "Doe"}
Enter fullscreen mode Exit fullscreen mode

We'll take a break here. In the next part of the series we'll use another package named AutoMapper to take advantage of data transfer objects which will allow us to specify what properties we wish to send and receive from and within our user actions.

Top comments (0)