Configure endpoint programmatically - c#

First of, let me start by saying I have no experience of doing endpoint/networking type of stuff, so my question might seem a bit stupid, so please bear with me :)
I am working on porting an App written for Windows Phone 7 onto Windows 8 (Metro app). In the original App, there was a ServicesReference.ClientConfig file which defined the URL, bindings and other bits for the App to connect to the server (addresses changed):
<client>
<endpoint address="https://someurl.com/userservice.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_UserServices"
contract="UserApi.UserServices" name="User_Services" />
<endpoint address="https://someurel.com/dataservice.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_DataServices"
contract="DataApi.DataServices" name="Data_Services" />
Also, in the WP7 project, there were already two "Service References" added (one for User Services and one for Data Services), with supporting Reference.cs generated files. When I tried adding the service references into the Win8 project (VS2012), it generated a blank reference.cs file, so I simply added the Reference.cs file from the WP7 project into the W8 project, and also copied the ServiceReferences.ClientConfig file into the W8 project (so in terms of directory structure, it looked identical to the WP7 project).
I think these Reference.cs files are the ones which provide the interface for the contracts
Now, when I run my W8 app, I get an error during the part where it needs access to the service:
InvalidOperationException was unhandled by user code
Could not find endpoint element with name
'User_Services' and contract
'UserApi.UserServices' 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.
So I figured the App isn't using the ServicesReferces.ClientConfig file to pickup the endpoints and network adresses, or it wasn't finding the Reference.cs files which I have importes into the project. So, assuming first it is not finding the endpoints correctly through the ServicesReferences.ClientConfig file, is it possible to do the same in code?
All I got so far is this:
BasicHttpBinding binding = new BasicHttpBinding();
EndpointAddress endpoint = new EndpointAddress(new Uri("https://someurl.com/someservice.svc"));
but I don't how to take this further (I added this into App.xaml.cs)
Hope the question makes sense. If there is any further information you need, please let me know and I will try to find out about it while I go and educate myself more on this endpoint business
Thanks in advance

I had the same problem and I tried to wrap everything in some classes.. This is what I did:
First of all, I created a class called ClientService where it creates and wraps the EndpointAdress:
EDIT for Win8:
public class ClientService
{
public Type ContractType {get;set;}
public EndpointAdress EndpointAdress {get;set;}
public Binding Binding { get; private set; }
public ClientService(Type contractType)
{
ContractType = contractType;
CreateEndpoint();
CreateBinding();
}
private void CreateEndpoint()
{
EndpointAdress = new EndpointAddress(....) //You can get some parameters about the service adress in the Constructor of this class
}
private void CreateBinding()
{
Binding = new BasicHttpBinding(); //Or your specific Binding
}
}
Once I have this, I create a static class with all my client registrations. I add all of them once I start my app. Something like this:
ClientServices.AddClientService(new ClientService(typeof(IYourService));
public static class ClientServices
{
private static readonly Dictionary<Type, ClientService> _clientServices;
static ClientServices()
{
_clientServices = new Dictionary<Type, ClientService>();
}
public static void AddClientService(ClientService clientService)
{
if (!_clientServices.ContainsKey(clientService.ContractType))
{
_clientServices.Add(clientService.ContractType, clientService);
}
}
public static ClientService GetClientServiceBy(Type contract)
{
if (_clientServices.ContainsKey(contract))
{
return _clientServices[contract];
}
throw new ArgumentException(string.Format("The contract's Type {0} is not registered. Please register the client's endpoint.", contract));
}
}
So, when my application starts I have all my client endpoints registered in a static class. Now when I want to call a service I have a wrapper called ServiceInvoker. I use it like this whenever I want to call a Service:
var invoker = new ServiceInvoker();
var result = invoker.InvokeService<IMyService, MyObjectReturnType>(
proxy => proxy.DoSomething(myParameters));
return result;
Where InvokeService looks like this:
public TResult InvokeService<TServiceContract, TResult>(Func<TServiceContract, TResult> invokeHandler) where TServiceContract : class
{
ICommunicationObject communicationObject;
var arg = CreateCommunicationObject<TServiceContract>(out communicationObject);
var result = default(TResult);
try
{
result = invokeHandler(arg);
}
catch (Exception ex)
{
throw;
}
finally
{
try
{
if (communicationObject.State != CommunicationState.Faulted)
communicationObject.Close();
}
catch
{
communicationObject.Abort();
}
}
return result;
}
private TServiceContract CreateCommunicationObject<TServiceContract>(out ICommunicationObject communicationObject)
where TServiceContract : class
{
var clientService = GetClientService(typeof(TServiceContract));
var arg = new ChannelFactory<TServiceContract>(clientService.Binding, clientService.EndpointAdress).CreateChannel();
communicationObject = (ICommunicationObject)arg;
return arg;
}
private ClientService GetClientService(Type type)
{
var clientService = ClientServices.GetClientServiceBy(type);
return clientService;
}
The main problem here is that since DLL's cannot be referenced in a Windows Store App, the only way to make this example work is to copy all Service Interfaces and possible ojects that we transfer to a Class Library (Windows Store apps). This way we will be able to create a channel and connect to the WCF service.
Copying the interfaces can be a workaround but is NOT a good approach.
Service Reference or other code generation tools are the way to go.
In addition, async and await are not possible in this scenario.
**ServiceInvoker apprach is used from creating WCF ChannelFactory

Related

generate webservice in Visual Studio with a dynamic root URL

I want to create a C# application that communicates with two SOAP webservices. These webservices (WSDL files) use the same url
<root>/...dirPath.../dms.cfc?wsdl
<root>/...dirPath.../cobra.cfc?wsdl
<root> should be dynamic because the application user has to set this variable.
First of all I took this
How can I dynamically switch web service addresses in .NET without a recompile?
and tried this
https://www.codeproject.com/Articles/12317/How-to-make-your-Web-Reference-proxy-URL-dynamic
Further I found this link
https://learn.microsoft.com/en-us/sql/reporting-services/report-server-web-service/net-framework/setting-the-url-property-of-the-web-service?view=sql-server-2017
but these links didn't help I can't find the settings URL behaviour and I can't access the URL property by code.
I created a static class that should handle both webservices. The user is able to change the webservice root url.
An example URL would be
http://localhost:8500/CoBRA/...dirPath.../dms.cfc?wsdl
or
http://myInstance.com/CoBRA/...dirPath.../dms.cfc?wsdl
handled by this code
public static class CoBRAService
{
private static cobraClient cobraBaseClient = new cobraClient();
private static dmsClient cobraDmsClient = new dmsClient();
public static void SetWebserviceRootUrl(string rootUrl)
{
// cobraBaseClient.url = $"{rootUrl}/path/dms.cfc?wsdl";
// cobraDmsClient.url = $"{rootUrl}/path/cobra.cfc?wsdl";
}
}
Both webservices don't inherit from System.Web.Services.Protocols.SoapHttpClientProtocol they implement this public partial class cobraClient : System.ServiceModel.ClientBase<MyProject.CoBRA_Base.cobra>, MyProject.CoBRA_Base.cobra
This is my project structure
Where can I set the webservice url or how can I access the url property?
if your "CoBRA_BaseClient" and "CoBRA_DMSClient" inherited from System.ServiceModel.ClientBase< TChannel >
then you can try the following:
public static CoBRA_BaseClient CreateService()
{
CoBRA_BaseClient service = new CoBRA_BaseClient();
service.Endpoint.Address = new EndpointAddress("uri");
return service;
}
public static CoBRA_DMSClient CreateService()
{
CoBRA_DMSClient service = new CoBRA_DMSClient();
service.Endpoint.Address = new EndpointAddress("uri");
return service;
}

WCF ServiceHosts, ServiceEndpoints and Bindings

I currently am running some WCF REST services in a Windows Service (not IIS), using the WebServiceHost. I have a separate interface and class defined for each service, but I'm having some issues understanding how WebServiceHost, ServiceEndpoint and ServiceContracts can be used together to create a selfhosted solution.
The way that I currently set things up is that I create a new WebServiceHost for each class which implements a service and use the name of the class as part of the URI but then define the rest of the URI in the interface.
[ServiceContract]
public interface IEventsService
{
[System.ServiceModel.OperationContract]
[System.ServiceModel.Web.WebGet(UriTemplate = "EventType", ResponseFormat=WebMessageFormat.Json)]
List<EventType> GetEventTypes();
[System.ServiceModel.OperationContract]
[System.ServiceModel.Web.WebGet(UriTemplate = "Event")]
System.IO.Stream GetEventsAsStream();
}
public class EventsService: IEventsService
{
public List<EventType> GetEventTypes() { //code in here }
public System.IO.Stream GetEventsAsStream() { // code in here }
}
The code to create the services looks like this:
Type t = typeof(EventService);
Type interface = typeof(IEventService);
Uri newUri = new Uri(baseUri, "Events");
WebServicesHost host = new WebServiceHost(t, newUri);
Binding binding = New WebHttpBinding();
ServiceEndpoint ep = host.AddServiceEndpoint(interface, binding, newUri);
This works well and the service endpoint for each service is created at an appropriate url.
http://XXX.YYY.ZZZ:portnum/Events/EventType
http://XXX.YYY.ZZZ:portnum/Events/Event
I then repeat for another service interface and service class. I would like to remove the Events in the Url though but if I do that and create multiple WebServiceHosts with the same base URL I get the error:
The ChannelDispatcher at 'http://localhost:8085/' with contract(s) '"IOtherService"' is unable to open its IChannelListener
with the internal Exception of:
"A registration already exists for URI 'http://localhost:8085/'."
I'm trying to understand how the WebServiceHost, ServiceEndpoint and ServiceContract work together to create the ChannelListener.
Do I need a separate WebServiceHost for each class which implements a service? I don't see a way to register multiple types with a single WebServiceHost
Secondly, I'm passing in the interface to the AddServceEndpoint method and I assume that method checks the object for all of the OperationContract members and adds them, the problem is how does the WebServiceHost know which class should map to which interface.
What I would love would be an example of creating a WCF self hosted service which runs multiple services while keeping the interface and the implementation classes separate.
Sounds to me like the problem that you are having is you are trying to register more than one service on the same service URI. This will not work, as you have noticed, each service must have a unique endpoint.
Unique By
IP
Domain
Port Number
Full URL
Examples
http://someserver/foo -> IFoo Service
http://someserver/bar -> IBar Service
http://somedomain -> IFoo Service
http://someotherdomain -> IBar Service
http://somedomain:1 -> IFoo Service
http://somedomain:2 -> IBar Service
You get the idea.
So to directly address your question, if you want more than once service to be at the root url for you site, you will have to put them on different ports. So you could modify your code to be something like
public class PortNumberAttribute : Attribute
{
public int PortNumber { get; set; }
public PortNumberAttribute(int port)
{
PortNumber = port;
}
}
[PortNumber(8085)]
public interface IEventsService
{
//service methods etc
}
string baseUri = "http://foo.com:{0}";
Type iface = typeof(IEventsService);
PortNumberAttribute pNumber = (PortNumberAttribute)iface.GetCustomAttribute(typeof(PortNumberAttribute));
Uri newUri = new Uri(string.Format(baseUri, pNumber.PortNumber));
//create host and all that
I think it might be useful for you to re-think about your URI approach. Uri is a unique resource identifier.
Each your endpoint says that you try to expose outside a different kind of resource it's "Events" and "OtherResource". Thus you need to change your UriTemplates a bit.
I would make it so:
[ServiceContract]
public interface IEventTypesService
{
[OperationContract]
[WebGet(UriTemplate = "", ResponseFormat=WebMessageFormat.Json)]
IList<EventType> GetEventTypes();
[OperationContract]
[WebGet(UriTemplate = "{id}")]
EventType GetEventType(string id);
}
[ServiceContract]
public interface IEventsService
{
[OperationContract]
[WebGet(UriTemplate = "")]
Stream GetEventsAsStream();
[OperationContract]
[WebGet(UriTemplate = "{id}")]
Event GetEvent(string id);
}
public class EventsService: IEventsService, IEventTypesService
{
public IList<EventType> GetEventTypes() { //code in here }
public EventType GetEventType(string id) { //code in here }
public Stream GetEventsAsStream() { // code in here }
public EventType GetEventType(string id) { // code in here }
}
Type t = typeof(EventService);
Type interface1 = typeof(IEventsService);
Type interface2 = typeof(IEventTypesService);
var baseUri = new Uri("http://localhost");
Uri eventsUri= new Uri(baseUri, "Events");
Uri eventTypesUri= new Uri(baseUri, "EventTypes");
WebServicesHost host = new WebServiceHost(t, baseUri);
Binding binding = New WebHttpBinding();
host.AddServiceEndpoint(interface1, binding, eventsUri);
host.AddServiceEndpoint(interface2, binding, eventTypesUri);
And yes, you are right - you have to have different addresses, but it's really different resources. To understand it better you can refer: RESTful API Design, best-practices-for-a-pragmatic-restful-api
To finish, there is a way to use the same address, but the approach a bit weird:
Using the same address
The following solution:
allows a single object to handle a specific endpoint
no part of the path is in the URI template
uses the same port for all of the services
It does requires more than one WebServiceHost - one per object that handles requests. Another difficulty is that adding deeper endpoints (like /events/2014) means they either need to have unique parameters or the URI template must include part of the path, if you go convention over configuration that shouldn't be a problem.
A WebServiceHost can only host one thing (class) but that object can have multiple interfaces to handle multiple different types of requests on different URLs. How can different WebServiceHosts bind to the same domain:port? They can't so I guess WebServiceHost wraps an underlying static object that routes requests to the right object. This doesn't technically answer your question but I think this implementation allows you to do what you want right?
A console app that hosts the web services.
public class Program
{
static void Main (string[] args)
{
var venueHost = new WebServiceHost (typeof (Venues));
venueHost.AddServiceEndpoint (typeof (IVenues), new WebHttpBinding (), "http://localhost:12345/venues");
venueHost.Open ();
var eventHost = new WebServiceHost (typeof (Events));
eventHost.AddServiceEndpoint (typeof (IEvents), new WebHttpBinding (), "http://localhost:12345/events");
eventHost.Open ();
while (true)
{
var k = Console.ReadKey ();
if (k.KeyChar == 'q' || k.KeyChar == 'Q')
break;
}
}
}
The Venues class implements IVenues and handles any requests to http://localhost:12345/venues/
[ServiceContract]
public interface IVenues
{
[WebInvoke (Method = "GET", UriTemplate = "?id={id}")]
string GetVenues (string id);
}
public class Venues : IVenues
{
public string GetVenues (string id)
{
return "This would contain venue data.";
}
}
The Events class implements IEvents and handles any requests to http://localhost:12345/events/
[ServiceContract]
public interface IEvents
{
[WebInvoke (Method = "GET", UriTemplate = "?venue={venue}")]
string GetEvents (string venue);
}
public class Events : IEvents
{
public string GetEvents (string venue)
{
return "This would contain event data.";
}
}
WCF self hosting can be done in many ways like Console application hosting, Windows service hosting, etc.
I had tried to host two services using a single console application. The structure of the services was similar to what you mentioned, that is, separate classes and interfaces for both the services.
You might want to have a look at this link:
Hosting two WCf services using one console app

WCF Client - Best Practise

I just wanted to hear your Opinion about a WCF Client implementation.
I have an Server that provides several services like SecurityManager.
This service is defined in the Interface ISecurityManager and implemented in the Class SecurityManager.
So far everything is fine.
On the Client side I want to implement the services calls via a seperated Class. My question is whether I do this also in an SecurityManager Class which implements the same ISecurityManager Interface?
What is here the best practise?
Visual Studio Generator
You can ask Visual Studio to build a client for you, right-clicking your client project and adding a Service Reference. There's a dialog where you can either type your service url or discover it from within the solution.
Creating a Client
You can build the client class inheriting from ClientBase<ISecurityManager>, ISecurityManager. Being an operation example on this client class:
public void ExampleMethod(int id)
{
Channel.ExampleMethod(id);
}
Like a real man does
Or without any client class, just calling it:
ServiceInvokerinvoker invoker = new ServiceInvoker();
var result = invoker.InvokeService<ISecurityManager, ReturnType>( proxy => proxy.ExampleMethod(1) );
Last two options assuming you already have configured the ISecurityManager client:
<client>
<endpoint name="ServiceName"
address="http://ServiceName.test/Service"
binding="basicHttpBinding"
contract="ISecurityManager" />
</client>
I would suggest to use a generic Wrapper make WCF calls.
So everytime you need to make a WCF call you can do it like this:
var invoker = new ServiceInvoker();
var result = invoker.InvokeService<ISecurityManager, MyObjectReturnType>(
proxy => proxy.DoSomething(myParameters));
return result;
I use ServiceInvoker to create the channel and manage any exceptions that would occur inside. It creates a channel with the ISecurityManager contract, with return type of MyObjectReturnType and with the action DoSomething (method from ISecurityManager contract).
You can use this solution with all your interfaces, without any extra class implementation!
InvokeService would be something like this:
public TResult InvokeService<TServiceContract, TResult>(Func<TServiceContract, TResult> invokeHandler) where TServiceContract : class
{
ICommunicationObject communicationObject;
var arg = CreateCommunicationObject<TServiceContract>(out communicationObject);
var result = default(TResult);
try
{
result = invokeHandler(arg);
}
catch (Exception ex)
{
Logger.Log(ex);
throw;
}
finally
{
try
{
if (communicationObject.State != CommunicationState.Faulted)
communicationObject.Close();
}
catch
{
communicationObject.Abort();
}
}
return result;
}
private TServiceContract CreateCommunicationObject<TServiceContract>(out ICommunicationObject communicationObject)
where TServiceContract : class
{
//Create the Channel
// ICommunicationObject is an out parameter for disposing purposes
return channel;
}
To implement the service calls "via a separate class" you can just generate a service reference using svcutil.exe or visual studio from a running instance of your service.
This will generate a set of types off your service contract which will be completely "separate" from the assemblies containing your service contract and implementation.

Async WCF: wait for another call

We have an old Silverlight UserControl + WCF component in our framework and we would like to increase the reusability of this feature. The component should work with basic functionality by default, but we would like to extend it based on the current project (without modifying the original, so more of this control can appear in the full system with different functionality).
So we made a plan, where everything looks great, except one thing. Here is a short summary:
Silverlight UserControl can be extended and manipulated via ContentPresenter at the UI and ViewModel inheritance, events and messaging in the client logic.
Back-end business logic can be manipulated with module loading.
This gonna be okay I think. For example you can disable/remove fields from the UI with overriden ViewModel properties, and at the back-end you can avoid some action with custom modules.
The interesting part is when you add new fields via the ContentPresenter. Ok, you add new properties to the inherited ViewModel, then you can bind to them. You have the additional data. When you save base data, you know it's succeeded, then you can start saving your additional data (additional data can be anything, in a different table at back-end for example). Fine, we extended our UserControl and the back-end logic and the original userControl still doesn't know anything about our extension.
But we lost transaction. For example we can save base data, but additional data saving throws an exception, we have the updated base data but nothing in the additional table. We really doesn't want this possibility, so I came up with this idea:
One WCF call should wait for the other at the back-end, and if both arrived, we can begin cross thread communication between them, and of course, we can handle the base and the additional data in the same transaction, and the base component still doesn't know anything about the other (it just provide a feature to do something with it, but it doesn't know who gonna do it).
I made a very simplified proof of concept solution, this is the output:
1 send begins
Press return to send the second piece
2 send begins
2 send completed, returned: 1
1 send completed, returned: 2
Service
namespace MyService
{
[ServiceContract]
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service1
{
protected bool _sameArrived;
protected Piece _same;
[OperationContract]
public Piece SendPiece(Piece piece)
{
_sameArrived = false;
Mediator.Instance.WaitFor(piece, sameArrived);
while (!_sameArrived)
{
Thread.Sleep(100);
}
return _same;
}
protected void sameArrived(Piece piece)
{
_same = piece;
_sameArrived = true;
}
}
}
Piece (entity)
namespace MyService
{
[DataContract]
public class Piece
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string SameIdentifier { get; set; }
}
}
Mediator
namespace MyService
{
public sealed class Mediator
{
private static Mediator _instance;
private static object syncRoot = new Object();
private List<Tuple<Piece, Action<Piece>>> _waitsFor;
private Mediator()
{
_waitsFor = new List<Tuple<Piece, Action<Piece>>>();
}
public static Mediator Instance
{
get
{
if (_instance == null)
{
lock (syncRoot)
{
_instance = new Mediator();
}
}
return _instance;
}
}
public void WaitFor(Piece piece, Action<Piece> callback)
{
lock (_waitsFor)
{
var waiter = _waitsFor.Where(i => i.Item1.SameIdentifier == piece.SameIdentifier).FirstOrDefault();
if (waiter != null)
{
_waitsFor.Remove(waiter);
waiter.Item2(piece);
callback(waiter.Item1);
}
else
{
_waitsFor.Add(new Tuple<Piece, Action<Piece>>(piece, callback));
}
}
}
}
}
And the client side code
namespace MyClient
{
class Program
{
static void Main(string[] args)
{
Client c1 = new Client(new Piece()
{
ID = 1,
SameIdentifier = "customIdentifier"
});
Client c2 = new Client(new Piece()
{
ID = 2,
SameIdentifier = "customIdentifier"
});
c1.SendPiece();
Console.WriteLine("Press return to send the second piece");
Console.ReadLine();
c2.SendPiece();
Console.ReadLine();
}
}
class Client
{
protected Piece _piece;
protected Service1Client _service;
public Client(Piece piece)
{
_piece = piece;
_service = new Service1Client();
}
public void SendPiece()
{
Console.WriteLine("{0} send begins", _piece.ID);
_service.BeginSendPiece(_piece, new AsyncCallback(sendPieceCallback), null);
}
protected void sendPieceCallback(IAsyncResult result)
{
Piece returnedPiece = _service.EndSendPiece(result);
Console.WriteLine("{0} send completed, returned: {1}", _piece.ID, returnedPiece.ID);
}
}
}
So is it a good idea to wait for another WCF call (which may or may not be invoked, so in a real example it would be more complex), and process them together with cross threading communication? Or not and I should look for another solution?
Thanks in advance,
negra
If you want to extend your application without changing any existing code, you can use MEF that is Microsoft Extensibility Framework.
For using MEF with silverlight see: http://development-guides.silverbaylabs.org/Video/Silverlight-MEF
I would not wait for 2 WCF calls from Silverlight, for the following reasons:
You are making your code more complex and less maintainable
You are storing business knowledge, that two services should be called together, in the client
I would call a single service that aggreagated the two services.
It doesn't feel like a great idea to me, to be honest. I think it would be neater if you could package up both "partial" requests in a single "full" request, and wait for that. Unfortunately I don't know the best way of doing that within WCF. It's possible that there's a generalized mechanism for this, but I don't know about it. Basically you'd need some loosely typed service layer where you could represent a generalized request and a generalized response, routing the requests appropriately in the server. You could then represent a collection of requests and responses easily.
That's the approach I'd look at, personally - but I don't know how neatly it will turn out in WCF.

.Net Remoting to WCF Challenge!

I am trying to migrate my .net remoting code to wcf but I'm finding it difficult. Can someone help me migrate this simple Remoting based program below to use WCF? The program implements a simple publisher/subscriber pattern where we have a single TemperatureProviderProgram that publishers to many TemperatureSubcriberPrograms that subcribe to the TemperatureProvider.
To run the programs:
Copy the TemperatureProviderProgram and TemperatureSubcriberProgram into seperate console application projects.
Copying to remaining classes and interfaces into a common Class Library project then add a reference to System.Runtime.Remoting library
Add a reference to the Class Library project from the console app projects.
Complie and run 1 TemperatureProviderProgram and multiple TemperatureSubcriberProgram.
Please note no IIS or xml should be used. Thanks in advance.
public interface ITemperatureProvider
{
void Subcribe(ObjRef temperatureSubcriber);
}
[Serializable]
public sealed class TemperatureProvider : MarshalByRefObject, ITemperatureProvider
{
private readonly List<ITemperatureSubcriber> _temperatureSubcribers = new List<ITemperatureSubcriber>();
private readonly Random randomTemperature = new Random();
public void Subcribe(ObjRef temperatureSubcriber)
{
ITemperatureSubcriber tempSubcriber = (ITemperatureSubcriber)RemotingServices.Unmarshal(temperatureSubcriber);
lock (_temperatureSubcribers)
{
_temperatureSubcribers.Add(tempSubcriber);
}
}
public void Start()
{
Console.WriteLine("TemperatureProvider started...");
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
TcpServerChannel tcpChannel = new TcpServerChannel("TemperatureProviderChannel", 5001, provider);
ChannelServices.RegisterChannel(tcpChannel, false);
RemotingServices.Marshal(this, "TemperatureProvider", typeof(ITemperatureProvider));
while (true)
{
double nextTemp = randomTemperature.NextDouble();
lock (_temperatureSubcribers)
{
foreach (var item in _temperatureSubcribers)
{
try
{
item.OnTemperature(nextTemp);
}
catch (SocketException)
{}
catch(RemotingException)
{}
}
}
Thread.Sleep(200);
}
}
}
public interface ITemperatureSubcriber
{
void OnTemperature(double temperature);
}
[Serializable]
public sealed class TemperatureSubcriber : MarshalByRefObject, ITemperatureSubcriber
{
private ObjRef _clientRef;
private readonly Random portGen = new Random();
public void OnTemperature(double temperature)
{
Console.WriteLine(temperature);
}
public override object InitializeLifetimeService()
{
return null;
}
public void Start()
{
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
int port = portGen.Next(1, 65535);
TcpServerChannel tcpChannel = new TcpServerChannel(string.Format("TemperatureSubcriber_{0}", Guid.NewGuid()), port, provider);
ChannelServices.RegisterChannel(tcpChannel, false);
ITemperatureProvider p1 = (ITemperatureProvider)RemotingServices.Connect(typeof(ITemperatureProvider), "tcp://localhost:5001/TemperatureProvider");
_clientRef = RemotingServices.Marshal(this, string.Format("TemperatureSubcriber_{0}_{1}.rem", Environment.MachineName, Guid.NewGuid()));
p1.Subcribe(_clientRef);
}
}
public class TemperatureProviderProgram
{
static void Main(string[] args)
{
TemperatureProvider tp = new TemperatureProvider();
tp.Start();
}
}
public class TemperatureSubcriberProgram
{
static void Main(string[] args)
{
Console.WriteLine("Press any key to start TemperatureSubcriber.");
Console.ReadLine();
TemperatureSubcriber ts = new TemperatureSubcriber();
ts.Start();
Console.ReadLine();
}
}
In WCF, with a "push" from the server you're really talking about duplex comms; the MarshalByRefObject is largely redundant here (AFAIK). The page here discusses various scenarios, including duplex/callbacks.
If the issue is xml (for some philosophical reason), then simply using NetDataContractSerializer rather than DataContractSerializer might help.
The other approach is to have the clients "pull" data periodically; this works well if you need to support basic http, etc.
What it sounds like you want to do is use WCF NetTcpBinding with Callbacks.
Take a look at this: http://www.codeproject.com/KB/WCF/publisher_subscriber.aspx
"Learning WCF" by Michele Bustamante is also very good. You can get Chpt1 for VS2008 at her website along with the code for the book. Chpt1 will explain/demo setting up connections and such. She also has downloadable sample code. One of the Samples is a DuplexPublishSubscribe.
You will need to modify your logic a bit. If you want to migrate this app to WCF. You will need to have clients pull data from the service at regular intervals.
You will also need a Windows service or application to host the WCF like the console you are using in the previous code.
Well I build real time systems so polling is not an option - I need to push data.
Also I am finding there is no WCF equivalent of System.Runtime.Remoting.ObjRef! This is an extremely useful type that encapsulates a service endpoint and can be serialise and passed around the network to other remoting service.
Think I’ll be sticking with good old remoting until the ObjRef equivalent is introduced.
Yes it is true, just one correction..
ObjRefs are created automatically when any MarshalByRefObject derived object is going outside the appdomain.
So in this case your ITemperatureProvider interface Subscribe method shoud take ITemperatureSubscriber instead of objref.
And then on client side just call p1.Subscribe(this) and the remoting layer will generate ObjRef from the object that will be serialized and sent. (sending b reference)

Categories