Entering a WCF Contract Using Dynamic Proxy Generation

I am currently working on a WPF application with a WCF backend. We have implemented a customer registration solution and a journal registration solution for handling exceptions, and they work fine, but it is often difficult to bundle information together over a wire. If an exception occurs on the server, I need a way to pass the exception token back through the wire so that I can register it on the client with the exception. Thus, when I find it difficult to remove a client error, I can very easily correlate it with the exception of the server.

I would like to give a little more information about our architecture, and then Ill will report on my problem.

Our implementation of WCF is slightly more reliable than a ready-made method for setting a service reference. We have implemented the dynamic creation of proxies on the client. We accomplished this by creating web service interfaces that are shared between the client and server, and used the Castle.DynamicProxy.ProxyGenerator class and the CreateInterfaceProxyWithoutTarget method to create the proxy server. In addition, when we call the CreateInterfaceProxyWithoutTarget method, we specify the implementation of IInterceptor. There are three main classes on the server that are used to track and behavior errors:

FaultOperationInvoker (implements IOperationInvoker): Attempts to call a service method using IOperationInvoker.Invoke. If this is the type of error exception that it returns, if it is an exception, it tries to determine if there is a defect contract that has a specific data type, and if so, then wrap and create a new failure exception with detailed information.

internal class FaultOperationInvoker : IOperationInvoker { IOperationInvoker innerOperationInvoker; FaultDescription[] faults; public FaultOperationInvoker(IOperationInvoker invoker, FaultDescription[] faults) { this.innerOperationInvoker = invoker; this.faults = faults; } #region IOperationInvoker Members object[] IOperationInvoker.AllocateInputs() { return this.innerOperationInvoker.AllocateInputs(); } object IOperationInvoker.Invoke(object instance, object[] inputs, out object[] outputs) { try { return this.innerOperationInvoker.Invoke(instance, inputs, out outputs); } catch (FaultException e) { ServerLogger.GetLogger(instance.GetType().FullName).LogException(e, "Unhandled exception in service operation."); //allow fault exceptions to bubble out throw; } catch (Exception e) { Type exceptionType = e.GetType(); ServerLogger.GetLogger(instance.GetType().FullName).LogException(e, "Unhandled exception in service operation."); //if the excpetion is serializable and there operation is tagged to support fault contracts of type WcfSerivceFaultDetail if (faults != null && (faults.OfType<WcfServiceFaultDetail>().Any() || faults.Count(x => x.DetailType == typeof(WcfServiceFaultDetail)) > 0)) { throw new FaultException<WcfServiceFaultDetail>(new WcfServiceFaultDetail(true, e), "Unhandled exception during web service call.", WcfFaultCustomExceptionFactory.GetFaultCodeForExceptionType(exceptionType)); } else { throw new FaultException("Unhandled exception during web service call.", WcfFaultCustomExceptionFactory.GetFaultCodeForExceptionType(exceptionType)); } } } 

FaultOperationBehavior (implements IOperationBehavior) indicates the caller of the operation statement to invault, as described above.

 [AttributeUsage(AttributeTargets.Method)] public class FaultOperationBehavior : System.Attribute, IOperationBehavior { #region IOperationBehavior Members void IOperationBehavior.AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { } void IOperationBehavior.ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation) { } void IOperationBehavior.ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation) { dispatchOperation.Invoker = new FaultOperationInvoker(dispatchOperation.Invoker, operationDescription.Faults.ToArray()); } void IOperationBehavior.Validate(OperationDescription operationDescription) { } #endregion } 

ExceptionTraceBehavior (Inherits Attribute, implements IServiceBehavior) to handle exceptions that implement IServiceBehavior. We also have a class (FaultOperationBehavior)

 [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class, Inherited = true)] public class ExceptionTraceBehavior : Attribute, IServiceBehavior { public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach (var ep in serviceDescription.Endpoints) { foreach (var operation in ep.Contract.Operations) { if (operation.Behaviors.Contains(typeof(FaultOperationBehavior))) continue; operation.Behaviors.Add(new FaultOperationBehavior()); //Check to see if this operation description contains a wcf service fault detail operation. If it doesn't, add one. if (operation.Faults != null && (operation.Faults.Count == 0 || operation.Faults.Count > 0 && operation.Faults.Count(x => x.DetailType == typeof(WcfServiceFaultDetail)) == 0)) { FaultDescription faultDescription = new FaultDescription(operation.Name); //faultDescription.Name = "WcfServiceFaultDetail"; //faultDescription.Namespace = ""; faultDescription.DetailType = typeof(WcfServiceFaultDetail); operation.Faults.Add(faultDescription); } } } } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } } 

Each of our service interfaces has a specific implementation. All our services also inherit our base class of service, which is decorated with the ExceptionTrace attribute.

So, now with background information, that is the problem. I want a fault contract with a WCFServiceFaultDetail verbose type in every service operation, but I don’t want to put the FaultContract attribute on every service operation. As you can see in ExceptionTraceBehavior, I figured out how to programmatically add a malfunction contract, and it works great for adding an error to an operation. When the usual old exception falls into the operating invoker, it discovers that there is a corresponding fault contract and throws a new FaultExcption. However, when the client delays the exception, it gets caught (FaultExcection fe) instead of catch (FaultException fe).

However, if you delete the code to programmatically add a fault contract, I decorate each operation with [FaultContract (typeof (WcfServiceFaultDetail))], the client catches the exception as expected.

The only thing I can understand is that, since the proxy is generated dynamically from the interface, not from WSDL or other metadata, and there is no agreement on fault reduction on the interface, my program contract for malfunction is not respected.

With this in mind, Ive tried to figure out how to add a malfunction contract in the implementation of IInterceptor, but was unsuccessful.

Therefore, I hope that someone has already done this and can provide some details. Any help is appreciated.

+7
source share
1 answer

I'm not a WCF expert, but my hands are a little dirty.

I think you're right. Failure contracts are resolved from assembly metadata when creating a ChannelFactory of a specific type. Since your interfaces are not embellished with the corresponding FaultContract attributes, your client uses the default contract without any details.

Adding FaultContract attributes to your interface methods at run time will not work either.

One solution can generate and dynamically use types at runtime to generate pipes.

I have never done this, but I think it can be done starting with the AppDomain DefineDynamicAssembly.

0
source

All Articles