除非标记为不安全,否则C#CLR引发安全异常

除非标记为不安全,否则C#CLR引发安全异常,c#,.net,sql-server,clr,sqlclr,C#,.net,Sql Server,Clr,Sqlclr,我在SQL Server 2012中有一个C#CLR(.NET 4.5)存储过程,它是从事务中调用的。CLR调用需要TLS 1.2的Web服务 如果我使用“创建具有权限集的程序集”=不安全,则一切正常,但我确实希望避免这种情况,而是使用外部访问。然而,事实证明这是一个真正的挑战 使用外部访问时存在两个问题: private static readonly object Locker = new object(); public static string LogMessage(string me

我在SQL Server 2012中有一个C#CLR(.NET 4.5)存储过程,它是从事务中调用的。CLR调用需要TLS 1.2的Web服务

如果我使用“创建具有权限集的程序集”=不安全,则一切正常,但我确实希望避免这种情况,而是使用外部访问。然而,事实证明这是一个真正的挑战

使用外部访问时存在两个问题:

private static readonly object Locker = new object();
public static string LogMessage(string message)
{
    message = String.Format("{0}: {1}" + Environment.NewLine, DateTime.Now, message);

    lock (Locker)
    {
        var file = new FileStream(GetConfigValue("LogFile"), FileMode.Append, FileAccess.Write);
        var sw = new StreamWriter(file);
        sw.Write(message);
        sw.Flush();
        sw.Close();
    }
    return message;
}
1) 我从CLR写入一个日志文件(用于维护和调试),该文件不起作用,因为不允许使用

private static readonly object Locker = new object();
public static string LogMessage(string message)
{
    message = String.Format("{0}: {1}" + Environment.NewLine, DateTime.Now, message);

    lock (Locker)
    {
        var file = new FileStream(GetConfigValue("LogFile"), FileMode.Append, FileAccess.Write);
        var sw = new StreamWriter(file);
        sw.Write(message);
        sw.Flush();
        sw.Close();
    }
    return message;
}
错误消息:

System.Security.HostProtectionException:试图执行CLR主机禁止的操作

受保护的资源(仅在完全信任的情况下可用)是:所有需要的资源是:同步、外部线程

我可以改为登录到数据库表,但由于对CLR的调用来自事务中,因此错误将导致回滚,这也将回滚日志记录。我可以使用事件日志,但如果可能的话,我真的不想这样做

2) 我调用的web服务需要TLS 1.2,但不允许设置
ServerCertificateValidationCallback

ServicePointManager.ServerCertificateValidationCallback = AcceptAllCertifications;
错误消息:

System.Security.SecurityException:请求“System.Security.Permissions.SecurityPermission,mscorlib,版本=4.0.0.0,区域性=neutral,PublicKeyToken=b77a5c561934e089”类型的权限失败。
System.Security.SecurityException:
在System.Security.CodeAccessSecurityEngine.Check(对象请求、堆栈爬网标记和堆栈标记、布尔isPermSet) 在System.Security.CodeAccessPermission.Demand()中 位于System.Net.ServicePointManager.set_服务器CertificateValidationCallback(RemoteCertificateValidationCallback值) 在AcmeClr.StoredProcedures.ProcessPayment(SqlMoney金额、SqlString票证号、SqlString和resultCode、SqlString和resultText)

这个问题有没有解决方法?有没有一种方法可以在不使用不安全的情况下工作?

对于问题1(使用锁来管理同时写入同一日志文件的多个线程/会话),除了在
不安全
模式下使用锁之外,没有其他方法。然而,好消息是,您可能一开始就不需要使用锁。锁真正要做的就是确保同时发生的两个
LogMessage()
调用不会发生冲突,其中一个会出错。这应该可以通过使用另一个允许传入选项/enum的重载来解决。您应该能够传入
FileShare.ReadWrite
以防止出现错误。您已经在将
DateTime.Now
连接到
message
中,因此,如果先一条消息在后一条消息之后写入,这将有助于解决序列问题

但在这个特定争用之外,即使使用锁,您仍然会遇到两个会话在执行SQLCLR WebService存储过程的同时交错消息的问题。你如何区分这些信息?我建议在存储过程开始时创建一个新的
Guid
,并将其连接到每条消息中,以便您可以将特定调用的消息关联到存储过程(我假设您的代码有多个调用
LogMessage()
)的位置,并将这些消息与其他调用区分开来(不论是否同时进行)

如果出于任何原因,上面的两个部分(
FileShare.ReadWrite
和将Guid连接到
消息中
)不能防止错误发生,那么您可以忘记
FileShare
选项(尽管我仍然更喜欢将其设置为
Read
,以便在写入文件时可以轻松地检查文件)而不是将Guid连接到
消息中
,而是将其附加到
GetConfigValue(“日志文件”)的末尾
value,就在文件扩展名之前。然后,特定调用的消息会自动相互关联,并与其他并发调用区分开来。这只意味着您有一堆日志文件

关于这一点,还有三个想法:

  • 通过在
    日志消息()中设置
    method,实际上您正在增加阻塞,因为跨不同会话对该方法的并发调用将必须等待锁所有者释放锁。毕竟,这是锁的第一点,对吗?除非在处理事务时,您真的不想延长它们超过绝对长度非常必要,通过SQLCLR进行Web服务调用的本质是,您已经在使事务依赖于网络延迟和外部系统的响应(即使是内部系统)

  • 你提到:

    我可以改为登录到数据库表,但由于对CLR的调用来自事务中,因此错误将导致回滚,这也将回滚日志记录

    不一定。如果您使用进程中的“上下文连接”,那么是的,这是真的。如果在连接字符串中未指定
    Enlist
    关键字,或者确实指定了
    Enlist=true;
    ,则使用常规/外部连接也是真的。但是,如果指定
    Enlist=false;
    ,则应该是独立/断开连接的事务,而不是如果调用存储过程的事务回滚,et将回滚

  • 虽然这在使用SQLCLR代码的情况下可能没有帮助,但我至少要提到,对于纯T-SQL代码,当您想解决回滚时丢失日志记录的问题时,可以选择将这些记录最初写入表变量。T