<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>invalid operation - MySql</title>
    <link>http://blog.invalidoperation.com/</link>
    <description>how do i wrote codes</description>
    <language>en-us</language>
    <copyright>ray</copyright>
    <lastBuildDate>Mon, 16 Feb 2009 02:16:00 GMT</lastBuildDate>
    <generator>newtelligence dasBlog 2.2.8279.16125</generator>
    <managingEditor>ray.oneill@gmail.com</managingEditor>
    <webMaster>ray.oneill@gmail.com</webMaster>
    <item>
      <trackback:ping>http://blog.invalidoperation.com/Trackback.aspx?guid=5b20c588-314d-4ce2-b9f1-ed07b4fb7793</trackback:ping>
      <pingback:server>http://blog.invalidoperation.com/pingback.aspx</pingback:server>
      <pingback:target>http://blog.invalidoperation.com/PermaLink,guid,5b20c588-314d-4ce2-b9f1-ed07b4fb7793.aspx</pingback:target>
      <dc:creator>Ray</dc:creator>
      <wfw:comment>http://blog.invalidoperation.com/CommentView,guid,5b20c588-314d-4ce2-b9f1-ed07b4fb7793.aspx</wfw:comment>
      <wfw:commentRss>http://blog.invalidoperation.com/SyndicationService.asmx/GetEntryCommentsRss?guid=5b20c588-314d-4ce2-b9f1-ed07b4fb7793</wfw:commentRss>
      <slash:comments>1</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
For most ORM or data layer utilities there is some component of code generation involved
and somewhere along the line retrieving some metadata about the underlying structure
of a database is usually involved. Seeing as it relates to <a href="http://blog.invalidoperation.com/2008/12/11/CurrentProjectNDeavor.aspx">NDeavor</a>,
I figured I’d write about the ins and outs of getting metadata when running close
to the database. Basically, there are a few basic ways to access database specific
metadata directly through .Net.
</p>
        <h5>Vendor-specific API.
</h5>
        <p>
I only know of one comprehensive API for this and it’s <a href="http://www.microsoft.com/downloads/details.aspx?familyid=d09c1d60-a13c-4479-9b91-9e8b9d835cdc&amp;displaylang=en">Sql
Management Objects (SMO)</a> which is Sql-Server only. A tremendously useful API for
any Microsoft-dominated shops, though. Enumerating through basic structures like tables
and columns is incredibly easy, if a little slow. Getting the table name, column name,
and data type name of every column in every table in the <a href="http://www.codeplex.com/ChinookDatabase">chinook</a> database
might look like this:
</p>
        <pre class="c#" name="code">public void PrintChinookTableColumns()
{
    ServerConnection sc = new ServerConnection(@".\SQLEXPRESS");
    sc.Connect();
    Server server = new Server(sc);
    Database chinookDb = server.Databases["chinook"];
    server.SetDefaultInitFields(typeof(Table), "IsSystemObject");
    foreach (Table table in chinookDb.Tables)
    {
        if (table.IsSystemObject)
            continue;

        string tableName = table.Name;

        foreach (Column col in table.Columns)
        {
            string columnName = col.Name;
            string dataTypeName = col.DataType.Name;

            Console.WriteLine("{0} {1} {2}", tableName, columnName, dataTypeName);
        }
    }
    sc.Disconnect();
}</pre>
        <p>
 
</p>
        <h5>Querying vendor-specific system tables. 
</h5>
        <p>
          <strong>
          </strong>Vendor lock-in at it’s finest, and typically unsupported at that.
Usefulness is dependent on what level of information is available and the amount of
time you’re willing to spend figuring out what the various columns and values mean.
Getting table, column, and data type names in a specific database in Sql Server Express
2005 might look like this:
</p>
        <pre class="sql" name="code">use chinook
go

SELECT  T.name as TableName, 
        C.name as ColumnName,
        ST.name as DataType
FROM 
    sys.tables T 
    INNER JOIN sys.columns C on T.object_id = C.object_id
    INNER JOIN sys.systypes ST on C.system_type_id = ST.type</pre>
        <h5> 
</h5>
        <h5>Querying <em>standard</em> metadata tables.
</h5>
        <p>
          <strong>
          </strong>Emphasis on ‘standard’, because there really is none. There is a
spec as part of the SQL standards that came along sometime around SQL92/SQL99 (not
sure) which dictates the INFORMATION_SCHEMA tables which store very basic metadata
about the tables, views, columns, procedures, functions, parameters, constraints,
etc, of a specific relational database. Support for these is relative to how much
the vendor cares about doing so. Sql Server and MySql both have pretty strong support
for these. But even then, the spec is very loose and while simple queries to say,
get all table names and column names, might be portable, more complex ones likely
won’t be. There’s other discrepancies as well, for instance the verbiage around ‘schema’
and ‘catalog’. In MySql terms <em>schema</em> is analogous to a Sql Server <em>database</em>,
and this translates down into their implementation of these metadata tables. Back
to the example of tables, columns, and data types in the chinook database:
</p>
        <pre class="sql" name="code">Sql Server: 
use chinook 
go 

SELECT 
    T.TABLE_NAME as TableName,    
    C.COLUMN_NAME as ColumnName, 
    C.DATA_TYPE as DataType 
FROM 
    INFORMATION_SCHEMA.TABLES T 
    INNER JOIN INFORMATION_SCHEMA.COLUMNS C ON 
        T.TABLE_NAME = C.TABLE_NAME AND 
        T.TABLE_SCHEMA = C.TABLE_SCHEMA AND 
        T.TABLE_CATALOG = C.TABLE_CATALOG

 

MySql: 
SELECT 
    T.TABLE_NAME as TableName, 
    C.COLUMN_NAME as ColumnName, 
    C.DATA_TYPE as DataType 
FROM 
    INFORMATION_SCHEMA.TABLES T 
    INNER JOIN INFORMATION_SCHEMA.COLUMNS C ON 
        T.TABLE_NAME = C.TABLE_NAME AND 
        T.TABLE_SCHEMA = C.TABLE_SCHEMA 
WHERE T.TABLE_SCHEMA = 'chinook';</pre>
        <h5> 
</h5>
        <h5>Using the schema-related functionality in Ado.Net 2.0. 
</h5>
        <p>
Introduced in .Net 2.0, the DbConnection base class has a method with a few overloads
called GetSchema(). This is supposed to allow implementers of Ado.Net providers a
way to provide metadata about the underlying connected data store. Since it’s not
strongly typed, and consists of returning DataTables, it’s entirely up to the implementer
as far as what information is provided. Typically calling the parameter-less version
of GetSchema() would return a DataTable that describes what metadata is available,
indicated by a string column called something like <em>MetaDataCollection</em> in
which you can pass to subsequent calls to GetSchema(string) to get that specific chunk
of metadata. There’s a decent write up of what’s available in the native Ado.Net providers
located <a href="http://msdn.microsoft.com/en-us/library/kcax58fh.aspx">here</a>.
Here’s an extension method to get all the metadata into one dataset for further manipulation,
which should work for the built-in Ado.Net providers, as well as MySql.Data.MysqlClient
using the latest <a href="http://dev.mysql.com/downloads/connector/net/5.2.html">MySql
Net Connector</a>:
</p>
        <pre class="c#" name="code">using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;

namespace System.Data
{
    public static class DbConnectionExtensions
    {
        public static DataSet GetSchemaSet(this DbConnection connection)
        {
            DataSet result = new DataSet();

            DataTable collections = connection.GetSchema();

            List&lt;string&gt; availableCollectionNames = (from row in collections.AsEnumerable()
                                                     select row.Field&lt;string&gt;("CollectionName"))
                                                     .Distinct()
                                                     .ToList();

            foreach (string collectionName in availableCollectionNames)
            {
                try
                {
                    DataTable schemaTable = connection.GetSchema(collectionName);
                    result.Tables.Add(schemaTable);
                }
                catch { }
                // this is bad form and shouldn't be necessary but SqlClient seems to try to give Sql2008 metadata for 
                // Sql2005 connections which results in unneeded exceptions
            }

            return result;
        }
    }
}</pre>
        <p>
The other mechanism for getting metadata is the GetSchemaTable() method on the System.Data.IDataReader
interface, which provides some limited information on the metadata for the columns
that exist in a particular instance of IDataReader, typically from a call to DbCommand.ExecuteReader().
Again, GetSchemaTable(), as you might have guessed, returns a DataTable, this time
with structural information only relating to the columns of the IDataReader itself.
This, combined with DbDataReader.GetName(int) and DbDataReader.GetDataTypeName(int)
can give some pretty useful information for table and column structure returned from
a select statement. That being said, unless you’re reverse-engineering result sets
from stored procedures, you’ll likely find the DbConnection.GetSchema() functionality
much more effective.
</p>
        <p>
If you’re using the System.Data.OleDb provider, there’s the GetOleDbSchemaTable method
on the OleDbConnection class which provides its own metadata about the underlying
database. It works a little differently and takes a System.Guid value which refers
to the type of metadata being requested, all of which are static fields in the System.Data.OleDb.OleDbSchemaGuid
class.
</p>
        <pre class="c#" name="code">using System.Collections.Generic;
using System.Data.OleDb;
using System.Linq;
using System.Reflection;

namespace System.Data
{
    public static class OleDbConnectionExtensions
    {
        public static DataSet GetOleDbSchemaSet(this OleDbConnection connection)
        {
            DataSet result = new DataSet();
            List&lt;FieldInfo&gt; guidMembers = typeof(OleDbSchemaGuid).GetFields(BindingFlags.Static | BindingFlags.Public).ToList();

            foreach (FieldInfo field in guidMembers)
            {
                if (field.FieldType == typeof(Guid))
                {
                    Guid val = (Guid)field.GetValue(null);

                    try
                    {
                        DataTable schemaTable = connection.GetOleDbSchemaTable(val, new object[] { });
                        result.Tables.Add(schemaTable);
                    }
                    catch { } // unfortunately not all schema guids supported by all oledb connections necessarily
                }
            }

            return result;
        }
    }
}</pre>
        <h5>Oddities and Errata
</h5>
        <p>
Under .NET 3.5 (maybe SP1 only), if you execute .GetSchema() on a System.Data.SqlClient.SqlConnection
connected to a Sql Server 2005, you’ll notice there's a row indicating that the metadatacollection
called <em>StructuredTypeMembers</em> is available. However, this is Sql Server 2008-specific
and will throw an exception if you call .GetSchema(“StructuredTypeMembers”) on that
same Sql 2005 connection. Why Microsoft let it even be exposed during calls to .GetSchema()
on non-2008 connections, who knows. They already have a precedent for exposing Sql
2008-specific metadata depending on 2005/2008 server version as indicated <a href="http://msdn.microsoft.com/en-us/library/ms254969.aspx">here</a> so
it seems pretty stupid on their part. That’s the reason for the empty catch statement
in the code block above.
</p>
        <p>
Using <a href="http://dev.mysql.com/downloads/connector/net/5.2.html">MySql Connector
Net 5.2.5</a>, the schema collection of <em>Foreign Key Columns</em> is available
but not indicated by the resulting DataTable returned by MySqlConnection.GetSchema().
I notified <a href="http://www.reggieburnett.com/">Reggie Burnett</a>, who maintains
the Net Connector, and he says it’s been fixed, so I’d expect to see that in a future
release.
</p>
        <p>
Getting the structure of result sets from stored procedures or functions is even more
difficult than any of the basic table/column structure stuff I’ve mentioned. Using
Sql Server, you can use the <em>SET FMTONLY ON </em>statement before the stored procedure
call, and <em>SET FMTONLY OFF </em>afterwards, in order to get an empty result set
from which you can derive schema without executing against live data. However, to
do that in code, you’d have to derive their parameters using some other means like
GetSchema() and build a statement block with the FMTONLY lines surrounding the procedure
call. Oh, and none of this works if the stored procedure uses temporary tables to
return that result set. And again, it’s Sql Server specific. For MySql, the only way
I’ve figured out is to:
</p>
        <ol>
          <li>
Derive the parameter information using MySqlConnection.GetSchema(“Procedure Parameters”) 
</li>
          <li>
Construct a MySqlCommand with command text equal to the procedure name and CommandType
set to CommandType.StoredProcedure 
</li>
          <li>
Add phony parameters using the previously-derived information and set their values
to DBNull.Value. 
</li>
          <li>
Execute the command via ExecuteReader() 
</li>
          <li>
Reverse engineer the schema using GetName(int), GetDataTypeName(int) on the MySqlDataReader,
in tandem with GetSchemaTable(). Theoretically, if the information in the schema table
indicates the column came from a physical table, one could go to the source and look
at the values of MySqlConnection.GetSchema(“Columns”) for the source column definitions. 
</li>
        </ol>
        <p>
That’s likely how I’ll be doing it in the NDeavor providers where stored procedures
are supported like Oracle and Postgres. It’s less than ideal, but if you’re still
following this, you’d realize that’s my point! 
</p>
        <h5>Adding It Up
</h5>
        <p>
There’s a pattern to getting at your metadata and that pattern is it’s a pain in the
ass. I’m hoping NDeavor will provide an easy means of getting database schema out
of various platforms for the purposes of artifact-generation. But even so, I’m not
exactly planning on writing each NDeavor provider as its own one-off implementation
because there’s such inconsistency between metadata across platforms. I have a way
forward for a more lower-level means of getting at this stuff that solves a lot of
the cross-cutting discrepancies, and that will be in part 2.
</p>
        <img width="0" height="0" src="http://blog.invalidoperation.com/aggbug.ashx?id=5b20c588-314d-4ce2-b9f1-ed07b4fb7793" />
      </body>
      <title>Database Metadata in .Net Part 1 : The Problem</title>
      <guid isPermaLink="false">http://blog.invalidoperation.com/PermaLink,guid,5b20c588-314d-4ce2-b9f1-ed07b4fb7793.aspx</guid>
      <link>http://blog.invalidoperation.com/2009/02/16/DatabaseMetadataInNetPart1TheProblem.aspx</link>
      <pubDate>Mon, 16 Feb 2009 02:16:00 GMT</pubDate>
      <description>&lt;p&gt;
For most ORM or data layer utilities there is some component of code generation involved
and somewhere along the line retrieving some metadata about the underlying structure
of a database is usually involved. Seeing as it relates to &lt;a href="http://blog.invalidoperation.com/2008/12/11/CurrentProjectNDeavor.aspx"&gt;NDeavor&lt;/a&gt;,
I figured I’d write about the ins and outs of getting metadata when running close
to the database. Basically, there are a few basic ways to access database specific
metadata directly through .Net.
&lt;/p&gt;
&lt;h5&gt;Vendor-specific API.
&lt;/h5&gt;
&lt;p&gt;
I only know of one comprehensive API for this and it’s &lt;a href="http://www.microsoft.com/downloads/details.aspx?familyid=d09c1d60-a13c-4479-9b91-9e8b9d835cdc&amp;amp;displaylang=en"&gt;Sql
Management Objects (SMO)&lt;/a&gt; which is Sql-Server only. A tremendously useful API for
any Microsoft-dominated shops, though. Enumerating through basic structures like tables
and columns is incredibly easy, if a little slow. Getting the table name, column name,
and data type name of every column in every table in the &lt;a href="http://www.codeplex.com/ChinookDatabase"&gt;chinook&lt;/a&gt; database
might look like this:
&lt;/p&gt;
&lt;pre class="c#" name="code"&gt;public void PrintChinookTableColumns()
{
    ServerConnection sc = new ServerConnection(@&amp;quot;.\SQLEXPRESS&amp;quot;);
    sc.Connect();
    Server server = new Server(sc);
    Database chinookDb = server.Databases[&amp;quot;chinook&amp;quot;];
    server.SetDefaultInitFields(typeof(Table), &amp;quot;IsSystemObject&amp;quot;);
    foreach (Table table in chinookDb.Tables)
    {
        if (table.IsSystemObject)
            continue;

        string tableName = table.Name;

        foreach (Column col in table.Columns)
        {
            string columnName = col.Name;
            string dataTypeName = col.DataType.Name;

            Console.WriteLine(&amp;quot;{0} {1} {2}&amp;quot;, tableName, columnName, dataTypeName);
        }
    }
    sc.Disconnect();
}&lt;/pre&gt;
&lt;p&gt;
&amp;#160;
&lt;/p&gt;
&lt;h5&gt;Querying vendor-specific system tables. 
&lt;/h5&gt;
&lt;p&gt;
&lt;strong&gt;&lt;/strong&gt;Vendor lock-in at it’s finest, and typically unsupported at that.
Usefulness is dependent on what level of information is available and the amount of
time you’re willing to spend figuring out what the various columns and values mean.
Getting table, column, and data type names in a specific database in Sql Server Express
2005 might look like this:
&lt;/p&gt;
&lt;pre class="sql" name="code"&gt;use chinook
go

SELECT  T.name as TableName, 
        C.name as ColumnName,
        ST.name as DataType
FROM 
    sys.tables T 
    INNER JOIN sys.columns C on T.object_id = C.object_id
    INNER JOIN sys.systypes ST on C.system_type_id = ST.type&lt;/pre&gt;
&lt;h5&gt;&amp;#160;
&lt;/h5&gt;
&lt;h5&gt;Querying &lt;em&gt;standard&lt;/em&gt; metadata tables.
&lt;/h5&gt;
&lt;p&gt;
&lt;strong&gt;&lt;/strong&gt;Emphasis on ‘standard’, because there really is none. There is a
spec as part of the SQL standards that came along sometime around SQL92/SQL99 (not
sure) which dictates the INFORMATION_SCHEMA tables which store very basic metadata
about the tables, views, columns, procedures, functions, parameters, constraints,
etc, of a specific relational database. Support for these is relative to how much
the vendor cares about doing so. Sql Server and MySql both have pretty strong support
for these. But even then, the spec is very loose and while simple queries to say,
get all table names and column names, might be portable, more complex ones likely
won’t be. There’s other discrepancies as well, for instance the verbiage around ‘schema’
and ‘catalog’. In MySql terms &lt;em&gt;schema&lt;/em&gt; is analogous to a Sql Server &lt;em&gt;database&lt;/em&gt;,
and this translates down into their implementation of these metadata tables. Back
to the example of tables, columns, and data types in the chinook database:
&lt;/p&gt;
&lt;pre class="sql" name="code"&gt;Sql Server: 
use chinook 
go 

SELECT 
    T.TABLE_NAME as TableName,    
    C.COLUMN_NAME as ColumnName, 
    C.DATA_TYPE as DataType 
FROM 
    INFORMATION_SCHEMA.TABLES T 
    INNER JOIN INFORMATION_SCHEMA.COLUMNS C ON 
        T.TABLE_NAME = C.TABLE_NAME AND 
        T.TABLE_SCHEMA = C.TABLE_SCHEMA AND 
        T.TABLE_CATALOG = C.TABLE_CATALOG

 

MySql: 
SELECT 
    T.TABLE_NAME as TableName, 
    C.COLUMN_NAME as ColumnName, 
    C.DATA_TYPE as DataType 
FROM 
    INFORMATION_SCHEMA.TABLES T 
    INNER JOIN INFORMATION_SCHEMA.COLUMNS C ON 
        T.TABLE_NAME = C.TABLE_NAME AND 
        T.TABLE_SCHEMA = C.TABLE_SCHEMA 
WHERE T.TABLE_SCHEMA = 'chinook';&lt;/pre&gt;
&lt;h5&gt;&amp;#160;
&lt;/h5&gt;
&lt;h5&gt;Using the schema-related functionality in Ado.Net 2.0. 
&lt;/h5&gt;
&lt;p&gt;
Introduced in .Net 2.0, the DbConnection base class has a method with a few overloads
called GetSchema(). This is supposed to allow implementers of Ado.Net providers a
way to provide metadata about the underlying connected data store. Since it’s not
strongly typed, and consists of returning DataTables, it’s entirely up to the implementer
as far as what information is provided. Typically calling the parameter-less version
of GetSchema() would return a DataTable that describes what metadata is available,
indicated by a string column called something like &lt;em&gt;MetaDataCollection&lt;/em&gt; in
which you can pass to subsequent calls to GetSchema(string) to get that specific chunk
of metadata. There’s a decent write up of what’s available in the native Ado.Net providers
located &lt;a href="http://msdn.microsoft.com/en-us/library/kcax58fh.aspx"&gt;here&lt;/a&gt;.
Here’s an extension method to get all the metadata into one dataset for further manipulation,
which should work for the built-in Ado.Net providers, as well as MySql.Data.MysqlClient
using the latest &lt;a href="http://dev.mysql.com/downloads/connector/net/5.2.html"&gt;MySql
Net Connector&lt;/a&gt;:
&lt;/p&gt;
&lt;pre class="c#" name="code"&gt;using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;

namespace System.Data
{
    public static class DbConnectionExtensions
    {
        public static DataSet GetSchemaSet(this DbConnection connection)
        {
            DataSet result = new DataSet();

            DataTable collections = connection.GetSchema();

            List&amp;lt;string&amp;gt; availableCollectionNames = (from row in collections.AsEnumerable()
                                                     select row.Field&amp;lt;string&amp;gt;(&amp;quot;CollectionName&amp;quot;))
                                                     .Distinct()
                                                     .ToList();

            foreach (string collectionName in availableCollectionNames)
            {
                try
                {
                    DataTable schemaTable = connection.GetSchema(collectionName);
                    result.Tables.Add(schemaTable);
                }
                catch { }
                // this is bad form and shouldn't be necessary but SqlClient seems to try to give Sql2008 metadata for 
                // Sql2005 connections which results in unneeded exceptions
            }

            return result;
        }
    }
}&lt;/pre&gt;
&lt;p&gt;
The other mechanism for getting metadata is the GetSchemaTable() method on the System.Data.IDataReader
interface, which provides some limited information on the metadata for the columns
that exist in a particular instance of IDataReader, typically from a call to DbCommand.ExecuteReader().
Again, GetSchemaTable(), as you might have guessed, returns a DataTable, this time
with structural information only relating to the columns of the IDataReader itself.
This, combined with DbDataReader.GetName(int) and DbDataReader.GetDataTypeName(int)
can give some pretty useful information for table and column structure returned from
a select statement. That being said, unless you’re reverse-engineering result sets
from stored procedures, you’ll likely find the DbConnection.GetSchema() functionality
much more effective.
&lt;/p&gt;
&lt;p&gt;
If you’re using the System.Data.OleDb provider, there’s the GetOleDbSchemaTable method
on the OleDbConnection class which provides its own metadata about the underlying
database. It works a little differently and takes a System.Guid value which refers
to the type of metadata being requested, all of which are static fields in the System.Data.OleDb.OleDbSchemaGuid
class.
&lt;/p&gt;
&lt;pre class="c#" name="code"&gt;using System.Collections.Generic;
using System.Data.OleDb;
using System.Linq;
using System.Reflection;

namespace System.Data
{
    public static class OleDbConnectionExtensions
    {
        public static DataSet GetOleDbSchemaSet(this OleDbConnection connection)
        {
            DataSet result = new DataSet();
            List&amp;lt;FieldInfo&amp;gt; guidMembers = typeof(OleDbSchemaGuid).GetFields(BindingFlags.Static | BindingFlags.Public).ToList();

            foreach (FieldInfo field in guidMembers)
            {
                if (field.FieldType == typeof(Guid))
                {
                    Guid val = (Guid)field.GetValue(null);

                    try
                    {
                        DataTable schemaTable = connection.GetOleDbSchemaTable(val, new object[] { });
                        result.Tables.Add(schemaTable);
                    }
                    catch { } // unfortunately not all schema guids supported by all oledb connections necessarily
                }
            }

            return result;
        }
    }
}&lt;/pre&gt;
&lt;h5&gt;Oddities and Errata
&lt;/h5&gt;
&lt;p&gt;
Under .NET 3.5 (maybe SP1 only), if you execute .GetSchema() on a System.Data.SqlClient.SqlConnection
connected to a Sql Server 2005, you’ll notice there's a row indicating that the metadatacollection
called &lt;em&gt;StructuredTypeMembers&lt;/em&gt; is available. However, this is Sql Server 2008-specific
and will throw an exception if you call .GetSchema(“StructuredTypeMembers”) on that
same Sql 2005 connection. Why Microsoft let it even be exposed during calls to .GetSchema()
on non-2008 connections, who knows. They already have a precedent for exposing Sql
2008-specific metadata depending on 2005/2008 server version as indicated &lt;a href="http://msdn.microsoft.com/en-us/library/ms254969.aspx"&gt;here&lt;/a&gt; so
it seems pretty stupid on their part. That’s the reason for the empty catch statement
in the code block above.
&lt;/p&gt;
&lt;p&gt;
Using &lt;a href="http://dev.mysql.com/downloads/connector/net/5.2.html"&gt;MySql Connector
Net 5.2.5&lt;/a&gt;, the schema collection of &lt;em&gt;Foreign Key Columns&lt;/em&gt; is available
but not indicated by the resulting DataTable returned by MySqlConnection.GetSchema().
I notified &lt;a href="http://www.reggieburnett.com/"&gt;Reggie Burnett&lt;/a&gt;, who maintains
the Net Connector, and he says it’s been fixed, so I’d expect to see that in a future
release.
&lt;/p&gt;
&lt;p&gt;
Getting the structure of result sets from stored procedures or functions is even more
difficult than any of the basic table/column structure stuff I’ve mentioned. Using
Sql Server, you can use the &lt;em&gt;SET FMTONLY ON &lt;/em&gt;statement before the stored procedure
call, and &lt;em&gt;SET FMTONLY OFF &lt;/em&gt;afterwards, in order to get an empty result set
from which you can derive schema without executing against live data. However, to
do that in code, you’d have to derive their parameters using some other means like
GetSchema() and build a statement block with the FMTONLY lines surrounding the procedure
call. Oh, and none of this works if the stored procedure uses temporary tables to
return that result set. And again, it’s Sql Server specific. For MySql, the only way
I’ve figured out is to:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Derive the parameter information using MySqlConnection.GetSchema(“Procedure Parameters”) 
&lt;/li&gt;
&lt;li&gt;
Construct a MySqlCommand with command text equal to the procedure name and CommandType
set to CommandType.StoredProcedure 
&lt;/li&gt;
&lt;li&gt;
Add phony parameters using the previously-derived information and set their values
to DBNull.Value. 
&lt;/li&gt;
&lt;li&gt;
Execute the command via ExecuteReader() 
&lt;/li&gt;
&lt;li&gt;
Reverse engineer the schema using GetName(int), GetDataTypeName(int) on the MySqlDataReader,
in tandem with GetSchemaTable(). Theoretically, if the information in the schema table
indicates the column came from a physical table, one could go to the source and look
at the values of MySqlConnection.GetSchema(“Columns”) for the source column definitions. 
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
That’s likely how I’ll be doing it in the NDeavor providers where stored procedures
are supported like Oracle and Postgres. It’s less than ideal, but if you’re still
following this, you’d realize that’s my point! 
&lt;/p&gt;
&lt;h5&gt;Adding It Up
&lt;/h5&gt;
&lt;p&gt;
There’s a pattern to getting at your metadata and that pattern is it’s a pain in the
ass. I’m hoping NDeavor will provide an easy means of getting database schema out
of various platforms for the purposes of artifact-generation. But even so, I’m not
exactly planning on writing each NDeavor provider as its own one-off implementation
because there’s such inconsistency between metadata across platforms. I have a way
forward for a more lower-level means of getting at this stuff that solves a lot of
the cross-cutting discrepancies, and that will be in part 2.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://blog.invalidoperation.com/aggbug.ashx?id=5b20c588-314d-4ce2-b9f1-ed07b4fb7793" /&gt;</description>
      <comments>http://blog.invalidoperation.com/CommentView,guid,5b20c588-314d-4ce2-b9f1-ed07b4fb7793.aspx</comments>
      <category>Ado.Net</category>
      <category>database</category>
      <category>metadata</category>
      <category>MySql</category>
      <category>NDeavor</category>
      <category>Sql Server</category>
    </item>
  </channel>
</rss>