【问题标题】:CLR Trigger exception when try to communicate with a WCF service尝试与 WCF 服务通信时 CLR 触发异常
【发布时间】:2012-03-20 12:34:44
【问题描述】:

我有一个 CLR SQL 触发器,它尝试根据以下 article 与 WCF 服务进行通信

当我尝试更新/插入记录时,出现以下异常:

No row was updated.    
The data in row 1 was not committed.    
Error Source: .Net SqlClient Data Provider.    
Error Message: A .NET Framework error occured during execution of a user-defined routine or aggregate "WCFTrigger": System.Security.HostProtectionException: Attempt to perform an operation that was forbidden by the CLR host.    
The protected resource (only available with full trust) where: All    
The demanded resources were: Synchronization, ExternalThreading    
System.Security.HostProtectionException:    
  at System.ServiceModel.Description.TypeLoader.LoadContractDescriptionHelper(Type ContactType, Type ServiceType, Object serviceImplementation)    
  at System.ServiceModel.ChannelFactory '1.CreateDescription()    
  at System.ServiceModel.ChannelFactory.InitializeEndpoint(Binding binding, 
EndpointAddress address)    
  at System.ServiceModel.ChannelFactory '1..ctor(Binding binding, EndpointAddress address)    
  at System.ServiceModel.ClientBase '1..ctor(Binding binding, EndpointAddress address)    
  at ServiceClient.WCFServiceReference.ServiceContractClient..ctor(Binding binding, 
EndpointAddress address)

主持人就像文章中一样,客户端的app.config

<?xml version="1.0"?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IServiceContract" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
                    <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>
                    <security mode="Message">
                        <transport clientCredentialType="Windows" proxyCredentialType="None" realm=""/>
                        <message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default"/>
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:8000/services/MyService" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IServiceContract" contract="WCFServiceReference.IServiceContract" name="WSHttpBinding_IServiceContract">
                <identity>
                    <userPrincipalName value="xxx@yyy.zzzt"/>
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>

有人知道,那是什么,为什么?


触发器的代码:

public partial class Triggers {
    [SqlProcedure()]
    public static void SendData( String crudType ) {
        EndpointAddress endpoint = new EndpointAddress( new Uri( "http://localhost:8000/services/myservice" ) );
        WSHttpBinding httpBinding = new WSHttpBinding();
        ServiceClient.WCFServiceReference.ServiceContractClient  myClient = new ServiceClient.WCFServiceReference.ServiceContractClient( httpBinding, endpoint );

            switch( crudType ) {
                case "Update":
                    myClient.UpdateOccured();
                    break;
                case "Insert":
                    myClient.InsertOccured();
                    break;
        }
    }

    [Microsoft.SqlServer.Server.SqlTrigger( Name = "WCFTrigger",
       Target = "tbCR", Event = "FOR UPDATE, INSERT" )]
    public static void Trigger1() {
        SqlTriggerContext myContext = SqlContext.TriggerContext;

        switch( myContext.TriggerAction ) {
            case TriggerAction.Update:
                SendData( "Update" );
                break;
            case TriggerAction.Insert:
                SendData( "Insert" );
                break;
        }
    }
}

代理:

namespace ServiceClient.WCFServiceReference {
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    [System.ServiceModel.ServiceContractAttribute(ConfigurationName="WCFServiceReference.IServiceContract")]
    public interface IServiceContract {

        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IServiceContract/UpdateOccured", ReplyAction="http://tempuri.org/IServiceContract/UpdateOccuredResponse")]
        void UpdateOccured();

        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IServiceContract/InsertOccured", ReplyAction="http://tempuri.org/IServiceContract/InsertOccuredResponse")]
        void InsertOccured();
    }

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    public interface IServiceContractChannel : ServiceClient.WCFServiceReference.IServiceContract, System.ServiceModel.IClientChannel {
    }

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    public partial class ServiceContractClient : System.ServiceModel.ClientBase<ServiceClient.WCFServiceReference.IServiceContract>, ServiceClient.WCFServiceReference.IServiceContract {

        public ServiceContractClient() {
        }

        public ServiceContractClient(string endpointConfigurationName) : 
                base(endpointConfigurationName) {
        }

        public ServiceContractClient(string endpointConfigurationName, string remoteAddress) : 
                base(endpointConfigurationName, remoteAddress) {
        }

        public ServiceContractClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : 
                base(endpointConfigurationName, remoteAddress) {
        }

        public ServiceContractClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
                base(binding, remoteAddress) {
        }

        public void UpdateOccured() {
            base.Channel.UpdateOccured();
        }

        public void InsertOccured() {
            base.Channel.InsertOccured();
        }
    }
}

【问题讨论】:

    标签: c# wcf exception-handling triggers sqlclr


    【解决方案1】:

    问题是由于在部分信任代码访问安全 (CAS) 设置下在 SQL Server 中运行 .NET 代码(WCF 客户端调用服务)引起的。看看这个MSDN article 为 SQL Server 配置 CAS。

    编辑:

    作为启用完全信任的替代方法,在 WCF 服务上配置 webHttpBinding 端点。使用 System.Net HttpWebRequest & HttpWebResponse 类调用服务以避免使用基于 WCF ChannelFactory 的管道。

    【讨论】:

    • 可能是这样,但错误表明代码需要完全信任,这在 SQL CLR 中是不可能的。通常调用 Web 服务需要程序集和数据库所有者的 EXTERNAL 权限,但如果这是问题所在,您不会收到此处报告的异常。
    • 我已将 Trigger-project 的“权限级别”更改为不安全,但没有任何更改
    • 我认为问题的根源可能在于 WCF 正在动态创建程序集以充当服务代理。这可能就是为什么这段代码需要一个完全信任的环境。如果无法将 SQLCLR 配置为完全信任(我对 SQLCLR 了解不多),那么您将无法运行代码。
    【解决方案2】:

    您不能在任何 SQLCLR 程序集中执行任何多线程操作,并且异常告诉您您正在尝试使用线程。你是在异步调用 WCF 服务吗?

    我建议您从 SQLCLR 触发器发布您的代码以获得更多帮助。

    从 SQL CLR 调用 WCF 服务比调用 asmx Web 服务困难得多,你确定需要 wcf 吗?

    【讨论】:

    • 不,在“服务参考设置”中,“生成异步操作”没有被选中,从触发器中我同步调用了“SendData”!
    • “所需资源为:同步、外部线程”似乎表明您的代码中有某些内容,或者生成的代理正在尝试执行多线程操作。上次我尝试通过 SQL CLR 触发器使用 WCF 时,我刚刚放弃并创建了一个 asmx Web 服务,它奏效了。这是一个选项,也请发布您的触发代码。
    • 嗯,进一步检查可能与您的 wcf 服务而不是您的 SQL CLR 触发器有关,因为错误似乎不是发生在您的触发器中,而是发生在 WCF 服务中。您可以从简单的控制台应用程序调用服务吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-22
    • 2011-10-17
    相关资源
    最近更新 更多