views:

215

answers:

0

I have a non-trivial Linq To Sql query that I'm trying to break down into pieces for the sake of readability / further filtering / reuse.

The refactored code looks like this, where ids is the subquery performed to grab the ids.

 var results = from solution in context.csExtendedQAIncident_Docs
                join solutionText in context.csNTexts
                   on solution.chIdNo equals solutionText.chIdNo
                where solutionText.chColumnId == "Solution"
                //this is a very complicated subquery that returns a short list of the ids we need
                && (ids).Select(s => s.chIdNo)
                //the TOP query portion - applied to just the ids
                .Take(count ?? Settings.Current.WCFServices().Output.HomePage.MaxRows)
                .Contains(solution.chIdNo)
                select solution;

The 'ids' is an IOrderedQueryable<csExtendedQAIncident_Docs> which itself has a number of criteria and nested subqueries (i.e. Contains which gets translated into EXISTS style queries on SQL server)

In any event, the issue here is that when the full-blown subquery is included in the results query above, the query works without a hitch.

When the query is pulled out into it's own variable, the query dies at runtime with a NullReferenceException at SqlFactory.Member (partial stack trace below)

System.NullReferenceException: Object reference not set to an instance of an object. at System.Data.Linq.SqlClient.SqlFactory.Member(SqlExpression expr, MemberInfo member) at System.Data.Linq.SqlClient.QueryConverter.VisitMemberAccess(MemberExpression ma) at System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node) at System.Data.Linq.SqlClient.QueryConverter.Visit(Expression node) at System.Data.Linq.SqlClient.QueryConverter.VisitExpression(Expression exp) at System.Data.Linq.SqlClient.QueryConverter.VisitMethodCall(MethodCallExpression mc) at System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node) at System.Data.Linq.SqlClient.QueryConverter.Visit(Expression node) at System.Data.Linq.SqlClient.QueryConverter.VisitExpression(Expression exp) at

I suspect this is a bug in Linq to Sql's query evaluation process -- but was wondering if anyone else might have come across such an issue?

I stepped into the Framework source code -- but of course the trouble with that is that while I can hit the exact source line of the exception, the Linq assembly I'm using has been optimized and I can't inspect variables, etc.

Note: Keep in mind that this query, despite being a little long is actually not a great candidate in this scenario for a SPROC (especially since we're stuck on SQL 2000 here and can't parameterize TOP :( )

Edit: Found one other reference to a very similar issue out there that was never resolved http://www.eggheadcafe.com/software/aspnet/31934404/linq-combined-query.aspx

Edit2: Ask and ye shall receive ;0 I figured I would keep the id subquery out of here because I don't think its actually relevant (and as mentioned its complicated, etc). Please don't blame the crap db design on me -- but know that I worked on the SQL for this query for a bit to get acceptable performance, and then translated to Linq To Sql.

var ids = from solutionIds in context.csExtendedQAIncident_Docs
          where solutionIds.iIncidentTypeId == 102094
          && solutionIds.tiRecordStatus == 1
          && solutionIds.iLanguage == 102074
          && null != solutionIds.chIdNo
          && (from openTo in context.csOpenTos
              where onyxIdentity.GetDocumentAccessLevels().Union(new[] { "BUSG5" }).ToArray().Contains(openTo.vchOpenTo)
              select openTo.chIdNo
             ).Distinct().Contains(solutionIds.chIdNo)
          && (from solutionProductAssocation in context.csProductDocs
              where (from allowedProduct in context.KB_User_Allowed_Products
                     where allowedProduct.UserId == userId
                     select allowedProduct.ModelCode
                    ).Contains(solutionProductAssocation.chModelCd)
              select solutionProductAssocation.chIdNo).Distinct().Contains(solutionIds.chIdNo)
          orderby solutionIds.dtUpdateDate descending
          select solutionIds;