Visual Studio is pretty good at discovering the schema of a stored procedure if the procedure doesn't use temporary tables and returns only one result set. You can drag the stored procedure on to a DataSet or Linq to Sql Design Surface and it is generated for you. In real life, stored procedures don’t fit into that neat box.
Since I am lazy and don’t like to build DataSets by hand, I wrote this little class. Note that I am actually executing the T-SQL command. Any side effect of the stored procedure will take place; this is acceptable in my environment, it may not be in yours.
I am getting less information from this method than I get from static sources like SQL Server’s SMO; for example, I am not getting the length of strings. I am getting enough information to do what I want to do.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
namespace TsqlDesriptionUtil
{
public class TsqlCommandSchema
{
public static List<TableDescription> GetFieldDescription(
string conString, string commandString)
{
var rVal = new List<TableDescription>();
int tableNumber = 1;
using (var con = new SqlConnection(conString))
{
using (var com = new SqlCommand(commandString, con))
{
con.Open();
// WARNING: I am executing the command, beware of side effects!
SqlDataReader reader =
com.ExecuteReader(CommandBehavior.CloseConnection);
// Table Loop
do
{
var fieldDescriptionList = new List<FieldDescription>();
// Field Loop
for (int i = 0; i < reader.FieldCount; ++i)
{
Type fieldType = reader.GetProviderSpecificFieldType(i);
fieldDescriptionList.Add(
new FieldDescription(reader.GetName(i),
reader.GetDataTypeName(i), reader.GetFieldType(i)));
}
// T-SQL doesn't give table names, so I will make up my own
rVal.Add(new TableDescription(string.Format(
"Table{0}", tableNumber++), fieldDescriptionList));
} while (reader.NextResult());
con.Close();
}
}
return rVal;
}
}
public class TableDescription
{
public TableDescription(string tableName, List<FieldDescription> fields)
{
this.TableName = tableName;
this.Fields = fields;
}
public string TableName { get; set; }
public List<FieldDescription> Fields { get; set; }
public int Count
{
get
{
if (this.Fields != null)
return this.Fields.Count;
else
throw new Exception("Fields not loaded");
}
}
}
public class FieldDescription
{
public FieldDescription(string fieldName, string dataTypeName, Type fieldType)
{
this.FieldName = fieldName;
this.DataTypeName = dataTypeName;
this.FieldType = fieldType;
}
public string FieldName { get; set; }
public string DataTypeName { get; set; }
public Type FieldType { get; set; }
public string XsdTypeName
{
get
{
Type t = this.FieldType;
return getXsdTypeNameForType(t);
}
}
private static string getXsdTypeNameForType(Type t)
{
string rVal = string.Empty;
//.NET Framework type XML Schema (XSD) type
switch (t.FullName)
{
case "System.Boolean": rVal = "Boolean"; break;
case "System.Byte": rVal = "unsignedByte"; break;
case "System.Byte[]": rVal = "base64Binary"; break;
case "System.DateTime": rVal = "dateTime"; break;
case "System.Decimal": rVal = "decimal"; break;
case "System.Double": rVal = "Double"; break;
case "System.Int16": rVal = "short"; break;
case "System.Int32": rVal = "int"; break;
case "System.Int64": rVal = "long"; break;
case "System.SByte": rVal = "Byte"; break;
case "System.String": rVal = "string"; break;
case "System.String[]": rVal = "ENTITIES"; break;
case "System.TimeSpan": rVal = "duration"; break;
case "System.UInt16": rVal = "unsignedShort"; break;
case "System.UInt32": rVal = "unsignedInt"; break;
case "System.UInt64": rVal = "unsignedLong"; break;
case "System.Uri": rVal = "anyURI"; break;
case "System.Xml.XmlQualifiedName": rVal = "QName"; break;
}
return rVal;
}
}
}
I compiled the above C# source file into an assembly. Here is a xUnit test that demonstrates its use.
[Fact(DisplayName = "Run SP 001")]
public void RunSp001()
{
string conString = @"Data Source=localhost;Initial Catalog=My_Db;" +
"Integrated Security=True";
string commandString = "EXEC GetBigTableDataUseTempTab 101";
List<TableDescription> rVal =
TsqlCommandSchema.GetFieldDescription(conString, commandString);
foreach (var item in rVal)
{
Console.WriteLine(string.Format("{0}: {1}", item.TableName, item.Count));
foreach(var field in item.Fields)
Console.WriteLine(
string.Format("\t{0}\t{1}\t{2}\t{3}",
field.FieldName,
field.DataTypeName,
field.FieldType,
field.XsdTypeName));
}
}
1 comment:
You do not want to call the SP as you may affect some data in the DB.
Instead:
SELECT * FROM sys.dm_exec_describe_first_result_set ('owner.sprocName', NULL, 0) ;
Post a Comment