I have class implemented from IEnumerable<T> :

    public class MyList<T> : IEnumerable<T>
    {
        IQueryable Queryable;
        public MyList(IQueryable ts)
        {
            Queryable = ts;
        }
        public IEnumerator<T> GetEnumerator()
        {
            foreach (var item in Queryable)
            {
                yield return (T)item;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

When I select some property from IQuerable and do ToList(), it works:

        IQueryable propertiesFromClasses = classesIQuerable.Select(a => a.Property);
        MyList<MyClass> classes = new MyList<MyClass>(propertiesFromClasses);
        var toListResult = classes.ToList();

Now, I want to select dynamic types:

        IQueryable propertiesFromClasses = classesIQuerable.Select(a => new { SelectedProperty = a.Property });
        MyList<MyClass> classes = new MyList<MyClass>(propertiesFromClasses);
        var toListResult = classes.ToList();

It throws exception in yield return (T)item; of GetEnumerator() method:

System.InvalidCastException: 'Unable to cast object of type '<>f__AnonymousType0`1[System.String]' to type 'MyClass'.'

1 Answers

0
Harald Coppoolse On Best Solutions

The problem is obviously in the constructor of MyClass<T>.

Make a proper constructor

First of all, if you want to implement a class MyClass<Order> that represents an enumerable sequence of Order objects, why do you allow a constructor that accepts IQueryable<Student>?

Wouldn't it be better to only accept a sequence of Orders, or at least a sequence of items that can be converted to Orders?

This way your compiler will complain, instead of later during execution of the code.

public class MyList<T> : IEnumerable<T>
{
    IQueryable<T> queryable;
    public MyList(IQueryable<T> ts)
    {
        this.queryable = ts;
    }
    public IEnumerator<T> GetEnumerator()
    {
        return this.Queryable;
    }
    ...
}

If you'd done this, your compiler would already have complained, instead of a run-time exception.

The object that you created in your 2nd piece of code is an object that implements IQueryable<someAnonymousClass>. Your debugger will tell you that this object does not implement IQueryable<MyClass>

Objects that implement IQueryable<someAnonymousClass> also implements IQueryable. However, elements that can be enumerated from this IQueryable, are of type someAnonymousClass those objects can't be cast to MyClass without a proper conversion method.

Your first piece of code does not lead to this problem, because the object that you created implements IQueryable<MyClass>, and thus the enumerator will yield MyClass objects, which can be converted to MyClass objects without problems.

But again: if you had made a proper constructor, you would have noticed the problem at compile time, instead of at run-time.

Alternative solution

It could be that you really wanted to design a class that could hold a sequence of Students in and try to get extract all Orders from it, or a more realistic problem: put a sequence of Animals and enumerate all Birds. Such a class could extract all Birds from any sequence:

public class FilteredEnumerator<T> : IEnumerable<T>
{
    public FilteredEnumerator(IQueryable queryable)
    {
         this.queryable = queryable;
    }

    private readonly IQueryable queryable;

    private IEnumerator<T> GetEnumerator()
    {
        return this.queryable.OfType<T>();
    }
}

Although this would work. I'm not sure if it would be a very meaningful class. There is already a LINQ function that does what you want:

var birds = myDbContext.Animals.OfType();