用于数据访问的C#OO设计示例

时间:2010-11-15 03:00:15

标签: c# oop data-layers

我正在使用MS Enterprise Library 5.0(数据访问块)作为我的应用程序的主数据层。

虽然我理解OO的基础知识(是的,我需要继续学习 - 包含!),我试图通过了解在哪里/为什么/如何使用好的设计,即不重复代码不一定等,并且仍然试图保持代码简单易读,当然还有调试。

首先,我有以下类和默认/示例方法: (p.s: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”方法,这是一个虚方法,并覆盖params?我知道这听起来非常有用,而且确实如此!我想是学习的一部分。

总而言之,我当然试图重新使用“get”方法,其中params / proc名称明显不同,但是实际的返回值(在这种情况下是XML,因此while / append部分是连接返回的XML块)需要是相同的,即返回可以保留为XML或者如果提供,可以返回JSON。

我意识到这是非常基本的东西/概念,但任何指针/想法都会感激不尽!

大卫。

---编辑---

只是为了展示SQL 2008 XML部分,因为我很好奇关于不直接从SQL返回XML的评论 - 我再次理解,获取原始数据以便以不同方式使用它是一件好事,但是在我的情况下,我的所有前端框架都使用XML或JSON(顺便说一句,JS框架是www.dhtmlx.com,它很棒)。

所以SQL 2008的proc是:

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或直表格形式..或者我敢说甚至两个触发......但是再一次,有兴趣听听评论......

再次感谢所有输入 - 非常感谢。

2 个答案:

答案 0 :(得分:1)

这是一个非常复杂的问题,因此对此的答案将是多种多样的。首先,因为你正处于学习阶段......

  1. 坚持使用方法,参数,变量等名称的C#约定。除非你真的需要,否则不要使用匈牙利表示法(有一些边缘情况可以使用匈牙利表示法但不能使用它们。
  2. 使用Enum时,请使用Switch语句而不是if-else。
  3. 也属于上面的命名约定,但我认为我会单独强调这一点 - 枚举值应该是.Xml和.Json而不是XML和JSON。
  4. 我有一些问题: 1.你为什么使用Xml? 2.为什么包含成员联系人的数据库表没有表示成员联系人属性的“字段”?

    回答你关于重新使用“get”方法的一个问题....我不会为每个get获得单独的方法,因为参数的类型和数量总会发生变化。您可以在这些方法中使用所有“get”方法可以使用的私有方法,但每个方法本身应该是单独的。这是因为您应该查看具有公共(或内部)API的数据访问类。那么,当你看这个类的公共方法时,你应该很好地理解这个类可以做什么以及它需要什么参数才能做到这一点。拥有一种通用方法不仅可以让你通过箍试图将方形钉固定到圆孔中,而且通过查看你的公共方法,你将无法弄清楚它提供了什么功能。

    您的方法GetMemberContacts应具有以下签名

    public static List<MemberContact> GetMemberContacts(int memberId)
    

    因此,为了获得特定成员的联系人,成员的Id应该作为参数传递。该方法最有可能返回Membercontact或DataTabl / DataSet的列表。

    如果您需要Xml,那么业务层可以获取该数据并将其转换为xml。如果需要,可以使用相同的List并将其转换为Json。让业务层决定或更好,让它有3种方法以3种不同的方式返回特定成员的联系人(A List,Json,Xml)。

    不确定代码中的sStartWith和sEndWith是什么。这意味着,您需要一个更好的方法名称和更好的参数名称来正确指示意图。

答案 1 :(得分:1)

首先,您需要使用使用构造而不是手动处理事物。您可以在实现IDisposable的任何内容上使用 using 构造:

StringBuilder sb = new StringBuilder(); 

using(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); 
    using(IDataReader dr = db.ExecuteReader(cmd))
    { 
        while (dr.Read()) 
        { 
            sb.Append(dr.GetValue(0)); 
        } 
    }
}

using语句的优点(除了简洁,可读和通常很棒)是它将确保即使发生异常也会处置资源。在您的示例中,调用db.ExecuteReader(cmd)的异常会导致命令无法处理。

Shiv Kumar也提出了一些非常好的观点。我会做这样的事情:

public abstract class DataAccessLayerBase
{
    protected string GetXml(string storedProcedureName, OutputType outputType, params Tuple<string,DBType,object>[] parameters)
    {
        StringBuilder sb = new StringBuilder();

        //  Get the associated members based upon the criteria  
        Database db = DatabaseFactory.CreateDatabase("MyDatabase");  

        using(DbCommand cmd = db.GetStoredProcCommand(storedProcedureName))
        {
            foreach(var parameter in parameters)
            {
                db.AddInParameter(cmd, parameter.Item1, parameter.Item2, parameter.Item3);
            }

            using(IDataReader dr = db.ExecuteReader(cmd))
            {
                while (dr.Read())  
                {  
                    sb.Append(dr.GetValue(0));  
                }  
            }      

            switch(outputType)
            {
                case OutputType.Xml:
                    return sb.ToString();  
                case OutputType.Json:
                    XmlDocument xdoc = new XmlDocument();  
                    xdoc.LoadXml(sb.ToString());  
                    return JsonConvert.SerializeXmlNode(xdoc);  
                default:
                    throw new NotSupportedException(); // Some sort of error.
            }
        }
    } 
}

然后只为每个功能区域创建一个子类:

public class MemberContactsDal : DataAccessLayerBase
{
    public string GetMemberContacts(long memberID, string startsWith, string endsWith, OutputType outputType)
    {
        // Call the method in the base class to handle all of the parsing.
        return GetXml(
            "get_memberContactsXML", 
            outputType, 
            new Tuple("@memberID", DBType.Int64, memberID ), 
            new Tuple("@startsWith1", DBType.String, startsWith), 
            new Tuple("@startsWith2", DBType.String, endsWith)
        );
    }
}

听起来好像存储过程中存在一些有点可疑的东西,如果他们正在返回XML ...

我希望我的榜样有所帮助。我没有尝试编译它,但它应该是正确的。