【问题标题】:Query entity relation which is null on mocked Db Context查询在模拟 Db 上下文上为空的实体关系
【发布时间】:2018-04-12 12:10:33
【问题描述】:

我使用Microsoft docs 来模拟DbContext。我在查询为空的IOrderedQueryable 时遇到问题。

var report = new Report();
report.DataSource = null;
var q = context.Reports.Select(x => x.DataSource).OrderBy(x => x.Name);
var results = q.ToList();

当我调用ToList 时,它会在这一行的TestDbAsyncEnumerator.MoveNextAsync 中抛出一个错误

return Task.FromResult(_inner.MoveNext())

错误:

System.AggregateException:发生一个或多个错误。
System.NullReferenceException:对象引用未设置为对象的实例。

在 lambda_method(关闭,经验)
在 System.Linq.EnumerableSorter2.ComputeKeys(TElement[] elements, Int32 count)
at System.Linq.EnumerableSorter
1.Sort(TElement[] 元素,Int32 计数)
在 System.Linq.OrderedEnumerable1.<GetEnumerator>d__1.MoveNext()
at S360.Tests.TestDbAsyncEnumerator
1.MoveNextAsync(CancellationToken cancelToken)

如果我删除 OrderByOrderBy x =&gt; 0 一切正常。有什么解决方案可以模拟 DbContext 以便我的查询不会引发任何异常?

【问题讨论】:

    标签: c# entity-framework unit-testing mocking moq


    【解决方案1】:

    请参阅 Microsoft 文档中的 Limitations of EF in-memory test doubles

    当您投影到Select() 中的.DataSource 时,它很可能为null,当您尝试访问OrderBy() 中的.Name 时,这会导致NullReferenceException。

    当您对数据库执行此查询时,EF 将看到对 DataSource 导航属性的访问,并包含 JOIN 以便获取它。

    在内存中执行时,不会发生这种连接。

    您可以在测试设置中将预期的DataSource 实体添加到Reports 实体,或者更好的是使用Effort in-memory provider,它将填充导航属性,就像真正的数据库一样。

    【讨论】:

      【解决方案2】:

      就我而言,我还收到了与 lambda_method 相关的异常:

      System.NullReferenceException occurred
        HResult=-2147467261
        Message=Object reference not set to an instance of an object.
        Source=Anonymously Hosted DynamicMethods Assembly
        StackTrace:
             at lambda_method(Closure , InspectionAsset )
             at System.Linq.Enumerable.<>c__DisplayClass6_0`1.<CombinePredicates>b__0(TSource x)
             at System.Linq.Enumerable.WhereListIterator`1.MoveNext()
             at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
             at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
             at AssetManagement.Model.Repositories.AssetDataService.<LoadOtherAssetsFromTrackAsset>d__18.MoveNext() in C:\src\AssetManagement\AssetManagement.Model\Repositories\AssetDataService.cs:line 135
        InnerException: 
      

      对我来说,我最初是这样构建数据的,但query = query.Where(filter); 发生了异常:

      using Nito.AsyncEx;
      
          // ...
      
          private static readonly AsyncLock _assetsMutext = new AsyncLock();
          public List<Expression<Func<sset, bool>>> Filters { get; } = new List<Expression<Func<Asset, bool>>>();
      
          // ...
      
          public async Task<IQueryable<Asset>> LoadAssetsOnTrack(IList<Asset> trackAssets, List<Asset> assets)
          {
              IQueryable<IAsset> query = null;
              await Task.Run(async () =>
              {
                  using (await _assetsMutext.LockAsync())
                  {
                      var equipmentList = this.GetEquipment();
      
                      Parallel.ForEach(trackAssets, (asset, state) =>
                      {
                          if (!AssetRepository.GetIsAssetTrackType(asset.TypeCode))
                          {
                              var equipment = equipmentList.Where(x => x.AssetID == asset.AssetID).FirstOrDefault();
      
                              var inspectionAsset = new Asset()
                              {
                                  AssetID = equipment?.AssetID,
                                  Description = equipment?.Description,
                                  // ...
                              };
                              // ...
                              assets.Add(inspectionAsset);
                          }
                      });
                      query = assets.AsQueryable();
      
                      foreach (var filter in Filters)
                      {
                          query = query.Where(filter);
                      }
                  }
              });
              return query;
          }
      

      为了消除异常,我将函数更改为:

          public async Task<IQueryable<Asset>> LoadAssetsOnTrack(IList<Asset> trackAssets, List<Asset> assets)
          {
              IQueryable<IAsset> query = assets.AsQueryable();
      
              foreach (var filter in Filters)
              {
                  query = query.Where(filter);
              }
              await Task.Run(async () =>
              {
                  using (await _assetsMutext.LockAsync())
                  {
                      var equipmentList = this.GetEquipment();
      
                      foreach (var asset in trackAssets)
                      {
                          if (!AssetRepository.GetIsAssetTrackType(asset.TypeCode))
                          {
                              var equipment = equipmentList.Where(x => x.AssetID== asset.AssetID).FirstOrDefault();
      
                              var inspectionAsset = new Asset()
                              {
                                  AssetID = equipment?.AssetID,
                                  Description = equipment?.Description,
                                  // ...
                              };
                              // ...
                              assets.Add(inspectionAsset);
                          }
                      }
                  }
              });
      
              return query;
          }
      

      也就是说,我仍然不完全清楚为什么我会偶尔在函数的顶部版本中得到异常,而不是在底部版本中得到它。如果有人有任何进一步的见解,请告诉我。

      【讨论】:

        猜你喜欢
        • 2016-12-20
        • 1970-01-01
        • 2010-12-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-13
        • 1970-01-01
        • 2019-11-21
        相关资源
        最近更新 更多