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.
Related
I have the following strange issue with WCF, for which I cannot figure out the cause:
I am working with WCF to figure out, whether to use it for a remote-controlling API that I need to implement for a printer-like device. The device is controlled by a Windows-PC that runs the controller-software implemented in .Net. It for this software that I need to implement the API.
The service is self-hosting from inside the controller-software and I am currently figuring out how I can create a singleton-instance of the WCF service, so that I can create this instance with corresponding objects/classes from withing controller-software. I have gotten this to work using a reduced version, but oddly enough I am getting this warning, if the service does not include a default (parameter-less) constructor. Even weirder, I am doing exactly, what the exception is telling me in the second sentence (or at least I like to think I am). This exception is thrown in separate window with title WCF Service Host and the program continues to execute normally afterwards:
System.InvalidOperationException: The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.
at System.ServiceModel.Description.ServiceDescription.CreateImplementation(Type serviceType)
at System.ServiceModel.Description.ServiceDescription.SetupSingleton(ServiceDescription serviceDescription, Object implementation, Boolean isWellKnown)
at System.ServiceModel.Description.ServiceDescription.GetService(Type serviceType)
at System.ServiceModel.ServiceHost.CreateDescription(IDictionary`2& implementedContracts)
at System.ServiceModel.ServiceHostBase.InitializeDescription(UriSchemeKeyedCollection baseAddresses)
at System.ServiceModel.ServiceHost..ctor(Type serviceType, Uri[] baseAddresses)
at Microsoft.Tools.SvcHost.ServiceHostHelper.CreateServiceHost(Type type, ServiceKind kind)
at Microsoft.Tools.SvcHost.ServiceHostHelper.OpenService(ServiceInfo info)
Here is the code that I am using to create the service. I commented the commented line in Service.cs, that contains the default constructor. Interestingly, when I include the default constructor (and therefore the error is never thrown) it is never called (I confirmed that by setting a breakpoint). I you uncommented it, the exception is not thrown.
Server.cs:
public class Server
{
private ServiceHost svh;
private Service service;
public Server()
{
service = new Service("A fixed ctor test value that the service should return.");
svh = new ServiceHost(service);
}
public void Open(string ipAdress, string port)
{
svh.AddServiceEndpoint(
typeof(IService),
new NetTcpBinding(),
"net.tcp://"+ ipAdress + ":" + port);
svh.Open();
}
public void Close()
{
svh.Close();
}
}
Service.cs:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant,
InstanceContextMode = InstanceContextMode.Single)]
public class Service : IService
{
private string defaultString;
public Service(string ctorTestValue)
{
this.defaultString = ctorTestValue;
}
//// when this constructor is uncommented, I do not get the error
//public Service()
//{
// defaultString = "Default value from the ctor without argument.";
//}
public string GetDefaultString()
{
return defaultString;
}
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
public string Ping(string name)
{
Console.WriteLine("SERVER - Processing Ping('{0}')", name);
return "Hello, " + name;
}
static Action m_Event1 = delegate { };
static Action m_Event2 = delegate { };
public void SubscribeEvent1()
{
IMyEvents subscriber = OperationContext.Current.GetCallbackChannel<IMyEvents>();
m_Event1 += subscriber.Event1;
}
public void UnsubscribeEvent1()
{
IMyEvents subscriber = OperationContext.Current.GetCallbackChannel<IMyEvents>();
m_Event1 -= subscriber.Event1;
}
public void SubscribeEvent2()
{
IMyEvents subscriber = OperationContext.Current.GetCallbackChannel<IMyEvents>();
m_Event2 += subscriber.Event2;
}
public void UnsubscribeEvent2()
{
IMyEvents subscriber = OperationContext.Current.GetCallbackChannel<IMyEvents>();
m_Event2 -= subscriber.Event2;
}
public static void FireEvent1()
{
m_Event1();
}
public static void FireEvent2()
{
m_Event2();
}
public static Timer Timer1;
public static Timer Timer2;
public void OpenSession()
{
Timer1 = new Timer(1000);
Timer1.AutoReset = true;
Timer1.Enabled = true;
Timer1.Elapsed += OnTimer1Elapsed;
Timer2 = new Timer(500);
Timer2.AutoReset = true;
Timer2.Enabled = true;
Timer2.Elapsed += OnTimer2Elapsed;
}
void OnTimer1Elapsed(object sender, ElapsedEventArgs e)
{
FireEvent1();
}
void OnTimer2Elapsed(object sender, ElapsedEventArgs e)
{
FireEvent2();
}
}
IServices.cs:
public interface IMyEvents
{
[OperationContract(IsOneWay = true)]
void Event1();
[OperationContract(IsOneWay = true)]
void Event2();
}
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract(CallbackContract = typeof(IMyEvents))]
public interface IService
{
[OperationContract]
string GetData(int value);
[OperationContract]
string GetDefaultString();
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
// TODO: Add your service operations here
[OperationContract]
string Ping(string name);
[OperationContract]
void SubscribeEvent1();
[OperationContract]
void UnsubscribeEvent1();
[OperationContract]
void SubscribeEvent2();
[OperationContract]
void UnsubscribeEvent2();
[OperationContract]
void OpenSession();
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
// You can add XSD files into the project. After building the project, you can directly use the data types defined there, with the namespace "WcfService.ContractType".
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
Main for starting the server:
static void Main(string[] args)
{
// start server
var server = new Server();
server.Open("localhost", "6700");
Console.WriteLine("Server started.");
Console.ReadLine();
server.Close();
}
The problem is caused by the WcfSvcHost running while you're debugging in Visual Studio. According to this, "WCF Service Host enumerates the services in a WCF service project, loads the project’s configuration, and instantiates a host for each service that it finds. The tool is integrated into Visual Studio through the WCF Service template and is invoked when you start to debug your project."
You don't need to use the WCF Service Host since you're self-hosting, so you can disable it through the project properties page for the project containing the service. You should see a tab for "WCF Options" on the property page. On that, turn off the "Start WCF Service Host when debugging ..." option.
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));
I am trying to create a wcf service host with the help of code only(no config is involved).
I have a static int and InstanceContextMode.PerCall set for the wcftestservice. As per the tutorial provided on the internet, I should be having same value for every call to the wcf?
Note: I have tested this behavior in console application as well as windows service and I am testing the behavior with the help of wcf test client
Here is the code:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class WcfServiceTest : IContract
{
static int _counter;
public string GetData(string p1)
{
return Convert.ToString(_counter++);
}
}
Contract:
[ServiceContract]
public interface IContract
{
[OperationContract]
string GetData(string p1);
}
The service host code:
static ServiceHost _servicehost;
static void Main(string[] args)
{
string tcpPort = "8081";
string httpPort = "8888";
string urlWithoutProtocol = "{0}://localhost:{1}/WcfServiceTest";
string netTcpAddress = string.Format(urlWithoutProtocol, "net.tcp", tcpPort);
string httpAddress = string.Format(urlWithoutProtocol, "http", httpPort);
string netTcpMexAddress = netTcpAddress + "/mex";
string httpMexAddress = httpAddress + "/mex";
if (_servicehost != null)
{
_servicehost.Close();
}
_servicehost = new ServiceHost(typeof(wcftest.WcfServiceTest));
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
//smb.HttpGetUrl = httpUri;
//smb.HttpGetEnabled = true;
_servicehost.Description.Behaviors.Add(smb);
_servicehost.AddServiceEndpoint(typeof(wcftestcontract.IContract), new NetTcpBinding(), netTcpAddress);
Binding mexTcpBinding = MetadataExchangeBindings.CreateMexTcpBinding();
_servicehost.AddServiceEndpoint(typeof(IMetadataExchange), mexTcpBinding, netTcpMexAddress);
_servicehost.AddServiceEndpoint(typeof(wcftestcontract.IContract), new BasicHttpBinding(), httpAddress);
Binding mexHttpBinding = MetadataExchangeBindings.CreateMexHttpBinding();
_servicehost.AddServiceEndpoint(typeof(IMetadataExchange), mexHttpBinding, httpMexAddress);
_servicehost.Open();
Console.ReadKey();
}
If you want to check that it creates only once for Singleton mode, and each time for PerCall mode, then you need to increment your variable in constructor:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class WcfServiceTest : IContract
{
static int _counter;
public WcfServiceTest()
{
_counter++;
}
public string GetData(string p1)
{
return Convert.ToString(_counter);
}
}
Then for each call number will increase for PerCall, and it won't increase for Singleton mode
Yes, tutorial has a bug. In their sample should be this without static modifier:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class MyService:IMyService
{
int m_Counter = 0;
public int MyMethod()
{
m_Counter++;
return m_Counter;
}
}
Static variable means that it should be the same for all instances of object. So, for static variable there is no difference between PerCall and Signleton modes, as it will be the same.
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);
}
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?