Call an operation that was dynamically added to the service contract - c#

I have a WCF service contract (say IService1) to which I dynamically add an operation like described here.
How could I call a dynamically added operation from the client-side when all I have is an IService1 transparent proxy and the IClientChannel created via the ClientChannelFactory?
Update
I can get the RealProxy from the transparent proxy returned from the ChannelFactory using this method.
var realProxy = System.Runtime.Remoting.RemotingServices.GetRealProxy( transparentProxy );
Would it be possible to call realyProxy.Invoke(IMessage) with a fake message to trick the proxy into calling the dynamically added method?

Replace the GeneratePingMethod with this one:
private static void GenerateNewPingMethod(ServiceHost sh)
{
foreach (var endpoint in sh.Description.Endpoints)
{
ContractDescription contract = endpoint.Contract;
OperationDescription operDescr = new OperationDescription("Ping", contract);
MessageDescription inputMsg = new MessageDescription(contract.Namespace + contract.Name + "/Ping", MessageDirection.Input);
MessageDescription outputMsg = new MessageDescription(contract.Namespace + contract.Name + "/PingResponse", MessageDirection.Output);
MessagePartDescription retVal = new MessagePartDescription("PingResult", contract.Namespace);
retVal.Type = typeof(DateTime);
outputMsg.Body.WrapperName = "PingResponse";
outputMsg.Body.WrapperNamespace = contract.Namespace;
outputMsg.Body.ReturnValue = retVal;
operDescr.Messages.Add(inputMsg);
operDescr.Messages.Add(outputMsg);
operDescr.Behaviors.Add(new DataContractSerializerOperationBehavior(operDescr));
operDescr.Behaviors.Add(new PingImplementationBehavior());
contract.Operations.Add(operDescr);
}
}
and create your clients as such:
// this is your base interface
[ServiceContract]
public interface ILoginService
{
[OperationContract(Action = "http://tempuri.org/LoginService/Login", Name = "Login")]
bool Login(string userName, string password);
}
[ServiceContract]
public interface IExtendedInterface : ILoginService
{
[OperationContract(Action = "http://tempuri.org/LoginService/Ping", Name="Ping")]
DateTime Ping();
}
class Program
{
static void Main(string[] args)
{
IExtendedInterface channel = null;
EndpointAddress endPointAddr = new EndpointAddress("http://localhost/LoginService");
BasicHttpBinding binding = new BasicHttpBinding();
channel = ChannelFactory<IExtendedInterface>.CreateChannel(binding, endPointAddr);
if (channel.Login("test", "Test"))
{
Console.WriteLine("OK");
}
DateTime dt = channel.Ping();
Console.WriteLine(dt.ToString());
}
}

Related

Call to simple service fails with an apparently erroneous error message

When I call this service, I got this error message:
ContractDescription has zero operations; a contract must have at least
one operation
It makes no sense since my interface's function has the [OperationContract()] attribute defined.
The interface:
[ServiceContract()]
public interface ITest
{
[OperationContract()]
bool Connect(string password);
}
The SVC:
<%# ServiceHost Language="CS" Debug="true" Service="TestService.Test" CodeBehind="Test.svc.cs" %>
The svc.cs:
public class Test : ITest
{
public bool Connect(string password)
{
return true;
}
}
The call: the configuration is defined programatically because it is a library
public sealed class Validator
{
public static bool Connect(string password)
{
return ObtenirCLient().Connect(password);
}
private static LicensingService.LLMrqLicensingClient ObtenirCLient()
{
dynamic endpoint = new EndpointAddress("http://localhost/TestService/Test.svc");
LicensingService.LLMrqLicensingClient client = new LicensingService.LLMrqLicensingClient(ObtenirBinding(), endpoint);
client.Endpoint.Name = "LicHttp";
client.Endpoint.Contract = new Description.ContractDescription("TestService.ITest");
return client;
}
private static BasicHttpBinding ObtenirBinding()
{
return new BasicHttpBinding {
Name = "LicHttp",
Security = ObtenirSecurity()
};
}
private static BasicHttpSecurity ObtenirSecurity()
{
return new BasicHttpSecurity {
Mode = BasicHttpSecurityMode.TransportCredentialOnly,
Transport = ObtenirTransport()
};
}
private static HttpTransportSecurity ObtenirTransport()
{
return new HttpTransportSecurity { ClientCredentialType = HttpClientCredentialType.Windows };
}
}
If you see anything strange, please let me know!
Instead of
client.Endpoint.Contract = new Description.ContractDescription("TestService.ITest");
Try this:
client.Endpoint.Contract = ContractDescription.GetContract(typeof(ITest));

How to return from WCF service event per session to my main form

I Winform application that host WCF service with simple method that received string from client, this method open new instane of other class that open process and do stuff:
namespace ServiceLibrary
[ServiceContract()]
public interface IService1
{
[OperationContract]
string startProcess(string str);
}
[ServiceBehavior(
ConcurrencyMode = ConcurrencyMode.Multiple,
InstanceContextMode = InstanceContextMode.PerSession)]
public class service1 : IService1
{
public string startProcess(string str)
{
Jo job = new job();
job.Event += job_Event;
job.Start(str);
}
}
Inside Job class i have event that raised event with all my class properties (name, size etc...)
public delegate void StartEventHandler(Job obj);
public event StartEventHandler Event;
and from my service a im also subscride this event and from this event i want to send to my main form this object in order to uodate my UI:
job.Event += job_Event;
public void job_Event(Job obj)
{
// Send to to my main form and update UI
}
My problem is because my ServiceBehavior is ConcurrencyMode.Multiple and not Single i have several sessions of my service and i don't know how to raised an event in my form.
this is how i created my service from my main form:
urlService = "net.tcp://" + ipAddress.ToString() + ":8000/MyService";
ServiceHost new ServiceHost(typeof(ServiceLibrary.service1));
NetTcpBinding tcpBinding = new NetTcpBinding();
tcpBinding.TransactionFlow = false;
tcpBinding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
tcpBinding.Security.Mode = SecurityMode.None;
host.AddServiceEndpoint(typeof(ServiceLibrary.IService1), tcpBinding, urlService);
ServiceMetadataBehavior metadataBehavior;
metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
// Create the proxy object that is generated via the svcutil.exe tool
metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetUrl = new Uri("http://" + _ipAddress.ToString() + ":8001/MyService");
metadataBehavior.HttpGetEnabled = true;
metadataBehavior.ToString();
host.Description.Behaviors.Add(metadataBehavior);
urlMeta = metadataBehavior.HttpGetUrl.ToString();
}
host.Open();
You will need a class with static functions to access for your form. What about something like this?
public class MyFormFunctions
{
public static job_Event(Job obj)
{
/// do something to form
}
}
public string startProcess(string str)
{
Job job = new job();
MyFormFunctions.job_Event(job);
}

Pass to my form WCF client message

I have Winforms application that host WCF service.
this is my Button connect event:
private void btnConnect_Click(object sender, EventArgs e)
{
try
{
// Returns a list of ipaddress configuration
IPHostEntry ips = Dns.GetHostEntry(Dns.GetHostName());
// Get machine ipaddress
IPAddress _ipAddress = IPAddress.Parse(tbServerIp.Text);
// Create the url that is needed to specify where the service should be started
urlService = "net.tcp://" + _ipAddress.ToString() + ":8000/MyService";
// Instruct the ServiceHost that the type that is used is a ServiceLibrary.service1
host = new ServiceHost(typeof(ServiceLibrary.service1));
host.Opening += new EventHandler(host_Opening);
host.Opened += new EventHandler(host_Opened);
host.Closing += new EventHandler(host_Closing);
host.Closed += new EventHandler(host_Closed);
// The binding is where we can choose what transport layer we want to use. HTTP, TCP ect.
NetTcpBinding tcpBinding = new NetTcpBinding();
tcpBinding.TransactionFlow = false;
tcpBinding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
tcpBinding.Security.Mode = SecurityMode.None; // <- Very crucial
// Add a endpoint
host.AddServiceEndpoint(typeof(ServiceLibrary.IService1), tcpBinding, urlService);
// A channel to describe the service. Used with the proxy scvutil.exe tool
ServiceMetadataBehavior metadataBehavior;
metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
// This is how I create the proxy object that is generated via the svcutil.exe tool
metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetUrl = new Uri("http://" + _ipAddress.ToString() + ":8001/MyService");
metadataBehavior.HttpGetEnabled = true;
metadataBehavior.ToString();
host.Description.Behaviors.Add(metadataBehavior);
urlMeta = metadataBehavior.HttpGetUrl.ToString();
//pbIndicator.Image = Resources.indicator_green;
btnConnect.BackColor = Color.Red;
btnConnect.Text = "Stop";
}
host.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.StackTrace);
}
}
And this is my ServiceLibrary:
namespace ServiceLibrary
{
public delegate void StatusEventHandler(string srt);
[ServiceContract()]
public interface IService1
{
[OperationContract]
string MyOperation1(string myValue);
[OperationContract]
string MyOperation2(DataContract1 dataContractValue);
[OperationContract]
string HelloWorld(string str);
}
public class service1 : IService1
{
public event StatusEventHandler StartEvent;
public string MyOperation1(string myValue)
{
return "Hello: " + myValue;
}
public string MyOperation2(DataContract1 dataContractValue)
{
return "Hello: " + dataContractValue.FirstName;
}
public string HelloWorld(string str)
{
StartEvent(str);
//return "Helloworld from " + str;
}
}
[DataContract]
public class DataContract1
{
string firstName;
string lastName;
[DataMember]
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
[DataMember]
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
}
What i want to do is to pass the client message from HelloWorld function to my main form, so i try to create event inside class service1:
public delegate void StatusEventHandler(string srt);
public event StatusEventHandler StartEvent;
but it remained null even after registered from my main form (inside my button click event)
What is the simplest way to achieve that ?
When you created a service host, you gave a type, which means that with every call to your service, a new instance of that type will be created.
host = new ServiceHost(typeof(ServiceLibrary.service1));
You need to pass an instance of your service to the servicehost constructor, so every call will use that instance.
var yourServiceInstance = new ServiceLibrary.service1();
// attach event handlers here
host = new ServiceHost(yourServiceInstance);
When you do this, your service class needs to be configured for single instance mode:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class service1
{
// methods here
}

Windows Communication Foundation Null Reference Error

I am trying to use Windows Communication Foundation(WCF) for testing purposes in my application and I am getting a null object. I am new to using WCF, so any help would be appreciated.
From what I understand, each host has one service type(class). I have several interfaces and classes I want to test. My goal is to make one master class that has instances of the others, so I don't have to have several hosts running. Below is some of my code.
Host-
public class AutomationWCFHost
{
public ServiceHost host;
public AutomationWCFHost() {
Uri httpUrl = new Uri("http://localhost:8090/MyFun");
host = new ServiceHost(typeof(MyFun), httpUrl);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
}
public void StartWCF() {
this.host.Open();
}
public void StopWCF(){
this.host.Close();
Interfaces -
[ServiceContract]
public interface IMyFun {
[OperationContract]
string Echo(string msg);
IDataSources DataSources { [OperationContract]get; [OperationContract]set; }
}
[ServiceContract]
public interface IDataSources {
[OperationContract]
void Add(DataSource dataSource);
[OperationContract]
void Remove(DataSource dataSource);
}
Class - I am trying to make one base class so I don't have to have multiple hosts running.
public class MyFun : IMyFun {
public DataSources _dataSources;
public MyFun () {
_dataSources = new DataSources();
}
public string Echo(string msg) {
return msg;
}
public IDataSources DataSources { get { return _dataSources; } set {_dataSources = (DataSources)value;} }
}
public class DataSources : IDataSources{
....//Several methods and properties
}
Test -
public void MyTest() {
EndpointAddress address = new EndpointAddress("http://localhost:8090/MyFun");
BasicHttpBinding binding = new BasicHttpBinding();
ChannelFactory<IMyFun> factory = new ChannelFactory<IMyFun>(binding, address);
IMyFun channel = factory.CreateChannel();
Assert.AreEqual("Test", channel.Echo("Test"));
IDataSources dataSources = channel.DataSources;
dataSources.Add(newDataSource);
}
As I step through, it runs the echo method fine, but when it does
IDataSources dataSources = channel.DataSources;
dataSources is null. This makes it so
dataSources.Add(newDataSource);
fails due to null exception error.

send parameters in the WCF service constructor - error

I am trying to pass an object to the WCF service during creation - 'MasOperationsService'.
But I encounter an error and am unable to figure out why.
I am trying this piece of code from here...
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class MasOperationsService : IMasOperations
{
public MasOperationsService()
: this("INVALID")
{
throw new InvalidOperationException("This should never be called");
}
public MasOperationsService(string name)
{
}
//public CoAuthorSearchResult ExtractCoAuthorsFromAuthor(long AuthorCellId, uint LevelsToExtract)
//{
// //throw new NotImplementedException("Running This At Proxy, This should now query Slaves!!");
// return new CoAuthorSearchResult();
//}
}
public class MyInstanceProvider : IInstanceProvider
{
public object GetInstance(InstanceContext instanceContext, Message message)
{
string name = message.Headers.GetHeader<string>("Name", "http://my.namespace");
if (name != null)
{
return new MasOperationsService("Service " + name);
}
else
{
return new MasOperationsService("Service with no name");
}
}
public object GetInstance(InstanceContext instanceContext)
{
return new MasOperationsService("Service with no name");
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
}
public class MyServiceBehavior : IServiceBehavior
{
MyInstanceProvider myProvider = new MyInstanceProvider();
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
ed.DispatchRuntime.InstanceProvider = this.myProvider;
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
}
The MasOperationsService() is the service class. The Client code is LaunchWcfService()
public void LaunchWcfService()
{
string baseAddress = "http://localhost:8733/Design_Time_Addresses/MASService/Service1";
ServiceHost host = new ServiceHost(typeof(MasOperationsService), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(IMasOperations), GetBinding(), "");
host.Description.Behaviors.Add(new MyServiceBehavior());
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<IMasOperations> factory = new ChannelFactory<IMasOperations>(GetBinding(), new EndpointAddress(baseAddress));
IMasOperations proxy = factory.CreateChannel();
using (OperationContextScope scope = new OperationContextScope((IContextChannel)proxy))
{
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("Name", "http://my.namespace", "Name 1"));
//Console.WriteLine(proxy.Hello("foo"));
OperationContext.Current.OutgoingMessageHeaders.RemoveAll("Name", "http://my.namespace");
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("Name", "http://my.namespace", "Name 2"));
//Console.WriteLine(proxy.Hello("bar"));
}
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
static Binding GetBinding()
{
BasicHttpBinding result = new BasicHttpBinding();
return result;
}
Your problem is that you set InstanceContextMode = InstanceContextMode.Single.
This mode specify to use a single service instance across all service requests.
In this case, the WCF framework instanciate that singleton instance of your service at the moment you instantiate the ServiceHost, well before you could even try to plug your custom InstanceProvider and will use this same instance for all subsequent requests.
I'm not sure what you're trying to do but if the finality of all this is to have the values of your custom headers available inside any service implementation method, you're maybe better off using a base class for your methods input parameters using MessageContract instead of Datacontract.
[ServiceContract]
public interface IMasOperations
{
[OperationContract]
CoAuthorSearchResult ExtractCoAuthorsFromAuthor(ExtractCoAuthorsFromAuthorRequest request);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class MasOperationsService : IMasOperations
{
public CoAuthorSearchResult ExtractCoAuthorsFromAuthor(ExtractCoAuthorsFromAuthorRequest request)
{
Console.WriteLine("Name header accessed inside WS implementation though request.Name. Value = {0}.", request.Name);
return new CoAuthorSearchResult();
}
}
[MessageContract]
public class BaseRequest
{
[MessageHeader]
public string Name { get; set; }
}
[MessageContract]
public class ExtractCoAuthorsFromAuthorRequest : BaseRequest
{
[MessageBodyMember]
public long AuthorCellId { get; set; }
[MessageBodyMember]
public uint LevelsToExtract { get; set; }
}
[MessageContract]
public class CoAuthorSearchResult { }
public class Program
{
static readonly Binding _binding = new BasicHttpBinding();
public static void Main(string[] args)
{
string baseAddress = "http://localhost:8733/Design_Time_Addresses/MASService/Service1";
ServiceHost host = new ServiceHost(typeof(MasOperationsService), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(IMasOperations), _binding, string.Empty);
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<IMasOperations> factory = new ChannelFactory<IMasOperations>(_binding, new EndpointAddress(baseAddress));
IMasOperations channel = factory.CreateChannel();
CoAuthorSearchResult result = channel.ExtractCoAuthorsFromAuthor(new ExtractCoAuthorsFromAuthorRequest
{
Name = "http://my.namespace",
AuthorCellId = 0,
LevelsToExtract = 1,
});
ICommunicationObject o = channel as ICommunicationObject;
if (o != null)
{
if (o.State == CommunicationState.Opened)
{
o.Close();
}
else
{
o.Abort();
}
}
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
By default, WCF uses parameterless constructor. In order to use another constructor you should implement IInstanceProvider interface.
You can refer to the following question.
How do I pass values to the constructor on my wcf service?

Categories

Resources