投影投影接口定义
投影接口定义
- IProjection接口很简单
- 只有TryConvert一个方法
- 相同类型对象的转化,返回是否成功以及转化后的结果
- 约定不符合规则就不转化
- 一般情况下转化成功的结果会和源对象不同
- 当然经过多次投影后也可能会和源对象相同了
public interface IProjection<T> { bool TryConvert(T source, out T result); }2. 三种投影方式
2.1 使用前缀投影来演示
- 前缀投影就是把成员前面加个前缀来映射
- sourceMembers模拟对User的反射
- 通过Projection.Prefix("User")创建一个前缀投影
- 如果没有User前缀,就增加User
- 如果有User前缀就不处理
public record User(int Id, string UserName); public record UserDTO(int UserId, string UserName); var sourceMembers = new Dictionary<string, Func<User, object>>() { [nameof(User.Id)] = obj => obj.Id, [nameof(User.UserName)] = obj => obj.UserName }; var projection = Projection.Prefix("User");2.2 Filter投影的Case
2.2.1 csharp代码
IDictionary<string, Func<User, object>> result = projection.Filter(sourceMembers); Assert.Single(result); Assert.True(result.ContainsKey(nameof(UserDTO.UserId)));2.2.2 sql表示
SELECT Id AS UserId FROM User2.2.3 影像表示
User
Filter
Id
UserName
UserId
2.3 Through投影的Case
2.3.1 csharp代码
IDictionary<string, Func<User, object>> result = projection.Through(sourceMembers); Assert.Equal(sourceMembers.Count, result.Count); Assert.True(result.ContainsKey(nameof(UserDTO.UserId))); Assert.True(result.ContainsKey(nameof(UserDTO.UserName)));2.3.2 sql表示
SELECT Id AS UserId,UserName FROM User2.3.3 影像表示
User
Through
Through
Id
UserName
UserId
UserName
2.4 Cross投影的Case
- PocoEmit.Mapper重构用的就是Cross投影
2.4.1 csharp代码
IDictionary<string, Func<User, object>> result = projection.Cross(sourceMembers); Assert.Equal(3, result.Count); Assert.True(result.ContainsKey(nameof(User.Id))); Assert.True(result.ContainsKey(nameof(UserDTO.UserId))); Assert.True(result.ContainsKey(nameof(UserDTO.UserName)));2.4.2 sql表示
SELECT Id,UserName,Id AS UserId FROM User2.4.3 影像表示
User
Cross
Cross
Cross
Id
UserName
Id
UserName
UserId
3. 投影支持"横向"扩展
- 这里说的"横向"就是投影并联
- 把多个投影组合成多分支的映射规则
- 通过FirstReturn或ToFirstReturn方法实现
3.1 "横向"扩展的Case
// 包含User或U前缀就去掉前缀 var user = Projection.RemovePrefix("User"); var u = Projection.RemovePrefix("U"); var projection = Projection.FirstReturn(user, u); [Theory] [InlineData("UserId", "Id")] [InlineData("UId", "Id")] [InlineData("UUserName", "UserName")] [InlineData("UserUName", "UName")] public void TryConvert(string source, string expected) { projection.TryConvert(source, out var result); Assert.Equal(expected, result); }3.2 影像表示"横向"扩展
- RemoveUser和RemoveU两个并联的投影
- 并联投影按照顺序依次尝试
- 直到有一个投影成功了就返回结果
expected
source
RemoveUser
RemoveU
RemoveU
RemoveUser
UserId
UId
UUserName
UserUName
Id
UserName
UName
二、投影在PocoEmit.Mapper中的应用
1. AddPrefix
1.1 AddPrefix的Case
IMapper mapper = Mapper.Create(); mapper.ConfigureMap<AutoUserDTO, User>() .Source .AddPrefix("User"); var source = new AutoUserDTO { UserId = "222", UserName = "Jxj2" }; var converter = mapper.GetConverter<AutoUserDTO, User>(); var result = converter.Convert(source); Assert.NotNull(result); Assert.Equal(source.UserId, result.Id.ToString()); Assert.Equal(source.UserName, result.UserName);1.2 AddPrefix使用的是去除前缀投影(RemovePrefix)
- AddPrefix("User")会调用Projection.RemovePrefix("User")来创建一个去除前缀投影
- AutoUserDTO两个成员分别是UserId和UserName
- User两个成员分别是Id和UserName
- 对AutoUserDTO投影的效果相当于sql: SELECT UserId,UserName,UserId AS Id FROM AutoUserDTO
- 通过这个投影,两边的成员名就能完美匹配上了
1.3 影像演示一下这个过程
User
AutoUserDTO投影
AutoUserDTO
RemoveUser
RemoveUser
Mapping
Mapping
UserId
UserName
UserId
UserName
Id
Name
Id
UserName
1.4 使用前缀投影(Prefix)也可以实现类似的效果
- 通过Projection.Prefix("User")创建一个前缀投影
- 这时就需要对User进行投影
- 对User投影的效果相当于sql: SELECT Id,UserName,Id AS UserId FROM User
