0

I'm building a .NET Web API using Entity Framework 6 with AutoMapper to map entities to DTOs. When I try to introduce an TPH (table per hierarchy) with an abstract base class, I get a very long and very useless (to me at least) error. The error is below, I'm not sure what the actual error is. I've tried Googling various sections of it to no avail.

This is my mapping:

        Mapper.CreateMap<ParentClass, ParentClassDTO>()
            .Include<ClassA, ClassADTO>()
            .Include<ClassB, ClassBDTO>();

        Mapper.CreateMap<ClassA, ClassADTO>();
        Mapper.CreateMap<ClassB, ClassBDTO>();

If I remove abstract from the ParentClass, the error goes away but then AutoMapper does not use the child DTO classes, it only uses the ParentClassDTO. If I remove the mapping all together and just return the entities through the API, there are no errors and everything works as expected.

I don't even know where to start with the error below, how to start debugging it (web API running in debug mode still just outputs this error to screen), or what is responsible for the error. I've simplified my code as much as possible and tried other various things, but can't seem to get around this issue.

This specific mapping is a collection within another DTO class, which I am creating to return a IQueryable<AnotherClassDTO> from a Web API:

return db.AnotherClass.Project().To<AnotherClassDTO>();

The AnotherClassDTO (a separate class which contains the abstract collection) is setup like this:

public class AnotherClassDTO
{
    public IEnumerable<ParentClassDTO> Elements;
}

Error Message:

> {"message":"An error has occurred.","exceptionMessage":"The
> 'ObjectContent`1' type failed to serialize the response body for
> content type 'text/html;
> charset=utf-8'.","exceptionType":"System.InvalidOperationException","stackTrace":null,"innerException":{"message":"An
> error has occurred.","exceptionMessage":"Exception has been thrown by
> the target of an
> invocation.","exceptionType":"System.Reflection.TargetInvocationException","stackTrace":"
> at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[]
> arguments, Signature sig, Boolean constructor)\r\n at
> System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj,
> Object[] parameters, Object[] arguments)\r\n at
> System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags
> invokeAttr, Binder binder, Object[] parameters, CultureInfo
> culture)\r\n at System.Reflection.MethodBase.Invoke(Object obj,
> Object[] parameters)\r\n at
> System.Data.Entity.Core.Common.Internal.Materialization.Translator.TranslateColumnMap(Translator
> translator, Type elementType, ColumnMap columnMap, MetadataWorkspace
> workspace, SpanIndex spanIndex, MergeOption mergeOption, Boolean
> streaming, Boolean valueLayer)\r\n at
> System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.Prepare(ObjectContext
> context, DbQueryCommandTree tree, Type elementType, MergeOption
> mergeOption, Boolean streaming, Span span, IEnumerable`1
> compiledQueryParameters, AliasGenerator aliasGenerator)\r\n at
> System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1
> forMergeOption)\r\n at
> System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.b__6()\r\n
> at
> System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1
> func, IDbExecutionStrategy executionStrategy, Boolean
> startLocalTransaction, Boolean releaseConnectionOnSuccess)\r\n at
> System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.b__5()\r\n
> at
> System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1
> operation)\r\n at
> System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1
> forMergeOption)\r\n at
> System.Data.Entity.Core.Objects.ObjectQuery`1..GetEnumerator>b__0()\r\n
> at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()\r\n at
> Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter
> writer, IEnumerable values, JsonArrayContract contract, JsonProperty
> member, JsonContainerContract collectionContract, JsonProperty
> containerProperty)\r\n at
> Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter
> writer, Object value, JsonContract valueContract, JsonProperty member,
> JsonContainerContract containerContract, JsonProperty
> containerProperty)\r\n at
> Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter
> jsonWriter, Object value, Type objectType)\r\n at
> Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter
> jsonWriter, Object value, Type objectType)\r\n at
> Newtonsoft.Json.JsonSerializer.Serialize(JsonWriter jsonWriter, Object
> value)\r\n at
> System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type
> type, Object value, Stream writeStream, Encoding
> effectiveEncoding)\r\n at
> System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type
> type, Object value, Stream writeStream, Encoding
> effectiveEncoding)\r\n at
> System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type
> type, Object value, Stream writeStream, HttpContent content)\r\n at
> System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type
> type, Object value, Stream writeStream, HttpContent content,
> TransportContext transportContext, CancellationToken
> cancellationToken)\r\n--- End of stack trace from previous location
> where exception was thrown ---\r\n at
> System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
> task)\r\n at
> System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
> task)\r\n at
> System.Runtime.CompilerServices.TaskAwaiter.GetResult()\r\n at
> System.Web.Http.WebHost.HttpControllerHandler.d__1b.MoveNext()","innerException":{"message":"An
> error has occurred.","exceptionMessage":"Exception has been thrown by
> the target of an
> invocation.","exceptionType":"System.Reflection.TargetInvocationException","stackTrace":"
> at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[]
> arguments, Signature sig, Boolean constructor)\r\n at
> System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags
> invokeAttr, Binder binder, Object[] parameters, CultureInfo
> culture)\r\n at System.RuntimeType.CreateInstanceImpl(BindingFlags
> bindingAttr, Binder binder, Object[] args, CultureInfo culture,
> Object[] activationAttributes, StackCrawlMark& stackMark)\r\n at
> System.Activator.CreateInstance(Type type, BindingFlags bindingAttr,
> Binder binder, Object[] args, CultureInfo culture, Object[]
> activationAttributes)\r\n at System.Activator.CreateInstance(Type
> type, Object[] args)\r\n at
> System.Data.Entity.Core.Common.Internal.Materialization.CoordinatorScratchpad.Compile()\r\n
> at
> System.Data.Entity.Core.Common.Internal.Materialization.CoordinatorScratchpad.Compile()\r\n
> at
> System.Data.Entity.Core.Common.Internal.Materialization.CoordinatorScratchpad.Compile()\r\n
> at
> System.Data.Entity.Core.Common.Internal.Materialization.Translator.TranslateColumnMap[T](ColumnMap
> columnMap, MetadataWorkspace workspace, SpanIndex spanIndex,
> MergeOption mergeOption, Boolean streaming, Boolean
> valueLayer)","innerException":{"message":"An error has
> occurred.","exceptionMessage":"Instances of abstract classes cannot be
> created.","exceptionType":"System.InvalidOperationException","stackTrace":"
> at
> System.Runtime.CompilerServices.RuntimeHelpers._CompileMethod(IRuntimeMethodInfo
> method)\r\n at
> System.Reflection.Emit.DynamicMethod.CreateDelegate(Type delegateType,
> Object target)\r\n at
> System.Linq.Expressions.Compiler.LambdaCompiler.CreateDelegate()\r\n
> at
> System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression
> lambda, DebugInfoGenerator debugInfoGenerator)\r\n at
> System.Linq.Expressions.Expression`1.Compile()\r\n at
> System.Data.Entity.Core.Common.Internal.Materialization.CoordinatorFactory`1..ctor(Int32
> depth, Int32 stateSlot, Expression`1 hasData, Expression`1 setKeys,
> Expression`1 checkKeys, CoordinatorFactory[] nestedCoordinators,
> Expression`1 element, Expression`1 wrappedElement, Expression`1
> elementWithErrorHandling, Expression`1 initializeCollection,
> RecordStateFactory[] recordStateFactories)\r\n at
> System.Data.Entity.Core.Common.Internal.Materialization.CoordinatorFactory`1..ctor(Int32
> depth, Int32 stateSlot, Expression hasData, Expression setKeys,
> Expression checkKeys, CoordinatorFactory[] nestedCoordinators,
> Expression element, Expression elementWithErrorHandling, Expression
> initializeCollection, RecordStateFactory[] recordStateFactories)"}}}}
Ryan
  • 17,511
  • 23
  • 63
  • 88
  • Assuming you use a recent version of automapper, your code seems correct. The error message specifies clearly that the "abstract" part is the problem. Now, could you show where and how you use that mapping ? – Raphaël Althaus Apr 23 '15 at 07:01
  • I have updated the question with how I'm using the mapping. This mapping is a collection within another DTO which is being used in a Web API. Let me know if you need anything else, thanks! – Ryan Apr 23 '15 at 07:07
  • Oh, then you're using queryable extensions also... See maybe this question, it looks like a limitation with linq : http://stackoverflow.com/questions/29787197/iqueryable-extension-behavior-differing-for-automapper-polymorphic-collection – Raphaël Althaus Apr 23 '15 at 07:29
  • Ah, ok... so I can't use async controllers and use AutoMapper in this combo? Do I need to swap out to using regular Web API controllers? (Sorry for so many questions, it's been 5+ years since I developed in .NET. So much new stuff to learn!) Thanks again for your help! – Ryan Apr 23 '15 at 07:38
  • I updated the action to just return IEnumerable instead of IQueryable, and mapped it directly without .Project().To. Works like a charm now! Thanks for all your help... if you want to put that as an answer I will mark as answer (when I wakeup in the AM... going to sleep now!) – Ryan Apr 23 '15 at 07:52

1 Answers1

1

2 problems

  1. The error message clearly states that you can't map an abstract class
  2. You're working with Queryable extensions Project().To<T>() which seems to suffer, in this case, from limitations due to linq provider, as mentionned here

=> make your base class concrete

=> map from an IEnumerable<T> instead of an IQueryable<T>, without using Queryable extensions.

Community
  • 1
  • 1
Raphaël Althaus
  • 59,727
  • 6
  • 96
  • 122