WCF FaultContract терпит неудачу с NamedPipe
У меня есть простой механизм IPC, который использует WCF и именованные каналы. Моя цель - передать подробности исключений (включая трассировку стека) клиенту для целей ведения журнала (остальная часть ведения журнала приложения находится на клиенте).
Если я использую следующий код, я могу перехватить FaultException
Контракт:
[ServiceContract]
public interface IService
{
[OperationContract]
[FaultContract(typeof(Exception))]
void DoSomething();
}
Реализация:
public class Service : IService
{
public void DoSomething()
{
try
{
ThisWillThrowAnException();
}
catch (Exception e)
{
throw new FaultException<Exception>(e);
}
}
}
Клиент:
public void CallServer()
{
try
{
proxy.DoSomething();
}
catch (FaultException<Exception> e)
{
Console.WriteLine("Caught fault exception!");
}
}
Это работает нормально, и я вижу сообщение, напечатанное на консоли. Однако, если я хочу использовать свое собственное производное исключение вместо базового класса Exception, произойдет сбой.
Пользовательское исключение:
[Serializable]
public class MyException : Exception
{
public MyException () { }
public MyException (string message) : base(message) { }
public MyException (string message, Exception inner) : base(message, inner) { }
protected MyException (
SerializationInfo info,
StreamingContext context)
: base(info, context) { }
}
Измените FaultContract на IService.DoSomething, чтобы
typeof(MyException).
Измените предложение throw в Service на
new FaultException<MyException>(new MyException(e.Message, e);
Измените выражение catch в клиенте на
catch (FaultException<MyException> e)
Когда я выполняю это, CommunicationException перехватывается на клиенте с ошибкой: System.ServiceModel.CommunicationException: при чтении из канала произошла ошибка: канал был завершен. (109, 0x6d).
Класс MyException находится в общей библиотеке, доступной как клиенту, так и серверу.
Этот вопрос очень похож на этот вопрос, но это не помогло мне.
1 ответ
Я решил эту проблему, написав свою собственную ошибку DataContract, которая содержала сериализованный список StackFrames.
Видимо эта статья MSDN не совсем точна?
http://msdn.microsoft.com/en-us/library/ff649840.aspx
[DataContract]
public class MyFault
{
[DataMember]
public string Message { get; set; }
[DataMember]
public IList<SerializableMiniStackFrame> StackTrace { get; set; }
public static MyFault CreateFault(Exception e)
{
MyFault fault = new MyFault();
fault.Message = e.Message;
fault.InitTrace(e);
return fault;
}
/// <summary>
/// Initializes the stack trace based on when the inner exception was thrown.
/// </summary>
/// <param name="inner">The inner exception.</param>
private void InitTrace(Exception inner)
{
StackTrace trace = new StackTrace(inner, true);
InitTrace(trace);
}
/// <summary>
/// Initializes the internal serializable stack frames based on the given
/// stack trace.
/// </summary>
/// <param name="stackTrace">The stack trace.</param>
private void InitTrace(StackTrace stackTrace)
{
// Create a new list of serializable frames.
this.StackTrace = new List<SerializableMiniStackFrame>();
// Iterate over each frame in the stack trace.
foreach (StackFrame frame in stackTrace.GetFrames())
{
string type = "";
Type declaringType = frame.GetMethod().DeclaringType;
if (null != declaringType)
{
type = declaringType.FullName;
}
MethodBase method = frame.GetMethod();
string methodName = method.Name;
string parameters = string.Empty;
string delimiter = string.Empty;
foreach (ParameterInfo parameter in method.GetParameters())
{
parameters += string.Format("{0}{1} {2}", delimiter, parameter.ParameterType.Name, parameter.Name);
delimiter = ", ";
}
string file = Path.GetFileName(frame.GetFileName());
int line = frame.GetFileLineNumber();
// Create a serializable frame and add it to the list.
SerializableMiniStackFrame miniFrame = new SerializableMiniStackFrame(type, methodName, parameters, file, line);
this.StackTrace.Add(miniFrame);
}
}
}
/// <summary>
/// This class encapsulates basic stack frame information into a serializable
/// object.
/// </summary>
[DataContract]
public class SerializableMiniStackFrame
{
public SerializableMiniStackFrame() { }
public SerializableMiniStackFrame(string type, string method, string parameters, string file, int line)
{
this.Type = type;
this.Method = method;
this.Parameters = parameters;
this.File = file;
this.Line = line;
}
[DataMember]
public string Type { get; set; }
[DataMember]
public string Method { get; set; }
[DataMember]
public string Parameters { get; set; }
[DataMember]
public string File { get; set; }
[DataMember]
public int Line { get; set; }
}