带(或不带)IProgress 的存储库接口
Repository Interface with (or without) IProgress
我有一个存储库接口(简化的示例代码):
public interface IPersonRepository
{
Task<PersonDTO> Get();
}
有两个实现。
一个直接连接到数据库:
public SqlPersonRepository : SqlRepository, IPersonRepository
{
public SqlPersonRepository(IDbConnectionProvider dbCon) : base(dbCon) { }
public async Task<PersonDTO> Get()
{
// use dbCon and dapper to get PersonDTO from database
}
}
还有一个通过网络远程访问 api:
public ApiPersonRepository : ApiRepository, IPersonRepository
{
public ApiPersonRepository(IApiConnectionProvider apiCon) : base(apiCon) { }
public async Task<PersonDTO> Get()
{
// use apiCon (contains base url and access token) to perform an HTTP GET request
}
}
接口在这里很有意义,因为服务器可以使用SqlPersonRepository
。而远程(本机)客户端可以使用 ApiPersonRepository
。对于大多数用例,这就是我所需要的。
但是,我的应用程序支持从服务器提取数据子集,以便客户端应用程序可以在离线时 运行。在这种情况下,我不只是抓取一个人,而是抓取大量数据(几兆字节到几十兆字节),这些数据会通过慢速移动连接多次下载。我需要传递一个 IProgress
实现,以便我可以报告进度。
在那些情况下,我需要一个如下所示的 ApiDatabaseRepository
:
public ApiDatabaseRepository : ApiRepository, IDatabaseRepository
{
public ApiDatabaseRepository(IApiConnectionProvider apiCon) : base(apiCon) { }
public async Task<DatabaseDTO> Get(IProgress<int?> progress)
{
// use apiCon (contains base url and access token) to perform an HTTP GET request
// as data is pulled down, report back a percent downloaded, e.g.
progress.Report(percentDownloaded);
}
}
然而 SqlDatabaseRepository
不需要使用 IProgress
(即使 Dapper 可以报告数据库查询的进度,我认为它不能)。无论如何,我不担心直接查询数据库时的进度,但在进行 API 调用时我担心。
所以简单的解决方案是 SqlDatabaseRepository
实现接受 IProgress
参数,默认值为 null,然后实现方法忽略该值。
public SqlDatabaseRepository : SqlRepository, IDatabaseRepository
{
public SqlDatabaseRepository(IDbConnectionProvider dbCon) : base(dbCon) { }
public async Task<DatabaseDTO> Get(IProgress<int?> progress = null)
{
// use dbCon and dapper to get DatabaseDTO from database
// progress is never used
}
}
但这闻起来很有趣。当事情闻起来好笑的时候,我想知道我是不是做错了什么。此方法签名会指示将报告进度,即使它不会。
在这种情况下我应该使用设计模式还是不同的架构?
过于简单化,您基本上有 2 个选择:是否具有一致的界面。
当然还有其他可能适用于此的设计模式(例如,一些装饰器和工厂方法),但我认为它们有些矫枉过正。
如果您坚持需要一致接口的一般规则,我认为拥有“未完全实现”的回调技术并不是那么糟糕。您也可以考虑只实施它 - 或者至少:使其成为 return 有意义的东西。
我肯定会避免使用某种类型的 2 个不同接口的结构。尽管有时这是更好的选择(当检查某物是否支持某物时),例如;测试硬件组件是否可用 - 我认为在您的场景中它是矫枉过正。它还会在调用方放置更多逻辑,除非您只想在这种情况下打开 process-dialog 屏幕,否则我会避免它。
最后一点:还有其他进度报告模式,例如使用事件,或者传入可选的回调方法。后者看起来像您的解决方案,但实际上有点不同。
最终你仍然会遇到同样的问题,但可能值得考虑。
还有更多解决方案 - 但鉴于您提供的上下文,我不确定它们是否适用。请记住,这是高度基于意见的。
我有一个存储库接口(简化的示例代码):
public interface IPersonRepository
{
Task<PersonDTO> Get();
}
有两个实现。
一个直接连接到数据库:
public SqlPersonRepository : SqlRepository, IPersonRepository
{
public SqlPersonRepository(IDbConnectionProvider dbCon) : base(dbCon) { }
public async Task<PersonDTO> Get()
{
// use dbCon and dapper to get PersonDTO from database
}
}
还有一个通过网络远程访问 api:
public ApiPersonRepository : ApiRepository, IPersonRepository
{
public ApiPersonRepository(IApiConnectionProvider apiCon) : base(apiCon) { }
public async Task<PersonDTO> Get()
{
// use apiCon (contains base url and access token) to perform an HTTP GET request
}
}
接口在这里很有意义,因为服务器可以使用SqlPersonRepository
。而远程(本机)客户端可以使用 ApiPersonRepository
。对于大多数用例,这就是我所需要的。
但是,我的应用程序支持从服务器提取数据子集,以便客户端应用程序可以在离线时 运行。在这种情况下,我不只是抓取一个人,而是抓取大量数据(几兆字节到几十兆字节),这些数据会通过慢速移动连接多次下载。我需要传递一个 IProgress
实现,以便我可以报告进度。
在那些情况下,我需要一个如下所示的 ApiDatabaseRepository
:
public ApiDatabaseRepository : ApiRepository, IDatabaseRepository
{
public ApiDatabaseRepository(IApiConnectionProvider apiCon) : base(apiCon) { }
public async Task<DatabaseDTO> Get(IProgress<int?> progress)
{
// use apiCon (contains base url and access token) to perform an HTTP GET request
// as data is pulled down, report back a percent downloaded, e.g.
progress.Report(percentDownloaded);
}
}
然而 SqlDatabaseRepository
不需要使用 IProgress
(即使 Dapper 可以报告数据库查询的进度,我认为它不能)。无论如何,我不担心直接查询数据库时的进度,但在进行 API 调用时我担心。
所以简单的解决方案是 SqlDatabaseRepository
实现接受 IProgress
参数,默认值为 null,然后实现方法忽略该值。
public SqlDatabaseRepository : SqlRepository, IDatabaseRepository
{
public SqlDatabaseRepository(IDbConnectionProvider dbCon) : base(dbCon) { }
public async Task<DatabaseDTO> Get(IProgress<int?> progress = null)
{
// use dbCon and dapper to get DatabaseDTO from database
// progress is never used
}
}
但这闻起来很有趣。当事情闻起来好笑的时候,我想知道我是不是做错了什么。此方法签名会指示将报告进度,即使它不会。
在这种情况下我应该使用设计模式还是不同的架构?
过于简单化,您基本上有 2 个选择:是否具有一致的界面。
当然还有其他可能适用于此的设计模式(例如,一些装饰器和工厂方法),但我认为它们有些矫枉过正。
如果您坚持需要一致接口的一般规则,我认为拥有“未完全实现”的回调技术并不是那么糟糕。您也可以考虑只实施它 - 或者至少:使其成为 return 有意义的东西。
我肯定会避免使用某种类型的 2 个不同接口的结构。尽管有时这是更好的选择(当检查某物是否支持某物时),例如;测试硬件组件是否可用 - 我认为在您的场景中它是矫枉过正。它还会在调用方放置更多逻辑,除非您只想在这种情况下打开 process-dialog 屏幕,否则我会避免它。
最后一点:还有其他进度报告模式,例如使用事件,或者传入可选的回调方法。后者看起来像您的解决方案,但实际上有点不同。
最终你仍然会遇到同样的问题,但可能值得考虑。
还有更多解决方案 - 但鉴于您提供的上下文,我不确定它们是否适用。请记住,这是高度基于意见的。