68

I'm moving a project from project.json to the new-style csproj format, and it includes a class derived from DbParameterCollection. In my real project I'm using multi-targeting, but for the purposes of this question we only need to care about net45.

The compiler is telling me that I have to override three properties that I didn't have to before:

If you follow those documentation links (which are for .NET 4.5) you'll see that all the properties are virtual - not abstract. If I build the code just by calling csc, all is well... it's only when using the .NET Core SDK that I run into the issue.

Here's sample code to reproduce the problem:

Project file:

<Project Sdk="Microsoft.NET.Sdk">    
  <PropertyGroup>
    <TargetFramework>net45</TargetFramework>
  </PropertyGroup>    
</Project>

C# code:

using System;
using System.Collections;
using System.Data.Common;

public class DummyParameterCollection : DbParameterCollection
{
    public override int Count => 0;
    public override object SyncRoot => null;
    public override void Remove(object value) {}
    public override void RemoveAt(int index) {}
    public override void RemoveAt(string parameterName) {}
    public override int Add(object value) => 0;
    public override void Insert(int index, object value) {}
    public override void AddRange(Array values) {}
    public override void Clear() {}
    public override bool Contains(object value) => false;
    public override bool Contains(string value) => false;
    public override void CopyTo(Array array, int index) {}
    public override int IndexOf(object value) => -1;
    public override int IndexOf(string parameterName) => -1;
    protected override DbParameter GetParameter(int index) => null;
    protected override DbParameter GetParameter(string parameterName) => null;
    protected override void SetParameter(int index, DbParameter value) {}
    protected override void SetParameter(string parameterName, DbParameter value) {}
    public override IEnumerator GetEnumerator() => null;
}

Errors:

DummyParameterCollection.cs(5,14): error CS0534: 'DummyParameterCollection' does not implement inherited abstract member 'DbParameterCollection.IsSynchronized.get' [c:\Users\skeet\Test\ParameterCollection\ParameterCollection.csproj]
DummyParameterCollection.cs(5,14): error CS0534: 'DummyParameterCollection' does not implement inherited abstract member 'DbParameterCollection.IsFixedSize.get' [c:\Users\skeet\Test\ParameterCollection\ParameterCollection.csproj]
DummyParameterCollection.cs(5,14): error CS0534: 'DummyParameterCollection' does not implement inherited abstract member 'DbParameterCollection.IsReadOnly.get' [c:\Users\skeet\Test\ParameterCollection\ParameterCollection.csproj]

I believe I know the immediate cause of the problem, but not the reasons why it's like this, or the best workaround.

It looks like the .NET Core SDK (and VS2017 when it's loaded this project) uses the reference assemblies. If I open C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Data.dll in Reflector, that shows the properties as being abstract as well. Whereas if I open c:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Data.dll, that shows the properties as being virtual.

I can work around this by overriding the properties and just returning false from all of them - but is that the best way of handling this situation? Beyond that, is there any good reason why the reference assemblies don't match the real assemblies (and documentation) in this case? I would expect the reference assemblies to be autogenerated, so it's odd for some things to be incorrect like this...

Paulo Boaventura
  • 1,365
  • 1
  • 9
  • 29
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • While I do not have Reflector, I used IL Disassembler to look at the other dlls in the .NetFramework folder. All the versions up to v4.5 had those properties as `abstract` while from v4.5.1 onward had them virtual. As to why they differ? I cannot say without guessing. – Nkosi May 26 '17 at 10:03
  • So my guess is that they were changed to `virtual` in the incremental updates following v4.5 in the reference assemblies which would also explain why it would be in the most recent version of the framework in the windows folder. I make reference (no pun intended) to this question https://stackoverflow.com/questions/9701135/reference-assemblies-folder-and-different-assemblies-with-the-same-version – Nkosi May 26 '17 at 10:29
  • API docs of .net framework <= 3.5 show it as `abstract` as well. In .net standard it also is abstract. Yet the reference assembly in CoreFx for System.Data.Common now also show it as virtual https://github.com/dotnet/corefx/blame/master/src/System.Data.Common/ref/System.Data.Common.cs – Martin Ullrich May 26 '17 at 15:22
  • @MartinUllrich: Given comments on Twitter, it sounds like it went from abstract to virtual some time after 4.5, and the docs are just wrong. – Jon Skeet May 26 '17 at 15:23
  • @JonSkeet The new docs.MS API reference still has issues. If in doubt, especially when it comes to API signatures, I would stay with MSDN. I believe that's also why it's not allowed to be indexed by search engines. – svick May 26 '17 at 16:42
  • @svick: Mmm. Looks like that might indeed be the project. But MSDN doesn't show .NET 4.5 at all for that class :( – Jon Skeet May 26 '17 at 16:48
  • This thread here https://github.com/dotnet/corefx/issues/3480 states this: "The current design of System.Data.Common for .NET Core started in December 2012 [... was to ...] Enable providers and consumers to build a single binary/source against .NET Core, and then run that same binary on .NET Framework. [...] the reverse was not a goal; being able to take .NET Framework binary/source and run it without some changes on .NET Core.". Changing the properties from virtual to abstract is in line with this goal. – Simon Mourier May 30 '17 at 06:58
  • 1
    @SimonMourier: Sure, I have no problems with that happening - it's only the fact that the docs claim the properties are virtual when they appear to be abstract, along with it working back when we were using `project.json`, that's weird... – Jon Skeet May 30 '17 at 07:00
  • I regret the days when MSDN was the one and only reference for dev documentation. It's clearly a mess today, but I have hope :-) – Simon Mourier May 30 '17 at 07:08
  • 8
    @Somar: Please stop suggesting edits that make the error messages unreadable (by being on one very wide line). It's perfectly readable as it is. – Jon Skeet Jun 05 '17 at 15:33
  • Congrats sir, you've uncovered the spooky "documentation bug", oh my!! WTF was Somar thinking with those pesky edits.... – Bitcoin Murderous Maniac Aug 02 '17 at 02:38

1 Answers1

23

The reference assemblies are correct. In .NET Framework 4.5, these properties were abstract. They were changed to virtual in .NET Framework 4.5.1. It appears you've uncovered a documentation bug.

As you probably have already guessed, the difference between the two System.Data.dll assemblies you are observing is due to how .NET Framework separates reference assemblies and runtime assemblies. The reference assembly in C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Data.dll accurately reflects what would have been in the 4.5 runtime version of System.Data.dll. If you're able to get an old machine that hasn't yet upgraded to .NET Framework 4.5.1 (good luck), you'll find that runtime assembly in C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Data.dll has these properties as abstract. .NET Framework upgrades in-place. On a machine that has upgraded to .NET Framework 4.5.1 or newer, C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Data.dll has been replaced with the updated version (with virtual, not abstract, properties.)

As far as workarounds: compile for net451 instead, or implementing dummy methods are the best approaches. You could do other tricks to compile against a different version of System.Data.dll, but I wouldn't recommend it

I couldn't find official documentation on the API changes between .NET Framework 4.5 and 4.5.1 or an explanation of why this was changed, however, I found this comment from a member of the Entity Framework team: https://bugzilla.xamarin.com/show_bug.cgi?id=29167#c0.

The following (non-breaking) changes were made to the System.Data APIs in the .NET Framework 4.5.1 release....

The following member were added.

  • System.Data.Common.DbParameter.Precision
  • System.Data.Common.DbParameter.Scale
  • System.Data.SqlClient.SqlConnectionStringBuilder.ConnectRetryCount
  • System.Data.SqlClient.SqlConnectionStringBuilder.ConnectRetryInterval

The following member were changed from abstract to virtual.

  • System.Data.Common.DbDataReader.Close
  • System.Data.Common.DbDataReader.GetSchemaTable
  • System.Data.Common.DbParameter.SourceVersion
  • System.Data.Common.DbParameterCollection.IsFixedSize
  • System.Data.Common.DbParameterCollection.IsReadOnly
  • System.Data.Common.DbParameterCollection.IsSynchronized
natemcmaster
  • 25,673
  • 6
  • 78
  • 100
  • 5
    Thanks for the confirmation. I'm still not sure why it used to build with `project.json`, but that's another story :) – Jon Skeet Jul 10 '17 at 07:20