Language Integrated Query: Difference between revisions

Content deleted Content added
 
(59 intermediate revisions by 50 users not shown)
Line 1:
{{Short description|Microsoft .NET Framework component}}
{{About|software|the Las Vegas entertainment district|The Linq|the Japanese local idol group|LinQ}}
{{redirect|LINQ|other uses|Linq (disambiguation)}}
{{Multiple issues|
{{how-to|date=September 2024}}
{{More citations needed|date=September 2024}}
}}
 
{{Infobox programming language
| name = Language Integrated Query
| logo =
| paradigm =
| year =
| designer = [[Microsoft Corporation]]
| developer = [[Microsoft Corporation]]
| latest_release_version =
| latest_release_date =
| latest_test_version =
| latest_test_date =
| turing-complete = No
| typing = Strongly typed
| implementations = [[List of CLI languages|.NET languages]] ([[C Sharp (programming language)|C#]], [[F Sharp (programming language)|F#]], [[VB.NET]])
| dialects =
| influenced_by = [[SQL]], [[Haskell (programming language)|Haskell]]
| influenced =
| license =
| website = https://learn.microsoft.com/en-us/dotnet/standard/linq/
| license =
| website =
}}
'''Language Integrated Query''' ('''LINQ''', pronounced "link") is a [[Microsoft]] [[.NET Framework]] component that adds native data [[Query language|querying]] capabilities to [[List of CLI languages|.NET languages]], althoughoriginally portsreleased existas fora [[PHP]]major ([https://phplinq.codeplex.com/part PHPLinq]),of [[JavaScript]] ([https://linqjs.codeplex.com/NET linq.js]),Framework [[TypeScript]] ([https://github3.com/kutyel/linq.ts linq.ts]), and [[ActionScript5]] ([http://actionlinq.riaforge.org/ ActionLinq]) - but none of these ports are strictly equivalent to LINQ in C# for example (where it is a part of the language, not an external library, and where it often addresses a wider range of needs)2007.
 
LINQ extends the language by the addition of query [[Expression (computer science)|expressions]], which are akin to [[SQL]] statements, and can be used to conveniently extract and process data from [[Array data structure|arrays]], enumerable [[class (computer science)|class]]es, [[XML]] documents, [[relational database]]s, and third-party data sources. Other uses, which utilize query expressions as a general framework for readably composing arbitrary computations, include the construction of event handlers<ref name="reactive">{{cite web | url = http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx | title = Rx framework}}</ref> or [[Monad (functional programming)|monadic]] [[Parsing|parsers]].<ref name="parscomb">{{cite web | url = http://blogs.msdn.com/lukeh/archive/2007/08/19/monadic-parser-combinators-using-c-3-0.aspx | title = Monadic Parser Combinators using C#3 | accessdate = 2009-11-21}}</ref>
 
LINQ also defines a set of method names (called ''standard query operators'', or ''standard sequence operators''), along with translation rules used by the compiler to translate [[Fluent interface|fluent-style]] query expressions into expressions using these method names, [[Anonymous function#C.23 lambda expressions|lambda expressions]] and [[anonymous type]]s.
 
LINQ extends the language by the addition of query [[Expression (computer science)|expressions]], which are akin to [[SQL]] statements, and can be used to conveniently extract and process data from [[Array data structure|arrays]], enumerable [[class (computer science)|class]]es, [[XML]] documents, [[relational database]]s, and third-party data sources. Other uses, which utilize query expressions as a general framework for readably composing arbitrary computations, include the construction of event handlers<ref name="reactive">{{cite web | url = http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx | title = Rx framework| date = 10 June 2011}}</ref> or [[Monad (functional programming)|monadic]] [[Parsing|parsers]].<ref name="parscomb">{{cite web | url = http://blogs.msdn.com/lukeh/archive/2007/08/19/monadic-parser-combinators-using-c-3-0.aspx | title = Monadic Parser Combinators using C#3 | accessdateaccess-date = 2009-11-21}}</ref> It also defines a set of method names (called ''standard query operators'', or ''standard sequence operators''), along with translation rules used by the compiler to translate query syntax expressions into expressions using [[Fluent interface|fluent-style]] (called method syntax by Microsoft) with these method names, [[Anonymous function#C.23 lambda expressions|lambda expressions]] and [[anonymous type]]s.
Many of the concepts that LINQ has introduced were originally tested in Microsoft's [[Cω]] research project. LINQ was released as a major part of [[.NET Framework 3.5]] on November 19, 2007.
 
==Architecture of LINQ in the .NET Framework==
 
===Standard Queryquery Operatorsoperator API===
In what follows, the descriptions of the operators are based on the application of working with collections. Many of the operators take other functions as arguments. These functions may be supplied in the form of a named method or anonymous function.
 
The set of query [[Operator (programming)|operator]]s defined by LINQ is exposed to the user as the Standard Query Operator (SQO) [[API]]. The query operators supported by the API are:<ref name="sqo">{{cite web | url = http://download.microsoft.com/download/5/8/6/5868081c-68aa-40de-9a45-a3803d8134b8/standard_query_operators.doc | title = Standard Query Operators | publisher = Microsoft | accessdateaccess-date = 2007-11-30}}</ref>
;Select:
:The Select operator performs a [[projection (database)|projection]] on the collection to select interesting aspects of the elements. The user supplies an arbitrary function, in the form of a named or [[Anonymous function|lambda expression]], which projects the data members. The function is passed to the operator as a [[delegate (CLI)|delegate]]. This implements the [[Map (higher-order function)|Map]] higher-order function.
{{Further|Map (higher-order function)}}
The Select operator performs a [[projection (database)|projection]] on the collection to select
interesting aspects of the elements. The user supplies an arbitrary function, in the form of a named or [[Anonymous function|lambda expression]], which projects the data members. The function is passed to the operator as a [[delegate (CLI)|delegate]].
;Where:
:The Where operator allows the definition of a set of predicate rules that are evaluated for each object in the collection, while objects that do not match the rule are filtered away. The predicate is supplied to the operator as a delegate. This implements the [[Filter (higher-order function)|Filter]] higher-order function.
{{Further|Filter (higher-order function)}}
The Where operator allows the definition of a set of predicate rules that are evaluated for each object in the collection, while objects that do not match the rule are filtered away. The predicate is supplied to the operator as a delegate.
;SelectMany:
: For a user-provided mapping from collection elements to collections, semantically two steps are performed. First, every element is mapped to its corresponding collection. Second, the result of the first step is flattened by one level. ''Note:'' Select and Where are both implementable in terms of SelectMany, as long as singleton and empty collections are available. The translation rules mentioned above still make it mandatory for a LINQ provider to provide the other two operators. This implements the [[Bind (higher-order function)|bind]] higher-order function.
{{Further|Bind (higher-order function)}}
For a user-provided mapping from collection elements to collections, semantically two steps are performed. First, every element is mapped to its corresponding collection. Second, the result of the first step is flattened by one level. ''Note:'' Select and Where are both implementable in terms of SelectMany, as long as singleton and empty collections are available. The translation rules mentioned above still make it mandatory for a LINQ provider to provide the other two operators.
;Sum / Min / Max / Average:
These operators optionally take a function that retrieves a certain numeric value from each element in the collection and uses it to find the sum, minimum, maximum or average values of all the elements in the collection, respectively. Overloaded versions take no function and act as if the identity is given as the lambda.
;Aggregate:
A generalized Sum / Min / Max. This operator takes a function that specifies how two values are combined to form an intermediate or the final result. Optionally, a starting value can be supplied, enabling the result type of the aggregation to be arbitrary. Furthermore, a finalization function, taking the aggregation result to yet another value, can be supplied. This implement the [[Fold (higher-order function)|Fold]] higher-order function.
{{Further|Fold (higher-order function)}}
A generalized Sum / Min / Max. This operator takes a function that specifies how two values are combined to form an intermediate or the final result. Optionally, a starting value can be supplied, enabling the result type of the aggregation to be arbitrary. Furthermore, a finalization function, taking the aggregation result to yet another value, can be supplied.
;Join / GroupJoin: The Join operator performs an [[inner join]] on two collections, based on matching keys for objects in each collection. It takes two functions as delegates, one for each collection, that it executes on each object in the collection to extract the key from the object. It also takes another delegate in which the user specifies which data elements, from the two matched elements, should be used to create the resultant object. The GroupJoin operator performs a [[group join]]. Like the Select operator, the results of a join are instantiations of a different class, with all the data members of both the types of the source objects, or a subset of them.
;Take / TakeWhile: The Take operator selects the first n objects from a collection, while the TakeWhile operator, which takes a predicate, selects those objects that match the predicate (stopping at the first object that doesn't match it).
Line 57 ⟶ 54:
;Reverse: The Reverse operator reverses a collection.
;GroupBy: The GroupBy operator takes a function that extracts a key value and returns a collection of <code>IGrouping<Key, Values></code> objects, for each distinct key value. The <code>IGrouping</code> objects can then be used to enumerate all the objects for a particular key value.
;Distinct: The Distinct operator removes duplicate instances of an object from a collection. An overload of the operator takes an equality comparer object which defines the criteria for distinctness.
;Union / Intersect / Except: These operators are used to perform a [[union (set theory)|union]], [[intersection (set theory)|intersection]] and [[complement (set theory)|difference]] operation on two sequences, respectively. Each has an overload which takes an equality comparer object which defines the criteria for element equality.
;SequenceEqual: The SequenceEqual operator determines whether all elements in two collections are equal and in the same order.
;First / FirstOrDefault / Last / LastOrDefault: These operators take a predicate. The First operator returns the first element for which the predicate yields true, or, if nothing matches, throws an exception. The FirstOrDefault operator is like the First operator except that it returns the default value for the element type (usually a null reference) in case nothing matches the predicate. The last operator retrieves the last element to match the predicate, or throws an exception in case nothing matches. The LastOrDefault returns the default element value if nothing matches.
;Single: The Single operator takes a predicate and returns the element that matches the predicate. An exception is thrown, if none or more than one element match the predicate.
;SingleOrDefault: The SingleOrDefault operator takes a predicate and return the element that matches the predicate. If more than one element matches the predicate, an exception is thrown. If no element matches the predicate, a default value is returned.
;ElementAt: The ElementAt operator retrieves the element at a given index in the collection.
;Any / All: The Any operator checks, if there are any elements in the collection matching the predicate. It does not select the element, but returns true if at least one element is matched. An invocation of any without a predicate returns true if the collection non-empty. The All operator returns true if all elements match the predicate.
;Contains: The Contains operator checks, if the collection contains a given element.
;Count: The Count operator counts the number of elements in the given collection. An overload taking a predicate, counts the number of elements matching the predicate.
 
The Standardstandard Queryquery Operatoroperator API also specifies certain operators that convert a collection into another type:<ref name="sqo"/>
* AsEnumerable: Statically types the collection as an <code>IEnumerable<T></code>.<ref name="Enumerable Class">{{cite web|title=Enumerable Class|url=http://msdn.microsoft.com/en-us/library/system.linq.enumerable(v=vs.110).aspx|work=msdn|publisher=Microsoft|accessdateaccess-date=15 February 2014}}</ref>
* AsQueryable: Statically types the collection as an <code>IQueryable<T></code>.
* ToArray: Creates an array <code>T[]</code> from the collection.
Line 79 ⟶ 76:
 
===Language extensions===
While LINQ is primarily implemented as a [[library (computing)|library]] for .NET Framework 3.5, it also defines optional language extensions that make queries a first-class [[language construct]] and provide [[syntactic sugar]] for writing queries. These language extensions have initially been implemented in [[C Sharp (programming language)|C#]] 3.0,<ref name=Skeet>{{cite book |last=Skeet|first=Jon|title= C# in Depth |date=23 March 2019 |publisher= Manning |isbn= 978-1617294532}}</ref>{{rp|75}} [[VB.NET|VB 9.0]], [[F Sharp (programming language)|F#]]<ref name="linq">{{cite web |title=Query urlExpressions (F#)|url= httphttps://msdndocs.microsoft.com/en-usgb/librarydotnet/vstudiofsharp/hh225374.aspxlanguage-reference/query-expressions| title website=Microsoft Query Expressions (F#) Docs| accessdate access-date= 2012-12-19}}</ref> and [[Oxygene (programming language)|Oxygene]], with other languages like [[Nemerle]] having announced preliminary support. The language extensions include:<ref name="linq1">{{cite web | url = http://msdn.microsoft.com/en-us/library/bb397921.aspx | title = LINQ Framework | accessdateaccess-date = 2007-11-30}}</ref>
*Query syntax: A language is free to choose a query syntax that it will recognize natively. These language keywords must be translated by the compiler to appropriate LINQ method calls.
*Implicitly typed variables: This enhancement allows variables to be declared without specifying their types. The languages C# 3.0<ref name=Skeet/>{{rp|367}} and Oxygene declare them with the <code>var</code> keyword. In VB9.0, the <code>Dim</code> keyword without type declaration accomplishes the same. Such objects are still [[strong typing|strongly typed]]; for these objects the compiler infers the types of variables via [[type inference]], which allows the results of the queries to be specified and defined without declaring the type of the intermediate variables.
*[[Anonymous type]]s: Anonymous types allow classes that contain only data-member declarations to be inferred by the compiler. This is useful for the Select and Join operators, whose result types may differ from the types of the original objects. The compiler uses type inference to determine the fields contained in the classes and generates [[Mutator method|accessors and mutators]] for these fields.
*[[Object Initializerinitializer]]: Object initializers allow an object to be created and initialized in a single scope, as required for Select and Join operators.
*[[Anonymous function|Lambda expressions]]: Lambda expressions allow predicates and other projection functions to be written inline with a concise syntax, and support full lexical closure. They are captured into parameters as delegates or expression trees depending on the Query Provider.
 
For example, in the query to select all the objects in a collection with <code>SomeProperty</code> less than 10,
 
<sourcesyntaxhighlight lang="csharp">
IEnumerable<MyObject> SomeCollection = /* something here */
var results = from c in SomeCollection
where c.SomeProperty < 10
select new {c.SomeProperty, c.OtherProperty};
 
varIEnumerable<MyObject> results = from c in SomeCollection
foreach (var result in results)
where c.SomeProperty < 10
select new {c.SomeProperty, c.OtherProperty};
 
foreach (varMyObject result in results)
{
Console.WriteLine(result);
}
</syntaxhighlight>
</source>
 
the types of variables ''result'', ''c'' and ''results'' all are inferred by the compiler in accordance to the signatures of the methods eventually used. The basis for choosing the methods is formed by the query expression-free translation result
 
<sourcesyntaxhighlight lang="csharp">
varIEnumerble<MyObject> results =
SomeCollection
.Where(c => c.SomeProperty < 10)
Line 108 ⟶ 107:
 
results.ForEach(x => {Console.WriteLine(x.ToString());})
</syntaxhighlight>
</source>
 
===LINQ providers===
Line 118 ⟶ 117:
 
====LINQ to Objects====
The LINQ to Objects provider is used for in-memory collections, using the local query execution engine of LINQ. The code generated by this provider refers to the implementation of the standard query operators as defined on the <code>Sequence</code> pattern and allows <code>IEnumerable<T></code> collections to be queried locally. Current implementation of LINQ to Objects perform interface implementation checks to allow for fast membership tests, counts, and indexed lookup operations when they are supported by the runtime type of the IEnumerable.<ref name="Enumerable.ElementAt">{{cite web | url = http://msdn.microsoft.com/en-us/library/bb299233.aspx | title = Enumerable.ElementAt | accessdateaccess-date = 2014-05-07}}</ref><ref name="Enumerable.Contains">{{cite web | url = http://msdn.microsoft.com/en-us/library/bb352880.aspx | title = Enumerable.Contains | accessdateaccess-date = 2014-05-07}}</ref><ref name="Enumerable.Count">{{cite web | url = http://msdn.microsoft.com/en-us/library/bb338038.aspx | title = Enumerable.Count| accessdateaccess-date = 2014-05-07}}</ref>
 
====LINQ to XML (formerly called XLINQ)====
The LINQ to XML provider converts an XML document to a collection of <code>XElement</code> objects, which are then queried against using the local execution engine that is provided as a part of the implementation of the standard query operator.<ref>{{cite web | url = http://msdn2.microsoft.com/hi-in/library/bb308960(en-us).aspx | title = .NET Language-Integrated Query for XML Data | accessdatedate = 30 April 2007 | access-date = 2007-11-30}}</ref>
 
====LINQ to SQL (formerly called DLINQ)====
The LINQ to SQL provider allows LINQ to be used to query [[Microsoft SQL Server]] databases, including [[SQL Server Compact]] databases. Since SQL Server data may reside on a remote server, and because SQL Server has its own query engine, LINQ to SQL does not use the query engine of LINQ. Instead, it converts a LINQ query to a [[SQL]] query that is then sent to SQL Server for processing.<ref>{{cite web | url = http://www.hookedonlinq.com/LINQtoSQL5MinuteOverview.ashx | title = LINQ to SQL | accessdateaccess-date = 2007-11-30 | archive-url = https://archive.today/20130125231336/http://www.hookedonlinq.com/LINQtoSQL5MinuteOverview.ashx | archive-date = 2013-01-25 | url-status = dead }}</ref> However, since SQL Server stores the data as [[relational database|relational data]] and LINQ works with data encapsulated in objects, the two representations must be [[Object-Relational mapping|mapped]] to one another. For this reason, LINQ to SQL also defines a mapping framework. The mapping is done by defining classes that correspond to the tables in the database, and containing all or a subset of the columns in the table as data members.<ref name="ltos">{{cite web | url = http://msdn2.microsoft.com/hi-in/library/bb425822.aspx | title = LINQ to SQL: .NET Language-Integrated Query for Relational Data | accessdatedate = 30 April 2007 | access-date = 2007-11-30}}</ref> The correspondence, along with other [[relational model]] attributes such as [[primary key]]s, are specified using LINQ to SQL-defined [[attribute (computing)|attributes]]. For example,
 
<sourcesyntaxhighlight lang="csharp">
[Table(Name="Customers")]
public class Customer
Line 136 ⟶ 135:
public string CustName;
}
</syntaxhighlight>
</source>
 
This class definition maps to a table named <code>Customers</code> and the two data members correspond to two columns. The classes must be defined before LINQ to SQL can be used. [[Visual Studio 2008]] includes a mapping designer that can be used to create the mapping between the data schemas in the object as well as the relational ___domain. It can automatically create the corresponding classes from a [[database schema]], as well as allow manual editing to create a different view by using only a subset of the tables or columns in a table.<ref name="ltos"/>
Line 143 ⟶ 142:
 
====LINQ to DataSets====
Since the LINQ to SQL provider (above) works only with [[Microsoft SQL Server]] databases, in order to support any generic database, LINQ also includes the LINQ to DataSets. It uses ADO.NET to handle the communication with the database. Once the data is in ADO.NET Datasets, LINQ to DataSets execute queries against these datasets.<ref>{{cite web | url = http://www.hookedonlinq.com/LINQtoDatasets.ashx | title = LINQ to DataSets | accessdateaccess-date = 2007-11-30 | archive-url = https://archive.today/20130125171110/http://www.hookedonlinq.com/LINQtoDatasets.ashx | archive-date = 2013-01-25 | url-status = dead }}</ref>
 
==Performance==
{{Update|part=Performance|date=November 2021|reason=The source is old and now performs better than before}}
UsersNon-professional users may struggle with subtleties in the '''LINQ to Objects''' features and syntax. Sub-optimalNaive LINQ implementation patterns can lead to a seriouscatastrophic degradation of performance.<ref>{{cite web
| url=http://www.codeproject.com/KB/dotnet/LINQ_Performance_Test.aspx
| title=LINQ Performance Test: My First Visual Studio 2008 Project
| date=2007-12-21
|last=Vider|first=Guy
| accessdateaccess-date=2009-02-08}}</ref><ref>{{cite web
| url=http://msdn.microsoft.com/en-us/magazine/cc721610.aspx
| title=Increase LINQ Query Performance
Line 158:
| quote=''While it is true that LINQ is powerful and very efficient, large sets of data can still cause unexpected performance problems''
| date=2008
| accessdateaccess-date=2014-03-19}}</ref>
 
'''LINQ to XML''' and '''LINQ to SQL''' performance compared to ADO.NET depends on the use case.<ref>{{cite web
Line 167:
| quote=''When calling a query multiple times with Entity Framework the recommended approach is to use compiled LINQ queries. Compiling a query results in a performance hit the first time you use the query but subsequent calls execute much faster''
| date=2010-08-06
| accessdateaccess-date=2014-03-19}}</ref><ref>{{cite web
| url=http://www.codeproject.com/KB/dotnet/LinQ_Performance_net3_5.aspx
| title=Performance comparisons LinQ to SQL, ADO, C#
| date=2008-05-25
| last=Kshitij|first=Pandey
| accessdateaccess-date=2009-02-08}}</ref>
 
==PLINQ==
Version 4 of the .NET framework includes [[Parallel Extensions|PLINQ]], or ''Parallel LINQ'', a [[parallel computing|parallel]] execution engine for LINQ queries. It defines the <code>ParallelQuery<T></code> class. Any implementation of the <code>IEnumerable<T></code> interface can take advantage of the PLINQ engine by calling the <{{code>|AsParallel<T>(this IEnumerable<T>)</code>|csharp}} extension method defined by the ParallelEnumerable class in the System.Linq namespace of the .NET framework.<ref name="ParallelEnumerable">{{cite web | url = http://msdn.microsoft.com/en-us/library/dd413602(v=vs.110).aspx | title = ParallelEnumerable Class | accessdateaccess-date = 2014-05-07}}</ref> The PLINQ engine can execute parts of a query concurrently on multiple threads, providing faster results.<ref name="channel9">{{cite web | url = http://channel9.msdn.com/Showpost.aspx?postid=347531 | title = Programming in the Age of Concurrency: Concurrent Programming with PFX | accessdateaccess-date = 2007-10-16}}</ref>
 
== Predecessor languages ==
Many of the concepts that LINQ introduced were originally tested in Microsoft's '''Cω''' research project, formerly known by the [[codename]]s '''X#''' (X Sharp) and '''Xen'''. It was renamed to Cω after '''Polyphonic C#''' (another research language based on [[join calculus]] principles) was integrated into it.
 
Cω attempts to make datastores (such as [[databases]] and [[XML]] documents) accessible with the same ease and [[type safety]] as traditional types like [[String (computer science)|strings]] and [[Array data type|arrays]]. Many of these ideas were inherited from an earlier incubation project within the WebData XML team called X# and Xen. Cω also includes new constructs to support [[concurrent programming]]; these features were largely derived from the earlier Polyphonic C# project.<ref>{{cite book |last1=Eichert |first1=Steve |title=LINQ in Action |last2=Wooley |first2=James B. |last3=Marguerie |first3=Fabrice |publisher=Manning |year=2008 |isbn=9781638354628 |pages=56–57 (as reported in the Google Books search link - the book does not have page numbers)}}</ref>
 
First available in 2004 as a compiler preview, Cω's features were subsequently used by Microsoft in the creation of the LINQ features released in 2007 in .NET version 3.5<ref>[http://tomasp.net/articles/csharp3-concepts.aspx Concepts behind the C# 3.0 language | Articles | TomasP.Net] {{webarchive|url=https://web.archive.org/web/20070212035001/http://tomasp.net/articles/csharp3-concepts.aspx|date=2007-02-12}}</ref> The concurrency constructs have also been released in a slightly modified form as a library, named [[Joins (concurrency library)|''Joins Concurrency Library'']], for [[C Sharp (programming language)|C#]] and other .NET languages by [[Microsoft Research]].<ref>{{cite web |title=The Joins Concurrency Library |url=http://research.microsoft.com/research/pubs/view.aspx?type=inproceedings&id=2005 |access-date=2007-06-08}}</ref>
 
== Ports ==
Ports of LINQ exist for [[PHP]] ([https://phplinq.codeplex.com/ PHPLinq] {{Webarchive|url=https://web.archive.org/web/20180119124921/https://phplinq.codeplex.com/ |date=2018-01-19 }}), [[JavaScript]] ([https://github.com/mihaifm/linq linq.js]), [[TypeScript]] ([https://github.com/kutyel/linq.ts linq.ts]), and [[ActionScript]] ([http://actionlinq.riaforge.org/ ActionLinq] {{Webarchive|url=https://web.archive.org/web/20181225143803/http://actionlinq.riaforge.org/ |date=2018-12-25 }}), and C++ ([https://github.com/seijikun/CXXIter CXXIter]), although none are strictly equivalent to LINQ in the .NET inspired languages C#, F# and VB.NET (where it is a part of the language, not an external library, and where it often addresses a wider range of needs).{{citation needed|date=October 2018}}
 
==See also==
Line 187 ⟶ 197:
 
==External links==
{{Wikibooks|.NET Development Foundation}}
* [http://msdn.microsoft.com/en-us/netframework/aa904594.aspx Official Microsoft LINQ Project]
* [http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx 101 C# LINQ Samples]
* [http://www.developerfusion.com/article/8250/linq-to-objects-for-the-net-developer/ LINQ to Objects for the .NET developer]
* [https://social.msdn.microsoft.com/Forums/en-US/b0ed008e-b4f6-47f6-8b43-9838b94f5ced/what-is-the-future-of-linq-to-sql-as-of-2016?forum=linqtosql Future of LINQ to SQL] {{Webarchive|url=https://web.archive.org/web/20200721231257/https://social.msdn.microsoft.com/Forums/en-US/b0ed008e-b4f6-47f6-8b43-9838b94f5ced/what-is-the-future-of-linq-to-sql-as-of-2016?forum=linqtosql |date=2020-07-21 }}
* [http://www.codeproject.com/Articles/383749/How-does-it-work-in-Csharp-Part-3-Csharp-LINQ-in-d How does it work in C#? - Part 3 (C# LINQ in detail)]
 
Line 196 ⟶ 206:
{{Query languages}}
 
[[Category:.NET Framework terminology]]
[[Category:Query languages]]
[[Category:XML data access]]