Model is a collection of classes to interact with the database.
To create a data model for our application, we will start with the following two entities.
There's a one-to-many relationship between Author
and Book
entities. In other words, an author can write any number of books, and a book can be written by only one author.
In Solution Explorer, right click on the Models folder and choose Add > Class. Enter a class file name Author.cs and add the following code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RepositoryPatternDemo.Models
{
public class Author
{
public int AuthorId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
}
The AuthorId
property will become the primary key column of the database table that corresponds to this class. By default, Entity Framework interprets a property named Id
or <classname>Id
as the primary key.
Books
property is a navigation property, navigation properties hold other entities that are related to this entity.Books
property of an Auth
entity will hold all of the Book
entities that are related to that Author
entity.Author
row in the database has two related Book
rows, that Author
entity's Books
navigation property will contain those two Book
entities.Now let's add another entity class Book
, and replace the following code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RepositoryPatternDemo.Models
{
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
}
Id
property will be the primary key; this entity uses the Id
pattern instead of <classname>Id
by itself, as you saw in the Author
entity.The database context class provides the main functionality to coordinate Entity Framework with a given data model.
Microsoft.EntityFrameworkCore.DbContext
class.So let's create a folder in your project by right-clicking on your project in Solution Explorer and click Add > New Folder. Name the folder DAL (Data Access Layer). In that folder, create a new class file named BookStore.cs, and replace the following code.
using Microsoft.EntityFrameworkCore;
using RepositoryPatternDemo.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RepositoryPatternDemo.DAL
{
public class BookStore : DbContext
{
public BookStore(DbContextOptions<BookStore> options) : base(options)
{
}
public DbSet<Author> Authors { get; set; }
public DbSet<Book> Books { get; set; }
}
}
This code creates a DbSet
property for each entity set. In Entity Framework terminology, an entity set typically corresponds to a database table, and an entity corresponds to a row in the table.
ASP.NET Core implements dependency injection by default, and EF database context and other services are registered with dependency injection during application startup. Components that require these services (such as MVC controllers) are provided these services via constructor parameters. You'll see the controller constructor code that gets a context instance later in this tutorial.
To register BookStore
as a service, open Startup.cs
, and add the following code to the ConfigureServices
method.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<BookStore>(options
=> options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}
The name of the connection string is passed into the context by calling a method on a DbContextOptionsBuilder
object.
For local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file. So let's add the connection to that file as shown below.
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=(localdb)\\ProjectsV13;Initial Catalog=BookStore;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
The above connection string specifies that the Entity Framework will use a LocalDB
database named BookStore.mdf
.
The Entity Framework will create an empty database for you. So we need to write a method that's called after the database is created to populate it with test data.
In the DAL folder, add a new class BookStoreInitializer
, and replace the following code.
using RepositoryPatternDemo.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RepositoryPatternDemo.DAL
{
public class BookStoreInitializer
{
public static void Initialize(BookStore context)
{
context.Database.EnsureCreated();
// Look for any authors.
if (context.Authors.Any())
{
return; // DB has been seeded
}
var authors = new List<Author>
{
new Author { FirstName="Carson", LastName="Alexander", BirthDate = DateTime.Parse("1985-09-01")},
new Author { FirstName="Meredith", LastName="Alonso", BirthDate = DateTime.Parse("1970-09-01")},
new Author { FirstName="Arturo", LastName="Anand", BirthDate = DateTime.Parse("1963-09-01")},
new Author { FirstName="Gytis", LastName="Barzdukas", BirthDate = DateTime.Parse("1988-09-01")},
new Author { FirstName="Yan", LastName="Li", BirthDate = DateTime.Parse("2000-09-01")},
};
authors.ForEach(a => context.Authors.Add(a));
context.SaveChanges();
var books = new List<Book>
{
new Book { Title = "Introduction to Machine Learning", AuthorId = 1 },
new Book { Title = "Advanced Topics in Machine Learning", AuthorId = 1 },
new Book { Title = "Introduction to Computing", AuthorId = 1 },
new Book { Title = "Introduction to Microeconomics", AuthorId = 2 },
new Book { Title = "Calculus I", AuthorId = 3 },
new Book { Title = "Calculus II", AuthorId = 3 },
new Book { Title = "Trigonometry Basics", AuthorId = 4 },
new Book { Title = "Special Topics in Trigonometry", AuthorId = 4 },
new Book { Title = "Advanced Topics in Mathematics", AuthorId = 4 },
new Book { Title = "Introduction to AI", AuthorId = 4 },
};
books.ForEach(b => context.Books.Add(b));
context.SaveChanges();
}
}
}
In the Program.cs
file, replace the following code in the Main
method.
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using RepositoryPatternDemo.DAL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RepositoryPatternDemo
{
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<BookStore>();
BookStoreInitializer.Initialize(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred while seeding the database.");
}
}
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
On application startup, the Main
method does the following operations.
When you run your application for the first time, the database will be created and seeded with test data.
So far, whenever you change your data model, you can delete the database, update your seed method, and start afresh with a new database the same way.