wake-up-neo.com

Selbstreferenzierungsschleife erkannt - Daten von Web Api an den Browser zurückgeben

Ich verwende Entity Framework und habe ein Problem mit dem Abrufen von Eltern- und Kinddaten an den Browser. Hier sind meine Klassen:

 public class Question
 {
    public int QuestionId { get; set; }
    public string Title { get; set; }
    public virtual ICollection<Answer> Answers { get; set; }
}

public class Answer
{
    public int AnswerId { get; set; }
    public string Text { get; set; }
    public int QuestionId { get; set; }
    public virtual Question Question { get; set; }
}

Ich verwende den folgenden Code, um die Frage- und Antwortdaten zurückzugeben:

    public IList<Question> GetQuestions(int subTopicId, int questionStatusId)
    {
        var questions = _questionsRepository.GetAll()
            .Where(a => a.SubTopicId == subTopicId &&
                   (questionStatusId == 99 ||
                    a.QuestionStatusId == questionStatusId))
            .Include(a => a.Answers)
            .ToList();
        return questions; 
    }

Auf der C # -Seite scheint das zu funktionieren, jedoch stelle ich fest, dass die Antwortobjekte auf die Frage verweisen. Wenn ich die WebAPI verwende, um die Daten in den Browser zu laden, erhalte ich die folgende Meldung:

Der 'ObjectContent'1-Typ konnte den Antworttext für Inhaltstyp' application/json nicht serialisieren. Zeichensatz = utf-8 '.

Für die Eigenschaft 'question' mit dem Typ 'Models.Core.Question' wurde eine selbstverweisende Schleife erkannt.

Liegt das daran, dass die Frage Antworten hat und die Antworten einen Bezug auf die Frage haben? Alle Orte, die ich angeschaut habe, schlagen vor, einen Bezug zum Elternteil im Kind zu haben. Kann mir jemand einen Rat dazu geben.

76
user1943020

Ist das so, weil die Frage Antworten hat und die Antworten ein .__ haben. Verweis zurück zu Frage?

Ja. Es kann nicht serialisiert werden.

BEARBEITEN: Siehe Tallmaris 'Antwort und OttOs Kommentar, da dies einfacher ist und global festgelegt werden kann.

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Re‌​ferenceLoopHandling = ReferenceLoopHandling.Ignore;

Alte Antwort:

Projizieren Sie das EF-Objekt Question auf Ihr eigenes Zwischenobjekt oder DataTransferObject. Dieser Dto kann dann erfolgreich serialisiert werden.

public class QuestionDto
{
    public QuestionDto()
    {
        this.Answers = new List<Answer>();
    } 
    public int QuestionId { get; set; }
    ...
    ...
    public string Title { get; set; }
    public List<Answer> Answers { get; set; }
}

So etwas wie:

public IList<QuestionDto> GetQuestions(int subTopicId, int questionStatusId)
{
    var questions = _questionsRepository.GetAll()
        .Where(a => a.SubTopicId == subTopicId &&
               (questionStatusId == 99 ||
                a.QuestionStatusId == questionStatusId))
        .Include(a => a.Answers)
        .ToList();

    var dto = questions.Select(x => new QuestionDto { Title = x.Title ... } );

    return dto; 
}
67
Sam Leach

Sie können dies auch in Ihrer Application_Start() versuchen:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;

Es sollte Ihr Problem beheben, ohne viele Reifen durchzugehen.


EDIT: Verwenden Sie gemäß dem folgenden Kommentar von OttO Folgendes: ReferenceLoopHandling.Ignore.

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
53
Tallmaris

Wenn Sie OWIN verwenden, denken Sie daran, dass Sie keine weiteren GlobalSettings mehr benötigen! Sie müssen diese Einstellung in einem HttpConfiguration-Objekt ändern, das an die IAppBuilder-Funktion UseWebApi (oder die von Ihnen verwendete Serviceplattform) übergeben wird.

Würde so etwas aussehen.

    public void Configuration(IAppBuilder app)
    {      
       //auth config, service registration, etc      
       var config = new HttpConfiguration();
       config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
       //other config settings, dependency injection/resolver settings, etc
       app.UseWebApi(config);
}
21
Bon

In ASP.NET Core ist das Update wie folgt:

services
.AddMvc()
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
21
Mohsen Afshin

Bei Verwendung von DNX/MVC 6/ASP.NET vNext blah blah fehlt sogar HttpConfiguration. Sie müssen Formatierer konfigurieren, indem Sie die folgenden Codes in Ihrer Startup.cs-Datei verwenden.

public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().Configure<MvcOptions>(option => 
        {
            //Clear all existing output formatters
            option.OutputFormatters.Clear();
            var jsonOutputFormatter = new JsonOutputFormatter();
            //Set ReferenceLoopHandling
            jsonOutputFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            //Insert above jsonOutputFormatter as the first formatter, you can insert other formatters.
            option.OutputFormatters.Insert(0, jsonOutputFormatter);
        });
    }
5
Stewart Hou

ASP.NET Core-Web-API (.NET Core 2.0):

// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.Configure<MvcJsonOptions>(config =>
    {
        config.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    });
}
4

Mit diesem:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore

funktionierte nicht für mich Stattdessen habe ich eine neue, vereinfachte Version meiner Modellklasse erstellt, die nur zum Testen getestet wurde. Dieser Artikel behandelt einige der Probleme, die ich in meinem Modell hatte und die für EF gut funktionierten, aber nicht serialisierbar waren:

http://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-4

2
Mike

ReferenceLoopHandling.Ignore hat bei mir nicht funktioniert. Die einzige Möglichkeit, wie ich es umgehen konnte, bestand darin, die Verknüpfungen zu dem übergeordneten Element, das ich nicht wollte, per Code zu entfernen, und diejenigen zu behalten, die ich gemacht habe.

parent.Child.Parent = null;
1
Rob Sedgwick

Keine der Konfigurationen in den obigen Antworten funktionierte für mich in ASP.NET Core 2.2.

Ich habe die Attribute JsonIgnore zu meinen virtuellen Navigationseigenschaften hinzugefügt.

public class Question
{
    public int QuestionId { get; set; }
    public string Title { get; set; }
    [JsonIgnore]
    public virtual ICollection<Answer> Answers { get; set; }
}
0
chakeda

Aufgrund des langsamen Ladens erhalten Sie diesen Fehler. Daher ist mein Vorschlag, den virtuellen Schlüssel aus der Eigenschaft zu entfernen. Wenn Sie mit API arbeiten, ist das langsame Laden für Ihre API-Integrität nicht geeignet.

Sie müssen keine zusätzliche Zeile in Ihre Konfigurationsdatei einfügen.

public class Question
 {
    public int QuestionId { get; set; }
    public string Title { get; set; }
    public ICollection<Answer> Answers { get; set; }
}

public class Answer
{
    public int AnswerId { get; set; }
    public string Text { get; set; }
    public int QuestionId { get; set; }
    public Question Question { get; set; }
}
0

Entitäten db = neue Entitäten ()

db.Configuration.ProxyCreationEnabled = false;

db.Configuration.LazyLoadingEnabled = false;

0
Imran

Für eine neue Asp.Net-Webanwendung mit .Net Framework 4.5:

Web Api: Springen App_Start -> WebApiConfig.cs:

Sollte ungefähr so ​​aussehen:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

        // ReferenceLoopHandling.Ignore will solve the Self referencing loop detected error
        config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;

        //Will serve json as default instead of XML
        config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}
0
Ogglas

Ich habe festgestellt, dass dieser Fehler verursacht wurde, als ich eine edmx (XML-Datei, die ein konzeptionelles Modell definiert) einer vorhandenen Datenbank generiert hat und Navigationseigenschaften für die übergeordneten und die untergeordneten Tabellen hatten. Ich habe alle Navigationslinks zu den übergeordneten Objekten gelöscht, da ich nur zu Kindern navigieren wollte und das Problem behoben wurde.

0
Zymotik

Sie können eine neue untergeordnete Auflistung dynamisch erstellen, um dieses Problem leicht zu umgehen.

public IList<Question> GetQuestions(int subTopicId, int questionStatusId)
    {
        var questions = _questionsRepository.GetAll()
            .Where(a => a.SubTopicId == subTopicId &&
                   (questionStatusId == 99 ||
                    a.QuestionStatusId == questionStatusId))
            .Include(a => a.Answers).Select(b=> new { 
               b.QuestionId,
               b.Title
               Answers = b.Answers.Select(c=> new {
                   c.AnswerId,
                   c.Text,
                   c.QuestionId }))
            .ToList();
        return questions; 
    }
0
spadelives