I have a simple IPC mechanism that uses WCF and named pipes. My goal is to propagate exception details (including the stacktrace) to the client for logging purposes (the rest of the application logging is located on the client).
If I use the following code I am able to catch FaultException<Exception> on the client and see exception details:
Contract:
[ServiceContract]
public interface IService
{
[OperationContract]
[FaultContract(typeof(Exception))]
void DoSomething();
}
Implementation:
public class Service : IService
{
public void DoSomething()
{
try
{
ThisWillThrowAnException();
}
catch (Exception e)
{
throw new FaultException<Exception>(e);
}
}
}
Client:
public void CallServer()
{
try
{
proxy.DoSomething();
}
catch (FaultException<Exception> e)
{
Console.WriteLine("Caught fault exception!");
}
}
This works fine and I see the message printed on the console. However, if I want to use my own derived exception instead of the base Exception class, it fails.
Custom 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) { }
}
Change the FaultContract on IService.DoSomething to
typeof(MyException).
Change the throw clause in Service to
new FaultException<MyException>(new MyException(e.Message, e);
Change the catch clause in the client to
catch (FaultException<MyException> e)
When I execute this, a CommunicationException is caught on the client with the error:
System.ServiceModel.CommunicationException: There was an error reading from the pipe: The pipe has been ended. (109, 0x6d).
The MyException class is in a shared library available to both the client and server.
This question is very similar to this question, but that did not help me.
I resolved this by writing my own fault DataContract which contained a serialized list of StackFrames.
Apparently this MSDN article is not exactly accurate?
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; }
}
Related
I am working with Selenium and MSTest as framework and I would like to extend the TestClass, TestMethod and TestCleanup. Right now the tests are derived from a TestBase class that is defined as follows:
[TestClass]
public class TestBase : IDisposable
{
bool disposed = false;
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
public TestContext TestContext { get; set; }
protected HelperSelenium SeleniumHelper { get; set; }
[TestInitialize]
public void TestInitBase()
{
SeleniumHelper = new HelperSelenium(TestContext);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
handle.Dispose();
SeleniumHelper.GetWebDriver().Quit();
// Free any other managed objects here.
//
}
disposed = true;
}
}
The [TestClass] instantiates TestContext and [TestInitialize] instantiates the HelperSelenium with TestContext as a paremeter (this is necessary to tell Selenium which class it should use depending on a TestContext property.
I've tried with this code without success:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class SeleniumTestClass : TestClassAttribute
{
//Prueba para finalizacion de recursos
bool disposed = false;
/// <summary>
/// Bandera para saber si se ha llamado a dispose e instanciacion de un Selfhandle
/// </summary>
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
public TestContext TestContext { get; set; }
public HelperSelenium SeleniumHelper { get; set; }
#region ConfiguraciĆ³n Test
[TestInitialize]
public void TestInitBase()
{
SeleniumHelper = new HelperSelenium(TestContext);
}
public void Dispose()
{
//SeleniumHelper.GetWebDriver().Quit();
//SeleniumHelper.GetWebDriver().Dispose();
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
handle.Dispose();
SeleniumHelper.GetWebDriver().Quit();
// Free any other managed objects here.
//
}
disposed = true;
}
I would like to extend [TestClass] to [SeleniumTestClass] so if I use [SeleniumTestClass] in a derived class all of this would be done automatically. I've trying to do this because we are using EventFiringWebDriver which will throw an exception even inside a Wait.Until loop. Selenium can throw a exception in 2 ways:
An action has been performed that throws an exception, but the
exception is caught by Wait.Until
An action has been performed
that throws an exception, and the exception is outside a
Wait.Until
So if TestMethod fails, I would be able to take a screenshot while catching the exception. I think TestCleanup cannot be inherited so I am not sure if I would be able to catch the exception in the TestCleanup
I know I could wrap up the TestMethod and the TestCleanup each one in a try-catch but that would be cumbersome to do in each test we have and the test we would be creating in the future.
Any help is appreciated.
Try having the abstract class calling a new OnTest method:
[TestClass]
public abstract class TestBase
{
bool disposed = false;
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
public TestContext TestContext { get; set; }
protected HelperSelenium SeleniumHelper { get; set; }
[TestInitialize]
public void TestInitBase()
{
SeleniumHelper = new HelperSelenium(TestContext);
}
public abstract void OnTest();
[TestMethod()]
public void SeleniumTest()
{
try
{
OnTest();
}
catch(Exception ex){
}
}
}
Json is sent from the client to my api controller
Message
{"todo":{"title":"jo","isCompleted":false,"isDeleted":false,"testFK":null}}
I will list different consuming server side methods that should receive this message transformed into a object
public void Post(TaskEntity todo)
{
using (var context = new ToDoEntities())
{
context.Tasks.Add(todo.ToEf());
context.SaveChanges();
}
}
todo parameter is null.
public void Post(UpdateTodoInputMessage message)
{
using (var context = new ToDoEntities())
{
context.Tasks.Add(todo.todo.ToEf());
context.SaveChanges();
}
}
Message class
namespace MvcApplication1.Messages
{
[DataContract]
public class UpdateTodoInputMessage
{
[DataMember]
public TaskEntity todo { get; set; }
}
}
The todo property of the message class is null.
If I change the post method to a string then my client receives a 405 Method not allowed.
public void Post(string message)
{
using (var context = new ToDoEntities())
{
// context.Tasks.Add(todo.todo.ToEf());
// context.SaveChanges();
}
}
I am at a loss and would love some assistance as to how the deserialization of the string is taking place, and why I am getting these strange results.
The reason was that my Task Entity class didn't have a empty constructor
I am running into the below error the first time my ViewModel is being instantiated by the SimpleIoC. I believe I have setup the container as it should be, but for some reason, I am still getting the below error. Any ideas or assistance would be very much appreciated.
Microsoft.Practices.ServiceLocation.ActivationException was unhandled by user code
HResult=-2146233088
Message=Type not found in cache: Windows.UI.Xaml.Controls.Frame.
Source=GalaSoft.MvvmLight.Extras
StackTrace:
at GalaSoft.MvvmLight.Ioc.SimpleIoc.DoGetService(Type serviceType, String key) in c:\Users\Public\Downloads\CodePlex\MVVMLight\GalaSoft.MvvmLight\GalaSoft.MvvmLight.Extras (NET35)\Ioc\SimpleIoc.cs:line 532
at GalaSoft.MvvmLight.Ioc.SimpleIoc.GetService(Type serviceType) in c:\Users\Public\Downloads\CodePlex\MVVMLight\GalaSoft.MvvmLight\GalaSoft.MvvmLight.Extras (NET35)\Ioc\SimpleIoc.cs:line 768
at GalaSoft.MvvmLight.Ioc.SimpleIoc.MakeInstance[TClass]() in c:\Users\Public\Downloads\CodePlex\MVVMLight\GalaSoft.MvvmLight\GalaSoft.MvvmLight.Extras (NET35)\Ioc\SimpleIoc.cs:line 708
InnerException:
Here are pieces of my code related to this:
ViewModelLocator.cs (Located in my Win8 project)
public class ViewModelLocator
{
/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
// Create design time view services and models
//SimpleIoc.Default.Register<IDataService, DesignDataService>();
}
else
{
// Create run time view services and models
//SimpleIoc.Default.Register<IDataService, DataService>();
SimpleIoc.Default.Register<INavigationService, NavigationService>();
SimpleIoc.Default.Register<IParseService, ParseService>();
SimpleIoc.Default.Register<IServiceHandler, ServiceHandler>();
}
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<ActionViewModel>();
}
public MainViewModel MainVM
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public ActionViewModel ActionVM
{
get
{
return ServiceLocator.Current.GetInstance<ActionViewModel>();
}
}
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
MainViewModel.cs Constructor
public class MainViewModel : ViewModelBase
{
#region Variables
private readonly INavigationService _navigationService;
private readonly IParseService _parseService;
#endregion
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(INavigationService navigationService, IParseService parseService)
{
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
}
else
{
_navigationService = navigationService;
_parseService = parseService;
BuildCommonData();
}
}
I know this is long overdue, but here is the offending code in the implementation of my NavigationService class.
NavigationService class (Before)
public class NavigationService : INavigationService
{
/// <summary>
/// Gets the root frame.
/// </summary>
private Frame RootFrame;
public NavigationService(Frame rootFrame)
{
RootFrame = rootFrame;
}
public event NavigatingCancelEventHandler Navigating;
public void Navigate<T>(object parameter = null)
{
var type = typeof(T);
RootFrame.Navigate(type, parameter);
}
public void Navigate(string type, object parameter = null)
{
RootFrame.Navigate(Type.GetType(type), parameter);
}
public void GoBack()
{
if (RootFrame.CanGoBack)
{
RootFrame.GoBack();
}
}
public void GoForward()
{
if (RootFrame.CanGoForward)
{
RootFrame.GoForward();
}
}
}
I simply took out the constructor, and made the RootFrame private variable a property. Like so:
public class NavigationService : INavigationService
{
/// <summary>
/// Gets the root frame.
/// </summary>
private static Frame RootFrame
{
get { return Window.Current.Content as Frame; }
}
public event NavigatingCancelEventHandler Navigating;
public void Navigate<T>(object parameter = null)
{
var type = typeof(T);
RootFrame.Navigate(type, parameter);
}
public void Navigate(string type, object parameter = null)
{
RootFrame.Navigate(Type.GetType(type), parameter);
}
public void GoBack()
{
if (RootFrame.CanGoBack)
{
RootFrame.GoBack();
}
}
public void GoForward()
{
if (RootFrame.CanGoForward)
{
RootFrame.GoForward();
}
}
}
Simple, I know, but hope it's of some use.
I was getting the same error today in my Xamarin project. The actual error given was "System.Reflection.TargetInvocationException: 'Exception has been thrown by the target of an invocation.'" and then when I look up the InnerException I could see the actual error, which is Type not found in cache.
It was a silly mistake that I was using DataService instead of IDataService for the Constructor Dependency Injection.
public SearchViewModel(DataService dataService, IErrorLoggingService errorLoggingService, IDialogService dialogService, IResourceService resourceService, INavigationService navigationService) {
SearchCommand = new AsyncRelayCommand <SearchFilter>(SearchAsync);
DataService = dataService;
ErrorLoggingService = errorLoggingService;
DialogService = dialogService;
ResourceService = resourceService;
NavigationService = navigationService;
CancelCommand = new RelayCommand(Cancel);
}
And just for your information, this is how I registered my service.
SimpleIoc.Default.Register<IDataService, DataService>();
So the issue was fixed after changing to IDataService. Hope it helps.
First the code:
[ServiceContract]
public interface IWorker
{
[OperationContract]
void Process(XmlElement data);
[OperationContract]
void Update(Rule rule);
}
[DataContract]
public class Rule
{
[OperationContract]
public string Expression { get; set; }
[OperationContract]
public List<IAction> Actions { get; set; }
}
public interface IAction
{
void Execute(XmlElement data);
}
A dispatcher encodes data as xml and sends it to an IWorker instance where each expression is evaluated. When an IWorker instance evaluates an expression as true, IAction.Execute is called and the xml/data is passed.
What's the best way to serialize Rule.Actions? I've started writing a custom serializer but I'd prefer to see if there is an easier way.
Thanks.
I dont think you can use interfaces in DataContracts (someone correct me if im wrong, but i assume thats like trying to use a generic too). What I do, is have a parent class, then add the KnownType attribute. For instance
[DataContract]
public class Action
{
//members and properties
}
[DataContract]
public class SomeOtherAction:Action
{
//more implimentation
}
[DataContract]
[KnownType(typeof(SomeOtherAction))]
public class Rule
{
[DataMember]
List<Action> Actions{get;set;}
}
Now you can stuff any object that inherits from the parent Action object in to the Actions list, and it will properly serialize all their respective class properties (as long as the object is listed as a knowntype).
*I used "Action" name as an example to relate to yours, obviously Action is a keyword in .NET
Serialization is the process of converting between an object data and bytes which can be transferred over the wire. Interfaces define behavior, so by default WCF can't serialize such data. If you have the exact same assemblies on the client and the server, however, you can use the NetDataContractSerializer, which will essentially serialize (and be able to serialize) all the type information for the objects being serialized, so it can be recreated at the other side.
The code below shows how to use the NetDataContractSerializer in a service for that (based on the main example for this, the post from Aaron Skonnard at http://www.pluralsight-training.net/community/blogs/aaron/archive/2006/04/21/22284.aspx)
public class StackOverflow_6932356
{
[ServiceContract]
public interface IWorker
{
[OperationContract]
void Process(XmlElement data);
[OperationContract]
void Update(Rule rule);
}
[DataContract]
public class Rule
{
[DataMember]
public string Expression { get; set; }
[DataMember]
public List<IAction> Actions { get; set; }
}
public interface IAction
{
void Execute(XmlElement data);
}
public class Service : IWorker
{
static List<IAction> AllActions = new List<IAction>();
public void Process(XmlElement data)
{
foreach (var action in AllActions)
{
action.Execute(data);
}
}
public void Update(Rule rule)
{
AllActions = rule.Actions;
}
}
public class Action1 : IAction
{
public void Execute(XmlElement data)
{
Console.WriteLine("Executing {0} for data: {1}", this.GetType().Name, data.OuterXml);
}
}
public class Action2 : IAction
{
public void Execute(XmlElement data)
{
Console.WriteLine("Executing {0} for data: {1}", this.GetType().Name, data.OuterXml);
}
}
class NetDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
public NetDataContractSerializerOperationBehavior(OperationDescription operationDescription)
: base(operationDescription) { }
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer(name, ns);
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer(name, ns);
}
}
static void ReplaceDCSOB(ServiceEndpoint endpoint)
{
foreach (var operation in endpoint.Contract.Operations)
{
for (int i = 0; i < operation.Behaviors.Count; i++)
{
if (operation.Behaviors[i] is DataContractSerializerOperationBehavior)
{
operation.Behaviors[i] = new NetDataContractSerializerOperationBehavior(operation);
break;
}
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IWorker), new BasicHttpBinding(), "");
ReplaceDCSOB(endpoint);
host.Open();
Console.WriteLine("Host opened");
var factory = new ChannelFactory<IWorker>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
ReplaceDCSOB(factory.Endpoint);
var proxy = factory.CreateChannel();
proxy.Update(new Rule
{
Expression = "Expr",
Actions = new List<IAction> { new Action1(), new Action2() }
});
XmlDocument doc = new XmlDocument();
doc.LoadXml("<root><foo>bar</foo></root>");
proxy.Process(doc.DocumentElement);
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
I'm trying to get hold of an object from another application using WCF. With built in classes it works fine but I run into probems when trying to return a custom interface type from the WCF operation.
Whether I include the interface in both applications separately, or specif it as a shared assembly, I get the same result: a CommunicationException with the message "There was an error reading from the pipe: Unrecognized error 109".
The interface looks like this:
[ServiceContract]
public interface IBase {
int IntTest {
[OperationContract]
get;
}
String StringTest {
[OperationContract]
get;
}
IOther OtherTest {
[OperationContract]
get;
}
}
[ServiceContract]
public interface IOther {
String StringTest {
[OperationContract]
get;
}
}
My server looks like this:
public partial class MainWindow : Window {
private Base fb;
private ServiceHost host;
public MainWindow() {
InitializeComponent();
fb = new Base();
host = new ServiceHost(fb, new Uri[] { new Uri("net.pipe://localhost") });
host.AddServiceEndpoint(typeof(IBase), new NetNamedPipeBinding(),
"PipeReverse");
host.Open();
}
private void Window_Closing(object sender, CancelEventArgs e) {
host.Close();
}
}
And here is my implementation of the interface:
[Serializable]
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class Base : MarshalByRefObject, IBase {
public int IntTest {
get { return 4; }
}
public string StringTest {
get { return "A string from Base"; }
}
public IOther OtherTest {
get { return new Other(); }
}
}
[Serializable]
[DataContract]
public class Other : MarshalByRefObject, IOther {
[DataMember]
public string StringTest {
get { return "A string from Other"; }
}
}
The client looks like this:
public partial class Form1 : Form {
IBase obj;
public Form1() {
InitializeComponent();
ChannelFactory<IBase> pipeFactory = new ChannelFactory<IBase>(
new NetNamedPipeBinding(), new EndpointAddress(
"net.pipe://localhost/PipeReverse"));
obj = pipeFactory.CreateChannel();
}
private void button2_Click(object sender, EventArgs e) {
Console.WriteLine("Returns: " + obj.StringTest + " " +
obj.StringTest.Length);
Console.WriteLine("Returns: " + obj.IntTest);
Console.WriteLine(obj.OtherTest);
}
}
Everything works like a charm except this line:
Console.WriteLine(obj.OtherTest);
It give me a CommunicationException with the message "There was an error reading from the pipe: Unrecognized error 109". As far as I can tell that is a broken pipe due to a faulted state but I can't figure out why, or more importantly how to fix it. Any ideas?
I have no config file as everthing is done in the code above so I don't know how to turn on tracing, otherwise I would have included that too.
The returned property OtherTest needs to be a concrete type and not an interface, otherwise the serialization will not work.
This is typically a serialization error. Look to the [KnownType] attribute. The easiest way to test this out is to invoke the DataContractSerializer directly. You can use its WriteObject and ReadObject methods to get the true serialization errors. You can also inspect the stream(FileStream usually) to make sure you type serializes correctly.