如何使用AutoMapper和OData扩展子级别的子级别?

问题描述 投票:2回答:1

我目前正在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());
}

这是我的TPoco模型的一个例子(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; }
    [...]
}

我希望你能帮助我。

c# asp.net entity-framework-6 odata automapper
1个回答
3
投票

我这样做的方式,我使用.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是Entity Framework POCO类。

我在应用程序启动时设置了MapConfig,在这里我将可扩展属性设置为'Explicit expand'模式:

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

正如我在评论中所说,我通过在网址中嵌套$expand=来请求数据:

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

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

编辑:再看一遍,如果你想扩展myEntity.myChildElement.mySubChildElement你传递给AutoMapper的string[]'requiredExpands'必须包含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];
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.