Serialization Exception when adding a virtual object to a property

I have a class Report

in my web api project:

[KnownType(typeof(Employee))]
[DataContract]
public class Report
{
    [DataMember]
    public int Id { get; set; }

    public string ManagerId { get; set; }
    public string EmployeeId { get; set; }

    [DataMember]
    public virtual Employee Manager { get; set; }

    [DataMember]
    public virtual Employee Employee { get; set; }
}

      

If virtual

found in a method signature, I get the following exception:

Type 'System.Data.Entity.DynamicProxies.Report_1FFC700B8A805A61BF97A4B9A18D60F99AAA83EE08F4CA2E2454BADA9737B476' with data contract name 'Report_1FFC700B8A805A61BF97A4B9A18D60F99AAA83EE08F4CA2E2454BADA9737B476: http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies ' is expected. Consider using the DataContractResolver, or add any types that are not statically known to the list of known types — for example, using the KnownTypeAttribute or by adding them to the list of known types passed to the DataContractSerializer.

If that doesn't all work. What am I missing?


Edited to include object lookup code

ApiController

[ResponseType(typeof(IEnumerable<Report>))]
public IHttpActionResult GetLogs([FromUri] string id)
{
    return Ok(_loggingService.GetReportsByEmployeeId(id));
}

      

Service

public IQueryable<Report> GetReportsByEmployeeId(string employeeId)
{
    return _reports.GetAll().Where(x => x.ManagerId.Equals(employeeId) || x.EmployeeId.Equals(employeeId));
}

      

Repository

public IQueryable<T> GetAll()
{
    return _dbSet;
}

      

+3


source to share


1 answer


Presumably you are retrieving this object using an entity framework or similar ORM.

If you don't have virtual properties, ORM is safe to use an instance of your type. However, when you have virtual properties, the ORM must use its own type to support lazy loading, which is why you see the serializer complaining that it can't serialize the Type 'DynamicProxies'.

To avoid this, decouple your domain model from your serialization model and return an instance of your serialization model in your Select call.

Alternatively, disable lazy loading in the ORM (not recommended)

Edit: adding an example as discussed in the comments

As mentioned, ideally your domain model (the object representation of your data that you use to interact with the database) and your business model (the object representation of your data that you manipulate for business logic) should be separated. Also, nothing outside of the service should touch domain models directly, as this introduces unexpected lazy loading, dilution of responsibility across your codebase, or other questionable results.

This separation, by the way, will avoid the problems that you have. The example below, of course, feel free to change the namespaces and enter the names as you see fit.



Business model

namespace MyCompany.MyProject.BusinessModels
{
    [KnownType(typeof(Employee))]
    [DataContract]
    public class Report
    {
        [DataMember]
        public int Id { get; set; }

        [DataMember]
        public virtual Employee Manager { get; set; }

        [DataMember]
        public virtual Employee Employee { get; set; }
    }
}

      

Domain Model

namespace MyCompany.MyProject.DomainModels
{
    // this separation may seem like overkill, but when you come to
    // want different business models than your domain models, or to decorate
    // this model with Entity Framework specific attributes, you'll be glad
    // for the separation.
    public class Report
    {
        public int Id { get; set; }

        public virtual Employee Manager { get; set; }

        public virtual Employee Employee { get; set; }
    }
}

      

Service

public IQueryable<BusinessModels.Report> GetReportsByEmployeeId(string employeeId)
{
    return _reports // _reports is a collection of Type DomainModels.Report
            .GetAll()
            .Where(x => 
                 x.ManagerId.Equals(employeeId) 
                 || x.EmployeeId.Equals(employeeId))
            .Select(s =>
               new BusinessModels.Report
               {
                    Id = s.Id,
                    Employee = s.Employee,
                    Manager = s.Manager
               })
            .ToList(); 
            // We don't want database access happening outside of the service. 
            // ToList() executes the SQL *now* rather than waiting until 
            // the first time you enumerate the result.
}

      

+6


source







All Articles