簡體   English   中英

LINQ to Entities .Concat()在兩個IQueryable上引發NullReferencesException

[英]LINQ to Entities .Concat() on two IQueryables throws NullReferencesException

我有以下抽象:

public interface IRepository<TEntity>
{
    IQueryable<TEntity> Entities { get; }
}

具有以下封閉實施:

public class CustomerRepository : IRepository<Customer>
{
    private readonly MyDbContext dbContext;

    public CustomerRepository(MyDbContext dbContext)
    {
        this.dbContext = dbContext;
    }

    public IQueryable<Customer> Entities => InternalCustomers.Concat(ExternalCustomers);

    private IQueryable<Customer> InternalCustomers =>
        from customer in dbContext.InternalCustomers
        select new Customer
        {
            Id = customer.Id,
            Name = customer.Name
            Company = new Company
            {
                Id = 1,
                Name = "Company",
            },
        };

    private IQueryable<Customer> ExternalCustomers =>
        from customer in dbContext.ExternalCustomers
        select new Customer
        {
            Id = customer.Id,
            Name = customer.Name
            Company = new Company
            {
                Id = customer.Company.Id,
                Name = customer.Company.Name,
            },
        };
}

我省略了冗余屬性,並簡化了此示例以強調該問題。

Customer類是一個自定義DTO (我將EntityFramework的實體.InternalCustomers.ExternalCustomers映射到我自己的自定義DTO,可以將其簡化如下:

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Company Company { get; set; }
}

public class Company
{
    public int Id { get; set; }
    public string Name { get; set; }
}

我想指出的是, select中的所有屬性均以正確的順序設置。

執行.Entities查詢時,我收到一個NullReferenceException ,它是從EntitiyFramework.dll拋出的,具有以下頂部EntitiyFramework.dll跟蹤:

   at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitSetOp(SetOp op, Node n, AliasGenerator alias, Func`3 setOpExpressionBuilder)
   at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(UnionAllOp op, Node n)
   at System.Data.Entity.Core.Query.InternalTrees.UnionAllOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
   at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
   at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitAsRelOp(Node inputNode)
   at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(FilterOp op, Node n)
   at System.Data.Entity.Core.Query.InternalTrees.FilterOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
   at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
   at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(ConstrainedSortOp op, Node n)
   at System.Data.Entity.Core.Query.InternalTrees.ConstrainedSortOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
   at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
   at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitAsRelOp(Node inputNode)
   at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.BuildProjection(Node relOpNode, IEnumerable`1 projectionVars)
   at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(PhysicalProjectOp op, Node n)
   at System.Data.Entity.Core.Query.InternalTrees.PhysicalProjectOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
   at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
   at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator..ctor(Command itree, Node toConvert)
   at System.Data.Entity.Core.Query.PlanCompiler.ProviderCommandInfoUtils.Create(Command command, Node node)
   at System.Data.Entity.Core.Query.PlanCompiler.CodeGen.Process(List`1& childCommands, ColumnMap& resultColumnMap, Int32& columnCount)
   at System.Data.Entity.Core.Query.PlanCompiler.PlanCompiler.Compile(List`1& providerCommands, ColumnMap& resultColumnMap, Int32& columnCount, Set`1& entitySets)
   at System.Data.Entity.Core.Query.PlanCompiler.PlanCompiler.Compile(DbCommandTree ctree, List`1& providerCommands, ColumnMap& resultColumnMap, Int32& columnCount, Set`1& entitySets)
   at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition..ctor(DbProviderFactory storeProviderFactory, DbCommandTree commandTree, DbInterceptionContext interceptionContext, IDbDependencyResolver resolver, BridgeDataReaderFactory bridgeDataReaderFactory, ColumnMapFactory columnMapFactory)
   at System.Data.Entity.Core.EntityClient.Internal.EntityProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree, DbInterceptionContext interceptionContext)
   at System.Data.Entity.Core.Common.DbProviderServices.CreateCommandDefinition(DbCommandTree commandTree, DbInterceptionContext interceptionContext)
   at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.CreateCommandDefinition(ObjectContext context, DbQueryCommandTree tree)
   at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.Prepare(ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, Boolean streaming, Span span, IEnumerable`1 compiledQueryParameters, AliasGenerator aliasGenerator)
   at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__6()
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__5()
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0()
   at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
   at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>b__2[TResult](IEnumerable`1 sequence)
   at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable`1 query, Expression queryRoot)
   at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[TResult](Expression expression)
   at System.Data.Entity.Internal.Linq.DbQueryProvider.Execute[TResult](Expression expression)

如您所見,異常消息和stacktrace對我沒有幫助。 尤其是使用LINQ to Entities正在構建的“魔術”表達樹時,這是一個令人眼花puzzle亂的難題(至少對我而言)。

因此,我對存儲庫實現進行了一些細微調整,得出以下觀察結果:

  1. 執行InternalCustomers時,我從數據庫中獲得有效列表。
  2. 執行ExternalCustomers時,我還會從數據庫中獲取有效列表。
  3. 當我使用.Concat()方法時,似乎發生了異常。
  4. 當我具有用於ExternalContacts的以下代碼段時, 不會發生該異常:

程式碼片段:

Company = new Company
{
    Id = 0, //random number
    Name = "Hello",
},

我印象很深刻,這可能是Entity Framework 6.1.3中的錯誤,但我不確定。 我想知道這是否確實是一個錯誤,或者我正在做一些愚蠢的事情,經過3個小時的調查我無法弄清楚。

這絕對是一個錯誤,因為您沒有做錯任何事,而且該異常對用戶不友好。

有一種解決方法,但是需要其他編碼。 訣竅是使用中間投影來“平整”數據類(因為該錯誤與嵌套的Company投影某種程度上有關),然后使用Concat並最終將所需的投影應用於連接的結果。 所有這些都不會影響最終的SQL查詢,因為它應該是簡單的UNION ALL

外觀如下:

public class CustomerRepository : IRepository<Customer>
{
    private readonly MyDbContext dbContext;

    public CustomerRepository(MyDbContext dbContext)
    {
        this.dbContext = dbContext;
    }

    public IQueryable<Customer> Entities => InternalCustomersData.Concat(ExternalCustomersData).Select(CustomerSelector);

    private IQueryable<Customer> InternalCustomers => InternalCustomersData.Select(CustomerSelector);

    private IQueryable<Customer> ExternalCustomers => ExternalCustomersData.Select(CustomerSelector);

    private IQueryable<CustomerData> InternalCustomersData =>
        from customer in dbContext.InternalCustomers
        select new CustomerData
        {
            Id = customer.Id,
            Name = customer.Name,       
            CompanyId = 1,
            CompanyName = "Company",
        };

    private IQueryable<CustomerData> ExternalCustomersData =>
        from customer in dbContext.ExternalCustomers
        select new CustomerData
        {
            Id = customer.Id,
            Name = customer.Name,       
            CompanyId = customer.Company.Id,
            CompanyName = customer.Company.Name,
        };

    private static readonly Expression<Func<CustomerData, Customer>> CustomerSelector = data => new Customer
    {
        Id = data.Id,
        Name = data.Name,
        Company = new Company
        {
            Id = data.CompanyId,
            Name = data.CompanyName,
        }
    };

    private class CustomerData
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int CompanyId { get; set; }
        public string CompanyName { get; set; }
    }
}

煩人,但有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM