ASP.NetCore OData DTO $ expand导航ICollection属性导致ArgumentException

时间:2019-07-17 17:18:42

标签: odata automapper dto expand ef-core-2.2

我正在使用带有OData的Entity Framework从我的mysql数据库中获取数据,但是我不想向用户公开数据库实体,所以我创建了一些DTO并将其与Automapper映射。 我的问题是,除了使用$ expand(Collections)加载实体以外,其他所有东西都正常运行。

实体和DTO :(在我的项目中,dto和域模型看起来并不相同,这只是为了更好地阅读):

public partial class Product
{       

    public string Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public int CategoryId { get; set; }        

    public virtual Category Category { get; set; }     
    public virtual ICollection<ProductPrice> ProductPrices { get; set; }   
}

public class ProductDTO
{
    [Key]
    public string Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    [ForeignKey("Category")]
    public int CategoryId { get; set; }

    public virtual ICollection<ProductPriceDTO> ProductPrices { get; set; }
    public virtual CategoryDTO Category { get; set; }
}

public partial class Category
{     
    public int Id { get; set; }
    public string Title { get; set; }         
}

public class CategoryDTO
{
    [Key]
    public int Id { get; set; }
    public string Title { get; set; }
}

public partial class ProductPrice
{
    public string VendorId { get; set; }
    public string ProductId { get; set; }
    public decimal Price { get; set; }

    public virtual Product Product { get; set; }
    public virtual Vendor Vendor { get; set; }
}

public class ProductPriceDTO
{
    [Key]
    [ForeignKey("Vendor")]
    public string VendorId { get; set; }
    [Key]
    [ForeignKey("Product")]
    public string ProductId { get; set; }
    public decimal Price { get; set; }

    public virtual VendorDTO Vendor { get; set; }
    public virtual ProductDTO Product { get; set; }
}

通过以下方式创建模型:

 public IEdmModel GetEdmModel(IServiceProvider serviceProvider)
    {
        var builder = new ODataConventionModelBuilder(serviceProvider);
        builder.Namespace = "Functions";            
        //category
        builder.EntitySet<CategoryDTO>("Categories").EntityType.Select().Filter().OrderBy().Expand().Count().Page();
        //product
        builder.EntitySet<ProductDTO>("Products").EntityType.Select().Filter().OrderBy().Expand().Count().Page();
        //productprice
        builder.EntitySet<ProductPriceDTO>("ProductPrices").EntityType.Select().Filter().OrderBy().Expand().Count().Page();
return builder.GetEdmModel();
    }

自动映射器配置文件:

public AutoMapperProfile()
    {            
        CreateMap<Product, ProductDTO>()
            .ForMember(dto => dto.Category, conf => conf.AllowNull())
            .ForMember(dto => dto.ProductPrices, dest => dest.MapFrom(x => x.ProductPrices))
            .ForMember(dto => dto.ProductPrices, dest => dest.ExplicitExpansion())
            .ForMember(dto => dto.ProductPrices, conf => conf.AllowNull());
        CreateMap<ProductPrice, ProductPriceDTO>()
            .ForMember(dto => dto.Product, conf => conf.AllowNull())
            .ForMember(dto => dto.Vendor, conf => conf.AllowNull());            
    }

控制器:

[Authorize]
[ODataRoutePrefix("Products")]
public class ProductsController : BaseODataController
{
    private readonly IProductService ProductService;
    private readonly IProductPriceService ProductPriceService;

    public ProductsController(IMapper mapper, IProductService productService, IProductPriceService productPriceService) : base(mapper)
    {
        ProductService = productService;
        ProductPriceService = productPriceService;
    }

    [AllowAnonymous]
    [EnableQuery]
    public IQueryable<ProductDTO> Get(ODataQueryOptions queryOptions)
    {
        var query = ProductService.QueryProducts();
        string[] includes = GetExpandNamesFromODataQuery(queryOptions);
        if (includes != null && includes.Length > 0)
        {
            return query.ProjectTo<ProductDTO>(null, includes);
        }
        return query.ProjectTo<ProductDTO>();           
    }

    [AllowAnonymous]
    [EnableQuery]
    [ODataRoute("({key})")]
    public IQueryable<ProductDTO> Get([FromODataUri] string key, ODataQueryOptions queryOptions)
    {
        var query = ProductService.QueryProducts().Where(x => x.Id.Equals(key));
        string[] includes = GetExpandNamesFromODataQuery(queryOptions);
        if (includes != null && includes.Length > 0)
        {
            return query.ProjectTo<ProductDTO>(null, includes);
        }
        return query.ProjectTo<ProductDTO>();
    }        
}

如上所述,每个查询都可以正常工作($ select,$ filter,$ orderBy,$ count)。导航到类别(CategoryDTO)的效果也很好。

但是当我打以下电话时:

  

https://localhost:44376/odata/Products('631794')?$ expand =产品价格

我得到:

  

ArgumentException:类型的表达式   'System.Collections.Generic.IEnumerable 1[System.Tuple 3 [Dentiqo.DTOModels.ProductPriceDTO,Microsoft.EntityFrameworkCore.Query.Internal.MaterializedAnonymousObject,Microsoft.EntityFrameworkCore.Query.Internal.MaterializedAnonymousObject]]“   不能用于类型的参数   'System.Collections.Generic.IEnumerable 1[Dentiqo.DTOModels.ProductPriceDTO]&#x27; of method &#x27;System.Collections.Generic.List 1 [Dentiqo.DTOModels.ProductPriceDTO]   ToListProductPriceDTO'

     

参数名称:arg0

如果我将Get Method更改为IEnumerable并添加ToList(),一切正常。但是我不能这样做,因为查询非常慢。可能两个键(VendorID和ProductID)未正确映射?

0 个答案:

没有答案