Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC reads properties when object created

I have noticed the following behavior and I wonder if anyone can explain why it is happening and how it can be prevented.

In my partial classes I have added various read-only properties that are not present in the database. When the object is created, these properties are accessed before we get to the controller's Create method. However, properties declared with just get; set; are not accessed.

To elaborate, using very simple examples, if I have the following properties:

public bool getBoolProperty {
    get {
        return true;
    }
}
public bool getSetBoolProperty {
    get; set; 
}

If I then put breakpoints on the properties, when the object is created the breakpoint is hit for the first property but not the second. However, if I have a method:

public bool getBoolProperty() {
    return true;
}

then this is not accessed.

I have tried all sorts of variations and annotations

// empty set
public bool getBoolProperty {
    get {
        return true;
    }
    set {}
}

// not mapped attribute
[NotMapped]
public bool getBoolProperty {
    get {
        return true;
    }
}

// getting private variable
private bool _boolProperty = true;
public bool getPrivateBoolProperty {
    get {
        return _boolProperty;
    }
}

I've tried declaring the properties virtual, yet all variations, except the get; set; variety, are accessed when the object gets created. This behaviour also occurs for integer properties

public virtual int getIntProperty {
    get {
        return 1;
    }
}

and date/time properties,

public virtual DateTime getDateProperty {
    get {
        return DateTime.Now;
    }
}

but not for string properties

public string getStringProperty {
    get {
        return "Hello String";
    }
}

or other entity properties

public Item getItem {
    get {
        return new Item();
    }
}

The problem I have is that some of these properties can involve some logic that might need database access eg

public bool HasChildren {
    get {
        return this.Children !== null && this.Children.Count > 0;
    }
}

which, at the time of creation, the entity won't have.

Obviously I can get around this by making everything a method, but I would be interested to know why ASP.NET MVC accesses these properties on object creation (Some sort of internal validation perhaps), and I would be interested to know if there are any methods of preventing this, possibly by way of an annotation I haven't come across.

My project is database first, C#, EF 4.1, MVC

Edit

I should also point out I'm using POCO entities and accessing the database through stored procedures/function imports.

One thought occurred to me is that these properties are being accessed as part of the change tracking system as discussed here. It seems that what is probably happening is a snapshot is being taken of the newly created item. I tried turning off proxy creation

dbContext.ContextOptions.ProxyCreationEnabled = false;

but the properties are still getting accessed on creation.

Edit 20130322

Following @ladislavmrnka's suggestion of exploring the stack trace I got this:

at System.ComponentModel.ReflectPropertyDescriptor.GetValue(Object component)
at System.Web.Mvc.AssociatedMetadataProvider.<>c__DisplayClassb.<GetPropertyValueAccessor>b__a()
at System.Web.Mvc.ModelMetadata.get_Model()
at System.Web.Mvc.DataAnnotationsModelValidator.<Validate>d__1.MoveNext()
at System.Web.Mvc.ModelValidator.CompositeModelValidator.<Validate>d__5.MoveNext()
at System.Web.Mvc.DefaultModelBinder.OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
at System.Web.Mvc.DefaultModelBinder.BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Object model)
at System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
at System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
at System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
at System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
at System.Web.Mvc.Controller.ExecuteCore()
at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
at System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<>c__DisplayClassb.<BeginProcessRequest>b__5()
at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass1.<MakeVoidDelegate>b__0()
at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
at System.Web.Mvc.MvcHandler.<>c__DisplayClasse.<EndProcessRequest>b__d()
at System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f)
at System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action)
at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)
at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

In it you will notice the calls to a validator or two. As a test I changed my bool property to a nullable bool property:

public bool? getBoolProperty {
    get {
        return true;
    }
}

This time the property was not accessed when the object was created. This is my desired behaviour, however, I don't want to have to change all my custom properties to be nullable, so my question now becomes...

Is there any way to tell the framework not to validate a property? Perhaps via an attribute. This answer almost answers the question, but since I am using database first, I don't seem to have a ValidateOnSaveEnabled property to turn off. Perhaps I need to revisit my models.

like image 822
Mark_Gibson Avatar asked Mar 21 '13 11:03

Mark_Gibson


People also ask

Why TempData is used in MVC?

What is TempData and How to Use in MVC? TempData is used to transfer data from the view to the controller, the controller to the view, or from an action method to another action method of the same or a different controller. TempData temporarily saves data and deletes it automatically after a value is recovered.

What is HTML ActionLink in MVC?

Html. ActionLink creates a hyperlink on a view page and the user clicks it to navigate to a new URL. It does not link to a view directly, rather it links to a controller's action.

Can ViewData be accessed via the View Bag property?

To pass the strongly typed data from Controller to View using ViewBag, we have to make a model class then populate its properties with some data and then pass that data to ViewBag with the help of a property. And then in the View, we can access the data of model class by using ViewBag with the pre-defined property.


1 Answers

As with most of EF's problems, the answer lies with view models.

Because most of the properties that were being accessed were not relevant until the model had been created and persisted to the db, I created a view model especially for the Create view which only contained the absolute minimum number of properties needed to create the model. I modified the controller's Create method to accept my new view model and voila, on creating this new model there were no accesses to any of the irrelevant properties on the main model.

So the answer to my original question appears to be that entity framework performs validation on all non-nullable scalar properties when an object is created and one way to avoid this is as explained above.

like image 121
Mark_Gibson Avatar answered Oct 16 '22 23:10

Mark_Gibson