After socket error, stored prodecude doesn't w

2019-07-23 06:14发布

I'm developing an Socket server using TCPListener running on a Windows Service, .NET Framework 4.6.1, Entity Framework 6.1.3 and C#. As database I'm using SQL Server Enterprise 2012 SP 2.

I'm having problems with my database after I get a socket timeout on my socket client. Every time I get a socket timeout (or any other problem) on client side I get a strange behaviour on my database.

I have a socket server that uses a database stored procedure to update a table:

public TcpListenerServer(int serverPort)
{
    port = serverPort;
    connectionString =
        ConfigurationManager.ConnectionStrings["DbContext"].ConnectionString;

    tcpListenerServer = null;
    codesReceived = new CodesReceived();
    lockCodesUpdate = new System.Object();
    locked = false;
}

public void InitTCPServer()
{
    // Create server.
    IPEndPoint ipAddress = new IPEndPoint(IPAddress.Any, port);
    tcpListenerServer = new TcpListener(ipAddress);

    // Create cancellation token.
    tokenSource = new CancellationTokenSource();
    token = tokenSource.Token;

    // Start the service
    tcpListenerServer.Start(5);

    // Begin an asynchronous connection attemp
    tcpListenerServer.
        BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback),
                             tcpListenerServer);
}

public void Stop()
{
    if (tcpListenerServer != null)
        tcpListenerServer.Stop();
}

private void DoAcceptTcpClientCallback(IAsyncResult ar)
{
    TcpClient client = null;

    try
    {
        // Get the listener that handles the client request.
        TcpListener listener = (TcpListener)ar.AsyncState;

        // End the operation
        client = listener.EndAcceptTcpClient(ar);

        // Handle the data received
        Task.Run(() => { HandleCodeReceived(client, token); }, token);

        // Begin a new asynchronous connection attemp
        tcpListenerServer.
            BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback),
                                 tcpListenerServer);
    }
    catch (SocketException ex)
    {
        log.ErrorFormat("DoAcceptTcpClientCallback socket error: {0}, Message {1}", ex.ErrorCode, ex.Message);
        Stop();
    }
    catch (ObjectDisposedException)
    {
        return;
    }
}

private void HandleCodeReceived(TcpClient client, CancellationToken token)
{
    // client could be null or not be connected when it reach this method?
    if ((client != null) && (client.Connected))
    {
        try
        {
            // Read input message.
            NetworkStream netStream = client.GetStream();

            StreamReader reader = new StreamReader(netStream);
            string messageReceived = null;

            // The while do this: read ALL the lines sent.
            while ((messageReceived = reader.ReadLine()) != null)
            {
                // If we have a valid message
                if ((!string.IsNullOrWhiteSpace(messageReceived)) &&
                    (IsValid(messageReceived)))
                {
                    // Parse message received from client.
                    ClientMessage message =
                        new ClientMessage(messageReceived);

                    // First check if code is valid.
                    if ((string.IsNullOrWhiteSpace(message.Code)) ||
                        (message.Code.Equals("0")))
                    {
                        SendBackMessage(false, client);
                    }
                    else
                    {
                        ProcessCodeReceived(client, message);
                    }
                }
                else
                    SendBackMessage(false, client);
            }
        }
        catch (Exception ex)
        {
            log.Error(ex.Message);

            SendBackMessage(false, client);
        }
    }
}

private bool IsValid(string messageReceived)
{
    string[] cadenas = messageReceived.Split(":".ToCharArray());

    return ((cadenas != null) && (cadenas.Length == 8));
}

private void ProcessCodeReceived(TcpClient client, ClientMessage message)
{
    DateTime begin = DateTime.UtcNow;

    log.DebugFormat("ReadCode: {0} - Start", message.Code);

    StoredProcedureErrors result;
    result = ReadCode(message.Code, message.Nivel);

    if (result != StoredProcedureErrors.NoError)
        log.ErrorFormat(ReadCodeErrorMsg, result, message.Code);
    else
        codesReceived.Reset();

    DateTime end = DateTime.UtcNow;

    log.DebugFormat("ReadCode: {0} - End: {1} - {2}", message.Code, (end - begin).TotalMilliseconds, result);

    SendBackMessage(result == StoredProcedureErrors.NoError, client);
}

private void SendBackMessage(bool isCorrect, TcpClient client)
{
    if (isCorrect)
        client.GetStream().Write(ASCIIEncoding.UTF8.GetBytes(CorrectMsg + Environment.NewLine), 0, 4);
    else
        client.GetStream().Write(ASCIIEncoding.UTF8.GetBytes(IncorrectMsg + Environment.NewLine), 0, 4);
}

private StoredProcedureErrors ReadCode(
    string code,
    byte aggregationlevel)
{
    StoredProcedureErrors result =
        StoredProcedures.ReadCode(
            connectionString,
            code,
            aggregationlevel,
            UserName,
            Source,
            true);

    return result;
}

And the code that runs the stored procedure is:

public static StoredProcedureErrors ReadCode(
    string connectionString,
    string code,
    byte codeLevel,
    string param1,
    string param2,
    bool isAuto)
{
    GenericRepository<Code> repository = null;
    LDbContext dbContext = new LDbContext(connectionString);

    repository = new GenericRepository<Code>(dbContext);

    string procName = "ReadCode";

    List<SqlParameter> parameters = null;
    SqlParameter helperCodeParam = null;

    SqlParameter codeParam = new SqlParameter();
    codeParam.ParameterName = "@code";
    codeParam.SqlDbType = System.Data.SqlDbType.NVarChar;
    codeParam.Size = 20;
    codeParam.Direction = System.Data.ParameterDirection.Input;
    codeParam.Value = code;

    [ ... ]

    parameters = new List<SqlParameter>();
    parameters.Add(codeParam);
    parameters.Add(aggregationLevelParam);
    parameters.Add(param1Param);
    parameters.Add(param2Param);
    parameters.Add(isAutoParam);

    if (helperCodeParam != null)
        parameters.Add(helperCodeParam);

    parameters.Add(returnValueParam);

    repository.Execute(procName, parameters);

    return (StoredProcedureErrors)Convert.ToInt32(returnValueParam.Value);
}

The repository.Execute method is:

public void Execute(
    string storedProcedureName,
    IEnumerable<SqlParameter> parameters)
{
    SqlConnection connection = null;

    try
    {
        connection =
            dbContext.Database.Connection as SqlConnection;
        connection.Open();

        SqlCommand cmd = new SqlCommand();

        cmd.CommandText = storedProcedureName;
        cmd.CommandType = CommandType.StoredProcedure;

        if (parameters != null)
            cmd.Parameters.AddRange(parameters.ToArray());

        cmd.Connection = connection;

        cmd.ExecuteNonQuery();
    }
    finally
    {
        if (connection != null)
            connection.Close();
    }
}

My problem is very strange: ReadCode updates only one row on a table (that table is used very intensively adding new rows and updating other rows). I'm sure that the correct row is updated at the end of the stored procedure because I check it checking @@ERROR and @@ROWCOUNT. And also surrounding the update statement in a TRY - CATCH block (take a look to my DB administrator question about this problem).

But it doesn't updated. If I do a select on SQL Server Management Studio it doesn't updated.

My first question is:

  1. Is it a good idea to use a static method to run a stored procedure?
  2. How can I know what is happening?
  3. Does EF use a transaction by default? I think that the stored procedure doesn't end right due to the socket error and SqlCommand.ExecuteNonQuery does a rollback.

99,9% of the times it works perfectly, but after I get a socket error on client side it starts to do strange things on database.

UPDATE:
On client side, every time I get a SocketException I close the socket and create a new one. After doing that, I don't get a database error any more.

I don't think that is the solution for the problem but I'm not sure where the problem is.

0条回答
登录 后发表回答