在linq2db v5.3.2查询中使用自定义方法的问题求助
问题描述
使用linq2db 5.3.2版本(因关联其他应用无法升级,版本兼容问题会引发故障),需要在查询中实现去除字符串非数字字符的逻辑(例如将"A54778"转换为54778),用于表关联。
尝试过两种方案均失败:
- 自定义
ExtractNumber方法,linq2db无法解析该方法导致报错 - 使用
Regex.Replace(d.InvoiceNumber.ToString(), "[^0-9]", ""),同样无法被linq2db转换为SQL
若先将所有数据加载到内存中,自定义方法可正常运行,但数据量极大,这种方式性能极差。
需求:修复查询,最好无需创建数据库端函数。
原代码如下:
public void InsertPayoffData() { using var db = _db(); var query = from p in db.Payoff1C join d in db.Debit on new { Number = p.InvoiceNumber, Date = p.InvoiceDate } equals new { Number = ExtractNumber(d.InvoiceNumber.ToString()), Date = d.InvoiceDate } join k in db.Kredit on new { Number = p.PayDocNumber.ToString(), Date = p.PayDocDate } equals new { Number = ExtractNumber(k.PayDocNumber.ToString()), Date = k.PayDocDate } where !db.Payoff.Any(pf => pf.Debit_ID == d.DebitId && pf.Kredit_ID == k.Kredit_ID) select new Payoff { Debit_ID = d.DebitId, Kredit_ID = k.Kredit_ID, PayoffDate = p.PayDocDate, PayoffSum = p.PayOffSum, PayoffType = 0 }; var result = query.ToList(); db.BulkInsert(result); } private int ExtractNumber(string value) { if (string.IsNullOrEmpty(value)) { return 0; } var numberString = new string(value.Where(char.IsDigit).ToArray()); return int.TryParse(numberString, out int result) ? result : 0; }
解决方案
针对你的情况,有两种无需创建数据库端函数的方案,适配linq2db 5.3.2版本:
方案一:使用linq2db内置函数实现数字提取
利用linq2db提供的Sql.CharIsDigit(判断字符是否为数字)和Sql.TryConvert(安全类型转换)函数,将自定义逻辑转换为linq2db可解析的表达式,从而在数据库端执行过滤和转换,无需加载全量数据到内存。
修改后的代码如下:
public void InsertPayoffData() { using var db = _db(); var query = from p in db.Payoff1C join d in db.Debit on new { Number = p.InvoiceNumber, Date = p.InvoiceDate } equals new { Number = Sql.TryConvert<int>( new string(d.InvoiceNumber.Where(c => Sql.CharIsDigit(c)).ToArray()), 0 ), Date = d.InvoiceDate } join k in db.Kredit on new { Number = p.PayDocNumber.ToString(), Date = p.PayDocDate } equals new { Number = Sql.TryConvert<int>( new string(k.PayDocNumber.Where(c => Sql.CharIsDigit(c)).ToArray()), 0 ), Date = k.PayDocDate } where !db.Payoff.Any(pf => pf.Debit_ID == d.DebitId && pf.Kredit_ID == k.Kredit_ID) select new Payoff { Debit_ID = d.DebitId, Kredit_ID = k.Kredit_ID, PayoffDate = p.PayDocDate, PayoffSum = p.PayOffSum, PayoffType = 0 }; var result = query.ToList(); db.BulkInsert(result); }
说明:
Sql.CharIsDigit会被linq2db转换为对应数据库的字符判断逻辑(如SQL Server的LIKE '[0-9]',MySQL的REGEXP '^[0-9]$')Sql.TryConvert<int>(..., 0)确保当过滤后的字符串无法转换为整数时,返回默认值0,避免转换错误
方案二:调用数据库原生正则替换函数(依赖数据库支持)
如果你的数据库版本支持正则表达式替换(例如SQL Server 2017+的REGEXP_REPLACE、PostgreSQL的regexp_replace),可以直接通过linq2db的Sql.Functions.RegExpReplace调用数据库原生函数,简化数字提取逻辑:
以SQL Server为例,修改关联条件中的Number部分:
Number = Sql.TryConvert<int>(Sql.Functions.RegExpReplace(d.InvoiceNumber.ToString(), "[^0-9]", ""), 0)
注意:需要确认你的数据库版本支持该正则替换函数,且linq2db 5.3.2对应该数据库的提供商已实现该函数的映射。
内容的提问来源于stack exchange,提问作者user19627737
相关产品推荐
相关产品推荐

