自引用的组织结构树
class OrgUnit
{public long Id { get; set; }public long? ParentId { get; set; }public string Name { get; set; }public OrgUnit? Parent { get; set; }public List<OrgUnit> Children { get; set; } = new List<OrgUnit>();
}
class OrgUnitConfig : IEntityTypeConfiguration<OrgUnit>{public void Configure(EntityTypeBuilder<OrgUnit> builder){builder.ToTable("T_OrgUnits");builder.HasKey(o => o.Id);builder.Property(o => o.Name).IsUnicode().IsRequired().HasMaxLength(50);//根节点可空builder.HasOne<OrgUnit>(o => o.Parent).WithMany(u => u.Children).HasForeignKey(o=>o.ParentId).OnDelete(DeleteBehavior.Restrict);}}
static async Task Main (stringL] args)
{//既可以设置一个ou的Parent,也可以把一个节点加入父节点的Children.Add( ... )OrgUnit ouRoot= new OrgUnit{Name="中科集团全球总部”};OrgUnit ouAsia = new OrgUnit{Name="中科集团亚太区总部”};ouAsia. Parent = ouRoot;OrgUnit ouAmerica = new OrgUnit{Name="中科集团美洲总部”};ouAmerica. Parent = ouRoot:ouRoot.Children.Add(ouAsia);ouRoot.Children.Add(ouAmerica);OrgUnit ouUSA=new OrgUnit{Name="中科美国”};ouUSA. Parent = ouAmerica;ouAmerica.Children.Add(ouUSA); OrgUnit ouCan= new OrgUnit{Name=”中科加拿大”};ouCan. Parent = ouAmerica;ouAmerica.Children.Add(ouCan); OrgUnit ouChina = new OrgUnit {Name=”中科集团(中国)”};ouChina. Parent = ouAsia;ouAsia.Children.Add(ouChina); OrgUnit ouSg= new OrgUnit{Name=”中科集团(新加坡)”};ouSg. Parent = ouAsia;ouAsia.Children.Add(onSg); using(MyDbContext ctx = new MyDbContext ()){ctx.OrgUnits.Add (ouRpot);await ctx.SaveChangesAsync();}
}
只指定children也行
打印子节点
///<summary>
///缩进打印parent的所有的子节点
///</summary>
///<param name="identLeve1"></param>
///<param name="ctx"></param>
///<param name="parent ></param>static void PrintChildren(int identLeve1, MyDbContext ctx, OrgUnit parent)
{//不ToList()得到是IQueryble类型,这是延迟加载,会出现上述问题var children = ctx. OrgUnits. Where(o=>o. Parent==parent);foreach (var child in children){Console. WriteLine(new String('\t', identLeve1)+child.Name);PrintChildren(identLeve1+1,ctx,child);//打印以我为父节点的子节点}
}
一对一
一对一的关系配置必须在其中的一个实体类中声明一个外键属性。
builder.HasOne<Delivery>(o =>o.Delivery).WithOne(d =>d.Order).HasForeignKey<Delivery>(d=>d.OrderId);
在哪个实体类中配置外键,就在哪个实体类中进行连接的数据存储。
多对多
需要中间表进行中转
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>();
}
配置多对多的表关系Config
builder.HasMany<Teacher>(s=>s.Teachers).WithMany(t=>t.Students).UsingEntity(j=>j.ToTable("T_Students_Teachers"));
//需要配置一个中间关系表
拿取所有的教师和对应的学生
var teachers = ctx. Teachers. Include(t => t. Students)
foreach (var t in teachers)
{Console. WriteLine(t. Name) ;foreach (var s in t. Students){Console. WriteLine("\t"+s.Name);}
}
EFCore基于关系的复杂查询
一对多的关系语句查询
var items = ctx. Articles. Where(a=>a. Comments. Any(c=>c. Message. Contains("微软")));
foreach (var item in items)
{Console. WriteLine (item. Title);
}
var items = ctx. Comments. Where(c => c.Message. Contains("微软")).Select(c=>c. TheArticle). Distinct();
foreach (var item in items)
{Console. WriteLine(item.Tit1e);
}
IEnumerable和IQueryable区别
IEnumerable是客户端评估
IQueryable是服务器端评估
-
IEnumerable
通常与立即执行的查询相关联。当你对一个实现了IEnumerable
的集合进行查询时,查询操作通常会立即执行。 -
IQueryable
支持延迟执行。这意味着查询操作直到迭代或转换为
IEnumerable` 之前都不会执行。这在处理数据库查询时非常有用,可以减少不必要的数据传输。 -
使用
IEnumerable
可能在处理大量数据时导致性能问题,因为数据可能需要在内存中加载和处理。 -
使用
IQueryable
可以提高性能,因为它允许数据库执行查询,利用数据库的优化和索引。 -
由于 LINQ 扩展方法的存在,
IEnumerable
和IQueryable
都可以使用相同的查询语法,但是它们背后的执行机制不同。 -
当你需要对内存中的数据集合进行查询时,使用
IEnumerable
。 -
当你需要对数据库或其他数据源进行查询,并且希望利用数据库的查询优化时,使用
IQueryable
。
IQueryable的延迟执行
未使用变量不执行。不遍历不执行
-
IQueryable只是代表一个“可以放到数据库服务器去执行的查
询”,它没有立即执行,只是“可以被执行”而已。 -
对于IQueryable接口调用非终结方法的时候不会执行查询,而调
用终结方法的时候则会立即执行查询。 -
终结方法:遍历、ToArray()、ToList()、Min()、Max()、Count()
等: -
非终结方法:GroupBy()、OrderBy()、Include()、Skip()、
Take()等。 -
简单判断:一个方法的返回值类型如果是IQueryable类型,那 么
这个方法一般就是非终结方法,否则就是终结方法。通过使用延迟执行,可以组合出复杂的动态的sql查询语句
IQueryable可以被复用
IQueryable是一个待查询的逻辑,因此它是可以被重复使用的。
IQueryable<Book> 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);
分页查询
使用Skip().Take()来实现分页查询的功能。
需要知道满足条件的数据的总条数:使用IQueryable的复用。
LongCount(Long类型的总数)和Count(总数),这两个是linq的函数
pageSize:每页的数据的数量
pageIndex页码
每一页获得的数据
Skip((pageIndex-1)*pageSize).Take(pageSize)
总页数:
long pageCount=(long)Math.Ceiliing(count*1.0/pageSize);
math.Ceiling-----向上取整
IQueryable底层就是在调用DataReader,内存占用小,但是DB线程占用时间长。
- 遍历IQueryable并且进行数据处理的过程很耗时。
- 如果方法需要返回查询结果,并且在方法里销毁DhContext的活,是不能返回IQueryable的。必须一次性加载返回。
- 多个IQueryable的遍历嵌套。很多数据库的ADO.NET Core Provider是不支持多个DataReader同时执行的。把连接字符串中的MultipleActiveResultSets=true洲掉,其他数据库不支持这个。
foreach (var a in ctx. Articles. ToArray ())
{Console. WriteLine(a. Title);foreach (var c in ctx. Comments. ToArray ()){ Console. WriteLine(c. Message);}
}