得到";源“;的LINQ查询并序列化为字符串

本文关键字:查询 序列化 LINQ 字符串 quot 得到 | 更新日期: 2023-09-27 17:59:01

此问题与正在进行进化维护的当前运行的应用程序有关。请允许我用几行字来解释它是如何工作的。

该应用程序使用182个SQL查询扫描从大文本文件导入的大型数据集,这些查询按设计存储在数据库中,经过加密。主要的缺点是,由于SQL Server和MySQL的语法差异,我们为它们维护单独的查询。

当前允许root用户查看编辑SQL文本。查看SQL以了解查询在某些特定情况下是如何工作的(我们很少被要求修改它们)是一项要求。

为了克服DBMS的差异,我试图将这些查询转换为LINQ,由于它们的复杂性,这本身就是一项困难的任务。但我的问题是另一个。

考虑到我正在创建一个IQuery接口,该接口运行EF DbContext的特定查询并扫描参数化,我仍然希望保留谓词的可读且可能格式化的字符串表示,以显示根用户(否则,我们的功能分析师将不得不期待我的邮件带有LINQ源代码)。

我做了一些家庭作业。我读过关于字符串化Expression树的可能性,这是一个很好的起点。问题是,创建一个接口方法Expression<Func<?,bool>> GetWhereExpression()(稍后用于构建查询)是不可行的,因为查询跨越6个主要实体和一些查找表。让我明确地说:我的想法是像这样构建IQueryable

IQuery theQueryToRun;
var query = from SectionA a in dataContext.sectionA
            join SectionD b in dataContext.sectionB on a.A03 equals b.A03
            .... join ....
query = query.Where(theQueryToRun.GetWhereClause(...));
query = query.OrderBy(theQueryToRun.GetOrderByClause());
return query.ToList();

但这种方法永远不会起作用,因为每个查询都在数据集的不同联接上运行(有些是内部联接,有些是左联接,并非所有6个实体都联接在所有查询中)。

我能做的最好的事情就是创建一个接口方法IQueryable<NormalizedRow> DoQuery(DbContext dbContext,...)。现在,从这里开始,有人知道在运行IQueryable对象之前将其字符串化的方法吗?我可以使用相同的对象来运行查询并获取数据集,或者显示其";来源";。

另一个选项是创建CCD_ 8属性;粘贴查询的C#源代码,这肯定会起作用。但我们是有创造力的开发人员,不是吗?

以下是SQL查询的示例(格式由Instant SQL Formatter提供),其中包含要替换的原始列名和占位符(无需编辑任何内容):

SELECT a.a01,a.a01a,a.a01b,a.a02,a.a03,a11,a12,a12a,a12b,a12c,a21,a22,a23,a24,
       a25,a31,
       a31a,a31b,a32,a33,a33a,a33b,a33c,a34,a41,a42,a43,a51,a52,a53,a54,a54a,
       a54b,a54c,b11,b12,b13,b14,b15,z0,a.prog,a.utente,c11,d11,d13,d14,d14a,
       d14b,d14c,d15,d16,d17,d18,d19,d21,d22,d23,d31,d32,d41,d42,d43,d44,d45,z1
FROM   sezione_a a
       INNER JOIN sezione_d d
               ON a.a03 = d.a03
                  AND a.utente = d.utente
WHERE  ( ( a.a42 IN ( '0', '1', '' )
           AND d.d21 IN(SELECT codice
                        FROM   sottogruppi
                        WHERE  flagpf = 1) )
         AND ( d.d11 IS NOT NULL
               AND d.d11 <> '' )
         AND d.d45 IN( '1', '2' )
         AND ( ( d.d18 >= '19000101'
                 AND d.d18 <= a.a21 )
               AND ( d.d43 >= '19000101'
                     AND d.d43 <= a.a21 )
               AND d.d43 >= d.d18 )
         AND ( Date_add(Str_to_date(d.d43, '%Y%m%d'), INTERVAL 10 year) >
                     Str_to_date(a.a21, '%Y%m%d')
               AND Date_add(Str_to_date(d.d43, '%Y%m%d'), INTERVAL 10 year) <
                   Now() ) )
   AND ( ( a.a21 BETWEEN '@@datamin' AND '@@datamax' )
         AND a.utente = @@user
         AND a.a52 NOT IN @@a52 )
ORDER  BY a.a11 ASC,d.d11 ASC 

返回列表的C#代码(信不信由你,它也会做同样的事情):

    public IList<QueryRow> Run(Models.auitool2014Entities dataContext, int aUserId, DateTime? dataInizioControllo, DateTime? dataFineControllo, string[] a52Exclude, bool codifiche2014, out long totalCount)
    {
        string sysdate10String = DateTime.Now.AddYears(-10).ToString("yyyyMMdd", CultureInfo.InvariantCulture);
        var inner = codifiche2014 ?
            from Sottogruppo sg in dataContext.sottogruppi where sg.flagpf select sg.codice :
            from Sottogruppo2015 sg in dataContext.sottogruppi2015 where sg.flagpf select sg.codice;
        var q = dataContext.sezione_a.Join(
                    dataContext.sezione_d,
                    a => new { A03 = a.A03, User = a.utente },
                    d => new { A03 = d.A03, User = d.utente },
                    (a, d) => new SezioneJoin { A = a, D = d }
                )
            .Where(x =>
                x.A.utente == aUserId && //Flusso utente
                new string[] { "0", "1", String.Empty }.Contains(x.A.A42) && // A42 IN (0,1,'')
                inner.Contains(x.D.D21) && //D.D21 IN (SELECT CODICE FROM SOTTOGRUPPPI.A..A. WHERE FLAGPF = 1)
                (x.D.D11 != null && x.D.D11 != String.Empty) && //D11 IS NOT NULL AND D11 <> ''
                new string[] { "1", "2" }.Contains(x.D.D45) && //D45 IN ('1','2')
                (
                    (x.D.D18.CompareTo("19000101") >= 0 && x.D.D18.CompareTo(x.A.A21) <= 0) && //D18 >= '1900101' AND D18 <= A21
                    (x.D.D43.CompareTo("19000101") >= 0 && x.D.D43.CompareTo(x.D.D18) >= 0) // D43 >= '19000101' AND D43 >= D18
                ) &&
                x.D.D43.CompareTo(sysdate10String) < 0 // D43 <= (SYSDATE() - 10 YEARS)
            );
        if (dataInizioControllo != null)
        {
            string dataInzio = dataInizioControllo.Value.ToString("yyyyMMdd");
            q = q.Where(x => x.A.A21.CompareTo(dataInzio) >= 0);
        }
        if (dataFineControllo != null)
        {
            string dataFine = dataFineControllo.Value.ToString("yyyyMMdd");
            q = q.Where(x => x.A.A21.CompareTo(dataFine) <= 0);
        }
        if (a52Exclude != null)
            q = q.Where(x => !a52Exclude.Contains(x.A.A52));
        q = q
            .OrderBy(x => x.A.A11)
            .OrderBy(x => x.D.D11);
        totalCount = q.Count();
        return q.Take(Parameters.ROW_LIMIT).Select(j => new QueryRow
        {
            A01 = j.A.A01,
            A01a = j.A.A01a,
            A01b = j.A.A01b,
            A02 = j.A.A02,
            A03 = j.A.A03,
            A11 = j.A.A11,
            A12 = j.A.A12,
            A12a = j.A.A12a,
            A12b = j.A.A12b,
            A12c = j.A.A12c,
            ....... redacted for brevity
            D43 = j.D.D43,
            D44 = j.D.D44,
            D45 = j.D.D45,
            Z1 = j.D.Z1
        }).ToList();
    }

我有一个连接SectionA和SectionE的几乎相同的查询,例如

把它包起来

我希望从IQueryable<>生成一个输出文本,该文本至少可读,并且包含至少,如果不是全部的话,以下子句

         (x)=>
                new string[] { "0", "1", String.Empty }.Contains(x.A.A42) &&
                inner.Contains(x.D.D21) &&
                (x.D.D11 != null && x.D.D11 != String.Empty) &&
                new string[] { "1", "2" }.Contains(x.D.D45) &&
                (
                    (x.D.D18.CompareTo("19000101") >= 0 && x.D.D18.CompareTo(x.A.A21) <= 0) && //D18 >= '1900101' AND D18 <= A21
                    (x.D.D43.CompareTo("19000101") >= 0 && x.D.D43.CompareTo(x.D.D18) >= 0) // D43 >= '19000101'
                ) &&
                x.D.D43.CompareTo("20050623") < 0

LINQ to Entities中的IQueryable如何进行字符串化?

得到";源“;的LINQ查询并序列化为字符串

自己找到了答案。我发现它时很琐碎。

选项1

信用给@sxanatos

使用LINQ to Entities中的IQueryable.ToString()方法来获取已格式化的精确SQL查询。

  • Pro:这是将要启动的确切查询
  • Con:由于使用了多个别名或嵌套SELECT的组合,因此查询的可读性较差

选项2

使用IQueryable.Expression属性获取查询表达式。然后讨论了如何在堆栈溢出中字符串化Expression<Func<?,?>>的问题

  • Pro:如果你使用一个好的格式化程序,你会得到更好的可读性
  • Con:它可能与在数据集上运行的确切SQL查询不同