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?
Related
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 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 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());
}
}
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();
}
}