我在SQL中有一个过程,我试图转化为Linq:
SELECT O.Id, O.Name as Organization
FROM Organizations O
JOIN OrganizationsHierarchy OH ON O.Id=OH.OrganizationsId
where OH.Hierarchy like '%/12/%'
我最关心的一行是:
where OH.Hierarchy like '%/12/%'
我有一个存储层次结构的列,例如/1/3/12/,所以我只使用%/12/%来搜索它。
我的问题是,Linq或。net中使用百分号的等价是什么?
Contains在Linq中使用,就像like在SQL中使用一样。
string _search="/12/";
……
.Where(s => s.Hierarchy.Contains(_search))
你可以像下面这样在Linq中编写SQL脚本:
var result= Organizations.Join(OrganizationsHierarchy.Where(s=>s.Hierarchy.Contains("/12/")),s=>s.Id,s=>s.OrganizationsId,(org,orgH)=>new {org,orgH});
我假设您正在使用Linq-to-SQL*(参见下面的注释)。如果是,使用字符串。包含字符串。StartsWith和字符串。EndsWith生成使用SQL LIKE操作符的SQL。
from o in dc.Organization
join oh in dc.OrganizationsHierarchy on o.Id equals oh.OrganizationsId
where oh.Hierarchy.Contains(@"/12/")
select new { o.Id, o.Name }
or
from o in dc.Organization
where o.OrganizationsHierarchy.Hierarchy.Contains(@"/12/")
select new { o.Id, o.Name }
注意:* =如果您正在使用ADO。Net实体框架(EF / L2E),注意它不会像Linq-to-SQL那样进行相同的转换。尽管L2S进行了适当的转换,但L2E v1(3.5)将转换为一个t-sql表达式,该表达式将强制对您正在查询的表进行全表扫描,除非在where子句或连接筛选器中有其他更好的鉴别器。
更新:在EF/L2E v4 (.net 4.0)中修正了这个问题,所以它会像L2S一样生成一个SQL LIKE。
如果您需要为单元测试这样的客户端操作提供LIKE功能,CodeProject的这个方法很好地模拟了通配符的行为。
有点像@史蒂夫·阿克曼的回答,但更全面。
/// Published on CodeProject: http://www.codeproject.com/Articles/
/// 608266/A-Csharp-LIKE-implementation-that-mimics-SQL-LIKE
/// </remarks>
public static bool Like(this string s, string match, bool CaseInsensitive = true)
{
//Nothing matches a null mask or null input string
if (match == null || s == null)
return false;
//Null strings are treated as empty and get checked against the mask.
//If checking is case-insensitive we convert to uppercase to facilitate this.
if (CaseInsensitive)
{
s = s.ToUpperInvariant();
match = match.ToUpperInvariant();
}
//Keeps track of our position in the primary string - s.
int j = 0;
//Used to keep track of multi-character wildcards.
bool matchanymulti = false;
//Used to keep track of multiple possibility character masks.
string multicharmask = null;
bool inversemulticharmask = false;
for (int i = 0; i < match.Length; i++)
{
//If this is the last character of the mask and its a % or * we are done
if (i == match.Length - 1 && (match[i] == '%' || match[i] == '*'))
return true;
//A direct character match allows us to proceed.
var charcheck = true;
//Backslash acts as an escape character. If we encounter it, proceed
//to the next character.
if (match[i] == '\\')
{
i++;
if (i == match.Length)
i--;
}
else
{
//If this is a wildcard mask we flag it and proceed with the next character
//in the mask.
if (match[i] == '%' || match[i] == '*')
{
matchanymulti = true;
continue;
}
//If this is a single character wildcard advance one character.
if (match[i] == '_')
{
//If there is no character to advance we did not find a match.
if (j == s.Length)
return false;
j++;
continue;
}
if (match[i] == '[')
{
var endbracketidx = match.IndexOf(']', i);
//Get the characters to check for.
multicharmask = match.Substring(i + 1, endbracketidx - i - 1);
//Check for inversed masks
inversemulticharmask = multicharmask.StartsWith("^");
//Remove the inversed mask character
if (inversemulticharmask)
multicharmask = multicharmask.Remove(0, 1);
//Unescape \^ to ^
multicharmask = multicharmask.Replace("\\^", "^");
//Prevent direct character checking of the next mask character
//and advance to the next mask character.
charcheck = false;
i = endbracketidx;
//Detect and expand character ranges
if (multicharmask.Length == 3 && multicharmask[1] == '-')
{
var newmask = "";
var first = multicharmask[0];
var last = multicharmask[2];
if (last < first)
{
first = last;
last = multicharmask[0];
}
var c = first;
while (c <= last)
{
newmask += c;
c++;
}
multicharmask = newmask;
}
//If the mask is invalid we cannot find a mask for it.
if (endbracketidx == -1)
return false;
}
}
//Keep track of match finding for this character of the mask.
var matched = false;
while (j < s.Length)
{
//This character matches, move on.
if (charcheck && s[j] == match[i])
{
j++;
matched = true;
break;
}
//If we need to check for multiple charaters to do.
if (multicharmask != null)
{
var ismatch = multicharmask.Contains(s[j]);
//If this was an inverted mask and we match fail the check for this string.
//If this was not an inverted mask check and we did not match fail for this string.
if (inversemulticharmask && ismatch ||
!inversemulticharmask && !ismatch)
{
//If we have a wildcard preceding us we ignore this failure
//and continue checking.
if (matchanymulti)
{
j++;
continue;
}
return false;
}
j++;
matched = true;
//Consumse our mask.
multicharmask = null;
break;
}
//We are in an multiple any-character mask, proceed to the next character.
if (matchanymulti)
{
j++;
continue;
}
break;
}
//We've found a match - proceed.
if (matched)
{
matchanymulti = false;
continue;
}
//If no match our mask fails
return false;
}
//Some characters are left - our mask check fails.
if (j < s.Length)
return false;
//We've processed everything - this is a match.
return true;
}