Expression ‹TDelegate›. Компиляция в среде среднего доверия

При попытке скомпилировать Expression в веб-приложении со средним уровнем доверия я получаю исключение MethodAccessException. Кто-нибудь знает другой способ скомпилировать выражение со средним доверием или обходной путь, чтобы избежать этого исключения?

Код, который вызывает исключение:

Expression<Func<object>> efn = 
  Expression.Lambda<Func<object>>(Expression.Convert((plan,typeof(object)));

Func<object> fn = efn.Compile(); // Exception thrown here

План переменных - это выражение, которое представляет следующий план выполнения:

{
  Convert(Query(MyProjectNamespace.MyDatabaseTableObject).Provider).Execute
  (
    new QueryCommand(
    "SELECT [t0].[LinkId], [t0].[Url] FROM [dbo].[MyDatabaseTable] AS t0",
    value(System.String[]), 
    r0 => new MyDatabaseTableObject() 
    {
      Id = IIF(r0.IsDBNull(0), 0, 
        Convert(ChangeType(r0.GetValue(0), System.Int32))), 
      Url = IIF(r0.IsDBNull(1), null, 
        Convert(ChangeType(r0.GetValue(1), System.String)))
    }, 
    value(System.Collections.Generic.List[System.String])), 
    new [] {}
  )
}

Полная трассировка стека:

at System.Reflection.MethodBase.PerformSecurityCheck(Object obj, RuntimeMethodHandle method, IntPtr parent, UInt32 invocationFlags)
at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
at System.Linq.Expressions.ExpressionCompiler.AddGlobal(Type type, Object value)
at System.Linq.Expressions.ExpressionCompiler.GenerateConstant(ILGenerator gen, Type type, Object value, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateConstant(ILGenerator gen, ConstantExpression c, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateArgs(ILGenerator gen, ParameterInfo[] pis, ReadOnlyCollection`1 args)
at System.Linq.Expressions.ExpressionCompiler.GenerateMethodCall(ILGenerator gen, MethodInfo mi, ReadOnlyCollection`1 args, Type objectType)
at System.Linq.Expressions.ExpressionCompiler.GenerateMethodCall(ILGenerator gen, MethodCallExpression mc, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateConvert(ILGenerator gen, UnaryExpression u)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateConditional(ILGenerator gen, ConditionalExpression b)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateMemberAssignment(ILGenerator gen, MemberAssignment binding, Type objectType)
at System.Linq.Expressions.ExpressionCompiler.GenerateBinding(ILGenerator gen, MemberBinding binding, Type objectType)
at System.Linq.Expressions.ExpressionCompiler.GenerateMemberInit(ILGenerator gen, ReadOnlyCollection`1 bindings, Boolean keepOnStack, Type objectType)
at System.Linq.Expressions.ExpressionCompiler.GenerateMemberInit(ILGenerator gen, MemberInitExpression init)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateLambda(LambdaExpression lambda)
at System.Linq.Expressions.ExpressionCompiler.GenerateCreateDelegate(ILGenerator gen, LambdaExpression lambda)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateArgs(ILGenerator gen, ParameterInfo[] pis, ReadOnlyCollection`1 args)
at System.Linq.Expressions.ExpressionCompiler.GenerateNew(ILGenerator gen, NewExpression nex, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateArgs(ILGenerator gen, ParameterInfo[] pis, ReadOnlyCollection`1 args)
at System.Linq.Expressions.ExpressionCompiler.GenerateMethodCall(ILGenerator gen, MethodInfo mi, ReadOnlyCollection`1 args, Type objectType)
at System.Linq.Expressions.ExpressionCompiler.GenerateMethodCall(ILGenerator gen, MethodCallExpression mc, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateConvert(ILGenerator gen, UnaryExpression u)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateLambda(LambdaExpression lambda)
at System.Linq.Expressions.ExpressionCompiler.CompileDynamicLambda(LambdaExpression lambda)
at System.Linq.Expressions.Expression`1.Compile()
at SubSonic.Linq.Structure.DbQueryProvider.Execute(Expression expression)
at SubSonic.Linq.Structure.QueryProvider.System.Linq.IQueryProvider.Execute(Expression expression)
at SubSonic.Linq.Structure.Query`1.GetEnumerator()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at WebApplication1._Default.Page_Load(Object sender, EventArgs e)
at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)
at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)
at System.Web.UI.Control.OnLoad(EventArgs e)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

person Adam Cooper    schedule 07.07.2010    source источник
comment
У вас нет ReflectionPermission(ReflectionPermissionFlag.MemberAccess) для доступа к непубличным участникам.   -  person Jaroslav Jandek    schedule 07.07.2010
comment
@ Ярослав - Я знаю, что это причина ошибки. Я пытаюсь понять, для чего требуется это разрешение, и как его обойти, если это возможно.   -  person Adam Cooper    schedule 08.07.2010
comment
@Adam: если вы используете какие-либо непубличные члены (свойства, поля, методы, конструкторы), попробуйте без них (если можете). Даже внешние параметры могут вызвать такое поведение. Попробуйте поразмышлять над используемыми классами ...   -  person Jaroslav Jandek    schedule 09.07.2010
comment
@Jaroslav - хотя технически правильно - на самом деле это не имеет ничего общего с прямым использованием каких-либо невидимых методов; это тонкая проблема, связанная с запеканием Type экземпляров в выражения - RuntimeType (используется для всех экземпляров отраженного типа во время выполнения) является внутренним и вызывает StrongBox<T> срыв.   -  person Andras Zoltan    schedule 09.07.2010
comment
@Andras: Кажется, вероятно. Беглый взгляд на трассировку стека позволяет предположить, что это код ChangeType(r0.GetValue(0), System.Int32). Возможно, использование альтернативного метода, такого как ChangeTypeTo<T>, могло бы решить проблему - хотя это зависит от реализации SubSonic.   -  person Jaroslav Jandek    schedule 09.07.2010
comment
@ Ярослав: Использование дженерика - хорошая идея. Я нашел другое решение (в своем ответе), где вместо вызова Expression.Constant([type instance]) вы можете вызвать Expression.Constant([type instance], typeof(Type)), чтобы функции генерации кода отражались над удобным для отражения Type вместо RuntimeType.   -  person Andras Zoltan    schedule 09.07.2010


Ответы (1)


Основная проблема здесь в том, что тип, передаваемый в System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes), либо не является общедоступным, либо имеет конструктор, который не является общедоступным.

Теперь - учитывая простоту вашего примера кода по сравнению с глубиной трассировки стека, я считаю, что проблема заключается не в plan, а в выражении внутри plan (поскольку вы говорите в своем комментарии к ответу Марка, что это также выражение), которое ссылается на тип, который затем ограничен.

Выражение, которое здесь является источником ошибки, - это ConstantExpression, которое должно быть ограниченного типа.

Однако единственное, что сбивает с толку, это то, что аргумент типа, который AddGlobal передает Activator.CreateInstance, - это StrongBox<T>, который является общедоступным и имеет открытый конструктор, что означало бы, что эта ошибка должна быть невозможной.

Возможно, однако, есть что-то скрытое, связанное с StrongBox<T>, чего мы не можем увидеть через Reflector.

Итак, я бы посмотрел на все дерево выражений, представленное plan, и изучил бы все типы, указанные в ConstantExpressions, чтобы убедиться, что все они доступны. Если после этого будет показано, что все типы доступны, эта ошибка все равно возникает, значит, это ошибка в структуре.

Однако - я бы подумал, что такую ​​ошибку уже нашли для чего-то столь же простого, как ConstantExpression!

ИЗМЕНИТЬ (заменить предыдущее изменение) ОТВЕТОМ

Я понял, и это очень тонкая проблема. Вы можете воспроизвести этот небольшой фрагмент кода на странице aspx, настроенной для работы со средним уровнем доверия:

Type t = typeof([any type you fancy]);
Expression expr = Expression.Constant(t);
var lambda = Expression.Lambda<Func<Type>>(expr);
var del = lambda.Compile();
Response.Write(del().ToString());

Итак, в предоставленном вами коде это выражение, представляющее второй аргумент ChangeType (мне потребовалось некоторое время, чтобы понять, что это метод Sub Sonic), который выглядит как Type (код не виден, но я думаю, это разумное предположение!).

Он запечен в выражении как ConstantExpression экземпляра Type. Не спрашивайте, как я сузил параметр - много ползания стека и работы рефлектора;)

Как упоминалось в первой половине моего ответа, трудно понять, как код, который использует компилятор Expression Tree, может когда-либо создавать исключение MethodAccessException, поскольку он всегда обращается к общедоступному ctor типа StrongBox<T>.

Однако он расстроится, если тип, переданный в качестве универсального, не является общедоступным. «Но подождите, - скажете вы, - Type публично!».

Может быть, но экземпляр Type, возвращенный во время выполнения из typeof() или GetType(), не является экземпляром RuntimeType, который является внутренним.

Вот почему приведенный выше фрагмент кода также вызовет ту же ошибку.

Исправление

Измените код, который создает аргумент Type для ChangeType(,) с

Expression.Constant([type])

(что я почти гарантирую, что это на данный момент), чтобы

Expression.Constant([type], typeof(Type))

Это работает, потому что вы явно указываете компилятору использовать общедоступный Type для константы вместо отраженного типа RuntimeType.

Вы можете протестировать это исправление, применив его к моему примеру кода в предыдущем блоке и повторно запустив.

person Andras Zoltan    schedule 07.07.2010
comment
Спасибо, это действительно полезно. Есть ли у вас какие-нибудь предложения по быстрому отслеживанию недоступных типов? Я почти уверен, что это не ошибка фреймворка, но я считаю, что определение точной причины является серьезной проблемой. - person Adam Cooper; 07.07.2010
comment
Хммм ... ну я подумал, как ты мог это сделать. Возможно, вы можете использовать выражение посетитель, которое ищет ConstantExpression, тип которого либо не является общедоступным, либо имеет хотя бы один непубличный конструктор, что должно сузить его. В MSDN есть ссылка на пример выражения посетителя. Я посмотрю и выложу ответ. - person Andras Zoltan; 08.07.2010
comment
@Adam - я обновил свой ответ некоторым кодом, который может помочь вам в диагностике, наряду с этой ссылкой из MSDN, которая вам понадобится для посетителя дерева выражений. Он не идеален, но, надеюсь, он должен вас наладить. :) - person Andras Zoltan; 08.07.2010
comment
@Andras - это круто, спасибо, я дам тебе знать, как у меня дела :) - person Adam Cooper; 08.07.2010
comment
Хммм ... Возвращаются только два dodgyConstants: System.Int32 и System.String. - person Adam Cooper; 08.07.2010
comment
@ Адам: о боже, это немного странно ... Трассировка стека не врет, поэтому, возможно, это что-то большее. Есть некоторые проблемы с генерацией кода со средним доверием (разрешение RestrictedMemberAccess, упомянутое первым комментатором по вашему вопросу), но в документации MSDN по деревьям выражений не упоминается, что для каких-либо действий требуется что-то особенное. Вы можете попробовать запросить разрешение RestrictedMemberAccess - я просто не вижу причин, по которым оно вам действительно нужно ... - person Andras Zoltan; 08.07.2010
comment
Ага, вот что я пытаюсь докопаться. Я знаю, что могу добавить это разрешение и пропустить проблему, но я пытаюсь исправить проблему в SubSonic (subsonicproject.com ), поэтому его можно использовать со средним доверием. Отладка проблемы оказывается, мягко говоря, проблематичной, хотя отсюда и вопрос. Спасибо за вашу помощь :) - person Adam Cooper; 08.07.2010
comment
Думаю, мы сможем воспроизвести эту общую проблему в тестовой среде ... Я пошагаю и посмотрю, что я найду. - person Andras Zoltan; 09.07.2010
comment
@ Адам - ​​я разобрался (надеюсь). Смотрите мой обновленный ответ - это настоящий хранитель проблемы! - person Andras Zoltan; 09.07.2010
comment
@Andras - Честно говоря, не могу отблагодарить вас за это. Потрясающая детективная работа, вы абсолютно правы. Еще раз спасибо :) - person Adam Cooper; 10.07.2010
comment
@Adam - не проблема - замечательный баг и рад помочь. Удачи вам в работе над Subsonic :) - person Andras Zoltan; 10.07.2010
comment
Очень жаль, что это неясная проблема, потому что ответ такого качества заслуживает большего количества голосов. - person Mark Rendle; 29.08.2012