1

Hi community I recenty began learning how to code a web api,

I had a project in winform that I wanted to remake using web api, the thing is I can't use eager loading, I always get error 500, the code is like this:

public HttpResponseMessage GetExpediente()
    {
        db.Configuration.ProxyCreationEnabled = false;
        var expediente = db.Expediente.Include(x=>x.Documento);
        if (expediente.Any())
        {
            return Request.CreateResponse(HttpStatusCode.OK, expediente);
        }
        else
        {
            return Request.CreateErrorResponse(HttpStatusCode.NotFound,
                "No se encontraron expedientes.");
        }
    }

the error I always get is:

{"Message":"An error has occurred.","ExceptionMessage":"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.","ExceptionType":"System.InvalidOperationException","StackTrace":null,"InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Self referencing loop detected for property 'Expediente' with type 'Modelo.Expediente'. Path '[0].Documento[0]'.","ExceptionType":"Newtonsoft.Json.JsonSerializationException","StackTrace":"   en Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CheckForCircularReference(JsonWriter writer, Object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n   en Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)\r\n   en Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n   en Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n   en Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n   en Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n   en Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n   en Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n   en Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n   en Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n   en Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)\r\n   en Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)\r\n   en System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n   en System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n   en System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content)\r\n   en System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- Fin del seguimiento de la pila de la ubicación anterior donde se produjo la excepción ---\r\n   en System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   en System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   en System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()"}}

I've been looking for info. that could help me but I always find that I should disable lazyloading, I did, and it didn't work, can anybody here help me?

Btw my objective is to consume this api from a windows form project, someone told me to do this:

var emp1 = response.Content.ReadAsStringAsync().Result;
        var result = JsonConvert.DeserializeObject<IEnumerable<ExpedientePOCO>>(emp1);

but what I get from the line of var emp1 = ... is an error 500. Oh and when I take the include from the context, it works just fine, but I want to use Eager loading hope you can help!

Thanks! Regards!

What I understood of DTOs:

 public HttpResponseMessage GetExpediente()
    {
        db.Configuration.ProxyCreationEnabled = false;
        var expediente = db.Expediente.Include(x=>x.Documento);
        List<ExpedientePOCO> expPOCO = new List<ExpedientePOCO>();
        foreach (var entidad in expediente)
        {
            String fecha = String.Format("{0:dd-MM-yyy}", entidad.FechaCreacion);
            ExpedientePOCO expedientePOCO = new ExpedientePOCO()
            {
                idExpediente = entidad.idExpediente,
                NombreExpediente = entidad.NombreExpediente,
                FechaCreacion = Convert.ToDateTime(fecha),
                DuenioExpediente = entidad.DuenioExpediente,
                CantidadDocumento = entidad.CantidadDocumento
            };
            foreach (var documentos in entidad.Documento)
            {
                DocumentoPOCO documento = new DocumentoPOCO()
                {
                    idDocumento = documentos.idDocumento,
                    idExpediente = documentos.idExpediente,
                    NombreDocumento = documentos.NombreDocumento,
                    FechaCreacion = documentos.FechaCreacion,
                    Expedientes = expedientePOCO
                };
                expedientePOCO.Documentos.Add(documento);
            }
            expPOCO.Add(expedientePOCO);
        }
        if (expPOCO.Any())
        {
            return Request.CreateResponse(HttpStatusCode.OK, expPOCO);
        }
        else
        {
            return Request.CreateErrorResponse(HttpStatusCode.NotFound,
                "No se encontraron expedientes.");
        }
    }

1 Answer 1

2

The problem is, as the error message states, that you have an endless loop, most likely something like this:

public class A
{
    public B B { get; set; }
}

public class B
{
    public virtual ICollection<A> A { get; set; }
}

So you get A => B => A endlessly, hence the error. You can solve this by configuring the serializer (ReferenceLoopHandling.Ignore in NewtonSoft.Json), or by using attributes ([JsonIgnore]), depending on your specific needs and tools used.

Another solution is to use Data Transfer Objects (DTOs) or similar:

public class ADTO
{
    // needed properties only
    public BDTO B { get; set; }
}

public class BDTO
{
    // needed properties only
    public List<ADTO> A { get; set; }
}

And then select into DTOs instead of entities:

var data = db.As
    .Select(a => new ADTO 
    {
        x = a.x ....
        B = new BDTO { x = a.B.x ...  }
    }
    .ToList();

Or, the other way around:

var data = context.B
    .Select(b => new BDTO
    {
        x = b.x ...
        A = b.A
            .Select(a => new ADTO
            {
                x = a.x ...
            }
            .ToList()
    }
    .ToList();
Sign up to request clarification or add additional context in comments.

8 Comments

I don't really understand what DTOs are, I mean, from what I've read, it is a class that works inbetween the client and server, so I edited my question with what I think it would be a DTO
@AlexVarela Your understanding is correct, the posted code not quite. You use DTOs best by projecting the query onto them (i.e db.Expediente.Select(e => new ExpedientePOCO ....)) so instead of doing that in-memory, you leave the work to the SQL server
I tried this, var expediente = db.Expediente.Include(x=>x.Documento).Select(e => new ExpedientePOCO()); but it doesn't bring "Documento"
I checked it, but now I'm having the problem when I get to the part of Documentos = new DocumentoPOCO { } since, I cannot convert from an entity to a dto... jejejeje
Forget my last comment, it was my bad, but now I'm getting all the Documento data in blank this is my code now: var expediente = db.Expediente.Select(e => new ExpedientePOCO { idExpediente = e.idExpediente, NombreExpediente = e.NombreExpediente, CantidadDocumento = e.CantidadDocumento, DuenioExpediente = e.DuenioExpediente, FechaCreacion = e.FechaCreacion, Documentos = e.Documento.Select(x => new DocumentoPOCO()) });
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.