I'm currently working on a "server-app" / "client-app" project where the goal is to get some data from the server-app to the client app. I tried this with a WCF approach but since I've never worked with WCF it ain't an easy task for me.
So what I've already set up are the two apps in one solution à two different projects. Project one contains the server-app (TRunnerServer) and project two contains the client-app (TRunnerClient).
I've setup the interface for the service like that (in ServerApp MainWindowViewModel.cs):
[ServiceContract]
public interface ITRunnerService
{
[OperationContract]
ObservableCollection<Program> GetProgramList();
}
Than I've added the method to the class etc. like following:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
internal class MainWindowViewModel : ViewModelBase, ITRunnerService
{
public ObservableCollection<Program> GetProgramList()
{
return this.ProgramList;
}
public MainWindowViewModel()
{
var uris = new Uri[1];
string addr = "net.tcp://localhost:7000/MainWindowViewModel";
uris[0] = new Uri(addr);
ITRunnerService tRunnerService = this;
ServiceHost host = new ServiceHost(tRunnerService, uris);
var binding = new NetTcpBinding(SecurityMode.None);
host.AddServiceEndpoint(typeof(ITRunnerService), binding, string.Empty);
host.Open();
}
}
Now in the GUI of the application TRunnerClient I've got an button that I press to get the data from the TRunnerServer App.
I've just got an method binded with following:
private void Refresh(object parameter)
{
var uri = "net.tcp://localhost:7000/MainWindowViewModel";
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
var channel = new ChannelFactory<ITRunnerService>(binding);
var endPoint = new EndpointAddress(uri);
var proxy = channel.CreateChannel(endPoint);
this.ProgramList = proxy.GetProgramList();
}
Yet when I start the app only the client starts normal and the server app gives an exception based on the error warning from the xaml "A registration already exists for URI 'net.tcp://localhost:7000/MainWindowViewModel'".
How could I solve this problem?
Note: Other questions with a similar title didn't really helped me out before someone strikes it as duplicate.
The error message indicates that there is an old server process still hanging around. Try to kill it in the task manager and try again.
In order to avoid hanging processes, make sure that you exit the application gracefully and that you don't create any windows that you don't show and close.
Related
I have a WCF service that I have added as a reference. I change the address of the service so that it does not appear in the configuration, I try to make a shortcut so that I do not write the same things every time. What would you recommend on this?
internal class TestService
{
public static TestServisClient GetServiceClient
{
get
{
var binding = new BasicHttpBinding();
var endpoind = new EndpointAddress("http://localhost:64733/TestService.svc?wsdl");
return new TestServisClient(binding, endpoind);
}
}
}
testservice.getserviceclient.getdatatables();
Is it right to use it this way, should I call it again to close the service?
i think i have to do it this way
using (TestServisClient client = new TestServisClient(
new BasicHttpBinding(), new EndpointAddress(url)))
{ ... }
I'm currently using SignalR to communicate between a server and multiple separate processes spawned by the server itself.
Both Server & Client are coded in C#. I'm using SignalR 2.2.0.0
On the server side, I use OWIN to run the server.
I am also using LightInject as an IoC container.
Here is my code:
public class AgentManagementStartup
{
public void ConfigurationOwin(IAppBuilder app, IAgentManagerDataStore dataStore)
{
var serializer = new JsonSerializer
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
TypeNameHandling = TypeNameHandling.Auto,
TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple
};
var container = new ServiceContainer();
container.RegisterInstance(dataStore);
container.RegisterInstance(serializer);
container.Register<EventHub>();
container.Register<ManagementHub>();
var config = container.EnableSignalR();
app.MapSignalR("", config);
}
}
On the client side, I register this way:
public async Task Connect()
{
try
{
m_hubConnection = new HubConnection(m_serverUrl, false);
m_hubConnection.Closed += OnConnectionClosed;
m_hubConnection.TraceLevel = TraceLevels.All;
m_hubConnection.TraceWriter = Console.Out;
var serializer = m_hubConnection.JsonSerializer;
serializer.TypeNameHandling = TypeNameHandling.Auto;
serializer.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
m_managementHubProxy = m_hubConnection.CreateHubProxy(AgentConstants.ManagementHub.Name);
m_managementHubProxy.On("closeRequested", CloseRequestedCallback);
await m_hubConnection.Start();
}
catch (Exception e)
{
m_logger.Error("Exception encountered in Connect method", e);
}
}
On the server side I send a close request the following way:
var managementHub = GlobalHost.ConnectionManager.GetHubContext<ManagementHub>();
managementHub.Clients.All.closeRequested();
I never receive any callback in CloseRequestedCallback. Neither on the Client side nor on the server side I get any errors in the logs.
What did I do wrong here ?
EDIT 09/10/15
After some research and modifications, I found out it was linked with the replacement of the IoC container. When I removed everything linked to LightInject and used SignalR as is, everything worked. I was surprised about this since LightInject documented their integration with SignalR.
After I found this, I realised that the GlobalHost.DependencyResolver was not the same as the one I was supplying to the HubConfiguration. Once I added
GlobalHost.DependencyResolver = config.Resolver;
before
app.MapSignalR("", config);
I am now receiving callbacks within CloseRequestedCallback. Unfortunately, I get the following error as soon as I call a method from the Client to the Server:
Microsoft.AspNet.SignalR.Client.Infrastructure.SlowCallbackException
Possible deadlock detected. A callback registered with "HubProxy.On"
or "Connection.Received" has been executing for at least 10 seconds.
I am not sure about the fix I found and what impact it could have on the system. Is it OK to replace the GlobalHost.DependencyResolver with my own without registering all of its default content ?
EDIT 2 09/10/15
According to this, changing the GlobalHost.DependencyResolver is the right thing to do. Still left with no explanation for the SlowCallbackException since I do nothing in all my callbacks (yet).
Issue 1: IoC Container + Dependency Injection
If you want to change the IoC for you HubConfiguration, you also need to change the one from the GlobalHost so that returns the same hub when requesting it ouside of context.
Issue 2: Unexpected SlowCallbackException
This exception was caused by the fact that I was using SignalR within a Console Application. The entry point of the app cannot be an async method so to be able to call my initial configuration asynchronously I did as follow:
private static int Main()
{
var t = InitAsync();
t.Wait();
return t.Result;
}
Unfortunately for me, this causes a lot of issues as described here & more in details here.
By starting my InitAsync as follow:
private static int Main()
{
Task.Factory.StartNew(async ()=> await InitAsync());
m_waitInitCompletedRequest.WaitOne(TimeSpan.FromSeconds(30));
return (int)EndpointErrorCode.Ended;
}
Everything now runs fine and I don't get any deadlocks.
For more details on the issues & answers, you may also refer to the edits in my question.
the .net Windows Form application we developed (vb.net but c# answer is ok too) has some APIs (edit: yes, our own) to allow users to automate some tasks.
Everything is fine when the application is started through APIs by, say, Visual Studio. What we cannot get to work though is to assign an already running instance of our application to a new application object in visual studio.
We have seen there are some methods available for COM objects (getojbect) to access a running instance of an application but how about .net applications?
Rephrasing the question, we would like that, when a user calls the New() constructor of our application, the new object points to the running instance of our application (if any) instead of trying to create a new one (which is not possible by the way because we have made it single instance by checking through Mutex that no other instance of our application is running).
EDIT:
Sample code in the user application to automate some tasks
Imports TheApplication
Public Class WinFormByUser
Private ApplicationObject As TheApplication.MainForm
Public Sub OpenTheApplication()
ApplicationObject = New TheApplication.MainForm
Rem here theapplication should create a new instance if no instance of TheApplication is running. BUT, if an instance of the application
Rem is already running (in a different process, maybe started directly from the user), the ApplicationObject should point to the running
Rem instance from now on, instead of trying to create a new instance
ApplicationObject.DoSomething()
End Sub
End Class
Sample code inside TheApplication
Imports System.Threading
Public Class MainForm
Private ApplicationOpenedThroughAPI As Boolean = False
Private Shared mtx As Mutex
Private firstInstance As Boolean = False
Dim AppName As String = "TheApplicationName"
Public Sub New()
If Application.ProductName.ToString() <> AppName Then
Rem if TheApplication is opened externally through API the name is different therefore we can determine the boolean value
ApplicationOpenedThroughAPI = True
End If
mtx = New Mutex(True, AppName, firstInstance)
If firstInstance Then
InitializeComponent()
DoAllTheNecessaryStuff()
Else
If ApplicationOpenedThroughAPI = False Then
MsgBox("Application is running, can't open second instance")
Else
ReturnTheRunningInstance()
End If
End If
End Sub
Private Sub ReturnTheRunningInstance()
Rem please help here. what to do?
End Sub
Public Sub DoSomething()
Rem this does something and can be called by API user
End Sub
End Class
Please note that the solution could either be adding some code inside the application in the Sub ReturnTheRunningInstance() or in the user code, maybe checking if the application is running through something like Process.GetProcessesByName("TheApplicationName").Length and then do something in case.
Thanks!
We have seen there are some methods available for COM objects
(getojbect) to access a running instance of an application but how
about .net applications?
Let's start with this part. You essentially need to have one process access another process. .Net provides a variety of forms of cross-process communication. WCF seems the most appropriate here.
WCF is a large subject, but here's a basic architecture that might accomplish your goals.
Step 1
Have your application host a service, available to local callers over TCP.
Consider this pseudocode; there is plenty of documentation available on WCF once you know what to search for.
// the contract
[ServiceContract]
public interface IMyService
{
[OperationContract]
int Foo( int bar );
}
// the implementation
public MyService : IMyService
{
public int Foo( int bar ){ return bar * 100; }
}
// hosting the service within your application
var baseUri = new Uri( "net.tcp://localhost:59999/" );
var serviceHost = new ServiceHost( typeof( MyService ), baseUri );
// many options can/should be set here, e.g. throttling, security, and serialization behavior
var binding = new NetTcpBinding();
var endpoint = serviceHost.AddServiceEndpoint( typeof( IMyService ), binding, baseUri );
This is all you need for a caller to interface with an existing instance of the application, but it doesn't address the need to ensure that the app is running.
Step 2
A wrapper class may make it easier to locate/launch your application.
public sealed class MyWrapper
{
public IMyService GetService()
{
// TODO: perform appropriate OS-wide locking here
// TODO: see if app is running
// TODO: if not, launch it in a new process
// create a channel to connect the WCF endpoint we just defined
var channel = GetChannel();
// TODO: release lock
// return the channel to the caller
return channel;
}
public GetChannel( Binding binding, EndpointAddress endpointAddress )
{
var channelFactory = new ChannelFactory<IMyService>( binding, endpointAddress );
return _channelFactory.CreateChannel();
}
}
Step 3
Your callers can connect to your application from anywhere on the machine (or beyond, if you wish):
var wrapper = new Wrapper();
var service = wrapper.GetService();
int result = service.Foo( 123 );
While a bit unusual, your service code could also manipulate the GUI. For example:
var wrapper = new Wrapper();
var service = wrapper.GetService();
// call a method, the implementation of which launches a "contact form"
// with data preloaded for the specified contact ID
service.ShowContactForm( 1 );
Cleanup
Note that this syntax I've shown so far is elegant, but it doesn't handle closing the channel or channel factory. There are a variety of ways to do this; I've used a pattern like this:
public sealed class ServiceClient
{
private readonly ChannelFactory<IMyService> _channelFactory;
public ServiceClient( Binding binding, EndpointAddress endpointAddress )
{
_channelFactory = new ChannelFactory<IMyService>( binding, endpointAddress );
Channel = _channelFactory.CreateChannel();
}
public IMyService Channel { get; private set; }
public void Dispose()
{
if( Channel != null )
{
// TODO: check the state of the channel and close/abort appropriately
}
if( _channelFactory != null )
{
_channelFactory.Close();
}
}
}
public sealed class MyWrapper
{
public ServiceClient GetClient()
{
// Similar setup to the previous example, except the service client wraps
// the channel factory.
}
}
var wrapper = new Wrapper();
using( var client = wrapper.GetClient() )
{
client.Channel.Foo( 123 );
}
It's a bit more verbose, but it gives you much more control over cleanup and any other options you wish to control.
Solution Structure
All of this code can potentially live in one assembly. However, it may be cleaner to place the wrapper in a separate assembly and the service contract(s) interfaces into another assembly referenced by the wrapper and the main application.
Assembly 1: service contracts (interfaces)
Assembly 2: GUI application, references assembly 1 and implements its service contracts
Assembly 3: wrapper class, references assembly 1
I'd like to re-use the same host instance on every WCF call. Everything I've read on the Internet says the exact same thing; use the [ServiceBehavior] attribute. So, that's what I've done:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class DALService : IDataAccess
{
private string _adminConnStr;
public void ServiceStart()
{
_adminConnStr = ConnectionStringManager.GetAdminDatabaseConnectionString();
}
public IEnumerable<CustomerInfo> GetCustomerInfosOrderedByShortname()
{
// _adminConnStr is null here!
}
}
I've set a breakpoint in ServiceStart to make sure that was getting run. I've set a breakpoint in GetCustomerInfo, and _adminConnStr is once again null. I've also set a watch on *this and the addresses are indeed different.
I'm wondering if this is because I create the ServiceHost objects programmatically, rather than using IIS which sets all that stuff up for you. Perhaps I need to be doing something more when I create the host? Here's my code for that:
var contract = attribute.ContractType;
var serviceHost = new ServiceHost(attribute.ServiceType ?? instance.GetType());
serviceHosts.Add(serviceHost);
var uri = String.IsNullOrEmpty(service.UriPrefix)
? new Uri(String.Format("http://{0}:{1}/{2}", serverName, port, contract.Name))
: new Uri(String.Format("http://{0}:{1}/{2}/{3}", serverName, port, service.UriPrefix, contract.Name));
if (uris.Contains(uri))
throw new DuplicateServiceRegistrationException(uri);
uris.Add(uri);
serviceHost.Description.Behaviors.Add(new ServiceMetadataBehavior
{
HttpGetEnabled = true,
HttpGetUrl = uri
});
serviceHost.AddServiceEndpoint(contract, new BasicHttpBinding(), uri);
serviceHost.Open();
instance.ServiceStart(); // This line will set _adminConnStr
Console.WriteLine("{0} now listening at: {1}", service, uri);
Figured it out.
It seems that WCF won't set your Singleton instance until the first time you call it. Basically after this line:
var serviceHost = new ServiceHost(attribute.ServiceType ?? instance.GetType());
serverHost.SingletonInstance will be null. So, when I call:
instance.ServiceStart();
I'm calling ServiceStart on an instance that WCF doesn't know about.
So, I need to set the singleton instance on WCF when I create serviceHost:
var serviceHost = new ServiceHost(instance);
Then, I can call .ServiceStart on that instance. This will fix the issue.
I have 2 projects, project 1 have a reference to project 2.
Project 1 is using a simple service reference with a proxy class to connect to service. To be able to send username/password in header the following code is used on this proxy class :
public static void Connect()
{
_Myclient = new MyService.MyIntegrationClient();
_Scope = new OperationContextScope(_Myclient.InnerChannel);
IntegrationHeader ih = new IntegrationHeader
{
UserName = Properties.Settings.Default.MyUserLogin,
Password = Properties.Settings.Default.MyUserPassword
};
MessageHeader untyped = MessageHeader.CreateHeader("SecurityToken", "ns", ih);
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
}
So far so good, no problem to run and the usernamen/password can be read on service.
Project 2 are using channelFactory instead to connect to the same service. The code for creating the channel and adding messageheader looks like this :
public static IMyIntegration GetMyFactory(string userName, string password)
{
IMyIntegration client;
OperationContextScope operationContextScope;
IntegrationHeader integrationHeader;
ConfigurationChannelFactory<IMyIntegration> factory;
MessageHeader messageHeader;
integrationHeader = new IntegrationHeader { UserName = userName, Password = password };
messageHeader = MessageHeader.CreateHeader("SecurityToken", "ns", integrationHeader);
factory = new ConfigurationChannelFactory<IMyIntegration>("BasicHttpBinding_IMyIntegration", ConfigHelper.MyConfiguration, null);
client = factory.CreateChannel();
operationContextScope = new OperationContextScope((IClientChannel)client);
if (OperationContext.Current.OutgoingMessageHeaders.Count < 1)
OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader);
return client;
}
This is how the IntegrationHeader looks like :
[DataContract()]
public class IntegrationHeader
{
[DataMember]
public string UserName;
[DataMember]
public string Password;
}
Project 1 will first run a method in project 2 that connects(using above code) to a service Method at the service. After this is done project 1 will also make a connection to the same service but with the code that is first in this post.
So far so good, no problem.
Problem
The two service methods is then triggered again(a second time) but this time the above code is not needed because it was already done on the prev loop, so this time we do the service method request directly without creating any proxy classes or channelFactories.
The result is that the header on the sent message is missing this second time on the service?
If I remove the service call made by project 1 (the one with the proxy) there will be no problems?
Edit 1 :
If I only run the service calls that Project 1 does then it will work just fine, and if I only run the Service Calls that Project 2 does it will also work fine. The problem is when doing Project 2 call, Project 1 call and then back to Project 2 call again.
If I run it the other way around, Project 1, Project 2 then Project 1 again it will also fail on the same problem (third Project 1 call)?
Edit 2 :
I am using the OperationContext.Current.OutgoingMessageHeaders in both cases and it is only set the at the first call for each project, maybe thay are using the same context?
My guess is that the OperationContextScope is being disposed by the time your second call is made (after the one minute pause).
This will result in OperationContext.Current returning the previous instance of the operation context before the scope was created, which does not contain any headers.
The way to fix this would be to move the OpertationContextScope initialization out of the factory method:
var client = GetMyFactory("username", "password);
using (operationContextScope = new OperationContextScope((IClientChannel)client))
{
OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader);
client.DoSomething()
....
}
From MSDN:
When an OperationContextScope is created, the current OperationContext
is stored and the new OperationContext becomes the one returned by the
Current property. When the OperationContextScope is disposed, the
original OperationContext is restored.