Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Érdemes lehet olyan kódot használni, amely egy IQueryable vagy több IQueryable(Of T) azonosítót határoz meg egy adatforráson:
Dim companyNames As String() = {
"Consolidated Messenger", "Alpine Ski House", "Southridge Video",
"City Power & Light", "Coho Winery", "Wide World Importers",
"Graphic Design Institute", "Adventure Works", "Humongous Insurance",
"Woodgrove Bank", "Margie's Travel", "Northwind Traders",
"Blue Yonder Airlines", "Trey Research", "The Phone Company",
"Wingtip Toys", "Lucerne Publishing", "Fourth Coffee"
}
' We're using an in-memory array as the data source, but the IQueryable could have come
' from anywhere -- an ORM backed by a database, a web request, Or any other LINQ provider.
Dim companyNamesSource As IQueryable(Of String) = companyNames.AsQueryable
Dim fixedQry = companyNamesSource.OrderBy(Function(x) x)
A kód minden futtatásakor ugyanaz a lekérdezés lesz végrehajtva. Ez gyakran nem túl hasznos, mivel előfordulhat, hogy azt szeretné, hogy a kód különböző lekérdezéseket hajtson végre a futtatási feltételektől függően. Ez a cikk azt ismerteti, hogyan hajthat végre egy másik lekérdezést futtatókörnyezeti állapot alapján.
IQueryable /IQueryable(Of T) és kifejezésfák
Alapvetően egy IQueryable két összetevőből áll:
- Expression– az aktuális lekérdezés összetevőinek nyelvi és adatforrás-agnosztikus ábrázolása kifejezésfa formájában.
- Provider– egy LINQ-szolgáltató egy példánya, amely tudja, hogyan lehet az aktuális lekérdezést értékké vagy értékhalmazsá alakítani.
A dinamikus lekérdezések kontextusában a szolgáltató általában ugyanaz marad; a lekérdezés kifejezésfája lekérdezésenként eltérő lesz.
A kifejezésfák nem módosíthatók; ha másik kifejezésfát szeretne – és így egy másik lekérdezést –, akkor a meglévő kifejezésfát le kell fordítania egy újra, és így egy új IQueryable-ra.
A következő szakaszok a futtatókörnyezeti állapotra adott eltérő lekérdezési technikákat ismertetik:
- Futtatókörnyezeti állapot használata a kifejezésfán belülről
- További LINQ-metódusok meghívása
- Változtassa meg a LINQ-metódusoknak átadott kifejezésfát.
- Expression(Of TDelegate) kifejezésfa létrehozása gyári módszerekkel a Expression
- Metódushívási csomópontok hozzáadása egy IQueryablekifejezésfához
- Sztringek létrehozása és a Dinamikus LINQ-kódtár használata
Futtatókörnyezeti állapot használata a kifejezésfán belülről
Feltéve, hogy a LINQ-szolgáltató támogatja azt, a legegyszerűbben úgy kérdezhet le dinamikusan, ha közvetlenül a lekérdezésben lévő futtatókörnyezeti állapotra hivatkozik egy bezárt változón keresztül, például length a következő kód példájában:
Dim length = 1
Dim qry = companyNamesSource.
Select(Function(x) x.Substring(0, length)).
Distinct
Console.WriteLine(String.Join(", ", qry))
' prints: C, A, S, W, G, H, M, N, B, T, L, F
length = 2
Console.WriteLine(String.Join(", ", qry))
' prints: Co, Al, So, Ci, Wi, Gr, Ad, Hu, Wo, Ma, No, Bl, Tr, Th, Lu, Fo
A belső kifejezésfát – és így a lekérdezést – nem módosították; a lekérdezés csak azért ad vissza különböző értékeket, mert az érték length módosult.
További LINQ-metódusok meghívása
A beépített LINQ-metódusok általában két lépést hajtanak Queryable végre:
- Az aktuális kifejezésfa körbefuttatása a MethodCallExpression metódushívást jelképező módon.
- Adja vissza a becsomagolt kifejezésfát a szolgáltatónak, hogy visszaadjon egy értéket a szolgáltató metódusán IQueryProvider.Execute keresztül, vagy egy lefordított lekérdezési objektumot adjon vissza a IQueryProvider.CreateQuery metóduson keresztül.
Az eredeti lekérdezést lecserélheti egy IQueryable(Of T)-returning metódus eredményére egy új lekérdezés lekéréséhez. Ezt a futtatókörnyezet állapota alapján feltételesen megteheti, ahogyan az alábbi példában is látható:
' Dim sortByLength As Boolean = ...
Dim qry = companyNamesSource
If sortByLength Then qry = qry.OrderBy(Function(x) x.Length)
Változtassa meg a LINQ-metódusoknak átadott kifejezésfát.
A linq metódusoknak különböző kifejezéseket adhat át a futtatókörnyezet állapotától függően:
' Dim startsWith As String = ...
' Dim endsWith As String = ...
Dim expr As Expression(Of Func(Of String, Boolean))
If String.IsNullOrEmpty(startsWith) AndAlso String.IsNullOrEmpty(endsWith) Then
expr = Function(x) True
ElseIf String.IsNullOrEmpty(startsWith) Then
expr = Function(x) x.EndsWith(endsWith)
ElseIf String.IsNullOrEmpty(endsWith) Then
expr = Function(x) x.StartsWith(startsWith)
Else
expr = Function(x) x.StartsWith(startsWith) AndAlso x.EndsWith(endsWith)
End If
Dim qry = companyNamesSource.Where(expr)
Érdemes lehet a különböző alkifejezéseket is megírni egy külső könyvtár, például a LinqKitPredicateBuilder használatával:
' This is functionally equivalent to the previous example.
' Imports LinqKit
' Dim startsWith As String = ...
' Dim endsWith As String = ...
Dim expr As Expression(Of Func(Of String, Boolean)) = PredicateBuilder.[New](Of String)(False)
Dim original = expr
If Not String.IsNullOrEmpty(startsWith) Then expr = expr.Or(Function(x) x.StartsWith(startsWith))
If Not String.IsNullOrEmpty(endsWith) Then expr = expr.Or(Function(x) x.EndsWith(endsWith))
If expr Is original Then expr = Function(x) True
Dim qry = companyNamesSource.Where(expr)
Kifejezésfák és lekérdezések létrehozása gyári módszerekkel
Az addigi példákban már ismertük az elem típusát fordításkor –String és így a lekérdezés típusát isIQueryable(Of String). Előfordulhat, hogy összetevőket kell hozzáadnia egy tetszőleges elemtípusú lekérdezéshez, vagy az elem típusától függően különböző összetevőket kell hozzáadnia. Az alapoktól kezdve létrehozhat kifejezésfákat a gyári metódusok System.Linq.Expressions.Expressionhasználatával, és így a futtatáskor használt kifejezést egy adott elemtípushoz igazíthatja.
Kifejezés létrehozása (A TDelegate-ből)
Amikor olyan kifejezést hoz létre, amely a LINQ-metódusok egyikébe kerül, valójában a Expression(Of TDelegate) egy példányát hozza létre, ahol TDelegate valamilyen delegálási típus, például Func(Of String, Boolean): , Actionvagy egyéni delegálási típus.
A Expression(Of TDelegate) örököl az LambdaExpression osztálytól, amely az alábbihoz hasonló teljes lambda kifejezést jelöl:
Dim expr As Expression(Of Func(Of String, Boolean)) = Function(x As String) x.StartsWith("a")
Az A LambdaExpression két összetevőből áll:
- A
(x As String)tulajdonság által képviselt paraméterlistaParameters. - Egy test—
x.StartsWith("a")—amelyet a Body tulajdonság képvisel.
A Kifejezés(A TDelegate) létrehozásának alapvető lépései a következők:
A lambda kifejezésben szereplő paraméterek (ha vannak ilyenek) objektumainak definiálása ParameterExpression a Parameter gyári módszerrel.
Dim x As ParameterExpression = Parameter(GetType(String), "x")Hozza létre a saját törzsét a LambdaExpression, a definiált ParameterExpression(ok) és a gyári metódusok használatával az Expression-nél. Például egy kifejezés, amely a
x.StartsWith("a")-t képviseli, a következőképpen hozható létre:Dim body As Expression = [Call]( x, GetType(String).GetMethod("StartsWith", {GetType(String)}), Constant("a") )Csomagolja be a paramétereket és a törzset egy fordítási idő típusos kifejezésbe (Expression(Of TDelegate)), a megfelelő Lambda gyári metódus túlterhelésének használatával.
Dim expr As Expression(Of Func(Of String, Boolean)) = Lambda(Of Func(Of String, Boolean))(body, x)
Az alábbi szakaszok egy olyan forgatókönyvet mutatnak be, amelyben egy Expression(Of TDelegate)-t szeretne létrehozni, hogy azt egy LINQ-metódusba továbbítsa, és egy teljes példát mutat be arra, hogy ezt hogyan tegye meg a gyári metódusok használatával.
Forgatókönyv
Tegyük fel, hogy több entitástípussal rendelkezik:
Public Class Person
Property LastName As String
Property FirstName As String
Property DateOfBirth As Date
End Class
Public Class Car
Property Model As String
Property Year As Integer
End Class
Ezen entitástípusok bármelyike esetében csak azokat az entitásokat szeretné szűrni és visszaadni, amelyek egy adott szöveggel rendelkeznek az egyik mezőjükben string . Az Person esetében a FirstName és LastName tulajdonságokat szeretné keresni.
' Dim term = ...
Dim personsQry = (New List(Of Person)).AsQueryable.
Where(Function(x) x.FirstName.Contains(term) OrElse x.LastName.Contains(term))
De a Car esetében csak a Model tulajdonságban akar keresni.
' Dim term = ...
Dim carsQry = (New List(Of Car)).AsQueryable.
Where(Function(x) x.Model.Contains(term))
Bár megírhat egy egyéni függvényt IQueryable(Of Person) és egy másikat IQueryable(Of Car), az alábbi függvény hozzáadja ezt a szűrést bármely meglévő lekérdezéshez, függetlenül az adott elemtípustól.
példa
' Imports System.Linq.Expressions.Expression
Function TextFilter(Of T)(source As IQueryable(Of T), term As String) As IQueryable(Of T)
If String.IsNullOrEmpty(term) Then Return source
' T is a compile-time placeholder for the element type of the query
Dim elementType = GetType(T)
' Get all the string properties on this specific type
Dim stringProperties As PropertyInfo() =
elementType.GetProperties.
Where(Function(x) x.PropertyType = GetType(String)).
ToArray
If stringProperties.Length = 0 Then Return source
' Get the right overload of String.Contains
Dim containsMethod As MethodInfo =
GetType(String).GetMethod("Contains", {GetType(String)})
' Create the parameter for the expression tree --
' the 'x' in 'Function(x) x.PropertyName.Contains("term")'
' The type of the parameter is the query's element type
Dim prm As ParameterExpression =
Parameter(elementType)
' Generate an expression tree node corresponding to each property
Dim expressions As IEnumerable(Of Expression) =
stringProperties.Select(Of Expression)(Function(prp)
' For each property, we want an expression node like this:
' x.PropertyName.Contains("term")
Return [Call]( ' .Contains(...)
[Property]( ' .PropertyName
prm, ' x
prp
),
containsMethod,
Constant(term) ' "term"
)
End Function)
' Combine the individual nodes into a single expression tree node using OrElse
Dim body As Expression =
expressions.Aggregate(Function(prev, current) [OrElse](prev, current))
' Wrap the expression body in a compile-time-typed lambda expression
Dim lmbd As Expression(Of Func(Of T, Boolean)) =
Lambda(Of Func(Of T, Boolean))(body, prm)
' Because the lambda is compile-time-typed, we can use it with the Where method
Return source.Where(lmbd)
End Function
Mivel a TextFilter függvény egy IQueryable(Of T) ( és nem csak egy IQueryable) lekérdezést vesz fel és ad vissza, a szövegszűrő után további fordítási idő típusú lekérdezési elemeket is hozzáadhat.
Dim qry = TextFilter(
(New List(Of Person)).AsQueryable,
"abcd"
).Where(Function(x) x.DateOfBirth < #1/1/2001#)
Dim qry1 = TextFilter(
(New List(Of Car)).AsQueryable,
"abcd"
).Where(Function(x) x.Year = 2010)
Metódushívási csomópontok hozzáadása a IQueryablekifejezésfához
Ha egy IQueryable van az IQueryable(Of T) helyett, akkor közvetlenül nem hívhatja meg az általános LINQ-metódusokat. Az egyik alternatív megoldás a belső kifejezésfa felépítése a fenti módon, és a tükröződés használatával meghívja a megfelelő LINQ metódust a kifejezésfán való áthaladáskor.
A LINQ metódus funkcióit is duplikálhatja úgy, hogy a teljes fát egy MethodCallExpression-ba csomagolja, amely jelképezi a LINQ metódus hívását.
Function TextFilter_Untyped(source As IQueryable, term As String) As IQueryable
If String.IsNullOrEmpty(term) Then Return source
Dim elementType = source.ElementType
' The logic for building the ParameterExpression And the LambdaExpression's body is the same as in
' the previous example, but has been refactored into the ConstructBody function.
Dim x As (Expression, ParameterExpression) = ConstructBody(elementType, term)
Dim body As Expression = x.Item1
Dim prm As ParameterExpression = x.Item2
If body Is Nothing Then Return source
Dim filteredTree As Expression = [Call](
GetType(Queryable),
"Where",
{elementType},
source.Expression,
Lambda(body, prm)
)
Return source.Provider.CreateQuery(filteredTree)
End Function
Ebben az esetben nem rendelkezik általános fordítási idő T típusú helyőrzővel, ezért olyan túlterhelést fog használniLambda, amely nem igényel fordítási idő típusú adatokat, és amely LambdaExpression helyett létrehoz egy függvényt.
A Dinamikus LINQ-kódtár
A kifejezésfák létrehozása gyári módszerekkel viszonylag bonyolult; egyszerűbb sztringeket írni. A Dinamikus LINQ könyvtár olyan bővítő metódusokat IQueryable tesz elérhetővé, amelyek a Queryable standard LINQ-metódusoknak felelnek meg, és amelyek kifejezésfák helyett speciális szintaxisban fogadnak el karaktersorozatokat. A kódtár létrehozza a megfelelő kifejezésfát a sztringből, és visszaadja az eredményül kapott lefordított értéket IQueryable.
Az előző példa (beleértve a kifejezésfa felépítését) például a következőképpen írható át:
' Imports System.Linq.Dynamic.Core
Function TextFilter_Strings(source As IQueryable, term As String) As IQueryable
If String.IsNullOrEmpty(term) Then Return source
Dim elementType = source.ElementType
Dim stringProperties = elementType.GetProperties.
Where(Function(x) x.PropertyType = GetType(String)).
ToArray
If stringProperties.Length = 0 Then Return source
Dim filterExpr = String.Join(
" || ",
stringProperties.Select(Function(prp) $"{prp.Name}.Contains(@0)")
)
Return source.Where(filterExpr, term)
End Function