Reputation: 8880
In my application, I have cross-entity validation logic that requires me to look at the entire change set and I'm doing this using the BeforeSaveEntities
override.
I can construct the right logic by examining the saveMap
parameter, but what am I supposed to do if I find something invalid?
If I throw an exception, like I would for single entity validation in the BeforeSaveEntity
override, the whole save is aborted and the error is reported to the client. But some of the entities might be valid so I would want to save those and only abort the invalid parts.
Because BeforeSaveEntities
returns a saveMap
, I think I should be able to remove the invalid entities from the change set and continue to save the valid entities, but then how do I report the invalid parts to the client?
Is it possible to do a partial save of only the valid entities and at the same time, report a sensible error to the client to describe the parts of the save that failed?
Upvotes: 0
Views: 135
Reputation: 17863
Jay told you the way it is.
I wouldn't hold my breath waiting for Breeze to change because I think yours is a rare scenario and it isn't one we would want to encourage anyway.
But I'm weird and I can't stop thinking what I'd do if were you and I absolutely HAD to do it. I might try something like this.
Warning: this is pseudo-code and I'm making this up. I do not recommend or warrant this
Create a custom MyCustomEFContextProvider
that derives from EFContextProvider
.
Give it an ErrorEntities
property to hold the error object
Override (shadow) the SaveChanges
method with another that delegates to the base
public new CustomSaveResult SaveChanges(JObject saveBundle, TransactionSettings transactionSettings = null) { var result = base.SaveChanges(saveBundle, transactionSettings); // learn about CustomSaveResult below return new CustomSaveResult(this.ErrorEntities, result); }
Catch an invalid entity inside BeforeSaveEntities
Pass it with error message to your custom ErrorEntities
property
You get to that property via the EntityInfo
instance as in
((MyCustomEFContextProvider) info.ContextProvider).ErrorEntities.Add(new ErrorEntity(info, message));
Remove the invalid entity from the SaveMap
so it won't be included in the actual save
Let the save continue
The second line of your override SaveChanges
method creates a new instance of your CustomSaveResult
from the standard one and returns that to the caller.
public class CustomSaveResult : SaveResult { public List ErrorEntities; public CustomSaveResult(List errorEntities, SaveResult result){ // copy over everything this.Entities = result.Entities; this.KeyMappings = result.KeyMappings; this.Errors = this.Errors; // and now your error stuff this.ErrorEntities = errorEntities; } }
Let's assume the caller is your Web API controller's SaveChanges
method. Well you don't have to change a thing but you might make it clear by explicitly returning your custom SaveResult:
readonly MyCustomEFContextProvider _contextProvider = new MyCustomEFContextProvider(); ... [HttpPost] public CustomSaveResult SaveChanges(JObject saveBundle) { return _contextProvider.SaveChanges(saveBundle); }
JSON.Net will happily serialize the usual material + your custom ErrorEntities
property (be sure to make it serializable!) and send it to the Breeze client.
On the Breeze client you write your own variation on the stock Breeze Web API data service adapter. Yours does almost exactly the same thing as the Breeze version. But, when processing the save payload from the server, it also extracts this extra "error entities" material in the response and does whatever you want to do with it.
I don't know what that will be but now you have it.
See how easy that was? LOL.
Upvotes: 1
Reputation: 17052
Breeze does not currently support a save mechanism that both saves and returns an error at the same time. While possible this seems a bit baroque.
As you pointed out, you can
1) Throw an exception inside of the BeforeSaveEntities and fail the save. You can even specify which specific entity or entities caused the failure and why. In this case the entire save is aborted.
or
2) Remove 'bad' items from the saveMap within the BeforeSaveEntities and save only a subset of what was passed in. In this case you are performing a partial save.
But we don't support a hybrid of these two. Please add this to the Breeze User Voice if you feel strongly and we can see if other members of the community feel that this would be useful.
Upvotes: 0