I need to create a RESTful webservice in C#. This is what I have right now:
namespace WebService
{
[ServiceContract]
public interface IService
{
[OperationContract(Name="Add")]
[WebGet(UriTemplate = "/")]
int Add();
}
public class Service:IService
{
public int Add()
{
// do some calculations and return result
return res;
}
}
}
Now, my question is How do i host this service at a location say (http://localhost/TestService) and how can i test the service in console application client?
To host the service, WebServiceHost would be a better option. Check out this article http://msdn.microsoft.com/en-us/library/system.servicemodel.web.webservicehost.aspx on its usage.
To test the service in a console application, you can use the HTTPWebRequest/HttpWebResponse classes to make the requests/decipher response. Check out this for the usage http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.aspx
For simple requests you could also use fiddler (in the prototyping phase of your project perhaps)
See this answer here to get you started.
All you have to do is run this as a console app having the console run this code in Main method/
RestExample exampleService = new RestExample();
host = new ServiceHost(exampleService);
host.Open();
Be sure to throw in a
Console.WriteLine("Press any key to exit . . . .");
Console.ReadKey(true);
host.Close();
This way your service stays open until you are done.
Keep in mind you don't have to start it up as a rest service to test the implementation of the interface. In the RestExample, you can write unit test against that class to make sure each method performs as designed. Starting it as a console is an option, but to me is more integration testing rather than unit testing.
Related
I want to be able to use the TopShelf debugging abilities of my service in Visual Studio.
A lot of the examples and documentation out there refer to creating a Windows Console project in Visual Studio first, and then adding TopShelf, OWIN, etc
However, in my case I already have a perfectly good and working Windows Service project called QShipsService.sln, etc... and it uses a simple Connected Service (admittedly to old SOAP legacy services).
Can someone please direct me or provide an example of how to use TopShelf, with an existing non-Console like project?
I found my own solution...
The assumption I made was the default Windows Service project defaulting to wanting to register the program as a service and kick off the OnOpen() and OnClose() methods, once the service is running.
In my case I wanted to re-use an existing service that was based on a Timer(), and it would kick in every 4 hours to call a SOAP call and return some data. What I didn't realise was the ServiceConfigurator was trying to call its own Open() and Close() methods.
So I commented out the OnOpen and OnClose methods and allowed the configurator to call my worker process via Open() method instead, which is what I was meant to have done the first time!
For the noobs out there like me, here is the code...
//using System.ServiceProcess;
using Topshelf;
namespace QShipsService
{
static class Program
{
static void Main(string[] args)
{
HostFactory.Run(
configure =>
{
configure.Service<QShipsService.QshipsService>(
service =>
{
service.ConstructUsing(s => new QShipsService.QshipsService());
service.WhenStarted(s => s.QStart());
service.WhenStopped(s => s.QStop());
});
//Setup Account that window service use to run.
configure.RunAsLocalSystem();
//add details and names about the service
configure.SetServiceName("QshipsService");
configure.SetDisplayName("QshipsService");
configure.SetDescription("QshipsService Windows Service to extract data from the QSHIPS SOAP service. Data is recorded and maintained inside the SPOT's database in POT-DB.");
});
//## USE THIS IF WE'RE NOT USING TOPSHELF !! ##
// //this loads and starts the QshipsService (see QshipsService.cs program)
// ServiceBase[] ServicesToRun;
// ServicesToRun = new ServiceBase[]
// {
// new QShipsService.QshipsService()
// };
// ServiceBase.Run(ServicesToRun);
}
}
}
I'm learning SF, and am now trying to build a console client to a stateless service.
tried to follow instructions here: https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-connect-and-communicate-with-services
and added this to my StatelessService class
public interface IMyService : IService
{
Task<string> HelloWorldAsync();
}
and a simple implementation
public Task<string> HelloWorldAsync()
{
return Task.FromResult("HELLO FROM SERVICE!");
}
The rest is unchanged.
In my Console app I have
IMyService helloWorldClient = ServiceProxy.Create<IMyService>(
new Uri("fabric:/RestGateway/StatelessGateway1"));
string message = await helloWorldClient.HelloWorldAsync();
The service deployed to my local cluster and seems to work fine (green button) but I get an Exception when calling helloWorldClient.HelloWorldAsync().
Any idea how I can fix this?
Don't forget to add a communication listener to your service like this:
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
yield return new ServiceInstanceListener(ServiceRemotingExtensions.CreateServiceRemotingListener(this, Context));
}
Note:
The call to CreateServiceRemotingListener creates a specific communication listener that can be used only from within the cluster. So, when talking from your dev machine to a service running on your dev cluster this will work. You can't talk to services running on different machines like this.
To access your cluster from the outside, you could use ServiceBus, WCF or OWIN for instance. (or something you build yourself)
So I've read all of the other SO questions with regard to this but it looks like everything should be set up properly. I'm running a self-hosted SignalR console application that has a single empty Hub declaration (this is for one-way communication from the server to connected clients).
EDIT: Link to project which reproduces the issue - https://dl.dropboxusercontent.com/u/66477739/SignalRTest.zip
The demo solution has 3 projects:
SignalRTest.BusinessLogic - Simple console app that just publishes a message every 10 seconds. These messages are never received by the client. This simulates my business logic layer pushing notifications to connected clients.
SignalRTest.SelfHost - The self-hosted SignalR application + hub. I added a "Send" method to the hub, only to show that everything works when a client calls server.send('message'); but it doesn't work when the server initiates the message. Typing in this console window and hitting enter will send that text to the web app. THIS WORKS!
SignalRTest.UI - Just a blank MVC app with a single page that loads the necessary scripts. A successfully received message would just log console output, there will never be any UI changes on this sample.
PublishStatusHub.cs
public class PublishStatusHub : Hub
{
}
JS
$(document).ready(function () {
$.connection.hub.url = "http://localhost:8080/signalr";
var publish = $.connection.publishStatusHub;
publish.client.addMessage = function (message) {
console.log(message);
};
$.connection.hub.start().done(function() { });
});
Calling code from a separate assembly
var context = GlobalHost.ConnectionManager.GetHubContext<PublishStatusHub>();
context.Clients.All.addMessage("Starting publish...");
Although I can see that the above code is executed (via breakpoints), the client method is never called.
I did turn on SignalR logging and no exceptions are being thrown. Also, it's never logging the incoming messages.
Interestingly enough, I can get messages sent to the client through my Main() method in Program.cs within the self-hosted console app:
Program.cs
static void Main(string[] args)
{
string url = "http://localhost:8080";
using (WebApp.Start(url))
{
Console.WriteLine("Server running on {0}", url);
while (true)
{
string message = Console.ReadLine();
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<PublishStatusHub>();
hubContext.Clients.All.addMessage(message);
}
}
}
Now, if I type in the console window and hit enter, the message is sent successfully to the client and it's logged in the console output window.
HOWEVER, if I move this code to another file within the same console application and then call it from another assembly (which is the end goal), it fails silently again:
MessagePublisher.cs
public class MessagePublisher
{
public void Publish(string message)
{
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<PublishStatusHub>();
hubContext.Clients.All.addMessage(message);
}
}
Calling code from a separate assembly
var messagePublisher = new MessagePublisher();
messagePublisher.Publish("Test message");
I'm stuck on why I can't publish messages to connected clients from external assemblies.
In the project you posted, you are trying to publish a message from completely different console application (SignalRTest.BusinessLogic). This application does not host SignalR service, and so has no idea about connected clients. All clients are connected to the service hosted by another application (SinglaRTest.SelfHost) in another process. So there is no way how this could work. If you will start your service in BusinessLogic application, then this will work as usual.
I guess you've created a Constructor, you could remove it...
This question is a followon from my previous where I have since discovered that it is not working 100% on my computer: WCF service not running on non-dev machine
I'm working through this example:
http://msdn.microsoft.com/en-us/library/ff649818.aspx
It turns out that the InstallUtil step isn't really working here. I've discovered that if VS2010 has the project open, and you go to add a service reference like in step 8 of the tutorial, VS2010 actually starts up the service host and therefore a reference is created.
Here's how i've debugged so far:
Install the service as per InstallUtil, close down VS2010 solution; then open a completely different solution (TESTWCF) Try and add a service reference and it fails - cannot find at the specified address
Open WCFServiceLibrary1 project again as a separate instance of VS2010. Try and add a service reference to TESTWCF and it fails.
Within WCFServiceLibrary1, attempt step 8 - add a service reference. This causes the service host to start and the service is found.
With service host still running, in TESTWCF I then try and add service and it works.
Close down the service host and try and add reference in TESTWCF and it doesn't work again.
This all seems to be totally independant of the service running or not running as installed by InstallUtil.
I've also verified this through the creation of a new virtual server from scratch and loading things on one by one. And only when VS2010 was installed did it start to work - when I observed above.
Any ideas ?
WCF services can be self-hosted in an application (such as a console or a Windows Forms application)
I think you are over complicating it, you don't have to even install it with InstallUtil.
InstallUtil installs it to run as windows service, and you can make console application which will be serving as WCF service.
You have to import:
System.ServiceModel
System.ServiceModel.Web
System.Web.Services
I think those with web will be needed if you want to use it as web service with get and post.
Then you need to specify contract for client and server.
[ServiceContract(Name = "SomeService", Namespace = "http://some.domain.com/some/someservice",SessionMode=SessionMode.Required)]
public interface ISomeService
{
[OperationContract]
string Execute(string expression);
}
You have contract and now you have to implement it in service. nothing special in there just use this interface.
What is very important is app.config, you have to specify it well for client and for service. In config you have all stuff that points to service.
In client you have to add service as reference, it should find it as in point 8 but only if you have configs ok!
In client just do something in code like that:
using (ChannelFactory<ISomeService> channel = new ChannelFactory<ISomeService>("SomeService"))
{
ISomeService svc = channel.CreateChannel();
svc.Execute("my expression to evaluate by the service");
}
Try to make it easiest possible way without InstallUtil and such, it doesn't have to be windows service to serve stuff over network.
Success ! After like 4 days of effort on this, the MSDN tutorial has a fatal flaw.
In the first step of the tutorial you create a wcf service library and by default it names the service Service1. In step 2.6 of the tutorial you are asked to specify the base address:
net.tcp://localhost:8523/Service1
Step 3 you are asked to create a new windows service, and by default this is also called Service1.
In step 5.2 you are asked to make a reference to System.ServiceModel and to WcfServiceLibrary1.
In step 5.6 you replace the Onstart Method to start the service and, Step 8 shows the final code as being:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.ServiceModel;
using WcfServiceLibrary1;
namespace WindowsService1
{
public partial class Service1: ServiceBase
{
internal static ServiceHost myServiceHost = null;
public WCFServiceHost1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (myServiceHost != null)
{
myServiceHost.Close();
}
myServiceHost = new ServiceHost(typeof(Service1));
myServiceHost.Open();
}
protected override void OnStop()
{
if (myServiceHost != null)
{
myServiceHost.Close();
myServiceHost = null;
}
}
}
}
The crucial line of code which is wrong is:
myServiceHost = new ServiceHost(typeof(Service1));
Well it might behave differently in VS2008 or 2005 or maybe it's a config in VS2010 however, my VS2010 interprets Service1 to be that of the containing class ie:
WindowsService1.Service1
Whereas it should in fact be:
WcfServiceLibrary1.Service1
I noticed that 4 days ago but figured I didn't know enough about WCF and I was wrong somehow - esp when it appeared to work 'cause of VS2010 starting it up itself.
I have a console application I wrote in C# that polls multiple devices, collects some data, and stores the information on a database. The application runs on our web server, and I was wondering how to invoke a method call from the command console (so I can exec a command from php that will be read by the console application, a shell command would work as well).
Anyone got any ideas? I've been floating around 'the google' and have found nothing that will supply my current needs.
Also, i'm not adverse to making changes to the console application if an overhaul is needed there. Please, if your answer is COM Interop, provide a GOOD example of how I would build and call this from PHP / Apache2.
You could create a Service like this:
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(
Method = "GET",
UriTemplate = "/magic")]
void MagicMethod();
}
And a service implementation like this:
public class Service : IService
{
public void MagicMethod()
{
//magic here
}
}
to start a HTTP Service it should look like this:
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri("http://127.0.0.1:8080"))
ServiceEndpoint ep = host.AddServiceEndpoint(typeof(IService), new WebHttpBinding(), "");
ServiceDebugBehavior stp = host.Description.Behaviors.Find<ServiceDebugBehavior>();
stp.HttpHelpPageEnabled = false;
host.Open();
This will start a HTTP server on port 8080.
Then you can make a HTTP Get request to 'http://localhost:8080/magic' to invoke the method call.
Perhaps your console app can poll a directory for a certain file, and react to that.
Then your php app will only need to create that file and the console app should notice it and do whatever you want. I'm not really sure what you want to do.
I would look at using WCF. Your C# application would host a WCF service and then your PHP application could call into it, I believe PHP5 comes with a SOAP library which should make this relatively simple. Any other application you write will be able to easily call in to, especially if they're written in .NET.
I imagine COM would work fine, but I like the scalability of WCF, as if you have to end up moving these applications onto separate servers then you wouldn't need to change anything besides a URL.
There's a good example on this blog. If you're using PHP5 it should be a doddle, if you're having to use 4 then it will still be possible but will require just a bit more legwork.