【ASP.NET Core】EF Core 模型与数据库的创建

大家好,欢迎收看由土星卫视直播的大型综艺节目——老周吹逼逼。

今天咱们吹一下 EF Core 有关的话题。先说说模型和数据库是怎么建起来的,说装逼一点,就是我们常说的 “code first”。就是你先创建了数据模型,然后再根据模型来创建数据库。这种做法的一个好处是让面向对象的逻辑更好地表现出来。以前,咱们通常是先创建数据库的。

像 EF 这么嗨的东西,ASP.NET Core 中自然也是少不了的,即 EF Core。

好了,以上就是理论部分,比较乏味,是吧。那好,下面咱们干点正事。

构建模型

建立模型很简单,就是定义一个类(为了好理解,老周暂且不说关系模型)。来,看看,就像下面这个类,假设它表示的是某工厂生产的山寨产品信息。

    public class Product
    {
        public int ProdID { get; set; }
        public string ProdName { get; set; }
        public DateTime FinishDate { get; set; }
        public double Weight { get; set; }
    }

有人会问:完事了?嗯,完事了,这就是一个模型了,但还是不能创建数据库的。

继承 DBContext

虽然咱们有了山寨产品的模型类,但你还得实现一个数据上下文。通常呢,数据上下文是映射到某个数据库的。上下文的定义是从 DbContext 类派生出一个类,然后,把它与模型类关联起来。

    public class MyDBContext : DbContext
    {
        public DbSet<Product> Products { get; set; }
    }

DbSet 会映射到数据库中的一个表。

为了实现依赖注入,以及能够在 Startup 类中进行配置,你可以在自己实现的 DBContext 子类中公开构造函数,并且接收一个 DbContextOptions<TContext> 类型的参数注入,TContext 就是咱们自己定义的从 DBContext 类派生的类。

    public class MyDBContext : DbContext
    {
        public MyDBContext(DbContextOptions<MyDBContext> options)
            : base(options)
        {
            // 暂无其他代码
        }

        public DbSet<Product> Products { get; set; }
    }

注册服务

有了模型和数据上下文,接下来咱们要在 Startup 类中注册一下相关的服务,并且配置一下像连接字符串之类的参数。

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddDbContext<MyDBContext>(option =>
            {
                option.UseSqlServer("server=(localdb)\\MSSQLLocalDB;database=DemoDB");
            });
        }

创建数据库“迁移”

创建迁移的好处是灵活,如果你的模型后面修改了(比如添加了一个属性),那么你可以在原有的迁移基础上再添加新的迁移,这些数据迁移会不断叠加,所以,你不需要删除过去的迁移版本,因为后面添加的不会重复,只会包含更新数据模型的代码。

创建迁移有多种方式:1、dotnet 命令行;2、VS 中的 nuget 控制台;3、直接用代码。

dotnet cli 即使用 dotnet 命令行工具来对数据模型进行迁移,其命令为 dotnet ef <...>。这里老周演示的是用 VS 中的 nuget 控制台来处理,dotnet cli 的方法类似,你可以输入 dotnet ef --help 来查看帮助。

在 VS 中,打开 【工具】-【NuGet 包管理器】-【程序包管理器】菜单项,随后就能打开控制台窗口。你可以输入以下命令查看帮助文档。

get-help about_EntityFrameworkCore

你要是觉得名字太长了,可以这样输入

get-help about_*core

星号是通配符,它会查找所有以 about_ 开头,以 Core 结尾的说明文档。

好,下面咱们为前面已定义好的 MyDBContext 生成数据迁移代码,使用的命令是 Add-Migration。用法如下。

Add-Migration [-Name] <String> [-OutputDir <String>] [-Context <String>] [-Project <String>] [-StartupProject <String>] 

其实后面还有个参数列表的,但用不上,就不列出来了。注意,只有位于第一个位置的 -Name 参数名可以省略,后面的都不能省略参数名。即对于迁移点的命名,你可以输入

Add-Migration -Name "demo001"

也可以输入

Add-Migration "demo001"

-OutputDir 指的是生成的代码放在哪个目录下面,默认叫 Migrations。注意它是相对于项目目录的路径。-Context 指定的是你自己定义的 DBContext 的子类的名称,包含命名空间名称,如果是当前项目,可以不写。

-Project 和 -StartupProject 一般不用刻意指定,如果解决方案中有多个项目,可以指定一下,生成的迁移属于哪个项目。-StartupProject 可以不指定,让它选择与解决方案配置一致的启动项目。

好,下面咱们为刚刚定义的 MyDBContext 生成数据迁移。输入

add-migration "demo001" -Context "MyDBContext" -OutputDir "CustMigrations"

执行后就呵呵了,出现一个警告和一个异常。

警告信息只是 SDK 与运行时版本没统一而已,这个可以不鸟它,不影响命令执行。最大的问题是发生异常,这会导致命令不能执行。发生异常是因为我们上面定义的那个 Product 类,没有声明主键。

于是,我们就让 ProdID 作为主键,方法有两种。第一,通过“数据批注”,就像这样。

    public class Product
    {
        [Key]
        public int ProdID { get; set; }
        public string ProdName { get; set; }
        public DateTime FinishDate { get; set; }
        public double Weight { get; set; }
    }

如果你认为用特性来批注很难看,那就用第二种方法,在继承 DBContext 的类中重写 OnModelCreating 方法。

    public class MyDBContext : DbContext
    {
        ……

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Product>().HasKey(p => p.ProdID);
        }
    }

现在再执行一次 Add-Migration 命令,就顺利创建数据迁移了。

假如现在我觉得模型要修改,新增一个 Remark 属性。

    public class Product
    {
        public int ProdID { get; set; }
        public string ProdName { get; set; }
        public DateTime FinishDate { get; set; }
        public double Weight { get; set; }
        public string Remark { get; set; }
    }

此时,你不用删除前面创建的迁移,你只需要再加一个迁移即可,它会自动累积的。

add-migration "demo002" -Context "MyDBContext" -OutputDir "CustMigrations"

你能看到,demo002 迁移生成的代码,仅仅是添加了 Remark 列。

    public partial class demo002 : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.AddColumn<string>(
                name: "Remark",
                table: "Products",
                nullable: true);
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropColumn(
                name: "Remark",
                table: "Products");
        }
    }

所以,看得出来,它不会重复生成表结构的。Up 方法表示的是当前的状态,Down 方法是在执行 Remove-Migration 时进行回退,回退时删除 Remark 列。

创建数据库

有了上面的步聚,现在可以创建数据库了。这里老周以 SQLLocalDB 为例,在 CMD 中启动默认的 MSSQLLocalDB 实例。

sqllocaldb start mssqllocaldb

回到 VS 中,执行 Update-Database 命令。

update-database

无参数的情况下,执行所有迁移中的内容,为了使创建的数据库结构完整,应该执行所有迁移。

SQLLocalDB 创建的数据库默认存放在你的用户目录下,即 C:\\Users\\Your name\\ 下面,路径变量是 %userprofile%。

当你想删除数据库时,可以输入以下命令。

drop-database -Context "MyDBContext"

这时候,它会问你,真的要删库跑路吗?

此时你心意已决,删库跑路,输入 Y 或 A,确认。

测试数据库

好了,现在,模型也建好了,数据库也有了,可以来测一下了。

先创建个控制器。

    public class DemoController : Controller
    {
        MyDBContext _dbcontext;
        public DemoController(MyDBContext context)
        {
            _dbcontext = context;
        }

        [HttpGet]
        public ActionResult Products()
        {
            return View(_dbcontext.Products.ToList());
        }

        [HttpPost]
        public ActionResult Products(Product p)
        {
            if (ModelState.IsValid)
            {
                _dbcontext.Products.Add(p);
                _dbcontext.SaveChanges();
            }
            return View(_dbcontext.Products.ToList());
        }
    }

db context 可以在构造函数能过依赖注入来获取,因为前面我们已经在 Startup.ConfigureServices 方法中注册了相关服务。添加新记录时直接把方法参数接收到的 Product 实例 Add 到 DbSet 中即可,但要记得调用 SaveChanges 方法,因为调用方法后数据才会真正写入数据库。

控制器中包含了两个 Products 的 action 方法,使用以下路由规则,可以匹配出两个方法。

            app.UseMvc(route =>
            {
                route.MapRoute("test", "{controller=Demo}/{action=Products}");
            });

解决方法就是,无参数的 Products 方法以 GET 方式访问,而带参数的 Products 方法以 POST 方式访问。

创建一个与 Products 方法同名的视图。在视图中用 @model 指令定义 Model 的类型为 List<Product>,因为上面控制器中,调用 View 方法时,传递给视图的是 List<Product> 类型的 Model。

视图代码如下。

@using Web7362
@model List<Product>
@addTagHelper  *,Microsoft.AspNetCore.Mvc.TagHelpers
<html>
<body>
    <div>
        <form method="post">
            <table>
                <tr>
                    <td>
                        产品名称:
                    </td>
                    <td><input type="text" name="ProdName" /></td>
                </tr>
                <tr>
                    <td>完成日期:</td>
                    <td><input name="FinishDate" type="date"/></td>
                </tr>
                <tr>
                    <td>产品重量:</td>
                    <td>
                        <input name="Weight"/>
                    </td>
                </tr>
                <tr>
                    <td>产品备注:</td>
                    <td><input name="Remark" type="text" /></td>
                </tr>
                <tr>
                    <td colspan="2">
                        <input type="submit" value="新增" />
                    </td>
                </tr>
            </table>
        </form>
    </div>
    <div>
        <table border="1">
            @foreach(var p in Model)
            {
                <tr>
                    <td>@p.ProdID</td>
                    <td>@p.ProdName</td>
                    <td>@p.FinishDate</td>
                    <td>@p.Weight</td>
                    <td>@p.Remark</td>
                </tr>
            }
        </table>
    </div>
</body>
</html>

第一个 div 中的 form 用于提交新的山寨产品记录,第二个 div 用来显示产品列表。

当提交时,如何把 form 中输入的内容传递给 Product 新对象,你可能会想到使用 asp-for 标签帮助器。但此处不能使用 asp-for 帮助器,因为 Model 的类型是 List<Product> ,不是 Product 类型。

那咋办呢,可以利用 input 元素的 name 值,将 name 值设置为与  Product 类的各属性名称相同的值即可。

     <input type="text" name="ProdName" />
     <input name="FinishDate" type="date"/>
     <input name="Weight"/>
     <input name="Remark" type="text" />

这样设置后,在提交时 Model Binder 就可以自动识别并填充 Product 实例的各个属性了。

你也会问了,为啥没有为 ProdID 属性弄个 input 元素?因为这个属性是主键,其值由数据库生成,不必手动输入。

来来来,看看效果。

IDesignTimeDbContextFactory<out TContext> 接口

这个接口有两种情况下,你可以考虑使用。

1、默认项目模板生成的 Main 方法被你修改了。准确地说,是你删除了 CreateWebHostBuilder 方法。默认生成的 Main 是这样的。

        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();

然后,你嫌它生成的代码不好看,也觉得日志太多影响性能,所以改为这样。

        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseStartup<Startup>()
                .UseKestrel()
                .UseUrls("http://localhost:7676")
                .UseEnvironment(EnvironmentName.Development)
                .UseSetting(WebHostDefaults.ApplicationKey, "大飞侠充值系统")
                .Build();
            host.Run();
        }

这样一来,你想执行 Add-Migration 命令,就会收到这条错误。

2、设计时需要。有时候,你用来开发测试的数据库服务器和正式投入使用的不是同一个服务器。这时候,你可以实现 IDesignTimeDbContextFactory<out TContext> 接口,创建用于测试的数据上下文(尤其是连接字符串)。

下面用另一个示例来演示一下。

先创建一个模型类。

    public class Charge
    {
        public int ID { get; set; }
        public DateTime Time { get; set; }
        public decimal Money { get; set; }
        public string PhoneNo { get; set; }
    }

然后是实现数据上下文。

    public class DemoDBContext : DbContext
    {
        public DemoDBContext(DbContextOptions<DemoDBContext> options)
            : base(options) { }

        public DbSet<Charge> Charges { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Charge>().HasKey(o => o.ID);
        }
    }

由于默认的 Main 函数被修改了,执行 Add-Migration 命令,会发生错误。

其实,错误信息中已经告诉你解决方法了,就是实现 IDesignTimeDbContextFactory<out TContext> 接口。所以,就实现一下呗。

    public class CustDesigntimeContext : IDesignTimeDbContextFactory<DemoDBContext>
    {
        public DemoDBContext CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<DemoDBContext>();
            // 设置连接字符串
            optionsBuilder.UseSqlServer("server=(localdb)\\mssqllocaldb;database=test_db");
            // 创建上下文实例
            return new DemoDBContext(optionsBuilder.Options);
        }
    }

现在,再执行 Add-Migration 命令就正常了。

add-migration "check01" -outputdir "MgChecks" -context "DemoDBContext"

然后可以创建数据库。

 update-database

接下来用 Web API 来测一下。先在 Startup.ConfigureServices 方法中注册一下相关服务。

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddDbContext<DemoDBContext>(opt =>
            {
                opt.UseSqlServer("server=(localdb)\\mssqllocaldb;database=test_db");
            });
        }

实现 IDesignTimeDbContextFactory 接口只用于设计阶段,不用于应用程序运行阶段,所以,相关的配置还是要做的。

定义控制器。

    [Route("charger/[action]")]
    public class ChargerController : Controller
    {
        private readonly DemoDBContext _dbcontext;
        public ChargerController(DemoDBContext cxt)
        {
            _dbcontext = cxt;
            // 初始化一些数据
            if (!_dbcontext.Charges.Any())
            {
                Charge c1 = new Charge
                {
                    PhoneNo = "13325236411",
                    Money = 50.00M,
                    Time = new DateTime(2018, 10, 9, 20, 16, 0)
                };
                Charge c2 = new Charge
                {
                    PhoneNo = "15900254200",
                    Money = 100.00M,
                    Time = new DateTime(2018, 6, 22, 19, 0, 0)
                };
                Charge c3 = new Charge
                {
                    PhoneNo = "13500001122",
                    Money = 30.1M,
                    Time = new DateTime(2018, 10, 13, 15, 20, 10)
                };
                _dbcontext.Charges.AddRange(c1, c2, c3);
                _dbcontext.SaveChanges();
            }
        }

        public ActionResult Index()
        {
            return Json(_dbcontext.Charges);
        }
    }

运行结果如下。

好了,今天的内容就到这里了,文中示例的源代码可以拼命点 这里 下载。

原文地址:https://www.cnblogs.com/tcjiaan/p/9785556.html

时间: 2024-10-24 11:11:34

【ASP.NET Core】EF Core 模型与数据库的创建的相关文章

asp.net core+ef core

asp.net core+ef core 官方的文档https://docs.asp.net/en/latest/tutorials/first-mvc-app/start-mvc.html 先来看一下实现的效果 开始之前,确定本机已经有.NET Core环境.https://www.microsoft.com/net/core#windows 1.创建解决方案的文件结构如下图(模糊处理的过文件是自己后面加的和ef生成的). 2.要使用ef core,先引用ef core相关的程序包.https

.net core EF Core 视图的应用

由之前的一篇文章<.net core Entity Framework 与 EF Core>我们都已经知道 EF Core 增加了许多特性,并且性能上也有了很大的提升. 但是EF Core是不支持存储过程及视图的映射的,那么直接通过 DbContext 是没有办法直接调用(就是不能直接 "点" 出来)到存储过程与视图的. 上一篇<.net core EF Core 调用存储过程>中已经讲到了存储过程的调用了,这篇就只讲视图了. 对视图来讲,在数据库中 EF Co

在vs2015上使用asp.net core+ef core

官方的文档https://docs.asp.net/en/latest/tutorials/first-mvc-app/start-mvc.html 先来看一下实现的效果 开始之前,确定本机已经有.NET Core环境.https://www.microsoft.com/net/core#windows 1.创建解决方案的文件结构如下图(模糊处理的过文件是自己后面加的和ef生成的). 2.要使用ef core,先引用ef core相关的程序包.https://docs.efproject.net

asp.net core ef core mysql 新增数据并发异常处理

net core 2.0发布后,一直想体验下,因种种原因,一直在拖着没进行. 前阵子公司要加个新的内部管理后台,正好可以用asp.net core来做下,体验下net core的魅力. 啃过文档后就上手了,一切很顺利. 直到周五,出现了一个并发异常的问题,本以为可以很快处理掉的,但没想到一直花费了很长时间才解决掉,现在记录下情况,有相同经历的伙伴以后可以参考. 先上异常截图. 异常提示: Microsoft.EntityFrameworkCore.DbUpdateConcurrencyExcep

[.Net Core] EF Core实践(DB First)

一.开发环境: VS2015, .Net Core 1.0.0-preview2-003156 二.准备数据: CREATE DATABASE [Blogging]; GO USE [Blogging]; GO CREATE TABLE [Blog] ( [BlogId] int NOT NULL IDENTITY, [Url] nvarchar(max) NOT NULL, CONSTRAINT [PK_Blog] PRIMARY KEY ([BlogId]) ); GO CREATE TAB

asp.net Core EF core ( Entity Framework 7 ) 数据库更新维护

CreateData-baseIfNotExists等之前的API已经废弃,现在采用的是微软封装好,简化.高效的API,migrations 因为,旧API,要付出高昂的代价,以及局限性 打开VS2017,选择工具->NutGet包管理器->程序包管理器控制台 1.输入Add-Migration MyFirstMigration 指令 就会根据当前的dbcontext自动生成Migrations文件夹及文件,这些文件用于新建.或者扩展专属于Migrations 这个API的扩展的数据库 然后在

EntityFramework Core技术线路(EF7已经更名为EF Core,并于2016年6月底发布)

官方文档英文地址:https://github.com/aspnet/EntityFramework/wiki/Roadmap 历经延期和更名,新版本的实体框架终于要和大家见面了,虽然还有点害羞.请大家多体谅! 下面正式进入主题: Entity Framework Core (EF Core) 下面是EF Core 的计划和技术线路,注意,这些计划是可能发现变化的,因为很多事是很难预测的.即便如此,我们还是尽可能保持计划的公开和透明,以解大家对EF Core期望,以及做出相应的安排. Sched

[转]EntityFramework Core技术线路(EF7已经更名为EF Core,并于2016年6月底发布)

本文转自:http://www.cnblogs.com/VolcanoCloud/p/5572408.html 官方文档英文地址:https://github.com/aspnet/EntityFramework/wiki/Roadmap 历经延期和更名,新版本的实体框架终于要和大家见面了,虽然还有点害羞.请大家多体谅! 下面正式进入主题: Entity Framework Core (EF Core) 下面是EF Core 的计划和技术线路,注意,这些计划是可能发现变化的,因为很多事是很难预测

EF Core实践 (使用MS SqlServer)

这里使用 MS SQLSERVER ,网上大多使用 SQLite 先来一个CodeFirst 新建项目 这里我们选择  ASP.NET Core Web Application (.NET Core)  这里选择web 应用程序,然后更改身份验证 改为 不进行身份验证 然后再包管理控制台里执行下面两条命令 引用 EntityFrameworkCore Install-Package Microsoft.EntityFrameworkCore 再引用 EntityFrameworkCore.Sql