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.