Breeze.js mixing DTOs and entities
Asked Answered
I

1

4

In Ward's article "The Breeze Server: Have It Your Way":

The typical business application has a minimum of 200 domain model types. 90+% of the time the shape of the data I'm sending over the wire is the same as the shape of the entity in my business model.
...
When the shape of a client entity doesn't align well with the shape of a server-side business entity, I may switch to a DTO for that particular case.

This hits the nail right on the head for our application, but what's the best way to switch just some entities for DTOs?

For example, our User entity contains sensitive properties that should not be exposed to the client. It also has related data that gets pulled from other systems and returned to the client, which ideally should just be extra properties on the client-side User objects. User seems an ideal candidate for switching to a DTO.

If User were an isolated entity this might be easier, but the problem is that User is referenced basically everywhere in the model. Almost every entity has a CreatedBy property, for example.

Is there a way to switch the User entity for a User DTO everywhere in the model? For all the other entities in the model that reference users, we still need to be able to load them with their user properties expanded, to query them on those user properties, and to save them with changes to those user properties.

I'm not sure how to do this other than building a big DTO model that is 95% identical to the entity model, and have some mapping code/framework to go between them. But, as Ward says in this post, "I don't like DTOs for every type; that's overkill that can ruin productivity."

Ilyssa answered 28/3, 2014 at 10:49 Comment(1)
I'm interested in mixing dto and real entities. Do you have a working sample of that scenario ? In particular, how do you had dto's to the metadata ?Fugacious
P
5

You are in good company. Questions like this are stacking up. I hope to provide better guidance "soon".

In the short run (assuming you're a .NET developer), you may find some clues in the DocCode sample. Search for "ProductDto". DocCode doesn't show how you'd save changes to it so I'll have to hold that off until another time.

Your scenario may actually be easy to address.

Step #1: Use a custom DbContext

Start by writing a sub-class of your business model's DbContext. Add to this sub-class an override to your OnModelCreating and teach it to ignore the User properties that should not be part of the model.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<User>().Ignore(u => u.whatever);
   ...
   base.OnModelCreating(modelBuilder);
}

Now refer to THIS derived DbContext when communicating with clients.

Notice that this involves a very small amount of code and is easy to maintain. It doesn't interfere with your use of the base DbContext which retains full access to all properties of User.

Step #2: Configure JSON.NET to exclude these properties from serialization

Follow James Newton King's guidance. Look in particular at IContractResolver if you don't want to decorate/pollute your User class with the [JsonIgnore] attribute. James is the author of JSON.NET.

Peterson answered 28/3, 2014 at 18:6 Comment(4)
Saving changes is the interesting part, so "better guidance" would be very helpful!Roybn
Indeed! :-). In brief: (1) remove DTO from EntityInfos, (2) retrieve corresponding business model entity from Db (or create such an entity if this is an insert), (3) update this copy from DTO, (4) add this entity to the EntityInfos (don't forget the OriginalValues properties for an update), (5) rinse and repeat for all such DTOs, (6) let it go ... and EF will save it, (7) intercept the "after save" and remap the updated/inserted business entity into its DTO form in the SaveResult so that you send the DTO, not the "real" entity, back to the client. Devil in details I'm sure.Peterson
@Peterson -Will this work for EF setup with Database First? The method is stubbed in with an override with: throw new UnintentionalCodeFirstException();Beatrix
@Beatrix - Did you ever get around with the issue with the UnintentionalCodeFirstException ?Downing

© 2022 - 2024 — McMap. All rights reserved.