Welcome back! In part 1 of this series we created the solution and scaffolded the data model based on an existing database. In part 2 we will clean-up the data model by improving the naming of entities and navigation properties, we will create two data migrations – one to create the database and another to seed the database, and finally, we will create a simple controller to test drive our completed solution.
Renaming the entities
The code that has been scaffolded does not follow a naming convention. Unlike previous versions, EF Core does not support singularization or pluralization during scaffolding. Instead, it uses the name exactly as specified in the database. So for the Customers
table, it has generated a Customers
class, rather than a Customer
class. I have found two options to fix this issue:
- Refactor | Rename entity classes
- Follow this guide – EF Core 1.1 Pluralization in Reverse Engineer
In this case, I recommend the first option, as there are only 11 entities this will be relatively quick. However, if you were dealing with a large number of entities, the second option would be much more productive.
Follow these steps to rename the file and class names:
- Right-click on Categories.cs
- Click Rename
- Type the new name Category
- Press Enter
- When prompted, click Yes
This will automatically update the file and class names, along with any references. Repeat the above steps for the remaining files list in the table below.
Filename | New Filename |
---|---|
Customers.cs | Customer.cs |
Employees.cs | Employee.cs |
EmployeeTerritories.cs | EmployeeTerritory.cs |
OrderDetails.cs | OrderDetail.cs |
Orders.cs | Order.cs |
Products.cs | Product.cs |
Shippers.cs | Shipper.cs |
Suppliers.cs | Supplier.cs |
Territories.cs | Territory.cs |
Refactoring the navigation properties
In some cases, the code that has been scaffolded uses poorly named navigation properties, e.g. InverseReportsToNavigation
. To improve this we can just rename the navigation properties and update any associated usages.
Follow these steps to rename the navigation properties:
- Open Employee.cs
- Find the following lines:
[InverseProperty("InverseReportsToNavigation")] public virtual Employee ReportsToNavigation { get; set; } [InverseProperty("ReportsToNavigation")] public virtual ICollection<Employee> InverseReportsToNavigation { get; set; }
- Update the code as follows:
[InverseProperty("DirectReports")] public virtual Employee Manager { get; set; } [InverseProperty("Manager")] public virtual ICollection<Employee> DirectReports { get; set; }
- Open Order.cs
- Find the following line:
public virtual Shipper ShipViaNavigation { get; set; }
- Update the code as follows:
public virtual Shipper Shipper { get; set; }
- Open Shipper.cs
- Find the following line:
[InverseProperty("ShipViaNavigation")]
- Update the code as follows:
[InverseProperty("Shipper")]
- Build the solution (Ctrl + Shift + B)
Now that we’ve cleaned up our data model, we can begin creating our data migrations.
Creating the base migration
The first migration is typically referred to as the base migration or simply, initial create. It is the migration that is responsible for creating all of the initial database tables.
Follow these steps to create the first migration:
- Open the Package Manager Console (View | Other Windows | Package Manager Console)
- Ensure the Default project is set to NorthwindTraders.Data
- Execute the following command:
Add-Migration -Name "InitialCreate"
Once complete, a new Migrations folder will be added to the project and will contain the new InitialCreate migration.
Seeding the database
Our project would not be complete without seeding the database. There are a number of ways that we can approach this including using a database initializer, however to keep it simple, we will seed the database using a migration. Within the migration, we will use T-SQL scripts to seed the database.
Follow these steps create a the new migration to seed the database:
- Within the NorthwindTraders.Data project, right-click on the Migrations folder and click Add | Existing Item…
- Change the File Type to All Files (*.*)
- Find and select the SeedData_Down.sql and SeedData_Up.sql files
- Click Add
- Right-click on the NorthwindTraders.Data project and select Properties
- Select the Resources tab and click on ‘This project does not contain a default resources file. Click here to create one.‘
- Within Solution Explorer, drag and drop the SeedData_Down.sql and SeedData_Up.sql onto the newly created resources file
- Open the Package Manager Console (setting the default project NorthwindTraders.Data) and execute the following command:
Add-Migration -Name "SeedData"
- From within the Migrations folder, open the newly created migration and add the following using statement:
using NorthwindTraders.Data.Properties;
- Update the migration as follows:
protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.Sql(Resources.ResourceManager.GetString("SeedData_Up")); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.Sql(Resources.ResourceManager.GetString("SeedData_Down")); }
- Run a quick build (Ctrl + Shift + B)
Now that the data model is complete and the data migrations are prepared, we are ready to test the solution.
Testing the solution
In this final section, we add a Web API controller to our NorthwindTraders project. This controller will query the Customers
table in the database in order to demonstrate that our code first version of Northwind is ready to go.
First we need to update the generated NorthwindContext
. It requires two simple changes, the first is to add a new constructor, and the second is to remove the OnConfiguring
method – it is not required.
- Within NorthwindTraders.Data, open the NorthwindContext.cs file
- Add a new constructor as follows:
public NorthwindContext(DbContextOptions<NorthwindContext> options) : base(options) { }
- Remove the
OnConfiguring
method
With the new constructor in place, we will be able to specify relevant options (e.g. connection string) when the context is instantiated.
Next, we will need to install the Microsoft EF Core SQL Server provider.
- Open the Package Manager Console
- Set the Default project to NorthwindTraders
- Execute the following command:
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Next, we will add a connection string and configure dependency injection.
- Within the NorthwindTraders project, open the appsettings.json file, and update as follows:
{ "ConnectionStrings": { "NorthwindDatabase": "Server=(LocalDb)\\MSSQLLocalDb;Database=NorthwindDb;Trusted_Connection=True;" }, "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Warning" } } }
- Change the above connection string, if necessary
- Open the Startup.cs file and add the following using statements
using Microsoft.EntityFrameworkCore; using NorthwindTraders.Data;
- Update the
ConfigureServices
method as follows:public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddDbContext<NorthwindContext>(options => options.UseSqlServer( Configuration.GetConnectionString("NorthwindDatabase"))); services.AddMvc(); }
- Build the solution (Ctrl + Shift + B)
With our NorthwindTraders project correctly configured, now is a good time to create the database.
- Open the Package Manager Console
- Set the Default project to NorthwindTraders.Data
- Execute the following command:
Update-Database
You can verify that the database was created successfully by opening SQL Server Object Explorer and locating the new database.
We will now create a new customers controller to query the database and return some data.
- Within the NorthwindTraders project, right-click on the Controllers folder and click Add | Controller
- Select API Controller – Empty and click Add
- Set Controller name to CustomersController and click Add
- Within the newly created CustomersController.cs file, add the following using statement:
using NorthwindTraders.Data;
- Update the code within the controller as follows:
public class CustomersController : Controller { private readonly NorthwindContext _context; public CustomersController(NorthwindContext context) { _context = context; } // GET api/customers [HttpGet] public IEnumerable<Customer> Get() { return _context.Customers.Select(c => new Customer { CustomerId = c.CustomerId, CompanyName = c.CompanyName, ContactName = c.ContactName, ContactTitle = c.ContactTitle, Address = c.Address, City = c.City, Country = c.Country }); } }
- Press F5 to build and run the solution
- Navigate to /api/customers (e.g. http://localhost:5210/api/customers) to verify that everything is working correctly
Next Steps
In part 2 of this series, I provided recommendations on refactoring the generated code including cleaning up the data context, renaming the entities, and renaming the navigation properties. We then built two migrations, the first responsible for creating the database and the second for seeding the database. Finally, we add a customers controller to the solution so that we could ensure that everything was working correctly. In case you have any issues, please refer to the following resources:
. View the source code
. Entity Framework Core Documentation
. ASP.NET Core Documentation
Thanks for reading. Please feel free to reply with any questions, comments, or improvements.
Nice. Thanks for sharing. I’m glad the migration commands didn’t change much from EF6 to EF Core. Cheers
You’re welcome. Actually there have been some changes, you now have two options, 1) command line tools integrated into Visual Studio or 2) IDE-independent and cross-platform tools. In this series, I went with option 1, since this is my current preference. You can learn more about these options here – https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/.