Following my post last year about modifying LINQ to SQL command text (evil, as it calls private methods through reflection) here’s an equally evil, but faster version that pre-compiles most of its work through an expression tree:
public delegate string ModifyCommandDelegate( string commandText, IDictionary<string, object> parameters );
public class DataContextInterceptor
{
private DataContext dc;
private object oldProvider;
private Type providerType;
private ModifyCommandDelegate modifyCommand;
private static Func<object, ModifyCommandDelegate, DataContext, Func<Expression, object>> CompileFactory = CreateCompileFactory().Compile();
private static Func<object, ModifyCommandDelegate, Func<Expression, IExecuteResult>> ExecuteFactory = CreateExecuteFactory().Compile();
public static TDataContext Intercept<TDataContext>( TDataContext dc, ModifyCommandDelegate modifyCommand )
where TDataContext : DataContext
{
new DataContextInterceptor( dc, modifyCommand );
// typeof( Expression ).GetProperty( "DebugView", BindingFlags.Instance | BindingFlags.NonPublic ).GetValue( executeFactoryExp, null ).Dump();
return dc;
}
public DataContextInterceptor( DataContext dc, ModifyCommandDelegate modifyCommand )
{
this.dc = dc;
this.modifyCommand = modifyCommand;
FieldInfo providerField = typeof( DataContext ).GetField( "provider", BindingFlags.Instance | BindingFlags.NonPublic );
var existingProvider = providerField.GetValue( dc );
if ( existingProvider is IProviderProxy )
{
// System.Diagnostics.Trace.WriteLine( string.Format( "DataContext {0} already intercepted", dc.GetHashCode() ) );
}
else
{
oldProvider = existingProvider;
var proxy = new ProviderProxy( this, oldProvider ).GetTransparentProxy();
providerField.SetValue( dc, proxy );
// System.Diagnostics.Trace.WriteLine( string.Format( "DataContext {0} intercepted", dc.GetHashCode() ) );
}
}
public static MethodCallExpression MakeMethodCall( Type type, string methodName, params Expression[] arguments )
{
// ( "Making MethodCallExpression for " + methodName ).Dump();
var methodInfo = type.GetMethod(
methodName,
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
null,
arguments.Select( a => a.Type ).ToArray(),
null );
if ( methodInfo == null ) throw new ArgumentException( string.Format( "Unable to find method {0}.{1}", type.Name, methodName ), "methodName" );
return Expression.Call( methodInfo, arguments );
}
public static MethodCallExpression MakeMethodCall( Expression instance, string methodName, params Expression[] arguments )
{
// ( "Making MethodCallExpression for " + instance.ToString() + "." + methodName ).Dump();
var methodInfo = instance.Type.GetMethod(
methodName,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
null,
arguments.Select( a => a.Type ).ToArray(),
null );
if ( methodInfo == null ) throw new ArgumentException( string.Format( "Unable to find method {0}.{1}", instance.Type.Name, methodName ), "methodName" );
return Expression.Call( instance, methodInfo, arguments );
}
protected internal object Compile( Expression query )
{
return DataContextInterceptor.CompileFactory( oldProvider, modifyCommand, dc )( query );
}
protected internal virtual IExecuteResult Execute( Expression query )
{
return DataContextInterceptor.ExecuteFactory( oldProvider, modifyCommand )( query );
}
public static Expression<Func<object, ModifyCommandDelegate, DataContext, Func<Expression, object>>> CreateCompileFactory()
{
var assembly = typeof( SqlProvider ).Assembly;
var providerType = assembly.GetType( "System.Data.Linq.SqlClient.SqlProvider" );
var providerParam = Expression.Parameter( typeof( object ), "provider" );
var modifyCommandParam = Expression.Parameter( typeof( ModifyCommandDelegate ), "modifyCommand" );
var dataContextParam = Expression.Parameter( typeof( DataContext ), "dataContext" );
return Expression.Lambda<Func<object, ModifyCommandDelegate, DataContext, Func<Expression, object>>>(
CreateCompileMethod( Expression.Convert( providerParam, providerType ), modifyCommandParam, dataContextParam ),
providerParam,
modifyCommandParam,
dataContextParam );
}
public static Expression<Func<Expression, object>> CreateCompileMethod( Expression oldProvider, Expression modifyCommand, Expression dataContextParam )
{
var assembly = typeof( SqlProvider ).Assembly;
var providerType = assembly.GetType( "System.Data.Linq.Provider.IProvider" );
var funcletizerType = assembly.GetType( "System.Data.Linq.SqlClient.Funcletizer" );
var annotationsType = assembly.GetType( "System.Data.Linq.SqlClient.SqlNodeAnnotations" );
var queryInfoType = assembly.GetType( "System.Data.Linq.SqlClient.SqlProvider" ).GetNestedType( "QueryInfo", BindingFlags.NonPublic );
var readerFactoryType = assembly.GetType( "System.Data.Linq.SqlClient.IObjectReaderFactory" );
var resultShapeType = assembly.GetType( "System.Data.Linq.SqlClient.SqlProvider" ).GetNestedType( "ResultShape", BindingFlags.NonPublic );
var compiledSubQueryType = assembly.GetType( "System.Data.Linq.SqlClient.ICompiledSubQuery" );
var typeSystemType = assembly.GetType( "System.Data.Linq.SqlClient.TypeSystem" );
var compiledQueryType = assembly.GetType( "System.Data.Linq.SqlClient.SqlProvider+CompiledQuery" );
var queryParam = Expression.Variable( typeof( Expression ), "query" );
var providerField = typeof( DataContext ).GetField( "provider", BindingFlags.Instance | BindingFlags.NonPublic );
var annotationsVar = Expression.Variable( annotationsType, "annotations" );
var queriesVar = Expression.Variable( queryInfoType.MakeArrayType(), "queries" );
var infoVar = Expression.Variable( queryInfoType, "info" );
var lambdaVar = Expression.Variable( typeof( LambdaExpression ), "lambda" );
var readerFactoryVar = Expression.Variable( readerFactoryType, "readerFactory" );
var subQueriesVar = Expression.Variable( compiledSubQueryType.MakeArrayType(), "subQueries" );
var resultShapeVar = Expression.Variable( resultShapeType, "resultShape" );
var returnTarget = Expression.Label( compiledQueryType );
var getQuery = MakeMethodCall( infoVar, "get_Query" );
var getResultType = MakeMethodCall( infoVar, "get_ResultType" );
var getResultElementType = MakeMethodCall( typeSystemType, "GetElementType", getResultType );
var modifySubQueries = CreateModifySubQueriesMethod();
var compiledQueryConstructor = compiledQueryType.GetConstructors( BindingFlags.NonPublic | BindingFlags.Instance ).First();
return Expression.Lambda<Func<Expression, object>>(
Expression.Block(
new[] { annotationsVar, queriesVar, infoVar, lambdaVar, readerFactoryVar, subQueriesVar, resultShapeVar },
// this.InitializeProviderMode();
MakeMethodCall( oldProvider, "InitializeProviderMode" ),
// SqlNodeAnnotations annotations = new SqlNodeAnnotations();
Expression.Assign( annotationsVar, Expression.New( annotationsType ) ),
// QueryInfo[] queries = this.BuildQuery( query, annotations );
Expression.Assign( queriesVar, MakeMethodCall( oldProvider, "BuildQuery", queryParam, annotationsVar ) ),
// var info = ModifyQueries( (IEnumerable)queries );
Expression.Assign( infoVar, Expression.Invoke( CreateModifyQueriesMethod(), queriesVar, modifyCommand ) ),
// this.CheckSqlCompatibility(queries, annotations);
MakeMethodCall( oldProvider, "CheckSqlCompatibility", queriesVar, annotationsVar ),
// var lambda = query as LambdaExpression;
Expression.Assign( lambdaVar, Expression.TypeAs( queryParam, lambdaVar.Type ) ),
// if ( lambda != null )
Expression.IfThen(
Expression.NotEqual( lambdaVar, Expression.Constant( null ) ),
// query = lambda.Body;
Expression.Assign( queryParam, Expression.Property( lambdaVar, "Body" ) ) ),
// IObjectReaderFactory readerFactory = null;
// ICompiledSubQuery[] subQueries = null;
// QueryInfo info = queries[queries.Length - 1];
// info defined above
// var resultShape = (int)Invoke( info, "get_ResultShape" );
Expression.Assign( resultShapeVar, MakeMethodCall( infoVar, "get_ResultShape" ) ),
Expression.Switch(
Expression.Convert( resultShapeVar, typeof( int ) ),
// if ( resultShape == 1 /* ResultShape.Singleton */ )
Expression.SwitchCase(
Expression.Block(
// subQueries = this.CompileSubQueries( info.Query );
Expression.Assign( subQueriesVar, MakeMethodCall( oldProvider, "CompileSubQueries", getQuery ) ),
// ModifySubQueries( (IEnumerable)subQueries );
Expression.Invoke( modifySubQueries, subQueriesVar, modifyCommand ),
// readerFactory = this.GetReaderFactory( info.Query, info.ResultType );
Expression.Assign( readerFactoryVar, MakeMethodCall( oldProvider, "GetReaderFactory", getQuery, getResultType ) ),
Expression.Empty() ),
Expression.Constant( 1 ) ),
// else if ( resultShape == 2 /* ResultShape.Sequence */ )
Expression.SwitchCase(
Expression.Block(
// subQueries = this.CompileSubQueries( info.Query );
Expression.Assign( subQueriesVar, MakeMethodCall( oldProvider, "CompileSubQueries", getQuery ) ),
// ModifySubQueries( (IEnumerable)subQueries );
Expression.Invoke( modifySubQueries, subQueriesVar, modifyCommand ),
// readerFactory = this.GetReaderFactory( info.Query, TypeSystem.GetElementType( info.ResultType ) );
Expression.Assign( readerFactoryVar, MakeMethodCall( oldProvider, "GetReaderFactory", getQuery, getResultElementType ) ),
Expression.Empty() ),
Expression.Constant( 2 ) ) ),
// dc.provider = oldProvider; // (Unfortunately needed to ensure compilation runs)
Expression.Assign( Expression.MakeMemberAccess( dataContextParam, providerField ), oldProvider ),
// return new CompiledQuery( oldProvider, query, queries, readerFactory, subQueries );
Expression.Return( returnTarget, Expression.New( compiledQueryConstructor, oldProvider, queryParam, queriesVar, readerFactoryVar, subQueriesVar ) ),
// --- Expression.Throw( Expression.New( typeof( Exception ).GetConstructor( new[] { typeof( string ) } ), Expression.Constant( "boo4" ) ) ),
Expression.Label( returnTarget, Expression.Constant( null, compiledQueryType ) ) ),
queryParam );
}
public static Expression<Func<object, ModifyCommandDelegate, Func<Expression, IExecuteResult>>> CreateExecuteFactory()
{
var assembly = typeof( SqlProvider ).Assembly;
var providerType = assembly.GetType( "System.Data.Linq.SqlClient.SqlProvider" );
var providerParam = Expression.Parameter( typeof( object ), "provider" );
var modifyCommandParam = Expression.Parameter( typeof( ModifyCommandDelegate ), "modifyCommand" );
return Expression.Lambda<Func<object, ModifyCommandDelegate, Func<Expression, IExecuteResult>>>(
CreateExecuteMethod( Expression.Convert( providerParam, providerType ), modifyCommandParam ),
providerParam,
modifyCommandParam );
}
public static Expression<Func<Expression, IExecuteResult>> CreateExecuteMethod( Expression oldProvider, Expression modifyCommand )
{
var assembly = typeof( SqlProvider ).Assembly;
var providerType = assembly.GetType( "System.Data.Linq.Provider.IProvider" );
var funcletizerType = assembly.GetType( "System.Data.Linq.SqlClient.Funcletizer" );
var annotationsType = assembly.GetType( "System.Data.Linq.SqlClient.SqlNodeAnnotations" );
var queryInfoType = assembly.GetType( "System.Data.Linq.SqlClient.SqlProvider" ).GetNestedType( "QueryInfo", BindingFlags.NonPublic );
var readerFactoryType = assembly.GetType( "System.Data.Linq.SqlClient.IObjectReaderFactory" );
var resultShapeType = assembly.GetType( "System.Data.Linq.SqlClient.SqlProvider" ).GetNestedType( "ResultShape", BindingFlags.NonPublic );
var compiledSubQueryType = assembly.GetType( "System.Data.Linq.SqlClient.ICompiledSubQuery" );
var typeSystemType = assembly.GetType( "System.Data.Linq.SqlClient.TypeSystem" );
var queryParam = Expression.Variable( typeof( Expression ), "query" );
var cachedResultVar = Expression.Variable( typeof( IExecuteResult ), "cachedResult" );
var annotationsVar = Expression.Variable( annotationsType, "annotations" );
var queriesVar = Expression.Variable( queryInfoType.MakeArrayType(), "queries" );
var infoVar = Expression.Variable( queryInfoType, "info" );
var lambdaVar = Expression.Variable( typeof( LambdaExpression ), "lambda" );
var readerFactoryVar = Expression.Variable( readerFactoryType, "readerFactory" );
var subQueriesVar = Expression.Variable( compiledSubQueryType.MakeArrayType(), "subQueries" );
var resultShapeVar = Expression.Variable( resultShapeType, "resultShape" );
var returnTarget = Expression.Label( typeof( IExecuteResult ) );
var getQuery = MakeMethodCall( infoVar, "get_Query" );
var getResultType = MakeMethodCall( infoVar, "get_ResultType" );
var getResultElementType = MakeMethodCall( typeSystemType, "GetElementType", getResultType );
var modifySubQueriesMethod = CreateModifySubQueriesMethod();
return Expression.Lambda<Func<Expression, IExecuteResult>>(
Expression.Block(
new[] { annotationsVar, queriesVar, infoVar, lambdaVar, readerFactoryVar, subQueriesVar, resultShapeVar },
// this.InitializeProviderMode();
MakeMethodCall( oldProvider, "InitializeProviderMode" ),
// query = Funcletizer.Funcletize(query);
Expression.Assign( queryParam, MakeMethodCall( funcletizerType, "Funcletize", queryParam ) ),
// if ( this.EnableCacheLookup )
Expression.IfThen(
MakeMethodCall( oldProvider, "get_EnableCacheLookup" ),
Expression.Block(
new[] { cachedResultVar },
// IExecuteResult cachedResult = this.GetCachedResult(query);
Expression.Assign( cachedResultVar, MakeMethodCall( oldProvider, "GetCachedResult", queryParam ) ),
// if ( cachedResult != null )
Expression.IfThen(
Expression.NotEqual( cachedResultVar, Expression.Constant( null ) ),
// return cachedResult;
Expression.Return( returnTarget, cachedResultVar ) ) ) ),
// SqlNodeAnnotations annotations = new SqlNodeAnnotations();
Expression.Assign( annotationsVar, Expression.New( annotationsType ) ),
// QueryInfo[] queries = this.BuildQuery(query, annotations);
Expression.Assign( queriesVar, MakeMethodCall( oldProvider, "BuildQuery", queryParam, annotationsVar ) ),
// var info = ModifyQueries( (IEnumerable)queries );
Expression.Assign( infoVar, Expression.Invoke( CreateModifyQueriesMethod(), queriesVar, modifyCommand ) ),
// this.CheckSqlCompatibility(queries, annotations);
MakeMethodCall( oldProvider, "CheckSqlCompatibility", queriesVar, annotationsVar ),
// var lambda = query as LambdaExpression;
Expression.Assign( lambdaVar, Expression.TypeAs( queryParam, lambdaVar.Type ) ),
// if ( lambda != null )
Expression.IfThen(
Expression.NotEqual( lambdaVar, Expression.Constant( null ) ),
// query = lambda.Body;
Expression.Assign( queryParam, Expression.Property( lambdaVar, "Body" ) ) ),
// IObjectReaderFactory readerFactory = null;
// ICompiledSubQuery[] subQueries = null;
// QueryInfo info = queries[queries.Length - 1];
// info defined above
// var resultShape = (int)Invoke( info, "get_ResultShape" );
Expression.Assign( resultShapeVar, MakeMethodCall( infoVar, "get_ResultShape" ) ),
Expression.Switch(
Expression.Convert( resultShapeVar, typeof( int ) ),
// if ( resultShape == 1 /* ResultShape.Singleton */ )
Expression.SwitchCase(
Expression.Block(
// subQueries = this.CompileSubQueries( info.Query );
Expression.Assign( subQueriesVar, MakeMethodCall( oldProvider, "CompileSubQueries", getQuery ) ),
// ModifySubQueries( (IEnumerable)subQueries );
Expression.Invoke( modifySubQueriesMethod, subQueriesVar, modifyCommand ),
// readerFactory = this.GetReaderFactory( info.Query, info.ResultType );
Expression.Assign( readerFactoryVar, MakeMethodCall( oldProvider, "GetReaderFactory", getQuery, getResultType ) ),
Expression.Empty() ),
Expression.Constant( 1 ) ),
// else if ( resultShape == 2 /* ResultShape.Sequence */ )
Expression.SwitchCase(
Expression.Block(
// subQueries = this.CompileSubQueries( info.Query );
Expression.Assign( subQueriesVar, MakeMethodCall( oldProvider, "CompileSubQueries", getQuery ) ),
// ModifySubQueries( (IEnumerable)subQueries );
Expression.Invoke( modifySubQueriesMethod, subQueriesVar, modifyCommand ),
// readerFactory = this.GetReaderFactory( info.Query, TypeSystem.GetElementType( info.ResultType ) );
Expression.Assign( readerFactoryVar, MakeMethodCall( oldProvider, "GetReaderFactory", getQuery, getResultElementType ) ),
Expression.Empty() ),
Expression.Constant( 2 ) ) ),
// return this.ExecuteAll(query, queries, readerFactory, null, subQueries);
Expression.Return( returnTarget, MakeMethodCall( oldProvider, "ExecuteAll", queryParam, queriesVar, readerFactoryVar, Expression.Constant( null, typeof( object[] ) ), subQueriesVar ) ),
// --- Expression.Throw( Expression.New( typeof( Exception ).GetConstructor( new[] { typeof( string ) } ), Expression.Constant( "boo4" ) ) ),
Expression.Label( returnTarget, Expression.Constant( null, typeof( IExecuteResult ) ) ) ),
queryParam );
}
private static LambdaExpression CreateModifyQueriesMethod()
{
var assembly = typeof( SqlProvider ).Assembly;
var queryInfoType = assembly.GetType( "System.Data.Linq.SqlClient.SqlProvider" ).GetNestedType( "QueryInfo", BindingFlags.NonPublic );
var queriesParam = Expression.Parameter( queryInfoType.MakeArrayType(), "queries" );
var modifyCommandParam = Expression.Parameter( typeof( ModifyCommandDelegate ), "modifyCommand" );
var returnLabel = Expression.Label();
var indexVar = Expression.Variable( typeof( int ), "index" );
var queryVar = Expression.Variable( queryInfoType, "query" );
return Expression.Lambda(
Expression.Block(
new[] { indexVar, queryVar },
// var index = 0;
Expression.Assign( indexVar, Expression.Constant( 0 ) ),
Expression.Loop(
Expression.Block(
// if ( index >= queries.Length ) break;
Expression.IfThen(
Expression.GreaterThanOrEqual( indexVar, Expression.ArrayLength( queriesParam ) ),
Expression.Break( returnLabel ) ),
// query = queries[ index ];
Expression.Assign( queryVar, Expression.ArrayIndex( queriesParam, indexVar ) ),
// ModifyQuery( query );
Expression.Invoke( CreateModifyQueryMethod(), queryVar, modifyCommandParam ),
// ++ index;
Expression.PreIncrementAssign( indexVar ) ),
returnLabel ),
queryVar ),
queriesParam,
modifyCommandParam );
}
private static LambdaExpression CreateModifySubQueriesMethod()
{
var assembly = typeof( SqlProvider ).Assembly;
var iCompiledSubQueryType = assembly.GetType( "System.Data.Linq.SqlClient.ICompiledSubQuery" );
var compiledSubQueryType = assembly.GetType( "System.Data.Linq.SqlClient.SqlProvider" ).GetNestedType( "CompiledSubQuery", BindingFlags.NonPublic );
var queryInfoType = assembly.GetType( "System.Data.Linq.SqlClient.SqlProvider" ).GetNestedType( "QueryInfo", BindingFlags.NonPublic );
var subQueriesType = iCompiledSubQueryType.MakeArrayType();
var queryInfoField = compiledSubQueryType.GetField( "queryInfo", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
var subQueriesField = compiledSubQueryType.GetField( "subQueries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
var subQueriesParam = Expression.Parameter( subQueriesType, "subQueries" );
var modifyCommandParam = Expression.Parameter( typeof( ModifyCommandDelegate ), "modifyCommand" );
var indexVar = Expression.Variable( typeof( int ), "index" );
var subQueryVar = Expression.Variable( compiledSubQueryType, "subQuery" );
var queryInfoVar = Expression.Variable( queryInfoType, "queryInfo" );
var nestedSubQueriesVar = Expression.Variable( iCompiledSubQueryType.MakeArrayType(), "nestedSubQueries" );
var modifySubQueriesDelegateType = typeof( Action<> ).MakeGenericType( iCompiledSubQueryType.MakeArrayType() );
var modifySubQueriesDelegateVar = Expression.Variable( modifySubQueriesDelegateType, "modifySubQueries" );
var loopTarget = Expression.Label();
var innerLambda = Expression.Lambda(
modifySubQueriesDelegateType,
Expression.Block(
new[] { indexVar },
// var index = 0;
Expression.Assign( indexVar, Expression.Constant( 0 ) ),
Expression.Loop(
Expression.Block(
new[] { subQueryVar },
// if ( index >= subQueries.Length ) break;
Expression.IfThen(
Expression.GreaterThanOrEqual( indexVar, Expression.ArrayLength( subQueriesParam ) ),
Expression.Break( loopTarget ) ),
// var subQuery = subQueries[ index ];
Expression.Assign( subQueryVar, Expression.TypeAs( Expression.ArrayIndex( subQueriesParam, indexVar ), compiledSubQueryType ) ),
// if ( subQuery != null )
Expression.IfThen(
Expression.NotEqual( subQueryVar, Expression.Constant( null ) ),
Expression.Block(
new[] { queryInfoVar, nestedSubQueriesVar },
// var queryInfo = subQuery.queryInfo;
Expression.Assign( queryInfoVar, Expression.MakeMemberAccess( subQueryVar, queryInfoField ) ),
// ModifyQuery( queryInfo, modifyCommand );
Expression.Invoke( CreateModifyQueryMethod(), queryInfoVar, modifyCommandParam ),
// var nestedSubQueries = subQuery.subQueries;
Expression.Assign( nestedSubQueriesVar, Expression.MakeMemberAccess( subQueryVar, subQueriesField ) ),
// if ( nestedSubQueries != null )
Expression.IfThen(
Expression.NotEqual( nestedSubQueriesVar, Expression.Constant( null ) ),
// ModifySubQueries( nestedSubQueries, modifyCommand );
Expression.Invoke( modifySubQueriesDelegateVar, nestedSubQueriesVar ) ) ) ),
// ++ index;
Expression.PreIncrementAssign( indexVar ) ),
loopTarget ) ),
subQueriesParam );
return Expression.Lambda(
Expression.Block(
new[] { modifySubQueriesDelegateVar },
Expression.Assign( modifySubQueriesDelegateVar, innerLambda ),
Expression.Invoke( modifySubQueriesDelegateVar, subQueriesParam ) ),
subQueriesParam,
modifyCommandParam );
}
private static LambdaExpression CreateModifyQueryMethod()
{
var assembly = typeof( SqlProvider ).Assembly;
var queryInfoType = assembly.GetType( "System.Data.Linq.SqlClient.SqlProvider" ).GetNestedType( "QueryInfo", BindingFlags.NonPublic );
var sqlParameterInfoType = assembly.GetType( "System.Data.Linq.SqlClient.SqlParameterInfo" );
var sqlParameterType = assembly.GetType( "System.Data.Linq.SqlClient.SqlParameter" );
var queryInfoParam = Expression.Parameter( queryInfoType, "query" );
var modifyCommandParam = Expression.Parameter( typeof( ModifyCommandDelegate ), "modifyCommand" );
var commandTextField = queryInfoType.GetField( "commandText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
var parametersField = queryInfoType.GetField( "parameters", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
var commandTextVar = Expression.Variable( typeof( string ), "commandText" );
var parameterInfosVar = Expression.Variable( typeof( ReadOnlyCollection<> ).MakeGenericType( sqlParameterInfoType ), "parameterInfos" );
var parametersVar = Expression.Variable( typeof( Dictionary<string, object> ), "parameters" );
var indexVar = Expression.Variable( typeof( int ), "index" );
var paramInfoVar = Expression.Variable( sqlParameterInfoType, "paramInfo" );
var paramVar = Expression.Variable( sqlParameterType, "param" );
var loopTarget = Expression.Label();
return Expression.Lambda(
Expression.IfThen(
Expression.NotEqual( modifyCommandParam, Expression.Constant( null ) ),
Expression.Block(
new[] { commandTextVar, parameterInfosVar, parametersVar, indexVar },
// var commandText = queryInfo.CommandText;
Expression.Assign( commandTextVar, Expression.MakeMemberAccess( queryInfoParam, commandTextField ) ),
// var parameterInfos = queryInfo.Parameters;
Expression.Assign( parameterInfosVar, Expression.MakeMemberAccess( queryInfoParam, parametersField ) ),
// var parameters = new Dictionary<string, object>();
Expression.Assign( parametersVar, Expression.New( typeof( Dictionary<string, object> ) ) ),
// var index = 0;
Expression.Assign( indexVar, Expression.Constant( 0 ) ),
Expression.Loop(
Expression.Block(
new[] { paramInfoVar, paramVar },
// if ( index >= parameterInfos.Count ) break;
Expression.IfThen(
Expression.GreaterThanOrEqual( indexVar, Expression.Property( parameterInfosVar, "Count" ) ),
Expression.Break( loopTarget ) ),
// var paramInfo = parameterInfos[ index ];
Expression.Assign( paramInfoVar, MakeMethodCall( parameterInfosVar, "get_Item", indexVar ) ),
// var param = paramInfo.Parameter;
Expression.Assign( paramVar, MakeMethodCall( paramInfoVar, "get_Parameter" ) ),
// parameters[ param.Name ] = paramInfo.Value;
MakeMethodCall( parametersVar, "set_Item", MakeMethodCall( paramVar, "get_Name" ), MakeMethodCall( paramInfoVar, "get_Value" ) ),
// ++ index;
Expression.PreIncrementAssign( indexVar ) ),
loopTarget ),
// queryInfo.CommandText = modifyCommand( commandText, parameters );
Expression.Assign( Expression.MakeMemberAccess( queryInfoParam, commandTextField ), Expression.Invoke( modifyCommandParam, commandTextVar, parametersVar ) ) ) ),
queryInfoParam,
modifyCommandParam );
}
internal interface IProviderProxy
{
DataContextInterceptor Interceptor { get; }
object OldProvider { get; }
}
public class ProviderProxy : RealProxy, IRemotingTypeInfo, IProviderProxy
{
public DataContextInterceptor Interceptor { get; private set; }
public object OldProvider { get; private set; }
internal ProviderProxy( DataContextInterceptor extender, object oldProvider )
: base( typeof( ContextBoundObject ) )
{
this.Interceptor = extender;
this.OldProvider = oldProvider;
}
public override IMessage Invoke( IMessage msg )
{
var call = msg as IMethodCallMessage;
if ( call != null && OldProvider != null )
{
try
{
if ( call.MethodBase.DeclaringType.Name == "IProvider" && call.MethodBase.DeclaringType.IsInterface )
{
Interceptor.providerType = call.MethodBase.DeclaringType;
switch ( call.MethodName )
{
case "Compile": return new ReturnMessage( Interceptor.Compile( call.Args.Cast<Expression>().First() ), null, 0, null, call );
case "Execute": return new ReturnMessage( Interceptor.Execute( call.Args.Cast<Expression>().First() ), null, 0, null, call );
}
}
return new ReturnMessage( call.MethodBase.Invoke( OldProvider, call.Args ), null, 0, null, call );
}
catch ( TargetInvocationException e )
{
return new ReturnMessage( e.InnerException, call );
}
}
throw new NotImplementedException();
}
public bool CanCastTo( Type fromType, object o )
{
return true;
}
public string TypeName
{
get { return this.GetType().Name; }
set { }
}
}
}
To use it, simply call DataContextInterceptor.Intercept on your DataContext before use (passing in a delegate to modify command text as needed):
DataContextInterceptor.Intercept( this, ( c, p ) => c.Replace( "[t0].[cName]", "NULL" ).Dump( "test") );
As it’s still calling private methods, you’ll need to be running in a full-trust environment.