Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

数组类型的导航 #1145

Closed
2A5F opened this issue Jun 3, 2022 · 5 comments
Closed

数组类型的导航 #1145

2A5F opened this issue Jun 3, 2022 · 5 comments
Labels
enhancement New feature or request

Comments

@2A5F
Copy link

2A5F commented Jun 3, 2022

Feature 特性

对数组做导航

简要描述原因

pg 之类的数据库支持数组类型
但是没有对数组做导航支持

使用场景

避免无必要的中间表

class User {
  public int[] RoleIds { get; set; }
  public Role[] Roles  { get; set; }
}
class Role {
  public int Id { get; set; }
}
@2881099
Copy link
Collaborator

2881099 commented Jun 3, 2022

收到,确实有这个需求

@2881099 2881099 added the enhancement New feature or request label Jun 3, 2022
@2881099
Copy link
Collaborator

2881099 commented Jun 4, 2022

PostgreSQL 【数组类型】的【导航属性】专属功能:

public enum TableRefType
{
    OneToOne, ManyToOne, OneToMany, ManyToMany,
    PgArrayToMany //PgArray 专用导航类型
}

方式一:select * from Role where Id in (RoleIds)

class User
{
    public int[] RoleIds { get; set; }
    [Navigate(nameof(RoleIds))]
    public List<Role> Roles { get; set; }
}

方式二:select * from User where RoleIds @> ARRAY[Id]::int4[]

class Role
{
    public int Id { get; set; }
    [Navigate(nameof(User.RoleIds))]
    public List<User> Users { get; set; }
}

1、支持 LazyLoading 延时加载
2、支持 IncludeMany、IncludeByPropertyName 贪婪加载

fsql.Select<User>().IncludeMany(a => a.Roles).ToList();
fsql.Select<User>().IncludeByPropertyName("Roles").ToList();

fsql.Select<Role>().IncludeMany(a => a.Users).ToList();
fsql.Select<Role>().IncludeByPropertyName("Users").ToList();

3、支持 Lambda 子查询

fsql.Select<User>().Where(a => a.Roles.Any(b => b.RoleName == "管理员")).ToList();
fsql.Select<User>().Where(a => a.Roles.Count() > 0).ToList();

fsql.Select<Role>().Where(a => a.Users.Any(b => b.UserName == "Admin")).ToList();
fsql.Select<Role>().Where(a => a.Users.Count() > 0).ToList();

//... 以及 AsSelect()

4、不支持级联保存、级联删除(因机制冲突)

@gitlsl
Copy link

gitlsl commented Jun 4, 2022

1.用 数组的话 不是没法插入 删除了。。 日常使用中大家用list比较多吧, 或者对 enumable进行操作?
2.级联保存、级联删除 不仅仅是这次数组, 我在想能否增加 参数控制,或者 用不同的函数名字, 控制 级联保持,删除时的机制,(清空老的,保留老的,等等之类, 估计写的健全比较困难)

@2881099
Copy link
Collaborator

2881099 commented Jun 6, 2022

2881099 added a commit that referenced this issue Jun 7, 2022
@2881099
Copy link
Collaborator

2881099 commented Jun 7, 2022

前言

PostgreSQL 是世界公认的功能最强大的开源数据库,除了基础数据类型 int4/int8/varchar/numeric/timestamp 等,还支持 int4[]/int8[]/varchar[]/numeric[]/timestamp[] 数组类型、hstore 类型(类似 c# Dictionary<string, string>)、gis 类型。

选择一项技术一定是可以帮助我们提升效率,并且使用一定是非常简便的,才能让我们赚取更多时间,早点下班甚至上班摸鱼。这篇文章主要讲数组类型,如何与 FreeSql 打出简便高效的【组合拳】。

PostgreSQL 可以为每一种类型创建数组,比如 int4 对应的数组类型是 int4[]、varchar 对应的数据类型是 varchar[]。

PostgreSQL 虽然支持多维数组,但是不建议使用,因为实际使用中会带来很多操作麻烦,违背了使用简便的初衷。一维数组足够应付很多场景。

FreeSql 本身已经支持五种导航属性,OneToOne/ManyToOne/OneToMany/ManyToMany/Parent,为它们量身打造了级联保存、贪婪加载、延迟加载、级联删除(递归)、CTE递归查询等功能。

今天 FreeSql 为 PostgreSQL Array 数组类型提供了第六种新的导航属性 PgArrayToMany 专属功能。


数组映射

FreeSql 支持 int[] 映射 int4[]

string[] 映射 varchar[]

DateTime[] 映射 timestamp[]

class Model
{
    public Guid Id { get; set; }
    public int[] TypeIds { get; set; }
    public Guid[] UserIds { get; set; }
}

等等,常用的类型都可以直接用数组进行映射,之后就可以像普通类型一样进行 CRUD 操作了。

var model = new Model
{
    Id = Guid.NewGuid(),
    TypeIds = new int[] { 1,2,3 },
    UserIds = new Guid[] { Guid.NewGuid(), Guid.NewGuid() }
};
fsql.Insert(model).ExecuteAffrows(); //插入
fsql.Update<Model>().SetSource(model).ExecuteAffrows(); //更新
fsql.Delete(model).ExecuteAffrows(); //删除
List<Model> list = fsql.Select<Model>().ToList(); //查询

表达式解析:

fsql.Select<Model>().Where(a => a.TypeIds.Any()).ToSql();
fsql.Select<Model>().Where(a => a.TypeIds.Contains(3)).ToSql();
fsql.Select<Model>().ToSql(a => new
{
    NewTypeIds = a.TypeIds.Concat(new int [] { 10, 11, 12 })
});
fsql.Select<Model>().Where(a => a.TypeIds.Length > 0).ToSql();

如果不够用还可以自定义解析:

[ExpressionCall]
public static class DbFunc {
  //必要定义 static + ThreadLocal
  static ThreadLocal<ExpressionCallContext> context = new ThreadLocal<ExpressionCallContext>();

  public static DateTime FormatDateTime(this DateTime that, string arg1)
  {
    var up = context.Value;
    if (up.DataType == FreeSql.DataType.PostgreSQL) //重写内容
      up.Result = $"array_xxx({up.ParsedContent["that"]}, {up.ParsedContent["arg1"]})";
    return that;
  }
}

var sql1 = fsql.Select<Model>()
  .ToSql(a => a.CreateTime.FormatDateTime("xxx"));
//SELECT array_xxx(a."CreateTime", 'xxx') as1 
//FROM "Model" a

issues #1145

Feature 特性:对数组做导航

简要描述原因:pg 之类的数据库支持数组类型,但是没有对数组做导航支持

使用场景:避免无必要的中间表

class User
{
  public int[] RoleIds { get; set; }
  public Role[] Roles  { get; set; }
}
class Role
{
  public int Id { get; set; }
}

解决方案

经过多方需求讨论之后,设定了【功能目标】如下:(已发布版本 v3.2.666-preview20220606)

功能名称:PostgreSQL 【数组类型】的【导航属性】专属功能

public enum TableRefType
{
    OneToOne, ManyToOne, OneToMany, ManyToMany,
    PgArrayToMany //PgArray 专用导航类型
}

方式一:select * from Role where Id in (RoleIds)

class User
{
    public int[] RoleIds { get; set; }
    [Navigate(nameof(RoleIds))]
    public List<Role> Roles { get; set; }
}

方式二:select * from User where RoleIds @> ARRAY[Id]::int4[]

class Role
{
    public int Id { get; set; }
    [Navigate(nameof(User.RoleIds))]
    public List<User> Users { get; set; }
}

1、支持 LazyLoading 延时加载

2、支持 IncludeMany、IncludeByPropertyName 贪婪加载

fsql.Select<User>().IncludeMany(a => a.Roles).ToList();
fsql.Select<User>().IncludeByPropertyName("Roles").ToList();

fsql.Select<Role>().IncludeMany(a => a.Users).ToList();
fsql.Select<Role>().IncludeByPropertyName("Users").ToList();

3、支持 Lambda 子查询

fsql.Select<User>().Where(a => a.Roles.Any(b => b.RoleName == "管理员")).ToList();
fsql.Select<User>().Where(a => a.Roles.Count() > 0).ToList();

fsql.Select<Role>().Where(a => a.Users.Any(b => b.UserName == "Admin")).ToList();
fsql.Select<Role>().Where(a => a.Users.Count() > 0).ToList();

//... 以及 AsSelect()

4、不支持级联保存、级联删除(因机制冲突)


资料补充

至此,FreeSql 支持了六种导航属性。

FreeSql 五种导航属性进化过程 OneToOne/ManyToOne/OneToMany/ManyToMany/Parent(文章内不包括 PgArrayToMany 介绍)

  • ManyToOne(N对1) 提供了简单的多表 join 查询;

  • OneToMany(1对N) 提供了简单可控的级联查询、级联保存功能;

  • ManyToMany(多对多) 提供了简单的多对多过滤查询、级联查询、级联保存功能;

  • Parent(父子关系) 提供了常用的 CTE查询、删除、递归功能;

  • PgArrayToMany(数组导航) 提供了 pgsql array 数组类型级联查询;

2881099 added a commit that referenced this issue Jun 9, 2022
@2A5F 2A5F closed this as completed Jun 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants