Navigation
Links

Powered by Squarespace

Entries in LINQ (5)

Wednesday
Dec102008

Sequence membership

Given two sorted sequences, this method returns the intersection, a-b, and b-a, using a single iteration over both sequences. The return type is an IEnumerable<KeyValuePair<T, int>>, where:

  • only in A = -1

  • in both = 0

  • only in B = 1


/// <summary>
/// Return an int for every element in sortedSetA and sortedSetB specifying objects
/// that belong to A but not to B (-1), objects which are both in A and in B (0), and objects
/// that belong to B but not A (1).
/// </summary>
/// <remarks>This method is an O(n+m) operation when the two sets have different members, where n
/// is the count of <paramref name="sortedSetA"/> and m is the count of <paramref name="sortedSetB"/>.
/// Otherwise the operation approaches O(n+m-n) where m is the count of the larger set and n is the
/// smaller set.
/// </remarks>
/// <typeparam name="T">Type of element in each set</typeparam>
/// <param name="setA">A set where each element is of type <typeparamref name="T"/></param>
/// <param name="setB">A set where each element is of type <typeparamref name="T"/></param>
/// <returns>Returns A \ B (-1), the intersection of A and B (0), and B \ A (1)</returns>
public static IEnumerable<KeyValuePair<T, int>> Membership<T> (IEnumerable<T> sortedSetA, IEnumerable<T> sortedSetB) {
return Membership<T> (sortedSetA, sortedSetB, Comparer<T>.Default);
}
/// <summary>
/// Return a KeyValuePair<int, T> for every element in sortedSetA and sortedSetB where the int
/// specifies membership and T is the element. The int specifies objects that belong to A but
/// not to B (-1), objects which are both in A and in B (0), and objects that belong to B but
/// not A (1).
/// </summary>
/// <remarks>This method is an O(n+m) operation when the two sets have different members, where n
/// is the count of <paramref name="sortedSetA"/> and m is the count of <paramref name="sortedSetB"/>.
/// Otherwise the operation approaches O(n+m-n) where m is the count of the larger set and n is the
/// smaller set.
/// </remarks>
/// <typeparam name="T">Type of element in each set</typeparam>
/// <param name="setA">A set where each element is of type <typeparamref name="T"/></param>
/// <param name="setB">A set where each element is of type <typeparamref name="T"/></param>
/// <param name="comparer"><see cref="IComparer"/> used to sort the sets</param>
/// <returns>Returns A \ B (-1), the intersection of A and B (0), and B \ A (1)</returns>
public static IEnumerable<KeyValuePair<T, int>> Membership<T> (IEnumerable<T> sortedSetA, IEnumerable<T> sortedSetB, IComparer<T> comparer) {
const int onlyA = -1;
const int both = 0;
const int onlyB = 1;

if (sortedSetA == null)
throw new ArgumentNullException ("sortedSetA");
if (sortedSetB == null)
throw new ArgumentNullException ("sortedSetB");
if (comparer == null)
throw new ArgumentNullException ("comparer");

IEnumerator<T> enumeratorA = sortedSetA.GetEnumerator ();
IEnumerator<T> enumeratorB = sortedSetB.GetEnumerator ();

bool nextA = enumeratorA.MoveNext ();
bool nextB = enumeratorB.MoveNext ();

// default value for type T
T a = default (T);
// default value for type T
T b = default (T);

// if both collections have a value
if (nextA & nextB) {
// get current value
a = enumeratorA.Current;
// get current value
b = enumeratorB.Current;

do {
// Compare a to b: is a < b, a == b, or a > b ?
int val = comparer.Compare (a, b);
// a == b
if (val == 0) {
// return both collections have the value a
yield return new KeyValuePair<T, int> (a, both);
// if collection a can move next
if (nextA = enumeratorA.MoveNext ())
// get the next a
a = enumeratorA.Current;
// if collection b can move next
if (nextB = enumeratorB.MoveNext ())
// get the next b
b = enumeratorB.Current;
}
// a < b
else if (val < 0) {
// return only collection a has the value a
yield return new KeyValuePair<T, int> (a, onlyA);
// if collection a can move next
if (nextA = enumeratorA.MoveNext ())
// get the next a
a = enumeratorA.Current;
}
// a > b
else {
// return only collection b has the value b
yield return new KeyValuePair<T, int> (b, onlyB);
// if collection b can move next
if (nextB = enumeratorB.MoveNext ())
// get the next b
b = enumeratorB.Current;
}
// loop while there are values for both collections
} while (nextA & nextB);
}
// if collection a has more values
if (nextA) {
// return only collection a has the value a
yield return new KeyValuePair<T, int> (a, onlyA);
// if collection a can move next
while (enumeratorA.MoveNext ()) {
// return only collection a has the value a
yield return new KeyValuePair<T, int> (enumeratorA.Current, onlyA);
}
}
// if collection b has more values
else if (nextB) {
// return only collection b has the value b
yield return new KeyValuePair<T, int> (b, onlyB);
// if collection b can move next
while (enumeratorB.MoveNext ()) {
// return only collection b has the value b
yield return new KeyValuePair<T, int> (enumeratorB.Current, onlyB);
}
}
}
Wednesday
Sep102008

LINQ to SQL produces incorrect TSQL when using UNION or CONCAT

When a LINQ to SQL query contains a Union or Concat with a second query, and the second query references a column twice, a SqlException will occur.

var a = from address in dc.Addresses
select new {
ID = address.AddressID,
Address1 = address.AddressLine1,
Address2 = address.AddressLine2,
};
var b = from address in dc.Addresses
select new {
ID = address.AddressID,
Address1 = address.AddressLine1,
Address2 = address.AddressLine1, // notice AddressLine1 repeated
};
var q = a.Take(10).Union (b.Take(10));
q.ToArray ();


SqlException: All the queries in a query expression containing a UNION operator must have the same number of expressions in their select lists.

SELECT [t2].[AddressID] AS [ID], [t2].[AddressLine1] AS [Address1], [t2].[AddressLine2] AS [Address2]
FROM (
SELECT TOP (10) [t0].[AddressID], [t0].[AddressLine1], [t0].[AddressLine2]
FROM [Person].[Address] AS [t0]
UNION
SELECT TOP (10) [t1].[AddressID], [t1].[AddressLine1]
FROM [Person].[Address] AS [t1]
) AS [t2]


Notice the third SELECT statement is only selecting two columns instead of the required three.

Please rate and validate this bug at the MSDN Microsoft Product Feedback Center so Microsoft responds with a solution or workaround.
Tuesday
Sep092008

Extending LINQ to SQL

Last year, Scott Guthrie stated “You can actually override the raw SQL that LINQ to SQL uses if you want absolute control over the SQL executed”, but I can’t find documentation describing an extensibility method.

I would like to modify the following LINQ to SQL query:
using (NorthwindContext northwind = new NorthwindContext ()) {
var q = from row in northwind.Customers
let orderCount = row.Orders.Count ()
select new {
row.ContactName,
orderCount
};
}

Which results in the following TSQL:
SELECT [t0].[ContactName], (
SELECT COUNT(*)
FROM [dbo].[Orders] AS [t1]
WHERE [t1].[CustomerID] = [t0].[CustomerID]
) AS [orderCount]
FROM [dbo].[Customers] AS [t0]

To:
using (NorthwindContext northwind = new NorthwindContext ()) {
var q = from row in northwind.Customers.With (
TableHint.NoLock, TableHint.Index (0))
let orderCount = row.Orders.With (
TableHint.HoldLock).Count ()
select new {
row.ContactName,
orderCount
};
}

Which would result in the following TSQL:
SELECT [t0].[ContactName], (
SELECT COUNT(*)
FROM [dbo].[Orders] AS [t1] WITH (HOLDLOCK)
WHERE [t1].[CustomerID] = [t0].[CustomerID]
) AS [orderCount]
FROM [dbo].[Customers] AS [t0] WITH (NOLOCK, INDEX(0))

Using:
public static Table<TEntity> With<TEntity> (
this Table<TEntity> table,
params TableHint[] args) where TEntity : class {

//TODO: implement
return table;
}
public static EntitySet<TEntity> With<TEntity> (
this EntitySet<TEntity> entitySet,
params TableHint[] args) where TEntity : class {

//TODO: implement
return entitySet;
}

And

public class TableHint {
//TODO: implement
public static TableHint NoLock;
public static TableHint HoldLock;
public static TableHint Index (int id) {
return null;
}
public static TableHint Index (string name) {
return null;
}
}


Using some type of LINQ to SQL extensibility, other than this one. Any ideas?

Please comment on this question over at StackOverflow.com
Wednesday
Apr092008

LINQ to SQL - code generation bug

The code generation performed by MSLinqToSQLGenerator or SQLMetal generates weird property code. For example, in AdvantureWorks, the table Product has a column ProductLine. Using the tools that come with LINQ to SQL, this column translates to a property:
[Column(Storage="_ProductLine", DbType="NChar(2)")]
public string ProductLine
{
get
{
return this._ProductLine;
}
set
{
if ((this._ProductLine != value)) {
this.OnProductLineChanging(value);
this.SendPropertyChanging();
this._ProductLine = value;
this.SendPropertyChanged("ProductLine");
this.OnProductLineChanged();
}
}
}
The odd code involves the SendPropertyChanging() method call. This method call should pass the name of the property, just like the SendPropertyChanged() method call, according to the documentation. Another interesting detail: The OnProductLineChanging and OnProductLineChanged partial method calls are out of order:

  1. Call OnProductLineChanging() partial method

  2. Raise PropertyChanging event, but don’t tell which property is changing – send an empty string instead

  3. Set the property’s field’s value to the specified new value

  4. Raise PropertyChanged event and specify which property is changing

  5. Call OnProductLineChanged() partial method

Why is the PropertyChanged event raised before calling the OnProductLineChanged partial method?

All classes created by LINQ to SQL add the following protected methods:

protected virtual void SendPropertyChanging() {
if ((this.PropertyChanging != null)) {
this.PropertyChanging(this, emptyChangingEventArgs);
}
}
protected virtual void SendPropertyChanged(String propertyName) {
if ((this.PropertyChanged != null)) {
this.PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
Again, what’s odd about this code is how the SendPropertyChanging() method does not have a property name parameter and sends a emptyChangingEventArgs field reference to the PropertyChanging event rather than creating a new instance of the EventArgs like the SendPropertyChanged() method call does. By creating a new instance of the EventArgs in SendPropertyChanged, it’s able to pass the property name in the constructor (like the documentation says it should).

Here is the field that is passed to all invocations of the event:
private static PropertyChangingEventArgs emptyChangingEventArgs = 
new PropertyChangingEventArgs(String.Empty);

As you can see from this constructor, the property that is changing is an empty string. Given the fact that this is a private field and should not be modified by extension methods, it’s odd that this field is not static readonly.

My guess is that the code is generated incorrectly to account for a data-binding or allocation problem. I’ve come to this conclusion by the emptyChangingEventArgs field – it reduces the object instance creation in half for each property change when there are event consumers for the changing event. The big disadvantage for event consumers is that they doesn’t know which property is changing on an object instance.

One alternative is to use PLINQO, which creates the properties correctly.

UPDATE: I have found that this has already been reported. Unfortunately, Microsoft has closed this bug and said it was by design, even though the generated code does not follow Microsoft's documentation for the PropertyChangingEventArgs class.
Friday
Nov302007

LINQ - WHERE X IN (…)

I couldn't figure out a way to perform the equivalent of WHERE Column1 IN ('A', 'B', 'C') in LINQ, where ('A', 'B', 'C') would represent an IEnumerable<T> where T is the type of Column1. So I thought it was an excellent time to write an extension method that would generate a dynamic expression tree that would add AND (Column1 == "A" OR Column1 = "B" OR ...) to the LINQ query. So I wrote the following code:
public static IQueryable<TSource> WhereIn<TSource, TKey> (
this IQueryable<TSource> source1,
Expression<Func<TSource, TKey>> keySelector,
IEnumerable<TKey> source2) {
if (null == source1)
throw new ArgumentNullException ("source1");
if (null == keySelector)
throw new ArgumentNullException ("keySelector");
if (null == source2)
throw new ArgumentNullException ("source2");
Expression where = null;
foreach (TKey value in source2) {
Expression equal = Expression.Equal (
keySelector.Body,
Expression.Constant (value, typeof (TKey))
);
if (null == where)
where = equal;
else
where = Expression.OrElse (where, equal);
}
return source1.Where<TSource> (
Expression.Lambda<Func<TSource, bool>> (
where, keySelector.Parameters));
}
An example of the usage:
var q = (from u in db.Users
where u.LastLogin > new DateTime (2007, 5, 1)
orderby u.LastLogin descending
select new { u.FirstName, u.LastName, u.UserName, u.LastLogin }
).WhereIn (u => u.UserName, new string[] { "A", "B", "C" });
A day later, I found the right way that will actually generate "WHERE X IN (...)" in LINQ to SQL thanks to Mark Blomsma.