So I have this function:
public static BaseList GetVersesByChapterVerseRanges(string translation, BaseList criteriaList)
{
BaseList returnValue = new BaseList();;
if(criteriaList.Count() > 0)
{
List<VerseMaster.BusinessLayer.Vers> queryList = new List<Vers>();
int bookId = criteriaList[0].Book.BookId;
int StartChapter = criteriaList[0].StartChapter;
int EndChapter = criteriaList[0].EndChapter;
int StartVerse = criteriaList[0].StartChapterStartVerse;
int EndVerse = criteriaList[0].EndChapterEndVerse;
var searchQuery = (from v in VerseMaster.BusinessLayer.Controller.Controller.VerseMasterEntities.Verses where v.Translation.ToLower().StartsWith(translation.ToLower()) && v.BookId == bookId && v.ChapterNumber >= StartChapter && v.ChapterNumber <= EndChapter && (v.ChapterNumber == StartChapter ? (v.VerseNumber >= StartVerse) : true) && (v.ChapterNumber == EndChapter ? (v.VerseNumber <= EndVerse) : true) orderby v.ChapterNumber, v.VerseNumber ascending select v);
for(int i = 1; i < criteriaList.Count(); i++)
{
bookId = criteriaList[i].Book.BookId;
StartChapter = criteriaList[i].StartChapter;
EndChapter = criteriaList[i].EndChapter;
StartVerse = criteriaList[i].StartChapterStartVerse;
EndVerse = criteriaList[i].EndChapterEndVerse;
VerseMaster.BusinessLayer.DataObjects.Helper.VerseSearchCriteria criteria = criteriaList[i];
searchQuery = (System.Linq.IOrderedQueryable<VerseMaster.BusinessLayer.Vers>)searchQuery.Concat(from v in VerseMaster.BusinessLayer.Controller.Controller.VerseMasterEntities.Verses where v.Translation.ToLower().StartsWith(translation.ToLower()) && v.BookId == bookId && v.ChapterNumber >= StartChapter && v.ChapterNumber <= EndChapter && (v.ChapterNumber == StartChapter ? (v.VerseNumber >= StartVerse) : true) && (v.ChapterNumber == EndChapter ? (v.VerseNumber <= EndVerse) : true) orderby v.ChapterNumber, v.VerseNumber ascending select v);
}
string traceString = ((System.Data.Objects.ObjectQuery)searchQuery).ToTraceString();
foreach (var entVerse in searchQuery)
{
VerseMaster.BusinessLayer.DataObjects.IVerse verse = new VerseMaster.BusinessLayer.DataObjects.Verse();
LoadVerseFromEntityVerse(ref verse, entVerse);
Console.Write(String.Format("Chapter={0} Verse={1} Text={2}\n", entVerse.ChapterNumber, entVerse.VerseNumber, entVerse.Verse));
//returnValue.Add((VerseMaster.BusinessLayer.DataObjects.Verse)verse);
}
}
return returnValue;
}
So for some reason I had to convert each of the parameters that go into the linq query into standard ints because of an error, not sure if its related. So the generated sql is:
SELECT
[UnionAll2].[C1] AS [C1],
[UnionAll2].[C2] AS [C2],
[UnionAll2].[C3] AS [C3],
[UnionAll2].[C4] AS [C4],
[UnionAll2].[C5] AS [C5],
[UnionAll2].[C6] AS [C6]
FROM (SELECT
[UnionAll1].[VerseId] AS [C1],
[UnionAll1].[ChapterNumber] AS [C2],
[UnionAll1].[VerseNumber] AS [C3],
[UnionAll1].[Verse] AS [C4],
[UnionAll1].[Translation] AS [C5],
[UnionAll1].[BookId] AS [C6]
FROM (SELECT
[Extent1].[VerseId] AS [VerseId],
[Extent1].[ChapterNumber] AS [ChapterNumber],
[Extent1].[VerseNumber] AS [VerseNumber],
[Extent1].[Verse] AS [Verse],
[Extent1].[Translation] AS [Translation],
[Extent1].[BookId] AS [BookId]
FROM [dbo].[Verses] AS [Extent1]
WHERE (( CAST(CHARINDEX(LOWER(@p__linq__0), LOWER([Extent1].[Translation])) AS int)) = 1) AND ([Extent1].[BookId] = @p__linq__1) AND ([Extent1].[ChapterNumber] >= @p__linq__2) AND ([Extent1].[ChapterNumber] <= @p__linq__3) AND ((CASE WHEN ([Extent1].[ChapterNumber] = @p__linq__4) THEN CASE WHEN ([Extent1].[VerseNumber] >= @p__linq__5) THEN cast(1 as bit) WHEN ( NOT ([Extent1].[VerseNumber] >= @p__linq__5)) THEN cast(0 as bit) END ELSE cast(1 as bit) END) = 1) AND ((CASE WHEN ([Extent1].[ChapterNumber] = @p__linq__6) THEN CASE WHEN ([Extent1].[VerseNumber] <= @p__linq__7) THEN cast(1 as bit) WHEN ( NOT ([Extent1].[VerseNumber] <= @p__linq__7)) THEN cast(0 as bit) END ELSE cast(1 as bit) END) = 1)
UNION ALL
SELECT
[Extent2].[VerseId] AS [VerseId],
[Extent2].[ChapterNumber] AS [ChapterNumber],
[Extent2].[VerseNumber] AS [VerseNumber],
[Extent2].[Verse] AS [Verse],
[Extent2].[Translation] AS [Translation],
[Extent2].[BookId] AS [BookId]
FROM [dbo].[Verses] AS [Extent2]
WHERE (( CAST(CHARINDEX(LOWER(@p__linq__8), LOWER([Extent2].[Translation])) AS int)) = 1) AND ([Extent2].[BookId] = @p__linq__9) AND ([Extent2].[ChapterNumber] >= @p__linq__10) AND ([Extent2].[ChapterNumber] <= @p__linq__11) AND ((CASE WHEN ([Extent2].[ChapterNumber] = @p__linq__12) THEN CASE WHEN ([Extent2].[VerseNumber] >= @p__linq__13) THEN cast(1 as bit) WHEN ( NOT ([Extent2].[VerseNumber] >= @p__linq__13)) THEN cast(0 as bit) END ELSE cast(1 as bit) END) = 1) AND ((CASE WHEN ([Extent2].[ChapterNumber] = @p__linq__14) THEN CASE WHEN ([Extent2].[VerseNumber] <= @p__linq__15) THEN cast(1 as bit) WHEN ( NOT ([Extent2].[VerseNumber] <= @p__linq__15)) THEN cast(0 as bit) END ELSE cast(1 as bit) END) = 1)) AS [UnionAll1]
UNION ALL
SELECT
[Extent3].[VerseId] AS [VerseId],
[Extent3].[ChapterNumber] AS [ChapterNumber],
[Extent3].[VerseNumber] AS [VerseNumber],
[Extent3].[Verse] AS [Verse],
[Extent3].[Translation] AS [Translation],
[Extent3].[BookId] AS [BookId]
FROM [dbo].[Verses] AS [Extent3]
WHERE (( CAST(CHARINDEX(LOWER(@p__linq__16), LOWER([Extent3].[Translation])) AS int)) = 1) AND ([Extent3].[BookId] = @p__linq__17) AND ([Extent3].[ChapterNumber] >= @p__linq__18) AND ([Extent3].[ChapterNumber] <= @p__linq__19) AND ((CASE WHEN ([Extent3].[ChapterNumber] = @p__linq__20) THEN CASE WHEN ([Extent3].[VerseNumber] >= @p__linq__21) THEN cast(1 as bit) WHEN ( NOT ([Extent3].[VerseNumber] >= @p__linq__21)) THEN cast(0 as bit) END ELSE cast(1 as bit) END) = 1) AND ((CASE WHEN ([Extent3].[ChapterNumber] = @p__linq__22) THEN CASE WHEN ([Extent3].[VerseNumber] <= @p__linq__23) THEN cast(1 as bit) WHEN ( NOT ([Extent3].[VerseNumber] <= @p__linq__23)) THEN cast(0 as bit) END ELSE cast(1 as bit) END) = 1)) AS [UnionAll2]
So the problem is that for some reason its treating each of the parameters I give it as if I sent 3 sets of the same parameter and concatenated so its returning the same set of verses 3 times. I’m not sure why its doing this as I am not an expert of linq or the entity framework, any suggestions?
This is a very tricky example of the modified closure issue. In the
forloop you generate a query part that uses the variablesbookIdetc. At the time that your query is executed it uses the values that these variables have at that moment, which are the values after the last iteration of the loop. (The tricky part is that usually the loop variable is captured, but here it is about variables that were declared outside the loop scope).It would be different if you declared the variables within the for loop, because each query part would capture its own instance of the variable (which is called a closure).
The good news: you can do this and at the same time clean up your code!
(do the ordering later)