EF Core 学习笔记

9 篇文章 0 订阅
1 篇文章 0 订阅

EF Core 学习笔记

以下 Model First 学习笔记。
学习笔记来源于B站Up主软件工艺师分享的学习视频:https://www.bilibili.com/video/BV1xa4y1v7rR

准备工作

  1. 建好项目
    在这里插入图片描述

  2. 在项目中安装 Microsoft.EntityFrameworkCore.SqlServer 的 Nuget 包;

  3. 使用数据库

    public class EFCoreDemoContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseSqlServer("Data Source=(localdb)\\MSSQLLocalDB; Initial Catalog=Demo");  //使用 SQL SERVER 数据库
        }
    
        public DbSet<League> Leagues { get; set; }
        public DbSet<Club> Clubs { get; set; }
        public DbSet<Player> Players { get; set; }
        public DbSet<Resume> Resumes { get; set; }
        public DbSet<Game> Games { get; set; }
    }
    
    /// <summary>
    /// 输出数据库操作日志
    /// </summary>
    public static readonly ILoggerFactory ConsoleLoggerFactory = LoggerFactory.Create(builder =>
                                                                                      {
                                                                                          //对日志进行过滤
                                                                                          builder.AddFilter((category, level) => 
                                                                                                            category == DbLoggerCategory.Database.Command.Name  //只输出数据库操作命令即可
                                                                                                            && level == LogLevel.Information)  //日志级别
                                                                                              .AddConsole();  //需要安装 Microsoft.Extensions.Logging.Console
                                                                                      });
    

如何使用 EF Core 生成数据库

流程:

  1. 在项目中使用 C# 代码定义/修改 Model
  2. 创建 Migration 文件。Migration 文件对源代码版本控制非常友好。
  3. 应用 Migration 到数据库/生成脚本

使用 Migration 命令需要安装两个 NuGet 库:

  1. Microsoft.EntityFrameworkCore.Tools
  2. Microsoft.EntityFrameworkCore.Design

其中 Design 是 Tools 的依赖项。两个库需要安装在 DataContext 所在的项目中。

在对应项目的控制台中执行命令:

add-migration xxx,生成对应数据库映射。

执行完后会生成两个文件:

  1. 项目名称+Snapshot:相当于一个”快照“,用于追踪所有 Model 的状态,不可手动更改。
  2. 时间戳+xxx:数据库迁移文件。包含以下方法:
    • Up():执行数据库操作
    • Down():执行回滚操作

script-migration:生成数据库迁移脚本。通常在生产环境中使用。

update-database(-verbose):生成数据库迁移脚本。通常开发环境中使用。

设置字段属性

可以在 C#代码中给 Model 中的字段设置 Attribute 来关联设置数据库中对应字段的。使用前需要在项目中安装 NuGet 包:System.ComponentModel.Annotations。

代码如下:

public class League
{
    public int Id { get; set; }

    [Required]  //必填项,不可为 null
    [MaxLength(100)]  //最大长度为 100
    public string Name { get; set; }

    [Required, MaxLength(50)]
    public string Country { get; set; }
}

public class Club
{
    //......
    [Column(TypeName = "date")]  //设置数据库中的数据类型为 日期 类型
    public DateTime DateOfEstablishment { get; set; }
}

更改后执行 add-migration xxx 和 update-database(-verbose)两条命令,即可完成数据库结构的更改。

实体间的对应关系

1:n 关系

有两种方式,两种方式在数据库中,均会在 n 的表中生产对应 1 的外键。

  • 在 1 的类中引入 n 的对象的集合
  • 在 n 的类中引入 1 的单个对象

如下代码,1:n 关系有 Club:Players 和 League:Clubs。

public class Club
{
    public Club()
    {
        Players = new List<Player>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string City { get; set; }

    [Column(TypeName = "date")]  //设置数据库中的数据类型为 日期 类型
    public DateTime DateOfEstablishment { get; set; }
    public string History { get; set; }
    public League League { get; set; }
    public List<Player> Players { get; set; }
}

m:n 关系

需要建立中间表(类),中间表(类)中存储 n 和 m 类中的对象(外键),同时分别在 n 和 m 对应的类中,建立中间类的对象集合。如下代码,Game 和 Player 是 n:m 的关系,GamePlayer 是中间表。

public class GamePlayer
{
    public int PlayerId { get; set; }
    public int GameId { get; set; }

    public Game Game { get; set; }
    public Player Player { get; set; }
}
public class Game
{
    public Game()
    {
        GamePlayers = new List<GamePlayer>();
    }

    public int Id { get; set; }
    public string Round { get; set; }
    public DateTimeOffset? StartTime { get; set; }

    public List<GamePlayer> GamePlayers { get; set; }
}
public class Player
{
    public Player()
    {
        GamePlayers = new List<GamePlayer>();
    }
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }

    public List<GamePlayer> GamePlayers { get; set; }
}

GamePlayer 的主键是联合主键,需要在 DbContext 中重写 OnModelCreating 方法设置,如下:

public class EFCoreDemoContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        //设置联合主键
        modelBuilder.Entity<GamePlayer>().HasKey(x => new { x.PlayerId, x.GameId });
    }

    public DbSet<League> Leagues { get; set; }
    public DbSet<Club> Clubs { get; set; }
    public DbSet<Player> Players { get; set; }
    public DbSet<Resume> Resumes { get; set; }
    public DbSet<Game> Games { get; set; }

}

1:1 关系

需要在两个类中分别建立对方的对象实体(外键关系),然后在 DbContext 中重写 OnModelCreating 方法设置,如下:

public class Resume
{
    public int Id { get; set; }
    public string Description { get; set; }

    public int PlayerId { get; set; }
    public Player Player { get; set; }
}
public class Player
{
    public Player()
    {
        GamePlayers = new List<GamePlayer>();
    }
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }

    public List<GamePlayer> GamePlayers { get; set; }

    public int ResumeId { get; set; }
    public Resume Resume { get; set; }

}
public class EFCoreDemoContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        //设置联合主键
        modelBuilder.Entity<GamePlayer>().HasKey(x => new { x.PlayerId, x.GameId });
        //设置一对一关系
        modelBuilder.Entity<Resume>().HasOne(x => x.Player).WithOne(x => x.Resume).HasForeignKey<Resume>(x => x.PlayerId);
    }

    public DbSet<League> Leagues { get; set; }
    public DbSet<Club> Clubs { get; set; }
    public DbSet<Player> Players { get; set; }
    public DbSet<Resume> Resumes { get; set; }
    public DbSet<Game> Games { get; set; }

}

数据库操作

添加、查询

单条数据添加
using var context = new EFCoreDemoContext();  //此处 using 为 C# 8 的语法,当context使用完后,会直接释放。

var serieA = new League
{
    Country = "Italy",
    Name = "Serie A"
};
context.Leagues.Add(serieA);

var count = context.SaveChanges();

Console.WriteLine(count);

context.SaveChanges(); 会自动生成数据库语句,完成数据库操作

批量数据库操作
using var context = new EFCoreDemoContext();
var serieB = new League
{
    Country = "Italy",
    Name = "Serie B"
};
var serieC = new League
{
    Country = "Italy",
    Name = "Serie C"
};
//添加多条数据
context.Leagues.AddRange(serieB, serieC);
context.Leagues.AddRange(new List<League> { serieB, serieC });
var count = context.SaveChanges();
Console.WriteLine(count);
插入两个不同表的数据
using var context = new EFCoreDemoContext();
var serieA = context.Leagues.Single(x => x.Name == "Serie A");

var serieB = new League
{
    Country = "Italy",
    Name = "Serie B"
};
var serieC = new League
{
    Country = "Italy",
    Name = "Serie C"
};

var milan = new Club
{
    Name = "AC Lian",
    City = "Milan",
    DateOfEstablishment = new DateTime(1899, 2, 1),
    League = serieA
};

context.AddRange(serieB, serieC, milan);
查询

使用LINQ查询:此处,如果没有后面的 ToList() 方法,则只是构建一个查询语句,而没有完成查询操作。需要在查询语句后面加上形如 ToList() 的方法,才可以进行查询。

using var context = new EFCoreDemoContext();          
var leagues = context.Leagues
    .Where(x => x.Country == "Italy")
    .ToList();

var leagues2 = (from lg in context.Leagues
                where lg.Country == "Italy"
                select lg).ToList();

使用 foreach 循环时,会自动打开数据库连接,且连接会一直打开,直到循环结束。此时若循环体操作复杂,则会引起数据问题。所以尽量先使用 ToList() 等方法完成查询,然后对 ToList() 的结果进行循环操作。

foreach (var league in context.Leagues)
{
    Console.WriteLine(league.Name);
}
能完成查询的方法

查询语句后,添加如下形式的方法,才可完成查询:

ToList():返回集合;

First():返回符合条件的第一条数据,若无返回值则会报错;

FirstOrDefault():返回符合条件的第一条数据,可以没有返回结果

Single():符合查询条件的只能是一个数据

SingleOrDefault():符合查询条件的只能是一个或没有数据

Last():返回最后一条数据,若无返回值则会报错;

LastOrDefault():返回最后一条数据,可以没有返回结果;

统计方法:Count()、LongCount()、Max()、Min()、Sum()、Average()

Find()

同时还有如上方法的异步版本,如 ToListAsync()

var first = context.Leagues
    .FirstOrDefault(x =>
                    EF.Functions.Like(x.Country, "%e%"));

Console.WriteLine(first?.Name);
查询使用参数的区别

如下两条查询语句,当查询语句中使用的是写死的字符串 ”Italy“,则生成的 SQL 语句中也是写死的固定值;若使用变量 italy,则生成的 SQL 语句中使用参数传递的形式传入 italy 的值。

var italy = "Italy";
var leagues = context.Leagues
    .Where(x => x.Country == "Italy")
    .ToList();
leagues = context.Leagues
    .Where(x => x.Country == italy)
    .ToList();
模糊查询的两种方法
var leagues3 = context.Leagues
    .Where(x => x.Country.Contains("e"))
    .ToList(); 
var leagues4 = context.Leagues
    .Where(x => 
           EF.Functions.Like(x.Country, "%e%")
          )
    .ToList();
生成 SQL 语句的顺序

如下语句,查询结果一致, EF 中只会生成一次 SQL 语句。因为在第一次的时候,context 中已经将结果存储在内存中,后面的 Find 方法可以直接从内存中取得对应的数据。

var findfirst = context.Leagues
    .SingleOrDefault(x => x.Id == 2);
var findfirst2 = context.Leagues.Find(2);

当将二者换了顺序之后,则会生成两条 SQL 语句。因为 Find 方法之前没有完成查询,它想完成查询只可以通过生成 SQL 语句来查询。

var findfirst2 = context.Leagues.Find(2);
var findfirst = context.Leagues
    .SingleOrDefault(x => x.Id == 2);
var last = context.Leagues
    .LastOrDefault(x =>
                   EF.Functions.Like(x.Country, "%e%"));

Console.WriteLine(last?.Name);

以上代码会产生异常,因为要使用 LastOrDefault 方法,必须对查询结果进行排序。如下:

var last = context.Leagues
    .OrderBy(x => x.Id)
    //.OrderByDescending(x => x.Id)
    .LastOrDefault(x =>
                   EF.Functions.Like(x.Country, "%e%"));

Console.WriteLine(last?.Name);

修改、删除

删除数据

EF Core 只能删除被 context 追踪的数据。而只有被查询出来的数据才可以被追踪

//删除
var milian = context.Clubs.Single(x => x.Name == "AC Milian");//先查询处需要处理的数据
//调用删除方法:四种
context.Clubs.Remove(milian);
//context.Remove(milian);
//context.Clubs.RemoveRange(milian, milian);
//context.RemoveRange(milian, milian);

var countRemove = context.SaveChanges();
Console.WriteLine(countRemove);
修改数据

只能修改被 context 追踪的数据

  1. 修改单条数据

    var leagueUpdate = context.Leagues.First();
    leagueUpdate.Name += "~~";
    var countUpdate = context.SaveChanges();
    Console.WriteLine(countUpdate);
    
  2. 批量修改数据

    var leaguesUpdate = context.Leagues.Skip(1).Take(3).ToList();
    //skip 和 Take 结合使用通常是做翻页使用的,skip 表示跳过前面一条数据,take 表示取三条数据
    foreach (var league in leaguesUpdate)
    {
        league.Name += "~~";
    }
    var countUpdates = context.SaveChanges();
    Console.WriteLine(countUpdates);
    
更改未追踪数据

上述修改方式并不符合大部分使用场景。常用场景是前端返回 json 数据,然后反序列化形成 C# 类,这些数据就都不是使用 Context 查询出来的

var leagueNoTracking = context.Leagues.AsNoTracking().First();  //AsNoTracking()表示取消追踪,用于模拟前端传过来的json数据
//AsNoTracking 可以在这单独设置,也可以全局设置

leagueNoTracking.Name += "~~";
context.Leagues.Update(leagueNoTracking); //将 leagueNoTracking 添加到 Context的追踪范围内,然后把状态设置为修改状态,此时该数据除主键外,所有的属性都是修改状态,提交后都会修改
var countUpdateNoTracking = context.SaveChanges();
Console.WriteLine(countUpdateNoTracking);

全局设置 NoTracking

EFCoreDemoContext.cs

public EFCoreDemoContext()
{
    //全局设置数据不追踪
    ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}

添加关系型数据

  • 有以下两种形式

    1. 添加单个外键属性
    2. 添加集合属性
    //添加关系型数据
    var serieAA = context.Leagues.Single(x => x.Name == "Serie A");
    var juventus = new Club
    {
        League = serieAA,  //第一种形式,相当于添加外键
        Name = "Juventus",
        City = "Torino",
        DateOfEstablishment = new DateTime(1897, 11, 1),
        Players = new List<Player>  //第二种形式
        {
            new Player
            {
                Name = "C. Ronaldo",
                DateOfBirth = new DateTime(1985,2,5)
            }
        }
    };
    context.Clubs.Add(juventus);
    int countAdd = context.SaveChanges();
    Console.WriteLine(countAdd);
    
  • 如下代码先将 Club juventus 查询出来,context 对其进行了追踪,故可以直接在其中添加 Player,而不需要添加外键

    var juventusOne = context.Clubs.Single(x => x.Name == "Juventus");
    juventusOne.Players.Add(new Player
                            {
                                Name = "Gonzalo Higuan",
                                DateOfBirth = new DateTime(1987, 12, 10),
                            });
    
    int countjuventusOne = context.SaveChanges();
    Console.WriteLine(countjuventusOne);
    
  • 利用新的 context 进行更新数据。此时数据及其更新状态已经在 juventusOne 有记录,使用新的 Context 可以进行正常更新。但是,使用此 update 方法,除了会正常增加数据,同时也会对 Clubs 中的 Juventus 数据进行更新。

    var juventusOne = context.Clubs.Single(x => x.Name == "Juventus");
    juventusOne.Players.Add(new Player
                            {
                                Name = "Gonzalo Higuan",
                                DateOfBirth = new DateTime(1987, 12, 10),
                            });
    
    //利用新的 context 进行更新数据
    {
        using var newContext = new EFCoreDemoContext();
        newContext.Clubs.Update(juventusOne);
    
        int countjuventusOne = newContext.SaveChanges();
        Console.WriteLine(countjuventusOne);
    }
    
  • 变化追踪:为了避免 Clubs 中的 Juventus 数据被更改,可以使用 Attach 方法。Attach 只会对产生变化的数据进行更改。如下代码中,Attach 发现 juventusOne 对象数据有主键,且并没有更改,所以不会对其进行更新;但是发现 juventusOne.Players 有一个 Player 没有主键,故会对其完成新增操作。

    var juventusOne = context.Clubs.Single(x => x.Name == "Juventus");
    juventusOne.Players.Add(new Player
                            {
                                Name = "Gonzalo Higuan",
                                DateOfBirth = new DateTime(1987, 12, 10),
                            });
    
    //利用新的 context 进行更新数据
    {
        using var newContext = new EFCoreDemoContext();
        newContext.Clubs.Attach(juventusOne);
    
        int countjuventusOne = newContext.SaveChanges();
        Console.WriteLine(countjuventusOne);
    }
    
对比

在这里插入图片描述注:Add 方法有主键时会报错

加载关联数据

预加载,Eager Lading

显式加载,Explicit Loading

懒加载,Lazy Loading

预加载
//查询出 clubs 及其关联的 League
var clubs = context.Clubs.Include(x => x.League).ToList();

var clubs01 = context.Clubs
    .Where(x => x.Id > 0)
    .Include(x => x.League)
    .FirstOrDefault();
//如果FirstOrDefault()放在Include前则不支持
//var clubs01 = context.Clubs
//    .FirstOrDefault()
//    .Where(x => x.Id > 0)
//    .Include(x => x.League);

//Include 可以多次使用
var clubs02 = context.Clubs
    .Where(x => x.Id > 0)
    .Include(x => x.League)
    .Include(x => x.Players)
    .FirstOrDefault();

//级联添加关联数据。如下,添加Player的关联数据
var clubs02 = context.Clubs
    .Where(x => x.Id > 0)
    .Include(x => x.League)
    .Include(x => x.Players)
    	.ThenInclude(y => y.Resume)
    .FirstOrDefault();

//多个级联
var clubs02 = context.Clubs
    .Where(x => x.Id > 0)
    .Include(x => x.League)
    .Include(x => x.Players)
    	.ThenInclude(y => y.Resume)
    .Include(x => x.Players)
    	.ThenInclude(y => y.GamePlayers)
    .FirstOrDefault();
//以下是错误的,因为如下第二个ThenInclude关联的是Resume
var clubs02 = context.Clubs
    .Where(x => x.Id > 0)
    .Include(x => x.League)
    .Include(x => x.Players)
    	.ThenInclude(y => y.Resume)
    	.ThenInclude(y => y.GamePlayers)
    .FirstOrDefault();
显式加载

example one:

var clubs02 = context.Clubs
    .Where(x => x.Id > 0)
    .Select(x => new
            {
                x.Id,
                LeagueName = x.League.Name,
                x.Name,
                Players = x.Players
                    .Where(p => p.DateOfBirth > new DateTime(1990, 1, 1))
            }).ToList();
//Context 无法追踪匿名类,只追踪它识别的类。
//在上面代码中,Players 是可以识别出来的,故可以完成追踪。如下:
foreach (var data in clubs02)
{
    foreach (var player in data.Players)
    {
        player.Name += "~~";
    }
}
context.SaveChanges();

example two:

using var context01 = new EFCoreDemoContext();
var info = context01.Clubs.First();

context01.Entry(info)
    .Collection(x => x.Players)  //关联属性是集合,使用Collection方法
    .Query().Where(x => x.DateOfBirth > new DateTime(1990,1,1))  //针对关联集合添加筛选条件
    .Load();

context01.Entry(info)
    .Reference(x => x.League)  //关联属性单个,使用Reference方法
    .Load();

缺点:只能针对单个数据进行加载。如上只能针对 info 这一个 Club 进行加载,若针对 List<Club> 则无法加载。

懒加载

在 EF Core 中,这个特性默认是关闭的,可以手动开启。使用过程中存在问题较多,不建议使用。

多个多关联数据查询

Game 和 Player 是多对多关系,有个中间表 GamePlayer,但是在 EFCoreDemoContext 中的 DbSet 中没有体线,可以按以下方式查询

var gamePlayer = context.Set<GamePlayer>()
    .Where(x => x.Player.Id > 0).ToList(); 

修改关联数据

修改关联数据

context 会记录 club 中 League 的状态,所以可以关联更改

var club = context.Clubs.Include(x => x.League)
                .First();
club.League.Name += "@";
context.SaveChanges();
var game = context.Games
    .Include(x => x.GamePlayers)
    .ThenInclude(x => x.Player)
    .First();
var firstPlayer = game.GamePlayers[0].Player;
firstPlayer.Name += "$";
{
    using var newContext = new EFCoreDemoContext();
    //newContext.Players.Update(firstPlayer); //update方法会把所有关联的属性都更新一遍。这里会更新 firstPlayer 上除了主键之外所有的属性
    newContext.Entry(firstPlayer).State = EntityState.Modified;  //这样设置就会只更改这一个 firstPlayer 数据
    newContext.SaveChanges();
}
设置多对多关系

方法一:

var gamePlayer01 = new GamePlayer
{
    GameId = 1,
    PlayerId = 3
};
context.Add(gamePlayer01);
context.SaveChanges();

方法二

var game01 = context.Games.First();
game.GamePlayers.Add(new GamePlayer
                     {
                         PlayerId = 4
                     });
context.SaveChanges();
删除多对多关系
var gamePlayer02 = new GamePlayer
{
    GameId = 1,
    PlayerId = 3
};   //这里为了简便是直接 new 出来的 GamePlayer,更正确的方式是从数据库中查询出来这个关系
context.Remove(gamePlayer02);
context.SaveChanges();
修改多对多关系

删除原来的关系,再建立新的关系

设置一对一的关系

变化追踪状态下

var player = context.Players.First();
player.Resume = new Resume
{
    Description = "2eew"
};
context.SaveChanges();

离线状态下

var player01 = context.Players
    .AsNoTracking()
    .OrderBy(x => x.Id)
    .Last();
player01.Resume = new Resume
{
    Description = "2eew"
};
{
    using var newContext = new EFCoreDemoContext();
    newContext.Attach(player01);
    newContext.SaveChanges();
}

此时,如果直接使用上述代码,但是更改了Description,对 player01.Resume 进行设置,则会报错。因为再 Resume 中的 Player 的 Id 是唯一索引,不能重复。

但是,如果使用 Include 将 Resume 添加到内存中,则可以更改。更改过程中,会先删除之前的 Resume 和 Player 的关系,再添加新的关系。

var player02 = context.Players
    .Include(x => x.Resume)
    .OrderBy(x => x.Id)
    .Last();
player02.Resume = new Resume
{
    Description = "asdfds"
};
context.SaveChanges();

执行原生的 SQL

数据库操作(视图、存储过程)

在 EF Core 中不可以直接操作数据库(生成视图、创建存储过程等),需要将对数据库的操作脚本放到 Migration 中,然后由 Migration 来操作数据库。

具体操作步骤:

  1. 使用 add-migration xxx 先生成一个 Migration

  2. 在 Migration 中 的Up 和 Down 方法中编写需要的 SQL 语句
    在这里插入图片描述

  3. 使用 update-database(-verbose)更新数据库

无主键的 Entity

.net core 3.1 中允许无主键的 Entity,它们不会被追踪,映射到没有主键的 Table 或 View。

如下代码,PlayerClub 无主键,在 DbContext 中添加了 PlayerClub 的DbSet 之后,需要在 OnModelCreating 进行如下设置。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    //...........
    
    modelBuilder.Entity<PlayerClub>()
        .HasNoKey()  //设置 无主键
        .ToView("PlayerClubView");  //映射到视图
}

针对这种没有主键的 Entity,查询出来的结果都是无法追踪的。

原生 SQL 查询

有如下四个查询方法(DbSet 上的方法):

  • FromSQLRaw(“select * …”)

  • FromSQLRawAsync(“select * …”)

  • FromSQLInterpolated($“select * where x = {var}”)

  • FromSQLInterpolatedAsync(“select * where x = {var}”)

FromSQLInterpolated 和 FromSQLInterpolatedAsync 为字符串插值形式,可以直接将对应参数值直接放到花括号中。

示例如下:

var clubsSQL = context.Clubs
    .FromSqlRaw("select * from dbo.Clubs")
    .Include(x => x.League)
    .ToList();

var clubsSQL = context.Clubs
    .FromSQLInterpolated($"select * from dbo.Clubs where Id > {0}")
    .FromSQLInterpolated($"EXEC dbo.xxx where Id > {0}")
    .ToList();
原生 SQL 查询的要求
  • 必须返回 Entity 类型的所有(标量)属性
  • 字段名和 Entity 的属性名匹配
  • 无法包含关联的数据
  • 只能查询已知的 Entity
执行非查询的 SQL 语句

有如下方法:

  • Context.Database.ExecuteSQLRaw()
  • Context.Database.ExecuteSQLRawAsync()
  • Context.Database.ExecuteSQLInterpolated()
  • Context.Database.ExecuteSQLInterpolatedAsync()

ExecuteSQLInterpolated 和 ExecuteSQLInterpolatedAsync 为字符串插值形式,可以直接将对应参数值直接放到花括号中。

以上方法:

  • 无法用与查询
  • 只能返回影响的行数
var count = context.Database
    .ExecuteSqlRaw("EXEC dbo.RemoveGamePlayersProcedurce {0}", 2);

count = context.Database
    .ExecuteSQLInterpolated($"EXEC dbo.RemoveGamePlayersProcedurce {2}");

如何在 ASP.NET Core 项目中配置 EF Core

在 DbContext 类(EFCoreDemoContext)中添加构造函数

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

在appsettings.json 设置连接字符串

"ConnectionStrings": {
    "LoaclDB": "Server=(localdb)\\ProjectsV84;Database=TestDB1;Trusted_Connection=True;MultipleActiveResultSets=true"
}

在 Startup.cs 中注册 EFCoreDemoContext:在 ConfigureServices 方法中添加如下代码

services.AddDbContext<EFCoreDemoContext>(options =>                          options.UseSqlServer(Configuration.GetConnectionString("LoaclDB")));

这样配置后,EFCoreDemoContext 就可以在项目中依赖注入地使用了。

### 回答1: 电脑桌面显示配置是指对电脑桌面上的图标、背景、分辨率等进行调整和设置。在Windows操作系统中,可以通过右键点击桌面空白处选择“个性化”来打开桌面显示配置界面。在这里,我们可以更改桌面背景图片、设置图标大小和排列方式,还可以调整电脑屏幕的分辨率以适应不同大小的显示器。 另外,如果想要让电脑桌面更加个性化,我们还可以下载和安装各种小部件和皮肤软件,通过它们来自定义桌面图标和壁纸,实现更多的视觉效果和功能。 而IP地址是指互联网协议地址,是一种用于在网络上唯一识别一个设备的地址。在Windows操作系统中,可以通过命令行工具或者控制面板来查看电脑IP地址。比如,我们可以按下Win键 + R键打开运行窗口,然后输入“cmd”(不带引号)并按下回车键,就会打开命令行窗口。在命令行中,可以输入“ipconfig”命令来查看电脑IP地址、子网掩码、网关等网络配置信息。 总之,电脑桌面显示配置IP地址都是在日常使用电脑时会涉及到的一些设置和信息。这些设置可以让我们更好地个性化电脑桌面,并且了解电脑所在网络的一些基本信息。 ### 回答2: 电脑桌面显示配置是指对电脑的显示器进行设置,包括屏幕分辨率、壁纸、桌面图标、任务栏等。我们可以在“控制面板”中找到“显示”选项,然后对显示器进行个性化的设置。 首先,屏幕分辨率是指屏幕上横向和纵向的像素数量,决定了屏幕上能显示多少信息。一般而言,我们可以根据自己的需要来选择适合自己的分辨率。较高的分辨率可以提供更清晰的图像,但可能使文本和图标变得更小,较低的分辨率则相反。 其次,壁纸是指桌面背景的图像或颜色。我们可以从系统提供的壁纸中选择,也可以使用自己的图片作为壁纸。通过壁纸,我们可以个性化自己的电脑桌面,体现自己的喜好和风格。 还有,桌面图标是指显示在桌面上的各种快捷方式和文件夹图标。我们可以通过拖拽或右键菜单进行图标的增加、删除和排序,以方便我们快速访问所需的程序和文件。 最后,任务栏是显示在屏幕底部的水平栏,用于快速启动程序和切换窗口。我们可以根据自己的使用习惯自定义任务栏,比如将常用的程序固定到任务栏上,调整任务栏的位置和尺寸等。 至于IP地址,它是计算机在网络中的唯一标识。我们可以通过在命令提示符或网络设置中输入“ipconfig”命令来获取自己电脑IP地址IP地址分为IPv4和IPv6两种,一般IPv4是由四个0到255之间的数字组成,用点来分隔,例如192.168.0.1。而IPv6则更为复杂,包含八组四位十六进制数字,用冒号分隔。 总之,电脑桌面显示配置IP地址设置是为了提高用户使用电脑的便利性和个性化需求,根据自己的喜好和实际需求进行设置,以提升工作和娱乐效率。 ### 回答3: 电脑桌面显示配置指的是电脑显示器的设置和布局。我们可以通过右键点击桌面的空白位置,选择“显示设置”来进行配置。在显示设置中,可以调整分辨率、缩放比例、屏幕旋转、多个显示器的顺序和位置等。 首先,我们可以通过显示设置调整分辨率。分辨率决定了显示器上像素的数量,较高的分辨率意味着更清晰的图像。调整分辨率可以根据个人喜好或应用程序需求。 其次,我们可以调整缩放比例。缩放比例决定了在显示器上的元素的大小。较大的缩放比例使得文字和图标更大,方便阅读和操作。 另外,显示设置还可以实现屏幕的旋转。通过选择适应自己的工作或娱乐需求,可以将屏幕旋转为横向或纵向模式。 对于使用多个显示器的用户,还可以调整显示器的顺序和位置。这样可以模拟多屏幕工作环境,提高工作效率。 IP(Internet Protocol)是指互联网协议,用于在网络中标识和定位计算机和其他设备。每个连接到互联网上的设备都会被分配一个IP地址。 我们可以通过以下方法查看电脑IP地址。首先,打开命令提示符(按下Win + R,然后输入“cmd”并按Enter键),然后输入“ipconfig”命令并按Enter键。在命令的输出中,可以找到标有“IPv4地址”或“IP地址”的一行,后面的数字就是电脑IP地址。 另外,也可以在控制面板中找到网络和互联网设置,然后点击网络连接,查看本地连接的详细信息,其中包括IP地址。 通过以上方法,我们可以轻松查看电脑桌面的显示配置IP地址。这些信息对于个性化设置和网络连接都非常重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值