MEF Composition error not propagating inner exception (except through message) - c#

I am working with MEF to load modules from different sources into my app. I have an example (code below) where I create a class that is composable which throws an exception in the constructor. That exception causes the composition to fail and thus throw an exception that says "why"... In the statement it says that the cause is an InvalidCastException... which is true.
If I print the exception, I know why it fails, but how do I retrieve the ACTUAL original exception (InvalidCastException) thrown, in order for me to handle it properly -- rather than just a generic catch, which would miss other exceptions -- in the module that is asking for an instance of the said class? Querying the CompositionException.Errors doesn't give me the original exception either...
CODE:
using System.ComponentModel.Composition;
using Microsoft.Practices.ServiceLocation;
[Export(typeof(A))]
Class A
{
[ImportingConstructor]
public A(ISomeinterface x, ISomeOtherInterface y)
{
//// Throw some exception (in my code it happens to be an InvalidCastException)
throw new InvalidCastException ("Unable to cast object of type 'Sometype' to type 'Someothertype'.");
}
}
Class B
{
public B(IServiceLocator serviceLocator)
{
try
{
//// Here is where the exception would be thrown as an "ActivationException"
A myAInstance = serviceLocator.GetInstance(A);
}
catch (ActivationException activationException)
{
//// Print original activation exception from trying to get the service
System.Diagnostics.Debug.WriteLine(activationException);
if (ex.InnerException is CompositionException)
{
CompositionException compositionException = (CompositionException)activationException.InnerException;
//// *** Here is where I want to retrieve the InvalidCastException in order to handle it properly
//// *** Also, componentException.InnerException is "null" and so is the componentException.Errors[0].Exception.InnerException
}
else
{
throw;
}
}
}
}
OUTPUT:
Microsoft.Practices.ServiceLocation.ActivationException: Activation error occured while trying to get instance of type A, key "" ---> System.ComponentModel.Composition.CompositionException: The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.
1) Unable to cast object of type 'Sometype' to type 'Someothertype'.
Resulting in: An exception occurred while trying to create an instance of type 'A'.
Resulting in: Cannot activate part 'A'.
Element: A --> A --> DirectoryCatalog (Path="C:\path\to\code\")
Resulting in: Cannot get export 'A (ContractName="A")' from part 'A'.
Element: A (ContractName="A") --> A --> DirectoryCatalog (Path="C:\path\to\code\")
at System.ComponentModel.Composition.Hosting.CompositionServices.GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition)
at System.ComponentModel.Composition.Hosting.CatalogExportProvider.GetExportedValue(CatalogPart part, ExportDefinition export, Boolean isSharedPart)
at System.ComponentModel.Composition.ExportServices.GetCastedExportedValue[T](Export export)
at System.ComponentModel.Composition.ExportServices.<>c__DisplayClass10`2.<CreateSemiStronglyTypedLazy>b__d()
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at Microsoft.Practices.Prism.MefExtensions.MefServiceLocatorAdapter.DoGetInstance(Type serviceType, String key) in c:\release\WorkingDir\PrismLibraryBuild\PrismLibrary\Desktop\Prism.MefExtensions\MefServiceLocatorAdapter.cs:line 73
at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance(Type serviceType, String key)
--- End of inner exception stack trace ---
at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance(Type serviceType, String key)
at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance[TService]()
at <code> line whatever...
System.ComponentModel.Composition.CompositionException: The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.

I found the solution the actual exception is nested deep in the compositionException.Errors hierarchy. One would think the original exception would be under the original composition exception but actually this is how to access it:
It happens to be the innerexception inside one of the errors of another composition exception which is itself one of the errors in the original composition exception
CompositionException compositionException = (CompositionException)activationException.InnerException;
CompositionException innerCompositionException = compositionException.Errors[0].Exception;
InvalidCastException castException = (InvalidCastException)innerCompositionException.Errors[0].Exception.InnerException
I won't delete this in case someone else runs into the same thing.

Related

How to throw a deserialized exception?

I'm serializing a Exception at the server with JsonConvert.SerializeObject and then encoding to a byte[] and deserializing in the client using JsonConvert.DeserializeObject. Everything works fine so far... The problem is when I throw the Exception the stacktrace being replaced, let me demostrate:
public void HandleException(RpcException exp)
{
// Get the exception byte[]
string exceptionString = exp.Trailer.GetBytes("exception-bin");
// Deserialize the exception
Exception exception = JsonConvert.DeserializeObject<Exception>(exceptionString, new
JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
// Log the Exception: The stacktrace is correct. Ex.: at ServerMethod()
Console.WriteLine(exception);
// Throw the same Exception: The stacktrace is changed. Ex.: at HandleException()
ExceptionDispatchInfo.Capture(exception).Throw();
}
If you deserialize an Exception and set JsonSerializerSettings.Context = new StreamingContext(StreamingContextStates.CrossAppDomain) then the deserialized stack trace string will get prepended to the displayed StackTrace even after the exception is thrown:
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
Context = new StreamingContext(StreamingContextStates.CrossAppDomain),
};
var exception = JsonConvert.DeserializeObject<Exception>(exceptionString, settings);
Notes:
This works because, in the streaming constructor for Exception, the deserialized stack trace string is saved into a _remoteStackTraceString which is later prepended to the regular stack trace:
if (context.State == StreamingContextStates.CrossAppDomain)
{
// ...this new exception may get thrown. It is logically a re-throw, but
// physically a brand-new exception. Since the stack trace is cleared
// on a new exception, the "_remoteStackTraceString" is provided to
// effectively import a stack trace from a "remote" exception. So,
// move the _stackTraceString into the _remoteStackTraceString. Note
// that if there is an existing _remoteStackTraceString, it will be
// preserved at the head of the new string, so everything works as
// expected.
// Even if this exception is NOT thrown, things will still work as expected
// because the StackTrace property returns the concatenation of the
// _remoteStackTraceString and the _stackTraceString.
_remoteStackTraceString = _remoteStackTraceString + _stackTraceString;
_stackTraceString = null;
}
While the serialization stream for Exception does contain the stack trace string, it does not attempt to capture the private Object _stackTrace which is used by the runtime to identify where in the executing assembly the exception was thrown. This would seem to be why ExceptionDispatchInfo is unable to copy and use this information when throwing the exception. Thus it seems to be impossible to throw a deserialized exception and restore its "real" stack trace from the serialization stream.
In order Json.NET to deserialize a type using its streaming constructor (and thus set the remote trace string as required), the type must be marked with [Serializable] and implement ISerializable. System.Exception meets both requirements, but some derived classes of Exception do not always add the [Serializable] attribute. If your specific serialized exception lacks the attribute, see Deserializing custom exceptions in Newtonsoft.Json.
Deserializing an exception with TypeNameHandling.All is insecure and may lead to injection of attack types when deserializing from untrusted sources. See: External json vulnerable because of Json.Net TypeNameHandling auto? whose answer specifically discusses deserialization of exceptions.
Demo fiddle here.
Just a small case that I want to point out: I'm calling this seralization/deserialization from two apps one is Blazor (.net 5) and another one is WinForms (.net framework 4.7). In the blazor one the method of the accepted answer did not work. What I do in this case is set te RemoteStackTrace via reflection.
// Convert string para exception
Exception exception = JsonConvert.DeserializeObject<Exception>(exceptionString, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
// Set RemoteStackTrace
exception.GetType().GetField("_remoteStackTraceString", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(exception, exception.StackTrace);
// Throw the Exception with original stacktrace
ExceptionDispatchInfo.Capture(exception).Throw();

C# Exception - make the "Source Error" the line where the method was called

In C#, if I call a canned method (like Regex.Replace) and it throws an exception, the error message marks the line where the method was called, like this:
Method Call:
//
//
string str = Regex.Replace("Hello", null, "");
//
//
However, if I write my own method (like MyRegex.MyReplace) and it throws an exception, the error message marks the line where the exception was thrown:
public static class MyRegex
{
public static string MyReplace(string input, string pattern, string replacement)
{
if (pattern == null)
{
throw new Exception("Pattern must not be null.");
}
else
{
return Regex.Replace(input, pattern, replacement);
}
}
}
Method Call:
//
//
string str = MyRegex.MyReplace("Hello", null, "");
//
//
How can I throw an Exception within a method, and have the error message mark where the method was called (like canned methods), instead of where the exception was thrown?
The line shown is the lowest point in the call stack where the source code is available.
You can't see any of the code inside Regex.Replace, so it only shows you the line in your code where you called it.
But because throw new Exception() is your code, that's what it shows.
If you built a DLL and used that in another project (and did not copy the .pdb file) then it would behave the same way as your Regex.Replace example, since the new project can't see the source code inside the DLL.
But if you copy over the .pdb file and run the new project on the same computer where the source code of the DLL is, then Visual Studio does know where to find the source code, and it will still show you the exception in your DLL code. (at least, I know Visual Studio will behave that way - I'm not sure if ASP.NET would display that to the browser)

"Attempted to access an element as a type incompatible with the array" thrown during creating a custom EmailService in Postal

I'm trying to configure Postal in class library project. To do this I need to configure custom EmailService. On Postal webpage there's a snippet for doing this:
// Get the path to the directory containing views
var viewsPath = Path.GetFullPath(#"..\..\Views");
var engines = new ViewEngineCollection();
engines.Add(new FileSystemRazorViewEngine(viewsPath));
var service = new EmailService(engines);
(See here: http://aboutcode.net/postal/outside-aspnet.html)
Surprisingly, following error is thrown:
System.ArrayTypeMismatchException occurred HResult=-2146233085
Message=Attempted to access an element as a type incompatible with the
array. Source=mscorlib StackTrace:
at System.Collections.Generic.List`1.Insert(Int32 index, T item) InnerException:
To be precise - it's thrown during adding FileSystemRazorViewEngine to ViewEngineCollection. FileSystemRazorViewEngine derives from IViewEngine and ViewEngineCollection derives from Collection<IViewEngine>.
And here's stacktrace:
mscorlib.dll!System.Collections.Generic.List.Insert(int
index, System.__Canon item) + 0x3e bytes
System.Web.Mvc.dll!System.Web.Mvc.ViewEngineCollection.InsertItem(int
index, System.Web.Mvc.IViewEngine item) + 0x89 bytes
I can't figure out what causes the exception.
I am getting same error. I suspect that Postal is built with slightly different version of System.Web.Mvc that the main project that causing ViewEngine incompatibility... But I only have production binaries of the postal...

Object reference not set to an instance of an object. with a non null object.

Ok I'm having this problem that is driving me crazy, I'm doing a search for a website, and it works great on my machine but when I upload it to gearhost it just blow with this error :
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not
set to an instance of an object.
Source Error:
Line 43: #if (Model != null)
Line 44: {
Line 45:
if (Model.Any())
Line 46: {
The error appear on Line 45, even though I'm sure I know its not null and its and IEnumerable.
My Model in razor is declared as follow:
#model IEnumerable<Posada>
And this does work if I send an empty search, but not when I use a string that returns rows or other that does not return any.
In my controller I just return a IEnumerable and use it as the model.
IEnumerable<Posada> posadas = unitOfWork.PosadaRepository.Get(includeProperties: "Estado,Lugar")
.Where(p => p.Nombre.ToLowerInvariant().Contains(query) ||
p.Estado.Nombre.ToLowerInvariant().Contains(query) ||
p.Lugar.Nombre.ToLowerInvariant().Contains(query))
.OrderBy(p => p.Id)
.Skip((page - 1)*pagingInfo.ItemsPerPage)
.Take(pagingInfo.ItemsPerPage);
Seems like NullReferenceException happens when you enumerate IEnumerable (when you execute Any()). IEnumerable is not null, and then most likely one gets NullReferenceException:
p.Nombre.ToLowerInvariant()
p.Estado.Nombre.ToLowerInvariant()
p.Lugar.Nombre.ToLowerInvariant()
pagingInfo.ItemsPerPage
Remember IEnumberable<> Only evaluate the query when it need to, so as stated by the other answer, when you use Any() your query gets evaluated and in that case Either Estado or Lugar can be null. Try something like:
(p.Estado!=null && p.Estado.Nombre.ToLowerInvariant().Contains(query) )||
(p.Lugar!=null && p.Lugar.Nombre.ToLowerInvariant().Contains(query)))

Exception.Data info is missing in EntLib log

How do I get the Exception Handling Application Block (EHAB) to write the values from the Exception.Data property in the log?
try
{
// ...
}
catch (Exception ex)
{
ex.Data.Add("Hello", "World");
throw ex;
}
The exception is logged correctly, but I can’t find the added data anywhere in the log entry created by EHAB.
As far as I understand, it’s a recommended practice to add additional relevant information to the exception itself like in the above example. That’s why I’m a little surprised that EHAB doesn’t include this by default.
Can I fix this by modifying the template with the EntLib Text Formatter Template Editor (screen shot below)? I can't find any info on the various "tokens" provided, but I assume the answer is hidden somewhere with them.
Text Formatter Template Editor http://img195.imageshack.us/img195/6614/capturegmg.png
Or do I really need to implement my own custom Text Formatter to accomplish this?
EDIT/UPDATE:
I do this in my Global.asax.cs in order to avoid having to add the HandleException method call everywhere in my code:
using EntLib = Microsoft.Practices.EnterpriseLibrary;
using System;
namespace MyApp
{
public class Global : System.Web.HttpApplication
{
protected void Application_Error(object sender, EventArgs e)
{
// I have an "All Exceptions" policy in place...
EntLib.ExceptionHandling.ExceptionPolicy.HandleException(Server.GetLastError(), "All Exceptions");
// I believe it's the GetLastError that's somehow returning a "lessor" exception
}
}
}
It turns out this is not the same as this (which works fine, and essentially solves my problem):
try
{
// ...
}
catch (Exception ex)
{
ex.Data.Add("Hello", "World");
bool rethrow = ExceptionPolicy.HandleException(ex, "Log Only Policy");
throw ex;
}
Going through all the code and adding try-catch's with a HandleException call just seems ... well, stupid.
I guess my problem really is how to use EHAB correctly, not configuration.
Any suggestion on how I can properly log all exceptions on a "global level" in an ASP.NET web application??
You shouldn't have to create your own formatter.
Each item in the Data IDictionary on the Exception object is added to the ExtendedProperties Dictionary of the LogEntry object and is logged (if specified by the formatter).
The Extended Properties:{dictionary({key} - {value})} config snippet should log all key/value pairs in the extended properties dictionary. If you want to log just one item from the collection you can use "keyvalue". In your example it would be something along the lines of:
Hello Key: {keyvalue(Hello)}
I modified the ExceptionHandlingWithLoggingQuickStart to add Data.Add("Hello", "World") and I see "Extended Properties: Hello - World" at the end of the Event Log entry. So it is working.
If you aren't seeing that behavior, you need to ensure:
That your exception with the Data items added is passed in to ExceptionPolicy.HandleException
That the policy specified in the HandleException method is configured for logging with a LoggingExceptionHandler in the configuration
That the formatter that is specified in the LoggingConfiguration is configured to support ExtendedProperties
If you can't see what the problem is try comparing what you have with what is in the QuickStart. If it's still not working, post your code and your config.
UPDATE:
If you are going to handle Application_Error in the global.asax then you will be receiving an HttpUnhandledException since the page did not handle the error. You can retrieve the Exception you are interested in by using the InnerException Property; the InnerException will contain the original Exception (unless it is in that exception's InnerException :) ). Alternately, you can use Server.GetLastError().GetBaseException() to retrieve the exception which is the root cause.

Categories

Resources