如何在 .NET Core 3.1 中结合 OData 4 和 Automapper [英] How to combine OData 4 and Automapper in .NET Core 3.1

查看:37
本文介绍了如何在 .NET Core 3.1 中结合 OData 4 和 Automapper的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试在 .NET Core 3.1 中结合 Automapper 和 OData,但我已经卡住了一段时间.我的 OData 控制器以这种方式工作,就像一个魅力:

I try to combine Automapper and OData in .NET Core 3.1, but I have been stuck for a while now. My OData controller works like a charm this way:

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

_context 是我的 EF DbContext.

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

However, I need to expose a UnitDto instead of Unit.

到目前为止我尝试的是:

What I tried so far is:

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

但这会导致空引用异常:

But that results into a null reference exception:

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

System.NullReferenceException: Object reference not set to an instance of an object.

在 lambda_method(Closure , QueryContext , DbDataReader , ResultContext , Int32[] , ResultCoordinator)
在 Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.AsyncEnumerator.MoveNextAsync() 在 Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.ReadInternal[T](对象值)在 Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.ReadInternal[T](Object value) at Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsyncEnumerable(ActionContext context, ObjectResult result, Object asyncEnumerable, Func2 reader)

at lambda_method(Closure , QueryContext , DbDataReader , ResultContext , Int32[] , ResultCoordinator)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.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, Func2 reader)
etc.

使用 Automapper.Aspnetcore.Odata.EFCoreGetQueryAsync 会导致相同的异常:

Using GetQueryAsync of Automapper.Aspnetcore.Odata.EFCore results in the same exception:

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

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

Any ideas on how to achieve this? Is this the way to go and am I doing something wrong or should I go another direction?

推荐答案

最后我找到了解决方法.因此,假设有更多人遇到此问题,这就是我为使其发挥作用所做的工作.

At the end I was able to find a workaround. So assuming more people have this issue, here is what I did to make it work.

首先是对数据库对象执行查询.然后得到它们的结果(使用JSON(反)序列化,不知道更好的方法)并将它们映射到DTO对象.

The first thing is to execute the query on the database objects. Then get their results (using JSON (de)serialization, did not know a better way) and map them to the DTO object.

[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 模型中,如下所示:

The thing is I want to use all queryoptions including $expand, $filter, $skip, $top and $count. But the query should be on the database object whereas it is queried upon the DTO objects. Therefore I linked it in my EDM model like this:

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

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

The result of the deserialize will now be the database object with one property changed when that specific one is filtered upon. Therefore I created a new UnitOData.

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

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

And mapped it to the original object depending on whether the specific property exists.

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

然后一切正常,除了分页.因此,创建了一个忽略 $skip$top 查询选项的自定义过滤器.

Then everything works fine expect the paging. So a created a custom filter that ignores the $skip and the $top query options.

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

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

Please share if there is a real solution.

这篇关于如何在 .NET Core 3.1 中结合 OData 4 和 Automapper的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆