投影投影接口定义

投影接口定义

  • 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 User
2.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 User
2.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 User
2.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