NDP - ADO.NET 2.0
Data Adapter basics

Contents

Introduction

Code shown below is in project DataSet\DtAdapter.

IDataAdapter provides methods and properties relating to filling DataSets and mapping tables and columns (more detail later).

IDataAdapter
Properties Description
MissingMappingAction Indicates or specifies whether unmapped source tables or columns are passed with their source names in order to be filtered or to raise an error.
MissingSchemaAction Indicates or specifies whether missing source tables, columns, and their relationships are added to the data set schema, ignored, or cause an error to be raised.
TableMappings Indicates how a source table is mapped to a data set table.
Method Description
Fill Adds or refreshes rows in the DataSet to match those in the data source using the DataSet name, and creates a DataTable named "Table".
FillSchema
Adds a DataTable named "Table" to the specified DataSet and configures the schema to match that in the data source based on the specified SchemaType.
GetFillParameters Gets the parameters set by the user when executing an SQL SELECT statement.
Update Calls the respective INSERT, UPDATE, or DELETE statements for each inserted, updated, or deleted row in the specified DataSet from a DataTable named "Table".


Interface IDbDataAdapter inherits from IDataAdapter; it adds command-related properties.

IDbDataAdapter
Public Properties Description
DeleteCommand Gets or sets an SQL statement for deleting records from the data set.
InsertCommand Gets or sets an SQL statement used to insert new records into the data source.
SelectCommand Gets or sets an SQL statement used to select records in the data source.
UpdateCommand Gets or sets an SQL statement used to update records in the data source.


Base class DataAdapter implements interface IDataAdapter.

DataAdapter
Properties Description
AcceptChangesDuringFill Gets or sets a value indicating whether AcceptChanges is called on a DataRow after it is added to the DataTable during any of the Fill operations.
AcceptChangesDuringUpdate Gets or sets whether AcceptChanges is called during a Update.
ContinueUpdateOnError Gets or sets a value that specifies whether to generate an exception when an error is encountered during a row update.
FillLoadOption Gets or sets the LoadOption that determines how the adapter fills the DataTable from the DbDataReader.
ReturnProviderSpecificTypes Gets or sets whether the Fill method should return provider-specific values or common CLS-compliant values.
Method Description
ResetFillLoadOption Resets FillLoadOption to its default state and causes Fill to honor AcceptChangesDuringFill.
ShouldSerializeAcceptChangesDuringFill Determines whether the AcceptChangesDuringFill property should be persisted.
ShouldSerializeFillLoadOption Determines whether the FillLoadOption property should be persisted.
Event Description
FillError Returned when an error occurs during a fill operation.

DbDataAdapter inherits from base class DataAdapter and implements interface IDbDataAdapter; it adds mostly protected properties useful for implementers. This is the base class for the provider-specific data adapters.

DbDataAdapter
Public Properties Description
UpdateBatchSize Gets or sets a value that enables or disables batch processing support, and specifies the number of commands that can be executed in a batch.

 

Filling a DataSet

MissingSchemaAction value Description
Add Adds the necessary columns to complete the schema.
AddWithKey Adds the necessary columns and primary key information to complete the schema. For more information about how primary key information is added to a DataTable, see FillSchema.To function properly with the .NET Framework Data Provider for OLE DB, AddWithKey requires that the native OLE DB provider obtains necessary primary key information by setting the DBPROP_UNIQUEROWS property, and then determines which columns are primary key columns by examining DBCOLUMN_KEYCOLUMN in the IColumnsRowset. As an alternative, the user may explicitly set the primary key constraints on each DataTable. This ensures that incoming records that match existing records are updated instead of appended. When using AddWithKey, the .NET Framework Data Provider for SQL Server appends a FOR BROWSE clause to the statement being executed. The user should be aware of potential side effects, such as interference with the use of SET FMTONLY ON statements. See SQL Server Books Online for more information.
Error An InvalidOperationException is generated if the specified column mapping is missing.
Ignore Ignores the extra columns.

Updating the database

Assume we have the following:

 static DataSet m_ds;
 static DbDataAdapter m_da;

We will provide our usual helper methods to get things from the provider factory:

        private static DbProviderFactory GetProviderFactory()
        {
            return DbProviderFactories
                    .GetFactory(ConfigurationManager.AppSettings["provider"]);
        }

        private static DbConnection CreateDbConnection()
        {
            string connStr = ConfigurationManager
                                .ConnectionStrings["SqlConnStr"]
                                .ConnectionString;
            DbConnection conn = GetProviderFactory().CreateConnection();
            conn.ConnectionString = connStr;
            return conn;
        }

        private static DbDataAdapter CreateDataAdapter()
        {
            return GetProviderFactory().CreateDataAdapter();
        }

        private static DbCommand CreateDbCommand()
        {
            return GetProviderFactory().CreateCommand();
        }

        private static DbParameter CreateDbParameter()
        {
            return GetProviderFactory().CreateParameter();
        }

To initialize, we first need to create the DataSet and DataAdapter.

            m_ds = new DataSet();
            m_da = CreateDataAdapter();

The SELECT command is easy. In this case, we are going to pull in all students from the table, so no parameterization is needed.

            DbCommand cmd;
            DbParameter param;
            
            cmd = CreateDbCommand();
            cmd.CommandText = "select Id, FirstName, LastName, Age from Student";
            m_da.SelectCommand = cmd;

Each of the remaining commands presents choices and complexity. Let's start with DELETE. The DELETE command itself can be written in several ways, depending on how much concurrency conflict checking you want to do.

The data for the id and possibly other fields will come from a deleted DataRow in the DataTable. We need to pick up original values because a deleted row does not have current values. Here goes:

            cmd = CreateDbCommand();
            cmd.CommandText = "Delete from Student where Id = @Id and "
                + " FirstName = @FN and LastName = @LN and Age = @Age";
            m_da.DeleteCommand = cmd;

            param = CreateDbParameter();
            param.DbType = DbType.Int32;
            param.Direction = ParameterDirection.Input;
            param.ParameterName = "@Id";
            param.SourceColumn = "Id";
            param.SourceVersion = DataRowVersion.Original;
            cmd.Parameters.Add(param);

            param = CreateDbParameter();
            param.DbType = DbType.String;
            param.Direction = ParameterDirection.Input;
            param.ParameterName = "@FN";
            param.SourceColumn = "FirstName";
            param.SourceVersion = DataRowVersion.Original;
            cmd.Parameters.Add(param);

            param = CreateDbParameter();
            param.DbType = DbType.String;
            param.Direction = ParameterDirection.Input;
            param.ParameterName = "@LN";
            param.SourceColumn = "LastName";
            param.SourceVersion = DataRowVersion.Original;
            cmd.Parameters.Add(param);

            param = CreateDbParameter();
            param.DbType = DbType.Int32;
            param.Direction = ParameterDirection.Input;
            param.ParameterName = "@Age";
            param.SourceColumn = "Age";
            param.SourceVersion = DataRowVersion.Original;
            cmd.Parameters.Add(param);

For the UPDATE command:

            cmd = CreateDbCommand();
            cmd.CommandText = "insert Student(FirstName, LastName, Age) " +
                              "VALUES( @FN, @LN, @Age);" + 
                              "select Scope_Identity() Id";
            m_da.InsertCommand = cmd;

            
            param = CreateDbParameter();
            param.DbType = DbType.String;
            param.Direction = ParameterDirection.Input;
            param.ParameterName = "@FN";
            param.SourceColumn = "FirstName";
            param.SourceVersion = DataRowVersion.Current;
            cmd.Parameters.Add(param);

            param = CreateDbParameter();
            param.DbType = DbType.String;
            param.Direction = ParameterDirection.Input;
            param.ParameterName = "@LN";
            param.SourceColumn = "LastName";
            param.SourceVersion = DataRowVersion.Current;
            cmd.Parameters.Add(param);

            param = CreateDbParameter();
            param.DbType = DbType.Int32;
            param.Direction = ParameterDirection.Input;
            param.ParameterName = "@Age";
            param.SourceColumn = "Age";
            param.SourceVersion = DataRowVersion.Current;
            cmd.Parameters.Add(param);

            param = CreateDbParameter();
            param.DbType = DbType.Int32;
            param.Direction = ParameterDirection.Output;
            param.ParameterName = "@Id";
            param.SourceColumn = "Id";
            param.SourceVersion = DataRowVersion.Current;
            cmd.Parameters.Add(param);

            cmd.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;

My, that seems like a lot of work. Fortunately, command builders can be used to generate the UPDATE, DELETE and INSERT commands from the SELECT statement. More here.

Here is code to drive this:

        private static void LoadStudents()
        {
            m_ds.Clear();

            using (DbConnection conn = CreateDbConnection())
            {
                m_da.SelectCommand.Connection = conn;
                m_da.Fill(m_ds,"Student");
            }
        }

        private static void InsertStudents()
        {
            DataTable dt = m_ds.Tables["Student"];

            DataRow dr;

            for (int i = 0; i < 5; ++i)
            {
                dr = dt.NewRow();
                dr["FirstName"] = "FN" + i;
                dr["LastName"] = "LN" + i;
                dr["Age"] = i;
                dt.Rows.Add(dr);
            }
        }

        private static void Update()
        {
            using ( DbConnection conn = CreateDbConnection() )
            {
                m_da.DeleteCommand.Connection = conn;
                m_da.InsertCommand.Connection = conn;
                m_da.UpdateCommand.Connection = conn;
               
                m_da.Update(m_ds.Tables["Student"]);
            }

        }


        private static void DeleteNewStudents()
        {
            foreach (DataRow dr in m_ds.Tables["Student"].Rows)
                if (((string)dr["FirstName"]).StartsWith("FN"))
                    dr.Delete();
        }

        private static void ShowStudents(string p)
        {
            DataTable dt = m_ds.Tables["Student"];

            Console.WriteLine("Here are the students {0}", p);

            foreach (DataRowView drv in dt.DefaultView)
            {
                foreach (DataColumn col in dt.Columns)
                    Console.Write("{0}\t", drv[col.Ordinal]);
                Console.WriteLine();
            }
        }

Output:

Here are the students before
1       Fred    Flintstone      45
2       Yogi    Bear    12
3       Jonny   Quest   16
4       George  Jetson  42
5       Tom     Smith   50
6       Tim     Smith   55
7       Jim     Smith   3
8       Tom     Jones   22
9       Tim     Jones   33
10      Joe     Jones   44


Here are the students after inserts
1       Fred    Flintstone      45
2       Yogi    Bear    12
3       Jonny   Quest   16
4       George  Jetson  42
5       Tom     Smith   50
6       Tim     Smith   55
7       Jim     Smith   3
8       Tom     Jones   22
9       Tim     Jones   33
10      Joe     Jones   44
        FN0     LN0     0
        FN1     LN1     1
        FN2     LN2     2
        FN3     LN3     3
        FN4     LN4     4


Here are the students after update
1       Fred    Flintstone      45
2       Yogi    Bear    12
3       Jonny   Quest   16
4       George  Jetson  42
5       Tom     Smith   50
6       Tim     Smith   55
7       Jim     Smith   3
8       Tom     Jones   22
9       Tim     Jones   33
10      Joe     Jones   44
34      FN0     LN0     0
35      FN1     LN1     1
36      FN2     LN2     2
37      FN3     LN3     3
38      FN4     LN4     4


Here are the students after deletes
1       Fred    Flintstone      45
2       Yogi    Bear    12
3       Jonny   Quest   16
4       George  Jetson  42
5       Tom     Smith   50
6       Tim     Smith   55
7       Jim     Smith   3
8       Tom     Jones   22
9       Tim     Jones   33
10      Joe     Jones   44