c# - .NET: acceso a miembros no públicos desde un ensamblaje dinámico
performance expression-trees (3)
El problema no son los permisos porque no hay ningún permiso que le permita acceder a un campo no público o miembro de otra clase sin reflexión. Esto es análogo a la situación en la que compiló dos conjuntos no dinámicos y un conjunto llama a un método público en el segundo conjunto. Luego, si cambia el método a privado sin volver a compilar el primer ensamblaje, la primera llamada de ensamblados fallará en el tiempo de ejecución . En otras palabras, la expresión en su ensamblaje dinámico se está compilando en una llamada de método común que no tiene permiso para llamar más de lo que lo hace desde otra clase, incluso en el mismo ensamblaje.
Dado que ningún permiso puede resolver su problema, es posible que pueda transformar las referencias de campos y métodos no públicos en subexpresiones que utilicen la reflexión.
Aquí hay un ejemplo tomado de su caso de prueba. Esto falla:
Expression<Func<int>> expression = () => 10 + GetValue();
pero esto tendrá éxito:
Expression<Func<int>> expression = () => 10 + (int)typeof(Program).GetMethod("GetValue", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null);
Como esto no falla con una excepción, puede ver que su ensamblaje dinámico tiene permiso de reflexión y puede acceder al método privado, pero no puede hacerlo usando una llamada de método común en la que CompileToMethod
resulta.
Estoy trabajando en una biblioteca que permite a los usuarios ingresar expresiones arbitrarias. Mi biblioteca luego compila esas expresiones como parte de una expresión más grande en un delegado. Ahora, por razones aún desconocidas, compilar la expresión con Compile
veces / a menudo resulta en un código que es mucho más lento de lo que sería si no fuera una expresión compilada. Hice una pregunta sobre esto antes y una solución alternativa era no utilizar Compile
, sino CompileToMethod
y crear un método static
en un nuevo tipo en un nuevo ensamblaje dinámico. Eso funciona y el código es rápido.
Pero los usuarios pueden ingresar expresiones arbitrarias y resulta que si el usuario llama a una función no pública o accede a un campo no público en la expresión, lanza una System.MethodAccessException
(en el caso de un método no público) cuando el Se invoca delegado.
Lo que probablemente podría hacer aquí es crear un nuevo ExpressionVisitor
que verifique si la expresión accede a algo que no sea público y use la Compile
más lenta en esos casos, pero preferiría que el conjunto dinámico de alguna manera obtenga los derechos para acceder a lo no público miembros. O averigüe si hay algo que pueda hacer para que Compile
sea más lento (a veces).
El código completo para reproducir este problema:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
namespace DynamicAssembly
{
public class Program
{
private static int GetValue()
{
return 1;
}
public static int GetValuePublic()
{
return 1;
}
public static int Foo;
static void Main(string[] args)
{
Expression<Func<int>> expression = () => 10 + GetValue();
Foo = expression.Compile()();
Console.WriteLine("This works, value: " + Foo);
Expression<Func<int>> expressionPublic = () => 10 + GetValuePublic();
var compiledDynamicAssemblyPublic = (Func<int>)CompileExpression(expressionPublic);
Foo = compiledDynamicAssemblyPublic();
Console.WriteLine("This works too, value: " + Foo);
var compiledDynamicAssemblyNonPublic = (Func<int>)CompileExpression(expression);
Console.WriteLine("This crashes");
Foo = compiledDynamicAssemblyNonPublic();
}
static Delegate CompileExpression(LambdaExpression expression)
{
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName("MyAssembly"+ Guid.NewGuid().ToString("N")),
AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("Module");
var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public);
var methodBuilder = typeBuilder.DefineMethod("MyMethod",
MethodAttributes.Public | MethodAttributes.Static);
expression.CompileToMethod(methodBuilder);
var resultingType = typeBuilder.CreateType();
var function = Delegate.CreateDelegate(expression.Type,
resultingType.GetMethod("MyMethod"));
return function;
}
}
}
Si usted construye el ensamblaje no dinámico, puede incluir un InternalsVisibleTo
para el ensamblaje dinámico (incluso funciona con un nombre seguro). Eso permitiría usar miembros internos, ¿cuál podría ser suficiente en su caso?
Para tener una idea, aquí hay un ejemplo que se muestra en caliente para permitir que el ensamblaje dinámico de Moq use material interno de otro ensamblaje: http://blog.ashmind.com/2008/05/09/mocking-internal-interfaces-with-moq/
Si este enfoque no es suficiente, iría con una combinación de las sugerencias de Rick y Miguel: crear DynamicMethods "proxy" para cada invocación a un miembro no público y cambiar el árbol de expresiones para que se usen en lugar de las invocaciones originales.
Una vez tuve un problema para acceder a los elementos privados de una clase, desde el código IL generado usando DynamicMethod.
Resultó que había una sobrecarga del constructor de la clase DynamicMethod
que recibe el tipo de clase en el que se permitiría el acceso privado:
http://msdn.microsoft.com/en-us/library/exczf7b9.aspx
Este enlace contiene ejemplos de cómo acceder a datos privados ... Sé que esto no tiene nada que ver con los árboles de expresiones, pero podría darle algunas pistas sobre cómo hacerlo.
Es posible que haya algún tipo de cosa similar al compilar árboles de expresión ... o que pueda crear ese árbol de expresión como un Método Dinámico.