WCF service behavior extension throwing null reference exception - wcf

So, im trying to write routing service. The idea is that, every time someone calls routing service, endpoint is randomly selected by WCF behavior extension. I used slightly modified example from MSDN called DynamicReconfiguration to achieve that. Part of my web.config looks like this
<behaviors>
<serviceBehaviors>
<behavior>
<behavior name="behaviorWithUpdate">
<updateBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="updateBehavior" type="RouterService.UpdateBehavior, RouterService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<services>
and implementation of behavior and behavior extension
public class UpdateBehavior : BehaviorExtensionElement, IServiceBehavior
{
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
var rulesUpdateExtension = new RulesUpdateExtension();
serviceHostBase.Extensions.Add(rulesUpdateExtension);
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
class RulesUpdateExtension : IExtension<ServiceHostBase>
{
ServiceHostBase _owner;
List<EndpointAddress> _endpoints = new List<EndpointAddress>
{
new EndpointAddress("http://localhost:19338/Service1.svc"),
new EndpointAddress("http://localhost:20464/Service2.svc")
};
void IExtension<ServiceHostBase>.Attach(ServiceHostBase owner)
{
this._owner = owner;
UpdateRules(DateTime.Now.Second % 2 == 0 ? _endpoints[0] : _endpoints[1]);
}
void IExtension<ServiceHostBase>.Detach(ServiceHostBase owner)
{
}
void UpdateRules(EndpointAddress endpoint)
{
var rc = new RoutingConfiguration();
var serviceEndpoint = new ServiceEndpoint(
ContractDescription.GetContract(typeof(IService1)),
new BasicHttpBinding(),
endpoint);
rc.FilterTable.Add(new MatchAllMessageFilter(), new List<ServiceEndpoint> { serviceEndpoint });
this._owner.Extensions.Find<RoutingExtension>().ApplyConfiguration(rc);
}
}
public override Type BehaviorType
{
get { return typeof(UpdateBehavior); }
}
protected override object CreateBehavior()
{
return new UpdateBehavior();
}
}
Problem is that last line of UpdateRules method is throwing NullReferenceException. It cant find this extension even though i attach it in behavior. In example from MSDN, routing service is hosted in console application and here im trying to host it on IIS. Im missing something here...

In the sample RoutingService project, the code in the routing.cs programmatically injects the RoutingExtension into the RoutingService. This is normally done in the config file using the behaviors>serviceBehaviors>behavior>routing element to set up the filter table to use. However, since a WCF service can only be assigned a single service behavior through the config file, the sample RoutingService project dynamically adds the RoutingExtension in the service host initialization.
To do the same thing in IIS, you need to create a custom service host factory to perform the same function of the routing.cs code in the sample project. Look at this MSDN article for how to create a custom host. It also shows how you would configure it with IIS.

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>

Specify a Singleton service in a WCF self hosted service

I am writing an application that exposes a service via WCF. The service is self-hosted (console app) and needs to use a Singleton instance. I am trying to figure out how to specify singleton in the service configuration without using attributes on the service implementation. Is it possible to specify singleton in code without an attribute?
Thanks,
Erick
You can pass instance of the service to the ServiceHost constructor instead of passing a type. In such case your passed instance will be used as singleton.
Edit:
My former solution doesn't work. Providing instance to ServiceHost constructor still demands ServiceBehaviorAttribute with InstanceContextMode.Single. But this one should work:
var host = new ServiceHost(typeof(Service));
var behavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
behavior.InstanceContextMode = InstanceContextMode.Single;
host.Open();
ServiceBehaviorAttribute is included even if you don't specify it so you just need to get it and change default value.
If you want to move this into web.config or app.config, you could do so with a custom BehaviorExtensionElement and IServiceBehavior:
The IServiceBehavior will actually parse the value from config into the enum and set it (following #Ladislav's answer):
public class InstanceContextServiceBehavior : IServiceBehavior
{
InstanceContextMode _contextMode = default(InstanceContextMode);
public InstanceContextServiceBehavior(string contextMode)
{
if (!string.IsNullOrWhiteSpace(contextMode))
{
InstanceContextMode mode;
if (Enum.TryParse(contextMode, true, out mode))
{
_contextMode = mode;
}
else
{
throw new ArgumentException($"'{contextMode}' Could not be parsed as a valid InstanceContextMode; allowed values are 'PerSession', 'PerCall', 'Single'", "contextMode");
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
var behavior = serviceDescription.Behaviors.Find<ServiceBehaviorAttribute>();
behavior.InstanceContextMode = _contextMode;
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
return;
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
return;
}
}
The extension element allows you to pull it from config and pass it to the IServiceBehavior:
public class InstanceContextExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get
{
return typeof(InstanceContextServiceBehavior);
}
}
protected override object CreateBehavior()
{
return new InstanceContextServiceBehavior(ContextMode);
}
const object contextMode = null;
[ConfigurationProperty(nameof(contextMode))]
public string ContextMode
{
get
{
return (string)base[nameof(contextMode)];
}
set
{
base[nameof(contextMode)] = value;
}
}
}
And you can then register it in your config and use it:
<extensions>
<behaviorExtensions>
<add name="instanceContext" type="FULLY QUALFIED NAME TO CLASS"/>
</behaviorExtensions>
</extensions>
...
<serviceBehaviors>
<behavior name="Default">
<instanceContext contextMode="Single"/>

Using Ninject WCF Extension with WCF Web Service

I have a WCF web service in which I want to use my Repositories and Services which I wish to dependency inject into my WCF web service, however the Ninject WCF Extension example pretty much has a ctor which is instantiating an instance of each dependency, which I don't want, I wanted a purer dependency injection.
Has anyone had any success with using Ninject with WCF, Google seems to return little relevant results for the topics I am looking for.
The code behind for TimeService has:
<%# ServiceHost Language="C#" Debug="true" Service="WcfTimeService.TimeService" CodeBehind="TimeService.svc.cs" **Factory="Ninject.Extensions.Wcf.NinjectServiceHostFactory"** %>
The bastard injection ctors confuse the matter - Ninject will choose the most specific constructor. The general problem with the sample is that it's covering all the bases (IIS Hosted, EXE hosted, Service Hosted), and WCF doesnt exactly make all this stuff easy to manage either (#Ian Davis: I could easily be wrong, can you provide some more detail please , perhaps in the form of a summary of what the samples illustrate in the README, and perhaps more detail in the why of the various cases where you've used BI?)
The way I am currently using Ninject (v3) with my WCF is based on the Ninject WCF extension and Pieter De Rycke's great blog post.
In a nutshell, here's what I'm doing:
1) Via NuGet, I've added a reference to Ninject.Extensions.Wcf into my WCF project. This creates the App_Start folder with NinjectWebCommon.cs, which takes care of initializing Ninject.
2) Typically, you'd set up your Ninject mappings in the CreateKernel method in NinjectWebCommon.cs. However, since I have a MVC3 site in the same solution and want the same Ninject mappings for that site, my CreateKernel looks like this:
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
InfrastructureSetup.RegisterServices(kernel);
return kernel;
}
3) In InfrastructureSetup.RegisterServices, I have my Ninject mappings:
public static class InfrastructureSetup
{
public static void RegisterServices(IKernel kernel)
{
kernel.Bind<IRepositoryContext>().To<MyEntityFrameworkContext>().InRequestScope();
kernel.Bind<IFooRepository>().To<FooRepository>().InRequestScope();
kernel.Bind<IBarRepository>().To<BarRepository>().InRequestScope();
// ... and so on. I want InRequestScope() for the EF context, since
// otherwise my repositories (which take IRepositoryContext in their
// constructors) end up getting different EF contexts, messing things up
}
}
4) I want to also inject stuff (IFooService etc.) to my WCF constructors, so I've edited the Web.config for the WCF project with the advice from Pieter De Rycke:
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
<!-- Add the Ninject behavior to the WCF service. This is needed to support dependency injection to the WCF constructors -->
<ninject />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<!-- Add the Ninject behavior extension -->
<add name="ninject"
type="MyWCFProject.Infrastructure.NinjectBehaviorExtensionElement, MyWCFProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
5) In the MyWCFProject.Infrastructure namespace, I have three files which are basically copy-paste from Pieter:
NinjectBehaviorAttribute.cs:
using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Ninject.Web.Common;
namespace MyWCFProject.Infrastructure
{
public class NinjectBehaviorAttribute : Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
Type serviceType = serviceDescription.ServiceType;
// Set up Ninject to support injecting to WCF constructors
var kernel = new Bootstrapper().Kernel;
IInstanceProvider instanceProvider = new NinjectInstanceProvider(kernel, serviceType);
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher endpointDispatcher in dispatcher.Endpoints)
{
DispatchRuntime dispatchRuntime = endpointDispatcher.DispatchRuntime;
dispatchRuntime.InstanceProvider = instanceProvider;
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
}
NinjectBehaviorExtensionElement.cs:
using System;
using System.ServiceModel.Configuration;
namespace MyWCFProject.Infrastructure
{
public class NinjectBehaviorExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(NinjectBehaviorAttribute); }
}
protected override object CreateBehavior()
{
return new NinjectBehaviorAttribute();
}
}
}
NinjectInstanceProvider.cs:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using Ninject;
namespace MyWCFProject.Infrastructure
{
public class NinjectInstanceProvider : IInstanceProvider
{
private Type serviceType;
private IKernel kernel;
public NinjectInstanceProvider(IKernel kernel, Type serviceType)
{
this.kernel = kernel;
this.serviceType = serviceType;
}
public object GetInstance(InstanceContext instanceContext)
{
return this.GetInstance(instanceContext, null);
}
public object GetInstance(InstanceContext instanceContext, Message message)
{
return kernel.Get(this.serviceType);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
}
}
At the moment, this solution seems to be working well; dependency injection is working for both the WCF and the MVC3 site, I can request dependencies to be injected to the WCF constructors and the EF context stays around for the duration of the request.

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);

When and where to set a custom IOperationInvoker?

I'm trying to extend WCF so that I can have a RESTful web service, in which, for each operation, I perform a verification of the HTTP Authorization header, whose value I use to call a Login() method.
After the login is done, I wish to invoke the operation's corresponding method checking if a security exception is thrown, in which case I'll reply with a custom "access denied" message" using the appropriate HTTP Status Code.
With this in mind, I thought implementing a IEndpointBehavior that applies an implementaion of IOperationInvoker to each operation (setting the DispatchOperation.Invoker property) would be a good idea.
I decided to implement an IOperationInvoker using the Decorator design pattern. My implementation would need another IOperationInvoker in it's constructor to which the method invocations would be delegated.
This is my IOperationInvokerImplementation:
public class BookSmarTkOperationInvoker : IOperationInvoker{
private readonly IOperationInvoker invoker;
public BookSmarTkOperationInvoker(IOperationInvoker decoratee)
{
this.invoker = decoratee;
}
public object[] AllocateInputs()
{
return this.invoker.AllocateInputs();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
BeforeOperation(); // Where there's code to perform the login using WebOperationContext.Current
object o = null;
try
{
o = this.invoker.Invoke(instance, inputs, out outputs);
}
catch (Exception exception)
{
outputs = null;
return AfterFailedOperation(exception); // Return a custom access denied response
}
return o;
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
throw new Exception("The operation invoker is not asynchronous.");
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
throw new Exception("The operation invoker is not asynchronous.");
}
public bool IsSynchronous
{
get
{
return false;
}
}
}
I decided to implement an IEndpointBehavior by extending the behavior I already needed (WebHttpBehavior) this way I only use one beavior. Here's the code I wrote:
public class BookSmarTkEndpointBehavior : WebHttpBehavior
{
public override void Validate(ServiceEndpoint endpoint)
{
base.Validate(endpoint);
}
public override void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
base.AddBindingParameters(endpoint, bindingParameters);
}
public override void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
base.ApplyDispatchBehavior(endpoint, endpointDispatcher);
foreach (DispatchOperation operation in endpointDispatcher.DispatchRuntime.Operations)
{
IOperationInvoker defaultInvoker = operation.Invoker;
IOperationInvoker decoratorInvoker = new BookSmarTkOperationInvoker(defaultInvoker);
operation.Invoker = decoratorInvoker;
Console.Write("Before: " + ((object)defaultInvoker ?? "null"));
Console.WriteLine(" After: " + operation.Invoker);
}
}
public override void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
base.ApplyClientBehavior(endpoint, clientRuntime);
throw new Exception("The BookSmarTkEndointBehavior cannot be used in client endpoints.");
}
}
Now here's the problem:
Only the constructor is being invoked in the IOperationInvoker, none of the other methods are.
The decoratee IOperationInvoker (the one that's passed in the decorator's constructor) is null.
I'm guessing that maybe some other code from some other behavior is setting another IOperationInvoker in the OperationDispatcher.Invoker setting afterwards. Thus, overriding mine. This would clearly explain my situation.
What is happening and what should I do?
My service is self-hosted.
In case you need to see it, here is the configuration I have in the app.config file under system.serviceModel.
<services>
<service name="BookSmarTk.Web.Service.BookSmarTkService">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/service"/>
</baseAddresses>
</host>
<endpoint
address=""
behaviorConfiguration="BookSmaTkEndpointBehavior"
binding="webHttpBinding"
bindingConfiguration="BookSmarTkBinding"
contract="BookSmarTk.Web.Service.BookSmarTkService">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name ="BookSmartkServiceBehavior">
<serviceDebug httpHelpPageEnabled="true" httpHelpPageUrl="/help.htm" includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="BookSmaTkEndpointBehavior">
<!--<webHttp/>-->
<bookSmarTkEndpointBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="BookSmarTkBinding">
</binding>
</webHttpBinding>
</bindings>
<extensions>
<behaviorExtensions>
<add name="bookSmarTkEndpointBehavior" type="BookSmarTk.Web.Service.BookSmarTkEndpointBehaviorElement, BookSmarTk.Web.Service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
I you read this far I am deeply grateful towards you. Really, Thank You!
Instead of setting invokers at ApplyDispatchBehavior() method, you have to make an IOperationBehavior implementor:
public class MyOperationBehavior: IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new BookSmarTkOperationInvoker(dispatchOperation.Invoker);
}
public void Validate(OperationDescription operationDescription)
{
}
}
and then at ApplyDispatchBehavior() you should set that behavior:
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
foreach (var operation in endpoint.Contract.Operations) {
if (operation.Behaviors.Contains(typeof(MyOperationBehavior)))
continue;
operation.Behaviors.Add(new MyOperationBehavior());
}
}
I am building something similar (I think - don't have the time to look through all your code), but have gone about it in a different way.
To achieve this I am using the following:
An IMessageInspector to read the incoming HTTP request message headers (in this case extracting a session Id from a cookie and retrieving a session object from a cache).
A combination of an IPrincipal and an IAuthorizationPolicy to implement my own custom authorization code (WCF will automatically invoke my code for requests to web service methods which have the attribute '[PrincipalPermission(SecurityAction.Demand, Role="somerole")]' set).
An IErrorHandler which catches any uncaught exceptions from the web service methods (including a permission denied exception thrown if authorization fails -- i.e. the IsRole method you implement in the IPrincipal returns false). If you catch the security denied exception you can then use WebOperationContext.Current to set the custom HTTP error codes for the response message.
A custom behavior (an IContractBehavior - but you can also use an EndPoint or Service behavior or whatever you want) which creates all the above at runtime and attaches them to the appropriate endpoints.
I know this is very old, but for me Alexey's answer
worked. However, only when the ApplyDispatchBehaviour method calls the base method. Like this:
public override void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
base.ApplyDispatchBehavior(endpoint, endpointDispatcher);
foreach (var operation in endpoint.Contract.Operations)
{
if (operation.Behaviors.Contains(typeof(AccessControlOperationBehaviour)))
continue;
operation.Behaviors.Add(new AccessControlOperationBehaviour());
}
}