Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Make DBNull serializable #13845

Merged
merged 8 commits into from
Sep 8, 2017
Merged

Conversation

ViktorHofer
Copy link
Member

@ViktorHofer ViktorHofer commented Sep 7, 2017


private DBNull(SerializationInfo info, StreamingContext context)
{
throw new NotSupportedException(SR.NotSupported_DBNullSerial);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to add the string

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, it's already there by mistake...

{
if (info == null)
throw new ArgumentNullException(nameof(info));
Contract.EndContractBlock();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we're bothering with these

@ghost
Copy link

ghost commented Sep 7, 2017

Please, trim it down. That's lot of unnecessary code.

@ViktorHofer
Copy link
Member Author

I'm also for trimming it down. Thanks

switch (_unityType)
{
case EmptyUnity:
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure the indents are right

@ViktorHofer
Copy link
Member Author

I should have clarified that I didn't expect a code review but more the answer if I should trim it down. @danmosemsft please wait with commenting on code :)

#endif
class UnitySerializationHolder : ISerializable, IObjectReference
{
internal const int EmptyUnity = 0x0001;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use any of these save NullUnity, and don't plan to use them either. If we remove all the others, a bunch of other code could be removed from this file, and it would still be compatible?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would do that, would cut this file in half

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, though it begs the question as to whether any have any short-term benefits in including, because of dependencies elsewhere that are blocked similarly to how the DBNull case blocks some. (Any longer term benefits can be decided on later).

@ViktorHofer
Copy link
Member Author

That's the trimmed down version of it. We will need a manual typeforward for the UnitySerializationHolder here https://github.com/dotnet/corefx/blob/master/src/shims/manual/mscorlib.forwards.cs#L7 in corefx and tests here https://github.com/dotnet/corefx/blob/master/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTestData.cs#L239

And I'm not 100% sure if this really should remain in coreclr or just go into corefx near System.Data.Common.


_unityType = info.GetInt32(UnityTypeName);
_data = info.GetString(DataName);
_assemblyName = info.GetString(AssemblyNameName);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like more work than is necessary if looked at in isolation. Maybe a comment that it's for compatibility might help someone confused by that.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also remove the fields. There's no real need to read or store those values are written out for backward compat.

@danmoseley
Copy link
Member

Seems DBNull has to stay in corelib because of various magic for it in Convert etc. So it would be tricky to move this new class up. Does not seem worth it.

#if CORECLR
internal
#else
public // On CoreRT, this must be public because of the Reflection.Core/CoreLib divide and the need to whitelist past the ReflectionBlock.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that this is only used for DBNull, it no longer needs to be public on CoreRT.

_assemblyName = info.GetString(AssemblyNameName);
}

public virtual void GetObjectData(SerializationInfo info, StreamingContext context) =>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks weird for these methods to be marked "virtual" - makes it look like you're expecting someone to override them even though this class has no subtypes.

@ViktorHofer
Copy link
Member Author

Now that this is only used for DBNull, it no longer needs to be public on CoreRT.

I wasn't sure about that but will believe you :)

@ViktorHofer
Copy link
Member Author

What I'm still not sure is this:

Netfx code of UnitySerilizationHolder.

On serialization it first saves the data (string) https://referencesource.microsoft.com/#mscorlib/system/unityserializationholder.cs,137 and after that the UnityType (int).
But on deserialization it first reads the UnityType (int) https://referencesource.microsoft.com/#mscorlib/system/unityserializationholder.cs,173 and afterwards the data (string).

Does that make any sense? Am I missing something?

@ghost
Copy link

ghost commented Sep 7, 2017

Now that this is only used for DBNull, it no longer needs to be public on CoreRT.

I wasn't sure about that but will believe you :)

Hmm, you have a point. We don't need it to be public for Reflection.Core's sake, but I guess the binary formatter would still need to be able to find the class via Reflection. So I guess we still need the ifdef - just edit the comment to delete the part about Reflection.Core.

public object GetRealObject(StreamingContext context)
{
// We are always returning the same DBNull instance and ignoring serialization input.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're still trying to be compatible with legacy blobs, should we at least throw if "UnityType" isn't NullUnity?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely should throw as otherwise users would just get a random object. But before I can do that I need to verify the weird behavior I posted above. Thanks!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SerializationInfo isn't an ordered list - it's a dictionary of name-value pairs. So there's no problem.

@JonHanna
Copy link

JonHanna commented Sep 7, 2017

But on deserialization it first reads the UnityType (int)

Ordering doesn't matter, so it was probably just whatever order for each seemed most convenient at the time.

@ViktorHofer
Copy link
Member Author

ViktorHofer commented Sep 7, 2017

Aaah yes I overlooked that. Seems I spent too much time with simple serializable types :D

@ViktorHofer
Copy link
Member Author

I think it should be fine now. PTAL

/// UnitySerializationHelper should return from a call to GetObjectData. It contains
/// the unityType (defined above) and any optional data (used only for the reflection types).
/// </summary>
public static void GetUnitySerializationInfo(SerializationInfo info, int unityType, string data, Assembly assembly)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetUnitySerializationInfo can be made internal and the "data" and "assembly" parameters serve no purpose anymore.

Aside from that, LGTM.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forget my comment. was wrong.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why wouldn't DBNull be able to access it? It lives in the same assembly as UnitySerializationHolder.

@ViktorHofer
Copy link
Member Author

Thanks for the review.

@danmosemsft do you also want to review again? :)

// We are only support deserializing DBNull and throwing for everything else.
if (_unityType != NullUnity)
{
throw new ArgumentException(SR.Argument_InvalidUnity);
Copy link
Member

@danmoseley danmoseley Sep 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid Unity type. is not going to be a helpful message when deserialization fails of a perfectly good .NET Framework blob. How about Type '{0}' is not deserializable. where you get {0} from Data if it's avaialable else UnityType?

{
/// <summary>
/// Holds Null class for which we guarantee that there is only ever one instance of.
/// This only exists for backwarts compatibility with
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backwarts is a new word .. and the sentence isn't complete

Copy link
Member

@danmoseley danmoseley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll want to run tests vs desktop blobs - including one that contains something like a serialized Assembly so it exercises the unexpected unity type path

@@ -53,7 +56,7 @@ public object GetRealObject(StreamingContext context)
// We are only support deserializing DBNull and throwing for everything else.
if (_unityType != NullUnity)
{
throw new ArgumentException(SR.Argument_InvalidUnity);
throw new ArgumentException(SR.Format(SR.Argument_InvalidUnity, _data ?? "UnityType"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw new ArgumentException(SR.Format(SR.Argument_InvalidUnity, _data ?? _unityType ?? "UnityType"));

@danmoseley
Copy link
Member

LGTM

@danmoseley
Copy link
Member

It would be nice to get this into 2.0.2. But we may not have both sides in in time.

@ViktorHofer
Copy link
Member Author

The corefx changes are already ready to push but I want to wait till my build is finished.

@ViktorHofer ViktorHofer merged commit 07aa22b into dotnet:master Sep 8, 2017
dotnet-bot pushed a commit to dotnet/corert that referenced this pull request Sep 9, 2017
…lization

Make DBNull serializable

Signed-off-by: dotnet-bot <[email protected]>
jkotas pushed a commit to dotnet/corert that referenced this pull request Sep 9, 2017
…lization

Make DBNull serializable

Signed-off-by: dotnet-bot <[email protected]>
@ViktorHofer ViktorHofer deleted the DBNull-Serialization branch September 11, 2017 21:07
ViktorHofer added a commit to ViktorHofer/coreclr that referenced this pull request Sep 11, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants