117 votes

Entity framework boucle de référencement autodétectée

J'ai une erreur étrange. Je suis en train d'expérimenter avec un Web API .NET 4.5, Entity Framework et MS SQL Server. J'ai déjà créé la base de données et configuré les clés primaires et étrangères correctes ainsi que les relations.

J'ai créé un modèle .edmx et importé deux tables : Employee et Department. Un département peut avoir plusieurs employés et cette relation existe. J'ai créé un nouveau contrôleur appelé EmployeeController en utilisant les options de génération de code pour créer un contrôleur API avec des actions de lecture/écriture en utilisant Entity Framework. Dans l'assistant, j'ai sélectionné Employee comme modèle et l'entité correcte pour le contexte de données.

La méthode qui est créée ressemble à ceci :

public IEnumerable GetEmployees()
{
    var employees = db.Employees.Include(e => e.Department);
    return employees.AsEnumerable();
}

Quand j'appelle mon API via /api/Employee, j'obtiens cette erreur :

Le type 'ObjectContent`1' n'a pas réussi à sérialiser le corps de réponse pour le type de contenu 'application/json; ...System.InvalidOperationException","StackTrace":null,"InnerException":{"Message":"Une erreur s'est produite.","ExceptionMessage":"Détection de boucle auto-référente avec le type 'System.Data.Entity.DynamicProxies.Employee_5D80AD978BC68A1D8BD675852F94E8B550F4CB150ADB8649E8998B7F95422552'. Chemin '[0].Department.Employees'.","ExceptionType":"Newtonsoft.Json.JsonSerializationException","StackTrace":" ...

Pourquoi y a-t-il une auto-référence [0].Department.Employees? Cela n'a pas beaucoup de sens. Je m'attendrais à ce que cela se produise si j'avais des références circulaires dans ma base de données mais c'est un exemple très simple. Qu'est-ce qui pourrait mal se passer?

8voto

Thomas B. Lze Points 1080

Le message d'erreur signifie que vous avez une boucle de référence en boucle.

Le json que vous produisez ressemble à cet exemple (avec une liste d'un employé) :

[
employee1 : {
    name: "name",
    department : {
        name: "departmentName",
        employees : [
            employee1 : {
                name: "name",
                department : {
                    name: "departmentName",
                    employees : [
                        employee1 : {
                            name: "name",
                            department : {
                                et encore et encore....
                            }
                    ]
                }
            }
        ]
    }
}

]

Vous devez indiquer au contexte de base de données que vous ne souhaitez pas obtenir toutes les entités liées lorsque vous demandez quelque chose. L'option pour DbContext est Configuration.LazyLoadingEnabled

La meilleure façon que j'ai trouvée est de créer un contexte pour la sérialisation :

public class SerializerContext : LabEntities 
{
    public SerializerContext()
    {
        this.Configuration.LazyLoadingEnabled = false;
    }
}

8voto

newstockie Points 96

Ajoutez une ligne Configuration.ProxyCreationEnabled = false; dans le constructeur de la définition de classe partielle de votre modèle de contexte.

    public partial class VotreNomDeModeleDbContext : DbContext
{
    public VotreNomDeModeleDbContext()
        : base("name=NomDeChaineDeConnexionVotreDbContext")
    {
        Configuration.ProxyCreationEnabled = false;//cette ligne doit être ajoutée
    }

    public virtual DbSet Employés{ get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
    }
}

6voto

sindrem Points 510

Je n'avais qu'un seul modèle que je voulais utiliser, donc j'ai fini par obtenir le code suivant :

var JsonImageModel = Newtonsoft.Json.JsonConvert.SerializeObject(Images, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });

6voto

Si vous essayez de modifier ce paramètre dans le modèle Blazor (Hébergé par ASP.NET Core), vous devez passer ce qui suit à l'appel AddNewtonsoftJson dans Startup.cs dans le projet Server :

services.AddMvc().AddNewtonsoftJson(options => 
    options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore
);

6voto

D. Dahlberg Points 116

Je viens de rencontrer le même problème sur mon site .net core. La réponse acceptée ne fonctionnait pas pour moi mais j'ai découvert qu'une combinaison de ReferenceLoopHandling.Ignore et PreserveReferencesHandling.Objects l'a corrigé.

//sérialiser l'élément
var serializedItem = JsonConvert.SerializeObject(data, Formatting.Indented, 
new JsonSerializerSettings
{
     PreserveReferencesHandling = PreserveReferencesHandling.Objects,
     ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X