These days,i have learned breezejs,durandaljs, so i made an spa application for excersizing,but breezejs(or q.js) often gives out errors
[Q] Unhandled rejection reasons (should be empty): ["proto.saveChanges#http:...s/jquery-1.9.1.js:2750\n"] (Firefox)
[Q] Unhandled rejection reasons (should be empty):(no stack) Error: Client side validation errors encountered - see the entityErrors collection on this object for more detail (IE10, but why deleteing an entity triggers validation ?)
I feel disappointed to use breezejs, what on earth am i doing!!!
I just do saving and deleting customer, sometimes error occured as above, sometimes works fine.(how confused i am feeling! :'( )
Here is part of my datacontext
var saveChanges = function () {
return manager.saveChanges()
.then(saveSuccess)
.fail(saveFailure); //.done() does not work either
//
function saveSuccess() {
console.log("Save Success!");
}
//
function saveFailure(error) {
console.log("Save Failure!");
throw error;
}
};
To save a customer:
define(['modules/dataService'], function (datacontext) {
var ctor = function () {
this.entity = ko.observable();
};
ctor.prototype.activate = function () {
//problem code --> [Q] Unhandled rejection reasons (should be empty)
//it will always create empty Customer when activate is called.
//so error occured when i switch in because of creating empty Customer every time.
this.entity(datacontext.createEntity('Customer'));
};
ctor.prototype.saveClick = function () {
if (this.entity().entityAspect.validateEntity())
datacontext.saveChanges();
else
console.log('validation error!');
};
return ctor;
});
To delete a customer
define(function (require) {
var datacontext = require('modules/dataService');
var vm = {
customers: ko.observableArray(),
activate: function () {
var self = this;
return datacontext.getCustomers(self.customers);
},
deleteCustomer: deleteCustomer
};
return vm;
//delete customer
function deleteCustomer(customer) {
vm.customers.remove(customer);
//Sets the entity to an EntityState of 'Deleted'
customer.entityAspect.setDeleted();
datacontext.saveChanges();
}
});
I think my code would work fine, but it can't!
Where is the fatal error i make? plz let me know.
Thanks in advance!
I know this thread has been here for more than a year now but I thought I could share my story.
I just got the same error while using breeze + angularJS. After some research, I figured it out:
I was passing null values in some of the entitie's properties while those fields in the database table where marked as NOT NULL.
Breeze - saveChanges
In the implementation of breeze.saveChanges, a check is done on a internal flag (line 12743 approx : if (this.validationOptions.validateOnSave) ...)
This is to enable verification of the entity against the database schema (aka the metadata).
Now most of the time we tend to call saveChanges without any parameters. And the error does not show otherwise then in the console as a general validation error message.
What have i done
We'll my fix was in 2 parts:
Add some code in my calls to saveChanges in order to trap these errors and display a better message in the console (see code below)
Fix either the DB schema (i.e. Relax NOT NULL fields to NULLABLE) OR set some default values OR enforce the business logic by adding required attributes to input controls.
Here's a snippet of the code I now use to trap the errors:
return manager.saveChanges(null, null, null, function (errors) {
console.log('breeze saveChanges returned some errors: ');
errors.entityErrors.forEach(function(e) {
console.log(e.errorMessage, e);
});
}); // return promise
Related
I am using ASP.NET WebAPI 2 with Breeze. I want to be able to return meaningful error messages when saving changes using SaveChanges() method. This is in case there is an error.
The current implementation returns SaveResult. How can return message e.g
var cardDetail = _membershipContextProvider.Context.Database.SqlQuery<CardDetail>("IssuedCardsGetVerificationDetails #CardNo", parameter).FirstOrDefault();
if (cardDetail == null)
{
HttpResponseMessage msg = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(string.Format("The beneficiary with Card No. {0} was found in the NHIF database", CardNo)),
ReasonPhrase =string.Format("Card No. {0} Not Found in the NHIF Database!",CardNo)
};
throw new HttpResponseException(msg);
}
return cardDetail;
You need to throw an EntityErrorsException within the custom save method. This exception lets you both specify a top level message as well as a custom message for each failed entity.
[HttpPost]
public SaveResult MyCustomSaveMethod(JObject saveBundle) {
ContextProvider.BeforeSaveEntitiesDelegate = SaveThatMightThrow;
return ContextProvider.SaveChanges(saveBundle);
}
private Dictionary<Type, List<EntityInfo>> SaveThatMightThrow(Dictionary<Type, List<EntityInfo>> saveMap) {
List<EntityInfo> orderInfos;
// if this save tries to save ANY orders throw an exception.
if (saveMap.TryGetValue(typeof(Order), out orderInfos)) {
var errors = orderInfos.Select(oi => {
return new EFEntityError(oi, "WrongMethod", "Entity level detail error - Cannot save orders with this save method", "OrderID");
});
var ex = new EntityErrorsException("Top level error - Orders should not be saved with this method", errors);
// if you want to see a different error status code use this.
// ex.StatusCode = HttpStatusCode.Conflict; // Conflict = 409 ; default is Forbidden (403).
throw ex;
}
return saveMap;
}
Note that there is a bug in Breeze 1.4.16 where the top level error is not being propagated properly (it returns to the client as an empty string), however the entity level error messages will come thru just fine. This bug has been fixed in the latest GitHub repos, but you will need to get the fixed code from both the breeze.js and the breeze.server.net repos because the fix was to both the breeze.js client as well as the ContextProvider class in breeze.server.net. Or you can wait for the next breeze release in about a week.
I am starting to develop with Breeze.js and ASP MVC+WebApi Controllers. I am concerned about securities, as we should all be concerned about the possibility of a hacker coming into play. Now I did find the BeforeSaveEntity intercept and it does seem to be exactly what I want to use on the server side. I managed to get the security I want on the server side, but how do I deal with it on the client side, in my case with AngularJS, what and how should I catch the output and deal with it? Let me show you some code sample I use on the server side:
public class ConferenceContextProvider : EFContextProvider<ConferenceContext>
{
public ConferenceContextProvider() : base() { }
// Creating the BeforeSaveEntity for Security purposes, see more details at http://www.breezejs.com/documentation/efcontextprovider#SaveInterception
protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
// return false if we don’t want the entity saved.if (entityInfo.Entity.GetType() == typeof(Role)
&& entityInfo.EntityState == EntityState.Deleted)
{
return false;
}
else
{
return true;
}
}
protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
{
// return a map of those entities we want saved.
return saveMap;
}
}
and then on client side with AngularJS
// AngularJS DataService
function saveChanges() {
if (manager.hasChanges()) {
var promise =
manager.saveChanges()
.catch(queryFailed)
.finally(function (data) {
toastr.success('Save to DB Succeeded');
});
} else {
toastr.warning("Nothing to save");
};
}
How do I catch the result and deal with it? With Firebug I can see that the POST returns a JSON object with Entities array being filled (if user has access) or that same array being empty (if user has access denied). But if multiple changes happen, then the array might be filled with a portion of it applied. So what is the best approach on the client side with an access denied? Can someone give me a proper sample code on how to deal with acces denied? Thanks for your help
Overriding the BeforeSaveEntity method will mean that on the server once the payload it received your server will call the BeforeSaveEntity method once for each entity before the entity is saved. As the docs show if you return false it will simply not save the entity. Take note of the following line -
If the method returns false then the entity will be excluded from the
save. If the method throws an exception, the entire save is aborted
and the exception is returned to the client.
If you throw an HTTP error I think it should propagate properly you should be able to catch that error client side and display it. This is assuming if a payload contains an entity to delete you want to cancel the whole save.
Example -
protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
// throw http exception if there is an entity flagged for deletion
if (entityInfo.Entity.GetType() == typeof(Role)&& entityInfo.EntityState == EntityState.Deleted)
{
var response = new HttpResponseMessage(HttpStatusCode.BadRequest){ Content = new StringContent("Cannot delete an entity") };
throw new HttpResponseException(response);
}
else
{
return true;
}
}
And on your client-side query where you have a queryFailed method (pseudo code, examine the error that is thrown to construct this properly) -
function queryFailed (error) {
alert('Query failed - ' + error.message);
}
If you want to save all the other entities but this one and then return custom errors in the response you can do that as well but that will take additional customization and that would probably be a much more detailed answer
I defined some class to query a database.
class SqlGetData {
ConnectionPool pool;
List<String> rows;
SqlGetData(this.pool);
Future <List<String>> run(String sQuery) {
rows = new List<String>();
return readData(sQuery).then((_) {
return rows;
});
}
Future readData(String sQuery) {
return pool.query(sQuery).then((result) {
return result.forEach((row) {
String s = JSON.encode(row);
rows.add(s);
});
});
}
}
which I call like this:
var sql = new SqlGetData(pool);
sql.run('select firstName, lastName from person where id =' + s1).then((rows) {
some code here to process the data
});
If the database is not running I get an error on the return pool.query in readData, which I want to catch and pass to the client in some error message.
How and where can I code the try ... catch ... to prevent the server from dying? My problem is that I have to return futures, which is still difficult for me to grasp.
Take a look at this article Futures and Error Handling (if you haven't already).
There are two places:
.then((_) => doSomething(),
onError: (e) => doErrorHandling()).catchError((e) => doErrorHandling());
Guenter's answer is good. Here are a couple of extra tips.
It's more common to use .catchError() than the named parameter, if in doubt just use .catchError().
One problem with async code is if you forget to add a catchError handler anywhere in your codebase, and an error is triggered, it will bring your whole server down. Not good. However You can use Zones to handle uncaught errors in your code, and prevent this from happening.
There isn't much documentation about Zones at the time of writing, as it is a new feature. Florian Loitsch is working on an article which will appear here sometime soon. Here is an example of using runZoned():
runZoned(() {
var pool = new Pool.connect(...); // Don't know pool API, just making this up.
pool.query(sql).then((result) {
print(result);
});
// Oops developer forgot to add a .catchError() handler for the query.
// .catchError((e) => print('Query error: $e);
}, onError: (e) => print("Uncaught error: $e"));
This code will run without bringing down your server, despite the missing catchError() handler. Note, you will need to start the pool/connection within the same zone as the query is executed within.
I'm looking for documentation about what to expect at the client side when returning false from BeforeSaveEntity(EntityInfo entityInfo) but i found nothing so I decided to experiment myself.
I made a test on the Doccode project:
test("create customer returning false from contextprovider", 1, function () {
var em = newEm();
var category = em.createEntity('Category', { CategoryName: 'Julian Category' });
stop();
em.saveChanges().then(function (saveResult) {
//ok(category.entityAspect.entityState.isAdded(), "Added state because contextprovider return false");
ok(em.hasChanges() === false,"No changes pending");
})
.fail(function (error) {
debugger;
})
.fin(function () {
start();
});
});
And i found that the two assertions were true, so i think that it may be a bug.
To make the test i created a custom provider:
public class CustomEFContextProvider : EFContextProvider<NorthwindContext>
{
public CustomEFContextProvider() : base()
{
}
protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
if(entityInfo.Entity.GetType() == typeof( Category)){
return false;
}
return true;
}
}
and changed the NorthwindController to use it:
readonly CustomEFContextProvider _contextProvider =
new CustomEFContextProvider();
I'm returning false when a Category is saved, so the category doesn't get inserted on the database and it's not returned in the saveResult.entities array. The keyMappings is also empty. All of this is what i expected.
What i didn't expect is that the entitymanager.hasChanges function returns false because the category entity is marked as added, what in my opinion leaves the manager inconsistent.
It's a bug? I'm doing something wrong? Were my expectations wrong?
Thx.
Ok, as of v 1.2.5 ( just released), this should be fixed. Please post back to confirm if possible, and thx for the bug report. :)
Hm... it's an interesting question.
The reason that EntityManager.hasChanges returns true is that you haven't saved the "added" Category object, and therefore breeze doesn't call EntityAspect.acceptChanges on it because it is still in fact in an "added" but not savable state.
I think this is actually the right behavior, because if we were to call acceptChanges automatically on this entity, you would never know on the client that it had not been saved. You can of course call acceptChanges directly within the promise callback.
Does this make sense?
I have a controller action that does some work in the database and then exits when it's finished. This action is being called via jQuery's ajax function with the dataType set to 'json'.
If I set the return type of the action to void, everything will function just fine except Firefox will show an error in the console that says: "no element found".
It makes sense that Firefox would throw this error if it was expecting XML to come back. However, even when I change the dataType property of the ajax call to "text", I still receive the error. In order to get rid of the error with the return type void, I would have to set the Response's ContentType to "text/html". Or I could set the return type to JsonResult and return a new [empty] JsonResult object.
I'm sure there are several ways I can make this error go away, but I wanted to know the proper way to handle actions with no return values being called via ajax.
If it matters, I'm also using the async controller action pattern.
public void DoSomethingAsync(SomeJsonObjectForModelBinding model)
{
// do some database things
}
public void DoSomethingCompleted()
{
// nothing to do...
// what should my return type be?
// do I need to set the content type here?
}
I know this doesn't exactly answer your question, but I would argue that you should always have a return value coming back from an AJAX or web service call. Even if only to tell you that the operation was successful, or otherwise return the error (message) back to you.
I often define a class like this:
public class JsonResultData
{
private bool _success = true;
public bool Success
{
get { return _success; }
set { _success = value; }
}
public object Value { get; set; }
public List<string> Errors { get; set; }
public JsonResultData()
{
this.Errors = new List<string>();
}
}
And then use it to return data or any other call meta data in the JsonResultData wrapper like so:
return new JsonResult {
Data = new JsonResultData { Value = returnValue, Success = true }
};
I can't comment because of my reputation but I still wanted to contribute to clear the confusion in Kon's answer.
In an application I caught all exceptions within an ActionMethod, set an HttpStatusCode and added an error message to the response. I extracted the message in the Ajax error function and showed it to the user.
Everything worked out fine until the application got put on the staging server, who had some kind of settings that did not allow a return message within an erroneous response. Instead some standard Html was transmitted resulting in a JS error processing the response.
In the end I had to rewrite all my exception handling returning my application errors as successful Ajax call (which it actually is) and then differ within the Ajax success function, just the way it should be.
You should not mix system-level and application-level feedback. You may not be able to control the system-level feedback the way your application needs.