查询实体关系,在模拟的Db上下文中为null

时间:2017-10-31 10:46:08

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

我正在使用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.EnumerableSorter 2.ComputeKeys(TElement[] elements, Int32 count)
at System.Linq.EnumerableSorter
1.Sort(TElement []元素,Int32计数)
  在System.Linq.OrderedEnumerable 1.<GetEnumerator>d__1.MoveNext()
at S360.Tests.TestDbAsyncEnumerator
1.MoveNextAsync(CancellationToken cancellationToken)

如果我删除OrderByOrderBy x => 0,一切正常。我可以如何模拟DbContext以便我的查询不会抛出任何异常的任何解决方案?

2 个答案:

答案 0 :(得分:0)

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

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

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

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

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

答案 1 :(得分:0)

就我而言,我还收到了与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;
    }

也就是说,我仍然不完全清楚为什么我会偶尔在函数的最高版本中获取异常而不在底层版本中获取该异常。如果有人有进一步的见解,请告诉我。