0

I was having the exact same problem as discussed in breezejs issues with the save bundle and the answer explains it great.

The problem I have is - my application is somewhat large, having about 20+ entities that we modify. Hence if I override the BeforeSaveEntity() within my datacontext and add all the business logic there it would be very cumbersome. We do have a clear separation of concerns as I have mentioned in the following question (mind the half-completed title): Is it a good practice to use multuple

So is there anyway I can do this in a more organized manner? I mean handling the BeforeSaveEntity for related entities in one single repository and likewise?

Community
  • 1
  • 1
devC
  • 1,384
  • 5
  • 32
  • 56

1 Answers1

2

Of course you can branch inside your BeforeSaveEntities method, as shown in the code of the answer you linked to above. Inside each if block, you could instantiate a helper class or repository to handle each entity type.

A more domain-based approach would be to have multiple subclasses of EFContextProvider<MyDbContext>. Each one would have its own BeforeSaveEntities method to handle its own domain business rules:

public class AccountManagementContextProvider : EFContextProvider<MyDbContext>
{
    Type[] allowedTypes = new Type[] { typeof(Account), typeof(AccountAddress) };

    protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
    {
        var illegalTypes = saveMap.Keys.Except(allowedTypes);
        if (illegalTypes.Any())
        {
            throw new ArgumentException("Attempt to save illegal entities");
        }

        // account management stuff...
    }
}

// in a separate file...
public class InventoryContextProvider : EFContextProvider<MyDbContext>
{
    protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
    {
        // inventory stuff...
    }
}        
// etc.

You can instantiate the appropriate ContextProvider instance in your controller methods:

[HttpPost]
public SaveResult SaveAccount(JObject saveBundle)
{
    var context = new AccountManagementContextProvider();
    return context.SaveChanges(saveBundle);
}

[HttpPost]
public SaveResult SaveInventory(JObject saveBundle)
{
    var context = new InventoryContextProvider();
    return context.SaveChanges(saveBundle);
}

... which you call from the client using named saves:

    var saveOptions = new breeze.SaveOptions({ resourceName: 'SaveInventory' });
    return manager.saveChanges(null, saveOptions);
Community
  • 1
  • 1
Steve Schmitt
  • 3,124
  • 12
  • 14
  • Thanks Steve. One other concern I have is that user may send a malicious savebundle by including an object of a type that's not allowed to be saved - for example a lookup entity. In that case how do we do the validation? I mean will we have to validate everything in the bundle by considering all entities in the datacontext? – devC Jan 28 '15 at 14:39
  • 1
    Right - `BeforeSaveEntities` should check the types in the `saveMap` to make sure the client is not sending anything illegal. I updated my answer to show an example of that in the `AccountManagementContextProvider`. You might need to go deeper, and make sure that the current user is only saving the individual entities that she is allowed to save. – Steve Schmitt Jan 28 '15 at 18:58
  • Thank you also @SteveSchmitt for your excellent answer. I was wondering if you could talk about how to achieve what you mentioned at the end of your last comment, "make sure that the current user is only saving the individual entities that she is allowed to save". For example, if `Account` had an `ApplicationUser Owner` that was the only user allowed to edit it. – bcstrawn Jun 09 '16 at 19:33
  • If you use the [Authorize] attribute in the Web API method, the User will be available. You could then pass the user in the constructor of your context provider, and apply business rules based on the User. – Steve Schmitt Jun 10 '16 at 05:19