Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

TryUpdateModelAsync Out of Memory error #2799

Closed
tom-cognify opened this issue Jul 9, 2015 · 10 comments
Closed

TryUpdateModelAsync Out of Memory error #2799

tom-cognify opened this issue Jul 9, 2015 · 10 comments
Assignees
Milestone

Comments

@tom-cognify
Copy link

when calling TryUpdateModelAsync on most(not all) of the entitys in my domain i get an out of memory error. IIS Express seems to loop endlessly until it runs out of memory. This problem started happening in Beta 4, I just updated to Beta 5 and I'm still seeing the same behavior. Domain is about 50 entities.

Here is an example of controller Action.

public async Task<ActionResult> Edit(EnrollmentService updatedES)
    {
        var es = db.EnrollmentService.Find(updatedES.EnrollmentServiceId);
        if (ModelState.IsValid)
        {
            var updateResult = await TryUpdateModelAsync(es);

here is the Error:

2015-07-09 10:54:11.437 -07:00 Error: [Microsoft.AspNet.Diagnostics.ErrorHandlerMiddleware] An unhandled exception has occurred: Exception of type 'System.OutOfMemoryException' was thrown.
System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
   at System.String.Concat(String str0, String str1, String str2)
   at Microsoft.AspNet.Mvc.ModelBinding.ModelNames.CreatePropertyModelName(String prefix, String propertyName)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ExpandValidationNode(ValidationContext context, ModelExplorer modelExplorer)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateChildNodes(String currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.ValidateNonVisitedNodeAndChildren(String modelKey, ValidationContext validationContext, IList`1 validators)
   at Microsoft.AspNet.Mvc.ModelBinding.Validation.DefaultObjectValidator.Validate(ModelValidationContext modelValidationContext, ModelValidationNode validationNode)
   at Microsoft.AspNet.Mvc.ModelBindingHelper.<TryUpdateModelAsync>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNet.Mvc.Controller.<TryUpdateModelAsync>d__100`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNet.Mvc.Controller.<TryUpdateModelAsync>d__99`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at CaseLogic.Controller.EnrollmentServiceController.<Edit>d__6.MoveNext() in C:\Projects\cognify\caselogic\src\CaseLogic.Controller\EnrollmentServiceController.cs:line 127
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNet.Mvc.ControllerActionExecutor.<CastToObject>d__8`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNet.Mvc.ControllerActionExecutor.<ExecuteAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNet.Mvc.ControllerActionExecutor.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNet.Mvc.Core.ControllerActionInvoker.<InvokeActionAsync>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNet.Mvc.Core.FilterActionInvoker.<InvokeActionFilterAsync>d__49.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNet.Mvc.Core.FilterActionInvoker.<InvokeAsync>d__40.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Mvc.MvcRouteHandler.<InvokeActionAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Mvc.MvcRouteHandler.<RouteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Routing.Template.TemplateRoute.<RouteAsync>d__25.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Routing.RouteCollection.<RouteAsync>d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Diagnostics.ErrorHandlerMiddleware.<Invoke>d__4.MoveNext()
@rynowak
Copy link
Member

rynowak commented Jul 9, 2015

Could you post a more complete sample of your entity classes so we can investigate?

@tom-cognify
Copy link
Author

public abstract class BaseEntity<TId>
    {
        [NotMapped]
        public abstract TId Id { get; }
        /// <summary> Transient objects are not associated with an item already in storage.  For instance, a Customer is transient if its Id is 0. </summary>
        public virtual bool IsTransient()
        {
            return Id.Equals(default(TId));
        }

        [Column(TypeName="datetime2")]
        public DateTime CreatedDate { get; set; }
        [Column(TypeName = "datetime2")]
        public DateTime UpdatedDate { get; set; }
        public string UpdateBy { get; set; }
    }

      public class EnrollmentService : BaseEntity<int>
    {
        public override int Id => EnrollmentServiceId;

        public int EnrollmentServiceId { get; set; }
        public int EnrollmentId { get; set; }
        [Required(ErrorMessage = "Service is required.")]
        public int ServiceId { get; set; }
        //[Required(ErrorMessage = "Provider is required.")]
        public int? ProviderId { get; set; }
    ...
        public virtual Enrollment Enrollment { get; set; }
        public virtual Service Service { get; set; }
        public virtual Provider Provider { get; set; }
    }

    public class Enrollment : BaseEntity<int> {
        public override int Id {
            get { return EnrollmentId; }
        }

        public int EnrollmentId { get; set; }
        public int ClientId { get; set; }
        ....
        [ForeignKey("PrimaryCaseManagerUserId")]
        public virtual User PrimaryCaseManager { get; set; }
        [ForeignKey("SecondaryCaseManagerUserId")]
        public virtual User SecondaryCaseManager { get; set; }

        public int ProgramId { get; set; }
        public virtual Program Program { get; set; }
        public virtual Client Client { get; set; }
        public virtual ICollection<EnrollmentService> EnrollmentServices { get; set; }
        public virtual ICollection<Doc> Documents { get; set; }

        public virtual ICollection<Note> Notes { get; set; }
    }

        public class Service : BaseEntity<int>
    {
        public int ServiceId { get; set; }
        [Required]
        public int ProgramId { get; set; }
        public string Code { get; set; }
        [Required]
        public string Name { get; set; }
        public string SubName { get; set; }
    ....
        public ICollection<Provider> Providers { get; set; }
        public Program Program { get; set; }
    }

    public class Provider : BaseEntity<int>
    {
        public int ProviderId { get; set; }
        [Required]
        public string Name { get; set; }

    ....
        public virtual ICollection<Service> Services { get; set; }
    }

     public class Client : BaseEntity<int>
    {
        [NotMapped]
        public override int Id { get { return ClientId; } }
        public int ClientId { get; set; }
    ....
            [ForeignKey("HomeAddressId")]
        public virtual Address HomeAddress { get; set; }

        public virtual ICollection<Doc> Docs { get; set; }
        public virtual ICollection<Note> Notes { get; set; }
        public virtual ICollection<Enrollment> Enrollments { get; set; }
        public virtual ICollection<Contact> Contacts { get; set; }

      public virtual ICollection<ClientCareSetting> CareSettings{ get; set; }
     }

@rynowak
Copy link
Member

rynowak commented Jul 20, 2015

Thanks,

I'm looking into trying to reproduce the OOM issue. We definitely have a bug here and that shouldn't be happening.

A few thoughts in the meantime:

Using TryUpdateModel on your entity model means that data from request can update anything about it -- including all of the related entities exposed through relationships. There's a few ways to secure your app against this problem - here's an older article but the advice is still relevant: http://odetocode.com/blogs/scott/archive/2012/03/11/complete-guide-to-mass-assignment-in-asp-net-mvc.aspx

There are a few mitigations for this, but using ViewModels provides the most flexibility. http://stackoverflow.com/questions/11064316/what-is-viewmodel-in-mvc

By the same coin, validation is going to traverse your entire EF model and attempt to validate every entity - hence the number of levels of DefaultObjectValidator.Validate in the callstack you provided.

@tom-cognify
Copy link
Author

Thanks for the suggestions, agreed that ViewModels are the right way to do it, code is mostly from quick and dirty admin pages where we were too lazy to create Models. I hand't realize that it would try to validate all of the child entities, but that makes sense.

@rynowak
Copy link
Member

rynowak commented Jul 21, 2015

I was able to repro your OOM issue by creating a fake dataset based on your sample. It's definitely due to the creation and persistence of a high number of ModelValidationNode instances.

image

The hundreds of thousands of MVN instances are only eligible for collection after we finish validating.

@tom-cognify
Copy link
Author

I thought a bit more about your suggestion and I don't think using ViewModels helps, I tried a quick test and am seeing the same behavior using a ViewModel. I'm thinking the validation occurs on the entity you are trying to update which is the domain entity still with all of its related children.

public async Task<ActionResult> Edit(EnrollmentServiceModel updatedES)
{
    var es = db.EnrollmentService.Find(updatedES.EnrollmentServiceId);
    if (ModelState.IsValid)
    {
        var updateResult = await TryUpdateModelAsync(es);
 ....

public class EnrollmentServiceModel
{
    public int EnrollmentServiceId { get; set; }
    public int EnrollmentId { get; set; }
    [Required]
    public int ServiceId { get; set; }
    public int? ProviderId { get; set; }
....
}

@rynowak
Copy link
Member

rynowak commented Jul 21, 2015

I'm thinking the validation occurs on the entity you are trying to update which is the domain entity still with all of its related children.

That's absolutely correct.

If you're just using TryUpdateModelAsync to copy the values, have you looked into using Automapper?

@tom-cognify
Copy link
Author

yes, we do use AutoMapper already to populate ViewModels, I've just always used TryUpdateModel to update the EF entity, I thought that was the primary use case of that method?

@rynowak
Copy link
Member

rynowak commented Jul 21, 2015

TryUpdateModel is just model binding against a model object that already exists. In your example it performs full model binding twice - once on EnrollmentServiceModel and again on your DB entity.

rynowak added a commit that referenced this issue Jul 21, 2015
The change here is that when we create the ModelValidationNodes to just
return them rather than adding them to the tree. For a very large model,
having these extra nodes in the tree would eventually cause an OOM.

We're going to be taking a more thorough look at this code separately,
hence the quick fix.
rynowak added a commit that referenced this issue Jul 22, 2015
The change here is that when we create the ModelValidationNodes to just
return them rather than adding them to the tree. For a very large model,
having these extra nodes in the tree would eventually cause an OOM.

We're going to be taking a more thorough look at this code separately,
hence the quick fix.
@rynowak
Copy link
Member

rynowak commented Jul 22, 2015

bae442c

@rynowak rynowak closed this as completed Jul 22, 2015
@rynowak rynowak added this to the 6.0.0-beta7 milestone Jul 22, 2015
@rynowak rynowak added the bug label Jul 22, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants