Github源码地址:https://github.com/solenovex/Building-asp.net-core-2-web-api-starter-template-from-scratch
前三部分弄完,我们已经可以对内存数据进行CRUD的基本操作,并且可以在asp.net core 2中集成Nlog了。
下面继续:
Entity Framework Core 2.0
Entity Framework 是ORM(Object-Relational-Mapping)。ORM是一种让你可以使用面向对象的范式对数据库进行查询和操作。
简单的情况下,ORM可以把数据库中的表和Model对象一一映射起来;也有比较复杂的情况,ORM允许使用OO(面向对象)功能来做映射,例如:Person作为基类,Employee作为Person的派生类,他们俩可以在数据库中映射成一个表;或者在没有继承的情况下,数据库中的一个表可能和多个类有映射关系。
EF Core 不是 EF6的升级版,这个大家应该知道,EF Core是轻量级、具有很好的扩展性的,并且是跨平台的EF版本。
EF Core 目前有很多Providers,所以支持很多种数据库,包括:MSSQL,SQLite,SQL Compact,Postgres,MySql,DB2等等。而且还有一个内存的Provider,用于测试和开发。开发UWP应用的时候也可以使用EF Core(用SQLite Provider)。
EF Core支持两种模式:
Code First:简单理解为 先写C#(Model),然后生成数据库。
Database First:现在数据库中建立表,然后生成C#的Model。
由于用asp.net core 2.0开发的项目基本都是新项目,所以建议使用Code First。
创建 Entity
Entity就是普通的C#类,就像Dto一样。Dto是与外界打交道的Model,entity则不一样,有一些Dto的计算属性我们并不像保存在数据库中,所以entity中没有这些属性;而数据从entity传递到Dto后某些属性也会和数据库里面的形式不一样。
首先把我们原来的Product和Material这两个Dto的名字重构一下,改成ProductDto和MaterialDto。
建立一个Entities文件夹,在里面建立Product.cs:
namespace CoreBackend.Api.Entities{
public class Product {
public int Id { get; set; }
public string Name { get; set; }
public float Price { get; set; } }}
DbContext
EFCore使用一个DbContext和数据库打交道,它代表着和数据库之间的一个Session,可以用来查询和保存我们的entities。
DbContext需要一个Provider,以便能访问数据库(这里我们就用LocalDB吧)。
我们就建立一个DbContext吧(大一点的项目会使用多个DbContext)。建立MyContext并集成DbContext:
namespace CoreBackend.Api.Entities{
public class MyContext : DbContext {
public DbSet<Product> Products { get; set; } }}
这里我们为Product建立了一个类型为DbSet<T>的属性,它可以用来查询和保存实例(针对DbSet的Linq查询语句将会被解释成针对数据库的查询语句)。
因为我们需要使用这个MyContext,所以就需要先在Container中注册它,然后就可以在依赖注入中使用了。
打开Startup.cs,修改ConfigureServices,添加这一句话:
services.AddDbContext<MyContext>();
使用AddDbContext这个Extension method为MyContext在Container中进行注册,它默认的生命周期使Scoped。
但是它如何连接数据库?这就需要连接字符串,我们需要为DbContext提供连接字符串,这里有两种方式。
第一种是在MyContext中override OnConfiguring这个方法:
namespace CoreBackend.Api.Entities{ public class MyContext : DbContext {
public DbSet<Product> Products { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("xxxx connection string"); base.OnConfiguring(optionsBuilder); } }}
其中的参数optionsBuilder提供了一个UseSqlServer()这个方法,它告诉Dbcontext将会被用来连接Sql Server数据库,在这里就可以提供连接字符串,这就是第一种方法。
第二种方法:
先大概看一下DbContext的源码的定义:
namespace Microsoft.EntityFrameworkCore{ public class DbContext : IDisposable, IInfrastructure<IServiceProvider>, IDbContextDependencies, IDbSetCache, IDbContextPoolable {
public DbContext([NotNullAttribute] DbContextOptions options);
有一个Constructor带有一个DbContextOptions参数,那我们就在MyContext种建立一个Constructor,并overload这个带有参数的Constructor。
namespace CoreBackend.Api.Entities{
public class MyContext : DbContext {
public MyContext(DbContextOptions<MyContext> options) :base(options) { }
public DbSet<Product> Products { get; set; } }}
这种方法相对第一种的优点是:它可以在我们注册MyContext的时候就提供options,显然这样做比第一种override OnConfiguring更合理。
然后返回Startup:
public void ConfigureServices(IServiceCollection services) { services.AddMvc();
#if DEBUG services.AddTransient<IMailService, LocalMailService>();
#else services.AddTransient<IMailService, CloudMailService>();
#endif var connectionString = @"Server=(localdb)\MSSQLLocalDB;Database=ProductDB;Trusted_Connection=True"; services.AddDbContext<MyContext>(o => o.UseSqlServer(connectionString)); }
使用AddDbContext的另一个overload的方法,它可以带一个参数,在里面调用UseSqlServer。
关于连接字符串,我是用的是LocalDb,实例名是MSSQLLocalDB。可以在命令行查询本机LocalDb的实例,使用sqllocaldb info:
也可以通过VS的Sql Server Object Explorer查看:
连接字符串中的ProductDb是数据库名;连接字符串的最后一部分表示这是一个受信任的连接,也就是说使用了集成验证,在windows系统就是指windows凭证。
生成数据库
因为我们使用的是Code First,所以如果还没有数据库的话,它应该会自动建立一个数据库。
打开MyContext:
public MyContext(DbContextOptions<MyContext> options) :base(options) { Database.EnsureCreated(); }
这个Constructor在被依赖注入的时候会被调用,在里面写Database.EnsureCreated()。其中Database是DbContext的一个属性对象。
EnsureCreated()的作用是,如果有数据库存在,那么什么也不会发生。但是如果没有,那么就会创建一个数据库。
但是现在就运行的话,并不会创建数据库,因为没有创建MyContext的实例,也就不会调用Constructor里面的内容。
那我们就建立一个临时的Controller,然后注入MyContext,此时就调用了My