Problem
I got a "TypeError: object is not a function" error when
EntityManager.saveChanges
. The error arises before breeze sends anything to the server.When I read the (long) stack trace, I see that the error is thrown inside a Breeze method called
InitializeEntityPrototype.prototype.getProperty
.
There are other places you might encounter this error; this just happens to be the place I discovered it today.
Cause
Note that my application uses Knockout (ko). That means my entity model is managed by the breeze "ko" model adapter which expects every data property to be a ko.observable (an observable
or an observableArray
). That means that your property values are implemented as functions, not primitive data types, arrays, or objects.
Breeze initializes my entities with observable properties so I don't have to do it myself. But it is up to me preserve those observables when I set entity models in my code.
One of the most common mistakes in ko programming is to set the property rather than call the property setter.
todo.Description("foo"); // Correct ... call the ko.observable assignment function
todo.Description = "foo"; // WRONG ... wipes out the observable function !!!
The moment I make the mistake of assigning "foo" to todo.Description
, the observable function is gone ... and so is Breeze's ability to monitor changes to that property.
My data bound HTML controls may appear to continue working. Knockout doesn't have to bind to an observable; it will happily bind to a primitive data value. But now this becomes a one-time, read-only binding. The property ceases to be observable. Subsequent changes to the property will not be propagated to the screen.
In other words, my mistaken assignment results in a silent error in UI behavior.
But my mistake is not silent when Breeze attempts to process the property. Breeze assumes that an entity data property is a ko function. The Breeze ko model adapter doesn't bother to check ... it just calls what it presumes to be a function. Here's the (internal) entity getProperty
implementation:
proto.getProperty = function (propertyName) {
return this[propertyName]();
};
You can see why that is going to throw an exception if this[propertyName]
is anything other than a function.
Sadly for me, the error is likely to be thrown somewhere deep in the bowels of a Breeze operation. The error message "object is not a function" (or something similar) could be about anything. I'm unlikely to make the connection.
This S.O. question is a reminder to look for this particular cause.
What to do?
It's clearly my fault. Now I have to find where I assigned the entity property instead of calling the function ... and fix that.
My search depends upon knowing the property name. The present error message doesn't tell me the name. So I've got to breakpoint the code and catch the error as it is thrown.
A "simple" expedient is to temporarily monkey-patch this method in breeze.debug.js with a try/catch
version.
- open breeze.debug.js in an editor
- find "
return this[propertyName]();
" change it to this:
proto.getProperty = function(propertyName) { try { return this[propertyName](); } catch (err) { debugger; err.message = propertyName + ' is not a ko function; did you wipe it out by assignment?\n' + (err.message || ''); throw err; } }
run the app again with the Developer Tools open
It should stop at the debugger
line where you'll learn the property name and the entity involved.
Should Breeze provide a richer error message?
It would be nice if Breeze composed this message for me. In fact, the team is looking into doing that.
The main obstacle is performance. The getProperty
and setProperty
methods are on a hot path. They get called a lot, especially during query result materialization. The Breeze team is leery of extra logic in this sensitive area. We don't want everyone to pay a big price to catch developer error, at least not in a production (minified) Breeze library.
Let's give the Breeze team time to figure this out.