0

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'.'

Dilshod K
  • 2,924
  • 1
  • 13
  • 46
  • 2
    Obviously you cannot cast anything else than a `MyClass` object to `MyClass` and you won't be able to specify the type parameter with an anonymous type. Please refer to @JaredPar's answer [here](https://stackoverflow.com/a/775402/7252182). – mm8 May 03 '19 at 09:21
  • @mm8, But when I select property it works. And it isn't MyClass type – Dilshod K May 03 '19 at 10:06

1 Answers1

0

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();

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
  • You can not do return this.Queryable; (in your first code) and also you can not do this.queryable.OfType(); (in your second peace of code) – Dilshod K May 06 '19 at 13:22
  • Whether you can use `IQueryable.OfType<...>()` or not depends on how smart your `IQueryable.Provider` is. According to [Supported and unsupported LINQ methods for Liq to entities] (https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/supported-and-unsupported-linq-methods-linq-to-entities) this is one of the functions supported by entity framework – Harald Coppoolse May 06 '19 at 13:33