了解如何初始化 Vapor 4 存储库
Understanding how to initialize a Vapor 4 repository
我正在尝试使用存储库模式将一些代码从 Vapor 3 迁移到 Vapor 4。我已经阅读了 Vapor 4 文档中的 documentation of this specific pattern,我想我大部分都理解了。
但是,我没有得到的一件事是在 Application
扩展中设置存储库工厂的方式。文档中的示例显示了这一点:
extension Application {
private struct UserRepositoryKey: StorageKey {
typealias Value = UserRepositoryFactory
}
var users: UserRepositoryFactory {
get {
self.storage[UserRepositoryKey.self] ?? .init()
}
set {
self.storage[UserRepositoryKey.self] = newValue
}
}
}
如果我正确地阅读了 getter 方法(我可能不是 - 我远不是 Swift 专家),UserRepositoryFactory
结构的新实例将在引用 app.users
时创建并返回。然而,那时 self.storage[UserRepositoryKey.self]
的内容似乎没有任何变化。因此,如果我碰巧连续两次访问 app.users
,我将返回 2 个 不同的 个实例,并且 self.storage[UserRepositoryKey.self]
将保持设置为 nil
.
按照文档中的其余示例代码,它似乎定义了工厂在配置应用程序时将使用的 make
函数:
app.users.use { req in
DatabaseUserRepository(database: req.db)
}
这里似乎 app.users.use
会得到一个新的工厂实例并调用其 use
函数来为该实例设置适当的 make
方法。
稍后,当我去处理一个请求时,我使用了这个 Request
扩展定义的 request.users
方法:
extension Request {
var users: UserRepository {
self.application.users.make!(self)
}
}
这里似乎 self.application.users.make
将在 self.application.users
引用的不同存储库工厂实例上调用。因此,它不会应用先前在配置应用程序时设置的工厂 make 方法。
那么我在这里错过了什么?
看起来文档有点过时了。您可以看看视图或客户端是如何完成的,但是您需要在某个地方调用 initialize()
来设置存储库。这是我的工作存储库的样子:
import Vapor
extension Application {
struct Repositories {
struct Provider {
let run: (Application) -> ()
public init(_ run: @escaping (Application) -> ()) {
self.run = run
}
}
final class Storage {
var makeRepository: ((Application) -> APIRepository)?
init() { }
}
struct Key: StorageKey {
typealias Value = Storage
}
let application: Application
var repository: APIRepository {
guard let makeRepository = self.storage.makeRepository else {
fatalError("No repository configured. Configure with app.repositories.use(...)")
}
return makeRepository(self.application)
}
func use(_ provider: Provider) {
provider.run(self.application)
}
func use(_ makeRepository: @escaping (Application) -> APIRepository) {
self.storage.makeRepository = makeRepository
}
func initialize() {
self.application.storage[Key.self] = .init()
}
private var storage: Storage {
if self.application.storage[Key.self] == nil {
self.initialize()
}
return self.application.storage[Key.self]!
}
}
var repositories: Repositories {
.init(application: self)
}
}
它会在第一次使用时自动初始化。请注意,APIRepository
是用于我的存储库的协议。 FluentRepository
是该协议的 Fluent 实现。然后像你一样我有一个关于请求的扩展以在请求处理程序中使用它:
extension Request {
var repository: APIRepository {
self.application.repositories.repository.for(self)
}
}
最后,您需要配置它以使用正确的存储库。所以在我的 configure.swift 我有:
app.repositories.use { application in
FluentRepository(database: application.db)
}
在测试中,我可以将它切换到不接触数据库的 in-memory 存储库:
application.repositories.use { _ in
return inMemoryRepository
}
我已经设法从文档中获取示例 as-is。
通过调试器跟踪执行过程,如您所说,可以预测对 get
的调用,而这个 returns 来自 .init()
的实例作为故障转移先前存储的值。您引用的示例中包括:
struct UserRepositoryFactory {
var make: ((Request) -> UserRepository)?
mutating func use(_ make: @escaping ((Request) -> UserRepository)) {
self.make = make
}
}
接下来执行这个use
函数,即mutating
并更新变量make
。我相信正是对 make
的更改触发了对 set
的调用。它肯定会在 use
之后和 configure.swift
继续执行之前立即发生。因此,当服务器正式启动并且您实际在路由中使用 Repository
时,有一个存储的实例可以根据需要重用。
我正在尝试使用存储库模式将一些代码从 Vapor 3 迁移到 Vapor 4。我已经阅读了 Vapor 4 文档中的 documentation of this specific pattern,我想我大部分都理解了。
但是,我没有得到的一件事是在 Application
扩展中设置存储库工厂的方式。文档中的示例显示了这一点:
extension Application {
private struct UserRepositoryKey: StorageKey {
typealias Value = UserRepositoryFactory
}
var users: UserRepositoryFactory {
get {
self.storage[UserRepositoryKey.self] ?? .init()
}
set {
self.storage[UserRepositoryKey.self] = newValue
}
}
}
如果我正确地阅读了 getter 方法(我可能不是 - 我远不是 Swift 专家),UserRepositoryFactory
结构的新实例将在引用 app.users
时创建并返回。然而,那时 self.storage[UserRepositoryKey.self]
的内容似乎没有任何变化。因此,如果我碰巧连续两次访问 app.users
,我将返回 2 个 不同的 个实例,并且 self.storage[UserRepositoryKey.self]
将保持设置为 nil
.
按照文档中的其余示例代码,它似乎定义了工厂在配置应用程序时将使用的 make
函数:
app.users.use { req in
DatabaseUserRepository(database: req.db)
}
这里似乎 app.users.use
会得到一个新的工厂实例并调用其 use
函数来为该实例设置适当的 make
方法。
稍后,当我去处理一个请求时,我使用了这个 Request
扩展定义的 request.users
方法:
extension Request {
var users: UserRepository {
self.application.users.make!(self)
}
}
这里似乎 self.application.users.make
将在 self.application.users
引用的不同存储库工厂实例上调用。因此,它不会应用先前在配置应用程序时设置的工厂 make 方法。
那么我在这里错过了什么?
看起来文档有点过时了。您可以看看视图或客户端是如何完成的,但是您需要在某个地方调用 initialize()
来设置存储库。这是我的工作存储库的样子:
import Vapor
extension Application {
struct Repositories {
struct Provider {
let run: (Application) -> ()
public init(_ run: @escaping (Application) -> ()) {
self.run = run
}
}
final class Storage {
var makeRepository: ((Application) -> APIRepository)?
init() { }
}
struct Key: StorageKey {
typealias Value = Storage
}
let application: Application
var repository: APIRepository {
guard let makeRepository = self.storage.makeRepository else {
fatalError("No repository configured. Configure with app.repositories.use(...)")
}
return makeRepository(self.application)
}
func use(_ provider: Provider) {
provider.run(self.application)
}
func use(_ makeRepository: @escaping (Application) -> APIRepository) {
self.storage.makeRepository = makeRepository
}
func initialize() {
self.application.storage[Key.self] = .init()
}
private var storage: Storage {
if self.application.storage[Key.self] == nil {
self.initialize()
}
return self.application.storage[Key.self]!
}
}
var repositories: Repositories {
.init(application: self)
}
}
它会在第一次使用时自动初始化。请注意,APIRepository
是用于我的存储库的协议。 FluentRepository
是该协议的 Fluent 实现。然后像你一样我有一个关于请求的扩展以在请求处理程序中使用它:
extension Request {
var repository: APIRepository {
self.application.repositories.repository.for(self)
}
}
最后,您需要配置它以使用正确的存储库。所以在我的 configure.swift 我有:
app.repositories.use { application in
FluentRepository(database: application.db)
}
在测试中,我可以将它切换到不接触数据库的 in-memory 存储库:
application.repositories.use { _ in
return inMemoryRepository
}
我已经设法从文档中获取示例 as-is。
通过调试器跟踪执行过程,如您所说,可以预测对 get
的调用,而这个 returns 来自 .init()
的实例作为故障转移先前存储的值。您引用的示例中包括:
struct UserRepositoryFactory {
var make: ((Request) -> UserRepository)?
mutating func use(_ make: @escaping ((Request) -> UserRepository)) {
self.make = make
}
}
接下来执行这个use
函数,即mutating
并更新变量make
。我相信正是对 make
的更改触发了对 set
的调用。它肯定会在 use
之后和 configure.swift
继续执行之前立即发生。因此,当服务器正式启动并且您实际在路由中使用 Repository
时,有一个存储的实例可以根据需要重用。