Udostępnij za pośrednictwem


Instruktaż: Tworzenie dostawcą IQueryable LINQ

W tym temacie zaawansowane instrukcje krok po kroku dotyczące tworzenia niestandardowego LINQ dostawcy. Po zakończeniu można używać dostawcy utworzyć napisać LINQ kwerend usługi sieci Web Szukacz USA.

Usługa sieci Web Szukacz USA zapewnia interfejs do bazy danych zdjęć lotniczych w Stanach Zjednoczonych.Udostępnia on również metoda, która zwraca informacje o lokalizacjach w Stanach Zjednoczonych, danego fragmentu lub całej nazwy lokalizacji.Ta metoda, która nosi nazwę GetPlaceList, jest metoda, Twój LINQ dostawca będzie połączenie. Dostawca będzie używać Windows Communication Foundation (WCF) do komunikowania się z usługą sieci Web.Aby uzyskać więcej informacji na temat usługi sieci Web Szukacz-USA, zobacz Przegląd usług sieci Web USA Szukacz.

Ten dostawca jest stosunkowo proste IQueryable dostawcy.Oczekuje określonych informacji w kwerendach, które obsługuje i posiada system typu zamkniętego, narażając jednego typu do reprezentacji danych wynik.Ten dostawca analizuje tylko jeden typ wyrażenie wywołania metody drzewa wyrażenie, który reprezentuje kwerendę, która jest najbardziej wewnętrzne wywołanie Where.Wyodrębnia dane, które musi on mieć aby przesyłać kwerendy do usługi sieci Web z tego wyrażenia.Następnie wywołuje usługa sieci Web i wstawia zwrócone dane do drzewa wyrażenie miejsce początkowe IQueryable źródło danych.Reszta wykonanie kwerendy jest obsługiwane przez Enumerable implementacje operatorów standardowej kwerendy.

Przykłady kodu w tym temacie znajdują się w C# i Visual Basic.

W tym instruktażu przedstawiono następujące zadania:

  • Tworzenie projektu w Visual Studio.

  • Implementowanie interfejsów, które są wymagane dla IQueryableLINQ dostawca: IQueryable<T>, IOrderedQueryable<T>, i IQueryProvider.

  • Dodawanie niestandardowego typu .NET do reprezentowania danych z usługi sieci Web.

  • Tworzenie klasy kontekście kwerendy i klasy, która pobiera dane z usługi sieci Web.

  • Tworzenie podklasy odwiedzający drzewa wyrażenie, które wyszukuje wyrażenie, które reprezentuje wywołanie znajdujące się najniżej Queryable.Where metoda.

  • Tworzenie podklasy odwiedzający drzewa wyrażenie, które wyodrębnia informacje z LINQ kwerendy w żądanie usługi sieci Web.

  • Tworzenie podklasy odwiedzający drzewa wyrażenie, które modyfikuje drzewa wyrażenie, które reprezentuje pełną LINQ kwerendy.

  • Za pomocą klasy modułu szacującego częściowo oceny drzewa wyrażenie.Ten krok jest konieczny, ponieważ tłumaczy on wszystkie odniesienia zmiennych lokalnych w LINQ kwerendy do wartości.

  • Tworzenie helper drzewa wyrażenie klasy i nową klasę wyjątków.

  • Badania LINQ dostawcy z aplikacji klienckiej, która zawiera LINQ kwerendy.

  • Dodanie bardziej złożonych funkcji kwerendy do LINQ dostawcy.

    [!UWAGA]

    Dostawca LINQ, który tworzy w tym instruktażu jest dostępny jako próbka.Aby uzyskać więcej informacji Próbki LINQ.

Wymagania wstępne

W tym instruktażu wymaga funkcji, które są wprowadzane w Visual Studio 2008.

[!UWAGA]

Na danym komputerze mogą być używane inne nazwy lub lokalizacje pewnych elementów interfejsu użytkownika programu Visual Studio, które są używane w poniższych instrukcjach. Używana wersja programu Visual Studio oraz jej ustawienia określają te elementy. Aby uzyskać więcej informacji, zobacz Visual Studio, ustawienia.

Tworzenie projektu

Tworzenie projektu programu Visual Studio

  1. W Visual Studio, Utwórz nową Biblioteka klas aplikacji. Nazwa projektu LinqToTerraServerProvider.

  2. W Solution Explorer, wybierz opcję Class1.cs (lub Class1.vb) plik i zmień jego nazwę na QueryableTerraServerData.cs (lub QueryableTerraServerData.vb).W oknie dialogowym, które się pojawi, kliknij przycisk Tak zmienić wszystkie odwołania do elementu kodu.

    Utwórz dostawcę jako Biblioteka klas projektu w Visual Studio , ponieważ klient pliku wykonywalnego aplikacji spowoduje dodanie zestawu dostawcy jako odniesienie do ich projektu.

Aby dodać odwołanie usługi Usługa sieci Web

  1. W Solution Explorer, kliknij prawym przyciskiem myszy LinqToTerraServerProvider projektu, a następnie kliknij przycisk Dodać odwołanie do usługi.

    Dodać odwołanie do usługi zostanie otwarte okno dialogowe.

  2. W adres wpisz http://terraserver.microsoft.com/TerraService2.asmx.

  3. W nazw wpisz TerraServerReference , a następnie kliknij przycisk OK.

    Usługa sieci Web Szukacz USA dodaje się jako odwołanie do usługi, dzięki czemu aplikacja może komunikować się z usługą sieci Web na Windows Communication Foundation (WCF).Przez dodanie usługi odwołanie do projektu, Visual Studio generuje app.config pliku, który zawiera serwer proxy i punktu końcowego dla usługi sieci Web.Aby uzyskać więcej informacji, zobacz Usług Windows Communication Foundation i usług WCF w danych w programie Visual Studio.

Teraz masz projekt, który zawiera plik o nazwie app.config, plik o nazwie QueryableTerraServerData.cs (lub QueryableTerraServerData.vb) i odwołania usługi o nazwie TerraServerReference.

Interfejsy niezbędne do wykonania

Aby utworzyć LINQ dostawca, jako minimum, należy zaimplementować IQueryable<T> i IQueryProvider interfejsów. IQueryable<T>i IQueryProvider są uzyskiwane z innych wymaganych interfejsów; w związku z tym, poprzez realizację tych dwóch interfejsów, również realizują inne interfejsy, które są wymagane dla LINQ dostawcy.

Jeśli chcesz wsparcia sortowania operatorów kwerendy, takie jak OrderBy i ThenBy, musi też implementować IOrderedQueryable<T> interfejsu.Bo IOrderedQueryable<T> wynika z IQueryable<T>, można zaimplementować zarówno tych interfejsów w jednym typie, czego nie tego dostawcy.

Aby zaimplementować System.Linq.IQueryable'1 i System.Linq.IOrderedQueryable'1

  • W pliku QueryableTerraServerData.cs (lub QueryableTerraServerData.vb), Dodaj następujący kod.

    Imports System.Linq.Expressions
    
    Public Class QueryableTerraServerData(Of TData)
        Implements IOrderedQueryable(Of TData)
    
    #Region "Private members"
    
        Private _provider As TerraServerQueryProvider
        Private _expression As Expression
    
    #End Region
    
    #Region "Constructors"
    
        ''' <summary>
        ''' This constructor is called by the client to create the data source.
        ''' </summary>
        Public Sub New()
            Me._provider = New TerraServerQueryProvider()
            Me._expression = Expression.Constant(Me)
        End Sub
    
        ''' <summary>
        ''' This constructor is called by Provider.CreateQuery().
        ''' </summary>
        ''' <param name="_expression"></param>
        Public Sub New(ByVal _provider As TerraServerQueryProvider, ByVal _expression As Expression)
    
            If _provider Is Nothing Then
                Throw New ArgumentNullException("provider")
            End If
    
            If _expression Is Nothing Then
                Throw New ArgumentNullException("expression")
            End If
    
            If Not GetType(IQueryable(Of TData)).IsAssignableFrom(_expression.Type) Then
                Throw New ArgumentOutOfRangeException("expression")
            End If
    
            Me._provider = _provider
            Me._expression = _expression
        End Sub
    
    #End Region
    
    #Region "Properties"
    
        Public ReadOnly Property ElementType(
            ) As Type Implements IQueryable(Of TData).ElementType
    
            Get
                Return GetType(TData)
            End Get
        End Property
    
        Public ReadOnly Property Expression(
            ) As Expression Implements IQueryable(Of TData).Expression
    
            Get
                Return _expression
            End Get
        End Property
    
        Public ReadOnly Property Provider(
            ) As IQueryProvider Implements IQueryable(Of TData).Provider
    
            Get
                Return _provider
            End Get
        End Property
    
    #End Region
    
    #Region "Enumerators"
    
        Public Function GetGenericEnumerator(
            ) As IEnumerator(Of TData) Implements IEnumerable(Of TData).GetEnumerator
    
            Return (Me.Provider.
                    Execute(Of IEnumerable(Of TData))(Me._expression)).GetEnumerator()
        End Function
    
        Public Function GetEnumerator(
            ) As IEnumerator Implements IEnumerable.GetEnumerator
    
            Return (Me.Provider.
                    Execute(Of IEnumerable)(Me._expression)).GetEnumerator()
        End Function
    
    #End Region
    
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    
    namespace LinqToTerraServerProvider
    {
        public class QueryableTerraServerData<TData> : IOrderedQueryable<TData>
        {
            #region Constructors
            /// <summary>
            /// This constructor is called by the client to create the data source.
            /// </summary>
            public QueryableTerraServerData()
            {
                Provider = new TerraServerQueryProvider();
                Expression = Expression.Constant(this);
            }
    
            /// <summary>
            /// This constructor is called by Provider.CreateQuery().
            /// </summary>
            /// <param name="expression"></param>
            public QueryableTerraServerData(TerraServerQueryProvider provider, Expression expression)
            {
                if (provider == null)
                {
                    throw new ArgumentNullException("provider");
                }
    
                if (expression == null)
                {
                    throw new ArgumentNullException("expression");
                }
    
                if (!typeof(IQueryable<TData>).IsAssignableFrom(expression.Type))
                {
                    throw new ArgumentOutOfRangeException("expression");
                }
    
                Provider = provider;
                Expression = expression;
            }
            #endregion
    
            #region Properties
    
            public IQueryProvider Provider { get; private set; }
            public Expression Expression { get; private set; }
    
            public Type ElementType
            {
                get { return typeof(TData); }
            }
    
            #endregion
    
            #region Enumerators
            public IEnumerator<TData> GetEnumerator()
            {
                return (Provider.Execute<IEnumerable<TData>>(Expression)).GetEnumerator();
            }
    
            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return (Provider.Execute<System.Collections.IEnumerable>(Expression)).GetEnumerator();
            }
            #endregion
        }
    }
    

    IOrderedQueryable<T> Wprowadzania w życie przez QueryableTerraServerData klasy implementuje trzy właściwości zadeklarowane w IQueryable i dwie metody wyliczania zadeklarowane w IEnumerable i IEnumerable<T>.

    Ta klasa ma dwa konstruktorów.Konstruktor pierwszy nazywa się od aplikacji klienta do utworzenia obiektu, aby napisać LINQ kwerenda wykonana.Drugi konstruktor jest nazywany wewnętrznego do biblioteki dostawca przez kod w IQueryProvider realizacji.

    Gdy GetEnumerator wywoływana jest metoda obiektu typu QueryableTerraServerData, jest wykonywana kwerenda, która reprezentuje i wyniki kwerendy są wyliczane.

    Ten kod, oprócz nazwy klasy, nie dotyczy to usługodawca sieci Web Szukacz USA.W związku z tym, może zostać użyty do dowolnego LINQ dostawcy.

Aby zaimplementować System.Linq.IQueryProvider

  • Dodaj TerraServerQueryProvider klasy do projektu.

    Imports System.Linq.Expressions
    Imports System.Reflection
    
    Public Class TerraServerQueryProvider
        Implements IQueryProvider
    
        Public Function CreateQuery(
            ByVal expression As Expression
            ) As IQueryable Implements IQueryProvider.CreateQuery
    
            Dim elementType As Type = TypeSystem.GetElementType(expression.Type)
    
            Try
                Dim qType = GetType(QueryableTerraServerData(Of )).MakeGenericType(elementType)
                Dim args = New Object() {Me, expression}
                Dim instance = Activator.CreateInstance(qType, args)
    
                Return CType(instance, IQueryable)
            Catch tie As TargetInvocationException
                Throw tie.InnerException
            End Try
        End Function
    
        ' Queryable's collection-returning standard query operators call this method.
        Public Function CreateQuery(Of TResult)(
            ByVal expression As Expression
            ) As IQueryable(Of TResult) Implements IQueryProvider.CreateQuery
    
            Return New QueryableTerraServerData(Of TResult)(Me, expression)
        End Function
    
        Public Function Execute(
            ByVal expression As Expression
            ) As Object Implements IQueryProvider.Execute
    
            Return TerraServerQueryContext.Execute(expression, False)
        End Function
    
        ' Queryable's "single value" standard query operators call this method.
        ' It is also called from QueryableTerraServerData.GetEnumerator().
        Public Function Execute(Of TResult)(
            ByVal expression As Expression
            ) As TResult Implements IQueryProvider.Execute
    
            Dim IsEnumerable As Boolean = (GetType(TResult).Name = "IEnumerable`1")
    
            Dim result = TerraServerQueryContext.Execute(expression, IsEnumerable)
            Return CType(result, TResult)
        End Function
    End Class
    
    using System;
    using System.Linq;
    using System.Linq.Expressions;
    
    namespace LinqToTerraServerProvider
    {
        public class TerraServerQueryProvider : IQueryProvider
        {
            public IQueryable CreateQuery(Expression expression)
            {
                Type elementType = TypeSystem.GetElementType(expression.Type);
                try
                {
                    return (IQueryable)Activator.CreateInstance(typeof(QueryableTerraServerData<>).MakeGenericType(elementType), new object[] { this, expression });
                }
                catch (System.Reflection.TargetInvocationException tie)
                {
                    throw tie.InnerException;
                }
            }
    
            // Queryable's collection-returning standard query operators call this method.
            public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
            {
                return new QueryableTerraServerData<TResult>(this, expression);
            }
    
            public object Execute(Expression expression)
            {
                return TerraServerQueryContext.Execute(expression, false);
            }
    
            // Queryable's "single value" standard query operators call this method.
            // It is also called from QueryableTerraServerData.GetEnumerator().
            public TResult Execute<TResult>(Expression expression)
            {
                bool IsEnumerable = (typeof(TResult).Name == "IEnumerable`1");
    
                return (TResult)TerraServerQueryContext.Execute(expression, IsEnumerable);
            }
        }
    }
    

    Kod dostawcy kwerendy tej klasy implementuje cztery metody, które są wymagane do wykonania IQueryProvider interfejsu.Dwa CreateQuery metody tworzenia kwerend, które są skojarzone ze źródłem danych.Dwa Execute metody wysyłania kwerend takiej ma być wykonane.

    Niepodstawowego CreateQuery metoda wykorzystuje odbicie uzyskać typ element sekwencji, że kwerenda tworzy wróci, jeśli zostało wykonane.Następnie Activator klasy, aby utworzyć nowy QueryableTerraServerData instancji, która jest skonstruowana z typu elementu jako argument typu rodzajowego.Wynik wywołania niepodstawowego CreateQuery metoda jest taka sama, jak typowa CreateQuery metoda miał została wywołana z argumentem poprawnego typu.

    Większość logiki wykonanie kwerendy jest obsługiwane w innej klasy, który należy dodać później.Ta funkcjonalność jest obsługiwane, gdzie indziej bo to specyficzne dla źródła danych, którego dotyczy kwerenda, kod tej klasy jest rodzajowy dowolnej LINQ dostawcy.Aby użyć tego kodu dla innego dostawcy, trzeba zmienić nazwę klasy i nazwy typ kontekstu kwerendy, która odwołuje się do dwóch metod.

Dodawanie niestandardowego typu do reprezentowania danych wyników

Trzeba będzie typu .NET do reprezentowania danych uzyskanych z usługi sieci Web.Ten typ będzie używany w kliencie LINQ kwerendę, aby określić wyniki chce.Poniższa procedura tworzy tego typu. Tego typu o nazwie Place, zawiera informacje o jednej lokalizacji geograficznych miasta, parku lub jeziora.

Ten kod zawiera również typem wyliczenia o nazwie PlaceType, która definiuje różne rodzaje położenia geograficznego i jest używany w Place klasy.

Aby utworzyć typ niestandardowy wynik

  • Dodaj Place klasy i PlaceType wyliczenie do projektu.

    Public Class Place
        ' Properties.
        Public Property Name As String
        Public Property State As String
        Public Property PlaceType As PlaceType
    
        ' Constructor.
        Friend Sub New(ByVal name As String, 
                       ByVal state As String, 
                       ByVal placeType As TerraServerReference.PlaceType)
    
            Me.Name = name
            Me.State = state
            Me.PlaceType = CType(placeType, PlaceType)
        End Sub
    End Class
    
    Public Enum PlaceType
        Unknown
        AirRailStation
        BayGulf
        CapePeninsula
        CityTown
        HillMountain
        Island
        Lake
        OtherLandFeature
        OtherWaterFeature
        ParkBeach
        PointOfInterest
        River
    End Enum
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace LinqToTerraServerProvider
    {
        public class Place
        {
            // Properties.
            public string Name { get; private set; }
            public string State { get; private set; }
            public PlaceType PlaceType { get; private set; }
    
            // Constructor.
            internal Place(string name,
                            string state,
                            LinqToTerraServerProvider.TerraServerReference.PlaceType placeType)
            {
                Name = name;
                State = state;
                PlaceType = (PlaceType)placeType;
            }
        }
    
        public enum PlaceType
        {
            Unknown,
            AirRailStation,
            BayGulf,
            CapePeninsula,
            CityTown,
            HillMountain,
            Island,
            Lake,
            OtherLandFeature,
            OtherWaterFeature,
            ParkBeach,
            PointOfInterest,
            River
        }
    }
    

    Konstruktor dla Place typu upraszcza tworzenie obiektu wyników z typu, który jest zwracany przez usługę sieci Web.Podczas gdy dostawca może zwrócić wynik zdefiniowanych przez interfejs API usługi sieci Web bezpośrednio, będzie to wymagać aplikacje klienckie, aby dodać odwołanie do usługi sieci Web.Tworząc nowy typ jako część biblioteki dostawca, klient nie ma wiedzieć na temat typów i metod, które udostępnia usługi sieci Web.

Dodawanie funkcji do pobierania danych ze źródła danych

Ta implementacja dostawcy zakłada się, że najbardziej wewnętrzne wywołanie Queryable.Where zawiera informacje o lokalizacji, aby użyć kwerendy usługi sieci Web.Pola wewnętrznego Queryable.Where jest where klauzula (Where w klauzuli Visual Basic) lub Queryable.Where wywołanie metody, które występuje po raz pierwszy w LINQ kwerendy lub najbliższy "u dołu" drzewa wyrażenie, które reprezentuje kwerendy.

Aby utworzyć klasę kontekście kwerendy

  • Dodaj TerraServerQueryContext klasy do projektu.

    Imports System.Linq.Expressions
    
    Public Class TerraServerQueryContext
    
        ' Executes the expression tree that is passed to it.
        Friend Shared Function Execute(ByVal expr As Expression, 
                                       ByVal IsEnumerable As Boolean) As Object
    
            ' The expression must represent a query over the data source.
            If Not IsQueryOverDataSource(expr) Then
                Throw New InvalidProgramException("No query over the data source was specified.")
            End If
    
            ' Find the call to Where() and get the lambda expression predicate.
            Dim whereFinder As New InnermostWhereFinder()
            Dim whereExpression As MethodCallExpression = 
                whereFinder.GetInnermostWhere(expr)
            Dim lambdaExpr As LambdaExpression
            lambdaExpr = CType(CType(whereExpression.Arguments(1), UnaryExpression).Operand, LambdaExpression)
    
            ' Send the lambda expression through the partial evaluator.
            lambdaExpr = CType(Evaluator.PartialEval(lambdaExpr), LambdaExpression)
    
            ' Get the place name(s) to query the Web service with.
            Dim lf As New LocationFinder(lambdaExpr.Body)
            Dim locations As List(Of String) = lf.Locations
            If locations.Count = 0 Then
                Dim s = "You must specify at least one place name in your query."
                Throw New InvalidQueryException(s)
            End If
    
            ' Call the Web service and get the results.
            Dim places() = WebServiceHelper.GetPlacesFromTerraServer(locations)
    
            ' Copy the IEnumerable places to an IQueryable.
            Dim queryablePlaces = places.AsQueryable()
    
            ' Copy the expression tree that was passed in, changing only the first
            ' argument of the innermost MethodCallExpression.
            Dim treeCopier As New ExpressionTreeModifier(queryablePlaces)
            Dim newExpressionTree = treeCopier.Visit(expr)
    
            ' This step creates an IQueryable that executes by replacing 
            ' Queryable methods with Enumerable methods.
            If (IsEnumerable) Then
                Return queryablePlaces.Provider.CreateQuery(newExpressionTree)
            Else
                Return queryablePlaces.Provider.Execute(newExpressionTree)
            End If
        End Function
    
        Private Shared Function IsQueryOverDataSource(ByVal expression As Expression) As Boolean
            ' If expression represents an unqueried IQueryable data source instance,
            ' expression is of type ConstantExpression, not MethodCallExpression.
            Return (TypeOf expression Is MethodCallExpression)
        End Function
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    
    namespace LinqToTerraServerProvider
    {
        class TerraServerQueryContext
        {
            // Executes the expression tree that is passed to it.
            internal static object Execute(Expression expression, bool IsEnumerable)
            {
                // The expression must represent a query over the data source.
                if (!IsQueryOverDataSource(expression))
                    throw new InvalidProgramException("No query over the data source was specified.");
    
                // Find the call to Where() and get the lambda expression predicate.
                InnermostWhereFinder whereFinder = new InnermostWhereFinder();
                MethodCallExpression whereExpression = whereFinder.GetInnermostWhere(expression);
                LambdaExpression lambdaExpression = (LambdaExpression)((UnaryExpression)(whereExpression.Arguments[1])).Operand;
    
                // Send the lambda expression through the partial evaluator.
                lambdaExpression = (LambdaExpression)Evaluator.PartialEval(lambdaExpression);
    
                // Get the place name(s) to query the Web service with.
                LocationFinder lf = new LocationFinder(lambdaExpression.Body);
                List<string> locations = lf.Locations;
                if (locations.Count == 0)
                    throw new InvalidQueryException("You must specify at least one place name in your query.");
    
                // Call the Web service and get the results.
                Place[] places = WebServiceHelper.GetPlacesFromTerraServer(locations);
    
                // Copy the IEnumerable places to an IQueryable.
                IQueryable<Place> queryablePlaces = places.AsQueryable<Place>();
    
                // Copy the expression tree that was passed in, changing only the first
                // argument of the innermost MethodCallExpression.
                ExpressionTreeModifier treeCopier = new ExpressionTreeModifier(queryablePlaces);
                Expression newExpressionTree = treeCopier.Visit(expression);
    
                // This step creates an IQueryable that executes by replacing Queryable methods with Enumerable methods.
                if (IsEnumerable)
                    return queryablePlaces.Provider.CreateQuery(newExpressionTree);
                else
                    return queryablePlaces.Provider.Execute(newExpressionTree);
            }
    
            private static bool IsQueryOverDataSource(Expression expression)
            {
                // If expression represents an unqueried IQueryable data source instance,
                // expression is of type ConstantExpression, not MethodCallExpression.
                return (expression is MethodCallExpression);
            }
        }
    }
    

    Ta klasa służy do organizowania pracy, wykonywanie kwerendy.Po znalezieniu wyrażenie, które reprezentuje skrajnym polu Queryable.Where rozmowy, ten kod pobiera Wyrażenie lambda, który reprezentuje predykatu, który został przekazany do Queryable.Where.Następnie przechodzi predykatu wyrażenie do metody częściowo oceniane, tak, że wszystkie odwołania do zmiennych lokalnych są tłumaczone na wartości.Następnie wywołuje metodę wyodrębnić żądanej lokalizacji z predykat i wywołuje metodę innym celu uzyskania danych wyników z usługi sieci Web.

    W następnym kroku ten kod kopiuje drzewa wyrażenie, które reprezentuje LINQ kwerendy i sprawia, że jedna modyfikacja drzewa wyrażenie.Kod używa podklasy odwiedzający drzewa wyrażenie zastąpić wywołanie operatora wewnętrzne kwerenda jest stosowany do źródło danych z listy konkretnych Place obiektów, które zostały uzyskane z usługi sieci Web.

    Przed wykaz Place obiektów jest wstawiany do drzewa wyrażenie, jego typ jest zmieniana z IEnumerable do IQueryable przez wywołanie AsQueryable.Zmiana tego typu jest konieczne, ponieważ gdy drzewo wyrażenie jest przebudowywany, węzeł, który reprezentuje wywołanie metody Metoda operator wewnętrzne kwerenda jest rekonstruowany.Węzeł jest rekonstruowany, ponieważ zmienił się jeden z argumentów (czyli źródła danych, który jest stosowany do).Call(Expression, MethodInfo, IEnumerable<Expression>) Metoda, która jest używana do rekonstruowania węzła, będzie Zgłoś wyjątek, jeśli którykolwiek z argumentów nie jest możliwa do przypisania do odpowiadającego mu parametru metody, która będzie przekazywana do.W tym przypadku IEnumerable lista Place obiektów, nie będzie można przypisać do IQueryable parametr Queryable.Where.W związku z tym, jego typ jest zmieniany na IQueryable.

    Przez zmianę jej typu, aby IQueryable, Kolekcja również uzyskuje IQueryProvider Członkowskich dostępne przez Provider właściwość, że można utworzyć lub wykonać kwerendy.Dynamiczne typ IQueryable°Place jest kolekcja EnumerableQuery, który jest typem, który jest wewnętrznym System.Linq interfejsu API.Dostawca kwerendy, który jest skojarzony z tym typem wykonuje kwerendy, zastępując Queryable kwerendy standardowe operatora połączeń z odpowiednik Enumerable operatorów, tak że skutecznie kwerenda staje się LINQ do obiektów kwerendy.

    Ostateczny kod w TerraServerQueryContext klasy zwraca jedną z dwóch metod na IQueryable lista Place obiektów.Wywołuje CreateQuery , jeśli klient zwraca wyniki agreguje ustalony, lub Execute Jeśli kwerendy klient zwraca wynik agreguje ustalony.

    Kod tej klasy jest bardzo specyficzne dla tego dostawcy Szukacz-USA.W związku z tym, jest hermetyzowany w TerraServerQueryContext klasy, a nie bezpośrednio do bardziej ogólnych dodaje się IQueryProvider realizacji.

Dostawca tworzenia wymaga tylko informacje w Queryable.Where Predykat kwerendy usługi sieci Web. dlatego używa LINQ do obiektów do prac wykonywanych z LINQ kwerendy przy użyciu wewnętrznego EnumerableQuery typu.Alternatywny sposób używania LINQ do obiektów, aby wykonać kwerendy ma zawinąć częścią kwerendy mają być zrealizowane przez klienta LINQ do obiektów w LINQ do obiektów kwerendy.Można to osiągnąć przez wywołanie AsEnumerable<TSource> w dalszej części kwerendy, która jest częścią kwerendy, która wymaga szczególnych celach.Zaletą tego rodzaju realizacji jest to, że podział pracy między niestandardowego dostawcy i LINQ do obiektów jest bardziej przejrzysty.

[!UWAGA]

Dostawca, przedstawione w tym temacie jest proste dostawca, który obsługuje kwerendy minimalne własnych.W związku z tym, to opiera się na LINQ do obiektów w celu wykonania kwerendy.Kompleks LINQ dostawcy, takich jak LINQ to SQL może obsługiwać całego kwerendy bez przekazywanie wszelkich prac do LINQ do obiektów.

Aby utworzyć klasę, aby uzyskać dane z usługi sieci Web

  • Dodaj WebServiceHelper klasy (lub moduł w Visual Basic) do projektu.

    Imports System.Collections.Generic
    Imports LinqToTerraServerProvider.TerraServerReference
    
    Friend Module WebServiceHelper
        Private numResults As Integer = 200
        Private mustHaveImage As Boolean = False
    
        Friend Function GetPlacesFromTerraServer(ByVal locations As List(Of String)) As Place()
            ' Limit the total number of Web service calls.
            If locations.Count > 5 Then
                Dim s = "This query requires more than five separate calls to the Web service. Please decrease the number of places."
                Throw New InvalidQueryException(s)
            End If
    
            Dim allPlaces As New List(Of Place)
    
            ' For each location, call the Web service method to get data.
            For Each location In locations
                Dim places = CallGetPlaceListMethod(location)
                allPlaces.AddRange(places)
            Next
    
            Return allPlaces.ToArray()
        End Function
    
        Private Function CallGetPlaceListMethod(ByVal location As String) As Place()
    
            Dim client As New TerraServiceSoapClient()
            Dim placeFacts() As PlaceFacts
    
            Try
                ' Call the Web service method "GetPlaceList".
                placeFacts = client.GetPlaceList(location, numResults, mustHaveImage)
    
                ' If we get exactly 'numResults' results, they are probably truncated.
                If (placeFacts.Length = numResults) Then
                    Dim s = "The results have been truncated by the Web service and would not be complete. Please try a different query."
                    Throw New Exception(s)
                End If
    
                ' Create Place objects from the PlaceFacts objects returned by the Web service.
                Dim places(placeFacts.Length - 1) As Place
                For i = 0 To placeFacts.Length - 1
                    places(i) = New Place(placeFacts(i).Place.City, 
                                          placeFacts(i).Place.State, 
                                          placeFacts(i).PlaceTypeId)
                Next
    
                ' Close the WCF client.
                client.Close()
    
                Return places
            Catch timeoutException As TimeoutException
                client.Abort()
                Throw
            Catch communicationException As System.ServiceModel.CommunicationException
                client.Abort()
                Throw
            End Try
        End Function
    End Module
    
    using System;
    using System.Collections.Generic;
    using LinqToTerraServerProvider.TerraServerReference;
    
    namespace LinqToTerraServerProvider
    {
        internal static class WebServiceHelper
        {
            private static int numResults = 200;
            private static bool mustHaveImage = false;
    
            internal static Place[] GetPlacesFromTerraServer(List<string> locations)
            {
                // Limit the total number of Web service calls.
                if (locations.Count > 5)
                    throw new InvalidQueryException("This query requires more than five separate calls to the Web service. Please decrease the number of locations in your query.");
    
                List<Place> allPlaces = new List<Place>();
    
                // For each location, call the Web service method to get data.
                foreach (string location in locations)
                {
                    Place[] places = CallGetPlaceListMethod(location);
                    allPlaces.AddRange(places);
                }
    
                return allPlaces.ToArray();
            }
    
            private static Place[] CallGetPlaceListMethod(string location)
            {
                TerraServiceSoapClient client = new TerraServiceSoapClient();
                PlaceFacts[] placeFacts = null;
    
                try
                {
                    // Call the Web service method "GetPlaceList".
                    placeFacts = client.GetPlaceList(location, numResults, mustHaveImage);
    
                    // If there are exactly 'numResults' results, they are probably truncated.
                    if (placeFacts.Length == numResults)
                        throw new Exception("The results have been truncated by the Web service and would not be complete. Please try a different query.");
    
                    // Create Place objects from the PlaceFacts objects returned by the Web service.
                    Place[] places = new Place[placeFacts.Length];
                    for (int i = 0; i < placeFacts.Length; i++)
                    {
                        places[i] = new Place(
                            placeFacts[i].Place.City,
                            placeFacts[i].Place.State,
                            placeFacts[i].PlaceTypeId);
                    }
    
                    // Close the WCF client.
                    client.Close();
    
                    return places;
                }
                catch (TimeoutException timeoutException)
                {
                    client.Abort();
                    throw;
                }
                catch (System.ServiceModel.CommunicationException communicationException)
                {
                    client.Abort();
                    throw;
                }
            }
        }
    }
    

    Ta klasa zawiera funkcje, które pobiera dane z usługi sieci Web.Ten kod zawiera typ o nazwie TerraServiceSoapClient, który jest generowany automatycznie projektu przez Windows Communication Foundation (WCF), wywołanie metody usługi sieci Web GetPlaceList.Następnie każdy wynik jest tłumaczony od zwrotu typu metody usługi sieci Web typu .NET, definiujący przez dostawcę danych.

    Ten kod zawiera dwie kontrole, które zwiększają użyteczność biblioteki dostawca.Pierwsze sprawdzenie ogranicza maksymalny czas, aplikacja kliencka czeka na odpowiedź przez ograniczenie całkowita liczba połączeń, które są wykonane z usługą sieci Web, na kwerendę do pięciu.Dla każdej lokalizacji, określonej w kwerendzie klienta jest generowany jeden żądanie usługi sieci Web.W związku z tym dostawca zgłasza wyjątek, jeśli kwerenda zawiera więcej niż pięciu miejscach.

    Drugi wyboru określa, czy liczba wyników zwróconych przez usługę sieci Web jest równa maksymalną liczbę wyników, które mogą być zwracane.Jeśli liczba wyników jest maksymalna liczba, jest prawdopodobne, że wyniki z usługi sieci Web są obcinane.Zamiast niepełną listę do klienta, dostawca zgłasza wyjątek.

Dodawanie klas odwiedzający drzewa wyrażenie

Aby utworzyć użytkownika, że znajdzie skrajnym polu, gdzie wyrażenia przez wywołanie metody

  1. Dodaj InnermostWhereFinder klasy, która dziedziczy ExpressionVisitor klasy do projektu.

    Imports System.Linq.Expressions
    
    Class InnermostWhereFinder
        Inherits ExpressionVisitor
    
        Private innermostWhereExpression As MethodCallExpression
    
        Public Function GetInnermostWhere(ByVal expr As Expression) As MethodCallExpression
            Me.Visit(expr)
            Return innermostWhereExpression
        End Function
    
        Protected Overrides Function VisitMethodCall(ByVal expr As MethodCallExpression) As Expression
            If expr.Method.Name = "Where" Then
                innermostWhereExpression = expr
            End If
    
            Me.Visit(expr.Arguments(0))
    
            Return expr
        End Function
    End Class
    
    using System;
    using System.Linq.Expressions;
    
    namespace LinqToTerraServerProvider
    {
        internal class InnermostWhereFinder : ExpressionVisitor
        {
            private MethodCallExpression innermostWhereExpression;
    
            public MethodCallExpression GetInnermostWhere(Expression expression)
            {
                Visit(expression);
                return innermostWhereExpression;
            }
    
            protected override Expression VisitMethodCall(MethodCallExpression expression)
            {
                if (expression.Method.Name == "Where")
                    innermostWhereExpression = expression;
    
                Visit(expression.Arguments[0]);
    
                return expression;
            }
        }
    }
    

    Ta klasa dziedziczy klasy podstawowej wyrażenie drzewo odwiedzających do wykonywania funkcji znajdowania określonego wyrażenia.Klasy podstawowej wyrażenie drzewo użytkownik ma na celu być dziedziczone i specjalistyczne dla określonego zadania, który obejmuje przechodzenie do drzewa wyrażenie.Przesłonięcia klasy pochodne VisitMethodCall metoda do poszukiwania wyrażenie, które reprezentuje znajdujące się najniżej wywołanie Where w drzewa wyrażenie, które reprezentuje kwerendy klienta.Wewnętrzne wyrażenie to jest wyrażenie wyodrębniające lokalizacje wyszukiwania od dostawcy.

  2. Dodaj using dyrektyw (Imports instrukcji w języku Visual Basic) do pliku następujące obszary nazw: System.Collections.Generic, System.Collections.ObjectModel i System.Linq.Expressions.

Aby utworzyć użytkownika, który wyodrębnia dane kwerendy usługi sieci Web

  • Dodaj LocationFinder klasy do projektu.

    Imports System.Linq.Expressions
    Imports ETH = LinqToTerraServerProvider.ExpressionTreeHelpers
    
    Friend Class LocationFinder
        Inherits ExpressionVisitor
    
        Private _expression As Expression
        Private _locations As List(Of String)
    
        Public Sub New(ByVal exp As Expression)
            Me._expression = exp
        End Sub
    
        Public ReadOnly Property Locations() As List(Of String)
            Get
                If _locations Is Nothing Then
                    _locations = New List(Of String)()
                    Me.Visit(Me._expression)
                End If
                Return Me._locations
            End Get
        End Property
    
        Protected Overrides Function VisitBinary(ByVal be As BinaryExpression) As Expression
            ' Handles Visual Basic String semantics.
            be = ETH.ConvertVBStringCompare(be)
    
            If be.NodeType = ExpressionType.Equal Then
                If (ETH.IsMemberEqualsValueExpression(be, GetType(Place), "Name")) Then
                    _locations.Add(ETH.GetValueFromEqualsExpression(be, GetType(Place), "Name"))
                    Return be
                ElseIf (ETH.IsMemberEqualsValueExpression(be, GetType(Place), "State")) Then
                    _locations.Add(ETH.GetValueFromEqualsExpression(be, GetType(Place), "State"))
                    Return be
                Else
                    Return MyBase.VisitBinary(be)
                End If
            Else
                Return MyBase.VisitBinary(be)
            End If
        End Function
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    
    namespace LinqToTerraServerProvider
    {
        internal class LocationFinder : ExpressionVisitor
        {
            private Expression expression;
            private List<string> locations;
    
            public LocationFinder(Expression exp)
            {
                this.expression = exp;
            }
    
            public List<string> Locations
            {
                get
                {
                    if (locations == null)
                    {
                        locations = new List<string>();
                        this.Visit(this.expression);
                    }
                    return this.locations;
                }
            }
    
            protected override Expression VisitBinary(BinaryExpression be)
            {
                if (be.NodeType == ExpressionType.Equal)
                {
                    if (ExpressionTreeHelpers.IsMemberEqualsValueExpression(be, typeof(Place), "Name"))
                    {
                        locations.Add(ExpressionTreeHelpers.GetValueFromEqualsExpression(be, typeof(Place), "Name"));
                        return be;
                    }
                    else if (ExpressionTreeHelpers.IsMemberEqualsValueExpression(be, typeof(Place), "State"))
                    {
                        locations.Add(ExpressionTreeHelpers.GetValueFromEqualsExpression(be, typeof(Place), "State"));
                        return be;
                    }
                    else
                        return base.VisitBinary(be);
                }
                else
                    return base.VisitBinary(be);
            }
        }
    }
    

    Ta klasa jest używana, aby wyodrębnić informacje o lokalizacji z predykatu, który klient przekazuje do Queryable.Where.Wynika to z ExpressionVisitor klasy i przesłonięcia tylko VisitBinary metoda.

    ExpressionVisitor Klasy wysyła binarny wyrażeń, takich jak równość wyrażeń, jak place.Name == "Seattle" (place.Name = "Seattle" w Visual Basic), do VisitBinary metoda.W tym przesłanianie VisitBinary metodę, jeśli wyrażenie pasuje do wzorca wyrażenie równości, który może dostarczać informacje o lokalizacji, że informacje jest ekstrahowana i przechowywane w listę lokalizacji.

    Klasa ta używa gościem drzewa wyrażenie znaleźć informacje o lokalizacji w drzewie wyrażenie, ponieważ użytkownik jest przeznaczony dla przejeżdżające i analizie wyrażenie drzew.Kod wynikowy jest lepsze i mniej podatne niż jeśli miał została dokonana bez przy użyciu przez osobę odwiedzającą.

    Na tym etapie instruktażu dostawca obsługuje tylko ograniczony sposoby dostarczania informacji o lokalizacji, w kwerendzie.W dalszej części tematu należy dodać funkcję umożliwiającą więcej sposobów dostarczania informacji o lokalizacji.

Aby utworzyć użytkownika, który modyfikuje drzewa wyrażenie

  • Dodaj ExpressionTreeModifier klasy do projektu.

    Imports System.Linq.Expressions
    
    Friend Class ExpressionTreeModifier
        Inherits ExpressionVisitor
    
        Private queryablePlaces As IQueryable(Of Place)
    
        Friend Sub New(ByVal places As IQueryable(Of Place))
            Me.queryablePlaces = places
        End Sub
    
        Protected Overrides Function VisitConstant(ByVal c As ConstantExpression) As Expression
            ' Replace the constant QueryableTerraServerData arg with the queryable Place collection.
            If c.Type Is GetType(QueryableTerraServerData(Of Place)) Then
                Return Expression.Constant(Me.queryablePlaces)
            Else
                Return c
            End If
        End Function
    End Class
    
    using System;
    using System.Linq;
    using System.Linq.Expressions;
    
    namespace LinqToTerraServerProvider
    {
        internal class ExpressionTreeModifier : ExpressionVisitor
        {
            private IQueryable<Place> queryablePlaces;
    
            internal ExpressionTreeModifier(IQueryable<Place> places)
            {
                this.queryablePlaces = places;
            }
    
            protected override Expression VisitConstant(ConstantExpression c)
            {
                // Replace the constant QueryableTerraServerData arg with the queryable Place collection.
                if (c.Type == typeof(QueryableTerraServerData<Place>))
                    return Expression.Constant(this.queryablePlaces);
                else
                    return c;
            }
        }
    }
    

    Ta klasa pochodzi od ExpressionVisitor klasy i przesłonięcia VisitConstant metoda.W tej metodzie zastępuje obiekt, który wywołanie operatora wewnętrzne kwerendy standardowe jest stosowane do betonu wykaz z Place obiektów.

    Ta klasa modyfikator drzewa wyrażenie używa odwiedzający drzewa wyrażenie, ponieważ użytkownik ma na celu przy przechodzeniu przez folder, badanie i kopiowania drzewa wyrażenie.Przez wynikających z klasy podstawowej wyrażenie drzewo odwiedzających, ta klasa wymaga minimalny kod do wykonywania jego funkcji.

Dodawanie modułu szacującego wyrażenie

Predykatu, który jest przekazywany do Queryable.Where metoda w kwerendzie klienta mogą zawierać sub-expressions, które nie zależą od parametru Wyrażenie lambda.Te sub-expressions na białym tle mogą i powinny być oceniane natychmiast.Mogą być odwołania do zmiennych lokalnych lub zmienne składowe, które muszą być przetłumaczone na wartości.

Klasy następny opisuje metodę, PartialEval(Expression), który określa, które ewentualnie kontaktach w wyrażeniu można oszacować natychmiast.Tworząc wyrażenie lambda, zestawiania i wywoływanie delegata zwracane są następnie oblicza te wyrażenia.Wreszcie to zastępuje poddrzewa nowego węzła, który reprezentuje wartość stałą.Jest to znane jako częściowej oceny.

Aby dodać klasę do wykonywania oceny częściowe drzewa wyrażenie

  • Dodaj Evaluator klasy do projektu.

    Imports System.Linq.Expressions
    
    Public Module Evaluator
        ''' <summary>Performs evaluation and replacement of independent sub-trees</summary>
        ''' <param name="expr">The root of the expression tree.</param>
        ''' <param name="fnCanBeEvaluated">A function that decides whether a given expression node can be part of the local function.</param>
        ''' <returns>A new tree with sub-trees evaluated and replaced.</returns>
        Public Function PartialEval(
            ByVal expr As Expression, 
            ByVal fnCanBeEvaluated As Func(Of Expression, Boolean)
            )  As Expression
    
            Return New SubtreeEvaluator(New Nominator(fnCanBeEvaluated).Nominate(expr)).Eval(expr)
        End Function
    
        ''' <summary>
        ''' Performs evaluation and replacement of independent sub-trees
        ''' </summary>
        ''' <param name="expression">The root of the expression tree.</param>
        ''' <returns>A new tree with sub-trees evaluated and replaced.</returns>
        Public Function PartialEval(ByVal expression As Expression) As Expression
            Return PartialEval(expression, AddressOf Evaluator.CanBeEvaluatedLocally)
        End Function
    
        Private Function CanBeEvaluatedLocally(ByVal expression As Expression) As Boolean
            Return expression.NodeType <> ExpressionType.Parameter
        End Function
    
        ''' <summary>
        ''' Evaluates and replaces sub-trees when first candidate is reached (top-down)
        ''' </summary>
        Class SubtreeEvaluator
            Inherits ExpressionVisitor
    
            Private candidates As HashSet(Of Expression)
    
            Friend Sub New(ByVal candidates As HashSet(Of Expression))
                Me.candidates = candidates
            End Sub
    
            Friend Function Eval(ByVal exp As Expression) As Expression
                Return Me.Visit(exp)
            End Function
    
            Public Overrides Function Visit(ByVal exp As Expression) As Expression
                If exp Is Nothing Then
                    Return Nothing
                ElseIf Me.candidates.Contains(exp) Then
                    Return Me.Evaluate(exp)
                End If
    
                Return MyBase.Visit(exp)
            End Function
    
            Private Function Evaluate(ByVal e As Expression) As Expression
                If e.NodeType = ExpressionType.Constant Then
                    Return e
                End If
    
                Dim lambda = Expression.Lambda(e)
                Dim fn As [Delegate] = lambda.Compile()
    
                Return Expression.Constant(fn.DynamicInvoke(Nothing), e.Type)
            End Function
        End Class
    
    
        ''' <summary>
        ''' Performs bottom-up analysis to determine which nodes can possibly
        ''' be part of an evaluated sub-tree.
        ''' </summary>
        Class Nominator
            Inherits ExpressionVisitor
    
            Private fnCanBeEvaluated As Func(Of Expression, Boolean)
            Private candidates As HashSet(Of Expression)
            Private cannotBeEvaluated As Boolean
    
            Friend Sub New(ByVal fnCanBeEvaluated As Func(Of Expression, Boolean))
                Me.fnCanBeEvaluated = fnCanBeEvaluated
            End Sub
    
            Friend Function Nominate(ByVal expr As Expression) As HashSet(Of Expression)
                Me.candidates = New HashSet(Of Expression)()
                Me.Visit(expr)
    
                Return Me.candidates
            End Function
    
            Public Overrides Function Visit(ByVal expr As Expression) As Expression
                If expr IsNot Nothing Then
    
                    Dim saveCannotBeEvaluated = Me.cannotBeEvaluated
                    Me.cannotBeEvaluated = False
    
                    MyBase.Visit(expr)
    
                    If Not Me.cannotBeEvaluated Then
                        If Me.fnCanBeEvaluated(expr) Then
                            Me.candidates.Add(expr)
                        Else
                            Me.cannotBeEvaluated = True
                        End If
                    End If
    
                    Me.cannotBeEvaluated = Me.cannotBeEvaluated Or 
                                           saveCannotBeEvaluated
                End If
    
                Return expr
            End Function
        End Class
    End Module
    
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    
    namespace LinqToTerraServerProvider
    {
        public static class Evaluator
        {
            /// <summary>
            /// Performs evaluation & replacement of independent sub-trees
            /// </summary>
            /// <param name="expression">The root of the expression tree.</param>
            /// <param name="fnCanBeEvaluated">A function that decides whether a given expression node can be part of the local function.</param>
            /// <returns>A new tree with sub-trees evaluated and replaced.</returns>
            public static Expression PartialEval(Expression expression, Func<Expression, bool> fnCanBeEvaluated)
            {
                return new SubtreeEvaluator(new Nominator(fnCanBeEvaluated).Nominate(expression)).Eval(expression);
            }
    
            /// <summary>
            /// Performs evaluation & replacement of independent sub-trees
            /// </summary>
            /// <param name="expression">The root of the expression tree.</param>
            /// <returns>A new tree with sub-trees evaluated and replaced.</returns>
            public static Expression PartialEval(Expression expression)
            {
                return PartialEval(expression, Evaluator.CanBeEvaluatedLocally);
            }
    
            private static bool CanBeEvaluatedLocally(Expression expression)
            {
                return expression.NodeType != ExpressionType.Parameter;
            }
    
            /// <summary>
            /// Evaluates & replaces sub-trees when first candidate is reached (top-down)
            /// </summary>
            class SubtreeEvaluator : ExpressionVisitor
            {
                HashSet<Expression> candidates;
    
                internal SubtreeEvaluator(HashSet<Expression> candidates)
                {
                    this.candidates = candidates;
                }
    
                internal Expression Eval(Expression exp)
                {
                    return this.Visit(exp);
                }
    
                public override Expression Visit(Expression exp)
                {
                    if (exp == null)
                    {
                        return null;
                    }
                    if (this.candidates.Contains(exp))
                    {
                        return this.Evaluate(exp);
                    }
                    return base.Visit(exp);
                }
    
                private Expression Evaluate(Expression e)
                {
                    if (e.NodeType == ExpressionType.Constant)
                    {
                        return e;
                    }
                    LambdaExpression lambda = Expression.Lambda(e);
                    Delegate fn = lambda.Compile();
                    return Expression.Constant(fn.DynamicInvoke(null), e.Type);
                }
            }
    
            /// <summary>
            /// Performs bottom-up analysis to determine which nodes can possibly
            /// be part of an evaluated sub-tree.
            /// </summary>
            class Nominator : ExpressionVisitor
            {
                Func<Expression, bool> fnCanBeEvaluated;
                HashSet<Expression> candidates;
                bool cannotBeEvaluated;
    
                internal Nominator(Func<Expression, bool> fnCanBeEvaluated)
                {
                    this.fnCanBeEvaluated = fnCanBeEvaluated;
                }
    
                internal HashSet<Expression> Nominate(Expression expression)
                {
                    this.candidates = new HashSet<Expression>();
                    this.Visit(expression);
                    return this.candidates;
                }
    
                public override Expression Visit(Expression expression)
                {
                    if (expression != null)
                    {
                        bool saveCannotBeEvaluated = this.cannotBeEvaluated;
                        this.cannotBeEvaluated = false;
                        base.Visit(expression);
                        if (!this.cannotBeEvaluated)
                        {
                            if (this.fnCanBeEvaluated(expression))
                            {
                                this.candidates.Add(expression);
                            }
                            else
                            {
                                this.cannotBeEvaluated = true;
                            }
                        }
                        this.cannotBeEvaluated |= saveCannotBeEvaluated;
                    }
                    return expression;
                }
            }
        }
    }
    

Dodawanie klasy pomocy

Ta sekcja zawiera kod na trzy klasy pomocy dla dostawcy.

Aby dodać klasy pomocy, który jest używany przez implementację System.Linq.IQueryProvider

  • Dodaj TypeSystem klasy (lub moduł w Visual Basic) do projektu.

    Imports System.Collections.Generic
    
    Friend Module TypeSystem
    
        Friend Function GetElementType(ByVal seqType As Type) As Type
            Dim ienum As Type = FindIEnumerable(seqType)
    
            If ienum Is Nothing Then
                Return seqType
            End If
    
            Return ienum.GetGenericArguments()(0)
        End Function
    
        Private Function FindIEnumerable(ByVal seqType As Type) As Type
    
            If seqType Is Nothing Or seqType Is GetType(String) Then
                Return Nothing
            End If
    
            If (seqType.IsArray) Then
                Return GetType(IEnumerable(Of )).MakeGenericType(seqType.GetElementType())
            End If
    
            If (seqType.IsGenericType) Then
                For Each arg As Type In seqType.GetGenericArguments()
                    Dim ienum As Type = GetType(IEnumerable(Of )).MakeGenericType(arg)
    
                    If (ienum.IsAssignableFrom(seqType)) Then
                        Return ienum
                    End If
                Next
            End If
    
            Dim ifaces As Type() = seqType.GetInterfaces()
    
            If ifaces IsNot Nothing And ifaces.Length > 0 Then
                For Each iface As Type In ifaces
                    Dim ienum As Type = FindIEnumerable(iface)
    
                    If (ienum IsNot Nothing) Then
                        Return ienum
                    End If
                Next
            End If
    
            If seqType.BaseType IsNot Nothing AndAlso
               seqType.BaseType IsNot GetType(Object) Then
    
                Return FindIEnumerable(seqType.BaseType)
            End If
    
            Return Nothing
        End Function
    End Module
    
    using System;
    using System.Collections.Generic;
    
    namespace LinqToTerraServerProvider
    {
        internal static class TypeSystem
        {
            internal static Type GetElementType(Type seqType)
            {
                Type ienum = FindIEnumerable(seqType);
                if (ienum == null) return seqType;
                return ienum.GetGenericArguments()[0];
            }
    
            private static Type FindIEnumerable(Type seqType)
            {
                if (seqType == null || seqType == typeof(string))
                    return null;
    
                if (seqType.IsArray)
                    return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());
    
                if (seqType.IsGenericType)
                {
                    foreach (Type arg in seqType.GetGenericArguments())
                    {
                        Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);
                        if (ienum.IsAssignableFrom(seqType))
                        {
                            return ienum;
                        }
                    }
                }
    
                Type[] ifaces = seqType.GetInterfaces();
                if (ifaces != null && ifaces.Length > 0)
                {
                    foreach (Type iface in ifaces)
                    {
                        Type ienum = FindIEnumerable(iface);
                        if (ienum != null) return ienum;
                    }
                }
    
                if (seqType.BaseType != null && seqType.BaseType != typeof(object))
                {
                    return FindIEnumerable(seqType.BaseType);
                }
    
                return null;
            }
        }
    }
    

    IQueryProvider Wykonania, który dodano wcześniej używa tej klasy pomocy.

    TypeSystem.GetElementTypeużywa odbicie w celu uzyskania argumentu typu rodzajowego IEnumerable<T> (IEnumerable(Of T) w Visual Basic) kolekcji.Ta metoda jest wywoływana z nie uniwersalne CreateQuery metoda w celu wykonania dostawca kwerendy do dostarczania typu element zbioru wyników kwerendy.

    Ta klasa nie jest specyficzne dla tego usługodawca sieci Web Szukacz USA.W związku z tym, może zostać użyty do dowolnego LINQ dostawcy.

Aby utworzyć wyrażenie drzewo klasa

  • Dodaj ExpressionTreeHelpers klasy do projektu.

    Imports System.Linq.Expressions
    
    Friend Class ExpressionTreeHelpers
        ' Visual Basic encodes string comparisons as a method call to
        ' Microsoft.VisualBasic.CompilerServices.Operators.CompareString.
        ' This method will convert the method call into a binary operation instead.
        ' Note that this makes the string comparison case sensitive.
        Friend Shared Function ConvertVBStringCompare(ByVal exp As BinaryExpression) As BinaryExpression
    
            If exp.Left.NodeType = ExpressionType.Call Then
                Dim compareStringCall = CType(exp.Left, MethodCallExpression)
    
                If compareStringCall.Method.DeclaringType.FullName = 
                    "Microsoft.VisualBasic.CompilerServices.Operators" AndAlso 
                    compareStringCall.Method.Name = "CompareString" Then
    
                    Dim arg1 = compareStringCall.Arguments(0)
                    Dim arg2 = compareStringCall.Arguments(1)
    
                    Select Case exp.NodeType
                        Case ExpressionType.LessThan
                            Return Expression.LessThan(arg1, arg2)
                        Case ExpressionType.LessThanOrEqual
                            Return Expression.GreaterThan(arg1, arg2)
                        Case ExpressionType.GreaterThan
                            Return Expression.GreaterThan(arg1, arg2)
                        Case ExpressionType.GreaterThanOrEqual
                            Return Expression.GreaterThanOrEqual(arg1, arg2)
                        Case Else
                            Return Expression.Equal(arg1, arg2)
                    End Select
                End If
            End If
            Return exp
        End Function
    
        Friend Shared Function IsMemberEqualsValueExpression(
            ByVal exp As Expression, 
            ByVal declaringType As Type, 
            ByVal memberName As String) As Boolean
    
            If exp.NodeType <> ExpressionType.Equal Then
                Return False
            End If
    
            Dim be = CType(exp, BinaryExpression)
    
            ' Assert.
            If IsSpecificMemberExpression(be.Left, declaringType, memberName) AndAlso 
               IsSpecificMemberExpression(be.Right, declaringType, memberName) Then
    
                Throw New Exception("Cannot have 'member' = 'member' in an expression!")
            End If
    
            Return IsSpecificMemberExpression(be.Left, declaringType, memberName) OrElse 
                   IsSpecificMemberExpression(be.Right, declaringType, memberName)
        End Function
    
    
        Friend Shared Function IsSpecificMemberExpression(
            ByVal exp As Expression, 
            ByVal declaringType As Type, 
            ByVal memberName As String) As Boolean
    
            Return (TypeOf exp Is MemberExpression) AndAlso 
                   (CType(exp, MemberExpression).Member.DeclaringType Is declaringType) AndAlso 
                   (CType(exp, MemberExpression).Member.Name = memberName)
        End Function
    
    
        Friend Shared Function GetValueFromEqualsExpression(
            ByVal be As BinaryExpression, 
            ByVal memberDeclaringType As Type, 
            ByVal memberName As String) As String
    
            If be.NodeType <> ExpressionType.Equal Then
                Throw New Exception("There is a bug in this program.")
            End If
    
            If be.Left.NodeType = ExpressionType.MemberAccess Then
                Dim mEx = CType(be.Left, MemberExpression)
    
                If mEx.Member.DeclaringType Is memberDeclaringType AndAlso 
                   mEx.Member.Name = memberName Then
                    Return GetValueFromExpression(be.Right)
                End If
            ElseIf be.Right.NodeType = ExpressionType.MemberAccess Then
                Dim mEx = CType(be.Right, MemberExpression)
    
                If mEx.Member.DeclaringType Is memberDeclaringType AndAlso 
                   mEx.Member.Name = memberName Then
                    Return GetValueFromExpression(be.Left)
                End If
            End If
    
            ' We should have returned by now.
            Throw New Exception("There is a bug in this program.")
        End Function
    
        Friend Shared Function GetValueFromExpression(ByVal expr As expression) As String
            If expr.NodeType = ExpressionType.Constant Then
                Return CStr(CType(expr, ConstantExpression).Value)
            Else
                Dim s = "The expression type {0} is not supported to obtain a value."
                Throw New InvalidQueryException(String.Format(s, expr.NodeType))
            End If
        End Function
    End Class
    
    using System;
    using System.Linq.Expressions;
    
    namespace LinqToTerraServerProvider
    {
        internal class ExpressionTreeHelpers
        {
            internal static bool IsMemberEqualsValueExpression(Expression exp, Type declaringType, string memberName)
            {
                if (exp.NodeType != ExpressionType.Equal)
                    return false;
    
                BinaryExpression be = (BinaryExpression)exp;
    
                // Assert.
                if (ExpressionTreeHelpers.IsSpecificMemberExpression(be.Left, declaringType, memberName) &&
                    ExpressionTreeHelpers.IsSpecificMemberExpression(be.Right, declaringType, memberName))
                    throw new Exception("Cannot have 'member' == 'member' in an expression!");
    
                return (ExpressionTreeHelpers.IsSpecificMemberExpression(be.Left, declaringType, memberName) ||
                    ExpressionTreeHelpers.IsSpecificMemberExpression(be.Right, declaringType, memberName));
            }
    
            internal static bool IsSpecificMemberExpression(Expression exp, Type declaringType, string memberName)
            {
                return ((exp is MemberExpression) &&
                    (((MemberExpression)exp).Member.DeclaringType == declaringType) &&
                    (((MemberExpression)exp).Member.Name == memberName));
            }
    
            internal static string GetValueFromEqualsExpression(BinaryExpression be, Type memberDeclaringType, string memberName)
            {
                if (be.NodeType != ExpressionType.Equal)
                    throw new Exception("There is a bug in this program.");
    
                if (be.Left.NodeType == ExpressionType.MemberAccess)
                {
                    MemberExpression me = (MemberExpression)be.Left;
    
                    if (me.Member.DeclaringType == memberDeclaringType && me.Member.Name == memberName)
                    {
                        return GetValueFromExpression(be.Right);
                    }
                }
                else if (be.Right.NodeType == ExpressionType.MemberAccess)
                {
                    MemberExpression me = (MemberExpression)be.Right;
    
                    if (me.Member.DeclaringType == memberDeclaringType && me.Member.Name == memberName)
                    {
                        return GetValueFromExpression(be.Left);
                    }
                }
    
                // We should have returned by now.
                throw new Exception("There is a bug in this program.");
            }
    
            internal static string GetValueFromExpression(Expression expression)
            {
                if (expression.NodeType == ExpressionType.Constant)
                    return (string)(((ConstantExpression)expression).Value);
                else
                    throw new InvalidQueryException(
                        String.Format("The expression type {0} is not supported to obtain a value.", expression.NodeType));
            }
        }
    }
    

    Ta klasa zawiera metody, które służy do określenia informacji o i wyodrębnienia danych z określonych rodzajów drzew wyrażenie.W tym dostawcą, metody te są używane przez LocationFinder klasy, aby wyodrębnić informacje o lokalizacji z drzewa wyrażenie, które reprezentuje kwerendy.

Aby dodać typ wyjątku nieprawidłowa kwerend

  • Dodaj InvalidQueryException klasy do projektu.

    Public Class InvalidQueryException
        Inherits Exception
    
        Private _message As String
    
        Public Sub New(ByVal message As String)
            Me._message = message & " "
        End Sub
    
        Public Overrides ReadOnly Property Message() As String
            Get
                Return "The client query is invalid: " & _message
            End Get
        End Property
    End Class
    
    using System;
    
    namespace LinqToTerraServerProvider
    {
        class InvalidQueryException : System.Exception
        {
            private string message;
    
            public InvalidQueryException(string message)
            {
                this.message = message + " ";
            }
    
            public override string Message
            {
                get
                {
                    return "The client query is invalid: " + message;
                }
            }
        }
    }
    

    Ta klasa definiuje Exception typu, który dostawca mogą rzucać, gdy nie rozumie LINQ kwerendę od klienta.Definiując tego typu wyjątku kwerendy nieprawidłowy dostawca można rzucić wyjątku określonego w więcej niż tylko Exception z różnych miejsc w kodzie.

Wszystkie elementy, które są wymagane do kompilacji dostawcy zostało dodane.Budowanie LinqToTerraServerProvider projektu i sprawdzić, czy nie ma żadnych błędów kompilacji.

Testowanie dostawca programu LINQ

Można przetestować swój LINQ dostawcy przez tworzenie aplikacji klienta, który zawiera LINQ kwerendy przed źródła danych.

Aby utworzyć aplikację klient do testowania swojego dostawcy

  1. Dodaj nowy Console Application Tworzenie projektu i nadaj mu nazwę ClientApp.

  2. W nowym projekcie należy dodać odwołanie do zestawu dostawcy.

  3. Przeciągnij app.config pliku z Twój projekt do projektu klienta.(Ten plik jest niezbędne do komunikacji z usługą sieci Web).

    [!UWAGA]

    W Visual Basic, musisz kliknąć Pokaż wszystkie pliki przycisk, aby zobaczyć app.config pliku w Solution Explorer.

  4. Dodaj następujący using instrukcji (Imports instrukcja w Visual Basic) do Program.cs (lub Module1.vb w Visual Basic) pliku:

    using System;
    using System.Linq;
    using LinqToTerraServerProvider;
    
    Imports LinqToTerraServerProvider
    
  5. W Main metoda w pliku Program.cs (lub Module1.vb w Visual Basic), wstawić następujący kod:

    QueryableTerraServerData<Place> terraPlaces = new QueryableTerraServerData<Place>();
    
    var query = from place in terraPlaces
                where place.Name == "Johannesburg"
                select place.PlaceType;
    
    foreach (PlaceType placeType in query)
        Console.WriteLine(placeType);
    
    Dim terraPlaces As New QueryableTerraServerData(Of Place)
    
    Dim query = From place In terraPlaces 
                Where place.Name = "Johannesburg" 
                Select place.PlaceType
    
    For Each placeType In query
        Console.WriteLine(placeType.ToString())
    Next
    

    Kod ten tworzy nowe wystąpienie IQueryable<T> typu zdefiniowanego w dostawcy i kwerend, które obiekt za pomocą LINQ.Kwerendy określa lokalizację uzyskanie danych przy użyciu wyrażenia równości.Ponieważ źródło danych implementuje IQueryable, kompilator przekłada składni wyrażenia kwerendy wywołania operatorów kwerendy standardowe zdefiniowane w Queryable.Wewnętrznie, metody te kwerendy standardowe operator budowy drzewa wyrażenie i wywołanie Execute lub CreateQuery metod, które są realizowane w ramach swojej IQueryProvider realizacji.

  6. Budowanie ClientApp.

  7. Ustawianie aplikacji klienta jako projekt "Uruchamianie" dla rozwiązania.W Solution Explorer, kliknij prawym przyciskiem myszy ClientApp projekt i zaznacz jako projekt uruchamiania.

  8. Uruchom program i wyświetlić wyniki.Powinno być około trzech wyników.

Dodanie możliwości bardziej złożone kwerendy

Dostawca, który ma do tego punktu zapewnia bardzo ograniczony sposób dla klientów, aby określić informacje o lokalizacji, w LINQ kwerendy.W szczególności, dostawca jest tylko możliwość uzyskania informacji o lokalizacji z równości wyrażeń takich jak Place.Name == "Seattle" lub Place.State == "Alaska" (Place.Name = "Seattle" lub Place.State = "Alaska" w Visual Basic).

Następna procedura pokazuje, jak dodać obsługę dodatkowy sposób określania informacji o lokalizacji.Po dodaniu tego kodu dostawcy będą mogli wyodrębnić informacje o lokalizacji z wyrażenia wywołania metody, takie jak place.Name.StartsWith("Seat").

Aby dodać obsługę predykatów, które zawierają String.StartsWith

  1. W LinqToTerraServerProvider projektu, dodać VisitMethodCall metoda LocationFinder definicji klasy.

    Protected Overrides Function VisitMethodCall(ByVal m As MethodCallExpression) As Expression
        If m.Method.DeclaringType Is GetType(String) And m.Method.Name = "StartsWith" Then
            If ETH.IsSpecificMemberExpression(m.Object, GetType(Place), "Name") OrElse
               ETH.IsSpecificMemberExpression(m.Object, GetType(Place), "State") Then
                _locations.Add(ETH.GetValueFromExpression(m.Arguments(0)))
                Return m
            End If
        End If
    
        Return MyBase.VisitMethodCall(m)
    End Function
    
    protected override Expression VisitMethodCall(MethodCallExpression m)
    {
        if (m.Method.DeclaringType == typeof(String) && m.Method.Name == "StartsWith")
        {
            if (ExpressionTreeHelpers.IsSpecificMemberExpression(m.Object, typeof(Place), "Name") ||
            ExpressionTreeHelpers.IsSpecificMemberExpression(m.Object, typeof(Place), "State"))
            {
                locations.Add(ExpressionTreeHelpers.GetValueFromExpression(m.Arguments[0]));
                return m;
            }
        }
    
        return base.VisitMethodCall(m);
    }
    
  2. Ponowna kompilacja LinqToTerraServerProvider projektu.

  3. Aby przetestować nowe możliwości swojego dostawcy, otwórz plik Program.cs (lub Module1.vb w Visual Basic) w ClientApp projektu.Zastąp kod w Main metoda następujący kod:

    QueryableTerraServerData<Place> terraPlaces = new QueryableTerraServerData<Place>();
    
    var query = from place in terraPlaces
                where place.Name.StartsWith("Lond")
                select new { place.Name, place.State };
    
    foreach (var obj in query)
        Console.WriteLine(obj);
    
    Dim terraPlaces As New QueryableTerraServerData(Of Place)
    
    Dim query = From place In terraPlaces 
                Where place.Name.StartsWith("Lond") 
                Select place.Name, place.State
    
    For Each obj In query
        Console.WriteLine(obj)
    Next
    
  4. Uruchom program i wyświetlić wyniki.Powinno być około 29 wyników.

Następna procedura pokazuje, jak dodawanie funkcjonalności do dostawcy, aby włączyć kwerendy klienta określić informacje o lokalizacji za pomocą dwóch dodatkowych metod, w szczególności Enumerable.Contains i List<T>.Contains.Po dodaniu tego kodu dostawcy będą mogli wyodrębnić informacje o lokalizacji z wyrażenia w kwerendzie klienta, wywołanie metody, takie jak placeList.Contains(place.Name), gdzie placeList kolekcji jest lista konkretnych dostarczonych przez klienta.Zaletą poinformowanie klientów, użyj Contains metoda jest, że można określić dowolną liczbę lokalizacji tylko przez dodanie ich do placeList.Różnicowanie liczby lokalizacji nie powoduje zmiany składni kwerendy.

Aby dodać obsługę kwerend, które mają metoda zawiera w ich klauzuli 'where'

  1. W LinqToTerraServerProvider projektu w LocationFinder klasy definicji, zastąpić VisitMethodCall metoda następujący kod:

    Protected Overrides Function VisitMethodCall(ByVal m As MethodCallExpression) As Expression
        If m.Method.DeclaringType Is GetType(String) And m.Method.Name = "StartsWith" Then
            If ETH.IsSpecificMemberExpression(m.Object, GetType(Place), "Name") OrElse
               ETH.IsSpecificMemberExpression(m.Object, GetType(Place), "State") Then
                _locations.Add(ETH.GetValueFromExpression(m.Arguments(0)))
                Return m
            End If
        ElseIf m.Method.Name = "Contains" Then
            Dim valuesExpression As Expression = Nothing
    
            If m.Method.DeclaringType Is GetType(Enumerable) Then
                If ETH.IsSpecificMemberExpression(m.Arguments(1), GetType(Place), "Name") OrElse
                   ETH.IsSpecificMemberExpression(m.Arguments(1), GetType(Place), "State") Then
                    valuesExpression = m.Arguments(0)
                End If
    
            ElseIf m.Method.DeclaringType Is GetType(List(Of String)) Then
                If ETH.IsSpecificMemberExpression(m.Arguments(0), GetType(Place), "Name") OrElse
                   ETH.IsSpecificMemberExpression(m.Arguments(0), GetType(Place), "State") Then
                    valuesExpression = m.Object
                End If
            End If
    
            If valuesExpression Is Nothing OrElse valuesExpression.NodeType <> ExpressionType.Constant Then
                Throw New Exception("Could not find the location values.")
            End If
    
            Dim ce = CType(valuesExpression, ConstantExpression)
    
            Dim placeStrings = CType(ce.Value, IEnumerable(Of String))
            ' Add each string in the collection to the list of locations to obtain data about.
            For Each place In placeStrings
                _locations.Add(place)
            Next
    
            Return m
        End If
    
        Return MyBase.VisitMethodCall(m)
    End Function
    
    protected override Expression VisitMethodCall(MethodCallExpression m)
    {
        if (m.Method.DeclaringType == typeof(String) && m.Method.Name == "StartsWith")
        {
            if (ExpressionTreeHelpers.IsSpecificMemberExpression(m.Object, typeof(Place), "Name") ||
            ExpressionTreeHelpers.IsSpecificMemberExpression(m.Object, typeof(Place), "State"))
            {
                locations.Add(ExpressionTreeHelpers.GetValueFromExpression(m.Arguments[0]));
                return m;
            }
    
        }
        else if (m.Method.Name == "Contains")
        {
            Expression valuesExpression = null;
    
            if (m.Method.DeclaringType == typeof(Enumerable))
            {
                if (ExpressionTreeHelpers.IsSpecificMemberExpression(m.Arguments[1], typeof(Place), "Name") ||
                ExpressionTreeHelpers.IsSpecificMemberExpression(m.Arguments[1], typeof(Place), "State"))
                {
                    valuesExpression = m.Arguments[0];
                }
            }
            else if (m.Method.DeclaringType == typeof(List<string>))
            {
                if (ExpressionTreeHelpers.IsSpecificMemberExpression(m.Arguments[0], typeof(Place), "Name") ||
                ExpressionTreeHelpers.IsSpecificMemberExpression(m.Arguments[0], typeof(Place), "State"))
                {
                    valuesExpression = m.Object;
                }
            }
    
            if (valuesExpression == null || valuesExpression.NodeType != ExpressionType.Constant)
                throw new Exception("Could not find the location values.");
    
            ConstantExpression ce = (ConstantExpression)valuesExpression;
    
            IEnumerable<string> placeStrings = (IEnumerable<string>)ce.Value;
            // Add each string in the collection to the list of locations to obtain data about.
            foreach (string place in placeStrings)
                locations.Add(place);
    
            return m;
        }
    
        return base.VisitMethodCall(m);
    }
    

    Metoda ta dodaje każdy ciąg w kolekcji, Contains jest stosowany do listy lokalizacji, aby wykonać kwerendę sieci Web dodatku service z.Metoda o nazwie Contains jest zdefiniowany w obu Enumerable i List<T>.W związku z tym VisitMethodCall metoda musi sprawdzić dla obu tych typów deklarowania.Enumerable.Containsjest zdefiniowany jako metodę rozszerzenia; Dlatego zbioru, który jest stosowany do jest faktycznie pierwszy argument do metody.List.Containsjest zdefiniowany jako metodę wystąpienia; Dlatego zbioru, który jest stosowany do jest obiektem odbierającym metody.

  2. Ponowna kompilacja LinqToTerraServerProvider projektu.

  3. Aby przetestować nowe możliwości swojego dostawcy, otwórz plik Program.cs (lub Module1.vb w Visual Basic) w ClientApp projektu.Zastąp kod w Main metoda następujący kod:

    QueryableTerraServerData<Place> terraPlaces = new QueryableTerraServerData<Place>();
    
    string[] places = { "Johannesburg", "Yachats", "Seattle" };
    
    var query = from place in terraPlaces
                where places.Contains(place.Name)
                orderby place.State
                select new { place.Name, place.State };
    
    foreach (var obj in query)
        Console.WriteLine(obj);
    
    Dim terraPlaces As New QueryableTerraServerData(Of Place)
    
    Dim places = New String() {"Johannesburg", "Yachats", "Seattle"}
    
    Dim query = From place In terraPlaces 
                Where places.Contains(place.Name) 
                Order By place.State 
                Select place.Name, place.State
    
    For Each obj In query
        Console.WriteLine(obj)
    Next
    
  4. Uruchom program i wyświetlić wyniki.Powinno być około 5 wyników.

Następne kroki

W tym temacie instruktażu pokazałem, jak stworzyć LINQ dostawcy dla jednej metody usługi sieci Web.Jeśli chcesz wykonywać dodatkowe rozwoju LINQ dostawcy, należy rozważyć następujące możliwości:

  • Włącz LINQ dostawcy do obsługi innych sposobów określania lokalizacji w kwerendzie klienta.

  • Zbadanie innych metod Szukacz-USA w sieci Web umożliwia uzyskanie dostępu do usługi i utworzyć LINQ dostawcy, z jednej z tych metod.

  • Znaleźć innej usługi sieci Web są zainteresowane i utworzyć LINQ dostawcy dla niego.

  • Tworzenie LINQ dostawcy dla źródła danych, niż usługi sieci Web.

Aby uzyskać więcej informacji o tworzeniu dostawcy LINQ, zobacz LINQ: budynek dostawca IQueryable na blogach MSDN.

Zobacz też

Zadania

Próbki LINQ

Jak: Modyfikowanie wyrażenia drzew (C# i Visual Basic)

Informacje

IQueryable<T>

IOrderedQueryable<T>

Koncepcje

Włączenie źródła danych dla kwerend LINQ

Usług Windows Communication Foundation i usług WCF w danych w programie Visual Studio