代码重复 C# 的最佳实践
我正在尝试以减少/避免代码重复的方式构建我的代码,并且我遇到了一个有趣的问题。 每次我的代码调用存储过程时,我都需要传递存储过程常用的一些变量:例如用户名、域、server_ip 和 client_ip。 这些都来自 HttpRequest 对象或 system.environment 对象。
由于这些被传递到每个存储过程,我最初的想法是创建一个实用程序类,它是一个数据库包装器,并且每次都会初始化并传递这些,所以我不必在我的代码中这样做。 问题是 c# 类(在 App_Code 文件夹内)看不到 Httprequest 对象。 当然,我可以将其作为参数传递给包装器,但这将破坏创建包装器的整个目的。 我在这里错过了什么吗?
我意识到每次调用存储过程时重复 4 行代码并不是什么大问题,但我宁愿在早期阶段消除代码重复。
I am trying to structure my code in such a way to reduce/avoid code duplication and I have encountered an interesting problem. Every time my code invokes a stored proc, I need to pass few variables that are common to the stored proc: such as username, domain, server_ip and client_ip. These all come from either HttpRequest object or a system.environment object.
Since these are passed to every stored proc, my initial thought was to create a utility class that is a database wrapper and will initialize and pass these every time, so I don't have to do it in my code.
The problem is though that c# class (inside App_Code folder) doesn't see Httprequest object. Of course, I could pass this as an argument to the wrapper, but that would defeat the whole purpose of creating the wrapper. Am I missing something here?
I realize it's not such a huge deal to repeat 4 lines of code each time I call a stored proc, but I would rather eliminate the code duplication at the very early stages.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
尝试 System.Web.HttpContext.Current.Request 获取当前请求。
Try System.Web.HttpContext.Current.Request to get the current request.
你可能正走下滑坡。 DRY 的要点是不要在多个地方重复业务逻辑,因为需求的更改会导致需要在多个类似的地方更改代码。 如果这 4 行是上下文相关的,那么您不必仅仅因为这 4 行是相同的就进行重构。 您还通过引用 httprequest 破坏了封装,因为您正在使用全局变量。 作为您班级的使用者,我必须了解实现细节,我只能从 Web 应用程序中调用您。
话虽这么说,如果您考虑到这一点并且仍然想继续,这里是此类信息的另一种选择。 创建一个包含所需属性的自定义 SecurityPrincipal(实现 IPrincipal)并将其附加到线程。 当用户登录时填写它们,然后您可以在请求期间的任何地方访问它。 您的调用者仍然需要确保这已完成,但至少它不是特定于平台的。
否则,为了获得最佳封装,请将具有所需属性的类传递到需要使用这些属性的每个对象的构造函数中。
You are possibly headed down a slippery slope. The point to DRY is to not repeat business logic in multiple places where a change in requirement creates the need to change code in multiple similar places. You don't necessarily refactor just because 4 lines are the same if those 4 lines are context dependent. You have also broken encapsulation by referencing the httprequest in that you are using a global variable. As a consumer of you class I would have to know the implementation detail that I could only call you from a web application.
That being said, if you take that into account and still want to proceed, here is another option for information like this. Create a custom SecurityPrincipal (Implement IPrincipal) that contains the properties you need and attach it to the thread. Fill them when the user logs in and then you can access it anywhere during the request. Your caller would still need to make sure this was done but at least it isn't platform specific.
Otherwise for the best encapsulation, pass in a class with the properties you need into the constructor for each object that needs to consume those properties.
将数据层设置为从基类继承,该基类包含这些值的 4 个属性。 使公共构造函数需要这 4 个属性。
然后在业务层中执行类似的操作 - 基类在构造函数中使用这 4 个属性。
然后 UI 执行 new BusObj( Request["username"], ... ).method()
在数据层中,您可以使用一个方法来构建具有这 4 个属性的 SQLParameter 数组,然后每个方法都可以向大批。
Set up your data layer to inherit from a base class which contains 4 properties for those values. Make the public constructor require those 4 properties.
Then do something similar in the business layer - base class with those 4 properties in the constructor.
Then the UI does new BusObj( Request["username"], ... ).method()
Within the data layer you can have a method that builds a SQLParameter array with those 4 properties, then each method can add additional parameters to the array.
作为一般规则,无论编程语言如何,如果您可以眯起眼睛并且代码看起来相同,您应该从中创建一个函数/方法/消息并传递参数。
一旦你有需要大量参数的方法(4 是一个很好的经验法则,但它绝对是具体情况的基础),那么就需要考虑另一件事,是时候让该方法采用对象作为参数了而不是单独的参数。 99.99999999999999999999% 的时间这样的对象应该是不可变的(没有可写的实例变量)。
As a general rule regardless of programming language, if you can squint your eyes and the code looks the same you should make a function/method/message out of it and pass the parameters.
Another thing to look at once you have methods that take a large number of parameters (4 is a good rule of thumb, but it is definatly a case-by-case basis) it is time to make that method take an object as a parameter instead of individual parameters. 99.99999999999999999999% of the time such an object should be immutable (no writeable instance variables).
HttpContext.Current 与您在 HttpRequest 中找到的信息类似,更重要的是在 App_Code 中可用。
HttpContext.Current has similar information to what you find in HttpRequest and more importantly is available inside App_Code.
这是一个你可能喜欢也可能不喜欢的奇怪想法:定义一个“配置文件”类和一个函数,该函数将配置文件扩展为采用公共参数的函数的参数。
在具有 AddressOf 运算符的 VB.net 中,它可能会工作得更好。 我会非常谨慎地使用这种类型的东西,因为你很容易损害可读性和封装性。
Here's a weird idea you may or may not like: define a 'profile' class and a function that expands the profile into the arguments of functions taking the common arguments.
It might work better in VB.net where you have the AddressOf operator. I would be really cautious using this type of thing, because you could easily damage readability and encapsulation.
我会保持你现在的样子。 它更干净,更容易扩展/修改,并且更容易进行单元测试。
至于像其他人建议的那样使用 HttpContext ,我想说这是一个坏主意。 一旦开始在域中引入 HttpContext 的依赖关系,就很难将其删除。 如果稍后您想在没有 HttpContext 的情况下使用模块怎么办? 单元测试怎么样?
I would keep it the way you have it now. It's cleaner, easier to extend/modify, and easier to unit test.
As for using HttpContext instead as some others have suggested, I would say that it is a bad idea. Once you start introduce dependencies in your domain on HttpContext, it's very difficult to take it out. What if later on you wanted to use your module without an HttpContext? What about unit testing it?