Introduction
Yes, I know there are many articles on the internet on this topic and many are on CodeProject itself. However, the thing that I found missing was a single place where a newbie could get all the related information about LINQ. Later, one can drill down on the topics of his/her interest.
Through this article, I am trying to share information about this one of the salient feature of the .NET. I am looking forward to enable a beginner to write the code in the LINQ way if he is not doing that already. Also I hope to make one start thinking of the solutions in the same direction. Of course, this doesn't provide all the details about the topic. This article gives you the cursors and general understanding about LINQ.
What is LINQ
LINQ stands for the Language Integrated Query. It relies on the language improvements (C# 3.0 and VB 9.0 onwards).
This is what Microsoft says about LINQ:
"LINQ is a set of extensions to the .NET Framework that encompass language-integrated query, set, and transform operations. It extends C# and Visual Basic with native language syntax for queries and provides class libraries to take advantage of these capabilities." In simple terms, LINQ is something you can use for writing the queries that fits your needs and operates against various data stores including remote one, e.g. SQL, etc.
Why Use LINQ
In general programming scenarios, we queries various kinds of data stores, e.g., object collections, database and XML, etc. For querying these data stores, we have got to use different mechanisms that are best suited for them. For example, we use foreach
loop for querying the collection, write SQL queries for database and Xpath queries for querying the XML data.
So the point here is that we need to know individual terminology for doing similar things when we got to work with different data stores. To make my point more clear, consider the scenario where we need to find the customers from the specific region (say India).
Query using object:
Collapse foreach(Customer c in customers)
if (c.Region == “India") …
Query from database table:
Collapse SELECT * FROM Customers WHERE Region='India'
Query XML using xPath:
Collapse //Customers/Customer[@Region=‘India']
What LINQ tries to do here is that it provides you the UNIFORM APPROACH for querying and manipulating the different data sources. Therefore, for querying the data, you do not need to learn XPath, SQL and Object iterations. All you need to know is how to query using LINQ. Once you start using it in your day to day applications, you will realize the real power of it.
Moreover, LINQ comes with some perks which are as follows:
- Bridges the gap between OO and Relational data
- Integrated directly in the language
- Type checking and compile time safety
- Intellisense
- Debugger support
Sample LINQ Query and Its Dissection
A simple Linq query looks similar to the one as follows:
Collapse var contacts = from c in customers
where c.Region == "India"
select new {c.Name, c.Phone}
This small code fragment contains few of the language features supported by the 3.0 framework onwards which are identified in the following illustration. Both the queries are the same, it's just the notation that is changed.
Following are the terms that have popped up from a simple LINQ query.
1) Standard Query Expression
These are the language syntax which are there to assist you writing the LINQ queries that are understood by the .NET Framework.
2) Local Variable Type Inference
This allows you to use var instead of explicitly specifying the type. Linq queries usually return IEnumerable<T>
. In some cases, it's convenient to use the anonymous types. Point to notice here is that the variable deduces the variable type from the initializer. Scope of the var
is always local.
Collapse int i = 666;
string s = "Goodbye";
double d = 3.14;
int[] numbers = new int[] {1, 2, 3};
Dictionary<int,Order> orders = new Dictionary<int,Order>();
could be replaced by the following:
Collapse var i = 666;
var s = "Goodbye";
var d = 3.14;
var numbers = new int[] {1, 2, 3};
var orders = new Dictionary<int,Order>();
3) Object Initializers
This allows you to create an instance of an object in just one line by providing the values for the properties:
Collapse public class Point
{
private int x, y;
public int X { get { return x; } set { x = value; } }
public int Y { get { return y; } set { y = value; } }
}
The following line creates an instance of Point
object with the parameters provided.
Collapse Point a = new Point { X = 0, Y = 1 };
4) Anonymous Types
Usually we create the types with some name. This feature allows you to create the type that does not have any name.
Collapse var i1 = new {CustomerId = 123, Name = “santosh” };
var i2 = new {123, “santosh”};
5) Lambda Expressions
Definition:
A lambda expression is an anonymous function that can contain expressions and statements.
Let's see how the Lambda expression get evolved. Let's say we want to get the customers which have the City
as "London
". To do this, one can use one of our old traditional friends named delegate.
- Getting the customers using delegate:
Collapse List<Customer> customers = GetCustomers();
var local = customers.FindAll(CityEqualsLondon);
private bool CityEqualsLondon(Customer c){
return c.City == "London";
}
- Let's say you do not want to create a function, e.g.,
CityEqualsLondon()
that does the filtering stuff, you can use the anonymous method instead. It's just inline and you get the desired result. Collapse List<Customer> customers = GetCustomers();
var local = customers.FindAll(delegate(Customer c) { return c.City == "London"; });
- Why not have something that does the required thing without writing some common syntax that the compiler can anyway find out by looking at the code. The solution that came out was lambda expressions.
Collapse List<Customer> customers = GetCustomers();
var local = customers.FindAll(c => c.City == "London");
So the c => c.city == "London"
is a lambda expression that contains all the required information to give to compiler so the compiler knows what to do. "=>
" is read as "goes to
".
Following are two terms that you should better be knowing when using the LINQ or Lambda expressions.
Predicate
Collapse (p) => p.Gender == “F”
Projection
Collapse (p) => p.Gender ? “F” : “Female”
6) Extension Methods
This feature lets you add methods to the existing types without creating the derived type.
Following are the items that explains the Extension Methods.
- Extends Existing Types
- Adds Methods Without Derivation
- Accesses Public Members of Extended Types
- Must be:
public
and static
- Housed within a
static
class
- Use this keyword before parameter of extended type
Following is the code to create a sample extension method named ToMsgbox
that extends the strings
array in such way that the strings
gets concatenated with new lines and gets displayed in a message box. It's not a very good example, however, fair enough to explain the basic idea behind this.
Collapse public static class MyExtns
{
public static void ToMsgbox(this string[] strings)
{
MessageBox.Show(string.Join(Environment.NewLine, strings));
}
}
LINQ Architecture
The following figure explains the basic architecture of the LINQ. You write the LINQ queries in the language of your choice that operates on the LINQ enabled data sources. There are basically three main logical branches for the LINQ. LINQ to Object, LINQ to Database and Entities (known as DLINQ) and LINQ to XML (known as XLINQ).
LINQ to Objects
LINQ to Objects sample:
Collapse string[] testStrings = { "Santosh", "Vikram", "Mohit", "Chirag",
"Amit", "Vikas", "Vipul", "Andy" };
var temp = from str in testStrings
let words = str.ToLower().ToCharArray()
where str.Length > 4 && words[0] == 'v'
orderby str.Length descending
select str;
temp.ToArray().ToMsg();
LINQ to SQL/Entities (DLINQ)
When you query the database and entities using the LINQ queries, it's referred as DLINQ. Visual Studio provides you the option to do the OR mapping in the integrated designer. You can check out more details from one of the ScottGu's blog
here.
Here are some points to note about DLINQ:
- ORM Designer Maps Relational Structures to Classes
- Pushes query processing from in-memory to a remote data store
- Automatically generates parameterized SQL
- Fully supports stored procedures
- Tables have CRUD definitions (Default = use runtime)
- Can instead point to stored procedures
- Built on the concept of Deferred Loading
The following figure explains how the LINQ to SQL works:
Following is the sample code snippet to explain the deferred execution. You see, the actual query is triggered to the database when DataBind()
is executed. So the execution of actual query keeps on getting deferred up to the time till there is no other option. Most query operators don’t execute when declared.
Collapse var query = from c in ctx.Customers
where c.Region == “India“
select c;
gridViewCustomers.DataSource = query;
gridViewCustomers.DataBind();
In addition to this,
- Query gets executed when enumerated
- Bound to a
DataBound
control
- Execute within
ForEach
loop Iteration
- Transformed to collection
ToList()
, ToArray()
, etc.
Deferred Execution
- Benefits:
- Fetches data only when needed, not all at once
- Minimize network traffic, memory consumption and load on database
- Great for when can but fetch objects as user requests them
- Drawbacks:
- Chatty Interface: Each request for child record invokes explicit database query
Immediate Execution
- Benefits:
- Singe query returns all data
- Great if you know that you will need all data returned
- Drawbacks:
- Large amount of data returned, whether used or not
Here is what an ORM designer look like:
Here come a few sample DLINQ queries:
Select products
Collapse var products = from p in _NWindDataContext.Products
where p.CategoryID == 1
select new { p.ProductName, p.CategoryID };
Collapse dataGridView1.DataSource = products;
Insert a product
Collapse Product product = new Product();
product.CategoryID = 1;
product.ProductName = "testProduct";
_NWindDataContext.Products.InsertOnSubmit(product);
_NWindDataContext.SubmitChanges();
RefreshData();
Update Product
Collapse var product = (from p in _NWindDataContext.Products
where p.CategoryID == 1 && p.ProductName.Contains("testProduct")
select p).First();
product.ProductName = "santosh";
_NWindDataContext.SubmitChanges();
RefreshData();
Delete Product
Collapse var product = (from p in _NWindDataContext.Products
where p.CategoryID == 1 && p.ProductName.Contains("santosh")
select p).First();
_NWindDataContext.Products.DeleteOnSubmit(product);
_NWindDataContext.SubmitChanges();
RefreshData();
LINQ to XML (XLINQ)
Is simple words, when you write your LINQ queries that operates on the XML, they are referred as XLINQ queries.
Collapse XDocument xdoc = XDocument.Load("auths.xml");
var auths = from a in xdoc.Root.Descendants("author")
where a.Element("au_fname").Value.Contains("a")
orderby a.Element("au_fname").Value.Length descending
select a.Element("au_fname").Value;
Expression Tree
Expression trees represent code in a tree-like data structure, where each node is an expression. You can create an expression tree of lambda expressions that are quite useful when you query using LINQ and there are many conditional filters (where
clauses) you want to apply on the query.
Collapse public delegate bool Predicate<T>(T item);
Expression<Predicate<Customer>> test = c => c.Region== "India";
ParameterExpression c = Expression.Parameter(typeof(Customer), "c");
Expression expr = Expression.Equal(
Expression.Property(c, typeof(Customer).GetProperty("Region")),
Expression.Constant("India")
);
Expression<Predicate<Customer>> test =
Expression.Lambda<Predicate<Customer>>(expr, c);
Parallel LINQ - (PLINQ)
Framework 4.0 introduces parallel processing. LINQ also got its share by letting the LINQ query run in parallel. How the query gets executed in parallel is a complicated process. I do not want to burden you with more details about this. Please find the link at the end of this article, in case you want to have more details.
From the programming perspective, if you want to know how to write a LINQ query that takes advantage of this feature, please refer to the following code snippet and notice the AsParallel()
:
Collapse int[] output = arr
.Select(x => Test(x))
.ToArray();
int[] output = arr.AsParallel()
.Select(x => Test(x))
.ToArray();