获取LINQ查询的“源”并序列化为字符串

时间:2015-06-23 08:31:37

标签: c# linq entity-framework

此问题与正在进行进化维护的当前实时应用程序有关。请允许我花几行来解释它的工作原理。

应用程序使用182个SQL查询扫描从大文本文件导入的大型数据集,这些SQL查询按设计存储在数据库本身中,并进行加密。主要的缺点是我们正在为SQL Server和MySQL维护单独的查询,因为它们的语法不同。

目前,root用户可以查看编辑 SQL文本。查看SQL以了解查询如何适用于某些特定情况(我们很少被要求修改它们)是一项要求。

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

鉴于我正在创建一个IQuery接口来运行EF DbContext中的特定查询并扫描参数化,我仍然希望将谓词的可读且可能格式化的字符串表示形式保留到显示root用户(否则我们的功能分析师将不得不期望我的邮件使用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对象进行字符串化的方法?我可以使用相同的对象运行查询和获取数据集或显示其“源”。

另一种选择是创建一个string Source{ get; }属性,在其中我复制并粘贴查询的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字符串化?

1 个答案:

答案 0 :(得分:3)

自己找到答案。一旦我找到它就会琐碎。

选项1

归功于@xanatos

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

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

选项2

使用IQueryable.Expression属性获取查询表达式。然后,Stackoverflow

中广泛讨论了如何对Expression<Func<?,?>>进行字符串化的主题
  • Pro:如果你使用一个好的格式化程序,你会获得更好的可读性
  • Con:它可能与在数据集
  • 上运行的确切SQL查询不同