I’m pretty new to programming, so bear with me if my question isn’t specific enough. Right now I’m trying to make a simple Client Logon to my server. So the server App knows which users are connected. When a client connects I want an event to fire on the server that update the userlist. But it doesn’t and I can’t figure out why. Hope you can help.
In the codes I have removed how the users should be displayed in the serverApp. Right now I just need the event to work.
In my Service Library:
INetworkService contract:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace NetworkLib
{
[ServiceContract]
public interface INetworkService
{
[OperationContract]
void Logon(UserInfo userInfo);
[OperationContract]
void Logout();
}
}
NetworkService Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace NetworkLib
{
public class NetworkService : INetworkService
{
public event EventHandler UserListChanged;
public void Logon(UserInfo userInfo)
{
OnUserListChanged();
}
public void Logout()
{
OnUserListChanged();
}
private void OnUserListChanged()
{
var handler = UserListChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
}
UserInfo Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
namespace NetworkLib
{
[DataContract]
public class UserInfo
{
[DataMember]
public string Name;
}
}
In my ServerApp (WPF):
using System.ServiceModel;
using NetworkLib;
namespace ServerApp
{
public partial class MainWindow : Window
{
NetworkService networkService;
public MainWindow()
{
InitializeComponent();
ServiceHost host = new ServiceHost(typeof(NetworkService));
host.Open();
networkService = new NetworkService();
networkService.UserListChanged += networkService_UserListChanged;
}
private void networkService_UserListChanged(object sender, EventArgs e)
{
MessageBox.Show("It Works!");
}
}
}
In my ClientApp (WPF): (Have made a Service Reference to the Server)
namespace ClientApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
ServiceReference.NetworkServiceClient proxy = new ServiceReference.NetworkServiceClient();
ServiceReference.UserInfo userInfo = new ServiceReference.UserInfo();
userInfo.Name = "Test";
proxy.Logon(userInfo);
}
}
}
You subscribe to event of other NetworkService instance than ServiceHost instantiates. In your case every time you make request to server, new NetworkService instance is created. Place the following attribute above NetworkService class:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
Then subscribe to event:
var serviceInstance = (NetworkService)host.SingletonInstance;
serviceInstance.UserListChanged += networkService_UserListChanged;
When creating your ServiceHost, you should provide NetworkService instance instead of typeof(NetworkService)
ServiceHost host = new ServiceHost(networkService);
You need to initialize it first, of course.
I didnt look in great detail but overall your code looks ok. It is sometimes useful in this situation to run 2 instances of VisualStudio - one for server in debug and one for client in debug. If you put a break point in client button1_Click code in VS thats debugging client, and a break point in NetworkServices.Logout in VS thats debugging server you will be able to step from client to server code and see easily whats going wrong where.
Why do you need an event model here? Why not just handle your "event" directly in NetworkService.Logout(). Does pushing this off to an event and then having to wire that event (which as ilya.dofofeev correctly points out is not on the same object) provide any real benefit?
Related
I am loading a .NET application using the ICLRRuntimeHost::ExecuteInDefaultAppDomain method which starts the application up fine. However, the application is using Log4Net with which it should output an info message after starting up, but it never does. It works fine when opening it up normally though, so it must be something with the CLR Hosting environment that makes it unable to function properly, I just don't know what exactly.
Here is the code that does the logging:
using System;
using System.Windows.Forms;
using log4net;
namespace TestApplication
{
public partial class MainForm : Form
{
private static readonly ILog log = LogManager.GetLogger
(MethodBase.GetCurrentMethod().DeclaringType);
public MainForm()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
log.Info("Application has started");
}
}
}
I'm using ServiceModelEx WCF library from Juval Lowy's "Programming WCF Services". I'm trying to implement a Publish-Subscribe Service with publisher and subscriber. What I have done so far is the publisher and the discover-publish service.
Service Contract:
[ServiceContract]
interface IMyEvents
{
[OperationContract(IsOneWay=true)]
void OnEvent1(int number);
}
Discover - publish Service:
class MyPublishService : DiscoveryPublishService<IMyEvents>, IMyEvents
{
public void OnEvent1(int number)
{
FireEvent(number);
}
}
Discover - publish service host:
ServiceHost host = DiscoveryPublishService<IMyEvents>.
CreateHost<MyPublishService>();
host.Open();
// later..
host.Close();
Publisher:
IMyEvents proxy = DiscoveryPublishService<IMyEvents>.CreateChannel();
proxy.OnEvent1();
(proxy as ICommunicationObject).Close();
My question is how can I implement the subscriber? The book says to implement the service contract. That's simple.
class EventServiceSubscriber : IMyEvents
{
public void OnEvent1(int number)
{
// do something
}
}
but how can i host the subscriber? How subscriber can connect to the Publish-Subscribe service?
To get this to work I created a SubcriptionService as follows:
using ServiceLibrary.Contracts;
using ServiceModelEx;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace Subscriber
{
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any, IncludeExceptionDetailInFaults = DebugHelper.IncludeExceptionDetailInFaults, InstanceContextMode = InstanceContextMode.Single, UseSynchronizationContext = false)]
class SubscriptionService : DiscoveryPublishService<IMyEvents>, IMyEvents
{
public void OnEvent1()
{
Debug.WriteLine("SubscriptionService OnEvent1");
}
public void OnEvent2(int number)
{
Debug.WriteLine("SubscriptionService OnEvent2");
}
public void OnEvent3(int number, string text)
{
Debug.WriteLine("SubscriptionService OnEvent3");
}
}
}
Then I set up a host for this service as follows:
ServiceHost<SubscriptionService> _SubscriptionHost = DiscoveryPublishService<IMyEvents>.CreateHost<SubscriptionService>();
_SubscriptionHost.Open();
A basic working sample can be found in my Github account at the following url.
https://github.com/systemsymbiosis/PublishSubscribeWithDiscovery
There are a bunch of articles around that cover this subject. For starters, this one. You can host your subscriber in different ways like a console application or a ASP.NET application. Every application type has some kind of startup method so that'd be a good place to implement your subscription/publishing logic.
I've been watching some tuts on how to create a C# Windows Service; all good but no one says how to make te service run, at the end of installation, a specific file from installation folder(in my case hidden.vbs)(my app has 2 project: the service itself and the setup).
After the install of the setup, the service starts PROJECT_NAME.exe and PROJECT_NAME.svhost.exe
Tell me please if you need any other code in order to help me...
Here is my Program.cs
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.ServiceProcess;
namespace PROJECT_NAME
{
[RunInstaller(true)]
public partial class ProjectInstaller : System.Configuration.Install.Installer
{
public ProjectInstaller()
{
InitializeComponent();
}
private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
{
new ServiceController(serviceInstaller1.ServiceName).Start();
}
}
}
Service1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
namespace PROJECT_NAME
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
public void OnDebug()
{
OnStart(null);
}
protected override void OnStart(string[] args)
{
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "OnStart.txt");
}
protected override void OnStop()
{
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "OnStart.txt");
}
}
}
Also, here is a pic of my Solution Explorer http://i.imgur.com/wbqUGOc.png ; please tell me how or where should I import the files I need the service to run.
It's my first time in C#, I'm not willing to understand it now, but to make this service because I will need it in my work..
Your script can do one of several things to start a service:
Issue the net start console command to launch a service (e.g. net start "My Service Name")
OR
Programmaticaly, call the StartService API.
My project is to write a web service and a web form that consumes it. It should have two text boxes and a button. The user enters an text speak acronym in the first text box and presses the button. The web service compares the textbox1 entry against a dictionary file, and displays the resulting full word in the second text box. This is the code I have so far and I am really struggling to get it to work, any help would be appreciated. At this point I have 'Type or namespace definition, or end of file expected' error. Here are the two files i have.
Default.aspx.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
public partial class _Default : System.Web.UI.Page
{
private Dictionary<string, string> _dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
protected void Page_Load(object sender, EventArgs e)
{
using (var reader = new StreamReader(File.OpenRead(#"C:/dictionary.csv")))
{
while (!reader.EndOfStream)
{
string[] tokens = reader.ReadLine().Split(';');
_dictionary[tokens[0]] = tokens[1];
}
}
}
protected void Button1_Click(object sender, EventArgs e)
{
localhost.Service obj = new localhost.Service();
TextBox1.Text = (obj.Translate());
}
}
Service.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.IO;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class Service : System.Web.Services.WebService
{
public Service () {
//Uncomment the following line if using designed components
//InitializeComponent();
}
[WebMethod]
public string Translate(string input)
{
string output;
if(_dictionary.TryGetValue(input, out output))
return output;
// Obviously you might not want to throw an exception in this basis example,
// you might just go return "ERROR". Up to you, but those requirements are
// beyond the scope of the question! :)
throw new Exception("Sinatra doesn't know this ditty");
}
}
}
Not sure if the question is still unanswered. But here are my suggestions.
In the web service file i.e. say Service1.cs you are not declaring the _dictionary object. So you will be moving the dictionary object declaration and initialization in the constructor of the service.
Some thing like this below.
public WebService1()
{
using (var reader = new StreamReader(File.OpenRead(#"C:/dictionary.csv")))
{
while (!reader.EndOfStream)
{
string[] tokens = reader.ReadLine().Split(',');
_dictionary[tokens[0]] = tokens[1];
}
}
}
Also in the split method I would assume you wanted to use the comma instead of the semicolon(that was used in your sample).
And then in the consumption of the service, you would do some thing like this below. I was not sure what you were trying to do using the localhost object in your sample.
ServiceReference1.WebService1SoapClient obj = new WebService1SoapClient();
TextBox2.Text = obj.Translate(TextBox1.Text);
Hope this helps.
-Davood.
I have a few questions regarding WCF:
- Can a program act as both client and server ?
- What's wrong with my code :
The service:
[ServiceContract]
public interface IShout
{
[OperationContract]
String Broadcast(String message);
}
The implementation:
public class eveShout : IShout
{
public String Broadcast(String message)
{
return message + " reply";
}
}
I start the service in the form contructor:
ServiceHost s = new ServiceHost(typeof(IShout));
s.AddServiceEndpoint(typeof(IShout), new BasicHttpBinding(), "http://localhost:9189");
s.Open();
The, When I click a button on another form, I want to send a message and get a reply back.
I use the following code:
ChannelFactory<IShout> channel = new ChannelFactory<IShout>(new BasicHttpBinding(), "http://localhost:9189");
IShout shout = channel.CreateChannel();
String reply = shout.Broadcast("Test");
Note: all the code is in the same namespace.
Note: I first start the "server" (open) then the app continues.
when i run the code, the server is created. I use netstat -a to see if the port is open. when I run the command, i get 9189 is in listening state. but the code stops at the command reply = shout("test"). and I get anexception that says
The request channel timeout while waiting for a reply after 00:00:59...
Yes, you can have an app act as both client and server.
I see a couple of things that may need correcting. First, try adding OperationContract.
[ServiceContract]
public interface IShout
{
[OperationContract]
String Broadcast(String message);
}
Then, take the type of the class, not the interface.
ServiceHost s = new ServiceHost(typeof(eveShout));
s.AddServiceEndpoint(typeof(IShout), new BasicHttpBinding(), "http://localhost:9189");
s.Open();
Make sure you you have permission to access the namespace (s.Open() should throw an exception if it does not).
net http add urlacl url=http://+:9189/ user=...
See if these suggestions help.
(oh yeh, and make Broadcast public in your class)
A quick example WindowsFormsApplication looks like this...
// form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
ChannelFactory<IShout> channel = new ChannelFactory<IShout>(new BasicHttpBinding(), "http://localhost:9189");
IShout shout = channel.CreateChannel();
String reply = shout.Broadcast("Test");
}
}
}
// and Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.ServiceModel;
namespace WindowsFormsApplication1
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
ServiceHost s = new ServiceHost(typeof(eveShout));
s.AddServiceEndpoint(typeof(IShout), new BasicHttpBinding(), "http://localhost:9189");
s.Open();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
public class eveShout : IShout
{
public String Broadcast(String message)
{
return message + " reply";
}
}
[ServiceContract]
public interface IShout
{
[OperationContract]
String Broadcast(String message);
}
}
See if you can get something as simple as this working. That will at least prove to you that it can be done and that the problem is somewhere else.
Enable WCF debugging.
The easiest way to do this is with WCF Service Configuration Editor. Open the utility and then browse to open your application's configuration file. From the diagnostics section simply click 'enable tracing'. The default tracing will be fine.
Once you run your application, the framework will dump a log file to a location specified in your configuration file. Double click to open it and read through the red events (these are the ones that have exceptions or unexpected outcome). It's very helpful and should help you identify where the problem is occurring.