Search This Blog

Wednesday, June 23, 2010

Dataset Unleashed

Terms in ADO.NET:
1) DataTable: DataSet is made up of a collection of tables, relationships, and constraints. In
ADO.NET, DataTable objects are used to represent the tables in a DataSet.
A DataTable represents one table of in-memory relational data. The data is local to the .NET-based application in which it resides, but can be populated from a data source such as Microsoft SQL Server using a DataAdapter.
The DataTable class is a member of the System.Data namespace.
You can create and use a DataTable independently or as a member of a DataSet, can also be used in conjunction with other .NET Framework objects, including the DataView.
The DataSet has Tables property which contains a collection of DataTable that it holds.
The schema or structure of a table is represented by columns and constraints. You define the schema of a DataTable using DataColumn objects as well as ForeignKeyConstraint and UniqueConstraint objects.
In addition to a schema, a DataTable must also have rows to contain and order data. The DataRow class represents the actual data contained in a table. You use the DataRow and its properties and methods to retrieve, evaluate, and manipulate the data in a table. As you access and change the data within a row, the DataRow object maintains both its current and original state.
You can create parent-child relationships between tables using one or more related columns in the tables. You create a relationship between DataTable objects using a DataRelation. DataRelation objects can then be used to return the related child or parent rows of a particular row.
Creating DataTable: If you are creating a DataTable programmatically, you must first define its schema by adding DataColumn objects to the DataColumnCollection.
DataTable workTable = new DataTable("Customers");

/*adding column to table*/
DataColumn workCol = workTable.Columns.Add("CustID", typeof(Int32));
workCol.AllowDBNull = false;
workCol.Unique = true;

workTable.Columns.Add("CustLName", typeof(String));
workTable.Columns.Add("CustFName", typeof(String));
workTable.Columns.Add("Purchases", typeof(Double));

In the example, notice that the properties for the CustID column are set to not allow DBNull values and to constrain values to be unique. However, if you define the CustID column as the primary key column of the table, the AllowDBNull property will automatically be set to false and the Unique property will automatically be set to true.
Defining Primary Keys:
A database table commonly has a column or group of columns that uniquely identifies each row in the table. This identifying column or group of columns is called the primary key.
When you identify a single DataColumn as the PrimaryKey for a DataTable, the table automatically sets the AllowDBNull property of the column to false and the Unique property to true. For multiple-column primary keys, only the AllowDBNull property is automatically set to false.
The PrimaryKey property of a DataTable receives as its value an array of one or more DataColumn objects, as shown in the following examples. The first example defines a single column as the primary key.
workTable.PrimaryKey = new DataColumn[] {workTable.Columns["CustID"]};

// Or

DataColumn[] columns = new DataColumn[1];
columns[0] = workTable.Columns["CustID"];
workTable.PrimaryKey = columns;

The following example defines two columns as a primary key.
workTable.PrimaryKey = new DataColumn[] {workTable.Columns["CustLName"],
workTable.Columns["CustFName"]};

// Or

DataColumn[] keyColumn = new DataColumn[2];
keyColumn[0] = workTable.Columns["CustLName"];
keyColumn[1] = workTable.Columns["CustFName"];
workTable.PrimaryKey = keyColumn;

Adding Data to a DataTable:
After you create a DataTable and define its structure using columns and constraints, you can add new rows of data to the table. To add a new row, declare a new variable as type DataRow. A new DataRow object is returned when you call the NewRow method. The DataTable then creates the DataRow object based on the structure of the table, as defined by the DataColumnCollection.
The following example demonstrates how to create a new row by calling the NewRow method.
DataRow workRow = workTable.NewRow();

You then can manipulate the newly added row using an index or the column name, as shown in the following example.
workRow["CustLName"] = "Smith";
workRow[1] = "Smith";

After data is inserted into the new row, the Add method is used to add the row to the DataRowCollection, shown in the following code.
workTable.Rows.Add(workRow);

You can also call the Add method to add a new row by passing in an array of values, typed as Object, as shown in the following example.
workTable.Rows.Add(new Object[] {1, "Smith"});

Passing an array of values, typed as Object, to the Add method creates a new row inside the table and sets its column values to the values in the object array. Note that values in the array are matched sequentially to the columns, based on the order in which they appear in the table.
The following example adds 10 rows to the newly created Customers table.
DataRow workRow;

for (int i = 0; i <= 9; i++)
{
workRow = workTable.NewRow();
workRow[0] = i;
workRow[1] = "CustName" + i.ToString();
workTable.Rows.Add(workRow);
}

DataTable Constraints:
You can use constraints to enforce restrictions on the data in a DataTable, in order to maintain the integrity of the data. A constraint is an automatic rule, applied to a column or related columns, that determines the course of action when the value of a row is somehow altered. Constraints are enforced when the System.Data.DataSet.EnforceConstraints property of the DataSet is true. For a code example that shows how to set the EnforceConstraints property, see the EnforceConstraints reference topic.
There are two kinds of constraints in ADO.NET: the ForeignKeyConstraint and the UniqueConstraint. By default, both constraints are created automatically when you create a relationship between two or more tables by adding a DataRelation to the DataSet. However, you can disable this behavior by specifying createConstraints = false when creating the relation.

ForeignKeyConstraint:
A ForeignKeyConstraint enforces rules about how updates and deletes to related tables are propagated. For example, if a value in a row of one table is updated or deleted, and that same value is also used in one or more related tables, a ForeignKeyConstraint determines what happens in the related tables.
The DeleteRule and UpdateRule properties of the ForeignKeyConstraint define the action to be taken when the user attempts to delete or update a row in a related table. The following table describes the different settings available for the DeleteRule and UpdateRule properties of the ForeignKeyConstraint.
Rule setting Description
Cascade Delete or update related rows.
SetNull Set values in related rows to DBNull.
SetDefault Set values in related rows to the default value.
None Take no action on related rows. This is the default.
A ForeignKeyConstraint can restrict, as well as propagate, changes to related columns. Depending on the properties set for the ForeignKeyConstraint of a column, if the EnforceConstraints property of the DataSet is true, performing certain operations on the parent row will result in an exception. For example, if the DeleteRule property of the ForeignKeyConstraint is None, a parent row cannot be deleted if it has any child rows.
You can create a foreign key constraint between single columns or between an array of columns by using the ForeignKeyConstraint constructor. Pass the resulting ForeignKeyConstraint object to the Add method of the table's Constraints property, which is a ConstraintCollection. You can also pass constructor arguments to several overloads of the Add method of a ConstraintCollection to create a ForeignKeyConstraint.
When creating a ForeignKeyConstraint, you can pass the DeleteRule and UpdateRule values to the constructor as arguments, or you can set them as properties as in the following example (where the DeleteRule value is set to None)
ForeignKeyConstraint custOrderFK = new ForeignKeyConstraint("CustOrderFK",
custDS.Tables["CustTable"].Columns["CustomerID"],
custDS.Tables["OrdersTable"].Columns["CustomerID"]);
custOrderFK.DeleteRule = Rule.None;
// Cannot delete a customer value that has associated existing orders.
custDS.Tables["OrdersTable"].Constraints.Add(custOrderFK);

AcceptRejectRule:
Changes to rows can be accepted using the AcceptChanges method or canceled using the RejectChanges method of the DataSet, DataTable, or DataRow. When a DataSet contains ForeignKeyConstraints, invoking the AcceptChanges or RejectChanges methods enforces the AcceptRejectRule. The AcceptRejectRule property of the ForeignKeyConstraint determines which action will be taken on the child rows when AcceptChanges or RejectChanges is called on the parent row.
The following table lists the available settings for the AcceptRejectRule.
Rule setting Description
Cascade Accept or reject changes to child rows.
None Take no action on child rows. This is the default.
Example
The following example creates a ForeignKeyConstraint, sets several of its properties, including the AcceptRejectRule, and adds it to the ConstraintCollection of a DataTable object.
private void CreateConstraint(DataSet dataSet,
string table1, string table2,string column1, string column2)
{
// Declare parent column and child column variables.
DataColumn parentColumn;
DataColumn childColumn;
ForeignKeyConstraint foreignKeyConstraint;

// Set parent and child column variables.
parentColumn = dataSet.Tables[table1].Columns[column1];
childColumn = dataSet.Tables[table2].Columns[column2];
foreignKeyConstraint = new ForeignKeyConstraint
("SupplierForeignKeyConstraint", parentColumn, childColumn);

// Set null values when a value is deleted.
foreignKeyConstraint.DeleteRule = Rule.SetNull;
foreignKeyConstraint.UpdateRule = Rule.Cascade;
foreignKeyConstraint.AcceptRejectRule = AcceptRejectRule.None;

// Add the constraint, and set EnforceConstraints to true.
dataSet.Tables[table1].Constraints.Add(foreignKeyConstraint);
dataSet.EnforceConstraints = true;
}

UniqueConstraint:

The UniqueConstraint object, which can be assigned either to a single column or to an array of columns in a DataTable, ensures that all data in the specified column or columns is unique per row. You can create a unique constraint for a column or array of columns by using the UniqueConstraint constructor. Pass the resulting UniqueConstraint object to the Add method of the table's Constraints property, which is a ConstraintCollection. You can also pass constructor arguments to several overloads of the Add method of a ConstraintCollection to create a UniqueConstraint. When creating a UniqueConstraint for a column or columns, you can optionally specify whether the column or columns are a primary key.
You can also create a unique constraint for a column by setting the Unique property of the column to true. Alternatively, setting the Unique property of a single column to false removes any unique constraint that may exist. Defining a column or columns as the primary key for a table will automatically create a unique constraint for the specified column or columns. If you remove a column from the PrimaryKey property of a DataTable, the UniqueConstraint is removed.
The following example creates a UniqueConstraint for two columns of a DataTable.
DataTable custTable = custDS.Tables["Customers"];
UniqueConstraint custUnique = new UniqueConstraint(new DataColumn[]
{custTable.Columns["CustomerID"],
custTable.Columns["CompanyName"]});
custDS.Tables["Customers"].Constraints.Add(custUnique);

DataTable Events:
The DataTable object provides a series of events that can be processed by an application. The following table describes DataTable events.
Event Description
Initialized
Occurs after the EndInit method of a DataTable is called. This event is intended primarily to support design-time scenarios.
ColumnChanged
Occurs after a value has been successfully changed in a DataColumn.

ColumnChanging
Occurs when a value has been submitted for a DataColumn.
RowChanged
Occurs after a DataColumn value or the RowState of a DataRow in the DataTable has been changed successfully.
RowChanging
Occurs when a change has been submitted for a DataColumn value or the RowState of a DataRow in the DataTable.
RowDeleted
Occurs after a DataRow in the DataTable has been marked as Deleted.
RowDeleting
Occurs before a DataRow in the DataTable is marked as Deleted.
TableCleared
Occurs after a call to the Clear method of the DataTable has successfully cleared every DataRow.
TableClearing
Occurs after the Clear method is called but before the Clear operation begins.
TableNewRow
Occurs after a new DataRow is created by a call to the NewRow method of the DataTable.
Disposed
Occurs when the DataTable is Disposed. Inherited from MarshalByValueComponent.

Most operations that add or delete rows do not raise the ColumnChanged and ColumnChanging events. However, the ReadXml method does raise ColumnChanged and ColumnChanging events, unless the XmlReadMode is set to DiffGram or is set to Auto when the XML document being read is a DiffGram.
Additional Related Events
________________________________________
The Constraints property holds a ConstraintCollection instance. The ConstraintCollection class exposes a CollectionChanged event. This event fires when a constraint is added, modified, or removed from the ConstraintCollection.
The Columns property holds a DataColumnCollection instance. The DataColumnCollection class exposes a CollectionChanged event. This event fires when a DataColumn is added, modified, or removed from the DataColumnCollection. Modifications that cause the event to fire include changes to the name, type, expression or ordinal position of a column.
The Tables property of a DataSet holds a DataTableCollection instance. The DataTableCollection class exposes both a CollectionChanged and a CollectionChanging event. These events fire when a DataTable is added to or removed from the DataSet.
Changes to DataRows can also trigger events for an associated DataView. The DataView class exposes a ListChanged event that fires when a DataColumn value changes or when the composition or sort order of the view changes. The DataRowView class exposes a PropertyChanged event that fires when an associated DataColumn value changes.
Sequence of Operations:
Here is the sequence of operations that occur when a DataRow is added, modified, or deleted:
1. Create the proposed record and apply any changes.
2. Check constraints for non-expression columns.
3. Raise the RowChanging or RowDeleting events as applicable.
4. Set the proposed record to be the current record.
5. Update any associated indexes.
6. Raise ListChanged events for associated DataView objects and PropertyChanged events for associated DataRowView objects.
7. Evaluate all expression columns, but delay checking any constraints on these columns.
8. Raise ListChanged events for associated DataView objects and PropertyChanged events for associated DataRowView objects affected by the expression column evaluations.
9. Raise RowChanged or RowDeleted events as applicable.
10. Check constraints on expression columns.


Example
The following example demonstrates how to create event handlers for the RowChanged, RowChanging, RowDeleted, RowDeleting, ColumnChanged, ColumnChanging, TableNewRow, TableCleared, and TableClearing events. Each event handler displays output in the console window when it is fired.
static void DataTableEvents()
{
DataTable table = new DataTable("Customers");
// Add two columns, id and name.
table.Columns.Add("id", typeof(int));
table.Columns.Add("name", typeof(string));

// Set the primary key.
table.Columns["id"].Unique = true;
table.PrimaryKey = new DataColumn[] { table.Columns["id"] };

// Add a RowChanged event handler.
table.RowChanged += new DataRowChangeEventHandler(Row_Changed);

// Add a RowChanging event handler.
table.RowChanging += new DataRowChangeEventHandler(Row_Changing);

// Add a RowDeleted event handler.
table.RowDeleted += new DataRowChangeEventHandler(Row_Deleted);

// Add a RowDeleting event handler.
table.RowDeleting += new DataRowChangeEventHandler(Row_Deleting);

// Add a ColumnChanged event handler.
table.ColumnChanged += new
DataColumnChangeEventHandler(Column_Changed);

// Add a ColumnChanging event handler.
table.ColumnChanging += new
DataColumnChangeEventHandler(Column_Changing);

// Add a TableNewRow event handler.
table.TableNewRow += new
DataTableNewRowEventHandler(Table_NewRow);

// Add a TableCleared event handler.
table.TableCleared += new
DataTableClearEventHandler(Table_Cleared);

// Add a TableClearing event handler.
table.TableClearing += new
DataTableClearEventHandler(Table_Clearing);

// Add a customer.
DataRow row = table.NewRow();
row["id"] = 1;
row["name"] = "Customer1";
table.Rows.Add(row);

table.AcceptChanges();

// Change the customer name.
table.Rows[0]["name"] = "ChangedCustomer1";

// Delete the row.
table.Rows[0].Delete();

// Clear the table.
table.Clear();
}


private static void Row_Changed(object sender, DataRowChangeEventArgs e)
{
Console.WriteLine("Row_Changed Event: name={0}; action={1}",
e.Row["name"], e.Action);
}

private static void Row_Changing(object sender, DataRowChangeEventArgs e)
{
Console.WriteLine("Row_Changing Event: name={0}; action={1}",
e.Row["name"], e.Action);
}

private static void Row_Deleted(object sender, DataRowChangeEventArgs e)
{
Console.WriteLine("Row_Deleted Event: name={0}; action={1}",
e.Row["name", DataRowVersion.Original], e.Action);
}

private static void Row_Deleting(object sender,
DataRowChangeEventArgs e)
{
Console.WriteLine("Row_Deleting Event: name={0}; action={1}",
e.Row["name"], e.Action);
}

private static void Column_Changed(object sender, DataColumnChangeEventArgs e)
{
Console.WriteLine("Column_Changed Event: ColumnName={0}; RowState={1}",
e.Column.ColumnName, e.Row.RowState);
}

private static void Column_Changing(object sender, DataColumnChangeEventArgs e)
{
Console.WriteLine("Column_Changing Event: ColumnName={0}; RowState={1}",
e.Column.ColumnName, e.Row.RowState);
}

private static void Table_NewRow(object sender,
DataTableNewRowEventArgs e)
{
Console.WriteLine("Table_NewRow Event: RowState={0}",
e.Row.RowState.ToString());
}

private static void Table_Cleared(object sender, DataTableClearEventArgs e)
{
Console.WriteLine("Table_Cleared Event: TableName={0}; Rows={1}",
e.TableName, e.Table.Rows.Count.ToString());
}

private static void Table_Clearing(object sender, DataTableClearEventArgs e)
{
Console.WriteLine("Table_Clearing Event: TableName={0}; Rows={1}",
e.TableName, e.Table.Rows.Count.ToString());
}

Some Illustrations of working with Datatable:
The following example creates two DataTable objects and one DataRelation object, and adds the new objects to a DataSet. The tables are then displayed in a DataGridView control.
// Put the next line into the Declarations section.
private System.Data.DataSet dataSet;

private void MakeDataTables()
{
// Run all of the functions.
MakeParentTable();
MakeChildTable();
MakeDataRelation();
BindToDataGrid();
}

private void MakeParentTable()
{
// Create a new DataTable.
System.Data.DataTable table = new DataTable("ParentTable");
// Declare variables for DataColumn and DataRow objects.
DataColumn column;
DataRow row;

// Create new DataColumn, set DataType,
// ColumnName and add to DataTable.
column = new DataColumn();
column.DataType = System.Type.GetType("System.Int32");
column.ColumnName = "id";
column.ReadOnly = true;
column.Unique = true;
// Add the Column to the DataColumnCollection.
table.Columns.Add(column);

// Create second column.
column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "ParentItem";
column.AutoIncrement = false;
column.Caption = "ParentItem";
column.ReadOnly = false;
column.Unique = false;
// Add the column to the table.
table.Columns.Add(column);

// Make the ID column the primary key column.
DataColumn[] PrimaryKeyColumns = new DataColumn[1];
PrimaryKeyColumns[0] = table.Columns["id"];
table.PrimaryKey = PrimaryKeyColumns;

// Instantiate the DataSet variable.
dataSet = new DataSet();
// Add the new DataTable to the DataSet.
dataSet.Tables.Add(table);

// Create three new DataRow objects and add
// them to the DataTable
for (int i = 0; i<= 2; i++)
{
row = table.NewRow();
row["id"] = i;
row["ParentItem"] = "ParentItem " + i;
table.Rows.Add(row);
}
}

private void MakeChildTable()
{
// Create a new DataTable.
DataTable table = new DataTable("childTable");
DataColumn column;
DataRow row;

// Create first column and add to the DataTable.
column = new DataColumn();
column.DataType= System.Type.GetType("System.Int32");
column.ColumnName = "ChildID";
column.AutoIncrement = true;
column.Caption = "ID";
column.ReadOnly = true;
column.Unique = true;

// Add the column to the DataColumnCollection.
table.Columns.Add(column);

// Create second column.
column = new DataColumn();
column.DataType= System.Type.GetType("System.String");
column.ColumnName = "ChildItem";
column.AutoIncrement = false;
column.Caption = "ChildItem";
column.ReadOnly = false;
column.Unique = false;
table.Columns.Add(column);

// Create third column.
column = new DataColumn();
column.DataType= System.Type.GetType("System.Int32");
column.ColumnName = "ParentID";
column.AutoIncrement = false;
column.Caption = "ParentID";
column.ReadOnly = false;
column.Unique = false;
table.Columns.Add(column);

dataSet.Tables.Add(table);

// Create three sets of DataRow objects,
// five rows each, and add to DataTable.
for(int i = 0; i <= 4; i ++)
{
row = table.NewRow();
row["childID"] = i;
row["ChildItem"] = "Item " + i;
row["ParentID"] = 0 ;
table.Rows.Add(row);
}
for(int i = 0; i <= 4; i ++)
{
row = table.NewRow();
row["childID"] = i + 5;
row["ChildItem"] = "Item " + i;
row["ParentID"] = 1 ;
table.Rows.Add(row);
}
for(int i = 0; i <= 4; i ++)
{
row = table.NewRow();
row["childID"] = i + 10;
row["ChildItem"] = "Item " + i;
row["ParentID"] = 2 ;
table.Rows.Add(row);
}
}

private void MakeDataRelation()
{
// DataRelation requires two DataColumn
// (parent and child) and a name.
DataColumn parentColumn =
dataSet.Tables["ParentTable"].Columns["id"];
DataColumn childColumn =
dataSet.Tables["ChildTable"].Columns["ParentID"];
DataRelation relation = new
DataRelation("parent2Child", parentColumn, childColumn);
dataSet.Tables["ChildTable"].ParentRelations.Add(relation);
}

private void BindToDataGrid()
{
// Instruct the DataGrid to bind to the DataSet, with the
// ParentTable as the topmost DataTable.
dataGrid1.SetDataBinding(dataSet,"ParentTable");
}

Overview of ADO.NET:

Overview of ADO.NET:
Most applications need to interact in stored data of some form as well as store user data for further retrieval. This data can be in a normal file like spreadsheet, plain text,xml files or in dedicated application to store data i.e. RDMS of some sort. RDMS system also has vendor specific implementation of retrieving data programmatically as they are designed to work with all mainstream programming languages this is pain on programmers part to work with these varied data stores as for each one programmer need to master its own way of interacting with these data store.
What is ADO.NET: ADO.NET is object-oriented set of libraries that provides consistent access to local or remote data in data sources like RDMS (Oracle, MSSQL, MYSQL, SYBASE etc.), XML files, text files or spreadsheet.


The ADO.NET classes are found in System.Data.dll, and are integrated with the XML classes found in System.Xml.dll. When compiling code that uses the System.Data namespace, reference both System.Data.dll and System.Xml.dll.
ADO.NET is broken down into 3 namespaces in the .NET framework. Namespaces are used to organize components into groups based on organizational and logical reasons.


Evolution of ADO.NET
The first data access model, DAO (data access model) was created for local databases with the built-in Jet engine which had performance and functionality issues. Next came RDO (Remote Data Object) and ADO (Active Data Object) which were designed for Client Server architectures but, soon ADO took over RDO. ADO was a good architecture but as the language changes so is the technology. With ADO, all the data is contained in a recordset object which had problems when implemented on the network and penetrating firewalls. ADO was a connected data access, which means that when a connection to the database is established the connection remains open until the application is closed. Leaving the connection open for the lifetime of the application raises concerns about database security and network traffic. Also, as databases are becoming increasingly important and as they are serving more people, a connected data access model makes us think about its productivity. For example, an application with connected data access may do well when connected to two clients, the same may do poorly when connected to 10 and might be unusable when connected to 100 or more. Also, open database connections use system resources to a maximum extent making the system performance less effective.

Why ADO.NET?
To cope up with some of the problems mentioned above, ADO .NET came into existence. ADO .NET addresses the above mentioned problems by maintaining a disconnected database access model which means, when an application interacts with the database, the connection is opened to serve the request of the application and is closed as soon as the request is completed. Likewise, if a database is Updated, the connection is opened long enough to complete the Update operation and is closed. By keeping connections open for only a minimum period of time, ADO .NET conserves system resources and provides maximum security for databases and also has less impact on system performance. Also, ADO .NET when interacting with the database uses XML and converts all the data into XML format for database related operations making them more efficient.
In short Goals of ADO.NET Architecture(w.r.t. DAO,RDO,ADO ) are:
• XML integration
• Disconnected data model
• Scalability


ADO.NET is broken down into 3 namespaces in the .NET framework. Namespaces are used to organize components into groups based on organizational and logical reasons.



XML and ADO.NET
ADO.NET leverages the power of XML to provide disconnected access to data. ADO.NET was designed hand-in-hand with the XML classes in the .NET Framework — both are components of a single architecture.
ADO.NET and the XML classes in the .NET Framework converge in the DataSet object. The DataSet can be populated with data from an XML source, whether it is a file or an XML stream. The DataSet can be written as World Wide Web Consortium (W3C) compliant XML, including its schema as XML Schema definition language (XSD) schema, regardless of the source of the data in the DataSet. Because the native serialization format of the DataSet is XML, it is an excellent medium for moving data between tiers making the DataSet an optimal choice for remoting data and schema context to and from an XML Web service.
The Dataset can also be synchronized with an XmlDataDocument to provide relational and hierarchical access to data in real time

The ADO.NET Data Architecture
Data Access in ADO.NET relies on two components: DataSet and Data Provider.
DataSet
The dataset is a disconnected, in-memory representation of data. It can be considered as a local copy of the relevant portions of the database. The DataSet is persisted in memory and the data in it can be manipulated and updated independent of the database. When the use of this DataSet is finished, changes can be made back to the central database for updating. The data in DataSet can be loaded from any valid data source like Microsoft SQL server database, an Oracle database or from a Microsoft Access database.DataSets only hold data and do not interact with a Data Source
The DataSet contains a collection of one or more DataTable objects made up of rows and columns of data, as well as primary key, foreign key, constraint, and relation information about the data in the DataTable objects.
Data Provider
The Data Provider is responsible for providing and maintaining the connection to the database. A Data Provider is a set of related components that work together to provide data in an efficient and performance driven manner. The .NET Framework currently comes with two DataProviders: the SQL Data Provider which is designed only to work with Microsoft's SQL Server 7.0 or later and the OleDb DataProvider which allows us to connect to other types of databases like Access and Oracle.
In ADO an attempt was made to create as generic methods as possible, whereas with ADO.NET, the optimized data providers for specific database products are created e.g.Mssql server. Optimized ADO.NET provider just like for MSSQL server are available for other database like Oracle & Mysql etc. from there vendors or by third party providers.

Provider Name API prefix Data Source Description
ODBC Data Provider Odbc Data Sources with an ODBC interface. Normally older data bases.
OleDb Data Provider OleDb Data Sources that expose an OleDb interface, i.e. Access or Excel.
Oracle Data Provider Oracle For Oracle Databases.
SQL Data Provider Sql For interacting with Microsoft SQL Server.
Borland Data Provider Bdp Generic access to many databases such as Interbase, SQL Server, IBM DB2, and Oracle.



Each Data Provider consists of the following component classes:
a) Connection: The Connection object which provides a connection to the database.
Microsoft Visual Studio .NET provides two types of Connection classes: the SqlConnection object, which is designed specifically to connect to Microsoft SQL Server 7.0 or later, and the OleDbConnection object, which can provide connections to a wide range of database types like Microsoft Access and Oracle. The Connection object contains all of the information required to open a connection to the database.
b) Command: The Command object which is used to execute a command.
The Command object is represented by two corresponding classes: SqlCommand and OleDbCommand. Command objects are execute commands to a database across a data connection. The Command objects can be used to execute stored procedures on the database, SQL commands, or return complete tables directly. Command objects provide three methods that are used to execute commands on the database:
1)ExecuteNonQuery: Executes commands that have no return values such as INSERT,
UPDATE or DELETE
2) ExecuteScalar: Returns a single value from a database query

3) ExecuteReader: Returns a result set by way of a DataReader object

c) DataReader: The DataReader object which provides a forward-only, read only, connected recordset.
Unlike other components of the Data Provider, DataReader objects cannot be directly instantiated. Rather, the DataReader is returned as the result of the Command object's ExecuteReader method.
The SqlCommand.ExecuteReader method returns a SqlDataReader object, and the OleDbCommand.ExecuteReader method returns an OleDbDataReader object. The DataReader can provide rows of data directly to application logic when you do not need to keep the data cached in memory. Because only one row is in memory at a time, the DataReader provides the lowest overhead in terms of system performance but requires the exclusive use of an open Connection object for the lifetime of the DataReader.
d) DataAdapter: The DataAdapter object which populates a disconnected DataSet with data and performs update
DataAdapter is essentially the middleman facilitating all communication between the database and a DataSet. The DataAdapter is used either to fill a DataTable or DataSet with data from the database with it's Fill method. After the memory-resident data has been manipulated, the DataAdapter can commit the changes to the database by calling the Update method. The
DataAdapter provides four properties that represent database commands:
1) SelectCommand
2) InsertCommand
3) DeleteCommand
4) UpdateCommand
e) When the Update method is called, changes in the DataSet are copied back to the database and the appropriate InsertCommand, DeleteCommand, or UpdateCommand is executed.

The DataAdapter uses Command objects to execute SQL commands at the data source to both load the DataSet with data, and reconcile changes made to the data in the DataSet back to the data source.
The following diagram illustrates the components of ADO.NET architecture.

Choosing between DataReader or a DataSet
When deciding whether your application should use a DataReader or a DataSet, you should consider the type of functionality that your application requires.
Use a DataSet to do the following:
• Remote data between tiers or from an XML Web service.
• Interact with data dynamically such as binding to a Windows Forms control or combining and relating data from multiple sources.
• Cache data locally in your application.
• Provide a hierarchical XML view of relational data and use tools like an XSL Transformation or an XML Path Language (XPath) Query on your data.
• Perform extensive processing on data without requiring an open connection to the data source, which frees the connection to be used by other clients.
If you do not require the functionality provided by the DataSet, you can improve the performance of your application by using the DataReader to return your data in a forward-only read-only fashion. Although the DataAdapter uses the DataReader to fill the contents of a DataSet, by using the DataReader you can receive performance gains because you will save memory that would be consumed by the DataSet, as well as saving the processing required to create and fill the contents of the DataSet.

Tuesday, June 22, 2010

Enum in C#

Enum in c#:
The enum keyword is used to declare an enumeration, a distinct type consisting of a set of named constants called the enumerator list. Every enumeration type has an underlying type, which can be any integral type except char. The default underlying type of the enumeration elements is int. By default, the first enumerator has the value 0, and the value of each successive enumerator is increased by 1. For example:


enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri};
In this enumeration, Sat is 0, Sun is 1, Mon is 2, and so forth. Enumerators can have initializers to override the default values. For example:


enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};
In this enumeration, the sequence of elements is forced to start from 1 instead of 0.
A variable of type Days can be assigned any value in the range of the underlying type; the values are not limited to the named constants.
The default value of an enum E is the value produced by the expression (E)0.
Note
An enumerator may not contain white space in its name.
The underlying type specifies how much storage is allocated for each enumerator. However, an explicit cast is needed to convert from enum type to an integral type. For example, the following statement assigns the enumerator Sun to a variable of the type int using a cast to convert from enum to int:

int x = (int)Days.Sun;
When you apply System.FlagsAttribute to an enumeration that contains some elements combined with a bitwise OR operation, you will notice that the attribute affects the behavior of the enum when used with some tools. You can notice these changes when using tools such as the Console class methods, the Expression Evaluator, and so forth. (See example 3).
Robust Programming
________________________________________
Assigning additional values new versions of enums, or changing the values of the enum members in a new version, can cause problems for dependant source code. It is often the case that enum values are used in switch statements, and if additional elements have been added to the enum type, the test for default values can return true unexpectedly.
If other developers will be using your code, it is important to provide guidelines on how their code should react if new elements are added to any enum types.
Example
________________________________________
In this example, an enumeration, Days, is declared. Two enumerators are explicitly converted to integer and assigned to integer variables.

// keyword_enum.cs
// enum initialization:
using System;
public class EnumTest
{
enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

static void Main()
{
int x = (int)Days.Sun;
int y = (int)Days.Fri;
Console.WriteLine("Sun = {0}", x);
Console.WriteLine("Fri = {0}", y);
}
}
Output
Sun = 2
Fri = 7
In this example, the base-type option is used to declare an enum whose members are of the type long. Notice that even though the underlying type of the enumeration is long, the enumeration members must still be explicitly converted to type long using a cast.

// keyword_enum2.cs
// Using long enumerators
using System;
public class EnumTest
{
enum Range :long {Max = 2147483648L, Min = 255L};
static void Main()
{
long x = (long)Range.Max;
long y = (long)Range.Min;
Console.WriteLine("Max = {0}", x);
Console.WriteLine("Min = {0}", y);
}
}
Output
Max = 2147483648
Min = 255
The following code example illustrates the use and effect of the System.FlagsAttribute attribute on an enum declaration.

// enumFlags.cs
// Using the FlagsAttribute on enumerations.
using System;

[Flags]
public enum CarOptions
{
SunRoof = 0x01,
Spoiler = 0x02,
FogLights = 0x04,
TintedWindows = 0x08,
}

class FlagTest
{
static void Main()
{
CarOptions options = CarOptions.SunRoof | CarOptions.FogLights;
Console.WriteLine(options);
Console.WriteLine((int)options);
}
}
Output
SunRoof, FogLights
5
Comments
________________________________________
Notice that if you remove the initializer from Sat=1, the result will be:

Sun = 1
Fri = 6
Comments
________________________________________
Notice that if you remove FlagsAttribute, the example will output the following:
5
5
Further Examples:
public enum Orientation:int {Left=0,Right=1 };
private enum BoolMsg : byte { Zero = 1, One = 3 };
protected enum Orientation1 : long { Left = 0, Right = 1 };
internal enum BoolMsg1 : short { Zero = 1, One = 3 };


protected internal enum Orientation12 : uint { Left = 0, Right = 1 };
public enum Orientation11 : ulong { Left = 0, Right = 1 };
private enum BoolMsg11 : ushort { Zero = 1, One = 3 };
protected enum BoolMsg13 : sbyte { Zero = 1, One = 3 };

Delegates in .NET:


A delegate in C# is similar to a function pointer in C or C++. According to Microsoft A delegate is a type that references a method. Once a delegate is assigned a method, it behaves exactly like that method. The delegate method can be used like any other method, with parameters and a return value, as in this
 example:

public delegate int PerformCalculation(int x, int y);

Any method that matches the delegate's signature, which consists of the return type and parameters, can be assigned to the delegate. This makes is possible to programmatically change method calls, and also plug new code into existing classes. As long as you know the delegate's signature, you can assign your own delegated method.

This ability to refer to a method as a parameter makes delegates ideal for defining callback methods.
For example, a sort algorithm could be passed a reference to the method that compares two objects.
Delegate types are derived from the Delegate/System.Delegate class in the .NET Framework.

   Delegate types are sealed—they cannot be derived from— and it is not possible to derive custom classes from Delegate. Because the instantiated delegate is an object, it can be passed as a parameter, or assigned to a property. This allows a method to accept a delegate as a parameter, and call the delegate at some later time. This is known as an asynchronous callback, and is a common method of notifying a caller when a long process has completed. When a delegate is used in this fashion, the code using the delegate does not need any knowledge of the implementation of the method being used.

Delegates Overview
Delegates have the following properties:

1) Delegates are similar to C++ function pointers, but are type safe:
     Consider the following delegate declaration:
  
     Code:
          public delegate void MyDelegate (int myParam);

         This definition means that when one tries to pass a method to delegate instance, it MUST point to a  
         method that has the signature of one integer parameter, and no return  value.
             When using function pointers(c/c++), it is just a pointer, and I can cast it to anything, which could 
         lead to problems if I cast it to the wrong thing.

2) Delegates allow methods to be passed as parameters: 

Consider code bellow here we assigning a function which is to be passed as parameter to delegate then passing that delegate instance as an parameter to a method that invokes it.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication2
{
/*declaration of delegate*/
public delegate void methodTranspoter(string str);
class Program
{
static void Main(string[] args)
{
/*Instantiation of delegate*/
methodTranspoter inst = new methodTranspoter(method1);
CallTheMethod objct = new CallTheMethod();

/*
* passing function as parametre using delegate here,we passed delegate “inst” which ultimately point to a function
As parametre that get invoked
*/
objct.methodInvoker(inst, "parametreVal");
Console.ReadKey();

}
static void method1(string parm)
{
Console.WriteLine("I m In method1 and \"{0}\" is parametre",parm);
}
static void method2(string parm)
{
Console.WriteLine("I m In method2 and \"{0}\" is parametre", parm);
}
}
class CallTheMethod
{
/*Invoke Method passed as parametre*/
public void methodInvoker(methodTranspoter func,string parm)
{
func(parm);
}
}
}


3) Delegates can be used to define callback methods: 

The Callback function is normally implemented in the Business tier of a 3-tier architecture (of course there are exceptions to that). Once the Business Logic component implements a particular functionality, say a complex arithmetic calculation, it informs a client that the job was performed successfully. In order to inform the client, it needs an address of the function\method implemented in the client. The address of a function is just a memory address; this address does not carry any information such as number of parameters, data type of each parameter and its return value. Since the Callback function does not have any idea of the Method Signature it is going to call, in short, it is not Type Safe.

    However, Delegates provide the feature of Callback functions in a safe way. Taking the previous example, if the Business Logic finds that the function signature implemented by the Client differs in terms of number of parameters or parameter types or return values as opposed to the method signature, it is going to safely raise an error. 

For those familiar with design patterns, this is the observer design pattern.

Consider Following Code that illustrate the above concept:

Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication4
{
/*declaring delegate*/
public delegate void MakeDelegate (string PhoneNo);
class Program
{
static void Main(string[] args)
{
Customer objCustomer = new Customer();
/*instatiating delegate*/
MakeDelegate objDelegate = new MakeDelegate(NotifyClient);

/*reading user input*/
Console.WriteLine("Enter First Name:");
string FName = Console.ReadLine();

Console.WriteLine("Enter Last name:");
string LName = Console.ReadLine();

Console.WriteLine("Enter Phone Number:");
string PhoneNumber = Console.ReadLine();

objCustomer.FirstName =FName;
objCustomer.LastName = LName;

/*passing callback method as delegate instance
* if certain condition meet*/
objCustomer.ValidateCustomer(objDelegate, PhoneNumber);
Console.ReadKey();

}
/*method ultimately invoked on callback*/
private static void NotifyClient(string PhoneNo){
Console.WriteLine("This Customer is Eligible for 10% Discount");
}
}

class Customer{
public string FirstName;
public string LastName;
/*evaluate condition if required calls callback method */
public void ValidateCustomer (MakeDelegate objDelegate,String PhoneNo){
if(PhoneNo.StartsWith ("22")){
objDelegate.Invoke(PhoneNo);
}
/*if condition doesn't evaluate to be true no callback
* method called flow of execution doest get altered at all*/
}
}



}

4) Delegates can be chained together; 

For example, multiple methods can be called on a single event:

Consider following code sample DelegateSample is having 4 different notification methods of same signature.In Main() we created 4 delegateObj for each of the above method
We want all remaining 3 methods be chained to console deletegate instatance to do so we called

/*chaining is done here*/
_notifyConsole += _notifyEventLog;
_notifyConsole += _notifyDataBase;
_notifyConsole += _notifySound;

We changed our mind and removed sound device delegate instance from
Chain as follows

/*removing from chain*/
_notifyConsole -= _notifySound;

Now call to console notify function using corresponding delegate instance with

_notifyConsole("Hello");

This call lead to call to other 2 notify functions(db & eventlog) as they are chained but not sound notify function as it is not chained.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication5
{
//This is our delegate type
public delegate void Notify(string message);
class DelegateSample
{

public void NotifyToConsole(string _notificationMessage)
{
Console.WriteLine("Notifying Console " + _notificationMessage);
}

public void NotifyToEventLog(string _notificationMessage)
{
//Write an Entry in the Event Log
Console.WriteLine("Entry has been written in the event log as " + _notificationMessage);
}

public void NotifyDatabase(string _notificationMessage)
{
//Log in some database table
Console.WriteLine("Entry has been written to the DB as " + _notificationMessage);
}

public void NotifySound(string _notificationMessage)
{
//Log in some database table
Console.WriteLine("Entry has been written to the SOUND DEVICE as " + _notificationMessage);
}
}
class Program
{
static void Main(string[] args)
{
Notify _notifyConsole = new Notify(new DelegateSample().NotifyToConsole);
Notify _notifyEventLog = new Notify(new DelegateSample().NotifyToEventLog);
Notify _notifyDataBase = new Notify(new DelegateSample().NotifyDatabase);
Notify _notifySound = new Notify(new DelegateSample().NotifySound);

/*chaining is done here*/
_notifyConsole += _notifyEventLog;
_notifyConsole += _notifyDataBase;
_notifyConsole += _notifySound;

/*removing from chain*/
_notifyConsole -= _notifySound;

/*call to first one in chain will fire other two also*/
_notifyConsole("Hello");
Console.ReadKey();
}
}
}


5) Methods don't need to match the delegate signature exactly. 

For more information:Covariance and contravariance provide a degree of flexibility when matching method signatures with delegate types.

Covariance permits a method to have a more derived return type than what is defined in the delegate.
Contravariance permits a method with parameter types that are less derived than in the delegate type.

a) Covariance: 

This example bellow demonstrates how delegates can be used with methods that have return types that are derived from the return type in the delegate signature. The data type returned by SecondHandler is of type Dogs, which derives from the Mammals type that is defined in the delegate.

CODE: 

class Mammals
{
}

class Dogs : Mammals
{
}

class Program
{
// Define the delegate.
public delegate Mammals HandlerMethod();

public static Mammals FirstHandler()
{
return null;
}

public static Dogs SecondHandler()
{
return null;
}

static void Main()
{
HandlerMethod handler1 = FirstHandler;

// Covariance allows this delegate.
HandlerMethod handler2 = SecondHandler;
}
}

b) Contravariance :

This example bellow demonstrates how delegates can be used with methods that have parameters of a type that are base types of the delegate signature parameter type. With contravariance, you can now use one event handler in places where, previously, you would have to use separate handlers.

For example, you can now create an event handler that accepts an EventArgs input parameter and use it with the Button.MouseClick event that sends a MouseEventArgs type as a parameter, and also with TextBox.KeyDown event that sends a KeyEventArgs parameter.

CODE:

system.DateTime lastActivity;
public Form1()
{
InitializeComponent();

lastActivity = new System.DateTime();
this.textBox1.KeyDown += this.MultiHandler; //works with KeyEventArgs
this.button1.MouseClick += this.MultiHandler; //works with MouseEventArgs

}

// Event hander for any event with an EventArgs or
// derived class in the second parameter
private void MultiHandler(object sender, System.EventArgs e)
{
lastActivity = System.DateTime.Now;
}



6|) C# version 2.0 introduces the concept of Anonymous Methods, which permit code blocks to be passed as parameters in place of a separately defined method.:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication7
{
// Create a delegate instance
delegate void Del(int x);
class Program
{
static void Main(string[] args)
{
// Instantiate the delegate using an anonymous method
Del d = delegate(int k) 
{
   Console.WriteLine("{0}",k*k);
};
//Call to method defined anonymously above
d(2);

Console.ReadKey();
}
}
}


Delegates are of two types. 

1) Single-cast delegates /Uni-Cast Delegate
2) Multi-cast delegates 

A Single-cast delegate is one that can refer to a single method whereas a Multi-cast delegate can refer to and eventually fire off multiple methods that have the same signature.

A delegate is either public or internal.If no specifier is included in its signature then by default it is considered as internal

Uni-cast Delegate/ Single-cast Delegate:


Following Example illustrate Uni-cast Delegate in CSharp

using System;

namespace Delegates
{
public delegate int MyDelegate(int x, int y);

public class MyClass
{
public static int Add(int x, int y)
{
return x + y;

}

public static int Multiply(int x, int y)
{
return x * y;
}
}
class Program
{
static void Main(string[] args)
{
//create an instance of MyDelegate
MyDelegate delegate1 = new MyDelegate(MyClass.Add);

//Invoke Add method using the delegate;
int AddResult = delegate1(4, 6);
Console.WriteLine("4 + 6 = {0}\n", AddResult);

//create an instance of MyDelegate
MyDelegate delegate2 = new MyDelegate(MyClass.Multiply);

//Invoke Multiply method using the delegate;
int MultiplyResult = delegate2(4, 6);
Console.WriteLine("4 * 6 = {0}\n", MultiplyResult);

}
}
}

Multicast delegate:

 During Multicast Delegate's, a delegate object can maintain a list of methods to call, rather than a single method.
    If you want to add a method to the invocation list of a delegate object , you simply make use of the overloaded += operator, and if you want to remove a method from the invocation list you make use of the overloaded operator -=.

If you want to create a multicast delegate with return type you will get the return type of the last method in the invocation list. 

using System;

namespace Delegates
{
public delegate void MyMultiDelegate(int x, int y);

public class MyClass
{
public static void Add(int x, int y)
{
Console.WriteLine("4 + 6 = {0}\n", x + y);

}

public static void Multiply(int x, int y)
{
Console.WriteLine("4 * 6 = {0}\n", x * y );
}
}
class Program
{
static void Main(string[] args)
{
//create an instance of MyDelegate that points
// to the Add method.
MyMultiDelegate Multidelegate = new MyMultiDelegate(MyClass.Add);

//using the same instance of MulticastDelegate
//to call MyClass.Multibly() by adding it to it's
//invocation list.
Multidelegate += new MyMultiDelegate(MyClass.Multiply);

// This call both Add and Multiply methods
Multidelegate(4, 6);

//removing the Add() method from the invocation list
Multidelegate -= new MyMultiDelegate(MyClass.Add);

// This call onlly Multiply method
Multidelegate(4, 6);

}
}
}


Using delegate to call Static method:

class Program
{
public delegate void dels(string str);
static void Main(string[] args)
{
dels delsObj = new dels(printMsg);
delsObj("I m in simple delegate");
Console.ReadKey();
}

static void printMsg(string msg)
{
Console.WriteLine(msg);
}
}

Using Delegate to call non-Static Method:

Here delegate instance delObj is calling non-static method of Test Class by creating an object of Test class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication6
{
public delegate void mydel(string str);
class Program
{
static void Main(string[] args)
{
Test TestObj = new Test();
mydel delObj = new mydel(TestObj.consoleWrite);
delObj("hi");
Console.ReadKey();
}
}
class Test
{
public void consoleWrite(string str){
Console.WriteLine(str);
}
}
}

Monday, June 21, 2010

Access Modifiers in C#


All types and type members have an accessibility level, which controls whether they can be used from other code in our assembly or other assemblies.

    In C# there are 5 different types of Access Modifiers. You can use any of the 5 access modifiers to specify the accessibility of a type or member when you declare it:

1) public: 
     The type or member can be accessed by any other code in the same
     assembly or another assembly that references it. 
 2) private: 
     The type or member can only be accessed by code in the same class or struct. 
3) protected
     The type or member can only be accessed by code in the same class or
        struct, or in a derived class.  
4) internal: 
     The type or member can be accessed by any code in the same assembly,
        but not from another assembly.
5) protected internal: 
      The type or member can be accessed by any code in the same assembly, or by any derived class in 
      another assembly.

The following examples demonstrate how to specify access modifiers on a type and member:

public class Bicycle
{
    public void Pedal() { }
}
Not all access modifiers can be used by all types or members in all contexts, and in some cases the accessibility of a type member is constrained by the accessibility of its containing type.

Class and Struct Accessibility:

    Classes and structs that are declared directly within a namespace (in other words, they are not nested within other classes or structs) can be either public or internal. Internal is the default if no access modifier is specified.

     Nested classes and structs may also be declared as private. Private nested types are not accessible from outside the containing type. Derived classes cannot have greater accessibility than their base types.
    In other words, you cannot have a public class B that derives from an internal class A. If this were allowed, it would have the effect of making A public, because all protected or internal members of A are accessible from the derived class.
i.e. if you write following in a namespace it will not compile

namespace ConsoleApplication1
{

 internal class A
    {
        int i = 0;
    }

    public class B:A
    {
        int i = 0;
    }

 }
Here is an essence of error that flashed by compiler "Inconsistent accessibility: base class A is less accessible than class B  "

  You can enable specific other assemblies to access your internal types by using the InternalsVisibleToAttribute.
 Here we need to add line below on one of to the assembly
 [assembly:InternalsVisibleToAttribute("OtherAssemblyName")]
Class and Struct Member Accessibility:

    Class members (including nested classes and structs) can be declared with any of the five types of access.

Struct members cannot be declared as protected because structs do not support inheritance.
The accessibility of a member can never be greater than the accessibility of its containing type. 

For example:
 A public method declared in an internal type has only internal accessibility.
    When a member of a class or struct is a property, field, method, event, or delegate, and that member either is a type or has a type as a parameter or return value, the accessibility of the member cannot be greater than the type.

For example:
One cannot have a public method M that returns a class C unless C is also public. Likewise, you cannot have a protected property of type A if A is declared as private.

Point To Remember:
 1) User-defined operators must always be declared as public. 
 2) Destructors cannot have accessibility modifiers.

Example of using Access Modifier:

// public class:
public class Tricycle
{
    // protected method:
     protected void Pedal() { }

     // private field:
     private int wheels = 3;

   // protected internal property:
   protected internal int Wheels
  {
    get { return wheels; }
   }

}

    Interfaces declared directly within a namespace can be declared as public or internal and, like classes and structs, interfaces default to internal access. Interface members are always public because the purpose of an interface is to enable other types to access a class or struct. No access modifiers can be applied to interface members.
   Enumeration members are always public, and no access modifiers can be applied to enumeration members.
By default, delegates have internal access.

Note:
1) Access modifier cannot be used for all types e.g. one need not specify the access modifier for interface member, enumeration members & destructor.

2) One cannot use any other access modifier to interface other than internal or public .so interface can never get private access modifier.
 Default Access Modifiers:

  When we create a type or type members without assigning any access modifier it get the default access modifier specific to that context.

  When we define a data type of some kind (class, struct, enum, etc.) but do not specify an access modifier, it will get default access modifier to be internal.

    If we doesn’t specify access modifier for type members (e.g class or structure variable, methods) then by default become private.
  Default Access modifier for interface is internal.

References:http://www.davidarno.org/c-howtos/friend-assemblies-accessing-internal-classes-externally/