I have a test project that test the Inherit contracts and callback contract.
Update 1 : I updated all of the topic with interfaces implamantations
By reading this article: http://codeidol.com/csharp/wcf/Service-Contracts/Contract-Inheritance/#part-16
This is possibol but when i try it, it fails.
class Program
{
static void Main(string[] args)
{
ServiceHost Service_IServer = new ServiceHost(new ServiceImplemantation(), new Uri(#"net.tcp://localhost:8080/"));
Service_IServer.AddServiceEndpoint(typeof(IService), new NetTcpBinding(), "Service");
Service_IServer.Open();
ServiceHost Service_I_IP = new ServiceHost(new IPImplemantation(), new Uri(#"net.tcp://localhost:8080/"));
Service_I_IP.AddServiceEndpoint(typeof(I_IP), new NetTcpBinding(), "Service");
Service_I_IP.Open();
Console.ReadLine();
}
}
[ServiceContract]
public interface I_IP
{
[OperationContract]
string GetIP();
}
[ServiceContract]
public interface IService : I_IP
{
[OperationContract]
void ImTheServer_Print();
}
//
//
//
//
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class IPImplemantation : I_IP
{
public string GetIP()
{
return "1.2.3.4";
}
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class ServiceImplemantation : IPImplemantation, IService
{
public void ImTheServer_Print()
{
Console.WriteLine("ImTheServer_Print");
}
}
The error:
The ChannelDispatcher at 'net.tcp://localhost:8080/Service' with contract(s) '"I_IP"' is unable to open its IChannelListener.
Ensure you decorate it as OneWay.
[OperationContract(IsOneWay = true)]
void GetIP();
Related
I have a WCF Windows Service that checks for MSMQ messages.
It picks the messages up ok but the ProcessMSMQMessage event does not seem to get called.
Any ideas why this is? Have I set ProcessMSMQMessage event correctly? Or am I missing something?
My code is below. Thanks.
WCF Service Class...
public partial class MyService : ServiceBase
{
private ServiceHost host;
public MyService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
string queueName = ConfigurationManager.AppSettings["ProcessMsgQueueName"];
if (!MessageQueue.Exists(queueName))
{
MessageQueue thisQueue = MessageQueue.Create(queueName, true);
thisQueue.SetPermissions("Everyone", MessageQueueAccessRights.ReceiveMessage);
}
try
{
Uri serviceUri = new Uri("msmq.formatname:DIRECT=OS:" + queueName);
// communicate to MSMQ how to transfer and deliver the messages
MsmqIntegrationBinding serviceBinding = new MsmqIntegrationBinding();
serviceBinding.Security.Transport.MsmqAuthenticationMode = MsmqAuthenticationMode.None;
serviceBinding.Security.Transport.MsmqProtectionLevel = System.Net.Security.ProtectionLevel.None;
serviceBinding.SerializationFormat = MsmqMessageSerializationFormat.Binary;
host = new ServiceHost(typeof(MyService.Service1)); // add watcher class name
host.AddServiceEndpoint(typeof(MyService.IService1), serviceBinding, serviceUri);
host.Open();
}
catch (Exception ex)
{
EventLog.WriteEntry("SERVICE" + ex.Message, EventLogEntryType.Error);
}
}
protected override void OnStop()
{
if (host != null)
host.Close();
}
}
IService1 Contract...
[ServiceContract(Namespace = "MyService")]
[ServiceKnownType(typeof(Events.Dashboard_Message))]
public interface IService1
{
[OperationContract(IsOneWay = true)]
void ProcessMSMQMessage(MsmqMessage<Events.Dashboard_Message> msg);
}
Service1 Class...
public class Service1 : IService1
{
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void ProcessMSMQMessage(MsmqMessage<Events.Dashboard_Message> msg)
{
string msgName = msg.GetType().Name;
// send to eventlog
EventLog.WriteEntry("MyService", msgName);
}
}
Got it working finally.
The issue was in IService1 contract. Needed to add Action = "*".
[OperationContract(IsOneWay = true, Action = "*")]
I have the following types:
public enum MyEnum
{
Value1,
Value2
}
[DataContract]
public class Configuration
{
[DataMember]
public MyEnum MyValue { get; set; }
[DataMember]
public Credentials CredentialValues { get; set; }
}
[DataContract, KnownType(typeof(CustomCredentials))]
public class Credentials
{
}
[DataContract]
public class CustomCredentials : Credentials
{
[DataMember]
public string Property1 { get; set; }
[DataMember]
public string Property2 { get; set; }
}
And on my service interface, I have a function that returns an instance of Configuration with its CredentialValues property set to a fully populated instance of CustomCredentials. I receive no errors from the client or the server, but while the data is being property serialized on the server and received by the client, the properties on CustomCredentials never have a value. What do I need to change here in order to have these properties properly deserialized on the client?
For reference, the connection between client and server is made with a DuplexChannelFactory over a NetTcpBinding using a data/service contract project that is shared by the client and service applications (the service is self-hosted), so there are no service proxy types that could need to be regenerated.
Added this code to the Contracts project along with your DataContracts.
[ServiceContract(Namespace = "http://schemas.platinumray.com/duplex", SessionMode = SessionMode.Required, CallbackContract = typeof(IService1Callback))]
public interface IService1
{
[OperationContract(IsOneWay = true)]
void GetData();
}
public interface IService1Callback
{
[OperationContract(IsOneWay = true)]
void SetData(Configuration config);
}
Created the service.
public class Service1 : IService1
{
public void GetData()
{
var x = new Configuration()
{
MyValue = MyEnum.Value1,
CredentialValues = new CustomCredentials { Property1 = "Something", Property2 = "Something else" }
};
OperationContext.Current.GetCallbackChannel<IService1Callback>().SetData(x);
}
}
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost( typeof(Service1), new Uri[] { new Uri("net.tcp://localhost:6789") }))
{
host.AddServiceEndpoint(typeof(IService1), new NetTcpBinding(), "Service1");
host.Open();
Console.ReadLine();
host.Close();
}
}
}
Created the client.
public class CallbackHandler : IService1Callback
{
public void SetData(Configuration config)
{
Console.WriteLine(config.CredentialValues.GetType().Name);
Console.WriteLine(((CustomCredentials)config.CredentialValues).Property1);
Console.WriteLine(((CustomCredentials)config.CredentialValues).Property2);
}
}
class Program
{
static void Main(string[] args)
{
// Setup the client
var callbacks = new CallbackHandler();
var endpoint = new EndpointAddress(new Uri("net.tcp://localhost:6789/Service1"));
using (var factory = new DuplexChannelFactory<IService1>(callbacks, new NetTcpBinding(), endpoint))
{
var client = factory.CreateChannel();
client.GetData();
Console.ReadLine();
factory.Close();
}
}
}
Outputs the following as expected:
CustomCredentials
Something
Something else
So this actually worked without modifying any of your data contracts... The same results if I revert to a twoway operation and just return Configuration directly without using the callback.
Also tried making Credentials abstract but could not replicate your problem.
Have I missed something?
First the code:
[ServiceContract]
public interface IWorker
{
[OperationContract]
void Process(XmlElement data);
[OperationContract]
void Update(Rule rule);
}
[DataContract]
public class Rule
{
[OperationContract]
public string Expression { get; set; }
[OperationContract]
public List<IAction> Actions { get; set; }
}
public interface IAction
{
void Execute(XmlElement data);
}
A dispatcher encodes data as xml and sends it to an IWorker instance where each expression is evaluated. When an IWorker instance evaluates an expression as true, IAction.Execute is called and the xml/data is passed.
What's the best way to serialize Rule.Actions? I've started writing a custom serializer but I'd prefer to see if there is an easier way.
Thanks.
I dont think you can use interfaces in DataContracts (someone correct me if im wrong, but i assume thats like trying to use a generic too). What I do, is have a parent class, then add the KnownType attribute. For instance
[DataContract]
public class Action
{
//members and properties
}
[DataContract]
public class SomeOtherAction:Action
{
//more implimentation
}
[DataContract]
[KnownType(typeof(SomeOtherAction))]
public class Rule
{
[DataMember]
List<Action> Actions{get;set;}
}
Now you can stuff any object that inherits from the parent Action object in to the Actions list, and it will properly serialize all their respective class properties (as long as the object is listed as a knowntype).
*I used "Action" name as an example to relate to yours, obviously Action is a keyword in .NET
Serialization is the process of converting between an object data and bytes which can be transferred over the wire. Interfaces define behavior, so by default WCF can't serialize such data. If you have the exact same assemblies on the client and the server, however, you can use the NetDataContractSerializer, which will essentially serialize (and be able to serialize) all the type information for the objects being serialized, so it can be recreated at the other side.
The code below shows how to use the NetDataContractSerializer in a service for that (based on the main example for this, the post from Aaron Skonnard at http://www.pluralsight-training.net/community/blogs/aaron/archive/2006/04/21/22284.aspx)
public class StackOverflow_6932356
{
[ServiceContract]
public interface IWorker
{
[OperationContract]
void Process(XmlElement data);
[OperationContract]
void Update(Rule rule);
}
[DataContract]
public class Rule
{
[DataMember]
public string Expression { get; set; }
[DataMember]
public List<IAction> Actions { get; set; }
}
public interface IAction
{
void Execute(XmlElement data);
}
public class Service : IWorker
{
static List<IAction> AllActions = new List<IAction>();
public void Process(XmlElement data)
{
foreach (var action in AllActions)
{
action.Execute(data);
}
}
public void Update(Rule rule)
{
AllActions = rule.Actions;
}
}
public class Action1 : IAction
{
public void Execute(XmlElement data)
{
Console.WriteLine("Executing {0} for data: {1}", this.GetType().Name, data.OuterXml);
}
}
public class Action2 : IAction
{
public void Execute(XmlElement data)
{
Console.WriteLine("Executing {0} for data: {1}", this.GetType().Name, data.OuterXml);
}
}
class NetDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
public NetDataContractSerializerOperationBehavior(OperationDescription operationDescription)
: base(operationDescription) { }
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer(name, ns);
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer(name, ns);
}
}
static void ReplaceDCSOB(ServiceEndpoint endpoint)
{
foreach (var operation in endpoint.Contract.Operations)
{
for (int i = 0; i < operation.Behaviors.Count; i++)
{
if (operation.Behaviors[i] is DataContractSerializerOperationBehavior)
{
operation.Behaviors[i] = new NetDataContractSerializerOperationBehavior(operation);
break;
}
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IWorker), new BasicHttpBinding(), "");
ReplaceDCSOB(endpoint);
host.Open();
Console.WriteLine("Host opened");
var factory = new ChannelFactory<IWorker>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
ReplaceDCSOB(factory.Endpoint);
var proxy = factory.CreateChannel();
proxy.Update(new Rule
{
Expression = "Expr",
Actions = new List<IAction> { new Action1(), new Action2() }
});
XmlDocument doc = new XmlDocument();
doc.LoadXml("<root><foo>bar</foo></root>");
proxy.Process(doc.DocumentElement);
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
Here is the server code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.ServiceModel.Description;
namespace Console_Chat
{
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IMyCallbackContract))]
public interface IMyService
{
[OperationContract(IsOneWay = true)]
void NewMessageToServer(string msg);
[OperationContract(IsOneWay = false)]
bool ServerIsResponsible();
}
[ServiceContract]
public interface IMyCallbackContract
{
[OperationContract(IsOneWay = true)]
void NewMessageToClient(string msg);
[OperationContract(IsOneWay = true)]
void ClientIsResponsible();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyService : IMyService
{
public IMyCallbackContract callback = null;
/*
{
get
{
return OperationContext.Current.GetCallbackChannel<IMyCallbackContract>();
}
}
*/
public MyService()
{
callback = OperationContext.Current.GetCallbackChannel<IMyCallbackContract>();
}
public void NewMessageToServer(string msg)
{
Console.WriteLine(msg);
}
public void NewMessageToClient( string msg)
{
callback.NewMessageToClient(msg);
}
public bool ServerIsResponsible()
{
return true;
}
}
class Server
{
static void Main(string[] args)
{
String msg = "none";
ServiceMetadataBehavior behavior = new
ServiceMetadataBehavior();
ServiceHost serviceHost = new
ServiceHost(
typeof(MyService),
new Uri("http://localhost:8080/"));
serviceHost.Description.Behaviors.Add(behavior);
serviceHost.AddServiceEndpoint(
typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexHttpBinding(),
"mex");
serviceHost.AddServiceEndpoint(
typeof(IMyService),
new WSDualHttpBinding(),
"ServiceEndpoint"
);
serviceHost.Open();
Console.WriteLine("Server is up and running");
MyService server = new MyService();
server.NewMessageToClient("Hey client!");
/*
do
{
msg = Console.ReadLine();
// callback.NewMessageToClient(msg);
} while (msg != "ex");
*/
Console.ReadLine();
}
}
}
Here is the client's:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.ServiceModel.Description;
using Console_Chat_Client.MyHTTPServiceReference;
namespace Console_Chat_Client
{
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IMyCallbackContract))]
public interface IMyService
{
[OperationContract(IsOneWay = true)]
void NewMessageToServer(string msg);
[OperationContract(IsOneWay = false)]
bool ServerIsResponsible();
}
[ServiceContract]
public interface IMyCallbackContract
{
[OperationContract(IsOneWay = true)]
void NewMessageToClient(string msg);
[OperationContract(IsOneWay = true)]
void ClientIsResponsible();
}
public class MyCallback : Console_Chat_Client.MyHTTPServiceReference.IMyServiceCallback
{
static InstanceContext ctx = new InstanceContext(new MyCallback());
static MyServiceClient client = new MyServiceClient(ctx);
public void NewMessageToClient(string msg)
{
Console.WriteLine(msg);
}
public void ClientIsResponsible()
{
}
class Client
{
static void Main(string[] args)
{
String msg = "none";
client.NewMessageToServer(String.Format("Hello server!"));
do
{
msg = Console.ReadLine();
if (msg != "ex")
client.NewMessageToServer(msg);
else client.NewMessageToServer(String.Format("Client terminated"));
} while (msg != "ex");
}
}
}
}
callback = OperationContext.Current.GetCallbackChannel();
This line constanly throws a NullReferenceException, what's the problem?
Thanks!
You can't just start a WCF service with a callback contract and immediately try to execute a client callback. There are no clients yet.
In your code, I see you manually instantiating a MyService and trying to execute a callback method. This simply won't work. If you want to use the GetCallbackChannel method then it has to be done when there is actually a channel - i.e. in the context of an actual operation invoked by a remote WCF client. Otherwise, there is no current OperationContext and you'll get a null reference exception because OperationContext.Current returns null.
Callbacks are intended to be used with long-running service operations. For example:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyService : IMyService
{
// One-way method
public void PerformLongRunningOperation()
{
var callback =
OperationContext.Current.GetCallbackChannel<IMyCallbackContract>();
var result = DoLotsOfWork();
callback.LongRunningOperationFinished(result);
}
}
To test this you would have to actually create a client - start a new project, add a reference to this service, implement the callback that the importer generates, create an InstanceContext with the callback, create the client proxy using that InstanceContext, and finally invoke its PerformLongRunningOperation method.
If you are trying to develop a pub/sub implementation, where clients do not actually initiate the operations but simply register themselves to receive some callback, have a look at this page: Using Callback Contracts in WCF for Asynchronous Publish/Subscribe Event-Style Communication.
How can I obtain the domain name or full URL of the requester?
I'm not sure that I understand your question, but if you need the domain name of the Windows user making the call to a service operation, use this:
OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name
This will return "{domain}\{username}".
Try this and let me know what you think (you'll probably want to paste this code into an mstest project):
[TestClass]
public class AlternativeCredentials
{
// Contracts
[ServiceContract]
interface IMyContract
{
[OperationContract]
string GetUserName();
}
// Service
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
class MyService : IMyContract
{
public string GetUserName()
{
return OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name;
}
}
// Client
class MyContractClient : ClientBase<IMyContract>, IMyContract
{
public MyContractClient() { }
public MyContractClient(Binding binding, string address) :
base(binding, new EndpointAddress(address)) { }
public string GetUserName()
{ return Channel.GetUserName(); }
}
#region Host
static string address = "net.tcp://localhost:8001/" + Guid.NewGuid().ToString();
static ServiceHost host;
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext)
{
host = new ServiceHost(typeof(MyService));
host.AddServiceEndpoint(typeof(IMyContract), new NetTcpBinding(), address);
host.Open();
}
[ClassCleanup()]
public static void MyClassCleanup()
{
if (host.State == CommunicationState.Opened)
host.Close();
}
#endregion
[TestMethod]
public void UseUserNameCredentials()
{
using (MyContractClient proxy =
new MyContractClient(new NetTcpBinding(), address))
{
proxy.ClientCredentials.UserName.UserName = "MyUsername";
proxy.ClientCredentials.UserName.Password = "MyPassword";
proxy.Open();
Assert.AreEqual("EMS\\magood", proxy.GetUserName());
proxy.Close();
}
}
}