最近,I made a post about the developers I'm working with not using try catch blocks properly,并且在关键情况下使用try ... catch块并且一起忽略异常错误是不幸的。引起我的主要心脏疼痛。以下是他们执行此操作的几千个代码段之一的示例(遗漏了一些代码并不特别重要:
public void AddLocations(BOLocation objBllLocations)
{
try
{
dbManager.Open();
if (objBllLocations.StateID != 0)
{
// about 20 Paramters added to dbManager here
}
else
{
// about 19 Paramters added here
}
dbManager.ExecuteNonQuery(CommandType.StoredProcedure, "ULOCATIONS.AddLocations");
}
catch (Exception ex)
{
}
finally
{
dbManager.Dispose();
}
}
在我看来,这绝对令人失望,并且在发生某些潜在问题时不会通知用户。我知道很多人都说OOP是邪恶的,并且添加多个层会增加代码行数和程序的复杂性,从而导致代码维护的问题。我个人的大部分编程背景在这个领域采用了几乎相同的方法。下面我列出了我在这种情况下通常编码的方式的基本结构,并且在我的职业生涯中我一直在使用多种语言,但这个特殊的代码在C#中。但下面的代码是我如何使用Objects的一个很好的基本概念,它似乎对我有用,但由于这是一些相当智能的编程地雷的良好来源,我想知道如果我应该重新评估这个我用了这么多年的技术。主要是因为,在接下来的几周内,我将从外包开发人员那里深入研究不太好的代码并修改大量代码。我想尽可能地做到这一点。抱歉长代码参考。
// *******************************************************************************************
/// <summary>
/// Summary description for BaseBusinessObject
/// </summary>
/// <remarks>
/// Base Class allowing me to do basic function on a Busines Object
/// </remarks>
public class BaseBusinessObject : Object, System.Runtime.Serialization.ISerializable
{
public enum DBCode
{ DBUnknownError,
DBNotSaved,
DBOK
}
// private fields, public properties
public int m_id = -1;
public int ID { get { return m_id; } set { m_id = value; } }
private int m_errorCode = 0;
public int ErrorCode { get { return m_errorCode; } set { m_errorCode = value; } }
private string m_errorMsg = "";
public string ErrorMessage { get { return m_errorMsg; } set { m_errorMsg = value; } }
private Exception m_LastException = null;
public Exception LastException { get { return m_LastException; } set { m_LastException = value;} }
//Constructors
public BaseBusinessObject()
{
Initialize();
}
public BaseBusinessObject(int iID)
{
Initialize();
FillByID(iID);
}
// methods
protected void Initialize()
{
Clear();
Object_OnInit();
// Other Initializable code here
}
public void ClearErrors()
{
m_errorCode = 0; m_errorMsg = ""; m_LastException = null;
}
void System.Runtime.Serialization.ISerializable.GetObjectData(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
{
//Serialization code for Object must be implemented here
}
// overrideable methods
protected virtual void Object_OnInit()
{
// User can override to add additional initialization stuff.
}
public virtual BaseBusinessObject FillByID(int iID)
{
throw new NotImplementedException("method FillByID Must be implemented");
}
public virtual void Clear()
{
throw new NotImplementedException("method Clear Must be implemented");
}
public virtual DBCode Save()
{
throw new NotImplementedException("method Save Must be implemented");
}
}
// *******************************************************************************************
/// <summary>
/// Example Class that might be based off of a Base Business Object
/// </summary>
/// <remarks>
/// Class for holding all the information about a Customer
/// </remarks>
public class BLLCustomer : BaseBusinessObject
{
// ***************************************
// put field members here other than the ID
private string m_name = "";
public string Name { get { return m_name; } set { m_name = value; } }
public override void Clear()
{
m_id = -1;
m_name = "";
}
public override BaseBusinessObject FillByID(int iID)
{
Clear();
try
{
// usually accessing a DataLayerObject,
//to select a database record
}
catch (Exception Ex)
{
Clear();
LastException = Ex;
// I can have many different exception, this is usually an enum
ErrorCode = 3;
ErrorMessage = "Customer couldn't be loaded";
}
return this;
}
public override DBCode Save()
{
DBCode ret = DBCode.DBUnknownError;
try
{
// usually accessing a DataLayerObject,
//to save a database record
ret = DBCode.DBOK;
}
catch (Exception Ex)
{
LastException = Ex;
// I can have many different exception, this is usually an enum
// i do not usually use just a General Exeption
ErrorCode = 3;
ErrorMessage = "some really weird error happened, customer not saved";
ret = DBCode.DBNotSaved;
}
return ret;
}
}
// *******************************************************************************************
// Example of how it's used on an asp page..
protected void Page_Load(object sender, EventArgs e)
{
// Simplifying this a bit, normally, I'd use something like,
// using some sort of static "factory" method
// BaseObject.NewBusinessObject(typeof(BLLCustomer)).FillByID(34);
BLLCustomer cust = ((BLLCustomer)new BLLCustomer()).FillByID(34);
if (cust.ErrorCode != 0)
{
// There was an error.. Error message is in
//cust.ErrorMessage
// some sort of internal error code is in
//cust.ErrorCode
// Give the users some sort of message through and asp:Label..
// probably based off of cust.ErrorMessage
//log can be handled in the data, business layer... or whatever
lab.ErrorText = cust.ErrorMessage;
}
else
{
// continue using the object, to fill in text boxes,
// literals or whatever.
this.labID = cust.ID.toString();
this.labCompName = cust.Name;
}
}
最重要的是,我的问题是,我是否因为多层次的问题而复杂化,继承的类或者我的旧概念是否仍然运行良好且稳定?现在是否有更好的方法来完成这些事情?我是否应该像其他工作伙伴开发人员所建议的那样从asp.net页面代码后面进行直接的SQL调用(虽然最后的解决方案让我感觉很蠢),而不是通过业务对象和数据层(数据层不是显示,但基本上保存所有存储的proc调用)。是的,另一位开发人员确实问我为什么要完成分层事务的工作,当你只需要在* .aspx.cs代码后面直接键入你需要的内容,然后我可以获得超过1k行代码的乐趣背后。这里有什么建议吗?
答案 0 :(得分:1)
您是否考虑使用像NHibernate这样的ORM?重新发明轮子毫无意义。
对我来说,这是一种代码味道:
BLLCustomer cust = ((BLLCustomer)new BLLCustomer()).FillByID(34);
支架太多了!
我发现在像C#这样的语言中使用活动记录模式总是以泪流满面,因为单元测试很难(呃)。
答案 1 :(得分:1)
从第一位代码到下一位代码的跳跃是巨大的。是否需要复杂的业务对象层将取决于所讨论的应用程序的大小。至少我们的政策是在处理它们的地方记录异常。您向用户呈现的方式取决于您,但拥有日志至关重要,以便开发人员可以在必要时获取更多信息。
答案 2 :(得分:0)
为什么不在Page_Load事件中捕获异常?您可能期望并知道如何处理的一些异常,其他异常应由全局异常处理程序处理。
答案 3 :(得分:0)
我的经验法则是只捕捉我可以处理的错误或给用户一些有用的东西,这样如果他们做了他们再做的任何事情,它可能对他们有效。我抓住数据库异常;但只是在有关正在使用的数据的错误上添加更多信息;然后我重新抛出它。一般来说,处理错误的最佳方法是在UI堆栈顶部的任何地方都不会捕获它们。只有一个页面来处理错误并使用global.asax路由到它几乎可以处理所有情况。使用状态代码绝对不合时宜。它是COM的残余。
答案 4 :(得分:0)
是否可以使用抽象基类而不是具体类?这会强制在开发时实现方法而不是运行时异常。
此处同意的最佳评论来自舞蹈,您只应处理此时可以恢复的异常。抓住他人和重新抛出是最好的方法(尽管我认为很少这样做)。另外,确保它们已被记录....:)
答案 5 :(得分:0)
你的错误处理方式似乎已经过时了。只需创建一个新的exeption并从exeption继承,至少你有callstack。然后你应该用nlog或log4net之类的东西登录。这是2008年,所以使用泛型。你将不得不以这种方式减少投射。
并且像之前所说的那样使用ORM。不要试图重新发明轮子。