0

I have a Asp.NET MVC project, database first. I have an Create action in Place controller. In the action method I get my data like this:

// GET: /Place/Create
[Authorize]
public ActionResult Create()
{
    string userId = User.Identity.GetUserId();
    var places = db.Places.Where(p => p.UserId == userId);

    var placesVM = new PlacesVM(places);

    ViewBag.UserId = new SelectList(db.AspNetUsers, "Id", "UserName");
    return View(placesVM);
}

My Place view model looks like this:

public class PlacesVM
{
    public IQueryable<Place> Places { get; set; }
    public Place Place { get; set; }

    public PlacesVM(IQueryable<Place> places)
    {
        Places = places;
        Place = new Place();
    }
}

My Place Model:

public partial class Place
    {
        public string Id { get; set; }
        public string UserId { get; set; }
        //TODO: Validare cordonate
        public decimal X { get; set; }
        public decimal Y { get; set; }

        [Display(Name = "Title")]
        [Required]
        [StringLength(250, MinimumLength = 5)]
        public string Titlu { get; set; }

        [Display(Name = "Description")]
        [Required]
        [StringLength(500, MinimumLength = 10)]
        public string Descriere { get; set; }

        [Required]
        [Range(0, 1)]
        public byte Public { get; set; }

        public virtual AspNetUser AspNetUser { get; set; }
    }

AspNetUser:

public partial class AspNetUser
    {
        public AspNetUser()
        {
            this.AspNetUserClaims = new HashSet<AspNetUserClaim>();
            this.AspNetUserLogins = new HashSet<AspNetUserLogin>();
            this.Places = new HashSet<Place>();
            this.UserComments = new HashSet<UserComment>();
            this.AspNetRoles = new HashSet<AspNetRole>();
        }

        public string Id { get; set; }
        public string UserName { get; set; }
        public string PasswordHash { get; set; }
        public string SecurityStamp { get; set; }
        public string Discriminator { get; set; }

        public virtual ICollection<AspNetUserClaim> AspNetUserClaims { get; set; }
        public virtual ICollection<AspNetUserLogin> AspNetUserLogins { get; set; }
        public virtual ICollection<Place> Places { get; set; }
        public virtual ICollection<UserComment> UserComments { get; set; }
        public virtual ICollection<AspNetRole> AspNetRoles { get; set; }
    }

Now I would like to use Model.Places proprietes in the javascript part of the page. How can I do this?

I've tried to the following:

<script>
    var model = '@Html.Raw(Json.Encode(Model))';
</script>

But I got this error:

{"A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.Place_084A987E8F6FBE574A22E813FE314F2894AF728F244BDD6582AF50929FF1161D'."}

I have checked the following links on SO, but didn't manage to solve my problem:

Community
  • 1
  • 1
Gerald Hughes
  • 5,771
  • 20
  • 73
  • 131
  • Not related, but It would need to be `var model = @Html.Raw(Json.Encode(Model));` (without the quotes) if you wanted to serialize the model to a javascript object. –  Jun 05 '16 at 11:59
  • You need to show the model for `Place` (it would contain a property which is an object that contains a property `Place`, causing a circular reference) –  Jun 05 '16 at 12:00
  • Does class `AspNetUser` contain a property for `Place`? –  Jun 05 '16 at 12:07
  • AspNetUser contains: public virtual ICollection Places { get; set; } – Gerald Hughes Jun 05 '16 at 12:11
  • 1
    That's the cause of the problem (serializing `Place` also serializes `AspNetUser` which then serializes its property `Places` which then has to serialize each `Place` which serializes `AspNetUser` and so on and so on - a circular reference). You will need to use a view model (and should be anyway) with just the properties you need in the view (say) `class PlaceVM` and copy the properties you need from `Place` into it except `AspNetUser`. If you need anything related to `AspNetUser` in the view, just add some properties for it (say) `int UserID` and `string `UserName` –  Jun 05 '16 at 12:19
  • @StephenMuecke, thanks, I understand now. If you post this, I will accept your answer – Gerald Hughes Jun 05 '16 at 12:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/113840/discussion-between-stephen-muecke-and-stefan). –  Jun 05 '16 at 12:30

1 Answers1

2

You PlacesVM view model contains a property that is typeof Place which in turn contains a property which is typeof AspNetUser which in turn contains a property which is typeof Collection<Place>.

When the Json.Encode() method serializes you model, it serializes Place which then serializes AspNetUser which then serializes each Place which throws the error, because if allowed to continue, it would serialize each AspNetUser and so on and so on until the system ran out of memory.

Change you view model to include only those property that you need in the view - refer What is ViewModel in MVC?. Note that a view model should not generally contain properties which are data models, particularly when you editing data in a view. Instead, copy the properties from Place into PlaceVM that you need to edit, except AspNetUser, and if you need to display some properties of AspNetUser, then just include additional properties for then, for example public string UserName { get; set; }.

Side note: Your current view model does not contain a default (parameterless) constructor so the DefaultModelBinder will throw an exception when you submit.

Community
  • 1
  • 1