简体   繁体   中英

Empty Expression and Provider when using Moq and DBSet<T>

I got stuck at a really weird problem for days now and can't find a solution. I'm mocking a DatabaseContext and some DBSet<T> entities. Creating the moq works fine and looks good. When .Any() is called on a special DBSet , it crashes with NullReferenceException . I took a closer look and found out, that Expression and Provider of my mocked DBSet are null again. But I have no idea why.

Function, which creates the DB-Set:

private DbSet<T> GetQueryableMockDbSet<T>() where T : class
{
    var list = mockListCollection.Single(o => o.GetType().GetGenericArguments()[0] == typeof (T))
        .ToList()
        .ConvertAll(o => (T) o);

    var queryable = list.AsQueryable();

    var dbSet = new Mock<DbSet<T>>();
    dbSet.As<IQueryable<T>>()
        .Setup(m => m.Provider)
        .Returns(new TestDbAsyncQueryProvider<T>(queryable.Provider));

    //dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
    dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
    dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
    dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
    dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>(list.Add);

    dbSet.Object.AddRange(list);

    return dbSet.Object;
}

It is called here for every table of my DatabaseContext :

private void InitMockMethods<T>() where T : class, new()
{
    this.DataBaseContextMock.Setup(m => m.Set<T>()).Returns(this.GetQueryableMockDbSet<T>());
    this.DataBaseContextMock.Setup(m => m.Set<T>().Local).Returns(new ObservableCollection<T>(this.GetQueryableMockDbSet<T>()));

    this.DataBaseContextMock.Setup(m => m.Set<T>().Create()).Returns(new T());

    // first get the parameter name out of database context by generic type argument (e. g. DbSet<TblProject>  --> get name by TblProject)
    // because type and typename are not equal.
    // TblProject --> Name is TblProjects
    var paramName = typeof (IDatabaseContext).GetProperties().Single(o => o.CanWrite && o.PropertyType.GetGenericArguments()[0] == typeof(T)).Name;

    // build lamda expression for moq setup
    var parameter = Expression.Parameter(typeof(IDatabaseContext));
    var body = Expression.PropertyOrField(parameter, paramName);
    var lambdaExpression = Expression.Lambda<Func<IDatabaseContext, object>>(body, parameter);

    // setup moq
    this.DataBaseContextMock.Setup(lambdaExpression).Returns((DbSet<T>)this.GetQueryableMockDbSet<T>());
}

After that, all objects look fine. But when I call DatabaseContext.Set<TblVendor>().Any() , it crashes because Provider and Expression are null.

Is anyone out there, who's any idea, what could be wrong?!

Edit: Here's the test function:

[Test]
    [TestCase(11,31)]
    public void TestFilterExtension(int expGl1, int expGl2)
    {
        var userMock = new Mock<AspNetUser>();
        userMock.Setup(x => x.EnableFilterByUserAuthorization).Returns(true);
        userMock.Setup(x => x.Tablename).Returns("TblVendor");
        userMock.Setup(x => x.Columnname).Returns("gl");
        userMock.Setup(x => x.Value).Returns(expGl1.ToString);

        var gl = new Mock<TblVendor>();
        gl.Setup(n => n.Id).Returns(Guid.NewGuid);
        gl.Setup(x => x.GL).Returns(expGl1);

        var gl2 = new Mock<TblVendor>();
        gl2.Setup(n => n.Id).Returns(Guid.NewGuid);
        gl2.Setup(x => x.GL).Returns(expGl2);

        var manager = new VendorManager(base.Manager, MockProvider.Instance.DataBaseContextMock.Object,
            userMock.Object, MockProvider.Instance.EntityHelperMock.Object);

        //MockProvider.Instance.ClearMockList<TblVendor>();

        //manager.AddOrUpdate(gl.Object);
        //manager.AddOrUpdate(gl2.Object);

        var test = MockProvider.Instance.DataBaseContextMock.Object.Set<TblVendor>();
        var b = test.Any(); // <----- NullReferenceException because of missing Provider and or Expression
        //var b = manager.EntityCollection;
        //var data = manager.EntityCollection.ToList();

    }

I see you have used the async provider way, so i would tell you to try to implement the getenumerator async too, as explained in the msdn guide how to mock ef dbcontext.

Once it is done, avoid to moq queries because it is not needed. Your mock already will work with linq queries as expected! Also, if there is another function call as Include() you must mock it to return the itrm you want( most of the times will be the original object mock, the original list)

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