包含属性但排除该属性的属性之一

tob*_*oef 10 c# entity-framework asp.net-web-api2

假设我的一个控制器中有这样的方法:

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

使用这个我可以从数据库中获取产品并包含其Category属性.

在我的CategoryControllerI中有这个方法:

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

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

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

谢谢.

Ger*_*old 10

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

context.Configuration.ProxyCreationEnabled = false;
Run Code Online (Sandbox Code Playgroud)

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

在断开连接的场景中,例如Web AIP2(就像你最初标记问题一样),人们通常更喜欢默认禁用延迟加载,因为这个序列化器 - 延迟加载级联.

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

您可以通过获取产品来减少此影响AsNoTracking(这可以防止实体附加,即更改跟踪):

return db.Products.AsNoTracking()
         .Include(p => p.Category);
Run Code Online (Sandbox Code Playgroud)

现在类别只会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);
Run Code Online (Sandbox Code Playgroud)

AsNoTracking()此相结合将使用空Products数组("Products": [])序列化类别,因为它Product - Category - Product是一个引用循环.

在Web API中,有几种方法可以配置内置的Json.Net序列化程序,您可能希望按操作方法执行此操作.

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