使用 ExpressionMove 对表达式参数进行替换

    xiaoxiao2023-10-29  157

    一,为什么表达式的参数需要替换?     在使用领域模型编程时,我们的领域模型经常和数据模型是不一样的。领域模型最为贴近业务,数据模型反应的是数据库表。这二者的不一致经常给我们带来代码的复杂化。在模型的转换上,我们有 AutoMapper 这样的工具进行转换。 在查询时,领域模型的查询表达式是不能直接给数据模型进行查询的,我也没有找到有谁造过这样的轮子。     比如,我们现在有领域模型 Model1 代码如下:

    public class Model1 { public int Id { get; set; } public string Name { get; set; } public DateTime CreateTime { get; set; } public string[] Values { get; set; }//["a", "b", "c"] public int[] IntValues { get; set; }//[1, 2, 3] }

        有数据模型 Model4 代码如下:

    public class Model4 { public int id { get; set; } public string Name { get; set; } public string values { get; set; }//",a,b,c," public string intvalues { get; set; }//",1,2,3," }

        我在领域模型里写了一个查询表达式 Expression<Func<Model1, bool>> x => x.Name == "产品1" 如果想在数据库里查询,这个表达式是不行的。因为数据的模型是 Model4 ,即使字段名一样,也查不了。     通常的解决办法是不使用表达式,使用一个类来做为查询的模型。

    public class QueryModel { public int Id { get; set; } public string Key { get; set; } }

        然后在数据模型所在项目(一般是基础设施层)进行表达式或SQL语句的拼接。

    public List<Model4> Query(QueryModel queryModel) { Expression<Func<Model4, bool>> expression = null; if (queryModel.Id > 0) expression = x => x.id == queryModel.Id; if (!string.IsNullOrEmpty(queryModel.Key)) expression = expression.And(x => x.Name.Contains(queryModel.Key)); return Where(expression); }

        然而,这样的坏处也是显而易见的,增加了复杂度,而且在添加一个查询字段、或一个字段的查询方式时,都需要对查询模型进行修改。比如上边的例子中如果想增加个按 [不等于某个Id] 进行查询的需求,那 QueryModel 要增加字段, 查询方法里需要再拼接一段。     这时如果能有一个工具对表达式进行转换的话,那就方便太多了。然后 ExpressionMove 就诞生了。

    二,ExpressionMove 使用方法。     项目地址 : https://github.com/zl33842901/ExpressionMove     Nuget 关键字: xLiAd.ExpressionMove     具体使用方法可参见项目里的 UnitTest,在两个模型完全一样时(或者说用于查询的字段完全一样时),只需要用你的表达式 .BuildMover().MoveTo<Model2>() 就能实现转换了。如下:

    public class Model2 { public int Id { get; set; } public string Name { get; set; } public DateTime CreateTime { get; set; } } Expression<Func<Model1, bool>> expression = x => x.Id == 5 && x.Name == "a" && x.CreateTime < DateTime.Now; Expression<Func<Model2, bool>> result = expression.BuildMover().MoveTo<Model2>();

        当模型的一些字段类型一样,名称不一样时,可以使用配置类进行配置,然后在使用时进行转换。

    public class Model3 { public int id { get; set; } public string MyName { get; set; } public DateTime CreateTime { get; set; } } private MoverTypeConfiguration Configuration { get; } = new MoverTypeConfiguration(x => { x.CreateMap<Model1, Model3>() .FieldMap(x => x.Id, x => x.id) .FieldMap(x => x.Name, x => x.MyName); }); Expression<Func<Model1, bool>> expression = x => x.Id == 5 && x.Name == "a" && x.CreateTime < DateTime.Now; var provider = Configuration.Build(); var result = provider.Load(expression).MoveTo<Model3>();

        这种情况也可以使用字段的名称特性标记来实现。

        当领域模型是数组,数据模型是逗号分隔的字符串时(SQLServer里常用,PG里不必了),ExpressionMove 现在只支持 Contains 方法。

    private MoverTypeConfiguration Configuration { get; } = new MoverTypeConfiguration(x => { x.CreateMap<Model1, Model4>() .FieldMap(x => x.Id, x => x.id) .FieldMap(x => x.IntValues, x => x.intvalues) .FieldMap(x => x.Values, x => x.values); }); Expression<Func<Model1, bool>> expression = x => x.Id == 5 && x.IntValues.Contains(6) && x.Values.Contains("b"); var provider = Configuration.Build(); var result = provider.Load(expression).MoveTo<Model4>();

        当然这种实现并不是很完善,在需要存取数组的场景下,推荐使用 PG(PostgreSql),我的另一个组件 xLiAd.DapperEx 有专门为PG写的仓储,可以支持数组字段的操作。       这样基本能覆盖90%以上的场景了,再也不用写 QueryModel 了。

    最新回复(0)