【问题标题】:System.TypeLoadException: Could not load type 'Microsoft.EntityFrameworkCore.Query.Internal.IAsyncQueryProvider'System.TypeLoadException:无法加载类型“Microsoft.EntityFrameworkCore.Query.Internal.IAsyncQueryProvider”
【发布时间】:2020-09-28 11:01:18
【问题描述】:

我正在我的 .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();
        }

【问题讨论】:

  • 你能发布其余的堆栈跟踪吗?这将取决于GetUserAsync 在幕后所做的事情;例如,它可能正在尝试使用尚未设置的模拟服务提供者加载类型。
  • @rgvlee 我将它添加到问题中,以及我的存储库中的 GetUserAsync 函数

标签: unit-testing asp.net-core entity-framework-core asp.net-identity moq


【解决方案1】:

_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&lt;UserIdentity&gt;

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&lt;T&gt; 提供程序,以上只是您可以做到的一种方法。

如果我们将所有这些整合到一个有效的 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 而不是模拟;如果您想走这条路,请点击链接获取示例。

【讨论】:

    【解决方案2】:

    虽然我已经很晚了,但在与这个错误作斗争之后,我想也应该发布我的一点。

    在我的例子中,一个包 MockQueryable.Moq(https://www.nuget.org/packages/MockQueryable.Moq/) 被使用,升级解决了这个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多