WCF Visual Studio 2017 local development - c#

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.

Related

Problem consuming IIS hosted service - locally and remotely

I build 2 services for the project I'm currently working on.
1) a simple client-server request service using basicHttpBinding
2) a callback dataservice using WSDualHttpBinding
with some twiddling I was able to host both in IIS and I can visit both :/Service.svc in my browser locally as well as on another machine that shall interact with both services as well.
here is the app-config section for the callback dataservice
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="myBehavior">
<clientVia />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsDualHttpBinding>
<binding name="WSDualHttpBinding_IDataService">
<security mode="None">
<message negotiateServiceCredential="false" clientCredentialType="None" />
</security>
</binding>
</wsDualHttpBinding>
</bindings>
<client>
<endpoint address="http://<address>:50111/Service.svc/service"
binding="wsDualHttpBinding" bindingConfiguration="WSDualHttpBinding_IDataService" behaviorConfiguration="myBehavior"
contract="CallbackService.IDataService" name="WSDualHttpBinding_IDataService">
</endpoint>
</client>
</system.serviceModel>
the basicHttpBinding looks quite similar - only to another port
I set IIS to anonymous authentication .. User IUSR, no password ..
Originally it worked locally - but from the remote computer I received authentication errors .. so I tweaked some more - and now I get a timeout even locally .. though Visual Studio interacts with the service reference to both services just fine.
How do I get the service "to cooperate" again
And I'm at my wits end - any help greatly appreciated!?
Edit: first problem solved - local communication restored
public ClientCallbacks myCallbacks=new ClientCallbacks();
public DuplexChannelFactory<IDataService> channelFactory { get; set; }
public IDataService channel;
public int connect() {
WSDualHttpBinding binding = new WSDualHttpBinding()
{ Name = "WSDualHttpBinding_IDataService" };
this.channelFactory = new DuplexChannelFactory<IDataService>(
mainForm.dbCommunication.myCallbacks,
binding,
new EndpointAddress("http://<address>:50111/Service.svc/service"));
this.channel = this.channelFactory.CreateChannel();
...
...
channel.AddListener(int-parameter); // announce new callback-partner
...
...
this.newListe = this.myCallbacks.receivedTaskList; //get Tasklist over callback
This works locally - but calling it remotely I get a "new" error
System.ServiceModel.Security.SecurityNegotiationException:
The caller was not authenticated by the service. --->
System.ServiceModel.FaultException: The request for security token could not be satisfied
because authentication failed.
at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault
(Message message, EndpointAddress target)
at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody
(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
--- End of inner exception stack trace ---
And again I struggle to find a clue / solution .. I set IIS authentication to anonymous ... but it seems the communication is blocked before that point is reached .. What do I have to do to reach the service from a remote machine?
It seems that we should provide the credentials to have the client authenticated by the server-side. which sort of credentials should be supplied relies on the binding configuration of the server-side.
By default. The security mode of the WsDualhttpbinding is configured with Message security mode, and the client credential type is Windows.
These below code snippets are equivalent.
WSDualHttpBinding binding = new WSDualHttpBinding();
WSDualHttpBinding binding = new WSDualHttpBinding();
binding.Security.Mode = WSDualHttpSecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
From your code snippet, I didn’t see your configuration of the server-side. If Windows credential is required, please consider the below code segments.
//ChannelFactory<IService> channelFactory = new ChannelFactory(...);
channelFactory.Credentials.Windows.ClientCredential.UserName = "administrator";
channelFactory.Credentials.Windows.ClientCredential.Password = "123456";
IService svc = channelFactory.CreateChannel();
Feel free to let me know if the problem still exists.

WCF channelfactory with settings from the config file?

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.

Timeout error when accessing WCF service remotely

When I access the WCF service locally it works. To do this I type into my browser: http://localhost:54123/MyService/GetValue
This shows my expected json formatted output. However, when accessing remotely using http://myIPAddress:54123/MyService/GetValue I get ERR_CONNECTION_TIMED_OUT in Chrome.
I have my inbound IP whitelisted for all TCP ports, so I am not sure why I would be unable to access remotely. This is being hosted on an amazon EC2 instance if that makes any difference.
Here is the code I have in my main() method for hosting the service via Topshelf
const string serviceUri = "http://localhost:54123/MyService";
var host = HostFactory.New(configurator =>
{
configurator.Service<WcfServiceWrapper<MyServiceClass, IMyServiceClass>>(serviceConfigurator =>
{
serviceConfigurator.ConstructUsing(x =>
new WcfServiceWrapper<MyServiceClass, IMyServiceClass>("MyService", serviceUri));
serviceConfigurator.WhenStarted(service => service.Start());
serviceConfigurator.WhenStopped(service => service.Stop());
});
configurator.RunAsLocalSystem();
configurator.SetDescription("Runs My Service.");
configurator.SetDisplayName("MyService");
configurator.SetServiceName("MyService");
});
Here is the relevant code from my WcfWrapper start() method
var webHttpBinding = new WebHttpBinding(WebHttpSecurityMode.None);
_serviceHost.AddServiceEndpoint(typeof(TServiceContract), webHttpBinding, _serviceUri);
var webHttpBehavior = new WebHttpBehavior
{
DefaultOutgoingResponseFormat = WebMessageFormat.Json
};
_serviceHost.Description.Endpoints[0].Behaviors.Add(webHttpBehavior);
_serviceHost.Open();
openSucceeded = true;
Below is what I have in my config file
<configuration>
<system.serviceModel>
<services>
<service name="MyServiceClassNS.MyServiceClass">
<host>
<baseAddresses>
<add baseAddress="http://myIPAddress:54123/MyService"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata httpGetEnabled="True"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Most likely cause of the problem is that the firewall is blocking the call.

Castle Windsor WCF Facility HTTPS

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.

WCF 4 and NetTcpBinding

I am testing out the no configuration features of WCF 4.
I have built a simple service and deployed it to IIS. The service is deployed as a svc file
The client config is empty:
<configuration>
<system.serviceModel>
<bindings />
<client />
</system.serviceModel>
</configuration>
The config on the web server is:
<system.web>
<compilation debug="true" targetFramework="4.0" />
</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>
This code works fine:
BasicHttpBinding myBinding = new BasicHttpBinding();
EndpointAddress myEndpoint = new EndpointAddress("http://localhost/Service1.svc");
ChannelFactory<IService1> myChannelFactory = new ChannelFactory<IService1>(myBinding, myEndpoint);
IService1 wcfClient1 = myChannelFactory.CreateChannel();
int z = wcfClient1.Multiply(composite);
This code does not:
NetTcpBinding myBinding = new NetTcpBinding();
EndpointAddress myEndpoint = new EndpointAddress("net.tcp://localhost:808/Service1.svc");
ChannelFactory<IService1> myChannelFactory = new ChannelFactory<IService1>(myBinding, myEndpoint);
IService1 wcfClient1 = myChannelFactory.CreateChannel();
int z = wcfClient1.Multiply(composite);
The error that I get is:
Could not connect to
net.tcp://localhost/Service1.svc. The
connection attempt lasted for a time
span of 00:00:02.1041204. TCP error
code 10061: No connection could be
made because the target machine
actively refused it 127.0.0.1:808.
The net.tcp binding is set on the default web site.
I have a feeling that there is something simple that I am missing. Anyone have any ideas?
I get the feeling that your Net.Tcp Port Sharing Service is not enabled.
Found the problem. Although net.tcp looked like it was installed for the Default web site it needed to be activated using this command:
appcmd.exe set app "Default Web Site/" /enabledProtocols:http,net.tcp

Categories

Resources