【问题标题】:EF Reference gets lost when Web API returns IQueryableWeb API 返回 IQueryable 时,EF 参考丢失
【发布时间】:2013-04-01 00:32:28
【问题描述】:

我在这个问题上已经找了很长时间了。

这是交易。 我正在构建一个调用 Web API 以获取其数据的网站。我的 Web API 使用一个库,使用存储库模式。我的数据库模型(EF 模型优先)是在库中构建的。在那个模型中,我有一个基类 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/passes/all”中可用。

这是奇怪的部分。为了测试我的分页,我每页调用 1 pass。 对于我的第一个通行证,它工作正常。但是当我转到我的第二页时,他也获得了正确的通行证,但是对 User 的引用消失了。

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

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

但是,当我调用 'api/v1/passes/all?$top=2' 时,会加载第二遍的用户。 所以这就是我的想法得到吹嘘的地方!我不明白?为什么用户参考不与第二个一起出现?会不会和 EF 的延迟加载功能有关?

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

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

这是因为作为 dbset 传递,包含 CustomerCard 以及 Voucher。有没有办法告诉我的上下文期望或将其转换为 CustomerCard?

谁能帮帮我。如果您不明白我的问题,请尽管问!

谢谢!

编辑 2

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

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

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

【问题讨论】:

  • 能否为方法“all”添加控制器代码?
  • 第二个代码块是控制器代码。路由 'api/{version}/passes/all' 路由到 action = "GetAllPasses"。

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


【解决方案1】:

是的,您需要确保在查询时包含所有相关记录。有关一些示例,请参阅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);
}

【讨论】:

  • 这也是我最初的解决方案。问题是我的 EF 模型只创建了一个数据库集。 Users-property 仅适用于子类型 CustomerCard(在我的数据库 [dbo].[Passes_CustomerCard] 中生成的单独表。因此,当我想将您建议的方法与 Include 一起使用时,我收到错误消息说 User 不是属性。这是我最大的问题。我可以告诉我的上下文它需要从 CustomerCard 或其他东西中获取它吗?
  • 谢谢,我早上第一件事就试试!我会告诉你的
  • OfType() 比我最初的 for 循环更好。所以谢谢你。但我似乎无法在它之后使用 Include(r => r.User) 方法。 Include 只是 DbSet 的扩展。此外, Include 仅将字符串作为路径。但这不是问题。我当前的代码是 return context.Passes.Include("User").OfType();它给了我正确类型的通行证,但第二个中仍然没有用户。这是一个奇怪的问题。谢谢你的回答
  • 你有没有把Include放在OfType之后?只是好奇。
  • 是的,当我将 Include 放在 OfType 之后时,我可以包含子属性。我已经让它工作了。每次我使用我的上下文时,我都会打开 ProxyCreationEnabled(将其设置为 False)。然后在我的库中添加了适当的包含语句。现在我没有问题了。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-23
  • 2020-05-12
  • 2013-02-23
  • 1970-01-01
  • 2016-11-19
  • 2011-05-04
  • 2014-01-02
相关资源
最近更新 更多