当Web API返回IQueryable时,EF Reference会丢失

时间:2013-04-09 14:12:29

标签: asp.net-mvc entity-framework asp.net-web-api lazy-loading iqueryable

我一直在寻找这个问题。

这是交易。 我正在构建一个调用Web API来获取其数据的网站。我的Web API使用库,使用存储库模式。我的数据库模型(EF Model-first)是在库中构建的。在那个模型中,我有一个基类Pass。然后我有两个派生类,CustomerCard:Pass和Voucher:Pass。 My model from EF Designer

我有一种获取所有CustomerCards的方法。

public IQueryable<CustomerCard> GetAllPasses() {
        IList<CustomerCard> allCards = new List<CustomerCard>();

        var c_cards = context.Passes;
        foreach (var c_card in c_cards) {
            if (c_card is CustomerCard) {
                allCards.Add((CustomerCard)c_card);
            }
        }
        return allCards.AsQueryable<CustomerCard>();
    }

在我的ApiController中,我使用此方法获取传递并将其返回到网站,如下所示:

    [HttpGet]
    [Queryable]
    public IQueryable<CustomerCard> GetAllPasses(string version) {
        return passRepo.GetAllPasses().AsQueryable();
    }

我的Web API返回JSON格式。这是我的配置,以保留引用和东西:

        var json = config.Formatters.JsonFormatter;
        json.SerializerSettings.PreserveReferencesHandling =
            Newtonsoft.Json.PreserveReferencesHandling.Objects;
        json.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
        config.Formatters.Remove(config.Formatters.XmlFormatter);

我正在使用IQueryable,因为我希望能够在我的网站上分页数据。 api方法可在'/ api / v1 / pass / all'获得。

这是奇怪的部分。为了测试我的分页,我每页调用1次。 对于我的第一张通行证,它运作正常。但是当我进入第二页时,他也获得了正确的传递,但是对用户的引用已经消失。

正如您在my model中看到的那样,CustomerCard类具有属性User。这表明谁拥有客户卡。

所以这个调用会从传递中加载用户:'api / v1 / pass / all?$ top = 1' 但是当我调用这个时,用户实例是 NULL :'api / v1 / pass / all?$ top = 1&amp; $ skip = 1'。

然而,当我打电话给'api / v1 / pass / all?$ top = 2'时,第二遍的用户IS被加载。 所以这就是我的思绪被炸毁的地方!我不明白吗?为什么用户引用不与第二个一起出现?它可能与EF的延迟加载功能有关吗?

EDIT 当我在context.passes上使用扩展方法Include时,会抛出错误:

  

指定的包含路径无效。 EntityType'LCS_Model.Pass'   不声明名为“User”的导航属性。

这是因为Passes作为dbset,包含CustomerCard和Voucher。有没有办法可以告诉我的上下文预期或将其转换为CustomerCard?

有人可以帮助我。如果你不明白我的问题,请求离开!

全部谢谢!

编辑2

我的API控制器上的方法现在是

[HttpGet]
[Queryable]
public IQueryable<CustomerCard> GetAllPasses(string version) {
     return context.Passes.Include("User").OfType<CustomerCard>();
}

这给了我正确的物品。我的数据库中有2张客户卡。两者都来自同一个用户。我的API仍然加载了用户。当我的网站收到响应时,User属性变为null。我的猜测是因为它仍然从数组的第一个元素引用同一个用户。那可能吗?如果是的话,我该如何防止这种情况发生?

1 个答案:

答案 0 :(得分:2)

是的,您需要确保在进行查询时包含任何相关记录。有关示例,请参阅this。其次......我无法理解你为什么要使用for循环完成所有工作...如果你想做任何分页,那么服务器要做很多绝对不必要和浪费的工作。我想,除了你可能想要应用的任何其他过滤器之外,你的GetAllPasses看起来应该是这样的。

public IQueryable<CustomerCard> GetAllPasses() {
    return context.Passes.Include(r => r.User);
}

编辑(2):我需要更好地阅读。我必须承认,我不熟悉EF中的类型继承。我发现了一些可能在这里工作的内容:table per hierarchytable per concrete type,另请参阅MSDN Queryable.OfType<TResult>。这是一个猜测,但让我们试试:

public IQueryable<CustomerCard> GetAllPasses() {
    return context.Passes.OfType<CustomerCard>().Include(r => r.User);
}