I am just getting started with WCF and would like to set up a distributable networked system as follows: (but am not sure if it is possible.)
I have a .net client that has business logic. It will need various data from various sources so I would like to add a 'server' that contains an in-memory cache but also WCF capabilities to send/receive and publish/subscribe from data sources for data that is not cached. I think it should be possible for these server applications to be identical in terms of code, but highly configurable so that requests could be dealt with in a peer to peer fashion, or traditional client-server as required. I think it could be done so that essentially a server sends a request to wherever it has the endpoint configured and gets a response.
Essentially a server would be configured as below:
Server A
========
Operation 1 - Endpoint I
Operation 2 - Endpoint II
Server B
========
Operation 1 - Endpoint IV
Operation 2 - Endpoint III
The configuration would be stored for each server in app.config and loaded into memory at startup. So each WCF operation would have its own WCF config (in terms of endpoints etc.) and it would send particular requests to different places according to that configuration.
From what I have read of WCF I think this is possible. I don't know have enough experience to know if this is a standard WCF pattern that I am describing (if so please let me know). Otherwise, my main question is, how do I programatically configure each operation (as above) in WCF?
Please let me know if I have not explained myself clearly.
Thanks in advance for any help,
Will
I don't know if this exactly will get you what you are looking for, but I this is what I use to add my WCF endpoints to my Windows Service. This is the code that the service runs to load all the wcf services:
IDictionary<string, ServiceHost> hosts;
NetTcpBinding binding;
CustomBinding mexBinding;
private void AddService(Type serviceImp, Type serviceDef, string serviceName)
{
ServiceHost host = new ServiceHost(serviceImp);
string address = String.Format(baseAddress, wcfPort, serviceName);
string endAdd = address;
string mexAdd = address + "/mex";
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
host.Description.Behaviors.Add(behavior);
host.AddServiceEndpoint(serviceDef, binding, endAdd);
host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding, mexAdd);
host.Open();
hosts.Add(serviceDef.Name, host);
}
There's a baseAddress string that I didn't copy in, but it just has the net.tcp address for the endpoint. Likewise for the wcfPort. Different baseAddresses and ports are used for debug, testing and production.
Just in case it isn't clear, serviceImp is the service implementation and serviceDef is the interface that defines the contract. Hope this helps.
EDIT - Here are some references I used to help me figure all of this stuff out:
Creating WCF Service Host Programmatically
Net.Tcp Port Sharing Sample, Part 2
Service Station: WCF Addressing In Depth
As far as I know you can't specify configuration on per operation basis. The lowest level is the interface level. The simplest (ugly) solution would be to put each operation in a separate interface.
Putting each operation in a separate interface is a valid and good design approach. Agatha Request/Response Layer follows this approach. Have a look at this and this is pretty useful and extensible
http://code.google.com/p/agatha-rrsl/
Related
In my .NET 6 ASP application I plan on exposing 3 types of network endpoints:
Razor pages-based web UI, listening either on a TCP port or Unix socket
Public gRPC API, listening on TCP or Unix socket as well
Server management gRPC API for internal use only, listening only on a Unix socket for security reasons
The public parts (Razor and public gRPC) could be used behind a reverse proxy, which is why I plan on allowing both TCP and Unix sockets (in case the proxy is on the same machine, but I can make them TCP-only). I also need to have them all in the same Host, so they can access the same services (same instances, not just services configured in similar ways), and so I can also register regular Hosted Services. Lastly, since both Microsoft and gRPC documentation say to not use Grpc.Core, I do not wish to use it, even though it would completely solve my problem.
Since it is not possible to have several WebHosts inside a single Generic Host, my best bet is now to rely on MapWhen to direct the requests depending on which endpoint received the connection. However, the only data I have for the MapWhen predicate idicates the connection origin using the IPAddress class (via HttpContext.Connection), which cannot represent a Unix socket, so I can't use it to reliably identify the connection source.
If I go the MapWhen route, the code would have the following structure:
public void KestrelSetupCallback(KestrelServerOptions options) {
// The two *Endpoint below are either IPEndpoint or UnixSocketEndpoint
// obtained from app configuration
options.Listen(razorEndpoint, o => o.Protocols = HttpProtocols.Http1AndHttp2);
options.Listen(apiEndpoint, o => o.Protocols = HttpProtocols.Http2);
options.ListenUnixSocket(managementSocketPath, o => o.Protocols = HttpProtocols.Http2);
}
public void WebappSetupCallback(IApplicationBuilder app) {
// Additional middlewares can be added here if needed by the predicates
app.MapWhen(RazorPredicate, RazorSetupCallback);
app.MapWhen(ApiPredicate, ApiSetupCallback);
app.MapWhen(ManagementPredicate, ManagementSetupCallback);
}
I have found about the ListenOptions.Use method , which I could call on each endpoint's ListenOptions to add a connection middleware that would set a Feature identifying the endpoint. I am not sure if that is possible though (if the IFeatureCollection is writable at this point), and I would like to know if there are other options.
Would this approach work, given my use case? Would I need to alter the code structure?
Aside from that, what approach could I take to implement the *Predicate methods?
Is there a better alternate method to achieve my use case? This feels very XYZ Problem to me and I'm afraid of missing a feature designed precisely for that.
There are ~3 ways to do this (there's more but lets start here):
Split the pipeline like you have above. Use MapWhen to determine what conditions should run which branch
You can use RequireHost on endpoints/controllers etc to determine which ones will get matched depending on the incoming host.
You can boot up multiple hosts in the same process and treat them like separate islands. They'll have separate config, DI, logging etc.
I've looked at a bunch of threads like Detect if wcf service is activated but these solutions require the client to proactively detect if the WCF service is running. But what if I am in the middle of a transaction and the WCF service goes down or the connection is lost for some reason? In my testing there is no exception thrown; either nothing happens at all or that twirly circle thing just keeps going round and round. I want the client to detect if the service/connection is lost and gracefully tell the user it's down. I have timeouts set in my code:
NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
binding.OpenTimeout = TimeSpan.FromSeconds(15);
binding.SendTimeout = TimeSpan.FromSeconds(3000);
binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
this._engineChannel = new DuplexChannelFactory<IEngineApi>(this, binding, new EndpointAddress("net.pipe://localhost/Engine"));
But if I am in the middle of a transaction nothing actually happens; these timeouts don't seem to affect anything.
You can use one of the two approaches:
1
The two things I do are a telnet check to make sure the WCF process
has the socket open.
telnet host 8080 The second thing I do is always add an IsAlive method
to my WCF contract so that there is a simple method to call to check
that the service host is operating correctly.
public bool IsAlive() {
return true; }
Source: Pinging WCF Services
2
Use the Discovery/Announcement feature introduced in WCF 4.0
Discovery depends on the User Datagram Protocol (UDP). UDP is a connectionless protocol, and there is no direct connection required between the client and server. The client usages UDP to broadcast finding requests for any endpoint supporting a specified contract type. The discovery endpoints that support this contract will receive the request. The implementation of the discovery endpoint responds back to the client with the address of the service endpoints. Once the client determines the services, it invokes the service to set up call.
Simple usage example: http://www.codeproject.com/Articles/469549/WCF-Discovery
I will be deploying my first application based on WCF and would like to know the best way to deploy. Here is my architecture. Please see the attached image.
We have a WCF written using 4.0 framework and has 3 methods. A front end ASP.NET website (www.site.com) calls the WCF to save data as well as read data. In figure method1 is saving to data and method2 and 3 are for reading the data from SQL server 2008 R2 database.
In my ASP.Net webstie...
I am calling the Method1 and closing the connection...like this..
ServiceClient client = new ServiceClient();
client.Method1(data to be saved)
client.close();
I am calling method 2 and 3 as follows
ServiceClient client = new ServiceClient();
dropDown1list.datasource = client.Method2()
dropDown2list.datasource = client.Method3()
client.close();
Multiple users could be using the website at the same time to submit the data. Considering this architecture..what would be the best way to deploy the WCF so that it could handle multiple users at same time?. I read the article http://www.codeproject.com/Articles/89858/WCF-Concurrency-Single-Multiple-and-Reentrant-and and http://www.codeproject.com/Articles/86007/ways-to-do-WCF-instance-management-Per-call-Per.
I now believe I need to have my WCF service as
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple , InstanceContextMode = InstanceContextMode.PerCall )]
public class Service : IService
{
public bool Method1(data to be saved)
{
}
public List<string> Method2()
{
}
public List<string> Method2()
{
}
}
Am I right ?. Any suggestions ?.
Just answered a similar question yesterday. Based on your description and the picture, I don't see a need to change your architecture. If you're using one of the main WCF bindings (webHttpBinding, wsHttpBinding or BasicHTTPBinding), the service you deploy should easily be able handle dozens of concurrent users, all saving and reading at the same time.
Each client request will generate its own connection and web service objects, each of which can communicate concurrently with your database, whether that request is to read data or write data. When the response is sent back to the client, your WCF service will destroy the objects and clean up the memory for you as long as you're not doing something strange.
I've spent the last two years working on WCF web services on and industrial scale. Lately I've been working on a load testing / benchmarking project that spins up hundreds of concurrent users, each of which is slamming our WCF test server with XML artifacts that get loaded into the database. We've managed to load up to 160 packages (about 110kb - each per client) per second. WCF is not perfect, but it's quick, clean and scales really well.
My experience has been that your database will be your bottleneck, not your WCF web service. If your client wants to scale this archtecture up to an Amazon size web service, then you bring in an F5 load balancer and scale it up that way.
I have encountered some troubles connecting some different machines of mine to a dedicated Windows service that runs WCF (a listener).
Those machines have an external modem connected to them, which probably disturb TCP connection to the outside, but the machines should establish a connection via WCF with the aforementioned listener service.
Since all the connection I need is inside my network (different machines tough), and it seems that Windows Sharing (copying files, etc') does work, (assuming I am not mistaking and it uses the SMB protocol), I would prefer, if possible, to use that protocol, or other protocols that don't run over TCP, to have a working connection.
I could not find on MSDN any help, but maybe someone has tried something like that before, or has encountered similar problems, and can help me with a solution to that.
Some code (pretty generic I guess):
private const string ServiceEndointURL = "net.tcp://localhost:6789/MyService";
used under: [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = true)]
public class MyApplication : IService
where IService is defined:
[ServiceContract(Name = "MyService")]
public interface IService
{
[OperationContract]
bool HandleMessage(Message message);
}
Other side ("client"):
string ListenerURL = "net.tcp://xx.xx.xx.xx:6789/MyService";
ChannelFactory<IService> factory = new ChannelFactory<IService>
(new MyNetBindings(), ListenerURL );
IService channel = l_oFactory.CreateChannel();
((IContextChannel)channel).OperationTimeout = new TimeSpan(0, 1, 1);
EDIT:
It seems that the external modem is taking the place of the default gateway on that machine, which is probably the cause for no connection.
When manually adding a persistent route, with the original gateway (with low metrics) - it seems to be working again. Any way to automatically work around it?
Thanks in advance!
So... SMB is not a transport protocol. SMB itself mostly uses TCP, but it can also run on UDP. If you want to use WCF over UDP you can (more info here), but i doubt that will work better for you.
My guess is that you have a specific problem with WCF or your network, and you should try to fix that instead.
I have a remote WCF web service that I'm connecting to from my application.
The application may be running on a server with multiple IP addresses (or multiple physical network interfaces)
I need to make sure that I can control which IP address is being used for the outbound request, instead of just using the 'preferred' interface as per the normal metric rules.
The reason for this is that multiple copies of the software will be running on the same machine, each bound to a specific IP address for its own operations, and the remote service being connected to needs to know which one is being used to connect back to it at a later time (since getting the address wrong means connecting to the wrong service)
With legacy ASMX services this is done by overriding GetWebRequest(Uri uri) on the partial class generated for the service. But I cannot figure out at all how to do this with WCF.
On an unrelated SO post, MVP #JohnSaunders suggested this may be possible by taking over the entire transport mechanism used by WCF. But I've not yet figured out how to do this either.
This is a tricky problem, which WCF doesn't seem to cater for particularly well.
The only component in the .NET framework that seems to directly deal with the issue of the client address is the ServicePoint class. Specifically, it has a BindIPEndPointDelegate property which lets you control how it selects the client IP. The documentation for the property includes this:
Some load balancing techniques require a client to use a specific local IP address and port number, rather than IPAddress.Any (or IPAddress.IPv6Any for Internet Protocol Version 6) and an ephemeral port. Your BindIPEndPointDelegate can satisfy this requirement.
Thus, you should be able to modify the service point associated with your URL in code like this:
var servicePoint = ServicePointManager.FindServicePoint(
new Uri("http://contoso.com/service.svc"));
servicePoint.BindIPEndPointDelegate =
(sp, remote, retryCount) => new IPEndPoint(address, portNumber);
Obviously this kind of code requires your classes have awareness of the protocol and the endpoint address the client will be communicating with. It would likely be most appropriate to set up this logic as a client behaviour which can be applied to your client channel.
you can use a message property (HttpRequestMessageProperty) to add HTTP headers to any outgoing requests. You need to create a "scope" in which the property will be added to the current "operation context", and attach the property, with all headers you want, to the outgoing message properties of the context.
please look at this :
how-to-override-getwebrequest-method-using-service-reference-instead-of-web-reference-in-wcf
Use this:
New Class:
using System.Web.Services.Protocols;
using System.Windows.Forms;
using System;
public static class ExtensionMethods
{
public static string ApplyServerURL(this SoapHttpClientProtocol service)
{
try
{
string name = service.GetType().Name;
return string.Format("{0}{1}.svc", Settings.Instance.ServerAddress, name);
}
catch
{ return string.Empty; }
}
}
And now is something like this:
YourService us = new YourService();
us.Url = us.ApplyServerURL();