Brandon Weaver
Posted on February 21, 2021
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
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
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; }
}
}
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)
{
}
}
}
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);
}
}
}
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
}
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();
});
});
...
You'll also need to add references to the required namespaces.
using Microsoft.EntityFrameworkCore;
using AspNetCoreWebApiIntro.Data;
Next, at the top of the Configure
method add the following line.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors("AppPolicy");
...
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);
}
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)
{
...
}
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
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));
You should see the following log.
{id: 1, firstName: "Jane", lastName: "Doe"}
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.
Posted on February 21, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 26, 2024