Welcome to MSDN Blogs Sign in | Join | Help

Query performance

Understanding the reason for slowness of a SQL query and then tuning to boost it is a slightly complicated process, but it can be extremely rewarding in some cases. In this post, I am going to list down some ways in which you can tune the query performance for SQL Server Compact, along with pointers to relevant online resources. If you are a seasoned database developer, you probably know most of the tricks already.

Before I jump into it, an overview of the basics first.

Basics:

  •  Displaying actual and estimated Execution plans for SQL Server Compact: You should be familiar with Execution Plans (also called as query plans and showplans).
    • For non-parameterized queries: There are several easy ways to do this
      • When database is on a docked device or desktop: Generate a query plan using SQL Server Management Studio (SSMS) that gives a nice graphical view of the query plans. (You can connect to the database on device as long as it is docked. No need to copy the file to desktop.)
      • When database is on a device: Generate a query plan using Query Analyzer. This generates a .sqlplan file that can be viewed graphically in SQL Server Management Studio as before (preferable) or opened as a plain XML file.
    • For Parameterized (as well as non-parameterized) queries:
      • Use the TSQL Statements SET SHOWPLAN_XML  (for estimated execution plan) and  SET STATISTICS_XML (for actual execution plan), followed by SELECT @@SHOWPLAN to generate the query plans.  Store the results as an XML file. (Tip: To view the query plans graphically, just rename the file with a .sqlplan extension and it is ready to be opened in SSMS like an ordinary non-parameterized query plan.)
  • Indexes and statistics on indexes:
    • One of the important tasks of the query optimizer is to select the right index to execute the query. An optimizer can make better decisions if it has histogram data (i.e. statistics) about the distribution of values for an index.
      • CREATING/UPDATING/DELETING statistics on an index: SQL Server Compact 3.5 creates statistics on all indexes by default and updates them as required. (You can surely tweak these settings, but it is advisable not to. See CREATE STATISTICS /UPDATE STATISTICS/DROP STATISTICS).
      • To view the statistics distribution, the built in stored procedure sp_show_statistics is pretty handy. 
      • Statistics were not created by default on primary key indexes till version 3.1, so you might want to check this and add them yourself :- ).
  • ... A lot of other things too J, but this will do for the time being.
The Meat:

If you are trying to improve the performance of your application that uses SQL Server Compact, make sure you have checked the following resources on MSDN already (In this post I will not go into any of them):

  • Database Design and Performance: This page talks in some detail about the following:
    • Database denormalization
    • Variable and fixed sized columns
    • Effect of row and key lengths
  • Query Performance Tuning: This page explains some of the following:
    • Improving indexes by creating selective indexes, choosing the columns in a multi-column index, issues in indexing a small table etc.
    • When to create indexes
    • Using parameterized queries where possible.
    • Rewrite queries where possible
    • Using base table cursor (CommandType.TableDirect) mode for simple queries.

(The article on Query Process Overview and Performance Tuning approaches for SQL Server CE 2.0 covers the same points as above, but I found it better than the MSDN links J. It is relevant for SQL Server Compact 3.5 too.)

Now here are some additional ways you can improve performance:

1.       Recompile Stale query plans

<StoryTime>Sometime back I got a complaint about slow performance of our database even though the user was using parameterized queries and had created the correct indexes everywhere. The sequence in which he was performing operations was something like this:

-          Create the full database schema (tables, indexes etc).

-          Prepare SqlCeCommand objects for *all* queries that will ever be used by the application.

-          Populate the database with actual data. Until now all tables were empty.

-          Execute the queries using previously prepared command objects 

What's wrong with this?

 A prepared command object (SqlCeCommand) holds a query plan. The query optimizer generated this query plan and had optimized it for the state of database when the plan was generated. In this user's case, the database had just empty tables at that point in time. The optimizer therefore inferred that using a table scan is the best way of executing the query (as opposed to using any index). As a result, all his query plans used just table scans instead of the right indexes even when the table size increased! 

</StoryTime>

Moral of the story: Caching query plans is good, but only as long as the state of database when they are compiled is representative of the average state of database during execution. If your data is changing rapidly, it is better to recompile the queries every once in a while.

So, beware of stale query plans!

Note that query plans can be cached for both parameterized and non-parameterized queries and you can run into this problem in either case. (Another insider hack: SqlCeCommand.Prepare doesn't really prepare the query plan. It just marks the command object for plan (re)compilation and the plan is compiled when SqlCeCommand.ExecuteXXX is called next time.)

2.       Impression of using parameterized queries without really using them

Question: What's wrong (rather sub-optimal) with the following code:

SqlCeCommand cmd = con.CreateCommand();

cmd.CommandText = "SELECT * FROM table1 WHERE C1_DECIMAL = @p1";

cmd.Parameters.Add("@p1", SqlDbType.Decimal);

cmd.Parameters["@p1"].Value = 3.5;

SqlCeDataReader rdr = cmd.ExecuteReader();

while (rdr.Read()) { /* read the data */}

rdr.Close();


cmd.Parameters["@p1"].Value = 335.01;

rdr = cmd.ExecuteReader();

while (rdr.Read()) {  /* read the data */   }

rdr.Close();


Answer: The query plan gets compiled twice, once for every ExecuteReader call!

Reason: First time the plan is compiled, the parameter's precision is 2 and scale is 1 (as inferred from its value ‘3.5'). The query plan therefore uses this precision and scale values. The only other values it can accept are those that fit within this range.

When the query is executed again, the parameter's precision is 5 and scale is 2 (as inferred from its value ‘335.01'). Since it doesn't fit in the parameter as inferred during first plan compilation, the plan is recompiled silently!

Solution: If you can anticipate the range (size, precision or scale) of the parameter values, then specify it explicitly. The plan will be generated based on the specified range then. This holds true for all character, binary and numeric types. (The flip side is that for any parameter value that does not fit into the specified range, an error will be thrown.) So the above code can be modified as follows:

SqlCeCommand cmd = con.CreateCommand();

cmd.CommandText = "SELECT * FROM [TABLE1] WHERE [C1_DECIMAL] = @p1";

cmd.Parameters.Add("@p1", SqlDbType.Decimal);

cmd.Parameters["@p1"].Precision = 10;  // Playing safe by taking a larger range!

cmd.Parameters["@p1"].Scale = 5;

cmd.Parameters["@p1"].Value = 3.5;

SqlCeDataReader rdr = cmd.ExecuteReader();

while (rdr.Read()) { /* read the data */}

rdr.Close();

cmd.Parameters["@p1"].Value = 335.00;

rdr = cmd.ExecuteReader();

while (rdr.Read()){ /* read the data */ }

rdr.Close();


3.       Query optimizer did not choose the best index

The query optimizer usually does a good job of choosing the index. However, it does it on a best effort basis and there can be cases where it doesn't pick the right index. In such cases, it makes sense to use index hints and outsmart the optimizer:

E.g.  ...FROM [TABLE1] AS T1 JOIN [TABLE2] AS T2...  can be rewritten with index hints as

...FROM [TABLE1] AS T1 WITH (INDEX(IX_On_Table1))

 JOIN [TABLE2] AS T2 WITH (INDEX(IX_On_Table2))...

How do you know if your index is indeed better than the index chosen by optimizer?

Well, the easiest and brute force method is to run the query with and without an index hint and see which is better.  I often use the Query Analyzer on device and SSMS on desktop to get the execution times. Another way is to generate query plans for both queries and study them for better predicate matches, index ordering etc.

4.       Query optimizer did not choose the best join order

As in the case of choosing a wrong index, an optimizer can sometimes choose a wrong join order too. Again a corner case, but not impossible to run into. In such cases, you can specify the join order explicitly:

E.g. ...FROM [TABLE1] AS T1 INNER JOIN [TABLE2] AS T2 ON ...  can be rewritten with a forced join order as 

...FROM [TABLE1] AS T1

 INNER JOIN [TABLE2] AS T2 ON ...

 OPTION (FORCE ORDER)

 As with any sort of query hint, you should be extra careful that you are doing the right thing. For instance, the join order chosen for a query can change between multiple runs due to several reasons. The cardinality of the join tables could have changed significantly, or the data distribution could have changed. So even though the join order that you selected at the time of deployment was optimal, it may not remain so all the time. Ordinarily the optimizer would decide the join order on fly and therefore can adjust to such changes.

Not so when the join order has been forced.

That's all for the time being.

-Pragya Agarwal 

 

Dropping Defaults (Column Constraint)

There were some recent changes that were done in regards to column defaults. In this blog entry I want to bring out, what used to happen and what happens now.

I have discussed the case of dropping a default on a column in this post, however, in a case where you are facing problems dealing with column specific constraints(especially dropping), the appraoch I suggest below should help you with that.

What used to happen in version 3.1 and earlier?


Consider the following query:

CREATE TABLE foo (col INT DEFAULT 1, col2 INT CONSTRAINT cons DEFAULT 2);

Usually when defining defaults one would choose the way it has been done for first column “col”, but even the other options as shown for second column “col2” also works.
In version 3.1 and earlier, if at all you ever decide that you don’t want the default on “col2”, you could issue a statement like this:

ALTER TABLE foo DROP CONSTRAINT cons;

Here, the default for “col2” as declared above will be stored as a constraint with name “cons”, so you could issue an ALTER statement on the table and drop it. It worked.

CREATE TABLE foo (col INT DEFAULT 1, col2 INT CONSTRAINT cons DEFAULT 2)
INSERT INTO foo (col) VALUES (0)
INSERT INTO foo (col2) VALUES (0)
INSERT INTO foo VALUES (0, 0)
INSERT INTO foo VALUES (default, default)
ALTER TABLE foo DROP CONSTRAINT cons
INSERT INTO foo (col) VALUES (0)
 SELECT * FROM foo
                 col                col2
----------------------------------------
                   0                   2
                   1                   0
                   0                   0
                   1                   2
                   0              (NULL)


What changed in version 3.5?


In version 3.5 and onwards the constraint as declared above will not be table constraint anymore, rather the default constraint on a column will be maintained as column property at column level.

So, the statement,

 ALTER TABLE foo DROP CONSTRAINT cons

would NOT work anymore. The “CONSTRAINT cons” from the original statement will be ignored. On issuing above mentioned ALTER statement, you will get an error saying its a bad constraint ID and it cannot be found.

To drop such constraint you will have to issue the following statement (this works on all versions and is the right way to drop the defaults than going  for constraint names):

ALTER TABLE foo ALTER COLUMN col2 DROP DEFAULT

To see the column properties, you can query information_schema.columns. Look for the row with your column name, there will be two columns (COLUMN_HASDEFAULT and  COLUMN_DEFAULT), they indicate whether you have the default on that column or not and the default value for that column, if any.

If you are feeling lazy to type in this, you can also do this through GUI, by editing the table schema in Visual Studio or SQL Server. In the table schema, go to the particular column and for the default property delete the value which you had previously entered. This is equivalent to issuing the above ALTER TABLE statement, in fact, behind the scenes they both use the same statement.


Ravi Tandon
SDET, SQL Server Compact

Posted by SQLCEBLOG | 0 Comments

My First Entity Application Against SQLCE

Now that SQL Server Compact 3.5 SP1 Beta is released it's time to take the first step, making your first entity application using SQLCE as a backend.

In his previous post, Ambrish gave steps on how to install SQL Server Compact SP1 Beta, along with Ado.Net Entity Framework Beta 3 and writing a small program to query the Northwind Database via LINQ.

In this post, I will briefly walk you through what Entity Framework Beta 3 has offer to, and how can we leverage it to write different types of entity queries against SQL Server Compact.

Going by EF Definition at: http://msdn2.microsoft.com/en-us/library/bb399572.aspx

The most unique selling point in my opinion becomes the part where you can run the same program/application against any backend (assuming the database schema is same), you have to just modify connection strings. Let's say even if the schema is little different, you can get that easily fixed in your metadata files, and boom, your app works.

I am including few screenshots which will guide you on how to go by making your own application and the complete program.

Please do read the previous post (by Ambrish) carefully as I will assume that you are done with the setup and we are ready to go. For those who could get things set up I am posting some screen shots to help you with that.

First step is to include an Ado.Net Entity Data Model into your project, give it a name that makes sense, I gave it SSCNorthwind.

Add Ado.NET Entity Data Model

 

Generate Model From DB

 

The screenshot below, shows the database file that we selected, its connection string that will go into your App.config file and also the name you want to give it. For simplicity I chose NorthwindEntities.

Choosing the Connection and Saving App.Config

 

The screenshot below shows you how to include the database file that you placed in your project root for generating data model.

Select Northwind DB from your samples directory

 

The wizard below allows you to choose the tables that you want to be part of entity set, as SQL CE does not support stored procedures and views as of now, we will ignore those two options.

Choose the Tables you want. SP and Views are not supported for SQLCE

 

The red box in below shows the change that we have to make in App.Config to make this working J i.e. renaming the provider:

provider=Microsoft.SqlServerCe.Client.3.5 to provider=System.Data.SqlServerCe.3.5

This is being fixed, so in RTM version you won't have to do this manual step.

You can see that the name of Entity set is NortwindEntities, so whenever we make connection to this set, the connection string provided in App.Config file will be used.

Before:

Before

After:

After

This is how the solution explorer should look now:

Solution

If you face any error while adding Northwind.sdf file into your project, just ignore it for now.

I have tried to include some comments in my program below to help you understand what's going on.

Points to notice:

To connect to a Database using an Entity Connection, you can do it in two ways.

  1. First way is to specify your own connection string, like I have done at the start of program. Using that you can directly create an Entity Connection and work in the same way you used to work with SqlCeConnection and SqlCeCommand. That way can be seen in ESQL Query region.
  2. The second approach is using Contexts or what we call here NorthwindEntities, (remember at top we renamed Entities to this for better understanding). For using context, you can do that in multiple ways
    • First, you can give a connection string and it will create the set.
    • Second, you can also first create an Entity Connection like above and then pass it on the NorthwindEntities, which is what I have done in Object Query section.
    • And the last approach for which we created App.Config in first place. You don't specify anything at all, the entity set (NorthwindEntities) picks up your connection string from App.Config and creates a connection. LINQ to Entites Query section is done using this approach.

Program (Contents of Program.cs):

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Data.EntityClient;

using NorthwindModel;

using System.Data;

 

namespace MyFirstEntityApplicationForSSC

{

    class Program

    {

       

        #region Static Declarations

 

        //SQLCE Connection string for the sample database Northwind.sdf

        static string _sqlCeConnnectionString = @"Data Source = Northwind.sdf";

       

        //entityConnectionString: If your metadata(CSDL, SSDL and MSL, or in this case EDMX) files lie in the same place as application, you can use "."

        static string _entityConnectionString =

            String.Format(@"metadata=.;Provider=System.Data.SqlServerCe.3.5;provider connection string=""{0}""", _sqlCeConnnectionString);

 

        //New Values that you might want to use for changing the data

        static string _newFirstName = "AGENT";

        static string _newLastName = "SMITH";

        static string _newCountry = "MATRIX";

 

        #endregion Static Declarations

 

        #region Main

        /// <summary>

        /// MAIN: It calls the three different functions for following type of queries

        /// Description in layman language

        ///

        /// ESQL - This takes you back to old .Net 2.0 days where you used to set T-SQL statements and execute them, just that it here works on entities now instead of tables

        /// Example Demonstrates: Simple Select Query using Entity SQL.

        ///

        /// Object Query - Working on Data Objects was never so easy. This type of queries give you the power to work on data objects and most of all intellisense helps with things you can do

        /// Example Demonstrates: Query and Updation of data and then Re-Querying

        ///

        /// LINQ to Enity - LINQ means Language Integrated Query. This is a new and powerful way of writing queries that are checked for syntax at compile time itself. So, chances of failing in query logic at runtime become minimal

        /// Example Demostrates: Query the data using default settings for connection in App.Config that was configured via EF Designer.

        /// </summary>

        /// <param name="args"></param>

        static void Main(string[] args)

        {

            Console.WriteLine("SQL CE Northwind Entity App:");

            try

            {

                EntityConnection entityConnection = new EntityConnection(_entityConnectionString); //Creating a new entity connection using the entity connection string

                entityConnection.Open();

 

                Console.WriteLine("\nESQL Query:");

                ESQL_Query(entityConnection); //This does not use context, it's a simple esql command execution

 

                Console.WriteLine("\nLINQ To Entity Query:");

                LINQ_To_Entity_Query(); //This will make a new connection using the connection string in the App.Config file

 

                Console.WriteLine("\nObject Query:");

                ObjectQuery(entityConnection); //This will create the context with existing connection

 

                entityConnection.Close();

            }

            catch (Exception ex)

            {

                Console.WriteLine("\nFAIL! Oops, it was not expected, an exception has been thrown, details below:\n");

                Console.WriteLine(ex.Message);

            }

            Console.WriteLine("\nPress a Key to exit...");

            Console.ReadKey();

        }

        #endregion Main

 

 

        #region ESQL_Query

        /// <summary>

        /// ESQL_Query

        /// (Executing a simple ESQL query against Northwind Entity Set)

        /// Using the entity connection which is passed on as parameter

        /// it executes a simple entity command on the Employees entity set in the Northwind Context.

        /// The result of that query is later on printed on the screen using a data reader.

        /// </summary>

        /// <param name="entityConnection"></param>

        private static void ESQL_Query(EntityConnection entityConnection)

        {

            EntityCommand entityCommand = entityConnection.CreateCommand();

            entityCommand.CommandText = @"Select Emp.Employee_Id as Id, Emp.First_Name as Name from NorthwindEntities.Employees as Emp order by Emp.Employee_Id";

 

            EntityDataReader entityDataReader = entityCommand.ExecuteReader(CommandBehavior.SequentialAccess);

            //Note: You have to use CommandBehavior as SequentialAccess, otherwise an exception will be thrown

 

            while (entityDataReader.Read())

            {

                for (int i = 0; i < entityDataReader.FieldCount; i++)

                    Console.Write(entityDataReader[i].ToString() + "\t");

                Console.WriteLine();

            }

        }

        #endregion ESQL_Query

 

 

        #region LINQ_To_Entity_Query

        /// <summary>

        /// LINQ_To_Entity_Query

        /// (Executing a simple LINQ query against Northwind Entity Set)

        /// This function creates a new Northwind Context based on the setting provided for it in the App.Config File

        /// It executes a simple LINQ query on the Employees entity set in the Northwind Context.

        /// The result of that query is later on printed on the screen using the IQueryable structure.

        /// </summary>

        private static void LINQ_To_Entity_Query()

        {

            NorthwindEntities nwind = new NorthwindEntities(); //Uses the settings for connection string in App.Config File

 

            IQueryable<string> emps = from e in nwind.Employees where (e.Employee_ID%2)==0 select e.First_Name; //Even Number Employee Ids

 

            foreach (string e in emps)

            {

                Console.WriteLine(e);

            }

 

        }

        #endregion LINQ_To_Entity_Query

 

 

        #region ObjectQuery

        /// <summary>

        /// ObjectQuery

        /// (Executing a simple Object query against Northwind Entity Set and Updating the data)

        /// This function creates a new Northwind Context based on the entity connection thas has been passed to it as parameter

        /// It executes a simple Object query on the Employees entity set in the Northwind Context.

        /// The result of that query is later on printed on the screen and some changes are made to it.

        /// The changes are then saved using SaveChanges on Context and then re-querying them to see if things went fine.

        /// </summary>

        /// <param name="entityConnection"></param>

        private static void ObjectQuery(EntityConnection entityConnection)

        {

            NorthwindEntities nwind = new NorthwindEntities(entityConnection);

           

            var emps = nwind.Employees.Where((delegate(Employees e)

                                                {

                                                    return e.Employee_ID > 5;

                                                }

                                            ));

           

            //var emps = nwind.Employees.Where("it.Employee_ID > 5"); //This query is also equivalent to above one and will give same results

            //var emps = nwind.Employees.Where("it.Employee_ID > @empid",new ObjectParameter("@empid",5)); //This query is also equivalent to above one, but it uses parameters for the values

 

            foreach(Employees e in emps)

            {

                Console.WriteLine(e.Employee_ID + "\t" + e.First_Name + "\t" + e.Last_Name + "\t" + e.Country);

                if (e.Employee_ID == 10) //As soon as we encounter employee id as 10, we want to change some data

                {

                    Console.WriteLine("Changing Data");

                    e.First_Name = _newFirstName;

                    e.Last_Name = _newLastName;

                    e.Country = _newCountry;

                }

            }

 

            Console.WriteLine("Saving Data Changes");

            nwind.SaveChanges(); //Any changes made to the context are saved using this command

 

            emps = nwind.Employees; //This is simplest way to select all employees. If you happen to choose particular data you may modify the query accordingly, like nwind.Employees.Where("it.Employee_ID = 10")

 

            Console.WriteLine("Display Data Again:");

            foreach (Employees e in emps)

            {

                Console.WriteLine(e.Employee_ID + "\t" + e.First_Name + "\t" + e.Last_Name + "\t" + e.Country);

            }

 

        }

        #endregion ObjectQuery

 

        }

}

The output of the program would look like this:

SQL CE Northwind Entity App:

 

ESQL Query:

1       Nancy

2       Andrew

3       Janet

4       Margaret

5       Steven

6       Michael

7       Robert

8       Laura

9       Anne

10      Albert

11      Tim

12      Caroline

13      Justin

14      Xavier

15      Laurent

 

LINQ To Entity Query:

Andrew

Margaret

Michael

Laura

Albert

Caroline

Xavier

 

Object Query:

6       Michael Suyama  UK

7       Robert  King    UK

8       Laura   Callahan        USA

9       Anne    Dodsworth       UK

10      Albert  Hellstern       USA

Changing Data

11      Tim     Smith   USA

12      Caroline        Patterson       USA

13      Justin  Brid    France

14      Xavier  Martin  France

15      Laurent Pereira France

Saving Data Changes

Display Data Again:

1       Nancy   Davolio USA

2       Andrew  Fuller  USA

3       Janet   Leverling       USA

4       Margaret        Peacock USA

5       Steven  Buchanan        UK

6       Michael Suyama  UK

7       Robert  King    UK

8       Laura   Callahan        USA

9       Anne    Dodsworth       UK

10      AGENT   SMITH   MATRIX

11      Tim     Smith   USA

12      Caroline        Patterson       USA

13      Justin  Brid    France

14      Xavier  Martin  France

15      Laurent Pereira France

 

Press a Key to exit...

 

If you want your changes to persist or not to persist, modify the properties for the Northwind.sdf file in your solution properties as per your desire. By default it is "copy if newer" which means the database file in your execution folder will retain the changes. By changing it to "copy always" you can refresh your database each time.

I hope now you can now go back and start writing advanced programs using various techniques available.

Ado.Net Entity Framework is a new weapon to the list of a current developer's arsenal, opening multiple ways in which he can write his programs without worrying about the under lying intricacies.

The shift to entity plane seems very lucrative, easy and promising. And what more, all of this comes with handy tools which make life easy. Writing your code from the word Go!

The above topic was in relation to LINQ to Entities, if you are looking for LINQ to SQL refer to this blog post: http://blogs.msdn.com/sqlservercompact/archive/2007/08/21/linq-with-sql-server-compact-a-ka-dlinq-over-sql-ce.aspx

Happy Programming!

 

Ravi Tandon

SDET, SQL Server Compact

Posted by SQLCEBLOG | 6 Comments
Filed under:

Download available for SQL Server Compact 3.5 SP1 Beta for ADO.Net Entity Framework Beta 3

The SQL Server Compact 3.5 SP1 Beta for ADO.Net Entity Framework Beta 3 is available for download at Microsoft Download Center.

To install SQL Server Compact 3.5 SP1 Beta Release for ADO.NET Entity Framework Beta 3

  1. Install ADO.NET Entity Framework Beta 3 from Microsoft Download Center. You must ensure that the pre-requisites specified on the download page are installed before installing ADO.NET Entity Framework Beta 3.
  2. In Control Panel, double-click Add or Remove Programs. Uninstall SQL Server Compact 3.5 or SQL Server Compact 3.5 SP1 Beta.
  3. Install SQL Server Compact 3.5 SP1 Beta Release for the ADO.NET Entity Framework Beta 3 (SSCERuntime-ENU.msi) from the Microsoft Download Center. Note that administrator rights are required on a computer to install SQL Server Compact 3.5 SP1 Beta Release for ADO.NET Entity Framework Beta 3.

To install ADO.NET Entity Framework Tools December 07 Community Technology Preview (CTP)

  1. Install the ADO.NET Entity Framework Beta 3 and SQL Server Compact 3.5 SP1 Beta Release for the ADO.Net Entity Framework Beta 3 as specified above. Install ADO.NET Entity Framework Tools December 2007 CTP from the Microsoft Download Center. You must ensure that the pre-requisites specified on the download page are installed before installing ADO.NET Entity Framework Tools December 2007 CTP. The CTP installs on a Visual Studio 2008 installation.
  2. Copy the SQL Server Compact 3.5 SP1 Design Tools Beta (SSCEVSTools-ENU.exe) to your computer by clicking on Save or Save this program to disk and after the download is complete install SSCEVSTools-ENU.exe by double-clicking it. Do not install the SSCEVSTools-ENU.exe by clicking on the Run. While running the SSCEVSTools-ENU.exe file, Microsoft.SqlServerCe.Client.dll is extracted to %ProgramFiles%\Microsoft Visual Studio 9.0\Common7\IDE folder. The SSCEVSTools-ENU.exe will create a backup of the existing DLL at the location %ProgramFiles%\Microsoft Visual Studio 9.0\Common7\IDE.

For uninstalling SQL Server Compact 3.5 SP1 Beta refer to the read me.

The steps to create a sample application are as given below:

1.    Start Visual Studio 2008 and create a console project by clicking on the menu item File à New à Project. In the New Project window expand Visual Basic or Visual C#, select Windows and then select Console Application. For the project the .Net Framework version should be .Net Framework 3.5. Name the project as SQLCompactEDMProject. On clicking OK the project is created

2.    To generate the Entity Data Model for the Northwind.sdf, copy the Northwind.sdf from the folder %Program Files%\Microsoft SQL Server Compact Edition\v3.5\Samples to the users folder like C:\Users\<login name>\Documents\Visual Studio 2008\Projects\SQLCompactEDMProject\SQLCompactEDMProject. Select the SQLCompactEDMProject in the Solution Explorer and right-click, point to Add, and then click New Item. Select ADO.NET Entity Data Model in the Templates pane. Enter Northwind.edmx for the model name and click Add. The opening page of the Entity Data Model Wizard is displayed.

3.    Select Generate from database in the Choose Model Contents dialog box and click Next. Click the New Connection button. The Connection Properties dialog box is displayed.

4.    In the Connection Properties dialog click the button Change in the Data Source. In the Change Data Source dialog box select Microsoft SQL Server Compact 3.5 and click OK. Click the radio button My Computer in the Data Source. In the Connection Properties browse to the Northwind.sdf and click OK in the Connection Properties dialog. The Choose Your Data Connection screen in the Entity Data Model Wizard comes up.  

5.    The checkbox in front of Save entity connection settings in App.Config as: will be clicked by default. Change the name in the dialog from Entities to NorthwindEntities. Click Next to continue.

6.    The Choose Your Database Objects dialog box is displayed. By default all the tables in the database are selected and will be included in the EDM. Uncheck the box next to Tables. This deselects all the tables in the database. Expand the Tables node and select the Customers, Orders, and Products tables. Change the  Model Namespace: from Model to NorthwindModel

7.    Click Finish to complete the wizard.

a.    The wizard does the following:

b.    Adds references to the System.Data, System.Data.Entity, System.Core, System.Security, and System.Runtime.Serialization assemblies.

c.    Generates an .edmx file which encapsulates the information from the EDM mapping files.