Reputation: 105
I’m trying save some entities using breeze.js. Breeze is working fine and it saves all the changes as required. However, I have trouble validating and ensuring authorization is the server side. From what I’ve gather so far I guess the only way to do this is via examining the JObject passed into save bundles and constructing corresponding objects on the server side. I have to do this (instead of relying Breeze.SaveChanges as I have some logic on the server side). How do I do this? And how do I construct the Breeze.WebApi. SaveResult? Idea of any other way of solving this problem is also very welcome
Upvotes: 3
Views: 1650
Reputation: 21
So here is my thought on this one, since I am not using a ContextProvider at all. I am utilizing a SQL back-end and Ninject to inject a repository dependency into each controller I have. I have more items than the demo for "Todos" and want separate controllers out there and repositories as well. If I created the ContextProvider as shown by the breeze docs I would have one ContextProvider file with all the entities in it. This would be huge. If I separated them into separate contexts I would duplicating code in all the overrides.
Here is my Save Changes method in ContactFormController.cs :
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
var sr = new SaveResult() { KeyMappings = new List<KeyMapping>(), Entities = new List<object>()};
dynamic entity = saveBundle["entities"][0];
ContactForm form = entity.ToObject<ContactForm>();
EntityState state = entity.entityAspect.entityState;
switch (state)
{
case EntityState.Added:
KeyMapping mapping = new KeyMapping(){EntityTypeName = typeof(ContactForm).ToString(), TempValue = form.Id };
var validationErrors = _contactFormService.ProcessContactForm(ref form).Cast<object>().ToList();
//if we succeed then update the mappings
if (validationErrors.Count == 0)
{
//setup the new mappings
mapping.RealValue = form.Id;
sr.KeyMappings.Add(mapping);
//link the entity
sr.Entities.Add(form);
}
else
{
sr.Errors = validationErrors;
}
break;
}
return sr;
}
I dynamically change the endpoints before saves on the client side so that each controller in my webapi has a SaveChanges() method. I then call into the appropriate repository to process the backend functions as needed. This way I can run mock code or actual SQL changes depending on the repo injected.
If their are errors on the Processing of the form then we cast our custom List list to a List and assign it to the Errors property of the SaveResult. If there are no errors we send back the new key mappings to be updated on the client.
Ideally I want to reduce all the code in this controller and perhaps abstract it out to a utility method so there is less repeat in every controller. I like this method because then I can create normal repositories and not have them depend on a ContextProvider. Breeze independent at that point.
Upvotes: 1
Reputation: 17863
@jaq316 is correct: a custom EFContextProvider
is the place to intercept changes coming from the client. It is the place to both authorize and validate them . The documentation has more details. The essence of it is that you scrutinize the proposed changes within your overrides of the BeforeSaveEntity
and BeforeSaveEntities
virtual methods; alternatively you can attach handlers to the BeforeSaveEntityDelegate
and BeforeSaveEntitiesDelegate
.
Upvotes: 2
Reputation: 4290
This should be done by implementing a custom EFContextProvider
.
The code below implements a custom EFContextProvider
for the Northwind database and was taken directly from the documentation on the breeze.com website .
public class NorthwindContextProvider: EFContextProvider<NorthwindIBContext> { public NorthwindContextProvider() : base() { } protected override bool BeforeSaveEntity(EntityInfo entityInfo) { // return false if we don’t want the entity saved. // prohibit any additions of entities of type 'Role' if (entityInfo.Entity.GetType() == typeof(Role) && entityInfo.EntityState == EntityState.Added) { 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; } }
Upvotes: 2