Подтвердить что ты не робот

Справочная информация JavaScriptSerializer при использовании ScriptIgnore

У меня есть объекты Entity Framework, разделенные на отдельную библиотеку классов из моего веб-проекта и уровня доступа к данным. В моем контроллере я звоню в мой репозиторий, чтобы получить IEnumerable<RobotDog.Entities.Movie>, а затем попытаюсь выполнить сериализацию в json с помощью JavaScriptSerializer, но я получаю круговую ссылку, хотя я использую атрибут [ScriptIgnore].

ВАЖНО: Первоначально у меня были сущности, доступ к данным и все веб-сайты в рамках одного проекта, и я смог успешно сериализовать свои заявки без круговой ссылки. Когда я создал отдельные слои, когда у меня возникли проблемы. Я не изменил ни одного из объектов.

Пример одного из моих объектов в пространстве имен RobotDog.Entities:

namespace RobotDog.Entities {
    public class Character {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [MaxLength(200)]
        public string Name { get; set; }

        public virtual Person Person { get; set; }

        [ScriptIgnore]
        public virtual Movie Movie { get; set; }
    }
}

Мой контроллер:

namespace RobotDog.Web.Controllers {
    public class MoviesController : Controller {
        private UnitOfWork _unitOfWork = new UnitOfWork();

        [HttpGet]
        public ActionResult Index() {
            var user = Membership.GetUser(User.Identity.Name);
            if(user != null) {
                var movies = _unitOfWork.UserMovieRepository.Get(u => u.UserId == (Guid) user.ProviderUserKey).Select(m => m.Movie);
                var serializer = new JavaScriptSerializer();
                var json = serializer.Serialize(movies);
                return View(json);
            }
            return View();
        }

    }
}

Мой репозиторий:

namespace RobotDog.DataAccess.Movies {
    public class Repository<TEntity> : IRepository<TEntity> where TEntity : class {
        internal MovieContext Context;
        internal DbSet<TEntity> DbSet;

        public Repository(MovieContext context) {
            if (context == null)
                throw new ArgumentNullException("context");

            Context = context;
            DbSet = Context.Set<TEntity>();
        }

        public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> predicate = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null ) {
            IQueryable<TEntity> query = DbSet;

            if (predicate != null)
                query = query.Where(predicate);

            return orderBy != null ? orderBy(query).ToList() : query.ToList();
        }
    }
}
4b9b3361

Ответ 1

Графы круговых объектов не могут быть сериализованы JSON. И когда вы даете ему вторую мысль, это на самом деле имеет смысл. Правильный способ справиться с этим - использовать модели просмотра. Вы никогда не должны передавать свои объекты домена непосредственно к вашим представлениям. Всегда определяйте модель представления, содержащую только необходимые свойства, которые вы хотите открыть.

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

Затем все, что вам нужно сделать, - сопоставить модель вашего домена с моделью представления и передать эту модель представления JsonResult (да, это еще одна проблема в вашем коде - вы вручную сериализуете JSON и записываете сантехнический код в своем действии контроллера вместо делегируя это в рамки).

Итак:

[HttpGet]
public ActionResult Index() 
{
    var user = Membership.GetUser(User.Identity.Name);
    if(user != null) 
    {
        IEnumerable<Movie> movies = _unitOfWork
            .UserMovieRepository.Get(u => u.UserId == (Guid) user.ProviderUserKey)
            .Select(m => m.Movie);
        IEnumerable<MovieViewModel> moviesVm = ... map the domain model to your view model
        return Json(moviesVm, JsonRequestBehavior.AllowGet);
    }

    // return an empty movies array
    var empty = Enumerable.Empty<MovieViewModel>();
    return Json(empty, JsonRequestBehavior.AllowGet);
}

Важная вещь, которую вы должны сейчас сосредоточить, - это определить класс MovieViewModel, который будет содержать только информацию, которую вы хотите открыть клиенту как JSON. Разбейте все круговые ссылки. Не стесняйтесь иметь дополнительные модели просмотра, которые эта основная модель представления ссылается, чтобы отобразить другие объекты.

И самое главное: никогда не передавайте свои модели домена в представление. Всегда определяйте модели просмотра. Таким образом ваше приложение полностью не зависит от используемой вами технологии доступа к данным. Вы можете изменить свой уровень DAL столько, сколько хотите, не влияя на часть пользовательского интерфейса, потому что этот пользовательский интерфейс представлен моделями просмотра.

Ответ 2

Возможно, какой-то поздний ответ, но у меня была аналогичная проблема с классами POCO для Entity Framework Code-Firts. Проблема заключалась в том, что свойства были объявлены виртуальными. В этом случае EF создает класс прокси, который переопределяет виртуальное свойство. Кажется, что атрибут ScriptIgnore не применяется по умолчанию для переопределенных свойств, если вы не используете его так:

[ScriptIgnore(ApplyToOverrides=true)]