Entity Framework的入门应用

一、数据模型

  1. Database First (数据库优先):先创建数据库表,然后自动生成EDM文件,EDM文件生成模型类  
  2. Model First (模型优先):先创建Edm文件,Edm文件自动生成模型类和数据库;
  3. Code First(代码优先):自己写模型类,然后生成数据库,没有EDM。

 这里我们现在 Code First,选择Code First不能在数据库中创建数据表,否则在我们执行数据迁移(Migrations)时会报错。

二、安装 Entity Framework 

 可以看我另一篇文章

三、数据表的创建 

 在EntityFramework中对的数据模型的配置有两种模式,一种是 DataAnnotations(数据注释),另一个是 Fluent API(利用接口)。DataAnnotaions 配置相对简单些,Fluent API 可以配置些复杂的功能。

例如我们要创建用户表(不用执行,Code First全用代码管理数据库),Sql语句如下:

CREATE TABLE [dbo].[T_User] 
(
    [ID] [numeric](18, 0) NOT NULL IDENTITY,
    [UserName] [nvarchar](128),
    [PassWord] [nvarchar](128),
    [UserType] [int] NOT NULL,
    [Description] [nvarchar](512),
    [Timestamp] [datetime] NOT NULL,
    CONSTRAINT [PK_dbo.T_User] PRIMARY KEY ([ID])
)

我们先说用  DataAnnotations 创建用户对象:

namespace SqlEntityFramework
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    public enum UserType_e : int
    {
        Admin,
        System,
        User,
    }

    [Table("T_User")]    //为User类指定对应的数据库中的表
    public partial class User
    {
        [Key]  //指定为主键
        [Column(TypeName = "numeric", Order = 0)]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public decimal ID { get; set; }

        [StringLength(128)]
        [Column("UserName", Order = 1)]   //为UserName属性指定表中的对应的名称
        public string UserName { get; set; }

        [StringLength(128)]
        [Column(Order = 2)]
        public string PassWord { get; set; }

        [Column(Order = 3)]
        public UserType_e UserType { get; set; }

        [StringLength(512)]
        [Column(Order = 4)]
        public string Description { get; set; }

        [Column(Order = 5)]
        public DateTime Timestamp { get; set; }
    }
}

常用的数据 特性有以下这些:参考MSDN

特性名称描述举例
Table指定类将映射到的数据库表(只能应用于类):
1.参数(Name),类映射到的表的名称;
2.Schema:类映射到表的架构;
[Table("Name")]
Column

表示属性将映射到的数据库列:

1.可以带一个字符串参数(Name),映射到的列的名称;

2.Order:属性映射到的列的从零开始的顺序;

3.TypeName:映射到的列的数据库提供程序特定数据类型;

[Column(TypeName = "numeric", Order = 0)]
Key表示唯一标识实体的一个或多个属性:
1.指定属性为主键,可以给多个属性指定表示联合主键
[Key]
ForeignKey表示关系中用作外键的属性:
1.参数(Name),表示关联的导航属性或关联的外键属性的名称
[ForeignKey("Name")]
StringLength指定数据字段中允许的字符的最小长度和最大长度:
1.参数(MaximumLength)一个字符串,最大长度;
2.MinimumLength:一个字符串的最小长度;
[StringLength(128)]
DatabaseGenerated指定数据库生成属性值的方式:
1.参数(DatabaseGeneratedOption)用于在数据库中生成的属性的值的模式
   None:数据库不生成值
   Identity:在插入行时,数据库将生成值
   Computed:在插入或更新行时,数据库将生成值
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
Required指定数据字段值是必需的:
1.AllowEmptyStrings:是否允许为空字符串;
2.ErrorMessage:如果验证失败的错误消息;
[Required]
MaxLength允许的数组或字符串数据的最大长度:
1.可以带一个字符串参数(Length),表示数组或字符串数据的最大允许长度;
[MaxLength]
MinLength允许的数组或字符串数据的最小长度:
1.可以带一个字符串参数(Length),表示数组或字符串数据的最小允许长度;
[MinLength]
NotMapped表示应从数据库映射中排除属性或类[NotMapped]
ComplexType不用在一组类中描述域实体,然后将这些类分层以描述完整的实体(只能应用于类)[ComplexType]

     注意:

  1. 在数据库中表名或者列名都不区分大小写的,而在C#代码中是区分大小写的;
  2.  EF约定的主键是ID,所以可以不用再特殊指定Id为主键,若是其他的属性可以加上[Key];
  3. 每个对象模型都必须有一个主键;
  4. 一旦将模型更新到数据库后,已添加的特性就不要轻易更改(若有更改一定要进行迁移更新数据库),否则会出错;
  5. DatabaseGeneratedOption特性在创建表之后修改的话,并不能更新到数据库,不知道是不是EF版本的问题,若要修改需手动修改数据库(模型和数据库不同步会报错);
  6. Column.Order也只是在创建表时可以指定列的顺序,一旦创建好之后再改变顺序是不起作用的(只要不存在同样的序号改变值不会报错);

四、创建 DbContext 

using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;

namespace SqlEntityFramework
{
    public partial class DBContext : DbContext
    {
        static string connectionString = @"data source=127.0.0.1;initial catalog=Bridge;persist security info=True;user id=sa;password=123;MultipleActiveResultSets=True;App=EntityFramework";

        public DBContext()
            : base(connectionString)
        {
        }

        public virtual DbSet<User> T_User { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
        }
    }
}

 这里可以自己定义连接字符串,也可以从App.config中获取,若从App.config中获取应该这么写:

using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;

namespace SqlEntityFramework
{
    public partial class DBContext : DbContext
    {
        public DBContext()
            : base("name=DBContext")
        {
        }

        public virtual DbSet<User> T_User { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
        }
    }
}

App.config中添加:

	<connectionStrings>
		<add name="DBContext" connectionString="data source=127.0.0.1;initial catalog=Bridge;persist security info=True;user id=sa;password=123;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
	</connectionStrings>

五、 Fluent API

现在讲用Fluent API创建对象模型,在这里才说Fluent API,因为要在DBContext中定义接口。

1.Fluent API是重写 DbContext 的 OnModelCreating 方法:

using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;

namespace SqlEntityFramework
{
    public partial class DBContext : DbContext
    {
        public DBContext()
            : base("name=DBContext")
        {
        }

        public virtual DbSet<User> T_User { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>().ToTable("T_User").HasKey(u => u.ID);
            modelBuilder.Entity<User>().Property(u => u.ID).HasPrecision(10, 0).HasColumnType("numeric").HasColumnOrder(0).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            modelBuilder.Entity<User>().Property(u => u.UserName).HasMaxLength(64).HasColumnName("UserName").HasColumnOrder(1);
            modelBuilder.Entity<User>().Property(u => u.PassWord).HasMaxLength(64).HasColumnOrder(2);
            modelBuilder.Entity<User>().Property(u => u.UserType).HasColumnOrder(3);
            modelBuilder.Entity<User>().Property(u => u.Description).HasMaxLength(512).HasColumnOrder(4);
            modelBuilder.Entity<User>().Property(u => u.Timestamp).HasColumnOrder(5);
        }
    }
}

2.也可以为每个数据模型编写映射对象:

namespace SqlEntityFramework
{
    using System.Data.Entity.ModelConfiguration;
    using System.ComponentModel.DataAnnotations.Schema;

    public class UserMap : EntityTypeConfiguration<User>
    {
        public UserMap()
        {
            this.ToTable("T_User").HasKey(u => u.ID);
            this.Property(u => u.ID).HasPrecision(10, 0).HasColumnType("numeric").HasColumnOrder(0).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            this.Property(u => u.UserName).HasMaxLength(64).HasColumnName("UserName").HasColumnOrder(1);
            this.Property(u => u.PassWord).HasMaxLength(64).HasColumnOrder(2);
            this.Property(u => u.UserType).HasColumnOrder(3);
            this.Property(u => u.Description).HasMaxLength(512).HasColumnOrder(4);
            this.Property(u => u.Timestamp).HasColumnOrder(5);
        }
    }
}

 然后修改DBContext类:

using System;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.Linq;
using System.Reflection;

namespace SqlEntityFramework
{
    public partial class DBContext : DbContext
    {
        public DBContext()
            : base("name=DBContext")
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            Assembly asm = Assembly.LoadFrom(Assembly.GetExecutingAssembly().CodeBase);
            modelBuilder.Configurations.AddFromAssembly(asm);
            base.OnModelCreating(modelBuilder);
        }
    }
}

六、数据迁移

 https://docs.microsoft.com/zh-cn/ef/ef6/modeling/code-first/migrations/?redirectedfrom=MSDN

 每当我们更改了代码中的数据库模型,例如修改了对象的数据类型,添加或者删除了某个列,都需要通过数据迁移更新到数据库,以此来改进应用程序的数据库架构。

1.启用上下文迁移

   打开包管理器控制台并运行 Enable-Migrations 命令

   

  运行成功会在项目文件下创建一个 Migrations文件夹,并有一个 Configuration.cs 文件

namespace SqlEntityFramework.Migrations
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Migrations;
    using System.Linq;

    internal sealed class Configuration : DbMigrationsConfiguration<SqlEntityFramework.DBContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }

        protected override void Seed(SqlEntityFramework.DBContext context)
        {
            //  This method will be called after migrating to the latest version.

            //  You can use the DbSet<T>.AddOrUpdate() helper extension method
            //  to avoid creating duplicate seed data.
        }
    }
}

 2.添加迁移

  控制台运行 Add-Migration InititalCreate 命令

运行成功后会在  Migrations文件夹自动创建了一个 带有InititalCreate名称的文件,文件名称一般都是  【时间_操作名】

namespace SqlEntityFramework.Migrations
{
    using System;
    using System.Data.Entity.Migrations;
    
    public partial class InititalCreate : DbMigration
    {
        public override void Up()
        {
            CreateTable(
                "dbo.T_User",
                c => new
                    {
                        ID = c.Decimal(nullable: false, precision: 18, scale: 0, identity: true, storeType: "numeric"),
                        UserName = c.String(maxLength: 64),
                        PassWord = c.String(maxLength: 128),
                        UserType = c.Int(nullable: false),
                        Description = c.String(maxLength: 512),
                        Timestamp = c.DateTime(nullable: false),
                    })
                .PrimaryKey(t => t.ID);
            
        }
        
        public override void Down()
        {
            DropTable("dbo.T_User");
        }
    }
}
  1. 这里将ID的scale改为0,因为自增列不能是小数。
  2. Up方法就表示在你更新时要执行的方法,而Down表示你要退回时执行的方法,后续会说到。
  3. 你也可以在这两个方法里面自己编写需要执行的语句。

3.更新到数据库 

没有执行这一部之前,所有的更改都没有提交到数据库,所以一旦提交到数据库后就不要轻易删除上一步生成的DbMigration文件 

控制台运行 Update-Database  -verbose 命令 (可以不加上 -verbose,加上这个可以在控制台打印出执行的sql语句)

  1. 通过执行的sql语句我们可以发现除了创建了我们自己的表以外,还创建了一个名为 __MigrationHistory 的表,该表就是存储我们每次迁移后更新到数据库的信息;
  2. __MigrationHistory 表中的数据是和代码中的迁移文件一一对应的,也不要轻易的删除;
  3. 每添加一次迁移就要提交到数据库后才能进行下一次的迁移;

4. 迁移文件

每个迁移文件除了基本的代码外,还包括一个designer文件和一个resx文件

// <auto-generated />
namespace SqlEntityFramework.Migrations
{
    using System.CodeDom.Compiler;
    using System.Data.Entity.Migrations;
    using System.Data.Entity.Migrations.Infrastructure;
    using System.Resources;
    
    [GeneratedCode("EntityFramework.Migrations", "6.4.4")]
    public sealed partial class InititalCreate : IMigrationMetadata
    {
        private readonly ResourceManager Resources = new ResourceManager(typeof(InititalCreate));
        
        string IMigrationMetadata.Id
        {
            get { return "202101060641456_InititalCreate"; }
        }
        
        string IMigrationMetadata.Source
        {
            get { return null; }
        }
        
        string IMigrationMetadata.Target
        {
            get { return Resources.GetString("Target"); }
        }
    }
}

 

  1. IMigrationMetadata.Id 就对应了__MigrationHistory 表中的 MigrationId 
  2. 每次修改数据模型,EF是怎么知道我们改了哪呢?它就是通过比对__MigrationHistory表中所有的数据进行校验是否发生了改变

5.迁移到特定版本(包括降级) 

 控制台运行 Update-Database  –TargetMigration: InititalCreate 命令表示要迁移到 InititalCreate,就会执行 InititalCreate之后添加的迁移的Down方法。

如果想要一直回退到空数据库,可使用 Update-Database –TargetMigration: $InitialDatabase 命令 。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bridge_go

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值