Currently I'm working on a project which has an additional layer built on top of Entityframework for Converting Queries from entity to DTO. We use a custom queryables to abstract the expression creation to call convert before materializing.
public Expression ConvertExpression(Expression origExpression)
{
var expandedExpression = ExpressionExtensions.Expand(origExpression);
var dummyValueExpr = expandedExpression as ConstantExpression;
if (null != dummyValueExpr)
{
// Check if the expression is actually a dummy marker for some wrapper queryable
var dummyQueryableValueConstant = dummyValueExpr.Value as IDummyQueryableValueConstant;
if (null == dummyQueryableValueConstant)
{
// It's possible that the constant value is also another ConvertedQueryable.
var innerQueryable = dummyValueExpr.Value as IConvertedQueryable;
if ((null != innerQueryable) && (innerQueryable != this) && (innerQueryable.Expression is ConstantExpression))
{
return innerQueryable.ConvertExpression(innerQueryable.Expression);
}
}
if (null != dummyQueryableValueConstant)
{
return this.ConvertExpression(dummyQueryableValueConstant.StoredQueryable.Expression);
}
}
return expandedExpression;
}
We've overridden IQueryable Provider methods to call our conversion before execution:
public override object Execute(Expression expression)
{
var constructors = AddConstructorsFromExpression(expression);
var convertedExpression = this.ExpressionConverter.Visit(expression);
var result = this._queryProvider.Execute(convertedExpression);
if (null == result)
{
return null;
}
var types = GetInnerAndOuterTypesForInnerObject(result, convertedExpression.Type, expression.Type);
return this._innerToOuterConverterFactory()
.Convert(result, types.Key, types.Value, x => GetConstructorOrNull(constructors, x));
}
The Include statements work fine when I am not using a GroupBy or Select(x => new { x }); My first intuition was that the expression Generated By the queryable was wrong. So I mapped the expressions based on using our context vs using entityframework on the northwind
.Call System.Linq.Queryable.GroupBy( .Call System.Linq.Queryable.Where( .Call (.Call .Constant1[StagingNorthWindTest.Customer]>(System.Data.Entity.Core.Objects.ObjectQuery1[StagingNorthWindTest.Customer]).MergeAs(.Constant(AppendOnly)) ).IncludeSpan(.Constant(System.Data.Entity.Core.Objects.Span)), '(.Lambda #Lambda1))
.Lambda #Lambda1(StagingNorthWindTest.Customer $x) { True }
.Lambda #Lambda2(StagingNorthWindTest.Customer $x) { $x.City }
.Call System.Linq.Queryable.GroupBy( .Call System.Linq.Queryable.Where( .Call (.Call .Constant1[BDBPayroll.Services.DataAccess.RowEntities.DivisionRptPackageRow]>(System.Data.Entity.Core.Objects.ObjectQuery1[BDBPayroll.Services.DataAccess.RowEntities.DivisionRptPackageRow]).MergeAs(.Constant(AppendOnly)) ).IncludeSpan(.Constant(System.Data.Entity.Core.Objects.Span)), '(.Lambda #Lambda1))
.Lambda #Lambda1(BDBPayroll.Services.DataAccess.RowEntities.DivisionRptPackageRow $x) { True }
.Lambda #Lambda2(BDBPayroll.Services.DataAccess.RowEntities.DivisionRptPackageRow $x) { $x.DivisionRptPackageID }
The expressions Generated for the same actions against northwind vs our context use the same syntax and pathing. However the internal DbQueryProvider seems to ignore my include spans despite them properly containing includes.
Is there anything special that entityframework does when generating Projections I.E Group by or anonymous Selects?
EDIT
I finally found out a small difference when I use the normal EF, the include calls the dbQuery Path while, when I use my context it calls the QueryableExtensions.CommonInclude
if (dbQuery != null)
return (IQueryable<T>) dbQuery.Include(path);
ObjectQuery<T> objectQuery = source as ObjectQuery<T>;
if (objectQuery != null)
return (IQueryable<T>) objectQuery.Include(path);
return QueryableExtensions.CommonInclude<IQueryable<T>>(source, path);
What is the difference between these two methods?