简体   繁体   中英

Prevent Azure TableEntity property from being serialized in MVC 4 WebAPI

So I have a Model Subscription which inherits from Azure's TableEntity class for use in a WebApi Get method as follows:

[HttpGet]
public IEnumerable<Subscription> Subscribers()

In this method, I do a Select query on my subscribers table to find all subscribers, but I only want to return a few of the columns (properties) as follows:

var query = new TableQuery<Subscription>().Select(new string[] {
    "PartitionKey", 
    "RowKey", 
    "Description", 
    "Verified"
    });

The definition for the model is below:

public class Subscription : TableEntity
{
    [Required]
    [RegularExpression(@"[\w]+",
     ErrorMessage = @"Only alphanumeric characters and underscore (_) are allowed.")]
    [Display(Name = "Application Name")]
    public string ApplicationName
    {
        get
        {
            return this.PartitionKey;
        }
        set
        {
            this.PartitionKey = value;
        }
    }

    [Required]
    [RegularExpression(@"[\w]+",
     ErrorMessage = @"Only alphanumeric characters and underscore (_) are allowed.")]
    [Display(Name = "Log Name")]
    public string LogName
    {
        get
        {
            return this.RowKey;
        }
        set
        {
            this.RowKey = value;
        }
    }

    [Required]
    [EmailAddressAttribute]
    [Display(Name = "Email Address")]
    public string EmailAddress { get; set; }

    public string Description { get; set; }

    public string SubscriberGUID { get; set; }

    public bool? Verified { get; set; }
}

The following is the XML response of the API query:

<ArrayOfSubscription>
    <Subscription>
        <ETag>W/"datetime'2013-03-18T08%3A54%3A32.483Z'"</ETag>
        <PartitionKey>AppName1</PartitionKey><RowKey>Log1</RowKey>
        <Timestamp>
            <d3p1:DateTime>2013-03-18T08:54:32.483Z</d3p1:DateTime>
            <d3p1:OffsetMinutes>0</d3p1:OffsetMinutes>
        </Timestamp>
        <ApplicationName>AppName1</ApplicationName>
        <Description>Desc</Description>
        <EmailAddress i:nil="true"/>
        <LogName>Log1</LogName>
        <SubscriberGUID i:nil="true"/>
        <Verified>false</Verified>
    </Subscription>
</ArrayOfSubscription>

As you can see, the model not only has a few additional properties such as SubscriberGUID which I do not want to be serialized in the response (and since they are not in the select query, they are null anyway), but TableEntity itself has fields such as PartitionKey , RowKey , Etag , and Timestamp which are also being serialized.

How do I continue to use Azure tables but avoid serializing in the response these undesired fields I do not want the user to see.

Not disagreeing with the answer of using a specific DTO, but the Microsoft.WindowsAzure.Storage assembly now provides an attribute, the IgnorePropertyAttribute , that you can decorate your public property with to avoid serialization.

I haven't actually tried it yet but there is a method on TableEntity called ShouldSkipProperty() that checks a number of things before returning false (ie don't skip):

  • Is the Property Name one of "PartitionKey", "RowKey", "Timestamp" or "ETag" -> skip
  • Are EITHER of the getter and setter non-public -> skip
  • Is it static -> skip
  • Does the property have the attribute IgnorePropertyAttribute -> skip

Looks like it'll do the trick.

I would suggest using DTO (data transfer objects) to solve this type of issues. DTO's might mean more code (more classes) but would benefit you in the long term. You have much better control as to what would be put on the wire. They are better from a security standpoint too rather than using some serializer specific attributes to control what is being put on the wire.

Refer to this asp.net web API tutorial for more.

The use of the DTO is the way to go, IMHO, but to clarify, since it wasn't as obvious from the posts is where to implement to the DTO . I was hoping I could have just used it as part of the query, which I could not. Instead, I had to do this:

query.SelectColumns = new List<string> { "QuoteId", "RateId", "Date" };
var results = await MyCloudTable.ExecuteQuerySegmentedAsync(query, null);
return results.Select(d => new MyDto { QuoteId = d.QuoteId, RateId = d.RateId, Date = d.Date }).ToList();

You have to return your TableEntity derived object from your TableQuery, but since all the properties are null (from explicitly selecting the columns you want) there is no additional data on the wire. You then project into your DTO so you can return exactly the object you need.

You do not need to inherit from TableEntity class. You can use TableEntity.Flatten method to create a DynamicTableEntity from your Subscription class and write to table storage. And you can use TableEntity.ConvertBack method to recompose your subscription object when you read the DynamicTableEntity back from azure table storage. These static helper methods are available in Azure Table Storage SDK version >= 8.0.0

TableEntity.Flatten: https://msdn.microsoft.com/en-us/library/azure/mt775434.aspx TableEntity.ConvertBack: https://msdn.microsoft.com/en-us/library/azure/mt775432.aspx

Eliminating the need for you to further write up converter classes between DTO s and Business Data Models

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM