.NET Core中的动态Linq表达式

如何解决.NET Core中的动态Linq表达式

第一

我不能为此使用实体框架,因为这是一个桌面应用程序,在该应用程序中,所有数据库调用都首先通过Web API端点进行路由。此应用程序的要求是所有流量都是HTTP流量。

设置

可以说我有一个IEnumerable<BaseItem>,其中BaseItem是所有模型都继承的基础模型类。

我们还使用与查询返回的System.DataTable的列名和适当的数据类型相匹配的动态类和动态属性动态实例化并填充我们的集合。

这些动态类也继承自BaseItem,它公开了两个功能:
void SetValue(string PropertyName,object Value)
object GetValue(string PropertyName)

问题

我需要能够编写一个linq查询,该查询根据查询所引用表中的唯一约束列从集合中动态选择属性。我将其放在单独的列表中,其中字符串值与动态类上的动态属性名称匹配。

我知道解决方案可能是使用表达式树,但是我发现的所有示例都使用实际的属性引用,在这种情况下,只能通过GetValue和SetValue函数访问和设置属性。我很难解决这个问题。下面是我想要的linq查询的示例,如果我知道要返回的字段。

var Result = DefaultCollection.Select(x => new { BUSINESS_UNIT = x.GetValue("BUSINESS_UNIT"),FISCAL_YEAR = x.GetValue("FISCAL_YEAR") }).Distinct();

同样,我无法编写此代码,因为在运行时可以引用UniqueConstraint列表之前,我不知道需要在选择列表中包括哪些字段。

如何使用Linq Expression动态实现此目标?

从头开始示例

您已经从IEnumerable<BaseItem>中填充了System.Data.DataTable,可以说您运行的查询是SELECT FIRST_NAME,LAST_NAME,EMAIL ADDRESS FROM TABLEA

IEnumerable是通过自动化来填充的,其中会生成一个动态类来表示表中的一行,并将动态属性添加到该类中以表示表中的每一列。

通过BaseItem string result = item.GetValue("FIRST_NAME")item.SetValue("EMAIL_ADDRESS","MyEmail@Email.com');中的方法访问属性

您还查询了INFORMATION_SCHEMA以获取对TABLEA的唯一约束,并将其存储在public class ConstraintModel对象中,该对象具有public List<string> Columns代表组成唯一约束的列。在这种情况下,约束中只有一列“ EMAIL_ADDRESS”。

如果用户对IEnumerable<BaseItem>进行了更改,并且有多个项目具有相同的EMAIL_ADDRESS,则我们知道返回到SQL的UPDATE语句将失败。我们要检查是否没有违反唯一约束。

在用户保存时,我们要在内存中的IEnumerable对象上运行“验证检查”。理想情况下,我们将编写一个linq查询,由essentailly执行SELECT SUM(1),即EMAIL_ADDRESS GROUP BY EMAIL_ADDRESS,如果任何结果的值均大于1,则说明存在重复项。显然,上面的语法不是SQL的Linq,而是您的主意。我不知道在编译时需要在BaseItem上的哪些字段进行选择和分组,因此我需要利用Constraint.Columns对象并使用它编写动态linq表达式。此外,动态类的属性仅通过它们从BaseItem ... GetValue(string PropertyName)SetValue(string PropertyName,object Value)继承的方法公开;

解决方法

使用我的EnumerableExpressionExt类的简化版本来帮助为Expression表达式和Enumerable助手类构建ExpressionExt树:

public static class EnumerableExpressionExt {
    private static Type TEnumerable = typeof(Enumerable);
    private static Type TypeGenArg(this Expression e,int n) => e.Type.GetGenericArguments()[n];

    public static MethodCallExpression Distinct(this Expression src) => Expression.Call(TEnumerable,"Distinct",new[] { src.TypeGenArg(0) },src);

    public static MethodCallExpression Select(this Expression src,LambdaExpression resultFne) => Expression.Call(TEnumerable,"Select",new[] { src.TypeGenArg(0),resultFne.ReturnType },src,resultFne);
}

public static class ExpressionExt {
    public static ConstantExpression AsConst<T>(this T obj) => Expression.Constant(obj,typeof(T));

    public static LambdaExpression AsLambda(this Expression body) => Expression.Lambda(body);
    public static LambdaExpression Lambda(this ParameterExpression p1,Expression body) => Expression.Lambda(body,p1);

    public static NewExpression New(this Type t,params Expression[] vals) => Expression.New(t.GetConstructors()[0],vals,t.GetProperties());    

    public static ParameterExpression Param(this Type t,string pName) => Expression.Parameter(t,pName);
}

您可以创建自定义Expression扩展名以翻译您的特定方法:

public static class MyExpressionExt {
    public static MethodCallExpression GetValue(this Expression obj,string propName) =>
        Expression.Call(obj,"GetValue",null,propName.AsConst());
}

您可以按以下步骤构建Expression树:

// BaseItem x
var xParm = typeof(BaseItem).Param("x");
// typeof(new { object BUSINESS_UNIT,object FISCAL_YEAR })
var anonType = (new { BUSINESS_UNIT = default(object),FISCAL_YEAR = default(object) }).GetType();
// new { BUSINESS_UNIT = x.GetValue("BUSINESS_UNIT"),FISCAL_YEAR = x.GetValue("FISCAL_YEAR") }
var newExpr = anonType.New(xParm.GetValue("BUSINESS_UNIT"),xParm.GetValue("FISCAL_YEAR"));
// DefaultCollection.Select(x => newExpr)
var selExpr = DefaultCollection.AsConst().Select(xParm.Lambda(newExpr));
// DefaultCollection.Select(x => newExpr).Distinct()
var distExpr = selExpr.Distinct();

您可以像这样测试它(使用LINQPad的Dump方法):

// () => DefaultCollection.Select(x => newExpr).Distinct()
var f = distExpr.AsLambda();
var fc = f.Compile();    
fc.DynamicInvoke().Dump();

LINQPad对于输出编译器构建的Expression树并查看其构建方式,或IQueryable<T>查询Expression成员树也非常有帮助。

注意:我喜欢不必打电话给AsConst(),所以我还有其他帮助者,可以使之更容易。

,

在读完full example之后,听起来很像根本不需要动态linq来完成您想做的事情,但是您确实需要知道主键是什么,为什么不尝试一下呢?喜欢:

更新我已将一个简单的pk查找替换为一个功能性PK查找,以便可以使用复合主键。

List<string> primaryKeyColumns = new List<string> {"Id"}; // or whatever else
List<string> uniques = /// ConstraintModel.Columns;
List<BaseItem> baseItems = // your baseItems

// Now,wit
Func<BaseItem,BaseItem,bool> equalPrimaryKey = (left,right) => {
   foreach(var pk in primaryKeyColumns) {
     if(left.GetValue(pk) != right.GetValue(pk)) {
        return false;
     }
   }

   return true;
}; // or whatever else

var violators = baseItems.Select(x => {
   return baseItems.Any(z => {
      foreach(var unique in uniques) {
         if (z.GetValue(unique) == x.GetValue(unique) 
            && !equalPrimaryKey(x,z)) {
            return true;
         }
      }

     return false;
   });
}).ToArray();

这将为您提供一系列基础项目,这些基础项目可以被任何唯一列重复

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-