Pages

Showing posts with label LINQ. Show all posts
Showing posts with label LINQ. Show all posts

Tuesday, September 21, 2010

Dynamic LINQ OrderBy

Just stumbled into this oldie...

To do this without the dynamic LINQ library, you just need the code as below. This covers most common scenarios including nested properties.

To get it working with IEnumerable you could add some wrapper methods that go via AsQueryable - but the code below is the core Expression logic needed.

public static IOrderedQueryable OrderBy(this IQueryable source, string property)
{
return ApplyOrder(source, property, "OrderBy");
}
public static IOrderedQueryable OrderByDescending(this IQueryable source, string property)
{
return ApplyOrder(source, property, "OrderByDescending");
}
public static IOrderedQueryable ThenBy(this IOrderedQueryable source, string property)
{
return ApplyOrder(source, property, "ThenBy");
}
public static IOrderedQueryable ThenByDescending(this IOrderedQueryable source, string property)
{
return ApplyOrder(source, property, "ThenByDescending");
}
static IOrderedQueryable ApplyOrder(IQueryable source, string property, string methodName) {
string[] props = property.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach(string prop in props) {
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);

object result = typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] {source, lambda});
return (IOrderedQueryable)result;
}

Monday, August 30, 2010

Working with untyped datasets

Example : Working with untyped datasets

DataTable dataTable = new DataTable();
using (var connection = new SqlConnection
(Settings.Default.SkeetySoftDefectsConnectionString))
{
string sql = "SELECT Summary, Status FROM Defect";
new SqlDataAdapter(sql, connection).Fill(dataTable);
}
var query = from defect in dataTable.AsEnumerable()
where defect.Field("Status") != Status.Closed
select defect.Field("Summary");
foreach (string summary in query)
{
Console.WriteLine (summary);
}



Untyped datasets have two problems as far as LINQ is concerned. First, we don’t have
access to the fields within the tables as typed properties; second, the tables themselves
aren’t enumerable. To some extent both are merely a matter of convenience—we
could use direct casts in all the queries, handle DBNull explicitly and so forth, as well as
enumerate the rows in a table using dataTable.Rows.Cast. These
workarounds are quite ugly, which is why the DataTableExtensions and DataRow-
Extensions classes exist.
Code using untyped datasets is never going to be pretty, but using LINQ is far nicer
than filtering and sorting using DataTable.Select. No more escaping, worrying
about date and time formatting, and similar nastiness.
Listing 12.10 gives a simple example. It just fills a single defect table and prints the
summaries of all the defects that don’t have a status of “closed.”

Saturday, August 28, 2010

Dictionaries and LINQ

Because all IDictionary implementations are also enumerable, we can
run LINQ queries against them. Given the RecordCache class in Example 9-5, we might
choose to implement the cache item removal policy as shown in Example 9-10.
Example 9-10. LINQ query with dictionary source
private void DiscardAnyOldCacheEntries()
{
// Calling ToList() on source in order to query a copy
// of the enumeration, to avoid exceptions due to calling
// Remove in the foreach loop that follows.
var staleKeys = from entry in cachedRecords.ToList()
where IsStale(entry.Value)
select entry.Key;
foreach (int staleKey in staleKeys)
{
cachedRecords.Remove(staleKey);
}
}

But it’s also possible to create new dictionaries with LINQ queries. Example 9-11 il-
lustrates how to use the standard ToDictionary LINQ operator.
Example 9-11. LINQ’s ToDictionary operator
IDictionary buildingIdToNameMap =
MyDataSource.Buildings.ToDictionary(
building => building.ID,
building => building.Name);

This example presumes that MyDataSource is some data source class that provides a
queryable collection containing a list of buildings. Since this information would typi-
cally be stored in a database, you would probably use a database LINQ provider such
as LINQ to Entities or LINQ to SQL. The nature of the source doesn’t greatly matter,
though—the mechanism for extracting the resources into a dictionary object are the
same in any case. The ToDictionary operator needs to be told how to extract the key
from each item in the sequence. Here we’ve provided a lambda expression that retrieves
the ID property—again, this property would probably be generated by a database map-
ping tool such as the ones provided by the Entity Framework or LINQ to SQL. (We
will be looking at data access technologies in a later chapter.) This example supplies a
second lambda, which chooses the value—here we pick the Name property. This second
lambda is optional—if you don’t provide it, ToDictionary will just use the entire source
item from the stream as the value—so in this example, leaving out the second lambda
would cause ToDictionary to return an IDictionary (where Building is
whatever type of object MyDataSource.Buildings provides).
The code in Example 9-11 produces the same result as this:
var buildingIdToNameMap = new Dictionary();
foreach (var building in MyDataSource.Buildings)
{
buildingIdToNameMap.Add(building.ID, building.Name);
}

Wednesday, August 25, 2010

Query Expressions Versus Method Calls

LINQ query as method calls

var bigFiles = GetAllFilesInDirectory(@"c:\").
Where(file => new FileInfo(file).Length > 10000000);
Let’s compare this with the components of the original query:
var bigFiles = from file in GetAllFilesInDirectory(@"c:\")
where new FileInfo(file).Length > 10000000
select file;


using where

var bigFiles = from file in GetAllFilesInDirectory(@"c:\")
where new FileInfo(file).Length > 10000000
select "File: " + file;


using select

var bigFiles = GetAllFilesInDirectory(@"c:\").
Where(file => new FileInfo(file).Length > 10000000).
Select(file => "File: " + file);