WCF - Injecting into a Custom UserNamePasswordValidator with MEF - wcf

I have a WCF Service which is using DI via MEF. That part is working fine.
I also have a Custom UserNamePasswordValidator which works as long as I use a parameterless constructor and 'new' everything up. However I would like to introduce MEF into that as well.
The service is hosted in IIS so I have to intercept it somewhat to get MEF to work as it is.
I've provided cross sections of my code below and any help would be appreciated!
Here is my web.config: -
<behavior name="StandardServiceBehaviour">
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType=
"WebService.Validators.CustomUserNamePasswordValidator, WebService" />
</serviceCredentials>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<dataContractSerializer maxItemsInObjectGraph="67108864" />
</behavior>
My custom validator is as follows: -
[Export(typeof(ICustomUserNamePasswordValidator))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class CustomUserNamePasswordValidator : UserNamePasswordValidator, ICustomUserNamePasswordValidator
{
[ImportingConstructor]
public CustomUserNamePasswordValidator([NotNull] IConnectionProvider connectionProvider)
{
}
.....
}
I'm using a custom attribute on my Web Service called WebServiceExport, which subclasses the MEF Export attribute and includes an InstanceProvider: -
[WebServiceExport(typeof(IGeneralService))]
public class GeneralService : IGeneralService
{
....
}
Here is the Export Attribute: -
public class WebServiceExportAttribute : ExportAttribute, IContractBehavior, IContractBehaviorAttribute
{
public void ApplyDispatchBehavior(ContractDescription description, ServiceEndpoint endpoint, DispatchRuntime dispatch)
{
var contractType = description.ContractType;
dispatch.InstanceProvider = new MefInstanceProvider(contractType);
}
....
}
And finally here is the Instance Provider: -
public class MefInstanceProvider : IInstanceProvider
{
public MefInstanceProvider(Type serviceContract)
{
_serviceContract = serviceContract;
}
public object GetInstance(InstanceContext instanceContext, Message message)
{
BuildInstance(); //compose MEF parts
}
....
}

Related

WCF serviceAuthorizationManager not being invoked

Trying to add API Key authorization to an existing WCF service hosted on IIS 7
Followed Ron Jacob's tutorial for creating a class derived from ServiceAuthorizationManager.
It is not being invoked.
In case my understanding is wrong, I am expecting that all I have to do is correctly make the class and refer to it in Web.Config.
At that point my test web client should cease to get data from the service until the client is altered to handle API Key process flow.
The client however still consumes the contracts correctly and the Eventlog messages that I have placed in the ServiceAuthorizationManager class are not being generated.
I thought it must be the behavior node that I created
in the web.config but I have created that both manually and using the Visual Studio config editor tool and both entries don't work.
I believe the web config serviceAuthorization node is correct in that it correctly refers to the Namespace.Class of the authorization class and I have double checked that the assembly in the bin directory of the Webservice is CouponParkingWCF.dll.
The Class code is:
namespace CouponParkingWCF
{
public class APIKeyAuthorization:ServiceAuthorizationManager
{
public const string APIKEY = "ApiKey";
public const string APIKEYLIST = "APIKeyList";
public string GetAPIKey(OperationContext operationContext)
{
// Get the request message
ClsLogger.WriteInfoLog("InsideGetAPIKey");
var request = operationContext.RequestContext.RequestMessage;
// Get the HTTP Request
var requestProp = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
// Get the query string
NameValueCollection queryParams = HttpUtility.ParseQueryString(requestProp.QueryString);
// Return the API key (if present, null if not)
return queryParams[APIKEY];
}
public List<Guid> APIKeys
{
get
{
// Get from the cache
// Could also use AppFabric cache for scalability
var keys = HttpContext.Current.Cache[APIKEYLIST] as List<Guid>;
if (keys == null)
keys = PopulateAPIKeys();
return keys;
}
}
private List<Guid> PopulateAPIKeys()
{
Dt dt = new Dt();
List<Guid> keyList = dt.GetApiKeys();
return keyList;
}
public bool IsValidAPIKey(OperationContext operationContext)
{
// if verification is disabled, return true
//if (Global.APIKeyVerification == false)
// return true;
ClsLogger.WriteInfoLog("InsideIsValidAPIKey");
//return true;
string key = GetAPIKey(operationContext);
Guid apiKey;
// Convert the string into a Guid and validate it
if (Guid.TryParse(key, out apiKey) && APIKeys.Contains(apiKey))
{
return true;
}
// Send back an HTML reply
CreateErrorReply(operationContext, key);
return false;
}
private void CreateErrorReply(OperationContext operationContext, string key)
{
ClsLogger.WriteErrorLog("We have an Authorization Error. Oh Dear.");
}
protected override bool CheckAccessCore(OperationContext operationContext)
{
return IsValidAPIKey(operationContext);
}
}
}
The web config behaviors node is:
<behaviors>
<endpointBehaviors>
<behavior name="RestJSONEndpointBehavior">
<webHttp helpEnabled="false" defaultBodyStyle="Bare" defaultOutgoingResponseFormat="Json" />
</behavior>
<behavior name="RestXMLEndpointBehavior">
<webHttp helpEnabled="false" defaultOutgoingResponseFormat="Xml" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="">
<serviceAuthorization serviceAuthorizationManagerType="CouponParkingWCF.APIKeyAuthorization, CouponParkingWCF, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null" />
</behavior>
<behavior name="wsdl">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
hopefully someone can spot what I have done wrong.
Thanks
Bob
The problem is that I put the serviceAuthorization node inside its own behavior node.
It should go inside the existing"wsdl" node
<serviceBehaviors>
<behavior name="wsdl">
<serviceAuthorization serviceAuthorizationManagerType="CouponParkingWCF.APIKeyAuthorization, CouponParkingWCF, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null" />
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
This got the API KEY working but then we need to have it working for GET and POST.
Ron's example shows using the querystring which is fine for GET but not for POST.
So we moved to putting the API Key into the header. The WCF key extraction code is:
public string GetAPIKey(OperationContext operationContext)
{
try
{
var request = operationContext.RequestContext.RequestMessage;
var requestProp = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
string key = requestProp.Headers["ApiKey"];
return key;
}
catch (Exception exception)
{
ClsLogger.WriteErrorLog("GetAPIKey " + exception.Message);
throw;
}
}

How to add behavior to client endpoint in IIS Hosted WCF Service using ServiceHostFactory

I am looking at implementing IDispatchMessageInpector & IClientMessageInpector to look at the message objects in AfterReceiveRequest and BeforeSendRequest Methods.
My requirement is to make changes at code level of WCF service. No Configuration Changes.
How to attach this behaviour to all the endpoints which are calling this service and to service itself. Is implementing IContractBehaviour helps me?
Edit 1:
WCF Service is hosted on IIS. Is it possible to add the behavior thru code?
Edit 2:
Seems like using ServiceHostFactory we can achieve this.
How can i add behavior to client endpoint which are defined in webconfig?
yes, it is possible to add behavior for services hosted in IIS. Behaviors are not concerned with the hosting environment of the service. Carlos Figueira's blog provides examples of all types of behaviors you could apply to Service, Endpoints, Contracts and Operations. A sample code that I tried for my service hosted in IIS (with endpoint defined in web.config file) - Config file here needs to add the behavior as ExtensionElement
public class MyEndpointBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
Console.WriteLine("applying dispatch behavior");
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyInspector());
endpointDispatcher.DispatchRuntime.OperationSelector = new MyOperationSelector();
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public override Type BehaviorType
{
get { return this.GetType(); }
}
protected override object CreateBehavior()
{
return new MyEndpointBehavior();
}
}
public class MyOperationSelector : IDispatchOperationSelector
{
public string SelectOperation(ref Message message)
{
Console.WriteLine("good luck");
string action = message.Headers.Action;
return action.Substring(action.LastIndexOf('/') + 1);
}
}
public class MyInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
return (Message) request;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
}
}
}
Config file with behavior added as extension element -
<system.serviceModel>
<services>
<service name="RouteToServiceA.Service1">
<endpoint address="Service1" binding="basicHttpBinding" contract="RouteToServiceA.IService1" behaviorConfiguration="testEndPoint" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="testEndPoint">
<testBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="testBehavior" type="RouteToServiceA.MyEndpointBehavior, RouteToServiceA" />
</behaviorExtensions>
</extensions>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Using ServiceHostFactory we can add service behavior whereas adding behavior configuration to client endpoints which are in config seems like not possible to achieve. So i am going with configuration changes

I cannot invoke wcf methods using Browser(HTTP)

I have a service with below code
TaskService.svc
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class TodoService : ITodoService
{
private readonly List<Todo> _dataSource;
public TodoService()
{
_dataSource = new List<Todo>();
}
[WebGet(UriTemplate = "/Todos")]
public List<Todo> Todos()
{
return _dataSource;
}
}
and the data transfer object is as below
[ServiceContract]
public interface ITodoService
{
[OperationContract]
List<Todo> Todos();
}
[DataContract]
public class Todo
{
[DataMember]
public string Name { get; set; }
[DataMember]
public bool Completed { get; set; }
}
the web.config as below
<services>
<service name="SampleService.TodosBehavior">
<endpoint address="" behaviorConfiguration="SampleService.TodosBehavior"
binding="webHttpBinding" contract="SampleService.TodoService" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="SampleService.TodosBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
the application is hosted in IIS 7 with virtual directory SampleService. When ever i invoke using browser http://localhost/SampleService/TaskService.svc/Todos i am greeted with nothing. But i expected a empty array. Why is this so?
I think the WebGet() needs to be in your interface (ServiceContract) rather than your implementation of that interface. Also, do you need the slash before "Todos" in your UriTemplate?
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class TodoService : ITodoService
{
private readonly List<Todo> _dataSource;
public TodoService()
{
_dataSource = new List<Todo>();
}
public List<Todo> Todos()
{
return _dataSource;
}
}
[ServiceContract]
public interface ITodoService
{
[WebGet(UriTemplate = "Todos")]
[OperationContract]
List<Todo> Todos();
}
If you're hosting in IIS, what does your TaskService.svc file look like?
EDIT: Oh, this IS your .svc file. Never mind then.

How do you configure a MessageInspector when using StandardEndpoints in WCF REST 4.0

I'm trying create and configure a Message Inspector to perform some authentication of a WCF Rest HTTP request. I'm using 4.0 so trying to steer clear of the WCF Starter Kit although I have managed to get an old RequestInterceptor working in the way I want. The problem with using RequestInterceptor is that I lost the automaticFormatSelectionEnabled features provided by WebHttpBehavior which I really want to keep.
So my question is how do I configure the Message Inspector in a way that I still use the WebHttpBehavior and keep it's features.
My web.config looks like this
<standardEndpoints>
<webHttpEndpoint>
<!-- the "" standard endpoint is used by WebServiceHost for auto creating a web endpoint. -->
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true" />
<!-- Disable the help page for the directory end point-->
<standardEndpoint name="DirectoryEndpoint"/>
</webHttpEndpoint>
</standardEndpoints>
One way you can handle this is to create three objects.
The message inspector, responsible for analyzing the
request/response
The service behavior, automatically injects the inspector into the
pipeline
The configuration section, allows the behavior to be used in the
web.config
First create the message inspector by implementing IDispatchMessageInspector
and putting your validation code in the AfterReceiveRequest method:
public class HmacVerificationInspector : IDispatchMessageInspector
{
#region IDispatchMessageInspector Members
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request,
System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
Message dupeRequest = buffer.CreateMessage();
ValidateHmac(dupeRequest);
buffer.Close();
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply,
object correlationState)
{
}
#endregion
}
It's important to create a buffered copy of the message when reading it. Messages can only be opened once and not creating a copy will lead to problems down the pipe. My implementation of ValidateHmac throws an exception if it fails. This prevents the actual service from being called.
Second, create a behavior for your inspector. We'll use the behavior to inject the inspector into the WCF runtime. To create the behavior, derive a class from IEndpointBehavior so it looks like this
public class HmacVerificationBehavior : IEndpointBehavior
{
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
HmacVerificationInspector inspector = new HmacVerificationInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public void Validate(ServiceEndpoint endpoint)
{
}
#endregion
}
Notice I create a new instance of my inspector (HmacVerificationInspector) and inject it programmatically into the runtime.
Finally, the last step is to create a configuration section. We can use this to apply the behavior in the web config (thus being able to turn it on and off via configuration). Create a new class and inherit from BehaviorExtensionElement and IServiceBehavior:
public class HmacVerificationConfigurationSection : BehaviorExtensionElement, IServiceBehavior
{
#region IServiceBehavior Members
public void AddBindingParameters(ServiceDescription serviceDescription,
System.ServiceModel.ServiceHostBase serviceHostBase,
System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,
System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
#endregion
public override Type BehaviorType
{
get { return typeof(HmacVerificationBehavior); }
}
protected override object CreateBehavior()
{
return new HmacVerificationBehavior();
}
}
Now, to use the inspector, add the following to your web.config (you can set the name for your extension to whatever you want)
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="hmacVerification" type="NamespaceHere.HmacVerificationConfigurationSection, AssembleyHere, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<services>
<service name="MySecureService">
<endpoint address="" binding="webHttpBinding" contract="IMySecureService" behaviorConfiguration="web" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp automaticFormatSelectionEnabled="true" />
<hmacVerification />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
</system.serviceModel>
Couple of things, first you register the configuration section in the behavior extensions. Next, you use that configuration as an endpoint behavior which will then automatically inject the inspector and all requests to that endpoint will run through your inspector. If you want to turn off the inspector, remove the tag or select a different endpoint behavior. Note the use of the webHttp behavior also (which will allow you to keep automaticFormatSelectionEnabled.
Hope this helps

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