1

Is there an elegant way to save an entity without saving it's navigation properties?

I send an entity graph from web page to a Web API service. Since client doesn't have access to navigation properties, it sends data without those properties back to server. Once I mark the entity as State.Modified, EF attempts to save relations also.

I'm going to try out following workflow, but don't like it very much:

  1. Load original entity from database
  2. Create generic method to copy all non-navigation properties from modified to original entity
  3. Properties with name ending in Id will not be copied over (since I have foreign-keys mapped also)
  4. Save such entity

This is not very elegant. How would you do it?

Nikola Radosavljević
  • 6,871
  • 32
  • 44
  • What about a Mapping library, like AutoMapper, which will map, by default, only common properties ? You can also create general rules (think you could do something with "don't take properties ending in Id"). – Raphaël Althaus Mar 07 '14 at 13:57
  • This is the current idea. What I was trying to ask is, is there a way to tell EF not to save them, or let EF handle this otherwise. If there's not, you kind of gave me the answer. I'll wait a bit more to see if there are other ideas – Nikola Radosavljević Mar 07 '14 at 14:07

1 Answers1

0

This is not very elegant. How would you do it?

I would not write something that automagically works in most cases. I don't like automapping either.

So I'll end up doing things manually:

public User UpdateUser(User updatedUser)
{
    // Gets the original entity
    var dbUser = this.context.Users.SingleOrDefault(z => z.Id == updatedUser.Id);
    if (dbUser == null)
    {
        // Exception here
    }

    // Manually update ONLY properties that can be updated through UpdateUser
    dbUser.Name = updatedUser.Name;
    dbUser.FirstName = updatedUser.FirstName;
    ...

    // Saves the context
    this.context.SaveChanges();

    // Returns updated entity
    return dbUser;
}

Much more secure to not rely on the layer that calls your service OR a rule based on the property name, to ensure a property can be updated or not.

For example, in the above case, one can assume you don't want to update the User.PasswordHash property in case you have a dedicated ChangePassword service.

ken2k
  • 48,145
  • 10
  • 116
  • 176
  • I don't see anything magic in automapper, and don't like to write 20 lines of codes when I can write one. But that's just another point of view ;) – Raphaël Althaus Mar 07 '14 at 14:36
  • @RaphaëlAlthaus Yes, there's nothing magic, but there's too much abstraction of what's internally done, especially when you add specific mapping rules. Other issue is that it's really easy to make mistakes. Imagine the case you add a new property called `UserCreationDate` to the `User` entity. You really don't want to update it in `UpdateUser` service. It'll be the case because of automapper if you don't tell him to do otherwise. I prefer adding a piece of code that does _something new_, instead of adding a piece of code _that prevents another piece of code of doing something_. – ken2k Mar 07 '14 at 14:52
  • I agree in principle Ken, however I think that's one time responsibility for lead engineer and releases others of the responsibility. I'm aware that I shouldn't allow anyone to inject values into properties or related entities which are off limits, so I wonder what you think about what I've done currently. I've a method with following signature `void ApplyDbGraphChanges(entity)` which applies changes to given entity and traverses navigation properties that match provided types (`T1`, `T2`, ...). Of course, also have non-generic method and method with less generic params. – Nikola Radosavljević Mar 07 '14 at 15:48