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.
CodePudding user response:
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.