我发现我的C#应用程序做了很多查询,其中很多样板文件使我的代码空间变得混乱。我也想避免重复,但我不确定如何编写一种方法来执行此操作。
我正在使用ODP访问Oracle数据库。我不能使用Linq,因为我们的数据仓库人员拒绝指定主键,而对Linq的ODP支持似乎是......他们宁愿让你使用他们的平台。
我无法真正返回List,因为每个查询都会返回不同类型的不同数量。
string gufcode = String.Empty;
double cost = 0.0;
OracleCommand GUFCommand2 = thisConnection.CreateCommand();
String GUFQuery2 = "SELECT GUF_ID, COST_RATE FROM SIMPLE_TABLE";
GUFCommand2.CommandText = GUFQuery2;
OracleDataReader GUFReader2 = GUFCommand2.ExecuteReader();
while (GUFReader2.Read())
{
if (GUFReader2[0/**GUF_CODE**/] != DBNull.Value)
{
gufcode = Convert.ToString(BUFReader2[0]);
}
if (GUFReader2[1/**COST_RATE**/] != DBNull.Value)
{
cost = Convert.ToDouble(GUFReader2[1]);
}
effortRatioDictionary.Add(bufcode, percentageOfEffort);
}
GUFReader2.Close();
但是真的有更多的术语和更多这样的查询。我会说15个左右的查询 - 最多返回15个字段。
在任何地方复制/粘贴此样板文件会引发大量火灾:例如,如果我不更新复制粘贴中的所有内容,我将关闭错误的读取器(或者更糟糕的是)向数据库发送不同的查询字符串。 / p>
我希望能够做到这样的事情:
string gufQuery = "SELECT GUF_ID, COST_RATE FROM SIMPLE_TABLE";
List<something> gufResponse = miracleProcedure(gufQuery, thisConnection);
所以大多数样板都消失了。
我正在寻找简单的东西。
答案 0 :(得分:1)
我认为你无法抽象出一个函数的主要原因是每次返回数据都不同。
这意味着每次读取的次数也会不同。
你可以直接返回GUFReader2但是你将失去在你想要的功能内关闭它的能力。
我会说返回一个对象的数组(或列表)。
在程序中,只需读取每一行并在关闭连接时返回列表。
您的调用函数将始终知道预期数据的内容和顺序。它也必须转换对象数据,但无论如何你都在这个过程中这样做。
答案 1 :(得分:1)
一些提示:
从IDisposable派生,可以使用using statement更清晰的代码。
IMO你的神奇方法看起来应该更像这样:
List<T> list = doMagic("SIMPLE_TABLE", columns);
columns 可以是一个像这样的小结构数组:
struct Column
{
string Name;
Type DataType;
}
如果您经常使用相同的表/列,则可以使用枚举。
或者
您可以从XNA中的VertexDeclaration,VertexElement,VertexElementFormat和VertexElementUsage类型中获取灵感:http://msdn.microsoft.com/en-us/library/bb197344。
当以随机顺序处理不同数量的“输入”时,这被证明是非常有用的。
在我的情况下,我已经能够为OpenGL构建一个易于使用的类似XNA的框架。
关于列表的返回类型,请参阅我的第二个建议。
答案 2 :(得分:0)
Linq是正确的答案。我赞扬上面的David M,但我不能将其标记为正确答案,因为他只留下了评论。
我能够使用ArrayLists进行半通用化方法:
public static ArrayList GeneralQuery(string thisQuery, OracleConnection myConnection)
{
ArrayList outerAL = new ArrayList();
OracleCommand GeneralCommand = myConnection.CreateCommand();
GeneralCommand.CommandText = thisQuery;
OracleDataReader GeneralReader = GeneralCommand.ExecuteReader();
while (GeneralReader.Read())
{
for (int i = 0; i < GeneralReader.FieldCount; i++)
{
ArrayList innerAL = new ArrayList();
if (GeneralReader[i] != DBNull.Value)
{
innerAL.Add(GeneralReader[i]);
}
else
{
innerAL.Add(0);
}
outerAL.Add(innerAL);
}
}
GeneralReader.Close();
return outerAL;
}
调用此方法的代码如下所示:
thisConnection.Open();
List<ProjectWrapper> liProjectCOs = new List<ProjectWrapper>();
String ProjectQuery = "SELECT SF_CLIENT_PROJECT.ID, SF_CLIENT_PROJECT.NAMEX, SF_CHANGE_ORDER.ID, SF_CHANGE_ORDER.END_DATE, ";
ProjectQuery += "SF_CLIENT_PROJECT.CONTRACTED_START_DATE, SF_CHANGE_ORDER.STATUS, SF_CHANGE_ORDER.TYPE, SF_CLIENT_PROJECT.ESTIMATED_END_DATE, SF_CLIENT_PROJECT.CONTRACTED_END_DATE ";
ProjectQuery += "FROM SF_CLIENT_PROJECT, SF_CHANGE_ORDER ";
ProjectQuery += "WHERE SF_CHANGE_ORDER.TYPE = 'New' ";
ProjectQuery += "AND SF_CLIENT_PROJECT.ID = SF_CHANGE_ORDER.PROJECT";
ArrayList alProjects = GeneralQuery(ProjectQuery, thisConnection);
foreach( ArrayList proj in alProjects ) {
ProjectWrapper pw = new ProjectWrapper();
pw.projectId = Convert.ToString( proj[0] );
pw.projectId = Convert.ToString(proj[0]);
pw.projectName = Convert.ToString(proj[1]);
pw.changeOrderId = Convert.ToString(proj[2]);
pw.newEndDate = Convert.ToDateTime(proj[3]);
pw.startDate = Convert.ToDateTime(proj[4]);
pw.status = Convert.ToString(proj[5]);
pw.type = Convert.ToString(proj[6]);
if ( Convert.ToString(proj[7]) != "0" ) // 0 returned by generalquery if null
pw.oldEndDate = Convert.ToDateTime(proj[7]);
else
pw.oldEndDate = Convert.ToDateTime(proj[8]);
liProjectCOs.Add(pw);
这里有很多明显的缺点(尽管它比我之前尝试做的要好很多)。它比我与我们的数据仓库人员重新协商的Linq差得多。那里有一个新人,他更有帮助。
Linq将上面的代码行减少了2倍。从之前的非封装方式开始,它是4倍。