如何在.NET Core 3.1中结合OData 4和Automapper [英] How to combine OData 4 and Automapper in .NET Core 3.1
问题描述
我尝试在.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);
}
但这导致空引用异常:
System.NullReferenceException:对象引用未设置为对象的实例.
System.NullReferenceException: Object reference not set to an instance of an object.
在lambda_method处(Closure,QueryContext,DbDataReader,ResultContext,Int32 [],ResultCoordinator)
在Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.ReadInternal [T](Object value)在Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.在Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable 1.AsyncEnumerator.MoveNextAsync()在Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.ReadInternal [T](Object value)在Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsyncEnumerable(ActionContext上下文,ObjectResult结果,对象asyncEnumerable,Func
2阅读器)的ReadInternal [T](对象值)
等
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, Func
2 reader)
etc.
使用 Automapper.Aspnetcore.Odata.EFCore
的 GetQueryAsync
会导致相同的异常:
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屋!