得到";源“;的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
如何进行字符串化?
自己找到了答案。我发现它时很琐碎。
选项1
信用给@sxanatos
使用LINQ to Entities中的IQueryable.ToString()
方法来获取已格式化的精确SQL查询。
- Pro:这是将要启动的确切查询
- Con:由于使用了多个别名或嵌套
SELECT
的组合,因此查询的可读性较差
选项2
使用IQueryable.Expression
属性获取查询表达式。然后讨论了如何在堆栈溢出中字符串化Expression<Func<?,?>>
的问题
- Pro:如果你使用一个好的格式化程序,你会得到更好的可读性
- Con:它可能与在数据集上运行的确切SQL查询不同