使用 Reflection.Emit 和 xsd 文件从数据库表生成类会遇到哪些陷阱?
我正在玩类生成(一个表的类 - 继承等,现在不考虑......)。 所以我无耻地从这里复制了反射.发出代码。 将其重新设计为给定数据库中的每个表生成,并在项目的 bin 文件夹中使用以下批处理调用创建文件: for /f "tokens=*" %%i in ('dir *.xsd /b') 执行 "C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\xsd.exe" -c -l: c# -n:BusinessObjects %i
到目前为止一切顺利。 这个想法是每次新的数据库版本到达时重新生成类并将它们复制到“真实项目”中(我不需要任何运行时生成)并且还想享受智能感知。 这种方法可能会出现哪些陷阱、困难和问题,对于那些松散描述的需求有更好的建议吗?!
以下是创建程序集的控制台应用程序的生成代码:
using System;
using System.Collections.Generic;
using System.Text;
using log4net;
using log4net.Config;
using System.Data;
using System.Data.SqlClient;
using System.Threading;
using System.Reflection;
using System.Reflection.Emit;
namespace GenerateAssemblies
{
class Program
{
private static readonly ILog logger =
LogManager.GetLogger ( typeof ( Program ) );
static void Main ( string[] args )
{
DOMConfigurator.Configure(); //tis configures the logger
logger.Debug ( "APP START" );
DataTable dtTables = Program.GetTablesFromDb ( "POC" ) ;
foreach (DataRow dr in dtTables.Rows)
{
string strTableName = dr[0].ToString () ;
CodeEmitGeneratingAssemblies.DllGenerator.WriteXmlAndTxtFileOutOfDataTableByName ( strTableName);
CodeEmitGeneratingAssemblies.DllGenerator.CreateAssembly ( strTableName );
}
Console.WriteLine ( " Should have now all the dll's " );
Console.ReadLine ();
} //eof method
static DataTable GetTablesFromDb ( string strDbName )
{
DataTable dt = new DataTable ( "tables" );
string connectionString = "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=" + strDbName + ";Data Source=ysg";
using (SqlConnection connection = new SqlConnection ( connectionString ))
{
SqlCommand command = connection.CreateCommand ();
command.CommandText = string.Format ( "SELECT name from sys.tables" );
connection.Open ();
dt.Load ( command.ExecuteReader ( CommandBehavior.CloseConnection ) );
}
return dt;
} //eof method
} //eof class
namespace CodeEmitGeneratingAssemblies
{
public class DllGenerator
{
private static readonly ILog logger =
LogManager.GetLogger ( typeof ( DllGenerator ) );
public static void WriteXmlAndTxtFileOutOfDataTableByName (string strDataTableName)
{
DOMConfigurator.Configure (); //tis configures the logger
DataTable tableData = new DataTable ( strDataTableName );
string connectionString = "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=POC;Data Source=ysg";
using (SqlConnection connection = new SqlConnection ( connectionString ))
{
SqlCommand command = connection.CreateCommand ();
command.CommandText = string.Format ( "SELECT * FROM [" + strDataTableName + "]");
logger.Debug ( "command.CommandText is " + command.CommandText );
connection.Open ();
tableData.Load ( command.ExecuteReader ( CommandBehavior.CloseConnection ) );
}
tableData.WriteXml ( strDataTableName + ".xml" );
tableData.WriteXmlSchema ( strDataTableName + ".xsd" );
} //eof method
public static void CreateAssembly ( string strDataTableName )
{
AppDomain currentDomain = Thread.GetDomain ();
AssemblyName myAssemblyName = new AssemblyName ( );
myAssemblyName.Name = strDataTableName;
AssemblyBuilder builder = currentDomain.DefineDynamicAssembly (
myAssemblyName,
AssemblyBuilderAccess.RunAndSave );
builder.AddResourceFile ( "TableXml", strDataTableName + ".xml" );
builder.AddResourceFile ( "TableXsd", strDataTableName + ".xsd" );
builder.Save ( strDataTableName + ".dll" );
}
} //eof class
} //eof namespace
} //eof namespace
I am playing with class generation ( one class for a table - inheritance etc. not to be considered for now ... ). So I copied shamelessly from here the Reflection.Emit code. Reworked it to be generated per table in a given database and created the files with the following batch call in the Project's bin folder :
for /f "tokens=*" %%i in ('dir *.xsd /b') do "C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\xsd.exe" -c -l:c# -n:BusinessObjects %i
So far so good. The idea is each time when a new db version arrives to regenerate the classes and copy them in the "real project" ( I do not need any run-time generation ) and also would like to enjoy Intellisense. What pitfalls , difficulties and problems might arrise from this type of approach, any better suggestions for those loosely described requirements ?!
Here is the Generation code of the console app creating the assemblies :
using System;
using System.Collections.Generic;
using System.Text;
using log4net;
using log4net.Config;
using System.Data;
using System.Data.SqlClient;
using System.Threading;
using System.Reflection;
using System.Reflection.Emit;
namespace GenerateAssemblies
{
class Program
{
private static readonly ILog logger =
LogManager.GetLogger ( typeof ( Program ) );
static void Main ( string[] args )
{
DOMConfigurator.Configure(); //tis configures the logger
logger.Debug ( "APP START" );
DataTable dtTables = Program.GetTablesFromDb ( "POC" ) ;
foreach (DataRow dr in dtTables.Rows)
{
string strTableName = dr[0].ToString () ;
CodeEmitGeneratingAssemblies.DllGenerator.WriteXmlAndTxtFileOutOfDataTableByName ( strTableName);
CodeEmitGeneratingAssemblies.DllGenerator.CreateAssembly ( strTableName );
}
Console.WriteLine ( " Should have now all the dll's " );
Console.ReadLine ();
} //eof method
static DataTable GetTablesFromDb ( string strDbName )
{
DataTable dt = new DataTable ( "tables" );
string connectionString = "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=" + strDbName + ";Data Source=ysg";
using (SqlConnection connection = new SqlConnection ( connectionString ))
{
SqlCommand command = connection.CreateCommand ();
command.CommandText = string.Format ( "SELECT name from sys.tables" );
connection.Open ();
dt.Load ( command.ExecuteReader ( CommandBehavior.CloseConnection ) );
}
return dt;
} //eof method
} //eof class
namespace CodeEmitGeneratingAssemblies
{
public class DllGenerator
{
private static readonly ILog logger =
LogManager.GetLogger ( typeof ( DllGenerator ) );
public static void WriteXmlAndTxtFileOutOfDataTableByName (string strDataTableName)
{
DOMConfigurator.Configure (); //tis configures the logger
DataTable tableData = new DataTable ( strDataTableName );
string connectionString = "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=POC;Data Source=ysg";
using (SqlConnection connection = new SqlConnection ( connectionString ))
{
SqlCommand command = connection.CreateCommand ();
command.CommandText = string.Format ( "SELECT * FROM [" + strDataTableName + "]");
logger.Debug ( "command.CommandText is " + command.CommandText );
connection.Open ();
tableData.Load ( command.ExecuteReader ( CommandBehavior.CloseConnection ) );
}
tableData.WriteXml ( strDataTableName + ".xml" );
tableData.WriteXmlSchema ( strDataTableName + ".xsd" );
} //eof method
public static void CreateAssembly ( string strDataTableName )
{
AppDomain currentDomain = Thread.GetDomain ();
AssemblyName myAssemblyName = new AssemblyName ( );
myAssemblyName.Name = strDataTableName;
AssemblyBuilder builder = currentDomain.DefineDynamicAssembly (
myAssemblyName,
AssemblyBuilderAccess.RunAndSave );
builder.AddResourceFile ( "TableXml", strDataTableName + ".xml" );
builder.AddResourceFile ( "TableXsd", strDataTableName + ".xsd" );
builder.Save ( strDataTableName + ".dll" );
}
} //eof class
} //eof namespace
} //eof namespace
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你会遇到使用(关系)数据库驱动的面向对象设计的所有问题:
我更喜欢以相反的方式工作。 从oo模型到数据库
[编辑]
您可以尝试让混合模型发挥作用。 对于应用程序的一部分,从 DB 到 OO,对于另一部分,则相反。 这让你可以慢慢重构并迁移到 OO->DB。
You get all the problems of using a (relational)database-driven oo design:
I prefer working the other way around. From the oo model to the database
[edit]
You could try to get a hybrid model working. Going from DB to OO for part of the app, and the other way around for another part. That allows you to slowly refactor and migrate to OO->DB.
呃……与 SubSonic 的 oneliner :
Duh ... oneliner with SubSonic :