我正在将 OData 与 Entity Framework 结合使用。假设我有以下模型和 Controller 方法:
public class Model1
{
public int Id { get; set; }
public int Field2 { get; set; }
public int FieldFromOtherService { get; set; }
public Model2 Model2 { get; set; } // Navigation Property
public int Model2Id { get; set; }
}
public class Model2
{
public int Id { get; set; }
public int Field { get; set; }
}
[HttpGet, EnableQuery]
public IQueryable<Model1> Get()
{
return modelRepository.List();
}
Model1
具有不是从数据库获取的属性 FieldFromOtherService
- 它是从其他服务检索的。在应用 OData top、skip、expand 和 select 子句后,我需要一种方法来填充此属性。
有办法实现吗?我尝试对 IQueryable
进行包装并在评估后调用操作,但当查询更复杂时它会崩溃。
最佳答案
最后,我在 @zaitsman 的建议下成功实现了我的目标。这比我想象的更难,因为 OData 添加了不可访问的包装器(类 SelectAllAndExpand
、SelectAll
、SelectSomeAndInheritance
、SelectSome
)。当使用expand时,需要从wrapper中提取DTO。我的代码看起来或多或少像这样:
[HttpGet]
public IHttpActionResult Get(ODataQueryOptions<Model1> options)
{
var result = modelRepository.List();
Action<ICollection<Model1>> postAction = collection => { Console.WriteLine("Post Action"); };
return ApplyOdataOptionsAndCallPostAction(result, options, postAction);
}
private IHttpActionResult ApplyOdataOptionsAndCallPostAction<T>(
IQueryable<T> baseQuery,
ODataQueryOptions<T> options,
Action<ICollection<T>> postAction)
where T : class
{
var queryable = options.ApplyTo(baseQuery);
var itemType = queryable.GetType().GetGenericArguments().First();
var evaluatedQuery = ToTypedList(queryable, itemType);
var dtos = ExtractAllDtoObjects<T>(evaluatedQuery).ToList();
postAction(dtos)
return Ok(evaluatedQuery, evaluatedQuery.GetType());
}
private static IList ToTypedList(IEnumerable self, Type innerType)
{
var methodInfo = typeof(Enumerable).GetMethod(nameof(Enumerable.ToList));
var genericMethod = methodInfo.MakeGenericMethod(innerType);
return genericMethod.Invoke(null, new object[]
{
self
}) as IList;
}
private IEnumerable<T> ExtractAllDtoObjects<T>(IEnumerable enumerable)
where T : class
{
foreach (var item in enumerable)
{
if (item is T typetItem)
{
yield return typetItem;
}
else
{
var result = TryExtractTFromWrapper<T>(item);
if (result != null)
{
yield return result;
}
}
}
}
private static T TryExtractTFromWrapper<T>(object item)
where T : class
{
if (item is ISelectExpandWrapper wrapper)
{
var property = item.GetType().GetProperty("Instance");
var instance = property.GetValue(item);
if (instance is T val)
{
return val;
}
}
return null;
}
private IHttpActionResult Ok(object content, Type type)
{
var resultType = typeof(OkNegotiatedContentResult<>).MakeGenericType(type);
return Activator.CreateInstance(resultType, content, this) as IHttpActionResult;
}
关于c# - 在 OData 中评估 IQueryable 后如何填充模型中的某些属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47094145/