WCF WebHttp, Service, Behaviors, Endpoints, Custom Formatter - wcf

I followed this guide for creating my custom formatter so I could use the Newtonsoft Json.NET for object serialization since the built in Microsoft one doesn't support cycles from parent/child relationships.
http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/03/wcf-extensibility-message-formatters.aspx
In his example he's manually creating his ServiceHost. I am leveraging Routes and the WebServiceFactory taught to me by this guide.
http://blogs.msdn.com/b/endpoint/archive/2010/01/06/introducing-wcf-webhttp-services-in-net-4.aspx
From what I can tell I just need to figure out a way to add the appropriate Behaviour to my Service endpoints. Any help in pointing me in the right direction would be appreciated.
Some code snippets below for ease of reference...
In my Global.asax
WebServiceHostFactory webServiceHostFactory = new WebServiceHostFactory();
RouteTable.Routes.Add(new ServiceRoute(Accounts.Route, webServiceHostFactory, typeof(Accounts)));
If my web.config
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<!--
Configure the WCF REST service base address via the global.asax.cs file and the default endpoint
via the attributes on the <standardEndpoint> element below
-->
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="false" defaultOutgoingResponseFormat="Json"/>
</webHttpEndpoint>
</standardEndpoints>
In the Main function of his program
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITestService), new BasicHttpBinding(), "soap");
WebHttpBinding webBinding = new WebHttpBinding();
webBinding.ContentTypeMapper = new MyRawMapper();
host.AddServiceEndpoint(typeof(ITestService), webBinding, "json").Behaviors.Add(new NewtonsoftJsonBehavior());

To use the routes and get a reference to the service endpoint, you'll need your custom service host factory. It can do the same as the WebServiceHostFactory which you're currently using (simply return a reference to the WebServiceHost), but instead return a reference to your custom service host.
You can find more information about service host factories at http://blogs.msdn.com/b/carlosfigueira/archive/2011/06/14/wcf-extensibility-servicehostfactory.aspx.
public class MyServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return base.CreateServiceHost(serviceType, baseAddresses);
}
class MyServiceHost : WebServiceHost
{
public MyServiceHost(Type serviceType, Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{ }
protected override void OnOpening()
{
base.OnOpening();
foreach (ServiceEndpoint endpoint in this.Description.Endpoints)
{
CustomBinding custom = endpoint.Binding as CustomBinding;
if (custom != null)
{
custom = new CustomBinding(endpoint.Binding);
}
custom.Elements.Find<WebMessageEncodingBindingElement>().ContentTypeMapper = new MyRawMapper();
endpoint.Binding = custom;
endpoint.Behaviors.Add(new NewtonsoftJsonBehavior());
}
}
}
}

Related

How to use Autofac with a CustomServiceHostFactory in an IIS hosted WCF service?

Lets say I have a simple service contract:
[ServiceContract(Namespace = Constants.MyNamespace)]
public interface IAccountService
{
[OperationContract]
Account GetByAccountNumber(string accountNumber);
}
Here is the service:
[ServiceBehavior(Namespace = Constants.MyNamespace)]
public class AccountService : IAccountService
{
private readonly IUnitOfWorkAsync _uow;
private readonly IRepositoryAsync<Account> _repository;
public AccountService(IDataContextAsync dataContext)
{
_uow = new UnitOfWork(dataContext);
_repository = new Repository<Account>(dataContext, _uow);
}
public Account GetByAccountNumber(string accountNumber)
{
return _repository.GetByAccountNumber(accountNumber);
}
}
Here is the CustomServiceHostFactory:
public class CustomServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDbContext>().As<IDataContextAsync>();
builder.Register(c => new AccountService(c.Resolve<IDataContextAsync>())).As<IAccountService>();
using (var container = builder.Build())
{
var host = new CustomServiceHost(serviceType, baseAddresses);
host.AddDependencyInjectionBehavior<IAccountService>(container);
return host;
}
}
}
..where CustomServiceHost creates all of the bindings/behaviors programmatically. I am using file-less activation so my .config file just has section like this:
<serviceHostingEnvironment>
<serviceActivations>
<add service="Company.Project.Business.Services.AccountService"
relativeAddress="Account/AccountService.svc"
factory="Company.Project.WebHost.CustomServiceHostFactory"/>
</serviceActivations>
</serviceHostingEnvironment>
I publish to IIS and can view the site in a browser. It says "you have created a service". However, any call I try to make to the service from my client application gives the following error:
Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.
How do you use Autofac with WCF and a CustomServiceHostFactory?
I am able to use poor man's DI as a workaround for now but was hoping to get this working. I can't seem to find any good examples on the web. Thanks.
Don't dispose of the container. Instead of a using statement, keep the container alive. It needs to live as long as the host.
You'll notice in the default Autofac WCF stuff the container is a global static that lives for the app lifetime - that's why.

How can I capture WCF Service endpoint

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.

Inject dependency into IAuthorizationPolicy

I have a custom IAuthorizationPolicy which has a dependency on a repository
internal class CustomAuthorizationPolicy : IAuthorizationPolicy
{
private IBaseRepository _baseRepository;
public CustomAuthorizationPolicy(IBaseRepository baseRepository)
{
_baseRepository = baseRepository;
}
}
It is configured like this in web.config
<serviceAuthorization principalPermissionMode="Custom">
<authorizationPolicies>
<add policyType="CustomAuthorizationPolicy" />
</authorizationPolicies>
</serviceAuthorization>
This fails because WCF is not able to inject the required object when the policy is created.
It expects a parameterless constructor.
I am using StructureMap and has a custom IInstanceProvider that handles all other dependencies in my application. But I can't get it to handle this situation.
Is this possible to do??
I ended up solving this with the use of a custom ServiceHost and a ServiceHostFactory.
The factory sends the IoC container to the servicehost, which adds the new policy with a reference to the container. Now the policy can use the container to get the objects it needs.
public class CustomServiceHost : ServiceHost
{
public CustomServiceHost(IContainer container, Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
// Keep existing policies
var policies = new List<IAuthorizationPolicy>();
if (Authorization.ExternalAuthorizationPolicies != null)
{
policies.AddRange(Authorization.ExternalAuthorizationPolicies);
}
// Add new policy
policies.Add(new PasswordAuthorizationPolicy(container));
Authorization.ExternalAuthorizationPolicies = policies.AsReadOnly();
// Set correct mode
this.Authorization.PrincipalPermissionMode = PrincipalPermissionMode.Custom;
}
}

How to configure WCF binding programmatically?

I need to implement following using code:
<basicHttpBinding>
<binding name="NewBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
Is there any samples available? I'm working on WCF REST service and register routes manually. Placing this config into configuration doesn't work. I'd like to get it setup programmatically if possible. Also, at what point in code I should do it?
EDIT:
My service Routed in Global.asax like so:
foreach (var account in cmc.Accounts.Where(aa => aa.IsActive).ToList())
{
RouteTable.Routes.Add(
new ServiceRoute(
account.AccountId + "/mobile", new MyServiceHostFactory(), typeof(MobileService)));
}
And I have my own ServiceHost
public class MyServiceHost : WebServiceHost
{
private readonly Type _serviceType;
private readonly CompositionContainer _container;
public MyServiceHost(Type serviceType, CompositionContainer container, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
_serviceType = serviceType;
_container = container;
}
protected override void OnOpening()
{
if (Description.Behaviors.Find<MyServiceBehavior>() == null)
{
Description.Behaviors.Add(new MyServiceBehavior(_serviceType, _container));
}
base.OnOpening();
}
}
For this specific case, the equivalent binding is the following:
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

Specifying a WCF binding when using ServiceRoute

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