Issue with LLBLGen and ASP.Net running in Medium Trust

Posts   
1  /  2
 
    
hitchhiker
User
Posts: 48
Joined: 20-May-2009
# Posted on: 02-Jun-2009 13:31:09   

Nope, nothing is in the GAC, and those assembly restrictions don't seem to be present..

I downloaded LLBLGEN again, rebuilt the project from scratch, removed all my external dlls and limited the test to one statement.

It still fails.

Can I PM the project to somebody (including the now public config files)?

Thanks for all your time, Frank

ddewinter
User
Posts: 5
Joined: 28-May-2009
# Posted on: 02-Jun-2009 15:12:10   

Yes, feel free to send the project to me along with setup instructions (e.g. db script, web site config, etc.). Thanks.

(I'm new to these forums and don't see a PM option. You can email me at david DOT dewinter AT rev-net .com )

ddewinter
User
Posts: 5
Joined: 28-May-2009
# Posted on: 03-Jun-2009 04:49:17   

I investigated the issue with the sample project you sent. My reasoning tells me that there is something in LLBLGen Pro that causes Count() to fail in medium trust (which, from the reflection permission perspective, is the same as your custom configuration). Has anyone confirmed that Count does work in medium trust?

Here are the other things I tried: 1. I verified that your web site and your entity assembly have the same set of permissions. To do this I verified that I could create an instance of an internal class in your entity assembly using reflection from the web site. This would not work with RestrictedMemberAccess if your entities had higher permissions than your web site. 2. I used other query methods, like Min() and First(Expression<Func<XTasksLockEntity, bool>>), and they were compiled correctly.

So, I would assume something in the way Count is handled by LLBLGen Pro is using reflection to call an internal method in the .NET Framework in the final expression tree. I am not 100% sure on this, but it's the only explanation that makes sense.

hitchhiker
User
Posts: 48
Joined: 20-May-2009
# Posted on: 03-Jun-2009 09:30:53   

Thanks for the that!

So confirmed (99%) by two users now, if anybody on the LLBLGEN team has time, I can email them the project..

Thanks, Frank.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 03-Jun-2009 10:40:40   

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. simple_smile If you have more info for us to look into, please let us know.

Frans Bouma | Lead developer LLBLGen Pro
hitchhiker
User
Posts: 48
Joined: 20-May-2009
# Posted on: 05-Jun-2009 15:34:35   

Thanks for your time on this guys.. At least it's known, and the solution can be found by MS sometime in the future..

Back to 'new Predicate' for now then simple_smile

1  /  2