【问题标题】:How to expand sub levels of childs with AutoMapper and OData?如何使用 AutoMapper 和 OData 扩展子级别?
【发布时间】:2017-02-22 14:42:30
【问题描述】:

我目前正在 asp.net 中开发一个 Web 服务项目。我尝试在检索数据的函数中包含子元素。

它适用于网址:

http://localhost:8080/myEntity/?$expand=myChildElement

但我似乎无法像这样包含以下级别的子元素:

http://localhost:8080/myEntity/?$expand=myChildElement/mySubChildElement

这是我的功能:

public virtual async Task<IHttpActionResult> GetElements(ODataQueryOptions<TEntity> queryOptions)
{
    IQueryable<TPoco> query = context.Set<TPoco>();

    string[] includes = null;
    string includeText = queryOptions.SelectExpand != null ? queryOptions.SelectExpand.RawExpand : null;
    if(! string.IsNullOrEmpty(includeText))
    {
        includes = queryOptions.SelectExpand.RawExpand.Split(',');
        return Ok(await query.ProjectTo<TEntity>(null, includes).ToListAsync());
    }

    return Ok(await query.ProjectTo<TEntity>().ToListAsync());
}

这是我的 TPoc​​o 模型的示例(myChildElement 可以是实体“Project”,mySubChildElement 可以是子实体“ProjectType”):

public class ContractEntity : BaseEntity
{
    public ContractEntity()
    {
        this.Addresses = new HashSet<AddressEntity>();
    }

    override public Guid Id { get; set; }
    override public string DisplayName { get { return No.ToString(); } set { } }
    override public string ClassType { get { return "Contract"; } set { } }
    override public string MarkerColor { get { return Colors.Green.ToString(); } set { } }
    public int? No { get; set; }
    public DateTime? OfficialDate { get; set; }
    public DateTime? ValidDate { get; set; }
    public DateTime? SignatureDate { get; set; }
    public Guid? ProjectId { get; set; }
    [...]

    public virtual ICollection<AccountingItemEntity> AccountingItems { get; set; }
    public virtual ICollection<TagEntity> Tags { get; set; }
    public virtual ProjectEntity Project { get; set; }
    [...]
}

希望你能帮助我。

【问题讨论】:

  • @ShaneRay:我认为不会。我了解 OData 系统,但我想要将其合并到我的函数 GetElements 中。
  • 我认为您可能需要将函数中的 / 替换为 .。这意味着您将 myChildElement.mySubChildElement 传递给 ProjectTo 方法。此外,它可能区分大小写,因此请确保您的大小写正确。
  • @ShaneRay:使用/ 我有消息:找到了一条遍历多个导航属性的路径。请改写查询,使每个展开路径仅包含类型段和导航属性。。对于.,我收到第二条消息:找不到名为 myChildElement.mySubChildElement 的属性
  • 您可以在问题中添加 TPoc​​o 模型吗?

标签: c# asp.net entity-framework-6 odata automapper


【解决方案1】:

我这样做的方式是,我使用 .ProjectTo 返回 IQueryable 的事实,因此我让 OData 框架通过不调用 ToListAsync() 我自己而是返回 IQueryable 来处理查询。

// In some class that is ': ODataController'
[EnableQuery] //Attribute makes sure the OData framework handles querying
public IQueryable<TDto> Get(ODataQueryOptions<TDto> options) {
    //The names of the properties requested by the client in the '$expand=' query option
    string[] requestedExpands = Helpers.Expand.GetMembersToExpandNames(options); // I made a helper for this, you can probably just use your code, or see my impementation below.

    var set = Db.Set<TEntity>().AsNoTracking(); //You might want to remove AsNoTracking if it doesn't work
    var converted = set.ProjectTo<TDto>(MapConfig, null, requestedExpands);
    return converted;
}

在此示例中,TDto 是我要发送给客户端的类型 TEntity 是实体框架 POCO 类。

我的MapConfig 我在应用程序启动时设置,这里我将可扩展属性设置为“显式扩展”模式:

MapConfig = new MapperConfiguration(cfg => {
    cfg.CreateMap<EFType, DTOType>(MemberList.Destination)
        .ForMember(c => c.ExpandableProperty, options => options.ExplicitExpansion());
});

正如我在 cmets 中所说,我通过在 URL 中嵌套 $expand= 来请求数据:

api/myEntity?$expand=myChildElement($expan‌​d=mySubChildElement)

这对我有用,希望你能复制成功。

编辑:再看一遍,如果你想扩展myEntity.myChildElement.mySubChildElement,你传递给AutoMapper的string[]'requestedExpands'必须包含1个条目:myChildElement.mySubChildElement。我为所有 3 个实体定义了一个映射,再次使用 ExplicitExpansion 选项。


根据@Tim Pohlmann 的要求更新我对GetMembersToExpandNames 的实现:

public static class Expand {

    public static string[] GetMembersToExpandNames(ODataQueryOptions options) {
        return GetExpandPropertyPaths(GetExpandItems(options?.SelectExpand?.SelectExpandClause)).ToArray();
    }

    private static IEnumerable<string> GetExpandPropertyPaths(IEnumerable<ExpandedNavigationSelectItem> items, string prefix = "") {
        foreach(var item in items) {
            foreach(var res in GetExpandPropertyPaths(item, prefix)) {
                yield return res;
            }
        }
    }

    private static IEnumerable<string> GetExpandPropertyPaths(ExpandedNavigationSelectItem item, string prefix = "") {
        var curPropName = item.NavigationSource.Name;
        var nestedExpand = GetExpandItems(item.SelectAndExpand).ToArray();
        if(nestedExpand.Count() > 0) {
            foreach(var res in GetExpandPropertyPaths(nestedExpand, $"{prefix}{curPropName}.")) {
                yield return res;
            }
        } else {
            yield return $"{prefix}{curPropName}";
        }
    }

    private static IEnumerable<ExpandedNavigationSelectItem> GetExpandItems(SelectExpandClause sec) {
        if(sec != null) {
            var res = sec?.SelectedItems?.OfType<ExpandedNavigationSelectItem>();
            if(res != null) {
                return res;
            }
        }
        return new ExpandedNavigationSelectItem[0];
    }
}

【讨论】:

  • GetMemebersToExpandNames 的实现会很有帮助。
  • @TimPohlmann,已经添加了我的实现,我已经有一段时间没有使用这个代码了,所以我不确定它是否会起作用,但这就是我使用的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-01
  • 2020-01-20
  • 2021-12-05
  • 1970-01-01
  • 2018-02-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多