Search code examples
c#wcffilestreamnettcpbindingsqlclient

Setting the buffer size when reading a Stream in WCF and writing it to a Microsoft SQL Server database table


When receiving a Stream in WCF and writing it to a database table, what buffer size is used when writing the data to the database table and how can it be changed?

I'm using .NET Framework 4.6 and Microsoft SQL Server.

Server

[ServiceContract(Name = "IUploadFileService")]
public interface IUploadFileService
{
    [OperationContract]
    void UploadFile(UploadFileMessage uploadFileMessage);
}

[MessageContract]
public class UploadFileMessage
{
    [MessageBodyMember]
    public Stream FileContents { get; set; }
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class UploadFileService : IUploadFileService
{
    public void UploadFile(UploadFileMessage uploadFileMessage)
    {
        using (MyDbContext context = new MyDbContext())
        {
            DbConnection conn = context.Database.Connection;
            ConnectionState initialState = conn.State;

            try
            {
                if (initialState != ConnectionState.Open)
                {
                    conn.Open();
                }

                using (DbCommand cmd = conn.CreateCommand())
                {
                    // the FileContents field is a `varbinary(max)` data type
                    cmd.CommandText = "INSERT INTO [Files] (FileContents) VALUES (@FileContents)";
                    cmd.CommandTimeout = 0;

                    DbParameter parameter = cmd.CreateParameter();
                    parameter.ParameterName = "@FileContents";
                    parameter.DbType = DbType.Binary;
                    parameter.Size = -1;
                    parameter.Value = uploadFileMessage.FileContents; // what buffer size is used to read this Stream and write it to the database?
                    cmd.Parameters.Add(parameter);

                    cmd.ExecuteNonQuery();
                }
            }
            finally
            {
                if (initialState != ConnectionState.Open)
                {
                    conn.Close();
                }
            }
        }
    }
}

Server configuration

<?xml version="1.0" encoding="utf-8" ?>
<bindings>
    <netTcpBinding>
        <binding name="NetTcpBinding_IFileUploadService" closeTimeout="00:01:00" receiveTimeout="24.20:31:23.6470000" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" transferMode="Streamed">
            <readerQuotas maxArrayLength="32505856" maxStringContentLength="32505856" />
            <security mode="None">
                <transport clientCredentialType="None" protectionLevel="None" />
                <message clientCredentialType="None" />
            </security>
        </binding>
    </netTcpBinding>
</bindings>

Client

private static void UploadFile()
{
     EndpointAddress endpointAddress = new EndpointAddress("net.tcp://localhost:4502/FileUploadService");

     NetTcpBinding binding = new NetTcpBinding(SecurityMode.None)
     {
         TransferMode = TransferMode.Streamed,
         MaxBufferSize = int.MaxValue,
         MaxReceivedMessageSize = int.MaxValue,
         SendTimeout = TimeSpan.MaxValue
     };

     FileUploadServiceClient fileUploadServiceClient = new FileUploadServiceClient(binding, endpointAddress);

     using (FileStream fileStream = new FileStream(@"c:\test.bin", FileMode.Open)) // read buffer size is 4096 bytes by default
     {
         fileUploadServiceClient.UploadFile(fileStream);
     }
}

Is it any of these?

  • Default value specified in FileStream used to open the file on the client
  • NetTcpBinding.MaxBufferSize on the client
  • maxBufferSize in the WCF server configuration
  • DbParameter.Size, which is int.MaxValue because it's set to -1 and the maximum number of bytes that can be entered in a varbinary(max) database field
  • Network Packet Size configuration setting in the SQL Server database
  • MTU values on the network
  • Smallest value of all of the above

Thank you.


Solution

  • Windows Communication Foundation (WCF) transports support two modes for transferring messages:

    1.Buffered transfers hold the entire message in a memory buffer until the transfer is complete. A buffered message must be completely delivered before a receiver can read it.

    2.Streamed transfers expose the message as a stream. The receiver starts processing the message before it is completely delivered.

    I see that you use the second transfer mode, streaming, which can improve the scalability of services by eliminating the need for large memory buffers.

    To prevent denial of service attacks, WCF exposes a MaxReceivedMessageSize attribute to limit the size attribute of incoming messages, so it also limits the maximum amount of memory accessed when processing the message. The unit is set in bytes, and the default value is 65,536 bytes.

    For more information about Streaming Message Transfer,Please refer to the following link:

    https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/streaming-message-transfer