Expression .Compile dans un environnement moyen confiance
-
02-10-2019 - |
Question
Lorsque vous essayez de compiler une expression dans une fiducie moyenne application web Je reçois MethodAccessException. Quelqu'un sait-il d'une autre façon de compiler une expression en confiance moyenne ou une solution de contournement pour éviter cette exception?
Le code qui lève l'exception:
Expression<Func<object>> efn =
Expression.Lambda<Func<object>>(Expression.Convert((plan,typeof(object)));
Func<object> fn = efn.Compile(); // Exception thrown here
Le plan variable est une expression qui représente le plan d'exécution suivant:
{
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 [] {}
)
}
Le plein trace de la pile:
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)
La solution
Le problème sous-jacent est que le type étant passé à System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
est soit pas publique, ou a un constructeur qui ne sont pas publiques.
- étant donné la simplicité de votre exemple de code par rapport à la profondeur de la stacktrace Je crois que le problème ne réside pas dans plan
, mais dans une expression au sein plan
(puisque vous dites dans votre commentaire sur la réponse de Marc qu'il est aussi une expression) que les références du type qui est alors limité.
L'expression qui est la source de l'erreur ici est un ConstantExpression
qui doit être du type restreint.
Toutefois, la confusion seule chose à ce sujet, est que l'argument de type qui passe à AddGlobal
Activator.CreateInstance
est StrongBox<T>
, qui est public et a un constructeur public -. Qui impliquerait que cette erreur devrait être impossible
Peut-être, cependant, il y a quelque chose de caché associé à StrongBox<T>
que nous ne pouvons voir à travers réflecteur.
Alors, je regarderais tout l'arbre d'expression représenté par plan
et en examinant tous les types mentionnés dans ConstantExpression
s pour vous assurer qu'ils sont tous accessibles. Si après avoir fait que tous les types sont présentés pour être accessibles, cette erreur se produit encore, alors il pourrait être un bug dans le cadre.
Cependant - j'aurais pensé qu'un tel bug aurait été trouvé déjà quelque chose d'aussi simple que ConstantExpression
EDIT (Remplacement de montage précédent) AVEC LA RÉPONSE
Je l'ai, et il est un problème très subtil. Vous pouvez reproduire avec ce petit bout de code dans une page ASPX qui est configuré pour fonctionner en fiducie moyen:
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());
Ainsi, dans le code que vous avez fourni, il est l'expression qui représente le second argument de ChangeType
(m'a pris un certain temps pour se rendre compte que c'est un sous méthode Sonic), qui semble être un Type
(ne peut pas voir la code, mais je pense que c'est une estimation raisonnable!).
Il est cuit dans l'expression comme ConstantExpression
d'une instance de Type
. Ne demandez pas comment je PRECISEE le paramètre - beaucoup de pile rampants et le travail de réflecteur;)
Comme mentionné dans la première moitié de ma réponse, il est difficile de voir comment le code que le compilateur arbre expression utilise peut jamais créer un MethodAccessException, car il est l'accès toujours cteur publique du type StrongBox<T>
.
Cependant, il se fâcher si le type adopté en tant que générique n'est pas publique. "Mais attendez," vous dites: "Type
est public!".
Ce pourrait être, mais l'instance de Type
retourné lors de l'exécution de typeof()
ou GetType()
est pas - c'est une instance de RuntimeType
- qui est interne
Quelle est la raison pour laquelle l'extrait de code ci-dessus déclenchera également la même erreur.
Le correctif
Modifier le code qui produit l'argument Type
pour ChangeType(,)
de
Expression.Constant([type])
(que je presque garantir qu'il est à l'heure actuelle) à
Expression.Constant([type], typeof(Type))
Cela fonctionne, parce que vous dites explicitement le compilateur d'utiliser le Type
public pour la constante, au lieu du type de RuntimeType
réfléchi.
Vous pouvez tester ce correctif en l'appliquant à mon exemple de code dans le bloc précédent et de refaire.