如何在.NET Core 3.1中结合OData 4和Automapper

问题描述

我尝试在.NET Core 3.1中结合使用Automapper和OData,但现在已经有一段时间了。我的OData控制器通过这种方式像超级按钮一样工作:

[ODataRoute]
[EnableQuery]
public IQueryable<Unit> Get()
{
    return _context.Units;
}

_context是我的EF DbContext

但是,我需要公开UnitDto而不是Unit

到目前为止,我尝试过的是:

[ODataRoute]
[EnableQuery]
public IQueryable<UnitDto> Get()
{
    var units = _context.Units;
    return units.ProjectTo<UnitDto>(_mapper.ConfigurationProvider);
}

但这导致空引用异常:

System.NullReferenceException:对象引用未设置为对象的实例。

在lambda_method处(Closure,QueryContext,DbDataReader,ResultContext,Int32 [],ResultCoordinator)
在Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable 1.AsyncEnumerator.MoveNextAsync() at Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.ReadInternal[T](Object value) at Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.ReadInternal[T](Object value) at Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsyncEnumerable(ActionContext context,ObjectResult result,Object asyncEnumerable,Func 2阅读器处)

使用GetQueryAsync中的Automapper.Aspnetcore.Odata.EFCore会导致相同的异常:

[HttpGet]
public async Task<IActionResult> GetAll(ODataQueryOptions<UnitDto> options)
{
    return Ok(await _context.Units.GetQueryAsync(_mapper,options));
}

关于如何实现这一目标的任何想法?这是要走的路,我做错什么了吗?还是应该朝另一个方向走?

解决方法

最后,我找到了解决方法。因此,假设有更多的人遇到这个问题,这就是我要做的事情。

第一件事是对数据库对象执行查询。然后获取它们的结果(使用JSON(反)序列化,不知道一种更好的方法)并将它们映射到DTO对象。

[ODataRoute]
[EnableCustomQuery]
public IEnumerable<UnitDto> Get(ODataQueryOptions<Unit> opts)
{
       var queryResult = opts.ApplyTo(_context.Units);

       var units = _mapper.Map<List<Unit>>(JsonConvert.DeserializeObject<List<UnitOData>>(JsonConvert.SerializeObject(queryResult)));

       return _mapper.Map<List<UnitDto>>(units);
 }

我想使用所有查询选项,包括$expand$filter$skip$top$count。但是查询应该在数据库对象上,而在DTO对象上查询。因此,我将其链接到我的EDM模型中,如下所示:

var unitsConfig = builder.EntitySet<Unit>("UnitsWithoutDto");
unitsConfig.EntityType.Property(p => p.InternalReference).Name = "ExternalReference";

反序列化的结果现在将是具有特定属性的数据库对象,当该特定对象被过滤时,该属性被更改。因此,我创建了一个新的UnitOData

public class UnitOData : Unit
{
    public string DisplayName { get; set; }
}

根据特定属性是否存在将其映射到原始对象。

CreateMap<UnitOData,Unit>()
     .ForMember(a => a.InternalReference,opt => opt.MapFrom(src => src.ExternalReference ?? src.InternalReference));

然后一切正常,请期待分页。因此,创建了一个自定义过滤器,该过滤器将忽略$skip$top查询选项。

public class EnableCustomQueryAttribute : EnableQueryAttribute
{
    public override IQueryable ApplyQuery(IQueryable queryable,ODataQueryOptions queryOptions)
    {
        var ignoreQueryOptions = AllowedQueryOptions.Skip | AllowedQueryOptions.Top;
        return queryOptions.ApplyTo(queryable,ignoreQueryOptions);
    }
}

如果有真正的解决方案,请分享。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...