Welcome to MSDN Blogs Sign in | Join | Help

Released - SQL Server Compact v3.5 SP1, SQL Server 2008 RTM, Visual Studio 2008 SP1, .NET Framework v3.5 SP1

Yes, all of them are released on August 11, 2008. 

SQL Server Compact v3.5 SP1:

What is new in this Service Pack? - Click here and here

Where to download?

SQL Server Compact v3.5 SP1 for Desktops (32-bit and 64-bit)

SQL Server Compact v3.5 SP1 for Devices (all platforms & processors)

Server Tools (32-bit and 64-bit) (See Also: this)

Documentation, Books Online

Visual Studio 2008 SP1

What is new in this Service Pack? - Click here

Where to download? - Click here

 

.NET Framework v3.5 SP1 - Download it here

SQL Server 2008 RTM - Click here

The good thing is with VS 2008 SP1, you will get "SQL Server Compact SP1 for Desktop (32-bit)", and of course updated "SQL Server Compact SP1 Design Tools".

We received a huge feedback from you on VS 2005 SP1 that we should limit the Service Pack Size.  With that feedback, we felt it is a reasonable to ask customers to download "SQL Server Compact v3. SP1 for Devices" explicitly than including it with Visual Studio 2008 SP1 there by bloating VS 2008 SP1 size.  We also made a decision to not include "SQL Server Compact v3.5 SP1 for Desktops (64-bit)" so as to make sure we do not trouble all VS 2008 SP1 customers.

What about Platform Builder Updates?

Platform Builder 5.0 and 6.0 Updates for SQL Server Compact v3.5 SP1 are also released to Platform Builder team and they should be available in the next Platform Builder 5.0 and 6.0 Update.  I will update this blog post with the download URL soon after they are available. 

Windows CE 5.0 Platform Builder Monthly Update (July 2008) - Download it here

Windows Embedded CE 6.0 Platform Builder Monthly Update (July 2008) - Download it here

 

Thanks,

Laxmi Narsimha Rao Oruganti

Posted by laxminro | 1 Comments
Filed under:

Insight into SQL Server Compact database corruptions

Database corruptions are often the most painful bugs to repro and debug. A good percentage of the SQL Server Compact bugs reported are in this category. However it so turns out that in majority of the cases Compact is not the culprit. Surprised!! Let us see why.

How SQL Server Compact detects a file corruption?

Compact database comprises of a series of pages, each 4K in size. When a page is written to disk the following sequence of steps is executed.

 

1.       Compute the checksum on the page data and store it as part of the page.

2.       Encrypt the page.

3.       Write to disk at a given file location or offset.

4.       Update system pages/run time cache with the page offset.

 

When a page is read from disk the following sequence of steps is executed.

 

1.       Read from the disk.

2.       Decrypt the page.

3.       Re-compute the checksum and compare it with the expected value. (If it fails page is not fully baked. Meaning file write failed)

4.       Check if this is the same page that we are expecting as per the system pages/ run time cache. (If this fails we lost a complete page write)

 

If (3) or (4) of the above read steps fail, the db is marked as corrupt and any further db activity is suspended till it is repaired successfully.

 

Notes:

·         Verify reports step (3) errors as “Bad Checksum - idPage: %d, tyPage: %d, iPage: %d”.

·         Verify reports step (4) errors as “Page with valid ordinal not found - idPage: %d, iPage: %d”

What could have triggered the corruption?

1.       A simple device on/off or suspend/resume can result in half-baked file. (No reason to panic as we handle this as described in the next section.)

2.       Compact relies on underlying FAT/TFAT file system to manage the database file. Any bugs in these components can lead to Compact file corruption. Compact stresses file system in many different ways as it tries to keep the file size as small as possible. As it employs shadow paging the writes and reads are quite random. (Note: 6 months back a bug has been identified in FAT/TFAT system that results in a Compact file losing some of the clusters during shrink operation.)

3.       If SD card is being used for db file storage, it’s being equally stressed as the above.

4.       More importantly SD cards bring in additional complexities during Suspend/Resume cycles. Here is a good blog link: http://blogs.msdn.com/windowsmobile/archive/2007/01/12/everything-you-want-to-know-about-sd.aspx

Am I suggesting SQL Server Compact bugs can’t result in any kind of corruption?

Of course Compact code bugs can result in corruption. A lot of them have been found internally before v3.0 release and also reported by customers thereafter. They are more logical in nature like index constraint is compromised or a table page says there are 1000 rows when there are only 55.  We did a very good job in fixing these and they are hard to find now. However Compact bugs don’t result in physical corruptions like “checksum errors” or “page missing” errors.

What does Compact do to prevent corruptions?

We believe most of the corruptions should be due to device suspend/resume or on/off cycles.  For ex: Compact is trying to write to a file and the user simply boots his device. Now file is in half-baked state. Or let’s say process gets killed before it can complete the flush cycle. How do we avoid this?

 

Well, we use shadow paging to overcome this situation. We can loosely categorize the db pages into user data pages, system pages and header page. As the name suggests user data pages contain all the user data. Header pages and system pages help us locate a specific user page on the disk. When it’s time to flush the changes to disk, we write shadows of the user data pages first. Then the shadows of the updated system pages are written. Finally the header page is written. The old pages are retained till the header page is updated. This ensures that we have a transactional write to the disk. Either the change is completely present or not.

Conclusion: Why do we still see corruptions?

Clearly the above is not enough. Some of the interesting cases are

·  What if any of the underlying systems ‘SD card software’ or FAT/TFAT has a bug?

·  Suspend/resume cycle can result in file buffer loss if SD card is used. So the changes supposed to be written to disk could be lost.

         

The intent is not to blame other components. However in the last 3 years, at least 90% of the corruption bugs reported are due to bad checksum. It means disk write is bad for some reason (which is not owned by Compact).  The fault is somewhere in the bottom stack and it has to be fixed. Our options are very limited without a repro but we are trying hard to improve our debugging infrastructure. We are hoping that with this knowledge customers will be able to understand the corruptions better and come up with good practices that would vastly reduce these corruptions. 

Thanks

Raja

Posted by SQLCEBLOG | 2 Comments
Filed under: ,

Katmai new datatypes support

In Katmai, new datatypes are introduced. Some of these (including some older types in Yukon) are supported for merge replication synching with SQL Compact in SQL Compact 3.5 SP1. These types are mapped to downlevel compatible types for SSC. These types and their mappings are, as follows:

 

SQL type                              Compact type                  Format                               

Date                                     nvarchar(10)                   ‘yyyy-mm-dd’

Time                                     nvarchar(16)                   ‘hh:mi:ss.nnnnnnn”

Datetime2                            nvarchar(27)                   ‘yyyy-mm-dd hh:mi:ss.nnnnnnn’

DatetimeOffset                    nvarchar(34)                  ‘yyyy-mm-dd hh:mi:ss.nnnnnnn [+|-]hh:mi’

Geometry                             image

Geography                           image

 

The format of strings used when inserting/updating the data on SQL Compact, should be same as that given above, or as the type demands, in case of Spatial types.

 

Spatial types are interesting, because, one can map them to image columns on the client without data loss. The data can even be manipulated on the client side, if the required spatial type libraries are present. Other new types in Katmai, like, HierarchyID etc... are not supported for merge with SQL Compact.

 

Thanks

Udaya

Posted by SQLCEBLOG | 0 Comments
Filed under:

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)