Hola, chicos,
¡Buen día!

En esta publicación me gustaría demostrarle, ya que estoy (y tengo la intención de continuar) publicando varias cosas interesantes sobre CLR, cómo enviar advertencias (PRINT) y mensajes de error (RAISEERROR) a SQL Server cuando se ejecutan sus procedimientos almacenados compilados en CLR.

Aunque el post es pequeño, decidí crear un post solo con esto, porque uso mucho esta clase en los SP's que publicaré aquí en el futuro, así que entiendo que es más fácil crear esta referencia que volver a publicar esta clase varias veces.

Código fuente del archivo Server.cs (requisito previo)

using System;
using System.Collections.Generic;
using System.Data.SqlClient;

namespace Bibliotecas.Model
{

    public class ServidorAtual
    {

        public string NomeServidor { get; set; }

        public ServidorAtual()
        {

            try
            {

                using (var conn = new SqlConnection(Servidor.Context))
                {

                    conn.Open();

                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = "SELECT @@SERVERNAME AS InstanceName";
                        NomeServidor = (string) cmd.ExecuteScalar();
                    }

                    var partes = NomeServidor.Split('\\');

                    if (partes.Length <= 1) return;
                    if (string.Equals(partes[0], partes[1], StringComparison.CurrentCultureIgnoreCase))
                        NomeServidor = partes[0];
                }

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

    }


    public static class Servidor
    {

        public static string Ds_Usuario => "Usuario";
        public static string Ds_Senha => "Senha";

        public static string PRODUCAO => "data source=PRODUCAO;initial catalog=CLR;persist security info=False;Enlist=False;packet size=4096;user id='" + Ds_Usuario + "';password='" + Ds_Senha + "'";
        public static string Context => "context connection=true";
        public static string Localhost => "data source=LOCALHOST;initial catalog=CLR;persist security info=False;Enlist=False;packet size=4096;user id='" + Ds_Usuario + "';password='" + Ds_Senha + "'";

        public static string getLocalhost()
        {

            var servidorAtual = new ServidorAtual().NomeServidor;
            return "data source=" + servidorAtual + ";initial catalog=CLR;persist security info=False;Enlist=False;packet size=4096;user id='" + Ds_Usuario + "';password='" + Ds_Senha + "'";

        }

        public static List<string> Servidores
        {
            get
            {
                var servidores = new List<string>
                {
                    PRODUCAO,
                    Localhost
                };

                return servidores;

            }
        }

    }

}

Código fuente de la clase de retorno:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using Microsoft.SqlServer.Server;

namespace Bibliotecas.Model
{

    public static class Retorno
    {

        public static void Erro(string erro)
        {

            /*

            IF (OBJECT_ID('CLR.dbo.Log_Erro') IS NOT NULL) DROP TABLE CLR.dbo.Log_Erro
            CREATE TABLE CLR.dbo.Log_Erro (
	            Id_Erro INT IDENTITY(1,1),
	            Dt_Erro DATETIME DEFAULT GETDATE(),
	            Nm_Objeto VARCHAR(100),
	            Ds_Erro VARCHAR(MAX),
	            CONSTRAINT [PK_Log_Erro] PRIMARY KEY CLUSTERED (Id_Erro)
            )

            */

            using (var conexao = new SqlConnection(Servidor.getLocalhost()))
            {

                var comando = new SqlCommand("INSERT INTO dbo.Log_Erro (Nm_Objeto, Ds_Erro) VALUES (@Nm_Objeto, @Ds_Erro)", conexao);

                var stackTrace = new StackTrace();
                var objeto = stackTrace.GetFrame(1).GetMethod().Name;

                comando.Parameters.Add(new SqlParameter("@Nm_Objeto", SqlDbType.VarChar, 100)).Value = objeto;
                comando.Parameters.Add(new SqlParameter("@Ds_Erro", SqlDbType.VarChar, 8000)).Value = erro;
                conexao.Open();

                comando.ExecuteNonQuery();
            }
            

            throw new ApplicationException(erro);
        }


        public static void Mensagem(string mensagem)
        {

            using (var conexao = new SqlConnection(Servidor.Context))
            {

                var Comando = new SqlCommand("IF ( (512 & @@OPTIONS) = 512 ) select 1 else select 0", conexao);
                conexao.Open();

                if ((int) Comando.ExecuteScalar() != 0) return;

                var retorno = SqlContext.Pipe;
                retorno?.Send(mensagem.Length > 4000 ? mensagem.Substring(0, 4000) : mensagem);
            }

        }

        public static void RetornaReader(SqlDataReader dataReader)
        {
            var retorno = SqlContext.Pipe;
            retorno?.Send(dataReader);
        }
    }

    public class Ret : Exception
    {
        public Ret(string str) : base(str)
        {      
        }
    }
}

Una vez creada esta clase en su proyecto CLR, simplemente impórtela a su procedimiento almacenado y comience a enviar advertencias y mensajes de error, como lo demostraré a continuación:
Simulación de un error en un método CLR:

using Bibliotecas.Model;
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void stpTeste(SqlString Ds_String)
    {

        try
        {

            if (Ds_String.Value == "ERRO")
            {
                // Vou forçar um erro ao tentar inserir dados em uma tabela que não existe, causando uma Exception
                using (var Conexao = new SqlConnection(Servidor.Localhost))
                {

                    var Comando = new SqlCommand("INSERT INTO dbo.Erro (Nm_Objeto, Ds_Erro) VALUES (@Nm_Objeto, @Ds_Erro)", Conexao);

                    Comando.Parameters.Add(new SqlParameter("@Nm_Objeto", SqlDbType.VarChar, 100)).Value = "Vai dar erro";
                    Comando.Parameters.Add(new SqlParameter("@Ds_Erro", SqlDbType.VarChar, 8000)).Value = "Descrição do Erro";
                    Conexao.Open();

                    Comando.ExecuteNonQuery();
                }
            }

            Retorno.Mensagem("Alerta enviado para o banco (PRINT)");

        }
        catch(Exception e)
        {
            Retorno.Erro("Mensagem de erro (RAISEERROR) gravada. Descrição do erro: " + e.Message);
        }

    }

}

Ejemplo de uso:

SQL Server - sql server clr c# csharp enviar avisos mensagens de erro warnings send text print error messages 2
SQL Server - sql server clr c# csharp enviar advertencias mensajes de error advertencias enviar texto imprimir mensajes de error 2

Consultando el historial de errores:
Como habrás notado, en el método de error coloqué una declaración SQL para registrar el historial de cuándo se llama a este método, creando un registro de errores de CLR, lo que facilita encontrar posibles problemas en tus procedimientos de CLR.

SQL Server - sql server clr c# csharp mensagens de erro print error messages
SQL Server - mensajes de error de sql server clr c# csharp imprimir mensajes de error

¡Eso es todo, amigos!
Si tienes alguna duda déjala aquí en los comentarios.

Abrazo.

sql server clr c# csharp enviar advertencias mensajes de error advertencias enviar texto imprimir mensajes de error

sql server clr c# csharp enviar advertencias mensajes de error advertencias enviar texto imprimir mensajes de error