C# 数据访问的 OO 设计示例
我使用 MS Enterprise Library 5.0(数据访问块)作为应用程序的主数据层。
虽然我了解面向对象的基础知识(是的,我需要不断学习 - 不断!),但我正在努力理解在哪里/为什么/如何利用良好的设计,即不要重复不必要的代码等,并且仍在努力保持代码易于阅读,当然也易于调试。
首先,我有以下类和默认/示例方法: (ps:db/proc 的返回值是 XML)
public class ajaxget
{
public enum outputType : int { JSON = 0, XML = 1 }
public static string getMemberContacts(string sStartsWith, string sEndswith, outputType eOT)
{
// Get the associated members based upon the criteria
Database db = DatabaseFactory.CreateDatabase("MyDatabase");
DbCommand cmd = db.GetStoredProcCommand("get_memberContactsXML");
db.AddInParameter(cmd, "@memberID", DbType.Int64, Convert.ToInt64(sID));
db.AddInParameter(cmd, "@startsWith1", DbType.String, sStartsWith);
db.AddInParameter(cmd, "@startsWith2", DbType.String, sEndswith);
IDataReader dr = db.ExecuteReader(cmd);
StringBuilder sb = new StringBuilder();
while (dr.Read())
{
sb.Append(dr.GetValue(0));
}
// Clean up
dr.Close();
dr.Dispose();
cmd.Dispose();
// What format to return?
if (eOT == outputType.XML)
{
return sb.ToString();
}
if (eOT == outputType.JSON)
{
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(sb.ToString());
return JsonConvert.SerializeXmlNode(xdoc);
}
}
}
所以..到目前为止非常基本。我只是想在这里建立一个模板,说明我应该如何继续说更多“getMemberContacts”方法。即我应该创建一个通用的“get”方法(这是一个虚拟方法),并覆盖参数吗?我知道这听起来很治愈,确实如此!我想这是学习的一部分。
所以总而言之,我当然尝试重用“get”方法,其中 params/proc 名称明显不同,但是实际返回值(在本例中为 XML,因此连接返回的 while/append 部分) XML 块)需要相同,即返回可以保留为 XML,或者如果提供,可以返回 JSON。
我意识到这是非常基本的东西/概念,但任何指示/想法将不胜感激!
大卫.
--- 编辑 ---
只是为了显示 SQL 2008 XML 部分,因为我对有关不直接从 SQL 返回 XML 的评论感到好奇 - 再次,我通常理解获取原始数据以便使用以不同的方式实现它是一件好事,但就我而言,我的所有前端框架都使用 XML 或 JSON(顺便说一下,JS 框架是 www.dhtmlx.com,非常棒)。
因此,SQL 2008 的过程是:
CREATE PROCEDURE [dbo].[get_messagesForMemberXMLByID]
@memberID as bigint=null,
@days as int=-7
AS
BEGIN
SET NOCOUNT ON;
/* Setup the starting point (in the past) from when we wish to select the messages */
Declare @startDate datetime
set @startDate = DateAdd(d,@days, getdate())
SELECT inboxID as "@id", convert(varchar(12),messageCreated,13) as messageCreated, convert(varchar(8), messageCreated,108) as messageCreatedTime, subject,message, messageOpened, messageFrom, messageFromID
FROM bizzbox
WHERE memberID = @memberID
AND convert(char(8), messageCreated, 112) BETWEEN convert(char(8), @startDate,112) AND convert(char(8), getdate(), 112)
ORDER BY messageCreated desc
FOR XML PATH('row'), ROOT('rows')
END
输出拉回为:
<rows>
<row id="1">
<messageCreated>31 Oct 2010 </messageCreated>
<messageCreatedTime>21:27:32</messageCreatedTime>
<subject>Testing 123</subject>
<message>Wibble Bibble!</message>
<messageFrom>David</messageFrom>
<messageFromID>7</messageFromID>
</row>
</rows>
.. 这正是我需要为前端格式化数据的方式。
我可以明白为什么不使用这种方法的原因 - 例如,如果我使用另一个框架或需要直接数据表中的数据怎么办..我想我可以有一个 param 变量以 XML 或直接表格形式输出..或者我敢说甚至是两个过程..但同样,有兴趣听到无论如何的评论...
再次感谢所有的投入 - 真的非常感谢。
I am using the MS Enterprise Library 5.0 (data access blocks) for the main data layer of my application.
Although I understand the basics of OO (yes, I need to keep learning - contantly!), I am trying to work through understanding where/why/how to make use of good design, i.e. not repeating code un-necessarily etc, and also still trying to keep the code simple to read and of course debug.
So first of the bat, I have the following class and default/example method:
( p.s: the return value from the db/proc is XML )
public class ajaxget
{
public enum outputType : int { JSON = 0, XML = 1 }
public static string getMemberContacts(string sStartsWith, string sEndswith, outputType eOT)
{
// Get the associated members based upon the criteria
Database db = DatabaseFactory.CreateDatabase("MyDatabase");
DbCommand cmd = db.GetStoredProcCommand("get_memberContactsXML");
db.AddInParameter(cmd, "@memberID", DbType.Int64, Convert.ToInt64(sID));
db.AddInParameter(cmd, "@startsWith1", DbType.String, sStartsWith);
db.AddInParameter(cmd, "@startsWith2", DbType.String, sEndswith);
IDataReader dr = db.ExecuteReader(cmd);
StringBuilder sb = new StringBuilder();
while (dr.Read())
{
sb.Append(dr.GetValue(0));
}
// Clean up
dr.Close();
dr.Dispose();
cmd.Dispose();
// What format to return?
if (eOT == outputType.XML)
{
return sb.ToString();
}
if (eOT == outputType.JSON)
{
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(sb.ToString());
return JsonConvert.SerializeXmlNode(xdoc);
}
}
}
So.. pretty basic so far. I am just trying to establish a template here of how I should proceed with say more "getMemberContacts" methods.. i.e. should I make a general "get" method which is a virtual method, and override the params? I know this sounds very remedial and it is! part of learning I guess.
So in summary, I am trying to of course re-use a "get" method, where the params/proc name is obviously different, however the actual return value (XML in this case, hence the while/append part that concatenates the returning XML blocks) needs to be the same, i.e. where the return can be left as XML or if supplied, JSON can be returned.
I realise this is very basic stuff/concept, but any pointers/ideas would be gratefully received!
David.
--- EDIT ---
Just to show the SQL 2008 XML part as I am curious about the comments about not returning XML straight from SQL - again I understand generally that getting the raw data in order to use it in different ways is a good thing, but in my case all of my front-end framework uses either XML or JSON (JS framework by the way is www.dhtmlx.com which has been awesome).
So the proc from SQL 2008 is:
CREATE PROCEDURE [dbo].[get_messagesForMemberXMLByID]
@memberID as bigint=null,
@days as int=-7
AS
BEGIN
SET NOCOUNT ON;
/* Setup the starting point (in the past) from when we wish to select the messages */
Declare @startDate datetime
set @startDate = DateAdd(d,@days, getdate())
SELECT inboxID as "@id", convert(varchar(12),messageCreated,13) as messageCreated, convert(varchar(8), messageCreated,108) as messageCreatedTime, subject,message, messageOpened, messageFrom, messageFromID
FROM bizzbox
WHERE memberID = @memberID
AND convert(char(8), messageCreated, 112) BETWEEN convert(char(8), @startDate,112) AND convert(char(8), getdate(), 112)
ORDER BY messageCreated desc
FOR XML PATH('row'), ROOT('rows')
END
Which the output pulls back as:
<rows>
<row id="1">
<messageCreated>31 Oct 2010 </messageCreated>
<messageCreatedTime>21:27:32</messageCreatedTime>
<subject>Testing 123</subject>
<message>Wibble Bibble!</message>
<messageFrom>David</messageFrom>
<messageFromID>7</messageFromID>
</row>
</rows>
.. which is exactly how I need the data formatted for my front-end.
I can see the reasons why not to use this method - i.e. what if I use another framework or need the data in a straight datatable for instance.. I guess I can have a param variable to output as XML or straight tabular form.. or dare I say even two procs.. but again, interested to hear the comments either way...
Thanks again for all the input - it really is very appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先,您需要使用 using 构造,而不是手动处理事物。您可以在任何实现 IDisposable 的对象上使用 using 构造:
using 语句的优点(除了简洁、可读且通常很棒之外)是,即使出现异常,它也能确保资源被释放发生。在您的示例中,调用 db.ExecuteReader(cmd) 时出现的异常将导致命令无法释放。
Shiv Kumar 也提出了一些非常好的观点。我会做这样的事情:
然后为每个功能区域创建一个子类:
如果存储过程返回 XML,听起来确实有点可疑……
我希望我的示例有所帮助。我没有尝试编译它,但它应该是正确的。
First up, you need to use a using construct instead of manually disposing of things. You can use a using construct on anything that implements IDisposable:
The advantage of the using statement (besides being concise, readable and generally awesome) is that it will ensure that the resources are disposed of even if an exception occurs. In your example, an exception from the call to db.ExecuteReader(cmd) would have caused the command to not get disposed.
Shiv Kumar has also made some very good points. I'd do something like this:
Then just create a subclass for each functional area:
It does sound like there is something a bit supsicious going on in your stored procedures if they are returning XML though...
I hope that my example helps. I haven't tried to compile it, but it should be about right.
这是一个非常复杂的问题,因此答案会多种多样。首先,由于您正处于学习阶段...
我有一些问题:
1. 为什么使用Xml?
2. 为什么您保存成员联系人的数据库表没有代表成员联系人属性的“字段”?
要回答有关重新使用“get”方法的问题之一......我不会为每个 get 提供单独的方法,因为参数的类型和数量总是会发生变化。您可能在这些方法中具有作为所有“get”方法都可以使用的私有方法的通用功能,但每个方法本身应该是单独的。这是因为您应该查看具有公共(或内部)API 的数据访问类。也就是说,当您查看此类的公共方法时,您应该很好地了解该类可以做什么以及执行该操作需要哪些参数。拥有一种通用方法不仅会让您费尽心思将方钉插入圆孔,而且通过查看您的公共方法,您将不知道它提供了什么功能。
您的方法 GetMemberContacts 应具有以下签名
因此,为了获取特定成员的联系人,应将成员的 Id 作为参数传入。该方法很可能应该返回 Membercontact 或 DataTabl/DataSet 的列表。
如果您需要 Xml,那么业务层可以获取该数据并将其转换为 xml。如果需要,您可以使用相同的列表并将其转换为 Json。让业务层来决定,或者更好的是,让它有 3 个方法,以 3 种不同的方式(A List、Json、Xml)返回特定成员的联系人。
不确定您的代码中的 sStartWith 和 sEndWith 是什么。这意味着,您需要一个更好的方法名称和更好的参数名称来正确指示意图。
This is a pretty involved question and therefore answers to this will be many and varied. First of, since you're in the learning phase...
I have some questions:
1. Why are you using Xml?
2. Why does your database table that holds the member contacts not have "fields" that represent the attributes of a member's contacts?
To answer one of your question about re-using a "get" method.... I wouldn't I'd have seperate methods for each get because invariably the types and number of parameters will change. You may have common functionality within these methods made as private method that all "get" methods could use, but the each method itself should be seperate. This is because you should look at the data access class having a public (or internal) API. That is whe nyou look at this class's public methods, you should get a good sense of what this class can do and what parameters it requires in order to do it. Having one generic method will not only make you go through hoops trying to fit a square peg into a round hole but by looking at your public methods, you won't have a clue what functionality it provides.
Your method GetMemberContacts should have the following signature
So in order to get a specific member's contacts that member's Id should be passed in as a parameter. The method should most likely return a list of Membercontact or a DataTabl/DataSet.
If you need Xml, then the business layer can take that data and convert it into xml. If you need to you can use the same List and convert it to Json. Let the business layer decide or better yet, let it have 3 methods that return a specific member's contacts in 3 different ways (A List, Json, Xml).
Not sure what sStartWith and sEndWith are in your code. Which means, you need a better method name and better parameters names that indicate the intent correctly.