Welcome to MSDN Blogs Sign in | Join | Help

Data Access Technologies

(Data Access, XML, SSIS, SQL-CE ...)
How to convert an IEnumerable to a DataTable in the same way as we use ToList or ToArray

LINQ provides us some extension methods to convert an IEnumerable to a collection e.g. ToList(), ToArray() etc. But what if you want to convert the enumerable to a DataTable. There is no built in extension method for that currently.

                var v = (from x in collection Select x).ToDataTable();

 

But if required we can create our own extension method as shown below.

public static class Extenders

{

LINQ provides us some extension methods to convert an IEnumerable to a collection e.g. ToList(), ToArray() etc. But what if you want to convert the enumerable to a DataTable. There is no built in extension method for that currently.

                var v = (from x in collection Select x).ToDataTable();

 

But if required we can create our own extension method as shown below.

public static class Extenders

{

    public static DataTable ToDataTable<T>(this IEnumerable<T> collection, string tableName)

    {

        DataTable tbl = ToDataTable(collection);

        tbl.TableName = tableName;

        return tbl;

    }

 

    public static DataTable ToDataTable<T>(this IEnumerable<T> collection)

    {

        DataTable dt = new DataTable();

        Type t = typeof(T);

        PropertyInfo[] pia = t.GetProperties();

        //Create the columns in the DataTable

        foreach (PropertyInfo pi in pia)

        {

            dt.Columns.Add(pi.Name, pi.PropertyType);

        }

        //Populate the table

        foreach (T item in collection)

        {

            DataRow dr = dt.NewRow();

            dr.BeginEdit();

            foreach (PropertyInfo pi in pia)

            {

                dr[pi.Name] = pi.GetValue(item, null);

            }

            dr.EndEdit();

            dt.Rows.Add(dr);

        }

        return dt;

    }

}

 

Basically above code will create a DataTable with columns corresponding to the properties in the IEnumerable object. Then it will populate it with the data from that collection.

For calling it just import this namespace in your class and use it as shown below

      var v = (from x in collection Select x).ToDataTable();

 

 

Written by: Naresh Joshi (MSFT)

How to force System.Xml.Xsl.XslCompiledTransform class to not use self closing tags for empty nodes

In .NET 2.0 framework when you use System.Xml.Xsl.XslCompiledTransform class for transforming a document then we always get self closing tags for all the elements which have nothing inside their body. There is no direct way to ask it to use separate closing tags

e.g.

<BODY>

<TABLE></TABLE>

<BR></BR>

</BODY>

 

transforms as

 

<BODY>

<TABLE/>

<BR/>

</BODY>

 

This might cause problems when we are transforming a document for HTML processing as HTML doesn't identify self closing tags properly. So although <Table/> is a valid XML closed tag but HTML identifies it as starting of a table only which causes its output to be different than what we want.

The Xslcompiledtransform class uses self closing tags for all the elements irrespective of whether they are empty or not.

This didn't used to happen with System.Xml.Xsl.XslTransform class in .NET 1.1 framework. It used separate closing tags for empty elements.

 

 

One way to resolve this issue is to override the  default WriteEndElement behaviour of XmlTextWriter class using inheritence. In the overriden method we will write non self closing nodes even if the node is empty. In the Transform object we will use this derived writer object instead of the XmlTextWriter object itself.

Here are the steps.

 

·         Create a new class XmlHtmlWriter inherited from XmlTextWriter.

·         Override two functions WriteStartElement and WriteEndElement.

·         Create a list fullyClosedElements for storing names of all the tags which you want not to be self closed

·         In WriteStartElement function record the tag which is being written and in WriteEndElement check whether we need to close this element using separate closing tag or not by checking whether it exists in fullyClosedElements list or not.

·         To use separate tag for closing call WriteFullEndElement function else call base.WriteEndElement .

·         Then use an instance of this XmlHtmlWriter class in your application for output of xsl transform.

 

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Xml;

 

namespace XSLSelfClosing

{

    public class XmlHtmlWriter : XmlTextWriter

    {

        public XmlHtmlWriter(System.IO.Stream stream, Encoding en)

            : base(stream, en)

        {

 

            //Put all the elemnts for which you want self closing tags in this list.

            //Rest of the tags would be explicitely closed

            fullyClosedElements.AddRange(new string[] { "br", "hr" });

        }

 

        string openingElement = "";

        List<string> fullyClosedElements = new List<string>();

 

        public override void WriteEndElement()

        {

            if (fullyClosedElements.IndexOf(openingElement) < 0)

                WriteFullEndElement();

            else

                base.WriteEndElement();

        }

        public override void WriteStartElement(string prefix, string localName, string ns)

        {

            base.WriteStartElement(prefix, localName, ns);

            openingElement = localName;

        }

    }

}

 

Written by: Naresh Joshi (MSFT)

Hung condition on a query to SQLCLR stored procedure doing P/Invoke on a native dll

If you run into a scenario where you see your managed stored procedure from SQLCLR hung perpetually while calling a native dll method using P/Invoke, You may have to check following things.

Run following query from another connection

 select os.task_address, os.state, os.last_wait_type, clr.state, clr.forced_yield_count from sys.dm_os_workers os join sys.dm_clr_tasks clr on (os.task_address = clr.sos_task_address) where clr.type = 'E_TYPE_USER'

In last_wait_type column If you see anything except from “CLR_.....” or “SQLCLR_.....” e.g. MISCELLENOUS .

Stop your SQL Server service from Configuration manager or command prompt e.g.:

C:\>Net Stop MSSQL$SQLExpess

Start SQL Server in console mode with following command (this is just for testing, you cannot use SQL in console mode forever)

sqlservr.exe -c -iSQLExpress

Execute your SQLCLR stored procedure again. This time you might get additional interactive information from your native dll (for e.g: some error message box or popup). You would notice that as soon as you click on Ok or Cancel buttons for the message box, the query execution would be successful.

In this case the dll may have encountered a runtime exception and have tried to create UI handle for the message box control. Since SQL Service runs in windows service mode it’s not possible for SQL to create a UI handle hence the user never see the message box window. This happens because deploying native dlls in SQLCLR involves marking these dlls as unsafe. Unsafe dll are not verified by SQLCLR for all code paths at the time of deployment and its security, stability and testing is expected to be performed by user before throwing it up on SQL Server.

Although P/Invoke is not a bad thing but native assembly has to be tested before being called from SQL process. If the managed code calls unmanaged code such as P/Invoke, the calling thread will switch to pre-emptive.  This acts like an extended stored procedure.  Any UI created from SQL process is unsupported and could lead for unforeseen behaviour like this.

Only supported assemblies from SQLCLR are mentioned in this KB http://support.microsoft.com/kb/922672

 

Written by: Runeet Vashisht (MSFT)

How to find out the data-reader referencing an ADO.NET connection object to fix the error: "There is already an open DataReader associated with this Command which must be closed first"

Sometimes while executing a DB command in ADO.NET we come across below error.

There is already an open DataReader associated with this Command which must be closed first

 

This error is generated when we try to execute some query using a connection (with MARS disabled) which was earlier being used by some other component to execute some other query and that component forgot to close itself. In most of the cases this component is a DataReader. A simple piece of code to generate such an error would be

                  SqlConnection con;

        private void Form1_Load(object sender, EventArgs e)

        {

            con = new SqlConnection("data source=server;Integrated Security=SSPI;Initial Catalog=DBname");

            con.Open();

        }

        private void button1_Click(object sender, EventArgs e)

        {

            string sql = "select * from table1";

            SqlCommand cmd = new SqlCommand(sql, con);

            SqlDataReader rdr = cmd.ExecuteReader();

        }

        private void button2_Click(object sender, EventArgs e)

        {

            string sql = "select * from table2";

            SqlCommand cmd = new SqlCommand(sql, con);

            cmd.ExecuteReader();

        }

 

If we click button2 after button 1 we will get above error. In this code we can easily see which object was using that connection earlier but in complex real world code it is difficult and time consuming to do that as it is generally spread over multiple files/tiers.

 

If we really intend to get multiple  resultsets simultaneously using the same connection then we would have to enable MARS (Multiple Active Result Sets) if it is SQL 2005 or above.

 

But if that is not the case then we have to figure out the object which was not closed properly and is still referencing the connection. So to pinpoint that object we can just put the function  written below anywhere in our code and call it just before the line where the above error is generated or more preferably in the catch block used for above error. The function accepts the connection and will return the property values for all the reader/command objects still attached to that connection e.g. CommandTimeout, CommandText (sql query) etc. By searching for this query in the project we can figure out the location where the reader was left unclosed. It might be slightly difficult to do the search if the query has been built dynamically instead of a simple string literal.

 

Here is the function (Add “using System.Reflection” at the top)

 

private string GetReferencedObjects(SqlConnection con)

        {

            System.Text.StringBuilder result = new StringBuilder();

 

            Type t = con.GetType();

            object innerConnection = t.GetField("_innerConnection", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(con);

            Type tin = innerConnection.GetType();

            object rc = tin.BaseType.BaseType.GetField("_referenceCollection", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(innerConnection);

 

            if (rc == null)

                return "";

 

            int count = Convert.ToInt32(rc.GetType().GetField("_dataReaderCount", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(rc));

            object items = rc.GetType().BaseType.GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(rc);

 

            MethodInfo miGetValue = items.GetType().GetMethod("GetValue", new Type[] { typeof(int) });

 

            result.AppendFormat("<Items timestamp=\"{0}\">" + Environment.NewLine, DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToLongTimeString());

            for (int i = 0; i < count; i++)

            {

                miGetValue.Invoke(items, new object[] { i });

                object referencedObj = miGetValue.Invoke(items, new object[] { i });

                SqlDataReader rdr = referencedObj.GetType().GetProperty("Target").GetValue(referencedObj, null) as SqlDataReader;

 

                SqlCommand cmd = rdr.GetType().GetField("_command", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(rdr) as SqlCommand;

 

                PropertyInfo[] properties = cmd.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);

                result.AppendFormat("\t<Command id=\"{0}\">" + Environment.NewLine, i);

                foreach (PropertyInfo pi in properties)

                {

                    if (pi.PropertyType.IsPrimitive || pi.PropertyType == typeof(string))

                        result.AppendFormat("\t\t<{0}>{1}</{0}>" + Environment.NewLine, pi.Name, pi.GetValue(cmd, null).ToString());

 

                    if (pi.PropertyType == typeof(SqlConnection) && pi.Name == "Connection")

                    {

                        result.Append("\t\t<Connection>" + Environment.NewLine);

                        SqlConnection con1 = pi.GetValue(cmd, null) as SqlConnection;

                        PropertyInfo[] propertiesCon = con1.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);

                        result.AppendFormat("\t\t\t<State>{0}</State>" + Environment.NewLine, con1.State.ToString());

                        foreach (PropertyInfo picon in propertiesCon)

                        {

                            if (picon.PropertyType.IsPrimitive || picon.PropertyType == typeof(string))

                                result.AppendFormat("\t\t\t<{0}>{1}</{0}>" + Environment.NewLine, picon.Name, picon.GetValue(con1, null).ToString());

                        }

                        result.Append("\t\t</Connection>" + Environment.NewLine);

                    }

                }

                result.AppendFormat("\t</Command>" + Environment.NewLine);

            }

            result.AppendFormat("</Items>" + Environment.NewLine);

 

            return result.ToString();

        }

 

It can be called something like this

            try

            {

                cmd.ExecuteReader();

            }

            catch (InvalidOperationException ex)

            {

                if (ex.Message == "There is already an open DataReader associated with this Command which must be closed first")

                {

                    string s = GetReferencedObjects(con);

                    if (s != string.Empty)

                        MessageBox.Show(s);     //Or log the message somewhere

                }

            }

=A sample output would look like

<Items timestamp="12/18/2008 2:32:38 AM">

<Command id="0">

<Connection>

<State>Open</State>

<StatisticsEnabled>False</StatisticsEnabled>

<ConnectionString>data source=naresh-pc2003;Integrated Security=SSPI;Initial Catalog=Test</ConnectionString>

<ConnectionTimeout>15</ConnectionTimeout>

<Database>Test</Database>

<DataSource>naresh-pc2003</DataSource>

<PacketSize>8000</PacketSize>

<ServerVersion>09.00.3282</ServerVersion>

<WorkstationId>NARESH-PC2008</WorkstationId>

<FireInfoMessageEventOnUserErrors>False</FireInfoMessageEventOnUserErrors>

</Connection>

<NotificationAutoEnlist>True</NotificationAutoEnlist>

<CommandText>select * from table1</CommandText>

<CommandTimeout>30</CommandTimeout>

<DesignTimeVisible>True</DesignTimeVisible>

</Command>

</Items>

 

You can tweak the function as per need to get more info. It uses reflection as access to private members is required to get this information.

 

 

Written by: Naresh Joshi (MSFT)

Page view tracker