Loadupasync Navigation Properties for Azure Mobile Service

I have a Place

data_object on the service side that contains a navigation property Roads

:

public class Place : EntityData
{
    ...
    public List<Road> Roads { get; set; }
}

      

And now on the client side, I want to get the object Place

using its id, but the navigation property Roads

just won't load. Is there any parameter or attribute I can add to make it work?

My code for it:

var roadList = await App.MobileService.GetTable<Place>()
                .LookupAsync(placeId);

      

+3


source to share


1 answer


Since loading navigation properties into EF requires a JOIN operation on the database (which is expensive), they are not loaded by default, as you noticed. If you want them to be loaded, you need to request this from the client by sending a query string parameter $expand=<propertyName>

.

There are two ways to do this: on the server and on the client. If you want to do this on the server, you can implement an action filter that will modify the client request and add this query string parameter. You can do this using the filter below:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
class ExpandPropertyAttribute : ActionFilterAttribute
{
    string propertyName;

    public ExpandPropertyAttribute(string propertyName)
    {
        this.propertyName = propertyName;
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        base.OnActionExecuting(actionContext);
        var uriBuilder = new UriBuilder(actionContext.Request.RequestUri);
        var queryParams = uriBuilder.Query.TrimStart('?').Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries).ToList();
        int expandIndex = -1;
        for (var i = 0; i < queryParams.Count; i++)
        {
            if (queryParams[i].StartsWith("$expand", StringComparison.Ordinal))
            {
                expandIndex = i;
                break;
            }
        }

        if (expandIndex < 0)
        {
            queryParams.Add("$expand=" + this.propertyName);
        }
        else
        {
            queryParams[expandIndex] = queryParams[expandIndex] + "," + propertyName;
        }

        uriBuilder.Query = string.Join("&", queryParams);
        actionContext.Request.RequestUri = uriBuilder.Uri;
    }
}

      

And then you can decorate your method with this attribute:

[ExpandProperty("Roads")]
public SingleItem<Place> GetPlace(string id) {
    return base.Lookup(id);
}

      



Another way to implement this is to change the client side code to send this header. Currently, an overload LookupAsync

(and all other CRUD operations) that accepts additional query string parameters cannot be used to add a parameter $expand

(or any other parameter $-*

), so you need to use a handler for that. For example, this is one of these handlers:

class MyExpandPropertyHandler : DelegatingHandler
{
    string tableName
    string propertyName;

    public MyExpandPropertyHandler(string tableName, string propertyName)
    {
        this.tableName = tableName;
        this.propertyName = propertyName;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request.Method.Method == HttpMethod.Get.Method &&
            request.RequestUri.PathAndQuery.StartsWith("/tables/" + tableName, StringComparison.OrdinalIgnoreCase))
        {
            UriBuilder builder = new UriBuilder(request.RequestUri);
            string query = builder.Query;
            if (!query.Contains("$expand"))
            {
                if (string.IsNullOrEmpty(query))
                {
                    query = "";
                }
                else
                {
                    query = query + "&";
                }

                query = query + "$expand=" + propertyName;
                builder.Query = query.TrimStart('?');
                request.RequestUri = builder.Uri;
            }
        }

        return await base.SendAsync(request, cancellationToken);
        return result;
    }
}

      

And you have to use the handler by creating a new instance MobileServiceClient

:

var expandedClient = new MobileServiceClient(
    App.MobileService.ApplicationUrl,
    App.MobileService.ApplicationKey,
    new MyExpandPropertyHandler("Place", "Roads"));
var roadList = await App.MobileService.GetTable<Place>()
    .LookupAsync(placeId);

      

+5


source







All Articles