我们子公司的IT部门有一家咨询公司为他们编写了一个ASP.NET应用程序。现在它出现了间歇性问题,混淆了当前用户是谁,并且已经知道他错误地向Joe展示了一些Bob的数据。
顾问们被带回来进行故障排除,我们被邀请听取他们的解释。有两件事情发生了。
首先,顾问主管提供了这个伪代码:
void MyFunction()
{
Session["UserID"] = SomeProprietarySessionManagementLookup();
Response.Redirect("SomeOtherPage.aspx");
}
他继续说会话变量的赋值是异步的,这似乎是不真实的。对查询函数的调用可以异步执行某些操作,但这似乎是不明智的。
鉴于所谓的异步性,他的理论是在重定向不可避免的ThreadAbort异常被引发之前没有分配会话变量。然后,此故障阻止SomeOtherPage显示正确的用户数据。
其次,他举了一个他推荐的编码最佳实践的例子。而不是写:
int MyFunction(int x, int x)
{
try
{
return x / y;
}
catch(Exception ex)
{
// log it
throw;
}
}
他推荐的技术是:
int MyFunction(int x, int y, out bool isSuccessful)
{
isSuccessful = false;
if (y == 0)
return 0;
isSuccessful = true;
return x / y;
}
这肯定会起作用,在某些情况下从性能角度来看可能更好。
然而,从这些和其他讨论点来看,我们认为这个团队在技术上并不精通。
评论
答案 0 :(得分:19)
我倾向于同意这里。显然你没有提供太多,但他们似乎没有提供足够的能力。
答案 1 :(得分:14)
我同意。这些家伙似乎很无能。
(顺便说一句,我要查看是否在“SomeProprietarySessionManagementLookup”中,他们正在使用静态数据。看到这个 - 行为完全按照你描述的几个月前继承的项目当我们终于看到它时,这是一个彻头改变的时刻...并且希望我们能够与写下它的人面对面...)
答案 2 :(得分:12)
如果顾问编写了一个应该能够跟踪用户的应用程序并且只向正确的用户显示正确的数据并且它没有这样做,那么显然有些错误。一个好的顾问会发现问题并解决它。一个糟糕的顾问会告诉你这是异步性的。
答案 3 :(得分:8)
在异步部分,唯一可行的方法是,如果正在进行的赋值实际上是Session上的索引器设置器,它隐藏了一个没有回调的异步调用,表明成功/失败。这似乎是一个可怕的设计选择,它看起来像你的框架中的核心类,所以我发现它不太可能。
通常,异步调用可以指定回调,以便您可以确定结果是什么,或者操作是否成功。 Session的文档应该非常清楚,如果它实际上隐藏了一个异步调用,但是......看起来不像顾问知道他在说什么......
分配给Session索引器的方法调用不能是异步的,因为要异步获取值,你必须使用回调......没办法,所以如果没有明确的回调,那肯定不是asynch(好吧,内部可能有一个异步调用,但是方法的调用者会认为它是同步的,所以如果内部方法例如异步调用Web服务则无关紧要。)
对于第二点,我认为这会好得多,并且基本保持相同的功能:
int MyFunction(int x, int y)
{
if (y == 0)
{
// log it
throw new DivideByZeroException("Divide by zero attempted!");
}
return x / y;
}
答案 4 :(得分:5)
对于第一点,这确实看起来很奇怪。
在第二个方面,尝试避免除以0是合理的 - 它完全可以避免,并且避免很简单。但是,在某些情况下使用out参数来指示成功是合理的,例如int.TryParse和DateTime.TryParseExact - 调用者无法轻易确定其参数是否合理。即使这样,返回值通常是成功/失败,out参数是方法的结果。
答案 5 :(得分:4)
Asp.net会议,如果您使用的是内置提供商,则不会意外地给您其他人的会话。 SomeProprietarySessionManagementLookup()
可能是罪魁祸首,正在返回坏的价值或者只是不工作。
Session["UserID"] = SomeProprietarySessionManagementLookup();
首先从异步分配返回值SomeProprietarySessionManagementLookup()就不会起作用了。顾问代码可能看起来像:
public void SomeProprietarySessionManagementLookup()
{
// do some async lookup
Action<object> d = delegate(object val)
{
LookupSession(); // long running thing that looks up the user.
Session["UserID"] = 1234; // Setting session manually
};
d.BeginInvoke(null,null,null);
}
顾问并不是完全满足于BS,但是他们写了一些错误的代码。 Response.Redirect()确实抛出一个ThreadAbort,如果专有方法是异步的,asp.net 不知道等待异步方法在asp.net本身保存之前写回会话会议。这可能是它有时有效的原因,有时则不然。
如果asp.net会话在进程中,它们的代码可能会起作用,但状态服务器或数据库服务器不会。这取决于时间。
我测试了以下内容。我们在开发中使用状态服务器。此代码有效,因为会话是在主线程完成之前写入的。
Action<object> d = delegate(object val)
{
System.Threading.Thread.Sleep(1000); // waits a little
Session["rubbish"] = DateTime.Now;
};
d.BeginInvoke(null, null, null);
System.Threading.Thread.Sleep(5000); // waits a lot
object stuff = Session["rubbish"];
if( stuff == null ) stuff = "not there";
divStuff.InnerHtml = Convert.ToString(stuff);
下一段代码不起作用,因为在异步方法设置会话值时会话已经保存回状态服务器。
Action<object> d = delegate(object val)
{
System.Threading.Thread.Sleep(5000); // waits a lot
Session["rubbish"] = DateTime.Now;
};
d.BeginInvoke(null, null, null);
// wait removed - ends immediately.
object stuff = Session["rubbish"];
if( stuff == null ) stuff = "not there";
divStuff.InnerHtml = Convert.ToString(stuff);
第一步是让顾问使他们的代码同步,因为他们的性能技巧根本不起作用。如果这样可以解决问题,请让顾问使用Asynchronous Programming Design Pattern
正确实施答案 6 :(得分:2)
我同意他的一部分 - 将y检查为零而不是捕获(昂贵的)异常肯定会更好。 out bool isSuccessful似乎真的和我约会,但无论如何。
re:异步sessionid buffoonery - 可能是也可能不是,但听起来好像是顾问为了掩护而冒烟。
答案 7 :(得分:1)
Cody's rule of thumb is dead right.如果你不得不问,他可能不会。
看起来第二点显然不正确。 .NET的标准解释说,如果一个方法失败,它应该抛出一个异常,它似乎更接近原始;不是顾客的建议。假设异常是准确的&amp;具体描述失败。
答案 8 :(得分:1)
顾问们首先创建了代码吗?它不起作用。我觉得你已经有了很多污垢。
异步回答听起来像BS,但可能有一些内容。据推测,他们提供了一个合适的解决方案以及描述他们自己创建的问题的伪代码。我更倾向于在他们的解决方案上判断他们,而不是他们对问题的表达。如果他们的理解存在缺陷,那么他们的新解决方案也无济于事。然后你就会知道他们是白痴。 (事实上,看看你的代码的其他任何领域是否有相似的证据)
另一个是代码风格问题。有很多不同的方法可以解决这个问题。我个人不喜欢这种风格,但在某些情况下它会适合。
答案 9 :(得分:1)
他们在异步上错了。
分配发生,然后页面重定向。该函数可以异步启动并返回(甚至可以以自己的方式改变Session),但无论它返回什么,都必须在重定向之前给出的代码中分配。
他们在任何低级代码中的防御性编码风格上都是错误的,甚至在更高级别的函数中也是如此,除非它是一个特定的商业案例,0或NULL或空字符串或其他任何东西应该以这种方式处理 - 其中情况下,它总是成功的(成功的标志是令人讨厌的代码味道)并且不是例外。例外是例外。你不想通过溺爱函数的调用者来掩盖这样的行为。尽早抓住并抛出异常。我认为Maguire在编写固体代码或代码完成中的McConnell中涵盖了这一点。无论哪种方式,它都闻起来。
答案 10 :(得分:1)
这家伙不知道他在做什么。明显的罪魁祸首就在这里:
Session["UserID"] = SomeProprietarySessionManagementLookup();
答案 11 :(得分:0)
而不是堆积在顾问身上,你可以轻易地抓住购买他们服务的人。没有顾问是完美的,也不是招聘经理......但是在一天结束时,你应该采取的真正方向非常明确:而不是试图找出错误你应该花费精力去协同寻找解决方案即可。无论一个人如何熟练地担任他们的角色和责任,他们肯定会有不足之处。如果您确定存在无能力模式,那么您可以选择转换到另一个资源,但是分配责备从未解决过历史中的单个问题。
答案 12 :(得分:0)
这整个答案流都充满了典型的程序员态度。这让我想起了Joel的“你永远不应该做的事”文章(从头开始重写。)我们对这个系统一无所知,除了有一个bug,还有一些人在线发布了一些代码。有这么多未知数,说“这家伙不知道他在做什么”是荒谬的。
答案 13 :(得分:0)
如果顾问在您的服务器上部署了他们的ASP.NET应用程序,那么他们可能已经以未编译的形式部署了它,这意味着会有一堆* .cs文件浮动,您可以查看。< / p>
如果您能找到的只是他们编译的.NET程序集(DLL和EXE),那么您仍然可以将它们反编译成可读的源代码。我敢打赌,如果你查看代码,你会发现他们在他们的专有查找代码中使用静态变量。然后你会有一些非常具体的东西来展示你的老板。
答案 14 :(得分:0)
如果SomeProprietarySessionManagementLookup();正在进行异步分配,它更像是这样:
SomeProprietarySessionManagementLookup(Session["UserID"]);
代码将结果分配给Session [“UserID”]这一事实表明它不应该是异步的,并且应该在调用Response.Redirect之前获得结果。如果SomeProprietarySessionManagementLookup在计算结果之前返回,则无论如何它们都存在设计缺陷。
抛出异常或使用out参数是一个意见和环境的问题,在实际操作中不会构成任何方式的豆子。对于成为问题的异常性能,您需要多次调用该函数,这可能本身就是一个问题。
答案 15 :(得分:0)
会话是可能的。毫无疑问,这是一个错误,但可能是写入到达您在下次读取后使用的任何自定义会话状态提供程序。会话状态提供程序API适应锁定以防止此类事情,但如果实现者忽略了所有这些,那么您的顾问可能会说实话。
第二个问题也有点有效。它不是惯用的 - 它是像int.TryParse这样的稍微颠倒的版本,它可以避免因抛出大量异常而导致的性能问题。但是,除非你将这些代码调用得非常多,否则它不太可能产生明显的差异(相比之下,每页少一个数据库查询等)。这肯定不是你应该默认做的事情。
答案 16 :(得分:0)
我猜你的顾问建议使用状态变量而不是异常进行错误处理是一种更好的做法?我不同意。人们多久忘记或懒得对错误检查返回值?此外,通过/失败变量不提供信息。除了整数x / y太大或x是NaN之外,还有更多的事情可以出错。当出现问题时,状态变量无法告诉您出了什么问题,但异常可以。特殊情况除外,除以零或NaN绝对是例外情况。
答案 17 :(得分:0)
很奇怪。在第二项上,它可能会更快,也可能不会更快。它当然不是相同的功能。
答案 18 :(得分:0)
在会话中存储非异步。除非该函数是异步的,否则这不是真的。但即使这样,因为它没有调用BeginCall并且在完成时调用了某些东西,所以在Session行完成之前,下一行代码将不会执行。
对于第二个声明,虽然可以使用,但这不是最佳做法,您需要注意一些事项。你可以节省抛出异常的成本,但是你不想知道你试图除以零而不是仅仅移动它吗?
我认为这根本不是一个可靠的建议。
答案 19 :(得分:0)
我必须同意John Rudy的观点。我的直觉告诉我问题出在SomeProprietarySessionManagementLookup()中。
..而你的顾问听起来并不确定。
答案 20 :(得分:0)
典型的“顾问”bollocks:
try..catch
,但抛出只应出现在异常的情况下。如果变量y
不应为零,则ArgumentOutOfRangeException
是合适的。答案 21 :(得分:-1)
关于第二点,我不会在这里使用例外。特殊情况保留例外情况 但是,将任何东西除以零肯定不等于零(至少在数学中),所以这将是特定于案例的。