How do we load related objects (Eager Loading) in

2019-05-08 18:34发布

If I have following model structure

public class QuestionItem: EntityData
{
    public string Content { get; set; }
    public bool IsAnswered { get; set; }
    public int NumberOfAnswers
    {
        //todo: make it computable
        get;
        set;
    }
    public UserItem By { get; set; }
    public string ById { get; set; }
    public string AtLocation { get; set; }
}

& parent entity as

public class UserItem:EntityData
{

    public string UserName { get; set; }

    public string Gender { get; set; }

    public string BaseLocation { get; set; }

    public int Age { get; set; }

    public int Points { get; set; }

}

this does generate the DB Tables with proper FK relationships. but when querying Question Items list, the QuestionItem.By property (reference object to UserItem table) is null.

generally this is achieved by using an .Include method on query level, but i am not able to find where exactly i should do this.

any help is appreciated.

Supreet

2条回答
做个烂人
2楼-- · 2019-05-08 18:41

Have the GetAllQuestionItems method look like this:

public IList<QuestionItem> GetAllQuestionItems()
{
    return context.QuestionItems.Include( "UserItem" ).ToList();
}

This is an alternative to the $expand hack. I discovered it while exploring Azure App Service mobile apps for myself. Your original idea to solve it simply by chaining a call to Include would make more sense to me, if it worked. Next, I'd like to figure out why it doesn't work.

查看更多
Juvenile、少年°
3楼-- · 2019-05-08 18:51

As @JuneT mentioned, you need to send an $expand header from the client. The reason for that is that by default Entity Framework will not traverse object graphs, as this requires a join in the database and can have a negative performance impact if you don't need to do that.

Another alternative, which I mentioned in this blog post, is to use a filter in the server-side to add that header for you. For example, with this attribute 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;
    }
}

You can decorate your action to force the expansion of the related entity.

[ExpandProperty("By")]
public IQueryable<QuestionItem> GetAll() {
    return base.Query();
}
查看更多
登录 后发表回答