Home > Enterprise >  Trying to return data from db back to controller and getting "Consider using a DataContractReso
Trying to return data from db back to controller and getting "Consider using a DataContractReso

Time:09-17

I'm instantiating an instance of DashboardActions.cs inside of my TabController.cs which is suppposed to return an IEnumerable<Tab> coming from Entity Framework. I've shown the code for the GET method of the TabController.cs and the GetTabs method of the DashboardActions.cs class that the controller is instantiating. Tab is an entity in my EF model. Here's the full error message:

<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace/>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>Type 'System.Data.Entity.DynamicProxies.Tab_3CD81772060539EC79CB677CA17899B90117059EFFC0976D087CA8B0FBE38520' with data contract name 'Tab_3CD81772060539EC79CB677CA17899B90117059EFFC0976D087CA8B0FBE38520:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver if you are using DataContractSerializer or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to the serializer.</ExceptionMessage>
<ExceptionType>System.Runtime.Serialization.SerializationException</ExceptionType>
<StackTrace> at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) at WriteArrayOfTabToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract ) at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__22.MoveNext()</StackTrace>
</InnerException>
</Error>

TabController.cs

public IEnumerable<Tab> GetTabs()
        {
            IEnumerable<Tab> recentPages = null;

            try
            {
                using (var context = new Clarity.BusinessLayer.CLARITY_DNN())
                {
                    recentPages = (from t in context.Tabs
                                   join tp in context.TabPermissions on t.TabID equals tp.TabID
                                   where tp.RoleID == -1 && tp.AllowAccess && tp.PermissionID == 3
                                  && !t.IsDeleted && t.IsVisible && t.PortalID == 0 &&
                                  !string.IsNullOrEmpty(t.Description)
                                   && t.TabName != "Home"
                                   select t)
                    .OrderByDescending(p => p.LastModifiedOnDate ?? p.CreatedOnDate)
                    .Take(20);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            return recentPages;
       }

TabController.cs

public class TabController : ApiController
    {
       
        // GET: Dashboard
        public IEnumerable<Tab> Get()
        {
            var recentTabsInstance = new DashboardActions();
            var recentTabs = recentTabsInstance.GetTabs();

            return recentTabs;
        }
    }

I tried altering the controller action with the KnownType decorator as such, to no avail:

public class TabController : ApiController
    {
        [KnownType(typeof(DashboardActions))]
        [DataContract]
        // GET: Dashboard
        public IEnumerable<Tab> Get()
        {
            var recentTabsInstance = new DashboardActions();
            var recentTabs = recentTabsInstance.GetTabs();

            return recentTabs;
        }
    }

It keeps saying that the KnownType can only be used on class,struct, etc. but DashboardActions is a class.. Any help is greatly appreciated.

CodePudding user response:

The last error means that KnownType attribute can only be applied to a class and in your example you apply it to the Get() method. But let's forget about that.

The underlying problem is how Entity Framework works and how this confuses the Data Contracts layer. The simple solution is not to return the Entity Framework objects directly, but to copy the values into a view model class and return that as a result. Because internally EF creates extra types to hold the values. Those look like the classes you have defined, but they are not the same. In this case EF created the type System.Data.Entity.DynamicProxies.Tab_3CD81772060539EC79CB677CA17899B90117059EFFC0976D087CA8B0FBE38520 to implement the Tab class. The DataContracts layer doesn't recognize this class. (Who can? Honestly.)

So my solution would be to create a TabModel class and copy the needed values inside the query at the line select t)

new class:

public class TabModel
{
    public int TabID { get;set; }
    public DateTime LastModifiedOnDate { get;set; }
    public DateTime CreatedOnDate{ get;set; }
    // Other properties you need.
}

Now change the query to return objects of this type instead of the EF objects themselves. End the query like this:

                                select new TabModel()
                                     {
                                         TabID = t.TabID, 
                                         LastModifiedOnDate = t.LastModifiedOnDate, 
                                         CreatedOnDate= t.CreatedOnDate, 
                                         // Other properties
                                     })
                .OrderByDescending(p => p.LastModifiedOnDate ?? p.CreatedOnDate)
                .Take(20);

And don't forget to change the signature of IEnumerable<Tab> GetTabs() to IEnumerable<TabModel> GetTabs()

Now the TabController is much cleaner because it doesn't expose EF objects to the outside world and the Data Constracts layer can deal with a nice and simple TabModel object. Also if the data model needs to change in future versions the web interface can remain the same. That gives a lot of flexibility when changing this code in the future.

CodePudding user response:

System.Data.Entity.DynamicProxies.Tab_3CD81772060539EC79CB677CA17899B90117059EFFC0976D087CA8B0FBE38520

That's a Lazy Loading Proxy wrapper for your entity. So turn off Lazy Loading for that controller. See:

EF Core

EF6

  • Related