2

I do not have 'full' the version of MS SQL (SQL Express 2008) so I do not have the profiler tool.

I want to see the SQL generated by my Entity Framework code, but all of the examples I find use the

var x = from u in table
        select u;

type of syntax; But most of my queries are more like ..

var x = context.Users.Single(n => n.Name == "Steven");

type of syntax. What can I do to see the SQL generated, from this manner of coding? Any ideas?

Ciel
  • 17,312
  • 21
  • 104
  • 199
  • By the way there might be an easier suggestion for you to use in this question http://stackoverflow.com/questions/3915124/how-do-i-find-out-the-actual-sql-that-this-statement-generates – Martin Smith Oct 12 '10 at 13:41

2 Answers2

3

check out this link... it has a tracing provider for EF4.

I'll try to give an example based on how I'm using this in a unit test.

Step 1: Register the provider

There are a couple ways to configure the provider. For my unit tests I configured the provider in code by calling the static method RegisterProvider();

[AssemblyInitialize()]
public static void AssemblyInit(TestContext context) {
    EFTracingProviderConfiguration.RegisterProvider();
}

Step 2: Create a sub-class of your entity model to provide the tracing extensions

public partial class ExtendedNorthwindEntities : NorthwindEntities {
    private TextWriter logOutput;

    public ExtendedNorthwindEntities(string connectionString)
        : base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
                connectionString,
                "EFTracingProvider")) {
    }

    #region Tracing Extensions

    private EFTracingConnection TracingConnection {
        get { return this.UnwrapConnection<EFTracingConnection>(); }
    }

    public event EventHandler<CommandExecutionEventArgs> CommandExecuting {
        add { this.TracingConnection.CommandExecuting += value; }
        remove { this.TracingConnection.CommandExecuting -= value; }
    }

    public event EventHandler<CommandExecutionEventArgs> CommandFinished {
        add { this.TracingConnection.CommandFinished += value; }
        remove { this.TracingConnection.CommandFinished -= value; }
    }

    public event EventHandler<CommandExecutionEventArgs> CommandFailed {
        add { this.TracingConnection.CommandFailed += value; }
        remove { this.TracingConnection.CommandFailed -= value; }
    }

    private void AppendToLog(object sender, CommandExecutionEventArgs e) {
        if (this.logOutput != null) {
            this.logOutput.WriteLine(e.ToTraceString().TrimEnd());
            this.logOutput.WriteLine();
        }
    }

    public TextWriter Log {
        get { return this.logOutput; }
        set {
            if ((this.logOutput != null) != (value != null)) {
                if (value == null) {
                    CommandExecuting -= AppendToLog;
                }
                else {
                    CommandExecuting += AppendToLog;
                }
            }

            this.logOutput = value;
        }
    }

    #endregion
}

Step 3: Attach to the Log property

var context = new ExtendedNorthwindEntities("name=\"NorthwindEntities\"");

context.Log = System.Console.Out;
Tom Brothers
  • 5,929
  • 1
  • 20
  • 17
  • I am not using an ObjectContext, though, i am using the CodeOnly DbContext object, and it does not seem to support any of this. – Ciel Aug 30 '10 at 18:29
  • In your Example, what is "NorthwindEntities" that you are inheriting from? What kind of class, etc? – Ciel Aug 30 '10 at 19:28
  • I also don't use any unit tests, I don't know what you're talking about in the first part of your post. – Ciel Aug 30 '10 at 19:39
  • Aha! I get it. The DbContext has an instance of the ObjectContext in it's Database property. I can just abstract this to get the Log property. Thanks!! – Ciel Aug 30 '10 at 19:41
  • No... That isn't quite working. Can you tell me what "NorthwindEntities" is? I'm still lost on this. – Ciel Aug 30 '10 at 19:52
  • And I keep getting the error "Data Source" is not an accepted value, in the connection string. – Ciel Aug 30 '10 at 20:06
  • In my example the NorthwindEntities was generated by the designer which was an ObjectContext sub-class. I haven't worked with the CodeOnly DbContext so I don't know if the trace provider would work with that... – Tom Brothers Aug 31 '10 at 01:17
  • Aaugh! All I keep getting is that it says "The Server" or "The Data Source=" is not supported. Can you show me an example of the connection string you're using? – Ciel Aug 31 '10 at 16:03
1

Does Express Edition support extended events? If so this will capture statement and sp completed events in a similar way to Profiler.

Edit: I have changed it to use a memory target rather than a file target. Ideally uncomment the WHERE sections and replace with an appropriate user name to capture only events of interest or you can filter by spid with WHERE (([sqlserver].[session_id]=(56))) for example.

IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name='test_trace')
    DROP EVENT SESSION [test_trace] ON SERVER;
CREATE EVENT SESSION [test_trace]
ON SERVER
ADD EVENT sqlserver.sp_statement_completed(
     ACTION (package0.callstack, sqlserver.session_id, sqlserver.sql_text)
    -- WHERE (([sqlserver].[username]='Domain\Username'))
    ),
ADD EVENT sqlserver.sql_statement_completed(
     ACTION (package0.callstack, sqlserver.session_id, sqlserver.sql_text)
     --WHERE (([sqlserver].[username]='Domain\Username'))
     )
ADD TARGET package0.ring_buffer
WITH (MAX_MEMORY = 4096KB, EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS, 
MAX_DISPATCH_LATENCY = 1 SECONDS, MAX_EVENT_SIZE = 0KB, 
MEMORY_PARTITION_MODE = NONE, TRACK_CAUSALITY = OFF, STARTUP_STATE = OFF)

ALTER EVENT SESSION [test_trace] ON SERVER STATE = START

And to review the results (Query generated using Adam Machanic's XE Code Generator)

DECLARE 
    @session_name VARCHAR(200) = 'test_trace'

SELECT 
    pivoted_data.* 
FROM 
( 
 SELECT MIN(event_name) AS event_name,
     MIN(event_timestamp) AS event_timestamp,
     unique_event_id,
     CONVERT ( BIGINT, MIN (
         CASE
             WHEN d_name = 'cpu'
             AND d_package IS NULL
             THEN d_value
         END ) ) AS [cpu],
     CONVERT ( BIGINT, MIN (
         CASE
             WHEN d_name = 'duration'
             AND d_package IS NULL
             THEN d_value
         END ) ) AS [duration],
     CONVERT ( BIGINT, MIN (
         CASE
             WHEN d_name = 'object_id'
             AND d_package IS NULL
             THEN d_value
         END ) ) AS [object_id],
     CONVERT ( INT, MIN (
         CASE
             WHEN d_name = 'object_type'
             AND d_package IS NULL
             THEN d_value
         END ) ) AS [object_type],
     CONVERT ( DECIMAL(28,0), MIN (
         CASE
             WHEN d_name = 'reads'
             AND d_package IS NULL
             THEN d_value
         END ) ) AS [reads],
     CONVERT ( VARCHAR(MAX), MIN (
         CASE
             WHEN d_name = 'session_id'
             AND d_package IS NOT NULL
             THEN d_value
         END ) ) AS [session_id],
     CONVERT ( INT, MIN (
         CASE
             WHEN d_name = 'source_database_id'
             AND d_package IS NULL
             THEN d_value
         END ) ) AS [source_database_id],
     CAST((SELECT CONVERT ( VARCHAR(MAX), MIN (
         CASE
             WHEN d_name = 'sql_text'
             AND d_package IS NOT NULL
             THEN d_value
         END ) )  AS [processing-instruction(x)] FOR XML PATH('') ) AS XML) AS [sql_text],
     CONVERT ( DECIMAL(28,0), MIN (
         CASE
             WHEN d_name = 'writes'
             AND d_package IS NULL
             THEN d_value
         END ) ) AS [writes]
 FROM
    ( 
        SELECT 
            *, 
            CONVERT(VARCHAR(400), NULL) AS attach_activity_id 
        FROM 
        ( 
            SELECT 
                event.value('(@name)[1]', 'VARCHAR(400)') as event_name, 
                event.value('(@timestamp)[1]', 'DATETIME') as event_timestamp, 
                DENSE_RANK() OVER (ORDER BY event) AS unique_event_id, 
                n.value('(@name)[1]', 'VARCHAR(400)') AS d_name, 
                n.value('(@package)[1]', 'VARCHAR(400)') AS d_package, 
                n.value('((value)[1]/text())[1]', 'VARCHAR(MAX)') AS d_value, 
                n.value('((text)[1]/text())[1]', 'VARCHAR(MAX)') AS d_text 
            FROM 
            ( 
                SELECT 
                    ( 
                        SELECT 
                            CONVERT(xml, target_data) 
                        FROM sys.dm_xe_session_targets st 
                        JOIN sys.dm_xe_sessions s ON 
                            s.address = st.event_session_address 
                        WHERE 
                            s.name = @session_name 
                            AND st.target_name = 'ring_buffer' 
                    ) AS [x] 
                FOR XML PATH(''), TYPE 
            ) AS the_xml(x) 
            CROSS APPLY x.nodes('//event') e (event) 
            CROSS APPLY event.nodes('*') AS q (n) 
        ) AS data_data 
    ) AS activity_data 
    GROUP BY 
        unique_event_id 
) AS pivoted_data; 
Martin Smith
  • 438,706
  • 87
  • 741
  • 845
  • Have you installed Management Studio? (Or the SQL Express equivalent) You should just be able to run it there. I couldn't find any information either way on the feature comparison matrix as to whether extended events were supported in Express edition so I think they may well be or they should have been listed. – Martin Smith Aug 28 '10 at 20:27
  • Msg 25602, Level 17, State 21, Line 14 The target, "CE79811F-1A80-40E1-8F5D-7445A3F375E7.package0.asynchronous_file_target", encountered a configuration error during initialization. Object cannot be added to the event session. – Ciel Aug 30 '10 at 19:25
  • @Stacey - That might be a file permissions thing possibly. I've changed the code in my post to not write to a file system target. – Martin Smith Aug 30 '10 at 23:34
  • Well, if I run this code, then the database is open, so none of the queries can be run... – Ciel Aug 31 '10 at 15:31
  • @Stacey - I see you got it solved anyway but just curious what do you mean? (Is this some limitation with Express editions that I was not aware of?) – Martin Smith Aug 31 '10 at 15:33
  • Well DbContext has to recreate the database when it is run. So I cannot have SQL code attached to it, because the instance has to be closed. At least that's all I can understand. if I look at the SQL database and try to run my code, it fails, If I right click and tell it to 'close connection', and run again, it works. – Ciel Aug 31 '10 at 15:35
  • I didn't really solve it. I just got the code to run. I don't think I can do what I want to do at all unless I shell out the thousands for full version of SQL. The above part only works if I use ObjectContext - which doesn't work with my existing code. So I'm pretty much just out of luck. The code you gave me, I run it in SQL express and the output doesn't really make any sense whatsoever, so I can't really use it for analyzing anything, unfortunately. And since I cannot afford the full version to get the analyzer, I have no choices. – Ciel Aug 31 '10 at 15:37
  • @Stacey - Ah right I see. Thanks for explaining it. By the way if this is a frequent need the developer edition comes with SQL Profiler and is obviously a lot cheaper! AFAIK you can use it to profile SQL Express instances. – Martin Smith Aug 31 '10 at 15:48
  • How much is "A lot cheaper"? I have a budget of $20. – Ciel Aug 31 '10 at 15:50
  • @Stacey - Looks like you'd be [looking at about $50](http://www.google.com/products/catalog?hl=en&q=sql+server+2008+developer+edition&um=1&ie=UTF-8&cid=9230582366035071055#ps-sellers) so over budget then. – Martin Smith Aug 31 '10 at 16:14
  • Is there a way to actually pipe the results in the second query into a file? – Ciel Aug 31 '10 at 16:17
  • I think I can make this work if I can get the results back as a string. I'm going to use ExecuteReader() to run the queries inline with the database, instead of SQL server. Any ideas on this? – Ciel Aug 31 '10 at 17:32
  • Okay... I'm a bit lost, but I think I can get this to work. If I want to run this in my actual C# code, where would I 'run' the queries? After the database construction? Where would I run the second query? – Ciel Aug 31 '10 at 18:21
  • Could you append it to show your original version that actually wrote the results to a file, also? – Ciel Aug 31 '10 at 18:26
  • @Stacey - I'll come back to this tomorrow. In the meantime the version that wrote to a file is still there in the answer revisions http://stackoverflow.com/posts/3586817/revisions – Martin Smith Aug 31 '10 at 23:17
  • Any idea what the command is to make it log comments, as well? – Ciel Sep 13 '10 at 17:54
  • @Stacey - Sorry can you give me an example of what you mean? – Martin Smith Sep 13 '10 at 20:02