My project is not finding the service reference endpoint in runtime. I believe it's due to incorrect injection in my Startup.cs. I'm new to the appsettings.json and Startup.cs method of configuration but have successfully scoped my class library and Dbcontext in the Startup.cs.
Note, if it makes a difference, this VS solution contains a class library and a .NET/angular2 web project. The call to the Service is initiated from angular website to the Web API, which calls methods on the class library where actual processing occurs.
The service reference "CybersourceTrxnProcessor" shows up in my class library project (see image) and ITransactionProcessor is exposed and accessible (i.e. code-hinting working perfectly). The web project DOES NOT have the service reference in the solution explorer.
When I added the reference, the sections were added to the app.config file (see below) and I copied them to the web.config in the web project.
How do I 'recreate' the web.config settings in the appsettings and Startup?
When attempting to process a test payment, this line of code throws an exception:
TransactionProcessorClient proc = new TransactionProcessorClient("ITransactionProcessor");
I have also tried defining the endpoint manually just prior but the same error results:
System.ServiceModel.EndpointAddress theendpoint = new System.ServiceModel.EndpointAddress("https://ics2wstesta.ic3.com/commerce/1.x/transactionProcessor");
TransactionProcessorClient proc = new TransactionProcessorClient("ITransactionProcessor", theendpoint);
This is the error:
An Exception occurred while trying to process your payment. Please try again. Could not find endpoint element with name 'ITransactionProcessor' and contract 'CybersourceTrxnProcessor.ITransactionProcessor' 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 name could be found in the client element.
Here is what config file looks like, generated when I added the service reference to the project in Visual Studio (and also matches what's in an older MVC project):
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="ITransactionProcessor">
<security mode="TransportWithMessageCredential" />
</binding>
<binding name="ITransactionProcessor1" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://ics2wstesta.ic3.com/commerce/1.x/transactionProcessor" binding="basicHttpBinding" bindingConfiguration="ITransactionProcessor"
contract="CybersourceTrxnProcessor.ITransactionProcessor" name="portXML" />
</client>
</system.serviceModel>
This is the appsettings.json:
"ITransactionProcessor": {
"security": { "mode": "TransportWithMessageCredential" },
"client": {
"endpoint": {
"address": "https://ics2wstesta.ic3.com/commerce/1.x/transactionProcessor",
"binding": "basicHttpBinding",
"bindingConfiguration": "ITransactionProcessor",
"contract": "CybersourceTrxnProcessor.ITransactionProcessor",
"name": "portXML"
}
}
}
This is what I have in Startup.cs (also need to set the Security mode to TransportWithMessageCredential as prescribed by Cybersource docs):
services.AddScoped<ITransactionProcessor>(provider => {
var client = new TransactionProcessorClient();
client.Endpoint.Address = new EndpointAddress(Configuration["ITransactionProcessor:client:endpoint:address"]);
client.Endpoint.Contract = new System.ServiceModel.Description.ContractDescription(Configuration["ITransactionProcessor:client:endpoint:contract"]);
client.Endpoint.Binding = new System.ServiceModel.BasicHttpBinding();
client.Endpoint.Name = "portXML";
return client;
});
Just FYI, I finally figured this out. Everything I had was correct except ONE tiny thing (doesn't it almost always come down to something simple). The error actually told me exactly what it needed. I simply needed to change my appsettings.json like so:
"name": "portXML"
to
"name": "ITransactionProcessor"
Related
I built a WCF service library and hosted it through a host application. Then I constructed a client application, but it seems that the address of the service host is hard coded in the client program. What if the host changes its address? Is it possible to write the client application so that the address of the host can be entered by the client at run time?
Yes, it's possible, if you write the WCF client proxy by hand, instead of generating it automatically with Visual Studio adding a service reference.
Let's start from this example (https://learn.microsoft.com/it-it/dotnet/framework/wcf/feature-details/how-to-use-the-channelfactory), just to understand how ChannelFactory works, and then modify it a little bit, adding the following function.
private ChannelFactory<IMath> _myChannelFactory;
// ...
private IMath GetChannel(string endpointConfigurationName, string endpointAddress)
{
if (_myChannelFactory == null)
{
this.DebugLog("Channel factory is null, creating new one");
if (String.IsNullOrEmpty(endpointAddress))
{
_myChannelFactory = new ChannelFactory<IMath>(endpointConfigurationName);
}
else
{
_myChannelFactory = new ChannelFactory<IMath>(endpointConfigurationName, new EndpointAddress(endpointAddress));
}
}
return _myChannelFactory.CreateChannel();
}
You can define the default server IP in the client App.config file
<system.serviceModel>
<!-- ... -->
<client>
<endpoint address="net.tcp://192.168.10.55:81/math/" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_IMath"
contract="MyNamespace.IMath" name="NetTcpBinding_IMath" />
</client>
</system.serviceModel>
In this way, when GetChannel("NetTcpBinding_IMath", "net.tcp://127.0.0.1:81/math") is called, it picks up the endpoint configuration from App.config file, replacing the default address (192.168.10.55) with the new one (127.0.0.1).
Some more documentation about ChannelFactory: https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.channelfactory-1.createchannel?view=netframework-4.8
I've created a service reference. When I try to give it a namespace of ServiceSoap, it generates the namespace as ServiceSoap.ServiceSoap, and, as a result, I get an app.config with this for the client section:
<client>
<endpoint address="http://blablabla/_mmwebext/mmwebext.dll?Soap"
binding="basicHttpBinding" bindingConfiguration="ServiceSoap"
contract="ServiceSoap.ServiceSoap" name="ServiceSoap" />
<endpoint address="http://blablabla/_mmwebext/mmwebext.dll?Soap"
binding="customBinding" bindingConfiguration="ServiceSoap12"
contract="ServiceSoap.ServiceSoap" name="ServiceSoap12" />
</client>
But when I try to instantiate a new ServiceSoapClient object, I'm getting back this error:
"Could not find endpoint element with name 'ServiceSoap' and contract 'ServiceSoap' 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 name could be found in the client element."
That's if I try it specifying the name and endpoint:
_mm = new ServiceSoapClient("ServiceSoap", endpoint.AbsoluteUri.ToString());
OR if I try it with a parameterless constructor:
_mm = new ServiceSoapClient();
It seems I need to be able to either: 1) pass in the contract name OR 2) force the service reference to stop adding the ServiceSoap. in front of the name I'm specifying as the name space. I am not seeing a way to do either of these (the constructor doesn't take a parameter for contract name and I don't see a way to stop the namespace creation from turning ServiceSoap to ServiceSoap.ServiceSoap.
The problem here was that this line:
[System.ServiceModel.ServiceContractAttribute(Namespace = "http://menandmice.com/webservices/", ConfigurationName = "ServiceSoap")]
Needed to be changed to:
[System.ServiceModel.ServiceContractAttribute(Namespace = "http://menandmice.com/webservices/", ConfigurationName = "ServiceSoap.ServiceSoap")]
So apparently "ConfigurationName" ACTUALLY means CONTRACT name, or at least needs to match the contract name in the app.config.
How do I find out what endpoint I should be requesting in order to trigger GetAccounts?
I've got two applications running on my local cluster:
The fabric/Service is a web api application with the following configuration:
internal sealed class Web : StatelessService
{
public Web(StatelessServiceContext context)
: base(context)
{
}
/// <summary>
/// Optional override to create listeners (like tcp, http) for this service instance.
/// </summary>
/// <returns>The collection of listeners.</returns>
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[]
{
new ServiceInstanceListener(serviceContext => new OwinCommunicationListener(Startup.ConfigureApp,
serviceContext, ServiceEventSource.Current, "ServiceEndpoint"))
};
}
}
The startup is configured like so:
public static class Startup
{
// This code configures Web API. The Startup class is specified as a type
// parameter in the WebApp.Start method.
public static void ConfigureApp(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
var config = new HttpConfiguration();
//config.Routes.MapHttpRoute(
// name: "DefaultApi",
// routeTemplate: "api/{controller}/{id}",
// defaults: new { id = RouteParameter.Optional }
//);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
config.MapHttpAttributeRoutes();
var container = new UnityContainer();
container.RegisterType<IAccountService, AccountService>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);
appBuilder.UseWebApi(config);
}
}
And finally the service manifest:
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="WebPkg"
Version="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceTypes>
<!-- This is the name of your ServiceType.
This name must match the string used in RegisterServiceType call in Program.cs. -->
<StatelessServiceType ServiceTypeName="WebType" />
</ServiceTypes>
<!-- Code package is your service executable. -->
<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>removed...........Accounts.Web.exe</Program>
<WorkingFolder>CodePackage</WorkingFolder>
</ExeHost>
</EntryPoint>
</CodePackage>
<!-- Config package is the contents of the Config directoy under PackageRoot that contains an
independently-updateable and versioned set of custom configuration settings for your service. -->
<ConfigPackage Name="Config" Version="1.0.0" />
<Resources>
<Endpoints>
<!-- This endpoint is used by the communication listener to obtain the port on which to
listen. Please note that if your service is partitioned, this port is shared with
replicas of different partitions that are placed in your code. -->
<Endpoint Protocol="http" Name="ServiceEndpoint" Type="Input" />
</Endpoints>
</Resources>
</ServiceManifest>
And my controller:
[HttpGet]
[Route("accounts", Name = "GetAccounts")]
public async Task<IHttpActionResult> GetAccounts(){//dostuff}
How do I find out what endpoint I should be requesting in order to trigger GetAccounts?
Service Fabric provides a built-in reverse proxy. It is enabled by default in your local development cluster. A reverse proxy allows you to use dynamic ports (as shown in your .gif). When using a reverse proxy you can call your service with the port number of your reverse proxy (19081 by default). The address format in your use case, a stateless service with a singleton partition, is:
protocol://clusterAddress:reverseProxyPort/applicationName/serviceName
In your example the service would be called with:
http://clusterAddress:19081/Service/Web/api/controller/accounts/GetAccounts
I suppose this Web Api is exposed to the outside world?
The stateless service you are using to host it in has dynamic port enabled.
For external facing services it is best to give it a fixed port.
In the service manifest file you can add the port number in the endpoint definition:
<Endpoint Protocol="http" Name="ServiceEndpoint" Type="Input" Port="80">
See See this link for more info.
Once you have the portnumber you can access the web api at http://localhost:80/api/[controller]/accounts
You can then lookup the actual port number in the explorer, whether you are using dynamic ports or not.
To see the endpoint port number browse to a node beneath your service like this:
(See the endpoint at the right side?)
Note that if the endpoint contains the ip of a specific node, you need the ip or FQDN of the cluster. But for now it seems ok since you are using localhost.
In Service Fabric, a service runs somewhere in a Service Fabric cluster, typically distributed across multiple VMs. It can be moved from one place to another, either by the service owner, or automatically by Service Fabric. Services are not statically tied to a particular machine or address.
A Service Fabric application is generally composed of many different services, where each service performs a specialized task. These services may communicate with each other to form a complete function, such as rendering different parts of a web application. There are also client applications that connect to and communicate with services.
For example, in order to accept external traffic on port 80, the following things must be configured:
Write a service that listens on port 80. Configure port 80 in the service's ServiceManifest.xml and open a listener in the service, for example, a self-hosted web server.
XML
<Resources>
<Endpoints>
<Endpoint Name="WebEndpoint" Protocol="http" Port="80" />
</Endpoints>
</Resources>
C#
class HttpCommunicationListener : ICommunicationListener
{
...
public Task<string> OpenAsync(CancellationToken cancellationToken)
{
EndpointResourceDescription endpoint =
serviceContext.CodePackageActivationContext.GetEndpoint("WebEndpoint");
string uriPrefix = $"{endpoint.Protocol}://+:{endpoint.Port}/myapp/";
this.httpListener = new HttpListener();
this.httpListener.Prefixes.Add(uriPrefix);
this.httpListener.Start();
string publishUri = uriPrefix.Replace("+", FabricRuntime.GetNodeContext().IPAddressOrFQDN);
return Task.FromResult(publishUri);
}
...
}
class WebService : StatelessService
{
...
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[] { new ServiceInstanceListener(context => new HttpCommunicationListener(context))};
}
...
}
This document discusses how to set up communication with and between services in Service Fabric:
Connect and communicate with services in Service Fabric
I tried this question in a generic way on this post: https://stackoverflow.com/q/18968846/147637
But that did not get us to the result.
Soooo, here it is concretely!
I have the code below. It works. In VS, you add a web reference, code up the below, and then.... start fiddling the app.config.
And it works.
But I need to get rid of the app config. It is a problem that crucial pieces of the code are not in the.... code. It is hard to document, and easy for folks looking at this example to forget to look in the app config (this is an example for other devs).
So the question is: How do I move the contents of app.config into code?
(I am a part part part time coder. Pointing me at generic documentation won't get me there, sorry to say!)
**// .cs file:**
using myNameSpace.joesWebService.WebAPI.SOAP;
namespace myNameSpace
{
class Program
{
static void Main(string[] args)
{
// create the SOAP client
joesWebServerClient server = new joesWebServerClient();
string payloadXML = Loadpayload(filename);
// Run the SOAP transaction
string response = server.WebProcessShipment(string.Format("{0}#{1}", Username, Password), payloadXML);
=================================================
**app.config**
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<!-- Some non default stuff has been added by hand here -->
<binding name="IjoesWebServerbinding" maxBufferSize="256000000" maxReceivedMessageSize="256000000" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://joesWebServer/soap/IEntryPoint"
binding="basicHttpBinding" bindingConfiguration="IjoesWebServerbinding"
contract="myNameSpace.joesWebService.WebAPI.SOAP.IjoesWebServer"
name="IjoesWebServerSOAP" />
</client>
</system.serviceModel>
</configuration>
Generally speaking, a config file is preferred over hard-coding the settings because all you need to do with a config file is change the values you want to change and then restart the application. If they're hardcoded, you have to modify the source, recompile and redeploy.
Having said that, you can pretty much do everything in code that you do in the config file for WCF (I seem to recall a few exceptions, but don't remember them off hand).
One way to achieve what you're looking for is to define the binding in your code and create the client via ChannelFactory<T>, where T is the interface for your service (more accurately the service contract, which is usually in an interface and then implemented by a class).
For example:
using System.ServiceModel;
using myNameSpace.joesWebService.WebAPI.SOAP;
namespace myNameSpace
{
class Program
{
static void Main(string[] args)
{
// Create the binding
BasicHttpBinding myBinding = new BasicHttpBinding();
myBinding.MaxBufferSize = 256000000;
myBinding.MaxReceivedMessageSize = 256000000;
// Create the Channel Factory
ChannelFactory<IjoesWebServer> factory =
new ChannelFactory<IjoesWebServer>(myBinding, "http://joesWebServer/soap/IEntryPoint");
// Create, use and close the client
IjoesWebService client = null;
string payloadXML = Loadpayload(filename);
string response;
try
{
client = factory.CreateChannel();
((IClientChannel)client).Open();
response = client.WebProcessShipment(string.Format("{0}#{1}", Username, Password), payloadXML);
((IClientChannel)client).Close();
}
catch (Exception ex)
{
((ICientChannel)client).Abort();
// Do something with the error (ex.Message) here
}
}
}
Now you don't need a config file. The additional settings you had in the example are now in the code.
The advantage of ChannelFactory<T> is that once you create an instance of the factory, you can generate new channels (think of them as clients) at will by calling CreateChannel(). This will speed things up as most of your overhead will be in the creation of the factory.
An additional note - you're using I<name> in a lot of places in your config file. I usually denotes an interface, and if a full time developer were to look at your project it might be a little confusing for them at first glance.
With WCF 4.5, if you add a static config method to your WCF service class, then it will load automatically and ignore what's in app.config file.
<ServiceContract()>
Public Interface IWCFService
<OperationContract()>
Function GetData(ByVal value As Integer) As String
<OperationContract()>
Function GetDataUsingDataContract(ByVal composite As CompositeType) As CompositeType
End Interface
Public Class WCFService
Implements IWCFService
Public Shared Function CreateClient() As Object
End Function
Public Shared Sub Configure(config As ServiceConfiguration)
'Define service endpoint
config.AddServiceEndpoint(GetType(IWCFService), _
New NetNamedPipeBinding, _
New Uri("net.pipe://localhost/WCFService"))
'Define service behaviors
Dim myServiceBehaviors As New Description.ServiceDebugBehavior With {.IncludeExceptionDetailInFaults = True}
config.Description.Behaviors.Add(myServiceBehaviors)
End Sub
Public Function GetData(ByVal value As Integer) As String Implements IWCFService.GetData
Return String.Format("You entered: {0}", value)
End Function
Public Function GetDataUsingDataContract(ByVal composite As CompositeType) As CompositeType Implements IWCFService.GetDataUsingDataContract
End Function
End Class
I'm still looking into how to do the same for the client. I'll try to update when I figure it out if there's any interest.
I have added a WCF service reference to Silverlight application and here's what the binding from web.config that I have looks like
<bindings>
<wsDualHttpBinding>
<binding name="wsDualHttpBinding">
<security mode="None" />
</binding>
</wsDualHttpBinding>
<pollingDuplexHttpBinding>
<binding name="multipleMessagesPerPollPollingDuplexHttpBinding"
duplexMode="MultipleMessagesPerPoll" />
</pollingDuplexHttpBinding>
</bindings>
And I have this snippet to create a service client instance
var serviceClient = new DuplexCallerIdServiceClient(
new PollingDuplexHttpBinding(PollingDuplexMode.MultipleMessagesPerPoll),
new EndpointAddress("http://localhost:51445/Service/MyService.svc"));
My concern is that why do I have to provide an absolute url in code. I have a winforms application that uses the same service and I can just do new DuplexCallerIdServiceClient() to create a service client instance which seems ideal. Is there any way I can work around it. I cannot change the binding settings.
Thanks
You do not have to hardcode the service URL. Replace the hard coded string that either is passed in as an argument or makes a function call (or gets some object's property) to populate the constructor with a valid service URL.
Here's one way among many:
var serviceClient = new DuplexCallerIdServiceClient(
new PollingDuplexHttpBinding(PollingDuplexMode.MultipleMessagesPerPoll),
new EndpointAddress(Info.Instance.ServiceURL));
Where Info is a singleton object, Instance gets the singleton's instance and ServiceUrl is a string property that comes from... wherever. Database, config file, hard coded to start etc...
P.S. Careful with the Singleton pattern, but as config info entities they can be very useful.