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.


2 comments
Comments feed for this article
July 7, 2012 at 9:49 am
Interesting .NET Links – July 7, 2012 | TechBlog
[...] Modifying LINQ to SQL command text without Reflection – Chris Cavanagh Related Posts :Interesting .NET Links – July 5, 2012Interesting .NET Links – March 7 , 2012Interesting .NET Links – April 20 , 2012Interesting .NET Links – June 19 , 2012Interesting .NET Links – June 15 , 2012Interesting .NET Links – April 4 , 2012Interesting .NET Links – February 2 , 2012Interesting .NET Links – February 21 , 2012Interesting .NET Links – June 27, 2012Interesting .NET Links – January 24 , 2012By Blogsdna Previous postInteresting .NET Links – July 5, 2012 [...]
July 9, 2012 at 10:54 am
Dew Drop – July 9, 2012 (#1,359) | Alvin Ashcraft's Morning Dew
[...] LINQ to SQL command text again and Modifying LINQ to SQL command text without Reflection (Chris [...]