简体   繁体   中英

EF6: getting data from derived DbSet

My ultimate reason of doing what I do is to provide some caching capabilities to the DbContext . In order to achieve this, I'm trying to extend default DbSet functionality by overriding Set<T>() method in the context, and returning object of type that is directly derived from general DbSet class. Like this:

public class Context : DbContext
{
    ...

    public override DbSet<TEntity> Set<TEntity>()
    {
        //var set = base.Set<TEntity>();
        var set = new XxSet<TEntity>();
        return set;
    }
}

class XxSet<TEntity> : System.Data.Entity.DbSet<TEntity>
    where TEntity : class
{
    public override TEntity Find(params object[] keyValues)
    {
        return base.Find(keyValues);
    }
}

Looks clear and simple, but when I run my tests on this, EF gives me an error:

The member 'IQueryable.Provider' has not been implemented on type 'XxSetˊ1' which inherits from 'DbSetˊ1'. Test doubles for 'DbSetˊ1' must provide implementations of methods and properties that are used.

It asks me to implement IQueryable interface. That's I see, but why? Shouldn't it already be implemented in the base class? I guess it must, otherwise, how would work the base functionality of Set<T>() method, which is returning object instantiated by System.Data.Entity.DbSet<TEntity> type.

Am I missing something here?

BTW. I know about Microsoft example of creating in-memory DbSets. I just want to understand why can't I do this simple way.

I don't know why it dosen't work, this looks like something that could have been done but entity dosen't like it, in this link :

NSubstitute DbSet / IQueryable<T>

there are similar errors, sorry if it dosen't help you at all –

It was the answer then ? Glad it helped you.

I've had some of these issues with classes implementing interfaces inheriting IDbSet. It's because all (or at least most of) the IQueryable methods are not implemented in the DbSet, but are extension Methods of the Queryable class. Most mocking frameworks cannot mock static extension methods. I overcame the problem by adding the method to my derived interface and implemented the methods in my class : IDerivedDbSet as per these examples:

 public IQueryable<T> Where(Expression<Func<T, bool>> predicate)
    {
        return this._dbSet.Where(predicate);
    }

    public bool Any(Expression<Func<T, bool>> predicate)
    {
        return this._dbSet.Any(predicate);
    }

Testing where these methods are used is a bit dirty though, because you now have to mock the method instead of relying on the extension actually executing on an IQueryable injected by a mock. Here's an example:

 this._context = Substitute.For<IDerivedContext>();
 this._permissionSet = Substitute.For<IDerivedSet<Permission>> ();
 this._permission1 = new Permission
        {
            UserID = 1,
            MenuID = 26,
            PermissionAllow = true
        };
 var queryable = (new List<Permission> { this._permissionLink1 }).AsQueryable();
 this._permissionSet.Any(Arg.Any<Expression<Func<Permission, bool>>>()).Returns(x => queryable.Any((Expression<Func<Permission, bool>>)x[0]));

 var result = this._permissionsProvider.HasPermissionToSomething(this._context, 1);
 Assert.IsTrue(result);

The dirty bit is that the expression parameter for the IDerivedDbSet.Any method is then used as the expression parameter for the Queryable.Any extension method invoked on queryable directly. It's not great, because it assumes that the two Any methods do the same thing, but it does allow me mock the actual query implemented and assert that the tested class is returning the correct result on that basis. If I change the query in the tested class method, the test fails.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM