26

I'm trying to create a mock for my IRepository interface:

public interface IRepository<T> : ICollection<T>, IQueryable<T>
{
}

With this implementation:

public class RepositoryFake<T> : List<T>, IRepository<T>
{
    public Expression Expression
    {
        get
        {
            return this.AsQueryable().Expression;
        }
    }

    public Type ElementType
    {
        get
        {
            return this.AsQueryable().ElementType;
        }
    }

    public IQueryProvider Provider
    {
        get
        {
            return this.AsQueryable().Provider;
        }
    }
}

But when I use it, I'm getting StackOverflow exception. How to implement this interface correctly to be able to use just a List as a repository?

Usage is very simple

[Test]
public void Test()
{
    RepositoryFake<User> users = new RepositoryFake<User>();
    users.Add(new User());

    List<User> list = (from user in users 
                 where user.Id == "5"
                 select user).ToList();

    Assert.That(list, Is.Empty);
}

Here is screenshot of exception:

Exception

Sly
  • 15,046
  • 12
  • 60
  • 89

2 Answers2

31

The reason for your problem is that if you perform AsQueryable it checks if the object already implements IQueryable and if yes returns it.

Use new EnumerableQuery<T>(this) instead of AsQueryable which doesn't perform this check.


Workaround for .net 3.5:

return ((IEnumerable<T>)this).Select(x=>x).AsQueryable()

First casts to IEnumerable<T> so the chosen Select method will be Enumerable.Select not Queryable.Select. The identity select will then return a new object that does not implement IQueryable<T>, so the check if it's implemented in AsQueryable fails.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • The constructor taking an `IEnumerable` is public. Check http://msdn.microsoft.com/en-us/library/cc190806.aspx At least on .net 4 – CodesInChaos Jul 20 '11 at 16:47
  • You included both the cast to `IEnumerable` and the `Select`? – CodesInChaos Jul 20 '11 at 16:53
  • 1
    Second option did the job. Thanks! – Sly Jul 20 '11 at 16:55
  • Seems very weird to me. In my particular case this caused our server to use 100% CPU, debugging revealed that all db rows were instantiated as objects in memory – dksh Jan 24 '14 at 05:32
  • @dksh This is designed to reuse `IQueryable` code on an `IEnumerable`, for example for testing. It's not designed to query a SQL database since anything `IEnumerable` based, even when converted to an `IQueryable` needs to iterate over everything instead of translating the query to SQL. – CodesInChaos Jan 24 '14 at 08:26
1

Try using base.AsQueryable() instead of this.AsQueryable().

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445