WCF server not sending data to excel on another computer - c#

I have a C# application which takes data in from a sensor.
It has the code below: (unfortunately not written by me and not documented, and the old developer has long left the country).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace Client
{
[ServiceContract]
public interface IDataRequest
{
[OperationContract]
string query(string instr, string attr);
}
public class DataRequest : IDataRequest
{
public string query(string symbol, string attr)
{
//data_set is a simple dictionary in another file
Data data = MAIN.data_set[symbol].Value;
return data.last;//numeric value of sensor reading
}
}
class WCFServer
{
ServiceHost host = null;
public void open()
{
host = new ServiceHost(
typeof(DataRequest),
new Uri[]{new Uri("net.pipe://localhost")});
host.AddServiceEndpoint(typeof(IDataRequest), new NetNamedPipeBinding(), "DataRequest");
try
{
host.Open();
}
catch (AddressAlreadyInUseException) { }
}
public void close()
{
try
{
host.Close();
}
catch (CommunicationObjectFaultedException) { }
}
}
}
Then in Excel, I can place in a cell:
=RTD("data",,"sensor1","last"
Everything works fine on the one computer.
However, when I move the application and excel file to another computer, nothing works, instead of seeing the data value streaming into excel I just see #N/A. The client application itself works on another computer, so i know the C# code is fine, just something to do with the WCF link from C# to Excel.
Is there something else involved? Registration of the WCF server or anything?

Add the servername in RTD: RTD("data,server_name,"sensor1","last"). Documentation: https://support.microsoft.com/en-us/kb/289150

Related

ExcelDNA RTD with SignalR

I'm trying to hook up ExcelDNA RTD with a ASP.NET SignalR server.
Whenever there is a change on the server a message get pushed to the connected clients, and my ExcelDna add-in is getting the new messages but the registered function is not updated.
My RTD server:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
using ExcelDna.Integration;
using ExcelDna.Integration.Rtd;
namespace DMT.Excel.AddIn
{
[ComVisible(true)]
public class SignalRServer : ExcelRtdServer
{
private HubConnection _connection;
private List<Topic> _topics = new List<Topic>();
public TradesRtdServer()
{
_connection = new HubConnectionBuilder()
.WithUrl("http://localhost:5000/api/test/hub")
.WithAutomaticReconnect()
.Build();
_connection.On<object>("Test", m =>
{
foreach (Topic topic in _topics)
{
topic.UpdateValue(m);
}
});
Task.Run(() => _connection.StartAsync());
}
protected override bool ServerStart()
{
DmtAddIn.Logger.Information("ServerStart");
return true;
}
protected override void ServerTerminate()
{
DmtAddIn.Logger.Information("ServerTerminate");
}
protected override object ConnectData(Topic topic, IList<string> topicInfo, ref bool newValues)
{
DmtAddIn.Logger.Information("ConnectData: {0} - {{{1}}}", topic.TopicId, string.Join(", ", topicInfo));
_topics.Add(topic);
return ExcelErrorUtil.ToComError(ExcelError.ExcelErrorNA);
}
protected override void DisconnectData(Topic topic)
{
_topics.Remove(topic);
DmtAddIn.Logger.Information("DisconnectData: {0}", topic.TopicId);
}
}
}
My function
[ExcelFunction(Name = "SignalR.Test.RTD")]
public static object GetSignalRMessages()
{
return XlCall.RTD("Excel.AddIn.Trading.SignalRServer", null, "Test");
}
When I debug I can see topic.UpdateValue(m); is being hit whenever a message is pushed from the server but not GetSignalRMessages
Am I missing anything to propagate the topic change to the function?
Thank you!
Joseph
I managed to solve this by sending a string from the SignalR server, then deserialize it on the client side
The ExcelRtdServer checks whether the value passed into UpdateValue is different to the previous value. You might be passing either the same value every time, or some value that is interpreted to an Excel data type as the same (e.g. some object type that is converted to the same string every time).
You might be better off building this through the IObservable or Rx abstractions, that are a bit higher-level than the ExcelRtdServer. See the samples here https://github.com/Excel-DNA/Samples/tree/master/RtdClocks
Maybe something like this project which combines Rx and SignalR: https://github.com/jwooley/SignalrRxSamples

System.InvalidOperationException when calling WCF Service

I'm trying to create a WCF callback service with netTcpBinding. When I try to call a method of the service I get following exception:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.ServiceModel.dll
Additional information: The InstanceContext provided to the ChannelFactory contains a UserObject that does not implement the CallbackContractType 'Client.WCFService.IHostFunctionsCallback'.
I've added a service reference instead of using SvcUtil.exe
I've searched the internet for fixing this problem, but I haven't found a solution yet.
Here's my implementation:
IHostFunctions.cs (Part of HostLibrary)
using System.ServiceModel;
namespace HostLibrary
{
[ServiceContract(CallbackContract = typeof(ICallback))]
public interface IHostFunctions
{
[OperationContract]
void OpenSession();
}
}
ICallback.cs (Part of HostLibrary)
using System.ServiceModel;
namespace HostLibrary
{
public interface ICallback
{
[OperationContract]
void OnCallback();
}
}
HostFunctions.cs (Part of HostLibrary)
using System;
using System.ServiceModel;
using System.Timers;
namespace HostLibrary
{
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class HostFunctions : IHostFunctions
{
#region Implementation of IHostFunctions
public static ICallback Callback;
public static Timer Timer;
public void OpenSession()
{
Console.WriteLine("> Session opened at {0}", DateTime.Now);
Callback = OperationContext.Current.GetCallbackChannel<ICallback>();
Timer = new Timer(1000);
Timer.Elapsed += OnTimerElapsed;
Timer.Enabled = true;
}
void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
Callback.OnCallback();
}
#endregion
}
}
Callback.cs (Part of Client)
using System;
using HostLibrary;
namespace Client
{
public class Callback : ICallback
{
#region Implementation of ICallback
public void OnCallback()
{
Console.WriteLine("> Received callback at {0}", DateTime.Now);
}
#endregion
}
}
Program.cs of the service
using System;
using System.ServiceModel;
using HostLibrary;
namespace WCF_TCP_Callbacks
{
internal static class Program
{
private static void Main(string[] args)
{
using (var sh = new ServiceHost(typeof (HostFunctions)))
{
sh.Open();
Console.WriteLine("Service started.");
Console.ReadLine();
Console.WriteLine("Stopping service...");
sh.Close();
}
}
}
}
Program.cs of the client
using System;
using System.Globalization;
using System.ServiceModel;
using System.Threading;
using Client.WCFService;
namespace Client
{
internal class Program
{
private static void Main(string[] args)
{
var callback = new Callback();
using (var proxy = new HostFunctionsClient(new InstanceContext(callback)))
{
proxy.OpenSession();
}
Console.ReadLine();
}
}
}
The code is from http://adamprescott.net/2012/08/15/a-simple-wcf-service-callback-example/ but with netTcpBinding.
by default WCF will attempt to dispatch using an available SynchronizationContext. The problem with this callback is the UI thread is already blocked in an outbound call. SO for the call to dispatch we need to tell WCF not to use the SynchronizationContext – again using the CallbackBehavior attribute:
[CallbackBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant, UseSynchronizationContext=false)]
public class Callback : ICallback
{
....
}
for further detail look this link http://www.dotnetconsult.co.uk/weblog2/PermaLink,guid,b891610a-6b78-4b54-b9a6-4ec81c82b7c0.aspx
and one more post describe it further
http://stefanoricciardi.com/2009/08/28/file-transfer-with-wcp/
I fixed that problem by simply renaming the class ICallback to IHostFunctionsCallback.
I still don't know why this works now as I didn't use IHostFunctionsCallback before.
I know post is old :
What is really the name of your callback Class?
the code you posted says this:
Callback : ICallback
The Error Message says this:
CallbackContractType 'Client.WCFService.IHostFunctionsCallback'
So is the Callback as per your code above, or is it really defined as:
Client.WCFService.IHostFunctionsCallback
I would say you have decorated an attribute reference to the callback channel incorrectly, or inherited from the wrong callback. Search your project to make sure you named everything correctly.
EDIT
As to why the fix worked and what happened:
I answered for the case of others.It may be if you were in a team environment that someone changed the name of the Callback class interface from so generic to something more understandable - in WCF that is what your userobject is - it is the contract. You may have used Visual Studio to generate your Client or Service at one point. which can also foul things up, which is what it looks like to me as the naming convention follows IService[Callback].

Exchange Server 2007 Transport Agent Issue

This is the first time i am working on Exchange Server Development. Below is a simple Transport Agent that i am using, this agent should simply update the email Subjects as shown below in the code.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Exchange.Data.Transport;
using Microsoft.Exchange.Data.Transport.Smtp;
namespace MyAgents
{
public sealed class MyAgentFactory : SmtpReceiveAgentFactory
{
public override SmtpReceiveAgent CreateAgent(SmtpServer server)
{
return new MyAgent();
}
}
public class MyAgent : SmtpReceiveAgent
{
public MyAgent()
{
this.OnEndOfData += new EndOfDataEventHandler(MyEndOfDataHandler);
}
private void MyEndOfDataHandler(ReceiveMessageEventSource source, EndOfDataEventArgs e)
{
e.MailItem.Message.Subject = "This message passed through my agent: " + e.MailItem.Message.Subject;
}
}
}
Below is the Powershell script i am using to install the Agent.
Net Stop MSExchangeTransport
Install-TransportAgent -Name MyAgent -AssemblyPath EmailLogger.dll -TransportAgentFactory MyAgents.MyAgentFactory
Enable-TransportAgent -Identity MyAgent
Net Start MSExchangeTransport
Agent installed successfully using Exchange Management Shell.
Now when i send/receive emails in exchange, Email subjects are not modified. Emails have their original subjects. I don't know why?
I also performed the steps mentioned in below links to debug the Agent but breakpoints are not being hit by Visual Studio Debugger.
http://www.sf-tools.net/Messaging/tabid/55/EntryId/163/Exchange-2010-Transport-Agent.aspx
Debugging MS Exchange 2007 Transport Agent
http://omarjames.com/blog/index.php/debugging-exchange-transport-agent/
My System Configuration
I am using the Exchange Server 2007 Virtual Machine provided by Microsoft from link below
http://www.microsoft.com/en-pk/download/details.aspx?id=14901
I also installed the Visual Studio 2008 on the VM for debugging.
Please help me in resolving the issue?
Problem Solved. :)
I must use Routing Agent instead of SmtpReceive Agent because only Routing Agents are guaranteed to see all the Emails passing through Exchange Server.
Below is the modified working code, Everything else remains same
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Exchange.Data.Transport;
using Microsoft.Exchange.Data.Transport.Routing;
namespace MyAgents
{
public sealed class MyAgentFactory : RoutingAgentFactory
{
public override RoutingAgent CreateAgent(SmtpServer server)
{
return new MyAgent();
}
}
public class MyAgent : RoutingAgent
{
public MyAgent()
{
this.OnSubmittedMessage += new SubmittedMessageEventHandler(this.MySubmittedMessageHandler);
}
public void MySubmittedMessageHandler(SubmittedMessageEventSource source, QueuedMessageEventArgs e)
{
e.MailItem.Message.Subject = "This message passed through my agent: " + e.MailItem.Message.Subject;
}
}
}

How share data in WCF webservice

In order to call webservices dynamicly, I use WCF Dynamic Proxy from Microsoft
If I understood properly how it works, the code load the wsdl and compile on system class in order to consume distant webservice. I put this code in a "generic webservice". Its goal is to call any webservice with a request in parameter, and respond the answer of the webservice called.
But a problem appears : each request to this "generic webservice" pulls a new compilation of the proxy, and use time and ressources of the server.
My objective is to save instance of each proxies during a laps of time, and renew the instance when this laps is reached.
After few hours of googling, I found two ways :
Use my WCF webservice "by session", but I don't find any tutorial which explains how create easily the session layer
Use a singleton in order to save my datas and mutualize them with all instances of webservice
I exclude the first solution because I don't know how to do this. So I decided to use the second way.
There is my implementation :
FactoryTest is the singleton, contening the hashtable with instances
ProxyTest is the class which contains information about each instances of distant webservices
There is the code of FactoryTest :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WcfSamples.DynamicProxy;
using System.Threading;
using System.Collections;
namespace WS_Generic
{
public sealed class FactoryTest
{
private static object syncRoot = new Object();
private static Hashtable hashFactory = new Hashtable();
public static DynamicProxy getProxy(String sServiceWsdl, String sContract)
{
if (hashFactory[sServiceWsdl] == null || ((ProxyTest)hashFactory[sServiceWsdl]).getTimeFromCreation().TotalSeconds > 60 * 60 * 6)
{
lock (syncRoot)
{
if (hashFactory[sServiceWsdl] == null || ((ProxyTest)hashFactory[sServiceWsdl]).getTimeFromCreation().TotalSeconds > 60 * 60 * 6)
{
hashFactory.Add(sServiceWsdl, new ProxyTest(sServiceWsdl, sContract));
}
}
}
return ((ProxyTest)hashFactory[sServiceWsdl]).getProxy();
}
public static bool isProxyExists(String sServiceWsdl, String sContract)
{
lock (syncRoot)
{
return hashFactory[sServiceWsdl] == null ? false : true;
}
}
}
}
There is the code of ProxyTest :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WcfSamples.DynamicProxy;
namespace WS_Generic
{
public class ProxyTest
{
private DateTime instanceCreation;
private String sServiceWsdl;
private String sContract;
private DynamicProxyFactory factory;
private volatile DynamicProxy proxy;
public ProxyTest(String sServiceWsdl, String sContract)
{
instanceCreation = DateTime.Now;
this.sServiceWsdl = sServiceWsdl;
this.sContract = sContract;
this.factory = new DynamicProxyFactory(this.sServiceWsdl);
this.proxy = factory.CreateProxy(this.sContract);
}
public DynamicProxy getProxy()
{
return proxy;
}
public TimeSpan getTimeFromCreation()
{
return DateTime.Now.Subtract(instanceCreation);
}
}
}
The problem is the webservice seems to reset the static status of FactoryTest after each call. So each time I called the webservice, my hashtable is empty and the factory create a new instance.
If anybody had already the problem of share datas between differents threads in WCF webservice (and found the solution), thanks in advance to give me some tips :)
PS : Sorry for my english, that's not my native language
If you store data in static variable WCF itself will not affect their purging. The problem must be somewhere else (application restart, app domain recreation, etc.).
Btw. this solution has only very limited usage because long living shared proxies should not be used and in many cases it can result in unexpected behavior. It can perhaps work only for services using basicHttpBinding.

How to create a winform app in visual studio 2010 to host a wcf service

I have a working skeleton WCF service. I want to host it in a winform app with a simple start and stop button.
This is how I host in a console app, easy to change to win app
public Program()
{
Console.WriteLine("This is the SERVER console");
var myUri = new Uri[1];
myUri[0] = new Uri(ConfigurationManager.AppSettings["baseAddress"]);
var timeEntryService = new WCFTimeEntryService();
var host = new ServiceHost(timeEntryService, myUri);
host.Open();
Console.WriteLine("Service Started!");
Console.WriteLine("Click any key to close...");
Console.ReadKey();
host.Close();
}
EDIT
First you need an interface that both client and server will use to communicate.
using System;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Data;
namespace TimeEntryService
{
[ServiceContract]
public interface ITimeEntry
{
[OperationContract]
string Ping();
}
}
Then you create the class that will do the work when a client calls.
using System.ServiceModel;
using System.Data;
namespace TimeEntryService
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class WCFTimeEntryService : ITimeEntry
{
public string Ping()
{
return "Pong";
}
}
}
Then make sure you update your App.config (Use WCF Service Configuration Editor)
In my VS2010 its under Tools -> Service Configuration Editor
(Not sure if you need to do something to get it to show there).
When it runs up, you can use the WCF Test Client to confirm its working.
C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\WcfTestClient.exe

Categories