前言
學(xué)習參考:
1. .NET 6教程,.Net Core 2022視頻教程,楊中科主講— 以及資料參考
2. 官方文檔EF core
常用的語句
3..netmvc
https://www.cnblogs.com/1016391912pm/p/12024671.html
https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Querying/Overview
第一個EFCore應(yīng)用
https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-types?tabs=fluent-api
//添加包
Microsoft.EntityFrameworkCore.Sqlite
Pomelo.EntityFrameworkCore.MySql
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Tools
Add-Migration InitialCreate//實現(xiàn)遷移
Update-database//生成數(shù)據(jù)庫
Update-Database InitialCreate
Remove-migration//刪除最后一次的遷移腳本
Script-Migration//生成遷移SQL代碼
Script-Migration D F//生成版本D到版本F的SQL腳本
Script-Migration D//生成版本D到最新版本的SQL腳本:
dotnet add package Microsoft.EntityFrameworkCore.Design//添加 EF Core 工具所需的包。
dotnet tool install --global dotnet-ef//將安裝 dotnet ef,用于創(chuàng)建遷移和基架的工具。
dotnet ef migrations add InitialCreate --context PizzaContext//創(chuàng)建數(shù)據(jù)庫的遷移
dotnet ef database update --context PizzaContext//將應(yīng)用遷移生成數(shù)據(jù)庫。
builder.Services.AddSqlite<PizzaContext>("Data Source=ContosoPizza.db");
什么是ORM
(Object Relational Mapping關(guān)系對象映射)
讓開發(fā)者用對象操作的形式操作關(guān)系數(shù)據(jù)庫。
.net 常用的orm:EF core、Dapper、SqlSugar、FreeSql
EF Core學(xué)習與深入
一、了解 EF Core
1、Entity Framework Core(EF Core)是微軟官方的ORM框架。優(yōu)點:功能強大、官方支持、生產(chǎn)效率高、力求屏蔽底層數(shù)據(jù)庫差異;缺點:復(fù)雜、上手門檻高、不熟悉EFCore的話可能會進坑。
2、Dapper。優(yōu)點:簡單,N分鐘即可上手,行為可預(yù)期性強;缺點:生產(chǎn)效率低,需要處理底層數(shù)據(jù)庫差異。
3、EF Core是模型驅(qū)動(Model-Driven)的開發(fā)思想,Dapper是數(shù)據(jù)庫驅(qū)動(DataBase-Driven)的開發(fā)思想的。沒有優(yōu)劣,只有比較。
4、性能: Dapper等≠性能高;EF Core≠性能差。
5、EF Core是官方推薦、推進的框架,盡量屏蔽底層數(shù)據(jù)庫差異,.NET開發(fā)者必須熟悉,根據(jù)的項目情況再決定用哪個。
對于后臺系統(tǒng)、信息系統(tǒng)等和數(shù)據(jù)庫相關(guān)開發(fā)工作量大的系統(tǒng),且團隊比較穩(wěn)定,用EF Core;對于互聯(lián)網(wǎng)系統(tǒng)等數(shù)據(jù)庫相關(guān)工作量不大的系統(tǒng),或者團隊不穩(wěn)定,用Dapper。
1.DbContext
EF中的上下文(DbContext)簡介
DbContext是實體類和數(shù)據(jù)庫之間的橋梁,DbContext主要負責與數(shù)據(jù)交互,主要作用:
1、DbContext包含所有的實體映射到數(shù)據(jù)庫表的實體集(DbSet < TEntity >)。
2、DbContext 將LINQ-to-Entities查詢轉(zhuǎn)換為SQL查詢并將其發(fā)到數(shù)據(jù)庫。
3、更改跟蹤: 它跟蹤每個實體從數(shù)據(jù)庫中查詢出來后發(fā)生的修改變化。
4、持久化數(shù)據(jù): 它也基于實體狀態(tài)執(zhí)行插入、更新和刪除操作到數(shù)據(jù)庫中
DbContext類是EntityFramework (簡稱 EF)中的一個類,可以理解為一個數(shù)據(jù)庫對象的實例。在 EF中,無需手動的拼接 SQL 語句對數(shù)據(jù)庫進行增刪改查,而是通過 DbContext 來進行相應(yīng)操作。
2.數(shù)據(jù)注釋、Fluent API學(xué)習
https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-types?tabs=fluent-api
1、視圖與實體類映射:modelBuilder.Entity<Blog>().ToView("blogsView");
2、排除屬性映射:
modelBuilder.Entity<Blog>().Ignore(b => b. Name2);
3、配置列名:
modelBuilder.Entity<Blog>().Property(b =>b.BlogId).HasColumnName("blog_id");
4、配置列數(shù)據(jù)類型:
builder.Property(e => e.Title) .HasColumnType("varchar(200)")
5、配置主鍵
默認把名字為Id或者“實體類型+Id“的屬性作為主鍵,可以用HasKey()來配置其他屬性作為主鍵。modelBuilder.Entity<Student>().HasKey(c => c.Number);
支持復(fù)合主鍵,但是不建議使用。
6、生成列的值
modelBuilder.Entity<Student>().Property(b => b.Number).ValueGeneratedOnAdd();
7、可以用HasDefaultValue()為屬性設(shè)定默認值
modelBuilder.Entity<Student>().Property(b => b.Age).HasDefaultValue(6);
8、索引
modelBuilder.Entity<Blog>().HasIndex(b => b.Url);
復(fù)合索引
modelBuilder.Entity<Person>().HasIndex(p => new { p.FirstName, p.LastName });
唯一索引:IsUnique();聚集索引:IsClustered()
9... 用EF Core太多高級特性的時候謹慎,盡量不要和業(yè)務(wù)邏輯混合在一起,以免“不能自拔”。比如Ignore、Shadow、Table Splitting等……
## ef core
//模型聲明Blogs、AuditEntry、Post
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AuditEntry>();
}
}
# 從模型中排除類型
[NotMapped]
public class BlogMetadata
{
public int BlogId { get; set; }
public string Url { get; set; }
[NotMapped]//排除屬性
public DateTime LoadedFromDatabase { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<BlogMetadata>();
// modelBuilder.Entity<BlogMetadata>()
// .Ignore(b => b.LoadedFromDatabase);
}
# 表名稱、表架構(gòu)、表注釋
按照約定,每個實體類型都將設(shè)置為映射到與公開實體的 DbSet 屬性名稱相同的數(shù)據(jù)庫表。 如果給定實體不存在 DbSet,則使用類名稱。
[Table("blogs", Schema = "blogging")]
[Comment("Blogs managed on the website")]//表注釋
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable("blogs");
// modelBuilder.Entity<Blog>()
// .ToTable("blogs", schema: "blogging");
modelBuilder.Entity<Blog>()
.HasComment("Blogs managed on the website");
}
# 模型的數(shù)據(jù)注釋和FluentAPI的屬性定義
.HasKey(c => c.LicensePlate);[Key]//主鍵
.HasName("PrimaryKey_BlogId");//主鍵名
.Ignore(b => b.LoadedFromDatabase);[NotMapped]//排除類型
HasColumnName("blog_id");[Column("blog_id")]//列名
HasColumnType("varchar(200)");[Column(TypeName = "varchar(200)")]//列數(shù)據(jù)類型
HasMaxLength(500);[MaxLength(500)]//最大長度
HasPrecision(14, 2);[Precision(14, 2)]//屬性配置為精度為 14 和小數(shù)位數(shù)為 2
IsUnicode(false);[Unicode(false)]//Unicode
IsRequired();[Required]//必須屬性
.HasComment();[Comment("Blogs managed on the website")]//列注釋
.HasColumnOrder(0);[Column(Order = 0)]//列順序
.HasDefaultValue(3);//默認值
.HasDefaultValueSql("getdate()");//默認值
public class Blog
{
[Column("blog_id")]
[MaxLength(500)]
public int BlogId { get; set; }
[Column(TypeName = "varchar(200)")]
public string Url { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.BlogId)
.HasColumnName("blog_id");
modelBuilder.Entity<Blog>(
eb =>
{
eb.Property(b => b.Url).HasColumnType("varchar(200)");
eb.Property(b => b.Rating).HasColumnType("decimal(5, 2)");
});
}
# 分組配置
public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder
.Property(b => b.Url)
.IsRequired();
}
}
new BlogEntityTypeConfiguration().Configure(modelBuilder.Entity<Blog>());
# 查詢數(shù)據(jù) 關(guān)聯(lián)查詢Include
支持的操作包括:Where、OrderBy、OrderByDescending、ThenBy、ThenByDescending、Skip 和 Take。
var blogs = context.Blogs.ToList();//加載所有數(shù)據(jù)
.Include(blog => blog.Posts).ToList();//Posts所有數(shù)據(jù)
.Single(b => b.BlogId == 1);//加載單個實體
.SingleOrDefault(b => b.BlogId == 1);//
.Where(b => b.Url.Contains("dotnet")).ToList();//篩選
.OrderByDescending(blog => blog.Rating).Select(
blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog.Url) })
.ToList();排序
.Include(
blog => blog.Posts
.Where(post => post.BlogId == 1)
.OrderByDescending(post => post.Title)
.Take(5))
.ToList();
context.Blogs.Add(blog);//添加數(shù)據(jù)
context.Blogs.Remove(blog);//刪除數(shù)據(jù)
context.SaveChanges();//保存
二、簡單的案例使用1(推薦參考最佳)
第一個EFCore應(yīng)用
連接mysql(Pomelo.EntityFrameworkCore.MySql)
string connection = “server=127.0.0.1;userid=root;pwd=123456;port=3306;database=blogging;sslmode=none;Convert Zero Datetime=True”;//
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseMySql(connection, new MySqlServerVersion(new System.Version(5, 7, 37)));
1.創(chuàng)建一個控制臺程序
2.添加包
Microsoft.EntityFrameworkCore.Sqlite
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Tools
3.增加model類Model.cs //是一對多的
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public string DbPath { get; }
public BloggingContext()
{
var folder = Environment.SpecialFolder.LocalApplicationData;
var path = Environment.GetFolderPath(folder);//
DbPath = System.IO.Path.Join(path, "blogging.db");
}
// The following configures EF to create a Sqlite database file in the
// special "local" folder for your platform.
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={DbPath}");
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; } = new();
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
4.在程序包管理器控制臺
Add-Migration FirstMigration
Update-Database
5.Program.cs(進行CRUD)
using (var db = new BloggingContext())
{
// Note: dababase path
Console.WriteLine($"Database path: {db.DbPath}.");
//Create
//Console.WriteLine("Inserting a new blog");
//db.Add(new Blog { Url = "http://baidu.com" });//http://blogs.msdn.com/adonet;
//db.SaveChanges();
// Read
Console.WriteLine("Querying for a blog");
var blog = db.Blogs
.OrderBy(b => b.BlogId)
.FirstOrDefault();
//var blogs = db.Blogs.Where(b => b.BlogId > 1);
//foreach (var item in blogs)
//{
// Console.WriteLine($"{item.Url}");
//}
// Update
Console.WriteLine("Updating the blog and adding a post");
blog!.Url = "https://devblogs.microsoft.com/dotnet";
blog.Posts.Add(
new Post { Title = "Hello World", Content = "I wrote an app using EF Core!" });
db.SaveChanges();
Delete
Console.WriteLine("Delete the blog");
db.Remove(blog);
db.SaveChanges();
}
三、簡單的案例使用2
命令行
//需要添加所需的包
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design//添加 EF Core 工具所需的包。
dotnet tool install --global dotnet-ef//將安裝 dotnet ef,用于創(chuàng)建遷移和基架的工具。
dotnet ef migrations add InitialCreate --context PizzaContext//創(chuàng)建數(shù)據(jù)庫的遷移
dotnet ef database update --context PizzaContext//將應(yīng)用遷移生成數(shù)據(jù)庫。
- 在項目根目錄中,添加一個名為 Data 的新文件夾。
- 在 Data 目錄中,創(chuàng)建一個名為 PizzaContext.cs 的新文件。 將以下代碼添加到空文件中:
- 在 Program.cs 中,將 // Add the PizzaContext
using Microsoft.EntityFrameworkCore;
using ContosoPizza.Models;
namespace ContosoPizza.Data;
public class PizzaContext : DbContext
{
public PizzaContext (DbContextOptions<PizzaContext> options)
: base(options)
{
}
public DbSet<Pizza> Pizzas => Set<Pizza>();//表名
public DbSet<Topping> Toppings => Set<Topping>();
public DbSet<Sauce> Sauces => Set<Sauce>();
}
//-------------------
builder.Services.AddSqlite<PizzaContext>("Data Source=ContosoPizza.db");
四、簡單的案例使用3
兩種配置方式
1、Data Annotation
把配置以特性(Annotation)的形式標注在實體類中。
[Table(“T_Books”)]
public class Book
{
}
優(yōu)點:簡單;缺點:耦合。
2、Fluent API
builder.ToTable(“T_Books”);
把配置寫到單獨的配置類中。
缺點:復(fù)雜;優(yōu)點:解耦
Guid主鍵
1、 Guid算法(或UUID算法)生成一個全局唯一的Id。適合于分布式系統(tǒng),在進行多數(shù)據(jù)庫數(shù)據(jù)合并的時候很簡單。優(yōu)點:簡單,高并發(fā),全局唯一;缺點:磁盤空間占用大。
yzk開源的高效批量修改、刪除的開源項目Zack.EFCore.Batch
1、經(jīng)典步驟:建實體類;建DbContext;生成數(shù)據(jù)庫;編寫調(diào)用EF Core的業(yè)務(wù)代碼。
程序包管理器控制臺
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Pomelo.EntityFrameworkCore.MySql
optionsBuilder.UseMySql("server=localhost;user=root;password=root;database=ef",
new MySqlServerVersion(new Version(5, 6, 0)));
Install-Package Npgsql.EntityFrameworkCore.PostgreSQL
optionsBuilder.UseNpgsql("Host=127.0.0.1;Database=ef;Username=postgres;Password=123456");
//Book.cs
public class Book
{
public long Id { get; set; }//主鍵
[Required]
[MaxLength(100)]
public string Title { get; set; }//標題
public DateTime PubTime { get; set; }//發(fā)布日期
public double Price { get; set; }//單價
}
//創(chuàng)建實現(xiàn)了IEntityTypeConfiguration接口的實體配置類,配置實體類和數(shù)據(jù)庫表的對應(yīng)關(guān)系
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).HasMaxLength(20).IsRequired();
}
}
//創(chuàng)建繼承自DbContext的類
class TestDbContext:DbContext
{
public DbSet<Book> Books { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
string connStr = "Server=.;Database=demo1;Trusted_Connection=True;MultipleActiveResultSets=true";
optionsBuilder.UseSqlServer(connStr);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
}
}
//插入數(shù)據(jù)
//只要操作Books屬性,就可以向數(shù)據(jù)庫中增加數(shù)據(jù),但是通過C#代碼修改Books中的數(shù)據(jù)只是修改了內(nèi)存中的數(shù)據(jù)。對Books做修改后,需要調(diào)用DbContext的異步方法SaveChangesAsync()把修改保存到數(shù)據(jù)庫。也有同步的保存方法SaveChanges(),但是用EF Core都推薦用異步方法。
//查詢數(shù)據(jù)
1、DbSet實現(xiàn)了IEnumerable<T>接口,因此可以對DbSet實施Linq操作來進行數(shù)據(jù)查詢。EF Core會把Linq操作轉(zhuǎn)換為SQL語句。面向?qū)ο?,而不是面向?shù)據(jù)庫(SQL)。
2、ctx.Books.Where(b => b.Price > 80)
Book b1 = ctx.Books.Single(b => b.Title== "零基礎(chǔ)趣學(xué)C語言");
Book b2 = ctx.Books.FirstOrDefault(b=>b.Id==9);
3、可以使用OrderBy操作進行數(shù)據(jù)的排序
IEnumerable<Book> books = ctx.Books.OrderByDescending(b => b.Price);
//查詢數(shù)據(jù)2
1、GroupBy也可以
var groups = ctx.Books.GroupBy(b => b.AuthorName)
.Select(g => new { AuthorName = g.Key, BooksCount = g.Count(), MaxPrice = g.Max(b => b.Price) });
foreach(var g in groups)
{
Console.WriteLine($"作者名:{g.AuthorName},著作數(shù)量:{g.BooksCount},最貴的價格:{g.MaxPrice}");
}
2、大部分Linq操作都能作用于EF Core。
//修改、刪除
1、要對數(shù)據(jù)進行修改,首先需要把要修改的數(shù)據(jù)查詢出來,然后再對查詢出來的對象進行修改,然后再執(zhí)行SaveChangesAsync()保存修改。
var b = ctx.Books.Single(b=>b.Title== "數(shù)學(xué)之美");
b.AuthorName = "Jun Wu";
await ctx.SaveChangesAsync();
2、刪除也是先把要修改的數(shù)據(jù)查詢出來,然后再調(diào)用DbSet或者DbContext的Remove方法把對象刪除,然后再執(zhí)行SaveChangesAsync()保存修改。
var b = ctx.Books.Single(b => b.Title == "數(shù)學(xué)之美");
ctx.Remove(b);//也可以寫成ctx.Books.Remove(b);
await ctx.SaveChangesAsync();
//批量修改、刪除 性能低:查出來,再一條條Update、Delete,
五、身份驗證(mvc,ef)
ASP.NET Core MVC 登錄驗證
1,創(chuàng)建一個mvc的項目
2,添加包,創(chuàng)建一個實體類,相應(yīng)的上下文類。
3,數(shù)據(jù)庫的遷移,實現(xiàn),program.cs的配置。
4,在HomeController添加[Authorize],
5, 添加一個User控制器,以及視圖Login,添加內(nèi)容_Layout.cshtml
Microsoft.EntityFrameworkCore.Sqlite
Pomelo.EntityFrameworkCore.MySql
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Tools
Add-Migration InitialCreate//實現(xiàn)遷移
Update-database//生成數(shù)據(jù)庫
//User.cs
public class User
{
public int Id { get; set; }
public string Account { get; set; }
public string Password { get; set; }
}
//MyDbContext.cs
using Microsoft.EntityFrameworkCore;
public class MyDbContext:DbContext
{
public DbSet<User> Users { get; set; }
public MyDbContext(DbContextOptions options) : base(options)
{
}
}
//program.cs
//builder.Services.AddSqlite<MyDbContext>("Data Source=ContosoPizza.db");//連接Sqlite
builder.Services.AddDbContext<MyDbContext>(options=>options.UseMySql("server=localhost;user=root;password=123456;database=Myhaha",
new MySqlServerVersion(new Version(5, 6, 0))));
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)//身份驗證
.AddCookie(options =>
{
options.LoginPath = "/User/Login";//初識路徑
});
app.UseAuthentication();
//UserController.cs
public class UserController : Controller
{
private readonly MyDbContext _context;
public UserController(MyDbContext myDbContext)
{
_context = myDbContext;
}
public IActionResult Index()
{
return View();
}
public IActionResult Login(string returnUrl)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
[HttpPost]
public async Task< IActionResult> Login(UserLoginRequest request)
{
if(await _context.Users.AnyAsync(a => a.Account == request.Account && a.Password == request.Password))
//if (request.Account=="admin"&&request.Password=="admin")
{
var claim = new List<Claim>()
{
new Claim(ClaimTypes.Name,request.Account),//保存了數(shù)據(jù)。
};
var clainmnsIdentity=new ClaimsIdentity(claim, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(clainmnsIdentity),
new AuthenticationProperties
{
IsPersistent = true,//持久cookie
});
}
else{ return RedirectToAction(nameof(Login));}
return Redirect(request.ReturnUrl ?? "/");
//return RedirectToAction(nameof(Index));
}
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync();
return RedirectToAction(nameof(Login));
}
}
public class UserLoginRequest : User
{
public string ReturnUrl { get; set; }
}
//Login.cshtml
<form asp-action="Login" method="post">
<input type="hidden" name="returnUrl" value="@ViewBag.ReturnUrl" />
<input type="text" name="account" value="" />
<input type="password" name="password" value="" />
<input type="submit" value="登錄" />
</form>
//_Layout.cshtml
添加內(nèi)容
<li class="nav-item">
@User.Identity.Name
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="User" asp-action="Logout">退出登錄</a>
</li>
其他
1.數(shù)據(jù)庫表之間的關(guān)系
數(shù)據(jù)庫表之間的關(guān)系:一對一、一對多、多對多
一對多:HasOne(…).WithMany(…);
一對一:HasOne(…).WithOne (…);
多對多:HasMany (…).WithMany(…);
1、IQueryable books = ctx.Books;
books.Where(b => b.Price > 1.1)
2、IEnumerable books = ctx.Books;
books.Where(b => b.Price > 1.1)
- 一對多:實體類,一對多:關(guān)系配置
//1、文章實體類Article、評論實體類Comment。一篇文章對應(yīng)多條評論。
public class Article
{
public long Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public List<Comment> Comments { get; set; }
= new List<Comment>();
}
public class Comment
{
public long Id { get; set; }
public Article Article { get; set; }
public string Message { get; set; }
}
//一對多:關(guān)系配置
class ArticleConfig : IEntityTypeConfiguration<Article>
{
public void Configure(EntityTypeBuilder<Article> builder)
{
builder.ToTable("T_Articles");
builder.Property(a => a.Content).IsRequired().IsUnicode();
builder.Property(a => a.Title).IsRequired().IsUnicode().HasMaxLength(255);
}
}
class CommentConfig : IEntityTypeConfiguration<Comment>
{
public void Configure(EntityTypeBuilder<Comment> builder)
{
builder.ToTable("T_Comments");
builder.HasOne<Article>(c=>c.Article).WithMany(a => a.Comments).IsRequired();//HasForeignKey(c=>c.ArticleId)
builder.Property(c=>c.Message).IsRequired().IsUnicode();
}
}
//獲取關(guān)系數(shù)據(jù)
Article a = ctx.Articles.Include(a=>a.Comments).Single(a=>a.Id==1);
Console.WriteLine(a.Title);
ctx.Articles.Where(a=>a.Comments.Any(c=>c.Message.Contains("微軟")));
ctx.Comments.Where(c => c.Message.Contains("微軟"))
.Select(c => c.Article).Distinct();
foreach(Comment c in a.Comments)
{
Console.WriteLine(c.Id+":"+c.Message);
}
//Include定義在Microsoft.EntityFrameworkCore命名空間中
//關(guān)聯(lián)查詢
//CommentConfig:
builder.HasOne<Article>(c=>c.Article).WithMany(a => a.Comments).IsRequired();
//ArticleConfig:
builder.HasMany<Comment>(a => a.Comments).WithOne(c => c.Article).IsRequired();
- 一對一關(guān)系
class Order
{
public long Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public Delivery Delivery { get; set;}
}
class Delivery
{
public long Id { get; set; }
public string CompanyName { get; set; }
public String Number { get; set; }
public Order Order { get; set; }
public long OrderId { get; set; }
}
1、builder.HasOne<Delivery>(o => o.Delivery).WithOne(d => d.Order).HasForeignKey<Delivery>(d=>d.OrderId);
2、測試插入和獲取數(shù)據(jù)。
- 多對多(老師—學(xué)生。)
class Student
{
public long Id { get; set; }
public string Name { get; set; }
public List<Teacher> Teachers { get; set; } = new List<Teacher>();
}
class Teacher
{
public long Id { get; set; }
public string Name { get; set; }
public List<Student> Students { get; set; } = new List<Student>();
}
builder.HasMany<Teacher>(s => s.Teachers).WithMany(t=>t.Students).UsingEntity(j=>j.ToTable("T_Students_Teachers"));
2.所謂IQueryable
1、IQueryable只是代表一個“可以放到數(shù)據(jù)庫服務(wù)器去執(zhí)行的查詢”,它沒有立即執(zhí)行,只是“可以被執(zhí)行”而已。
2、對于IQueryable接口調(diào)用非終結(jié)方法的時候不會執(zhí)行查詢,而調(diào)用終結(jié)方法的時候則會立即執(zhí)行查詢。
3、終結(jié)方法:遍歷、ToArray()、ToList()、Min()、Max()、Count()等;
4、非終結(jié)方法:GroupBy()、OrderBy()、Include()、Skip()、Take()等。
5、簡單判斷:一個方法的返回值類型如果是IQueryable類型,那么這個方法一般就是非終結(jié)方法,否則就是終結(jié)方法。
1、IQueryable是一個待查詢的邏輯,因此它是可以被重復(fù)使用的。
2、IQueryable books = ctx.Books.Where(b => b.Price <= 8);
Console.WriteLine(books.Count());
Console.WriteLine(books.Max(b=>b.Price));
var books2 = books.Where(b=>b.PubTime.Year>2000);
1、DataReader:分批從數(shù)據(jù)庫服務(wù)器讀取數(shù)據(jù)。內(nèi)存占用小、 DB連接占用時間長;
2、DataTable:把所有數(shù)據(jù)都一次性從數(shù)據(jù)庫服務(wù)器都加載到客戶端內(nèi)存中。內(nèi)存占用大,節(jié)省DB連接。
驗證IQueryable用什么方式
1、用insert into select多插入一些數(shù)據(jù),然后加上Delay/Sleep的遍歷IQueryable。在遍歷執(zhí)行的過程中,停止SQLServer服務(wù)器。
IQueryable內(nèi)部就是在調(diào)用DataReader。
2、優(yōu)點:節(jié)省客戶端內(nèi)存。
缺點:如果處理的慢,會長時間占用連接。
//如何一次性加載數(shù)據(jù)到內(nèi)存
1、一次性加載數(shù)據(jù)到內(nèi)存:用IQueryable的ToArray()、ToArrayAsync()、ToList()、ToListAsync()等方法。
2、等ToArray()執(zhí)行完畢,再斷服務(wù)器試一下。
//何時需要一次性加載
1、場景1:遍歷IQueryable并且進行數(shù)據(jù)處理的過程很耗時。
2、場景2:如果方法需要返回查詢結(jié)果,并且在方法里銷毀DbContext的話,是不能返回IQueryable的。必須一次性加載返回。
3、場景3:多個IQueryable的遍歷嵌套。很多數(shù)據(jù)庫的ADO.NET Core Provider是不支持多個DataReader同時執(zhí)行的。把連接字符串中的MultipleActiveResultSets=true刪掉,其他數(shù)據(jù)庫不支持這個。
//對應(yīng)異步方法
1、SaveChanges()、SaveChangesAsync()
2、異步方法大部分是定義在Microsoft.EntityFrameworkCore這個命名空間下EntityFrameworkQueryableExtensions等類中的擴展方法,記得using。
3、AddAsync()、AddRangeAsync()、AllAsync()、AnyAsync、AverageAsync、ContainsAsync、CountAsync、FirstAsync、FirstOrDefaultAsync、ForEachAsync、LongCountAsync、MaxAsync、MinAsync、SingleAsync、SingleOrDefaultAsync、SumAsync等
Dapper ORM
https://dapper-tutorial.net/step-by-step-tutorial
https://www.w3cschool.cn/dapperorm/
https://www.jb51.net/article/216497.htm
https://www.codenong.com/cs106392582/
https://www.cnblogs.com/yixuanhan/p/9238781.html
https://blog.csdn.net/dxm809/article/details/112038117
簡單案例
using Dapper;
using MySql.Data.MySqlClient;
using System.Data;
using System.Data.SqlClient;
List<User> user = new List<User>();
string strcon = "Database=traytest;Data Source=localhost;User Id=root;Password=123456;CharSet=utf8;port=3306";
IDbConnection db = new MySqlConnection(strcon);
db.Open();
//queryid(28);
Insert(new User { name= "2222",age="2222" });
//查詢
void query()
{
user = db.Query<User>("Select * From user").ToList();
foreach (var item in user)
{
Console.WriteLine("name=" + item.name + "~~~age=" + item.age);
}
}
void queryid(int id)
{
user = db.Query<User>($"Select * From user where id={id}").ToList();
foreach (var item in user)
{
Console.WriteLine("name=" + item.name + "~~~age=" + item.age);
}
}
//添加
void Insert(User user)
{
string insertSqlStr = $"INSERT INTO user(id,Name,age)VALUES(null,{user.name},{user.age})";
//db.Execute(insertSqlStr, user); //這種是按實體插入
db.Execute(insertSqlStr); //這種是按實體插入
}
public class User
{
public int id { get; set; }
public string name { get; set; }
public string age { get; set; }
}
-------------------
//查詢
#region Query
void queryid(int id)
{
user = db.Query<User>($"Select * From user where id={id}").ToList();
foreach (var item in user)
{
Console.WriteLine("name=" + item.name + "~~~age=" + item.age);
}
}
#endregion
#region Update
//更新
void Update(User user)
{
string sql = "UPDATE user SET name = @name,age=@age WHERE id = (@id)";
db.Execute(sql, user); //這種是按實體插入
Console.WriteLine("更新成功");
}
//更新多個
void UpdateNumbs(List<User> users)
{
string sql = "UPDATE user SET name = @name,age=@age WHERE id = (@id)";
db.Execute(sql, users); //這種是按實體插入
Console.WriteLine("更新成功多個");
}
#endregion
#region Delete
//刪除
void Delete(User user)
{
string sql = "DELETE FROM user WHERE id = @id";
db.Execute(sql, user); //這種是按實體刪除
Console.WriteLine("刪除成功");
}
//刪除多個
void DeleteNumbs(List<User> users)
{
string sql = "DELETE FROM user WHERE id = @id";
db.Execute(sql, users); //這種是按實體刪除多個
Console.WriteLine("刪除多個");
}
#endregion
#region Add
//添加
void Insert(User user)
{
//string insertSqlStr = $"INSERT INTO user(id,name,age)VALUES(null,{user.name},{user.age})";
string insertSqlStr = $"INSERT INTO user(id,name,age)VALUES(null,(@name),(@age))";
//db.Execute(insertSqlStr, user); //這種是按實體插入
db.Execute(insertSqlStr, user); //這種是按實體插入
Console.WriteLine("添加成功");
}
//添加多個
void InsertNumbs(List<User> users)
{
string insertSqlStr = $"INSERT INTO user(id,name,age)VALUES(null,(@name),(@age))";
//db.Execute(insertSqlStr, user); //這種是按實體插入
db.Execute(insertSqlStr, users); //這種是按實體插入
Console.WriteLine("添加多個成功");
}
#endregion
SqlSugar ORM
官網(wǎng)
簡單案例
//查詢所有
using SqlSugar;
using MySql.Data;
Demo();
static void Demo()
{
//創(chuàng)建數(shù)據(jù)庫對象
SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = "Database=traytest;Data Source=localhost;User Id=root;Password=123456;CharSet=utf8;port=3306",
//ConnectionString = "server=localhost;Database=traytest;Uid=root;Pwd=123456; AllowLoadLocalInfile=true",
DbType = DbType.MySql,
//DbType = DbType.MySqlConnector,
IsAutoCloseConnection = true
});
//調(diào)試SQL事件,可以刪掉(要放在執(zhí)行方法之前)
db.Aop.OnLogExecuting = (sql, pars) =>
{
Console.WriteLine(sql);//輸出sql,查看執(zhí)行sql
//5.0.8.2 獲取無參數(shù)化 SQL
//UtilMethods.GetSqlString(DbType.SqlServer,sql,pars)
};
//查詢表的所有
//var list = db.Queryable<User>().ToList();
//foreach (var item in list)
//{
// Console.WriteLine("name=" + item.Name + "~~~age=" + item.Age);
//}
//插入
//db.Insertable(new User() { Age="jack", Name = "jack" }).ExecuteCommand();
//更新
//db.Updateable(new User() { Id = 30, Age = "jack2", Name = "jack2" }).ExecuteCommand();
//刪除
//db.Deleteable<User>().Where(it => it.Id == 30).ExecuteCommand();
}
//實體與數(shù)據(jù)庫結(jié)構(gòu)一樣
public class User
{
//數(shù)據(jù)是自增需要加上IsIdentity
//數(shù)據(jù)庫是主鍵需要加上IsPrimaryKey
//注意:要完全和數(shù)據(jù)庫一致2個屬性
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
public string Age { get; set; }
public string Name { get; set; }
}
Freesql ORM
官網(wǎng)文章來源:http://www.zghlxwxcb.cn/news/detail-444179.html
簡單案例
IFreeSql fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.MySql, "Database=traytest;Data Source=localhost;User Id=root;Password=123456;CharSet=utf8;port=3306")
.UseAutoSyncStructure(true) //自動同步實體結(jié)構(gòu)到數(shù)據(jù)庫,F(xiàn)reeSql不會掃描程序集,只有CRUD時才會生成表。
.Build(); //請務(wù)必定義成 Singleton 單例模式
List<User> select = fsql.Select<User>().ToList();
foreach (var item in select)
{
Console.WriteLine(item.Age);
}
//實體與數(shù)據(jù)庫結(jié)構(gòu)一樣
public class User
{
public int Id { get; set; }
public string Age { get; set; }
public string Name { get; set; }
}
ADO.NET(不是orm)
參考鏈接:
官方文檔
易百教程
asp.net連接mysql數(shù)據(jù)庫+mysql公共類
https://blog.csdn.net/qq_23018459/article/details/79446935
對理解有幫助
c#中用來批量更新數(shù)據(jù)庫SqlCommandBuilder文章來源地址http://www.zghlxwxcb.cn/news/detail-444179.html
- 由于Ado直接操作數(shù)據(jù)庫,性能開銷最小;而ORM架構(gòu)大多使用反射來獲取對象屬性,然后映射成數(shù)據(jù)字段,或者反過來把字段映射成屬性,反射有性能上的開銷。
- 五大對象:
Connection(連接對象)
Command(命令對象)
DataReader(只讀,數(shù)據(jù)庫)
DataAdapter(數(shù)據(jù)適配器)
DataSet(數(shù)據(jù)集對象)
到了這里,關(guān)于.NET-4.ORM 常見框架EFcorn、Dapper、SqlSugar、FreeSql 和ADO.NET的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!