【EF Core】基础

简介

Entity Framework Core(EF Core)是微软官方的ORM框架。优点:功能强大、官方支持、生产效率高、力求屏蔽底层数据库差异;缺点:复杂、上手门槛高、不熟悉EFCore的话可能会进坑。

Nuget包推荐

SqlServer:Microsoft.EntityFrameworkCore.SqlServer
MySQL:Pomelo.EntityFrameworkCore.MySql  (开源开发者,下载量最高)

Code First开发流程

Console控制台

  • 安装对应数据库的Nuget包,如下:
Install-Package Microsoft.EntityFrameworkCore.SqlServer
  • 创建实体类
public class Book
{
    public long Id { get; set; }//主键
    public string Title { get; set; }//标题
    public DateTime PubTime { get; set; }//发布日期
    public double Price { get; set; }//单价
    public string AuthorName { get; set; }//作者名字
    public string Decription { get; set; }
    //public string Content { get; set; }
}
  • 创建实体配置类
    配置类需要实现接口IEntityTypeConfiguration<TEntity>,它是用来配置实体类和表的对应关系
public class BookEntityConfig : IEntityTypeConfiguration<Book>
{
    public void Configure(EntityTypeBuilder<Book> builder)
    {
        builder.ToTable("T_Books");
        builder.Property(e => e.Title).HasMaxLength(50).IsRequired();
        builder.Property(e => e.AuthorName).HasColumnType("varchar(100)").IsRequired();

    }
}
  • 创建DbContext
    SqlServer:
    public class TestDbContext : DbContext
    {
        public DbSet<Book> Books { get; set; }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            string connStr = "Data Source=.;Initial Catalog=EFCoreDB;User ID=sa;Password=123456";
            optionsBuilder.UseSqlServer(connStr);
            optionsBuilder.LogTo(Console.WriteLine);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
        }
    }

MySql:

class TestDbContext : DbContext
{
    public DbSet<Book> Books { get; set; }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseMySql("server=localhost;user=root;password=adfa3_ioz09_08nljo;database=ef",
            new MySqlServerVersion(new Version(8, 6, 20)));
    }
}

ASP.NET Core

DbContext:

public class TestDbContext:DbContext
{
    public DbSet<Dog> Dogs { get; private set; }

    public TestDbContext(DbContextOptions<TestDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
    }
}

注册DbContext:

services.AddDbContext<TestDbContext>(opt => {
    string connStr = "Server=.;User ID=sa;Password=123456;Database=DogsDemo;Trusted_Connection=True;";
    opt.UseSqlServer(connStr);
});

使用DbContext:

var sp = services.BuildServiceProvider();
var ctx = sp.GetRequiredService<TestDbContext>();

约定映射规则

1:表名采用DbContext中的对应的DbSet的属性名。
2:数据表列的名字采用实体类属性的名字,列的数据类型采用和实体类属性类型最兼容的类型。
3:数据表列的可空性取决于对应实体类属性的可空性。
4:名字为Id的属性为主键,如果主键为short, int 或者 long类型,则默认采用自增字段,如果主键为Guid类型,则默认采用默认的Guid生成机制生成主键值。

两种配置方式

Data Annotation

把配置以特性(Annotation)的形式标注在实体类中。

[Table("T_Books")]
public class Book
{
}

优点:简单;缺点:耦合。

Fluent API

把配置写到单独的配置类中。

builder.ToTable("T_Books");

缺点:复杂;优点:解耦

大部分功能重叠。可以混用,但是不建议混用。

IQueryable

1、IQueryable只是代表一个“可以放到数据库服务器去执行的查询”,它没有立即执行,只是“可以被执行”而已。
2、对于IQueryable接口调用非终结方法的时候不会执行查询,而调用终结方法的时候则会立即执行查询。
3、终结方法:遍历、ToArray()、ToList()、Min()、Max()、Count()等;
4、非终结方法:GroupBy()、OrderBy()、Include()、Skip()、Take()等。
5、简单判断:一个方法的返回值类型如果是IQueryable类型,那么这个方法一般就是非终结方法,否则就是终结方法。

IEnumerable、IQueryable区别

普通集合的版本(IEnumerable)是在内存中过滤(客户端评估),而IQueryable版本则是把查询操作翻译成SQL语句(服务器端评估)

使用EF执行sql

尽管EF Core已经非常强大,但是仍然存在着无法被写成标准EF Core调用方法的SQL语句,少数情况下仍然需要写原生SQL。
三种情况:非查询语句、实体查询、任意SQL查询

非查询语句

使用dbCtx.Database. ExecuteSqlInterpolated () dbCtx.Database. ExecuteSqlInterpolatedAsync()方法来执行原生的非查询SQL语句。

ctx.Database.ExecuteSqlInterpolatedAsync(@$"insert into T_Books(Title,PubTime,Price,AuthorName)
select Title, PubTime, Price,{aName} from T_Books where Price > {price}");

除了ExecuteSqlInterpolated ()、ExecuteSqlInterpolatedAsync() ,还有ExecuteSqlRaw()、ExecuteSqlRawAsync() 也可以执行原生SQL语句,但需要开发人员自己处理查询参数等了,因此不推荐使用

实体相关sql

如果要执行的原生SQL是一个查询语句,并且查询的结果也能对应一个实体,就可以调用对应实体的DbSet的FromSqlInterpolated()方法来执行一个查询SQL语句,同样使用字符串内插来传递参数。

IQueryable<Book> books = ctx.Books.FromSqlInterpolated(@$"select * from T_Books
		where DatePart(year,PubTime)>{year}
		order by newid()");

FromSqlInterpolated()方法的返回值是IQueryable类型的,因此我们可以在实际执行IQueryable之前,对IQueryable进行进一步的处理。

IQueryable<Book> books = ctx.Books.FromSqlInterpolated(@$"select * from T_Books where DatePart(year,PubTime)>{year}");
foreach(Book b in books.Skip(3).Take(6))

把只能用原生SQL语句写的逻辑用FromSqlInterpolated()去执行,然后把分页、分组、二次过滤、排序、Include等其他逻辑尽可能仍然使用EF Core的标准操作去实现。

局限性:
SQL 查询必须返回实体类型对应数据库表的所有列;结果集中的列名必须与属性映射到的列名称匹配。
只能单表查询,不能使用Join语句进行关联查询。但是可以在查询后面使用Include()来进行关联数据的获取。

执行任意sql

FromSqlInterpolated()只能单表查询,但是在实现报表查询等的时候,SQL语句通常是非常复杂的,不仅要多表Join,而且返回的查询结果一般也都不会和一个实体类完整对应。因此需要一种执行任意SQL查询语句的机制。
dbCxt.Database.GetDbConnection()获得ADO.NET Core的数据库连接对象。这里不讲解ADO.NET基础知识。

DbConnection conn = ctx.Database.GetDbConnection();
if (conn.State != ConnectionState.Open)
{
	conn.Open();
}
using (var cmd = conn.CreateCommand())
{
	cmd.CommandText = @"xxx";
	var p1 = cmd.CreateParameter();
	p1.ParameterName = "@year";
	p1.Value = year;
	cmd.Parameters.Add(p1);
	using (var reader = cmd.ExecuteReader())
}

推荐用Dapper等框架执行原生复杂查询SQL。

将sql输出到控制台

第一种方式:

public class TestDbContext : DbContext
{
  public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });

  protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  {
      string connStr = "Data Source=.;Initial Catalog=EFCoreDB;User ID=sa;Password=123456";
      optionsBuilder.UseSqlServer(connStr);
      optionsBuilder.UseLoggerFactory(MyLoggerFactory);
  }
}

第二种方式:
输出的信息很多,如果只想看sql语句,需要自己过滤下

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            string connStr = "Data Source=.;Initial Catalog=EFCoreDB;User ID=sa;Password=123456";
            optionsBuilder.UseSqlServer(connStr);
            //optionsBuilder.LogTo(Console.WriteLine);//输出全部信息
            optionsBuilder.LogTo(msg => {
                if (!msg.Contains("CommandExecuting"))
                {
                    Console.WriteLine(msg);
                }
            });

        }

第三种方式:
如果想查看单个sql语句,可以使用ToQueryString()方法

            using (var ctx = new TestDbContext())
            {
                var books = ctx.Books.Where(b => b.Id == 1);
                string sql = books.ToQueryString();
                Console.WriteLine(sql);
                Console.ReadKey();
            }

EF系列:
https://blog.csdn.net/xingkongtianyuzhao/category_9704639.html

ef复杂查询:
https://www.cnblogs.com/wzk153/p/11850712.html

posted @ 2022-04-13 20:06  .Neterr  阅读(896)  评论(0编辑  收藏  举报