I need SQLite's glob
function in a (C#) method which has to return Expression<Func<RandomEntity, bool>>
-- and I need glob
because only then the index is used (already checked using EXPLAIN QUERY PLAN [..]
).
So I added the following function mapping to <edmx:StorageModels><Schema>
(SSDL):
<Function Name="glob" Aggregate="false" BuiltIn="true" NiladicFunction="false" IsComposable="true" ReturnType="bit">
<Parameter Name="pattern" Mode="In" Type="nvarchar"/>
<Parameter Name="target" Mode="In" Type="nvarchar"/>
</Function>
and a C# stub method:
public static class SQLiteFunctions
{
[DbFunction("Model.Store", "glob")]
public static bool Glob(string pattern, string target)
{
throw new NotImplementedException("Only exists for IQueryable/Expression<Func<T,bool>>!");
}
}
and the usage (just a sample producing tiny SQL):
var count = context.Users.Count(u => SQLiteFunctions.Glob("admin*", u.Name));
While this works the resulting SQL uses a "silly" = 1
comparison since the model only knows the datatype "bit" (which is 0 or 1) and I found no real boolean type although the native glob function seems to be true boolean:
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
Count([Filter1].[A1]) AS [A1]
FROM ( SELECT
1 AS [A1]
FROM [Users] AS [Extent1]
WHERE (glob('admin*', [Extent1].[Name])) = 1
) AS [Filter1]
) AS [GroupBy1]
The problem: This way (with "= 1") SQLite doesn't use the index and the query is badly slow. If I remove the 3 characters "= 1" the query becomes fast and also the query plan changes from "SCAN TABLE" to "SEARCH TABLE USING INDEX".
Any ideas how to make the function a true boolean function?
Addition:
I also tried using the CSDL section ():
<Function Name="GlobMatch" ReturnType="Edm.Boolean">
<Parameter Name="globPattern" Type="Edm.String" />
<Parameter Name="target" Type="Edm.String" />
<DefiningExpression> glob(globPattern, target) </DefiningExpression>
</Function>
and
[DbFunction("DoPiMo", "GlobMatch")]
public static bool Glob2(string globPattern, string target)
{
throw new NotImplementedException("Only exists for IQueryable/Expression<Func<T,bool>>!");
}
but this only produces a runtime error saying "glob" is not defined/known.
When doing a prefix search with LIKE or GLOB, SQLite rewrites it as two comparisons.
You could just write the two comparisons directly:
u.Name >= "admin" and u.Name < "admio"
来源:https://stackoverflow.com/questions/24043080/using-the-glob-function-in-linq-to-entities