0

I'm new to WCF and am trying to make my first service (a simple usage reporting service). I've gone through examples and tutorials and created a service. I have a simple test program that can run my core code and send the report. Currently I'm running locally hosted in the debugger, but running this simple exe program hosts the service, sends the report, and the service creates the log file just like it's supposed to... all is good.

Now, my actual program is an addin to another commercial program that runs in it's API (Autodesk Revit). When I run the exact same code inside of the Revit API I get an error that there is no endpoint defined. My guess is that this is because it's looking for the main Revit.exe.config which obviously will not have my endpoint defined. I have a .config file for my dll created (MyLibrary.dll.config) and in the executing directory for my code and it defines the endpoint properly, but that doesn't seem to be recognized.

So my question is how do I get it to load the connection settings from this config file? Or is there another way I should be doing this? I'm open to setting it in code somehow or whatever, just can't figure out how to get it to connect...

I'm not sure if it matters, but here is the configuration that is working in the standalone program:

<?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>

My constructor that is throwing the endpoint exception is simply:

_reporter = new ReportingServiceClient();

Here is the exception that is thrown:

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

Any help would be greatly appreciated...

sfaust
  • 2,089
  • 28
  • 54

3 Answers3

2

it's not relevant that the dll's configuration file is in the same folder as the application. only the application's (executable's) app.config file is read. the solution is to copy the WCF service configuration from the dll config file to you application's app.config file.

the other solution, for modular applications, is to set the service's ABC in code. the problem with this is that you cannot configure it without rebuilding and redeploying the addin.

to create a WCF proxy entirely in code you could use something like this:

IServiceContract proxy = ChannelFactory<IServiceContract>.CreateChannel(new WSHttpBinding(),
                    new EndpointAddress("<you url here>"));
flo_badea
  • 774
  • 5
  • 8
  • Thanks that looks promising I will try it out today... So what is the difference between that and what I posted below? Sorry if that's a newbie questions but I'm new to WCF and trying to understand... – sfaust Aug 22 '14 at 14:58
  • Ok that started me on a track of some good reading so thanks :). So to see if I understand here; using your code above, it creates a 'raw' interface implementation that in my case is an object of type IReportingService. In my code below, it creates an object of type ReportingServiceClient, but if I understand, that class is basically just a wrapper around the IReportingService class that you are creating and calling my service methods from it just passes through to the IReportingService anyway. So your code and mine are doing essentially the same thing, is that correct? – sfaust Aug 22 '14 at 16:59
  • 1
    from your class name i think you're deriving from ClientBase. Usually this happens when you auto-generate the code. ClientBase is indeed a wrapper over what i posted. ClientBase also offers better connection management meaning you can directly use it in a using statement. You need a conversion to dispose of the resources in my example. now that i think about it there is a way to use your dll config file. this will help your design i think. imo copying the settings to the main app.config defeats the purpose of using modules. now the app knows something about the module. read next comment. – flo_badea Aug 23 '14 at 15:47
  • 1
    you can use the dll config by using ConfigurationManager.OpenMappedExeConfiguration. this way you can change the configuration without redeploying and also without the main app knowing anything about the module. you can open the dll config by using something like: var map=new ExeConfigurationFileMap(); map.ExeConfigFilename = string.Format("{0}.config", Assembly.GetExecutingAssembly().Location); var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None); in theory you should be able to read WCF settings by using the config variable. i never tested this. – flo_badea Aug 23 '14 at 16:02
0

I found this post that lead me to a possible answer. I have now updated my constructor as follows:

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

I've left the binding as just default although it looks like I can set other properties if I need to. I just pulled the endpoint address out of the config file that worked in the standalone program and used that to construct.

This seems to be working as expected inside of Revit at least with preliminary tests. Can anyone comment on if this would cause any issues or if there is a better way to handle this situation?

Community
  • 1
  • 1
sfaust
  • 2,089
  • 28
  • 54
  • I'd say what you have here is fine. Obviously, you'll have to decide things like how much of your WCF service should be client configurable. Also be aware that there are settings in the BasicHttpService that you might have to increase if the amount of data passed is going to be large. So be prepared to create a factory method to create your BasicHttpService. – Matt Aug 22 '14 at 20:48
  • I was just reading about this a little, should I be using WCHttpBinding instead? Both client and server are well above .NET 3.0 (3.5 minimum). – sfaust Aug 22 '14 at 23:38
0

Though you can inject endpoint info into a proxy class ReportingServiceClient through your application codes, the first class programming approach is to use app.config.

In your client config, you have an endpoint named "BasicHttpBinding_IReportingService", to use this endpoint, you should then write:

_reporter = new ReportingServiceClient("BasicHttpBinding_IReportingService");

If you want

_reporter = new ReportingServiceClient();

to work, remove the name attribute or make the name attribute value in the client endpoint defined in the client config. This will be the so called "default endpoint element" mentioned by the exception.

ZZZ
  • 2,752
  • 2
  • 25
  • 37
  • 1
    But the issue is that it won't read that config because I am running under a different application and I can't put my config info into the main application config. Unless I'm mistaken it won't read that endpoint because it is not reading the config, right? – sfaust Aug 22 '14 at 14:59
  • I understand that using app.config is best practice but unless I misunderstand it's not possible in this case. that's the basic question I'm asking... – sfaust Aug 22 '14 at 19:58
  • you have myclient.dll that needs certain config setting. When it is used in App.exe, the settings has to be copied to App.exe.config; in App2.exe, then app2.exe.config; when being hosted in a Web service in IIS, then web.config. This is a must for all good reasons, though you may write a lot codes to circumvent it. – ZZZ Aug 22 '14 at 21:43