时间:2021年04月26日 | 作者 : aaronyang | 分类 : .NET CORE | 浏览: 662次 | 评论 0 人
EF6是最后一个NF版本
EF支持读写MSSQL,SQLite,Azure CosmosDB
目前EFC 6已发布(当前时间 2021-04-26)
EFCore文档数据库提供者:https://github.com/BlueshiftSoftware/EntityFrameworkCore
EFCore微软 https://github.com/dotnet/efcore/tree/main/docs
从SQLite学习使用EF
macOS,在/usr/bin/目录中,sqlite3的命令行应用程序
Windows到 https://www.sqlite.org/download.html
下载 第三个,解压到D盘
配置系统环境变量
下载Northwind.sql文件,ctrl+S另存为
https://raw.githubusercontent.com/markjprice/cs8dotnetcore3/master/sql-scripts/Northwind.sql
vs也可以ctrl+反引号,打开和vscode一样的终端
但是恢复数据库没用,提示<错误,打开cmd进行恢复
https://www.runoob.com/sqlite/sqlite-create-database.html
书上说的使用SQLiteStudio管理Northwind库
我就不用了,我使用Navicat,网上自己找安装教程
EFCore(以后简称EFC)提供了操作的dll包
Microsoft.EntityFrameworkCore.SqlServer 操作MSSQL 2008+
Microsoft.EntityFrameworkCore.SQLite 操作SQLite
MySQL.Data.EntityFrameworkCore 操作MySQL
Microsoft.EntityFrameworkCore.InMemory 在内存中
更多第三方,链接
通过Nuget安装SQLite
编码约定:
表的名称与DbContext类的DbSet<T>属性的名称匹配
列的名称与类中的属性名称匹配
.NET类型string是数据库的nvarchar类型
.NET类型int是数据库的int类型
对于ID属性,如果类名为Product,则该属性重命名为ProductID。然后假定该属性是主键。如果该属性是整数或者Guid类型,那就可以假定为IDENTITY类型,插入时自动赋值的列类型
更多约定,查看
https://docs.microsoft.com/zh-cn/ef/core/what-is-new/ef-core-5.0/whatsnew
引入
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
添加理解的代码
public class Product { [Required] [StringLength(40)] public string ProductName { get; set; } [Column(TypeName ="money")] public decimal? UnitPrice { get; set; } } public class Category { /// <summary> /// 可以超过nvarchar的8000字符,用ntext映射 /// </summary> [Column(TypeName = "ntext")] public string Description { get; set; } public decimal? UnitPrice { get; set; } }
定义模型另一种方法Fluent APi
比如ProductName上的特性,可以在 OnModelCreating 方法替换为等效的FluentAPI
modelBuilder.Entity<Product>() .Property(product => product.ProductName) .IsRequired() .HasMaxLength(40)
使用FluentAPI提供初始数据以填充数据库。EF Core会自动计算出需要执行哪些插入,更新或删除操作。如果想要确保新数据库在Product表中至少一行,就调用HasData方法,如下所示:
modelBuilder.Entity<Product>() .HasData(new Product{ ProductID=1, ProductName="CA1", UnitPrice=9.88M });
打开Categories有4列
然后映射这个表
public class Category { public Category() { this.Products = new List<Product>(); } public int CategoryID { get; set; } public string CategoryName { get; set; } [Column(TypeName = "ntext")] public string Description { get; set; } public virtual ICollection<Product> Products { get; set; } }
这里Picture列不映射,Products体现一对多
CategoryName后面用FluentAPI映射 不为空,最多15字符
CategoryID按照主键约定来的
====================www.ayjs.net=====www.ayjs.net============
我的navicat不能打开这个表设计
我用sqlitestudio打开
映射需要的列
public class Product { public int ProductID { get; set; } [Required] [StringLength(40)] public string ProductName { get; set; } [Column("UnitPrice", TypeName = "money")] public decimal? Cost { get; set; } [Column("UnitsInStock")] public short? Stock { get; set; } public bool Discontinued { get; set; } public int CategoryID { get; set; } public virtual Category Category { get; set; } }
这里有被virtual修饰的属性,这允许EFC 实现延迟加载的功能,在NC2.0之前不可用。
注意这里的ID都是int类型的
引入using Microsoft.EntityFrameworkCore;
DbContext提供操作数据库,动态生成SQL和操作数据。
DbSet<T>代表表
OnConfiguring重载,用于设置数据库连接字符串。
同样,DbContext派生类还可以有名为OnModelCreating的重载方法。在这里,可以编写Fluent API语句,作为用特性装饰实体类的替代选择。
public class NorthWind:DbContext { public DbSet<Category> Categories { get; set; } public DbSet<Product> Products { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionBuilder) { string path = @"D:\aycore\Code\Chapter11\Northwind.db"; optionBuilder.UseSqlite($"FileName={path}"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Category>() .Property(c => c.CategoryName) .IsRequired() .HasMaxLength(15); } }
引入using System.Linq;
查询目录
class Program { static void Main(string[] args) { QueryingCategories(); Console.ReadKey(); } static void QueryingCategories() { using (var db=new NorthWind()) { Console.WriteLine("类别和产品"); IQueryable<Category> cats = db.Categories.Include(c=>c.Products); foreach (var cat in cats) { Console.WriteLine($"{cat.CategoryName} 有{cat.Products.Count}个产品"); } } } }
效果
static void QueryingProducts() { using (var db=new NorthWind()) { Console.WriteLine("价格大于50产品"); decimal price = 50M; //IOrderedEnumerable<Product> var products = db.Products.AsEnumerable().Where(p => p.Cost > price).OrderByDescending(x => x.Cost); foreach (var item in products) { Console.WriteLine("{0}:{1} 花费{2:$#,##0.00} 有{3}个stock",item.ProductID,item.ProductID,item.Cost,item.Stock); } } }
如果控制台不能显示Unicode字符,可以cmd内输入
chcp 65001
回车就行了
监视EFC和数据库之间交互,启用日志记录功能
注册日志提供程序
实现日志提供程序
引入
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore.Infrastructure;
当日志级别为None,Trace和Information时候,禁用ConsoleLogger,然后的其他的启用。
ConsoleLogger通过向控制台写入日志来实现Log方法
public class ConsoleLoggerProvider : ILoggerProvider { public ILogger CreateLogger(string categoryName) { return new ConsoleLogger(); } public void Dispose() { } } public class ConsoleLogger : ILogger { //如果你的logger使用了非托管资源,你返回个实现了IDisposable的类 public IDisposable BeginScope<TState>(TState state) { return null; } public bool IsEnabled(LogLevel logLevel) { switch (logLevel) { case LogLevel.Trace: case LogLevel.Information: case LogLevel.None: return false; case LogLevel.Debug: case LogLevel.Warning: case LogLevel.Error: case LogLevel.Critical: default: return true; }; } public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { Console.WriteLine($"Level:{logLevel},Event ID:{eventId.Id}"); if(state != null) { Console.Write($",State{state}"); } if (exception != null) { Console.Write($",Exception{exception.Message}"); } Console.WriteLine(); } }
在Querying的两个方法中加入下面代码,注册日志记录器
static void QueryingProducts() { using (var db = new NorthWind()) { var logfactory = db.GetService<ILoggerFactory>(); logfactory.AddProvider(new ConsoleLoggerProvider()); Console.WriteLine("价格大于50产品"); decimal price = 50M; //IOrderedEnumerable<Product> var products = db.Products.AsEnumerable().Where(p => p.Cost > price).OrderByDescending(x => x.Cost); foreach (var item in products) { Console.WriteLine("{0}:{1} 花费{2:$#,##0.00} 有{3}个库存", item.ProductID, item.ProductID, item.Cost, item.Stock); } } }
事件ID的值及含义特定于.NET数据提供程序。如果想知道LINQ查询是如何 转换成SQL语句并执行的,事件ID是20100
过滤,只输出 20100的事件
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { if (eventId.Id == 20100) { Console.WriteLine($"Level:{logLevel},Event ID:{eventId.Id}"); if (state != null) { Console.Write($",State{state}"); } if (exception != null) { Console.Write($",Exception{exception.Message}"); } Console.WriteLine(); } }
EFCore2.2 引入了查询标记特性,以允许向日志中添加SQL注释
static void QueryingProducts() { using (var db = new NorthWind()) { var logfactory = db.GetService<ILoggerFactory>(); logfactory.AddProvider(new ConsoleLoggerProvider()); Console.WriteLine("价格大于50产品"); decimal price = 50M; //IOrderedEnumerable<Product> //var products = db.Products.AsEnumerable().Where(p => p.Cost > price).OrderByDescending(x => x.Cost); var products = db.Products.TagWith("产品价格筛选,然后排序").AsEnumerable().Where(p => p.Cost > price).OrderByDescending(x => x.Cost); foreach (var item in products) { Console.WriteLine("{0}:{1} 花费{2:$#,##0.00} 有{3}个库存", item.ProductID, item.ProductName, item.Cost, item.Stock); } } }
更多:https://docs.microsoft.com/zh-cn/ef/core/querying/tags
static void QueryingWithLike() { using (var db = new NorthWind()) { var logfactory = db.GetService<ILoggerFactory>(); logfactory.AddProvider(new ConsoleLoggerProvider()); Console.WriteLine("搜索产品名:"); string productname = "che"; var products = db.Products.Where(p => EF.Functions.Like(p.ProductName, $"%{productname}%")); foreach (var item in products) { Console.WriteLine("{0} 的库存数量{1}. 是否停产? {2}", item.ProductName, item.Stock, item.Discontinued); } } }
用到了EF.Functions.Like
====================www.ayjs.net=================
产品可以停产,因此要确保停产的产品不会返回。
添加全局过滤器,删除停产的产品
在OnModelCreating方法添加个HasQueryFilter
public class NorthWind : DbContext { public DbSet<Category> Categories { get; set; } public DbSet<Product> Products { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionBuilder) { string path = @"D:\aycore\Code\Chapter11\Northwind.db"; optionBuilder.UseSqlite($"FileName={path}"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Category>() .Property(c => c.CategoryName) .IsRequired() .HasMaxLength(15); modelBuilder.Entity<Product>().HasQueryFilter(p => !p.Discontinued); } }
只剩3个了,那个Gumbo Mix过滤了
EF通常三种加载模式 :
立即加载 Eager Loading
延迟加载Lazy Loading
显式加载Explicit Loading
在QueryingCategories方法中使用了 Include ,这是立即加载(也被称为早期加载)
删掉Include代码,执行下
现在的products都为空了
EFC2.1引入延迟加载,从而能够自动加载缺失的相关数据。
要启用延迟加载,开发人员必须
为代理引用NuGet包
配置延迟加载以使用代理
安装 Microsoft.EntityFrameworkCore.Proxies
protected override void OnConfiguring(DbContextOptionsBuilder optionBuilder) { string path = @"D:\aycore\Code\Chapter11\Northwind.db"; optionBuilder.UseLazyLoadingProxies().UseSqlite($"FileName={path}"); }
看日志,每次都多了个select查询
====================www.ayjs.net=================
与延迟加载相似,不同之处在于可以控制加载哪些相关数据以及何时加载。
static void QueryingCategories() { using (var db = new NorthWind()) { var logfactory = db.GetService<ILoggerFactory>(); logfactory.AddProvider(new ConsoleLoggerProvider()); //Console.WriteLine("类别和产品"); //IQueryable<Category> cats = db.Categories.Include(c => c.Products); //IQueryable<Category> cats = db.Categories; //foreach (var cat in cats) //{ // Console.WriteLine($"{cat.CategoryName} 有{cat.Products.Count}个产品"); //} //显示加载实体 IQueryable<Category> cats; db.ChangeTracker.LazyLoadingEnabled = false; Console.Write("是否启用贪婪加载?(Y/N): "); bool eagerloading = (Console.ReadKey().Key == ConsoleKey.Y); bool explicitloading = false; Console.WriteLine(); if (eagerloading) { cats = db.Categories.Include(c => c.Products); } else { cats = db.Categories; Console.Write("是否启用显示加载?(Y/N):"); Console.WriteLine(); explicitloading = (Console.ReadKey().Key == ConsoleKey.Y); foreach (var cat in cats) { if (explicitloading) { Console.Write("是否为" + cat.CategoryName + "加载显示加载产品?(Y/N):"); Console.WriteLine(); var c = (Console.ReadKey().Key == ConsoleKey.Y); if (c) { var products = db.Entry(cat).Collection(c2 => c2.Products); if (!products.IsLoaded) products.Load(); } } Console.WriteLine($"{cat.CategoryName} 有{cat.Products.Count}个产品"); } } } }
主要代码:
第一步禁用
db.ChangeTracker.LazyLoadingEnabled = false;
第二步:
var products = db.Entry(cat).Collection(c2 => c2.Products); if (!products.IsLoaded) products.Load();
增删改过后,请调用SaveChanges方法
插入实体,显示产品
static bool AddProduct(int categoryID, string productName, decimal? price) { using (var db = new NorthWind()) { var newp = new Product { CategoryID=categoryID, ProductName=productName, Cost=price }; db.Products.Add(newp); int affected = db.SaveChanges(); return affected == 1; } } static void ListProducts() { using (var db = new NorthWind()) { Console.WriteLine("{0,-3} {1,-35} {2,8} {3,5} {4}","ID","Product Name","Cost","Stock","Disc."); var _p = db.Products.OrderByDescending(x=>x.Cost); foreach (var item in _p) { Console.WriteLine("{0:000} {1,-35} {2,8:$#,##0.00} {3,5} {4}",item.ProductID,item.ProductName,item.Cost,item.Stock,item.Discontinued); } } }
在Main方法测试
AddProduct(6, "杨洋界面", 599M); ListProducts();
由于我测试,运行了2次,所以有两条记录
先获取一遍,然后修改属性
using (var db=new NorthWind()) { var p = db.Products.FirstOrDefault(x => x.ProductName == "杨洋界面"); if (p != null) { p.Cost = 2021M; } db.SaveChanges(); } ListProducts();
删除实体,先找出 需要删除的 实体集合,然后RemoveRange
ListProducts(); using (var db = new NorthWind()) { var p = db.Products.Where(x => x.ProductName.StartsWith("杨洋")); db.Products.RemoveRange(p); db.SaveChanges(); } ListProducts();
先列出来一遍
删除后
DbContext类是可以销毁的,并且是按照单一工作单元原则设计的。
ASP.NET CORE与EF CORE的相关的特点:在构建Web应用程序和Web服务时,可通过汇集数据库上下文来提高代码的效率。这将允许创建和释放尽可能多的DbContext派生对象,从而确保代码仍然是有效的。
https://docs.microsoft.com/zh-cn/ef/core/what-is-new/ef-core-2.0/#dbcontext-pooling
每次SaveChanges方法,都会启动隐式事务,以便错误后回滚。
原子性,一致性,隔离性,持久性,ACID
隔离级别 锁 允许的完整性问题
ReadUncommitted 无 脏读,不可重复读,幻像数据
ReadCommitted 当编辑时,应用读取锁以组织其他用户读取记录,直到事务结束 不可重复读和幻像数据
RepeatableRead 当读取时,应用编辑锁以阻止其他用户编辑记录,知道事务结束 幻影数据
Searializable 应用键范围的锁以防止任何可能影响结果的操作,包括插入和删除 无
Snapshot 无 无
引入using Microsoft.EntityFrameworkCore.Storage;
AddProduct(6, "杨洋界面1", 599M); AddProduct(6, "杨洋界面2", 599M); ListProducts(); using (var db = new NorthWind()) { using (IDbContextTransaction t=db.Database.BeginTransaction()) { Console.WriteLine("隔离级别:"+t.GetDbTransaction().IsolationLevel); var p = db.Products.Where(x => x.ProductName.StartsWith("杨洋")); db.Products.RemoveRange(p); db.SaveChanges(); t.Commit(); } } ListProducts();
效果:
新增成功
然后定义显示事务,删除产品
====================www.ayjs.net=================
抖音:wpfui 工作wpf,目前主maui
招聘合肥一枚WPF工程师,跟我一个开发组,10-15K,欢迎打扰
目前在合肥市企迈科技就职
AYUI8全源码 Github地址:前往获取
杨洋(AaronYang简称AY,安徽六安人)和AY交流
高中学历,2010年开始web开发,2015年1月17日开始学习WPF
声明:AYUI7个人与商用免费,源码可购买。部分DEMO不免费
不是从我处购买的ayui7源码,我不提供任何技术服务,如果你举报从哪里买的,我可以帮你转正为我的客户,并送demo
查看捐赠AYUI7.X MVC教程 更新如下:
第一课 第二课 程序加密教程
额 本文暂时没人评论 来添加一个吧
发表评论