Reputation: 1566
I've got a service that has a whole slew of methods for creating and managing assets for our site. It's also got two methods that need to be accessible externally, lets say "GetConfiguration" and "GetElement". I want to expose the same service at different endpoints using different ServiceContracts. BUT I want those two methods to be available at both endpoints.
Here's the relevant section of my App.Config:
<service name="Manager.Manager" behaviorConfiguration="Manager.ManagerBehavior">
<endpoint address="" binding="customBinding" bindingConfiguration="NetHttpBinding" contract="Manager.IManager" />
<endpoint address="Runtime" binding="customBinding" bindingConfiguration="NetHttpBinding" contract="Manager.IPublicManager" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
When I load this thing into IIS and try to access it, I get the following error:
An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is:
System.InvalidOperationException: An exception was thrown in a call to a WSDL export extension: System.ServiceModel.Description.DataContractSerializerOperationBehavior
contract: http://tempuri.org/:IPublicManager ----> System.InvalidOperationException: The Manager.IPublicManager.GetConfiguration operation references a message element [http://tempuri.org/:GetConfiguration] that has already been exported from the Manager.IManager.GetConfiguration operation. You can change the name of one of the operations by changing the method name or using the Name property of OperationContractAttribute. Alternatively, you can control the element name in greater detail using the MessageContract programming model.
at System.ServiceModel.Description.MessageContractExporter.AddElementToSchema(XmlSchemaElement element, String elementNs, XmlSchemaSet schemaSet)
at System.ServiceModel.Description.MessageContractExporter.ExportWrappedPart(Message message, String elementName, String elementNs, XmlSchemaSet schemaSet, Boolean skipSchemaExport)
at System.ServiceModel.Description.DataContractSerializerMessageContractExporter.ExportBody(Int32 messageIndex, Object state)
at System.ServiceModel.Description.MessageContractExporter.ExportMessage(Int32 messageIndex, Object state)
at System.ServiceModel.Description.MessageContractExporter.ExportMessageContract()
at System.ServiceModel.Description.WsdlExporter.CallExtension(WsdlContractConversionContext contractContext, IWsdlExportExtension extension)
--- End of inner ExceptionDetail stack trace ---
at System.ServiceModel.Description.WsdlExporter.CallExtension(WsdlContractConversionContext contractContext, IWsdlExportExtension extension)
at System.ServiceModel.Description.WsdlExporter.CallExportContract(WsdlContractConversionContext contractContext)
at System.ServiceModel.Description.WsdlExporter.ExportContract(ContractDescription contract)
at System.ServiceModel.Description.WsdlExporter.ExportEndpoint(ServiceEndpoint endpoint, XmlQualifiedName wsdlServiceQName)
at System.ServiceModel.Description.WsdlExporter.ExportEndpoints(IEnumerable`1 endpoints, XmlQualifiedName wsdlServiceQName)
at System.ServiceModel.Description.ServiceMetadataBehavior.MetadataExtensionInitializer.GenerateMetadata()
at System.ServiceModel.Description.ServiceMetadataExtension.EnsureInitialized()
at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.InitializationData.InitializeFrom(ServiceMetadataExtension extension)
at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.GetInitData()
at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.TryHandleDocumentationRequest(Message httpGetRequest, String[] queries, Message& replyMessage)
at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.ProcessHttpRequest(Message httpGetRequest)
at SyncInvokeGet(Object , Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
So it doesn't like that the Operations have the same name. Any ideas? They both use the same Service class. Maybe I'm just approaching the problem entirely the wrong way?
Upvotes: 4
Views: 1431
Reputation: 1999
1) Create the two interface/ contract with service contract attribute. 2) Implement both contract in service. 3) create endpoint and mention different contract name (interface) though service is same.
For more information please see think link
https://www.safaribooksonline.com/library/view/learning-wcf/9780596101626/ch01s07.html
Upvotes: 0
Reputation: 15086
Although this doesn't exactly solve your problem, I hope it can be of some help. I use webHttpBinding
since I am most familiar with that.
The idea here is to split public and private functionality into two separate interfaces:
[ServiceContract]
public interface IPrivate : IPublic
{
[WebGet(UriTemplate = "/admin")]
[OperationContract]
int PrivateStuff();
}
[ServiceContract]
public interface IPublic
{
[WebGet(UriTemplate = "/common")]
[OperationContract]
int PublicStuff();
}
As you can see implementors of IPrivate
must also implement IPublic
why the service implements IPrivate
to be exposable as both.
The service implementation can now be exposed as both an IPublic
and an IPrivate
and the endpoints can be exposed beneath the service ("service1.svc"):
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="rest">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="ManagerService.Service">
<endpoint address="private" behaviorConfiguration="rest" contract="ManagerService.IPrivate" binding="webHttpBinding"/>
<endpoint address="public" behaviorConfiguration="rest" contract="ManagerService.IPublic" binding="webHttpBinding" />
<endpoint address="/private/mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
<endpoint address="/public/mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
The service is now exposed in it's public part at http://host/service1.svc/common
whereas the private version is exposed at http://host/service1.svc/admin
The meta data exchange does not seem to work correctly, but that is probably due to the binding type.
Upvotes: 1