6
6
using System . Linq ;
7
7
using System . Text ;
8
8
using System . Text . Encodings . Web ;
9
+ using System . Threading . Tasks ;
10
+ using System . Net ;
11
+ using Microsoft . EntityFrameworkCore . Storage ;
9
12
10
13
namespace IQueryableObjectSource
11
14
{
@@ -22,55 +25,138 @@ public override void TransferData(object target, Stream incomingData, Stream out
22
25
23
26
try
24
27
{
25
- using var command = queryable . CreateDbCommand ( ) ;
26
- var provider = GetDatabaseProvider ( command ) ;
27
-
28
- if ( provider == null )
28
+ var dbOperation = ConvertStreamToString ( incomingData ) ;
29
+ switch ( dbOperation )
29
30
{
30
- return ;
31
+ case "GetQuery" :
32
+ HandleGetQuery ( queryable , outgoingData ) ;
33
+ break ;
34
+
35
+ default :
36
+ HandleGetQueryPlan ( queryable , incomingData , outgoingData ) ;
37
+ break ;
31
38
}
39
+ }
40
+ catch ( Exception ex )
41
+ {
42
+ WriteError ( outgoingData , ex . Message ) ;
43
+ }
44
+ }
45
+ private void HandleGetQuery ( IQueryable queryable , Stream outgoingData )
46
+ {
47
+ using var queryWriter = new BinaryWriter ( outgoingData , Encoding . Default , true ) ;
48
+ queryWriter . Write ( false ) ; // Indicates no error
49
+ queryWriter . Write ( GenerateHtml ( queryable . ToQueryString ( ) ) ) ;
50
+ }
51
+ private void HandleGetQueryPlan ( IQueryable queryable , Stream incomingData , Stream outgoingData )
52
+ {
53
+ using var command = queryable . CreateDbCommand ( ) ;
54
+ var provider = GetDatabaseProvider ( command ) ;
32
55
33
- var query = queryable . ToQueryString ( ) ;
34
- var rawPlan = provider . ExtractPlan ( ) ;
56
+ if ( provider == null )
57
+ {
58
+ return ;
59
+ }
35
60
36
- var buffer = new byte [ 3 ] ;
37
- var isBackgroundDarkColor = false ;
61
+ var query = queryable . ToQueryString ( ) ;
62
+ var rawPlan = provider . ExtractPlan ( ) ;
38
63
39
- var r = 255 ;
40
- var g = 255 ;
41
- var b = 255 ;
64
+ var ( r , g , b ) = ReadBackgroundColor ( incomingData ) ;
65
+ var isBackgroundDarkColor = r * 0.2126 + g * 0.7152 + b * 0.0722 < 255 / 2.0 ;
42
66
43
- if ( incomingData . Read ( buffer , 0 , buffer . Length ) == buffer . Length )
44
- {
45
- r = buffer [ 0 ] ;
46
- g = buffer [ 1 ] ;
47
- b = buffer [ 2 ] ;
48
- }
67
+ var planFile = GeneratePlanFile ( provider , query , rawPlan , r , g , b , isBackgroundDarkColor ) ;
68
+
69
+ using var writer = new BinaryWriter ( outgoingData , Encoding . Default , true ) ;
70
+ writer . Write ( false ) ; // Indicates no error
71
+ writer . Write ( planFile ) ;
72
+ }
73
+ private ( int r , int g , int b ) ReadBackgroundColor ( Stream incomingData )
74
+ {
75
+ var buffer = new byte [ 3 ] ;
76
+ var r = 255 ;
77
+ var g = 255 ;
78
+ var b = 255 ;
79
+
80
+ if ( incomingData . Read ( buffer , 0 , buffer . Length ) == buffer . Length )
81
+ {
82
+ r = buffer [ 0 ] ;
83
+ g = buffer [ 1 ] ;
84
+ b = buffer [ 2 ] ;
85
+ }
49
86
50
- isBackgroundDarkColor = r * 0.2126 + g * 0.7152 + b * 0.0722 < 255 / 2.0 ;
87
+ return ( r , g , b ) ;
88
+ }
51
89
52
- var planFile = Path . Combine ( provider . GetPlanDirectory ( ResourcesLocation ) , Path . ChangeExtension ( Path . GetRandomFileName ( ) , "html" ) ) ;
90
+ private string GeneratePlanFile ( DatabaseProvider provider , string query , string rawPlan , int r , int g , int b , bool isBackgroundDarkColor )
91
+ {
92
+ var planDirectory = provider . GetPlanDirectory ( ResourcesLocation ) ;
93
+ var planFile = Path . Combine ( planDirectory , Path . ChangeExtension ( Path . GetRandomFileName ( ) , "html" ) ) ;
53
94
54
- var planPageHtml = File . ReadAllText ( Path . Combine ( provider . GetPlanDirectory ( ResourcesLocation ) , "template.html" ) )
55
- . Replace ( "{backColor}" , $ "rgb({ r } { g } { b } )")
56
- . Replace ( "{textColor}" , isBackgroundDarkColor ? "white" : "black" )
57
- . Replace ( "{plan}" , JavaScriptEncoder . UnsafeRelaxedJsonEscaping . Encode ( rawPlan ) . Replace ( "'" , "\\ '" ) )
58
- . Replace ( "{query}" , JavaScriptEncoder . UnsafeRelaxedJsonEscaping . Encode ( query ) . Replace ( "'" , "\\ '" ) ) ;
95
+ var planPageHtml = File . ReadAllText ( Path . Combine ( planDirectory , "template.html" ) )
96
+ . Replace ( "{backColor}" , $ "rgb({ r } { g } { b } )")
97
+ . Replace ( "{textColor}" , isBackgroundDarkColor ? "white" : "black" )
98
+ . Replace ( "{plan}" , JavaScriptEncoder . UnsafeRelaxedJsonEscaping . Encode ( rawPlan ) . Replace ( "'" , "\\ '" ) )
99
+ . Replace ( "{query}" , JavaScriptEncoder . UnsafeRelaxedJsonEscaping . Encode ( query ) . Replace ( "'" , "\\ '" ) ) ;
59
100
60
- File . WriteAllText ( planFile , planPageHtml ) ;
101
+ File . WriteAllText ( planFile , planPageHtml ) ;
61
102
62
- using var writer = new BinaryWriter ( outgoingData , Encoding . Default , true ) ;
63
- writer . Write ( false ) ;
64
- writer . Write ( planFile ) ;
65
- }
66
- catch ( Exception ex )
103
+ return planFile ;
104
+ }
105
+
106
+ private void WriteError ( Stream outgoingData , string errorMessage )
107
+ {
108
+ using var writer = new BinaryWriter ( outgoingData , Encoding . Default , true ) ;
109
+ writer . Write ( true ) ; // Indicates an error occurred
110
+ writer . Write ( errorMessage ) ;
111
+ }
112
+
113
+ public static string ConvertStreamToString ( Stream stream )
114
+ {
115
+ using ( MemoryStream memoryStream = new MemoryStream ( ) )
67
116
{
68
- using var writer = new BinaryWriter ( outgoingData , Encoding . Default , true ) ;
69
- writer . Write ( true ) ;
70
- writer . Write ( ex . Message ) ;
117
+ stream . CopyTo ( memoryStream ) ;
118
+ byte [ ] byteArray = memoryStream . ToArray ( ) ;
119
+
120
+ //Try to convert byte array to a string using UTF-8 encoding
121
+ try
122
+ {
123
+ string result = Encoding . UTF8 . GetString ( byteArray ) ;
124
+ return result ;
125
+ }
126
+ catch ( Exception )
127
+ {
128
+ return "GetQueryPlan" ;
129
+ }
71
130
}
72
131
}
73
132
133
+ private static string GenerateHtml ( string query )
134
+ {
135
+ string escapedQuery = WebUtility . HtmlEncode ( query ) ;
136
+
137
+ // Simple HTML structure to display the query
138
+ StringBuilder htmlBuilder = new StringBuilder ( ) ;
139
+ htmlBuilder . AppendLine ( "<html>" ) ;
140
+ htmlBuilder . AppendLine ( "<head><title>Query Plan Visualizer</title>" ) ;
141
+ htmlBuilder . AppendLine ( "<style>" ) ;
142
+ htmlBuilder . AppendLine ( "body { font-family: Arial, sans-serif; margin: 20px; }" ) ;
143
+ htmlBuilder . AppendLine ( "h2 { color: #333; }" ) ;
144
+ htmlBuilder . AppendLine ( ".query-box { background-color: #f4f4f4; padding: 10px; border-radius: 5px; border: 1px solid #ccc; overflow-x: auto; max-width: 100%; }" ) ;
145
+ htmlBuilder . AppendLine ( "pre { white-space: pre-wrap; word-wrap: break-word; }" ) ;
146
+ htmlBuilder . AppendLine ( "</style>" ) ;
147
+ htmlBuilder . AppendLine ( "</head>" ) ;
148
+ htmlBuilder . AppendLine ( "<body>" ) ;
149
+ htmlBuilder . AppendLine ( "<h2>SQL Query</h2>" ) ;
150
+ htmlBuilder . AppendLine ( "<div class='query-box'>" ) ;
151
+ htmlBuilder . AppendLine ( "<pre>" + escapedQuery + "</pre>" ) ;
152
+ htmlBuilder . AppendLine ( "</div>" ) ;
153
+ htmlBuilder . AppendLine ( "</body>" ) ;
154
+ htmlBuilder . AppendLine ( "</html>" ) ;
155
+
156
+ return htmlBuilder . ToString ( ) ;
157
+ }
158
+
159
+
74
160
private static DatabaseProvider GetDatabaseProvider ( DbCommand command )
75
161
{
76
162
return command . GetType ( ) . FullName switch
0 commit comments