【问题标题】:Getting "Object is read only" error when setting ClientCredentials in WCF在 WCF 中设置 ClientCredentials 时出现“对象为只读”错误
【发布时间】:2008-10-13 20:57:31
【问题描述】:

我有一个名为 ServerClient 的由 Visual Studio(客户端)生成的代理对象。我正在尝试在使用此代码打开新连接之前设置 ClientCredentials.UserName.UserName/Password:

InstanceContext context = new InstanceContext(this);

m_client = new ServerClient(context);
m_client.ClientCredentials.UserName.UserName = "Sample";

一旦代码到达 UserName 行,它就会失败并出现“对象是只读的”错误。我知道如果连接已经打开或出现故障,可能会发生这种情况,但此时我还没有调用 context.Open()。

我已将绑定(使用 netTcpBinding)配置为使用 Message 作为其安全模式,并且 MessageClientCredentialType 设置为 UserName。

有什么想法吗?

【问题讨论】:

    标签: visual-studio wcf


    【解决方案1】:

    我注意到,在为服务创建代理类的实例后,我可以设置一次用户名和密码而不会出错,并成功调用我的 web 服务。然后,当我尝试在现有实例上再次设置用户名和密码时(当然没有必要),我收到您提到的“对象为只读”错误。每个实例生命周期设置一次值对我有用。

    【讨论】:

    • 更恰当地说,需要在调用任何服务方法之前设置凭据。在调用方法后尝试设置凭据会出错 - 凭据是在您进行服务调用时隐式设置的,现在无法更改;它们是不可变的。
    • 在我的例子中,即使像向客户端对象添加事件处理程序这样看似无害的事情(仅此而已)也会导致属性变为只读。
    • 就我而言,它设置了超时client.InnerChannel.OperationTimeout = TimeSpan.FromMilliseconds(Timeout)。有道理,您已经创建了一个频道,更改工厂配置伙伴为时已晚。 (这在 4.5.2 和 4.7.2 之间发生了变化)
    【解决方案2】:

    您似乎只能在实例化周期的早期访问这些属性。如果我覆盖代理类 (ServerClient) 中的构造函数,我可以设置这些属性:

    base.ClientCredentials.UserName.UserName = "Sample";
    

    我开始感谢那些建议不要使用 VS 提供的自动构建代理的人。

    【讨论】:

    • 我使用微软代理的唯一原因是因为它会自动生成异步方法..
    【解决方案3】:

    解决办法如下:

    using SysSvcmod = System.ServiceModel.Description;
    
    SysSvcmod.ClientCredentials clientCredentials = new SysSvcmod.ClientCredentials();
    clientCredentials.UserName.UserName = "user_name";
    clientCredentials.UserName.Password = "pass_word";
    
    m_client.ChannelFactory.Endpoint.Behaviors.RemoveAt(1);
    m_client.ChannelFactory.Endpoint.Behaviors.Add(clientCredentials);
    

    【讨论】:

    • 什么是 m_client?
    • 这个解决方案真的帮我解决了这个问题!非常感谢!
    【解决方案4】:

    我有类似的代码通过UserName 很好:

      FooServiceClient client = new FooServiceClient("BasicHttpBinding_IFooService");
      client.ClientCredentials.UserName.UserName = "user";
      client.ClientCredentials.UserName.Password = "password";
    

    尝试在 app.config 中使用绑定名称创建代理。

    【讨论】:

      【解决方案5】:

      我遇到了同样的问题,我的代码在我更改代码时开始工作,即在初始化客户端对象后立即为客户端凭据分配值。

      这里是解决方案,

      ProductClient Manager = new  ProductClient();    
      Manager.ClientCredentials.UserName.UserName = txtUserName.Text;
      Manager.ClientCredentials.UserName.Password = txtPassword.Text;
      

      【讨论】:

      • 我将我的凭据设置行移到了我的客户端创建旁边(正如你提到的),我的代码开始工作了。
      【解决方案6】:

      正确的语法是:

      // Remove the ClientCredentials behavior.
      client.ChannelFactory.Endpoint.Behaviors.Remove<ClientCredentials>();
      
      // Add a custom client credentials instance to the behaviors collection.
      client.ChannelFactory.Endpoint.Behaviors.Add(new MyClientCredentials());
      

      http://msdn.microsoft.com/en-us/library/ms730868.aspx

      它对我有用。

      【讨论】:

        【解决方案7】:

        如果通过->添加服务引用->高级->添加Web引用->Url/wsdl(本地磁盘文件)添加服务引用,则不会出现这种情况。

        【讨论】:

        • 我刚刚使用了您的解决方案 - 它有所帮助,但我想知道当 .wsdl 文件将由服务所有者更新时,这是否会成为问题?或者直到我重复添加服务引用->高级->添加Web引用-> Url/wsdl(本地磁盘文件)。并添加一个新的xml文件之前我不会看到新方法?旧生成的代码会出现一些冲突吗?谢谢!
        • 你必须手动将 wsdl 移植到相同的位置并更新
        【解决方案8】:

        我正面临这个问题,我试图创建一个通用方法来为不同的端点创建客户端。

        我是如何做到这一点的。

            public static T CreateClient<T>(string url) where T : class
            {
                EndpointAddress endPoint = new EndpointAddress(url);
                CustomBinding binding = CreateCustomBinding();
        
                T client = (T)Activator.CreateInstance(typeof(T), new object[] { binding, endPoint });
                SetClientCredentials(client);
        
                return client;
            }
        
            public static void SetClientCredentials(dynamic obj)
            {
                obj.ChannelFactory.Endpoint.Behaviors.Remove<ClientCredentials>();
                obj.ChannelFactory.Endpoint.Behaviors.Add(new CustomCredentials());
        
                obj.ClientCredentials.UserName.UserName = "UserId";
                obj.ClientCredentials.UserName.Password = "Password";
            }
        

        【讨论】:

          【解决方案9】:

          我认为您的问题可能与 InstanceContext 的使用有关。我认为这只需要来自服务器端的双工通信通道。

          我承认我对此不确定,但我认为在这种情况下,您是在告诉客户端使用现有的实例上下文,因此它认为已经有一个正在运行的服务并且不允许更改。

          是什么推动了 InstanceContext 的使用?

          【讨论】:

            【解决方案10】:

            如果使用双工客户端,当您实例化它时,您的客户端派生自 DuplexClientBase 中的 DuplexChannelFactory 将使用现有凭据进行初始化,以便它可以打开回调通道,这就是凭据为只读的原因。

            我赞同 Mike 的问题,还问如果您不打算使用 NetTcpBinding 固有的传输级别安全性,为什么还要使用它?也许基于 HTTP 的绑定会更合适?这将允许您使用基于证书的安全性,我相信可以在实例化后对其进行修改 (http://msdn.microsoft.com/en-us/library/ms576164.aspx)。

            【讨论】:

              【解决方案11】:

              在黑暗中开枪,但 netTcpBinding 是否允许用户名和密码验证?尝试使用 http 绑定使用应用层 (SOAP) 安全性

              【讨论】:

                【解决方案12】:

                或者您可以简单地检查凭据

                    if (client.ClientCredentials.ClientCertificate.Certificate == null || string.IsNullOrEmpty(client.ClientCredentials.ClientCertificate.Certificate.Thumbprint))
                    {
                        client.ClientCredentials.ClientCertificate.SetCertificate(
                            StoreLocation.LocalMachine,
                            StoreName.My,
                            X509FindType.FindByThumbprint, ConfigurationManager.AppSettings.Get("CertificateThumbprint"));
                    }
                

                【讨论】:

                  猜你喜欢
                  • 2011-04-13
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-09-27
                  • 2014-06-04
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-02-25
                  相关资源
                  最近更新 更多