views:

71

answers:

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?

+1  A: 

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
Where would I even run such code?
Stacey
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
Msg 25602, Level 17, State 21, Line 14The target, "CE79811F-1A80-40E1-8F5D-7445A3F375E7.package0.asynchronous_file_target", encountered a configuration error during initialization. Object cannot be added to the event session.
Stacey
@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
Well, if I run this code, then the database is open, so none of the queries can be run...
Stacey
@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
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.
Stacey
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.
Stacey
@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
How much is "A lot cheaper"? I have a budget of $20.
Stacey
Martin Smith
Is there a way to actually pipe the results in the second query into a file?
Stacey
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?
Stacey
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?
Stacey
Could you append it to show your original version that actually wrote the results to a file, also?
Stacey
@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
Any idea what the command is to make it log comments, as well?
Stacey
@Stacey - Sorry can you give me an example of what you mean?
Martin Smith
+2  A: 

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
I am sorry, I am unable to get this to work at all.
Stacey
I am not using an ObjectContext, though, i am using the CodeOnly DbContext object, and it does not seem to support any of this.
Stacey
In your Example, what is "NorthwindEntities" that you are inheriting from? What kind of class, etc?
Stacey
I also don't use any unit tests, I don't know what you're talking about in the first part of your post.
Stacey
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!!
Stacey
No... That isn't quite working. Can you tell me what "NorthwindEntities" is? I'm still lost on this.
Stacey
And I keep getting the error "Data Source" is not an accepted value, in the connection string.
Stacey
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
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?
Stacey