【问题标题】:WCF client configuration from custom location来自自定义位置的 WCF 客户端配置
【发布时间】:2014-08-22 14:37:41
【问题描述】:

我是 WCF 的新手,正在尝试创建我的第一个服务(一个简单的使用情况报告服务)。我浏览了示例和教程并创建了一个服务。我有一个简单的测试程序,可以运行我的核心代码并发送报告。目前我在调试器中本地运行,但运行这个简单的 exe 程序托管服务,发送报告,服务创建日志文件,就像它应该做的那样......一切都很好。

现在,我的实际程序是另一个在其 API (Autodesk Revit) 中运行的商业程序的插件。当我在 Revit API 中运行完全相同的代码时,我收到一个错误,即没有定义端点。我的猜测是,这是因为它正在寻找主要的 Revit.exe.config,它显然不会定义我的端点。我为我的 dll 创建了一个 .config 文件(MyLibrary.dll.config),并且在我的代码的执行目录中,它正确定义了端点,但似乎无法识别。

所以我的问题是如何让它从这个配置文件中加载连接设置?还是我应该这样做的另一种方式?我愿意以某种方式或其他方式在代码中设置它,只是不知道如何让它连接......

我不确定这是否重要,但这是在独立程序中运行的配置:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
      <bindings>
        <basicHttpBinding>
          <binding name="BasicHttpBinding_IReportingService" />
        </basicHttpBinding>
      </bindings>
      <client>
        <endpoint address="http://localhost:8733/Design_Time_Addresses/SPECtrumReportingService/Service1/"
            binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IReportingService"
            contract="ReportService.IReportingService" name="BasicHttpBinding_IReportingService" />
      </client>
    </system.serviceModel>
</configuration>

我抛出端点异常的构造函数很简单:

_reporter = new ReportingServiceClient();

这里是抛出的异常:

Could not find default endpoint element that references contract 'ReportService.IReportingService' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element.
   at System.ServiceModel.Description.ConfigLoader.LoadChannelBehaviors(ServiceEndpoint serviceEndpoint, String configurationName)
   at System.ServiceModel.ChannelFactory.InitializeEndpoint(String configurationName, EndpointAddress address)
   at System.ServiceModel.ChannelFactory`1..ctor(String endpointConfigurationName, EndpointAddress remoteAddress)
   at System.ServiceModel.ConfigurationEndpointTrait`1.CreateSimplexFactory()
   at System.ServiceModel.ClientBase`1.CreateChannelFactoryRef(EndpointTrait`1 endpointTrait)
   at System.ServiceModel.ClientBase`1.InitializeChannelFactoryRef()
   at RDES.Revit.Sumex.CommonUse.ReportService.ReportingServiceClient..ctor() in c:\RD\Projects\13-004 SPECtrum\Code\SPECtrumBase\Service References\ReportService\Reference.cs:line 241
   at RDES.Revit.Sumex.CommonUse.ImportVM..ctor() in c:\RD\Projects\13-004 SPECtrum\Code\SPECtrumBase\ImportVM.cs:line 41

任何帮助将不胜感激...

【问题讨论】:

  • 您能发布异常的详细信息吗?
  • 当然,添加了异常

标签: c# .net wcf revit revit-api


【解决方案1】:

dll 的配置文件与应用程序位于同一文件夹中无关紧要。仅读取应用程序(可执行文件)的 app.config 文件。解决方案是将 WCF 服务配置从 dll 配置文件复制到应用程序的 app.config 文件。

对于模块化应用程序,另一种解决方案是在代码中设置服务的 ABC。这样做的问题是如果不重新构建和重新部署插件就无法配置它。

要完全在代码中创建 WCF 代理,您可以使用以下代码:

IServiceContract proxy = ChannelFactory<IServiceContract>.CreateChannel(new WSHttpBinding(),
                    new EndpointAddress("<you url here>"));

【讨论】:

  • 谢谢,看起来很有希望我今天会尝试一下......那么这与我在下面发布的内容有什么区别?对不起,如果这是一个新手问题,但我是 WCF 的新手并试图理解......
  • 好的,这让我开始了一些好的阅读,所以谢谢:)。所以看看我是否理解这里;使用上面的代码,它创建了一个“原始”接口实现,在我的例子中是一个 IReportingService 类型的对象。在我下面的代码中,它创建了一个 ReportingServiceClient 类型的对象,但如果我理解的话,该类基本上只是您正在创建的 IReportingService 类的一个包装器,并且从中调用我的服务方法只是传递给 IReportingService。所以你的代码和我的代码本质上是一样的,对吗?
  • 从你的类名我认为你是从 ClientBase 派生的。通常在您自动生成代码时会发生这种情况。 ClientBase 确实是我发布的内容的包装。 ClientBase 还提供了更好的连接管理,这意味着您可以直接在 using 语句中使用它。在我的示例中,您需要转换来处理资源。现在我想到了,有一种方法可以使用您的 dll 配置文件。我认为这将有助于您的设计。 imo 将设置复制到主 app.config 违背了使用模块的目的。现在应用程序对模块有所了解。阅读下一条评论。
  • 您可以通过 ConfigurationManager.OpenMappedExeConfiguration 使用 dll 配置。这样您就可以在不重新部署的情况下更改配置,并且主应用程序也不了解该模块的任何信息。您可以使用以下内容打开 dll 配置: var map=new ExeConfigurationFileMap(); map.ExeConfigFilename = string.Format("{0}.config", Assembly.GetExecutingAssembly().Location); var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);理论上,您应该能够使用 config 变量读取 WCF 设置。我从来没有测试过这个。
【解决方案2】:

我找到了this post,这让我找到了一个可能的答案。我现在更新了我的构造函数如下:

_reporter = new ReportingServiceClient(new BasicHttpBinding(), new EndpointAddress("http://localhost:8733/Design_Time_Addresses/SPECtrumReportingService/Service1/"));

我将绑定保留为默认值,但看起来我可以根据需要设置其他属性。我只是从在独立程序中工作的配置文件中提取了端点地址并使用它来构建。

至少在初步测试中,这似乎在 Revit 内部按预期工作。任何人都可以评论这是否会导致任何问题,或者是否有更好的方法来处理这种情况?

【讨论】:

  • 我想说你这里的东西很好。显然,您必须决定有多少 WCF 服务应该是客户端可配置的。另请注意,如果传递的数据量很大,您可能需要增加 BasicHttpService 中的一些设置。所以准备好创建一个工厂方法来创建你的 BasicHttpService。
  • 我刚刚读到这个,我应该改用 WCHttpBinding 吗?客户端和服务器都远高于 .NET 3.0(最低 3.5)。
【解决方案3】:

虽然您可以通过应用程序代码将端点信息注入代理类 ReportingServiceClient,但第一类编程方法是使用 app.config。

在您的客户端配置中,您有一个名为“BasicHttpBinding_IReportingService”的端点,要使用此端点,您应该编写:

_reporter = new ReportingServiceClient("BasicHttpBinding_IReportingService");

如果你愿意

_reporter = new ReportingServiceClient();

要工作,请删除名称属性或在客户端配置中定义的客户端端点中设置名称属性值。这将是异常提到的所谓的“默认端点元素”。

【讨论】:

  • 但问题是它不会读取该配置,因为我在不同的应用程序下运行并且我无法将我的配置信息放入主应用程序配置中。除非我弄错了,否则它不会读取该端点,因为它没有读取配置,对吧?
  • 我知道使用 app.config 是最佳实践,但除非我误解,否则在这种情况下是不可能的。这是我要问的基本问题...
  • 你有 myclient.dll 需要某些配置设置。在 App.exe 中使用时,需要将设置复制到 App.exe.config 中;在 App2.exe 中,然后是 app2.exe.config;当托管在 IIS 中的 Web 服务中时,则为 web.config。出于所有充分的理由,这是必须的,尽管您可能会编写很多代码来规避它。
猜你喜欢
  • 2015-05-23
  • 1970-01-01
  • 1970-01-01
  • 2011-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-05
相关资源
最近更新 更多