Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are three properties in DbParameterCollection abstract in reference assemblies but virtual otherwise?

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:

  • IsFixedSize
  • IsReadOnly
  • IsSynchronized

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...

like image 253
Jon Skeet Avatar asked May 26 '17 08:05

Jon Skeet


1 Answers

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
like image 110
natemcmaster Avatar answered Nov 05 '22 02:11

natemcmaster