首页 > 解决方案 > System.TypeLoadException:无法加载类型“Microsoft.EntityFrameworkCore.Query.Internal.IAsyncQueryProvider”

问题描述

我正在我的 .NET Core Web 应用程序中测试我的身份操作,但一直遇到问题。我最近找到了一种创建模拟用户管理器而不会遇到其参数问题的方法,但随后出现一个新错误,我也找不到任何解决方案:“System.TypeLoadException:无法加载类型'Microsoft.EntityFrameworkCore。 Query.Internal.IAsyncQueryProvider'"

这是我的相关代码:

设置模拟用户管理器:

var _userManager = new Mock<FakeUserManager>();
            UserIdentity user1 = new UserIdentity() { Id = UserId1, UserName = "test@gmail.com", Score = 5 };
            UserIdentity user2 = new UserIdentity() { Id = UserId2, UserName = "pragim@gmail.com", Score = 1 };
            UserIdentity user3 = new UserIdentity() { Id = UserId3, UserName = "ajax@gmail.com", Score = 0 };
            UserIdentity user4 = new UserIdentity() { Id = UserId4, UserName = "pim@gmail.com", Score = 4 };
            List<UserIdentity> users = new List<UserIdentity>() { user1, user2, user3, user4 };
            var mock = users.AsQueryable().BuildMock();
            _userManager.Setup(x => x.Users).Returns(mock.Object);

var identityRepository = new IdentityRepository(_userManager.Object, null, null);
            _identityService = new IdentityService(identityRepository);

FakeUserManager.cs:

public class FakeUserManager : UserManager<UserIdentity>
    {
        public FakeUserManager()
            : base(new Mock<IUserStore<UserIdentity>>().Object,
                  new Mock<IOptions<IdentityOptions>>().Object,
                  new Mock<IPasswordHasher<UserIdentity>>().Object,
                  new IUserValidator<UserIdentity>[0],
                  new IPasswordValidator<UserIdentity>[0],
                  new Mock<ILookupNormalizer>().Object,
                  new Mock<IdentityErrorDescriber>().Object,
                  new Mock<IServiceProvider>().Object,
                  new Mock<ILogger<UserManager<UserIdentity>>>().Object)
        { }

    }

测试方法:

        [TestMethod()]
        public async Task GetUserAsyncTest()
        {
            //Arrange

            //Act
            var user = await _identityService.GetUserAsync(UserId4);

            //Assert
            Assert.AreEqual("pim@gmail.com", user.UserName);
        }

有谁知道我的问题的绕过/解决方案?

更新:堆栈跟踪:

EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
    EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, CancellationToken cancellationToken)
    EntityFrameworkQueryableExtensions.SingleOrDefaultAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
    IdentityRepository.GetUserAsync(Nullable`1 userId) line 55
    IdentityService.GetUserAsync(Nullable`1 id) line 172
    IdentityServiceTests.GetUserAsyncTest() line 102
    ThreadOperations.ExecuteWithAbortSafety(Action action)

以及存储库中的 GetUserAsync 方法:

        public async Task<UserIdentity> GetUserAsync(Guid? userId)
        {
            return await _userManager.Users.Where(x => x.Id.Equals(userId)).SingleOrDefaultAsync();
        }

标签: unit-testingasp.net-coreentity-framework-coreasp.net-identitymoq

解决方案


_users序列需要一个实现IAsyncQueryProvider. 有几种方法可以做到这一点,以下是一个模拟的实现。

首先,一些基于您的 OP 的脚手架:

public class UserIdentity : IdentityUser<Guid>
{
    public int Score { get; set; }
}

public class IdentityRepository
{
    readonly UserManager<UserIdentity> _userManager;

    public IdentityRepository(UserManager<UserIdentity> userManager)
    {
        _userManager = userManager;
    }

    public async Task<UserIdentity> GetUserAsync(Guid? userId)
    {
        return await _userManager.Users.SingleOrDefaultAsync(x => x.Id.Equals(userId));
    }
}

创建您的模拟IAsyncQueryProvider并将其添加到您自己的IQueryable<UserIdentity>

var user1 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "foo@bar.com", Score = 1 };
var user2 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "bar@baz.com", Score = 2 };
var user3 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "tony stark", Score = 3000 };
var dataSource = new List<UserIdentity> { user1, user2, user3 }.AsQueryable();

var providerMock = new Mock<IAsyncQueryProvider>();
providerMock.Setup(x => x.ExecuteAsync<Task<UserIdentity>>(It.IsAny<Expression>(), It.IsAny<CancellationToken>()))
    .Returns((Expression providedExpression, CancellationToken providedCancellationToken) => Task.FromResult(dataSource.Provider.Execute<UserIdentity>(providedExpression)));

var usersMock = new Mock<IQueryable<UserIdentity>>();
usersMock.Setup(x => x.ElementType).Returns(dataSource.ElementType);
usersMock.Setup(x => x.Expression).Returns(dataSource.Expression);
usersMock.Setup(x => x.Provider).Returns(providerMock.Object);

...

userManagerMock.Setup(x => x.Users).Returns(() => usersMock.Object);

您需要一种设置IQueryable<T>提供程序的方法,以上只是您可以做到的一种方法。

如果我们将所有这些整合到一个有效的 LINQPad 示例中:

void Main()
{
    var user1 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "foo@bar.com", Score = 1 };
    var user2 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "bar@baz.com", Score = 2 };
    var user3 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "tony stark", Score = 3000 };
    var dataSource = new List<UserIdentity> { user1, user2, user3 }.AsQueryable();

    var providerMock = new Mock<IAsyncQueryProvider>();
    providerMock.Setup(x => x.ExecuteAsync<Task<UserIdentity>>(It.IsAny<Expression>(), It.IsAny<CancellationToken>()))
        .Returns((Expression providedExpression, CancellationToken providedCancellationToken) => Task.FromResult(dataSource.Provider.Execute<UserIdentity>(providedExpression)));

    var usersMock = new Mock<IQueryable<UserIdentity>>();
    usersMock.Setup(x => x.ElementType).Returns(dataSource.ElementType);
    usersMock.Setup(x => x.Expression).Returns(dataSource.Expression);
    usersMock.Setup(x => x.Provider).Returns(providerMock.Object);

    var userManagerMock = new Mock<UserManager<UserIdentity>>
            (new Mock<IUserStore<UserIdentity>>().Object,
                  new Mock<IOptions<IdentityOptions>>().Object,
                  new Mock<IPasswordHasher<UserIdentity>>().Object,
                  new IUserValidator<UserIdentity>[0],
                  new IPasswordValidator<UserIdentity>[0],
                  new Mock<ILookupNormalizer>().Object,
                  new Mock<IdentityErrorDescriber>().Object,
                  new Mock<IServiceProvider>().Object,
                  new Mock<ILogger<UserManager<UserIdentity>>>().Object);

    userManagerMock.Setup(x => x.Users).Returns(() => usersMock.Object);

    var identityRepository = new IdentityRepository(userManagerMock.Object);

    var result = identityRepository.GetUserAsync(user2.Id).Result;

    Console.WriteLine(result);
}

public class UserIdentity : IdentityUser<Guid>
{
    public int Score { get; set; }
}

public class IdentityRepository
{
    readonly UserManager<UserIdentity> _userManager;

    public IdentityRepository(UserManager<UserIdentity> userManager)
    {
        _userManager = userManager;
    }

    public async Task<UserIdentity> GetUserAsync(Guid? userId)
    {
        return await _userManager.Users.SingleOrDefaultAsync(x => x.Id.Equals(userId));
    }
}

我们得到了想要的结果:

在此处输入图像描述

IdentityService在上面省略了你的,因为它本质上是答案的细节;你只需要得到一个工作,嘲笑UserManager。对于我的模拟库,特别是EntityFrameworkCore.Testing,我会使用具体IAsyncQueryProvider而不是模拟;如果您想走这条路,请点击链接获取示例。


推荐阅读