ASP.NetCore OData DTO $ expand导航属性导致空数组

时间:2019-07-13 15:04:17

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

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

有2个具有2个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();           
        return builder.GetEdmModel();
        //productprice
        builder.EntitySet<ProductPriceDTO>("ProductPrices").EntityType.Select().Filter().OrderBy().Expand().Count().Page();
    }

自动映射器配置文件:

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)。 但是当我打以下电话时:

  

https://localhost:44376/odata/Products('631794')?$ expand =类别

我得到:

  

{“ @ odata.context”:“ https://localhost:44376/odata/ $ metadata#Products”,“ value”:[

作为回应。

在Visual Studio的输出中,有一条消息:

  

在类型'System.Int16'和'System.Boolean'之间未定义任何强制操作符。

我认为Automapper配置文件一定有问题。正如我在某处阅读的带有包含参数的.ProjectTo()创建一个Select来从导航属性中获取相关数据。我认为用DTO中的[ForeignKey]创建关系就足够了。

0 个答案:

没有答案