Does SqlCommand.Clone() create a deep copy or shallow copy?
Asked Answered
K

2

7

Does SqlCommand.Clone() create a deep copy or shallow copy? Also, is it safe to call Clone() from multiple threads concurrently (create one command that multiple threads can copy, set parameter values, and execute)?

Koller answered 26/7, 2011 at 4:32 Comment(2)
For more information about cloning, deep versus shallow copies, and examples, see the Object.MemberwiseClone method #699710Maiocco
The reason for my question was if it is threadsafe to Clone() SqlCommand from multiple threads at the same time. From the discussion it seems that is true, so even though it isn't a deep clone, it does clone the parameter set. So at start-up you can prepare the SqlCommand once, then later clone it from many threads in parallel to save some work.Koller
B
3

It is not safe to call Clone from multiple threads because the SqlCommand class itself is not a thread safe class. you should lock before cloning..

However you can look at the SqlCommand.Clone() method using programs like Reflector, here is the actual code:

public SqlCommand Clone()
{
    SqlCommand command = new SqlCommand(this);
    Bid.Trace("<sc.SqlCommand.Clone|API> %d#, clone=%d#\n", this.ObjectID, command.ObjectID);
    return command;
}

internal static void Trace(string fmtPrintfW, int a1, int a2)
{
    if (((modFlags & ApiGroup.Trace) != ApiGroup.Off) && (modID != NoData))
    {
        NativeMethods.Trace(modID, UIntPtr.Zero, UIntPtr.Zero, fmtPrintfW, a1, a2);
    }
}

[DllImport("System.Data.dll", EntryPoint="DllBidTraceCW", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Unicode)]
internal static extern void Trace(IntPtr hID, UIntPtr src, UIntPtr info, string fmtPrintfW, int a1, int a2);

private SqlCommand(SqlCommand from) : this()
{
    this.CommandText = from.CommandText;
    this.CommandTimeout = from.CommandTimeout;
    this.CommandType = from.CommandType;
    this.Connection = from.Connection;
    this.DesignTimeVisible = from.DesignTimeVisible;
    this.Transaction = from.Transaction;
    this.UpdatedRowSource = from.UpdatedRowSource;
    SqlParameterCollection parameters = this.Parameters;
    foreach (object obj2 in from.Parameters)
    {
        parameters.Add((obj2 is ICloneable) ? (obj2 as ICloneable).Clone() : obj2);
    }
}

You can see that it create a new instance and add to it all properties of the old one, but it does not deep copy all properties "like Connection for instance" and so it a shallow copy.

Burny answered 26/7, 2011 at 4:40 Comment(2)
I don't expect it to clone critcal resources like SqlConnection. I would say because it clones all the parameters that are ICloneable that it is performing a 'best-attempt' at a deep copy. In the app I'm reviewing it never uses the 'original' SqlCommand, so the Connection and Transaction properties will always be null on the 'from' or 'original' instance.Koller
@yzorg: right, but because it doesn't clone all data deeply "like SqlConnection, Parameters.." it is then considered a shallow copy. It only considered a deep copy if it copies all data deeply, so if you change any property from the original or the copy, it will not effect the other one in anyway.Burny
G
2

The SqlCommand.Clone method performs a shallow copy. Any properties that are a reference type will represent the same object in both SqlCommand instances. So, not thread safe.

AFAIK, all Clone() (MemberwiseClone) methods in the .NET framework are shallow copies.

You haven't posted your code, but I would suggest creating a new SqlCommand rather than cloning.

Greenwood answered 26/7, 2011 at 4:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.