It compiles the expression into a delegate which is then executed when the data comes in. This is also shown in the stacktrace. This is the same issue as with linq to sql and entity framework when they run in medium trust: they too compile expressions into delegates which they execute during projections as ddewinter showed. The problem is with the compilation of the expression, it seems this isn't allowed in medium trust. What's so weird is that the set of workarounds for linq to sql work which apparently aren't working for our code.
You can check for compilation if you simply create a query with a custom projection in linq and run that in medium trust:
var q = from c in metaData.Customer select new { c.CustomerId, c.Country};
// enumerate q
this will compile the lambda c=>new {c.CustomerId, c.Country} into a delegate which is executed at runtime when the data is fetched. This compilation step (by calling Expression.Compile) fails apparently under medium trust because the code (which is .NET library code) does some reflection under the hood and runs into a private method it needs access to it seems (see stacktrace: http://www.llblgen.com/TinyForum/GotoMessage.aspx?MessageID=89121&ThreadID=15694).
I really have no idea how to fix this, as there's no way to fix this from our part: the Expression.Compile() call is not something we can drop from the linq provider, it's essential as otherwise custom projections aren't possible.
The query you issued, results in a projection of this expression:
(values, indices) => Convert(Convert(ChangeType(values[indices[0]], System.Int32)))
The stacktrace shows the origin of the Compile call:
private ProjectionExecutionElements SetupProjectionElementsForExecution(QueryExpression toExecute)
{
ProjectionExecutionElements toReturn = new ProjectionExecutionElements();
ValueListProjectionDefinition projection = (ValueListProjectionDefinition)toExecute.Projection;
toReturn.Fields = GetElementCreator().CreateResultsetFields(projection.QueryElements.Count);
toReturn.ValueProjectors = new List<IDataValueProjector>();
int index = 0;
foreach(IEntityField field in projection.QueryElements)
{
toReturn.Fields.DefineField(field, index);
index++;
}
foreach(DataValueProjector valueProjector in projection.DataValueProjectors)
{
toReturn.ValueProjectors.Add(valueProjector);
}
if(projection.IsSingleValueList && (projection.ProjectionInstantiatorLambda == null))
{
Type listType = toExecute.Type;
if(toExecute.Type.IsGenericType)
{
listType = toExecute.Type.GetGenericArguments()[0];
}
toReturn.Results = LLBLGenProProviderBase.CreateResultsContainer(listType, null);
toReturn.Projector = (IGeneralDataProjector)
Activator.CreateInstance(typeof(DataProjectorToValueList<>).MakeGenericType(new Type[] { listType }), new object[] { toReturn.Results });
}
else
{
toReturn.Results = LLBLGenProProviderBase.CreateResultsContainer(projection.DestinationType, GetElementCreator());
LLBLGenProProviderBase.CheckProjection(projection);
toReturn.Projector = LLBLGenProProviderBase.CreateMultiValueProjector(projection.DestinationType, toReturn.Results,
(ProjectionValueProducerFunc)projection.ProjectionInstantiatorLambda.Compile(),
projection.ProjectionFuncIndices);
}
toReturn.Relations = CreateRelationCollectionToPass(toExecute);
toReturn.Filter = toExecute.FilterToUse;
LLBLGenProProviderBase.FixupFieldIfNoSourceFound(toReturn.Fields, toReturn.Relations, toExecute.LastMergedSourceWithProjection, GetElementCreator());
return toReturn;
}
it compiles the projection lambda so it can create instances of the objects to return. As the lambda is pretty simple, it's not doing that much work, but as it has to handle every possible projection (including in-memory property access, method calls etc. ) it uses a compiled version of it to create the values to return.
The SQL query itself contains the Count(*) and will return 1 value. It's the Compile() method which goes wrong here in medium trust, and as said, I can't remove that, it's otherwise impossible to do proper linq queries with custom projections, but also I can't change the way it works as it's a .NET method (Expression.Compile). So I'm afraid I can't help you. Medium trust is a problem for MS as well, and they've struggled with linq to sql with medium trust (and EF) as well, (if I'm not mistaken it's still not solved in all cases as well)
If nothing else helps, please use our own query api for this query to obtain the value, which has no trust issues.
I'd like to thank David DeWinter of the entity framework team for the time spend on this issue.
If you have more info for us to look into, please let us know.