包含属性但排除其中一个属性的属性

时间:2016-08-07 15:43:40

标签: c# entity-framework asp.net-web-api2

让我说我的一个控制器中有这样的方法:

[Route("api/Products")]
public IQueryable<Product> GetProducts() {
    return db.Products
             .Include(p => p.Category);
}

使用此功能,我可以从数据库中获取产品并包含其Category属性。

在我的CategoryController中我有这个方法:

[Route("api/Categories")]
public IQueryable<Category> GetCategories() {
    return db.Categories
             .Include(c => c.Parent)
             .Include(c => c.Products)
             .Include(c => c.SubCategories);
}

当我向CategoryController发送GET请求时,它按预期工作,我得到类别,其父级,产品和子类别。但是,当我向ProductController发送GET请求时,我并不想将所有产品都包含在所请求产品的类别中,我只需要有关该类别的基本信息。

那么,我如何让GetProducts()返回数据库中的产品,包括每个产品的Category属性,但不包括该类别的Products列表属性,仍然保留其他属性,如id,title等?

谢谢。

1 个答案:

答案 0 :(得分:9)

如评论中所述,第一步是禁用延迟加载。您可以通过从集合属性中删除virtual修饰符来执行此操作,这是永久性的,或者通过每个上下文实例禁用它,这是临时的:

context.Configuration.ProxyCreationEnabled = false;

(禁用代理创建也会禁用延迟加载,但会使生成的对象保持更轻量级。)

在断开连接的场景中,例如Web AIP2(正如您最初标记问题的那样),人们通常更喜欢默认禁用延迟加载,因为此序列化器 - 延迟加载级联。

但是,您无法阻止Entity Framework执行关系修正 。加载Product会将其附加到上下文中。 Include() - 其类别会将那些附加到上下文中,并且EF会使用附加的产品填充其Products个集合,无论您是否喜欢。

通过使用AsNoTracking获取产品(可防止实体附加,即更改跟踪),您可以在某种程度上减少此影响:

return db.Products.AsNoTracking()
         .Include(p => p.Category);

现在,类别只会将Products填充为Product的类别。

顺便说一下,在断开连接的场景中,首选使用AsNoTracking也是首选。无论如何,这些实体都不会被相同的上下文实例保存,并且会提高性能。

解决方案

  • 返回DTO,而非实体类型

通过使用DTO对象,您可以完全控制将要序列化的对象图。懒人装载让你大吃一惊。但是,所需的DTO课程数量可能是压倒性的。

  • 返回匿名类型。

这会引起一些人的注意,因为我们永远不应该从方法中返回匿名类型,对吧?好吧,他们将一个动作方法作为Json字符串,就像命名类型一样,并且javascript客户端不知道区别。你可能会说它只会使弱类型的javascript环境更近一步。唯一的事情是命名的DTO类型用作数据契约(各种类型),匿名类型可以(太)轻松地更改并破坏客户端代码。但我们总是对所有东西进行单元测试,对吗?嗯,它是小型开发团队的可行选择。

  • 调整序列化程序。

您可以告诉Json.Net序列化程序忽略引用循环。直接使用JsonConvert,它看起来像这样:

var products = db.Products.AsNoTracking().Include(p => p.Category);
var setting = new JsonSerializerSettings
{
    Formatting = Newtonsoft.Json.Formatting.Indented, // Just for humans
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
var json = JsonConvert.SerializeObject(products, setting);

AsNoTracking()结合使用时,这将使用空Products数组("Products": [])序列化类别,因为Product - Category - Product是参考循环。

在Web API中,有几种方法可以配置内置的Json.Net序列化程序,您可能希望这样做per action method

就个人而言,我更喜欢使用DTO。我喜欢控制(也是通过电线的属性)我并不特别喜欢依靠序列化器为我解决我忽略的事情。