我应该如何提供对此自定义 DAL 的访问?

发布于 2024-09-01 18:54:14 字数 1408 浏览 1 评论 0原文

我正在为订购系统项目编写自定义 DAL (VB.NET)。我想解释一下它现在是如何编码的,并接受一些替代想法,以使针对 DAL 的编码更容易/更具可读性。 DAL 是 n 层(不是 n 层)应用程序的一部分,其中每一层都位于其自己的程序集/DLL 中。

DAL 由几个具有特定行为的类组成。例如,有一个 Order 类,负责检索和保存订单。大多数类只有两个方法,“Get”和“Save”,每个方法都有多个重载。这些类被标记为 Friend 并且仅对 DAL(位于其自己的程序集中)可见。

在大多数情况下,DAL 返回我所说的“数据对象”。该对象是一个仅包含数据和验证的类,位于 BLL 和 DAL 都可以读取的公共程序集中。

为了提供对 DAL 的公共访问,我当前有一个具有许多共享成员的静态(模块)类。简化版本看起来像这样:

Public Class DAL
    Private Sub New
    End Sub

    Public Shared Function GetOrder(OrderID as String) as OrderData

        Dim OrderGetter as New OrderClass
        Return OrderGetter.GetOrder(OrderID)

    End Function

End Class

Friend Class OrderClass
    Friend Function GetOrder(OrderID as string) as OrderData
    End Function
End Class

BLL 会要求这样的命令:

DAL.GetOrder("123456")

正如你可以想象的,这很快就会变得很麻烦。我主要感兴趣的是构建对 DAL 的访问,以便智能感知非常直观。就目前而言,DAL 类中有太多名称相似的方法/函数。

我的一个想法是将 DAL 分解为嵌套类:

Public Class DAL
    Private Sub New
    End Sub

    Public Class Orders
        Private Sub New
        End Sub

        Public Shared Function Get(OrderID as string) as OrderData
        End Function

    End Class

End Class

所以 BLL 会这样调用:

DAL.Orders.Get("12345")

这会稍微清理一下,但它留下了很多只引用其他类的类,这是我不喜欢的由于某种原因。

在不诉诸从 BLL 到 DAL 传递数据库特定指令(例如 where 子句)的情况下,为 DAL 提供单点访问的最佳或最常见做法是什么?

I'm writing a custom DAL (VB.NET) for an ordering system project. I'd like to explain how it is coded now, and receive some alternate ideas to make coding against the DAL easier/more readable. The DAL is part of an n-tier (not n-layer) application, where each tier is in it's own assembly/DLL.

The DAL consists of several classes that have specific behavior. For instance, there is an Order class that is responsible for retrieving and saving orders. Most of the classes have only two methods, a "Get" and a "Save," with multiple overloads for each. These classes are marked as Friend and are only visible to the DAL (which is in it's own assembly).

In most cases, the DAL returns what I will call a "Data Object." This object is a class that contains only data and validation, and is located in a common assembly that both the BLL and DAL can read.

To provide public access to the DAL, I currently have a static (module) class that has many shared members. A simplified version looks something like this:

Public Class DAL
    Private Sub New
    End Sub

    Public Shared Function GetOrder(OrderID as String) as OrderData

        Dim OrderGetter as New OrderClass
        Return OrderGetter.GetOrder(OrderID)

    End Function

End Class

Friend Class OrderClass
    Friend Function GetOrder(OrderID as string) as OrderData
    End Function
End Class

The BLL would call for an order like this:

DAL.GetOrder("123456")

As you can imagine, this gets cumbersome very quickly. I'm mainly interested in structuring access to the DAL so that Intellisense is very intuitive. As it stands now, there are too many methods/functions in the DAL class with similar names.

One idea I had is to break down the DAL into nested classes:

Public Class DAL
    Private Sub New
    End Sub

    Public Class Orders
        Private Sub New
        End Sub

        Public Shared Function Get(OrderID as string) as OrderData
        End Function

    End Class

End Class

So the BLL would call like this:

DAL.Orders.Get("12345")

This cleans it up a bit, but it leaves a lot of classes that only have references to other classes, which I don't like for some reason.

Without resorting to passing DB specific instructions (like where clauses) from BLL to DAL, what is the best or most common practice for providing a single point of access for the DAL?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

无尽的现实 2024-09-08 18:54:14

你所做的确实是一个进步。您已经创建了一系列存储库类,它们的工作就是向您返回实体。您还可以将 Write/Update/Delete 方法放入新的 Orders 对象中,并将这些内容放在一处,这很好。

你最后提出的问题是我们大家都会处理的问题。如果您曾经想知道为什么 LINQ-to-SQL 或 LINQ-to-Entities 很好,这就是原因。它与 IQuerable 接口有关。使用这两种技术中的任何一种,您的 Get 方法都可以返回 IQuerable(Order) (似乎返回所有可能的订单对象),然后您可以对其执行 LINQ;这实际上会将您的业务标准应用到生成的 SQL 查询!乍一看似乎很神奇,但它与 IQuerable 的本质有关。

如果您使用这样的“db”LINQ 提供程序,那么您必须赢得专业标签并以老式方式进行操作:聪明点。您仍然想利用 LINQ 到对象的优势!哦,是的,你知道。您可以添加一个 GetByCustomer(CustomerID As Int) 来获取客户 ID 并返回 IEnumerable(Order),这将返回 0 条记录、10 条或 50 条记录,具体取决于记录。 (您的 Customer 对象可能有一个 Orders 属性,它将返回此方法的结果。)但是您不希望为每种可能性不断添加自定义方法。假设您经常希望获得客户的最新订单;您可能想要添加一个GetLatestByCustomer(CustomerID as Int)

但是,当您仅需要一次客户第二次订单时该怎么办?向 DAL 对象添加一个方法 GetSecondOrderByCustomer() 会很疯狂,对吧?此时,我们的 DAL 对象确实变得混乱了。在这种情况下,您可以针对 GetByCustomer() 方法使用 LINQ:

Dim 2ndOrder As Order = (From O in DAL.Orders.GetByCustomer("123")
                            Order By OrderDate).Skip(1).Take(1)

现在,这非常干净、清晰,并且您不会用我们必须考虑的非常专业的内容来弄乱您的 DAL。要求。但请注意,在幕后您会收到给定客户的每一个订单!这不应该是世界末日;我确定您的 Orders 表有一个按 CustomerID 的索引,对吧?但是,有时您会取回 60 条订单记录,只是为了获取第二条订单记录。

如果在某些时候您有一个非常疯狂的查询需求(获取两个日期之间的所有订单,交付到威斯康星州密尔沃基,通过信用卡支付,通过传真收到),那么您可能必须提供直接 SQL 选项在你的 DAL 中。如果可以的话,你想努力避免这种情况,因为那样你就会失去你的抽象。您可以添加Orders.GetBetweenDates(FirstDate As date, SecondDate As date)。是的 。 。 。但看到危险了吗?后来的一些开发人员可能会调用它来获取几年内的所有订单,然后使用 LINQ 进一步过滤。这很容易导致幕后的表扫描。这些是您必须考虑的事情类型。

你必须谨慎。当你想要抽象(这很好!)时,你必须做出权衡。 LINQ-to-SQL 等的吸引力在于,您可以执行类似于上面示例的 LINQ 查询,并且它只会从数据库获取一条记录!

What you have done is indeed an improvement. You have made a series of Repository classes who's job in life it to return you the entities. You can also put your Write/Update/Delete methods in your new Orders object and have these things on one place, which is good.

You problem you bring up at the end is an issue we all deal with. If you've ever wondered why LINQ-to-SQL or LINQ-to-Entities is good, this is why. It has to do with the IQuerable interface. Using either of these technologies, your Get method could return an IQuerable(Order) (seemingly returning every order object possible), which you could then do LINQ against; this would actually apply your business criteria to the generated SQL query! It seems like magic at first, but has to do with the nature of the IQuerable.

If you're not using such a 'db' LINQ provider, then you have to earn your professional label and do it the old fashioned way: be clever. You still want to take advantage of LINQ-to-object! Oh yes you do. You can add a GetByCustomer(CustomerID As Int) that takes a customer ID and returns an IEnumerable(Order), and this would return 0 records, or 10 or 50, depending on the records. (Your Customer object would probably have an Orders property that would return the results of this method.) But you don't want to keep adding custom methods for every eventuality. Let's say you often want to get the Customer's latest order; you probably want to add a GetLatestByCustomer(CustomerID as Int).

But what about when you need, just one time, a customers second order. It would be crazy to add a method to you DAL object, GetSecondOrderByCustomer(), right? At this point we're really cluttering up our DAL object. In this case, you could use LINQ against your GetByCustomer() method:

Dim 2ndOrder As Order = (From O in DAL.Orders.GetByCustomer("123")
                            Order By OrderDate).Skip(1).Take(1)

Now, this is pretty clean, clear, and you're not cluttering up your DAL with what we would have to consider a pretty specialized request. Note though that behind the scenes you are getting every order for the given customer! This should not be the end of the world; I'm sure your Orders table has an index by CustomerID, right? But, sometimes you're going to get back 60 order records just to get that 2nd one.

If at some point you have a really crazy query need (get all the orders between two dates, delivered to Milwaukee WI, paid for by credit card, that came in by fax) then you'll probably have to provide a straight-sql option in your DAL. You want to fight to avoid this if you can though, because then you're losing your abstraction. Could you add Orders.GetBetweenDates(FirstDate As date, SecondDate As date). Yeah . . . but see the danger? Some later developer might call that to get all the orders for a couple years and then use LINQ to filter further. This could easily cause a table scan behind the scenes. These are the types of things you have to consider.

You have to be prudent. When you want to abstract (which is good!), you have to make tradeoffs. The attraction of LINQ-to-SQL and such is that you can do a LINQ query similar to the example above and it will just get one record from the db!

半山落雨半山空 2024-09-08 18:54:14

我建议的一项更改是将 DAL 类基于接口并添加构造函数重载,这将允许您注入依赖项,例如 ORM 组件、连接、命令等......这有很多优点,其中最重要的是您可以单独注入模拟以进行单元测试,并且可以模拟 DAL 本身以进行消费层测试。

这在某种程度上是一个人为的示例,但这是我可以建议的模式的一个非常基本的示例,

Public Interface IDal
    Function GetData(ByVal OrderID As String) As String
End Interface

Public Class Dal : Implements IDal
    Private _connection As IDbConnection

    Public Sub New()
        Me.New(IoC.Resolve(Of IDbConnection))
    End Sub

    Friend Sub New(ByVal Connection As IDbConnection)
        _connection = Connection
    End Sub

    Public Function GetData(ByVal OrderID As String) As String Implements IDal.GetData
        ' Use injected connection and Get the data
    End Function
End Class

请记住,这只是为了说明您可以根据需要修改的模式。请注意构造函数的 Friend 范围。这实际上只意味着由 Frield 类和程序集(例如测试程序集)调用,尽管您当然可以将其公开并让使用层注入它们自己的所有依赖项。

“IoC.Resolve(Of IDbConnection)”位引入了使用您选择的控制反转 (IoC) 容器的概念,尽管实际上并不需要它。只要确保您使用默认的具体实现来更新您的依赖项(如果没有注入)。依赖注入和 IoC 是非常强大的模式,非常适合在 DAL 中使用。希望这有帮助。

One change I would suggest would be to base the DAL class on an interface and add constructor overloads that would allow you to inject dependencies such as ORM Components, Connections, Commands, etc... This has a number of advantages, with the most important being that you can inject mocks for unit testing in isolation and the DAL itself can be mocked for consuming layer testing.

This is somewhat of a constrived sample, but here is a really basic example of the pattern I can suggesting

Public Interface IDal
    Function GetData(ByVal OrderID As String) As String
End Interface

Public Class Dal : Implements IDal
    Private _connection As IDbConnection

    Public Sub New()
        Me.New(IoC.Resolve(Of IDbConnection))
    End Sub

    Friend Sub New(ByVal Connection As IDbConnection)
        _connection = Connection
    End Sub

    Public Function GetData(ByVal OrderID As String) As String Implements IDal.GetData
        ' Use injected connection and Get the data
    End Function
End Class

Keep in mind, that is just to illustrate the pattern which you can modified as needed. Note the Friend scope of he constructor. This is really only meant to be called by Frield classes and assemblies such as test assemblies, although you could certainly make it public and let consuming layers inject all their own depencies.

That "IoC.Resolve(Of IDbConnection)" bit pulls in the notion of using an Inversion of Control (IoC) container of your choosing although it is not really needed. Just make sure you New-up your dependencies using default concrete implementations if none were injected. Dependency Injection and IoC are really powerfull patterns which are ideal for use in a DAL. Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文