EFCore之SQL扩展组件BeetleX.EFCore.Extension
EFCore是.NETCore团队开发的一个ORM组件,但这个组件在执行传统SQL的时候并不方便,因此BeetleX.EFCore.Extension的设计目的是让EFCore执行传统SQL更简单方便。
引用
在使用组件之前需要引用它,可以通过以下地址获取最新版本:https://www.nuget.org/packages/BeetleX.EFCore.Extension/
使用
引用组件后就可以使用,组件的操作操作都是针对EFCore的DBContext对象来进行数据库操作。
SQL sql = "select * from employees"; var employees = sql.List<Employee, NorthwindContext>();
组件提供SQL对象用来操作传统的SQL语句,以上代码是在NorthwindContext数据库上执行查询语句并返回相关信息列表。
sql = "select * from customers where country=@country"; sql += ("@country", "UK"); var customers = sql.List<Customer, NorthwindContext>();
以上是针对参数化的SQL处理,在操作上DBContext可以通过泛参传入或通过实例以变量的参数传入,通过泛参传入的好处是不用实例化DBContext。
批量更新
在EFCore更新某个条件的字段信息操作起来比较麻烦,所以组件也扩展出相关方法来解决。
using (NorthwindContext db = new NorthwindContext()) { db.Customers.Where(c => c.CustomerID == "ken") .Update(c => new Customer { City="GZ" }); }
以上操作是把国家是UK的所有记录Region改成uk
批量删除
同样EFCore在批条件删除上也不怎么方便,组件同样也在DBSet的基础上扩展了Delete批删除方法.
using (NorthwindContext db = new NorthwindContext()) { db.Customers.Where(c => c.CustomerID == "ken") .Delete(); }
以上是删除国家是UK的所有记录.
Select对象
Select对象是针对单个表的个性查询需求制定的,它可以定义查询不同的定段和条件来返回到指定的对象中。
class CustomerName { public string CustomerID { get; set; } public string CompanyName { get; set; } } [Fact] Select<Customer> select = new Select<Customer>("CustomerID", "CompanyName"); select &= c => c.Country == "UK"; var items = select.List<CustomerName, NorthwindContext>();
以上是针对客户信息查询一些字段并返回到其他结构的对象列表中。
SQL对象
为了让字符的Sql语句处理更方便,所以组针对Sql语句封了一个对象。该对象主要用于执行Sql语句并返回结果,它的主要作用是简单化DbCommand的处理。在这基础上提供参数化处理,查询支持返回固定类型和动态类型,支持同步和异步操作等。
定义对象
该对象支持直接从String转义,所以在定义的时候可以直接指向一个Sql的字符串。
SQL sql = "select * from customers";
以上是定义一个客户查询的SQL对象,定义完成后就可以进行相关查询操作,它提供了Execute,ExecuteScalar,ListFirst和List方法,分别是返回符合查询的第一个对象和返回符查询条件某个区中的对象;以上两个方法都支持异步ExecuteAsync,ExecuteScalarAsync,ListFirstAsync和ListAsync,并支持多个重载版本可操作。
ListFirst
public T ListFirst<T, DB>() where DB : DbContext, new() public T ListFirst<T>(DbContext db) where T : new() public Task<T> ListFirstAsync<T, DB>() where DB : DbContext, new() where T : new() public async Task<T> ListFirstAsync<T>(DbContext db) where T : new()
T可以是任意对象类型,组件会自动做字段和属性匹配;DbContext支持两种方式提供,一种基于泛参在这情况下对应DbContext必须提供默认构建函数;别一种则直接通参数传入对应的实例对象。
List
public IList<T> List<T, DB>(Region region = null) where T : new() where DB : DbContext, new() public IList<T> List<T>(DbContext db, Region region = null) where T : new() public void List(DbContext db, Region region, Action<IDataReader> handler) public async Task<IList<T>> ListAsync<T, DB>(Region region = null) where T : new() where DB : DbContext, new() public async Task<IList<T>> ListAsync<T>(DbContext db, Region region = null) where T : new() public async Task ListAsync(DbContext db, Region region, Action<IDataReader> handler)
T可以是任意对象类型,组件会自动做字段和属性匹配;DbContext支持两种方式提供,一种基于泛参在这情况下对应DbContext必须提供默认构建函数;别一种则直接通参数传入对应的实例对象。region是缺省参数用于指定加载那个区间的数据,默认不指定的情况下组件只加载100条数据。
SQL sql = "select * from customers"; var customers = sql.List<Customer, NorthwindContext>(); customers = await sql.ListAsync<Customer, NorthwindContext>();
动态类型
有很多时候只查询个别字段,但又不可以定义相关结构对象的情况下可以使用动态类型传入.
SQL sql = "select CustomerID,CompanyName from customers"; var items=sql.List<ExpandoObject, NorthwindContext>(); foreach(dynamic item in items) { Console.WriteLine(item.CustomerID); }
可以通过ExpandoObject作为动态类型对象传入,后继通过dynamic定义操作即可.
分页获取
虽然在查询过程中提供了Region参数用于获取某个区间的数据,但在分页应用中还是需要统计总数和页数的处理,为了简化这个操作组件封了DBRegionData<T>对象来简化这操作.
SQL sql = "select * from customers"; DBRegionData<Customer> region = new DBRegionData<Customer>(0, 20); await region.ExecuteAsync<NorthwindContext>(sql); foreach (var item in region.Items) { Console.WriteLine(item.CompanyName); }
以上示例是按20条来分页,获取第一页的数据。DBRegionData<T>提供了Count,Pages,Index和Items等属性可提供访问。
拼装和参数化
在编写Sql语句的时候往往都不能一条写完,很多时候针对不同的条件来组装语句;SQL对象重写了运算符直接支持和String相加的处理。
SQL sql = "select * from customers where 1=1"; sql += " and country=@country"; sql += ("@country", "UK"); Console.WriteLine(sql.Count<NorthwindContext>());
SQL支持和String直接相加,参数变量也是通过加的方式来添加,只是对应参数必须以ValueTask<string,object>的方式编写。
表达式条件
对于直接用String来拼接Sql语句容易出错,那可以采用对象提供的表达式来进行条件增加处理;这种处理方式的好处是不容易写错字段名称和参数化的处理。
SQL sql = "select * from orders where 1=1"; sql.And().Where<Order>((o) => o.EmployeeID == 3 && o.CustomerID == "henry");
对象提供了Where方法来编写条件表达式,该表达式支持多类型传入处理。
SQL sql = "select * from orders where 1=1"; sql.And().Where<Order>((o) => o.EmployeeID == 3 && o.CustomerID == "henry"); sql.OrderBy<Order>(o => o.OrderDate.ASC());
同样排序方式也可以通过表达式的方式来添加.
转义对象
DBObjectList<T>,DBValueList<T>DBExecute<T>和DBExecute都支持转义操作,对应的结构是ValueTuple<DbContext, SQL>.在转义过程中组件可支持执行处理。
using (NorthwindContext db = new NorthwindContext()) { SQL sql = "select * from customers"; DBObjectList<Customer> customers = (db, sql); sql = "select CustomerID,CompanyName from customers"; DBObjectList<ExpandoObject> names = (db, sql); sql = "select count(*) from customers"; DBValueList<long> count = (db, sql); }
事务
SQL对象无须针对事务做任何设置,它的事务开启主要是由对应的DbContext对象所决定的。
执行链跟踪
在新版中添加了执行跟踪链,可以进一步查看组件执行SQL的情况。
using (CodeTrackFactory.TrackReport("AutoExecute", CodeTrackLevel.Bussiness, null, "EFCore", "BeetleX")) { using (NorthwindContext db = new NorthwindContext()) { DBValueList<string> values = (db, "select customerid from customers"); DBObjectList<CustomerName> items = (db, "select CustomerID,CompanyName from customers"); DBExecute<string> id = (db, "select CompanyName from customers where CustomerID='ALFKI'"); var item = db.Customers.Where(c => c.CustomerID == "AROUT").FirstOrDefault(); item.Region = "GuangZhou"; db.SaveChanges(); } } Console.WriteLine(CodeTrackFactory.Activity?.GetReport());