I am currently registering a WCF service using the following code:
var factory = new DefaultServiceHostFactory();
RouteTable.Routes.Add(new ServiceRoute("XXXEndPoint", factory, IXXXEndPoint)));
This is all well and good however I also need to change the MaxStringContentLength property of the reader quota settings. It appears that the default value of 8192 is used, regardless of my attempts to change this and I guess this is from the DefaultServiceModel?
Are there any suitable hooks to override this setting on the DefaultServiceModel, or should I be deriving my own service host/model classes, or am I going about this the wrong way?
Any suggestions appreciated.
EDIT: Please note that the configuration of the binding must be performed programatically (not via configuration files).
Thanks
You can make it by extending host factory.
By little modification of code bellow you can pass an additional parameter to WCFServiceHostFactory to set it from Global.asax
I have used the classes bellow to always set it to Int32.MaxValue
//in Global.asax
routes.Add(new ServiceRoute(routePrefix,
new WCFServiceHostFactory(),
serviceType));
//WCFServiceHostFactory.cs
namespace MyServices.MyService
{
public class WCFServiceHostFactory:WebServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
WCFServiceHost host = new WCFServiceHost(serviceType, baseAddresses);
return host;
}
}
}
//WCFServiceHost.cs
namespace MyServices.MyService
{
public class WCFServiceHost:WebServiceHost
{
public WCFServiceHost(): base ()
{
}
public WCFServiceHost (object singletonInstance, params Uri [] baseAddresses)
: base (singletonInstance, baseAddresses)
{
}
public WCFServiceHost(Type serviceType, params Uri[] baseAddresses)
: base (serviceType, baseAddresses)
{
}
protected override void OnOpening ()
{
base.OnOpening ();
foreach (Uri baseAddress in BaseAddresses) {
bool found = false;
foreach (ServiceEndpoint se in Description.Endpoints)
if (se.Address.Uri == baseAddress)
{
found = true;
((WebHttpBinding)se.Binding).ReaderQuotas.MaxStringContentLength = Int32.MaxValue;//here you set it and also set it below
}
if (!found) {
if (ImplementedContracts.Count > 1)
throw new InvalidOperationException ("Service '"+ Description.ServiceType.Name + "' implements multiple ServiceContract types, and no endpoints are defined in the configuration file. WebServiceHost can set up default endpoints, but only if the service implements only a single ServiceContract. Either change the service to only implement a single ServiceContract, or else define endpoints for the service explicitly in the configuration file. When more than one contract is implemented, must add base address endpoint manually");
var enumerator = ImplementedContracts.Values.GetEnumerator ();
enumerator.MoveNext ();
Type contractType = enumerator.Current.ContractType;
WebHttpBinding binding = new WebHttpBinding();
binding.ReaderQuotas.MaxStringContentLength = Int32.MaxValue; //here also
AddServiceEndpoint (contractType, binding, baseAddress);
}
}
foreach (ServiceEndpoint se in Description.Endpoints)
if (se.Behaviors.Find<WebHttpBehavior> () == null)
se.Behaviors.Add (new WebHttpBehavior ());
// disable help page.
ServiceDebugBehavior serviceDebugBehavior = Description.Behaviors.Find<ServiceDebugBehavior> ();
if (serviceDebugBehavior != null) {
serviceDebugBehavior.HttpHelpPageEnabled = false;
serviceDebugBehavior.HttpsHelpPageEnabled = false;
}
}
}
}
Related
I am building a POC that aims to demonstrate how a dependency hierarchy can be constructed from within a WCF service with Unity and keep all assemblies in an application completely loosely coupled.
What I did is to create the following class libraries:
Data Access Layer:
- One assembly with the repository interface.
- One assembly with the implementation of this interface that pretends to access a DB.
- One assembly with the implementation of this interface that pretends to access XML docs.
Business Layer:
- One assembly with the business object interface.
- One assembly with the implementation of this interface that receives on its constructor the repository interface.
Service Layer:
- One assembly with the service interface.
- One assembly with the implementation of this interface that receives on its constructor the business object interface.
Finally I created an assembly with a Service Host Factory, a Service Host, and an Instance Provider that will be in charge of creating the dependency hierarchy. The code looks like so:
public class UnityServiceHostFactory : ServiceHostFactory
{
private readonly UnityContainer _container;
public UnityServiceHostFactory()
{
_container = new UnityContainer();
new ContainerConfigurator().Configure(_container);
}
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new UnityServiceHost(_container, serviceType, baseAddresses);
}
}
public class ContainerConfigurator
{
public void Configure(UnityContainer container)
{
container.RegisterType<IInvoiceRepository, InvoiceRepository>("dbInvoiceRepository");
container.RegisterType<IInvoiceRepository, XmlInvoice>("xmlInvoiceRepository");
container.RegisterType<IInvoiceFinder, InvoiceFinder>();
}
}
public class UnityServiceHost : ServiceHost
{
public UnityServiceHost(UnityContainer container, Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
if(container == null) throw new ArgumentNullException("container");
var contracts = ImplementedContracts.Values;
foreach (var c in contracts)
{
var instanceProvider = new UnityInstanceProvider(container, serviceType);
c.Behaviors.Add(instanceProvider);
}
}
}
public class UnityInstanceProvider : IInstanceProvider, IContractBehavior
{
private readonly UnityContainer _container;
private readonly Type _serviceType;
public UnityInstanceProvider(UnityContainer container, Type serviceType)
{
if (container == null) throw new ArgumentNullException("container");
if (serviceType == null) throw new ArgumentNullException("serviceType");
_container = container;
_serviceType = serviceType;
}
public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}
public object GetInstance(InstanceContext instanceContext, Message message)
{
return _container.Resolve(_serviceType);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
_container.Teardown(instance);
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.InstanceProvider = this;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
}
I am testing this with a console application, I instantiate the service proxy and make the call to its method but, since both registrations are named Unity does not know which one to instantiate. If I remove the name from either of them it gets resolved successfully.
Basically I would like to be able to do something like this:
static void Main(string[] args)
{
//First call, want it to resolve to the InvoiceRepository concrete type
//new InvoiceService(new InvoiceFinder(new InvoiceRepository))
var invoiceService1 = new InvoiceProxy();
var response1 = invoiceService1.GetSumarizedInvoiceBy(new InvoiceRequest(1));
//Second call, want it to resolve to the XmlInvoice concrete type
//new InvoiceService(new InvoiceFinder(new XmlInvoice))
var invoiceService2 = new InvoiceProxy();
var response2 = invoiceService2.GetSumarizedInvoiceBy(new InvoiceRequest(2));
}
Notice how invoiceService1 and invoiceService2 are two different instances of the same service but the dependency within its own dependency was resolved differently for both.
What do I have to do so that I can tell Unity which repository to instantiate when I am either instantiating the service proxy or calling its method?
Thanks for your help.
Pass the name you gave Unity in the RegisterType<>() call to Resolve<>().
From Resolving an Object by Type and Registration Name
// Create container and register types
IUnityContainer myContainer = new UnityContainer();
myContainer.RegisterType(typeof(MyServiceBase), typeof(DataService), "Data");
myContainer.RegisterType(typeof(MyServiceBase), typeof(LoggingService), "Logging");
// Retrieve an instance of each type
MyServiceBase myDataService = (MyServiceBase)myContainer.Resolve(typeof(MyServiceBase), "Data");
You are using the generic extension methods that are equivalent to this example.
I have a custom OperationBehavior. I would like to apply it for all operations at once.
Unfortunately, OperationBehaviors cannot be configured per entire service or in web.config.
When hosting WCF service in a test application, I can do the following:
foreach (var ep in _serviceHost.Description.Endpoints)
{
foreach (OperationDescription od in ep.Contract.Operations)
{
od.Behaviors.Add(new MyOperationBehavior());
}
}
_serviceHost.Open();
But how do I do it in a IIS hosted web application?
I tried to get OperationContext.Current.Host.Description.Endpoints in Application_Start but of course OperationContext.Current is not available before any operation has started, so my approach fails.
You can use a ServiceHostFactory to do that. With it, you can get access to the OM prior to the service being opened.
This is an example:
public class MyFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
ServiceHost host = base.CreateServiceHost(serviceType, baseAddresses);
foreach (var ep in host.Description.Endpoints)
{
foreach (OperationDescription od in ep.Contract.Operations)
{
od.Behaviors.Add(new MyOperationBehavior());
}
}
return host;
}
}
And you can get more information about service host factories at http://blogs.msdn.com/b/carlosfigueira/archive/2011/06/14/wcf-extensibility-servicehostfactory.aspx
At the end I found an alternative solution: use a contract behavior which injects any other behvaior as needed. Like this:
public class InjectAllOperationsBehavior : Attribute, IContractBehavior
{
private IOperationBehavior _operationBehavior = null;
public InjectAllOperationsBehavior(Type operationBehaviorType)
{
_operationBehavior =
(IOperationBehavior)Activator.CreateInstance(operationBehaviorType);
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
foreach (OperationDescription opDescription in contractDescription.Operations)
{
opDescription.Behaviors.Add(_operationBehavior);
}
}
... other IContractBehavior methods can be left empty. You might want also to use the ApplyClientBehavior method with the same code from the ApplyDispatchBehavior method.
I'm implementing a WCF service hosted by IIS, that impersonates the caller. When I have the service endpoint configuration in the Web.config-file everything works as intended.
I want to set the service endpoint programmatically but I'm missing something because the caller is not impersonated (the endpoint works fine except for that little detail). Is there any way I can capture the service endpoint created from web.config in code so that when debugging I can find what the difference is between this one and the one I create programmatically?
Thanks,
Christian
You can use the default service host factory to access the endpoint from web.config in your code (and possibly attach a debugger to the IIS process to see what it contains).
public class MyServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new MyServiceHost(serviceType, baseAddresses);
}
}
public class MyServiceHost : ServiceHost
{
public MyServiceHost(Type serviceType, Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
}
protected override void OnOpening()
{
// At this point you have access to the endpoint descriptions
foreach (var endpoint in this.Description.Endpoints)
{
Console.WriteLine("Endpoint at {0}", endpoint.Address.Uri);
Binding binding = endpoint.Binding;
BindingElementCollection elements = binding.CreateBindingElements();
foreach (var element in elements)
{
Console.WriteLine(" {0}", element);
}
}
base.OnOpening();
}
}
And in the .svc file, specify the Factory="YourNamespace.MyServiceHostFactory" attribute.
Instantiating an unregistered service with known services (injecting them via ctr).
I want to avoid container pollution.
Here is another way to resolve unregistered concrete types from container. Note that all autofac constructor finding and selecting logic, all registration event handlers remain in force.
First, you define this method:
public static object ResolveUnregistered(this IComponentContext context, Type serviceType, IEnumerable<Parameter> parameters)
{
var scope = context.Resolve<ILifetimeScope>();
using (var innerScope = scope.BeginLifetimeScope(b => b.RegisterType(serviceType)))
{
IComponentRegistration reg;
innerScope.ComponentRegistry.TryGetRegistration(new TypedService(serviceType), out reg);
return context.ResolveComponent(reg, parameters);
}
}
The idea is that you get component registration from derived context and resolve it in the current context.
Then you can create some handy overloads:
public static object ResolveUnregistered(this IComponentContext context, Type serviceType)
{
return ResolveUnregistered(context, serviceType, Enumerable.Empty<Parameter>());
}
public static object ResolveUnregistered(this IComponentContext context, Type serviceType, params Parameter[] parameters)
{
return ResolveUnregistered(context, serviceType, (IEnumerable<Parameter>)parameters);
}
public static TService ResolveUnregistered<TService>(this IComponentContext context)
{
return (TService)ResolveUnregistered(context, typeof(TService), Enumerable.Empty<Parameter>());
}
public static TService ResolveUnregistered<TService>(this IComponentContext context, params Parameter[] parameters)
{
return (TService)ResolveUnregistered(context, typeof(TService), (IEnumerable<Parameter>)parameters);
}
I found a solution that required some custom code. Somethings are specific to my app, but I think you can get the picture.
Resolve(parameter.ParameterType) would be a call to your container.
public object ResolveUnregistered(Type type)
{
var constructors = type.GetConstructors();
foreach (var constructor in constructors)
{
try
{
var parameters = constructor.GetParameters();
var parameterInstances = new List<object>();
foreach (var parameter in parameters)
{
var service = Resolve(parameter.ParameterType);
if (service == null) throw new NopException("Unkown dependency");
parameterInstances.Add(service);
}
return Activator.CreateInstance(type, parameterInstances.ToArray());
}
catch (NopException)
{
}
}
throw new NopException("No contructor was found that had all the dependencies satisfied.");
}
Here is a way to resolve an unregistered type with know constructor (ctor) properties. This is based on a previous great post: https://stackoverflow.com/a/6994144/2641447.
In this solution, it is absolutely great that constructor finding and selecting logic is handled by Autofac.
The comment is referred to dispose issues what I have solved with the 'ExternallyOwned()' wich configure the component so that instances are never disposed by the container.
I think that an improvement of the solution may be the following:
public static object ResolveUnregistered(this IComponentContext context, Type serviceType, IEnumerable<Parameter> parameters)
{
var scope = context.Resolve<ILifetimeScope>();
using (var innerScope = scope.BeginLifetimeScope(b => b.RegisterType(serviceType).ExternallyOwned()))
return innerScope.Resolve(serviceType, parameters);
}
The usings:
using Autofac;
using Autofac.Core;
using System;
using System.Collections.Generic;
How to configure a WCF service for a load balancer and specific the end points
You could try writing a custom service host factory which will use the load balancer's url as base address:
public class CustomServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(
Type serviceType, Uri[] baseAddresses)
{
Uri uri = null;
if (baseAddresses.Length < 2)
{
uri = baseAddresses[0];
}
else
{
// TODO: You need to choose the load balancer's url here:
uri = baseAddresses[????];
}
return base.CreateServiceHost(serviceType, new Uri[] { uri });
}
}