简体   繁体   中英

Circular reference when serializing an object: Cannot set culprit properties to null

I have a circular reference exception when serializing a c# model object (I'm using Entity Framework, and the object is the model passed to the view that comes from a LINQ query).

(It does not matter whether I use System.Web.Script.Serialization.JavaScriptSerializer() or Newtonsoft.Json.JsonConvert.SerializeObject(Model)).

I found which properties are causing the circular reference and come to the conclusion that setting them to null (as I don't need them in the view) could be a solution, but for any reason I cannot set them to null successfully.

@section scripts{
    @Html.Partial("_GoogleMap", "")
    @Scripts.Render("~/bundles/google_maps_plugins")
    var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
    <script type="text/javascript" defer>
        $(function () {
            my_app.finca.InicializarFormulario(@Html.Raw(serializer.Serialize(Model)));
        });
    </script>
}

...

var q =
    from
        finca in db.finca
    join consecion in db.concesion
        on finca.concesion_id equals consecion.concesion_id
    join oficina in db.oficina
        on finca.oficina_mas_cercana_id equals oficina.oficina_id
    join planificacion in db.planificacion
        on finca.planificacion_defecto_id equals planificacion.planificacion_id
    join ruta in db.ruta
        on finca.ruta_defecto_id equals ruta.ruta_id
    join acometida in db.acometida
        on finca.acometida_defecto_id equals acometida.acometida_id
    where
        finca.finca_id == finca_id
    select new FincaViewModel
    {
        descripcion = finca.descripcion,
        finca_id = finca.finca_id,
        oficina_mas_cercana_id = finca.oficina_mas_cercana_id,
        oficina = finca.oficina.nombre,
        planificacion_id = finca.planificacion_defecto_id,
        planificacion = finca.planificacion.nombre_planificacion,
        ruta_id = finca.ruta_defecto_id,
        ruta = ruta.descripcion,
        acometida_id = finca.acometida_defecto_id,
        acometida = acometida.nombre,
        direccion_id = finca.direccion_id,
        codigo_gis = finca.codigo_gis,
        concesion_id = finca.concesion_id,
        concesion = finca.concesion.nombre_concesion,
        disponible_contratacion_bit = finca.disponible_contratacion,
        disponible_contratacion = finca.disponible_contratacion ? General.Si : General.No,
        numero_alternativo = finca.numero_alternativo,
        fecha_ultima_inspeccion = finca.fecha_ultima_inspeccion,
        latitud = finca.latitud,
        longitud = finca.longitud,
        ps = finca.ps,
        orden_trabajo = finca.orden_trabajo
    };

var f = await q.FirstOrDefaultAsync();
f.ps = RemoveCircularReference(f.ps);

...

private ICollection<ps> RemoveCircularReference(ICollection<ps> ps) {
    ps.ToList().ForEach(p => p.contrato.ToList().ForEach(c => 
    {
        c.cliente.tipo_cliente.cliente = null;
        c.cliente.cliente_historial = null;
    }));

    return ps;
}

The fact is that after RemoveCircularReference the properties are still not null, which is nonsense. Weirdly, if I set a breakpoint after the method and launch it again the properties are indeed set to null, but not the first time:s

Edit 1: Adding "finca" class.

public class FincaViewModel
{
    public int finca_id { get; set; }
    public string descripcion { get; set; }
    public DateTime fecha_creacion { get; set; }
    public string disponible_contratacion { get; set; }
    public bool disponible_contratacion_bit { get; set; }
    public int concesion_id { get; set; }
    public string concesion { get; set; }
    public string codigo_gis { get; set; }
    public string numero_alternativo { get; set; }
    public DateTime? fecha_ultima_inspeccion { get; set; }
    public decimal? latitud { get; set; }
    public decimal? longitud { get; set; }
    public int oficina_mas_cercana_id { get; set; }
    public int direccion_id { get; set; }
    public string direccion { get; set; }
    public short ambito_id { get; set; }
    public string ambito { get; set; }
    public ICollection<ps> ps { get; set; }
    public ICollection<PSFincaViewModel> psvm { get; set; }
    public int pscount { get; set; }
    public int ctcount { get; set; }
    public string oficina { get; set; }
    public int planificacion_id { get; set; }
    public int planificacion_defecto_id { get; set; }
    public string planificacion { get; set; }
    public int ruta_id { get; set; }
    public int ruta_defecto_id { get; set; }
    public string ruta { get; set; }
    public ICollection<orden_trabajo> orden_trabajo { get; set; }
    public ICollection<FichaOtViewModel> orden_trabajo_vm { get; set; }
    public string orden_trabajo_tipo { get; set; }
    public int acometida_id { get; set; }
    public int acometida_defecto_id { get; set; }
    public string acometida { get; set; }
    public ICollection<AcometidaViewModel> acometidas { get; set; }
}

Edit 2: Lazy loading.

I don't know if it may have something to do but I have:

db.Configuration.LazyLoadingEnabled = false;

I've tried to set it to true just to test, but in that case f.ps.contrato is not fetch.

Edit 3: Lazy loading (part 2).

I'm quite sure the issue has to do with lazy, but I don't know yet how to limit recursion in serializer.

Edit 4: App ends unexpectedly when trying the link suggested by @GSerg.

The link seems to be useful, but when using: var settings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects }; my app just "gets crazy" and ends unexpectedly

Edit 5: New attempt with JsonConvert.

I've also tried with JsonConvert using

var settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; 

to no avail (the same result), the app ends unexpectedly, what makes me suspect I'm running into memory problems or whatever. Well, I'm stuck, and I'll have to change direction in finding a solution.

After many different unsuccessful attempts on avoiding/skipping the circular reference when serializing I decided to change the direction on finding a solution.

The problem regarding not being able to set null to the culprit properties was the lazy loading, but disabling it would prevent "contratos" in "ps" to be fetched.

What I ended doing is to disable lazy loading and loading the properties in question manually (instead of the Entity Framework auto asigning).

db.Configuration.LazyLoadingEnabled = false;

var q =
        from
            finca in db.finca
        join ...
        where ...
        select new FincaViewModel
        {
            ...
            ps = finca.ps,
            orden_trabajo = finca.orden_trabajo
        };

    var f = await q.FirstOrDefaultAsync();
    f.acometidas = acometidasFinca;
    f.psvm = ObtenerPS(f.ps);
    f.orden_trabajo_vm = await ObtenerOT(f.orden_trabajo);

    f.ps = ContratosPorPs(f.ps);

    return f;

And

public ICollection<ps> ContratosPorPs(ICollection<ps> ps)
{
    foreach (var p in ps)
    {
        p.contrato = new ContratoOperations().ContratosPorPs(p.ps_id);
    }

    return ps;
}

Now I can serialize with no circular reference exception.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM