Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
180 views
in Technique[技术] by (71.8m points)

breeze - Ignore a property from a code first generated entity

Is there a .Net attribute that will tell Breeze to completely ignore a property?

I know that making the property protected is one way to hide it from Breeze and prevent its serialization, but what if I would like it to remain public?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It is tricky to devise an easy, maintainable way to have EF-generated metadata say one thing to the client and another to EF itself.

But you can do it if you're willing to have two DbContexts: the "real" DbContext for server-side model operations and another DbContext strictly for client metadata generation.

A DbContext for Metadata

It isn't as difficult as it sounds. I experimented with such a thing in DocCode. I created a NorthwindMetadataContext that inherits from the NorthwindContext. It hides the disused CustomerID_OLD property.

Here is that DbContext in its entirety:

public class NorthwindMetadataContext : NorthwindContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Customer>().Ignore(t => t.CustomerID_OLD);
    }
}

You read that right! I simply add a single EF Fluent API instruction to hide the CustomerID_OLD property from the NorthwindMetadataContext.

It's still on the Customer type and it's still known to the base NorthwindContext. That means a query will return the CustomerID_OLDproperty when you query with the NorthwindContext but will not return the property when you query with the NorthwindMetadataContext.

Of course you only use the NorthwindMetadataContext to generate metadata for the Breeze client. All of your server logic continues to use the "real", base NorthwindContext

I make sure that's what happens by changing the way the NorthwindRepository implements the Metadata property:

public string Metadata
{
    get
    {
        var metaContextProvider = new EFContextProvider<NorthwindMetadataContext>();
        return metaContextProvider.Metadata();
    }
}

All other members of the NorthwindRepository use an EFContextProvider for the "real" DbContext:

private readonly EFContextProvider<NorthwindContext>
    _contextProvider = new EFContextProvider<NorthwindContext>();

This works like a charm. As far as the client know, the CustomerID_OLD property doesn't exist.

Try to keep the hidden data off the wire

Although you've hidden the property from Breeze clients, the property remains on the server-side entity type and, because we use the "real" DbContext for the query and save operations, you can see the CustomerID_OLD property going over the wire in the query results payload.

To keep that from happening, you can add the JSON.NET serializer [JsonIgnore] attribute to the Customer.CustomerID_OLD property in the model (you use other JSON.NET configuration options if you don't want to pollute your model with JSON.NET serialization attributes).

Run again and CustomerID_OLD is no longer serialized. The CustomerID_OLD property is now completely invisible to the client while still accessible in your model on the server.

BEWARE

The property that is hidden in metadata is hidden from Breeze clients ... but not from the world. Because you want that property available on the SERVER-SIDE TYPE, an evil client (not a Breeze client) can still GET that data with a projection even though you have hidden it when serializing the complete type.

The following query URL returns projection data that include the CustomerID_OLD that would be invisible if you queried for entire Customer objects.

http://localhost:47595/breeze/Northwind/Customers?$filter=startswith(CompanyName,'C') eq true&$select=CustomerID_OLD,CompanyName

Here is a bit of the result:

    {
        "$id": "1",
        "$type": "_IB_em9q7XTURqKf5bmIrAQD0bJ6f_po[[System.String, mscorlib],[System.String, mscorlib]], _IB_em9q7XTURqKf5bmIrAQD0bJ6f_po_IdeaBlade",
        "CustomerID_OLD": "CACTU",
        "CompanyName": "Cactus Comidas para llevar"
    },

Perhaps more seriously, a POST can update to that hidden property in the payload of a "SaveChanges" request.

As always with sensitive data, you must inspect every save request to make sure that the user is permitted to save each and every changed property value identified in the OriginalValues collection.

If you have security concerns, I feel it is much safer to use DTOs for types that carry data that must not be exposed to unauthorized clients. I don't like DTOs for every type; that's overkill that can ruin productivity. But I do like them for entity types with significant confidentiality requirements.

Example code

I have not yet decided if I will keep this example in the published DocCode or stash it in my private reserve. It is a cool trick.

My worry is that people will think this technique is a secure way to hide confidential data which it definitely is not. It's fine for hiding data that you'd rather keep hidden. But if it MUST be kept off the wire, you better use a DTO.

Let me know if what I've described is clear enough for you to proceed without a concrete example.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...