Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dude, where's my object? or, Why does Linq not return my object?

Tags:

Once I have the results of my Linq query, I am not always happy. There could be a result that I was expecting to be there but wasn't. For example, my client was expecting that a customer was in a customer list, but it wasn't. It is my client saying "Dude, where's my customer?", not me. I am the Dude, and to remain a dude, I have to give my client the reason.

Is there a simple way to take a given object instance and a Linq query and determine which expressions within the query excluded that instance?

Edit Ok, here is a better example

Output should be something along the lines:

Your Customer was excluded for 2 reasons:

Customer FirstName is Carl but it should be Daniel

Customer Age is 18 but it should be > 20

    public class Customer
    {
        public string FirstName { get; set; }
        public int Age { get; set; }
    }

    [Test]
    public void Dude_wheres_my_object_test1()
    {
        var daniel = new Customer { FirstName = "Daniel", Age = 41 };
        var carl =  new Customer {  FirstName = "Carl", Age= 18 };
        var Customers = new List<Customer>() { daniel, carl };

        // AsQueryable() to convert IEnumerable<T> to IQueryable<T> in 
        //the case of LinqtoObjects - only needed for this test, not 
        //production code where queies written for LinqToSql etc normally 
        //return IQueryable<T>
        var query = from c in Customers.AsQueryable()
                    where c.Age > 20
                    where c.FirstName == "Daniel"
                    select c;
        //query would return Daniel as you'd expect, but not executed here.

        //However I want to explain why Carl was not in the results

        string[] r = DudeWheresMyObject(query, carl);
        Assert.AreEqual("Age is 18 but it should be > 20", r[0]);
        Assert.AreEqual("FirstName is Carl but it should be Daniel", r[1]);


        //Should even work for a Customer who is not 
        //in the original Customers collection...
        var ficticiousCustomer = new Customer { FirstName = "Other", Age = 19};
        string[] r2= DudeWheresMyObject(query, 
                                         ficticiousCustomer);
        Assert.AreEqual("Age is 19 but it should be > 20", r2[0]);
        Assert.AreEqual("FirstName is Other but it should be Daniel", r2[1]);
    }

    public string[] DudeWheresMyObject<T>(IQueryable<T> query, T instance)
    {
        //Do something here with the query.Expression and the instance

    }

First of all, before I attempt to write some fancy Fluent framework, Has anyone done this already?

So far, I have considered navigating the expression tree and executing each branch against an IQueryable that only contains my object. Now I don't have a great deal of experience using raw expression trees, so I would like those who have to suggest any pitfalls or even explain whether this is a dead end and why.

I am anxious that anything that results from this should:

  • Be Reusable - Should be applicable to any object compared against a Linq query returning objects of the same class.
  • Not affect the performance of the original query (this should just be standard Linq).
  • Should be Linq-implementation agnostic.
  • If there are multiple property values set on the missing instance that excluded it from the results, then all of those reasons should be reported.

Edit I am not suggesting that I keep executing LinqToSql against the database multiple times with different permutations of the query and comparing the results. Rather, I am looking for a way to take a single instance and compare it to the expression tree (without executing the query directly again)

Also, I would like an indication of whether others might find this useful. If so, I would consider starting an open source project to solve it.

like image 261
Daniel Dyson Avatar asked Oct 14 '12 19:10

Daniel Dyson


People also ask

Does LINQ select return new object?

Another option is to use LINQ's Select method. Normally, all we ask the Select method to do is return the object that will make up the new collection -- in fact, the Select method insists that the lambda expression passed to it return an object.

What does LINQ return?

LINQ queries return results as objects. It enables you to uses object-oriented approach on the result set and not to worry about transforming different formats of results into objects. The following example demonstrates a simple LINQ query that gets all strings from an array which contains 'a'.

Does LINQ ever return null?

in conclusion no, it won't return null since null can't say sequence contains no elements it will always say object reference not set to an instance of an object ;) Does this answer the question?

What a LINQ query returns in Ling to object?

Language-Integrated Query (LINQ) makes it easy to access database information and execute queries. By default, LINQ queries return a list of objects as an anonymous type. You can also specify that a query return a list of a specific type by using the Select clause.


1 Answers

I think you'd have to re-create the query as linq-to-objects and deal with the subtle differences between linq-to-sql/entities/whatever and linq-to-objects, accepting that some providers just won't work realistically.

You have your object you want to find in an in memory IEnumerable<T> or something.

You'd have to walk the expression tree somehow and snip out the leaves, so say you had:

where obj.foo == true && obj.bar == "yes"

you'd have to figure out that obj.foo == true and obj.bar == "yes" are leaves and start there. It'd be a sort of depth first search of the expression tree.

So, construct linq to objects queries that only had those leaves. See if the object is included in the results. If not then we've found out why it's excluded, if not then go up the tree (i.e. make the where query include more clauses, getting closer to the orignal one until the object disappears from the results).

As I see it the tough parts would be handling the differences between original linq to 'whatever' and link to objects, figuring out where to split the where claues, dealing with things like joins which can also exclude things and dealing with things like SqlMethods.Like that don't work in linq to objects.

like image 105
George Duckett Avatar answered Nov 10 '22 07:11

George Duckett