Runtime: 将接口作为官方 ADO.NET Provider API 而不是类

创建于 2015-09-26  ·  174评论  ·  资料来源: dotnet/runtime

从我目前在System.Data.Common来看,接口(IDbCommand、IDbConnection 等)已被删除,以支持使用抽象类。

但是在新的 API 中,大部分主要方法都不是虚拟的或抽象的。 仅在 DbCommand 上我们就可以看到:

public DbConnection Connection { get; set; }
public DbParameterCollection Parameters { get; }
public DbTransaction Transaction { get; set; }
public DbParameter CreateParameter();
public Task<int> ExecuteNonQueryAsync();
public DbDataReader ExecuteReader();
public DbDataReader ExecuteReader(CommandBehavior behavior);
public Task<DbDataReader> ExecuteReaderAsync();
public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior);
public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken);
public Task<DbDataReader> ExecuteReaderAsync(CancellationToken cancellationToken);
public Task<object> ExecuteScalarAsync();

虽然这些方法当然可以是虚拟的或抽象的,但让真正的接口回来会更有用,并使任何公共 API 依赖于这些接口而不是抽象类。

这在开发库时非常有用。 今天很难模拟数据读取器以使其返回用于测试目的的特定值。 确保调用 ExecuteReaderAsync 而不是 ExecuteReader 等也是如此。

我建议提供者工厂应该作为一个接口:

public interface IDbProviderFactory {
    IDbCommand CreateCommand();
    IDbConnection CreateConnection();
    IDbConnectionStringBuilder CreateConnectionStringBuilder();
    IDbParameter CreateParameter();
}

然后从那里到提供者的其余部分,如IDbDataReaderIDbTransaction等。

我们知道接口在过去由于某种原因变得不同步,抽象类成为官方 API,但在 corefx 中不再需要这种情况。

请注意,这并不意味着以任何方式删除 System.Data.Common,而是让 Common 类实现这些接口,除非您正在实现提供程序,否则您不会使用 System.Data.Common。 应用程序将只依赖于接口。

请考虑这一点,以使 API 在 corefx 1.0 上更具可测试性。

与有关 dotnet/runtime#14302 和 dotnet/runtime#15269 的讨论相关。

area-System.Data

最有用的评论

我们不能向接口添加成员

正确,这是接口的一个 _good_ 特性。 对抽象基类的偏好是帮助 API 熵前进的最安全方式,而不是对抗它。

虽然您不必_必须_遵循OOD接口隔离原则(ISP) 规定_不应该强迫客户端依赖它不使用的方法_。

如果向现有抽象添加新方法,则自动违反了 ISP。

您可以决定您“不必遵守 SOLID”,因为您是 Microsoft,并且您正在使用 BCL,因此“正常规则不适用”(不是实际引用;只是解释正常情况)相反的论点)。

维护了几个开源项目 6-7 年,根据我的经验,最好保持接口小。 如果需要为抽象添加新功能,请引入新接口。

所有174条评论

他们会切换到抽象基类,因为接口不能被版本化。

我猜非虚拟方法调用另一个虚拟方法。 虚拟方法会损害性能,因此您不希望不必要地虚拟化。

@JamesNK我明白了。 但是,由于 .NET Core 是一个新的 API,而 ADO.NET API 在近十年中非常稳定,您认为这仍然是一个有效的问题吗? 此外,谈到数据库访问,我的猜测是虚拟方法的成本与数据库访问的成本相比相形见绌。

@NickCraver@roji@FransBouma既然你们似乎对 ADO.NET API 感兴趣,对此有什么要说的吗?

@YoungGah ,这值得追求吗?

我猜非虚拟方法调用另一个虚拟方法。 虚拟方法会损害性能,因此您不希望不必要地虚拟化。

在对远程数据库执行查询和处理结果的过程中,virtcall 损失的纳秒可以忽略不计。 此外,ADO.NET 从一开始就使用这个系统(.NET 中的许多其他 API 也是如此)并且没有人抱怨他们的 DB 代码由于虚方法调用而变得如此缓慢;)

我可以在您的列表中看到异步方法,所以我猜就在几年前,MS 无法向 IDbCommand 添加异步方法。 谁知道明天会带来什么需要新的方法或属性。

接口没有版本。

性能只是不做虚拟的原因之一。 可能会减少用于植入的表面积? 我会让 MS 的人说他们为什么决定不这样做,我对 ADO.NET 了解不多,所以我只是在猜测。

@JamesNK我认为您的担忧是

  1. ADO.NET 自 .NET 2.0 以来一直非常稳定,这十年 - 尽管后来添加了异步 API,但它并没有改变 API 的行为,只是添加了异步对应物 - 我没有看到任何大的变化很快就会出现数据库驱动程序范式
  2. CoreFx 应该有不同的版本控制理念,因为您可以为旧应用程序保留以前的 CLR。 所以接口版本问题不应该在这里产生这样的影响

还要考虑,即使是“本地主机”上的 sql 服务器也会花费至少几毫秒来连接并返回一个空查询。 在实践中,大多数对关系数据库的 _fast_ 查询需要大约 20 毫秒。

与在虚拟方法查找中节省微秒相比,能够使用 NSubstitute 或 Moq 等标准工具模拟 API 对于当今的开发人员来说更有价值。

我想我在这里没有非常强烈的意见,但这里有一些评论:

  • 同意上面的说法,在用于数据库访问的 API 中删除虚拟与非虚拟可以忽略不计
  • 基类确实允许 ADO.NET 提供实现,我猜这就是大多数非虚拟非抽象方法的内容 - 不接受 CommandBehavior 的 ExecuteReader 的重载将CommandBehavior.Default传递给重载确实如此。 如果您切换到接口,则每个提供程序都必须使用完全相同的样板来实现 ExecuteReader()...
  • 我不确定这在所有主要的模拟框架中都有效,但至少在 Moq 中,模拟基类不是和模拟接口一样容易吗?

所以总的来说,删除基类或接口的想法似乎很好(更简单)。 由于我没有看到接口的任何优势(除非我对模拟的易用性有误),并且基类可以提供通用功能(即非虚拟非抽象方法),我猜微软的方法是转储接口对我来说似乎很好...

我同意@roji的所有观点。

@roji只是一个说明,我不建议删除基类,我建议将接口添加为默认 API。 基类仍然可以实现默认行为。

至于测试,我在测试我的 API 是否调用了正确的方法时遇到了很大的问题。 例如,要检查 ExecuteDataReader 是否收到正确的参数,您必须检查另一个使用不同参数在内部调用的受保护方法。 这远非理想。

目前,除非我弄错了,唯一可以模拟 ADO.NET API 的框架是 MS Fakes 框架,它可以通过拦截调用来模拟任何东西。 Moq 和其他人不能这样做。

我有兴趣看看其他人是否有类似的问题。

@roji只是一个说明,我不建议删除基类,我建议将接口添加为默认 API。 基类仍然可以实现默认行为。

对不起,我误解了。 在这种情况下,您的提议是不是或多或少地保持了 .NET 中的样子(不是说这有什么问题)?

至于测试,我在测试我的 API 是否调用了正确的方法时遇到了很大的问题。 例如,要检查 ExecuteDataReader 是否收到正确的参数,您必须检查另一个使用不同参数在内部调用的受保护方法。 这远非理想。

如果我理解你的场景(不确定),Moq 的CallBase对这种场景很有用 - 默认实现是从基类继承的

@罗吉

你的提议是不是或多或少地保持了 .NET 中的样子(不是说这有什么问题)?

不完全是。 接口 API 在 .NET 1.0 中添加并在 2.0 中弃用。 从 2.0 开始,接口是为了兼容性而存在,但 Data.Common 中没有 ProviderFactory 或其他类的接口。 异步 API 或 2.0 或更新的方法也没有。

Moq 只能模拟可模拟的东西。 必须有一些它可以覆盖的虚拟或抽象方法,或者它可以调用的受保护方法。 当前的 API 为某些情况提供了方法,但不为大多数情况提供方法。 除非您使用反射,否则有许多事情是内部的、私人的和遥不可及的。 只有 MS Fakes 可以做到,因为它用垫片替换了引用,但这仅在 VS Enterprise 上可用,对开源项目无用。

听起来我有一个非常具体的案例,但肯定任何曾经试图嘲笑这个 api 的人都面临这个问题。 只是谷歌,几乎每个解决方案都以“模拟遗留接口 API 或构建一个可以模拟的包装器”告终:

@nvivo好的,感谢提供额外的细节 - 我承认我在

我不明白的是,为什么您要模拟 API 的内部、私有和其他遥不可及的方法。 您不应该模拟可直接用于您自己的应用程序代码的公共方法(这是您要测试的内容)吗? 我确实看到了非虚拟方法的问题(例如 0 参数 ExecuteReader() 重载),但鉴于在 ADO.NET 中这些总是 (?) 调用一些虚拟重载(例如 ExecuteReader(CommandBehavior)),是否有真正的问题在这里?

只是想了解您的问题场景,您能举一个简单的例子吗?

@nvivo我们目前没有计划引入接口,因为这个线程上的几个人已经指出了版本控制问题。 接口落后的一个很好的例子是在 .NET Framework 4.5 中添加异步和流方法。 当我们添加这些新功能时,我们仔细研究了扩展接口。 我们当时的选择是提供 InterfaceFooV2 或单独的异步和流接口。 我们不想添加 InterfaceFooV2,因为我们可以预见我们将来会想要添加更多 API。 继续为每个新功能添加单独的接口会令人困惑,因为它们与现有接口无关。

@roji我有一些情况,我想确保调用 ExecuteReader 的特定重载,而不是“任何重载”。 这是你只在库中拥有的东西,而不是用户代码。

@YoungGah感谢您提供的信息。 那我就关了。

负责此更改的人员是否了解其影响? 核心 ADO.NET 接口已经存在了十多年,数据访问是大多数业务应用程序的中心,我很难想象如何不故意破坏这么多现有代码库不是最高优先级? 这些是 .NET 中一些最关键的高级接口,消除了这些中断每个 ADO .NET 数据访问库以及因此每个使用它的项目。 删除它们会造成人为的碎片化,导致沮丧和混乱,从而阻碍 CoreCLR 的采用。

您仍然可以通过为IDbCommand任何新 API 添加扩展方法来使接口版本化并使它们源兼容,例如:

public interface IDbCommand
{
    //...
}

public class DbCommand : IDbCommand
{
    void NewApi();
}

public static class DbCommandExtensions
{
    public static void NewApi(this IDbCommand cmd)
    {
        ((DbCommand)cmd).NewApi();
    }
}

在 DNX 发布后,核心IDbCommand接口永远不必更改,您可以继续使用上述策略添加功能。 您也可以稍后(在一个主要的破坏版本中)汇总这些扩展并将它们合并到核心界面中。 无论哪种方式,我们都能获得核心稳定的 ADO.NET 接口,这对于迁移现有代码库以及采用 CoreCLR 至关重要。

@davkean要求我提供具体示例,说明删除核心 ADO .NET 接口会产生什么影响。 我无法想象在没有评估它对现有 .NET 生态系统产生的不可估量的影响的情况下会考虑这种更改,但是它也已经完成,所以有可能没有考虑它 - 我将在这里假设 -在它不是。

尽管 EF 作为 .NET 的默认 ORM 的角色,并在占据大部分市场份额方面取得了巨大成功,但仍有大量 .NET 开发人员出于多种不同的原因更喜欢使用替代 ORM。 例如,与 CoreCLR 相关的一个重要特性是它们具有在 Mono/Linux/OSX 上运行的一流支持以及支持多个替代 RDBMS。 由于 CoreCLR 正在大力宣传 Linux/OSX 开发者市场,因此对 alt RDBM 的支持越多越好。 采用 Micro ORM 的开发人员群体的另一个重要特征是,他们在 MS 生态系统默认值之外进行了评估,以选择最适合他们的 ORM。 从我所看到的一切来看,活跃的 .NET OSS(即反暗物质)开发人员和采用 Micro ORM 的开发人员之间存在高度相关性,同样我希望这与 CoreCLR 的早期采用者具有高度相关性 - 他们的主要价值主张是在 OSX/Linux 上开发。 这些是在做出像这样的基本突破性设计选择时,将周围的 .NET 生态系统包含在您的决策中是有益的一些原因。

替代 ORM 下载

粗略浏览一下 NuGet 下载,可以了解非 EF 市场份额的情况:

NHibernate - 1M+
小巧玲珑- 1M+
OrmLite - 500k+
Simple.Data - 300k+
PetaPoco - ~100k
NPoco - 30k+

实际数字远不止这些,因为 Dapper、Massive、PetaPoco、NPoco 等许多 Micro ORM 被设计为适合单个插入 .cs,因此 NuGet 没有报告其真实使用情况。 还有像 LLBLGen Pro 这样的闭源 ORM,它们拥有庞大的用户群,但 NuGet 没有报告其使用情况,同样,我确定我错过了许多我忘记/不知道的其他 ORM。

对替代 ORM 的影响

感谢 GitHub,我们可以快速搜索,看看有多少不同的源文件包含核心
IDbConnectionIDbCommandIDataReader受此更改影响的 ADO .NET 接口:

IDb连接指令数据读取器
NHibernate59181132
小巧玲珑172117
OrmLite1795426
简单数据29276
国家石油公司4103

注意:这些结果仅显示源文件,实际损坏引用的数量要高得多。

对客户源代码的影响

此更改的实际影响还扩展到使用这些 ORM 的所有项目依赖项。
不幸的是,效果不仅限于内部实现,因为它也会破坏客户
源代码,因为许多 Micro ORM 只是 ADO.NET 接口上的扩展方法,因此客户端
代码如下:

IDbConnection db = ...

//Dapper
db.Query<Dog>("select Age = <strong i="49">@Age</strong>, Id = @Id", new { Age = (int?)null, Id = guid });

//OrmLite
db.Select<Author>(q => q.Name.StartsWith("A"));

使用扩展方法的一个扩展特性是这些 ORM 是“开放式的”,客户可以通过在他们自己的项目中添加扩展方法来使用他们自己的一流 API 扩展 ORM - 这些也被破坏了。

显然,任何传递IDbConnection源代码现在也被禁止在 CoreCLR 上工作。

例如,核心 ADO.NET 接口在 ServiceStack 等高级框架中被大量使用,因为它是启用多 RDBMS 数据访问的最小依赖项。 还假设在所有不太可能改变的类中,它将是核心 ADO.NET 接口。

概括

我个人很惊讶没有这些接口的 .NET 会有未来。
接口在设计上是针对特定目的的,以允许多种实现,而 ADO.NET 就是其中之一
.NET 中最重要的“开放提供者模型”。 我不知道是什么优先级导致这些接口被删除,但是依赖这些接口的大量现有 .NET 代码库以及替代 EF .NET 生态系统都应该被赋予更高的优先级。 这会造成重大中断,并且是支持现有 .NET 4.x 和 CoreCLR 平台所需的主要障碍,迫使必须将大量额外复杂性应用于受此影响的所有现有代码库。

目前的看法是,ADO.NET/CoreCLR 正在重新设计,以便为 EF 和 SQL Server 提供一流的支持,而生态系统的其余部分则被忽略——像这样的不透明的破坏决策只会重新强化这种刻板印象.

作为 .NET 团队的前任成员(我现在在 Roslyn 工作),我与 SQL 和实体框架团队一起大量参与了新数据通用的原始设计。 我目前没有参与其中,但我可以添加一些背景信息来帮助纠正我在 twitter 和上面看到的一些陈述。

当前面向 .NET Core 的 System.Data.Common 设计始于 2012 年 12 月,比我们开源大约早了 2 年。

目标:

  • 为 .NET Core 设计一个现代的表面区域,减少概念的重复( IDbConnectionDbConnection )、混淆、错误和分层问题(从 DataCommon 拆分 SqlClient,从核心抽象拆分 DataSet) .NET 1.0 的原始设计。 一种很容易被现有消费者和 .NET Framework 的新开发人员采用的方法。
  • 使提供者和使用者能够针对 .NET Core 构建单个二进制文件/源,然后在 .NET Framework 上运行相同的二进制文件。 请注意,反向不是目标; 能够采用 .NET Framework 二进制文件/源代码并在不更改 .NET Core 的情况下运行它。

纠正一些散布的事情:

  • 目前的接口是不可版本化的。 我们无法向接口添加成员, @mythz通过扩展方法提供的上述提议要求提供者无论如何都从抽象基类派生。
  • System.Data.Common _尚未_远离提供程序模型。 这些接口被删除是因为它们是一个遗留的 .NET 1.0 概念,被 .NET 2.0 中引入的抽象基类替换/复制。 在我们做出这个决定的时候,我们能找到的每个提供者都是从基类派生的。
  • 与接口一样,基类是可模拟的。
  • 我们知道使用 .NET 1.0 接口的用户需要进行一些更改,但是,移动到基类是一个非常简单的端口。 例如,请参阅 AutoMapper 的以下几行更改:(https://github.com/AutoMapper/AutoMapper.Data/blob/master/AutoMapper.Data/DataReaderMapper.cs#L14)。

我无法理解的一些事情:

我们不能向接口添加成员

怎么还不能向 CoreCLR 接口添加成员,完全撕掉它们就可以了?

@mythz通过扩展方法提供的上述提议要求提供者无论如何都从抽象基类派生。

重要的部分是接口存在并允许引用它们的源代码进行编译。

如果您不想对接口进行版本控制,请对它们进行 EOL,只需将接口恢复为它们被撕掉之前的状态,并减轻现在对使用它们的每个其他库施加的负担。 我的意思是这些核心接口从来没有过时,没有提供警告或迁移路径。 然而,我们是否因为采用已发布的、众所周知的、稳定的 API 作为我们库中不可或缺的一部分而受到惩罚?

移动到基类是一个非常简单的端口。

这需要添加到每个引用 ADO.NET 接口的源文件中,并强制客户使用自定义构建符号来乱扔他们的代码。

这里对向后兼容性的关注似乎并不相同,但是在未来的版本中故意破坏现有客户并不是一种选择(我很惊讶甚至考虑到 ADO .NET 的更大市场份额)。 我们不能破坏现有的 4.x 客户,但我们被要求支持 CoreCLR - 那么这让想要保持现有向后兼容性并支持 CoreCLR 的现有 4.x 库何去何从? 我们是否也应该复制文档/示例?

怎么还不能向 CoreCLR 接口添加成员,完全撕掉它们就可以了?

.NET Core 中的表面区域需要与 .NET Framework 二进制兼容,以使第一方和第三方能够针对 .NET Core 进行构建,并在不更改 .NET Framework 的情况下运行可移植性。 向接口添加成员违反了这一点,因为这些成员的使用者在 .NET Framework 上运行时会失败。

我不是在争论删除或添加这些接口,我只是想添加一些背景来解释为什么设计最终会出现在它的位置。 我会让包括@YoungGah@saurabh500在内的当前所有者解决这个问题。

简单总结一下线程,您认为 Microsoft 应该移植这些接口的原因是使生态系统能够轻松移植到 .NET Core,同时维护其 .NET Framework 实现?

是使生态系统能够轻松移植到 .NET Core,同时维护其 .NET Framework 实现?

是的。

简单总结一下线程,您认为 Microsoft 应该移植这些接口的原因是使生态系统能够轻松移植到 .NET Core,同时维护其 .NET Framework 实现?

是的。 如果我将我的代码库 (LLBLGen Pro) 移植到 corefx,外部 API 现在会被破坏:然后我必须公开 2 个 api 或破坏我所有用户的现有代码库。

你们这些人打破我们的东西可能没问题,因为你们感觉不到疼痛,我们有。 这对我来说不好:我要么不得不忍受一个被砍掉的代码库并维护 2 个做同样事情的 API,要么破坏我用户的代码,因为你认为这没问题。

我也不明白为什么接口没有版本,它只是一个接口,就像一个类也有一个接口一样。 CoreFX 可以完美地将异步方法添加到接口中。

.NET Core 中的表面区域需要与 .NET Framework 二进制兼容,以使第一方和第三方能够针对 .NET Core 进行构建,并在不更改 .NET Framework 的情况下运行可移植性。 向接口添加成员违反了这一点,因为这些成员的使用者在 .NET Framework 上运行时会失败。

简单的解决方案:添加现在的接口。 一旦大家意识到上面的这条规则实际上相当愚蠢,您可以将很久以前需要添加到接口的方法添加到接口中并继续。

我使用 MS 软件的时间足够长,以至于上面的规则在纸面上很好,但在实践中被打破了,第二个重要的 MS 团队需要它被打破。 如果您像 CoreFX 营销/公关宣传中所说的那样“开放”和“与众不同”,请展示出来。 我所看到的关于 System.Data 和 CoreFX 的所有内容是“MS 需要的已经完成,其他人需要的只是搁置或忽略”。

我忘记提及的另一件事是:福勒昨天在 Twitter 上提到你需要每个人都移植他们的东西。 我必须自己支付将我的 500K LoC 代码库移植到 CoreFX 的费用,这需要时间、精力并且会占用其他功能的时间。 完全人为的额外摩擦(这是一个新平台!怎么会有限制性规则?)真的没有任何帮助:它增加了额外的维护成本,需要额外的时间来移植代码和测试,并给我们的用户带来额外的负担。

所有这一切都超出了您的范围,而您似乎并不关心。 但是你忘记了一件事:如果我们不移植我们的代码,而我有更多的人呢? 我愿意投入时间和自己的资金将我的大型代码库移植到你的新框架中,但很遗憾地说,每当我遇到问题时,我都会遇到限制、奇怪的规则和无休止的无休止的争论. Iow:我感到非常孤独,同时你似乎非常希望我们喜欢你的新框架。

就像我很久以前说过的:卖给我这个框架,这个新的 CoreFX。 好吧,保持摩擦并引入大量移动和带走的奶酪并不会产生很大的动力来投入大量时间(和金钱)。

只是我的 2 美分。

@FransBouma请让我们尝试让这次谈话保持专业、富有成效并专注于事实。

我不赞成或反对添加接口。 但是,向接口添加方法是不兼容的。 让我们来看看这个:

1) 将 IDbConnection.OpenAsync 添加到 .NET Core
2) 任何调用此方法的人现在都将无法在 .NET Framework 上运行(违反了我上面提到的核心原则/目标)。 这也破坏了 XAML 设计器和其他一些依赖于这一事实的 VS 功能。
3) 为了更新 .NET Framework,我们发布了带有 IDbConnection.OpenAsync 的新版本 .NET Framework“4.7”
4) 在添加此方法之前实现 IDbConnection 的每种类型现在都无法在 .NET Framework“4.7”上加载

这就是我们不能向接口添加方法的原因。

如果我对自己与 MS 沟通问题的进展感到沮丧,你们都不会知道这件事,并认为一切都是玫瑰和彩虹。 如果这看起来不专业,那就这样吧,我不在乎 MS 是否认为我是专业人士。

这就是说:我不喜欢接口,所以如果它们消失了,从那时起理论上有类而没有接口可以使用的事实不会让我成为一个悲伤的熊猫:应该做什么可以理论上也可以通过基类来完成,今天,因为今天所有主要的 ADO.NET 提供者都很好地从基类派生出来(这在过去的 IIRC 中并不是这样,ODP.NET 实现了一个接口,但不是派生自基类)。 这也是为什么我最初在本主题前面提到的原因并没有真正认为删除它们是一件大事。 从那以后,我有一些时间来考虑它,我认为它_是_一件大事。

我们并不生活在火星上的真空中,堆栈底部的中间件/框架现在有一个问题:这些框架的当前 .NET 完整版本的用户希望继续在 CoreFX 上使用它们,因为他们知道这些框架。 然而,将它们移植到 CoreFX 是一个很大的 PITA,由于无数的原因,其中一个经常使用的接口暴露在 CoreFX 上不存在的公共 API 中(以及这个线程的原因)。

仅出于这个原因,我希望看到接口回来。 就我个人而言,不是出于技术原因(例如异步需要基类,这已经是一团糟)。 我知道他们缺乏某些方法,但那是你的问题,而不是我我的问题和(现在解释)MS 对此的回应是:举手说“无法完成!”。 但我没有那种奢侈。 你制造了这个烂摊子,你解决了它。 你想让我移植我的代码,投入大量时间和金钱(我必须为自己支付)来支持你的新框架,你为什么要把_你的_问题变成_我的_问题?

查看您的 4 步方案:如果您将 CoreFX 视为一个单独的框架,则向接口添加方法不是问题。 反正不是这样吗? 它与多年前的 Compact Framework 相同(我确实将我的框架移植到了它,我学到了一些深刻的教训,然后告诉我移植到 CoreFX 不会简单、快速和容易,并且保留两个代码库也不会):我们从 1 个 API 开始,然后有人忘记了某些东西,或者 MS 中的某个团队需要某些东西,并且只有少数低级堆栈开发人员会遇到的重大变化等等,两条路将分裂。

(例如:Compact Framework 忘记了 'SerializableAttribute'。他们添加了一个虚拟属性,在以后的版本中什么都不做,但这破坏了预期不存在的代码并定义了他们自己的代码)

不过,分道扬镳是可以理解的:试图让事情兼容太严格了。 我现在在这里预测,这个规则将来会被打破。

将事物视为“兼容”不仅在 API 签名级别很重要,而且在 API _behavior_ 级别也很重要。 相信这两者在 API 行为上完全相同(CoreFX 和 .NET Full)风险太大:框架开发人员必须在 CoreFX 和 .NET full 上测试相同的功能,单独在 CoreFX 上进行测试是不可能的足以假设代码在未来完全相同的 .NET 上 100% 工作:因为你怎么能保证呢? CoreFX 上的调用堆栈 20 次调用已经触及了很多其他代码,而不是 .NET full,这里和那里的一个小细节,事情发生了变化。

所有这一切的重点是:它是一个单独的框架:针对 CoreFX 编译的代码可能与针对 .NET full 编译的代码不同。

有几种情况:

1) 框架有一个代码库,其中 100% 在 CoreFX 上编译。 这提供了一个可在 .NET full 上运行的 dll
2) 一个框架有一个代码库,其中 70% 在 CoreFX 上编译,100% 在 .NET full 上编译。 这提供了 2 个 dll:一个用于 CoreFX,一个用于 .NET full。 在 .NET 上完整运行 CoreFX 版本是很愚蠢的,因为会错过 30% 的功能。

在 1) 的情况下,我理解你的观点。 在 2) 的情况下(所有当前的 .NET 完整目标框架都是这种情况,其中包括 _all_ 3rd 方 ORM)您的观点确实毫无意义,因为无论如何它们都必须使用 2 个 dll:有效地 2 个代码库必须单独维护,单独测试并单独迁移到自己的新版本。 特别是如果 CoreFX 获得了不属于 .NET full 的新功能(情况将如此)。 (顺便说一句:如果你将 DbDataReader.GetSchemaTable() 添加到 CoreFX,它返回一个与 DataTable 不同的数据结构,因为 MS 拒绝移植它,在 CoreFX 上使用 DbDataReader.GetSchemaTable 的代码也会在 .NET full 上中断。如果你以不同的方式命名它它会中断,因为方法不存在。我说:如果使用了 _both_ 框架中没有的东西,代码就会中断。这并不意味着 CoreFX 中不应该存在这些东西)。

在 CoreFX 上没有接口会使框架在情况 2) 中的情况变得持久:它们无法成为适合 1) 的框架,因为例如它们的 API 公开了接口。

微软重写他们自己的东西,所以他们的框架成为情况 1) 中的框架很酷,但是我们没有一百万美元的预算,15+ 人在 ORM 运行时和我们这边的一个大型公关机器,他们将抚平打破每个应用程序。 所以我们要么被困在 2) 中,要么需要 MS 的一些帮助才能移动到 1)。

就是这里的利害关系。 你在推特上说“告诉我们你需要什么”。 我们做到了。 反复。 特别是关于 System.Data没有通信。 没有。 没有未来的计划,没有讨论要做什么,只是死胡同,有时如果一个 MS 人介入,它与此事没有真正的利害关系。 感谢您在这方面的时间,我们获得的背景越多越好,但与此同时,这就像和同事谈论这个:它不会得到解决,因为负责人不在参与讨论。

如果这让我听起来很沮丧并且上帝禁止“不专业”,那就这样吧。

谢谢收听。 顺便说一句,我对 System.Data 没有任何幻想:将代码移植到 API 将是一场火车事故,并且由于负责人与在其 API 之上编写关键框架的开发人员没有沟通,所以几乎没有希望的事情将改变。 不是你的错, @davkean ,这不是个人的。

我必须回应上述关于缺乏沟通的挫败感。 我们还需要批量插入和模式信息。 这些缺失的核心(双向)功能在一个多月内(参见 dotnet/runtime#15269 和 dotnet/runtime#14302)没有任何进展或交流。 然而,微软将当前代码标记为“可供发布的候选代码”,这本身就是“它已经足够好”的信息。 它不是。 缺少需要添加的核心内容,如果您遵循这些线程,则出于类似的版本控制原因需要在第一个版本中。

查看 dotnet/runtime#14302 上的最后更新(“为什么 DataTable/View/Set Absent?”),是 22 天前的提问:

那么 System.Data.Common 现在是 CoreCLR V1 的功能完整了吗?

是的,沮丧可能会被认为是不专业的。 文本的语气和上下文很糟糕,而且总是如此,但这就是我们在这里的限制。 我认为这里的每个人都在努力提高效率,但是我们从 CoreFX 方面对System.Data区域的实际进展进行了相当多的阻挠,也就是说,坦率地说,作为图书馆作者,这让双方都感到愤怒和这些位的用户。

我们需要这些核心功能部件,无论是否需要接口——我对接口并不苛刻,我们已经在没有它们的情况下移植了 Dapper。 但是缺少数据表、结果模式信息、批量插入等在“候选发布”中是不可接受的。 当几乎普遍认为它尚未准备好发布时,Microsoft 是增加了将当前代码标记为 RC 的挫败感的人。 是的,它只是一个标签,但它既是一个不正确的标签,又是一个大大增加了紧迫性的标签,因为它是基于一个任意的时间表(应该改变以反映现实)。 我不认为该线程中的任何人对该日程安排负责,但值得指出的是,这是令人沮丧的主要因素_级别_。

让我们回到根本问题。 我们需要这些部分,我们数百万用户中的许多人都需要。 所以让我们修复它。

让我们不要忘记下载 100 万次以上的NHibernate:

| IDbConnection | IDb命令| IDataReader |
| --- | --- | --- |
| 59 | 181 | 132 |

目前的看法是,ADO.NET/CoreCLR 正在重新设计,以便为 EF 和 SQL Server 提供一流的支持,而生态系统的其余部分则被忽略——像这样的不透明的破坏决策只会重新强化这种刻板印象.

像这样的事情加强了这种看法: https :

据我所知,在 SqlClient 程序集之外以任何有用的方式实现该 API 的方法为零

我目前可以在没有接口的情况下进行测试。 但老实说,我不明白接口版本控制和兼容性的原因。

.NET Core 的想法不就是一个没有兼容性负担的新框架,并且它与您的应用程序捆绑在一起,因此您不必处理这样的问题吗? 由于缺少模式和数据表等内容,提供程序已经与 .NET 中的提供程序不兼容,那么什么会破坏兼容性? 如果界面发生变化,只需针对新版本进行编译并将其与您的应用程序捆绑在一起。

听起来,设计的大多数借口只是旧框架的担忧,不适用于新框架。 不管怎样,让我们​​看看它在实践中的效果如何。

对于那些打算支持多个框架,并且历史上针对接口的使用者......我只想分享Dapper使用的一堆丑陋; 我不是说这是 _good_,但这足以让它编译。 当然,它被复制到一大堆文件中……我分享这个主要是为了强调另一个影响:

https://github.com/StackExchange/dapper-dot-net/blob/master/Dapper/SqlMapper.cs#L6 -L16

我们不能向接口添加成员

正确,这是接口的一个 _good_ 特性。 对抽象基类的偏好是帮助 API 熵前进的最安全方式,而不是对抗它。

虽然您不必_必须_遵循OOD接口隔离原则(ISP) 规定_不应该强迫客户端依赖它不使用的方法_。

如果向现有抽象添加新方法,则自动违反了 ISP。

您可以决定您“不必遵守 SOLID”,因为您是 Microsoft,并且您正在使用 BCL,因此“正常规则不适用”(不是实际引用;只是解释正常情况)相反的论点)。

维护了几个开源项目 6-7 年,根据我的经验,最好保持接口小。 如果需要为抽象添加新功能,请引入新接口。

如果这里有赞成票,我会赞成@ploeh的评论 +1000

像往常一样来自@ploeh 的深刻评论。

@FransBouma ,我们将以不会破坏完整框架的方式添加 DbDataReader.GetSchemaTable() 的替换功能。
@NickCraver , SqlBulkCopy 是我们未来的计划,我们正在研究架构。 我们在架构上进展缓慢,因为我们还需要在使我们的堆栈在 x 平台上工作方面取得合理的进展。
@mythz ,感谢您提供有关客户影响的示例、数字和评估。 我们将审查它们。

@YoungGah请更新与信息相关的问题,以便这些问题保持最新状态,例如https://github.com/dotnet/corefx/issues/1039 ,否则稀疏信息会分散在各处。 很高兴您将添加 GetSchemaTable(和 DbConnection 的等效项,不要忘记那个!),但是很难获得有关将发生什么和 _when_ 的任何信息。 有什么计划什么时候会添加吗? 我们现在要做的只是暗示在“未来”可能会添加一些东西。 老实说,这对于规划代码库的移植并不是很好。

@FransBouma ,是的,我也会更新其他线程。 至于您要求提供有关什么以及何时可用的更多信息,我完全理解你们为什么需要它。 我将发布一个列表,表明该特性/功能是否在 v1 上可用,是否有目的地删除,在 v1 后可用,或者未来可用性的设计待定。 我会尝试在接下来的 2 周内发布它。 至于 DbDataReader 上的 get schema 函数和 DbConnection 函数,我们的计划是在 rc2 版本中提供它。 如果计划因某些不可预见的原因发生变化,我们将更新社区。

这里发生了什么,以供将来参考@YoungGah; IDataReader 依赖于 DataTable,DataTable 依赖于 DataSet(我们认为这是一个单独的层 - 因为它的策略很重,不像这些类型是无策略的),所以这里有一些设计工作来打破,如果这些接口曾经带回来。

我会在这里为@ploeh的方法投其他票; 有接口,但比当前 BCL 中的大多数接口细粒度得多。 这涉及@davkean关于解耦以及解决版本控制的评论。

为什么不能有一个继承旧界面的新界面。 旧的过时了。 将来删除旧的。 至少你可以扩展它而不破坏现有的用途。

或多个较小的接口。 我刚得到 ploehs 评论。

我不明白需要与原始 .NET 完美兼容。 这里没有什么可打破的,这是一个新框架,也是打破与遗留代码的联系并应用长期需要但会损害原始框架的更改的绝佳机会。

当我重新提出接口时,我并没有考虑引入原始 1.1 接口,而是使用新设计更新接口。 正如@ploeh所说,它们甚至可能更多。

接口是的,如果可能的话,遗留支持,但此时不应成为优先事项。

重新开放,因为对这个话题有很多兴趣。

这里没有什么可打破的,这是一个新框架,也是打破与遗留代码的联系并应用长期需要但会损害原始框架的更改的绝佳机会。

那么摆脱原始设计糟糕的接口并在 ADO.NET 等基类上进行标准化的绝佳机会已经在做吗? :巨魔的脸:

不过说真的,您可以在干净的 API 或向后兼容性之间进行选择。 选一个。 我看不出有什么办法让你的蛋糕也吃掉。

@JamesNK但这正是重点。 不需要向后兼容性,期间。

你开玩笑,但是带有接口的糟糕设计的 API 之所以糟糕,是因为它们设计得糟糕,而不是因为它们是接口。 =) 这不像接口在 NET 中的任何地方都没有使用,或者这是新的东西。 这次只需正确设计,然后继续。

ADO.NET 是所有 .NET 中最稳定的代码段之一。 它在 15 年内发生了两三个严重的变化。 它是稳定接口并使每个人都更简单的完美 API。

请注意,这里的这个主题也是评论最多的问题之一,并且对接口与虚拟方法、可测试性和其他内容进行了长时间的讨论。

@nvivo我必须承认,我很困惑。 在我们确定基类支持可测试性之后,该线程演变为带回接口以支持将 .NET Framework 代码移植到 .NET Core。 重新设计界面和引入新的东西如何帮助呢?

为什么我们不能拥有用于向后兼容的原始接口并继续您选择的任何内容(抽象类或小接口)? 原始接口可以位于新堆栈的顶部并提供向后兼容性。 这部分也可以是可选的。
这将使移植变得容易,并且仍然允许新的方式。

@davkean

我无法对所有在这里发表评论的人做出回应。 我提议使用接口作为 ADO.NET 的 API,更新为新的当前 API。 我没有要求带来原来的接口和它所有的问题。 目的是拥有一个更清晰定义的 API,并使其更容易模拟和测试依赖于它的代码,主要是数据抽象库而不是用户代码。

正如@ploeh明智地所说,接口更

现在,看看类是如何创建一个糟糕的设计的:

  • 为什么 DbCommand 有DesignTimeVisible 属性? 设计时支持是否要求将连接定义为连接?
  • 为什么有一个事件来通知状态更改但不通知其他事情,例如执行的命令或启动的事务? 通知甚至是连接存在的要求还是使构建 UI 变得更容易的东西?
  • ConnectionStringBuilder是提供者存在的必要条件吗? 还是让 VS 向导开箱即用是一件好事?
  • 为什么 DbDataReader为某些核心类型定义了类型(GetByteArray()GetDateTimeOffset() ? 如果这可以在外部使用字符串或流完成,是否还需要检索 TextReader ? 如果这是一个接口,像这样的方法是否会被添加到 API 或创建为扩展方法或具体类中的帮助程序(如 SqlDataReader 中的GetSql*系列)?

这些都是反问句。 我相信他们所有人都有令人信服的答案,并且已经考虑过事情。 关键是当前的设计显然不是被认为是 API 定义的东西,它可能会受到接口的更多关注。

老实说,从外面看,关于设计的讨论听起来像这样:

  • 让我们将这些事件保留在这里,因为 Visual Studio 中的向导更容易开箱即用
  • 让我们删除模式检索方法,因为我们有 EF,这就是世界上每个人都应该使用的方法。
  • 让我们保留这些方便的方法,因为从 .NET 1.1 开始就支持它,我们永远不会破坏兼容性!
  • 让我们删除数据表和数据集,让每个来自 .NET 1.1 的人无论如何都要更改他们的整个代码库!
  • 让我们构建一个专注于云计算、社区、开源和未来应用的新的提供者模型......
  • ...让我们根据昨天的需要使用 SqlClient 作为 _sole_ 测试用例来构建这个模型!
  • 让我们构建一个将与每个应用程序捆绑在一起的全新框架,因此没有人不必担心更新会破坏他们的应用程序_永远_!
  • .. 然后让我们决定不添加接口,因为任何更改都可能破坏它们的更新!

是的,那里有一点咆哮,这个讨论无处可去=)......但只是想把它从我的胸膛里拿出来。 现在是 2015 年,一切都在崩溃,我们已经习惯了。 未来几年 ASP.NET MVC 将有 20 次更新,这将导致比 ADO.NET 中的界面更改更多的破坏性更改。

我仍然喜欢 .NET 以及您使用它所做的一切,我确信及时推出 .NET Core v1 是一种匆忙,并非一切都会完美。 我只是希望随着时间的推移,社区可以帮助将其引导到其他方向,并且在我们移动时不要害怕破坏事物。

对于 ORM 维护者来说,为什么不做

``` c#

如果 COREFX

命名空间 System.Data {
公共接口 IDbConnection { ... }
}
``

并使用适配器模式用您自己的实现包装新的 System.Data? 事实上,你可以为它制作一个开源代码包并分享。

It's 2015, everything breaks all the time and we're used to it. There will be 20 updates to ASP.NET MVC in the next years that will cause a lot more breaking changes than interface changes in ADO.NET.

I still love .NET and what you're doing with it in general, I'm sure it's a rush to get .NET Core v1 out in time and not everything will be perfect. I just hope the community can help steer this to other directions as the time goes and you're not afraid to break things as we move.
- nvivo

这就是问题; 为了满足某个任意的截止日期,我们正在匆忙重写,而不是经过深思熟虑的方法。
如果需要的话,我会早点晚点,Q4/16,而不是一些闪亮的新破废话。 现在是 2015 年,一切都坏了是一个可怕的理由。

@thefringeninja要么添加了一个完全不必要且令人困惑的依赖项,该依赖项仅适用于一半的系统(在共享情况下),要么导致名称冲突,需要extern alias取消选择(以及很多混淆为什么需要System.Data.IDbConnection不会接受您提供的不同但相同的System.Data.IDbConnection )。 基本上,这会使事情变得更糟 10 倍。

你能举一个具体的例子@mgravell吗? 如果您使用Type.GetType("System.Data.IDbConnection, System.Data") ,或者在 PCL 场景中,我可以看到这将如何破坏。

如果 orm A 定义了 System.Data.IDbConnection,并且 orm B 定义了
System.Data.IDbConnection,那么现在有两个完全不同的
具有相同名称/命名空间、冲突和
实际上不从任何数据库提供程序工作。 什么都解决不了,
基本上。 更糟糕的是:它租给了人们希望通过的无法使用的 API
在 SqlConnection 或 NgpSqlConnection 中 - 它不起作用。

事实上,你可以为它制作一个开源代码包并分享。

如果它不是 System.Data.Common,那么这意味着 DbConnection 没有实现它,并且:你最好不要打扰。

您永远不会就每个 ORM 或 ADO .NET 提供程序维护者承担外部第 3 方依赖项(几乎保证 SQL Server 不会)达成共识,并且重新定义核心 BCL 接口的 2 个库不能一起使用。 对于在核心库中引用 System.Data 接口(现在需要定义)的高级框架(如 ServiceStack)更糟糕的是,将不再能够使用任何未引用相同接口的 ORM - 这没有- 一个人愿意或应该。

您可以确保每个库引用相同的 System.Data 接口的唯一方法是,如果它们是使用实现它们的基类来恢复的——我仍然不清楚这有什么危害。

@mgravell啊传递依赖,没有考虑过。 :+1:

您知道我不明白为什么这是一个问题,除非您将代码与您不拥有的代码紧密耦合。 保护您的代码免受其依赖! 把它包起来,把它抽象出来。 有很多方法可以做到这一点并使您的代码可测试。 上面提到了很多。 您集成测试您不拥有的位。 这就是它的工作方式。 你不应该嘲笑 BCL 对象! 如果你是,那么你的设计就不好。

@nvivo我知道这是你最初的问题,但它的方向现在已经变成了一个线程,为了兼容原因恢复 v1 时代的接口。 让我们继续关注这一点 - 如果您想讨论对当前表面积进行更改,请提交新问题。

@mythz 接口有两个问题; 1) 他们引入了对 DataSet 的(严重)依赖,这不属于无策略抽象;2) 他们引入了一组并行的基类抽象,但被 v1 表面区域锁定。 我们想避免这种混淆。

我同意第三方提供这些接口是不可行的——它们需要在核心抽象上实现才能有用。

我知道这是你最初的问题,但它的方向现在已经变成了一个线程,为了兼容原因恢复 v1 时代的接口。 让我们继续关注这一点 - 如果您想讨论对当前表面积进行更改,请提交新问题。

这完全没有意义。

@nvivo这意味着,尽管已经提交了问题,但您并不是遇到问题的人 - 关闭它就很明显。 问题在于恢复 System.Data 接口以减轻支持依赖它们的整个生态系统的负担。 你似乎没问题:

现在是 2015 年,一切都在崩溃,我们已经习惯了。

但是对于我们这些必须支持现有代码库和我们客户的代码库的人来说,这不是一个令人满意的策略,并且绝对不应该成为影响所有 .NET 的 BCL 库的保管人的默认策略。

但是对于我们这些必须支持现有代码库的人来说,这并不是一个令人满意的策略

@mythz这是断章取义。 那并非我的本意。 这里的所有人都必须支持现有的代码库,我怀疑讨论中是否有 .NET 的新手。

这场谈话变成了什么的问题是它没有多大意义。 .NET Core 是一个新框架,而不是升级版。 许多现有的完整 .NET API 不存在,也不会存在。 无论如何,向后兼容性不会像那样工作。

@nvivo这种确切的情绪就是为什么这个问题不适用于您。 如果您认为向后兼容性不重要,那么您从未尝试过支持针对多个平台的有意义的代码库 - 您也不是代表 CoreCLR 团队发言。 这些库不是从头开始开发的,如果您阅读上面的内容,您会发现 CoreCLR 库的主要目标是在完整的 .NET Framework 上运行。 CoreCLR 是另一个平台目标,移植现有库对其成功至关重要,这是 .NET 团队积极鼓励的,而这些缺失的接口目前正在阻碍这一目标。

由于所有这些关于接口不是版本友好的讨论,这让我思考 Go 编程语言如何通过隐式接口回避这个问题。

我被要求扩展 _policy-free_ 抽象的含义。

基本上,无策略是指 System.Data.Common 抽象包含几乎零业务规则 - 它们所做的只是提供给定提供者必须实现的 API 形状。 这与 DataSet 形成对比,DataSet 有很多业务规则和代码。 与包含策略的类型相比,无策略类型的版本往往不那么频繁,因为代码较少,因此错误和设计更改较少。 我们希望 3rd 方库之间 _exchanged_[1] 的抽象和类型不频繁地[2] 版本化,以减少在跨大型包图协调依赖项时遇到的问题数量。 您不能将交换类型作为应用程序的一部分嵌入或合并,而非交换类型则可以。 因此,为什么我们希望它们分开。

[1] _exchanged_,我的意思是那些倾向于出现在公共 API 上的类型。 例如,我们认为Collection<T>是一种交换类型,而不是List<T>
[2] 对这些使用语义版本控制会导致与上面删除的接口几乎相同的中断,因为您“分叉”了生态系统; 库需要决定是在中断之前还是之后以库为目标,还是自己分叉来处理拆分。

@mythz我必须支持 orms/customers 作为商业应用程序和现有代码的一部分......我并不是说我同意任何一方,但事实是 dnx 是全新的框架/运行时。 如果它没有一些接口来处理它......使用编译器指令。

如果它没有一些接口来处理它......使用编译器指令。

哦? 返回 IDataReader 的方法或接受 IDbConnection 的方法如何? 还是 IDbTransaction? 如果您的 _customers_ 代码使用该 api,您将如何#ifdev 解决这个问题?

'处理'……这是何等的嚣张?

很简单,更新底层包(您的组织)以返回您自己的类型或自 2.0 以来他们支持的基本类型。 如果您的目标是旧版本的 .net,您可以使用指令来返回接口并将其标记为已弃用。

是的,这确实很糟糕,但我相信他们(一直在思考这个问题的非常聪明的人)当时是有充分理由的(回到 .net 2.0)。 可以进行讨论吗,也许这肯定会改变.. 但事实是人们升级到新的运行时将不得不做一些工作。 它不是在一个花哨的 ui 中点击一个按钮,然后为他们剪掉一些工作。但与此同时,我同意你的观点,升级到新框架应该比去和替换一堆类型更容易。

@FransBouma他可能也是提倡强命名的人。

@FransBouma @phillip-haydon 可以把你带到其他地方,你不能指望那样做,人们会认真对待你。 如果您有任何疑问,请查看我的开源贡献/参与的项目......我将不得不处理这个......无论如何......

为了记录,我反对强命名。

处理它...

你说我在拖钓?

@FransBouma他可能也是一个提倡强命名的人。” 偏离主题,对本次演讲没有太大帮助。 是的,这感觉就像你的拖钓..

在此讨论中要考虑的一个重要事实是,十年前引入基类时,IDb* 接口作为 .NET 2.0 中 ADO.NET 的 API 已被弃用。 它们没有被标记为已弃用,但自那时以来构建的任何内容都取决于基类。 想到了 App.config 提供程序和连接字符串支持。

如果您有依赖于这些接口的代码,那么您正在针对一个非常过时的 API 进行编码,并且不支持异步方法之类的东西,这意味着如果您希望人们继续使用它,您无论如何都需要对其进行更新。

很简单,更新底层包(您的组织)以返回您自己的类型或自 2.0 以来他们支持的基本类型。 如果您的目标是旧版本的 .net,您可以使用指令来返回接口并将其标记为已弃用。

它是一个公共 API,被成千上万的应用程序使用,并且该 API 自 .net 1.0 以来一直是公开的并在使用中。 相反,它并不“简单”。 我们不能仅仅改变 API,因为微软认为这是他们必须做的事情才能让我们的生活更美好:这对我们的用户和我们来说都是一个巨大的负担。

是的,这确实很糟糕,但我相信他们(一直在思考这个问题的非常聪明的人)当时是有充分理由的(回到 .net 2.0)。 可以进行讨论吗,也许这肯定会改变.. 但事实是人们升级到新的运行时将不得不做一些工作。 它不是在一个花哨的 ui 中点击一个按钮,然后为他们剪掉一些工作。但与此同时,我同意你的观点,升级到新框架应该比去和替换一堆类型更容易。

就是这样:它不被视为“新运行时”。 如果是这样的话,就像我已经讨论过的那样,这将不是问题。 然而,微软认为 CoreFX 目标 dll 也必须在 .NET full 上工作,所以没有新的框架。 对于面向 .NET full 的库的维护者来说,CoreFX 中没有的功能(所以今天有很多库)会带来很多“乐趣”,因为他们将不得不维护 2 个版本。 不是 2 或 3 个 #ifdevs 问题就解决了,而是其中很多。 也许这对您来说有所不同,因为您可以向客户收取更改 apis 所花费的时间。 当您创建一个被许多人使用的通用系统时,情况就不同了。

如果紧凑的框架支持是任何指导方针,那么在完整的 .net 和 CoreCLR 上支持 ORM 将是一个可怕的时间槽,很多挫折,实际上并没有真正获得多少:你不会获得新功能,你只能解决它们的 _absence_ .

(并且在有人开始说:“但它在 linux 上运行,你会得到那个”:我们的东西在 Mono 上运行多年。所以不,这不是一个真正需要获得的新功能,它已经存在了)。

SellMeThis 框架。 哦,为什么我什至打扰。

但是从那时起构建的任何东西都依赖于基类

_cough_ Linq-to-SQL DataContext _cough_

事实上,许多非 MS ORM 都是如此,因此存在问题。 对于小巧玲珑,我们只是一点点
项目符号并迁移到 DbConnection。 如果我们有时间重来,我
强烈建议 MS 在过时时使用 [Obsolete]。 但:
我们无法改变过去。

然而,这是一个非常痛苦的问题需要解决,尤其是因为大多数
库作者需要继续使用这两个 API(编译一种方式用于
net40 等,以及 DNX 的另一种方式)。 我以前发过可怕的烂摊子
那个 dapper 用来做这件事的:它不漂亮,也不是那么简单

如果。

2015 年 11 月 27 日 20:07,“Natan Vivo”通知@ github.com 写道:

在此讨论中要考虑的一个重要事实是 IDb*
十年前,接口在 .NET 2.0 中作为 ADO.NET 的 API 被弃用
当引入基类时。 它们没有被标记为已弃用,但是
从那时起构建的任何东西都依赖于基类。
想到了 App.config 提供程序和连接字符串支持。

如果您有依赖于这些接口的代码,那么您正在针对
非常过时的 API,不支持异步方法之类的东西,这意味着
如果您希望人们继续使用它,则无论如何都需要对其进行更新。


直接回复此邮件或在 GitHub 上查看
https://github.com/dotnet/corefx/issues/3480#issuecomment -160198453。

我没有看到由于使用接口而无法添加异步方法的意义。 您可以为异步方法创建接口。 IAsyncDbCommandIAsyncDataReader等等。然后你可以让基类实现这两种类型的接口。

ADO.NET 用户要么使用异步版本,要么使用同步版本,而不是同时使用两者。 所以这会很有效。

对于库开发人员来说,功能是否增长并且接口保持不变并不重要。 这不就是目的吗? 为新功能引入新界面。 使用基类只是一种痛苦。

我可以在这里总结一下这个主题吗?

多位独立公认的 .NET 数据工具社区专家,
包括多个 ORM 作者和维护者告诉你 - 相当
很明显——这代表了一系列重要的问题。 我不认为
我们中的任何人都不知道其中的微妙之处,或者对编程很幼稚
原则,我们大多数人(如果不是所有人)都知道所有的背景故事,
因为我们当时就在那里。

官方的回应似乎是“对我们来说似乎很好,EF很高兴”。
是的,我们知道这一点,因为这个决定是首先做出的。

好吧,我们都表达了我们的意见,即使它没有成果。
2015 年 11 月 27 日 20:41,“Jonas Gauffin”通知@github.com 写道:

我没有看到无法将异步方法添加为
使用接口的结果。 您可以为以下对象创建 _new_ 接口
异步方法。 IAsyncDbCommand, IAsyncDataReader 等。 然后你可以
使基类实现两种类型的接口。

ADO.NET 用户使用异步版本或同步版本
版本,而不是两者。 所以这会很有效。

对于库开发人员来说,功能是否增长并不重要
接口保持不变。 这不就是目的吗? 介绍新的
新功能的接口。 使用基类只是一种痛苦。


直接回复此邮件或在 GitHub 上查看
https://github.com/dotnet/corefx/issues/3480#issuecomment -160201361。

伙计们.. 更新您的代码并提升您的主要版本。 完毕。

是的,但是在定位核心时,没有理由不能使用编译器指令来填充该接口。 我已经用我们的一些 pcl 包做到了这一点。

我确实认为微软需要重申核心不是 dot net full 但这仍然没有帮助..我认为微软需要清理一些接口说实话。 最近有一篇博客文章在那里有非常不一致的接口,你永远不知道该选择哪个..我认为定义第二个异步接口很糟糕。 如果一切都是异步的就好了..

如果通过完整的框架来确保需要标记为已弃用的内容......并作为 4.6.2 发布,那就太好了

@mgravell +100。 说得好,100%同意。

实际受影响有多大? 我们在这里谈论coreclr? .NET 桌面将在未来很多年继续存在,直到 coreclr 能够赶上。 正是针对那些抱怨的人,您在这里承保什么损失? 很多人基本上都说这是世界末日。

@leppie确实,它将存在很多年。 在未来的几年里,我们也必须在我们的代码中维护这些黑客和变通方法。 这里的争论点是移除两者之间的共同桥梁。 该工作量已转移到所有库开发人员而不是 BCL。 我理解界面的利弊两面,但我不理解这里某些人的“它是次要的,继续前进”的态度。

让我们在这里坦率地说:如果图书馆使用者都必须做同样的事情,那么它应该在 BCL 中。 辩论是“那采取什么形式?”

移除接口的好处是,有一个 _additional_ 版本控制机制,现在正在使用新的打包模型:现在工具可以更好地支持 X、Y 和 Z 中可用的类。 例如,目前 dotnet5.2 与 5.4。 但是那里也有缺点。 例如,SqlClient 仍然没有实现今天的接口(参见 dotnet/runtime#14302 和 dotnet/runtime#15269),并且考虑到@YoungGah所说的(除非我误读)我们正在等待在 5.4 或 5.5 上为模式、批量插入等提供相同级别的支持。

那么 5.6 (1.5) 会发生什么? 如果将更多成员添加到抽象类中,每个数据提供者应该跟上移动目标的步伐吗? 每个消费者都需要对每个可用的功能进行版本确定? 我们需要为平台的每个版本编译一个版本的程序集,以匹配传入的类的抽象基类? 所有这些如何通过添加来进行还不是 100% 清楚。 没有变化的界面要清晰得多。 最重要的是文档:哪些功能、方法等可用在哪些版本_和平台_将成为所有图书馆作者前进的巨大痛点。 这种担忧在这里只是半相关的,但它在起作用。

至于现在,我正在焦急地等待未来两周的更新。 我担心它会有效,因为一直以来的消息传递都是:“我们正在做 X,因为它适用于 SQL 和实体框架,这已经足够了”——没有使用任何这些词。 对我而言,库作者方面的挫败感是几个月来在这些方面缺乏进展(在代码和讨论方面)。

100% 同意。

当我设计了 SqlFu(我的 Micro ORM)的 v2(及更高版本)时,我必须决定将扩展方法附加到何处:附加到 DbConnection/DbCommand 或接口。 我找到了这个,我决定去抽象类。

因此,我没有受到影响,尽管我受到IDataReader删除的影响,因为 MS 以他们的智慧决定不明确说明哪些接口应该被视为过时,哪些不应该被视为过时。 但就我而言,更换接口并不难。

但是,我看到了拥有专用接口的价值,而且我认为 MS 保留/添加它们并不难。 如果他们认为旧的界面设计得不是很好,那很好! 设计新的更具体。 至少在未来我们不必处理同样的问题。

(这是美国的感恩节周,所以微软人的回应将非常有限,直到他们下周回到办公室)

只是想重申 - 以免在此线程中丢失任何好的建议/错误,如果您对当前基类/表面区域和/或它们的未来版本有问题,请提交新问题,让我们只讨论 v1 接口。

@NickCraver类似于 .NET Framework 的版本间兼容性,.NET Core 表面区域的版本之间不会有重大变化。 例如,向基类添加抽象成员将是_不会_进行更改的一个示例。

@davkean你有多大信心这不会发生? 鉴于我们看到的是一个不完整的表面积并且不能保证会改变,很难相信丢失的部分不会在以后出现。 但是,我认为没有重大更改是_更重要的事情,我相信这里的大多数库作者也会假设这一点。 这意味着在 RTM 命中之前妥善处理这些项目更为重要。

对于在表面区域提交的单独问题的记录,请参阅 dotnet/runtime#14302 和 dotnet/runtime#15269 分别于 9 月 25 日和 10 月 2 日提供 Microsoft 的最新更新 - 尽管此后多次要求更新和活动。 那是 2 个月和 2 次发布,一直保持沉默。 尽管 dotnet/runtime#14302 是这个 repo 中最活跃的问题(这个问题刚刚成为第二个)。 你能理解我们的沮丧吗?

我绝对有信心我们不会进行重大更改,Data Common 团队正在考虑在不引入抽象成员的情况下进行更改。

@NickCraver抱歉,我们在这里做得很糟糕 - 这些问题没有被遗忘, @YoungGah在上面提供了有关它们的更新,我会确保她根据进度更新问题。 许多 MS 仍然习惯于这种_在开放事物中工作_,随着时间的推移它会变得更好 - 感谢您打电话给我们。

@niemyjski

dnx 是全新的框架/运行时

如果您认为 dnx 运行时和 corefx 库是凭空出现的,那么您就严重低估了从头开发它所需的时间。 CoreFx 库在完整的 .NET Framework 上运行的事实应该给你一个线索,不,它不是全新的。

如果它没有一些接口来处理它......使用编译器指令。

是的,但是在定位核心时,没有理由不能使用编译器指令来填充该接口。

如果您在进入这个线程之前不厌其烦地阅读评论,您就会知道 a) 是有原因的,并且 b) 对于核心 BCL 接口来说,这是一个不可行的策略。

@nvivo

在此讨论中要考虑的一个重要事实是,十年前引入基类时,IDb* 接口作为 .NET 2.0 中 ADO.NET 的 API 已被弃用。

如果是这种情况,那么您就不会打开一个问题,告诉他们恢复使用十年前您明知已被基类弃用的接口。 不推荐使用的 API 通信方式是使用[Obsolete]属性,这是其存在的唯一目的。 如果它没有被广泛传播,那么无论您现在的想法如何,它都不会被弃用。 大多数非 MS .NET ORM 依赖于它们的事实应该表明它的弃用传达得很差,如果有的话。

如果您有依赖于这些接口的代码,那么您正在针对一个非常过时的 API 进行编码,并且不支持异步方法之类的东西,这意味着如果您希望人们继续使用它,您无论如何都需要对其进行更新。

一个虚假的稻草人 - 一个并不意味着另一个。 我们已经添加了对异步 API 的支持,不,添加它们不会破坏现有的客户代码库,也不需要更改任何现有的 API。

好的,这一切让一个干净的开始很棒,但我可以问一个问题:为了支持你自己的框架做出了哪些妥协? 过去的哪些恐怖已经被迁移,因为它们需要让实体框架运行?

使 MicroORM 消失将是一种耻辱,它们使 .Net 代码具有一定的性能(EF 对于无法接受 500 毫秒加载几行的应用程序来说是一种无法使用的野兽)。

至于接口与基类:只要可以重用的所有东西都是虚拟的,基类就很好。 例如,WCF 中最令人恼火的设计决策之一是大量使用包含许多功能的密封类。 假设您必须对处理 XML 消息的方式进行微调(因为:互操作)。 您必须重新实现,而不是继承和覆盖一个小函数。 在 WCF 示例中,SOLID 中的 S 被跳过,因此您通常需要实现一个大型接口,而无需进行任何测试以确保其具有生产质量。

所以:我们可以适应的基类是一件好事。

我绝对有信心我们不会进行重大更改,Data Common 团队正在考虑在不引入抽象成员的情况下进行更改。

@davkean这是不可能保证的,你知道的。 ADO.NET 是一个子系统,用于与具有多种功能的 3rd 方软件进行通信,这些功能通过带有一些扩展点的通用 API 公开。 事情发生了变化,即使在数据库领域,这些变化也会波及到用于通信和使用这些外部数据库服务的 API。 此外:行为的变化_是_也是一个突破性的变化。 过去几年,我们在 ADO.NET 中也看到了这些(例如关于错误处理)。

整个 ADO.NET API 充满了这些变化的副作用,通常由 SQL Server 驱动; 一般情况下,事物是经过设计的,然后转移到 SQL Client 的情况几乎不是这样,但反过来(例如,在基类中被 SqlClient 忽略的功能并不多,如果有的话)。 添加到第 3 方所需的东西从未进入 API。

简而言之,它是一个 API,它在 .NET 1.0 开始时具有通用设计(事实证明在许多领域存在严重缺陷),从那时起,它通过左右修补功能来应对变化景观。 _大多数_仍然在 API 中(如果不是全部的话)。 现在微软将删除一个:接口。

通过删除 API 的随机部分绝对_没有_获得任何好处:没有通过此添加任何功能(例如,您可以花时间在此而不是将其推回此处),但是利用该 API 部分的代码_将_不起作用。 如果所有这些奶酪移动背后的要点是“重新开始”,那么一定要这样做,但是通过重新设计 API 使其成为真正的通用 API 来实现,摆脱_所有_多年来堆积的杂物.

但这还没有完成。 没有人想知道为什么,我们都知道为什么。 我们也知道,如果删除接口会严重伤害 MS 内的团队,那么它们一开始就不会被删除。

如果 Microsoft 可以通过基类添加新功能,那么 3rd 方提供程序会自动实现该功能的“一种形式”(例如使用 Async,如果提供程序未实现,则异步方法回退到同步方法),太好了:意味着我们 3rd 方 ORM 开发人员(并面对现实:许多开发人员通过 3rd 方(微)ORM 的代码访问_您的_API)可以简单地定位一个类,仅此而已。

但不能保证你会这样做。 例如,Microsoft 内部没有人会为将 Oracle 或 PostgreSql 添加到 ADO.NET 的特定功能而烦恼。 例如,UDT 的使用、文档树、通过游标的多个结果集,每个 ADO.NET 提供者都必须找到自己处理这些功能的方式。 如果(何时?)SQL Server 获得文档树功能,例如使用 JSON 文档,那么 ADO.NET 是否会更新为新的 API? 就像您过去对 ADO.NET 提供程序的错误报告所做的那样?

你不能做出这样的保证,在这里声明 MS 将来不会破坏任何东西是不明智的。 他们总是有,有时甚至不得不:毕竟每个 API 都有缺陷,而 ADO.NET 充满了缺陷; 总有一天有人会“修复”它们。

所以,总结一下:接口是 ADO.NET 的一部分,API 中其他地方的拙劣部分也是 ADO.NET 的一部分。 那些没有被删除以修复 API,也没有重构 API 以使其成为更通用的 API:它保持原样,删除了一些元素,如 DataSet/Table 依赖元素,因为这些元素没有移植(并且有是否还有其他问题正在讨论类似的进展),除了......接口被删除。

单从这个角度来看,已经没有任何意义了。

@mythz

如果是这种情况,那么您就不会打开一个问题,告诉他们恢复使用您明知已弃用的接口

你不可能阅读OP并理解这一点。 这些讨论变得过于宗教化,而你只是假设了没人说过的事情。

我打开这个问题是因为我相信接口更擅长描述 api 并帮助测试。 如果完成了,我认为它们不应该与有问题的 15 年历史的 api 兼容。 向后兼容性从来都不是问题的重点,直到你们将讨论转移到那个问题上。

并不是说我认为事情应该为了它而破裂。 但是界面版本控制是过去的问题。 如果 corefx 在主要版本之间更改了某些内容,则其预期的主要版本会有重大更改。 如果他们破坏了次要版本之间的接口,那只是草率。

我们已经添加了对异步 API 的支持

您不能在同步 api 之上添加异步 api。 如果您使用 IDbConnection 或 IDbCommand 执行此操作,则您做错了。 如果您不使用这些接口,那么您实际上没有任何意义捍卫与它们的任何向后兼容性。

我们已经添加了对异步 API 的支持

您不能在同步 api 之上添加异步 api。 如果您使用 IDbConnection 或 IDbCommand 执行此操作,则您做错了。 如果您不使用这些接口,那么您实际上没有任何意义捍卫与它们的任何向后兼容性。

在 ADO.NET 中,这就是他们所做的:默认情况下,异步方法回退到同步变体。 这样,所有不支持异步的 ADO.NET 提供者(阅读:此时除了 SqlServer 之外的所有提供者)不必实现他们不支持的东西:ORM 中的第 3 方代码提供异步 api 可以针对ADO.NET 中的异步方法,如果 ado.net 提供程序不支持异步,实际上没有人会知道。 嗯...你会知道,因为它更慢,但除此之外。

现在它也很好地说明了 ADO.NET 中没有任何“设计”或通用体系结构:在通用 API 中没有_没有_方法可以创建事务保存点。 尽管几乎_所有_数据库都支持在其从 DbTransaction 的派生类上使用“Save(string)”方法。 除了 OleDbTransaction (因为 MS Access 不支持它,至少这是我的怀疑)。

这并不容易,但没有人说这很容易。 这个问题并不新鲜,OleDB 和 ODBC 已经处理了很多年,JDBC 已经找到了解决它的方法,微软不必重新发明轮子来克服这样的事情。 它也不是 DB 领域独有的:例如,每个视频卡都通过其 API 支持不同的功能子集,并通过 Direct3D/X 向开发人员公开。 在这些其他世界中设计的方式实际上很有趣:API 是设计好的,需要支持它的各方(JDBC 驱动程序、OleDB 驱动程序编写者等)必须实现这些。 您的驱动程序不支持 X? 您的驱动程序不符合 X。“Oracle 不支持 ADO.NET v10”。 Oracle 内部没有人愿意阅读该内容。 相反 SqlClient 是领先的,从货车上掉下来的东西被添加到 ADO.NET 中,就是这样。

在 ADO.NET 中,这就是他们所做的:默认情况下,异步方法回退到同步变体。

不,它不是。 API 公开默认情况下回退到同步方法的异步方法,但提供程序会覆盖真正的异步操作。 @mythz 所说的是他正在使用 IDbCommand 和 IDbConnection 并这样做。

这是不可能的,期间。 如果你这样做了,要么你做的不对,要么你没有使用界面。 如果底层 api 不是异步的,你就不能发明异步。

不,它不是。 API 公开默认情况下回退到同步方法的异步方法,但提供程序会覆盖真正的异步操作。 @mythz 所说的是他正在使用 IDbCommand 和 IDbConnection 并这样做。

除了 SqlClient 之外,没有官方提供者这样做,所有其他的,例如 ODP.NET,不实现任何形式的异步代码,因此调用代码回退到同步变体(DbDataReader/DbCommand 等中的异步方法,它们实际执行同步代码)。 所以用户代码调用了一个异步变体,它在后台进行同步操作。 这导致在实践中没有执行异步(因为所有代码在实践中都是同步的)。 也许 devart 的提供者在他们自己的实现中实现了一个异步 api,不确定。

无论如何,这不是关于做对,而是关于 API 的版本控制。

@nvivo

我打开这个问题是因为我相信接口更擅长描述 api 并帮助测试。 如果完成了,我认为它们不应该与有问题的 15 年历史的 api 兼容。 向后兼容性从来都不是问题的重点,直到你们将讨论转移到那个问题上。

好的,所以您知道核心 ADO.NET 接口在 10 年前已被弃用,所有内容都转移到基类,但您认为他们现在应该放弃它并返回到接口,巧合的是使用与现有接口相同的名称,但是现有的接口应该不再存在,因为不需要向后兼容,对吗? 当然,听起来合法。

如果你想推动一个平台向前发展,你可以随着时间的推移不断发展 API 并并行支持它们,让每个人也有能力支持并行 API,并允许他们计划自己的方式和他们的客户。 在没有警告的情况下撕掉它们会不必要地破坏依赖它们的生态系统,并将复杂性降低到每个下游依赖项。

您不能在同步 api 之上添加异步 api。 如果您使用 IDbConnection 或 IDbCommand 执行此操作,则您做错了。 如果您不使用这些接口,那么您实际上没有任何意义捍卫与它们的任何向后兼容性。

我希望你不要用对你显然不了解的事情的评论来污染这个线程。 如果您想了解 Async API 的实现方式,请阅读源代码 - 并停止盲目传播虚假信息。 第三方库不可能扩展 System.Data 接口。 我们提供了一个与实现无关的 API,为了支持每个主要的 RDBMS,它公开了每个 ADO.NET 提供程序在其面向外部的 API 中实现的最小依赖 - 即核心 System.Data 接口。 异步 API 是 IDbConnection 的扩展方法,它在幕后利用了支持它们的具体 ADO.NET 提供程序上的异步 API。 在内部,每个受支持的 ADO.NET 提供程序都有具体的依赖关系,而不管是否支持异步。 您关于我们“实际上没有任何意义捍卫与它们的任何向后兼容性”的建议是缺乏经验且完全没有根据的。

让我向围栏的 Microsoft 一侧提出这个问题(cc @davkean @YoungGah):假设这是一个完美的世界,什么也没有出现。 你什么时候想破坏东西? 像 6.0 这样的主要版本? 其他时间? 反对接口的论点是它们没有版本。 嗯,是的,这是有效的 - 但是_如果我们也不更改抽象类_,这也是一个有争议的问题。 那么......我们可以在那里得到一些澄清吗?

跟进:
如果答案是肯定的(在 RTM 之后的某个时间点会有变化),那么我们会看到什么样的休息? 添加,新方法? 如果我继承了我的提供者的基类,那么当您添加人们正在使用的冲突方法时会发生什么?

如果答案是否定的(从不):为什么不直接添加接口?

这个线程有点挂在讨论_现在_ - 这主要是一件好事,因为这些东西需要尽快修复。 这里的每个图书馆作者都知道在发布完成后获得任何东西是多么困难,这就是我们如此努力的原因。 不幸的是,对于_未来_的添加和更改(如果有的话)缺乏明确的计划,这会导致更多信息不足的争论。

未来有什么计划?

在这种情况下,我们不应该通过抽象类强制实现。 海事组织

Microsoft 不会对 .NET 4.5 进行破坏性更改。 它是 Windows 的一部分。 兼容性为王。

Microsoft 可以在 .NET Core 中做出不影响 4.5 的重大更改。 我怀疑他们是否会在 ADO.NET 等低级别发布 1.0 RTM,但门槛较低。 它不是 Windows 的一部分,.NET Core 版本可以并行部署。

抽象类可以更改 - 这并没有破坏。 只需添加一个具有默认实现的虚拟方法。 它已经用 ADO.NET 异步方法完成了。 随着 .NET Core 的引入,我相信共享类的更改需要与 .NET 4.5 版本一起完成,以保持兼容性。 如果这是错误的并且仅适用于接口,有人会纠正我:grin:

@FransBouma

除了 SqlClient 之外,没有任何官方提供者会这样做,所有其他提供者,例如 ODP.NET,不实现任何形式的异步代码,因此调用代码回退到同步变体

你是对的,但这不是 API 的问题,而是实现者更懒惰或缺乏理解的问题。 例如,MySql 连接器通过创建 TaskCompletionSource 并使用同步方法完成它们来重新实现所有异步方法,这很荒谬。 他们可以只删除一半的代码库并保持相同的行为。

并不是说接口会解决这个问题,但没有异步的默认行为至少会让他们中的一些人考虑清楚。 90% 的技术人员不了解异步操作这一事实也无济于事。

抽象类可以更改 - 这并没有破坏。 只需添加一个具有默认实现的虚拟方法。 它已经用 ADO.NET 异步方法完成了。

这是打破。 对于在不知道它被添加的情况下对这个实现进行子类化的所有库来说,这是破坏性的,然后人们就会消费这种想法。 哦,这个实现现在支持 postgresql BAM 错误 wtf 发生...

数据库抽象的强制实现是错误的。

它的接口或基类无关紧要。 会有突破性的变化。 但是强制预定义实现是错误的。

多态性不是这样工作的。 你不能在不知道的情况下覆盖一个方法。 如果您的引用是 DbConnection 并且您调用 QueryAsync,它只会调用该方法或它已被覆盖的任何内容。 不会调用恰好已经存在于子类中的名为 QueryAsync 的方法。

你会混淆覆盖一个方法与用相同的名称隐藏它。

@JamesNK

如果方法定义为抽象方法,则基类中不存在实现。 这违反了第 3 方的合同,因为它要求他们在子类中添加实现。

如果您将方法设为虚拟以便可以覆盖它,则基类中存在对子类没有意义的实现。 这仍然是破坏性的,因为存在未由库作者实现的实现。 确保您的应用程序可以编译,并且一切都很好,但是有人调用了该方法,并且它对子类无效。 那是错误的。 这是不属于子类的强制实现。

因此,可以存在不属于子类的实现的抽象类。 或者第三方不存在默认实现的接口。

@phillip-haydon 这就是为什么它被实现为虚拟方法,而不是抽象方法。

您可以添加内容,它只会破坏已经具有相同签名(名称/参数)成员的子类。 如果 args 不同,如果开发人员错误地重载可能会引入微妙的错误。

这是不属于子类的强制实现。

那就别放那里了。

@jamesnk

不要把它放在那里。 这就是为什么我们争论删除接口的原因。

虚拟化并不能解决问题。 不应该有预定义的实现。 故事结局

@JamesNK在这种情况下,我们没有把它放在那里, throw new NotImplementedException()里面的那个虚拟? 首先,这并不是它存在的理由,它充斥着更多(运行时)问题。

让我们看看今天:当提供者支持它时,我更愿意看到一个IDbAsyncConnection添加,而不是一堆在幕后同步导致混乱和低效率的方法,这就是我们今天对这些的

我更愿意看到一个 IDbAsyncConnection 在提供者支持时添加,而不是一堆在幕后同步的方法导致混乱和低效

@NickCraver +1000。 就像这里的这个错误,oracle 团队只是不明白异步意味着什么。

你可以用接口来做到这一点。 它们的问题是你不能接受需要多个接口的参数,例如我需要一个既是 IDbAsyncConnection 又是 IDbConnection 的类型。 你失去了强类型,你必须开始查询接口 COM 风格,我认为这不是非常用户友好。 它是 API 设计中的一个工具,有其一席之地,但我不知道我是否会默认使用它。

如果默认实现抛出 NotImplementedException,那么将其栓接到基类是错误的做法。 就像我说的,那就不要把它放在那里。 如果您看到有人这样做,请提出问题。

无论哪种方式,无论是接口还是抽象基类,我的经验是将新功能添加到最初不是为它们设计的库中而不破坏世界是非常困难的。

@JamesNK大概IDbAsyncConnection会在这里继承IDbConnection ,但不一定是这种情况 - 他们可以共享共同的成员或从共同的基础继承。 例如在 Dapper 中,我们可能会实现如下:

``` C#
IEnumerable询问(这个IDbConnection cnn,CommandDefinition cmd)

``` C#
Task<IEnumerable<T>> QueryAsync<T>(this IDbAsyncConnection cnn, CommandDefinition cmd)

我想大多数具有同步/异步方法的库都有类似的用途和实现。

_Edit:_ 打字后,我意识到名称末尾的Async对所有这些都好得多......

多年来,ADO.NET 发生了以下主要变化:

  1. 在 2003 (1.1) 上,他们对 1.0 进行了重大更改并重新设计了它
  2. 在 2005 (2.0) 中,他们使用今天存在的基类转移到提供者模型
  3. 在 2012 (4.5) 上,他们添加了异步支持,除了添加异步执行相同操作的新方法之外,实际上并没有改变任何其他内容。

回到 2003 年 api 的定义方式是一个改变,要么破坏兼容性(人们不想要),要么删除过去十年添加的功能。 但是,使用当前设计向 .NET Core 添加新接口与任何 .NET 版本_源兼容_。 重新编译是您保持过去 15 年编写的大多数代码工作所需的全部内容。 而且您无论如何都需要重新编译以定位 corefx。

这个 API 已经稳定了很长时间。 如果人们愿意,它可以重新设计为界面。 像往常一样,这里没有技术问题,归结为疤痕、偏好和自我。

为什么不把接口带回来并将它们标记为过时?

虽然这个想法被放弃了,但我想知道在这种情况下,组装中性接口是否可以帮助看到这个http://davidfowl.com/assembly-neutral-interfaces/然后它们的实现
http://davidfowl.com/assembly-neutral-interfaces-implementation/

我认为装配中性接口在这里是一个红鲱鱼; 如果有的话
要发生,这完全是“共同”的事情,因为“共同”存在。 加上它
由于该功能已消失,因此没有实际意义。
2015 年 11 月 28 日下午 5:38,“Shahid Khan”通知@ github.com 写道:

虽然这个想法被放弃了,但我想知道大会是否中立
inferfaces 可以在这种情况下有所帮助看到这一点
http://davidfowl.com/assembly-neutral-interfaces/然后他们的
执行
http://davidfowl.com/assembly-neutral-interfaces-implementation/


直接回复此邮件或在 GitHub 上查看
https://github.com/dotnet/corefx/issues/3480#issuecomment -160323344。

我观察到的:

  • CoreClr(新的闪亮框架)理念背后的人与其实现背后的人有着完全不同的心态(向后兼容 15 年的旧代码库是王道)。

我的想法:

  • 删除接口会使事情变得更糟
  • 没有什么是过时的,直到用 [Obsolete] 装饰。 在任何时候,谁想什么都无关紧要。 这是合同,如果您根本不满意它,那么您就不是。

我想要的是:

  • 使用接口作为 API 的基面而不是基类。

我的感受:

  • 我们在2015年后期和它的令人沮丧和难过,看到周围的人这争论。

接口不能版本化。

接口肯定可以通过接口继承轻松版本化吗? 如果它做一些完全不同的事情; 以及它的不同界面。

interface IOldInterface {}
interface INewInterface : IOldInterface {}
interface IDifferentInterface {}

class SomeClass : IOldInterface, INewInterface, IDifferentInterface
{
}

只是不要称它为IInterfaceV2IInterfaceV3它需要解释它添加的内容。

将旧接口放在上下文[Obsolete]中,并使用一些新接口并按照它们的实际名称调用它们; 而不是在这个时代看起来很正常的非异步方法。

public interface IDbUtilityProviderFactory 
{
    IDbConnectionStringBuilder CreateConnectionStringBuilder();
    IDbParameter CreateParameter();
}

public interface IDbBlockingProviderFactory : IDbUtilityProviderFactory 
{
    IDbBlockingCommand CreateBlockingCommand();
    IDbBlockingConnection CreateBlockingConnection();
}

public interface IDbAyncProviderFactory : IDbUtilityProviderFactory 
{
    IDbCommandAsync CreateAsyncCommand();
    IDbConnectionAsync CreateAsyncConnection();
}

@abatishchev

CoreClr(新的闪亮框架)理念背后的人与其实现背后的人有着完全不同的心态(向后兼容 15 年的旧代码库是王道)。

谢谢你说清楚,看来需要指出来。 一般来说,我相信 MS 团队非常关心向后兼容性,这对于语言及其平台的发展至关重要。 只是围绕 System.Data 的决策并没有考虑到更广泛的生态系统——这些核心抽象应该平等地服务于生态系统。

  • 通过删除接口,你会让事情变得更糟。
  • 没有什么是过时的,直到用 [Obsolete] 装饰。 在任何时候,谁想什么都无关紧要。 这是合同,如果您根本不满意它,那么您就不是。

恰恰。

关于接口版本控制。 如果我错了,请纠正我,但我认为 CoreClr 的重点是更细粒度的独立版本控制? 突破性改变? 繁荣! 新的主要版本发布。
看起来,经过 15 年的设计经验,您将重复同样的错误,但现在拥有独立版本和发布的 nuget 包。 上面的论点与旧的单体框架相同,尽管它不再是了。
如果必须向后兼容,那么您就不能删除这些接口。 如果不是,那么让我们停止谈论它并从头开始设计 API,这一次听取主要 ORM 作者和其他 ADO.NET 经验丰富的开发人员的意见。
谢谢你。

@abatishchev很好的观点。 我自己也想知道:向后兼容论点的真正意义是什么? 如果向 CoreClr 添加一项新功能,则使用它的所有内容都不会在 .net full 上运行,因此为了安全起见,只能使用普通行为。 (这从来没有奏效)。

抱歉让我久久沉默。

移植到 .NET Core

首先,让我们谈谈房间里的大象,那就是 .NET Core 几乎没有像许多人(包括我们)所希望的那样多的可用 API。

我正在与我的团队一起整理一组关于我们将如何处理将现有资产移植到 .NET Core 领域的文档。

我们计划将目前仅存在于 .NET Framework/Mono 中的更多功能移植到 .NET Core。 这份文件将说明我们将如何做到这一点、我们如何确定优先级以及机制将是什么。 我不仅希望这项工作能够公开进行,而且我还希望社区能够帮助我们移植更多功能。

重大变化

当人们谈论 .NET Core 时,有一个方面会引起很多混淆。 让我澄清一件事:

如果 .NET Core 的 API 比 .NET Framework 少,这并不是一个重大变化。

原因是 .NET Core 是一个新平台,从技术上讲,它可以拥有一组任意的 API。 然而,当然,我们不想要一个任意的集合——这就是我们过去所做的。 .NET Core 的目标是创造一个故事,让人们可以创作将在 .NET Framework 和 .NET Core 上运行的库(并使用控制台应用程序在一定程度上扩展甚至应用程序)。 这要求两个平台都有一个 100% 兼容的子集。 在这种情况下,您会听到我们谈论重大更改。

最重要的是,我们的目的是在 .NET Core 中拥有较高的兼容性。 换句话说,我们不打算在 .NET Core API 的一个版本和另一个版本之间执行 API 中断更改。

接口

向接口添加成员——根据定义——是一个突破性的变化。 有些人认为影响很小,并且有办法对其进行建模,但就今天而言,它是一个二进制和源中断的变化。

WinRT 基于 COM 因而严重依赖于接口,通过创建更多接口来解决这个问题,例如IFooIFoo2IFoo3 。 这是可行的,但如果没有运行时或语言功能使其可以忍受,它肯定会很混乱。 到目前为止,这样的功能还不存在。 但是,作为语言设计团队的一员,我很想听听建议。 其他语言和平台在该领域有相关的想法,我也在积极研究选项(例如混合/特征、Swift 的扩展-一切,或接口的默认成员)。

由于我们仍然关心向后兼容性,因此我们通常更喜欢抽象基类型而不是接口。

ADO.NET 接口

说了这么多,让我们来谈谈这个线程中的原始问题:公开 ADO.NET 提供程序接口。

正如 David 解释的那样:当引入抽象基类型时,我们认为这些已被弃用,这是在 .NET 2 / Visual Studio 2005 中。似乎人们强烈认为拥有这些接口对于将 ORM 框架移植到 .NET Core 至关重要。 对我来说,这提供了足够的证据表明我们应该将接口移植到 .NET Core。

但是,与所有新 API 一样,我们需要注意 .NET Core 的主要目标之一,即具有组件化堆栈。 接口IDataReader依赖于DataTable ,而后者依赖于DataSet 。 正如 dotnet/runtime#14302 中所述,我们不反对添加对DataTable支持,但我们认为DataSet遗留的。 但是,我们可能仍然将它添加为一个单独的包,但无论哪种方式,这都需要打破从接口 -> DataTable -> DataSet的依赖链。 我将与@YoungGah 合作,看看我们可以在那里做些什么。

这种方法会解决这些问题吗?

除非我弄错了,IDataReader 中唯一的 DataTable 依赖项是
GetSchemaTable() 方法,已经在 DataTable 中详细讨论过
链。 然而,我确实欣然承认,事实上有一个
希望在以后添加_some_头脑中的类似功能(无论是
是否通过 DataTable)使得将其暴露在
接口,因为以后扩展接口是有问题的。 不会的
就像“暂时删除方法,稍后添加其他内容”一样简单
2015 年 12 月 5 日上午 12:17,“Immo Landwerth”通知@github.com 写道:

抱歉让我久久沉默。
移植到 .NET Core

首先,让我们谈谈房间里的大象,那就是.NET
Core 几乎没有像许多人那样多的可用 API —— 包括
我们——希望如此。

我正在与我的团队合作,将一组关于我们进展的文档放在一起
接近将现有资产移植到 .NET Core 的领域。

我们正计划移植更多目前仅
存在于 .NET Framework / Mono 到 .NET Core 中。 该文件将调用
我们将如何做到这一点,我们如何确定优先级,以及机制将如何
是。 我不仅希望这项工作公开进行,我还希望
启用社区帮助我们移植更多功能。
重大变化

当人们谈论时,有一个领域会引起很多混乱
.NET 核心。 让我澄清一件事:

如果 .NET Core 的 API 少于 .NET,这并不是一个重大变化
框架。

原因是 .NET Core 是一个新平台,它在技术上可以
有一组任意的 API。 然而,当然,我们不想要一个
任意集
http://blogs.msdn.com/b/dotnet/archive/2014/12/04/introducing-net-core.aspx
——这就是我们过去所做的。 .NET Core 的目标是拥有一个
人们可以创作库的故事(以及使用控制台应用程序
甚至扩展应用程序)将在 .NET Framework 和 .NET Core 上运行。 这个
要求两个平台都有一个 100% 的子集
兼容的。 在这种情况下,您会听到我们谈论重大更改。

最重要的是,我们的目的是在 .NET Core 中拥有较高的兼容性。
换句话说,我们不打算在
.NET Core API 的一个版本和另一个版本。
接口

向接口添加成员——根据定义——是一个突破性的变化。
有些人认为影响很小,并且有建模的方法
那个,但就今天而言,它是一个二进制和源突破性的变化。

WinRT,它基于 COM,因此严重依赖于接口,
通过创建更多的接口来解决这个问题,例如 IFoo、IFoo2、
IFoo3。 这是可行的,但如果没有运行时或
语言功能使其可以忍受。 到目前为止,这样的功能还不存在。
然而,作为语言设计团队的一员,我很感兴趣
听取建议。 其他语言和平台在这方面有相关的想法
空间,我也在积极寻找选项(例如
mix-ins/traits, Swift 的扩展 - 一切,或默认成员
接口)。

由于我们仍然关心向后兼容性,因此我们通常更喜欢
接口上的抽象基类型。
ADO.NET 接口

说了这么多,让我们来谈谈这个线程中的原始问题:
公开 ADO.NET 提供程序接口。

正如大卫所解释的:当抽象基础
在 .NET 2 / Visual Studio 2005 中引入了类型。似乎
坚信拥有这些接口对于
将 ORM 框架移植到 .NET Core。 对我来说,这提供了足够的证据
我们应该将接口移植到 .NET Core。

但是,对于所有新 API,我们需要注意其中一个
.NET Core 的主要目标,即具有组件化堆栈。 这
接口 IDataReader 依赖于 DataTable,它有一个
对数据集的依赖。 如 dotnet/runtime#14302 中所述
https://github.com/dotnet/corefx/issues/1039 ,我们不反对添加
支持DataTable,但我们真的不想移植DataSet。 所以
添加接口将需要打破这种依赖关系。 我会和
@YoungGah https://github.com/YoungGah一起看看我们可以在那里做什么。

这种方法会解决这些问题吗?


直接回复此邮件或在 GitHub 上查看
https://github.com/dotnet/corefx/issues/3480#issuecomment -162115855。

是的,IDataReader 依赖于 DataTable,而 DataTable 依赖于 DataSet。

如前所述,我们无法从接口中删除成员,因此我们需要以其他方式打破这种依赖关系; 要么不移植接口,要么破坏 DataTable -> DataSet 依赖。

这种方法会解决这些问题吗?

是的,即使没有GetSchemaTable()方法恢复 ADO.NET 接口也可以解决我目前面临的对 ADO.NET 接口问题的严重依赖。

我假设打破 DataTable -> DataSet 依赖,如果这意味着GetSchemaTable()也将被包括在内,这将是那些依赖GetSchemaTable()人的首选方法(所以这将是我的投票) - 但是我会给受此影响的开发人员提供更多权重。

@terrajobst感谢您的总结和分享您的考虑。

@terrajobst感谢您的总结和解释。 我维护 Npgsql,所以我是从 ADO.NET 提供程序的角度编写的,而不是 ORM。

在 .NET Core 中,微软似乎希望移除 ADO.NET 的一些长期遗留功能,即 DataTable/DataSet 和接口。 这将创建一个更好、更清晰的 API,但为已经依赖于接口和 DataTable/DataSet API 的用户创造了大量工作。

我个人认为杀掉DataTable/DataSet是一件好事,应该引入一个新的更好的元数据API(我认为即使不杀掉DataTable/DataSet也是如此)。 我不最小化
这种破坏给 ORM(和其他)消费者带来的痛苦,但要记住的一件事是,如果有机会清理和引入破坏 - .NET Core 就是这个机会。

另一个令人沮丧的因素是,保留 ADO.NET 接口也意味着保留 DataTable/DataSet。 换句话说,虽然我个人对接口问题的感觉不是很强烈,但保留它们意味着保留 DataTable/DataSet,而这似乎更成问题。

因为我不知道有多少 ORM 仍然依赖于接口,而且我也不知道它们转换到基类需要多少努力,所以选择不是很清除这里。 但我的直觉是抓住这个机会把事情清理干净,即使这意味着有些人会受苦......

PS无论做什么,都不要走接口爆炸的路线,即IFoo2、IFoo3。 这只是一个非解决方案。
PPS 如果您决定在 .NET Core 中创建一个新的非 DataTable 元数据 API 并希望 .NET Core 库在 .NET Framework 中运行,这意味着您必须发布带有新 API 的新 .NET Framework .NET 核心。

@terrajobst感谢您打破沉默;)。 我认为核心问题是 ADO.NET 没有设计。 已经有一段时间了,它显示在 API 的各个部分,这是一个填充功能的大杂烩,没有考虑超过几分钟(似乎)。 我同意@roji 的观点:.NET 核心是一生一次的机会来做一些事情,所以 .NET 核心不应该受到它应该向后兼容的(在我看来,愚蠢的)规则的阻碍.NET 完整。

也就是说,如果 ADO.NET 的情况不会变得更好(即它得到了一个真正的设计,其中设计了一个通用 API,然后专门用于 SqlClient,而不是相反!所以功能不可用SQL Server 但在其他数据库中被添加到通用 API 中,而不是留给 ADO.NET 提供程序编写者),那么接下来最好的事情是尽可能多地移植,包括接口和我们自己的 3rd 方开发者 #ifdef周围会留下的坑洼。

然而,DataTable 依赖性可能是一个问题,因为 IDataReader 接口将很难保持与 .NET 完整 _if_ 数据表的向后兼容,无论哪种形式都没有移植。 但我认为无论如何这是一个失败的原因。 MS 多次表示 .NET full 不会像 .NET core 那样收到那么多/那么频繁的更新,所以如果 _new_ 被添加到 .NET core 中,没有人可以使用它,因为使用它会使库立即与 .NET 完全不兼容。 我可能在这里错过了一些东西,所以如果是这样,请纠正我:)

仅此一项就造成了一个奇怪的情况:必须有向后兼容性,但在实践中这似乎很难实现,而且无论如何都是转移注意力。 恕我直言,如果首先解决这个问题,那么其结果可用于就如何处理 .NET 核心的 API 做出正确的决定,即改进它们,而不是从 .NET 中拖出设计不良的旧垃圾。 这并不是说所有的 API 都设计得很糟糕,但是对于 ADO.NET(正如我在之前的一篇文章中所描述的),很多年来事情都做得不好。 彻底打破并实施一个更健壮的系统(JDBC 仍然很强大,ODBC 仍然像 25 年前那样工作)并且更适合于适应变化。

对此多考虑一点, @FransBouma对向后兼容性要求的评论很有意义。 由于组件化的 Nuget 打包,.NET Core 的承诺之一是更快的迭代——而不是 .NET Framework 更新一年一次/两次,.NET Core 更新可以在需要时发布。 如果更新永远不会破坏与 .NET Framework 的向后兼容性,那么它的价值似乎受到严重限制......?

我知道向后兼容性是超出此 ADO.NET 特定讨论范围的要求,我只是想知道。

这是@roji的另一种方式:通过不破坏 API 来启用短迭代。

如果您是 .Net 开发人员,并且由于底层框架的 API 不断变化,您的构建每周都会中断,那么不久您就会开始考虑使用其他平台。

因此,它是由不间断更改驱动的快速迭代。

(已编辑)
您只能在不破坏更改的情况下进行迭代,直到您必须向类接口添加某些内容、行为更改(!)或行为添加,这不是字面意义上的破坏性更改(行为更改是 tho),而是将其用于.net core 将使您的代码与 .net full 不兼容,因此从这个意义上说,它将使 .net core 不再向后兼容 .net full _对于那段代码_。 恕我直言,哪个 .NET 核心始终将具有与 .NET 完全相同的(类)接口和行为,直到最后一个字节(恕我直言不可持续),或者将获得稍后向后移植的新功能(这就是MS 说 btw) 到 .NET full,有效地使其不向后兼容。 这当然取决于您是哪一边:如果您说:“有一组通用的(类)接口 X,具有定义的行为 B,并且 .NET 核心和 .NET 都完全实现了这些,并且 X 和 B 会赢” t 在未来发生变化”,这仍然意味着在 X & B 之外有一个新框架,它将获得新事物,这正是事物可以改变的地方,也是未来所在的地方。

对此可以走得更远,例如,.net 核心中 X 和 B 中使用的接口/类实际上是 .NET 核心中新类/接口的包装器。 然后由使用它们的开发人员来使用 X & B 或具有更好设计且没有与 .NET full 相同的 API 的新的。

由于我们已经必须#ifdef 解决 X 和 B 中遗漏的问题,因此在支持这两个框架时,恕我直言,随着行为迟早发生变化(甚至可能非常微妙),最好能够简单地针对 .NET 核心中的新接口/类,就像在给定情况下的不同异常)无论如何都会在那里突然出现,因此将代码“移植”到 .NET 核心及其 _new_ API(不是 X&B)会更好。 无论如何,我们必须移植,至少我是这么看的。

@ryanbnl ,我同意 @FransBouma。 关键不在于我们希望能够破坏 API。 将 .NET Core 限制为可在 .NET Framework 中运行意味着您将永远无法向任何 .NET Core 接口添加任何内容,或向任何抽象基类添加抽象成员。 对于使用 .NET Core 的人来说,这两个更改并没有真正破坏向后兼容性,它们之一破坏了与 .NET Framework 的兼容性。

@roji除非它是框架包的新外部(例如不是 GAC),否则它可以与框架和核心兼容并按自己的节奏运行; 但是对于 ORM 提供者来说,这可能会发生更多变化,并且名称System.Data.IDbConnection已经被采用了......

@benaadams这是一个有效的评论 - 我只考虑了非 nuget 框架 API,例如 ADO.NET。 然而,这些似乎涵盖了足够的 API 空间,值得关注 - .NET Core 将无法在不变得无法运行的情况下发展其中任何一个(阅读:添加接口方法或抽象方法)

我不确定你对 IDbConnection 的意思是什么......

@roji只是指提供这些类型的新包,例如System.Data.Database ; 保持与核心和完整兼容,不能重新定义任何类型的 GAC 在完整框架中否则会发生冲突。

在关键点上; 是否有任何理由不能在 nuget 中完整地和核心地存在; 并弃用当前的System.Data api post 4.6.1+?

现在会造成更大的痛苦; 但是一些兼容已经被破坏,它删除了接口,或者只删除了DataSet所以一些返工已经需要由 ORM 提供者为 coreclr 完成。

存在于 nuget 和 GAC 框架之外的全新 api 可以向后兼容以与netstandard1.2一起使用,例如 4.5.2+、coreclr、UWP、mono/Xamarin 等。虽然现在会更痛苦 - 但是这可能是一个比以后更好的时间。

鉴于新的 API 正在用于 achema 等问题,表示DataSet不会出现(或DataTable ),这是否应该关闭? 听起来基类是基于 dotnet/corefx#5609 中的注释和类型转发的前进方向,这意味着给定GetSchemaTable()接口没有用,而其他一些接口则没有将它们带到兼容……这样说公平吗?

应该关闭什么? 如果我们因为 DataTable/DataSet 的依赖而不能拥有GetSchemaTable() ,那是一回事,但是接口仍然应该被恢复(如果需要,没有 GetSchema)并轻松移植对它们有深度依赖的现有代码库。 缺少的接口是一个阻碍,在我们开始支持 dnx/core 的工作之前,我仍在等待它们的发布。

我同意@mythz ,接口是另一个主题,非常重要。 可能不适合大多数用户,但这些相同的用户使用由一个小组编写的代码,并且该代码 _is_ 依赖于这些接口(以及其他重要的 ADO.NET 缺失功能,如 DbProviderFactory)。

老实说,随着对“RTM”标签的极端推动,我几乎不希望我们能在 1.0 中获得可靠的 API。 它将再次像 .NET 2.0 一样:第一个版本所犯的所有错误都将得到纠正。

@FransBouma

将接口添加到 .NET Core 将是一个附加更改。 因此,即使它没有满足 V1 的要求,也可以在后续的任何版本中添加它,甚至是 1.1。

然后将更正第一个版本所犯的所有错误。

无意冒犯,但这就是软件的工作方式。

@terrajobst

因此,即使它没有满足 V1 的要求,也可以在后续的任何版本中添加它,甚至是 1.1。

是的,这并不是技术上不可能,而是这样做的结果(或不这样做)会产生(深远的)后果,而遭受这种后果的不是微软,而是我们。 到目前为止,我几乎没有看到对此的冷漠。 在新框架上工作是一件很酷和令人兴奋的事情,但它并不是在一个干净的房间里为新观众开发的。 相反,也不是没有历史可以学习。

无意冒犯,但这就是软件的工作方式。

看,这正是我上面的意思:你不是必须处理你的决定的后果的人,我必须这样做。 而且我已经处理过你的前任做出类似决定的后果,所以我提醒你不要犯同样的错误。

我知道软件是如何工作的,我现在已经是一名专业的软件开发人员超过 21 年了。 我给出了我诚实的建议,不是作为一个新手,而是作为这个特定领域久经沙场的专家。 你可以随心所欲,只是我希望你在做决定之前三思而后行,因为后果是深远的,正如我所说:我们必须处理这些,而不是你。

即使错误可以稍后修复,但还没有发生,这是一个不首先做的好理由,不是吗?

你们反应过度了。 我怀疑这会产生与旧的 .NET 相同的后果。

由于 coreclr 捆绑在每个应用程序中,legacy 具有非常不同的含义。 就像几乎没有人关心来自 asp.net mvc 5 的功能是否不会向后移植到 mvc 4 或 3 一样。这里的遗留将具有不同的含义,并且在其他项目中有历史可以证明这一点。

好消息@terrajobst愿意在下一个版本中考虑这一点。

@nvivo请不要试图淡化_I_必须处理的后果,因为您不必处理它们,但我必须处理。

感谢@FransBouma把我

其实,虽然我开了issue,但对我的工作和我关心的事情完全没有影响。 我只是在想像你这样在地球上辛勤工作的可怜的开发人员。

我真的很高兴像你这样的人在这里解决难题。 请不要犹豫,一次又一次(一次又一次)告诉我们_你_必须处理的重要问题有多少。

谢谢@FransBouma。

_sigh_ 我在哪里说这么多? 我只想说请不要轻描淡写,因为你这样做是因为“你反应过度”了,我不认为我反应过度。 “你不必处理它们”我的意思是:对_我_的后果。 因为我知道那些是我的反应方式。 显然这是“反应过度”。

但是无所谓。

我们在这里这里有这个问题的答案。

官方的答案是:接口不会出现在 .NET Core 1.0 上,虽然不太可能,但将来可能会考虑以与 .NET 上现有的不同形式的接口。

我正在关闭这个问题,因为原始问题已得到解决。

@nvivo谢谢,但最好将任何官方回应留给实际负责该项目的人,他们也能够在确定问题已解决后自行解决问题。

@terrajobst 接口是否有更新的官方响应/时间表? 跟踪这个工作项目的最佳方式是什么? 我们应该开一个新问题还是继续在这里提供任何更新?

让我们暂时将其打开。 在我看来,答案不是“让我们不要公开接口”。 答案是“让我们找到一种公开它们的方法,但让我们考虑一下它对 DataTable 依赖项意味着什么”。

抱歉这么晚才回复你们。 在我们团队内部讨论了各种选项之后,我们决定用一个空的 DataTable 类带回接口。 这不是一个理想的解决方案,但考虑到 RTM 的时间框架,这种方法将确保我们可以在未来围绕 DataTable/DataSet 寻求可行的选择。 我们将尝试通过 v1 RTM 引入 System.Data.Common 的接口; SqlClient 不会通过 v1 实现接口。 感谢您的反馈和耐心。 您的反馈是使数据堆栈成为可行产品的关键部分。

@YoungGah感谢更新,如果 DataTable 类只是空的占位符,SqlClient v1 实现什么需要这么多时间/努力(即阻止它们)?

@mythz成本是在基本类型中实现接口/将它们转发到现有方法。 成本应该是最低的,但通常情况下会出现 :smile:

我们在 System.Data.Common 中为 .Net CoreFX 添加了以下接口

数据参数
数据参数集合
数据读取器
数据记录
指令
IDb连接
IDb数据参数
IDb事务

这是在 PR https://github.com/dotnet/corefx/pull/6359 中完成的

@saurabh500好东西,谢谢!

:+1:

:+1:

惊人的; 是否有一个里程碑来击中 nuget? rc3?

2016 年 2 月 25 日 02:54,Saurabh Singh通知@ github.com
写道:

我们在 System.Data.Common 中为 .Net CoreFX 添加了以下接口

数据参数
数据参数集合
数据读取器
数据记录
指令
IDb连接
IDb数据参数
IDb事务

这是在 PR dotnet/corefx#6359 https://github.com/dotnet/corefx/pull/6359 中完成的


直接回复此邮件或在 GitHub 上查看
https://github.com/dotnet/corefx/issues/3480#issuecomment -188577701。

问候,

马克

正如 PR 所评论的,我们需要了解为什么这些接口上没有异步方法。 正如提议的那样,这基本上是 ADO.NET 1.1 接口的精简版本,我认为这个想法不应该只是与旧代码兼容。

接口应该关注 ado.net 的当前状态,因为异步方法应该是当今访问任何数据库的默认方式。 没有对异步方法的真正支持,这些接口对于现代开发毫无用处
发展。

甚至包括 .NET 4.5 中的异步方法,也应该添加一些额外的方法,如 DbTrasaction.CommitAsync。

postgres 提供者在他们的 api 中添加了一些额外的方法,比如 CommitAsync,这些方法非常有用和必要。

当前的接口很好。 改变它们的含义太大了。

异步模型与同步模型完全不同,正如您可能知道的那样,如果您选择异步模型,则应该一直这样做。 因此,实际上没有理由为两个 ​​API 使用相同的接口。 为异步 API 创建新的。

如果 .NET 团队想要提供更现代的 API,为什么不创建一个不称为 ADO.NET 的新 API? 没有遗产受到阻碍,也没有来自社区的抱怨。 这也很符合 dnx 的分布方式? 即,独立包。

:+1: 在接口上,很好的妥协。

我不认为这个想法应该只与旧代码兼容。

这就是_整个_的想法。 否则,基类就可以了。 这是我们想要避免的大量移植痛苦。

如果没有对异步方法的真正支持,这些接口对现代开发毫无用处。

我不同意这一点,但我并不反对接口的异步版本(今天没有人实现)。 这将是一个新功能。 我们不能追溯地向现有接口添加成员,这只会破坏太多东西。 拥有IDbReaderAsync或其他东西并不是疯狂的 IMO,但这是一个不同的讨论。

我坚信async方法应该_not_ 位于基类上,如果默认实现是同步包装器 - 那是非常糟糕和浪费的。 如果还有其他提案,那就这样吧,但同样:无论如何,这应该是一个不同的问题。

好吧,也许我在这里用错误的方式表达了自己,或者我的话太强了。

如果需要,我赞成使用额外的异步接口。 我不满意的是有一些东西定义了 ADO.NET 的官方合同(这就是接口),但在其中的任何地方都没有异步方法。

但是,为异步方法提供替代接口可能会导致其他问题......

我坚信异步方法不应该在基类上,如果默认实现是同步包装器 - 那是非常糟糕和浪费的。

我同意,这是大多数提供商不费心实现真正的异步 API 的主要原因。 但是改变它会破坏更多的代码,并且可能比删除接口产生更多的噪音,因为自 2.0 以来,基类一直是提供者的实际 API。

与删除过去几年编写的所有异步代码相比,将库升级为不使用任何 1.1 接口的影响几乎为零,这将是灾难性的。 妥协是两者兼而有之。 今天编写的任何代码都应该使用 async apis,所以忽略它没有任何意义。

今天编写的任何代码都应该使用异步 API。

我不想伤得太重,但那个理想的世界与现实相去甚远。 异步非常普遍且具有传染性。 您不能仅仅依赖库中的异步 API 并期望整个应用程序成为异步使用者(将其代码的 _ton_ 也更改为异步)。 由于许多死锁和效率原因,同步 -> 无处不在的异步也是非常糟糕的。 未来很多年都会有同步代码编写。

对这两种API

与删除过去几年编写的所有异步代码相比,将库升级为不使用任何 1.1 接口将造成几乎为零的影响

你指的是什么? 不存在用于此类代码的异步 API。 如果您依赖此类 API,则它们不是基于基类或接口,而是直接依赖于提供者。 不会受这个影响。

今天编写的任何代码都应该使用 async apis,所以忽略它没有任何意义。

遗漏很多东西是没有意义的......除了我们都受到资源(尤其是时间)的限制。 我不相信有人会_永久地_遗漏任何东西。 没有什么是不可能的。 它只是还没有达到。 我会打开另一个问题,专门为下一代启动异步接口规范。

你指的是什么? 不存在用于此类代码的异步 API。 如果您依赖此类 API,则它们不是基于基类或接口,而是直接依赖于提供者。 不会受这个影响。

.NET 4.5 在提供程序基类上引入了异步方法。 这是在 2012 年,大约 4 年前,所以它已经成为 ADO.NET 提供程序 API 的一部分有一段时间了。 Entity Framework 6(2013 年发布)依赖于所有提供者的异步 API。

异步方法已经是 ADO.NET 的一部分,如果它没有包含在 .NET Core 中,很多人都会尖叫。 我有使用 ADO.NET 中的异步方法的 _legacy code_。

我提倡,因为它们_已经_是 ADO.NET 的一部分,所以这也应该出现在新的接口 API 中。

如果人们想要(并且应该)使用异步 API,他们已经可以做到
_在此更改之前_通过使用基本类型。 最终,请求
支持接口是出于向后兼容的原因;
将方法添加到接口_完全把它从水里吹出来_。
也就是说,它实际上几乎可以作为 _extension methods_ 和
针对抽象基类型进行类型检查,但是......非常丑陋而不是
值得 IMO 的痛苦。

所以; 简短版本:我个人不能落后于向
接口,因为这破坏了我们首先想要它们的东西
地方。 如果你想要异步:你需要针对基类进行编码,或者使用
为您掩盖这些细节的工具。

我提倡,因为它们已经是 ADO.NET 的一部分,所以它也应该出现在新的接口 API 中。

您完全误解了这些 ADO.NET 接口的目的,即保持与现有代码的兼容性。 这些不是 _new_ 接口,而是 _existing_ 接口。 如果您想访问较新的 API,请参考具体的基本类型。

@nvivo抱歉,我只是没有关注您 - 我在谈论 _interface_ API - 那些从未存在过。 基本类型已经具有所有相同的*Async方法 - 是否缺少某些特定的东西? 我认为您认为它们应该捆绑到接口中...是的,但这是我鼓励您打开的另一个问题。

我更希望它们从一开始就是一个接口,因为使当前方法工作​​(异步超过同步)所需的基本实现是使整个方法工作的可怕权衡。 但是,我们也不能两全其美:要么将它们移到接口,要么它们存在(就像当前的情况一样)以最大程度地减少中断。

是的,我想我们在这里绕圈子。 我之前说过,我认为不应该_just__添加接口来帮助移植代码。 从兼容性的角度来看,自 2005 年以来,基类一直是 ADO.NET 的官方 API,而这正是提供程序所实现的。 任何使用 IDbCommand 或 IDbConnection 的东西都可以很容易地被移植(并且应该被移植)以使用带有搜索/替换的基类并且没有缺点。

我知道您不是 ifdefs 的粉丝,但是无论如何,为新平台支持它只是迁移的一部分。

我同意这应该一直是接口,但由于它们不是,我希望这个问题不会重演。 如果要添加接口,它们至少应该代表当前的 API,而不是十年前的样子。 异步方法是当前 API 的一个组成部分,这也是微软一段时间以来的发展方向。 它仍然是源兼容的,只是更完整。

@mgravell

如果人们想要(并且他们应该)使用异步 API,他们已经可以_在此更改之前_通过使用基本类型来做到这一点。

这不是关于能够做任何事情。 它是关于建筑的。 接口就是契约,.NET Core 是一个新框架,它将此契约添加到重新设计的 API 版本中。

.NET 核心不应该仅仅为了帮助迁移真正的旧代码而向新 API 添加正式合同,而大多数其他东西无论如何都会丢失。 如果这是一个问题,人们只是因为他们无论如何都需要更改代码的原因而看不够。

如果这就是团队所做的一切,那也没关系.. IMO 只是一个糟糕的选择。

任何使用 IDbCommand 或 IDbConnection 的东西都可以很容易地被移植(并且应该被移植)以使用带有搜索/替换的基类并且没有缺点。

错误的。 多个库作者在此线程中多次讨论了这些问题,这些作者具有受此影响的第一手经验。

我知道你不喜欢 ifdefs

任何要求最终客户使用 ifdefs 的解决方案都是糟糕的开发体验和非入门者,即永远不会有成功的产品要求客户在存在替代方案时用 #defs 乱扔他们的代码。

如果要添加接口,它们至少应该代表当前的 API

这些不是新的接口,它们是恢复的接口。 当前和未来的 API 是基类,而不是这些接口。 这里应该没有问题,您可以忘记这些接口的存在,并像恢复这些接口之前一样继续使用基本类型。

没有新的价值被添加到这个线程了。 现有的 ADO.NET 接口已恢复,因此该线程可以停止。 该线程只需要更新DataTableGetSchemaTable()因为它们与现有接口有关。 如果您想提出架构更改或倡导新接口,请打开一个新问题 - 这将阻止此列表中的每个人都收到垃圾邮件。

@mythz让我们同意不同意。

作为另一位 ORM 开发人员,只需添加我的 2 美分,抽象类在没有接口支持时总是一种代码味道。 很想看到提供新的接口来匹配抽象类和用最低要求的接口 API 重载的方法签名。

向社区竖起大拇指,让他们畅所欲言。

当没有接口支持时,抽象类总是一种代码味道

@psib​​ernetic你能帮我理解那句话吗? 这是代码异味怎么办?

@psib​​ernetic

接口和抽象类为我们提供了契约,两者都为我们提供了 API 的抽象和良好的定义。 当实现可以实现多个接口的类或者是另一个基类的子类时,接口最有用(假设这是该基类的一个非常大的优势)。 特别是在这种情况下,特定提供者的 Connection、Command 等的具体类与抽象 API 定义具有很强的 IS A 关系。 当某些开发人员需要将 IDbConnection 或 IConnection 的具体实现添加到子类时,我真的无法想象这种情况。 几乎唯一的场景是只为抽象类派生的新类,并且在接口上“复制”相同的定义对于 API 设计者来说是更多的工作(不必要)。

您是否看到具有两个相同抽象的特定且具体的优势或场景? 在这个特定的 API 设计中,接口何时确实提供了实际和真正的好处_over_抽象类?

我能想到的接口的唯一优点是我们需要与旧接口的向后兼容性,以减少依赖于这些接口的实际运行代码。 如果我们没有旧的接口,我很确定抽象类就足够了。

@eocampo您是对的,抽象类可能提供“足够好”的抽象和契约。 我总是尝试提供非常窄的接口来表示可以采取的操作,例如 IAsyncCommand 等。 这允许我的框架以在框架设计时可能没有考虑过的方式插入,从而减少出现可怕的 NotSupportedExceptions 或 NotImplementedExceptions 的可能性。

@davkean代码的味道是,在大多数情况下,虽然不是全部,但您需要实现者实现或继承可能不相关的整个基本功能集。 我记得看到 IDataReader 实现从缓存或内存中读取。 不确定 DbDataReader 抽象类是否允许这样做,但名称暗示不允许。

dot net 中主要遵循的最佳实践模型是公开接口并从基类继承,不是吗?

dot net 中主要遵循的最佳实践模型是公开接口并从基类继承,不是吗?

@psib​​ernetic并不总是如此。 例如,MSDN 站点上的这个推荐已经有十多年了。 至少从 .Net Framework 2.0 开始,该指南非常普遍。

这也是早期 .Net 中库设计指南的一个很好的参考:

http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613

无论如何,我认为这里的艰难讨论现在是关于两个主题的:

a) 接口只是为了向后兼容,或者我们可以“从头开始”(破坏代码)以允许更清晰的接口和 API 设计。
b) 以与完整的 .Net 框架不兼容为代价,我们可以在多大程度上实现现代和简洁的设计。 (.Net Core 和 Full core 在数据访问方面的兼容性 [不是最低级别和强制性兼容性])

从我的角度来看,如果我们将抽象基类作为主要和首选契约,那么 _interfaces_ 必须匹配旧的只是为了兼容性。 我知道@nvivo已经声明,在 .Net 2.0 之后,官方契约是抽象基类,所以我们_可能_认为接口不会解决兼容性问题,但@mythz@mikeobrien也在这里提供了关于提供者依赖的硬数据在 1.1 接口上。

为了停止垃圾邮件和讨论这里的主题,我们需要再次阅读这篇长篇对话,我不知道我们是否可以就我们正在解决的特定主题的列表达成一致,或者创建两三个新主题是否是个好主意每个特定主题的问题。 我更倾向于第一个建议,因为这里有很多优点。 我不知道我们如何重新总结所有这些并清除一些噪音(甚至是我的)。

说到接口,是否有计划最终使 System.Data 的部分通用? 一直困扰我的是 System.Data 从未真正将其 API 更新到 .NET 1.1 之外,让人们不得不使用 .AsEnumerable() 扩展方法之类的技巧来获取 IEnumerable出一个数据表。 为什么像 DataRowCollection 这样的集合没有升级到实现泛型接口,而框架中的其他所有东西在 2.0 出现时都做了?

是否会有带有类型重定向的存根 System.Data? 我需要使用 ODP.NET 但现在我不能。

创建 dotnet/corefx#7874

@mgravell @ploeh “Rickasaurus”暗示类型类即将出现(至少对于 F#,不确定 C# 或 .NET 的一般情况 https://news.ycombinator.com/threads?id=Rickasaurus)。 如果它们是针对所有 .NET 而来的,它会解决问题吗?

我不是 Haskell 专家,但我的理解是它们允许您在事后使用简单的IDbConnectionIDbConnectionAsync和任何未来的共享接口,而不会破坏源代码或二进制兼容性,并且无需强制第 3 方提供程序实现所有内容。 这同时保留了简单的可模拟性。

这是正确的理解吗? 如果是这样,此功能是否有可能真正出现在 .NET 中?

此页面是否有帮助?
0 / 5 - 0 等级