c# - 在 OData 中评估 IQueryable 后如何填充模型中的某些属性?

标签 c# .net entity-framework asp.net-web-api odata

我正在将 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 添加了不可访问的包装器(类 SelectAllAndExpandSelectAllSelectSomeAndInheritanceSelectSome)。当使用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/

相关文章:

c# - 如何使用 WriteEvent 将事件记录为 Critical

c# - PrimaryLanguageOverride 在发布 appxbundle 中不起作用

c# - 使用 Reactive Extensions (Rx) 进行套接字编程实用吗?

.net - 如何将图像 MouseDown 事件与 RoutedUICommand 绑定(bind)

.NET:从 LINQ to SQL 到 Entity Framework 的转换

C# 正则表达式匹配与拆分相同字符串

c# - 为什么设置 MenuItem.InputGestureText 不会在我执行输入手势时激活 MenuItem?

c# - 以编程方式将 Entity Framework 数据库重置为 $InitialDatabase

c# - 将关系数据库存储为 XML

c# - .NET 项目的最佳开源项目托管站点