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"/>
Related
I developed a MVC application using abp boilerplate and now I have the necessity to expose some services via WFC/SOAP.
The idea is to create a WFC Service, inject the required IApplicationService and use it.
Something like:
// this code does not work
public class MyFirstService : IMyFirstService, ITransientDependency {
private readonly ICourseAppService _courseAppService;
// Injection here does not work!
public MyFirstService(ICourseAppService courseAppService) {
_courseAppService = courseAppService;
}
public CourseDto GetData(int id) {
return _courseAppService.Get(id);
}
}
But this code does not work. :-(
The first error I have is from WCF saying the Service does not have a default constructor without parameters. So I am on the wrong way.
How can I inject the service into the SOAP service?
The answer https://stackoverflow.com/a/46048289/752004 did not help me.
WCF uses Reflection to create service instance, so if your service has no constructor without parameters , wcf will fail to create the service instance which is why wcf shows the error.
It is not easy to integrate injection framework with wcf.
You should customize instance provider(which provides wcf service instance).
https://blogs.msdn.microsoft.com/carlosfigueira/2011/05/31/wcf-extensibility-iinstanceprovider/
In your customized instance provider , you could provide your injected service instance in the method GetInstance.
Then you should make wcf use your own instance provider using service behavior.
For example
public class MyServiceAttribute : Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher item in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher item1 in item.Endpoints)
{
item1.DispatchRuntime.InstanceProvider = new MyInstanceProvider(); // apply customized instanceProvider
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
Then you should customize a ServiceHost to apply the service behavior.
Like
public class MyUnityServiceHost : ServiceHost
{
protected MyUnityServiceHost()
{
}
protected override void OnOpening()
{
base.OnOpening();
if (this.Description.Behaviors.Find<MyServiceAttribute >() == null)
{
this.Description.Behaviors.Add(new MyServiceAttribute ());//add your behavior
}
}
}
At last, you should customize HostFactory to create your customized servicehost.
https://blogs.msdn.microsoft.com/carlosfigueira/2011/06/13/wcf-extensibility-servicehostfactory/
You could refer to the similar discussion below.
Injecting data to a WCF service
Abp uses the Castle Windsor, so following the suggestions from this answer and this article I found the solution.
Once imported the nuget package Castle.WcfIntegrationFacility, I created a new WCF library and in it I created a AbbModule class where I registered MyService (defined in pt. 3):
[DependsOn(typeof(BookingCoreModule), typeof(BookingApplicationModule))]
public class BookingSoapModule : AbpModule {
public override void Initialize() {
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
IocManager.IocContainer.AddFacility<WcfFacility>().Register(
Component
.For<IMyService>()
.ImplementedBy<MyService>()
.Named("MyService")
);
}
}
Then I created my IMyService interface (note that it extends ITransientDependency):
[ServiceContract]
public interface IMyService : ITransientDependency {
[OperationContract]
CourseDto GetCourse(int courseId);
}
Finally I implemented the interface with a constructor using injection:
public class MyService : IMySecondService {
private readonly ICourseAppService _courseAppService;
public IAbpSession AbpSession { get; set; }
public ILogger Logger { get; set; }
public MyService(ICourseAppService courseAppService) {
AbpSession = NullAbpSession.Instance;
Logger = NullLogger.Instance;
_courseAppService = courseAppService;
}
public CourseDto GetCourse(int courseId) {
AsyncHelper.RunSync(async () => {
var course = await _courseAppService.Get(courseId);
return course;
});
}
}
I would to intercept all post request to a custom WCF service (.net 3.5 SP1) in order to validate the presence of a specific header.
What I tried so far:
public class ServiceFactory : WebServiceHostFactory
{
protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
var result = base.CreateServiceHost(serviceType, baseAddresses);
result.Opened += result_Opened;
return result;
}
private void result_Opened(object sender, EventArgs e)
{
var ctx = HttpContext.Current;
var request = ctx.Request;
if (request.HttpMethod == "POST")
{
// Validate if the request contains my header
if(request.Headers["MyHeader"] != "42")
throw new VeryBadThingsException("boom");
}
}
}
I also set up my svc files to use this factory.
This is sometimes working. Actually, not all my web services calls are hooked by the open event handler. The web service actual implementation is reached, so I suppose the problem is not the web service itself.
What should I do to correctly hook all incoming requests to my service?
PS: to descbribe a bit more my context, the service is hosted by SharePoint 2010. That means I can't change the web.config file (technically it's possible, but it's a pain to deploy and maintain).
And I acutally inherits the class Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHostFactory
You should implement the IDispatchMessageInspector on the service side for this. The message instance passed to you in the AfterReceiveRequest method has a Headers property where you can check for your required headers.
Your current solution doesn't work for every call because it is only getting called when a new service host is opened. Once instantiated (and opened), that service host instance is servicing subsequent calls. But, because it is already opened, your code is not getting called on these subsequent calls.
You need to extend the WCF pipeline by adding a message inspector. The message inspector of the client will be responsible for adding the header and the message inspector of the server will be responsible for validating if the header exists.
Good practice: if you want to create custom headers, specify a custom namespace to ease lookup.
public static class WCFSOAPNamespaces
{
private const string root = "http://www.schemas.productname.com/";
public const string Headers = root + "headers/";
}
Server
The IDispatchMessageInspector handles all incoming messages to the server. This is the place where you will check for the existence of the header on the server.
public class DispatchMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
const string headerName = "nameOfTheHeader";
var someHeaderData = request.Headers.GetHeader<string>(headerName, WCFSOAPNamespaces.Headers);
//someHeaderData is the content that you want to check for every request. Attention: it throws System.ServiceModel.MessageHeaderException if the header doesn't exist
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState) { }
}
Client
The IClientMessageInspector handles messages on the client. If you need to add custom headers to the message, here is the place. If you do not need to add custom header, you can jump this first piece of code.
public class ClientMessageInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState) { }
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
const string headerName = "nameOfTheHeader";
string headerContent = ""; //fill this variable with the content
var header = new MessageHeader<string>(headerContent ?? string.Empty);
var untyped = header.GetUntypedHeader(headerName, WCFSOAPNamespaces.Headers);
request.Headers.Add(untyped);
return null;
}
}
Both (client and server)
Even if you do not need a message inspector on the Client, you still need this configuration to add message inspection to your server-side application. More specifically, we need an EndpointBehavior to handle the MessageInspector. Then, we need to set the services endpoits to use this custom endpoint behavior.
In this example, I put the 2 inspectors in the same behavior, but you can create separate behaviors if you need.
public class EndpointBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public EndpointBehavior() { }
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new ClientMessageInspector());
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new DispatchMessageInspector());
}
public void Validate(ServiceEndpoint endpoint) { }
public override Type BehaviorType
{
get { return this.GetType(); }
}
protected override object CreateBehavior()
{
return new EndpointBehavior();
}
}
Then, set your Endpoint to use this behavior.
Programmatically
...
ServiceEndpoint endpoint;
...
endpoint.Behaviors.Add(new EndpointBehavior());
Config
...
<services>
<service name="...">
<endpoint address="..." binding="..." contract="..." behaviorConfiguration="endpointBehaviorName" />
</service>
...
<behaviors>
...
<endpointBehaviors>
<behavior name="endpointBehaviorName">
<customEndpointBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
...
<extensions>
<behaviorExtensions>
<add name="customEndpointBehavior" type="FullNamespace.EndpointBehavior , AssemblyName" />
</behaviorExtensions>
</extensions>
From now on, all the requests will pass through this point.
Hope it helps.
Ok, I manage to swim between all objects, with the help of the code project article Add Custom Message Header in WCF 4 Calls.
Especially, it helped me to figure out how to properly attach a ServiceBehavior through code, using attributes.
I finally have this:
internal class ValidateSPFormDigestAttribute : Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase host)
{
foreach (ChannelDispatcher cDispatcher in host.ChannelDispatchers)
{
foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
{
eDispatcher.DispatchRuntime.MessageInspectors.Add(new ValidateSPFormDigestInspector());
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
internal class ValidateSPFormDigestInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
if (!SPUtility.ValidateFormDigest())
{
throw new FaultException(new FaultReason("Invalid form digest token"));
}
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
}
}
And I attach my custom behavior on the service directly:
[BasicHttpBindingServiceMetadataExchangeEndpoint]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ValidateSPFormDigest]
public class MyCustomService: IWidgetAdminService
The immediate benefits, is that I no more require creating a custom web service factory!
Leaving a SOAP field element empty results in a cast error for native types. (sadly cannot use xsi:nil="true" due to client constraints)
Marking the WCF contract native type as nullable<> does not appear to be enough to stop the following error being returned to the client.
The string '' is not a valid Boolean value.
at System.Xml.XmlConvert.ToBoolean(String s)
at System.Xml.XmlConverter.ToBoolean(String value)
System.FormatException
does anyone know the best method of instructing the DataContractSerializer to convert empty elements to be deserialized to null?
My example WCF service contract;
[ServiceContract()]
public interface IMyTest
{
[OperationContract]
string TestOperation(TestRequest request);
}
[ServiceBehavior()]
public class Settings : IMyTest
{
public string TestOperation(TestRequest request)
{
if (request.TestDetail.TestBool.HasValue)
return "Bool was specified";
else
return "Bool was null";
}
}
[DataContract()]
public class TestRequest
{
[DataMember(IsRequired = true)]
public int ID { get; set; }
[DataMember(IsRequired = true)]
public TestDetail TestDetail { get; set; }
}
[DataContract()]
public class TestDetail
{
[DataMember()]
public bool? TestBool { get; set; }
}
How can we get WCF to accept the following submission;
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ster="mynamespace">
<soapenv:Header/>
<soapenv:Body>
<ster:TestOperation>
<ster:request>
<ster:ID>1</ster:ID>
<ster:TestDetail>
<ster:TestBool></ster:TestBool>
</ster:TestDetail>
</ster:request>
</ster:TestOperation>
</soapenv:Body>
</soapenv:Envelope>
The client is only able to change the value it inserts <ster:TestBool>{here}</ster:TestBool> so true false or nothing are the only options.
Ok I believe I have cracked this by using an Operation Behavior to modify the underlying message before its formatted via IDispatchMessageFormatter.
The following code provides a solution against a service that is based on WCF file-less activation.
I wanted to have my IOperationBehavior live in the form of a Attribute class. Then I could simply decorate each Service Operation with my new attribute which would instigate the IOperationBehavior for that Operation - very nice and simple for the end user.
The key problem is where you apply the behavior, this is critical. The order of the operation behaviors that are called by WCF when applying the behavior via an attribute are different to when applying at the service host. The attribute based order is as follows:
System.ServiceModel.Dispatcher.OperationInvokerBehavior
MyOperationBehaviorAttribute
System.ServiceModel.OperationBehaviorAttribute
System.ServiceModel.Description.DataContractSerializerOperationBehavior
System.ServiceModel.Description.DataContractSerializerOperationGenerator
For some reason an operation behavior (only when applied via the use of an attribute) will be called before the DataContractSerializerOperationBehavior. This is a problem because in my behavior I want to delegate deserialization to the DataContractSerializerOperationBehavior Formatter (passed into my behavior as the inner formatter) within my formatter, after I have adjusted the message (see code). I don't want to have to re-write a deserialization routine when Microsoft provided a perfectly good deserializer already. I merely correct the XML in the first instance so that blanks are converted to nulls which are correctly represented within the XML so that the DataContractSerializer can tie them up to nullable types in the service interface.
So this means we cannot use attribute-based behaviors as they were intended since WCF may well be broken in a rather subtle way here since I can see no reason for this phenomenon. So we can still add an IOperationBehavior to an operation, we just have to manually assign it at the service host creation stage, because then our IOperationBehavior is inserted into the 'correct' sequence, that is, after the DataContractSerializerOperationBehavior has been created, only then can I get a reference to the inner formatter.
// This operation behaviour changes the formatter for a specific set of operations in a web service.
[System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)]
public class NullifyEmptyElementsAttribute : Attribute
{
// just a marker, does nothing
}
public class NullifyEmptyElementsBahavior : IOperationBehavior
{
#region IOperationBehavior Members
public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { }
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
// we are the server, we need to accept client message that omit the xsi:nill on empty elements
dispatchOperation.Formatter = new NullifyEmptyElementsFormatter(dispatchOperation.Formatter);
}
public void Validate(OperationDescription operationDescription) { }
#endregion IOperationBehavior Members
}
/// <summary>
/// This customized formatter intercepts the deserialization process to perform extra processing.
/// </summary>
public class NullifyEmptyElementsFormatter : IDispatchMessageFormatter
{
// Hold on to the original formatter so we can use it to return values for method calls we don't need.
private IDispatchMessageFormatter _innerFormatter;
public NullifyEmptyElementsFormatter(IDispatchMessageFormatter innerFormatter)
{
// Save the original formatter
_innerFormatter = innerFormatter;
}
/// <summary>
/// Check each node and add the xsi{namespace}:nil declaration if the inner text is blank
/// </summary>
public static void MakeNillable(XElement element)
{
XName _nillableAttributeName = "{http://www.w3.org/2001/XMLSchema-instance}nil"; // don't worry, the namespace is what matters, not the alias, it will work
if (!element.HasElements) // only end nodes
{
var hasNillableAttribute = element.Attribute(_nillableAttributeName) != null;
if (string.IsNullOrEmpty(element.Value))
{
if (!hasNillableAttribute)
element.Add(new XAttribute(_nillableAttributeName, true));
}
else
{
if (hasNillableAttribute)
element.Attribute(_nillableAttributeName).Remove();
}
}
}
public void DeserializeRequest(System.ServiceModel.Channels.Message message, object[] parameters)
{
var buffer = message.CreateBufferedCopy(int.MaxValue);
var messageSource = buffer.CreateMessage(); // don't affect the underlying stream
XDocument doc = null;
using (var messageReader = messageSource.GetReaderAtBodyContents())
{
doc = XDocument.Parse(messageReader.ReadOuterXml()); // few issues with encoding here (strange bytes at start of string), this technique resolves that
}
foreach (var element in doc.Descendants())
{
MakeNillable(element);
}
// create a new message with our corrected XML
var messageTarget = Message.CreateMessage(messageSource.Version, null, doc.CreateReader());
messageTarget.Headers.CopyHeadersFrom(messageSource.Headers);
// now delegate the work to the inner formatter against our modified message, its the parameters were after
_innerFormatter.DeserializeRequest(messageTarget, parameters);
}
public System.ServiceModel.Channels.Message SerializeReply(System.ServiceModel.Channels.MessageVersion messageVersion, object[] parameters, object result)
{
// Just delegate this to the inner formatter, we don't want to do anything with this.
return _innerFormatter.SerializeReply(messageVersion, parameters, result);
}
}
public class MyServiceHost : ServiceHost
{
public MyServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses) { }
protected override void OnOpening()
{
base.OnOpening();
foreach (var endpoint in this.Description.Endpoints)
{
foreach (var operation in endpoint.Contract.Operations)
{
if ((operation.BeginMethod != null && operation.BeginMethod.GetCustomAttributes(_NullifyEmptyElementsBahaviorAttributeType, false).Length > 0)
||
(operation.SyncMethod != null && operation.SyncMethod.GetCustomAttributes(_NullifyEmptyElementsBahaviorAttributeType, false).Length > 0)
||
(operation.EndMethod != null && operation.EndMethod.GetCustomAttributes(_NullifyEmptyElementsBahaviorAttributeType, false).Length > 0))
{
operation.Behaviors.Add(new NullifyEmptyElementsBahavior());
}
}
}
}
}
Perhaps since I am only modifying the incoming message, I could instead use IDispatchMessageInspector which will remove the dependency on the IDispatchMessageFormatter activation order. But this works for now ;)
Usage:
Add to your operation
[ServiceContract(Namespace = Namespaces.MyNamespace)]
public interface IMyServiceContrct
{
[OperationContract]
[NullifyEmptyElements]
void MyDoSomthingMethod(string someIneteger);
}
Tie into your service
A. if you have .svc simply reference MyServiceHost
<%# ServiceHost
Language="C#"
Debug="true"
Service="MyNameSpace.MyService"
Factory="MyNameSpace.MyServiceHost" %>
B. if your using file-less activation services, add this to your web.config file
<system.serviceModel>
... stuff
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" >
<!-- WCF File-less service activation - there is no need to use .svc files anymore, WAS in IIS7 creates a host dynamically - less config needed-->
<serviceActivations >
<!-- Full access to Internal services -->
<add relativeAddress="MyService.svc"
service="MyNameSpace.MyService"
factory="MyNameSpace.MyServiceHost" />
</serviceActivations>
</serviceHostingEnvironment>
... stuff
</system.serviceModel>
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.
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.