Обнаружено, что

У меня странная ошибка. Я экспериментирую с веб-API.NET 4.5, Entity Framework и MS SQL Server. Я уже создал базу данных и установил правильные первичные и внешние ключи и отношения.

Я создал модель .edmx и импортировал две таблицы: Employee and Department. У отдела может быть много сотрудников, и это соотношение существует. Я создал новый контроллер под названием EmployeeController, используя параметры леса для создания контроллера API с помощью операций чтения/записи с использованием Entity Framework. В мастере выберите Employee как модель и правильный объект для контекста данных.

Созданный метод выглядит следующим образом:

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

Когда я вызываю свой API через /api/Employee, я получаю эту ошибку:

Тип ObjectContent`1 не смог сериализовать тело ответа для типа контента 'application/json;... System.InvalidOperationException "," StackTrace ": null," InnerException ": {" Сообщение ":" Произошла ошибка. "," ExceptionMessage ":" Исключительный цикл привязки с типом "System.Data.Entity.DynamicProxies.Employee_5D80AD978BC68A1D8BD675852F94E8B550F4CB150ADB8649E8998B7F95422552. Path '[0].Department.Employees'. "," ExceptionType ":" Newtonsoft.Json.JsonSerializationException "," StackTrace ":"...

Почему он сам ссылается на [0].Department.Employees? Это не имеет большого смысла. Я ожидал бы, что это произойдет, если в моей базе данных была круговая ссылка, но это очень простой пример. Что может быть не так?

Ответ 1

Ну, правильный ответ для стандартного Json formater на основе Json.net - установить ReferenceLoopHandling на Ignore.

Просто добавьте это в Application_Start в Global.asax:

HttpConfiguration config = GlobalConfiguration.Configuration;

config.Formatters.JsonFormatter
            .SerializerSettings
            .ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;

Это правильный путь. Он будет игнорировать ссылку, обратную к объекту.

Другие ответы, сфокусированные на изменении возвращаемого списка, исключая данные или создавая объект фасада, а иногда это не вариант.

Использование атрибута JsonIgnore для ограничения ссылок может занять много времени и если вы хотите сериализовать дерево, начиная с другой точки, которая будет проблемой.

Ответ 2

Это происходит потому, что вы пытаетесь напрямую сериализовать коллекцию объектов EF. Поскольку отдел имеет ассоциацию с сотрудником и сотрудником отдела, сериализатор JSON будет контур бесконечно читать d.Employee.Departments.Employee.Departments и т.д.

Чтобы исправить это право до сериализации, создайте анонимный тип с помощью реквизита, который вы хотите

Пример (psuedo):

departments.select(dep => new { 
    dep.Id, 
    Employee = new { 
        dep.Employee.Id, dep.Employee.Name 
    }
});

Ответ 3

У меня была такая же проблема, и я обнаружил, что вы можете просто применить атрибут [JsonIgnore] к свойству навигации, которое вы не хотите сериализовать. Он по-прежнему будет сериализовать как родительский, так и дочерний объекты, но просто избегает цикла саморегуляции.

Ответ 4

Я знаю, что этот вопрос довольно старый, но он все еще популярен, и я не вижу никакого решения для ASP.net Core.

Я случай ASP.net Ядра, вам необходимо добавить новый JsonOutputFormatter в Startup.cs файле:

    public void ConfigureServices(IServiceCollection services)
    {

        services.AddMvc(options =>
        {
            options.OutputFormatters.Clear();
            options.OutputFormatters.Add(new JsonOutputFormatter(new JsonSerializerSettings()
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            }, ArrayPool<char>.Shared));
        });

        //...
    }

После его реализации сериализатор JSON просто игнорирует ссылки на циклы. Это означает: он будет возвращать ноль вместо бесконечной загрузки объектов, ссылающихся друг на друга.

Без вышеуказанного решения с использованием:

var employees = db.Employees.ToList();

Будет загружать Employees и связанных с ними Departments.

После установки для ReferenceLoopHandling Ignore Departments будет установлено значение null, если вы не включите его в свой запрос:

var employees = db.Employees.Include(e => e.Department);

Также имейте в виду, что он очистит все выходные форматы, если вы не хотите, вы можете попробовать удалить эту строку:

options.OutputFormatters.Clear();

Но удаление по какой-то причине в моем случае снова вызывает исключение из-за self referencing loop.

Ответ 5

Основная проблема заключается в том, что сериализация модели сущности, которая имеет отношение к другой модели сущности (отношение внешнего ключа). Это отношение вызывает саморегуляцию, это приведет к исключению при сериализации в json или xml. Есть много вариантов. Без сериализации моделей сущностей с помощью пользовательских моделей. Значения или данные из данных модели сущности, сопоставленные с пользовательскими моделями (сопоставление объектов) с использованием Automapper или Valueinjector, затем возвращает запрос, и он будет сериализован без каких-либо других проблем. Или вы можете сериализовать модель сущности, поэтому сначала отключите прокси в модели сущностей

public class LabEntities : DbContext
{
   public LabEntities()
   {
      Configuration.ProxyCreationEnabled = false;
   }

Чтобы сохранить ссылки на объекты в XML, у вас есть два варианта. Более простой вариант - добавить [DataContract (IsReference = true)] к вашему классу модели. Параметр IsReference позволяет ссылаться на ссылки. Помните, что DataContract делает выбор сериализации, поэтому вам также необходимо добавить атрибуты DataMember к свойствам:

[DataContract(IsReference=true)]
public partial class Employee
{
   [DataMember]
   string dfsd{get;set;}
   [DataMember]
   string dfsd{get;set;}
   //exclude  the relation without giving datamember tag
   List<Department> Departments{get;set;}
}

В формате Json в global.asax

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = 
    Newtonsoft.Json.PreserveReferencesHandling.All;

в формате xml

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
var dcs = new DataContractSerializer(typeof(Employee), null, int.MaxValue, 
    false, /* preserveObjectReferences: */ true, null);
xml.SetSerializer<Employee>(dcs);

Ответ 6

Ошибка сообщения означает, что у вас есть цикл саморегуляции.

Вы создаете json, как этот пример (со списком одного сотрудника):

[
employee1 : {
    name: "name",
    department : {
        name: "departmentName",
        employees : [
            employee1 : {
                name: "name",
                department : {
                    name: "departmentName",
                    employees : [
                        employee1 : {
                            name: "name",
                            department : {
                                and again and again....
                            }
                    ]
                }
            }
        ]
    }
}

]

Вы должны указать контекст db, что вы не хотите получать все связанные объекты, когда вы запрашиваете что-то. Опция для DbContext Configuration.LazyLoadingEnabled

Лучший способ найти контекст для сериализации:

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

Ответ 7

Добавьте строку Configuration.ProxyCreationEnabled = false; в конструктор определения частичного класса вашей контекстной модели.

    public partial class YourDbContextModelName : DbContext
{
    public YourDbContextModelName()
        : base("name=YourDbContextConn_StringName")
    {
        Configuration.ProxyCreationEnabled = false;//this is line to be added
    }

    public virtual DbSet<Employee> Employees{ get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
    }
}

Ответ 8

У меня была только одна модель, которую я хотел использовать, поэтому я получил следующий код:

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

Ответ 9

У меня была такая же проблема на моем основном сайте .net. Принятый ответ не сработал для меня, но я обнаружил, что комбинация ReferenceLoopHandling.Ignore и PreserveReferencesHandling.Objects исправила это.

//serialize item
var serializedItem = JsonConvert.SerializeObject(data, Formatting.Indented, 
new JsonSerializerSettings
{
     PreserveReferencesHandling = PreserveReferencesHandling.Objects,
     ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});

Ответ 10

Я знаю, что это старый вопрос, но вот решение, которое я нашел для очень похожей проблемы кодирования в моем собственном коде:

var response = ApiDB.Persons.Include(y => y.JobTitle).Include(b => b.Discipline).Include(b => b.Team).Include(b => b.Site).OrderBy(d => d.DisplayName).ToArray();
        foreach (var person in response)
        {
            person.JobTitle = new JobTitle()
            {
                JobTitle_ID = person.JobTitle.JobTitle_ID,
                JobTitleName = person.JobTitle.JobTitleName,
                PatientInteraction = person.JobTitle.PatientInteraction,
                Active = person.JobTitle.Active,
                IsClinical = person.JobTitle.IsClinical
            };
        }

Так как объект person содержит все из таблицы person, а объект title вакансии содержит список лиц с таким названием, база данных сохранила собственную ссылку. Я думал, что отключение создания прокси и отложенная загрузка это исправит, но, к сожалению, этого не произошло.

Для тех, кто не может этого сделать, попробуйте решение выше. Явное создание нового объекта для каждого объекта, который сам ссылается, но пропускает список объектов или объект, который возвращается к предыдущему объекту, это исправит, так как отключение отложенной загрузки не работает для меня.

Ответ 12

саморегуляция в качестве примера

================================================== ===========

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public int ManagerId { get; set; }
    public virtual Employee Manager { get; set; }

    public virtual ICollection<Employee> Employees { get; set; }

    public Employee()
    {
        Employees = new HashSet<Employee>();
    }
}

================================================== ===========

        HasMany(e => e.Employees)
            .WithRequired(e => e.Manager)
            .HasForeignKey(e => e.ManagerId)
            .WillCascadeOnDelete(false);

Ответ 13

Если вы пытаетесь изменить этот параметр в шаблоне Blazor (ASP.NET Core Hosted), вам нужно передать следующее в вызов AddNewtonsoftJson в Startup.cs в проекте Server:

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