Following the sample for compression by Microsoft. I have added the encoder, encoder factory, and binding element to my solution. The difference from their sample is that we do not register our endpoints via the config file (requirement), but instead use a custom Service Host Factory.
Service Host:
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
ServiceHost host = base.CreateServiceHost(serviceType, baseAddresses);
if (host.Description.Endpoints.Count == 0)
{
host.AddDefaultEndpoints();
}
host.Description.Behaviors.Add(new MessagingErrorHandler());
return host;
}
So what I have tried is to add a custom binding to my endpoint, but to register that endpoint with the binding it looks like I have to use the AddServiceEndpoint but that will require an interface which is unknown. I know I could get all the interfaces that the serviceType implements and do a getInterfaces()[0], but that seems to be an unsafe approach to me.
So is there a way to register my endpoint with the custom binding and not know the interface, or is there a maybe a better approach that I should take.
My attempt at adding custom binding:
CustomBinding compression = new CustomBinding();
compression.Elements.Add(new GZipMessageEncodingBindingElement());
foreach (var uri in baseAddresses)
{
host.AddServiceEndpoint(serviceType, compression, uri);//service type is not the interface and is causing the issue
}
Your custom binding needs a transport binding element; currently you only have a message encoding binding element. You need to add probably a HttpTransportBindingElement to your custom binding as well:
CustomBinding compression = new CustomBinding(
new GZipMessageEncodingBindingElement()
new HttpTransportBindingElement());
As far as finding the interface from the service type, there's no built-in logic for that. The logic used in the WebServiceHostFactory is similar to the one shown below (this code goes 1 inheritance / implementation level deep, but you could in theory go deeper too.
private Type GetContractType(Type serviceType)
{
if (HasServiceContract(serviceType))
{
return serviceType;
}
Type[] possibleContractTypes = serviceType.GetInterfaces()
.Where(i => HasServiceContract(i))
.ToArray();
switch (possibleContractTypes.Length)
{
case 0:
throw new InvalidOperationException("Service type " + serviceType.FullName + " does not implement any interface decorated with the ServiceContractAttribute.");
case 1:
return possibleContractTypes[0];
default:
throw new InvalidOperationException("Service type " + serviceType.FullName + " implements multiple interfaces decorated with the ServiceContractAttribute, not supported by this factory.");
}
}
private static bool HasServiceContract(Type type)
{
return Attribute.IsDefined(type, typeof(ServiceContractAttribute), false);
}
Related
I'm trying to get the WCF service to run in InstanceContextMode.Single that way all requests can share the same state of the service. However, when I try to start the service with this behavior I can still see that the service's constructor gets called with each request. I couldn't figure out a quick way to update the ServiceBehaviorAttribute so that's why I'm replacing it (the default value for InstanceContextMode is not Single). Seems like there's one instance when we start it up and then another instance for all requests that come in later on. Any ideas what might be going wrong?
/// <summary>Constructor</summary>
CAutomation::CAutomation()
{
//TODO: pull from config
m_Host = gcnew ServiceHost(CAutomation::typeid, gcnew Uri("http://localhost:8001/GettingStarted"));
// add a service endpoint.
m_Host->AddServiceEndpoint(IAutomation::typeid, gcnew WSHttpBinding(), "Automation");
// add behavior
ServiceMetadataBehavior^ smb = gcnew ServiceMetadataBehavior();
smb->HttpGetEnabled = true;
m_Host->Description->Behaviors->Add(smb);
// enforce single instance behavior
m_Host->Description->Behaviors->RemoveAt(0);
ServiceBehaviorAttribute^ sba = gcnew ServiceBehaviorAttribute();
sba->InstanceContextMode = InstanceContextMode::Single;
m_Host->Description->Behaviors->Add(sba);
}
/// <summary>Starts the automation service.</summary>
void CAutomation::Start()
{
m_Host->Open();
}
Typically you set the ServiceBehaviorAttribute as a real attribute for the class that implements your service. I'm not C++/CLI expert, but I guess that since you're passing CAutomation::typeid to ServiceHost constructor, then CAutomation is your service class. Is that correct?
If so, then it should be enough to set ServiceBehaviorAttribute on the CAutomation class.
Igor Labutin pointed me in the right direction but the true issue here is that the creation of the service host object will create an instance of the class whose type is passed in to its constructor, at least when in [ServiceBehaviorAttribute(InstanceContextMode = InstanceContextMode::Single)]. Basically, the ServiceHost object should not have been the CAutomation class constructor. I moved that object outside of that constructor into another object which was responsible for when the service was supposed to start up and that corrected the issue. I'll paste a sample bit of code which helps to illustrate the better approach.
class Program
{
static void Main(string[] args)
{
Uri address = new Uri
("http://localhost:8080/QuickReturns/Exchange");
ServiceHost host = new ServiceHost(typeof(TradeService);
host.Open();
Console.WriteLine("Service started: Press Return to exit");
Console.ReadLine();
}
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,
ReturnUnknownExceptionsAsFaults=true)]
public class TradeService : ITradeService
{
private Hashtable tickers = new Hashtable();
public Quote GetQuote(string ticker)
{
lock (tickers)
{
Quote quote = tickers[ticker] as Quote;
if (quote == null)
{
// Quote doesn't exist
throw new Exception(
string.Format("No quotes found for ticker '{0}'",
ticker));
}
return quote;
}
}
public void PublishQuote(Quote quote)
{
lock (tickers)
{
Quote storedQuote = tickers[quote.Ticker] as Quote;
if (storedQuote == null)
{
tickers.Add(quote.Ticker, quote);
}
else
{
tickers[quote.Ticker] = quote;
}
}
}
}
I'm building on a previously answered question in which ICar implementations are bound using Ninject Conventions Extensions and a custom IBindingGenerator, and the ICarFactory interface is bound using the Ninject Factory Extensions' ToFactory() method and a custom instance provider.
I'm trying to refactor so that I can bind and make use of a IVehicleFactory<T>, where T is constrained to ICar, rather than the previous ICarFactory. This way, I can specify the vehicle I want in the generic type parameter, instead of passing in the name of the vehicle type in the factory's CreateCar() method.
Is it possible to bind open generic interfaces using the ToFactory() technique?
I have a feeling that I'm barking up the wrong tree, but when I was specifying an ICar type by its name, it seemed like the natural evolution to specify the ICar type itself as a generic type parameter...
Here's the test that currently fails:
[Fact]
public void A_Generic_Vehicle_Factory_Creates_A_Car_Whose_Type_Equals_Factory_Method_Generic_Type_Argument()
{
using (StandardKernel kernel = new StandardKernel())
{
// arrange
kernel.Bind(typeof(IVehicleFactory<>))
.ToFactory(() => new UseFirstGenericTypeArgumentInstanceProvider());
kernel.Bind(
scanner => scanner
.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<ICar>()
.BindWith(new BaseTypeBindingGenerator<ICar>()));
IVehicleFactory<Mercedes> factory
= kernel.Get<IVehicleFactory<Mercedes>>();
// act
var car = factory.CreateVehicle();
// assert
Assert.IsType<Mercedes>(car);
}
}
And the InvalidCastException thrown:
System.InvalidCastException was unhandled by user code
Message=Unable to cast object of type 'Castle.Proxies.ObjectProxy' to type 'IVehicleFactory`1[Mercedes]'.
Source=System.Core
StackTrace:
at System.Linq.Enumerable.<CastIterator>d__b1`1.MoveNext()
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
at Ninject.ResolutionExtensions.Get[T](IResolutionRoot root, IParameter[] parameters) in c:\Projects\Ninject\ninject\src\Ninject\Syntax\ResolutionExtensions.cs:line 37
at NinjectFactoryTests.A_Generic_Vehicle_Factory_Creates_A_Car_Whose_Type_Name_Equals_Factory_Method_String_Argument() in C:\Programming\Ninject.Extensions.Conventions.Tests\NinjectFactoryTests.cs:line 37
InnerException:
And the factory interface:
public interface IVehicleFactory<T> where T : ICar
{
T CreateVehicle();
}
And the custom instance provider, whose breakpoints I can't even get the debugger to stop on, so I really don't know what's going on in there:
public class UseFirstGenericTypeArgumentInstanceProvider : StandardInstanceProvider
{
protected override string GetName(MethodInfo methodInfo, object[] arguments)
{
var genericTypeArguments = methodInfo.GetGenericArguments();
var genericMethodDefinition = methodInfo.GetGenericMethodDefinition();
var g = genericMethodDefinition.MakeGenericMethod(genericTypeArguments.First());
return g.MemberType.GetType().Name;
}
protected override ConstructorArgument[] GetConstructorArguments(MethodInfo methodInfo, object[] arguments)
{
return base.GetConstructorArguments(methodInfo, arguments).Skip(1).ToArray();
}
}
EDIT 1 - Change IVehicleFactory signature and custom instance provider
Here's I've changed the IVehicleFactory signature to use a generic Create<T>() method, and explicitly bound Mercedes to itself.
public interface IVehicleFactory
{
T CreateVehicle<T>() where T : ICar;
}
And the new custom instance provider which returns the name of the first generic type parameter:
public class UseFirstGenericTypeArgumentInstanceProvider : StandardInstanceProvider
{
protected override string GetName(MethodInfo methodInfo, object[] arguments)
{
var genericTypeArguments = methodInfo.GetGenericArguments();
return genericTypeArguments[0].Name;
}
}
Here's the new test, still not passing:
[Fact]
public void A_Generic_Vehicle_Factory_Creates_A_Car_Whose_Type_Name_Equals_Factory_Method_String_Argument()
{
using (StandardKernel kernel = new StandardKernel())
{
// arrange
kernel.Bind<IVehicleFactory>()
.ToFactory(() => new UseFirstGenericTypeArgumentInstanceProvider())
.InSingletonScope();
kernel.Bind<Mercedes>().ToSelf();
IVehicleFactory factory = kernel.Get<IVehicleFactory>();
// act
var car = factory.CreateVehicle<Mercedes>();
// assert
Assert.IsType<Mercedes>(car);
}
}
}
A Ninject.ActivationException is thrown:
Ninject.ActivationException: Error activating Mercedes
No matching bindings are available, and the type is not self-bindable.
Activation path:
1) Request for Mercedes
I don't know why it can't find the Mercedes class, since I explicitly self-bound it. Can you spot what I'm doing wrong?
Use generic methods:
public interface IVehicleFactory
{
CreateVehicle<T>();
}
I'm trying to follow a guide from http://kellabyte.com/2010/11/13/building-extensible-wcf-service-interfaces-with-datacontractresolver/ to create and attach a DataContractSerializer.
I've declared the serializer and implemented the methods, then attached it to both the client and server with the following code:
public class ModuleDataContractResolver : DataContractResolver {
public override bool TryResolveType(Type type, Type declaredType,
DataContractResolver knownTypeResolver,
out System.Xml.XmlDictionaryString typeName,
out System.Xml.XmlDictionaryString typeNamespace) {
....// I return a true/false here
}
public override Type ResolveName(string typeName, string typeNamespace,
Type declaredType, DataContractResolver knownTypeResolver) {
....// I return a type here
}
-
var endpoint = _svcHost.Description.Endpoints.FirstOrDefault()
ContractDescription cd = endpoint.Contract;
foreach (OperationDescription opdesc in cd.Operations) {
DataContractSerializerOperationBehavior serializerBehavior = opdesc.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (serializerBehavior == null) {
serializerBehavior = new DataContractSerializerOperationBehavior(opdesc);
opdesc.Behaviors.Add(serializerBehavior);
}
serializerBehavior.DataContractResolver = new ModuleDataContractResolver();
}
Despite attaching the resolver, these two methods are called on neither the service nor the client, so the service is throwing an exception. Am I missing a step?
UPDATE: I'm not entirely convinced this isn't due to using MEF to return these types. The type in question is a MEF type, which is detected by the service but only exposed as an interface to the client, so the assembly is not loaded.
The idea is to have the service load a list of MEF modules, then expose them over this WCF service to the client as an interface.
Service side:
foreach (OperationDescription operation in endpoint.Contract.Operations)
{
operation.Behaviors.Find<DataContractSerializerOperationBehavior>()
.DataContractResolver = new ModuleDataContractResolver();
}
Client side:
foreach (var operation in factory.Endpoint.Contract.Operations)
{
operation.Behaviors.Find<DataContractSerializerOperationBehavior>()
.DataContractResolver = new ModuleDataContractResolver();
}
Eventually finding the last solution anywhere which I hadn't tried, a post by dpblogs showed how to use an attribute in the service interface's method declarations. This finally caused my resolving methods to be called.
I have a set of SOAP webservices that are wrapping exceptions using IErrorHandler, specifically:
public sealed class ErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// don't wrap existing fault exceptions
if ((error is FaultException)) return;
// our basic service fault
var businessFault = new BusinessFault { FaultMessage = error.Message, FaultReference = "Internal" };
// Resource based faultReason
var faultReason = new FaultReason(Properties.Resources.BusinessFaultReason);
var faultcode = FaultCodeFactory.CreateVersionAwareSenderFaultCode(InternalFaultCodes.BusinessFailure.ToString(), Service.Namespace);
var faultException = new FaultException<BusinessFault>(
businessFault,
faultReason,
faultcode);
// Create message fault
var messageFault = faultException.CreateMessageFault();
// Create message using Message Factory method
fault = Message.CreateMessage(version, messageFault, faultException.Action);
}
}
I have now added extra endpoints for Json and Pox which work fine, unless an exception occurs. In the case of the Json endpoint the FaultException is returned as XML.
I am aware from other SO posts that in the case of REST I would be better throwing a WebHttpException:
throw new WebFaultException<BusinessFault>(detail, HttpStatusCode.BadRequest);
Or overriding the response message properties in ProvideFault, thus:
var wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);
fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);
var rmp = new HttpResponseMessageProperty
{
StatusCode = System.Net.HttpStatusCode.BadRequest,
StatusDescription = "See fault object for more information."
};
fault.Properties.Add(HttpResponseMessageProperty.Name, rmp);
However, MSDN has some interesting remarks about WebHttpException namely:
When using a WCF REST endpoint (WebHttpBinding and WebHttpBehavior or
WebScriptEnablingBehavior) the HTTP status code on the response is set
accordingly. However, WebFaultException can be used with non-REST
endpoints and behaves like a regular FaultException.
When using a WCF REST endpoint, the response format of the serialized
fault is determined in the same way as a non-fault response. For more
information about WCF REST formatting, see WCF REST Formatting.
It would suggest therefore that I need to convert my current ProvideFault method to provide a new WebHttpException (wrapping any existing Exceptions or FaultExceptions) and then SOAP would still work as well.
Would anyone like to take a stab at what that would look like (.Net4.0 btw)? I want one error handler to rule them all!
I was under the impression that using webHttpBinding was a way to get the "all-in-one" functionality of JSON/POX/SOAP as opposed to using separate bindings for each (i.e. wsHttpBinding, basicHttpBinding etc.). So wouldn't you be able to just throw the WebHttpException and then have that give you all the error details you needed regardless of the technology?
In a REST application I'm working on, I created a new class derived from WebFaultException<T> that attaches some additional data to caught service exceptions. Calling the CreatingMessageFault() method on the instance of the derived class let me return my selected exception data from the ProvideFault() method of the error handler as the SOAP fault, letting WCF determine the correct message format.
I am using webHttpBinding to bind all but some third-party services.
Edit: Added code example
public class ErrorHandler : IErrorHandler, IServiceBehavior
{
public virtual void ProvideFault( Exception error, MessageVersion version, ref Message fault )
{
// Include next level of detail in message, if any.
MyFaultException myFaultException =
((error is MyFaultException) &&
((MyFaultException)error).Detail != null)
? new MyFaultException(error.Message + " - " +
((MyFaultException)error).Detail.Message, error)
: new MyFaultException( error.Message, error );
MessageFault messageFault = myFaultException.CreateMessageFault();
fault = Message.CreateMessage( version, messageFault, myFaultException.Action );
}
}
and
/// <summary>
/// Class used to return exception data from my WCF services.
/// </summary>
/// <remarks>
/// This class is used by a web service to pass exception data back and a
/// data object to the client. This class inherits WebFaultException, which
/// is handled specially by the WCF WebServiceHost2 service class and
/// generates a WebException on the client.
/// </remarks>
public class MyFaultException : WebFaultException<BusinessFault>
{
public class MyFaultException : WebFaultException<BusinessFault>
{
public MyFaultException(string message)
: this(HttpStatusCode.BadRequest, message) { }
public MyFaultException(HttpStatusCode statusCode, string message)
: base(new BusinessFault(message), statusCode) { }
}
then in your service, you can throw the exception to pass fault data to your client:
try
{
// Successful operation proceeds normally.
}
catch (ApplicationException e)
{
// Failure generates MyFaultException.
throw new MyFaultException("Operation failed with " + e.Message);
}
I'm having an issue getting OutputCaching to work with HttpContext.RewritePath for a WCF 4.0 WebHttp service.
My service is localized. The idea is that you call a URL like so:
/languageCode/ServiceName/Method
e.g.
/en/MyService/GetItems
And it'll return the results localized to the correct language.
My scheme is based on this article. The idea is to create a derivative of RouteBase that creates a unique, "private" route to the real service. When the user makes a request, the language code is unpacked from the URL and set as the culture for the current thread, and then HttpContext.RewritePath is used to load the actual service.
For the life of me I can't figure out how to work OutputCaching into the mix. I've decorated my service method with AspNetCacheProfile and am seeing my own VaryByCustom override called. However despite receiving a duplicate result from VaryByCustom, .NET continues into my service method anyway.
Lots of code below, sorry for the dump but I suspect it's all relevant.
How I add a route in Global.asax.cs
RouteTable.Routes.Add(new CulturedServiceRoute(
"devices",
new StructureMapServiceHostFactory(),
typeof(DeviceService)));
VaryByCustom override in Global.asax.cs:
public override string GetVaryByCustomString(
HttpContext context, string custom)
{
// This method gets called twice: Once for the initial request, then a
// second time for the rewritten URL. I only want it to be called once!
if (custom == "XmlDataFreshness")
{
var outputString = String.Format("{0}|{1}|{2}",
XmlDataLoader.LastUpdatedTicks,
context.Request.RawUrl,
context.Request.HttpMethod);
return outputString;
}
return base.GetVaryByCustomString(context, custom);
}
This is the dynamic service route class.
public class CulturedServiceRoute : RouteBase, IRouteHandler
{
private readonly string _virtualPath = null;
private readonly ServiceRoute _innerServiceRoute = null;
private readonly Route _innerRoute = null;
public CulturedServiceRoute(
string pathPrefix,
ServiceHostFactoryBase serviceHostFactory,
Type serviceType)
{
if (pathPrefix.IndexOf("{") >= 0)
{
throw new ArgumentException(
"Path prefix cannot include route parameters.",
"pathPrefix");
}
if (!pathPrefix.StartsWith("/")) pathPrefix = "/" + pathPrefix;
pathPrefix = "{culture}" + pathPrefix;
_virtualPath = String.Format("Cultured/{0}/", serviceType.FullName);
_innerServiceRoute = new ServiceRoute(
_virtualPath, serviceHostFactory, serviceType);
_innerRoute = new Route(pathPrefix, this);
}
public override RouteData GetRouteData(
HttpContextBase httpContext)
{
return _innerRoute.GetRouteData(httpContext);
}
public override VirtualPathData GetVirtualPath(
RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// This method is called even if VaryByCustom
// returns a duplicate response!
var culture = requestContext.RouteData.Values["culture"].ToString();
var ci = new CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = ci;
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture(ci.Name);
requestContext.HttpContext.RewritePath("~/" + _virtualPath, true);
return _innerServiceRoute.RouteHandler.GetHttpHandler(requestContext);
}
}
Finally, the relevant portions of the service itself:
[ServiceContract]
[AspNetCompatibilityRequirements(
RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class DeviceService
{
[AspNetCacheProfile("MyCacheProfile")]
[WebGet(UriTemplate = "")]
public IEnumerable<DeviceListItemModel> GetDevices()
{
// This is called AFTER the first VaryByCustom override is called.
// I'd expect it not to be called unless VaryByCustom changes!
var devices =
from d in _deviceRepository.GetAll()
where d.ReleaseDate < DateTime.Now
orderby d.Id descending
select new DeviceListItemModel(d);
return devices;
}
UPDATE: My cache profile:
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="MyCacheProfile" varyByCustom="XmlDataFreshness"
varyByHeader="accept" varyByParam="*" location="Server"
duration="3600" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
Hmmm seems like a valid approach to me. Is the cache profile configured correctly? Is varyByCustom called multiple times and certain to return the same result when the cache does not need to be updated?