如何支持OData查询语法但返回非Edm模型

Luk*_*ett 4 entity-framework odata

将我的EF模型暴露给API似乎总是错误的.我希望我的API将自定义实体模型返回给调用者,但在后面使用EF.

所以我可能有PersonRestEntity一个CRUD操作的控制器和一个Person后面的EF代码优先实体和映射值.

当我这样做时,我不能再~/people?$top=10在URL中使用以下内容等

[EnableQuery]
public IQueryable<Person> Get(ODataQueryOptions<Person> query) { ... }
Run Code Online (Sandbox Code Playgroud)

因为那暴露了Person哪些是私有数据库实现.

我怎么能吃蛋糕吃呢?

Luk*_*ett 9

我找到了一个方法.诀窍不仅仅是IQueryable从控制器返回,因为您需要首先实现查询.这并不意味着物化整套到RAM中,查询仍然在数据库中运行,但明确将查询和物化可以此后返回映射实体的结果.

定义此操作,指定DbSet实体类型:

public async Task<HttpResponseMessage> Get(ODataQueryOptions<Person> oDataQuery)
Run Code Online (Sandbox Code Playgroud)

然后手动应用查询,DbSet<Person>以便:

var queryable = oDataQuery.ApplyTo(queryableDbSet);
Run Code Online (Sandbox Code Playgroud)

然后使用以下命令运行查询并将结果转换为您公开公开的实体集合:

var list = await queryable.ToListAsync(cancellationToken);
return list
    .OfType<Person>()
    .Select(p => MyEntityMapper.MapToRestEntity(p));
Run Code Online (Sandbox Code Playgroud)

然后您可以HttpResponseMessage正常返回列表.

就是这样,虽然显然实体之间的属性名称不匹配或在任何一个类上都不存在,但是会出现一些问题,因此最好确保要包含在查询选项中的属性在两个实体.

另外,我猜你可以选择不支持过滤器,只允许$top$skip自己强加默认订单.这可以像这样来实现,确保订购可查询第一,然后跳过,然后顶部.就像是:

IQueryable queryable = people
    .GetQueryable(operationContext)
    .OrderBy(r => r.Name);

if (oDataQuery.Skip != null)
    queryable = oDataQuery.Skip.ApplyTo(queryable, new System.Web.OData.Query.ODataQuerySettings());                

if (oDataQuery.Top != null)
    queryable = oDataQuery.Top.ApplyTo(queryable, new System.Web.OData.Query.ODataQuerySettings());

var list = await queryable.ToListAsync(operationContext.CreateToken());

return list
    .OfType<Person>()
    .Select(i => this.BuildPersonEntity(i));
Run Code Online (Sandbox Code Playgroud)

更多信息:

如果单纯使用非泛型ODataQueryOptions

无法创建EDM模型,因为控制器上的'Get'操作'People'有一个返回类型'System.Net.Http.HttpResponseMessage',它不实现IEnumerable

并且在不同情况下会发生其他错误.