Welcome to MSDN Blogs Sign in | Join | Help

SQL Server 2008, Noverber 2007 CTP was recently released.

We've also released a CTP of Visual Studio 2005 Support for SQL Server 2008. This will enable you to use VS2005 against SQL Server 2008. For more information about this patch, see the Knowledge Base Article 942246.

Here's a list of new data tools features in Visual Studio. I will be discussing each one in more detail in upcoming posts.

Object Relational Designer

The object relational mapping technology lets you map relational databases to objects. Once the mapping is done, you can manipulate mapped objects as normal objects and easily submit changes back to relational database without writing complex data access logic. It is the latest enhancement in data access technology and there are many different implementations out there currently. However, no O/R implementation comes with a graphical designer that makes it easy to create and modify mapping.

The Object Relational Designer in Visual Studio is Microsoft's answer to how O/R mapping should be done. It's a graphical designer which lets you easily map database objects such as tables and stored procedures to LINQ To SQL classes and methods. You can just drag out database objects from the Database Explorer onto the designer. Designer then takes care of creating mapping and generating proper LINQ To SQL code. You can also modify different aspects of mapping through the designer and let the designer take care of updating the code automatically. Association and inheritance relationships can also be easily created easily. The stored procedure support is also a very unique feature. Instead of letting LINQ To SQL generate INSERT, UPDATE and DELETE SQL statements, you can map methods created based on the stored procedures to each behavior.

If you've tried May CTP of LINQ, you probably remember a version of the O/R Designer we included. It was called DLinq Designer. What you see in March CTP of Visual Studio Orcas is the next generation of DLinq Designer and it's now called the O/R Designer.

  

Hierarchical Update in Typed Dataset

Keeping track of all inserts, updates and deletes across multiple related datatables and sending those changes in the right order back to the server is not an easy task. How do you make sure that new orders for your customer are added correctly to the system while updating the shipping address of the same customer and deleting one of old orders that had been cancelled all at the same time?

With hierarchical update support in Typed Dataset, all you need to do is to call UpdateAll() method of the new TableAdapterManager component we've added. It takes care of collecting all changes and sending them back to the server in the right order. Of course, everything is wrapped into a transaction.

We believe this would significantly improve the productivity of developers using Typed Dataset to create data applications. You can try this new feature in March CTP of Visual Studio Orcas.

  

N –Tier Support in Typed Dataset

I admit. Typed Dataset code we generate in Visual Studio 2005 is not N-Tier ready. If you open up the code file, you will notice that we've done a great job separating types from data access logic. Dataset and typed datatables are declared in one Typed Dataset class and all TableAdapter classes are declared under a separate namespace. But we generate both sets in one code file. For a lot of applications this is not an issue. But when you are building multi-tiered applications, having them in one file doesn't really help. This exact problem is discussed in detail on Splitting Typed Datasets from TableAdapters blog entry from Steve Lasker. The solution proposed in this blog entry was to open up the generated code and manually copy and paste type declaration out into a new class file. Although this did allow you to use Typed Dataset in N-Tier scenario, it had one major problem. Since you are manually modifying generated code, anytime you make a change to Typed Dataset via Dataset Designer, you have to remember to copy and paste code. That was painful.

In Orcas, you can instruct the designer to generate Dataset portion of the code into another project in your solution. No more manual copy/paste. You can even make changes to Typed Dataset from the designer and it will make sure that updated code is generated into the specified project. You can still choose to stay in 2-Tier model. Designer will continue to generate everything in a single file. When you are ready to take your 2-Tier application to N-Tier, just tell the designer which project is the Dataset project and you are done. It's that simple. A preview of this feature is in March CTP of Visual Studio Orcas and you will be able to try the polished version in Beta 1.

  

Local Data Cache with SQL Compact Edition

SQL Compact Edition enables many exciting scenarios for developers. The most interesting scenario is to use SQL Compact Edition database file as a local cache of data that do not change often. For instance, your application might keep a list of products in local cache while exchanging order information with remote server. Once in a while, you can synch products list from the remote server but you will mostly use local cache.

Since SQL CE is light-weight database that has very little overhead, it's the perfect candidate for the local cache store. You can use the Sync Service for ADO.NET to synchronize data between remote database server and local SQL CE database file. Sounds great, right? But how do you set everything up so that you can do this?

Visual Studio Orcas includes new project item template "Local Data Cache". Adding "Local Data Cache" to your project creates .sync file which is an XML file that describes what gets synched and how. This file comes with the designer that lets you configure different aspects of synchronization easily. Designer also creates SQL CE .sdf database file to be used as local cache store as well as synchronization code necessary to interact with the Sync Service. A preview of this feature is in March CTP of Visual Studio Orcas and you will be able to try the polished version in Beta 1.

 

 

Other Enhancements to Data Tools

Above four are major data tools features in Visual Studio Orcas. But there are tons of other enhancements we've made to existing data tools. I will be dedicating a separate post to describe other important enhancements we've made.

- Young Joo, Program Manager, Visual Basic

Several people asked me whether TableAdapter can be used to retrieve multiple result sets from stored procedures. The quick answer is... No, you can't do that with TableAdapter.Fill() method that gets generated as a part of Typed Dataset. But there's a very simple work-around.

DataAdapter.Fill() & Multiple Result Sets

TableAdapter.Fill() method calls DataAdapter.Fill() to retrieve data from database. And DataSet.Fill() method allows you to retrieve multiple result sets from a stored procedure. In order to retrieve multiple result sets, an overload of DataAdapter.Fill() that takes Dataset as a parameter needs to be used. If you do that, you get back a Dataset that contains multiple data tables filled with result sets returned by your stored procedure.

Here's a simple example that shows you how this works.

Let's say that you have a stored procedure called dbo.spSelectCustomersOrders in Northwind database.

CREATE PROCEDURE spSelectCustomersOrders

AS

BEGIN

    SET NOCOUNT ON;

    SELECT * FROM Customers

    SELECT * FROM Orders

END

GO

Below code will call this stored procedure and fill Dataset with both result sets.

Dim myConn As New System.Data.SqlClient.SqlConnection

Dim myAdapter As New System.Data.SqlClient.SqlDataAdapter

Dim mySelectCommand As New System.Data.SqlClient.SqlCommand

Dim myDataset As New System.Data.DataSet

myConn.ConnectionString = "Data Source=.\SQLExpress;Initial Catalog=Northwind;Integrated Security=True"

mySelectCommand.Connection = myConn

mySelectCommand.CommandText = "dbo.spSelectCustomersOrders"

myAdapter.SelectCommand = mySelectCommand

myAdapter.Fill(myDataset)

For Each table As System.Data.DataTable In myDataset.Tables

Console.WriteLine("Table Name:" & table.TableName)

Next

Output of this code looks like this.

Table Name: Table
Table Name: Table1

As you can see, DataAdapter.Fill() method executed the stored procedure and put each result set into two separate DataTables.

TableAdapter Solution

But why doesn't TableAdapter.Fill() correctly handle multiple result sets? It's because TableAdapter.Fill() calls DataAdapter.Fill() overload that takes DataTable, not Dataset. This can be easily addressed by creating a new Fill method in TableAdapter that calls DataAdapter.Fill() overload that takes Dataset.

Assume we have NorthwindDataset.xsd that contains both Customers and Orders. And let's use the above stored procedure for our new Fill method. Add following code to your partial class file. You can create a partial class file by either double-clicking on the Dataset Designer surface, right-click on the Dataset Designer surface and choose "View Code" or add an empty class file manually.

Namespace NorthwindDataSetTableAdapters

Partial Public Class CustomersTableAdapter

Public Function FillCustomersOrders(ByVal dataSet As NorthwindDataSet) As Integer

Dim multiSelectCommand As New System.Data.SqlClient.SqlCommand

Dim returnValue As Integer

multiSelectCommand.Connection = Me.Connection

multiSelectCommand.CommandText = "dbo.spSelectCustomersOrders"

Me.Adapter.SelectCommand = multiSelectCommand

'' Map auto-created Table1 that holds the second result-set (Orders rows) to

'' Orders DataTable in our Dataset.

Me.Adapter.TableMappings.Add("Table1", "Orders")

returnValue = Me.Adapter.Fill(dataSet)

Return returnValue

End Function

End Class

End Namespace

Couple of key points to note.

First, this new FillCustomersOrders takes Dataset as a parameter. This way, when we call DataAdapter.Fill() we can retrieve both result sets and fill Dataset appropriately.

Second, notice how we use TableMapping to map automatically created DataTable to Orders DataTable in our Dataset. When result sets are retrieved by DataAdapter.Fill() method, each result set is stored in separate DataTables in Dataset. By default, these DataTables are named as Table, Table1, Table2, etc. In order to map these DataTables to typed DataTables defined in our Dataset, we need to use TableMapping. If you open up code behind file for NorthwindDataset.xsd, for example, and browse to InitAdapter() method of one of TableAdapter class, you will see something similar to below.

tableMapping.SourceTable = "Table"

tableMapping.DataSetTable = "Customers"

'' Colum mapping code skipped

...

Me._adapter.TableMappings.Add(tableMapping)

This block of code ensures that what gets returned by DataAdapter.Fill method maps to the correct typed DataTable in our Dataset. In our case with FillCustomersOrders, we know that the second result set contains Orders rows. So we need to create a mapping between Table1 and Orders to make sure Orders DataTable gets filled correctly.

After you add above code to the partial class file, you can call FillCustomersOrders to fill both Customers and Orders with just one round-trip to database.

CustomersTableAdapter.FillCustomersOrders(Me.NorthwindDataSet)

Performance Consideration

I can see how this kind of capability could be helpful in some cases. But you have to remember that this comes with its own issues. Although you might think that avoiding multiple trips to database would help in terms of performance, you could end up hurting performance by retrieving large set of data when you only need a sub-set. A lot of times, it is much better to retrieve smaller set of data, only what you need at the moment, and make another call to database when you need other sets of data. ADO.NET does a pretty good job of optimizing connection pooling so making multiple connections to database doesn't cause performance bottleneck in many cases. Rule of thumb. Only retrieve what you need.

But I can see how retrieving multiple result sets can be helpful in some cases. So, make use of the technique I described here and just make sure you consider performance issues always.

Additional Resources

- Young Joo, Program Manager, Visual Basic

The October letter from the editor (http://msdn.com/vfoxpro/letters) is now live. This announces the October CTP of Sedna and SP2 for Visual FoxPro.

The CTP previews several enhancements: Enhancements to Reporting, SQL Server Upsizing Wizard and DataExplorer. It also includes earlier components: NET4COM and the MY library for VFP.

This entry was originally posted on SmartClientData blog.

If you’ve been wondering why you are not able to access stored procedure return values from TableAdapter, here’s the solution for you.

I will use tbTasks table and spInsertTask stored procedure to demonstrate the solution.  You can see definitions for both below.

CREATE TABLE dbo.tbTasks

(

       intID INT NOT NULL IDENTITY(1,1),

       strName VARCHAR(100) NOT NULL,

       intPriority INT NOT NULL,

       dtDueDate DATETIME NOT NULL

)

----------------------------------------

CREATE PROCEDURE dbo.spInsertTask

       (

              @strName VARCHAR(200),

              @intPriority INT,

              @dtDueDate DATETIME

       )

AS

 

INSERT INTO tbTasks (strName, intPriority, dtDueDate)

VALUES (@strName, @intPriority, @dtDueDate)

 

RETURN SCOPE_IDENTITY()

GO

 

Notice that tbTasks has an identity column named intID.  Also, stored procedure spInsertTask returns the new identity column value using SCOPE_IDENTITY().  Knowing this new identity value is extremely useful on the client side.

Create a new Typed Dataset called TasksDataset and add tbTasks.  Also, add a new query to tbTasksTableAdapter using spInsertTask stored procedure.  When adding a new query, choose ‘A single value’ option.

At this point, you probably would expect that following code would assign the new identity value returned by spInsertTask stored procedure to returnValue variable.

[ VB ]

Dim taTasks As New TasksDatasetTableAdapters.tbTasksTableAdapter

Dim TaskName As String

Dim TaskPriority As Integer

Dim TaskDueDate As Date

Dim returnValue As Integer

 

TaskName = "Test"

TaskPriority = 1

TaskDueDate = Now()

 

returnValue = taTasks.InsertTask(TaskName, TaskPriority, TaskDueDate)

 

[C#]

TasksDatasetTableAdapters.tbTasksTableAdapter taCustomers = new WindowsApplication1.TasksDatasetTableAdapters.tbTasksTableAdapter();

String taskName;

int taskPriority;

DateTime taskDueDate;

int returnValue;

 

taskName = "Test";

taskPriority = 1;

taskDueDate = System.DateTime.Now;

 

returnValue = taCustomers.InsertTask(taskName, taskPriority, taskDueDate);

 

However, running above code results in System.InvalidOperationException during run-time for VB and “Cannot implicitly convert type 'int?' to 'int'.” compile error for C#.  If you look at what actually gets returned by tbTasksTableAdapter.InsertTask() function, you will understand why above code does not work.  You can find the function from the generated Typed Dataset code, TasksDataset.Designer.vb / TasksDataset.Designer.cs in this case.

[ VB ]

Public Overridable Overloads Function InsertTask(...) As System.Nullable(Of Integer)

    ...

    ...

    Dim returnValue As Object

    Try

        returnValue = command.ExecuteScalar

    Finally

        ...

    End Try

    If ((returnValue Is Nothing) _

                OrElse (returnValue.GetType Is GetType(System.DBNull))) Then

        Return New System.Nullable(Of Integer)

    Else

        Return New System.Nullable(Of Integer)(CType(returnValue, Integer))

    End If

End Function

* C# version omitted since there’s no significant difference.

As you can see from above, what gets returned from InsertTask function is actually the return value of System.Data.SqlClient.SqlCommand.ExecuteScalar() which is the first column of the first row in the result set, or a null reference if the result set is empty, not the return value of the stored procedure.  In this case, InsertTask returns null since the stored procedure does not return any result set.

If you choose ‘No value’ option, System.Data.SqlClient.SqlCommand.ExecuteNonQuery() is used instead.  And the return value of ExecuteNonQuery() is the number of rows affected.  Again, this is not the stored procedure return value.

So, how do you retrieve the stored procedure return value?  Although it’s not immediately obvious, there’s an easy way to access it.  Let’s look at the definition of the command object for the stored procedure.  You can see it from tbTasksTableAdapter.InitCommandCollection() in TasksDataset.Designer.vb / TasksDataset.Designer.cs file.

[ VB]

Private Sub InitCommandCollection()

    Me._commandCollection = New System.Data.SqlClient.SqlCommand(1) {}

    Me._commandCollection(0) = New System.Data.SqlClient.SqlCommand

    Me._commandCollection(0).Connection = Me.Connection

    Me._commandCollection(0).CommandText = "SELECT intID, strName, intPriority, dtDueDate FROM dbo.tbTasks"

    Me._commandCollection(0).CommandType = System.Data.CommandType.Text

    Me._commandCollection(1) = New System.Data.SqlClient.SqlCommand

    Me._commandCollection(1).Connection = Me.Connection

    Me._commandCollection(1).CommandText = "dbo.spInsertTask"

    Me._commandCollection(1).CommandType = System.Data.CommandType.StoredProcedure

    Me._commandCollection(1).Parameters.Add(New System.Data.SqlClient.SqlParameter("@RETURN_VALUE", System.Data.SqlDbType.Int, 4, System.Data.ParameterDirection.ReturnValue, 10, 0, Nothing, System.Data.DataRowVersion.Current, false, Nothing, "", "", ""))

    Me._commandCollection(1).Parameters.Add(New System.Data.SqlClient.SqlParameter("@strName", System.Data.SqlDbType.VarChar, 200, System.Data.ParameterDirection.Input, 0, 0, Nothing, System.Data.DataRowVersion.Current, false, Nothing, "", "", ""))

    Me._commandCollection(1).Parameters.Add(New System.Data.SqlClient.SqlParameter("@intPriority", System.Data.SqlDbType.Int, 4, System.Data.ParameterDirection.Input, 10, 0, Nothing, System.Data.DataRowVersion.Current, false, Nothing, "", "", ""))

    Me._commandCollection(1).Parameters.Add(New System.Data.SqlClient.SqlParameter("@dtDueDate", System.Data.SqlDbType.DateTime, 8, System.Data.ParameterDirection.Input, 23, 3, Nothing, System.Data.DataRowVersion.Current, false, Nothing, "", "", ""))

End Sub

* C# version omitted since there’s no significant difference.

You can see from above that parameters collection does actually include @RETURN_VALUE parameter.  It’s the first parameter in the collection.  When the stored procedure is executed, return value from the stored procedure is added to this item in the collection.  So, all we need to do is to retrieve this value after executing InsertTask() function.  To do that, I will add some code to the partial class defined in TasksDataset.vb / TasksDataset.cs.

[ VB ]

Namespace TasksDatasetTableAdapters

    Partial Public Class tbTasksTableAdapter

        Public Function GetReturnValue(ByVal commandIndex As Integer) As Object

            Return Me.CommandCollection(commandIndex).Parameters(0).Value

        End Function

    End Class

End Namespace

 

[ C# ]

namespace WindowsApplication1.TasksDatasetTableAdapters {

    public partial class tbTasksTableAdapter

    {

        public object GetReturnValue(int commandIndex)

        {

            return this.CommandCollection[commandIndex].Parameters[0].Value;

        }

    }

}

 

Since Dataset Designer does not generate partial class structure for TableAdapters, you will have to add above code yourself to partial class file.  The commandIndex parameter is the index of the command object in _commandCollection to retrieve return value from.  You can get that information by looking at tbTasksTableAdapter.InitCommandCollection(). Now, let’s modify the code that was not running to use this new function.

[ VB ]

Dim taTasks As New TasksDatasetTableAdapters.tbTasksTableAdapter

Dim TaskName As String

Dim TaskPriority As Integer

Dim TaskDueDate As Date

Dim returnValue As Integer

 

TaskName = "Test"

TaskPriority = 1

TaskDueDate = Now()

 

taTasks.InsertTask(TaskName, TaskPriority, TaskDueDate)

returnValue = taTasks.GetReturnValue(1)

 

[C#]

TasksDatasetTableAdapters.tbTasksTableAdapter taCustomers = new WindowsApplication1.TasksDatasetTableAdapters.tbTasksTableAdapter();

String taskName;

int taskPriority;

DateTime taskDueDate;

int returnValue;

 

taskName = "Test";

taskPriority = 1;

taskDueDate = System.DateTime.Now;

 

taCustomers.InsertTask(taskName, taskPriority, taskDueDate);

returnValue = (int)taCustomers.GetReturnValue(1);

 

We pass in 1 as a parameter value to GetReturnValue() since our stored procedure is located at index 1 in _commandCollection.  Above code will correctly retrieve return value from the stored procedure which is the new identity value of intID column.  If you have more than one stored procedures that return something, you can retrieve those return values by calling GetReturnValue() with correct index.

Typed Dataset simplifies data access layer development significantly by generating necessary code for you based on the information you provide via Dataset Designer.  Although generated code covers large number of scenarios, I suggest that you take a close look at generated code and find out how you can extend the functionality of default Typed Dataset.  And definitely let us know how we can improve it to make Typed Dataset more powerful and flexible.

Also, don’t forget to let me know if you have better ways to retrieve return values from stored procedures.  What I suggest here is just one solution and I am sure that you have other solutions that might be more elegant than this.

Young Joo

Today we released the June 2006 CTP of VFP “Sedna”. It is available for download here. I’ve also published the monthly letter: http://msdn.com/vfoxpro/letters that contains information about this CTP.

http://msdn.microsoft.com/vfoxpro/letters/ is online.

Milind Lele

As many of you know, after a major release, people at Microsoft tend to look around to see what they’d like to work on next. After working on two releases of Visual Studio and a similar number of releases of Visual FoxPro, I’ve accepted a position in Microsoft.com as an architect focusing on our community products.

 

I accepted this position because it marries the community issues that I’m so passionate about, with the ability to quickly and immediately touch our customers around the world. In essence, I will be responsible for a multi-release plan developing a platform that includes the following:

a.      blogs.msdn.com,

b.      forums.microsoft.com,

c.       on-line chats,

d.      CodeZone search,

e.       GotDotNet

f.        and others…

 

We want to turn this into a platform that every division in Microsoft can use to quickly build their own unique community presence and push that presence out to their partners in the community.

 

Given that this involves architecture, cross-division impact and customer presence, I just couldn’t say no.  It’s been an amazing four years here in DevDiv – getting to work on multiple releases of VB, Data Tools and VFP.

 

In Visual Studio 2005 we made data easier to work with, something we’ll be improving in Orcas. We’ve also got LINQ coming down the pike – deeply integrating querying and data capabilities into VB and C# - something that I think will quickly become a capability that is considered a “must have” for any general purpose programming languages. Now I look forward to helping build tools that make it easier for everyone to work more closely with our customers.

 

In Visual FoxPro, we’ve revamped the report writer, improved its extensibility and deepened its integration with SQL Server and .NET.

 

Speaking of Visual FoxPro, the folks at MSCOM have been good enough to let me continue to drive that forward as we work on Sedna. So, I’ll still be the person responsible for VFP, working with Calvin, Milind and the rest of the team. As you can see, I’ll still have one foot in DevDiv. <g> However, I don’t feel right posting to the VS Data blog, so I’ve created a new one here (where I'll be cross-posting this). Come by for more details on the new position, Visual FoxPro, life, the universe and everything. Note that VFP is staying in the Data Team, so Milind (and any others who want to) will continue blogging here. I just think it’s time I get a personal blog – like Ken and Calvin.

 

One last note to the VS Data team: you all rock – I know you’re working on some amazing things for the future. I look forward to working with it all in my new role.

 

There's a conference coming to Seattle that looks really interesting for language wonks:

The .NET Programming Languages And Compilers Symposium: Lang .NET 2006

Here are a few of the things that they'll be discussing:

  • Dynamic languages and scripting
  • AJAX and ATLAS
  • Domain specific languages
  • Functional languages
  • Object-oriented and aspect-oriented programming
  • Web-services and mobile code
  • Libraries
  • Language-Integrated Query (LINQ)
  • Compiler frameworks
  • Garbage collection
  • JIT compilation
  • Visual Programming
  • Success and failure stories
  • Non-standard language features and implementation techniques
  • Tools and IDE support

Great folks on the committee. Guess I know what I'll be doing in early August!

Today I published the April 2006 letter for Visual FoxPro.

Incidentally, in that letter I mentioned that the Team System team is about to release a MSCCI provider for Team Foundation Server which includes support for Visual FoxPro 9.0. Then this afternoon YAG sent me an email saying that the beta for the MSCCI is already available for download!

Paul Flessner announced the new addition to the SQL Server family. This will provide a lightweight storage on all clients and will include support for seamlessly synchronizing with other SQL Server editions.

Milind Lele

Jim Gray was interviewed for Behind The Code back in early March. Jim's a Technical Fellow here at Microsoft and manages our Bay Area Research Center. For data folks like me, he is one of the people that came up with the theories that underly transaction processing in databases - and he works on scaleable servers with things like Terraserver today.

They cover his entire life and career - from growing up in Italy thru Tandem and DEC to Microsoft. He's even joined by someone with whom I've shared a number of airplane rides recently - Tom Barclay.

I've had this on my "to do" list for a while, and actually had the free hour to watch the video today. I'm just sorry I didn't do it earlier. Highly recommended.

Kate's blog entry reminds me that I haven't mentioned that I'll be speaking at Devteach in Montreal on May 8-12. It's a great conference - I spoke at the first one, and have missed the past few years - looking forward to being back again.

My keynote will be about Linq (of course <g>).

On the way back, I'll be stopping at the Vermont .NET User Group - a place I've been wanting to speak at for years. Should be a lot of fun.

 

 

ttp://www.ozfoxrocks.com/Default.aspx?tabid=57

In this podcast Craig and Scott discuss design and testing, and a chat with Craig Boyd about Vista and Visual FoxPro 'Sedna'.

In the introduction they talk about Ken Levy, his great work with FoxPro and the community -- and they give me a warm and hillarious welcome to the Fox world!

We have released the first CTP (Community Technology Preview) of Sedna.  This March 2006 CTP includes the latest build of NET4COM — a library of COM wrappers around a selected subset of the Microsoft .NET Framework 2.0.  We will release other CTPs periodically. Each CTP will have incrementally increasing functionality and latest builds of Sedna until we release a full public beta later this year.

More Posts Next page »
 
Page view tracker