c# - 如何使用 AutoMapper 和 OData 扩展子级别?

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

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

它与 url 一起使用:

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; }
    [...]
}

我希望你能帮助我。

最佳答案

我这样做的方式是,我使用了 .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 我在应用程序启动时设置,这里我将可扩展属性设置为“显式扩展”模式:

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

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

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

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

编辑:再看一遍,如果你想扩展 myEntity.myChildElement.mySubChildElement 你传递的 string[] 'requestedExpands' AutoMapper 必须包含 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];
    }
}

关于c# - 如何使用 AutoMapper 和 OData 扩展子级别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42394599/

相关文章:

c# - 获取当前操作的完整路径

c# - 如何使用日期范围获取目录中的文件?

c# - 自进程的重复控制台输出

javascript - 如何将值从 javascript 发送到服务器端 (asp.net)?

mysql - 'Project3.x' 中的 EF OData MySql 未知列 'where clause'

c# - Entity Framework 连接字符串的奇怪行为

c# - 如何向工厂模式添加新的派生类型?

javascript - 默认关闭 Div

asp.net - 处理大量后置变量 ASP.Net

c# - 即使定义了其他主键, Entity Framework 6 也会创建 Id 列