how to find the service address in WCF client - wcf

I created a WCF service and client in same machine, the services address is wrote into Client's code, so I can easily find the service and create connection to service.
Then I try to deploy them into Intranet. The first problem is: how could Client find the address of server. In actual environment, customers can install service at any computer in Intranet, is there any way to let client find the server address?

WCF service could expose a specific endpoint as a discovery endpoint to all clients so that client could find where the service lies. You could even use UDP multicast to enable the service to be discovered by the client.
You could check the official document.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/wcf-discovery
I have made a demo, wish it is useful to you.
Server.
class Program
{
static void Main(string[] args)
{
using (ServiceHost sh=new ServiceHost(typeof(MyService)))
{
sh.Open();
Console.WriteLine("serivce is ready...");
Console.ReadLine();
sh.Close();
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string SayHello();
}
public class MyService : IService
{
public string SayHello()
{
return "Hello, I am a Clown";
}
}
Server app.config
<system.serviceModel>
<services>
<service name="DiscoveryEndpoint20181024.MyService" behaviorConfiguration="mybehavior">
<endpoint address="http://10.157.18.188:4800" binding="wsHttpBinding" contract="DiscoveryEndpoint20181024.IService"></endpoint>
<endpoint kind="discoveryEndpoint" address="http://localhost:9999" binding="wsHttpBinding"></endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="mybehavior">
<serviceMetadata />
<serviceDiscovery />
</behavior>
</serviceBehaviors>
</behaviors>
Client.
class Program
{
static void Main(string[] args)
{
DiscoveryClient client = new DiscoveryClient("my_client");
client.ClientCredentials.Windows.ClientCredential.UserName = "administrator";
client.ClientCredentials.Windows.ClientCredential.Password = "abcd1234!";
FindCriteria crit = new FindCriteria(typeof(IService));
FindResponse resp = client.Find(crit);
if (resp != null && resp.Endpoints.Count > 0)
{
EndpointDiscoveryMetadata epaddrMtd = resp.Endpoints[0];
ChannelFactory<IService> factory = new ChannelFactory<IService>(new WSHttpBinding(), epaddrMtd.Address);
factory.Credentials.Windows.ClientCredential.UserName = "administrator";
factory.Credentials.Windows.ClientCredential.Password = "abcd1234!";
IService service = factory.CreateChannel();
var result=service.SayHello();
Console.WriteLine(result);
Console.ReadLine();
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string SayHello();
}
class DemoService : IService
{
public string SayHello()
{
OperationContext context = OperationContext.Current;
return $"the address:{OperationContext.Current.Channel.LocalAddress.Uri}";
}
}
Client.config
<system.serviceModel>
<client>
<endpoint name="my_client" kind="discoveryEndpoint" address="http://10.157.18.188:9999" binding="wsHttpBinding"></endpoint>
</client>
</system.serviceModel>

Related

Declaratively configure WCF behavior extension on programmatically constructed endpoint

I have a WCF behavior extension that I would like to add to a WCF client. However, the client is constructed programmatically. The endpoint address may vary, but I know the type. I could add the behavior programmatically or in the config file (preferred), but I need to pass some configuration in the config file only.
I don't want this in Common behaviors (machine.config).
I can add the behavior programmatically
endpoint.Behaviors.Add(new MyCustomBehavior())
But I'd rather do it in config, so I can configure the extension there as well.
Is it possible to declaratively add and configure an endpoint behavior extension to a programmatically constructed endpoint knowing only the the type or interface while leaving the client endpoint to be constructed programmatically?
<system.serviceModel>
<client>
<!-- Created programmatically -->
</client>
<extensions>
<behaviorExtensions>
<add name="MyCustomBehavior" type="namespace.CustomBehaviors", MyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="MyCustomBehavior">
<MyCustomBehavior MyImportantBehaviorParam1="foo" />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
Of course I could put the config in another section, and have my behavior read it there, but I'd rather use the WCF facilities if possible.
To do that you need to create a behavior configuration extension for your endpoint. For more information on how to do that, check https://learn.microsoft.com/en-us/archive/blogs/carlosfigueira/wcf-extensibility-behavior-configuration-extensions.
Update: I see your issue now. There's no direct way to add to an endpoint created via code a behavior declared in configuration. You can still do it, though, but you'll need to use some reflection to access the CreateBehavior method of the behavior configuration extension (the method is protected) to actually create the endpoint behavior to add it to the endpoint created via code. The code below shows how this can be done.
public class StackOverflow_10232385
{
public class MyCustomBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
Console.WriteLine("In {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
Console.WriteLine("In {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
Console.WriteLine("In {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
}
public void Validate(ServiceEndpoint endpoint)
{
Console.WriteLine("In {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
}
}
public class MyCustomBehaviorExtension : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(MyCustomBehavior); }
}
protected override object CreateBehavior()
{
return new MyCustomBehavior();
}
}
[ServiceContract]
public interface ITest
{
[OperationContract]
string Echo(string text);
}
public class Service : ITest
{
public string Echo(string text)
{
return text;
}
}
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(ITest), new BasicHttpBinding(), "");
var configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ServiceModelSectionGroup smsg = configuration.GetSectionGroup("system.serviceModel") as ServiceModelSectionGroup;
EndpointBehaviorElement endpointBehaviorElement = smsg.Behaviors.EndpointBehaviors["MyCustomBehavior_10232385"];
foreach (BehaviorExtensionElement behaviorElement in endpointBehaviorElement)
{
MethodInfo createBehaviorMethod = behaviorElement.GetType().GetMethod("CreateBehavior", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Type.EmptyTypes, null);
IEndpointBehavior behavior = createBehaviorMethod.Invoke(behaviorElement, new object[0]) as IEndpointBehavior;
endpoint.Behaviors.Add(behavior);
}
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.Echo("Hello"));
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
And the configuration for this code:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="myCustomBehavior_10232385" type="QuickCode1.StackOverflow_10232385+MyCustomBehaviorExtension, QuickCode1"/>
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="MyCustomBehavior_10232385">
<myCustomBehavior_10232385/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>

MSMQ WCF Throttling

I have a windows service that reads my message queue through WCF. I want the service to process one message before another message (intensive memory actions per msg). I set the throttling configuration to 1, but it does not seem to do anything. If i have 6 messages in my queue, it takes 4 right after the start.
Am i missing something?
My web.config :
<system.serviceModel>
<client>
<endpoint
address="net.tcp://spserv30:9999/services/SPInterface"
binding="netTcpBinding" bindingConfiguration="tcpspbinding"
contract="Itineris.OPM.WCFSP.ActionContracts.ISPActions" >
</endpoint>
</client>
<services>
<service name="Itineris.OPM.MSMQProcessorV2.MSMQProcessor" behaviorConfiguration="Throttled" >
<endpoint address="msmq.formatname:DIRECT=OS:localhost\private$\documents" binding="msmqIntegrationBinding"
bindingConfiguration="MSMQProcessorBinding" contract="Itineris.OPM.MSMQProcessorV2.IMSMQProcessor" />
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="tcpspbinding" transferMode="StreamedRequest" />
</netTcpBinding>
<msmqIntegrationBinding>
<binding name="MSMQProcessorBinding" maxReceivedMessageSize="2147483647"
receiveRetryCount="0" retryCycleDelay="00:10:00" maxRetryCycles="0"
receiveErrorHandling="Move">
<security mode="None" />
</binding>
</msmqIntegrationBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="Throttled">
<serviceThrottling
maxConcurrentCalls="1"
maxConcurrentSessions="1"
maxConcurrentInstances="1"
/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
My servicehost creation :
protected override void OnStart(string[] args)
{
if (_serviceHost != null)
{
if (_serviceHost.State != CommunicationState.Faulted)
_serviceHost.Close();
else
_serviceHost.Abort();
}
//create servicehost
_serviceHost = new ServiceHost(typeof(MSMQProcessor));
_serviceHost.Open();
_serviceHost.Faulted += serviceHost_Faulted;
// Already load configuration here so that service does not start if there is a configuration error.
new DocumentGeneratorV2.LoadGeneratorConfigurator().Load();
var startLog = new LogEntry {Message = "Itineris MSMQ Processor Service V2 has started"};
startLog.Categories.Add(CategoryGeneral);
startLog.Priority = PriorityNormal;
Logger.Write(startLog);
}
private void serviceHost_Faulted(object sender, EventArgs e)
{
if (!_isClosing)
{
_serviceHost.Abort();
_serviceHost = new ServiceHost(typeof(MSMQProcessor));
_serviceHost.Faulted += serviceHost_Faulted;
_serviceHost.Open();
}
}
Class with contract :
[ServiceContract(Namespace = "http://Itineris.DocxGenerator.MSMQProcessor")]
[ServiceKnownType(typeof(string))]
public interface IMSMQProcessor
{
[OperationContract(IsOneWay = true, Action = "*")]
void GenerateWordDocument(MsmqMessage<string> message);
}
public class MSMQProcessor : IMSMQProcessor
{
/// <summary>
/// Method that processed the message and generates a word document
/// </summary>
/// <param name="message">message from MSMQ to be processed</param>
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void GenerateWordDocument(MsmqMessage<string> message)
{
DocumentGeneration documentGenerator = null;
var state = new DocumentStatus();
var docGenerator = new DocumentGenerator(new LoadGeneratorConfigurator().Load());
var deserializer = new XmlSerializer(typeof(DocumentGeneration));
documentGenerator = deserializer.Deserialize(new StringReader(message.Body)) as DocumentGeneration;
if(documentGenerator == null)
throw new Exception("Deserializing of the message has failed");
docGenerator.MailQueue = appSettings["MAILQUEUE"];
docGenerator.GenerateDocument(documentGenerator);
var builder = new StringBuilder();
builder.Append("The documents have been saved to the following locations: \r\n");
}
}
Your service as configured in the question should only process message at a time. Although you are not using the ServiceBehavior attribute for the service implementation class, the default value for the ConcurrencyMode is Single not Multiple (which could cause the behavior you are seeing). The default value of InstanceContextMode is Per Session but the maxConcurrentInstances and maxConcurrentSessions values force support for a single session at a time.
The only other option that I see is to force the ServiceHost to use only one service instance by using a different constructor. Here is the code:
// ... snipped ...
//force single service instance to be used by servicehost
var singleton = new MSMQProcessor();
_serviceHost = new ServiceHost(singleton);
_serviceHost.Open();
// ... snipped ...

Duplex WCF service - Direct call and callback do not use same channel

My goal is to reach a WCF service behind a firewall with no incoming ports opened.
The solution I chose is to host a duplex WCF service on the public side, that has as callback the same contract that was used if no firewall was involved.
It worked if I used netTcpBinding but since I need streamed communication I had to use the custom binding.
Everything works fine until I raise up the firewall. At that point, the direct call (from behind the firewall out) works fine, but the callback does not (firewall stops it).
The question is WHY? Shoudn't they use the same channel as for the predefined netTcpBinding?
Here is my channels stack in app.config:
<customBinding>
<binding name="ReversedServiceBinding">
<compositeDuplex />
<oneWay />
<binaryMessageEncoding />
<tcpTransport transferMode="Streamed" />
</binding>
</customBinding>
Actual behavior is correct. You have used compositeDuplex binding element - the meaning of composite word is exactly what you get - two separate channels each working for one direction. Composite duplex is needed only for transport channel which doesn't support duplex communication by default. That is not the case for TCP transport channel - check remarks. If you don't use compositeDuplex it should work like you have expected. Also there is no need to define whole new custom binding to allow streamed transport. NetTcpBinding also has TransportMode property which can be specified in configuration.
EDIT:
I have prepared working example with Net.TCP duplex communication. It doesn't use streaming. You are right that streaming is not possible when duplex TCP communication is used. You can try to combine duplex communication with chunking channel or check WCF Xtensions (commercial product).
Shared contracts
namespace NetTcpDuplexContracts
{
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(IDuplexServiceCallback))]
public interface IDuplexService
{
[OperationContract(IsOneWay = true)]
void DoAction(string message);
}
public interface IDuplexServiceCallback
{
[OperationContract(IsOneWay = true)]
void ConfirmAction(string message);
}
}
Service and host
namespace NetTcpDuplexService
{
public class DuplexService : IDuplexService
{
public void DoAction(string message)
{
Console.WriteLine("DoAction: " + message);
var callbackChannel =
OperationContext.Current.GetCallbackChannel<IDuplexServiceCallback>();
callbackChannel.ConfirmAction("Ping back " + message);
}
}
class Program
{
public static void Main(string[] args)
{
try
{
using (var host = new ServiceHost(typeof(DuplexService)))
{
host.Open();
Console.ReadLine();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.ReadLine();
}
}
}
}
Service configuration
<configuration>
<system.serviceModel>
<services>
<service name="NetTcpDuplexService.DuplexService">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8800/NetTcpDuplexService"/>
</baseAddresses>
</host>
<endpoint address="" binding="netTcpBinding" contract="NetTcpDuplexContracts.IDuplexService" />
</service>
</services>
</system.serviceModel>
</configuration>
Callback and client
namespace NetTcpDuplexClient
{
public class DuplexServiceCallback : IDuplexServiceCallback
{
public void ConfirmAction(string message)
{
Console.WriteLine(message);
}
}
class Program
{
public static void Main(string[] args)
{
DuplexChannelFactory<IDuplexService> factory = null;
try
{
var callbackService = new DuplexServiceCallback();
var context = new InstanceContext(callbackService);
factory = new DuplexChannelFactory<IDuplexService>(context, "IDuplexService_NetTcp");
var channel = factory.CreateChannel();
channel.DoAction("Hello world");
factory.Close();
Console.ReadLine();
}
catch (Exception e)
{
if (factory != null && factory.State != CommunicationState.Closed)
{
factory.Abort();
}
Console.WriteLine(e.Message);
Console.ReadLine();
}
}
}
}
Client configration
<configuration>
<system.serviceModel>
<client>
<endpoint name="IDuplexService_NetTcp" address="net.tcp://localhost:8800/NetTcpDuplexService" binding="netTcpBinding"
contract="NetTcpDuplexContracts.IDuplexService" />
</client>
</system.serviceModel>
</configuration>

WCF endpoint exception

I am just trying with various WCF(in .Net 3.0) scenarios.
I am using self hosting.
I am getting an exception as "Service 'MyServiceLibrary.NameDecorator' has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element."
I have a config file as follows (which has an endpoint)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="Lijo.Samples.NameDecorator"
behaviorConfiguration="WeatherServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8010/ServiceModelSamples/FreeServiceWorld"/>
</baseAddresses>
</host>
<endpoint address=""
binding="wsHttpBinding"
contract="Lijo.Samples.IElementaryService" />
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WeatherServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="False"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
And a Host as
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Runtime.Serialization;
namespace MySelfHostConsoleApp
{
class Program
{
static void Main(string[] args)
{
System.ServiceModel.ServiceHost myHost = new ServiceHost(typeof(MyServiceLibrary.NameDecorator));
myHost.Open();
Console.ReadLine();
}
}
}
My Service is as follows
using System.ServiceModel;
using System.Runtime.Serialization;
namespace MyServiceLibrary
{
[ServiceContract(Namespace = "http://Lijo.Samples")]
public interface IElementaryService
{
[OperationContract]
CompanyLogo GetLogo();
}
public class NameDecorator : IElementaryService
{
public CompanyLogo GetLogo()
{
CircleType cirlce = new CircleType();
CompanyLogo logo = new CompanyLogo(cirlce);
return logo;
}
}
[DataContract]
public abstract class IShape
{
public abstract string SelfExplain();
}
[DataContract(Name = "Circle")]
public class CircleType : IShape
{
public override string SelfExplain()
{
return "I am a Circle";
}
}
[DataContract(Name = "Triangle")]
public class TriangleType : IShape
{
public override string SelfExplain()
{
return "I am a Triangle";
}
}
[DataContract]
[KnownType(typeof(CircleType))]
[KnownType(typeof(TriangleType))]
public class CompanyLogo
{
private IShape m_shapeOfLogo;
[DataMember]
public IShape ShapeOfLogo
{
get
{
return m_shapeOfLogo;
}
set
{
m_shapeOfLogo = value;
}
}
public CompanyLogo(IShape shape)
{
m_shapeOfLogo = shape;
}
}
}
Could you please help me to understand what I am missing here?
Thanks
Lijo
You're self-hosting in a console app - how is your config set up??
Does your MySelfHostConsoleApp project have an app.config file?
Do you have the MySelfHostConsoleApp.exe.config in the same directory as the MySelfHostConsoleApp.exe file?
The error message just really means the config cannot be found and thus cannot be interpreted and used.
UPDATE: the other option is that WCF cannot interpret the config if it's present.
Check this out:
in your .NET code, your service class that implements the service is called MyServiceLibrary.NameDecorator
however, in your config, you call your service:
<service name="Lijo.Samples.NameDecorator"
That's not going to work! You're mixing up the .NET namespaces and the service namespaces here - and the name you need to put in your service-side config is the .NET fully qualified type name (including the .NET namespace - not the service namespace!).
Your service host will look for an entry <service name="MyServiceLibrary.NameDecorator"> based on your code - but it won't find it.
So you need to make sure to sync those two things up - the fully qualified service class name (including namespace and all) MUST match the name="...." attribute in your <service> tag in your config.

WCF Constructor with parameter / Custom Behavior Created In Code

I've a must to create wcf service with parameter.
I'm following this http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/8f18aed8-8e34-48ea-b8be-6c29ac3b4f41
First this is that I don't know how can I set this custom behavior "MyServiceBehavior" in my Web.config in ASP.NET MVC app that will host it.
As far as I know behaviors must be declared in section in wcf.config.
How can I add reference there to my behavior class from service assembly?
An second thing is that in the following example they have created local host (they use
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
to host in console application), but how I can add headers
OperationContext.Current.OutgoingMessageHeaders.Add ...
used to initialize constructor when I use in my WPF client application service reference and it will already create instance of web service "client" class
PBSDataCacheSyncContractClient client = new PBSDataCacheSyncContractClient();
is't it too late? Or when I have my own custom behavior can I do something like this:
PBSDataCacheSyncContractClient client = new PBSDataCacheSyncContractClient(my var for service constructor) ?
Regards,
Daniel Skowroński
EDIT: 31-05-2010
#manunt
I've improved my second question.
For answer to my first question, I've managed to create custom extenstion but I can't register it.
My scenario:
I have definitions for my web service in WCF library (interface, contract, implementation of IInstanceProvider, BehaviorExtensionElement)
then I reference it to another project ASP.NET application
inside ASP.NET application I have WCF service file and it is pointed to my class from WCF library
all my configuration is declared in web.config
In my WCF library I have:
namespace PBS.SyncService
{
using System;
using System.Data;
using System.Collections.ObjectModel;
using System.ServiceModel;
using Microsoft.Synchronization.Data;
using System.ServiceModel.Activation;
using Microsoft.Synchronization.Data.Server;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Configuration;
[XmlSerializerFormat()]
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public partial class PBSDataCacheSyncService : object, IPBSDataCacheSyncContract
{
private PBSDataCacheServerSyncProvider _serverSyncProvider;
public PBSDataCacheSyncService()
{
this._serverSyncProvider = new PBSDataCacheServerSyncProvider();
}
public PBSDataCacheSyncService(long doctorId)
{
this._serverSyncProvider = new PBSDataCacheServerSyncProvider();
this._serverSyncProvider.DoctorId = doctorId;
this._serverSyncProvider.InitializeCustomSyncProvider();
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public virtual SyncContext ApplyChanges(Microsoft.Synchronization.Data.SyncGroupMetadata groupMetadata, DataSet dataSet, Microsoft.Synchronization.Data.SyncSession syncSession)
{
return this._serverSyncProvider.ApplyChanges(groupMetadata, dataSet, syncSession);
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public virtual SyncContext GetChanges(Microsoft.Synchronization.Data.SyncGroupMetadata groupMetadata, Microsoft.Synchronization.Data.SyncSession syncSession)
{
return this._serverSyncProvider.GetChanges(groupMetadata, syncSession);
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public virtual SyncSchema GetSchema(Collection<string> tableNames, Microsoft.Synchronization.Data.SyncSession syncSession)
{
return this._serverSyncProvider.GetSchema(tableNames, syncSession);
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public virtual SyncServerInfo GetServerInfo(Microsoft.Synchronization.Data.SyncSession syncSession)
{
return this._serverSyncProvider.GetServerInfo(syncSession);
}
public bool InitializeCustomSyncProvider(long doctorId)
{
this._serverSyncProvider.DoctorId = doctorId;
return this._serverSyncProvider.InitializeCustomSyncProvider();
}
}
[XmlSerializerFormat()]
[ServiceContractAttribute()]
public interface IPBSDataCacheSyncContract
{
[OperationContract()]
SyncContext ApplyChanges(Microsoft.Synchronization.Data.SyncGroupMetadata groupMetadata, DataSet dataSet, Microsoft.Synchronization.Data.SyncSession syncSession);
[OperationContract()]
SyncContext GetChanges(Microsoft.Synchronization.Data.SyncGroupMetadata groupMetadata, Microsoft.Synchronization.Data.SyncSession syncSession);
[OperationContract()]
SyncSchema GetSchema(Collection<string> tableNames, Microsoft.Synchronization.Data.SyncSession syncSession);
[OperationContract()]
SyncServerInfo GetServerInfo(Microsoft.Synchronization.Data.SyncSession syncSession);
[OperationContract()]
bool InitializeCustomSyncProvider(long doctorId);
[OperationContract()]
string[] GetSyncAdapterInfo();
}
public class PBSDataCacheSyncProvider : IInstanceProvider
{
public object GetInstance(InstanceContext instanceContext, Message message)
{
string doctorId = message.Headers.GetHeader<string>("DoctorId", "http://***/SyncService.svc");
if (doctorId != null)
{
return new PBSDataCacheSyncService(Convert.ToInt64(doctorId));
}
else
{
return new PBSDataCacheSyncService();
}
}
public object GetInstance(InstanceContext instanceContext)
{
return new PBSDataCacheSyncService();
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
}
public class PBSDataCacheSyncBehavior : BehaviorExtensionElement, IServiceBehavior
{
PBSDataCacheSyncProvider pbsProvider = new PBSDataCacheSyncProvider();
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.pbsProvider;
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
public override Type BehaviorType
{
get { return typeof(PBSDataCacheSyncBehavior); }
}
protected override object CreateBehavior()
{
return new PBSDataCacheSyncBehavior();
}
}
}
My WCF Service file has name: SyncService.svc and in my makrup I have:
<%# ServiceHost Language="C#" Debug="true" Service="PBS.SyncService.PBSDataCacheSyncService" CodeBehind="PBS.SyncService.PBSDataCache.Server.SyncContract.cs" %>
My web.config:
<service name="PBS.Web.SyncService" behaviorConfiguration="behPBSDataCacheSyncBehavior">
<host>
<baseAddresses>
<add baseAddress="http://***/SyncService.svc" />
</baseAddresses>
</host>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<endpoint address="" binding="basicHttpBinding" contract="PBS.SyncService.IPBSDataCacheSyncContract" />
</service>
<serviceBehaviors>
<behavior name="behPBSDataCacheSyncBehavior">
<PBSDataCacheSyncBehavior /> <!-- this element is being ignored -->
</behavior>
</serviceBehaviors>
<extensions>
<behaviorExtensions>
<add name="PBSDataCacheSyncBehavior" type="PBS.SyncService.PBSDataCacheSyncBehavior, PBS.SyncService,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
Can you tell me what I'm missing in this point?
Why parser ignores my custom extension declaration?
I have following error:
Configuration Error
Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.
Parser Error Message: An error occurred creating the configuration section handler for system.serviceModel/behaviors: Extension element 'PBSDataCacheSyncBehavior' cannot be added to this element. Verify that the extension is registered in the extension collection at system.serviceModel/extensions/behaviorExtensions.
Parameter name: element
EDIT: 01-06-2010
Problem with parser resolved by typing all the declaration in one single line.
I still don't know how to add header when I have service reference.
In my WPF application I have only client instance witch implements my IPBSDataCacheSyncContract autogenerated by Service Reference.
And when I initialize it it only has constructors:
public PBSDataCacheSyncContractClient() {
}
public PBSDataCacheSyncContractClient(string endpointConfigurationName) :
base(endpointConfigurationName) {
}
public PBSDataCacheSyncContractClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}
public PBSDataCacheSyncContractClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}
public PBSDataCacheSyncContractClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress) {
}
Where I can add headers?
"As for the second question - you should define message contract with needed headers in it and provide header values for each message separately." Could you be more specific?
EDIT: 02-06-2010
I've encountered other issue.
When I have now my configuration httpGetEnabled is ignored... :
<serviceBehaviors>
<behavior name="behPBSDataCacheSyncBehavior">
<PBSDataCacheSyncBehavior />
<serviceMetadata httpGetEnabled="true" /><!-- ignored -->
<serviceDebug includeExceptionDetailInFaults="true" /><!-- ignored -->
</behavior>
</serviceBehaviors>
How can I fix it?
EDIT: 02-06-2010
OK I've figured workaround. Still it is weird but it works!
My problem was with web.config. And none name behavior entry entry is recognized by my service and not any other... So I simply added no name behavior to collection.
<serviceBehaviors>
<behavior name="">
<PBSDataCacheSyncBehavior />
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
<behavior name="behPBSDataCacheSyncBehavior">
<PBSDataCacheSyncBehavior />
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
And I add header in my code this way:
int doctorId = 2;
Sync.PBSDataCacheSyncContractClient client = new Sync.PBSDataCacheSyncContractClient();
new OperationContextScope (client.InnerChannel);
OperationContext.Current.OutgoingMessageHeaders.Add(
MessageHeader.CreateHeader("DoctorId", "http://***/SyncService.svc", doctorId));
I've changed topic to be more useful.
HTH
Regards,
Daniel Skowroński
I know what the problem is with the behavior not being found and why you need the hack with the behavior with no name.
If you look at this line in your svc markup file:
<%# ServiceHost Language="C#" Debug="true" Service="PBS.SyncService.PBSDataCacheSyncService" CodeBehind="PBS.SyncService.PBSDataCache.Server.SyncContract.cs" %>
and this line in your web.Config:
<service name="PBS.Web.SyncService" behaviorConfiguration="behPBSDataCacheSyncBehavior">
You will notice that the name specified in the service tag is different from the Service class specified in the Service attribute in the markup file.
I think it should be something like this:
instead of
<service name="PBS.Web.SyncService" behaviorConfiguration="behPBSDataCacheSyncBehavior">
this
<service name="PBS.SyncService.PBSDataCacheSyncService" behaviorConfiguration="behPBSDataCacheSyncBehavior">
These two values I think have to the same not sure but in my case the two values were different and I had to do the blank service name hack. But by setting both values the same, it worked. It found the behavior without needing the blank one and I was able to access my wsdl.
Answer for the first question you can find here.
Regarding error you are getting - do not split definition of your extension into two lines, because xml parser cannot handle that.
A sample how to define custom headers without specifying message contract:
var client = new Service1Client();
new OperationContextScope(client.InnerChannel);
MessageHeader<string> typedHeader = new MessageHeader<string>("headercontent");
MessageHeader header = typedHeader.GetUntypedHeader("myheader", "myns");
OperationContext.Current.OutgoingMessageHeaders.Add(header);