gpt4 book ai didi

dependency-injection - 使用 f# 时如何不为 Api Controller 中的每个新方法触摸我的 DI 容器

转载 作者:行者123 更新时间:2023-12-04 17:46:07 26 4
gpt4 key购买 nike

在将 WebApi 与 Ninject 之类的东西一起使用时,我正试图围绕如何在 F# 中处理 DI。

例如,在 C# 中,当我连接容器时,我会简单地告诉 DI 类型解析为什么,例如:

kernel.Bind<ISomeInterface>().To<SomeClass>();

当 Controller 构造函数需要时,我的 Api Controller 将自动连接它。

太好了,现在我可以整天向界面和类添加方法,而无需再次触摸容器。

但是在 F# 中(除非我做的完全错误)我创建了部分应用程序,然后将它们传递给 Controller ​​,每次我添加一个方法时,我都必须在容器中再次连接。也许这是正确的,我不确定,但似乎布线要多得多。

为了澄清我的意思,让我们来看一个典型的 REST Api。对于每个具有 CRUD 的实体 - 例如:

客户(创建、读取、更新、删除)。

然后我是否必须将每个功能注入(inject) Controller ?

所以在这个例子中,假设我有服务 -> 域 -> repo 模型:
let createCustomerFunc = createCustomerDomainFunc createCustomerRepoFunc
let getAllCustomersFunc = getAllCustomerDomainFunc getAllCustomerRepoFunc
let updateCustomerFunc cust = [...]
let deleteCustomerFunc id = [...]
let getSingleCustomerFunc id = [...]

现在在我的容器中,当我绑定(bind)它时,我会执行以下操作:
kernel.Bind<CustomerController>().To<CustomerController>()
.WithConstructorArgument(createCustomerFunc, getAllCustomerFunc, etc...)
|> ignore

现在,如果我添加方法:GetActiveCustomers,我将不得不修改上面的代码以传递新的部分应用程序?

感觉……错了——我只是在错误地处理这个问题吗?

最佳答案

使用 DI 容器有一些优点,也有一些缺点。

主要优点是,如果 you have a large code base, you can use convention over configuration连接所有依赖项。通过约定优于配置,您还可以获得代码必须更加一致的好处,因为它必须遵循约定。

但是,有几个缺点。最直接的就是你失去了rapid feedback from the compiler .您可以更轻松地对系统进行更改,并且当一切都编译时,系统在运行时会失败。有人希望你可以请一个DI Container进行自我诊断,但是you can't .

使用 DI 容器的另一个不太明显的缺点是,正如您所说,向 Controller 等简单地添加更多成员变得太容易了。这实际上增加了耦合,或降低了内聚,但是 DI 提供的基于反射的自动化容器对您隐藏了这个问题。

由于我认为利大于弊,所以我建议您使用Pure DI而不是 DI 容器。

这适用于 C#、Visual Basic .NET 或 Java 中的面向对象编程,但同样适用于 F# 中的函数式编程。

在未混合的函数式 F# 代码库中,我根本不会使用类或接口(interface);相反,我只会将函数组合在一起。

在混合代码库中,例如ASP.NET Web API,我会尽快跨越 OOP 和 FP 之间的桥梁。正如我在我的 Test-Driven Development with F# 中解释的那样谈话( expanded material available on Pluralsight ),我会像这样将一个函数注入(inject)到 Controller 中:

type ReservationsController(imp) =
inherit ApiController()
member this.Post(rendition : ReservationRendition) : IHttpActionResult =
match imp rendition with
| Failure(ValidationError msg) -> this.BadRequest msg :> _
| Failure CapacityExceeded -> this.StatusCode HttpStatusCode.Forbidden :> _
| Success () -> this.Ok () :> _

这就是该 Controller 的整个代码库。所有行为均由 imp 实现.

在应用程序的启动代码中, imp是这样组成的:
let imp =
Validate.reservationValid
>> Rop.bind (Capacity.check 10 SqlGateway.getReservedSeats)
>> Rop.map SqlGateway.saveReservation

您可能会争辩说上述 ReservationsController只定义了一个 Post方法。如果 Controller 必须公开更多方法怎么办?

在这种情况下,为每个方法注入(inject)一个实现函数。在 REST API 中,任何 Controller 无论如何都应该只有 2-3 个方法,这实际上意味着 2-3 个依赖项。在我看来,这是完全可以接受的依赖数量。

2-3 方法最大值的原因是在适当的 RESTful 设计中,资源倾向于遵循一些交互模式:
  • GET
  • POST
  • POST , GET
  • PUT , GET
  • DELETE , GET
  • PUT , DELETE , GET

  • 完整的组合( POSTGETPUTDELETE)是 REST 设计的味道,但所有这些都是完全不同的讨论。

    关于dependency-injection - 使用 f# 时如何不为 Api Controller 中的每个新方法触摸我的 DI 容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34191800/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com