views:

90

answers:

1

I have the following Subsonic 3.0 query, which contains a nested NotIn query:

public List<Order> GetRandomOrdersForNoReason(int shopId, int typeId)
{
    // build query    
    var q = new SubSonic.Query.Select().Top("1")
        .From("Order")
        .Where("ShopId")
        .IsEqualTo(shopId)
        .And(OrderTable.CustomerId).NotIn(
            new Subsonic.Query.Select("CustomerId")
                .From("Customer")
                .Where("TypeId")
                .IsNotEqualTo(typeId))
            .OrderDesc("NewId()");

    // Output query
    Debug.WriteLine(q.ToString());   

    // returned typed list
    return q.ExecuteTypedList<Order>();
}

The internal query appears to be incorrect:

SELECT TOP 1 *
 FROM [Order]
 WHERE ShopId = @0 AND CustomerId NOT IN (SELECT CustomerId
 FROM [Customer]
 WHERE TypeId = @0)
 ORDER BY NewId() ASC

You'll notice that both parameters are @0. I'm assuming that the parameters are enumerated (starting at zero), for each "new" Select query. However, in this case where the two Select queries are nested, I would have expected the output to have two parameters named @0 and @1.

My query is based on one that Rob Conery gave on his blog as a preview of the "Pakala" query tool that became Subsonic 3. His example was:

int records = new Select(Northwind.Product.Schema)
    .Where("productid")
    .In(
        new Select("productid").From(Northwind.Product.Schema)
        .Where("categoryid").IsEqualTo(5)
        )
    .GetRecordCount();

Has anyone else seen this behavior? Is it a bug, or is this an error or my part? Since I'm new to Subsonic I'm guessing that this probably programmer error on my part but I'd like confirmation if possible.

+1  A: 

I'm not sure about SubSonic 3 but in SubSonic 2 if you would run this code the inner query would be executed first and the second query would have the CategoryIds allready defined as a parameter in the query.
Maybe this is a bug and you should post it on github.

Anyway you could make your query work for the moment and behave like a SubSonic 2 Subquery with this little change:

var q = new SubSonic.Query.Select().Top("1")
    .From("Order")
    .Where("ShopId")
    .IsEqualTo(shopId)
    .And(OrderTable.CustomerId).NotIn(
        new Subsonic.Query.Select("CustomerId")
            .From("Customer")
            .Where("TypeId")
            .IsNotEqualTo(typeId)
            .ExecuteTypedList<int>()
    )
    .OrderDesc("NewId()");

NotIn should take a IEnumerable as a parameter but q will contain the whole list of CustomerIds as a parameter before the outer part is executed.

Not a real solution but a quick fix for the moment (if it doesn't affect performance to much).

SchlaWiener