В настоящее время я работаю над приложением WPF с бэкэндом WCF. Мы внедрили решение для регистрации клиентов и решение для регистрации журналов для обработки исключений, и они отлично работают, но часто сложно связать информацию вместе по проводу. Если на сервере возникает исключение, мне нужен способ передать маркер исключения обратно через провод, чтобы я мог зарегистрировать его на клиенте с исключением. Таким образом, когда я затрудняюсь снять клиентскую ошибку, я могу очень легко сопоставить ее с исключением сервера.Ввод контракта WCF с использованием генератора динамических прокси Замок
Я хотел бы дать немного больше информации о нашей архитектуре, а затем я изложу свою проблему.
Наша реализация WCF немного более надежна, чем готовый метод установки ссылки на службу. Мы реализовали динамическое создание прокси на клиенте. Мы достигли этого, создав интерфейсы веб-сервисов, которые совместно используются клиентом и сервером, и использовали класс Castle.DynamicProxy.ProxyGenerator и метод CreateInterfaceProxyWithoutTarget для создания прокси-сервера. Кроме того, когда мы вызываем метод CreateInterfaceProxyWithoutTarget, мы указываем реализацию IInterceptor. На сервере есть три основных класса, которые используются для нашего поведения следов и неисправностей:
FaultOperationInvoker (реализует IOperationInvoker): пытается вызвать метод сервиса с помощью IOperationInvoker.Invoke. Если это тип исключения, который он вызывает, если он является исключением, он пытается определить, существует ли контракт с дефектом, имеющий конкретный тип детали, и если да, тогда оберните и создайте новое исключение сбоя с подробной информацией.
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 (Реализует IOperationBehavior) указывает операцию отправки вызова значение операции неисправность вызывающего, описанной выше.
[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 Атрибут, Реализует IServiceBehavior) для обработки исключений, которые реализуют IServiceBehavior. У нас также есть класс (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)
{
}
}
Каждый из наших сервисных интерфейсов имеет конкретную реализацию. Все наши сервисы также наследуют наш базовый класс обслуживания, который украшен атрибутом ExceptionTrace.
Итак, теперь с информацией о форе, вот в чем проблема. Я хочу, чтобы в каждой сервисной операции был заключен контракт на неисправность с подробным типом WCFServiceFaultDetail, но я не хочу помещать атрибут FaultContract в каждую операцию обслуживания. Как вы можете видеть в ExceptionTraceBehavior, я понял, как программно добавить контракт о неисправности, и он отлично работает для добавления ошибки в операцию. Когда обычное старое исключение попадает в операционную invoker, он обнаруживает, что существует соответствующий контракт о неисправности и бросает новый FaultExcption. Однако, когда исключение задерживает клиент, оно попадает в код catch (FaultExcection fe) вместо кода catch (FaultException fe).
Однако, если вы удалите код, чтобы программно добавить контракт о неисправности, я украшаю каждую операцию с помощью [FaultContract (typeof (WcfServiceFaultDetail))], клиент ловит исключение, как ожидалось.
Единственное, что я могу понять, это то, что, поскольку прокси генерируется динамически из интерфейса, а не из WSDL или других метаданных, и на интерфейсе нет дефектного контракта, мой контракт по программному сбою не будучи заслуженным.
С этой мыслью я попытался выяснить, как добавить контракт с неисправностью в реализацию IInterceptor, но не имел успеха.
Так что я надеюсь, что кто-то уже сделал это и может предоставить некоторые детали. Любая помощь приветствуется.
Для корреляции ошибок клиент/сервер вы просмотрели раскрытие идентификатора активности? – Paciv
Я этого не сделал, я посмотрю на это. Спасибо за совет. – BernicusMaximus