update

2881099
2023-03-02 15:24:28 +08:00
parent 88adc217e8
commit 95b1aeff47
3 changed files with 194 additions and 254 deletions

@@ -1,37 +1,41 @@
[中文](%e5%ae%9e%e4%bd%93%e5%85%b3%e7%b3%bb) | **English**
Navigation properties are one of FreeSql's characteristic functions, which can be configured by agreement or customized configuration of the relationship between objects.
Navigation attribute is one of the features of FreeSql, providing OneToMany, ManyToOne, ManyToMany, OneToOne, Parent, [PgArrayToMany](https://github.com/dotnetcore/FreeSql/issues/1145#issuecomment-1148625334) Six relationships.
Navigation properties have six configuration relationships: OneToMany, ManyToOne, ManyToMany, OneToOne, Parent, And [PgArrayToMany](https://www.cnblogs.com/FreeSql/p/16351417.html).
What can navigation attributes do?
With navigation properties, multi-table query is very convenient. Directly using navigation objects in lambda expressions can get the IDE's BUFF blessing.
- [《Multi Tables》](Query-from-Multi-Tables) Where(a => a.Parent.Parent.Name == "xx")
- [《Greed Loading》](Greed-Loading) Include/IncludeMany
- [《Lazy Loading》](Lazy-Loading)
- [《Parent-Child Relp》](Parent-Child-Relationship-Query)
- [《Cascade Saving》](Cascade-Saving)
- [《Cascade Deletion》](Cascade-Deletion)
- Naming convention,or not (need to specify Navigate attribute association);
- If there is no association relationship, you can specify the `On` condition when querying, `LeftJoin(a => a.Parent.Id == a.ParentId)`;
- If there is an association relationship, just use the navigation object directly, and the `On` condition will be automatically attached;
# Custom Navigation Relationship
[《What problems can navigation properties solve?》](https://www.cnblogs.com/kellynic/p/13575053.html)
> Warm up note: to load navigation attributes, you need to solve the problem of dead cycle reference. When the reference relationship is very complex, it may lead to the failure of using navigation attributes for the first time. The second time is enough. The solution is to preheat all entity classes when the program starts, and while execute fsql.Select\<object\>().Astype (entity type);
OneToMany/ManyToMany supported by collection navigation properties: ICollection\<T\>、List\<T\>、ObservableCollection\<T\>
## Custom Navigation Relationship
OneToMany/ManyToMany supported types: ICollection\<T\>、List\<T\>、ObservableCollection\<T\>
```csharp
//Navigation properties, OneToMany
[Navigate(nameof(song_tag.song_id))]
public virtual List<song_tag> Obj_song_tag { get; set; }
//Find the song_id property in song_tag and associate it with this ENTITY.PrimaryKey
//OneToMany
class Group
{
[Navigate(nameof(User.GroupId))]
public List<User> Users { get; set; }
//Find the GroupId property in User and associate it with this ENTITY.PrimaryKey
}
//Navigation properties, ManyToOne/OneToOne
[Navigate(nameof(song_id))]
public virtual Song Obj_song { get; set; }
//Find the song_id property in THIS ENTITY and associate it with the Song.PrimaryKey
//ManyToOne
class User
{
public int GroupId { get; set; }
[Navigate(nameof(GroupId))]
public Group Group { get; set; }
//Find the GroupId property in THIS ENTITY and associate it with the Group.PrimaryKey
}
//Navigation properties, ManyToMany
[Navigate(ManyToMany = typeof(tag_song))]
public virtual List<tag> tags { get; set; }
//ManyToMany
[Navigate(ManyToMany = typeof(TagSong))]
public List<Tag> Items { get; set; }
```
---
@@ -45,26 +49,34 @@ fsql.CodeFirst.ConfigEntity<YOUR_ENTITY>(a => a
);
```
Priority: Attribute> FluentApi
> Note:
1. Set `Column(IsIgnore = true)` on Property, then the navigation property will be invalid
2. The string set by Navigate is the property name of the type, NOT THE TABLE IR FIELD NAME.
> extend:
> Warm-up description: The navigation attribute configuration is loaded because it is necessary to solve the dead cycle reference. When the mutual reference relationship is very complex, it may cause the first use of navigation attributes to fail. The second time is enough. The solution is to warm up all entity classes when the program starts, and execute fsql.Select\<object\>().AsType(entityClass) in a loop;
- [Create non primary key navigation attributes OneToOne/ManyToOne](https://github.com/dotnetcore/FreeSql/issues/604)
# Associate with non-primary key
```csharp
//OneToMany
[Navigate(nameof(TagSong.SongId), TempPrimary = nameof(Code))]
public List<TagSong> Item { get; set; }
//ManyToOne
[Navigate(nameof(SongId), TempPrimary = nameof(Song.Code))]
public Song Item { get; set; }
```
## Detect Navigation Properties
How to detect whether a navigation property is configured to take effect:
```csharp
var tbref = fsql.CodeFirst
.GetTableByEntity(typeof(T))
.GetTableRef("Children", true);
fsql.CodeFirst.GetTableByEntity(typeof(T))
.TestNavigate("roles")
.TestNavigate("users");
```
Method signature:
@@ -73,124 +85,77 @@ Method signature:
GetTableRef(string propertyName, bool isThrow);
```
## Naming convention (no need to specify Navigate)
### One-to-One
# PgArrayToMany
```csharp
class User
{
public int Id { get; set; }
public UserExt Ext { get; set; }
public int[] RoleIds { get; set; }
[Navigate(nameof(RoleIds))]
public List<Role> Roles { get; set; }
}
class Role
{
public int Id { get; set; }
[Navigate(nameof(User.RoleIds))]
public List<User> Users { get; set; }
}
```
more.. [#1145](https://github.com/dotnetcore/FreeSql/issues/1145)
## Naming convention (no need to specify Navigate)
Tip: You can understand the content of this section a little. It is not necessary to master it. You can skip it.
```csharp
class Group
{
public int Id { get; set; } //Id、GroupId、Group_id
public List<User> AUsers { get; set; }
public List<User> BUsers { get; set; }
public int ParentId { get; set; } //ParentId、Parent_id
public Group Parent { get; set; }
public List<Group> Childs { get; set; }
}
class User
{
public int Id { get; set; } //Id、UserId、User_id
public UserExt Ext { get; set; }
public int AGroupId { get; set; }
public Group AGroup { get; set; }
public int BGroupId { get; set; }
public Group BGroup { get; set; }
public List<Role> Roles { get; set; }
}
class UserExt
{
public int UserId { get; set; }
public User User { get; set; }
}
```
[《How to add data in one-to-one mode?》](https://github.com/2881099/FreeSql/issues/45)
### Many-to-One
```csharp
class Group
class Role
{
public int Id { get; set; } //Id、GroupId、Group_id
}
class User
{
public int Id { get; set; } //Id、UserId、User_id
public int AGroupId { get; set; }
public Group AGroup { get; set; }
public int BGroupId { get; set; }
public Group BGroup { get; set; }
}
```
### One-to-Many
```csharp
class Group
{
public int Id { get; set; } //Id、GroupId、Group_id
public ICollection<User> AUsers { get; set; }
public ICollection<User> BUsers { get; set; }
}
class User
{
public int Id { get; set; } //Id、UserId、User_id
public int AGroupId { get; set; }
public Group AGroup { get; set; }
public int BGroupId { get; set; }
public Group BGroup { get; set; }
}
```
[《How to add data in one-to-many mode?》](https://github.com/2881099/FreeSql/issues/46)
### Parent and Children
```csharp
class Group
{
public int Id { get; set; } //Id、GroupId、Group_id
public int ParentId { get; set; } //ParentId、Parent_id
public Group Parent { get; set; }
public ICollection<Group> Childs { get; set; }
}
```
The parent-children relationship is similar to One-to-Many mode. You can also refer to this link:
[《How to add data in one-to-many mode?》](https://github.com/2881099/FreeSql/issues/46)
### Many-to-Many
```csharp
class Song
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
class Song_tag
{
public int Song_id { get; set; }
public virtual Song Song { get; set; }
public int Tag_id { get; set; }
public virtual Tag Tag { get; set; }
}
class Tag
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public string Name { get; set; }
public int? Parent_id { get; set; }
public virtual Tag Parent { get; set; }
public List<User> Users { get; set; }
}
class UserRole
{
public int UserId { get; set; }
public User User { get; set; }
public virtual ICollection<Song> Songs { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public int RoleId { get; set; }
public Role Role { get; set; }
}
```
Song, Tag, Song_tag, these three entities use the four relationships: OneToMany, ManyToOne, Parent, and ManyToMany.
## Reference
- [《CodeFirst Mode, Part 1: Entity Attributes》](Entity-Attributes)

@@ -35,7 +35,7 @@
* [FluentApi](FluentApi)
* [自定义特性](%e8%87%aa%e5%ae%9a%e4%b9%89%e7%89%b9%e6%80%a7)
* [类型映射](%e7%b1%bb%e5%9e%8b%e6%98%a0%e5%b0%84)
* [导航配置](%e5%ae%9e%e4%bd%93%e5%85%b3%e7%b3%bb) [Entity Relationship](Entity-Relationship) ✨
* [导航属性](%e5%ae%9e%e4%bd%93%e5%85%b3%e7%b3%bb) [Entity Relationship](Entity-Relationship) ✨
* [迁移结构](CodeFirst#%e8%bf%81%e7%a7%bb%e7%bb%93%e6%9e%84)
* [DbFirst](DbFirst)
* [表达式函数](%e8%a1%a8%e8%be%be%e5%bc%8f%e5%87%bd%e6%95%b0)

@@ -1,37 +1,43 @@
**中文** | [English](Entity-Relationship)
导航属性是 FreeSql 的特色功能之一,可通过约定配置、或自定义配置对象间的关系。
导航属性是 FreeSql 的特色功能之一,提供 OneToMany, ManyToOne, ManyToMany, OneToOne, Parent, [PgArrayToMany](https://www.cnblogs.com/FreeSql/p/16351417.html) 六种关系。
导航属性有 OneToMany, ManyToOne, ManyToMany, OneToOne, Parent, [PgArrayToMany](https://www.cnblogs.com/FreeSql/p/16351417.html) 六种配置关系。
导航属性能干什么?
有了导航属性多表查询会非常方便lambda 表达式中直接使用导航对象点点点,舒服!!
- [《多表查询》](%e5%a4%9a%e8%a1%a8%e6%9f%a5%e8%af%a2) Where(a => a.Parent.Parent.Name == "xx")
- [《贪婪加载》](%e8%b4%aa%e5%a9%aa%e5%8a%a0%e8%bd%bd) Include/IncludeMany
- [《延时加载》](%e5%bb%b6%e6%97%b6%e5%8a%a0%e8%bd%bd)
- [《树表查询》](%e6%9f%a5%e8%af%a2%e7%88%b6%e5%ad%90%e5%85%b3%e7%b3%bb)
- [《级联保存》](%e8%81%94%e7%ba%a7%e4%bf%9d%e5%ad%98)
- [《级联删除》](%E8%81%94%E7%BA%A7%E5%88%A0%E9%99%A4)
- 可约定(命名约定),可不约定(需指定 Navigate 特性关联);
- 无关联的,查询时可以指明 On 条件LeftJoin(a => a.Parent.Id == a.ParentId)
- 已关联的直接使用导航对象就行On 条件会自动附上;
导航属性进行多表查询非常方便lambda 表达式中直接使用导航对象点点点,舒服!!
[《导航属性【到底】可以解决什么问题?》](https://www.cnblogs.com/kellynic/p/13575053.html)
# 自定义配置
> 预热说明:导航属性加载,因为要解决死循环引用问题,当引用关系很复杂的时候,有可能导致首次使用导航属性失败,第二次就可以了。解决办法可以程序启动时就预热所有实体类,循环执行 fsql.Select\<object\>().AsType(实体类);
OneToMany/ManyToMany 集合导航属性支持的类型ICollection\<T\>、List\<T\>、ObservableCollection\<T\>
# 自定义导航关系
OneToMany/ManyToMany 支持的类型ICollection\<T\>、List\<T\>、ObservableCollection\<T\>
```csharp
//导航属性,OneToMany
[Navigate(nameof(song_tag.song_id))]
public virtual List<song_tag> Obj_song_tag { get; set; }
//在 song_tag 查找 song_id 属性,与 本实体.主键 关联
//OneToMany
class Group
{
[Navigate(nameof(User.GroupId))]
public List<User> Users { get; set; }
//在 User 查找 GroupId 属性,与 本实体.主键 关联
}
//导航属性ManyToOne/OneToOne
[Navigate(nameof(song_id))]
public virtual Song Obj_song { get; set; }
//在 本实体 查找 song_id 属性,与 Song.主键 关联
//ManyToOne
class User
{
public int GroupId { get; set; }
[Navigate(nameof(GroupId))]
public Group Group { get; set; }
//在 本实体 查找 GroupId 属性,与 Group.主键 关联
}
//导航属性,ManyToMany
[Navigate(ManyToMany = typeof(tag_song))]
public virtual List<tag> tags { get; set; }
//ManyToMany
[Navigate(ManyToMany = typeof(TagSong))]
public List<Tag> Items { get; set; }
```
---
@@ -39,27 +45,37 @@ public virtual List<tag> tags { get; set; }
也可以使用 FluentApi 在外部设置导航关系:
```csharp
fsql.CodeFirst.ConfigEntity<实体类>(a => a
.Navigate(b => b.roles, null, typeof(多对多中间实体类))
fsql.CodeFirst.ConfigEntity<T>(a => a
.Navigate(b => b.roles, null, typeof(TMid))
.Navigate(b => b.users, "uid")
);
```
优先级,特性 > FluentApi
> 注意:
1、属性设置 Column(IsIgnore = true) 后,导航属性会失效
2、Navigate 设置的字符串是 类属性名,不是表 字段名!!!
> 扩展:
> 预热说明:导航属性配置的加载,因为要解决死循环引用,当相互引用关系很复杂的时候,可能导致首次使用导航属性失败,第二次就可以了。解决办法可以程序启动时就预热所有实体类,循环执行 fsql.Select\<object\>().AsType(实体类);
- [如何建立非主键导航属性 OneToOne/ManyToOne](https://github.com/dotnetcore/FreeSql/issues/604)
# 与非主键关联
```csharp
//OneToMany
[Navigate(nameof(TagSong.SongId), TempPrimary = nameof(Code))]
public List<TagSong> Item { get; set; }
//ManyToOne
[Navigate(nameof(SongId), TempPrimary = nameof(Song.Code))]
public Song Item { get; set; }
```
Non-primary key association rights support OneToMany/ManyToOne relationships and can only be valid when querying. (Cascade saving and deletion are not supported)
# 检测导航属性
如何检测一个导航属性是否配置生效:
如何检测一个导航属性是否效:
```csharp
var tbref = fsql.CodeFirst
@@ -69,118 +85,77 @@ var tbref = fsql.CodeFirst
GetTableRef(string propertyName, bool isThrow);
# PgArrayToMany
# 约定命名(无须指明 Navigate
### OneToOne 一对一
```csharp
class User
{
public int Id { get; set; }
public UserExt Ext { get; set; }
public int[] RoleIds { get; set; }
[Navigate(nameof(RoleIds))]
public List<Role> Roles { get; set; }
}
class Role
{
public int Id { get; set; }
[Navigate(nameof(User.RoleIds))]
public List<User> Users { get; set; }
}
```
更多资料:[#1145](https://github.com/dotnetcore/FreeSql/issues/1145)
# 约定命名(无须指明 Navigate
提示:本节内容稍微了解即可,不是必须掌握的,可以跳过。
```csharp
class Group
{
public int Id { get; set; } //Id、GroupId、Group_id
public List<User> AUsers { get; set; }
public List<User> BUsers { get; set; }
public int ParentId { get; set; } //ParentId、Parent_id
public Group Parent { get; set; }
public List<Group> Childs { get; set; }
}
class User
{
public int Id { get; set; } //Id、UserId、User_id
public UserExt Ext { get; set; }
public int AGroupId { get; set; }
public Group AGroup { get; set; }
public int BGroupId { get; set; }
public Group BGroup { get; set; }
public List<Role> Roles { get; set; }
}
class UserExt
{
public int UserId { get; set; }
public User User { get; set; }
}
```
[《OneToOne 一对一,怎么添加数据?》](https://github.com/2881099/FreeSql/issues/45)
### ManyToOne 多对一
```csharp
class Group
class Role
{
public int Id { get; set; } //Id、GroupId、Group_id
}
class User
{
public int Id { get; set; } //Id、UserId、User_id
public int AGroupId { get; set; }
public Group AGroup { get; set; }
public int BGroupId { get; set; }
public Group BGroup { get; set; }
}
```
### OneToMany 一对多
```csharp
class Group
{
public int Id { get; set; } //Id、GroupId、Group_id
public ICollection<User> AUsers { get; set; }
public ICollection<User> BUsers { get; set; }
}
class User
{
public int Id { get; set; } //Id、UserId、User_id
public int AGroupId { get; set; }
public Group AGroup { get; set; }
public int BGroupId { get; set; }
public Group BGroup { get; set; }
}
```
[《OneToMany 一对多,怎么添加数据?》](https://github.com/2881099/FreeSql/issues/46)
### Parent 父子
```csharp
class Group
{
public int Id { get; set; } //Id、GroupId、Group_id
public int ParentId { get; set; } //ParentId、Parent_id
public Group Parent { get; set; }
public ICollection<Group> Childs { get; set; }
}
```
父子关系,与一对多其实差不多,添加数据参数上面的连接;
### ManyToMany 多对多
```csharp
class Song
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
class Song_tag
{
public int Song_id { get; set; }
public virtual Song Song { get; set; }
public int Tag_id { get; set; }
public virtual Tag Tag { get; set; }
}
class Tag
{
[Column(IsIdentity = true)]
public int Id { get; set; }
public string Name { get; set; }
public int? Parent_id { get; set; }
public virtual Tag Parent { get; set; }
public List<User> Users { get; set; }
}
class UserRole
{
public int UserId { get; set; }
public User User { get; set; }
public virtual ICollection<Song> Songs { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public int RoleId { get; set; }
public Role Role { get; set; }
}
```
Song、Tag、Song_tag这三个实体使用了 OneToMany、ManyToOne、Parent、ManyToMany 4种关系。
- [《实体特性说明》](%e5%ae%9e%e4%bd%93%e7%89%b9%e6%80%a7)
- [《FluentApi享受纯净实体类》](FluentApi)