I have a server&client solution that are using WCF. The client will ask a service about the URL to an active server at runtime and to be able to set this I use ChannelFactory. I however still needs to use all other WCF settings from the config file. This is how I do it :
var clientSection = ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection;
var address = string.Empty;
for(int i = 0; i < clientSection.Endpoints.Count; i++)
{
if(clientSection.Endpoints[i].Name == endpointConfigurationName)
{
var endpointAddress = new EndpointAddress(clientSection.Endpoints[i].Address.ToString());
var netHttpBinding = new NetHttpBinding(clientSection.Endpoints[i].BindingConfiguration);
var serviceEndpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(T)), netHttpBinding, endpointAddress);
var channelFactory = new ChannelFactory<T>(serviceEndpoint);
break;
}
}
The problem is that I got 2 BehaviorExtensions that are used by some of the endpoints like this.
<services>
<endpoint binding="netHttpBinding" behaviorConfiguration="protoEndpointBehavior" address="BinaryHttpProto" bindingNamespace="http://MyApp.ServiceContracts/2007/11" contract="MyApp.ServiceContracts.IMyAppClientService" />
</services>
<behaviors>
<endpointBehaviors>
<behavior name="protoEndpointBehavior">
<protobuf />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="protobuf" type="ProtoBuf.ServiceModel.ProtoBehaviorExtension, protobuf-net, Version=2.0.0.668, Culture=neutral, PublicKeyToken=257b51d87d2e4d67" />
</behaviorExtensions>
</extensions>
The question is how I read this from the clientSection.Endpoints? and sets it on the channelFactory? I know that I could create then manually like this :
serviceEndpoint.EndpointBehaviors.Add(new ProtoEndpointBehavior());
serviceEndpoint.EndpointBehaviors.Add(new CustomMessageInspectorBehavior());
But then this will be a hard coded static and it will apply to all endpoints, I need to be able to change it from the config.
You don't need to create the ChannelFactory by yourself. Just create a ClientService class that inherits from ClientBase<T>. The constructor of the ClientBase<T> accepts a EndpointName and adds automatically the behavior that is associated with this Endpoint. The ClientBase<T> also give you the possibility to access the ChannelFactory<T> and you can open as much channel as you want. the only thing that you need further to do, is to add a name for each EndPoint in the config that you want to use.
<endpoint binding="..." name="MyEndPoint" ... />
I hade to create everything in code, a mixed solution was no good, not in my case where I use alot of custom stuff.
Related
March 27, 2020 UPDATE
It has been 4 days and I have padded my office walls now. :)
BIG QUESTION
This will only work locally if I change the following settings
Anonymous Authentication = Enabled
Does anybody know how to get around this issue with
- IIS Express
- Visual Studio 2017
I stepped out of the work code and created a test locally and I am only stuck on one issue now.
Authentication is now my BLOCKER
I will need to use the settings for deployment as I do not have any control on the DEV | SIT | UAT | PROD --- IAAS or PAAS - I can only code CI and CD.
I removed all the ---- Configuration
This was not in the original source code
I do have to comment out the code in the
MultipleBindingServiceHost.cs file
The localhost is complaining about the 2 URLs (I will revisit once I get all the security holes in the code fixed.
string rawUrl = ConfigurationManager.AppSettings["tsRawUrl"];
ServiceEndpoint endpoint = AddServiceEndpoint(typeof(ITicketService), httpBinding, baseAddress, new Uri(rawUrl));
endpoint.Behaviors.Add(new WebHttpBehavior());
#if (!DEBUG)
string vanityUrl = ConfigurationManager.AppSettings["tsVanityUrl"];
ServiceEndpoint endpoint2 = AddServiceEndpoint(typeof(ITicketService), httpBinding, baseAddress, new Uri(vanityUrl));
endpoint2.Behaviors.Add(new WebHttpBehavior());
#endif
March 23, 2020
I have been trying to figure out WCF and Local Machine development for the past week and now I am coming to stackoverflow for the communities assistance.
I have been tasked with supporting an application that has two WCF Services
Web.config appSettings Settings as follows:
<appSettings>
<add key="tsVanityUrl" value="http://localhost:1574/TicketService.svc" />
<add key="tsRawUrl" value="http://localhost:1574/TicketService.svc" />
<add key="fsVanityUrl" value="http://localhost:1574/FileService.svc" />
<add key="fsRawUrl" value="http://localhost:1574/FileService.svc" />
</appSettings>
Web.config system.serviceModel
<system.serviceModel>
<!-- START RBD Additions for Local Development -->
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<behaviors>
<serviceBehaviors>
<behavior name="TicketBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
<behavior name="FileBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="TicketSystem.TicketService" behaviorConfiguration="TicketBehavior">
<endpoint address="/TicketService.svc"
binding="basicHttpBinding"
contract="TicketSystem.ITicketService"
/>
</service>
<service name="TicketSystem.FileService" behaviorConfiguration="FileBehavior">
<endpoint address="/FileService.svc"
binding="basicHttpBinding"
contract="TicketSystem.IFileService"
/>
</service>
</services>
<!-- END RBD Additions for Local Development -->
</system.serviceModel>
I keep getting the following error:
The value could not be added to the collection, as the collection already contains an item of the same type: 'System.ServiceModel.Description.ServiceMetadataBehavior'. This collection only supports one instance of each type.
Parameter name: item
This points me to the MultipleBindingServiceHost.cs file
protected override void ApplyConfiguration()
{
base.ApplyConfiguration();
ServiceMetadataBehavior mexBehavior = new ServiceMetadataBehavior();
Description.Behaviors.Add(mexBehavior);
WebHttpBinding httpBinding = new WebHttpBinding();
foreach (Uri baseAddress in BaseAddresses)
{
if (baseAddress.Scheme == Uri.UriSchemeHttp)
{
httpBinding.Security.Mode = WebHttpSecurityMode.None;
mexBehavior.HttpGetEnabled = true;
}
else if (baseAddress.Scheme == Uri.UriSchemeHttps)
{
httpBinding.Security.Mode = WebHttpSecurityMode.Transport;
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
mexBehavior.HttpsGetEnabled = true;
}
//ServiceEndpoint endpoint = AddServiceEndpoint(typeof(TicketSystem.ITicketService),
// httpBinding,
// baseAddress);
//endpoint.Behaviors.Add(new WebHttpBehavior());
//Fix for 404 Vanity URL Issue
string rawUrl = ConfigurationManager.AppSettings["tsRawUrl"];
ServiceEndpoint endpoint = AddServiceEndpoint(typeof(ITicketService), httpBinding, baseAddress, new Uri(rawUrl));
endpoint.Behaviors.Add(new WebHttpBehavior());
string vanityUrl = ConfigurationManager.AppSettings["tsVanityUrl"];
ServiceEndpoint endpoint2 = AddServiceEndpoint(typeof(ITicketService), httpBinding, baseAddress, new Uri(vanityUrl));
endpoint2.Behaviors.Add(new WebHttpBehavior());
break;
}
}
}
I know I am very close and probably missing something very simple, but after spending multiple days on this I have to post on stackoverflow to get the services running on my local maching.
WCF service application project doesn’t support multiple service contracts in one service host. One service project has one service host, we need to specify the service implemented class during starting the host. Consequently, it is impossible to host multiple service contracts in one host. We could create multiple hosts in a console/Windows NT service application so as to support multiple service contracts.
using (ServiceHost sh = new ServiceHost(typeof(TestService)), sh2 = new ServiceHost(typeof(MyService1)))
{
sh.Opened += delegate
{
Console.WriteLine("service is ready");
};
sh.Closed += delegate
{
Console.WriteLine("service is closed");
};
sh2.Opened += delegate
{
Console.WriteLine("service2 is ready...");
};
sh2.Closed += delegate
{
Console.WriteLine("Service is closed...");
};
sh.Open();
sh2.Open();
Console.ReadLine();
sh.Close();
sh2.Close();
}
Feel free to let me know if there is anything I can help with.
I know how can I read information from the config file in the activities by using C#
var servicesSection = (ClientSection)WebConfigurationManager.GetSection("system.serviceModel/client");
ChannelEndpointElement endpoint = servicesSection.Endpoints[0];
But when I try to read this information in the if statement of the workflow service, it doesn't work.
I tried the following code to read the endpoint information from the web.config file.
((ClientSection)WebConfigurationManager.GetSection("system.serviceModel/client")).Endpoints[0].toString().Equals("");
but it doesn't work.
some how, it doesn't understand the type casting and I can't convert the GetSection output to a clientSection object. do you know how can I do that in the if statement of the workflow service?( check something from the config file before calling some other activities)
I had similar requirement, after some prototyping, I managed following way. Hope this will help you and others.
/* Using in System.ServiceModel.dll */
using System.ServiceModel.Configuration;
using System.Web.Configuration;
/* Inside any method */
var clientSection = ((ClientSection)(WebConfigurationManager.GetSection("system.serviceModel/client")));
if (clientSection != null)
{
foreach (ChannelEndpointElement endPoint in clientSection.Endpoints)
{
..... endPoint.Name / endPoint.Address etc.
}
}
You can read any element from configuration and cast it to appropriate element type.
To read endpoint, binding and other sections from app.config, there are defined set of Section classes that help us read settings.
For example, to read list of bindings, we could simply use,
private void GetNetTcpBindingName()
{
Configuration appConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ServiceModelSectionGroup serviceModel = ServiceModelSectionGroup.GetSectionGroup(appConfig);
BindingsSection oBinding = serviceModel.Bindings;
List<BindingCollectionElement> bindingCollection = oBinding.BindingCollections;
NetTcpBindingCollectionElement netTCPBindingCollectionElement = (NetTcpBindingCollectionElement)bindingCollection.Where(obj => obj.BindingName.Equals("netTcpBinding")).SingleOrDefault();
if (netTCPBindingCollectionElement != null)
{
Console.WriteLine(netTCPBindingCollectionElement.ConfiguredBindings.ElementAt(0).Name);
}
}
Given the following app.config XML (section of interest in bold),
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ITrainerManagement" />
</basicHttpBinding>
**<netTcpBinding>
<binding name="NetTcpBinding_ILiveStream">
<security mode="None" />
</binding>
</netTcpBinding>**
</bindings>
<client>
<endpoint address="net.tcp://sever-pc/PST.TS.LiveStream/LiveStream.svc"
binding="netTcpBinding" bindingConfiguration="NetTcpBinding_ILiveStream"
contract="LiveStreamServiceReference.ILiveStream" name="NetTcpBinding_ILiveStream" />
<endpoint address="http://10.5.50.115/PST.TS.TrainerService/TrainerManagement.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ITrainerManagement"
contract="TrainerManagementServiceReference.ITrainerManagement"
name="BasicHttpBinding_ITrainerManagement" />
</client>
</system.serviceModel>
Hope this helps. This can be used to read any standard settings.
I successfully integrated the Caste WCF Facility with my services. Now I try to configure an HTTPS communication based on BasicHttpBinding.
According the following blog post, this should not be a big deal: http://blog.adnanmasood.com/2008/07/16/https-with-basichttpbinding-note-to-self/
Here's my setup. On client-side, I configure the Windsor container using the following code:
BasicHttpBinding clientBinding = new BasicHttpBinding();
// These two lines are the only thing I changed here to allow HTTPS
clientBinding.Security.Mode = BasicHttpSecurityMode.Transport;
clientBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
// Everything else worked well with HTTP
clientBinding.MaxReceivedMessageSize = 163840;
clientBinding.MaxBufferSize = (int)clientBinding.MaxReceivedMessageSize;
container = new WindsorContainer();
container.AddFacility<WcfFacility>();
container.Register(
Component.For<IClientService>()
.AsWcfClient(new DefaultClientModel {
Endpoint = WcfEndpoint.BoundTo(clientBinding)
.At(configuration.Get(CFGKEY_SERVICE_CLIENT))
})
);
Besides that, I don't have any configuration on client-side. This worked well using HTTP communication.
The server side got the following configuration within Web.config:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
When I'm trying to connect through https://, I get the following exception:
System.ServiceModel.EndpointNotFoundException: There was no endpoint listening at https://myuri.com/Services/Client.svc that could accept the message.
Any ideas what's missing?
Thank you in advance.
Fixed it by myself, the above code is correct, the problem has been located inside my Windsor service installer on server-side. The following snippet for each service point will do the job.
As you can see, I've put the absolute service URI as well as transport mode (either http or https) into the app settings section of the Web.config file. Of course it would be nice to use the default WCF configuration model but this did not work.
.Register(
Component
.For<MyNamespace.ContractInterface>()
.ImplementedBy<MyNamespace.ImplementationClass>()
.Named("ServiceName").LifestylePerWcfOperation()
.AsWcfService(
new DefaultServiceModel().Hosted().AddEndpoints(
WcfEndpoint.BoundTo(new BasicHttpBinding {
Security = new BasicHttpSecurity {
Mode = (WebConfigurationManager.AppSettings["Service.WCFFacility.TransportMode"] == "http") ? BasicHttpSecurityMode.None : BasicHttpSecurityMode.Transport,
Transport = new HttpTransportSecurity {
ClientCredentialType = HttpClientCredentialType.None
}
}
}).At(WebConfigurationManager.AppSettings["Service.WCFFacility.Endpoint"])
)
)
);
The server configuration remains as shown above, except the app setting keys.
Hope this might help someone experiencing similar problems.
I need to change my web.config file and add the MaxReceivedMessageSize property in
my web.config - but where?
The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="false"><assemblies><add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /></assemblies></compilation>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
You need to define a binding configuration for the binding you want to use and then you need to define your services (on the server-side) and clients (on the client side) to use that binding and binding configuration:
<system.serviceModel>
<bindings>
<!-- pick whichever binding you want .... -->
<basicHttpBinding>
<!-- binding configuration with a name -->
<binding name="ExtendedMaxSize"
maxBufferSize="999999" maxReceivedMessageSize="999999" />
</basicHttpBinding>
</bindings>
<services>
<service name="Yournamespace.YourServiceClass" behaviorConfiguration="...">
<!-- define endpoint with your binding and the name of the binding configuration
that you have defined just above -->
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="ExtendedMaxSize"
contract="Yournamespace.IYourServiceContract" />
</service>
</services>
To help those who may end up here like I did.
I cannot add to the comments above yet (Usually someone already has the answers long before I have the problem), so I have to add an answer.
I have an MVC 4 app, and I suspect the initial sample above is from the web.config of the actual WCF service project. One of the comments mentions they suspect it is an MVC 4 app and the default config settings.
But how do you fix the problem? From more research, it appears that the change actually needs to be made to the web.config for the CLIENT, in other words, the web config for the project with the REFERENCE to the WCF service. You will find it is much easier to make the change there. That version of the web.config will actually resemble what you are looking for.
That worked easily for me and fixed my issue.
No need, contrary to often claimed, to set on the server.
Contrary to what MSDN is saying, it is not enough to set the limit on the transport binding element. Need to set on binding itself too.
For example:
var targetBinding = new BasicHttpsBinding();
targetBinding.MaxReceivedMessageSize = MaxWcfMessageSize;
targetBinding.MaxBufferPoolSize = MaxWcfMessageSize;
targetBinding.MaxBufferSize = MaxWcfMessageSize;
var targetBindingElements = targetBinding.CreateBindingElements();
var httpsBindElement = targetBindingElements.Find<HttpsTransportBindingElement>();
httpsBindElement.MaxReceivedMessageSize = MaxWcfMessageSize;
httpsBindElement.MaxBufferPoolSize = MaxWcfMessageSize;
httpsBindElement.MaxBufferSize = MaxWcfMessageSize;
TextMessageEncodingBindingElement tmbebe = targetBindingElements.Find<TextMessageEncodingBindingElement>();
tmbebe.ReaderQuotas.MaxArrayLength = MaxWcfMessageSize;
I've got a hosted WCF service that I created a custom factory for, so that this would work with multiple host headers:
/// <summary>
/// Required for hosting where multiple host headers are present
/// </summary>
public class MultipleHostServiceFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
List<Uri> addresses = new List<Uri>();
addresses.Add(baseAddresses[0]);
return base.CreateServiceHost(serviceType, addresses.ToArray());
}
}
I'm pretty sure that my config files are now right, on both client and server (can be seen here).
The error I'm getting appears to be related to the factory:
Manual addressing is enabled on this factory, so all messages sent must be pre-addressed.
public string GetData(int value) {
return base.Channel.GetData(value);
}
The error occurs at line return base.Channel.GetData(value);.
I experienced this error and the problem was resolved by adding the WebHttpBehavior (line 2 below):
var factory = new ChannelFactory<IService>(new WebHttpBinding(), uri);
factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
var proxy = factory.CreateChannel();
I added a service reference as usual and got this error. Turns out all I had to do was to amend the client config to use an endpoint config with a behaviour specifing webhttp
<client>
<endpoint address="http://localhost:9000/GeoConverterService/GeoConverterService.svc"
binding="webHttpBinding"
contract="GeoConverter.IGeoConverterService"
behaviorConfiguration="webhttp"/>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="webhttp">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
I don't think this necessarily has anything to do with your factory.
See
http://msdn.microsoft.com/en-us/library/system.servicemodel.channels.transportbindingelement.manualaddressing.aspx
or others among the first few Bing hits for "manualaddressing". It sounds like the binding being used is incompatible with some other portion of the stack/messaging logic.
So this has finally come to an end!
Brian - thanks for your guidance on this. The bindings were mis-aligned b/t the client and server, and I finally ended up going with the following in both:
<basicHttpBinding>
<binding name="TransportSecurity">
<security mode="Transport">
<transport clientCredentialType="None"/>
</security>
</binding>
</basicHttpBinding>
... and setting their endpoint binding and bindingConfiguration attributes accordingly:
<endpoint binding="basicHttpBinding"
bindingConfiguration="TransportSecurity"
contract="ServiceReference1.IService"
name="WebHttpBinding_IService"
address="https://mysslserver.com/Service.svc" />
Since this is relatively new turf for me, just the explanation of why those errors were popping up lead me in the right direction :).