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.
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 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
}
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.
I have a datacontact with many members that has a custom class
I would like to force a new instance if the property is null on deserialization.
is there a way to do that?
If your are using DataContract serialization then you can override its default behaviour using the OnDeserialized attribute.
From MSDN: When applied to a method, specifies that the method is called during deserialization of an object in an object graph. The order of deserialization relative to other objects in the graph is non-deterministic.
Here is my sample code:
namespace MySpace
{
public class MyCustomClass
{
public string MyStrData { get; set; }
}
[DataContract]
public class Data
{
[DataMember]
public int mInt;
[DataMember]
public MyCustomClass MyCustonObj;
[OnDeserialized]
void OnDeserialized(StreamingContext c)
{
if (MyCustonObj == null)
{
MyCustonObj = new MyCustomClass();
MyCustonObj.MyStrData = "Overridden in serialization";
}
}
[OnDeserializing]
void OnDeserializing(StreamingContext c)
{
if (MyCustonObj == null)
{
MyCustonObj = new MyCustomClass();
MyCustonObj.MyStrData = "Overridden in deserializing";
}
}
[OnSerialized]
void OnSerialized(StreamingContext c)
{
// if you wan to do somehing when serialized here or just remove them
}
[OnSerializing]
void OnSerializing(StreamingContext c)
{
// if you wan to do somehing during serializing here or just remove them
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
Data Method(Data dd);
}
public class Service : IService
{
public Data Method(Data dd)
{
return dd;
}
}
class Program
{
static void Main(string[] args)
{
string Url = "http://localhost:8000/";
Binding binding = new BasicHttpBinding();
ServiceHost host = new ServiceHost(typeof(Service));
host.AddServiceEndpoint(typeof(IService), binding, Url);
host.Open();
ChannelFactory<IService> fac = new ChannelFactory<IService>(binding);
fac.Open();
IService proxy = fac.CreateChannel(new EndpointAddress(Url));
Data d = new Data();
d.mInt = 5;
Console.WriteLine("Data before calling service " + d.mInt);
Console.WriteLine("Data before calling service " + (d.MyCustonObj == null ? "null" : d.MyCustonObj.MyStrData));
d = proxy.Method(d);
fac.Close();
host.Close();
Console.WriteLine("Data after calling service " + d.mInt);
Console.WriteLine("Data after calling service " + d.MyCustonObj.MyStrData);
Console.ReadLine();
}
}